1use std::path::PathBuf;
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6pub mod runtime;
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct Envelope<T> {
10 pub request_id: String,
11 #[serde(skip_serializing_if = "Option::is_none")]
12 pub thread_id: Option<String>,
13 pub body: T,
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
17#[serde(rename_all = "snake_case")]
18pub enum ThreadStatus {
19 Running,
20 Idle,
21 Completed,
22 Failed,
23 Paused,
24 Archived,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
28#[serde(rename_all = "snake_case")]
29pub enum SessionSource {
30 Interactive,
31 Resume,
32 Fork,
33 Api,
34 Unknown,
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct Thread {
39 pub id: String,
40 pub preview: String,
41 pub ephemeral: bool,
42 pub model_provider: String,
43 pub created_at: i64,
44 pub updated_at: i64,
45 pub status: ThreadStatus,
46 #[serde(skip_serializing_if = "Option::is_none")]
47 pub path: Option<PathBuf>,
48 pub cwd: PathBuf,
49 pub cli_version: String,
50 pub source: SessionSource,
51 #[serde(skip_serializing_if = "Option::is_none")]
52 pub name: Option<String>,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
56#[serde(rename_all = "snake_case")]
57pub enum ThreadGoalStatus {
58 Active,
59 Paused,
60 Blocked,
61 UsageLimited,
62 BudgetLimited,
63 Complete,
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
67pub struct ThreadGoal {
68 pub thread_id: String,
69 pub goal_id: String,
70 pub objective: String,
71 pub status: ThreadGoalStatus,
72 #[serde(skip_serializing_if = "Option::is_none")]
73 pub token_budget: Option<i64>,
74 pub tokens_used: i64,
75 pub time_used_seconds: i64,
76 pub created_at: i64,
77 pub updated_at: i64,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct ThreadStartParams {
82 #[serde(skip_serializing_if = "Option::is_none")]
83 pub model: Option<String>,
84 #[serde(skip_serializing_if = "Option::is_none")]
85 pub model_provider: Option<String>,
86 #[serde(skip_serializing_if = "Option::is_none")]
87 pub cwd: Option<PathBuf>,
88 #[serde(default)]
89 pub persist_extended_history: bool,
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct ThreadResumeParams {
94 pub thread_id: String,
95 #[serde(skip_serializing_if = "Option::is_none")]
96 pub history: Option<Vec<Value>>,
97 #[serde(skip_serializing_if = "Option::is_none")]
98 pub path: Option<PathBuf>,
99 #[serde(skip_serializing_if = "Option::is_none")]
100 pub model: Option<String>,
101 #[serde(skip_serializing_if = "Option::is_none")]
102 pub model_provider: Option<String>,
103 #[serde(skip_serializing_if = "Option::is_none")]
104 pub cwd: Option<PathBuf>,
105 #[serde(skip_serializing_if = "Option::is_none")]
106 pub approval_policy: Option<String>,
107 #[serde(skip_serializing_if = "Option::is_none")]
108 pub sandbox: Option<String>,
109 #[serde(skip_serializing_if = "Option::is_none")]
110 pub config: Option<Value>,
111 #[serde(skip_serializing_if = "Option::is_none")]
112 pub base_instructions: Option<String>,
113 #[serde(skip_serializing_if = "Option::is_none")]
114 pub developer_instructions: Option<String>,
115 #[serde(skip_serializing_if = "Option::is_none")]
116 pub personality: Option<String>,
117 #[serde(default)]
118 pub persist_extended_history: bool,
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize)]
122pub struct ThreadForkParams {
123 pub thread_id: String,
124 #[serde(skip_serializing_if = "Option::is_none")]
125 pub path: Option<PathBuf>,
126 #[serde(skip_serializing_if = "Option::is_none")]
127 pub model: Option<String>,
128 #[serde(skip_serializing_if = "Option::is_none")]
129 pub model_provider: Option<String>,
130 #[serde(skip_serializing_if = "Option::is_none")]
131 pub cwd: Option<PathBuf>,
132 #[serde(skip_serializing_if = "Option::is_none")]
133 pub approval_policy: Option<String>,
134 #[serde(skip_serializing_if = "Option::is_none")]
135 pub sandbox: Option<String>,
136 #[serde(skip_serializing_if = "Option::is_none")]
137 pub config: Option<Value>,
138 #[serde(skip_serializing_if = "Option::is_none")]
139 pub base_instructions: Option<String>,
140 #[serde(skip_serializing_if = "Option::is_none")]
141 pub developer_instructions: Option<String>,
142 #[serde(default)]
143 pub persist_extended_history: bool,
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct ThreadListParams {
148 #[serde(default)]
149 pub include_archived: bool,
150 #[serde(skip_serializing_if = "Option::is_none")]
151 pub limit: Option<usize>,
152}
153
154#[derive(Debug, Clone, Serialize, Deserialize)]
155pub struct ThreadReadParams {
156 pub thread_id: String,
157}
158
159#[derive(Debug, Clone, Serialize, Deserialize)]
160pub struct ThreadSetNameParams {
161 pub thread_id: String,
162 pub name: String,
163}
164
165#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct ThreadGoalSetParams {
167 pub thread_id: String,
168 pub objective: String,
169 #[serde(skip_serializing_if = "Option::is_none")]
170 pub token_budget: Option<i64>,
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct ThreadGoalGetParams {
175 pub thread_id: String,
176}
177
178#[derive(Debug, Clone, Serialize, Deserialize)]
179pub struct ThreadGoalClearParams {
180 pub thread_id: String,
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize)]
184#[serde(tag = "kind", rename_all = "snake_case")]
185pub enum ThreadRequest {
186 Create {
187 #[serde(default)]
188 metadata: Value,
189 },
190 Start(ThreadStartParams),
191 Resume(ThreadResumeParams),
192 Fork(ThreadForkParams),
193 List(ThreadListParams),
194 Read(ThreadReadParams),
195 SetName(ThreadSetNameParams),
196 GoalSet(ThreadGoalSetParams),
197 GoalGet(ThreadGoalGetParams),
198 GoalClear(ThreadGoalClearParams),
199 Archive {
200 thread_id: String,
201 },
202 Unarchive {
203 thread_id: String,
204 },
205 Message {
206 thread_id: String,
207 input: String,
208 },
209}
210
211#[derive(Debug, Clone, Serialize, Deserialize)]
213pub struct ThreadResponse {
214 pub thread_id: String,
216 pub status: String,
218 #[serde(skip_serializing_if = "Option::is_none")]
220 pub thread: Option<Thread>,
221 #[serde(default)]
223 pub threads: Vec<Thread>,
224 #[serde(skip_serializing_if = "Option::is_none")]
226 pub goal: Option<ThreadGoal>,
227 #[serde(skip_serializing_if = "Option::is_none")]
229 pub model: Option<String>,
230 #[serde(skip_serializing_if = "Option::is_none")]
232 pub model_provider: Option<String>,
233 #[serde(skip_serializing_if = "Option::is_none")]
235 pub cwd: Option<PathBuf>,
236 #[serde(skip_serializing_if = "Option::is_none")]
238 pub approval_policy: Option<String>,
239 #[serde(skip_serializing_if = "Option::is_none")]
241 pub sandbox: Option<String>,
242 #[serde(default)]
244 pub events: Vec<EventFrame>,
245 #[serde(default)]
247 pub data: Value,
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
252#[serde(tag = "kind", rename_all = "snake_case")]
253pub enum AppRequest {
254 Capabilities,
256 ConfigGet { key: String },
258 ConfigSet { key: String, value: String },
260 ConfigUnset { key: String },
262 ConfigList,
264 Models,
266 ThreadLoadedList,
268}
269
270#[derive(Debug, Clone, Serialize, Deserialize)]
272pub struct AppResponse {
273 pub ok: bool,
275 pub data: Value,
277 #[serde(default)]
279 pub events: Vec<EventFrame>,
280}
281
282#[derive(Debug, Clone, Serialize, Deserialize)]
284pub struct PromptRequest {
285 #[serde(skip_serializing_if = "Option::is_none")]
287 pub thread_id: Option<String>,
288 pub prompt: String,
290 #[serde(skip_serializing_if = "Option::is_none")]
292 pub model: Option<String>,
293}
294
295#[derive(Debug, Clone, Serialize, Deserialize)]
297pub struct PromptResponse {
298 pub output: String,
300 pub model: String,
302 #[serde(default)]
304 pub events: Vec<EventFrame>,
305}
306
307#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
309#[serde(rename_all = "snake_case")]
310pub enum AskForApproval {
311 UnlessTrusted,
313 OnFailure,
315 OnRequest,
317 Reject {
319 sandbox_approval: bool,
320 rules: bool,
321 mcp_elicitations: bool,
322 },
323 Never,
325}
326
327#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
329#[serde(rename_all = "snake_case")]
330pub enum ToolKind {
331 Function,
333 Mcp,
335}
336
337#[derive(Debug, Clone, Serialize, Deserialize)]
339pub struct LocalShellParams {
340 pub command: String,
342 #[serde(skip_serializing_if = "Option::is_none")]
344 pub cwd: Option<String>,
345 #[serde(skip_serializing_if = "Option::is_none")]
347 pub timeout_ms: Option<u64>,
348}
349
350#[derive(Debug, Clone, Serialize, Deserialize)]
352#[serde(tag = "type", rename_all = "snake_case")]
353pub enum ToolPayload {
354 Function { arguments: String },
356 Custom { input: String },
358 LocalShell { params: LocalShellParams },
360 Mcp {
362 server: String,
363 tool: String,
364 raw_arguments: Value,
365 #[serde(skip_serializing_if = "Option::is_none")]
366 raw_tool_call_id: Option<String>,
367 },
368}
369
370#[derive(Debug, Clone, Serialize, Deserialize)]
372#[serde(tag = "type", rename_all = "snake_case")]
373pub enum ToolOutput {
374 Function {
376 #[serde(skip_serializing_if = "Option::is_none")]
378 body: Option<Value>,
379 success: bool,
381 },
382 Mcp {
384 result: Value,
386 },
387}
388
389#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
391#[serde(rename_all = "snake_case")]
392pub enum NetworkPolicyRuleAction {
393 Allow,
395 Deny,
397}
398
399#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
401pub struct NetworkPolicyAmendment {
402 pub host: String,
404 pub action: NetworkPolicyRuleAction,
406}
407
408#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
410#[serde(tag = "type", rename_all = "snake_case")]
411pub enum ReviewDecision {
412 Approved,
414 ApprovedExecpolicyAmendment,
416 ApprovedForSession,
418 NetworkPolicyAmendment {
420 host: String,
421 action: NetworkPolicyRuleAction,
422 },
423 Denied,
425 Abort,
427}
428
429#[derive(Debug, Clone, Serialize, Deserialize)]
431#[serde(rename_all = "snake_case")]
432pub enum McpStartupStatus {
433 Starting,
435 Ready,
437 Failed { error: String },
439 Cancelled,
441}
442
443#[derive(Debug, Clone, Serialize, Deserialize)]
445pub struct McpStartupUpdateEvent {
446 pub server_name: String,
448 pub status: McpStartupStatus,
450}
451
452#[derive(Debug, Clone, Serialize, Deserialize)]
454pub struct McpStartupFailure {
455 pub server_name: String,
457 pub error: String,
459}
460
461#[derive(Debug, Clone, Serialize, Deserialize)]
463pub struct McpStartupCompleteEvent {
464 pub ready: Vec<String>,
466 pub failed: Vec<McpStartupFailure>,
468 pub cancelled: Vec<String>,
470}
471
472#[derive(Debug, Clone, Serialize, Deserialize)]
474pub struct NetworkApprovalContext {
475 pub host: String,
477 pub protocol: String,
479}
480
481#[derive(Debug, Clone, Serialize, Deserialize)]
483pub struct ExecApprovalRequestEvent {
484 pub call_id: String,
486 pub approval_id: String,
488 pub turn_id: String,
490 pub command: String,
492 pub cwd: String,
494 pub reason: String,
496 #[serde(default, skip_serializing_if = "Option::is_none")]
498 pub matched_rule: Option<Box<str>>,
499 #[serde(skip_serializing_if = "Option::is_none")]
501 pub network_approval_context: Option<NetworkApprovalContext>,
502 #[serde(default)]
504 pub proposed_execpolicy_amendment: Vec<String>,
505 #[serde(default)]
507 pub proposed_network_policy_amendments: Vec<NetworkPolicyAmendment>,
508 #[serde(default)]
510 pub additional_permissions: Vec<String>,
511 #[serde(default)]
513 pub available_decisions: Vec<ReviewDecision>,
514}
515
516#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
518#[serde(rename_all = "snake_case")]
519pub enum ResponseChannel {
520 #[default]
522 Text,
523 Reasoning,
525}
526
527impl ResponseChannel {
528 pub const fn is_text(&self) -> bool {
530 matches!(self, ResponseChannel::Text)
531 }
532}
533
534#[derive(Debug, Clone, Serialize, Deserialize)]
536pub struct ApprovalDecisionRequest {
537 pub decision: String,
539 #[serde(default)]
541 pub remember: bool,
542}
543
544#[derive(Debug, Clone, Serialize, Deserialize)]
550#[serde(tag = "event", rename_all = "snake_case")]
551pub enum EventFrame {
552 ResponseStart { response_id: String },
554 ResponseDelta {
556 response_id: String,
557 delta: String,
558 #[serde(default, skip_serializing_if = "ResponseChannel::is_text")]
559 channel: ResponseChannel,
560 },
561 ResponseEnd { response_id: String },
563 ToolCallStart {
565 response_id: String,
566 tool_name: String,
567 arguments: Value,
568 },
569 ToolCallResult {
571 response_id: String,
572 tool_name: String,
573 output: Value,
574 },
575 McpStartupUpdate { update: McpStartupUpdateEvent },
577 McpStartupComplete { summary: McpStartupCompleteEvent },
579 McpToolCallBegin {
581 server_name: String,
582 tool_name: String,
583 },
584 McpToolCallEnd {
586 server_name: String,
587 tool_name: String,
588 ok: bool,
589 },
590 ExecApprovalRequest { request: ExecApprovalRequestEvent },
592 ApplyPatchApprovalRequest { request: ExecApprovalRequestEvent },
594 ElicitationRequest {
596 server_name: String,
597 request_id: String,
598 prompt: String,
599 },
600 ExecCommandBegin { command: String, cwd: String },
602 ExecCommandOutputDelta { command: String, delta: String },
604 ExecCommandEnd { command: String, exit_code: i32 },
606 PatchApplyBegin { path: String },
608 PatchApplyEnd { path: String, ok: bool },
610 TurnStarted { turn_id: String },
612 TurnComplete { turn_id: String },
614 TurnAborted { turn_id: String, reason: String },
616 ThreadGoalUpdated { goal: ThreadGoal },
618 ThreadGoalCleared { thread_id: String },
620 Error {
622 response_id: String,
623 message: String,
624 },
625}