1use std::path::PathBuf;
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6pub mod agent_run;
7pub mod event_msg;
8pub mod fleet;
9pub mod ids;
10pub mod journal;
11pub mod op;
12pub mod runtime;
13pub mod workroom;
14
15pub trait Status {
21 fn is_terminal(&self) -> bool;
24
25 fn is_active(&self) -> bool;
28
29 fn is_paused(&self) -> bool;
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct Envelope<T> {
36 pub request_id: String,
37 #[serde(skip_serializing_if = "Option::is_none")]
38 pub thread_id: Option<String>,
39 pub body: T,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
43#[serde(rename_all = "snake_case")]
44pub enum ThreadStatus {
45 Running,
46 Idle,
47 Completed,
48 Failed,
49 Paused,
50 Archived,
51}
52
53impl Status for ThreadStatus {
54 fn is_terminal(&self) -> bool {
55 matches!(self, Self::Completed | Self::Failed | Self::Archived)
56 }
57 fn is_active(&self) -> bool {
58 matches!(self, Self::Running)
59 }
60 fn is_paused(&self) -> bool {
61 matches!(self, Self::Paused)
62 }
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
66#[serde(rename_all = "snake_case")]
67pub enum SessionSource {
68 Interactive,
69 Resume,
70 Fork,
71 Api,
72 Unknown,
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct Thread {
77 pub id: String,
78 pub preview: String,
79 pub ephemeral: bool,
80 pub model_provider: String,
81 pub created_at: i64,
82 pub updated_at: i64,
83 pub status: ThreadStatus,
84 #[serde(skip_serializing_if = "Option::is_none")]
85 pub path: Option<PathBuf>,
86 pub cwd: PathBuf,
87 pub cli_version: String,
88 pub source: SessionSource,
89 #[serde(skip_serializing_if = "Option::is_none")]
90 pub name: Option<String>,
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
94#[serde(rename_all = "snake_case")]
95pub enum ThreadGoalStatus {
96 Active,
97 Paused,
98 Blocked,
99 UsageLimited,
100 BudgetLimited,
101 Complete,
102}
103
104impl Status for ThreadGoalStatus {
105 fn is_terminal(&self) -> bool {
106 matches!(self, Self::Complete)
107 }
108 fn is_active(&self) -> bool {
109 matches!(self, Self::Active)
110 }
111 fn is_paused(&self) -> bool {
112 matches!(self, Self::Paused)
113 }
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
117pub struct ThreadGoal {
118 pub thread_id: String,
119 pub goal_id: String,
120 pub objective: String,
121 pub status: ThreadGoalStatus,
122 #[serde(skip_serializing_if = "Option::is_none")]
123 pub token_budget: Option<i64>,
124 pub tokens_used: i64,
125 pub time_used_seconds: i64,
126 pub continuation_count: i64,
127 pub created_at: i64,
128 pub updated_at: i64,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize)]
132pub struct ThreadStartParams {
133 #[serde(skip_serializing_if = "Option::is_none")]
134 pub model: Option<String>,
135 #[serde(skip_serializing_if = "Option::is_none")]
136 pub model_provider: Option<String>,
137 #[serde(skip_serializing_if = "Option::is_none")]
138 pub cwd: Option<PathBuf>,
139 #[serde(default)]
140 pub persist_extended_history: bool,
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize)]
144pub struct ThreadResumeParams {
145 pub thread_id: String,
146 #[serde(skip_serializing_if = "Option::is_none")]
147 pub history: Option<Vec<Value>>,
148 #[serde(skip_serializing_if = "Option::is_none")]
149 pub path: Option<PathBuf>,
150 #[serde(skip_serializing_if = "Option::is_none")]
151 pub model: Option<String>,
152 #[serde(skip_serializing_if = "Option::is_none")]
153 pub model_provider: Option<String>,
154 #[serde(skip_serializing_if = "Option::is_none")]
155 pub cwd: Option<PathBuf>,
156 #[serde(skip_serializing_if = "Option::is_none")]
157 pub approval_policy: Option<String>,
158 #[serde(skip_serializing_if = "Option::is_none")]
159 pub sandbox: Option<String>,
160 #[serde(skip_serializing_if = "Option::is_none")]
161 pub config: Option<Value>,
162 #[serde(skip_serializing_if = "Option::is_none")]
163 pub base_instructions: Option<String>,
164 #[serde(skip_serializing_if = "Option::is_none")]
165 pub developer_instructions: Option<String>,
166 #[serde(skip_serializing_if = "Option::is_none")]
167 pub personality: Option<String>,
168 #[serde(default)]
169 pub persist_extended_history: bool,
170}
171
172#[derive(Debug, Clone, Serialize, Deserialize)]
173pub struct ThreadForkParams {
174 pub thread_id: String,
175 #[serde(skip_serializing_if = "Option::is_none")]
176 pub path: Option<PathBuf>,
177 #[serde(skip_serializing_if = "Option::is_none")]
178 pub model: Option<String>,
179 #[serde(skip_serializing_if = "Option::is_none")]
180 pub model_provider: Option<String>,
181 #[serde(skip_serializing_if = "Option::is_none")]
182 pub cwd: Option<PathBuf>,
183 #[serde(skip_serializing_if = "Option::is_none")]
184 pub approval_policy: Option<String>,
185 #[serde(skip_serializing_if = "Option::is_none")]
186 pub sandbox: Option<String>,
187 #[serde(skip_serializing_if = "Option::is_none")]
188 pub config: Option<Value>,
189 #[serde(skip_serializing_if = "Option::is_none")]
190 pub base_instructions: Option<String>,
191 #[serde(skip_serializing_if = "Option::is_none")]
192 pub developer_instructions: Option<String>,
193 #[serde(default)]
194 pub persist_extended_history: bool,
195}
196
197#[derive(Debug, Clone, Serialize, Deserialize)]
198pub struct ThreadListParams {
199 #[serde(default)]
200 pub include_archived: bool,
201 #[serde(skip_serializing_if = "Option::is_none")]
202 pub limit: Option<usize>,
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize)]
206pub struct ThreadReadParams {
207 pub thread_id: String,
208}
209
210#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct ThreadSetNameParams {
212 pub thread_id: String,
213 pub name: String,
214}
215
216#[derive(Debug, Clone, Serialize, Deserialize)]
217pub struct ThreadGoalSetParams {
218 pub thread_id: String,
219 pub objective: String,
220 #[serde(skip_serializing_if = "Option::is_none")]
221 pub token_budget: Option<i64>,
222}
223
224#[derive(Debug, Clone, Serialize, Deserialize)]
225pub struct ThreadGoalGetParams {
226 pub thread_id: String,
227}
228
229#[derive(Debug, Clone, Serialize, Deserialize)]
230pub struct ThreadGoalClearParams {
231 pub thread_id: String,
232}
233
234#[derive(Debug, Clone, Serialize, Deserialize)]
235pub struct ThreadGoalProgressParams {
236 pub thread_id: String,
237 #[serde(default)]
238 pub token_delta: i64,
239 #[serde(default)]
240 pub time_delta_seconds: i64,
241 #[serde(default)]
242 pub record_continuation: bool,
243}
244
245#[derive(Debug, Clone, Serialize, Deserialize)]
246#[serde(tag = "kind", rename_all = "snake_case")]
247pub enum ThreadRequest {
248 Create {
249 #[serde(default)]
250 metadata: Value,
251 },
252 Start(ThreadStartParams),
253 Resume(ThreadResumeParams),
254 Fork(ThreadForkParams),
255 List(ThreadListParams),
256 Read(ThreadReadParams),
257 SetName(ThreadSetNameParams),
258 GoalSet(ThreadGoalSetParams),
259 GoalGet(ThreadGoalGetParams),
260 GoalClear(ThreadGoalClearParams),
261 GoalRecordProgress(ThreadGoalProgressParams),
262 Archive {
263 thread_id: String,
264 },
265 Unarchive {
266 thread_id: String,
267 },
268 Message {
269 thread_id: String,
270 input: String,
271 },
272}
273
274#[derive(Debug, Clone, Serialize, Deserialize)]
276pub struct ThreadResponse {
277 pub thread_id: String,
279 pub status: String,
281 #[serde(skip_serializing_if = "Option::is_none")]
283 pub thread: Option<Thread>,
284 #[serde(default)]
286 pub threads: Vec<Thread>,
287 #[serde(skip_serializing_if = "Option::is_none")]
289 pub goal: Option<ThreadGoal>,
290 #[serde(skip_serializing_if = "Option::is_none")]
292 pub model: Option<String>,
293 #[serde(skip_serializing_if = "Option::is_none")]
295 pub model_provider: Option<String>,
296 #[serde(skip_serializing_if = "Option::is_none")]
298 pub cwd: Option<PathBuf>,
299 #[serde(skip_serializing_if = "Option::is_none")]
301 pub approval_policy: Option<String>,
302 #[serde(skip_serializing_if = "Option::is_none")]
304 pub sandbox: Option<String>,
305 #[serde(default)]
307 pub events: Vec<EventFrame>,
308 #[serde(default)]
310 pub data: Value,
311}
312
313#[derive(Debug, Clone, Serialize, Deserialize)]
315#[serde(tag = "kind", rename_all = "snake_case")]
316pub enum AppRequest {
317 Capabilities,
319 ConfigGet { key: String },
321 ConfigSet { key: String, value: String },
323 ConfigUnset { key: String },
325 ConfigList,
327 ConfigReload,
340 Models,
342 ThreadLoadedList,
344 SubmitUserInput {
349 request_id: String,
350 answers: Vec<UserInputAnswerEvent>,
351 },
352}
353
354#[derive(Debug, Clone, Serialize, Deserialize)]
356pub struct AppResponse {
357 pub ok: bool,
359 pub data: Value,
361 #[serde(default)]
363 pub events: Vec<EventFrame>,
364}
365
366#[derive(Debug, Clone, Serialize, Deserialize)]
368pub struct PromptRequest {
369 #[serde(skip_serializing_if = "Option::is_none")]
371 pub thread_id: Option<String>,
372 pub prompt: String,
374 #[serde(skip_serializing_if = "Option::is_none")]
376 pub model: Option<String>,
377}
378
379#[derive(Debug, Clone, Serialize, Deserialize)]
381pub struct PromptResponse {
382 pub output: String,
384 pub model: String,
386 #[serde(default)]
388 pub events: Vec<EventFrame>,
389}
390
391#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
393#[serde(rename_all = "snake_case")]
394pub enum AskForApproval {
395 UnlessTrusted,
397 OnFailure,
399 OnRequest,
401 Reject {
403 sandbox_approval: bool,
404 rules: bool,
405 mcp_elicitations: bool,
406 },
407 Never,
409}
410
411#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
413#[serde(rename_all = "snake_case")]
414pub enum ToolKind {
415 Function,
417 Mcp,
419}
420
421#[derive(Debug, Clone, Serialize, Deserialize)]
423pub struct LocalShellParams {
424 pub command: String,
426 #[serde(skip_serializing_if = "Option::is_none")]
428 pub cwd: Option<String>,
429 #[serde(skip_serializing_if = "Option::is_none")]
431 pub timeout_ms: Option<u64>,
432}
433
434#[derive(Debug, Clone, Serialize, Deserialize)]
436#[serde(tag = "type", rename_all = "snake_case")]
437pub enum ToolPayload {
438 Function { arguments: String },
440 Custom { input: String },
442 LocalShell { params: LocalShellParams },
444 Mcp {
446 server: String,
447 tool: String,
448 raw_arguments: Value,
449 #[serde(skip_serializing_if = "Option::is_none")]
450 raw_tool_call_id: Option<String>,
451 },
452}
453
454#[derive(Debug, Clone, Serialize, Deserialize)]
456#[serde(tag = "type", rename_all = "snake_case")]
457pub enum ToolOutput {
458 Function {
460 #[serde(skip_serializing_if = "Option::is_none")]
462 body: Option<Value>,
463 success: bool,
465 },
466 Mcp {
468 result: Value,
470 },
471}
472
473impl ToolOutput {
474 pub fn success(&self) -> bool {
479 match self {
480 Self::Function { success, .. } => *success,
481 Self::Mcp { result } => {
482 matches!(result.get("isError"), None | Some(Value::Bool(false)))
483 }
484 }
485 }
486}
487
488#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
490#[serde(rename_all = "snake_case")]
491pub enum NetworkPolicyRuleAction {
492 Allow,
494 Deny,
496}
497
498#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
500pub struct NetworkPolicyAmendment {
501 pub host: String,
503 pub action: NetworkPolicyRuleAction,
505}
506
507#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
509#[serde(tag = "type", rename_all = "snake_case")]
510pub enum ReviewDecision {
511 Approved,
513 ApprovedExecpolicyAmendment,
515 ApprovedForSession,
517 NetworkPolicyAmendment {
519 host: String,
520 action: NetworkPolicyRuleAction,
521 },
522 Denied,
524 Abort,
526}
527
528#[derive(Debug, Clone, Serialize, Deserialize)]
530#[serde(rename_all = "snake_case")]
531pub enum McpStartupStatus {
532 Starting,
534 Ready,
536 Failed { error: String },
538 Cancelled,
540}
541
542#[derive(Debug, Clone, Serialize, Deserialize)]
544pub struct McpStartupUpdateEvent {
545 pub server_name: String,
547 pub status: McpStartupStatus,
549}
550
551#[derive(Debug, Clone, Serialize, Deserialize)]
553pub struct McpStartupFailure {
554 pub server_name: String,
556 pub error: String,
558}
559
560#[derive(Debug, Clone, Serialize, Deserialize)]
562pub struct McpStartupCompleteEvent {
563 pub ready: Vec<String>,
565 pub failed: Vec<McpStartupFailure>,
567 pub cancelled: Vec<String>,
569}
570
571#[derive(Debug, Clone, Serialize, Deserialize)]
573pub struct NetworkApprovalContext {
574 pub host: String,
576 pub protocol: String,
578}
579
580#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
587pub struct UserInputOptionEvent {
588 pub label: String,
590 pub description: String,
592}
593
594#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
596pub struct UserInputQuestionEvent {
597 pub header: String,
599 pub id: String,
601 pub question: String,
603 pub options: Vec<UserInputOptionEvent>,
605 #[serde(default)]
607 pub allow_free_text: bool,
608 #[serde(default)]
610 pub multi_select: bool,
611}
612
613#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
619pub struct UserInputRequestEvent {
620 pub call_id: String,
622 pub turn_id: String,
624 pub request_id: String,
626 pub questions: Vec<UserInputQuestionEvent>,
628}
629
630#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
632pub struct UserInputAnswerEvent {
633 pub id: String,
635 pub label: String,
637 pub value: String,
639}
640
641#[derive(Debug, Clone, Serialize, Deserialize)]
643pub struct ExecApprovalRequestEvent {
644 pub call_id: String,
646 pub approval_id: String,
648 pub turn_id: String,
650 pub command: String,
652 pub cwd: String,
654 pub reason: String,
656 #[serde(default, skip_serializing_if = "Option::is_none")]
658 pub matched_rule: Option<Box<str>>,
659 #[serde(skip_serializing_if = "Option::is_none")]
661 pub network_approval_context: Option<NetworkApprovalContext>,
662 #[serde(default)]
664 pub proposed_execpolicy_amendment: Vec<String>,
665 #[serde(default)]
667 pub proposed_network_policy_amendments: Vec<NetworkPolicyAmendment>,
668 #[serde(default)]
670 pub additional_permissions: Vec<String>,
671 #[serde(default)]
673 pub available_decisions: Vec<ReviewDecision>,
674}
675
676#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
678#[serde(rename_all = "snake_case")]
679pub enum ResponseChannel {
680 #[default]
682 Text,
683 Reasoning,
685}
686
687impl ResponseChannel {
688 pub const fn is_text(&self) -> bool {
690 matches!(self, ResponseChannel::Text)
691 }
692}
693
694#[derive(Debug, Clone, Serialize, Deserialize)]
696pub struct ApprovalDecisionRequest {
697 pub decision: String,
699 #[serde(default)]
701 pub remember: bool,
702}
703
704#[derive(Debug, Clone, Serialize, Deserialize)]
710#[serde(tag = "event", rename_all = "snake_case")]
711pub enum EventFrame {
712 ResponseStart { response_id: String },
714 ResponseDelta {
716 response_id: String,
717 delta: String,
718 #[serde(default, skip_serializing_if = "ResponseChannel::is_text")]
719 channel: ResponseChannel,
720 },
721 ResponseEnd { response_id: String },
723 ToolCallStart {
725 response_id: String,
726 tool_name: String,
727 arguments: Value,
728 },
729 ToolCallResult {
731 response_id: String,
732 tool_name: String,
733 output: Value,
734 },
735 McpStartupUpdate { update: McpStartupUpdateEvent },
737 McpStartupComplete { summary: McpStartupCompleteEvent },
739 McpToolCallBegin {
741 server_name: String,
742 tool_name: String,
743 },
744 McpToolCallEnd {
746 server_name: String,
747 tool_name: String,
748 ok: bool,
749 },
750 ExecApprovalRequest { request: ExecApprovalRequestEvent },
752 ApplyPatchApprovalRequest { request: ExecApprovalRequestEvent },
754 UserInputRequest { request: UserInputRequestEvent },
759 ElicitationRequest {
761 server_name: String,
762 request_id: String,
763 prompt: String,
764 },
765 ExecCommandBegin { command: String, cwd: String },
767 ExecCommandOutputDelta { command: String, delta: String },
769 ExecCommandEnd { command: String, exit_code: i32 },
771 PatchApplyBegin { path: String },
773 PatchApplyEnd { path: String, ok: bool },
775 TurnStarted { turn_id: String },
777 TurnComplete { turn_id: String },
779 TurnAborted { turn_id: String, reason: String },
781 ThreadGoalUpdated { goal: ThreadGoal },
783 ThreadGoalCleared { thread_id: String },
785 Error {
787 response_id: String,
788 message: String,
789 },
790}