use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::path::PathBuf;
use crate::env::EnvPolicy;
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum LaneKind {
Acp,
Rpc,
Cli,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum PromptChannel {
#[default]
ArgOrStdin,
Stdin,
ArgOnly,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum StreamMode {
#[default]
Stdio,
Pty,
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct CliLaneSpec {
#[serde(default)]
pub args: Vec<String>,
#[serde(default)]
pub prompt_channel: PromptChannel,
#[serde(default)]
pub stream: StreamMode,
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct RpcCallSpec {
pub method: String,
#[serde(default)]
pub params: serde_json::Value,
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct RpcLaneSpec {
#[serde(default)]
pub session_open: Option<RpcCallSpec>,
pub prompt: RpcCallSpec,
#[serde(default)]
pub cancel: Option<RpcCallSpec>,
#[serde(default)]
pub session_close: Option<RpcCallSpec>,
#[serde(default)]
pub text_event_method: Option<String>,
#[serde(default)]
pub result_text_path: Option<String>,
#[serde(default)]
pub passthrough: Vec<String>,
}
impl RpcLaneSpec {
pub fn capabilities(&self) -> Capabilities {
Capabilities {
streaming: self.text_event_method.is_some(),
cancellation: self.cancel.is_some(),
session_resume: false,
permission_prompts: false,
detail: None,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(tag = "lane", rename_all = "kebab-case")]
pub enum LaneSpec {
Acp {
#[serde(default)]
mcp_servers: Vec<serde_json::Value>,
#[serde(default)]
prefer_load: bool,
},
Rpc(Box<RpcLaneSpec>),
Cli(CliLaneSpec),
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct CommandSpec {
pub program: String,
#[serde(default)]
pub args: Vec<String>,
#[serde(default)]
pub cwd: Option<PathBuf>,
#[serde(default)]
pub env: EnvPolicy,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AgentSpec {
pub id: String,
#[serde(default)]
pub client_name: Option<String>,
#[serde(flatten)]
pub lane: LaneSpec,
pub command: CommandSpec,
#[serde(default)]
pub max_output_bytes: Option<usize>,
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Capabilities {
pub streaming: bool,
pub cancellation: bool,
pub session_resume: bool,
pub permission_prompts: bool,
#[serde(default)]
pub detail: Option<serde_json::Value>,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "kebab-case")]
pub enum AgentEvent {
MessageChunk { text: String },
ToolCall {
summary: String,
raw: Option<serde_json::Value>,
},
Plan { raw: serde_json::Value },
Truncated,
Raw { raw: serde_json::Value },
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct TurnOutcome {
pub stop_reason: String,
pub text: String,
pub truncated: bool,
#[serde(default)]
pub exit_code: Option<i32>,
}
#[derive(Clone, Debug, Default)]
pub struct SessionRequest {
pub resume: Option<String>,
pub cwd: Option<PathBuf>,
pub env: BTreeMap<String, String>,
}