hive-agent: dashboard Cancel and idle stalls for ACP agents
`/api/cancel` stopped a turn only by SIGINTing a child process whose argv0 is `claude`, so on an ACP agent it reported "no claude process to interrupt" and the turn ran on. The serve loop now stores the session's canceller on the `Bus` when the runtime has one, and `/api/cancel` uses it: the agent is sent `session/cancel`, and the next wake prompt carries the interrupted hint, as for a signalled claude turn. Without a canceller (claude), the SIGINT path is untouched. An ACP turn stopped by the idle watchdog (`HIVE_TURN_IDLE_SECS`) becomes `TurnError::AgentStall(note)`, handled like `ApiStall`: park for `HIVE_STALL_SLEEP_SECS`, requeue, and record `api_stall`. The TurnEnd note is the runtime's message (how long the agent was silent, whether it had to be killed, and that a silently retried provider error such as HTTP 429 looks like this), not the claude-specific `ApiStall` text. A cancel the agent ignores is a `Failed` turn. Refs #4391
This commit is contained in:
parent
bcbeb8ac9c
commit
91e47732a6
7 changed files with 115 additions and 9 deletions
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::atomic::{AtomicBool, AtomicI64, AtomicU64, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::{Arc, Mutex, OnceLock};
|
||||
|
||||
use chrono::Utc;
|
||||
use hive_claude::TokenUsage;
|
||||
|
|
@ -331,6 +331,10 @@ pub struct Bus {
|
|||
/// set, the compact has already run and that flag has already been
|
||||
/// cleared by `take_compact`.
|
||||
post_compact_wake: Arc<Mutex<Option<String>>>,
|
||||
/// Stops the turn in flight, for a runtime that offers it (ACP). Set once
|
||||
/// by the serve loop when it builds the session; unset on claude, whose
|
||||
/// turn `/api/cancel` stops by signalling the `claude` process.
|
||||
turn_canceller: Arc<OnceLock<hive_runtime::Canceller>>,
|
||||
/// Current fresh-claude-session id (FK to `sessions.id`). Set by the
|
||||
/// bin loop after minting a session row on a fresh start; stamped onto
|
||||
/// every `turn_stats` row until the next fresh session. `None` before
|
||||
|
|
@ -422,6 +426,7 @@ impl Bus {
|
|||
session_reset_pending: Arc::new(AtomicBool::new(false)),
|
||||
compact_pending: Arc::new(Mutex::new(None)),
|
||||
post_compact_wake: Arc::new(Mutex::new(None)),
|
||||
turn_canceller: Arc::default(),
|
||||
session_id: Arc::new(Mutex::new(None)),
|
||||
fresh_session: Arc::new(AtomicBool::new(false)),
|
||||
tool_calls: Arc::new(Mutex::new(std::collections::HashMap::new())),
|
||||
|
|
@ -445,6 +450,19 @@ impl Bus {
|
|||
self.event_seq.fetch_add(1, Ordering::SeqCst) + 1
|
||||
}
|
||||
|
||||
/// Record how the session's turn in flight is stopped. Only the first call
|
||||
/// takes effect; there is one session per harness.
|
||||
pub fn set_turn_canceller(&self, canceller: hive_runtime::Canceller) {
|
||||
let _ = self.turn_canceller.set(canceller);
|
||||
}
|
||||
|
||||
/// How to stop the turn in flight, if the runtime offers a way (see
|
||||
/// [`Self::set_turn_canceller`]).
|
||||
#[must_use]
|
||||
pub fn turn_canceller(&self) -> Option<&hive_runtime::Canceller> {
|
||||
self.turn_canceller.get()
|
||||
}
|
||||
|
||||
/// Request a session reset (operator `POST /api/new-session`). Deferred:
|
||||
/// the flag is consumed at the next turn boundary by `drive_turn`, which
|
||||
/// archives the current session so no claude process is mid-write when the
|
||||
|
|
|
|||
Loading…
Reference in a new issue