1use std::path::PathBuf;
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6pub mod agent_mail;
7pub mod agent_run;
8pub mod event_msg;
9pub mod fleet;
10pub mod ids;
11pub mod journal;
12pub mod op;
13pub mod runtime;
14pub mod workroom;
15
16pub trait Status {
22 fn is_terminal(&self) -> bool;
25
26 fn is_active(&self) -> bool;
29
30 fn is_paused(&self) -> bool;
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct Envelope<T> {
37 pub request_id: String,
38 #[serde(skip_serializing_if = "Option::is_none")]
39 pub thread_id: Option<String>,
40 pub body: T,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
44#[serde(rename_all = "snake_case")]
45pub enum ThreadStatus {
46 Running,
47 Idle,
48 Completed,
49 Failed,
50 Paused,
51 Archived,
52}
53
54impl Status for ThreadStatus {
55 fn is_terminal(&self) -> bool {
56 matches!(self, Self::Completed | Self::Failed | Self::Archived)
57 }
58 fn is_active(&self) -> bool {
59 matches!(self, Self::Running)
60 }
61 fn is_paused(&self) -> bool {
62 matches!(self, Self::Paused)
63 }
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
67#[serde(rename_all = "snake_case")]
68pub enum SessionSource {
69 Interactive,
70 Resume,
71 Fork,
72 Api,
73 Unknown,
74}
75
76#[derive(Debug, Clone, Serialize, Deserialize)]
77pub struct Thread {
78 pub id: String,
79 pub preview: String,
80 pub ephemeral: bool,
81 pub model_provider: String,
82 pub created_at: i64,
83 pub updated_at: i64,
84 pub status: ThreadStatus,
85 #[serde(skip_serializing_if = "Option::is_none")]
86 pub path: Option<PathBuf>,
87 pub cwd: PathBuf,
88 pub cli_version: String,
89 pub source: SessionSource,
90 #[serde(skip_serializing_if = "Option::is_none")]
91 pub name: Option<String>,
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
95#[serde(rename_all = "snake_case")]
96pub enum ThreadGoalStatus {
97 Active,
98 Paused,
99 Blocked,
100 UsageLimited,
101 BudgetLimited,
102 Complete,
103}
104
105impl Status for ThreadGoalStatus {
106 fn is_terminal(&self) -> bool {
107 matches!(self, Self::Complete)
108 }
109 fn is_active(&self) -> bool {
110 matches!(self, Self::Active)
111 }
112 fn is_paused(&self) -> bool {
113 matches!(self, Self::Paused)
114 }
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
118pub struct ThreadGoal {
119 pub thread_id: String,
120 pub goal_id: String,
121 pub objective: String,
122 pub status: ThreadGoalStatus,
123 #[serde(skip_serializing_if = "Option::is_none")]
124 pub token_budget: Option<i64>,
125 pub tokens_used: i64,
126 pub time_used_seconds: i64,
127 pub continuation_count: i64,
128 pub created_at: i64,
129 pub updated_at: i64,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct ThreadStartParams {
134 #[serde(skip_serializing_if = "Option::is_none")]
135 pub model: Option<String>,
136 #[serde(skip_serializing_if = "Option::is_none")]
137 pub model_provider: Option<String>,
138 #[serde(skip_serializing_if = "Option::is_none")]
139 pub cwd: Option<PathBuf>,
140 #[serde(default)]
141 pub persist_extended_history: bool,
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
145pub struct ThreadResumeParams {
146 pub thread_id: String,
147 #[serde(skip_serializing_if = "Option::is_none")]
148 pub history: Option<Vec<Value>>,
149 #[serde(skip_serializing_if = "Option::is_none")]
150 pub path: Option<PathBuf>,
151 #[serde(skip_serializing_if = "Option::is_none")]
152 pub model: Option<String>,
153 #[serde(skip_serializing_if = "Option::is_none")]
154 pub model_provider: Option<String>,
155 #[serde(skip_serializing_if = "Option::is_none")]
156 pub cwd: Option<PathBuf>,
157 #[serde(skip_serializing_if = "Option::is_none")]
158 pub approval_policy: Option<String>,
159 #[serde(skip_serializing_if = "Option::is_none")]
160 pub sandbox: Option<String>,
161 #[serde(skip_serializing_if = "Option::is_none")]
162 pub config: Option<Value>,
163 #[serde(skip_serializing_if = "Option::is_none")]
164 pub base_instructions: Option<String>,
165 #[serde(skip_serializing_if = "Option::is_none")]
166 pub developer_instructions: Option<String>,
167 #[serde(skip_serializing_if = "Option::is_none")]
168 pub personality: Option<String>,
169 #[serde(default)]
170 pub persist_extended_history: bool,
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct ThreadForkParams {
175 pub thread_id: String,
176 #[serde(skip_serializing_if = "Option::is_none")]
177 pub path: Option<PathBuf>,
178 #[serde(skip_serializing_if = "Option::is_none")]
179 pub model: Option<String>,
180 #[serde(skip_serializing_if = "Option::is_none")]
181 pub model_provider: Option<String>,
182 #[serde(skip_serializing_if = "Option::is_none")]
183 pub cwd: Option<PathBuf>,
184 #[serde(skip_serializing_if = "Option::is_none")]
185 pub approval_policy: Option<String>,
186 #[serde(skip_serializing_if = "Option::is_none")]
187 pub sandbox: Option<String>,
188 #[serde(skip_serializing_if = "Option::is_none")]
189 pub config: Option<Value>,
190 #[serde(skip_serializing_if = "Option::is_none")]
191 pub base_instructions: Option<String>,
192 #[serde(skip_serializing_if = "Option::is_none")]
193 pub developer_instructions: Option<String>,
194 #[serde(default)]
195 pub persist_extended_history: bool,
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize)]
199pub struct ThreadListParams {
200 #[serde(default)]
201 pub include_archived: bool,
202 #[serde(skip_serializing_if = "Option::is_none")]
203 pub limit: Option<usize>,
204}
205
206#[derive(Debug, Clone, Serialize, Deserialize)]
207pub struct ThreadReadParams {
208 pub thread_id: String,
209}
210
211#[derive(Debug, Clone, Serialize, Deserialize)]
212pub struct ThreadSetNameParams {
213 pub thread_id: String,
214 pub name: String,
215}
216
217#[derive(Debug, Clone, Serialize, Deserialize)]
218pub struct ThreadGoalSetParams {
219 pub thread_id: String,
220 pub objective: String,
221 #[serde(skip_serializing_if = "Option::is_none")]
222 pub token_budget: Option<i64>,
223}
224
225#[derive(Debug, Clone, Serialize, Deserialize)]
226pub struct ThreadGoalGetParams {
227 pub thread_id: String,
228}
229
230#[derive(Debug, Clone, Serialize, Deserialize)]
231pub struct ThreadGoalClearParams {
232 pub thread_id: String,
233}
234
235#[derive(Debug, Clone, Serialize, Deserialize)]
236pub struct ThreadGoalProgressParams {
237 pub thread_id: String,
238 #[serde(default)]
239 pub token_delta: i64,
240 #[serde(default)]
241 pub time_delta_seconds: i64,
242 #[serde(default)]
243 pub record_continuation: bool,
244}
245
246#[derive(Debug, Clone, Serialize, Deserialize)]
247#[serde(tag = "kind", rename_all = "snake_case")]
248pub enum ThreadRequest {
249 Create {
250 #[serde(default)]
251 metadata: Value,
252 },
253 Start(ThreadStartParams),
254 Resume(ThreadResumeParams),
255 Fork(ThreadForkParams),
256 List(ThreadListParams),
257 Read(ThreadReadParams),
258 SetName(ThreadSetNameParams),
259 GoalSet(ThreadGoalSetParams),
260 GoalGet(ThreadGoalGetParams),
261 GoalClear(ThreadGoalClearParams),
262 GoalRecordProgress(ThreadGoalProgressParams),
263 Archive {
264 thread_id: String,
265 },
266 Unarchive {
267 thread_id: String,
268 },
269 Message {
270 thread_id: String,
271 input: String,
272 },
273}
274
275#[derive(Debug, Clone, Serialize, Deserialize)]
277pub struct ThreadResponse {
278 pub thread_id: String,
280 pub status: String,
282 #[serde(skip_serializing_if = "Option::is_none")]
284 pub thread: Option<Thread>,
285 #[serde(default)]
287 pub threads: Vec<Thread>,
288 #[serde(skip_serializing_if = "Option::is_none")]
290 pub goal: Option<ThreadGoal>,
291 #[serde(skip_serializing_if = "Option::is_none")]
293 pub model: Option<String>,
294 #[serde(skip_serializing_if = "Option::is_none")]
296 pub model_provider: Option<String>,
297 #[serde(skip_serializing_if = "Option::is_none")]
299 pub cwd: Option<PathBuf>,
300 #[serde(skip_serializing_if = "Option::is_none")]
302 pub approval_policy: Option<String>,
303 #[serde(skip_serializing_if = "Option::is_none")]
305 pub sandbox: Option<String>,
306 #[serde(default)]
308 pub events: Vec<EventFrame>,
309 #[serde(default)]
311 pub data: Value,
312}
313
314#[derive(Debug, Clone, Serialize, Deserialize)]
316#[serde(tag = "kind", rename_all = "snake_case")]
317pub enum AppRequest {
318 Capabilities,
320 ConfigGet { key: String },
322 ConfigSet { key: String, value: String },
324 ConfigUnset { key: String },
326 ConfigList,
328 ConfigReload,
341 Models,
343 ThreadLoadedList,
345 SubmitUserInput {
350 request_id: String,
351 answers: Vec<UserInputAnswerEvent>,
352 },
353}
354
355#[derive(Debug, Clone, Serialize, Deserialize)]
357pub struct AppResponse {
358 pub ok: bool,
360 pub data: Value,
362 #[serde(default)]
364 pub events: Vec<EventFrame>,
365}
366
367#[derive(Debug, Clone, Serialize, Deserialize)]
369pub struct PromptRequest {
370 #[serde(skip_serializing_if = "Option::is_none")]
372 pub thread_id: Option<String>,
373 pub prompt: String,
375 #[serde(skip_serializing_if = "Option::is_none")]
377 pub model: Option<String>,
378}
379
380#[derive(Debug, Clone, Serialize, Deserialize)]
382pub struct PromptResponse {
383 pub output: String,
385 pub model: String,
387 #[serde(default)]
389 pub events: Vec<EventFrame>,
390}
391
392#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
394#[serde(rename_all = "snake_case")]
395pub enum AskForApproval {
396 UnlessTrusted,
398 OnFailure,
400 OnRequest,
402 Reject {
404 sandbox_approval: bool,
405 rules: bool,
406 mcp_elicitations: bool,
407 },
408 Never,
410}
411
412#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
414#[serde(rename_all = "snake_case")]
415pub enum ToolKind {
416 Function,
418 Mcp,
420}
421
422#[derive(Debug, Clone, Serialize, Deserialize)]
424pub struct LocalShellParams {
425 pub command: String,
427 #[serde(skip_serializing_if = "Option::is_none")]
429 pub cwd: Option<String>,
430 #[serde(skip_serializing_if = "Option::is_none")]
432 pub timeout_ms: Option<u64>,
433}
434
435#[derive(Debug, Clone, Serialize, Deserialize)]
437#[serde(tag = "type", rename_all = "snake_case")]
438pub enum ToolPayload {
439 Function { arguments: String },
441 Custom { input: String },
443 LocalShell { params: LocalShellParams },
445 Mcp {
447 server: String,
448 tool: String,
449 raw_arguments: Value,
450 #[serde(skip_serializing_if = "Option::is_none")]
451 raw_tool_call_id: Option<String>,
452 },
453}
454
455#[derive(Debug, Clone, Serialize, Deserialize)]
457#[serde(tag = "type", rename_all = "snake_case")]
458pub enum ToolOutput {
459 Function {
461 #[serde(skip_serializing_if = "Option::is_none")]
463 body: Option<Value>,
464 success: bool,
466 },
467 Mcp {
469 result: Value,
471 },
472}
473
474impl ToolOutput {
475 pub fn success(&self) -> bool {
480 match self {
481 Self::Function { success, .. } => *success,
482 Self::Mcp { result } => {
483 matches!(result.get("isError"), None | Some(Value::Bool(false)))
484 }
485 }
486 }
487}
488
489#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
491#[serde(rename_all = "snake_case")]
492pub enum NetworkPolicyRuleAction {
493 Allow,
495 Deny,
497}
498
499#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
501pub struct NetworkPolicyAmendment {
502 pub host: String,
504 pub action: NetworkPolicyRuleAction,
506}
507
508#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
510#[serde(tag = "type", rename_all = "snake_case")]
511pub enum ReviewDecision {
512 Approved,
514 ApprovedExecpolicyAmendment,
516 ApprovedForSession,
518 NetworkPolicyAmendment {
520 host: String,
521 action: NetworkPolicyRuleAction,
522 },
523 Denied,
525 Abort,
527}
528
529#[derive(Debug, Clone, Serialize, Deserialize)]
531#[serde(rename_all = "snake_case")]
532pub enum McpStartupStatus {
533 Starting,
535 Ready,
537 Failed { error: String },
539 Cancelled,
541}
542
543#[derive(Debug, Clone, Serialize, Deserialize)]
545pub struct McpStartupUpdateEvent {
546 pub server_name: String,
548 pub status: McpStartupStatus,
550}
551
552#[derive(Debug, Clone, Serialize, Deserialize)]
554pub struct McpStartupFailure {
555 pub server_name: String,
557 pub error: String,
559}
560
561#[derive(Debug, Clone, Serialize, Deserialize)]
563pub struct McpStartupCompleteEvent {
564 pub ready: Vec<String>,
566 pub failed: Vec<McpStartupFailure>,
568 pub cancelled: Vec<String>,
570}
571
572#[derive(Debug, Clone, Serialize, Deserialize)]
574pub struct NetworkApprovalContext {
575 pub host: String,
577 pub protocol: String,
579}
580
581#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
588pub struct UserInputOptionEvent {
589 pub label: String,
591 pub description: String,
593}
594
595#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
597pub struct UserInputQuestionEvent {
598 pub header: String,
600 pub id: String,
602 pub question: String,
604 pub options: Vec<UserInputOptionEvent>,
606 #[serde(default)]
608 pub allow_free_text: bool,
609 #[serde(default)]
611 pub multi_select: bool,
612}
613
614#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
620pub struct UserInputRequestEvent {
621 pub call_id: String,
623 pub turn_id: String,
625 pub request_id: String,
627 pub questions: Vec<UserInputQuestionEvent>,
629}
630
631#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
633pub struct UserInputAnswerEvent {
634 pub id: String,
636 pub label: String,
638 pub value: String,
640}
641
642#[derive(Debug, Clone, Serialize, Deserialize)]
644pub struct ExecApprovalRequestEvent {
645 pub call_id: String,
647 pub approval_id: String,
649 pub turn_id: String,
651 pub command: String,
653 pub cwd: String,
655 pub reason: String,
657 #[serde(default, skip_serializing_if = "Option::is_none")]
659 pub matched_rule: Option<Box<str>>,
660 #[serde(skip_serializing_if = "Option::is_none")]
662 pub network_approval_context: Option<NetworkApprovalContext>,
663 #[serde(default)]
665 pub proposed_execpolicy_amendment: Vec<String>,
666 #[serde(default)]
668 pub proposed_network_policy_amendments: Vec<NetworkPolicyAmendment>,
669 #[serde(default)]
671 pub additional_permissions: Vec<String>,
672 #[serde(default)]
674 pub available_decisions: Vec<ReviewDecision>,
675}
676
677#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
679#[serde(rename_all = "snake_case")]
680pub enum ResponseChannel {
681 #[default]
683 Text,
684 Reasoning,
686}
687
688impl ResponseChannel {
689 pub const fn is_text(&self) -> bool {
691 matches!(self, ResponseChannel::Text)
692 }
693}
694
695#[derive(Debug, Clone, Serialize, Deserialize)]
697pub struct ApprovalDecisionRequest {
698 pub decision: String,
700 #[serde(default)]
702 pub remember: bool,
703}
704
705#[derive(Debug, Clone, Serialize, Deserialize)]
711#[serde(tag = "event", rename_all = "snake_case")]
712pub enum EventFrame {
713 ResponseStart { response_id: String },
715 ResponseDelta {
717 response_id: String,
718 delta: String,
719 #[serde(default, skip_serializing_if = "ResponseChannel::is_text")]
720 channel: ResponseChannel,
721 },
722 ResponseEnd { response_id: String },
724 ToolCallStart {
726 response_id: String,
727 tool_name: String,
728 arguments: Value,
729 },
730 ToolCallResult {
732 response_id: String,
733 tool_name: String,
734 output: Value,
735 },
736 McpStartupUpdate { update: McpStartupUpdateEvent },
738 McpStartupComplete { summary: McpStartupCompleteEvent },
740 McpToolCallBegin {
742 server_name: String,
743 tool_name: String,
744 },
745 McpToolCallEnd {
747 server_name: String,
748 tool_name: String,
749 ok: bool,
750 },
751 ExecApprovalRequest { request: ExecApprovalRequestEvent },
753 ApplyPatchApprovalRequest { request: ExecApprovalRequestEvent },
755 UserInputRequest { request: UserInputRequestEvent },
760 ElicitationRequest {
762 server_name: String,
763 request_id: String,
764 prompt: String,
765 },
766 ExecCommandBegin { command: String, cwd: String },
768 ExecCommandOutputDelta { command: String, delta: String },
770 ExecCommandEnd { command: String, exit_code: i32 },
772 PatchApplyBegin { path: String },
774 PatchApplyEnd { path: String, ok: bool },
776 TurnStarted { turn_id: String },
778 TurnComplete { turn_id: String },
780 TurnAborted { turn_id: String, reason: String },
782 ThreadGoalUpdated { goal: ThreadGoal },
784 ThreadGoalCleared { thread_id: String },
786 Error {
788 response_id: String,
789 message: String,
790 },
791}