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}