Converted all external tool calling to async
This commit is contained in:
@@ -25,8 +25,8 @@ pub(crate) use limits::{count_turns_in_log, resolve_session_log};
|
||||
impl AgentPool {
|
||||
/// Run a single watchdog pass synchronously (test helper).
|
||||
#[cfg(test)]
|
||||
pub fn run_watchdog_once(&self) {
|
||||
check_orphaned_agents(&self.agents);
|
||||
pub async fn run_watchdog_once(&self) {
|
||||
check_orphaned_agents(&self.agents).await;
|
||||
}
|
||||
|
||||
/// Run one watchdog pass: detect orphans, enforce limits, kill offenders.
|
||||
@@ -39,21 +39,22 @@ impl AgentPool {
|
||||
/// `retry_count` is incremented and the story stays in `2_current/` for
|
||||
/// re-attempt. This prevents the original kill-respawn loop (bug 646)
|
||||
/// while restoring the `max_retries` semantic for turn/budget overruns.
|
||||
pub fn run_watchdog_pass(&self, project_root: Option<&Path>) -> usize {
|
||||
let orphaned = check_orphaned_agents(&self.agents);
|
||||
pub async fn run_watchdog_pass(&self, project_root: Option<&Path>) -> usize {
|
||||
let orphaned = check_orphaned_agents(&self.agents).await;
|
||||
|
||||
if let Some(root) = project_root {
|
||||
let terminated = check_agent_limits(&self.agents, root);
|
||||
let terminated = check_agent_limits(&self.agents, root).await;
|
||||
let config = ProjectConfig::load(root).unwrap_or_default();
|
||||
for (key, reason) in &terminated {
|
||||
// Step 1: snapshot the agent's worktree path so we can find every
|
||||
// process running in it (claude + any subprocesses). This must
|
||||
// happen BEFORE we mutate the agent record so we can read the
|
||||
// worktree info safely.
|
||||
let worktree_path = self.agents.lock().ok().and_then(|lock| {
|
||||
let worktree_path = {
|
||||
let lock = self.agents.lock().await;
|
||||
lock.get(key)
|
||||
.and_then(|a| a.worktree_info.as_ref().map(|wt| wt.path.clone()))
|
||||
});
|
||||
};
|
||||
|
||||
// Step 2: SIGKILL every process running in the worktree and
|
||||
// BLOCK until verified gone. The previous mechanism — portable_pty's
|
||||
@@ -85,23 +86,24 @@ impl AgentPool {
|
||||
"[watchdog] No worktree path recorded for '{key}'; cannot tree-kill, \
|
||||
falling back to portable_pty SIGHUP (likely no-op for claude-code)."
|
||||
);
|
||||
self.kill_child_for_key(key);
|
||||
self.kill_child_for_key(key).await;
|
||||
}
|
||||
|
||||
// Step 3: NOW update the agent record. The process is verified
|
||||
// gone (or we logged that SIGKILL didn't take effect, which is
|
||||
// exceptional), so flipping status away from Running can no
|
||||
// longer open a window for a concurrent spawn.
|
||||
if let Ok(mut lock) = self.agents.lock()
|
||||
&& let Some(agent) = lock.get_mut(key)
|
||||
{
|
||||
agent.status = AgentStatus::Failed;
|
||||
agent.termination_reason = Some(reason.clone());
|
||||
if let Some(handle) = agent.task_handle.take() {
|
||||
// Best-effort abort of the outer tokio task. The PTY
|
||||
// blocking thread already returned (claude is dead),
|
||||
// so this is bookkeeping rather than load-bearing.
|
||||
handle.abort();
|
||||
let mut lock = self.agents.lock().await;
|
||||
if let Some(agent) = lock.get_mut(key) {
|
||||
agent.status = AgentStatus::Failed;
|
||||
agent.termination_reason = Some(reason.clone());
|
||||
if let Some(handle) = agent.task_handle.take() {
|
||||
// Best-effort abort of the outer tokio task. The PTY
|
||||
// blocking thread already returned (claude is dead),
|
||||
// so this is bookkeeping rather than load-bearing.
|
||||
handle.abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user