ag_agent/app_server/contract.rs
1//! Shared app-server contracts and request/response types.
2
3use std::future::Future;
4use std::path::PathBuf;
5use std::pin::Pin;
6use std::sync::{Arc, Mutex};
7
8use tokio::sync::mpsc;
9
10use crate::app_server::AppServerError;
11use crate::channel::AgentRequestKind;
12use crate::model::agent::ReasoningLevel;
13use crate::model::turn_prompt::TurnPrompt;
14
15/// Boxed async result used by [`AppServerClient`] trait methods.
16pub type AppServerFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>;
17
18/// Boxed async result that can borrow values from the current call frame.
19pub(crate) type BorrowedAppServerFuture<'scope, T> =
20 Pin<Box<dyn Future<Output = T> + Send + 'scope>>;
21
22/// Incremental event emitted during one app-server turn.
23///
24/// The caller receives these events through an [`mpsc::UnboundedSender`]
25/// channel while the turn is in progress, enabling real-time streaming of
26/// agent output and progress updates to the UI.
27#[derive(Clone, Debug, PartialEq)]
28pub enum AppServerStreamEvent {
29 /// Assistant text received while a turn is running.
30 AssistantMessage {
31 /// Text payload emitted by the provider.
32 message: String,
33 /// Optional provider phase label for this assistant item.
34 ///
35 /// Codex `item/completed` agent messages may include `phase` values
36 /// (for example, from multi-phase prompting flows). Providers that do
37 /// not expose phases set this to `None`.
38 phase: Option<String>,
39 /// Whether `message` is a partial delta chunk that should be appended
40 /// inline without paragraph spacing.
41 is_delta: bool,
42 },
43 /// An `item/started` event produced a progress description.
44 ProgressUpdate(String),
45}
46
47/// Input payload for one app-server turn execution.
48#[derive(Clone)]
49pub struct AppServerTurnRequest {
50 /// Session worktree folder where the provider runtime executes.
51 pub folder: PathBuf,
52 /// Live in-memory session output buffer updated by the streaming consumer.
53 ///
54 /// When set, restart-and-retry reads the latest accumulated output from
55 /// this buffer instead of the stale `session_output` snapshot, ensuring
56 /// content streamed before the crash is included in the replay prompt.
57 pub live_session_output: Option<Arc<Mutex<String>>>,
58 /// Provider-specific model identifier.
59 pub model: String,
60 /// Structured prompt payload for this turn.
61 pub prompt: TurnPrompt,
62 /// Canonical request kind that drives transport behavior and protocol
63 /// semantics for this turn.
64 pub request_kind: AgentRequestKind,
65 /// Provider-native thread/session id used to resume context in a newly
66 /// started runtime.
67 pub provider_conversation_id: Option<String>,
68 /// Persisted provider-native conversation id that already received the
69 /// full instruction bootstrap, when available.
70 pub persisted_instruction_conversation_id: Option<String>,
71 /// Reasoning effort preference for this turn.
72 ///
73 /// Ignored by providers/models that do not support reasoning effort.
74 pub reasoning_level: ReasoningLevel,
75 /// Stable agentty session id.
76 pub session_id: String,
77}
78
79/// Normalized result for one app-server turn.
80#[derive(Debug)]
81pub struct AppServerTurnResponse {
82 pub assistant_message: String,
83 pub context_reset: bool,
84 pub input_tokens: u64,
85 pub output_tokens: u64,
86 pub pid: Option<u32>,
87 /// Provider-native thread/session id observed after the turn.
88 pub provider_conversation_id: Option<String>,
89}
90
91/// Persistent app-server session boundary used by session workers.
92#[cfg_attr(any(test, feature = "test-utils"), mockall::automock)]
93pub trait AppServerClient: Send + Sync {
94 /// Executes one prompt turn for a session and returns normalized output.
95 ///
96 /// Intermediate events (agent messages, progress updates) are sent through
97 /// `stream_tx` as they arrive, enabling the caller to display streaming
98 /// output before the turn completes.
99 fn run_turn(
100 &self,
101 request: AppServerTurnRequest,
102 stream_tx: mpsc::UnboundedSender<AppServerStreamEvent>,
103 ) -> AppServerFuture<Result<AppServerTurnResponse, AppServerError>>;
104
105 /// Stops and forgets a session runtime, if one exists.
106 fn shutdown_session(&self, session_id: String) -> AppServerFuture<()>;
107}