use serde::{Deserialize, Serialize};
use serde_json::Value;
use vtcode_exec_events::VersionedThreadEvent;
pub mod handoff_url;
pub mod slash_command;
pub const PROTOCOL_VERSION: ProtocolVersion = ProtocolVersion { major: 1, minor: 32 };
pub const MAX_FRAME_BYTES: usize = 1024 * 1024;
pub const DEFAULT_QUEUE_CAPACITY: usize = 128;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct ProtocolVersion {
pub major: u16,
pub minor: u16,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ProtocolRange {
pub minimum: ProtocolVersion,
pub maximum: ProtocolVersion,
}
impl ProtocolRange {
pub fn negotiate(self, server: ProtocolVersion) -> Result<ProtocolVersion, ProtocolError> {
if self.minimum.major != self.maximum.major || server.major != self.minimum.major {
return Err(ProtocolError::IncompatibleVersion { client: self, server });
}
if server < self.minimum || server > self.maximum {
return Err(ProtocolError::IncompatibleVersion { client: self, server });
}
Ok(server)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(untagged)]
pub enum RequestId {
String(String),
Number(i64),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Request {
pub jsonrpc: String,
pub id: RequestId,
pub method: String,
#[serde(default)]
pub params: Value,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Notification {
pub jsonrpc: String,
pub method: String,
#[serde(default)]
pub params: Value,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Response {
pub jsonrpc: String,
pub id: RequestId,
pub result: Value,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ErrorResponse {
pub jsonrpc: String,
pub id: Option<RequestId>,
pub error: RpcError,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RpcError {
pub code: i32,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub data: Option<Value>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InitializeParams {
pub protocol: ProtocolRange,
pub client_name: String,
pub client_version: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InitializeResult {
pub protocol: ProtocolVersion,
pub workspace: String,
pub server_version: String,
#[serde(default)]
pub slash_commands: Vec<SlashCommandInfo>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SlashCommandInfo {
pub name: String,
pub description: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ThreadEventNotification {
pub session_id: String,
pub turn_id: Option<String>,
pub event: VersionedThreadEvent,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PrimaryAgentChanged {
pub session_id: String,
pub display_name: Option<String>,
}
pub mod method;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ProtocolError {
FrameTooLarge {
limit: usize,
},
IncompatibleVersion {
client: ProtocolRange,
server: ProtocolVersion,
},
StaleInteraction,
UnknownInteraction,
}
impl std::fmt::Display for ProtocolError {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::FrameTooLarge { limit } => write!(formatter, "JSON-RPC frame exceeds {limit} bytes"),
Self::IncompatibleVersion { client, server } => write!(
formatter,
"incompatible desktop protocol versions: client {}.{}..={}.{}, server {}.{}",
client.minimum.major,
client.minimum.minor,
client.maximum.major,
client.maximum.minor,
server.major,
server.minor
),
Self::StaleInteraction => {
formatter.write_str("interaction response does not match the active session and turn")
}
Self::UnknownInteraction => formatter.write_str("interaction is no longer pending"),
}
}
}
impl std::error::Error for ProtocolError {}
pub mod interaction_registry;
pub use interaction_registry::InteractionRegistry;
pub mod frames;
pub use frames::{read_frame, write_frame};
pub fn success_response(id: RequestId, result: impl Serialize) -> Result<Response, serde_json::Error> {
Ok(Response {
jsonrpc: "2.0".to_string(),
id,
result: serde_json::to_value(result)?,
})
}
pub fn error_response(id: Option<RequestId>, code: i32, message: impl Into<String>) -> ErrorResponse {
ErrorResponse {
jsonrpc: "2.0".to_string(),
id,
error: RpcError { code, message: message.into(), data: None },
}
}
pub mod live_events;
pub use live_events::{LiveEventReceiver, LiveEventSendError, LiveEventSender, live_event_channel};
pub mod secret;
pub use secret::{SecretStoreInfo, SecretStoreParams, SecretStoreResult};
pub mod storage;
pub use storage::{SessionDeleteResult, StorageSummary, WorktreeEntry, WorktreeRemoveParams, WorktreeRemoveResult};
pub mod composer;
pub use composer::{ComposerResource, ComposerResourceKind};
pub mod config;
pub use config::{
ConfigOpenFileParams, ConfigSetting, ConfigSettingsSnapshot, ConfigSettingsWriteParams, ConfigSettingsWriteResult,
ConfigTarget, ConfigTargetVersion, ConfigurationSnapshot, ModelCapabilities, ModelInfo, ModelSelectParams,
SharedUiPreferences,
};
pub mod interaction;
pub use interaction::{
InteractionChoice, InteractionHotkey, InteractionKind, InteractionRequest, InteractionResponse, InteractionStep,
};
pub mod review;
pub use review::{
MAX_MCP_APP_BODY_BYTES, McpAppReadParams, McpAppReadResult, WorkspaceReviewParams, WorkspaceReviewResult,
};
pub mod session;
pub use session::{
AgentModeInfo, MAX_RECENT_PROMPTS, MAX_SESSION_HISTORY_PAGE_MESSAGES, MAX_SESSION_LIST_LIMIT,
MAX_SESSION_OUTPUT_PAGE_BYTES, PermissionMode, RESTORED_OUTPUT_TRUNCATION_SUFFIX, RecentPrompt,
RecentPromptsParams, RecentPromptsResult, SessionCreateParams, SessionHistoryMessage, SessionHistoryPage,
SessionHistoryParams, SessionIdParams, SessionListParams, SessionLoadResult, SessionOptionsSelectParams,
SessionOptionsSnapshot, SessionOutputCursor, SessionOutputPage, SessionOutputReadParams, SessionSummary,
ThemePreferenceSelectParams,
};
pub mod turn;
pub use turn::{
MAX_QUEUE_ENTRY_ID_LEN, MAX_QUEUE_PREVIEW_CHARS, MAX_QUEUE_STATUS_PREVIEWS, SESSION_START_TURN_ID,
STARTUP_INTERACTION_PENDING_ERROR, TurnAccepted, TurnAttachment, TurnCancelParams, TurnQueueRemoveParams,
TurnQueueRemoveResult, TurnQueueStatus, TurnQueueTakeLatestParams, TurnQueueTakeLatestResult, TurnSubmitParams,
TurnUndoFile, TurnUndoParams, TurnUndoResult, queue_preview_label,
};
pub mod update;
pub use update::UpdateStatusSnapshot;
pub mod webmcp;
pub use webmcp::{WebmcpBridgeStatus, WebmcpPairParams, WebmcpPairResult};
#[cfg(test)]
mod tests;