use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::time::Duration;
pub struct RunOptions {
pub prompt: String,
pub cwd: Option<PathBuf>,
pub session_id: Option<String>,
pub mcp_config: Option<PathBuf>,
pub timeout: Duration,
pub on_progress: Option<ProgressCallback>,
}
impl Default for RunOptions {
fn default() -> Self {
Self {
prompt: String::new(),
cwd: None,
session_id: None,
mcp_config: None,
timeout: Duration::from_secs(5 * 60),
on_progress: None,
}
}
}
pub type ProgressCallback = Box<dyn Fn(StreamEvent) + Send + Sync>;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RunResult {
pub session_id: String,
pub result: String,
pub is_error: bool,
pub duration_ms: u64,
pub duration_api_ms: u64,
pub num_turns: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub total_cost_usd: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub usage: Option<Usage>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Usage {
pub input_tokens: u64,
pub output_tokens: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "lowercase")]
pub enum StreamEvent {
System(SystemEvent),
Assistant(AssistantEvent),
User(UserEvent),
Result(ResultEvent),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SystemEvent {
pub subtype: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssistantEvent {
pub message: AssistantMessage,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssistantMessage {
pub content: Vec<ContentBlock>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserEvent {
pub message: UserMessage,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserMessage {
pub content: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResultEvent {
pub subtype: String,
pub session_id: String,
pub result: String,
pub is_error: bool,
pub duration_ms: u64,
pub duration_api_ms: u64,
pub num_turns: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub total_cost_usd: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub usage: Option<RawUsage>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RawUsage {
pub input_tokens: u64,
pub output_tokens: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ContentBlock {
Text {
text: String,
},
ToolUse {
id: String,
name: String,
input: serde_json::Value,
},
ToolResult {
tool_use_id: String,
content: String,
#[serde(skip_serializing_if = "Option::is_none")]
is_error: Option<bool>,
},
}
#[derive(Debug, thiserror::Error)]
pub enum RunnerError {
#[error("Task cancelled")]
Cancelled,
#[error("Task timed out after {0:?}")]
Timeout(Duration),
#[error("Claude CLI failed: {0}")]
CliFailed(String),
#[error("No result from Claude CLI: {0}")]
NoResult(String),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("JSON parse error: {0}")]
Json(#[from] serde_json::Error),
}