Push request and insert further

This commit is contained in:
Ben Brandt 2025-07-23 13:11:54 +02:00
parent c3e4f3800d
commit cea8e61fd9
No known key found for this signature in database
GPG key ID: D4618C5D3B500571

View file

@ -180,6 +180,31 @@ pub struct ToolCall {
} }
impl ToolCall { impl ToolCall {
fn from_acp(
tool_call: acp::ToolCall,
status: ToolCallStatus,
language_registry: Arc<LanguageRegistry>,
cx: &mut App,
) -> Self {
Self {
id: tool_call.id,
label: cx.new(|cx| {
Markdown::new(
tool_call.label.into(),
Some(language_registry.clone()),
None,
cx,
)
}),
kind: tool_call.kind,
// todo! Do we assume there is either a coalesced content OR diff?
content: ToolCallContent::from_acp_contents(tool_call.content, language_registry, cx)
.into_iter()
.next(),
locations: tool_call.locations,
status,
}
}
fn to_markdown(&self, cx: &App) -> String { fn to_markdown(&self, cx: &App) -> String {
let mut markdown = format!( let mut markdown = format!(
"**Tool Call: {}**\nStatus: {}\n\n", "**Tool Call: {}**\nStatus: {}\n\n",
@ -780,98 +805,46 @@ impl AcpThread {
cx: &mut Context<Self>, cx: &mut Context<Self>,
) -> Result<()> { ) -> Result<()> {
let language_registry = self.project.read(cx).languages().clone(); let language_registry = self.project.read(cx).languages().clone();
let status = ToolCallStatus::Allowed {
let new_tool_call = ToolCall {
id: tool_call.id,
label: cx.new(|cx| {
Markdown::new(
tool_call.label.into(),
Some(language_registry.clone()),
None,
cx,
)
}),
kind: tool_call.kind,
// todo! Do we assume there is either a coalesced content OR diff?
content: ToolCallContent::from_acp_contents(tool_call.content, language_registry, cx)
.into_iter()
.next(),
locations: tool_call.locations,
status: ToolCallStatus::Allowed {
status: tool_call.status, status: tool_call.status,
},
}; };
let call = ToolCall::from_acp(tool_call, status, language_registry, cx);
if let Some((ix, current_call)) = self.tool_call_mut(tool_call.id) { let location = call.locations.last().cloned();
match &mut current_call.status {
ToolCallStatus::Allowed { status } => { if let Some((ix, current_call)) = self.tool_call_mut(&call.id) {
*status = tool_call.status; match &current_call.status {
}
ToolCallStatus::WaitingForConfirmation { .. } => { ToolCallStatus::WaitingForConfirmation { .. } => {
anyhow::bail!("Tool call hasn't been authorized yet") anyhow::bail!("Tool call hasn't been authorized yet")
} }
ToolCallStatus::Rejected => { ToolCallStatus::Rejected => {
anyhow::bail!("Tool call was rejected and therefore can't be updated") anyhow::bail!("Tool call was rejected and therefore can't be updated")
} }
ToolCallStatus::Canceled => { ToolCallStatus::Allowed { .. } | ToolCallStatus::Canceled => {}
current_call.status = ToolCallStatus::Allowed { status: new_status };
}
} }
*current_call = new_tool_call; *current_call = call;
let location = current_call.locations.last().cloned();
if let Some(location) = location {
self.set_project_location(location, cx)
}
cx.emit(AcpThreadEvent::EntryUpdated(ix)); cx.emit(AcpThreadEvent::EntryUpdated(ix));
} else { } else {
let language_registry = self.project.read(cx).languages().clone(); self.push_entry(AgentThreadEntry::ToolCall(call), cx);
let call = ToolCall {
id: tool_call.id,
label: cx.new(|cx| {
Markdown::new(
tool_call.label.into(),
Some(language_registry.clone()),
None,
cx,
)
}),
kind: tool_call.kind,
// todo! Do we assume there is either a coalesced content OR diff?
content: ToolCallContent::from_acp_contents(
tool_call.content,
language_registry,
cx,
)
.into_iter()
.next(),
locations: tool_call.locations,
status: ToolCallStatus::Allowed {
status: tool_call.status,
},
};
let location = call.locations.last().cloned();
if let Some(location) = location {
self.set_project_location(location, cx)
} }
self.push_entry(AgentThreadEntry::ToolCall(call), cx); if let Some(location) = location {
self.set_project_location(location, cx)
} }
Ok(()) Ok(())
} }
fn tool_call_mut(&mut self, id: acp::ToolCallId) -> Option<(usize, &mut ToolCall)> { fn tool_call_mut(&mut self, id: &acp::ToolCallId) -> Option<(usize, &mut ToolCall)> {
self.entries self.entries
.iter_mut() .iter_mut()
.enumerate() .enumerate()
.rev() .rev()
.find_map(|(index, tool_call)| { .find_map(|(index, tool_call)| {
if let AgentThreadEntry::ToolCall(tool_call) = tool_call if let AgentThreadEntry::ToolCall(tool_call) = tool_call
&& tool_call.id == id && &tool_call.id == id
{ {
Some((index, tool_call)) Some((index, tool_call))
} else { } else {
@ -885,7 +858,7 @@ impl AcpThread {
tool_call: acp::ToolCall, tool_call: acp::ToolCall,
possible_grants: Vec<acp::Grant>, possible_grants: Vec<acp::Grant>,
cx: &mut Context<Self>, cx: &mut Context<Self>,
) -> ToolCallRequest { ) -> oneshot::Receiver<acp::GrantId> {
let (tx, rx) = oneshot::channel(); let (tx, rx) = oneshot::channel();
let status = ToolCallStatus::WaitingForConfirmation { let status = ToolCallStatus::WaitingForConfirmation {
@ -893,61 +866,18 @@ impl AcpThread {
respond_tx: tx, respond_tx: tx,
}; };
let id = self.insert_tool_call(tool_call, status, cx); self.insert_tool_call(tool_call, status, cx);
ToolCallRequest { id, outcome: rx } rx
}
pub fn request_tool_call_confirmation(
&mut self,
tool_call_id: ToolCallId,
confirmation: acp_old::ToolCallConfirmation,
cx: &mut Context<Self>,
) -> Result<ToolCallRequest> {
let project = self.project.read(cx).languages().clone();
let Some((idx, call)) = self.tool_call_mut(tool_call_id) else {
anyhow::bail!("Tool call not found");
};
let (tx, rx) = oneshot::channel();
call.status = ToolCallStatus::WaitingForConfirmation {
confirmation: ToolCallConfirmation::from_acp(confirmation, project, cx),
respond_tx: tx,
};
cx.emit(AcpThreadEvent::EntryUpdated(idx));
Ok(ToolCallRequest {
id: tool_call_id,
outcome: rx,
})
} }
fn insert_tool_call( fn insert_tool_call(
&mut self, &mut self,
tool_call: acp_old::PushToolCallParams, tool_call: acp::ToolCall,
status: ToolCallStatus, status: ToolCallStatus,
cx: &mut Context<Self>, cx: &mut Context<Self>,
) -> acp_old::ToolCallId { ) {
let language_registry = self.project.read(cx).languages().clone(); let language_registry = self.project.read(cx).languages().clone();
let id = acp_old::ToolCallId(self.entries.len() as u64); let call = ToolCall::from_acp(tool_call, status, language_registry, cx);
let call = ToolCall {
id,
label: cx.new(|cx| {
Markdown::new(
tool_call.label.into(),
Some(language_registry.clone()),
None,
cx,
)
}),
icon: acp_icon_to_ui_icon(tool_call.icon),
content: tool_call
.content
.map(|content| ToolCallContent::from_acp(content, language_registry, cx)),
locations: tool_call.locations,
status,
};
let location = call.locations.last().cloned(); let location = call.locations.last().cloned();
if let Some(location) = location { if let Some(location) = location {
@ -955,8 +885,6 @@ impl AcpThread {
} }
self.push_entry(AgentThreadEntry::ToolCall(call), cx); self.push_entry(AgentThreadEntry::ToolCall(call), cx);
id
} }
pub fn authorize_tool_call( pub fn authorize_tool_call(
@ -965,7 +893,7 @@ impl AcpThread {
grant: acp::Grant, grant: acp::Grant,
cx: &mut Context<Self>, cx: &mut Context<Self>,
) { ) {
let Some((ix, call)) = self.tool_call_mut(id) else { let Some((ix, call)) = self.tool_call_mut(&id) else {
return; return;
}; };
@ -1135,7 +1063,7 @@ impl AcpThread {
call.status, call.status,
ToolCallStatus::WaitingForConfirmation { .. } ToolCallStatus::WaitingForConfirmation { .. }
| ToolCallStatus::Allowed { | ToolCallStatus::Allowed {
status: acp_old::ToolCallStatus::Running status: acp::ToolCallStatus::InProgress
} }
); );
@ -1147,6 +1075,7 @@ impl AcpThread {
respond_tx, .. respond_tx, ..
} = curr_status } = curr_status
{ {
// todo! do we need a way to cancel rather than reject?
respond_tx respond_tx
.send(acp_old::ToolCallConfirmationOutcome::Cancel) .send(acp_old::ToolCallConfirmationOutcome::Cancel)
.ok(); .ok();
@ -1698,7 +1627,7 @@ mod tests {
thread.entries[1], thread.entries[1],
AgentThreadEntry::ToolCall(ToolCall { AgentThreadEntry::ToolCall(ToolCall {
status: ToolCallStatus::Allowed { status: ToolCallStatus::Allowed {
status: acp_old::ToolCallStatus::Running, status: acp::ToolCallStatus::InProgress,
.. ..
}, },
.. ..
@ -1742,7 +1671,7 @@ mod tests {
thread.entries[1], thread.entries[1],
AgentThreadEntry::ToolCall(ToolCall { AgentThreadEntry::ToolCall(ToolCall {
status: ToolCallStatus::Allowed { status: ToolCallStatus::Allowed {
status: acp_old::ToolCallStatus::Finished, status: acp::ToolCallStatus::Completed,
.. ..
}, },
.. ..