Skip to main content

cookie_agent_protocol/
rpc.rs

1use std::{borrow::Cow, collections::BTreeMap, fmt};
2
3use schemars::{JsonSchema, Schema, SchemaGenerator, json_schema};
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6
7use crate::*;
8
9pub const SESSION_TREE_USAGE_MISSING_SESSION_CODE: i32 = -32030;
10pub const SESSION_TREE_USAGE_CORRUPT_DELEGATION_CODE: i32 = -32031;
11pub const SESSION_GOAL_GET_METHOD: &str = "session.goal.get";
12pub const SESSION_GOAL_SET_METHOD: &str = "session.goal.set";
13pub const SESSION_GOAL_LIFECYCLE_METHOD: &str = "session.goal.lifecycle";
14pub const SESSION_PRODUCERS_METHOD: &str = "session.producers";
15
16#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
17pub struct JsonRpcVersion(());
18impl JsonRpcVersion {
19    #[must_use]
20    pub const fn current() -> Self {
21        Self(())
22    }
23}
24impl Serialize for JsonRpcVersion {
25    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
26    where
27        S: serde::Serializer,
28    {
29        s.serialize_str("2.0")
30    }
31}
32impl<'de> Deserialize<'de> for JsonRpcVersion {
33    fn deserialize<D>(d: D) -> Result<Self, D::Error>
34    where
35        D: serde::Deserializer<'de>,
36    {
37        let value = String::deserialize(d)?;
38        if value == "2.0" {
39            Ok(Self::current())
40        } else {
41            Err(serde::de::Error::custom(
42                "JSON-RPC version must be exactly 2.0",
43            ))
44        }
45    }
46}
47impl JsonSchema for JsonRpcVersion {
48    fn inline_schema() -> bool {
49        true
50    }
51    fn schema_name() -> Cow<'static, str> {
52        Cow::Borrowed("JsonRpcVersion")
53    }
54    fn json_schema(_: &mut SchemaGenerator) -> Schema {
55        json_schema!({"type":"string","const":"2.0"})
56    }
57}
58
59#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
60#[serde(untagged)]
61pub enum JsonRpcId {
62    Null,
63    Number(i64),
64    String(String),
65}
66#[derive(Clone, Deserialize, JsonSchema, PartialEq, Serialize)]
67#[serde(deny_unknown_fields)]
68pub struct Request {
69    pub jsonrpc: JsonRpcVersion,
70    pub id: JsonRpcId,
71    pub method: String,
72    #[serde(skip_serializing_if = "Option::is_none")]
73    pub params: Option<Value>,
74}
75impl Request {
76    #[must_use]
77    pub fn new(id: JsonRpcId, method: impl Into<String>, params: Option<Value>) -> Self {
78        Self {
79            jsonrpc: JsonRpcVersion::current(),
80            id,
81            method: method.into(),
82            params,
83        }
84    }
85}
86impl fmt::Debug for Request {
87    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88        f.debug_struct("Request")
89            .field("jsonrpc", &self.jsonrpc)
90            .field("id", &self.id)
91            .field("method", &self.method)
92            .field("params", &self.params.as_ref().map(|_| "<redacted>"))
93            .finish()
94    }
95}
96#[derive(Clone, Deserialize, JsonSchema, PartialEq, Serialize)]
97#[serde(deny_unknown_fields)]
98pub struct SuccessResponse {
99    pub jsonrpc: JsonRpcVersion,
100    pub id: JsonRpcId,
101    pub result: Value,
102}
103impl fmt::Debug for SuccessResponse {
104    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105        f.debug_struct("SuccessResponse")
106            .field("jsonrpc", &self.jsonrpc)
107            .field("id", &self.id)
108            .field("result", &"<redacted>")
109            .finish()
110    }
111}
112#[derive(Clone, Deserialize, JsonSchema, PartialEq, Serialize)]
113#[serde(deny_unknown_fields)]
114pub struct JsonRpcError {
115    pub code: i32,
116    pub message: String,
117    #[serde(skip_serializing_if = "Option::is_none")]
118    pub data: Option<Value>,
119}
120impl fmt::Debug for JsonRpcError {
121    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122        f.debug_struct("JsonRpcError")
123            .field("code", &self.code)
124            .field("message", &self.message)
125            .field("data", &self.data.as_ref().map(|_| "<redacted>"))
126            .finish()
127    }
128}
129#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
130#[serde(deny_unknown_fields)]
131pub struct ErrorResponse {
132    pub jsonrpc: JsonRpcVersion,
133    pub id: JsonRpcId,
134    pub error: JsonRpcError,
135}
136#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
137#[serde(untagged)]
138pub enum Response {
139    Success(SuccessResponse),
140    Error(ErrorResponse),
141}
142#[derive(Clone, Deserialize, JsonSchema, PartialEq, Serialize)]
143#[serde(deny_unknown_fields)]
144pub struct Notification {
145    pub jsonrpc: JsonRpcVersion,
146    pub method: String,
147    #[serde(skip_serializing_if = "Option::is_none")]
148    pub params: Option<Value>,
149}
150impl Notification {
151    #[must_use]
152    pub fn new(method: impl Into<String>, params: Option<Value>) -> Self {
153        Self {
154            jsonrpc: JsonRpcVersion::current(),
155            method: method.into(),
156            params,
157        }
158    }
159}
160impl fmt::Debug for Notification {
161    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162        f.debug_struct("Notification")
163            .field("jsonrpc", &self.jsonrpc)
164            .field("method", &self.method)
165            .field("params", &self.params.as_ref().map(|_| "<redacted>"))
166            .finish()
167    }
168}
169
170#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
171#[serde(deny_unknown_fields)]
172pub struct ClientHello {
173    pub protocol_version: ProtocolVersion,
174}
175#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
176#[serde(deny_unknown_fields)]
177pub struct ServerHello {
178    pub protocol_version: ProtocolVersion,
179}
180
181#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
182#[serde(deny_unknown_fields)]
183pub struct SessionCreateParams {
184    pub selection: RunSelection,
185}
186#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
187#[serde(deny_unknown_fields)]
188pub struct SessionCreateResult {
189    pub session: SessionMeta,
190}
191#[derive(Clone, Debug, Default, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
192#[serde(deny_unknown_fields)]
193pub struct SessionListParams {
194    #[serde(skip_serializing_if = "Option::is_none")]
195    pub cwd_identity: Option<CwdIdentity>,
196}
197#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
198#[serde(deny_unknown_fields)]
199pub struct SessionListResult {
200    pub sessions: Vec<SessionMeta>,
201}
202#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
203#[serde(deny_unknown_fields)]
204pub struct SessionGetParams {
205    pub session_id: SessionId,
206}
207#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
208#[serde(deny_unknown_fields)]
209pub struct SessionGetResult {
210    pub session: SessionMeta,
211}
212
213#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
214#[serde(deny_unknown_fields)]
215pub struct SessionGoalGetParams {
216    pub session_id: SessionId,
217}
218
219#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
220#[serde(deny_unknown_fields)]
221pub struct SessionGoalGetResult {
222    #[serde(deserialize_with = "crate::deserialize_required_option")]
223    #[schemars(with = "crate::NullableSchema<GoalState>", required)]
224    pub goal: Option<GoalState>,
225}
226
227#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
228#[serde(deny_unknown_fields)]
229pub struct SessionGoalSetParams {
230    pub session_id: SessionId,
231    pub objective: String,
232    #[serde(default, skip_serializing_if = "Option::is_none")]
233    pub selection: Option<RunSelection>,
234}
235
236#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
237#[serde(deny_unknown_fields)]
238pub struct SessionGoalSetResult {
239    pub goal: GoalState,
240}
241
242#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
243#[serde(deny_unknown_fields)]
244pub struct SessionGoalLifecycleParams {
245    pub session_id: SessionId,
246    pub goal_id: GoalId,
247    pub expected_revision: u64,
248    pub action: GoalLifecycleAction,
249    #[serde(default, skip_serializing_if = "Option::is_none")]
250    pub selection: Option<RunSelection>,
251}
252
253#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
254#[serde(deny_unknown_fields)]
255pub struct SessionGoalLifecycleResult {
256    pub goal: GoalState,
257}
258
259#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
260#[serde(deny_unknown_fields)]
261pub struct SessionProducersParams {
262    pub session_id: SessionId,
263}
264
265#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
266#[serde(deny_unknown_fields)]
267pub struct SessionProducersResult {
268    pub producers: Vec<ProducerRegistration>,
269    pub plugin_recovery: Vec<PluginRecoveryState>,
270}
271
272#[derive(Clone, Debug, Default, Deserialize, JsonSchema, PartialEq, Serialize)]
273#[serde(deny_unknown_fields)]
274pub struct UsageRollup {
275    pub input_tokens: u64,
276    pub output_tokens: u64,
277    pub reasoning_tokens: u64,
278    pub cache_read_tokens: u64,
279    pub cache_write_tokens: u64,
280    pub request_count: u64,
281    #[serde(deserialize_with = "deserialize_required_option")]
282    #[schemars(with = "crate::NullableSchema<f64>", required)]
283    pub cache_hit_rate: Option<f64>,
284    #[serde(deserialize_with = "deserialize_required_option")]
285    #[schemars(with = "crate::NullableSchema<f64>", required)]
286    pub estimated_cost_usd: Option<f64>,
287    pub by_model: BTreeMap<ModelKey, ModelUsageRollup>,
288    #[serde(skip)]
289    #[schemars(skip)]
290    pub arithmetic_overflow: bool,
291}
292
293#[derive(Clone, Debug, Default, Deserialize, JsonSchema, PartialEq, Serialize)]
294#[serde(deny_unknown_fields)]
295pub struct ModelUsageRollup {
296    pub input_tokens: u64,
297    pub output_tokens: u64,
298    pub reasoning_tokens: u64,
299    pub cache_read_tokens: u64,
300    pub cache_write_tokens: u64,
301    pub request_count: u64,
302    #[serde(deserialize_with = "deserialize_required_option")]
303    #[schemars(with = "crate::NullableSchema<f64>", required)]
304    pub cache_hit_rate: Option<f64>,
305    #[serde(deserialize_with = "deserialize_required_option")]
306    #[schemars(with = "crate::NullableSchema<f64>", required)]
307    pub estimated_cost_usd: Option<f64>,
308    #[serde(skip)]
309    #[schemars(skip)]
310    pub observations: Vec<Usage>,
311    #[serde(skip)]
312    #[schemars(skip)]
313    pub cost_provenance: Vec<UsageCostProvenance>,
314    #[serde(skip)]
315    #[schemars(skip)]
316    pub arithmetic_overflow: bool,
317}
318
319#[doc(hidden)]
320#[derive(Clone, Copy, Debug, Eq, PartialEq)]
321pub enum UsageCostProvenance {
322    Stamped(Option<u64>),
323}
324
325#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
326#[serde(deny_unknown_fields)]
327pub struct SessionUsageParams {
328    pub session_id: SessionId,
329}
330
331#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
332#[serde(deny_unknown_fields)]
333pub struct SessionUsageResult {
334    pub session_id: SessionId,
335    pub usage: UsageRollup,
336}
337
338#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
339#[serde(deny_unknown_fields)]
340pub struct SessionTreeUsageResult {
341    pub session_id: SessionId,
342    pub usage: UsageRollup,
343    pub session_count: u64,
344}
345
346#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
347#[serde(deny_unknown_fields)]
348pub struct AgentUsageParams {
349    pub agent_id: AgentId,
350}
351
352#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
353#[serde(deny_unknown_fields)]
354pub struct AgentUsageResult {
355    pub agent_id: AgentId,
356    pub usage: UsageRollup,
357}
358
359#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
360#[serde(deny_unknown_fields)]
361pub struct GlobalUsageParams {}
362
363#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
364#[serde(deny_unknown_fields)]
365pub struct GlobalUsageResult {
366    pub usage: UsageRollup,
367}
368
369#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
370#[serde(deny_unknown_fields)]
371pub struct ChildSummary {
372    pub session_id: SessionId,
373    pub agent: AgentId,
374    #[serde(deserialize_with = "deserialize_nullable_title")]
375    #[schemars(with = "crate::NullableSchema<SessionTitle>", required)]
376    pub title: Option<SessionTitle>,
377    pub title_updated_seq: u64,
378    pub status: SessionStatus,
379    #[serde(deserialize_with = "deserialize_nullable_usage")]
380    #[schemars(with = "crate::NullableSchema<Usage>", required)]
381    pub usage: Option<Usage>,
382}
383fn deserialize_nullable_title<'de, D>(d: D) -> Result<Option<SessionTitle>, D::Error>
384where
385    D: serde::Deserializer<'de>,
386{
387    Option::deserialize(d)
388}
389fn deserialize_nullable_usage<'de, D>(d: D) -> Result<Option<Usage>, D::Error>
390where
391    D: serde::Deserializer<'de>,
392{
393    Option::deserialize(d)
394}
395#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
396#[serde(deny_unknown_fields)]
397pub struct SessionTree {
398    pub session: SessionMeta,
399    pub children: Vec<SessionTree>,
400}
401#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
402#[serde(deny_unknown_fields)]
403pub struct SessionChildrenParams {
404    pub session_id: SessionId,
405}
406#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
407#[serde(deny_unknown_fields)]
408pub struct SessionChildrenResult {
409    pub children: Vec<ChildSummary>,
410}
411#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
412#[serde(deny_unknown_fields)]
413pub struct SessionTreeParams {
414    pub session_id: SessionId,
415}
416#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
417#[serde(deny_unknown_fields)]
418pub struct SessionTreeResult {
419    pub tree: SessionTree,
420}
421#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
422#[serde(deny_unknown_fields)]
423pub struct SessionResumeParams {
424    pub session_id: SessionId,
425}
426#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
427#[serde(deny_unknown_fields)]
428pub struct SessionResumeResult {
429    pub session: SessionMeta,
430}
431
432#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
433#[serde(deny_unknown_fields)]
434pub struct SessionSetPermissionModeParams {
435    pub session_id: SessionId,
436    pub mode: PermissionMode,
437}
438#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
439#[serde(deny_unknown_fields)]
440pub struct SessionSetPermissionModeResult {}
441
442#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
443#[serde(rename_all = "snake_case")]
444pub enum PermissionRuleSource {
445    SessionOverlay,
446    AgentDocument,
447    Default,
448}
449
450#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
451#[serde(deny_unknown_fields)]
452pub struct EffectivePermissionRule {
453    pub resource: WildcardPattern,
454    pub effect: PermissionEffect,
455    pub source: PermissionRuleSource,
456}
457
458#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
459#[serde(deny_unknown_fields)]
460pub struct EffectivePermissionAction {
461    pub action: PermissionAction,
462    pub effect: PermissionEffect,
463    pub source: PermissionRuleSource,
464    pub patterns: Vec<EffectivePermissionRule>,
465}
466
467#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
468#[serde(deny_unknown_fields)]
469pub struct SessionPermissionGetParams {
470    pub session_id: SessionId,
471}
472
473#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
474#[serde(deny_unknown_fields)]
475pub struct SessionPermissionGetResult {
476    pub permissions: Vec<EffectivePermissionAction>,
477    #[serde(default, skip_serializing_if = "Option::is_none")]
478    pub current_mode: Option<PermissionMode>,
479}
480
481#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
482#[serde(deny_unknown_fields)]
483pub struct SessionPermissionSetParams {
484    pub session_id: SessionId,
485    pub action: PermissionAction,
486    pub resource: WildcardPattern,
487    pub effect: PermissionEffect,
488}
489
490#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
491#[serde(deny_unknown_fields)]
492pub struct SessionPermissionClearParams {
493    pub session_id: SessionId,
494    pub action: PermissionAction,
495    pub resource: WildcardPattern,
496}
497
498#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
499#[serde(deny_unknown_fields)]
500pub struct SessionPermissionMutationResult {
501    pub permissions: Vec<EffectivePermissionAction>,
502}
503
504#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
505#[serde(rename_all = "snake_case")]
506pub enum SkillSource {
507    User,
508    Project,
509}
510
511#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
512#[serde(deny_unknown_fields)]
513pub struct SkillDescriptor {
514    pub name: String,
515    pub description: String,
516    #[serde(deserialize_with = "crate::deserialize_required_option")]
517    #[schemars(with = "crate::NullableSchema<String>", required)]
518    pub when_to_use: Option<String>,
519    pub location: String,
520    pub source: SkillSource,
521    pub precedence_winner: bool,
522    pub permission_effect: PermissionEffect,
523    pub visible: bool,
524    pub user_invocable: bool,
525    #[serde(deserialize_with = "crate::deserialize_required_option")]
526    #[schemars(with = "crate::NullableSchema<String>", required)]
527    pub argument_hint: Option<String>,
528}
529
530#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
531#[serde(deny_unknown_fields)]
532pub struct SkillsListParams {
533    pub session_id: SessionId,
534}
535
536#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
537#[serde(deny_unknown_fields)]
538pub struct SkillsListResult {
539    pub skills: Vec<SkillDescriptor>,
540}
541
542#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
543#[serde(deny_unknown_fields)]
544pub struct SkillsGetParams {
545    pub session_id: SessionId,
546    pub name: String,
547    #[serde(default)]
548    pub args: String,
549}
550
551#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
552#[serde(deny_unknown_fields)]
553pub struct SkillsGetResult {
554    pub skill: SkillDescriptor,
555    pub rendered: String,
556}
557
558const SKILL_SUBMISSION_PREFIX: &str = "\u{0}cookie-skill:";
559
560#[must_use]
561pub fn encode_skill_submission(name: &str, args: &str) -> String {
562    encode_skill_submission_with_prompt(name, args, None)
563}
564
565#[must_use]
566pub fn encode_skill_submission_with_prompt(name: &str, args: &str, prompt: Option<&str>) -> String {
567    format!(
568        "{SKILL_SUBMISSION_PREFIX}{}",
569        serde_json::json!({"name": name, "args": args, "prompt": prompt})
570    )
571}
572
573#[must_use]
574pub fn decode_skill_submission(input: &str) -> Option<(String, String, Option<String>)> {
575    #[derive(Deserialize)]
576    #[serde(deny_unknown_fields)]
577    struct Submission {
578        name: String,
579        args: String,
580        prompt: Option<String>,
581    }
582    let submission: Submission =
583        serde_json::from_str(input.strip_prefix(SKILL_SUBMISSION_PREFIX)?).ok()?;
584    Some((submission.name, submission.args, submission.prompt))
585}
586
587#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
588#[serde(deny_unknown_fields)]
589pub struct SessionCompactParams {
590    pub session_id: SessionId,
591    #[serde(deserialize_with = "crate::deserialize_required_option")]
592    #[schemars(with = "crate::NullableSchema<SafeDisplayText>", required)]
593    pub focus: Option<SafeDisplayText>,
594}
595#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
596#[serde(deny_unknown_fields)]
597pub struct SessionCompactResult {
598    pub compacted: bool,
599    #[serde(default, skip_serializing_if = "Option::is_none")]
600    pub cancellation_reason: Option<String>,
601}
602
603#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
604#[serde(deny_unknown_fields)]
605pub struct SessionRevertParams {
606    pub session_id: SessionId,
607    #[schemars(range(min = 1))]
608    pub through_seq: u64,
609}
610#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
611#[serde(deny_unknown_fields)]
612pub struct SessionRevertResult {
613    pub session: SessionMeta,
614    #[serde(default, skip_serializing_if = "Option::is_none")]
615    pub instructions_override: Option<String>,
616}
617
618#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
619#[serde(deny_unknown_fields)]
620pub struct SessionForkParams {
621    pub session_id: SessionId,
622    #[schemars(range(min = 1))]
623    pub through_seq: u64,
624}
625#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
626#[serde(deny_unknown_fields)]
627pub struct SessionForkResult {
628    pub session_id: SessionId,
629}
630
631#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
632#[serde(tag = "type", rename_all = "snake_case", deny_unknown_fields)]
633pub enum SessionRenameChange {
634    Set { title: SessionTitle },
635    Clear,
636    Reset,
637}
638#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
639#[serde(deny_unknown_fields)]
640pub struct SessionRenameParams {
641    pub session_id: SessionId,
642    pub client_rename_id: ClientRenameId,
643    pub change: SessionRenameChange,
644}
645#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
646#[serde(deny_unknown_fields)]
647pub struct SessionRenameResult {
648    pub client_rename_id: ClientRenameId,
649    pub session: SessionMeta,
650}
651#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
652#[serde(deny_unknown_fields)]
653pub struct SessionRenameRecord {
654    pub client_rename_id: ClientRenameId,
655    pub change: SessionRenameChange,
656}
657impl SessionRenameRecord {
658    #[must_use]
659    pub fn conflicts_with(&self, request: &SessionRenameParams) -> bool {
660        self.client_rename_id == request.client_rename_id && self.change != request.change
661    }
662    #[must_use]
663    pub fn matches(&self, request: &SessionRenameParams) -> bool {
664        self.client_rename_id == request.client_rename_id && self.change == request.change
665    }
666}
667#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
668#[serde(rename_all = "snake_case")]
669pub enum SessionRenameErrorCode {
670    SessionNotFound,
671    InvalidTitle,
672    IdempotencyConflict,
673}
674#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
675#[serde(deny_unknown_fields)]
676pub struct SessionRenameError {
677    pub code: SessionRenameErrorCode,
678    pub session_id: SessionId,
679    pub client_rename_id: ClientRenameId,
680}
681
682#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
683#[serde(deny_unknown_fields)]
684pub struct RunStartParams {
685    pub session_id: SessionId,
686    pub client_run_id: ClientRunId,
687    pub selection: RunSelection,
688    pub input: String,
689    // An explicit model/agent/preset choice may restore a previously skipped prefix.
690    #[serde(default, skip_serializing_if = "is_false")]
691    pub reset_fallback: bool,
692}
693
694fn is_false(value: &bool) -> bool {
695    !value
696}
697#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
698#[serde(rename_all = "snake_case")]
699pub enum RunStartConflictCode {
700    IdempotencyConflict,
701}
702#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
703#[serde(deny_unknown_fields)]
704pub struct RunStartConflict {
705    pub code: RunStartConflictCode,
706    pub session_id: SessionId,
707    pub client_run_id: ClientRunId,
708}
709#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
710#[serde(deny_unknown_fields)]
711pub struct RunStartResult {
712    pub run_id: RunId,
713}
714#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
715#[serde(deny_unknown_fields)]
716pub struct RunSteerParams {
717    pub run_id: RunId,
718    pub input: String,
719}
720#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
721#[serde(deny_unknown_fields)]
722pub struct RunSteerResult {
723    pub accepted: bool,
724    #[serde(default, skip_serializing_if = "Option::is_none")]
725    pub handled_reason: Option<String>,
726}
727#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
728#[serde(deny_unknown_fields)]
729pub struct RunRecallSteerParams {
730    pub run_id: RunId,
731}
732#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
733#[serde(deny_unknown_fields)]
734pub struct RunRecallSteerResult {
735    #[serde(deserialize_with = "deserialize_required_option")]
736    #[schemars(with = "crate::NullableSchema<String>", required)]
737    pub recalled: Option<String>,
738}
739#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
740#[serde(deny_unknown_fields)]
741pub struct RunCancelParams {
742    pub run_id: RunId,
743}
744#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
745#[serde(deny_unknown_fields)]
746pub struct RunCancelResult {
747    pub cancelled: bool,
748}
749#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
750#[serde(deny_unknown_fields)]
751pub struct RunToolStdinParams {
752    pub run_id: RunId,
753    pub call_id: ToolCallId,
754    #[serde(skip_serializing_if = "Option::is_none")]
755    pub data: Option<String>,
756    pub eof: bool,
757}
758#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
759#[serde(deny_unknown_fields)]
760pub struct RunToolStdinResult {
761    pub accepted: bool,
762}
763
764#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
765#[serde(deny_unknown_fields)]
766pub struct EventsSubscribeParams {
767    pub session_id: SessionId,
768    #[serde(skip_serializing_if = "Option::is_none")]
769    pub cursor: Option<u64>,
770}
771#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
772#[serde(deny_unknown_fields)]
773pub struct EventsSubscribeResult {
774    pub events: Vec<StoredEvent>,
775}
776
777#[derive(Clone, Debug, Eq, JsonSchema, PartialEq, Serialize)]
778#[serde(deny_unknown_fields)]
779pub struct ApprovalRespondParams {
780    pub session_id: SessionId,
781    pub approval_id: ApprovalId,
782    #[schemars(range(min = 1))]
783    pub request_revision: u64,
784    pub operation_fingerprint: OperationFingerprint,
785    pub client_response_id: ClientResponseId,
786    pub decision: ApprovalUserDecision,
787    #[serde(skip_serializing_if = "Option::is_none")]
788    pub feedback: Option<ApprovalFeedback>,
789}
790impl ApprovalRespondParams {
791    pub fn validate(&self) -> Result<(), &'static str> {
792        if self.request_revision == 0 {
793            return Err("approval request revision must be positive");
794        }
795        if self.feedback.is_some() && self.decision != ApprovalUserDecision::Reject {
796            return Err("approval feedback is allowed only with reject");
797        }
798        Ok(())
799    }
800}
801impl<'de> Deserialize<'de> for ApprovalRespondParams {
802    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
803    where
804        D: serde::Deserializer<'de>,
805    {
806        #[derive(Deserialize)]
807        #[serde(deny_unknown_fields)]
808        struct Wire {
809            session_id: SessionId,
810            approval_id: ApprovalId,
811            request_revision: u64,
812            operation_fingerprint: OperationFingerprint,
813            client_response_id: ClientResponseId,
814            decision: ApprovalUserDecision,
815            feedback: Option<ApprovalFeedback>,
816        }
817        let wire = Wire::deserialize(deserializer)?;
818        let value = Self {
819            session_id: wire.session_id,
820            approval_id: wire.approval_id,
821            request_revision: wire.request_revision,
822            operation_fingerprint: wire.operation_fingerprint,
823            client_response_id: wire.client_response_id,
824            decision: wire.decision,
825            feedback: wire.feedback,
826        };
827        value.validate().map_err(serde::de::Error::custom)?;
828        Ok(value)
829    }
830}
831#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
832#[serde(deny_unknown_fields)]
833pub struct ApprovalRespondResult {
834    pub client_response_id: ClientResponseId,
835    pub approval: ApprovalRecord,
836}
837#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
838#[serde(rename_all = "snake_case")]
839pub enum ApprovalRespondErrorCode {
840    ApprovalNotFound,
841    ApprovalNotPending,
842    ApprovalRevisionConflict,
843    DecisionNotAllowed,
844    OperationFingerprintMismatch,
845    OperationChanged,
846    IdempotencyConflict,
847}
848#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
849#[serde(deny_unknown_fields)]
850pub struct ApprovalRespondError {
851    pub code: ApprovalRespondErrorCode,
852    pub session_id: SessionId,
853    pub approval_id: ApprovalId,
854    pub client_response_id: ClientResponseId,
855    #[serde(skip_serializing_if = "Option::is_none")]
856    pub expected_revision: Option<u64>,
857    #[serde(skip_serializing_if = "Option::is_none")]
858    pub found_revision: Option<u64>,
859}
860#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
861#[serde(deny_unknown_fields)]
862pub struct ApprovalListParams {
863    pub root_session_id: SessionId,
864    #[serde(skip_serializing_if = "Option::is_none")]
865    pub status: Option<ApprovalStatus>,
866}
867#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
868#[serde(deny_unknown_fields)]
869pub struct ApprovalListResult {
870    pub approvals: Vec<ApprovalRecord>,
871    pub tree_grants: Vec<TreeApprovalGrant>,
872}
873
874#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
875#[serde(rename_all = "snake_case")]
876pub enum McpConfigSource {
877    UserFile,
878    WorkspaceFile,
879    Runtime,
880}
881
882#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
883#[serde(rename_all = "snake_case")]
884pub enum McpConfigTarget {
885    UserFile,
886    WorkspaceFile,
887}
888
889#[derive(Clone, Copy, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
890#[serde(rename_all = "snake_case")]
891pub enum McpServerState {
892    Connected,
893    Connecting,
894    Failed,
895    NeedsAuth,
896    Disabled,
897    LazyNotConnected,
898    Disconnected,
899}
900
901#[derive(Clone, Default, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
902#[serde(deny_unknown_fields)]
903pub struct McpOAuthSettingsDefinition {
904    #[serde(default, skip_serializing_if = "Option::is_none")]
905    pub client_id: Option<String>,
906    #[serde(default, skip_serializing_if = "Option::is_none")]
907    pub client_secret: Option<String>,
908    #[serde(default, skip_serializing_if = "Option::is_none")]
909    pub client_metadata_url: Option<String>,
910    #[serde(default, skip_serializing_if = "Option::is_none")]
911    pub scopes: Option<Vec<String>>,
912}
913
914impl std::fmt::Debug for McpOAuthSettingsDefinition {
915    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
916        formatter
917            .debug_struct("McpOAuthSettingsDefinition")
918            .field("client_id", &self.client_id)
919            .field(
920                "client_secret",
921                &self.client_secret.as_ref().map(|_| "[REDACTED]"),
922            )
923            .field("client_metadata_url", &self.client_metadata_url)
924            .field("scopes", &self.scopes)
925            .finish()
926    }
927}
928
929#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
930#[serde(untagged)]
931pub enum McpOAuthDefinition {
932    Bool(bool),
933    Settings(McpOAuthSettingsDefinition),
934}
935
936#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
937#[serde(deny_unknown_fields)]
938pub struct McpServerDefinition {
939    #[serde(deserialize_with = "deserialize_required_option")]
940    #[schemars(with = "crate::NullableSchema<String>", required)]
941    pub command: Option<String>,
942    pub args: Vec<String>,
943    pub env: BTreeMap<String, String>,
944    #[serde(deserialize_with = "deserialize_required_option")]
945    #[schemars(with = "crate::NullableSchema<String>", required)]
946    pub cwd: Option<String>,
947    #[serde(deserialize_with = "deserialize_required_option")]
948    #[schemars(with = "crate::NullableSchema<String>", required)]
949    pub url: Option<String>,
950    pub headers: BTreeMap<String, String>,
951    #[serde(default, skip_serializing_if = "Option::is_none")]
952    pub oauth: Option<McpOAuthDefinition>,
953    pub enabled: bool,
954    pub lazy: bool,
955    #[serde(deserialize_with = "deserialize_required_option")]
956    #[schemars(with = "crate::NullableSchema<u64>", required)]
957    pub timeout_ms: Option<u64>,
958}
959
960#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
961#[serde(deny_unknown_fields)]
962pub struct McpServerInfo {
963    pub name: String,
964    pub source: McpConfigSource,
965    pub definition: McpServerDefinition,
966    pub state: McpServerState,
967    pub tool_count: u32,
968    #[serde(deserialize_with = "deserialize_required_option")]
969    #[schemars(with = "crate::NullableSchema<String>", required)]
970    pub message: Option<String>,
971    #[serde(default, skip_serializing_if = "Option::is_none")]
972    pub auth_in_progress: Option<bool>,
973}
974
975#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
976#[serde(deny_unknown_fields)]
977pub struct McpServerListParams {}
978
979#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
980#[serde(deny_unknown_fields)]
981pub struct McpServerListResult {
982    pub servers: Vec<McpServerInfo>,
983}
984
985#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
986#[serde(deny_unknown_fields)]
987pub struct McpServerAddParams {
988    pub name: String,
989    pub definition: McpServerDefinition,
990}
991
992#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
993#[serde(deny_unknown_fields)]
994pub struct McpServerEditParams {
995    pub name: String,
996    pub definition: McpServerDefinition,
997}
998
999#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1000#[serde(deny_unknown_fields)]
1001pub struct McpServerNameParams {
1002    pub name: String,
1003}
1004
1005#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1006#[serde(deny_unknown_fields)]
1007pub struct McpServerSetEnabledParams {
1008    pub name: String,
1009    pub enabled: bool,
1010}
1011
1012#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1013#[serde(deny_unknown_fields)]
1014pub struct McpServerPersistParams {
1015    pub name: String,
1016    pub target: McpConfigTarget,
1017}
1018
1019#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1020#[serde(deny_unknown_fields)]
1021pub struct McpServerMutationResult {
1022    #[serde(deserialize_with = "deserialize_required_option")]
1023    #[schemars(with = "crate::NullableSchema<McpServerInfo>", required)]
1024    pub server: Option<McpServerInfo>,
1025}
1026
1027#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1028#[serde(deny_unknown_fields)]
1029pub struct McpAuthBeginParams {
1030    pub server: String,
1031}
1032
1033#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1034#[serde(deny_unknown_fields)]
1035pub struct McpAuthBeginResult {
1036    pub server: String,
1037    pub authorization_url: String,
1038}
1039
1040#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1041#[serde(deny_unknown_fields)]
1042pub struct McpAuthCancelParams {
1043    pub server: String,
1044}
1045
1046#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
1047#[serde(deny_unknown_fields)]
1048pub struct McpAuthCancelResult {
1049    pub server: String,
1050}