1use std::path::PathBuf;
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6pub mod fleet;
7pub mod runtime;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct Envelope<T> {
11 pub request_id: String,
12 #[serde(skip_serializing_if = "Option::is_none")]
13 pub thread_id: Option<String>,
14 pub body: T,
15}
16
17#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
18#[serde(rename_all = "snake_case")]
19pub enum ThreadStatus {
20 Running,
21 Idle,
22 Completed,
23 Failed,
24 Paused,
25 Archived,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
29#[serde(rename_all = "snake_case")]
30pub enum SessionSource {
31 Interactive,
32 Resume,
33 Fork,
34 Api,
35 Unknown,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct Thread {
40 pub id: String,
41 pub preview: String,
42 pub ephemeral: bool,
43 pub model_provider: String,
44 pub created_at: i64,
45 pub updated_at: i64,
46 pub status: ThreadStatus,
47 #[serde(skip_serializing_if = "Option::is_none")]
48 pub path: Option<PathBuf>,
49 pub cwd: PathBuf,
50 pub cli_version: String,
51 pub source: SessionSource,
52 #[serde(skip_serializing_if = "Option::is_none")]
53 pub name: Option<String>,
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
57#[serde(rename_all = "snake_case")]
58pub enum ThreadGoalStatus {
59 Active,
60 Paused,
61 Blocked,
62 UsageLimited,
63 BudgetLimited,
64 Complete,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
68pub struct ThreadGoal {
69 pub thread_id: String,
70 pub goal_id: String,
71 pub objective: String,
72 pub status: ThreadGoalStatus,
73 #[serde(skip_serializing_if = "Option::is_none")]
74 pub token_budget: Option<i64>,
75 pub tokens_used: i64,
76 pub time_used_seconds: i64,
77 pub continuation_count: i64,
78 pub created_at: i64,
79 pub updated_at: i64,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct ThreadStartParams {
84 #[serde(skip_serializing_if = "Option::is_none")]
85 pub model: Option<String>,
86 #[serde(skip_serializing_if = "Option::is_none")]
87 pub model_provider: Option<String>,
88 #[serde(skip_serializing_if = "Option::is_none")]
89 pub cwd: Option<PathBuf>,
90 #[serde(default)]
91 pub persist_extended_history: bool,
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize)]
95pub struct ThreadResumeParams {
96 pub thread_id: String,
97 #[serde(skip_serializing_if = "Option::is_none")]
98 pub history: Option<Vec<Value>>,
99 #[serde(skip_serializing_if = "Option::is_none")]
100 pub path: Option<PathBuf>,
101 #[serde(skip_serializing_if = "Option::is_none")]
102 pub model: Option<String>,
103 #[serde(skip_serializing_if = "Option::is_none")]
104 pub model_provider: Option<String>,
105 #[serde(skip_serializing_if = "Option::is_none")]
106 pub cwd: Option<PathBuf>,
107 #[serde(skip_serializing_if = "Option::is_none")]
108 pub approval_policy: Option<String>,
109 #[serde(skip_serializing_if = "Option::is_none")]
110 pub sandbox: Option<String>,
111 #[serde(skip_serializing_if = "Option::is_none")]
112 pub config: Option<Value>,
113 #[serde(skip_serializing_if = "Option::is_none")]
114 pub base_instructions: Option<String>,
115 #[serde(skip_serializing_if = "Option::is_none")]
116 pub developer_instructions: Option<String>,
117 #[serde(skip_serializing_if = "Option::is_none")]
118 pub personality: Option<String>,
119 #[serde(default)]
120 pub persist_extended_history: bool,
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize)]
124pub struct ThreadForkParams {
125 pub thread_id: String,
126 #[serde(skip_serializing_if = "Option::is_none")]
127 pub path: Option<PathBuf>,
128 #[serde(skip_serializing_if = "Option::is_none")]
129 pub model: Option<String>,
130 #[serde(skip_serializing_if = "Option::is_none")]
131 pub model_provider: Option<String>,
132 #[serde(skip_serializing_if = "Option::is_none")]
133 pub cwd: Option<PathBuf>,
134 #[serde(skip_serializing_if = "Option::is_none")]
135 pub approval_policy: Option<String>,
136 #[serde(skip_serializing_if = "Option::is_none")]
137 pub sandbox: Option<String>,
138 #[serde(skip_serializing_if = "Option::is_none")]
139 pub config: Option<Value>,
140 #[serde(skip_serializing_if = "Option::is_none")]
141 pub base_instructions: Option<String>,
142 #[serde(skip_serializing_if = "Option::is_none")]
143 pub developer_instructions: Option<String>,
144 #[serde(default)]
145 pub persist_extended_history: bool,
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct ThreadListParams {
150 #[serde(default)]
151 pub include_archived: bool,
152 #[serde(skip_serializing_if = "Option::is_none")]
153 pub limit: Option<usize>,
154}
155
156#[derive(Debug, Clone, Serialize, Deserialize)]
157pub struct ThreadReadParams {
158 pub thread_id: String,
159}
160
161#[derive(Debug, Clone, Serialize, Deserialize)]
162pub struct ThreadSetNameParams {
163 pub thread_id: String,
164 pub name: String,
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct ThreadGoalSetParams {
169 pub thread_id: String,
170 pub objective: String,
171 #[serde(skip_serializing_if = "Option::is_none")]
172 pub token_budget: Option<i64>,
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize)]
176pub struct ThreadGoalGetParams {
177 pub thread_id: String,
178}
179
180#[derive(Debug, Clone, Serialize, Deserialize)]
181pub struct ThreadGoalClearParams {
182 pub thread_id: String,
183}
184
185#[derive(Debug, Clone, Serialize, Deserialize)]
186pub struct ThreadGoalProgressParams {
187 pub thread_id: String,
188 #[serde(default)]
189 pub token_delta: i64,
190 #[serde(default)]
191 pub time_delta_seconds: i64,
192 #[serde(default)]
193 pub record_continuation: bool,
194}
195
196#[derive(Debug, Clone, Serialize, Deserialize)]
197#[serde(tag = "kind", rename_all = "snake_case")]
198pub enum ThreadRequest {
199 Create {
200 #[serde(default)]
201 metadata: Value,
202 },
203 Start(ThreadStartParams),
204 Resume(ThreadResumeParams),
205 Fork(ThreadForkParams),
206 List(ThreadListParams),
207 Read(ThreadReadParams),
208 SetName(ThreadSetNameParams),
209 GoalSet(ThreadGoalSetParams),
210 GoalGet(ThreadGoalGetParams),
211 GoalClear(ThreadGoalClearParams),
212 GoalRecordProgress(ThreadGoalProgressParams),
213 Archive {
214 thread_id: String,
215 },
216 Unarchive {
217 thread_id: String,
218 },
219 Message {
220 thread_id: String,
221 input: String,
222 },
223}
224
225#[derive(Debug, Clone, Serialize, Deserialize)]
227pub struct ThreadResponse {
228 pub thread_id: String,
230 pub status: String,
232 #[serde(skip_serializing_if = "Option::is_none")]
234 pub thread: Option<Thread>,
235 #[serde(default)]
237 pub threads: Vec<Thread>,
238 #[serde(skip_serializing_if = "Option::is_none")]
240 pub goal: Option<ThreadGoal>,
241 #[serde(skip_serializing_if = "Option::is_none")]
243 pub model: Option<String>,
244 #[serde(skip_serializing_if = "Option::is_none")]
246 pub model_provider: Option<String>,
247 #[serde(skip_serializing_if = "Option::is_none")]
249 pub cwd: Option<PathBuf>,
250 #[serde(skip_serializing_if = "Option::is_none")]
252 pub approval_policy: Option<String>,
253 #[serde(skip_serializing_if = "Option::is_none")]
255 pub sandbox: Option<String>,
256 #[serde(default)]
258 pub events: Vec<EventFrame>,
259 #[serde(default)]
261 pub data: Value,
262}
263
264#[derive(Debug, Clone, Serialize, Deserialize)]
266#[serde(tag = "kind", rename_all = "snake_case")]
267pub enum AppRequest {
268 Capabilities,
270 ConfigGet { key: String },
272 ConfigSet { key: String, value: String },
274 ConfigUnset { key: String },
276 ConfigList,
278 Models,
280 ThreadLoadedList,
282}
283
284#[derive(Debug, Clone, Serialize, Deserialize)]
286pub struct AppResponse {
287 pub ok: bool,
289 pub data: Value,
291 #[serde(default)]
293 pub events: Vec<EventFrame>,
294}
295
296#[derive(Debug, Clone, Serialize, Deserialize)]
298pub struct PromptRequest {
299 #[serde(skip_serializing_if = "Option::is_none")]
301 pub thread_id: Option<String>,
302 pub prompt: String,
304 #[serde(skip_serializing_if = "Option::is_none")]
306 pub model: Option<String>,
307}
308
309#[derive(Debug, Clone, Serialize, Deserialize)]
311pub struct PromptResponse {
312 pub output: String,
314 pub model: String,
316 #[serde(default)]
318 pub events: Vec<EventFrame>,
319}
320
321#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
323#[serde(rename_all = "snake_case")]
324pub enum AskForApproval {
325 UnlessTrusted,
327 OnFailure,
329 OnRequest,
331 Reject {
333 sandbox_approval: bool,
334 rules: bool,
335 mcp_elicitations: bool,
336 },
337 Never,
339}
340
341#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
343#[serde(rename_all = "snake_case")]
344pub enum ToolKind {
345 Function,
347 Mcp,
349}
350
351#[derive(Debug, Clone, Serialize, Deserialize)]
353pub struct LocalShellParams {
354 pub command: String,
356 #[serde(skip_serializing_if = "Option::is_none")]
358 pub cwd: Option<String>,
359 #[serde(skip_serializing_if = "Option::is_none")]
361 pub timeout_ms: Option<u64>,
362}
363
364#[derive(Debug, Clone, Serialize, Deserialize)]
366#[serde(tag = "type", rename_all = "snake_case")]
367pub enum ToolPayload {
368 Function { arguments: String },
370 Custom { input: String },
372 LocalShell { params: LocalShellParams },
374 Mcp {
376 server: String,
377 tool: String,
378 raw_arguments: Value,
379 #[serde(skip_serializing_if = "Option::is_none")]
380 raw_tool_call_id: Option<String>,
381 },
382}
383
384#[derive(Debug, Clone, Serialize, Deserialize)]
386#[serde(tag = "type", rename_all = "snake_case")]
387pub enum ToolOutput {
388 Function {
390 #[serde(skip_serializing_if = "Option::is_none")]
392 body: Option<Value>,
393 success: bool,
395 },
396 Mcp {
398 result: Value,
400 },
401}
402
403#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
405#[serde(rename_all = "snake_case")]
406pub enum NetworkPolicyRuleAction {
407 Allow,
409 Deny,
411}
412
413#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
415pub struct NetworkPolicyAmendment {
416 pub host: String,
418 pub action: NetworkPolicyRuleAction,
420}
421
422#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
424#[serde(tag = "type", rename_all = "snake_case")]
425pub enum ReviewDecision {
426 Approved,
428 ApprovedExecpolicyAmendment,
430 ApprovedForSession,
432 NetworkPolicyAmendment {
434 host: String,
435 action: NetworkPolicyRuleAction,
436 },
437 Denied,
439 Abort,
441}
442
443#[derive(Debug, Clone, Serialize, Deserialize)]
445#[serde(rename_all = "snake_case")]
446pub enum McpStartupStatus {
447 Starting,
449 Ready,
451 Failed { error: String },
453 Cancelled,
455}
456
457#[derive(Debug, Clone, Serialize, Deserialize)]
459pub struct McpStartupUpdateEvent {
460 pub server_name: String,
462 pub status: McpStartupStatus,
464}
465
466#[derive(Debug, Clone, Serialize, Deserialize)]
468pub struct McpStartupFailure {
469 pub server_name: String,
471 pub error: String,
473}
474
475#[derive(Debug, Clone, Serialize, Deserialize)]
477pub struct McpStartupCompleteEvent {
478 pub ready: Vec<String>,
480 pub failed: Vec<McpStartupFailure>,
482 pub cancelled: Vec<String>,
484}
485
486#[derive(Debug, Clone, Serialize, Deserialize)]
488pub struct NetworkApprovalContext {
489 pub host: String,
491 pub protocol: String,
493}
494
495#[derive(Debug, Clone, Serialize, Deserialize)]
497pub struct ExecApprovalRequestEvent {
498 pub call_id: String,
500 pub approval_id: String,
502 pub turn_id: String,
504 pub command: String,
506 pub cwd: String,
508 pub reason: String,
510 #[serde(default, skip_serializing_if = "Option::is_none")]
512 pub matched_rule: Option<Box<str>>,
513 #[serde(skip_serializing_if = "Option::is_none")]
515 pub network_approval_context: Option<NetworkApprovalContext>,
516 #[serde(default)]
518 pub proposed_execpolicy_amendment: Vec<String>,
519 #[serde(default)]
521 pub proposed_network_policy_amendments: Vec<NetworkPolicyAmendment>,
522 #[serde(default)]
524 pub additional_permissions: Vec<String>,
525 #[serde(default)]
527 pub available_decisions: Vec<ReviewDecision>,
528}
529
530#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
532#[serde(rename_all = "snake_case")]
533pub enum ResponseChannel {
534 #[default]
536 Text,
537 Reasoning,
539}
540
541impl ResponseChannel {
542 pub const fn is_text(&self) -> bool {
544 matches!(self, ResponseChannel::Text)
545 }
546}
547
548#[derive(Debug, Clone, Serialize, Deserialize)]
550pub struct ApprovalDecisionRequest {
551 pub decision: String,
553 #[serde(default)]
555 pub remember: bool,
556}
557
558#[derive(Debug, Clone, Serialize, Deserialize)]
564#[serde(tag = "event", rename_all = "snake_case")]
565pub enum EventFrame {
566 ResponseStart { response_id: String },
568 ResponseDelta {
570 response_id: String,
571 delta: String,
572 #[serde(default, skip_serializing_if = "ResponseChannel::is_text")]
573 channel: ResponseChannel,
574 },
575 ResponseEnd { response_id: String },
577 ToolCallStart {
579 response_id: String,
580 tool_name: String,
581 arguments: Value,
582 },
583 ToolCallResult {
585 response_id: String,
586 tool_name: String,
587 output: Value,
588 },
589 McpStartupUpdate { update: McpStartupUpdateEvent },
591 McpStartupComplete { summary: McpStartupCompleteEvent },
593 McpToolCallBegin {
595 server_name: String,
596 tool_name: String,
597 },
598 McpToolCallEnd {
600 server_name: String,
601 tool_name: String,
602 ok: bool,
603 },
604 ExecApprovalRequest { request: ExecApprovalRequestEvent },
606 ApplyPatchApprovalRequest { request: ExecApprovalRequestEvent },
608 ElicitationRequest {
610 server_name: String,
611 request_id: String,
612 prompt: String,
613 },
614 ExecCommandBegin { command: String, cwd: String },
616 ExecCommandOutputDelta { command: String, delta: String },
618 ExecCommandEnd { command: String, exit_code: i32 },
620 PatchApplyBegin { path: String },
622 PatchApplyEnd { path: String, ok: bool },
624 TurnStarted { turn_id: String },
626 TurnComplete { turn_id: String },
628 TurnAborted { turn_id: String, reason: String },
630 ThreadGoalUpdated { goal: ThreadGoal },
632 ThreadGoalCleared { thread_id: String },
634 Error {
636 response_id: String,
637 message: String,
638 },
639}