Skip to main content

unified_agent_sdk/
executor.rs

1//! Provider-agnostic session execution contract.
2//!
3//! [`AgentExecutor`] is intentionally narrow: start a session, resume a session,
4//! report capabilities, and report availability. Provider-specific advanced
5//! controls remain in the underlying SDK crates so the unified surface stays
6//! predictable.
7
8use async_trait::async_trait;
9use std::path::Path;
10
11use crate::{error::Result, session::AgentSession, types::ExecutorType};
12
13/// Capability declaration for one executor implementation.
14///
15/// Use this to detect optional behavior before relying on it in higher-level
16/// orchestration code.
17#[derive(Debug, Clone)]
18pub struct AgentCapabilities {
19    /// Whether the executor can fork from an existing session history.
20    pub session_fork: bool,
21    /// Whether context/token usage events are exposed.
22    pub context_usage: bool,
23    /// Whether Model Context Protocol (MCP) tool calls are supported.
24    pub mcp_support: bool,
25    /// Whether structured output is supported.
26    pub structured_output: bool,
27}
28
29/// Runtime availability state for an executor backend.
30///
31/// This is intended for preflight checks such as “is the provider CLI installed
32/// and usable in the current environment?”.
33#[derive(Debug, Clone)]
34pub struct AvailabilityStatus {
35    /// Whether the executor is currently available.
36    pub available: bool,
37    /// Optional human-readable reason for unavailable (or additional diagnostics).
38    pub reason: Option<String>,
39}
40
41/// Runtime configuration applied when spawning or resuming a session.
42///
43/// Values in this struct are unified overrides. Each executor translates them to
44/// provider-specific settings at call time.
45///
46/// # Examples
47///
48/// ```rust
49/// use unified_agent_sdk::{PermissionPolicy, executor::SpawnConfig};
50///
51/// let config = SpawnConfig {
52///     model: Some("gpt-5-codex".to_string()),
53///     reasoning: Some("medium".to_string()),
54///     permission_policy: Some(PermissionPolicy::Prompt),
55///     env: vec![("RUST_LOG".to_string(), "debug".to_string())],
56///     context_window_override_tokens: Some(128_000),
57/// };
58///
59/// assert_eq!(config.context_window_override_tokens, Some(128_000));
60/// ```
61#[derive(Debug, Clone, Default)]
62pub struct SpawnConfig {
63    /// Optional model override.
64    pub model: Option<String>,
65    /// Optional reasoning level/effort override.
66    pub reasoning: Option<String>,
67    /// Optional permission policy override.
68    pub permission_policy: Option<crate::types::PermissionPolicy>,
69    /// Extra environment variables to forward to the executor process.
70    pub env: Vec<(String, String)>,
71    /// Optional context window capacity override (tokens) used for unified context usage events.
72    pub context_window_override_tokens: Option<u32>,
73}
74
75/// Core executor trait implemented by all providers.
76///
77/// The trait intentionally models only common behavior. Provider-specific advanced
78/// features should be layered outside this trait to keep API parity predictable.
79///
80/// # Examples
81///
82/// ```rust
83/// use std::path::Path;
84/// use unified_agent_sdk::{AgentExecutor, executor::SpawnConfig};
85///
86/// async fn run_prompt(executor: &dyn AgentExecutor) -> unified_agent_sdk::Result<()> {
87///     let session = executor
88///         .spawn(
89///             Path::new("."),
90///             "Summarize this repository.",
91///             &SpawnConfig {
92///                 model: None,
93///                 reasoning: Some("medium".to_string()),
94///                 permission_policy: None,
95///                 env: Vec::new(),
96///                 context_window_override_tokens: None,
97///             },
98///         )
99///         .await?;
100///
101///     let _metadata = session.metadata();
102///     Ok(())
103/// }
104/// ```
105#[async_trait]
106pub trait AgentExecutor: Send + Sync {
107    /// Returns the provider type implemented by this executor.
108    fn executor_type(&self) -> ExecutorType;
109
110    /// Starts a fresh session and sends `prompt` as the first turn.
111    ///
112    /// `working_dir` should point to the repository/workspace where tools and file
113    /// operations are expected to run.
114    async fn spawn(
115        &self,
116        working_dir: &Path,
117        prompt: &str,
118        config: &SpawnConfig,
119    ) -> Result<AgentSession>;
120
121    /// Resumes a previously created session and sends a follow-up `prompt`.
122    ///
123    /// `session_id` is the provider-native identifier returned by a previous session.
124    /// `reset_to` is optional and only supported by providers that expose rewind/reset
125    /// semantics.
126    async fn resume(
127        &self,
128        working_dir: &Path,
129        prompt: &str,
130        session_id: &str,
131        reset_to: Option<&str>,
132        config: &SpawnConfig,
133    ) -> Result<AgentSession>;
134
135    /// Returns static capability flags for this executor implementation.
136    fn capabilities(&self) -> AgentCapabilities;
137
138    /// Performs a lightweight availability check for required runtime dependencies.
139    fn availability(&self) -> AvailabilityStatus;
140}