hive-runtime: cancel and idle watchdog for ACP turns
`Runtime` gets a fourth operation, `canceller()`: a handle that stops the turn in flight from outside `run`. The ACP backend returns one; claude returns `None`, because the harness stops a claude turn by signalling the `claude` process, and that path is unchanged. Both stops send the agent `session/cancel`: - `Canceller::cancel()`, when asked from outside. The turn then ends normally, reported with stop reason `cancelled` whatever reason the agent gives. opencode 1.15.10, for one, answers a cancelled prompt with `end_turn` (`acp/agent.ts` `prompt()` always returns `end_turn`). - The idle watchdog, once no `session/update` has arrived for `Config::idle_timeout`, the same field claude's watchdog reads. The turn fails with `AcpError::IdleTimeout`. An agent that has not answered the prompt 10s after `session/cancel` is killed (`IdleKilled` / `CancelIgnored`), and the next turn respawns it. The watchdog is also what ends a turn stuck on a provider HTTP 429. opencode 1.15.10 retries a retryable provider error with no attempt limit (`session/retry.ts` `policy`, `session/processor.ts` `Effect.retry`) and forwards neither `session.status` nor `session.error` over ACP (its `handleEvent` only handles `permission.asked` and `message.part.*`). So the ACP client sees nothing at all until the provider recovers. The `IdleTimeout` message says a silently retried provider error looks like this. Refs #4391
This commit is contained in:
parent
8ec51ca774
commit
bcbeb8ac9c
6 changed files with 333 additions and 28 deletions
|
|
@ -21,7 +21,7 @@ mod acp;
|
|||
mod claude;
|
||||
mod spec;
|
||||
|
||||
pub use acp::{AcpError, AcpRuntime, PermissionAsk, PermissionPolicy};
|
||||
pub use acp::{AcpError, AcpRuntime, Canceller, PermissionAsk, PermissionPolicy};
|
||||
pub use claude::ClaudeRuntime;
|
||||
pub use hive_claude::{
|
||||
CompactionPolicy, Config, PercentPolicy, Progress, SessionStore, Sink, Telemetry, TokenUsage,
|
||||
|
|
@ -52,6 +52,11 @@ pub trait Runtime {
|
|||
/// Returns the file the session was moved to, or `None` if there was no
|
||||
/// session to archive. Only call it between turns.
|
||||
fn archive(&self) -> Result<Option<PathBuf>>;
|
||||
|
||||
/// A handle that stops the turn in flight from outside [`Self::run`].
|
||||
/// `None` for a runtime without one: a claude turn is stopped by
|
||||
/// signalling its `claude` process.
|
||||
fn canceller(&self) -> Option<Canceller>;
|
||||
}
|
||||
|
||||
/// The runtime an agent was configured with, chosen at startup from a
|
||||
|
|
@ -82,6 +87,13 @@ impl<P: CompactionPolicy> Runtime for AgentRuntime<P> {
|
|||
Self::Acp(r) => r.archive(),
|
||||
}
|
||||
}
|
||||
|
||||
fn canceller(&self) -> Option<Canceller> {
|
||||
match self {
|
||||
Self::Claude(r) => r.canceller(),
|
||||
Self::Acp(r) => r.canceller(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Why a runtime operation did not complete.
|
||||
|
|
|
|||
Loading…
Reference in a new issue