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