use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConnectionState {
Disconnected,
Connecting,
Connected,
Error,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum ToolResultType {
Success,
Failure,
Rejected,
Denied,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ToolBinaryResult {
pub data: String,
pub mime_type: String,
#[serde(rename = "type")]
pub result_type: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ToolResultObject {
pub text_result_for_llm: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub binary_results_for_llm: Option<Vec<ToolBinaryResult>>,
pub result_type: ToolResultType,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub session_log: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_telemetry: Option<HashMap<String, serde_json::Value>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ToolResult {
Text(String),
Object(ToolResultObject),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ToolInvocation {
pub session_id: String,
pub tool_call_id: String,
pub tool_name: String,
pub arguments: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ToolDefinition {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parameters: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ToolCallRequestPayload {
pub session_id: String,
pub tool_call_id: String,
pub tool_name: String,
pub arguments: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolCallResponsePayload {
pub result: ToolResultObject,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum SystemPromptSection {
#[serde(rename = "identity")]
Identity,
#[serde(rename = "tone")]
Tone,
#[serde(rename = "tool_efficiency")]
ToolEfficiency,
#[serde(rename = "environment_context")]
EnvironmentContext,
#[serde(rename = "code_change_rules")]
CodeChangeRules,
#[serde(rename = "guidelines")]
Guidelines,
#[serde(rename = "safety")]
Safety,
#[serde(rename = "tool_instructions")]
ToolInstructions,
#[serde(rename = "custom_instructions")]
CustomInstructions,
#[serde(rename = "last_instructions")]
LastInstructions,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum SectionOverrideAction {
Replace,
Remove,
Append,
Prepend,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SectionOverride {
pub action: SectionOverrideAction,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "mode", rename_all = "camelCase")]
pub enum SystemMessageConfig {
#[serde(rename = "append")]
Append {
#[serde(skip_serializing_if = "Option::is_none")]
content: Option<String>,
},
#[serde(rename = "replace")]
Replace { content: String },
#[serde(rename = "customize")]
Customize {
#[serde(skip_serializing_if = "Option::is_none")]
sections: Option<HashMap<SystemPromptSection, SectionOverride>>,
#[serde(skip_serializing_if = "Option::is_none")]
content: Option<String>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum PermissionKind {
Shell,
Write,
Mcp,
Read,
Url,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PermissionRequest {
pub kind: PermissionKind,
#[serde(skip_serializing_if = "Option::is_none")]
pub tool_call_id: Option<String>,
#[serde(flatten)]
pub extra: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub enum PermissionResultKind {
Approved,
DeniedByRules,
DeniedNoApprovalRuleAndCouldNotRequestFromUser,
DeniedInteractivelyByUser,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PermissionRequestResult {
pub kind: PermissionResultKind,
#[serde(skip_serializing_if = "Option::is_none")]
pub rules: Option<Vec<serde_json::Value>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UserInputRequest {
pub question: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub choices: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub allow_freeform: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UserInputResponse {
pub answer: String,
pub was_freeform: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BaseHookInput {
pub timestamp: f64,
pub cwd: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PreToolUseHookInput {
pub timestamp: f64,
pub cwd: String,
pub tool_name: String,
pub tool_args: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct PreToolUseHookOutput {
#[serde(skip_serializing_if = "Option::is_none")]
pub permission_decision: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub permission_decision_reason: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub modified_args: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub additional_context: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub suppress_output: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PostToolUseHookInput {
pub timestamp: f64,
pub cwd: String,
pub tool_name: String,
pub tool_args: serde_json::Value,
pub tool_result: ToolResultObject,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct PostToolUseHookOutput {
#[serde(skip_serializing_if = "Option::is_none")]
pub modified_result: Option<ToolResultObject>,
#[serde(skip_serializing_if = "Option::is_none")]
pub additional_context: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub suppress_output: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserPromptSubmittedHookInput {
pub timestamp: f64,
pub cwd: String,
pub prompt: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct UserPromptSubmittedHookOutput {
#[serde(skip_serializing_if = "Option::is_none")]
pub modified_prompt: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub additional_context: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub suppress_output: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionStartHookInput {
pub timestamp: f64,
pub cwd: String,
pub source: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub initial_prompt: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct SessionStartHookOutput {
#[serde(skip_serializing_if = "Option::is_none")]
pub additional_context: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub modified_config: Option<HashMap<String, serde_json::Value>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionEndHookInput {
pub timestamp: f64,
pub cwd: String,
pub reason: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub final_message: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct SessionEndHookOutput {
#[serde(skip_serializing_if = "Option::is_none")]
pub suppress_output: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cleanup_actions: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub session_summary: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ErrorOccurredHookInput {
pub timestamp: f64,
pub cwd: String,
pub error: String,
pub error_context: String,
pub recoverable: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct ErrorOccurredHookOutput {
#[serde(skip_serializing_if = "Option::is_none")]
pub suppress_output: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error_handling: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub retry_count: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub user_notification: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct McpLocalServerConfig {
pub tools: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "type")]
pub server_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub timeout: Option<u64>,
pub command: String,
pub args: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub env: Option<HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cwd: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct McpRemoteServerConfig {
pub tools: Vec<String>,
#[serde(rename = "type")]
pub server_type: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub timeout: Option<u64>,
pub url: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub headers: Option<HashMap<String, String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum McpServerConfig {
Local(McpLocalServerConfig),
Remote(McpRemoteServerConfig),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CustomAgentConfig {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub display_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tools: Option<Vec<String>>,
pub prompt: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub mcp_servers: Option<HashMap<String, McpServerConfig>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub infer: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub skills: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InfiniteSessionConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub enabled: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub background_compaction_threshold: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub buffer_exhaustion_threshold: Option<f64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AzureProviderOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub api_version: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ProviderConfig {
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "type")]
pub provider_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub wire_api: Option<String>,
pub base_url: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub api_key: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bearer_token: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub azure: Option<AzureProviderOptions>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ReasoningEffort {
Low,
Medium,
High,
Xhigh,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionConfig {
#[serde(skip_serializing_if = "Option::is_none")]
pub session_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reasoning_effort: Option<ReasoningEffort>,
#[serde(skip_serializing_if = "Option::is_none")]
pub config_dir: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tools: Option<Vec<ToolDefinition>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub system_message: Option<SystemMessageConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub available_tools: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub excluded_tools: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub provider: Option<ProviderConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub working_directory: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub streaming: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub include_sub_agent_streaming_events: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mcp_servers: Option<HashMap<String, McpServerConfig>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub custom_agents: Option<Vec<CustomAgentConfig>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub skill_directories: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub disabled_skills: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub infinite_sessions: Option<InfiniteSessionConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub model_capabilities: Option<HashMap<String, serde_json::Value>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub enable_config_discovery: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub github_token: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub request_permission: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub request_user_input: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub hooks: Option<bool>,
#[serde(skip)]
pub commands: Option<Vec<CommandDefinition>>,
#[serde(skip)]
pub on_elicitation_request: Option<ElicitationHandlerFn>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CommandContext {
pub session_id: String,
pub command: String,
pub command_name: String,
pub args: String,
}
#[derive(Clone)]
pub struct CommandHandlerFn(
pub Arc<dyn Fn(CommandContext) -> Result<(), Box<dyn std::error::Error + Send + Sync>> + Send + Sync>,
);
impl std::fmt::Debug for CommandHandlerFn {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("<command_handler>")
}
}
#[derive(Debug, Clone)]
pub struct CommandDefinition {
pub name: String,
pub description: Option<String>,
pub handler: CommandHandlerFn,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ElicitationContext {
pub session_id: String,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub requested_schema: Option<HashMap<String, serde_json::Value>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mode: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub elicitation_source: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ElicitationResult {
pub action: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<HashMap<String, serde_json::Value>>,
}
#[derive(Clone)]
pub struct ElicitationHandlerFn(
pub Arc<dyn Fn(ElicitationContext) -> Result<ElicitationResult, Box<dyn std::error::Error + Send + Sync>> + Send + Sync>,
);
impl std::fmt::Debug for ElicitationHandlerFn {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("<elicitation_handler>")
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ResumeSessionConfig {
pub session_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reasoning_effort: Option<ReasoningEffort>,
#[serde(skip_serializing_if = "Option::is_none")]
pub config_dir: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tools: Option<Vec<ToolDefinition>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub system_message: Option<SystemMessageConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub available_tools: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub excluded_tools: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub provider: Option<ProviderConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub working_directory: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub streaming: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub include_sub_agent_streaming_events: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mcp_servers: Option<HashMap<String, McpServerConfig>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub custom_agents: Option<Vec<CustomAgentConfig>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub skill_directories: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub disabled_skills: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub infinite_sessions: Option<InfiniteSessionConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub model_capabilities: Option<HashMap<String, serde_json::Value>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub enable_config_discovery: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub github_token: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub disable_resume: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub request_permission: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub request_user_input: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub hooks: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FileAttachment {
pub path: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub display_name: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DirectoryAttachment {
pub path: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub display_name: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SelectionRange {
pub start: Position,
pub end: Position,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Position {
pub line: u32,
pub character: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SelectionAttachment {
pub file_path: String,
pub display_name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub selection: Option<SelectionRange>,
#[serde(skip_serializing_if = "Option::is_none")]
pub text: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "camelCase")]
pub enum Attachment {
File(FileAttachment),
Directory(DirectoryAttachment),
Selection(SelectionAttachment),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ResponseFormat {
#[serde(rename = "text")]
Text,
#[serde(rename = "image")]
Image,
#[serde(rename = "json_object")]
JsonObject,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ImageOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub size: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub quality: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub style: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AssistantImageData {
pub format: String,
pub base64: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub revised_prompt: Option<String>,
pub width: u32,
pub height: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "camelCase")]
pub enum ContentBlock {
#[serde(rename = "text")]
Text { text: String },
#[serde(rename = "image")]
Image { image: AssistantImageData },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MessageOptions {
pub prompt: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub attachments: Option<Vec<Attachment>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mode: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub response_format: Option<ResponseFormat>,
#[serde(skip_serializing_if = "Option::is_none")]
pub image_options: Option<ImageOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
pub request_headers: Option<HashMap<String, String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionEvent {
pub id: String,
pub timestamp: String,
#[serde(rename = "parentId")]
pub parent_id: Option<String>,
#[serde(default)]
pub ephemeral: bool,
#[serde(rename = "type")]
pub event_type: String,
pub data: serde_json::Value,
}
impl SessionEvent {
pub fn is_assistant_message(&self) -> bool {
self.event_type == "assistant.message"
}
pub fn is_session_idle(&self) -> bool {
self.event_type == "session.idle"
}
pub fn is_session_error(&self) -> bool {
self.event_type == "session.error"
}
pub fn assistant_message_content(&self) -> Option<&str> {
if self.is_assistant_message() {
self.data.get("content").and_then(|v| v.as_str())
} else {
None
}
}
pub fn error_message(&self) -> Option<&str> {
if self.is_session_error() {
self.data.get("message").and_then(|v| v.as_str())
} else {
None
}
}
pub fn error_stack(&self) -> Option<&str> {
if self.is_session_error() {
self.data.get("stack").and_then(|v| v.as_str())
} else {
None
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PingResponse {
pub message: String,
pub timestamp: f64,
#[serde(skip_serializing_if = "Option::is_none")]
pub protocol_version: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetStatusResponse {
pub version: String,
pub protocol_version: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetAuthStatusResponse {
pub is_authenticated: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub auth_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub host: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub login: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status_message: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisionLimits {
pub supported_media_types: Vec<String>,
pub max_prompt_images: u32,
pub max_prompt_image_size: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ModelLimits {
#[serde(skip_serializing_if = "Option::is_none")]
pub max_prompt_tokens: Option<u64>,
pub max_context_window_tokens: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub vision: Option<VisionLimits>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ModelSupports {
pub vision: bool,
pub reasoning_effort: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelCapabilities {
pub supports: ModelSupports,
pub limits: ModelLimits,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelPolicy {
pub state: String,
pub terms: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelBilling {
pub multiplier: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ModelInfo {
pub id: String,
pub name: String,
pub capabilities: ModelCapabilities,
#[serde(skip_serializing_if = "Option::is_none")]
pub policy: Option<ModelPolicy>,
#[serde(skip_serializing_if = "Option::is_none")]
pub billing: Option<ModelBilling>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supported_reasoning_efforts: Option<Vec<ReasoningEffort>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub default_reasoning_effort: Option<ReasoningEffort>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionMetadata {
pub session_id: String,
pub start_time: String,
pub modified_time: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub summary: Option<String>,
pub is_remote: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum SessionLifecycleEventType {
#[serde(rename = "session.created")]
SessionCreated,
#[serde(rename = "session.deleted")]
SessionDeleted,
#[serde(rename = "session.updated")]
SessionUpdated,
#[serde(rename = "session.foreground")]
SessionForeground,
#[serde(rename = "session.background")]
SessionBackground,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LifecycleMetadata {
pub start_time: String,
pub modified_time: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub summary: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionLifecycleEvent {
#[serde(rename = "type")]
pub event_type: SessionLifecycleEventType,
pub session_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<LifecycleMetadata>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ForegroundSessionInfo {
#[serde(skip_serializing_if = "Option::is_none")]
pub session_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub workspace_path: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionFsConfig {
pub initial_cwd: String,
pub session_state_path: String,
pub conventions: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionFsFileInfo {
pub name: String,
pub size: i64,
pub is_directory: bool,
pub is_file: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_at: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub modified_at: Option<String>,
}
pub trait SessionFsProvider: Send + Sync {
fn read_file(&self, session_id: &str, path: &str) -> Result<String, Box<dyn std::error::Error + Send + Sync>>;
fn write_file(&self, session_id: &str, path: &str, content: &str) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
fn append_file(&self, session_id: &str, path: &str, content: &str) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
fn exists(&self, session_id: &str, path: &str) -> Result<bool, Box<dyn std::error::Error + Send + Sync>>;
fn stat(&self, session_id: &str, path: &str) -> Result<SessionFsFileInfo, Box<dyn std::error::Error + Send + Sync>>;
fn mkdir(&self, session_id: &str, path: &str, recursive: bool) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
fn readdir(&self, session_id: &str, path: &str) -> Result<Vec<String>, Box<dyn std::error::Error + Send + Sync>>;
fn readdir_with_types(&self, session_id: &str, path: &str) -> Result<Vec<SessionFsFileInfo>, Box<dyn std::error::Error + Send + Sync>>;
fn rm(&self, session_id: &str, path: &str, recursive: bool) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
fn rename(&self, session_id: &str, old_path: &str, new_path: &str) -> Result<(), Box<dyn std::error::Error + Send + Sync>>;
}
#[derive(Debug, Clone)]
pub struct CopilotClientOptions {
pub cli_path: Option<String>,
pub cli_args: Vec<String>,
pub cwd: Option<String>,
pub port: u16,
pub use_stdio: bool,
pub cli_url: Option<String>,
pub log_level: String,
pub auto_start: bool,
pub auto_restart: bool,
pub env: Option<HashMap<String, String>>,
pub github_token: Option<String>,
pub use_logged_in_user: Option<bool>,
pub session_idle_timeout_seconds: Option<u64>,
pub session_fs: Option<SessionFsConfig>,
}
impl Default for CopilotClientOptions {
fn default() -> Self {
Self {
cli_path: None,
cli_args: Vec::new(),
cwd: None,
port: 0,
use_stdio: true,
cli_url: None,
log_level: "info".to_string(),
auto_start: true,
auto_restart: true,
env: None,
github_token: None,
use_logged_in_user: None,
session_idle_timeout_seconds: None,
session_fs: None,
}
}
}