Skip to main content

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