Skip to main content

adk_managed/
runtime.rs

1//! Core trait and handle types for the managed agent runtime.
2//!
3//! The [`ManagedAgentRuntime`] trait defines the full lifecycle interface for
4//! managed agents: create agents from declarative definitions, start sessions,
5//! send/receive events, pause/resume/interrupt, and archive.
6//!
7//! # Architecture
8//!
9//! The runtime is a **library**, not a service. The platform hosts it.
10//! This trait is provider-agnostic — it behaves identically for Gemini, OpenAI,
11//! Anthropic, Ollama, and OpenAI-compatible providers.
12//!
13//! # Example
14//!
15//! ```rust,ignore
16//! use adk_managed::runtime::{ManagedAgentRuntime, AgentHandle, SessionHandle};
17//! use adk_managed::types::ManagedAgentDef;
18//!
19//! async fn example(runtime: &dyn ManagedAgentRuntime) {
20//!     let def = ManagedAgentDef::default();
21//!     let agent = runtime.create(def).await.unwrap();
22//!     let session = runtime.start_session(&agent, None).await.unwrap();
23//!     let status = runtime.status(&session).await.unwrap();
24//!     println!("Session status: {status:?}");
25//! }
26//! ```
27
28use std::collections::HashMap;
29
30use async_trait::async_trait;
31use futures::stream::BoxStream;
32use serde::{Deserialize, Serialize};
33
34use crate::types::{ManagedAgentDef, RuntimeError, SessionEvent, SessionStatus, UserEvent};
35
36// ─── Handle Types ────────────────────────────────────────────────────────────
37
38/// Opaque agent handle.
39///
40/// The platform assigns the user-facing `agt_` prefixed ID; the runtime uses
41/// this internal handle for lookups. The inner string is an implementation detail.
42///
43/// # Example
44///
45/// ```
46/// use adk_managed::runtime::AgentHandle;
47///
48/// let handle = AgentHandle("agent_abc123".to_string());
49/// assert_eq!(handle.0, "agent_abc123");
50/// ```
51#[derive(Debug, Clone, PartialEq, Eq, Hash)]
52pub struct AgentHandle(pub String);
53
54/// Opaque session handle.
55///
56/// Identifies an active or archived session within the runtime. The inner string
57/// is an implementation detail assigned by the runtime on session creation.
58///
59/// # Example
60///
61/// ```
62/// use adk_managed::runtime::SessionHandle;
63///
64/// let handle = SessionHandle("session_xyz789".to_string());
65/// assert_eq!(handle.0, "session_xyz789");
66/// ```
67#[derive(Debug, Clone, PartialEq, Eq, Hash)]
68pub struct SessionHandle(pub String);
69
70// ─── EnvironmentConfig ───────────────────────────────────────────────────────
71
72/// Optional environment configuration for a session.
73///
74/// Provides environment variables and working directory context that the agent
75/// session can use during execution (e.g., for sandbox or tool execution).
76///
77/// # Example
78///
79/// ```
80/// use adk_managed::runtime::EnvironmentConfig;
81///
82/// let env = EnvironmentConfig {
83///     env_vars: [("API_KEY".to_string(), "secret".to_string())].into(),
84///     working_dir: Some("/workspace".to_string()),
85/// };
86/// assert_eq!(env.env_vars.get("API_KEY").unwrap(), "secret");
87/// ```
88#[derive(Debug, Clone, Default, Serialize, Deserialize)]
89pub struct EnvironmentConfig {
90    /// Environment variables available to the agent.
91    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
92    pub env_vars: HashMap<String, String>,
93
94    /// Optional working directory for the session.
95    #[serde(skip_serializing_if = "Option::is_none")]
96    pub working_dir: Option<String>,
97}
98
99// ─── ManagedAgentRuntime Trait ───────────────────────────────────────────────
100
101/// The central async trait defining the managed agent lifecycle.
102///
103/// Implementations of this trait encapsulate the full agent lifecycle:
104/// creating agents from declarative definitions, starting durable sessions,
105/// sending/receiving events, pausing/resuming, interrupting, and archiving.
106///
107/// The trait is provider-agnostic: it takes a [`ManagedAgentDef`] with a
108/// [`ModelRef`](crate::types::ModelRef) and behaves identically regardless
109/// of which LLM provider powers the agent.
110///
111/// # Implementors
112///
113/// - `DefaultManagedAgentRuntime` — the default implementation
114///   composed from `Runner` + pluggable `SessionService` + optional sandbox/memory.
115///
116/// # Design Notes
117///
118/// - All methods return `Result<_, RuntimeError>` for structured error handling.
119/// - `stream_events` returns a `BoxStream` for SSE-compatible event delivery.
120/// - `from_seq` on `stream_events` enables `Last-Event-ID` reconnection.
121/// - The runtime is `Send + Sync` for use across async task boundaries.
122#[async_trait]
123pub trait ManagedAgentRuntime: Send + Sync {
124    /// Create a managed agent from a declarative definition.
125    ///
126    /// Resolves the [`ModelRef`](crate::types::ModelRef), builds a runnable
127    /// agent, and stores it in the internal registry. Returns an opaque handle
128    /// for use with `start_session`.
129    async fn create(&self, def: ManagedAgentDef) -> Result<AgentHandle, RuntimeError>;
130
131    /// Start a new session for the given agent.
132    ///
133    /// Creates a session in `Queued` status, initializes the session loop,
134    /// and returns a handle for event interaction. The optional
135    /// [`EnvironmentConfig`] provides env vars and working directory.
136    async fn start_session(
137        &self,
138        agent: &AgentHandle,
139        env: Option<EnvironmentConfig>,
140    ) -> Result<SessionHandle, RuntimeError>;
141
142    /// Send an event from the client to the agent session.
143    ///
144    /// Dispatches the [`UserEvent`] to the session loop. The event type
145    /// determines behavior:
146    /// - `user.message` — enqueues a message for processing
147    /// - `user.interrupt` — signals the session to stop at next boundary
148    /// - `user.custom_tool_result` — delivers a result to a parked tool call
149    /// - `user.tool_confirmation` — approves or denies a pending tool use
150    async fn send_event(
151        &self,
152        session: &SessionHandle,
153        event: UserEvent,
154    ) -> Result<(), RuntimeError>;
155
156    /// Subscribe to the session's event stream.
157    ///
158    /// Returns a stream of [`SessionEvent`]s. If `from_seq` is provided,
159    /// replays all events with `seq > from_seq` before attaching to the
160    /// live broadcast (enabling SSE `Last-Event-ID` reconnection).
161    async fn stream_events(
162        &self,
163        session: &SessionHandle,
164        from_seq: Option<u64>,
165    ) -> Result<BoxStream<'static, SessionEvent>, RuntimeError>;
166
167    /// Interrupt the session at the next safe boundary.
168    ///
169    /// Signals the session loop's cancellation token. The loop will stop
170    /// processing at the next inter-event boundary and emit `status.idle`.
171    async fn interrupt(&self, session: &SessionHandle) -> Result<(), RuntimeError>;
172
173    /// Pause the session, checkpointing current state.
174    ///
175    /// Stops consuming new input and persists the current run-state.
176    /// The session transitions to `Paused` status.
177    async fn pause(&self, session: &SessionHandle) -> Result<(), RuntimeError>;
178
179    /// Resume a paused session from its last checkpoint.
180    ///
181    /// Clears the pause flag, rehydrates state if needed, and returns
182    /// the session to active processing.
183    async fn resume(&self, session: &SessionHandle) -> Result<(), RuntimeError>;
184
185    /// Query the current status of a session.
186    async fn status(&self, session: &SessionHandle) -> Result<SessionStatus, RuntimeError>;
187
188    /// Archive a session (terminal state).
189    ///
190    /// Sets the session to `Archived` status and stops the session loop.
191    /// Archived sessions retain their event log for read access.
192    async fn archive(&self, session: &SessionHandle) -> Result<(), RuntimeError>;
193
194    /// Delete a session and its associated data.
195    ///
196    /// Archives the session (if not already terminal) and removes all
197    /// persisted data including events and checkpoints.
198    async fn delete_session(&self, session: &SessionHandle) -> Result<(), RuntimeError>;
199}