use serde::{Deserialize, Serialize};
use serde_json::Value;
use specta::Type;
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Type)]
pub struct ResponseError {
pub code: i64,
pub message: String,
#[serde(default)]
#[specta(type = Option<specta_typescript::Unknown>)]
pub data: Option<Value>,
}
impl ResponseError {
pub const RUNTIME: i64 = -32000;
pub const CONNECTION_LOST: i64 = -32001;
pub const METHOD_NOT_FOUND: i64 = -32601;
pub const INVALID_PARAMS: i64 = -32602;
pub fn new(code: i64, message: impl Into<String>) -> Self {
Self { code, message: message.into(), data: None }
}
pub fn runtime(message: impl Into<String>) -> Self {
Self::new(Self::RUNTIME, message)
}
pub fn connection_lost(message: impl Into<String>) -> Self {
Self::new(Self::CONNECTION_LOST, message)
}
pub fn method_not_found(method: &str) -> Self {
Self::new(Self::METHOD_NOT_FOUND, format!("unknown runtime method {method}"))
}
pub fn invalid_params(detail: impl std::fmt::Display) -> Self {
Self::new(Self::INVALID_PARAMS, format!("invalid params: {detail}"))
}
pub fn parse(error: &Value) -> Self {
serde_json::from_value::<Self>(error.clone()).unwrap_or_else(|_| Self::runtime(error.to_string()))
}
}
impl std::fmt::Display for ResponseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.message)
}
}
impl std::error::Error for ResponseError {}
impl From<String> for ResponseError {
fn from(message: String) -> Self {
Self::runtime(message)
}
}
impl From<&str> for ResponseError {
fn from(message: &str) -> Self {
Self::runtime(message)
}
}
impl From<ResponseError> for String {
fn from(error: ResponseError) -> Self {
error.message
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Type)]
pub struct PendingAgentRequest {
#[specta(type = specta_typescript::Unknown)]
pub id: Value,
pub method: String,
#[specta(type = specta_typescript::Unknown)]
pub params: Value,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Type)]
#[serde(rename_all = "snake_case")]
pub enum RunState {
Running,
RequiresAction,
Done,
Idle,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Type)]
#[serde(rename_all = "camelCase")]
pub struct SessionTask {
pub id: String,
pub title: String,
pub background: bool,
pub since: i64,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Type)]
#[serde(rename_all = "camelCase")]
pub struct SessionRunState {
pub session_id: String,
pub state: RunState,
pub reusable_draft: bool,
#[serde(rename = "owner")]
pub owner_id: String,
pub cwd: String,
pub title: String,
pub since: i64,
pub tasks: Vec<SessionTask>,
pub listening_ports: Vec<u16>,
pub execution_id: Option<String>,
pub execution_role: Option<String>,
pub last_activity_at: i64,
pub version: u64,
pub interruption: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Type)]
#[serde(rename_all = "camelCase")]
pub struct SessionLifecycleFrame {
pub sequence: u64,
pub recorded_at: i64,
pub line: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AttachInfo {
pub agent_id: String,
pub engine: String,
pub provider: String,
pub provider_revision: Option<u64>,
pub pending_agent_requests: Vec<PendingAgentRequest>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Type)]
#[serde(rename_all = "lowercase")]
pub enum FrameDirection {
In,
Out,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Type)]
#[serde(rename_all = "camelCase")]
pub struct SessionEventFrame {
pub sequence: u64,
pub recorded_at: i64,
pub direction: FrameDirection,
pub line: String,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AgentMetadata {
pub owner_id: String,
pub engine: String,
pub provider: String,
pub provider_revision: Option<u64>,
pub model: String,
pub connection: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "camelCase", rename_all_fields = "camelCase")]
pub enum RuntimeEvent {
Wire {
agent_id: String,
outbound: bool,
lines: Vec<String>,
},
Inbound {
agent_id: String,
lines: Vec<String>,
},
Outbound {
agent_id: String,
line: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
session_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
sequence: Option<u64>,
},
Stderr {
agent_id: String,
line: String,
},
Exit {
agent_id: String,
},
RunStates {
sessions: Vec<SessionRunState>,
},
AcpNotification {
agent_id: String,
method: String,
params: Value,
#[serde(default, skip_serializing_if = "Option::is_none")]
session_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
sequence: Option<u64>,
},
AcpAgentRequest {
agent_id: String,
id: Value,
method: String,
params: Value,
#[serde(default, skip_serializing_if = "Option::is_none")]
session_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
sequence: Option<u64>,
},
}