1use std::collections::BTreeMap;
2use std::path::PathBuf;
3
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6
7pub mod runtime {
8 use super::*;
9
10 pub const RUNTIME_EVENT_ENVELOPE_SCHEMA_VERSION: u32 = 1;
11
12 #[derive(Debug, Clone, Serialize, Deserialize)]
13 pub struct RuntimeEventEnvelope {
14 #[serde(default = "default_runtime_event_envelope_schema_version")]
15 pub schema_version: u32,
16 pub seq: u64,
17 pub event: String,
18 pub kind: String,
19 pub thread_id: String,
20 pub turn_id: Option<String>,
21 pub item_id: Option<String>,
22 pub timestamp: String,
23 #[serde(skip_serializing_if = "Option::is_none")]
24 pub created_at: Option<String>,
25 pub payload: Value,
26 #[serde(default)]
27 #[serde(flatten)]
28 pub extra: BTreeMap<String, Value>,
29 }
30
31 fn default_runtime_event_envelope_schema_version() -> u32 {
32 RUNTIME_EVENT_ENVELOPE_SCHEMA_VERSION
33 }
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct Envelope<T> {
38 pub request_id: String,
39 #[serde(skip_serializing_if = "Option::is_none")]
40 pub thread_id: Option<String>,
41 pub body: T,
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
45#[serde(rename_all = "snake_case")]
46pub enum ThreadStatus {
47 Running,
48 Idle,
49 Completed,
50 Failed,
51 Paused,
52 Archived,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
56#[serde(rename_all = "snake_case")]
57pub enum SessionSource {
58 Interactive,
59 Resume,
60 Fork,
61 Api,
62 Unknown,
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct Thread {
67 pub id: String,
68 pub preview: String,
69 pub ephemeral: bool,
70 pub model_provider: String,
71 pub created_at: i64,
72 pub updated_at: i64,
73 pub status: ThreadStatus,
74 #[serde(skip_serializing_if = "Option::is_none")]
75 pub path: Option<PathBuf>,
76 pub cwd: PathBuf,
77 pub cli_version: String,
78 pub source: SessionSource,
79 #[serde(skip_serializing_if = "Option::is_none")]
80 pub name: Option<String>,
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct ThreadStartParams {
85 #[serde(skip_serializing_if = "Option::is_none")]
86 pub model: Option<String>,
87 #[serde(skip_serializing_if = "Option::is_none")]
88 pub model_provider: Option<String>,
89 #[serde(skip_serializing_if = "Option::is_none")]
90 pub cwd: Option<PathBuf>,
91 #[serde(default)]
92 pub persist_extended_history: bool,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
96pub struct ThreadResumeParams {
97 pub thread_id: String,
98 #[serde(skip_serializing_if = "Option::is_none")]
99 pub history: Option<Vec<Value>>,
100 #[serde(skip_serializing_if = "Option::is_none")]
101 pub path: Option<PathBuf>,
102 #[serde(skip_serializing_if = "Option::is_none")]
103 pub model: Option<String>,
104 #[serde(skip_serializing_if = "Option::is_none")]
105 pub model_provider: Option<String>,
106 #[serde(skip_serializing_if = "Option::is_none")]
107 pub cwd: Option<PathBuf>,
108 #[serde(skip_serializing_if = "Option::is_none")]
109 pub approval_policy: Option<String>,
110 #[serde(skip_serializing_if = "Option::is_none")]
111 pub sandbox: Option<String>,
112 #[serde(skip_serializing_if = "Option::is_none")]
113 pub config: Option<Value>,
114 #[serde(skip_serializing_if = "Option::is_none")]
115 pub base_instructions: Option<String>,
116 #[serde(skip_serializing_if = "Option::is_none")]
117 pub developer_instructions: Option<String>,
118 #[serde(skip_serializing_if = "Option::is_none")]
119 pub personality: Option<String>,
120 #[serde(default)]
121 pub persist_extended_history: bool,
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
125pub struct ThreadForkParams {
126 pub thread_id: String,
127 #[serde(skip_serializing_if = "Option::is_none")]
128 pub path: Option<PathBuf>,
129 #[serde(skip_serializing_if = "Option::is_none")]
130 pub model: Option<String>,
131 #[serde(skip_serializing_if = "Option::is_none")]
132 pub model_provider: Option<String>,
133 #[serde(skip_serializing_if = "Option::is_none")]
134 pub cwd: Option<PathBuf>,
135 #[serde(skip_serializing_if = "Option::is_none")]
136 pub approval_policy: Option<String>,
137 #[serde(skip_serializing_if = "Option::is_none")]
138 pub sandbox: Option<String>,
139 #[serde(skip_serializing_if = "Option::is_none")]
140 pub config: Option<Value>,
141 #[serde(skip_serializing_if = "Option::is_none")]
142 pub base_instructions: Option<String>,
143 #[serde(skip_serializing_if = "Option::is_none")]
144 pub developer_instructions: Option<String>,
145 #[serde(default)]
146 pub persist_extended_history: bool,
147}
148
149#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct ThreadListParams {
151 #[serde(default)]
152 pub include_archived: bool,
153 #[serde(skip_serializing_if = "Option::is_none")]
154 pub limit: Option<usize>,
155}
156
157#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct ThreadReadParams {
159 pub thread_id: String,
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize)]
163pub struct ThreadSetNameParams {
164 pub thread_id: String,
165 pub name: String,
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize)]
169#[serde(tag = "kind", rename_all = "snake_case")]
170pub enum ThreadRequest {
171 Create {
172 #[serde(default)]
173 metadata: Value,
174 },
175 Start(ThreadStartParams),
176 Resume(ThreadResumeParams),
177 Fork(ThreadForkParams),
178 List(ThreadListParams),
179 Read(ThreadReadParams),
180 SetName(ThreadSetNameParams),
181 Archive {
182 thread_id: String,
183 },
184 Unarchive {
185 thread_id: String,
186 },
187 Message {
188 thread_id: String,
189 input: String,
190 },
191}
192
193#[derive(Debug, Clone, Serialize, Deserialize)]
195pub struct ThreadResponse {
196 pub thread_id: String,
198 pub status: String,
200 #[serde(skip_serializing_if = "Option::is_none")]
202 pub thread: Option<Thread>,
203 #[serde(default)]
205 pub threads: Vec<Thread>,
206 #[serde(skip_serializing_if = "Option::is_none")]
208 pub model: Option<String>,
209 #[serde(skip_serializing_if = "Option::is_none")]
211 pub model_provider: Option<String>,
212 #[serde(skip_serializing_if = "Option::is_none")]
214 pub cwd: Option<PathBuf>,
215 #[serde(skip_serializing_if = "Option::is_none")]
217 pub approval_policy: Option<String>,
218 #[serde(skip_serializing_if = "Option::is_none")]
220 pub sandbox: Option<String>,
221 #[serde(default)]
223 pub events: Vec<EventFrame>,
224 #[serde(default)]
226 pub data: Value,
227}
228
229#[derive(Debug, Clone, Serialize, Deserialize)]
231#[serde(tag = "kind", rename_all = "snake_case")]
232pub enum AppRequest {
233 Capabilities,
235 ConfigGet { key: String },
237 ConfigSet { key: String, value: String },
239 ConfigUnset { key: String },
241 ConfigList,
243 Models,
245 ThreadLoadedList,
247}
248
249#[derive(Debug, Clone, Serialize, Deserialize)]
251pub struct AppResponse {
252 pub ok: bool,
254 pub data: Value,
256 #[serde(default)]
258 pub events: Vec<EventFrame>,
259}
260
261#[derive(Debug, Clone, Serialize, Deserialize)]
263pub struct PromptRequest {
264 #[serde(skip_serializing_if = "Option::is_none")]
266 pub thread_id: Option<String>,
267 pub prompt: String,
269 #[serde(skip_serializing_if = "Option::is_none")]
271 pub model: Option<String>,
272}
273
274#[derive(Debug, Clone, Serialize, Deserialize)]
276pub struct PromptResponse {
277 pub output: String,
279 pub model: String,
281 #[serde(default)]
283 pub events: Vec<EventFrame>,
284}
285
286#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
288#[serde(rename_all = "snake_case")]
289pub enum AskForApproval {
290 UnlessTrusted,
292 OnFailure,
294 OnRequest,
296 Reject {
298 sandbox_approval: bool,
299 rules: bool,
300 mcp_elicitations: bool,
301 },
302 Never,
304}
305
306#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
308#[serde(rename_all = "snake_case")]
309pub enum ToolKind {
310 Function,
312 Mcp,
314}
315
316#[derive(Debug, Clone, Serialize, Deserialize)]
318pub struct LocalShellParams {
319 pub command: String,
321 #[serde(skip_serializing_if = "Option::is_none")]
323 pub cwd: Option<String>,
324 #[serde(skip_serializing_if = "Option::is_none")]
326 pub timeout_ms: Option<u64>,
327}
328
329#[derive(Debug, Clone, Serialize, Deserialize)]
331#[serde(tag = "type", rename_all = "snake_case")]
332pub enum ToolPayload {
333 Function { arguments: String },
335 Custom { input: String },
337 LocalShell { params: LocalShellParams },
339 Mcp {
341 server: String,
342 tool: String,
343 raw_arguments: Value,
344 #[serde(skip_serializing_if = "Option::is_none")]
345 raw_tool_call_id: Option<String>,
346 },
347}
348
349#[derive(Debug, Clone, Serialize, Deserialize)]
351#[serde(tag = "type", rename_all = "snake_case")]
352pub enum ToolOutput {
353 Function {
355 #[serde(skip_serializing_if = "Option::is_none")]
357 body: Option<Value>,
358 success: bool,
360 },
361 Mcp {
363 result: Value,
365 },
366}
367
368#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
370#[serde(rename_all = "snake_case")]
371pub enum NetworkPolicyRuleAction {
372 Allow,
374 Deny,
376}
377
378#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
380pub struct NetworkPolicyAmendment {
381 pub host: String,
383 pub action: NetworkPolicyRuleAction,
385}
386
387#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
389#[serde(tag = "type", rename_all = "snake_case")]
390pub enum ReviewDecision {
391 Approved,
393 ApprovedExecpolicyAmendment,
395 ApprovedForSession,
397 NetworkPolicyAmendment {
399 host: String,
400 action: NetworkPolicyRuleAction,
401 },
402 Denied,
404 Abort,
406}
407
408#[derive(Debug, Clone, Serialize, Deserialize)]
410#[serde(rename_all = "snake_case")]
411pub enum McpStartupStatus {
412 Starting,
414 Ready,
416 Failed { error: String },
418 Cancelled,
420}
421
422#[derive(Debug, Clone, Serialize, Deserialize)]
424pub struct McpStartupUpdateEvent {
425 pub server_name: String,
427 pub status: McpStartupStatus,
429}
430
431#[derive(Debug, Clone, Serialize, Deserialize)]
433pub struct McpStartupFailure {
434 pub server_name: String,
436 pub error: String,
438}
439
440#[derive(Debug, Clone, Serialize, Deserialize)]
442pub struct McpStartupCompleteEvent {
443 pub ready: Vec<String>,
445 pub failed: Vec<McpStartupFailure>,
447 pub cancelled: Vec<String>,
449}
450
451#[derive(Debug, Clone, Serialize, Deserialize)]
453pub struct NetworkApprovalContext {
454 pub host: String,
456 pub protocol: String,
458}
459
460#[derive(Debug, Clone, Serialize, Deserialize)]
462pub struct ExecApprovalRequestEvent {
463 pub call_id: String,
465 pub approval_id: String,
467 pub turn_id: String,
469 pub command: String,
471 pub cwd: String,
473 pub reason: String,
475 #[serde(skip_serializing_if = "Option::is_none")]
477 pub network_approval_context: Option<NetworkApprovalContext>,
478 #[serde(default)]
480 pub proposed_execpolicy_amendment: Vec<String>,
481 #[serde(default)]
483 pub proposed_network_policy_amendments: Vec<NetworkPolicyAmendment>,
484 #[serde(default)]
486 pub additional_permissions: Vec<String>,
487 #[serde(default)]
489 pub available_decisions: Vec<ReviewDecision>,
490}
491
492#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
494#[serde(rename_all = "snake_case")]
495pub enum ResponseChannel {
496 #[default]
498 Text,
499 Reasoning,
501}
502
503impl ResponseChannel {
504 pub const fn is_text(&self) -> bool {
506 matches!(self, ResponseChannel::Text)
507 }
508}
509
510#[derive(Debug, Clone, Serialize, Deserialize)]
512pub struct ApprovalDecisionRequest {
513 pub decision: String,
515 #[serde(default)]
517 pub remember: bool,
518}
519
520#[derive(Debug, Clone, Serialize, Deserialize)]
526#[serde(tag = "event", rename_all = "snake_case")]
527pub enum EventFrame {
528 ResponseStart { response_id: String },
530 ResponseDelta {
532 response_id: String,
533 delta: String,
534 #[serde(default, skip_serializing_if = "ResponseChannel::is_text")]
535 channel: ResponseChannel,
536 },
537 ResponseEnd { response_id: String },
539 ToolCallStart {
541 response_id: String,
542 tool_name: String,
543 arguments: Value,
544 },
545 ToolCallResult {
547 response_id: String,
548 tool_name: String,
549 output: Value,
550 },
551 McpStartupUpdate { update: McpStartupUpdateEvent },
553 McpStartupComplete { summary: McpStartupCompleteEvent },
555 McpToolCallBegin {
557 server_name: String,
558 tool_name: String,
559 },
560 McpToolCallEnd {
562 server_name: String,
563 tool_name: String,
564 ok: bool,
565 },
566 ExecApprovalRequest { request: ExecApprovalRequestEvent },
568 ApplyPatchApprovalRequest { request: ExecApprovalRequestEvent },
570 ElicitationRequest {
572 server_name: String,
573 request_id: String,
574 prompt: String,
575 },
576 ExecCommandBegin { command: String, cwd: String },
578 ExecCommandOutputDelta { command: String, delta: String },
580 ExecCommandEnd { command: String, exit_code: i32 },
582 PatchApplyBegin { path: String },
584 PatchApplyEnd { path: String, ok: bool },
586 TurnStarted { turn_id: String },
588 TurnComplete { turn_id: String },
590 TurnAborted { turn_id: String, reason: String },
592 Error {
594 response_id: String,
595 message: String,
596 },
597}