Skip to main content

missiond_runner/
types.rs

1//! Types for Claude CLI stream-json output
2//!
3//! Mirrors the TypeScript definitions in packages/runner/src/types.ts
4
5use serde::{Deserialize, Serialize};
6use std::path::PathBuf;
7use std::time::Duration;
8
9/// Runner configuration options
10pub struct RunOptions {
11    /// The prompt to send
12    pub prompt: String,
13    /// Working directory
14    pub cwd: Option<PathBuf>,
15    /// Resume session ID
16    pub session_id: Option<String>,
17    /// MCP config file path
18    pub mcp_config: Option<PathBuf>,
19    /// Timeout duration (default: 5 minutes)
20    pub timeout: Duration,
21    /// Progress callback
22    pub on_progress: Option<ProgressCallback>,
23}
24
25impl Default for RunOptions {
26    fn default() -> Self {
27        Self {
28            prompt: String::new(),
29            cwd: None,
30            session_id: None,
31            mcp_config: None,
32            timeout: Duration::from_secs(5 * 60),
33            on_progress: None,
34        }
35    }
36}
37
38/// Progress callback type
39pub type ProgressCallback = Box<dyn Fn(StreamEvent) + Send + Sync>;
40
41/// Execution result
42#[derive(Debug, Clone, Serialize, Deserialize)]
43#[serde(rename_all = "camelCase")]
44pub struct RunResult {
45    /// Session ID (can be used for --resume)
46    pub session_id: String,
47    /// Final result text
48    pub result: String,
49    /// Whether the result is an error
50    pub is_error: bool,
51    /// Execution duration in milliseconds
52    pub duration_ms: u64,
53    /// API call duration in milliseconds
54    pub duration_api_ms: u64,
55    /// Number of turns
56    pub num_turns: u32,
57    /// Total cost in USD
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub total_cost_usd: Option<f64>,
60    /// Token usage
61    #[serde(skip_serializing_if = "Option::is_none")]
62    pub usage: Option<Usage>,
63}
64
65/// Token usage statistics
66#[derive(Debug, Clone, Serialize, Deserialize)]
67#[serde(rename_all = "camelCase")]
68pub struct Usage {
69    pub input_tokens: u64,
70    pub output_tokens: u64,
71}
72
73/// Stream event types
74#[derive(Debug, Clone, Serialize, Deserialize)]
75#[serde(tag = "type", rename_all = "lowercase")]
76pub enum StreamEvent {
77    System(SystemEvent),
78    Assistant(AssistantEvent),
79    User(UserEvent),
80    Result(ResultEvent),
81}
82
83/// System event
84#[derive(Debug, Clone, Serialize, Deserialize)]
85pub struct SystemEvent {
86    pub subtype: String,
87    #[serde(skip_serializing_if = "Option::is_none")]
88    pub data: Option<serde_json::Value>,
89}
90
91/// Assistant event
92#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct AssistantEvent {
94    pub message: AssistantMessage,
95}
96
97/// Assistant message
98#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct AssistantMessage {
100    pub content: Vec<ContentBlock>,
101}
102
103/// User event
104#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct UserEvent {
106    pub message: UserMessage,
107}
108
109/// User message
110#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct UserMessage {
112    pub content: String,
113}
114
115/// Result event from stream-json
116#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct ResultEvent {
118    pub subtype: String,
119    pub session_id: String,
120    pub result: String,
121    pub is_error: bool,
122    pub duration_ms: u64,
123    pub duration_api_ms: u64,
124    pub num_turns: u32,
125    #[serde(skip_serializing_if = "Option::is_none")]
126    pub total_cost_usd: Option<f64>,
127    #[serde(skip_serializing_if = "Option::is_none")]
128    pub usage: Option<RawUsage>,
129}
130
131/// Raw usage from Claude CLI (snake_case)
132#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct RawUsage {
134    pub input_tokens: u64,
135    pub output_tokens: u64,
136}
137
138/// Content block in assistant message
139#[derive(Debug, Clone, Serialize, Deserialize)]
140#[serde(tag = "type", rename_all = "snake_case")]
141pub enum ContentBlock {
142    Text {
143        text: String,
144    },
145    ToolUse {
146        id: String,
147        name: String,
148        input: serde_json::Value,
149    },
150    ToolResult {
151        tool_use_id: String,
152        content: String,
153        #[serde(skip_serializing_if = "Option::is_none")]
154        is_error: Option<bool>,
155    },
156}
157
158/// Runner errors
159#[derive(Debug, thiserror::Error)]
160pub enum RunnerError {
161    #[error("Task cancelled")]
162    Cancelled,
163
164    #[error("Task timed out after {0:?}")]
165    Timeout(Duration),
166
167    #[error("Claude CLI failed: {0}")]
168    CliFailed(String),
169
170    #[error("No result from Claude CLI: {0}")]
171    NoResult(String),
172
173    #[error("IO error: {0}")]
174    Io(#[from] std::io::Error),
175
176    #[error("JSON parse error: {0}")]
177    Json(#[from] serde_json::Error),
178}