1use std::path::PathBuf;
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6pub mod agent_run;
7pub mod fleet;
8pub mod runtime;
9pub mod workroom;
10
11pub trait Status {
17 fn is_terminal(&self) -> bool;
20
21 fn is_active(&self) -> bool;
24
25 fn is_paused(&self) -> bool;
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct Envelope<T> {
32 pub request_id: String,
33 #[serde(skip_serializing_if = "Option::is_none")]
34 pub thread_id: Option<String>,
35 pub body: T,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
39#[serde(rename_all = "snake_case")]
40pub enum ThreadStatus {
41 Running,
42 Idle,
43 Completed,
44 Failed,
45 Paused,
46 Archived,
47}
48
49impl Status for ThreadStatus {
50 fn is_terminal(&self) -> bool {
51 matches!(self, Self::Completed | Self::Failed | Self::Archived)
52 }
53 fn is_active(&self) -> bool {
54 matches!(self, Self::Running)
55 }
56 fn is_paused(&self) -> bool {
57 matches!(self, Self::Paused)
58 }
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
62#[serde(rename_all = "snake_case")]
63pub enum SessionSource {
64 Interactive,
65 Resume,
66 Fork,
67 Api,
68 Unknown,
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct Thread {
73 pub id: String,
74 pub preview: String,
75 pub ephemeral: bool,
76 pub model_provider: String,
77 pub created_at: i64,
78 pub updated_at: i64,
79 pub status: ThreadStatus,
80 #[serde(skip_serializing_if = "Option::is_none")]
81 pub path: Option<PathBuf>,
82 pub cwd: PathBuf,
83 pub cli_version: String,
84 pub source: SessionSource,
85 #[serde(skip_serializing_if = "Option::is_none")]
86 pub name: Option<String>,
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
90#[serde(rename_all = "snake_case")]
91pub enum ThreadGoalStatus {
92 Active,
93 Paused,
94 Blocked,
95 UsageLimited,
96 BudgetLimited,
97 Complete,
98}
99
100impl Status for ThreadGoalStatus {
101 fn is_terminal(&self) -> bool {
102 matches!(self, Self::Complete)
103 }
104 fn is_active(&self) -> bool {
105 matches!(self, Self::Active)
106 }
107 fn is_paused(&self) -> bool {
108 matches!(self, Self::Paused)
109 }
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
113pub struct ThreadGoal {
114 pub thread_id: String,
115 pub goal_id: String,
116 pub objective: String,
117 pub status: ThreadGoalStatus,
118 #[serde(skip_serializing_if = "Option::is_none")]
119 pub token_budget: Option<i64>,
120 pub tokens_used: i64,
121 pub time_used_seconds: i64,
122 pub continuation_count: i64,
123 pub created_at: i64,
124 pub updated_at: i64,
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct ThreadStartParams {
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(default)]
136 pub persist_extended_history: bool,
137}
138
139#[derive(Debug, Clone, Serialize, Deserialize)]
140pub struct ThreadResumeParams {
141 pub thread_id: String,
142 #[serde(skip_serializing_if = "Option::is_none")]
143 pub history: Option<Vec<Value>>,
144 #[serde(skip_serializing_if = "Option::is_none")]
145 pub path: Option<PathBuf>,
146 #[serde(skip_serializing_if = "Option::is_none")]
147 pub model: Option<String>,
148 #[serde(skip_serializing_if = "Option::is_none")]
149 pub model_provider: Option<String>,
150 #[serde(skip_serializing_if = "Option::is_none")]
151 pub cwd: Option<PathBuf>,
152 #[serde(skip_serializing_if = "Option::is_none")]
153 pub approval_policy: Option<String>,
154 #[serde(skip_serializing_if = "Option::is_none")]
155 pub sandbox: Option<String>,
156 #[serde(skip_serializing_if = "Option::is_none")]
157 pub config: Option<Value>,
158 #[serde(skip_serializing_if = "Option::is_none")]
159 pub base_instructions: Option<String>,
160 #[serde(skip_serializing_if = "Option::is_none")]
161 pub developer_instructions: Option<String>,
162 #[serde(skip_serializing_if = "Option::is_none")]
163 pub personality: Option<String>,
164 #[serde(default)]
165 pub persist_extended_history: bool,
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize)]
169pub struct ThreadForkParams {
170 pub thread_id: String,
171 #[serde(skip_serializing_if = "Option::is_none")]
172 pub path: Option<PathBuf>,
173 #[serde(skip_serializing_if = "Option::is_none")]
174 pub model: Option<String>,
175 #[serde(skip_serializing_if = "Option::is_none")]
176 pub model_provider: Option<String>,
177 #[serde(skip_serializing_if = "Option::is_none")]
178 pub cwd: Option<PathBuf>,
179 #[serde(skip_serializing_if = "Option::is_none")]
180 pub approval_policy: Option<String>,
181 #[serde(skip_serializing_if = "Option::is_none")]
182 pub sandbox: Option<String>,
183 #[serde(skip_serializing_if = "Option::is_none")]
184 pub config: Option<Value>,
185 #[serde(skip_serializing_if = "Option::is_none")]
186 pub base_instructions: Option<String>,
187 #[serde(skip_serializing_if = "Option::is_none")]
188 pub developer_instructions: Option<String>,
189 #[serde(default)]
190 pub persist_extended_history: bool,
191}
192
193#[derive(Debug, Clone, Serialize, Deserialize)]
194pub struct ThreadListParams {
195 #[serde(default)]
196 pub include_archived: bool,
197 #[serde(skip_serializing_if = "Option::is_none")]
198 pub limit: Option<usize>,
199}
200
201#[derive(Debug, Clone, Serialize, Deserialize)]
202pub struct ThreadReadParams {
203 pub thread_id: String,
204}
205
206#[derive(Debug, Clone, Serialize, Deserialize)]
207pub struct ThreadSetNameParams {
208 pub thread_id: String,
209 pub name: String,
210}
211
212#[derive(Debug, Clone, Serialize, Deserialize)]
213pub struct ThreadGoalSetParams {
214 pub thread_id: String,
215 pub objective: String,
216 #[serde(skip_serializing_if = "Option::is_none")]
217 pub token_budget: Option<i64>,
218}
219
220#[derive(Debug, Clone, Serialize, Deserialize)]
221pub struct ThreadGoalGetParams {
222 pub thread_id: String,
223}
224
225#[derive(Debug, Clone, Serialize, Deserialize)]
226pub struct ThreadGoalClearParams {
227 pub thread_id: String,
228}
229
230#[derive(Debug, Clone, Serialize, Deserialize)]
231pub struct ThreadGoalProgressParams {
232 pub thread_id: String,
233 #[serde(default)]
234 pub token_delta: i64,
235 #[serde(default)]
236 pub time_delta_seconds: i64,
237 #[serde(default)]
238 pub record_continuation: bool,
239}
240
241#[derive(Debug, Clone, Serialize, Deserialize)]
242#[serde(tag = "kind", rename_all = "snake_case")]
243pub enum ThreadRequest {
244 Create {
245 #[serde(default)]
246 metadata: Value,
247 },
248 Start(ThreadStartParams),
249 Resume(ThreadResumeParams),
250 Fork(ThreadForkParams),
251 List(ThreadListParams),
252 Read(ThreadReadParams),
253 SetName(ThreadSetNameParams),
254 GoalSet(ThreadGoalSetParams),
255 GoalGet(ThreadGoalGetParams),
256 GoalClear(ThreadGoalClearParams),
257 GoalRecordProgress(ThreadGoalProgressParams),
258 Archive {
259 thread_id: String,
260 },
261 Unarchive {
262 thread_id: String,
263 },
264 Message {
265 thread_id: String,
266 input: String,
267 },
268}
269
270#[derive(Debug, Clone, Serialize, Deserialize)]
272pub struct ThreadResponse {
273 pub thread_id: String,
275 pub status: String,
277 #[serde(skip_serializing_if = "Option::is_none")]
279 pub thread: Option<Thread>,
280 #[serde(default)]
282 pub threads: Vec<Thread>,
283 #[serde(skip_serializing_if = "Option::is_none")]
285 pub goal: Option<ThreadGoal>,
286 #[serde(skip_serializing_if = "Option::is_none")]
288 pub model: Option<String>,
289 #[serde(skip_serializing_if = "Option::is_none")]
291 pub model_provider: Option<String>,
292 #[serde(skip_serializing_if = "Option::is_none")]
294 pub cwd: Option<PathBuf>,
295 #[serde(skip_serializing_if = "Option::is_none")]
297 pub approval_policy: Option<String>,
298 #[serde(skip_serializing_if = "Option::is_none")]
300 pub sandbox: Option<String>,
301 #[serde(default)]
303 pub events: Vec<EventFrame>,
304 #[serde(default)]
306 pub data: Value,
307}
308
309#[derive(Debug, Clone, Serialize, Deserialize)]
311#[serde(tag = "kind", rename_all = "snake_case")]
312pub enum AppRequest {
313 Capabilities,
315 ConfigGet { key: String },
317 ConfigSet { key: String, value: String },
319 ConfigUnset { key: String },
321 ConfigList,
323 ConfigReload,
336 Models,
338 ThreadLoadedList,
340 SubmitUserInput {
345 request_id: String,
346 answers: Vec<UserInputAnswerEvent>,
347 },
348}
349
350#[derive(Debug, Clone, Serialize, Deserialize)]
352pub struct AppResponse {
353 pub ok: bool,
355 pub data: Value,
357 #[serde(default)]
359 pub events: Vec<EventFrame>,
360}
361
362#[derive(Debug, Clone, Serialize, Deserialize)]
364pub struct PromptRequest {
365 #[serde(skip_serializing_if = "Option::is_none")]
367 pub thread_id: Option<String>,
368 pub prompt: String,
370 #[serde(skip_serializing_if = "Option::is_none")]
372 pub model: Option<String>,
373}
374
375#[derive(Debug, Clone, Serialize, Deserialize)]
377pub struct PromptResponse {
378 pub output: String,
380 pub model: String,
382 #[serde(default)]
384 pub events: Vec<EventFrame>,
385}
386
387#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
389#[serde(rename_all = "snake_case")]
390pub enum AskForApproval {
391 UnlessTrusted,
393 OnFailure,
395 OnRequest,
397 Reject {
399 sandbox_approval: bool,
400 rules: bool,
401 mcp_elicitations: bool,
402 },
403 Never,
405}
406
407#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
409#[serde(rename_all = "snake_case")]
410pub enum ToolKind {
411 Function,
413 Mcp,
415}
416
417#[derive(Debug, Clone, Serialize, Deserialize)]
419pub struct LocalShellParams {
420 pub command: String,
422 #[serde(skip_serializing_if = "Option::is_none")]
424 pub cwd: Option<String>,
425 #[serde(skip_serializing_if = "Option::is_none")]
427 pub timeout_ms: Option<u64>,
428}
429
430#[derive(Debug, Clone, Serialize, Deserialize)]
432#[serde(tag = "type", rename_all = "snake_case")]
433pub enum ToolPayload {
434 Function { arguments: String },
436 Custom { input: String },
438 LocalShell { params: LocalShellParams },
440 Mcp {
442 server: String,
443 tool: String,
444 raw_arguments: Value,
445 #[serde(skip_serializing_if = "Option::is_none")]
446 raw_tool_call_id: Option<String>,
447 },
448}
449
450#[derive(Debug, Clone, Serialize, Deserialize)]
452#[serde(tag = "type", rename_all = "snake_case")]
453pub enum ToolOutput {
454 Function {
456 #[serde(skip_serializing_if = "Option::is_none")]
458 body: Option<Value>,
459 success: bool,
461 },
462 Mcp {
464 result: Value,
466 },
467}
468
469impl ToolOutput {
470 pub fn success(&self) -> bool {
475 match self {
476 Self::Function { success, .. } => *success,
477 Self::Mcp { result } => {
478 matches!(result.get("isError"), None | Some(Value::Bool(false)))
479 }
480 }
481 }
482}
483
484#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
486#[serde(rename_all = "snake_case")]
487pub enum NetworkPolicyRuleAction {
488 Allow,
490 Deny,
492}
493
494#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
496pub struct NetworkPolicyAmendment {
497 pub host: String,
499 pub action: NetworkPolicyRuleAction,
501}
502
503#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
505#[serde(tag = "type", rename_all = "snake_case")]
506pub enum ReviewDecision {
507 Approved,
509 ApprovedExecpolicyAmendment,
511 ApprovedForSession,
513 NetworkPolicyAmendment {
515 host: String,
516 action: NetworkPolicyRuleAction,
517 },
518 Denied,
520 Abort,
522}
523
524#[derive(Debug, Clone, Serialize, Deserialize)]
526#[serde(rename_all = "snake_case")]
527pub enum McpStartupStatus {
528 Starting,
530 Ready,
532 Failed { error: String },
534 Cancelled,
536}
537
538#[derive(Debug, Clone, Serialize, Deserialize)]
540pub struct McpStartupUpdateEvent {
541 pub server_name: String,
543 pub status: McpStartupStatus,
545}
546
547#[derive(Debug, Clone, Serialize, Deserialize)]
549pub struct McpStartupFailure {
550 pub server_name: String,
552 pub error: String,
554}
555
556#[derive(Debug, Clone, Serialize, Deserialize)]
558pub struct McpStartupCompleteEvent {
559 pub ready: Vec<String>,
561 pub failed: Vec<McpStartupFailure>,
563 pub cancelled: Vec<String>,
565}
566
567#[derive(Debug, Clone, Serialize, Deserialize)]
569pub struct NetworkApprovalContext {
570 pub host: String,
572 pub protocol: String,
574}
575
576#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
583pub struct UserInputOptionEvent {
584 pub label: String,
586 pub description: String,
588}
589
590#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
592pub struct UserInputQuestionEvent {
593 pub header: String,
595 pub id: String,
597 pub question: String,
599 pub options: Vec<UserInputOptionEvent>,
601 #[serde(default)]
603 pub allow_free_text: bool,
604 #[serde(default)]
606 pub multi_select: bool,
607}
608
609#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
615pub struct UserInputRequestEvent {
616 pub call_id: String,
618 pub turn_id: String,
620 pub request_id: String,
622 pub questions: Vec<UserInputQuestionEvent>,
624}
625
626#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
628pub struct UserInputAnswerEvent {
629 pub id: String,
631 pub label: String,
633 pub value: String,
635}
636
637#[derive(Debug, Clone, Serialize, Deserialize)]
639pub struct ExecApprovalRequestEvent {
640 pub call_id: String,
642 pub approval_id: String,
644 pub turn_id: String,
646 pub command: String,
648 pub cwd: String,
650 pub reason: String,
652 #[serde(default, skip_serializing_if = "Option::is_none")]
654 pub matched_rule: Option<Box<str>>,
655 #[serde(skip_serializing_if = "Option::is_none")]
657 pub network_approval_context: Option<NetworkApprovalContext>,
658 #[serde(default)]
660 pub proposed_execpolicy_amendment: Vec<String>,
661 #[serde(default)]
663 pub proposed_network_policy_amendments: Vec<NetworkPolicyAmendment>,
664 #[serde(default)]
666 pub additional_permissions: Vec<String>,
667 #[serde(default)]
669 pub available_decisions: Vec<ReviewDecision>,
670}
671
672#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
674#[serde(rename_all = "snake_case")]
675pub enum ResponseChannel {
676 #[default]
678 Text,
679 Reasoning,
681}
682
683impl ResponseChannel {
684 pub const fn is_text(&self) -> bool {
686 matches!(self, ResponseChannel::Text)
687 }
688}
689
690#[derive(Debug, Clone, Serialize, Deserialize)]
692pub struct ApprovalDecisionRequest {
693 pub decision: String,
695 #[serde(default)]
697 pub remember: bool,
698}
699
700#[derive(Debug, Clone, Serialize, Deserialize)]
706#[serde(tag = "event", rename_all = "snake_case")]
707pub enum EventFrame {
708 ResponseStart { response_id: String },
710 ResponseDelta {
712 response_id: String,
713 delta: String,
714 #[serde(default, skip_serializing_if = "ResponseChannel::is_text")]
715 channel: ResponseChannel,
716 },
717 ResponseEnd { response_id: String },
719 ToolCallStart {
721 response_id: String,
722 tool_name: String,
723 arguments: Value,
724 },
725 ToolCallResult {
727 response_id: String,
728 tool_name: String,
729 output: Value,
730 },
731 McpStartupUpdate { update: McpStartupUpdateEvent },
733 McpStartupComplete { summary: McpStartupCompleteEvent },
735 McpToolCallBegin {
737 server_name: String,
738 tool_name: String,
739 },
740 McpToolCallEnd {
742 server_name: String,
743 tool_name: String,
744 ok: bool,
745 },
746 ExecApprovalRequest { request: ExecApprovalRequestEvent },
748 ApplyPatchApprovalRequest { request: ExecApprovalRequestEvent },
750 UserInputRequest { request: UserInputRequestEvent },
755 ElicitationRequest {
757 server_name: String,
758 request_id: String,
759 prompt: String,
760 },
761 ExecCommandBegin { command: String, cwd: String },
763 ExecCommandOutputDelta { command: String, delta: String },
765 ExecCommandEnd { command: String, exit_code: i32 },
767 PatchApplyBegin { path: String },
769 PatchApplyEnd { path: String, ok: bool },
771 TurnStarted { turn_id: String },
773 TurnComplete { turn_id: String },
775 TurnAborted { turn_id: String, reason: String },
777 ThreadGoalUpdated { goal: ThreadGoal },
779 ThreadGoalCleared { thread_id: String },
781 Error {
783 response_id: String,
784 message: String,
785 },
786}