Skip to main content

copilot_sdk_supercharged/
types.rs

1// Copyright (c) Microsoft Corporation. All rights reserved.
2
3//! Type definitions for the Copilot SDK.
4//!
5//! This module contains all the data structures used to communicate with the
6//! Copilot CLI server via JSON-RPC 2.0.
7
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10
11// ============================================================================
12// Connection State
13// ============================================================================
14
15/// Represents the connection state of the client.
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum ConnectionState {
18    Disconnected,
19    Connecting,
20    Connected,
21    Error,
22}
23
24// ============================================================================
25// Tool Types
26// ============================================================================
27
28/// Result type for tool execution.
29#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
30#[serde(rename_all = "camelCase")]
31pub enum ToolResultType {
32    Success,
33    Failure,
34    Rejected,
35    Denied,
36}
37
38/// Binary result from a tool execution.
39#[derive(Debug, Clone, Serialize, Deserialize)]
40#[serde(rename_all = "camelCase")]
41pub struct ToolBinaryResult {
42    pub data: String,
43    pub mime_type: String,
44    #[serde(rename = "type")]
45    pub result_type: String,
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub description: Option<String>,
48}
49
50/// Structured tool result with metadata.
51#[derive(Debug, Clone, Serialize, Deserialize)]
52#[serde(rename_all = "camelCase")]
53pub struct ToolResultObject {
54    pub text_result_for_llm: String,
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub binary_results_for_llm: Option<Vec<ToolBinaryResult>>,
57    pub result_type: ToolResultType,
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub error: Option<String>,
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub session_log: Option<String>,
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub tool_telemetry: Option<HashMap<String, serde_json::Value>>,
64}
65
66/// A tool result can be either a simple string or a structured object.
67#[derive(Debug, Clone, Serialize, Deserialize)]
68#[serde(untagged)]
69pub enum ToolResult {
70    Text(String),
71    Object(ToolResultObject),
72}
73
74/// Information about a tool invocation.
75#[derive(Debug, Clone, Serialize, Deserialize)]
76#[serde(rename_all = "camelCase")]
77pub struct ToolInvocation {
78    pub session_id: String,
79    pub tool_call_id: String,
80    pub tool_name: String,
81    pub arguments: serde_json::Value,
82}
83
84/// Definition of a tool that can be exposed to the Copilot CLI.
85#[derive(Debug, Clone, Serialize, Deserialize)]
86#[serde(rename_all = "camelCase")]
87pub struct ToolDefinition {
88    pub name: String,
89    #[serde(skip_serializing_if = "Option::is_none")]
90    pub description: Option<String>,
91    #[serde(skip_serializing_if = "Option::is_none")]
92    pub parameters: Option<serde_json::Value>,
93}
94
95/// Payload sent by the server when requesting a tool call.
96#[derive(Debug, Clone, Serialize, Deserialize)]
97#[serde(rename_all = "camelCase")]
98pub struct ToolCallRequestPayload {
99    pub session_id: String,
100    pub tool_call_id: String,
101    pub tool_name: String,
102    pub arguments: serde_json::Value,
103}
104
105/// Response payload for a tool call.
106#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct ToolCallResponsePayload {
108    pub result: ToolResultObject,
109}
110
111// ============================================================================
112// System Message Configuration
113// ============================================================================
114
115/// System message configuration for session creation.
116#[derive(Debug, Clone, Serialize, Deserialize)]
117#[serde(tag = "mode", rename_all = "camelCase")]
118pub enum SystemMessageConfig {
119    /// Append mode: SDK foundation + optional custom content.
120    #[serde(rename = "append")]
121    Append {
122        #[serde(skip_serializing_if = "Option::is_none")]
123        content: Option<String>,
124    },
125    /// Replace mode: Full control, caller provides entire system message.
126    #[serde(rename = "replace")]
127    Replace { content: String },
128}
129
130// ============================================================================
131// Permission Types
132// ============================================================================
133
134/// Kind of permission being requested.
135#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
136#[serde(rename_all = "lowercase")]
137pub enum PermissionKind {
138    Shell,
139    Write,
140    Mcp,
141    Read,
142    Url,
143}
144
145/// Permission request from the server.
146#[derive(Debug, Clone, Serialize, Deserialize)]
147#[serde(rename_all = "camelCase")]
148pub struct PermissionRequest {
149    pub kind: PermissionKind,
150    #[serde(skip_serializing_if = "Option::is_none")]
151    pub tool_call_id: Option<String>,
152    /// Additional fields from the server.
153    #[serde(flatten)]
154    pub extra: HashMap<String, serde_json::Value>,
155}
156
157/// Result of a permission request.
158#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
159#[serde(rename_all = "kebab-case")]
160pub enum PermissionResultKind {
161    Approved,
162    DeniedByRules,
163    DeniedNoApprovalRuleAndCouldNotRequestFromUser,
164    DeniedInteractivelyByUser,
165}
166
167/// Permission result returned by the handler.
168#[derive(Debug, Clone, Serialize, Deserialize)]
169pub struct PermissionRequestResult {
170    pub kind: PermissionResultKind,
171    #[serde(skip_serializing_if = "Option::is_none")]
172    pub rules: Option<Vec<serde_json::Value>>,
173}
174
175// ============================================================================
176// User Input Types
177// ============================================================================
178
179/// Request for user input from the agent (enables ask_user tool).
180#[derive(Debug, Clone, Serialize, Deserialize)]
181#[serde(rename_all = "camelCase")]
182pub struct UserInputRequest {
183    pub question: String,
184    #[serde(skip_serializing_if = "Option::is_none")]
185    pub choices: Option<Vec<String>>,
186    #[serde(skip_serializing_if = "Option::is_none")]
187    pub allow_freeform: Option<bool>,
188}
189
190/// Response to a user input request.
191#[derive(Debug, Clone, Serialize, Deserialize)]
192#[serde(rename_all = "camelCase")]
193pub struct UserInputResponse {
194    pub answer: String,
195    pub was_freeform: bool,
196}
197
198// ============================================================================
199// Hook Types
200// ============================================================================
201
202/// Base fields common to all hook inputs.
203#[derive(Debug, Clone, Serialize, Deserialize)]
204pub struct BaseHookInput {
205    pub timestamp: f64,
206    pub cwd: String,
207}
208
209/// Input for pre-tool-use hook.
210#[derive(Debug, Clone, Serialize, Deserialize)]
211#[serde(rename_all = "camelCase")]
212pub struct PreToolUseHookInput {
213    pub timestamp: f64,
214    pub cwd: String,
215    pub tool_name: String,
216    pub tool_args: serde_json::Value,
217}
218
219/// Output for pre-tool-use hook.
220#[derive(Debug, Clone, Serialize, Deserialize, Default)]
221#[serde(rename_all = "camelCase")]
222pub struct PreToolUseHookOutput {
223    #[serde(skip_serializing_if = "Option::is_none")]
224    pub permission_decision: Option<String>,
225    #[serde(skip_serializing_if = "Option::is_none")]
226    pub permission_decision_reason: Option<String>,
227    #[serde(skip_serializing_if = "Option::is_none")]
228    pub modified_args: Option<serde_json::Value>,
229    #[serde(skip_serializing_if = "Option::is_none")]
230    pub additional_context: Option<String>,
231    #[serde(skip_serializing_if = "Option::is_none")]
232    pub suppress_output: Option<bool>,
233}
234
235/// Input for post-tool-use hook.
236#[derive(Debug, Clone, Serialize, Deserialize)]
237#[serde(rename_all = "camelCase")]
238pub struct PostToolUseHookInput {
239    pub timestamp: f64,
240    pub cwd: String,
241    pub tool_name: String,
242    pub tool_args: serde_json::Value,
243    pub tool_result: ToolResultObject,
244}
245
246/// Output for post-tool-use hook.
247#[derive(Debug, Clone, Serialize, Deserialize, Default)]
248#[serde(rename_all = "camelCase")]
249pub struct PostToolUseHookOutput {
250    #[serde(skip_serializing_if = "Option::is_none")]
251    pub modified_result: Option<ToolResultObject>,
252    #[serde(skip_serializing_if = "Option::is_none")]
253    pub additional_context: Option<String>,
254    #[serde(skip_serializing_if = "Option::is_none")]
255    pub suppress_output: Option<bool>,
256}
257
258/// Input for user-prompt-submitted hook.
259#[derive(Debug, Clone, Serialize, Deserialize)]
260pub struct UserPromptSubmittedHookInput {
261    pub timestamp: f64,
262    pub cwd: String,
263    pub prompt: String,
264}
265
266/// Output for user-prompt-submitted hook.
267#[derive(Debug, Clone, Serialize, Deserialize, Default)]
268#[serde(rename_all = "camelCase")]
269pub struct UserPromptSubmittedHookOutput {
270    #[serde(skip_serializing_if = "Option::is_none")]
271    pub modified_prompt: Option<String>,
272    #[serde(skip_serializing_if = "Option::is_none")]
273    pub additional_context: Option<String>,
274    #[serde(skip_serializing_if = "Option::is_none")]
275    pub suppress_output: Option<bool>,
276}
277
278/// Input for session-start hook.
279#[derive(Debug, Clone, Serialize, Deserialize)]
280#[serde(rename_all = "camelCase")]
281pub struct SessionStartHookInput {
282    pub timestamp: f64,
283    pub cwd: String,
284    pub source: String,
285    #[serde(skip_serializing_if = "Option::is_none")]
286    pub initial_prompt: Option<String>,
287}
288
289/// Output for session-start hook.
290#[derive(Debug, Clone, Serialize, Deserialize, Default)]
291#[serde(rename_all = "camelCase")]
292pub struct SessionStartHookOutput {
293    #[serde(skip_serializing_if = "Option::is_none")]
294    pub additional_context: Option<String>,
295    #[serde(skip_serializing_if = "Option::is_none")]
296    pub modified_config: Option<HashMap<String, serde_json::Value>>,
297}
298
299/// Input for session-end hook.
300#[derive(Debug, Clone, Serialize, Deserialize)]
301#[serde(rename_all = "camelCase")]
302pub struct SessionEndHookInput {
303    pub timestamp: f64,
304    pub cwd: String,
305    pub reason: String,
306    #[serde(skip_serializing_if = "Option::is_none")]
307    pub final_message: Option<String>,
308    #[serde(skip_serializing_if = "Option::is_none")]
309    pub error: Option<String>,
310}
311
312/// Output for session-end hook.
313#[derive(Debug, Clone, Serialize, Deserialize, Default)]
314#[serde(rename_all = "camelCase")]
315pub struct SessionEndHookOutput {
316    #[serde(skip_serializing_if = "Option::is_none")]
317    pub suppress_output: Option<bool>,
318    #[serde(skip_serializing_if = "Option::is_none")]
319    pub cleanup_actions: Option<Vec<String>>,
320    #[serde(skip_serializing_if = "Option::is_none")]
321    pub session_summary: Option<String>,
322}
323
324/// Input for error-occurred hook.
325#[derive(Debug, Clone, Serialize, Deserialize)]
326#[serde(rename_all = "camelCase")]
327pub struct ErrorOccurredHookInput {
328    pub timestamp: f64,
329    pub cwd: String,
330    pub error: String,
331    pub error_context: String,
332    pub recoverable: bool,
333}
334
335/// Output for error-occurred hook.
336#[derive(Debug, Clone, Serialize, Deserialize, Default)]
337#[serde(rename_all = "camelCase")]
338pub struct ErrorOccurredHookOutput {
339    #[serde(skip_serializing_if = "Option::is_none")]
340    pub suppress_output: Option<bool>,
341    #[serde(skip_serializing_if = "Option::is_none")]
342    pub error_handling: Option<String>,
343    #[serde(skip_serializing_if = "Option::is_none")]
344    pub retry_count: Option<u32>,
345    #[serde(skip_serializing_if = "Option::is_none")]
346    pub user_notification: Option<String>,
347}
348
349// ============================================================================
350// MCP Server Configuration Types
351// ============================================================================
352
353/// Configuration for a local/stdio MCP server.
354#[derive(Debug, Clone, Serialize, Deserialize)]
355#[serde(rename_all = "camelCase")]
356pub struct McpLocalServerConfig {
357    pub tools: Vec<String>,
358    #[serde(skip_serializing_if = "Option::is_none")]
359    #[serde(rename = "type")]
360    pub server_type: Option<String>,
361    #[serde(skip_serializing_if = "Option::is_none")]
362    pub timeout: Option<u64>,
363    pub command: String,
364    pub args: Vec<String>,
365    #[serde(skip_serializing_if = "Option::is_none")]
366    pub env: Option<HashMap<String, String>>,
367    #[serde(skip_serializing_if = "Option::is_none")]
368    pub cwd: Option<String>,
369}
370
371/// Configuration for a remote MCP server (HTTP or SSE).
372#[derive(Debug, Clone, Serialize, Deserialize)]
373#[serde(rename_all = "camelCase")]
374pub struct McpRemoteServerConfig {
375    pub tools: Vec<String>,
376    #[serde(rename = "type")]
377    pub server_type: String,
378    #[serde(skip_serializing_if = "Option::is_none")]
379    pub timeout: Option<u64>,
380    pub url: String,
381    #[serde(skip_serializing_if = "Option::is_none")]
382    pub headers: Option<HashMap<String, String>>,
383}
384
385/// Union type for MCP server configurations.
386#[derive(Debug, Clone, Serialize, Deserialize)]
387#[serde(untagged)]
388pub enum McpServerConfig {
389    Local(McpLocalServerConfig),
390    Remote(McpRemoteServerConfig),
391}
392
393// ============================================================================
394// Custom Agent Configuration
395// ============================================================================
396
397/// Configuration for a custom agent.
398#[derive(Debug, Clone, Serialize, Deserialize)]
399#[serde(rename_all = "camelCase")]
400pub struct CustomAgentConfig {
401    pub name: String,
402    #[serde(skip_serializing_if = "Option::is_none")]
403    pub display_name: Option<String>,
404    #[serde(skip_serializing_if = "Option::is_none")]
405    pub description: Option<String>,
406    #[serde(skip_serializing_if = "Option::is_none")]
407    pub tools: Option<Vec<String>>,
408    pub prompt: String,
409    #[serde(skip_serializing_if = "Option::is_none")]
410    pub mcp_servers: Option<HashMap<String, McpServerConfig>>,
411    #[serde(skip_serializing_if = "Option::is_none")]
412    pub infer: Option<bool>,
413}
414
415// ============================================================================
416// Infinite Session Configuration
417// ============================================================================
418
419/// Configuration for infinite sessions with automatic context compaction.
420#[derive(Debug, Clone, Serialize, Deserialize)]
421#[serde(rename_all = "camelCase")]
422pub struct InfiniteSessionConfig {
423    #[serde(skip_serializing_if = "Option::is_none")]
424    pub enabled: Option<bool>,
425    #[serde(skip_serializing_if = "Option::is_none")]
426    pub background_compaction_threshold: Option<f64>,
427    #[serde(skip_serializing_if = "Option::is_none")]
428    pub buffer_exhaustion_threshold: Option<f64>,
429}
430
431// ============================================================================
432// Provider Configuration
433// ============================================================================
434
435/// Azure-specific provider options.
436#[derive(Debug, Clone, Serialize, Deserialize)]
437#[serde(rename_all = "camelCase")]
438pub struct AzureProviderOptions {
439    #[serde(skip_serializing_if = "Option::is_none")]
440    pub api_version: Option<String>,
441}
442
443/// Configuration for a custom API provider (BYOK).
444#[derive(Debug, Clone, Serialize, Deserialize)]
445#[serde(rename_all = "camelCase")]
446pub struct ProviderConfig {
447    #[serde(skip_serializing_if = "Option::is_none")]
448    #[serde(rename = "type")]
449    pub provider_type: Option<String>,
450    #[serde(skip_serializing_if = "Option::is_none")]
451    pub wire_api: Option<String>,
452    pub base_url: String,
453    #[serde(skip_serializing_if = "Option::is_none")]
454    pub api_key: Option<String>,
455    #[serde(skip_serializing_if = "Option::is_none")]
456    pub bearer_token: Option<String>,
457    #[serde(skip_serializing_if = "Option::is_none")]
458    pub azure: Option<AzureProviderOptions>,
459}
460
461// ============================================================================
462// Reasoning Effort
463// ============================================================================
464
465/// Valid reasoning effort levels for models that support it.
466#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
467#[serde(rename_all = "lowercase")]
468pub enum ReasoningEffort {
469    Low,
470    Medium,
471    High,
472    Xhigh,
473}
474
475// ============================================================================
476// Session Configuration
477// ============================================================================
478
479/// Configuration for creating a session.
480///
481/// This struct holds the parameters sent to the server via `session.create`.
482/// Tool handlers, permission handlers, user input handlers, and hooks
483/// are registered separately on the Rust side and are not serialized.
484#[derive(Debug, Clone, Default, Serialize, Deserialize)]
485#[serde(rename_all = "camelCase")]
486pub struct SessionConfig {
487    #[serde(skip_serializing_if = "Option::is_none")]
488    pub session_id: Option<String>,
489    #[serde(skip_serializing_if = "Option::is_none")]
490    pub model: Option<String>,
491    #[serde(skip_serializing_if = "Option::is_none")]
492    pub reasoning_effort: Option<ReasoningEffort>,
493    #[serde(skip_serializing_if = "Option::is_none")]
494    pub config_dir: Option<String>,
495    #[serde(skip_serializing_if = "Option::is_none")]
496    pub tools: Option<Vec<ToolDefinition>>,
497    #[serde(skip_serializing_if = "Option::is_none")]
498    pub system_message: Option<SystemMessageConfig>,
499    #[serde(skip_serializing_if = "Option::is_none")]
500    pub available_tools: Option<Vec<String>>,
501    #[serde(skip_serializing_if = "Option::is_none")]
502    pub excluded_tools: Option<Vec<String>>,
503    #[serde(skip_serializing_if = "Option::is_none")]
504    pub provider: Option<ProviderConfig>,
505    #[serde(skip_serializing_if = "Option::is_none")]
506    pub working_directory: Option<String>,
507    #[serde(skip_serializing_if = "Option::is_none")]
508    pub streaming: Option<bool>,
509    #[serde(skip_serializing_if = "Option::is_none")]
510    pub mcp_servers: Option<HashMap<String, McpServerConfig>>,
511    #[serde(skip_serializing_if = "Option::is_none")]
512    pub custom_agents: Option<Vec<CustomAgentConfig>>,
513    #[serde(skip_serializing_if = "Option::is_none")]
514    pub skill_directories: Option<Vec<String>>,
515    #[serde(skip_serializing_if = "Option::is_none")]
516    pub disabled_skills: Option<Vec<String>>,
517    #[serde(skip_serializing_if = "Option::is_none")]
518    pub infinite_sessions: Option<InfiniteSessionConfig>,
519    /// Set by the SDK based on whether handlers are registered (not user-set).
520    #[serde(skip_serializing_if = "Option::is_none")]
521    pub request_permission: Option<bool>,
522    /// Set by the SDK based on whether handlers are registered (not user-set).
523    #[serde(skip_serializing_if = "Option::is_none")]
524    pub request_user_input: Option<bool>,
525    /// Set by the SDK based on whether hooks are registered (not user-set).
526    #[serde(skip_serializing_if = "Option::is_none")]
527    pub hooks: Option<bool>,
528}
529
530/// Configuration for resuming a session.
531#[derive(Debug, Clone, Default, Serialize, Deserialize)]
532#[serde(rename_all = "camelCase")]
533pub struct ResumeSessionConfig {
534    pub session_id: String,
535    #[serde(skip_serializing_if = "Option::is_none")]
536    pub model: Option<String>,
537    #[serde(skip_serializing_if = "Option::is_none")]
538    pub reasoning_effort: Option<ReasoningEffort>,
539    #[serde(skip_serializing_if = "Option::is_none")]
540    pub config_dir: Option<String>,
541    #[serde(skip_serializing_if = "Option::is_none")]
542    pub tools: Option<Vec<ToolDefinition>>,
543    #[serde(skip_serializing_if = "Option::is_none")]
544    pub system_message: Option<SystemMessageConfig>,
545    #[serde(skip_serializing_if = "Option::is_none")]
546    pub available_tools: Option<Vec<String>>,
547    #[serde(skip_serializing_if = "Option::is_none")]
548    pub excluded_tools: Option<Vec<String>>,
549    #[serde(skip_serializing_if = "Option::is_none")]
550    pub provider: Option<ProviderConfig>,
551    #[serde(skip_serializing_if = "Option::is_none")]
552    pub working_directory: Option<String>,
553    #[serde(skip_serializing_if = "Option::is_none")]
554    pub streaming: Option<bool>,
555    #[serde(skip_serializing_if = "Option::is_none")]
556    pub mcp_servers: Option<HashMap<String, McpServerConfig>>,
557    #[serde(skip_serializing_if = "Option::is_none")]
558    pub custom_agents: Option<Vec<CustomAgentConfig>>,
559    #[serde(skip_serializing_if = "Option::is_none")]
560    pub skill_directories: Option<Vec<String>>,
561    #[serde(skip_serializing_if = "Option::is_none")]
562    pub disabled_skills: Option<Vec<String>>,
563    #[serde(skip_serializing_if = "Option::is_none")]
564    pub infinite_sessions: Option<InfiniteSessionConfig>,
565    #[serde(skip_serializing_if = "Option::is_none")]
566    pub disable_resume: Option<bool>,
567    #[serde(skip_serializing_if = "Option::is_none")]
568    pub request_permission: Option<bool>,
569    #[serde(skip_serializing_if = "Option::is_none")]
570    pub request_user_input: Option<bool>,
571    #[serde(skip_serializing_if = "Option::is_none")]
572    pub hooks: Option<bool>,
573}
574
575// ============================================================================
576// Message Types
577// ============================================================================
578
579/// Attachment for a file.
580#[derive(Debug, Clone, Serialize, Deserialize)]
581#[serde(rename_all = "camelCase")]
582pub struct FileAttachment {
583    pub path: String,
584    #[serde(skip_serializing_if = "Option::is_none")]
585    pub display_name: Option<String>,
586}
587
588/// Attachment for a directory.
589#[derive(Debug, Clone, Serialize, Deserialize)]
590#[serde(rename_all = "camelCase")]
591pub struct DirectoryAttachment {
592    pub path: String,
593    #[serde(skip_serializing_if = "Option::is_none")]
594    pub display_name: Option<String>,
595}
596
597/// Selection range within a file.
598#[derive(Debug, Clone, Serialize, Deserialize)]
599pub struct SelectionRange {
600    pub start: Position,
601    pub end: Position,
602}
603
604/// A position in a file (line and character).
605#[derive(Debug, Clone, Serialize, Deserialize)]
606pub struct Position {
607    pub line: u32,
608    pub character: u32,
609}
610
611/// Attachment for a code selection.
612#[derive(Debug, Clone, Serialize, Deserialize)]
613#[serde(rename_all = "camelCase")]
614pub struct SelectionAttachment {
615    pub file_path: String,
616    pub display_name: String,
617    #[serde(skip_serializing_if = "Option::is_none")]
618    pub selection: Option<SelectionRange>,
619    #[serde(skip_serializing_if = "Option::is_none")]
620    pub text: Option<String>,
621}
622
623/// Union type for message attachments.
624#[derive(Debug, Clone, Serialize, Deserialize)]
625#[serde(tag = "type", rename_all = "camelCase")]
626pub enum Attachment {
627    File(FileAttachment),
628    Directory(DirectoryAttachment),
629    Selection(SelectionAttachment),
630}
631
632/// Options for sending a message to a session.
633#[derive(Debug, Clone, Serialize, Deserialize)]
634#[serde(rename_all = "camelCase")]
635pub struct MessageOptions {
636    pub prompt: String,
637    #[serde(skip_serializing_if = "Option::is_none")]
638    pub attachments: Option<Vec<Attachment>>,
639    #[serde(skip_serializing_if = "Option::is_none")]
640    pub mode: Option<String>,
641}
642
643// ============================================================================
644// Session Event Types
645// ============================================================================
646
647/// A session event received from the server.
648///
649/// Events are delivered via `session.event` JSON-RPC notifications.
650/// The `event_type` field determines which variant's `data` is populated.
651#[derive(Debug, Clone, Serialize, Deserialize)]
652pub struct SessionEvent {
653    pub id: String,
654    pub timestamp: String,
655    #[serde(rename = "parentId")]
656    pub parent_id: Option<String>,
657    #[serde(default)]
658    pub ephemeral: bool,
659    #[serde(rename = "type")]
660    pub event_type: String,
661    /// The event data, varying by event_type. Use helper methods or
662    /// deserialize into specific types based on event_type.
663    pub data: serde_json::Value,
664}
665
666impl SessionEvent {
667    /// Returns true if this is an `assistant.message` event.
668    pub fn is_assistant_message(&self) -> bool {
669        self.event_type == "assistant.message"
670    }
671
672    /// Returns true if this is a `session.idle` event.
673    pub fn is_session_idle(&self) -> bool {
674        self.event_type == "session.idle"
675    }
676
677    /// Returns true if this is a `session.error` event.
678    pub fn is_session_error(&self) -> bool {
679        self.event_type == "session.error"
680    }
681
682    /// Attempts to extract the assistant message content.
683    /// Returns `None` if this is not an `assistant.message` event.
684    pub fn assistant_message_content(&self) -> Option<&str> {
685        if self.is_assistant_message() {
686            self.data.get("content").and_then(|v| v.as_str())
687        } else {
688            None
689        }
690    }
691
692    /// Attempts to extract the error message from a session.error event.
693    pub fn error_message(&self) -> Option<&str> {
694        if self.is_session_error() {
695            self.data.get("message").and_then(|v| v.as_str())
696        } else {
697            None
698        }
699    }
700
701    /// Attempts to extract the error stack from a session.error event.
702    pub fn error_stack(&self) -> Option<&str> {
703        if self.is_session_error() {
704            self.data.get("stack").and_then(|v| v.as_str())
705        } else {
706            None
707        }
708    }
709}
710
711// ============================================================================
712// Ping Response
713// ============================================================================
714
715/// Response from a ping request.
716#[derive(Debug, Clone, Serialize, Deserialize)]
717#[serde(rename_all = "camelCase")]
718pub struct PingResponse {
719    pub message: String,
720    pub timestamp: f64,
721    #[serde(skip_serializing_if = "Option::is_none")]
722    pub protocol_version: Option<u32>,
723}
724
725// ============================================================================
726// Status Responses
727// ============================================================================
728
729/// Response from status.get.
730#[derive(Debug, Clone, Serialize, Deserialize)]
731#[serde(rename_all = "camelCase")]
732pub struct GetStatusResponse {
733    pub version: String,
734    pub protocol_version: u32,
735}
736
737/// Response from auth.getStatus.
738#[derive(Debug, Clone, Serialize, Deserialize)]
739#[serde(rename_all = "camelCase")]
740pub struct GetAuthStatusResponse {
741    pub is_authenticated: bool,
742    #[serde(skip_serializing_if = "Option::is_none")]
743    pub auth_type: Option<String>,
744    #[serde(skip_serializing_if = "Option::is_none")]
745    pub host: Option<String>,
746    #[serde(skip_serializing_if = "Option::is_none")]
747    pub login: Option<String>,
748    #[serde(skip_serializing_if = "Option::is_none")]
749    pub status_message: Option<String>,
750}
751
752// ============================================================================
753// Model Types
754// ============================================================================
755
756/// Vision capabilities for a model.
757#[derive(Debug, Clone, Serialize, Deserialize)]
758#[serde(rename_all = "camelCase")]
759pub struct VisionLimits {
760    pub supported_media_types: Vec<String>,
761    pub max_prompt_images: u32,
762    pub max_prompt_image_size: u64,
763}
764
765/// Model capability limits.
766#[derive(Debug, Clone, Serialize, Deserialize)]
767#[serde(rename_all = "camelCase")]
768pub struct ModelLimits {
769    #[serde(skip_serializing_if = "Option::is_none")]
770    pub max_prompt_tokens: Option<u64>,
771    pub max_context_window_tokens: u64,
772    #[serde(skip_serializing_if = "Option::is_none")]
773    pub vision: Option<VisionLimits>,
774}
775
776/// What a model supports.
777#[derive(Debug, Clone, Serialize, Deserialize)]
778#[serde(rename_all = "camelCase")]
779pub struct ModelSupports {
780    pub vision: bool,
781    pub reasoning_effort: bool,
782}
783
784/// Model capabilities and limits.
785#[derive(Debug, Clone, Serialize, Deserialize)]
786pub struct ModelCapabilities {
787    pub supports: ModelSupports,
788    pub limits: ModelLimits,
789}
790
791/// Model policy state.
792#[derive(Debug, Clone, Serialize, Deserialize)]
793pub struct ModelPolicy {
794    pub state: String,
795    pub terms: String,
796}
797
798/// Model billing information.
799#[derive(Debug, Clone, Serialize, Deserialize)]
800pub struct ModelBilling {
801    pub multiplier: f64,
802}
803
804/// Information about an available model.
805#[derive(Debug, Clone, Serialize, Deserialize)]
806#[serde(rename_all = "camelCase")]
807pub struct ModelInfo {
808    pub id: String,
809    pub name: String,
810    pub capabilities: ModelCapabilities,
811    #[serde(skip_serializing_if = "Option::is_none")]
812    pub policy: Option<ModelPolicy>,
813    #[serde(skip_serializing_if = "Option::is_none")]
814    pub billing: Option<ModelBilling>,
815    #[serde(skip_serializing_if = "Option::is_none")]
816    pub supported_reasoning_efforts: Option<Vec<ReasoningEffort>>,
817    #[serde(skip_serializing_if = "Option::is_none")]
818    pub default_reasoning_effort: Option<ReasoningEffort>,
819}
820
821// ============================================================================
822// Session Metadata
823// ============================================================================
824
825/// Metadata about a session.
826#[derive(Debug, Clone, Serialize, Deserialize)]
827#[serde(rename_all = "camelCase")]
828pub struct SessionMetadata {
829    pub session_id: String,
830    pub start_time: String,
831    pub modified_time: String,
832    #[serde(skip_serializing_if = "Option::is_none")]
833    pub summary: Option<String>,
834    pub is_remote: bool,
835}
836
837// ============================================================================
838// Session Lifecycle Events
839// ============================================================================
840
841/// Types of session lifecycle events.
842#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
843pub enum SessionLifecycleEventType {
844    #[serde(rename = "session.created")]
845    SessionCreated,
846    #[serde(rename = "session.deleted")]
847    SessionDeleted,
848    #[serde(rename = "session.updated")]
849    SessionUpdated,
850    #[serde(rename = "session.foreground")]
851    SessionForeground,
852    #[serde(rename = "session.background")]
853    SessionBackground,
854}
855
856/// Metadata included in lifecycle events.
857#[derive(Debug, Clone, Serialize, Deserialize)]
858#[serde(rename_all = "camelCase")]
859pub struct LifecycleMetadata {
860    pub start_time: String,
861    pub modified_time: String,
862    #[serde(skip_serializing_if = "Option::is_none")]
863    pub summary: Option<String>,
864}
865
866/// Session lifecycle event notification.
867#[derive(Debug, Clone, Serialize, Deserialize)]
868#[serde(rename_all = "camelCase")]
869pub struct SessionLifecycleEvent {
870    #[serde(rename = "type")]
871    pub event_type: SessionLifecycleEventType,
872    pub session_id: String,
873    #[serde(skip_serializing_if = "Option::is_none")]
874    pub metadata: Option<LifecycleMetadata>,
875}
876
877// ============================================================================
878// Foreground Session Info
879// ============================================================================
880
881/// Information about the foreground session in TUI+server mode.
882#[derive(Debug, Clone, Serialize, Deserialize)]
883#[serde(rename_all = "camelCase")]
884pub struct ForegroundSessionInfo {
885    #[serde(skip_serializing_if = "Option::is_none")]
886    pub session_id: Option<String>,
887    #[serde(skip_serializing_if = "Option::is_none")]
888    pub workspace_path: Option<String>,
889}
890
891// ============================================================================
892// Client Options
893// ============================================================================
894
895/// Options for creating a CopilotClient.
896#[derive(Debug, Clone)]
897pub struct CopilotClientOptions {
898    /// Path to the CLI executable.
899    pub cli_path: Option<String>,
900    /// Extra arguments to pass to the CLI executable.
901    pub cli_args: Vec<String>,
902    /// Working directory for the CLI process.
903    pub cwd: Option<String>,
904    /// Port for the CLI server (TCP mode only).
905    pub port: u16,
906    /// Use stdio transport instead of TCP (default: true).
907    pub use_stdio: bool,
908    /// URL of an existing Copilot CLI server to connect to over TCP.
909    pub cli_url: Option<String>,
910    /// Log level for the CLI server.
911    pub log_level: String,
912    /// Auto-start the CLI server on first use (default: true).
913    pub auto_start: bool,
914    /// Auto-restart the CLI server if it crashes (default: true).
915    pub auto_restart: bool,
916    /// Environment variables to pass to the CLI process.
917    pub env: Option<HashMap<String, String>>,
918    /// GitHub token for authentication.
919    pub github_token: Option<String>,
920    /// Whether to use the logged-in user for authentication.
921    pub use_logged_in_user: Option<bool>,
922}
923
924impl Default for CopilotClientOptions {
925    fn default() -> Self {
926        Self {
927            cli_path: None,
928            cli_args: Vec::new(),
929            cwd: None,
930            port: 0,
931            use_stdio: true,
932            cli_url: None,
933            log_level: "info".to_string(),
934            auto_start: true,
935            auto_restart: true,
936            env: None,
937            github_token: None,
938            use_logged_in_user: None,
939        }
940    }
941}