Harden local tool execution
This commit is contained in:
@@ -210,6 +210,8 @@ impl App {
|
||||
return;
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
self.tool_cards.clear();
|
||||
#[cfg(target_os = "macos")]
|
||||
if !std::mem::take(&mut self.skip_compaction_once)
|
||||
&& crate::compaction::should_compact(self.context_used, self.context_limit)
|
||||
{
|
||||
@@ -388,11 +390,47 @@ impl App {
|
||||
return self.poll_tool_result_check();
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
if self.active_tools.is_some() {
|
||||
while let Some(event) = self
|
||||
.active_tools
|
||||
.as_ref()
|
||||
.and_then(crate::agent::try_tool_event)
|
||||
{
|
||||
match event {
|
||||
crate::agent::ToolEvent::State {
|
||||
index,
|
||||
state,
|
||||
result,
|
||||
} => {
|
||||
if let Some(card) = self.tool_cards.get_mut(index) {
|
||||
card.state = state;
|
||||
if result.is_some() {
|
||||
card.result = result;
|
||||
}
|
||||
}
|
||||
self.activity = Some(format!("Tool {} · {}", index + 1, state.label()));
|
||||
}
|
||||
crate::agent::ToolEvent::Approval {
|
||||
index,
|
||||
prompt,
|
||||
decision,
|
||||
} => {
|
||||
if let Some(card) = self.tool_cards.get_mut(index) {
|
||||
card.state = crate::agent::ToolLifecycle::AwaitingApproval;
|
||||
}
|
||||
self.pending_tool_approval = Some((prompt, decision));
|
||||
self.activity = Some(format!("Tool {} · Awaiting approval", index + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
if let Some(active) = &self.active_tools {
|
||||
match crate::agent::try_tool_result(active) {
|
||||
Ok(Some(result)) => {
|
||||
let cancelled = active.cancel.load(Ordering::Relaxed);
|
||||
self.active_tools = None;
|
||||
self.pending_tool_approval = None;
|
||||
if cancelled {
|
||||
self.generating = false;
|
||||
self.activity = Some("Stopped".into());
|
||||
@@ -453,6 +491,13 @@ impl App {
|
||||
&& !message.user
|
||||
{
|
||||
message.append(reasoning, &content);
|
||||
if !reasoning
|
||||
&& self.tool_cards.is_empty()
|
||||
&& crate::agent::has_tool_markup(&message.content)
|
||||
{
|
||||
self.tool_cards.push(crate::agent::ToolCard::streaming());
|
||||
self.activity = Some("Parsing tool call…".into());
|
||||
}
|
||||
transcript_changed = true;
|
||||
}
|
||||
}
|
||||
@@ -501,6 +546,7 @@ impl App {
|
||||
Ok(_) => {
|
||||
self.generating = false;
|
||||
self.activity = None;
|
||||
self.tool_cards.clear();
|
||||
start_queued = !self.queued_inputs.is_empty()
|
||||
|| self.manual_compaction_queued;
|
||||
}
|
||||
@@ -628,8 +674,13 @@ impl App {
|
||||
self.agent_tools = Some((session_id, Arc::new(Mutex::new(tools))));
|
||||
}
|
||||
let tools = Arc::clone(&self.agent_tools.as_ref().unwrap().1);
|
||||
self.tool_cards = calls
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(crate::agent::ToolCard::parsing)
|
||||
.collect();
|
||||
self.active_tools = Some(crate::agent::execute_async(tools, calls));
|
||||
self.activity = Some("Running tools…".into());
|
||||
self.activity = Some("Parsing tool calls…".into());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -685,6 +736,7 @@ impl App {
|
||||
.collect();
|
||||
assistant.reasoning_open = assistant_reasoning;
|
||||
self.conversation.push(assistant);
|
||||
self.tool_cards.clear();
|
||||
let idle_timeout = Duration::from_secs(self.config.idle_timeout_minutes.max(1) as u64 * 60);
|
||||
self.active_generation = Some(
|
||||
self.generation_service
|
||||
@@ -704,6 +756,20 @@ impl App {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub(super) fn stop_agent_jobs(&mut self) {
|
||||
let Some((_, tools)) = &self.agent_tools else {
|
||||
return;
|
||||
};
|
||||
let Ok(mut tools) = tools.try_lock() else {
|
||||
return;
|
||||
};
|
||||
let failures = tools.stop_all_jobs();
|
||||
if !failures.is_empty() {
|
||||
self.error = Some(failures.join("\n"));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn start_tool_result_check(
|
||||
&mut self,
|
||||
|
||||
Reference in New Issue
Block a user