//! `hive-runtime` — the layer an agent's turns are driven through, with one //! backend per runtime: //! //! - [`ClaudeRuntime`] — `claude --print`, via `hive_claude`'s //! [`hive_claude::InfiniteSession`]; //! - [`AcpRuntime`] — any Agent Client Protocol agent, spawned from the //! command, args and env it is handed (see [`RuntimeSpec`]). It knows no //! agent by name. //! //! Both report a turn through the same [`Sink`], in claude's `stream-json` //! shape: the ACP backend translates its `session/update` notifications into //! it, so a consumer of that stream reads either backend unchanged. //! //! Depends on no hyperhive binary crate, so the harness and the subagent //! daemon can both drive turns through it. use std::future::Future; use std::path::PathBuf; mod acp; mod claude; mod spec; pub use acp::{ AcpError, AcpRuntime, Canceller, Choice, Choices, PermissionAsk, PermissionPolicy, ReportedCost, SessionChoices, }; pub use claude::ClaudeRuntime; pub use hive_claude::{ CompactionPolicy, Config, PercentPolicy, Progress, SessionStore, Sink, Telemetry, TokenUsage, }; pub use spec::{ ACP_API_KEY_ENV_ENV, ACP_ARGS_ENV, ACP_COMMAND_ENV, ACP_ENV_ENV, AcpCommand, RUNTIME_ENV, RuntimeSpec, SpecError, }; /// The operations every runtime backend offers. /// /// Each takes the same [`Config`] the claude backend spawns from; a backend /// reads the fields that mean something to it and ignores the rest. pub trait Runtime { /// Run one turn on the agent's durable session, creating it if there is /// none, and stream it to `sink`. fn run( &self, config: &Config, prompt: &str, sink: &impl Sink, ) -> impl Future>; /// Compact the durable session now. With no session there is nothing to /// compact, and this succeeds without doing anything. fn compact(&self, config: &Config, sink: &impl Sink) -> impl Future>; /// Set the durable session aside so the next turn starts a fresh one. /// 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>; /// 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; /// A handle reading the model and effort the session lets the client /// pick. `None` for a runtime whose session offers none to read: claude's /// `--model` and `--effort` are passed through unchecked. fn choices(&self) -> Option; /// What the agent reported spending since the last call, one entry per /// currency. Empty for a runtime whose agent reports no cost: claude /// exports its own. fn take_reported_cost(&self) -> Vec; } /// The runtime an agent was configured with, chosen at startup from a /// [`RuntimeSpec`]. pub enum AgentRuntime { Claude(ClaudeRuntime

), Acp(Box>), } impl Runtime for AgentRuntime

{ async fn run(&self, config: &Config, prompt: &str, sink: &impl Sink) -> Result { match self { Self::Claude(r) => r.run(config, prompt, sink).await, Self::Acp(r) => r.run(config, prompt, sink).await, } } async fn compact(&self, config: &Config, sink: &impl Sink) -> Result<()> { match self { Self::Claude(r) => r.compact(config, sink).await, Self::Acp(r) => r.compact(config, sink).await, } } fn archive(&self) -> Result> { match self { Self::Claude(r) => r.archive(), Self::Acp(r) => r.archive(), } } fn canceller(&self) -> Option { match self { Self::Claude(r) => r.canceller(), Self::Acp(r) => r.canceller(), } } fn choices(&self) -> Option { match self { Self::Claude(r) => r.choices(), Self::Acp(r) => r.choices(), } } fn take_reported_cost(&self) -> Vec { match self { Self::Claude(r) => r.take_reported_cost(), Self::Acp(r) => r.take_reported_cost(), } } } /// Why a runtime operation did not complete. #[derive(Debug, thiserror::Error)] #[non_exhaustive] pub enum Error { /// From the claude backend, unchanged. #[error(transparent)] Claude(#[from] hive_claude::Error), /// From the ACP backend. #[error(transparent)] Acp(#[from] AcpError), } /// Result alias for this crate. pub type Result = std::result::Result;