1use std::path::PathBuf;
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6pub mod fleet;
7pub mod runtime;
8pub mod workroom;
9
10pub trait Status {
16 fn is_terminal(&self) -> bool;
19
20 fn is_active(&self) -> bool;
23
24 fn is_paused(&self) -> bool;
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct Envelope<T> {
31 pub request_id: String,
32 #[serde(skip_serializing_if = "Option::is_none")]
33 pub thread_id: Option<String>,
34 pub body: T,
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
38#[serde(rename_all = "snake_case")]
39pub enum ThreadStatus {
40 Running,
41 Idle,
42 Completed,
43 Failed,
44 Paused,
45 Archived,
46}
47
48impl Status for ThreadStatus {
49 fn is_terminal(&self) -> bool {
50 matches!(self, Self::Completed | Self::Failed | Self::Archived)
51 }
52 fn is_active(&self) -> bool {
53 matches!(self, Self::Running)
54 }
55 fn is_paused(&self) -> bool {
56 matches!(self, Self::Paused)
57 }
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
61#[serde(rename_all = "snake_case")]
62pub enum SessionSource {
63 Interactive,
64 Resume,
65 Fork,
66 Api,
67 Unknown,
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct Thread {
72 pub id: String,
73 pub preview: String,
74 pub ephemeral: bool,
75 pub model_provider: String,
76 pub created_at: i64,
77 pub updated_at: i64,
78 pub status: ThreadStatus,
79 #[serde(skip_serializing_if = "Option::is_none")]
80 pub path: Option<PathBuf>,
81 pub cwd: PathBuf,
82 pub cli_version: String,
83 pub source: SessionSource,
84 #[serde(skip_serializing_if = "Option::is_none")]
85 pub name: Option<String>,
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
89#[serde(rename_all = "snake_case")]
90pub enum ThreadGoalStatus {
91 Active,
92 Paused,
93 Blocked,
94 UsageLimited,
95 BudgetLimited,
96 Complete,
97}
98
99impl Status for ThreadGoalStatus {
100 fn is_terminal(&self) -> bool {
101 matches!(self, Self::Complete)
102 }
103 fn is_active(&self) -> bool {
104 matches!(self, Self::Active)
105 }
106 fn is_paused(&self) -> bool {
107 matches!(self, Self::Paused)
108 }
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
112pub struct ThreadGoal {
113 pub thread_id: String,
114 pub goal_id: String,
115 pub objective: String,
116 pub status: ThreadGoalStatus,
117 #[serde(skip_serializing_if = "Option::is_none")]
118 pub token_budget: Option<i64>,
119 pub tokens_used: i64,
120 pub time_used_seconds: i64,
121 pub continuation_count: i64,
122 pub created_at: i64,
123 pub updated_at: i64,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct ThreadStartParams {
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(default)]
135 pub persist_extended_history: bool,
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize)]
139pub struct ThreadResumeParams {
140 pub thread_id: String,
141 #[serde(skip_serializing_if = "Option::is_none")]
142 pub history: Option<Vec<Value>>,
143 #[serde(skip_serializing_if = "Option::is_none")]
144 pub path: Option<PathBuf>,
145 #[serde(skip_serializing_if = "Option::is_none")]
146 pub model: Option<String>,
147 #[serde(skip_serializing_if = "Option::is_none")]
148 pub model_provider: Option<String>,
149 #[serde(skip_serializing_if = "Option::is_none")]
150 pub cwd: Option<PathBuf>,
151 #[serde(skip_serializing_if = "Option::is_none")]
152 pub approval_policy: Option<String>,
153 #[serde(skip_serializing_if = "Option::is_none")]
154 pub sandbox: Option<String>,
155 #[serde(skip_serializing_if = "Option::is_none")]
156 pub config: Option<Value>,
157 #[serde(skip_serializing_if = "Option::is_none")]
158 pub base_instructions: Option<String>,
159 #[serde(skip_serializing_if = "Option::is_none")]
160 pub developer_instructions: Option<String>,
161 #[serde(skip_serializing_if = "Option::is_none")]
162 pub personality: Option<String>,
163 #[serde(default)]
164 pub persist_extended_history: bool,
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct ThreadForkParams {
169 pub thread_id: String,
170 #[serde(skip_serializing_if = "Option::is_none")]
171 pub path: Option<PathBuf>,
172 #[serde(skip_serializing_if = "Option::is_none")]
173 pub model: Option<String>,
174 #[serde(skip_serializing_if = "Option::is_none")]
175 pub model_provider: Option<String>,
176 #[serde(skip_serializing_if = "Option::is_none")]
177 pub cwd: Option<PathBuf>,
178 #[serde(skip_serializing_if = "Option::is_none")]
179 pub approval_policy: Option<String>,
180 #[serde(skip_serializing_if = "Option::is_none")]
181 pub sandbox: Option<String>,
182 #[serde(skip_serializing_if = "Option::is_none")]
183 pub config: Option<Value>,
184 #[serde(skip_serializing_if = "Option::is_none")]
185 pub base_instructions: Option<String>,
186 #[serde(skip_serializing_if = "Option::is_none")]
187 pub developer_instructions: Option<String>,
188 #[serde(default)]
189 pub persist_extended_history: bool,
190}
191
192#[derive(Debug, Clone, Serialize, Deserialize)]
193pub struct ThreadListParams {
194 #[serde(default)]
195 pub include_archived: bool,
196 #[serde(skip_serializing_if = "Option::is_none")]
197 pub limit: Option<usize>,
198}
199
200#[derive(Debug, Clone, Serialize, Deserialize)]
201pub struct ThreadReadParams {
202 pub thread_id: String,
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize)]
206pub struct ThreadSetNameParams {
207 pub thread_id: String,
208 pub name: String,
209}
210
211#[derive(Debug, Clone, Serialize, Deserialize)]
212pub struct ThreadGoalSetParams {
213 pub thread_id: String,
214 pub objective: String,
215 #[serde(skip_serializing_if = "Option::is_none")]
216 pub token_budget: Option<i64>,
217}
218
219#[derive(Debug, Clone, Serialize, Deserialize)]
220pub struct ThreadGoalGetParams {
221 pub thread_id: String,
222}
223
224#[derive(Debug, Clone, Serialize, Deserialize)]
225pub struct ThreadGoalClearParams {
226 pub thread_id: String,
227}
228
229#[derive(Debug, Clone, Serialize, Deserialize)]
230pub struct ThreadGoalProgressParams {
231 pub thread_id: String,
232 #[serde(default)]
233 pub token_delta: i64,
234 #[serde(default)]
235 pub time_delta_seconds: i64,
236 #[serde(default)]
237 pub record_continuation: bool,
238}
239
240#[derive(Debug, Clone, Serialize, Deserialize)]
241#[serde(tag = "kind", rename_all = "snake_case")]
242pub enum ThreadRequest {
243 Create {
244 #[serde(default)]
245 metadata: Value,
246 },
247 Start(ThreadStartParams),
248 Resume(ThreadResumeParams),
249 Fork(ThreadForkParams),
250 List(ThreadListParams),
251 Read(ThreadReadParams),
252 SetName(ThreadSetNameParams),
253 GoalSet(ThreadGoalSetParams),
254 GoalGet(ThreadGoalGetParams),
255 GoalClear(ThreadGoalClearParams),
256 GoalRecordProgress(ThreadGoalProgressParams),
257 Archive {
258 thread_id: String,
259 },
260 Unarchive {
261 thread_id: String,
262 },
263 Message {
264 thread_id: String,
265 input: String,
266 },
267}
268
269#[derive(Debug, Clone, Serialize, Deserialize)]
271pub struct ThreadResponse {
272 pub thread_id: String,
274 pub status: String,
276 #[serde(skip_serializing_if = "Option::is_none")]
278 pub thread: Option<Thread>,
279 #[serde(default)]
281 pub threads: Vec<Thread>,
282 #[serde(skip_serializing_if = "Option::is_none")]
284 pub goal: Option<ThreadGoal>,
285 #[serde(skip_serializing_if = "Option::is_none")]
287 pub model: Option<String>,
288 #[serde(skip_serializing_if = "Option::is_none")]
290 pub model_provider: Option<String>,
291 #[serde(skip_serializing_if = "Option::is_none")]
293 pub cwd: Option<PathBuf>,
294 #[serde(skip_serializing_if = "Option::is_none")]
296 pub approval_policy: Option<String>,
297 #[serde(skip_serializing_if = "Option::is_none")]
299 pub sandbox: Option<String>,
300 #[serde(default)]
302 pub events: Vec<EventFrame>,
303 #[serde(default)]
305 pub data: Value,
306}
307
308#[derive(Debug, Clone, Serialize, Deserialize)]
310#[serde(tag = "kind", rename_all = "snake_case")]
311pub enum AppRequest {
312 Capabilities,
314 ConfigGet { key: String },
316 ConfigSet { key: String, value: String },
318 ConfigUnset { key: String },
320 ConfigList,
322 ConfigReload,
335 Models,
337 ThreadLoadedList,
339 SubmitUserInput {
344 request_id: String,
345 answers: Vec<UserInputAnswerEvent>,
346 },
347}
348
349#[derive(Debug, Clone, Serialize, Deserialize)]
351pub struct AppResponse {
352 pub ok: bool,
354 pub data: Value,
356 #[serde(default)]
358 pub events: Vec<EventFrame>,
359}
360
361#[derive(Debug, Clone, Serialize, Deserialize)]
363pub struct PromptRequest {
364 #[serde(skip_serializing_if = "Option::is_none")]
366 pub thread_id: Option<String>,
367 pub prompt: String,
369 #[serde(skip_serializing_if = "Option::is_none")]
371 pub model: Option<String>,
372}
373
374#[derive(Debug, Clone, Serialize, Deserialize)]
376pub struct PromptResponse {
377 pub output: String,
379 pub model: String,
381 #[serde(default)]
383 pub events: Vec<EventFrame>,
384}
385
386#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
388#[serde(rename_all = "snake_case")]
389pub enum AskForApproval {
390 UnlessTrusted,
392 OnFailure,
394 OnRequest,
396 Reject {
398 sandbox_approval: bool,
399 rules: bool,
400 mcp_elicitations: bool,
401 },
402 Never,
404}
405
406#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
408#[serde(rename_all = "snake_case")]
409pub enum ToolKind {
410 Function,
412 Mcp,
414}
415
416#[derive(Debug, Clone, Serialize, Deserialize)]
418pub struct LocalShellParams {
419 pub command: String,
421 #[serde(skip_serializing_if = "Option::is_none")]
423 pub cwd: Option<String>,
424 #[serde(skip_serializing_if = "Option::is_none")]
426 pub timeout_ms: Option<u64>,
427}
428
429#[derive(Debug, Clone, Serialize, Deserialize)]
431#[serde(tag = "type", rename_all = "snake_case")]
432pub enum ToolPayload {
433 Function { arguments: String },
435 Custom { input: String },
437 LocalShell { params: LocalShellParams },
439 Mcp {
441 server: String,
442 tool: String,
443 raw_arguments: Value,
444 #[serde(skip_serializing_if = "Option::is_none")]
445 raw_tool_call_id: Option<String>,
446 },
447}
448
449#[derive(Debug, Clone, Serialize, Deserialize)]
451#[serde(tag = "type", rename_all = "snake_case")]
452pub enum ToolOutput {
453 Function {
455 #[serde(skip_serializing_if = "Option::is_none")]
457 body: Option<Value>,
458 success: bool,
460 },
461 Mcp {
463 result: Value,
465 },
466}
467
468#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
470#[serde(rename_all = "snake_case")]
471pub enum NetworkPolicyRuleAction {
472 Allow,
474 Deny,
476}
477
478#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
480pub struct NetworkPolicyAmendment {
481 pub host: String,
483 pub action: NetworkPolicyRuleAction,
485}
486
487#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
489#[serde(tag = "type", rename_all = "snake_case")]
490pub enum ReviewDecision {
491 Approved,
493 ApprovedExecpolicyAmendment,
495 ApprovedForSession,
497 NetworkPolicyAmendment {
499 host: String,
500 action: NetworkPolicyRuleAction,
501 },
502 Denied,
504 Abort,
506}
507
508#[derive(Debug, Clone, Serialize, Deserialize)]
510#[serde(rename_all = "snake_case")]
511pub enum McpStartupStatus {
512 Starting,
514 Ready,
516 Failed { error: String },
518 Cancelled,
520}
521
522#[derive(Debug, Clone, Serialize, Deserialize)]
524pub struct McpStartupUpdateEvent {
525 pub server_name: String,
527 pub status: McpStartupStatus,
529}
530
531#[derive(Debug, Clone, Serialize, Deserialize)]
533pub struct McpStartupFailure {
534 pub server_name: String,
536 pub error: String,
538}
539
540#[derive(Debug, Clone, Serialize, Deserialize)]
542pub struct McpStartupCompleteEvent {
543 pub ready: Vec<String>,
545 pub failed: Vec<McpStartupFailure>,
547 pub cancelled: Vec<String>,
549}
550
551#[derive(Debug, Clone, Serialize, Deserialize)]
553pub struct NetworkApprovalContext {
554 pub host: String,
556 pub protocol: String,
558}
559
560#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
567pub struct UserInputOptionEvent {
568 pub label: String,
570 pub description: String,
572}
573
574#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
576pub struct UserInputQuestionEvent {
577 pub header: String,
579 pub id: String,
581 pub question: String,
583 pub options: Vec<UserInputOptionEvent>,
585 #[serde(default)]
587 pub allow_free_text: bool,
588 #[serde(default)]
590 pub multi_select: bool,
591}
592
593#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
599pub struct UserInputRequestEvent {
600 pub call_id: String,
602 pub turn_id: String,
604 pub request_id: String,
606 pub questions: Vec<UserInputQuestionEvent>,
608}
609
610#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
612pub struct UserInputAnswerEvent {
613 pub id: String,
615 pub label: String,
617 pub value: String,
619}
620
621#[derive(Debug, Clone, Serialize, Deserialize)]
623pub struct ExecApprovalRequestEvent {
624 pub call_id: String,
626 pub approval_id: String,
628 pub turn_id: String,
630 pub command: String,
632 pub cwd: String,
634 pub reason: String,
636 #[serde(default, skip_serializing_if = "Option::is_none")]
638 pub matched_rule: Option<Box<str>>,
639 #[serde(skip_serializing_if = "Option::is_none")]
641 pub network_approval_context: Option<NetworkApprovalContext>,
642 #[serde(default)]
644 pub proposed_execpolicy_amendment: Vec<String>,
645 #[serde(default)]
647 pub proposed_network_policy_amendments: Vec<NetworkPolicyAmendment>,
648 #[serde(default)]
650 pub additional_permissions: Vec<String>,
651 #[serde(default)]
653 pub available_decisions: Vec<ReviewDecision>,
654}
655
656#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
658#[serde(rename_all = "snake_case")]
659pub enum ResponseChannel {
660 #[default]
662 Text,
663 Reasoning,
665}
666
667impl ResponseChannel {
668 pub const fn is_text(&self) -> bool {
670 matches!(self, ResponseChannel::Text)
671 }
672}
673
674#[derive(Debug, Clone, Serialize, Deserialize)]
676pub struct ApprovalDecisionRequest {
677 pub decision: String,
679 #[serde(default)]
681 pub remember: bool,
682}
683
684#[derive(Debug, Clone, Serialize, Deserialize)]
690#[serde(tag = "event", rename_all = "snake_case")]
691pub enum EventFrame {
692 ResponseStart { response_id: String },
694 ResponseDelta {
696 response_id: String,
697 delta: String,
698 #[serde(default, skip_serializing_if = "ResponseChannel::is_text")]
699 channel: ResponseChannel,
700 },
701 ResponseEnd { response_id: String },
703 ToolCallStart {
705 response_id: String,
706 tool_name: String,
707 arguments: Value,
708 },
709 ToolCallResult {
711 response_id: String,
712 tool_name: String,
713 output: Value,
714 },
715 McpStartupUpdate { update: McpStartupUpdateEvent },
717 McpStartupComplete { summary: McpStartupCompleteEvent },
719 McpToolCallBegin {
721 server_name: String,
722 tool_name: String,
723 },
724 McpToolCallEnd {
726 server_name: String,
727 tool_name: String,
728 ok: bool,
729 },
730 ExecApprovalRequest { request: ExecApprovalRequestEvent },
732 ApplyPatchApprovalRequest { request: ExecApprovalRequestEvent },
734 UserInputRequest { request: UserInputRequestEvent },
739 ElicitationRequest {
741 server_name: String,
742 request_id: String,
743 prompt: String,
744 },
745 ExecCommandBegin { command: String, cwd: String },
747 ExecCommandOutputDelta { command: String, delta: String },
749 ExecCommandEnd { command: String, exit_code: i32 },
751 PatchApplyBegin { path: String },
753 PatchApplyEnd { path: String, ok: bool },
755 TurnStarted { turn_id: String },
757 TurnComplete { turn_id: String },
759 TurnAborted { turn_id: String, reason: String },
761 ThreadGoalUpdated { goal: ThreadGoal },
763 ThreadGoalCleared { thread_id: String },
765 Error {
767 response_id: String,
768 message: String,
769 },
770}