Skip to main content

agent_client_protocol_schema/v2/
nes.rs

1//! Next Edit Suggestions (NES) types and constants.
2//!
3//! NES allows agents to provide predictive code edits via capability negotiation,
4//! document events, and a suggestion request/response flow. NES sessions are
5//! independent of chat sessions and have their own lifecycle.
6
7use std::collections::BTreeMap;
8
9use schemars::{JsonSchema, Schema};
10use serde::{Deserialize, Serialize};
11use serde_with::{DefaultOnError, VecSkipError, serde_as, skip_serializing_none};
12
13use super::{Meta, SessionId};
14use crate::{IntoOption, SkipListener};
15
16// Method name constants
17
18/// Method name for starting an NES session.
19pub(crate) const NES_START_METHOD_NAME: &str = "nes/start";
20/// Method name for requesting a suggestion.
21pub(crate) const NES_SUGGEST_METHOD_NAME: &str = "nes/suggest";
22/// Method name for accepting a suggestion.
23pub(crate) const NES_ACCEPT_METHOD_NAME: &str = "nes/accept";
24/// Method name for rejecting a suggestion.
25pub(crate) const NES_REJECT_METHOD_NAME: &str = "nes/reject";
26/// Method name for closing an NES session.
27pub(crate) const NES_CLOSE_METHOD_NAME: &str = "nes/close";
28/// Notification name for document open events.
29pub(crate) const DOCUMENT_DID_OPEN_METHOD_NAME: &str = "document/didOpen";
30/// Notification name for document change events.
31pub(crate) const DOCUMENT_DID_CHANGE_METHOD_NAME: &str = "document/didChange";
32/// Notification name for document close events.
33pub(crate) const DOCUMENT_DID_CLOSE_METHOD_NAME: &str = "document/didClose";
34/// Notification name for document save events.
35pub(crate) const DOCUMENT_DID_SAVE_METHOD_NAME: &str = "document/didSave";
36/// Notification name for document focus events.
37pub(crate) const DOCUMENT_DID_FOCUS_METHOD_NAME: &str = "document/didFocus";
38
39// Position primitives
40
41/// The encoding used for character offsets in positions.
42///
43/// Follows the same conventions as LSP 3.17. The default is UTF-16.
44#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
45#[non_exhaustive]
46pub enum PositionEncodingKind {
47    /// Character offsets count UTF-16 code units. This is the default.
48    #[serde(rename = "utf-16")]
49    Utf16,
50    /// Character offsets count Unicode code points.
51    #[serde(rename = "utf-32")]
52    Utf32,
53    /// Character offsets count UTF-8 code units (bytes).
54    #[serde(rename = "utf-8")]
55    Utf8,
56}
57
58/// A zero-based position in a text document.
59///
60/// The meaning of `character` depends on the negotiated position encoding.
61#[skip_serializing_none]
62#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
63#[serde(rename_all = "camelCase")]
64#[non_exhaustive]
65pub struct Position {
66    /// Zero-based line number.
67    pub line: u32,
68    /// Zero-based character offset (encoding-dependent).
69    pub character: u32,
70    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
71    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
72    /// these keys.
73    ///
74    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
75    #[serde(rename = "_meta")]
76    pub meta: Option<Meta>,
77}
78
79impl Position {
80    /// Builds a [`Position`] from protocol coordinate values.
81    #[must_use]
82    pub fn new(line: u32, character: u32) -> Self {
83        Self {
84            line,
85            character,
86            meta: None,
87        }
88    }
89
90    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
91    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
92    /// these keys.
93    ///
94    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
95    #[must_use]
96    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
97        self.meta = meta.into_option();
98        self
99    }
100}
101
102/// A range in a text document, expressed as start and end positions.
103#[skip_serializing_none]
104#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
105#[serde(rename_all = "camelCase")]
106#[non_exhaustive]
107pub struct Range {
108    /// The start position (inclusive).
109    pub start: Position,
110    /// The end position (exclusive).
111    pub end: Position,
112    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
113    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
114    /// these keys.
115    ///
116    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
117    #[serde(rename = "_meta")]
118    pub meta: Option<Meta>,
119}
120
121impl Range {
122    /// Builds a [`Range`] from protocol coordinate values.
123    #[must_use]
124    pub fn new(start: Position, end: Position) -> Self {
125        Self {
126            start,
127            end,
128            meta: None,
129        }
130    }
131
132    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
133    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
134    /// these keys.
135    ///
136    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
137    #[must_use]
138    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
139        self.meta = meta.into_option();
140        self
141    }
142}
143
144// Agent NES capabilities
145
146/// NES capabilities advertised by the agent during initialization.
147///
148/// Supplying `{}` means the agent supports the NES method surface. Omitted or
149/// `null` both mean the agent does not advertise support for `nes/*` methods.
150#[serde_as]
151#[skip_serializing_none]
152#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
153#[serde(rename_all = "camelCase")]
154#[non_exhaustive]
155pub struct NesCapabilities {
156    /// Events the agent wants to receive.
157    #[serde_as(deserialize_as = "DefaultOnError")]
158    #[schemars(extend("x-deserialize-default-on-error" = true))]
159    #[serde(default)]
160    pub events: Option<NesEventCapabilities>,
161    /// Context the agent wants attached to each suggestion request.
162    #[serde_as(deserialize_as = "DefaultOnError")]
163    #[schemars(extend("x-deserialize-default-on-error" = true))]
164    #[serde(default)]
165    pub context: Option<NesContextCapabilities>,
166    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
167    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
168    /// these keys.
169    ///
170    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
171    #[serde(rename = "_meta")]
172    pub meta: Option<Meta>,
173}
174
175impl NesCapabilities {
176    /// Builds an empty [`NesCapabilities`]; use builder methods to advertise supported sub-capabilities.
177    #[must_use]
178    pub fn new() -> Self {
179        Self::default()
180    }
181
182    /// Sets or clears the optional `events` field.
183    #[must_use]
184    pub fn events(mut self, events: impl IntoOption<NesEventCapabilities>) -> Self {
185        self.events = events.into_option();
186        self
187    }
188
189    /// Sets or clears the optional `context` field.
190    #[must_use]
191    pub fn context(mut self, context: impl IntoOption<NesContextCapabilities>) -> Self {
192        self.context = context.into_option();
193        self
194    }
195
196    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
197    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
198    /// these keys.
199    ///
200    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
201    #[must_use]
202    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
203        self.meta = meta.into_option();
204        self
205    }
206}
207
208/// Event capabilities the agent can consume.
209#[serde_as]
210#[skip_serializing_none]
211#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
212#[serde(rename_all = "camelCase")]
213#[non_exhaustive]
214pub struct NesEventCapabilities {
215    /// Document event capabilities.
216    #[serde_as(deserialize_as = "DefaultOnError")]
217    #[schemars(extend("x-deserialize-default-on-error" = true))]
218    #[serde(default)]
219    pub document: Option<NesDocumentEventCapabilities>,
220    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
221    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
222    /// these keys.
223    ///
224    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
225    #[serde(rename = "_meta")]
226    pub meta: Option<Meta>,
227}
228
229impl NesEventCapabilities {
230    /// Builds an empty [`NesEventCapabilities`]; use builder methods to advertise supported sub-capabilities.
231    #[must_use]
232    pub fn new() -> Self {
233        Self::default()
234    }
235
236    /// Sets or clears the optional `document` field.
237    #[must_use]
238    pub fn document(mut self, document: impl IntoOption<NesDocumentEventCapabilities>) -> Self {
239        self.document = document.into_option();
240        self
241    }
242
243    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
244    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
245    /// these keys.
246    ///
247    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
248    #[must_use]
249    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
250        self.meta = meta.into_option();
251        self
252    }
253}
254
255/// Document event capabilities the agent wants to receive.
256#[serde_as]
257#[skip_serializing_none]
258#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
259#[serde(rename_all = "camelCase")]
260#[non_exhaustive]
261pub struct NesDocumentEventCapabilities {
262    /// Whether the agent wants `document/didOpen` events.
263    #[serde_as(deserialize_as = "DefaultOnError")]
264    #[schemars(extend("x-deserialize-default-on-error" = true))]
265    #[serde(default)]
266    pub did_open: Option<NesDocumentDidOpenCapabilities>,
267    /// Whether the agent wants `document/didChange` events, and the sync kind.
268    #[serde_as(deserialize_as = "DefaultOnError")]
269    #[schemars(extend("x-deserialize-default-on-error" = true))]
270    #[serde(default)]
271    pub did_change: Option<NesDocumentDidChangeCapabilities>,
272    /// Whether the agent wants `document/didClose` events.
273    #[serde_as(deserialize_as = "DefaultOnError")]
274    #[schemars(extend("x-deserialize-default-on-error" = true))]
275    #[serde(default)]
276    pub did_close: Option<NesDocumentDidCloseCapabilities>,
277    /// Whether the agent wants `document/didSave` events.
278    #[serde_as(deserialize_as = "DefaultOnError")]
279    #[schemars(extend("x-deserialize-default-on-error" = true))]
280    #[serde(default)]
281    pub did_save: Option<NesDocumentDidSaveCapabilities>,
282    /// Whether the agent wants `document/didFocus` events.
283    #[serde_as(deserialize_as = "DefaultOnError")]
284    #[schemars(extend("x-deserialize-default-on-error" = true))]
285    #[serde(default)]
286    pub did_focus: Option<NesDocumentDidFocusCapabilities>,
287    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
288    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
289    /// these keys.
290    ///
291    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
292    #[serde(rename = "_meta")]
293    pub meta: Option<Meta>,
294}
295
296impl NesDocumentEventCapabilities {
297    /// Builds an empty [`NesDocumentEventCapabilities`]; use builder methods to advertise supported sub-capabilities.
298    #[must_use]
299    pub fn new() -> Self {
300        Self::default()
301    }
302
303    /// Sets or clears the optional `didOpen` field.
304    #[must_use]
305    pub fn did_open(mut self, did_open: impl IntoOption<NesDocumentDidOpenCapabilities>) -> Self {
306        self.did_open = did_open.into_option();
307        self
308    }
309
310    /// Sets or clears the optional `didChange` field.
311    #[must_use]
312    pub fn did_change(
313        mut self,
314        did_change: impl IntoOption<NesDocumentDidChangeCapabilities>,
315    ) -> Self {
316        self.did_change = did_change.into_option();
317        self
318    }
319
320    /// Sets or clears the optional `didClose` field.
321    #[must_use]
322    pub fn did_close(
323        mut self,
324        did_close: impl IntoOption<NesDocumentDidCloseCapabilities>,
325    ) -> Self {
326        self.did_close = did_close.into_option();
327        self
328    }
329
330    /// Sets or clears the optional `didSave` field.
331    #[must_use]
332    pub fn did_save(mut self, did_save: impl IntoOption<NesDocumentDidSaveCapabilities>) -> Self {
333        self.did_save = did_save.into_option();
334        self
335    }
336
337    /// Sets or clears the optional `didFocus` field.
338    #[must_use]
339    pub fn did_focus(
340        mut self,
341        did_focus: impl IntoOption<NesDocumentDidFocusCapabilities>,
342    ) -> Self {
343        self.did_focus = did_focus.into_option();
344        self
345    }
346
347    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
348    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
349    /// these keys.
350    ///
351    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
352    #[must_use]
353    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
354        self.meta = meta.into_option();
355        self
356    }
357}
358
359/// Marker for `document/didOpen` capability support.
360#[skip_serializing_none]
361#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
362#[serde(rename_all = "camelCase")]
363#[non_exhaustive]
364pub struct NesDocumentDidOpenCapabilities {
365    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
366    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
367    /// these keys.
368    ///
369    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
370    #[serde(rename = "_meta")]
371    pub meta: Option<Meta>,
372}
373
374impl NesDocumentDidOpenCapabilities {
375    /// Builds an empty [`NesDocumentDidOpenCapabilities`]; use builder methods to advertise supported sub-capabilities.
376    #[must_use]
377    pub fn new() -> Self {
378        Self::default()
379    }
380}
381
382/// Capabilities for `document/didChange` events.
383#[skip_serializing_none]
384#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
385#[serde(rename_all = "camelCase")]
386#[non_exhaustive]
387pub struct NesDocumentDidChangeCapabilities {
388    /// The sync kind the agent wants: `"full"` or `"incremental"`.
389    pub sync_kind: TextDocumentSyncKind,
390    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
391    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
392    /// these keys.
393    ///
394    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
395    #[serde(rename = "_meta")]
396    pub meta: Option<Meta>,
397}
398
399impl NesDocumentDidChangeCapabilities {
400    /// Builds an empty [`NesDocumentDidChangeCapabilities`]; use builder methods to advertise supported sub-capabilities.
401    #[must_use]
402    pub fn new(sync_kind: TextDocumentSyncKind) -> Self {
403        Self {
404            sync_kind,
405            meta: None,
406        }
407    }
408
409    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
410    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
411    /// these keys.
412    ///
413    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
414    #[must_use]
415    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
416        self.meta = meta.into_option();
417        self
418    }
419}
420
421/// How the agent wants document changes delivered.
422#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
423#[non_exhaustive]
424pub enum TextDocumentSyncKind {
425    /// Client sends the entire file content on each change.
426    #[serde(rename = "full")]
427    Full,
428    /// Client sends only the changed ranges.
429    #[serde(rename = "incremental")]
430    Incremental,
431}
432
433/// Marker for `document/didClose` capability support.
434#[skip_serializing_none]
435#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
436#[serde(rename_all = "camelCase")]
437#[non_exhaustive]
438pub struct NesDocumentDidCloseCapabilities {
439    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
440    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
441    /// these keys.
442    ///
443    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
444    #[serde(rename = "_meta")]
445    pub meta: Option<Meta>,
446}
447
448impl NesDocumentDidCloseCapabilities {
449    /// Builds an empty [`NesDocumentDidCloseCapabilities`]; use builder methods to advertise supported sub-capabilities.
450    #[must_use]
451    pub fn new() -> Self {
452        Self::default()
453    }
454}
455
456/// Marker for `document/didSave` capability support.
457#[skip_serializing_none]
458#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
459#[serde(rename_all = "camelCase")]
460#[non_exhaustive]
461pub struct NesDocumentDidSaveCapabilities {
462    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
463    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
464    /// these keys.
465    ///
466    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
467    #[serde(rename = "_meta")]
468    pub meta: Option<Meta>,
469}
470
471impl NesDocumentDidSaveCapabilities {
472    /// Builds an empty [`NesDocumentDidSaveCapabilities`]; use builder methods to advertise supported sub-capabilities.
473    #[must_use]
474    pub fn new() -> Self {
475        Self::default()
476    }
477}
478
479/// Marker for `document/didFocus` capability support.
480#[skip_serializing_none]
481#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
482#[serde(rename_all = "camelCase")]
483#[non_exhaustive]
484pub struct NesDocumentDidFocusCapabilities {
485    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
486    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
487    /// these keys.
488    ///
489    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
490    #[serde(rename = "_meta")]
491    pub meta: Option<Meta>,
492}
493
494impl NesDocumentDidFocusCapabilities {
495    /// Builds an empty [`NesDocumentDidFocusCapabilities`]; use builder methods to advertise supported sub-capabilities.
496    #[must_use]
497    pub fn new() -> Self {
498        Self::default()
499    }
500}
501
502/// Context capabilities the agent wants attached to each suggestion request.
503#[serde_as]
504#[skip_serializing_none]
505#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
506#[serde(rename_all = "camelCase")]
507#[non_exhaustive]
508pub struct NesContextCapabilities {
509    /// Whether the agent wants recent files context.
510    #[serde_as(deserialize_as = "DefaultOnError")]
511    #[schemars(extend("x-deserialize-default-on-error" = true))]
512    #[serde(default)]
513    pub recent_files: Option<NesRecentFilesCapabilities>,
514    /// Whether the agent wants related snippets context.
515    #[serde_as(deserialize_as = "DefaultOnError")]
516    #[schemars(extend("x-deserialize-default-on-error" = true))]
517    #[serde(default)]
518    pub related_snippets: Option<NesRelatedSnippetsCapabilities>,
519    /// Whether the agent wants edit history context.
520    #[serde_as(deserialize_as = "DefaultOnError")]
521    #[schemars(extend("x-deserialize-default-on-error" = true))]
522    #[serde(default)]
523    pub edit_history: Option<NesEditHistoryCapabilities>,
524    /// Whether the agent wants user actions context.
525    #[serde_as(deserialize_as = "DefaultOnError")]
526    #[schemars(extend("x-deserialize-default-on-error" = true))]
527    #[serde(default)]
528    pub user_actions: Option<NesUserActionsCapabilities>,
529    /// Whether the agent wants open files context.
530    #[serde_as(deserialize_as = "DefaultOnError")]
531    #[schemars(extend("x-deserialize-default-on-error" = true))]
532    #[serde(default)]
533    pub open_files: Option<NesOpenFilesCapabilities>,
534    /// Whether the agent wants diagnostics context.
535    #[serde_as(deserialize_as = "DefaultOnError")]
536    #[schemars(extend("x-deserialize-default-on-error" = true))]
537    #[serde(default)]
538    pub diagnostics: Option<NesDiagnosticsCapabilities>,
539    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
540    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
541    /// these keys.
542    ///
543    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
544    #[serde(rename = "_meta")]
545    pub meta: Option<Meta>,
546}
547
548impl NesContextCapabilities {
549    /// Builds an empty [`NesContextCapabilities`]; use builder methods to advertise supported sub-capabilities.
550    #[must_use]
551    pub fn new() -> Self {
552        Self::default()
553    }
554
555    /// Sets or clears the optional `recentFiles` field.
556    #[must_use]
557    pub fn recent_files(
558        mut self,
559        recent_files: impl IntoOption<NesRecentFilesCapabilities>,
560    ) -> Self {
561        self.recent_files = recent_files.into_option();
562        self
563    }
564
565    /// Sets or clears the optional `relatedSnippets` field.
566    #[must_use]
567    pub fn related_snippets(
568        mut self,
569        related_snippets: impl IntoOption<NesRelatedSnippetsCapabilities>,
570    ) -> Self {
571        self.related_snippets = related_snippets.into_option();
572        self
573    }
574
575    /// Sets or clears the optional `editHistory` field.
576    #[must_use]
577    pub fn edit_history(
578        mut self,
579        edit_history: impl IntoOption<NesEditHistoryCapabilities>,
580    ) -> Self {
581        self.edit_history = edit_history.into_option();
582        self
583    }
584
585    /// Sets or clears the optional `userActions` field.
586    #[must_use]
587    pub fn user_actions(
588        mut self,
589        user_actions: impl IntoOption<NesUserActionsCapabilities>,
590    ) -> Self {
591        self.user_actions = user_actions.into_option();
592        self
593    }
594
595    /// Sets or clears the optional `openFiles` field.
596    #[must_use]
597    pub fn open_files(mut self, open_files: impl IntoOption<NesOpenFilesCapabilities>) -> Self {
598        self.open_files = open_files.into_option();
599        self
600    }
601
602    /// Sets or clears the optional `diagnostics` field.
603    #[must_use]
604    pub fn diagnostics(mut self, diagnostics: impl IntoOption<NesDiagnosticsCapabilities>) -> Self {
605        self.diagnostics = diagnostics.into_option();
606        self
607    }
608
609    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
610    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
611    /// these keys.
612    ///
613    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
614    #[must_use]
615    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
616        self.meta = meta.into_option();
617        self
618    }
619}
620
621/// Capabilities for recent files context.
622#[skip_serializing_none]
623#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
624#[serde(rename_all = "camelCase")]
625#[non_exhaustive]
626pub struct NesRecentFilesCapabilities {
627    /// Maximum number of recent files the agent can use.
628    pub max_count: Option<u32>,
629    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
630    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
631    /// these keys.
632    ///
633    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
634    #[serde(rename = "_meta")]
635    pub meta: Option<Meta>,
636}
637
638impl NesRecentFilesCapabilities {
639    /// Builds an empty [`NesRecentFilesCapabilities`]; use builder methods to advertise supported sub-capabilities.
640    #[must_use]
641    pub fn new() -> Self {
642        Self::default()
643    }
644}
645
646/// Capabilities for related snippets context.
647#[skip_serializing_none]
648#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
649#[serde(rename_all = "camelCase")]
650#[non_exhaustive]
651pub struct NesRelatedSnippetsCapabilities {
652    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
653    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
654    /// these keys.
655    ///
656    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
657    #[serde(rename = "_meta")]
658    pub meta: Option<Meta>,
659}
660
661impl NesRelatedSnippetsCapabilities {
662    /// Builds an empty [`NesRelatedSnippetsCapabilities`]; use builder methods to advertise supported sub-capabilities.
663    #[must_use]
664    pub fn new() -> Self {
665        Self::default()
666    }
667}
668
669/// Capabilities for edit history context.
670#[skip_serializing_none]
671#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
672#[serde(rename_all = "camelCase")]
673#[non_exhaustive]
674pub struct NesEditHistoryCapabilities {
675    /// Maximum number of edit history entries the agent can use.
676    pub max_count: Option<u32>,
677    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
678    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
679    /// these keys.
680    ///
681    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
682    #[serde(rename = "_meta")]
683    pub meta: Option<Meta>,
684}
685
686impl NesEditHistoryCapabilities {
687    /// Builds an empty [`NesEditHistoryCapabilities`]; use builder methods to advertise supported sub-capabilities.
688    #[must_use]
689    pub fn new() -> Self {
690        Self::default()
691    }
692}
693
694/// Capabilities for user actions context.
695#[skip_serializing_none]
696#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
697#[serde(rename_all = "camelCase")]
698#[non_exhaustive]
699pub struct NesUserActionsCapabilities {
700    /// Maximum number of user actions the agent can use.
701    pub max_count: Option<u32>,
702    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
703    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
704    /// these keys.
705    ///
706    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
707    #[serde(rename = "_meta")]
708    pub meta: Option<Meta>,
709}
710
711impl NesUserActionsCapabilities {
712    /// Builds an empty [`NesUserActionsCapabilities`]; use builder methods to advertise supported sub-capabilities.
713    #[must_use]
714    pub fn new() -> Self {
715        Self::default()
716    }
717}
718
719/// Capabilities for open files context.
720#[skip_serializing_none]
721#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
722#[serde(rename_all = "camelCase")]
723#[non_exhaustive]
724pub struct NesOpenFilesCapabilities {
725    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
726    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
727    /// these keys.
728    ///
729    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
730    #[serde(rename = "_meta")]
731    pub meta: Option<Meta>,
732}
733
734impl NesOpenFilesCapabilities {
735    /// Builds an empty [`NesOpenFilesCapabilities`]; use builder methods to advertise supported sub-capabilities.
736    #[must_use]
737    pub fn new() -> Self {
738        Self::default()
739    }
740}
741
742/// Capabilities for diagnostics context.
743#[skip_serializing_none]
744#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
745#[serde(rename_all = "camelCase")]
746#[non_exhaustive]
747pub struct NesDiagnosticsCapabilities {
748    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
749    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
750    /// these keys.
751    ///
752    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
753    #[serde(rename = "_meta")]
754    pub meta: Option<Meta>,
755}
756
757impl NesDiagnosticsCapabilities {
758    /// Builds an empty [`NesDiagnosticsCapabilities`]; use builder methods to advertise supported sub-capabilities.
759    #[must_use]
760    pub fn new() -> Self {
761        Self::default()
762    }
763}
764
765// Client NES capabilities
766
767/// NES capabilities advertised by the client during initialization.
768#[serde_as]
769#[skip_serializing_none]
770#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
771#[serde(rename_all = "camelCase")]
772#[non_exhaustive]
773pub struct ClientNesCapabilities {
774    /// Whether the client supports the `jump` suggestion kind.
775    #[serde_as(deserialize_as = "DefaultOnError")]
776    #[schemars(extend("x-deserialize-default-on-error" = true))]
777    #[serde(default)]
778    pub jump: Option<NesJumpCapabilities>,
779    /// Whether the client supports the `rename` suggestion kind.
780    #[serde_as(deserialize_as = "DefaultOnError")]
781    #[schemars(extend("x-deserialize-default-on-error" = true))]
782    #[serde(default)]
783    pub rename: Option<NesRenameCapabilities>,
784    /// Whether the client supports the `searchAndReplace` suggestion kind.
785    #[serde_as(deserialize_as = "DefaultOnError")]
786    #[schemars(extend("x-deserialize-default-on-error" = true))]
787    #[serde(default)]
788    pub search_and_replace: Option<NesSearchAndReplaceCapabilities>,
789    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
790    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
791    /// these keys.
792    ///
793    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
794    #[serde(rename = "_meta")]
795    pub meta: Option<Meta>,
796}
797
798impl ClientNesCapabilities {
799    /// Builds an empty [`ClientNesCapabilities`]; use builder methods to advertise supported sub-capabilities.
800    #[must_use]
801    pub fn new() -> Self {
802        Self::default()
803    }
804
805    /// Sets or clears the optional `jump` field.
806    #[must_use]
807    pub fn jump(mut self, jump: impl IntoOption<NesJumpCapabilities>) -> Self {
808        self.jump = jump.into_option();
809        self
810    }
811
812    /// Sets or clears the optional `rename` field.
813    #[must_use]
814    pub fn rename(mut self, rename: impl IntoOption<NesRenameCapabilities>) -> Self {
815        self.rename = rename.into_option();
816        self
817    }
818
819    /// Sets or clears the optional `searchAndReplace` field.
820    #[must_use]
821    pub fn search_and_replace(
822        mut self,
823        search_and_replace: impl IntoOption<NesSearchAndReplaceCapabilities>,
824    ) -> Self {
825        self.search_and_replace = search_and_replace.into_option();
826        self
827    }
828
829    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
830    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
831    /// these keys.
832    ///
833    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
834    #[must_use]
835    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
836        self.meta = meta.into_option();
837        self
838    }
839}
840
841/// Marker for jump suggestion support.
842#[skip_serializing_none]
843#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
844#[serde(rename_all = "camelCase")]
845#[non_exhaustive]
846pub struct NesJumpCapabilities {
847    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
848    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
849    /// these keys.
850    ///
851    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
852    #[serde(rename = "_meta")]
853    pub meta: Option<Meta>,
854}
855
856impl NesJumpCapabilities {
857    /// Builds an empty [`NesJumpCapabilities`]; use builder methods to advertise supported sub-capabilities.
858    #[must_use]
859    pub fn new() -> Self {
860        Self::default()
861    }
862}
863
864/// Marker for rename suggestion support.
865#[skip_serializing_none]
866#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
867#[serde(rename_all = "camelCase")]
868#[non_exhaustive]
869pub struct NesRenameCapabilities {
870    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
871    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
872    /// these keys.
873    ///
874    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
875    #[serde(rename = "_meta")]
876    pub meta: Option<Meta>,
877}
878
879impl NesRenameCapabilities {
880    /// Builds an empty [`NesRenameCapabilities`]; use builder methods to advertise supported sub-capabilities.
881    #[must_use]
882    pub fn new() -> Self {
883        Self::default()
884    }
885}
886
887/// Marker for search and replace suggestion support.
888#[skip_serializing_none]
889#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
890#[serde(rename_all = "camelCase")]
891#[non_exhaustive]
892pub struct NesSearchAndReplaceCapabilities {
893    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
894    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
895    /// these keys.
896    ///
897    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
898    #[serde(rename = "_meta")]
899    pub meta: Option<Meta>,
900}
901
902impl NesSearchAndReplaceCapabilities {
903    /// Builds an empty [`NesSearchAndReplaceCapabilities`]; use builder methods to advertise supported sub-capabilities.
904    #[must_use]
905    pub fn new() -> Self {
906        Self::default()
907    }
908}
909
910// Document event notifications (client -> agent)
911
912/// Notification sent when a file is opened in the editor.
913#[skip_serializing_none]
914#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
915#[schemars(extend("x-side" = "agent", "x-method" = DOCUMENT_DID_OPEN_METHOD_NAME))]
916#[serde(rename_all = "camelCase")]
917#[non_exhaustive]
918pub struct DidOpenDocumentNotification {
919    /// The session ID for this notification.
920    pub session_id: SessionId,
921    /// The URI of the opened document.
922    pub uri: String,
923    /// The language identifier of the document (e.g., "rust", "python").
924    pub language_id: String,
925    /// The version number of the document.
926    pub version: i64,
927    /// The full text content of the document.
928    pub text: String,
929    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
930    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
931    /// these keys.
932    ///
933    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
934    #[serde(rename = "_meta")]
935    pub meta: Option<Meta>,
936}
937
938impl DidOpenDocumentNotification {
939    /// Builds [`DidOpenDocumentNotification`] with the required notification fields set; optional fields start unset or empty.
940    #[must_use]
941    pub fn new(
942        session_id: impl Into<SessionId>,
943        uri: impl Into<String>,
944        language_id: impl Into<String>,
945        version: i64,
946        text: impl Into<String>,
947    ) -> Self {
948        Self {
949            session_id: session_id.into(),
950            uri: uri.into(),
951            language_id: language_id.into(),
952            version,
953            text: text.into(),
954            meta: None,
955        }
956    }
957
958    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
959    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
960    /// these keys.
961    ///
962    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
963    #[must_use]
964    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
965        self.meta = meta.into_option();
966        self
967    }
968}
969
970/// Notification sent when a file is edited.
971#[skip_serializing_none]
972#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
973#[schemars(extend("x-side" = "agent", "x-method" = DOCUMENT_DID_CHANGE_METHOD_NAME))]
974#[serde(rename_all = "camelCase")]
975#[non_exhaustive]
976pub struct DidChangeDocumentNotification {
977    /// The session ID for this notification.
978    pub session_id: SessionId,
979    /// The URI of the changed document.
980    pub uri: String,
981    /// The new version number of the document.
982    pub version: i64,
983    /// The content changes.
984    pub content_changes: Vec<TextDocumentContentChangeEvent>,
985    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
986    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
987    /// these keys.
988    ///
989    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
990    #[serde(rename = "_meta")]
991    pub meta: Option<Meta>,
992}
993
994impl DidChangeDocumentNotification {
995    /// Builds [`DidChangeDocumentNotification`] with the required notification fields set; optional fields start unset or empty.
996    #[must_use]
997    pub fn new(
998        session_id: impl Into<SessionId>,
999        uri: impl Into<String>,
1000        version: i64,
1001        content_changes: Vec<TextDocumentContentChangeEvent>,
1002    ) -> Self {
1003        Self {
1004            session_id: session_id.into(),
1005            uri: uri.into(),
1006            version,
1007            content_changes,
1008            meta: None,
1009        }
1010    }
1011
1012    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1013    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1014    /// these keys.
1015    ///
1016    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1017    #[must_use]
1018    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1019        self.meta = meta.into_option();
1020        self
1021    }
1022}
1023
1024/// A content change event for a document.
1025///
1026/// When `range` is `None`, `text` is the full content of the document.
1027/// When `range` is `Some`, `text` replaces the given range.
1028#[skip_serializing_none]
1029#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1030#[serde(rename_all = "camelCase")]
1031#[non_exhaustive]
1032pub struct TextDocumentContentChangeEvent {
1033    /// The range of the document that changed. If `None`, the entire content is replaced.
1034    pub range: Option<Range>,
1035    /// The new text for the range, or the full document content if `range` is `None`.
1036    pub text: String,
1037    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1038    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1039    /// these keys.
1040    ///
1041    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1042    #[serde(rename = "_meta")]
1043    pub meta: Option<Meta>,
1044}
1045
1046impl TextDocumentContentChangeEvent {
1047    /// Builds a full-document change event that replaces the entire document text.
1048    #[must_use]
1049    pub fn full(text: impl Into<String>) -> Self {
1050        Self {
1051            range: None,
1052            text: text.into(),
1053            meta: None,
1054        }
1055    }
1056
1057    /// Builds an incremental document change event for a specific text range.
1058    #[must_use]
1059    pub fn incremental(range: Range, text: impl Into<String>) -> Self {
1060        Self {
1061            range: Some(range),
1062            text: text.into(),
1063            meta: None,
1064        }
1065    }
1066
1067    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1068    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1069    /// these keys.
1070    ///
1071    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1072    #[must_use]
1073    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1074        self.meta = meta.into_option();
1075        self
1076    }
1077}
1078
1079/// Notification sent when a file is closed.
1080#[skip_serializing_none]
1081#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1082#[schemars(extend("x-side" = "agent", "x-method" = DOCUMENT_DID_CLOSE_METHOD_NAME))]
1083#[serde(rename_all = "camelCase")]
1084#[non_exhaustive]
1085pub struct DidCloseDocumentNotification {
1086    /// The session ID for this notification.
1087    pub session_id: SessionId,
1088    /// The URI of the closed document.
1089    pub uri: String,
1090    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1091    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1092    /// these keys.
1093    ///
1094    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1095    #[serde(rename = "_meta")]
1096    pub meta: Option<Meta>,
1097}
1098
1099impl DidCloseDocumentNotification {
1100    /// Builds [`DidCloseDocumentNotification`] with the required notification fields set; optional fields start unset or empty.
1101    #[must_use]
1102    pub fn new(session_id: impl Into<SessionId>, uri: impl Into<String>) -> Self {
1103        Self {
1104            session_id: session_id.into(),
1105            uri: uri.into(),
1106            meta: None,
1107        }
1108    }
1109
1110    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1111    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1112    /// these keys.
1113    ///
1114    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1115    #[must_use]
1116    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1117        self.meta = meta.into_option();
1118        self
1119    }
1120}
1121
1122/// Notification sent when a file is saved.
1123#[skip_serializing_none]
1124#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1125#[schemars(extend("x-side" = "agent", "x-method" = DOCUMENT_DID_SAVE_METHOD_NAME))]
1126#[serde(rename_all = "camelCase")]
1127#[non_exhaustive]
1128pub struct DidSaveDocumentNotification {
1129    /// The session ID for this notification.
1130    pub session_id: SessionId,
1131    /// The URI of the saved document.
1132    pub uri: String,
1133    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1134    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1135    /// these keys.
1136    ///
1137    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1138    #[serde(rename = "_meta")]
1139    pub meta: Option<Meta>,
1140}
1141
1142impl DidSaveDocumentNotification {
1143    /// Builds [`DidSaveDocumentNotification`] with the required notification fields set; optional fields start unset or empty.
1144    #[must_use]
1145    pub fn new(session_id: impl Into<SessionId>, uri: impl Into<String>) -> Self {
1146        Self {
1147            session_id: session_id.into(),
1148            uri: uri.into(),
1149            meta: None,
1150        }
1151    }
1152
1153    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1154    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1155    /// these keys.
1156    ///
1157    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1158    #[must_use]
1159    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1160        self.meta = meta.into_option();
1161        self
1162    }
1163}
1164
1165/// Notification sent when a file becomes the active editor tab.
1166#[skip_serializing_none]
1167#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1168#[schemars(extend("x-side" = "agent", "x-method" = DOCUMENT_DID_FOCUS_METHOD_NAME))]
1169#[serde(rename_all = "camelCase")]
1170#[non_exhaustive]
1171pub struct DidFocusDocumentNotification {
1172    /// The session ID for this notification.
1173    pub session_id: SessionId,
1174    /// The URI of the focused document.
1175    pub uri: String,
1176    /// The version number of the document.
1177    pub version: i64,
1178    /// The current cursor position.
1179    pub position: Position,
1180    /// The portion of the file currently visible in the editor viewport.
1181    pub visible_range: Range,
1182    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1183    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1184    /// these keys.
1185    ///
1186    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1187    #[serde(rename = "_meta")]
1188    pub meta: Option<Meta>,
1189}
1190
1191impl DidFocusDocumentNotification {
1192    /// Builds [`DidFocusDocumentNotification`] with the required notification fields set; optional fields start unset or empty.
1193    #[must_use]
1194    pub fn new(
1195        session_id: impl Into<SessionId>,
1196        uri: impl Into<String>,
1197        version: i64,
1198        position: Position,
1199        visible_range: Range,
1200    ) -> Self {
1201        Self {
1202            session_id: session_id.into(),
1203            uri: uri.into(),
1204            version,
1205            position,
1206            visible_range,
1207            meta: None,
1208        }
1209    }
1210
1211    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1212    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1213    /// these keys.
1214    ///
1215    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1216    #[must_use]
1217    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1218        self.meta = meta.into_option();
1219        self
1220    }
1221}
1222
1223// NES session start
1224
1225/// Request to start an NES session.
1226#[serde_as]
1227#[skip_serializing_none]
1228#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1229#[schemars(extend("x-side" = "agent", "x-method" = NES_START_METHOD_NAME))]
1230#[serde(rename_all = "camelCase")]
1231#[non_exhaustive]
1232pub struct StartNesRequest {
1233    /// The root URI of the workspace.
1234    pub workspace_uri: Option<String>,
1235    /// The workspace folders.
1236    #[serde_as(deserialize_as = "DefaultOnError<Option<VecSkipError<_, SkipListener>>>")]
1237    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1238    #[serde(default)]
1239    pub workspace_folders: Option<Vec<WorkspaceFolder>>,
1240    /// Repository metadata, if the workspace is a git repository.
1241    #[serde_as(deserialize_as = "DefaultOnError")]
1242    #[schemars(extend("x-deserialize-default-on-error" = true))]
1243    #[serde(default)]
1244    pub repository: Option<NesRepository>,
1245    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1246    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1247    /// these keys.
1248    ///
1249    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1250    #[serde(rename = "_meta")]
1251    pub meta: Option<Meta>,
1252}
1253
1254impl StartNesRequest {
1255    /// Builds [`StartNesRequest`] with the required request fields set; optional fields start unset or empty.
1256    #[must_use]
1257    pub fn new() -> Self {
1258        Self {
1259            workspace_uri: None,
1260            workspace_folders: None,
1261            repository: None,
1262            meta: None,
1263        }
1264    }
1265
1266    /// Sets or clears the optional `workspaceUri` field.
1267    #[must_use]
1268    pub fn workspace_uri(mut self, workspace_uri: impl IntoOption<String>) -> Self {
1269        self.workspace_uri = workspace_uri.into_option();
1270        self
1271    }
1272
1273    /// Sets or clears the optional `workspaceFolders` field.
1274    #[must_use]
1275    pub fn workspace_folders(
1276        mut self,
1277        workspace_folders: impl IntoOption<Vec<WorkspaceFolder>>,
1278    ) -> Self {
1279        self.workspace_folders = workspace_folders.into_option();
1280        self
1281    }
1282
1283    /// Sets or clears the optional `repository` field.
1284    #[must_use]
1285    pub fn repository(mut self, repository: impl IntoOption<NesRepository>) -> Self {
1286        self.repository = repository.into_option();
1287        self
1288    }
1289
1290    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1291    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1292    /// these keys.
1293    ///
1294    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1295    #[must_use]
1296    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1297        self.meta = meta.into_option();
1298        self
1299    }
1300}
1301
1302impl Default for StartNesRequest {
1303    fn default() -> Self {
1304        Self::new()
1305    }
1306}
1307
1308/// A workspace folder.
1309#[skip_serializing_none]
1310#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1311#[serde(rename_all = "camelCase")]
1312#[non_exhaustive]
1313pub struct WorkspaceFolder {
1314    /// The URI of the folder.
1315    pub uri: String,
1316    /// The display name of the folder.
1317    pub name: String,
1318    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1319    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1320    /// these keys.
1321    ///
1322    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1323    #[serde(rename = "_meta")]
1324    pub meta: Option<Meta>,
1325}
1326
1327impl WorkspaceFolder {
1328    /// Builds [`WorkspaceFolder`] with the required fields set; optional fields start unset or empty.
1329    #[must_use]
1330    pub fn new(uri: impl Into<String>, name: impl Into<String>) -> Self {
1331        Self {
1332            uri: uri.into(),
1333            name: name.into(),
1334            meta: None,
1335        }
1336    }
1337
1338    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1339    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1340    /// these keys.
1341    ///
1342    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1343    #[must_use]
1344    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1345        self.meta = meta.into_option();
1346        self
1347    }
1348}
1349
1350/// Repository metadata for an NES session.
1351#[skip_serializing_none]
1352#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1353#[serde(rename_all = "camelCase")]
1354#[non_exhaustive]
1355pub struct NesRepository {
1356    /// The repository name.
1357    pub name: String,
1358    /// The repository owner.
1359    pub owner: String,
1360    /// The remote URL of the repository.
1361    pub remote_url: String,
1362    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1363    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1364    /// these keys.
1365    ///
1366    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1367    #[serde(rename = "_meta")]
1368    pub meta: Option<Meta>,
1369}
1370
1371impl NesRepository {
1372    /// Builds [`NesRepository`] with the required fields set; optional fields start unset or empty.
1373    #[must_use]
1374    pub fn new(
1375        name: impl Into<String>,
1376        owner: impl Into<String>,
1377        remote_url: impl Into<String>,
1378    ) -> Self {
1379        Self {
1380            name: name.into(),
1381            owner: owner.into(),
1382            remote_url: remote_url.into(),
1383            meta: None,
1384        }
1385    }
1386
1387    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1388    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1389    /// these keys.
1390    ///
1391    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1392    #[must_use]
1393    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1394        self.meta = meta.into_option();
1395        self
1396    }
1397}
1398
1399/// Response to `nes/start`.
1400#[skip_serializing_none]
1401#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1402#[schemars(extend("x-side" = "agent", "x-method" = NES_START_METHOD_NAME))]
1403#[serde(rename_all = "camelCase")]
1404#[non_exhaustive]
1405pub struct StartNesResponse {
1406    /// The session ID for the newly started NES session.
1407    pub session_id: SessionId,
1408    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1409    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1410    /// these keys.
1411    ///
1412    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1413    #[serde(rename = "_meta")]
1414    pub meta: Option<Meta>,
1415}
1416
1417impl StartNesResponse {
1418    /// Builds [`StartNesResponse`] with the required response fields set; optional fields start unset or empty.
1419    #[must_use]
1420    pub fn new(session_id: impl Into<SessionId>) -> Self {
1421        Self {
1422            session_id: session_id.into(),
1423            meta: None,
1424        }
1425    }
1426
1427    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1428    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1429    /// these keys.
1430    ///
1431    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1432    #[must_use]
1433    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1434        self.meta = meta.into_option();
1435        self
1436    }
1437}
1438
1439// NES session close
1440
1441/// Request to close an NES session.
1442///
1443/// The agent **must** cancel any ongoing work related to the NES session
1444/// and then free up any resources associated with the session.
1445#[skip_serializing_none]
1446#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1447#[schemars(extend("x-side" = "agent", "x-method" = NES_CLOSE_METHOD_NAME))]
1448#[serde(rename_all = "camelCase")]
1449#[non_exhaustive]
1450pub struct CloseNesRequest {
1451    /// The ID of the NES session to close.
1452    pub session_id: SessionId,
1453    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1454    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1455    /// these keys.
1456    ///
1457    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1458    #[serde(rename = "_meta")]
1459    pub meta: Option<Meta>,
1460}
1461
1462impl CloseNesRequest {
1463    /// Builds [`CloseNesRequest`] with the required request fields set; optional fields start unset or empty.
1464    #[must_use]
1465    pub fn new(session_id: impl Into<SessionId>) -> Self {
1466        Self {
1467            session_id: session_id.into(),
1468            meta: None,
1469        }
1470    }
1471
1472    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1473    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1474    /// these keys.
1475    ///
1476    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1477    #[must_use]
1478    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1479        self.meta = meta.into_option();
1480        self
1481    }
1482}
1483
1484/// Response from closing an NES session.
1485#[skip_serializing_none]
1486#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1487#[schemars(extend("x-side" = "agent", "x-method" = NES_CLOSE_METHOD_NAME))]
1488#[serde(rename_all = "camelCase")]
1489#[non_exhaustive]
1490pub struct CloseNesResponse {
1491    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1492    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1493    /// these keys.
1494    ///
1495    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1496    #[serde(rename = "_meta")]
1497    pub meta: Option<Meta>,
1498}
1499
1500impl CloseNesResponse {
1501    /// Builds [`CloseNesResponse`] with the required response fields set; optional fields start unset or empty.
1502    #[must_use]
1503    pub fn new() -> Self {
1504        Self::default()
1505    }
1506
1507    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1508    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1509    /// these keys.
1510    ///
1511    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1512    #[must_use]
1513    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1514        self.meta = meta.into_option();
1515        self
1516    }
1517}
1518
1519// NES suggest request
1520
1521/// What triggered the suggestion request.
1522#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1523#[non_exhaustive]
1524pub enum NesTriggerKind {
1525    /// Triggered by user typing or cursor movement.
1526    #[serde(rename = "automatic")]
1527    Automatic,
1528    /// Triggered by a diagnostic appearing at or near the cursor.
1529    #[serde(rename = "diagnostic")]
1530    Diagnostic,
1531    /// Triggered by an explicit user action (keyboard shortcut).
1532    #[serde(rename = "manual")]
1533    Manual,
1534    /// Custom or future suggestion trigger kind.
1535    ///
1536    /// Values beginning with `_` are reserved for implementation-specific
1537    /// extensions. Unknown values that do not begin with `_` are reserved for
1538    /// future ACP variants.
1539    #[serde(untagged)]
1540    Other(String),
1541}
1542
1543/// Request for a code suggestion.
1544#[serde_as]
1545#[skip_serializing_none]
1546#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1547#[schemars(extend("x-side" = "agent", "x-method" = NES_SUGGEST_METHOD_NAME))]
1548#[serde(rename_all = "camelCase")]
1549#[non_exhaustive]
1550pub struct SuggestNesRequest {
1551    /// The session ID for this request.
1552    pub session_id: SessionId,
1553    /// The URI of the document to suggest for.
1554    pub uri: String,
1555    /// The version number of the document.
1556    pub version: i64,
1557    /// The current cursor position.
1558    pub position: Position,
1559    /// The current text selection range, if any.
1560    #[serde_as(deserialize_as = "DefaultOnError")]
1561    #[schemars(extend("x-deserialize-default-on-error" = true))]
1562    #[serde(default)]
1563    pub selection: Option<Range>,
1564    /// What triggered this suggestion request.
1565    pub trigger_kind: NesTriggerKind,
1566    /// Context for the suggestion, included based on agent capabilities.
1567    #[serde_as(deserialize_as = "DefaultOnError")]
1568    #[schemars(extend("x-deserialize-default-on-error" = true))]
1569    #[serde(default)]
1570    pub context: Option<NesSuggestContext>,
1571    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1572    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1573    /// these keys.
1574    ///
1575    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1576    #[serde(rename = "_meta")]
1577    pub meta: Option<Meta>,
1578}
1579
1580impl SuggestNesRequest {
1581    /// Builds [`SuggestNesRequest`] with the required request fields set; optional fields start unset or empty.
1582    #[must_use]
1583    pub fn new(
1584        session_id: impl Into<SessionId>,
1585        uri: impl Into<String>,
1586        version: i64,
1587        position: Position,
1588        trigger_kind: NesTriggerKind,
1589    ) -> Self {
1590        Self {
1591            session_id: session_id.into(),
1592            uri: uri.into(),
1593            version,
1594            position,
1595            selection: None,
1596            trigger_kind,
1597            context: None,
1598            meta: None,
1599        }
1600    }
1601
1602    /// Sets or clears the optional `selection` field.
1603    #[must_use]
1604    pub fn selection(mut self, selection: impl IntoOption<Range>) -> Self {
1605        self.selection = selection.into_option();
1606        self
1607    }
1608
1609    /// Sets or clears the optional `context` field.
1610    #[must_use]
1611    pub fn context(mut self, context: impl IntoOption<NesSuggestContext>) -> Self {
1612        self.context = context.into_option();
1613        self
1614    }
1615
1616    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1617    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1618    /// these keys.
1619    ///
1620    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1621    #[must_use]
1622    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1623        self.meta = meta.into_option();
1624        self
1625    }
1626}
1627
1628/// Context attached to a suggestion request.
1629#[serde_as]
1630#[skip_serializing_none]
1631#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1632#[serde(rename_all = "camelCase")]
1633#[non_exhaustive]
1634pub struct NesSuggestContext {
1635    /// Recently accessed files.
1636    #[serde_as(deserialize_as = "DefaultOnError<Option<VecSkipError<_, SkipListener>>>")]
1637    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1638    #[serde(default)]
1639    pub recent_files: Option<Vec<NesRecentFile>>,
1640    /// Related code snippets.
1641    #[serde_as(deserialize_as = "DefaultOnError<Option<VecSkipError<_, SkipListener>>>")]
1642    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1643    #[serde(default)]
1644    pub related_snippets: Option<Vec<NesRelatedSnippet>>,
1645    /// Recent edit history.
1646    #[serde_as(deserialize_as = "DefaultOnError<Option<VecSkipError<_, SkipListener>>>")]
1647    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1648    #[serde(default)]
1649    pub edit_history: Option<Vec<NesEditHistoryEntry>>,
1650    /// Recent user actions (typing, navigation, etc.).
1651    #[serde_as(deserialize_as = "DefaultOnError<Option<VecSkipError<_, SkipListener>>>")]
1652    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1653    #[serde(default)]
1654    pub user_actions: Option<Vec<NesUserAction>>,
1655    /// Currently open files in the editor.
1656    #[serde_as(deserialize_as = "DefaultOnError<Option<VecSkipError<_, SkipListener>>>")]
1657    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1658    #[serde(default)]
1659    pub open_files: Option<Vec<NesOpenFile>>,
1660    /// Current diagnostics (errors, warnings).
1661    #[serde_as(deserialize_as = "DefaultOnError<Option<VecSkipError<_, SkipListener>>>")]
1662    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
1663    #[serde(default)]
1664    pub diagnostics: Option<Vec<NesDiagnostic>>,
1665    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1666    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1667    /// these keys.
1668    ///
1669    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1670    #[serde(rename = "_meta")]
1671    pub meta: Option<Meta>,
1672}
1673
1674impl NesSuggestContext {
1675    /// Builds [`NesSuggestContext`] with the required fields set; optional fields start unset or empty.
1676    #[must_use]
1677    pub fn new() -> Self {
1678        Self::default()
1679    }
1680
1681    /// Sets or clears the optional `recentFiles` field.
1682    #[must_use]
1683    pub fn recent_files(mut self, recent_files: impl IntoOption<Vec<NesRecentFile>>) -> Self {
1684        self.recent_files = recent_files.into_option();
1685        self
1686    }
1687
1688    /// Sets or clears the optional `relatedSnippets` field.
1689    #[must_use]
1690    pub fn related_snippets(
1691        mut self,
1692        related_snippets: impl IntoOption<Vec<NesRelatedSnippet>>,
1693    ) -> Self {
1694        self.related_snippets = related_snippets.into_option();
1695        self
1696    }
1697
1698    /// Sets or clears the optional `editHistory` field.
1699    #[must_use]
1700    pub fn edit_history(mut self, edit_history: impl IntoOption<Vec<NesEditHistoryEntry>>) -> Self {
1701        self.edit_history = edit_history.into_option();
1702        self
1703    }
1704
1705    /// Sets or clears the optional `userActions` field.
1706    #[must_use]
1707    pub fn user_actions(mut self, user_actions: impl IntoOption<Vec<NesUserAction>>) -> Self {
1708        self.user_actions = user_actions.into_option();
1709        self
1710    }
1711
1712    /// Sets or clears the optional `openFiles` field.
1713    #[must_use]
1714    pub fn open_files(mut self, open_files: impl IntoOption<Vec<NesOpenFile>>) -> Self {
1715        self.open_files = open_files.into_option();
1716        self
1717    }
1718
1719    /// Sets or clears the optional `diagnostics` field.
1720    #[must_use]
1721    pub fn diagnostics(mut self, diagnostics: impl IntoOption<Vec<NesDiagnostic>>) -> Self {
1722        self.diagnostics = diagnostics.into_option();
1723        self
1724    }
1725
1726    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1727    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1728    /// these keys.
1729    ///
1730    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1731    #[must_use]
1732    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1733        self.meta = meta.into_option();
1734        self
1735    }
1736}
1737
1738/// A recently accessed file.
1739#[skip_serializing_none]
1740#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1741#[serde(rename_all = "camelCase")]
1742#[non_exhaustive]
1743pub struct NesRecentFile {
1744    /// The URI of the file.
1745    pub uri: String,
1746    /// The language identifier.
1747    pub language_id: String,
1748    /// The full text content of the file.
1749    pub text: String,
1750    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1751    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1752    /// these keys.
1753    ///
1754    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1755    #[serde(rename = "_meta")]
1756    pub meta: Option<Meta>,
1757}
1758
1759impl NesRecentFile {
1760    /// Builds [`NesRecentFile`] with the required fields set; optional fields start unset or empty.
1761    #[must_use]
1762    pub fn new(
1763        uri: impl Into<String>,
1764        language_id: impl Into<String>,
1765        text: impl Into<String>,
1766    ) -> Self {
1767        Self {
1768            uri: uri.into(),
1769            language_id: language_id.into(),
1770            text: text.into(),
1771            meta: None,
1772        }
1773    }
1774
1775    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1776    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1777    /// these keys.
1778    ///
1779    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1780    #[must_use]
1781    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1782        self.meta = meta.into_option();
1783        self
1784    }
1785}
1786
1787/// A related code snippet from a file.
1788#[skip_serializing_none]
1789#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1790#[serde(rename_all = "camelCase")]
1791#[non_exhaustive]
1792pub struct NesRelatedSnippet {
1793    /// The URI of the file containing the snippets.
1794    pub uri: String,
1795    /// The code excerpts.
1796    pub excerpts: Vec<NesExcerpt>,
1797    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1798    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1799    /// these keys.
1800    ///
1801    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1802    #[serde(rename = "_meta")]
1803    pub meta: Option<Meta>,
1804}
1805
1806impl NesRelatedSnippet {
1807    /// Builds [`NesRelatedSnippet`] with the required fields set; optional fields start unset or empty.
1808    #[must_use]
1809    pub fn new(uri: impl Into<String>, excerpts: Vec<NesExcerpt>) -> Self {
1810        Self {
1811            uri: uri.into(),
1812            excerpts,
1813            meta: None,
1814        }
1815    }
1816
1817    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1818    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1819    /// these keys.
1820    ///
1821    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1822    #[must_use]
1823    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1824        self.meta = meta.into_option();
1825        self
1826    }
1827}
1828
1829/// A code excerpt from a file.
1830#[skip_serializing_none]
1831#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1832#[serde(rename_all = "camelCase")]
1833#[non_exhaustive]
1834pub struct NesExcerpt {
1835    /// The start line of the excerpt (zero-based).
1836    pub start_line: u32,
1837    /// The end line of the excerpt (zero-based).
1838    pub end_line: u32,
1839    /// The text content of the excerpt.
1840    pub text: String,
1841    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1842    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1843    /// these keys.
1844    ///
1845    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1846    #[serde(rename = "_meta")]
1847    pub meta: Option<Meta>,
1848}
1849
1850impl NesExcerpt {
1851    /// Builds [`NesExcerpt`] with the required fields set; optional fields start unset or empty.
1852    #[must_use]
1853    pub fn new(start_line: u32, end_line: u32, text: impl Into<String>) -> Self {
1854        Self {
1855            start_line,
1856            end_line,
1857            text: text.into(),
1858            meta: None,
1859        }
1860    }
1861
1862    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1863    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1864    /// these keys.
1865    ///
1866    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1867    #[must_use]
1868    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1869        self.meta = meta.into_option();
1870        self
1871    }
1872}
1873
1874/// An entry in the edit history.
1875#[skip_serializing_none]
1876#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1877#[serde(rename_all = "camelCase")]
1878#[non_exhaustive]
1879pub struct NesEditHistoryEntry {
1880    /// The URI of the edited file.
1881    pub uri: String,
1882    /// A diff representing the edit.
1883    pub diff: String,
1884    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1885    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1886    /// these keys.
1887    ///
1888    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1889    #[serde(rename = "_meta")]
1890    pub meta: Option<Meta>,
1891}
1892
1893impl NesEditHistoryEntry {
1894    /// Builds [`NesEditHistoryEntry`] with the required fields set; optional fields start unset or empty.
1895    #[must_use]
1896    pub fn new(uri: impl Into<String>, diff: impl Into<String>) -> Self {
1897        Self {
1898            uri: uri.into(),
1899            diff: diff.into(),
1900            meta: None,
1901        }
1902    }
1903
1904    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1905    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1906    /// these keys.
1907    ///
1908    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1909    #[must_use]
1910    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1911        self.meta = meta.into_option();
1912        self
1913    }
1914}
1915
1916/// A user action (typing, cursor movement, etc.).
1917#[skip_serializing_none]
1918#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1919#[serde(rename_all = "camelCase")]
1920#[non_exhaustive]
1921pub struct NesUserAction {
1922    /// The kind of action (e.g., "insertChar", "cursorMovement").
1923    pub action: String,
1924    /// The URI of the file where the action occurred.
1925    pub uri: String,
1926    /// The position where the action occurred.
1927    pub position: Position,
1928    /// Timestamp in milliseconds since epoch.
1929    pub timestamp_ms: u64,
1930    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1931    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1932    /// these keys.
1933    ///
1934    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1935    #[serde(rename = "_meta")]
1936    pub meta: Option<Meta>,
1937}
1938
1939impl NesUserAction {
1940    /// Builds [`NesUserAction`] with the required fields set; optional fields start unset or empty.
1941    #[must_use]
1942    pub fn new(
1943        action: impl Into<String>,
1944        uri: impl Into<String>,
1945        position: Position,
1946        timestamp_ms: u64,
1947    ) -> Self {
1948        Self {
1949            action: action.into(),
1950            uri: uri.into(),
1951            position,
1952            timestamp_ms,
1953            meta: None,
1954        }
1955    }
1956
1957    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1958    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1959    /// these keys.
1960    ///
1961    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1962    #[must_use]
1963    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
1964        self.meta = meta.into_option();
1965        self
1966    }
1967}
1968
1969/// An open file in the editor.
1970#[serde_as]
1971#[skip_serializing_none]
1972#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
1973#[serde(rename_all = "camelCase")]
1974#[non_exhaustive]
1975pub struct NesOpenFile {
1976    /// The URI of the file.
1977    pub uri: String,
1978    /// The language identifier.
1979    pub language_id: String,
1980    /// The visible range in the editor, if any.
1981    #[serde_as(deserialize_as = "DefaultOnError")]
1982    #[schemars(extend("x-deserialize-default-on-error" = true))]
1983    #[serde(default)]
1984    pub visible_range: Option<Range>,
1985    /// Timestamp in milliseconds since epoch of when the file was last focused.
1986    #[serde_as(deserialize_as = "DefaultOnError")]
1987    #[schemars(extend("x-deserialize-default-on-error" = true))]
1988    #[serde(default)]
1989    pub last_focused_ms: Option<u64>,
1990    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
1991    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
1992    /// these keys.
1993    ///
1994    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
1995    #[serde(rename = "_meta")]
1996    pub meta: Option<Meta>,
1997}
1998
1999impl NesOpenFile {
2000    /// Builds [`NesOpenFile`] with the required fields set; optional fields start unset or empty.
2001    #[must_use]
2002    pub fn new(uri: impl Into<String>, language_id: impl Into<String>) -> Self {
2003        Self {
2004            uri: uri.into(),
2005            language_id: language_id.into(),
2006            visible_range: None,
2007            last_focused_ms: None,
2008            meta: None,
2009        }
2010    }
2011
2012    /// Sets or clears the optional `visibleRange` field.
2013    #[must_use]
2014    pub fn visible_range(mut self, visible_range: impl IntoOption<Range>) -> Self {
2015        self.visible_range = visible_range.into_option();
2016        self
2017    }
2018
2019    /// Sets or clears the optional `lastFocusedMs` field.
2020    #[must_use]
2021    pub fn last_focused_ms(mut self, last_focused_ms: impl IntoOption<u64>) -> Self {
2022        self.last_focused_ms = last_focused_ms.into_option();
2023        self
2024    }
2025
2026    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2027    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2028    /// these keys.
2029    ///
2030    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2031    #[must_use]
2032    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2033        self.meta = meta.into_option();
2034        self
2035    }
2036}
2037
2038/// A diagnostic (error, warning, etc.).
2039#[skip_serializing_none]
2040#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2041#[serde(rename_all = "camelCase")]
2042#[non_exhaustive]
2043pub struct NesDiagnostic {
2044    /// The URI of the file containing the diagnostic.
2045    pub uri: String,
2046    /// The range of the diagnostic.
2047    pub range: Range,
2048    /// The severity of the diagnostic.
2049    pub severity: NesDiagnosticSeverity,
2050    /// The diagnostic message.
2051    pub message: String,
2052    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2053    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2054    /// these keys.
2055    ///
2056    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2057    #[serde(rename = "_meta")]
2058    pub meta: Option<Meta>,
2059}
2060
2061impl NesDiagnostic {
2062    /// Builds [`NesDiagnostic`] with the required fields set; optional fields start unset or empty.
2063    #[must_use]
2064    pub fn new(
2065        uri: impl Into<String>,
2066        range: Range,
2067        severity: NesDiagnosticSeverity,
2068        message: impl Into<String>,
2069    ) -> Self {
2070        Self {
2071            uri: uri.into(),
2072            range,
2073            severity,
2074            message: message.into(),
2075            meta: None,
2076        }
2077    }
2078
2079    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2080    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2081    /// these keys.
2082    ///
2083    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2084    #[must_use]
2085    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2086        self.meta = meta.into_option();
2087        self
2088    }
2089}
2090
2091/// Severity of a diagnostic.
2092#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2093#[non_exhaustive]
2094pub enum NesDiagnosticSeverity {
2095    /// An error.
2096    #[serde(rename = "error")]
2097    Error,
2098    /// A warning.
2099    #[serde(rename = "warning")]
2100    Warning,
2101    /// An informational message.
2102    #[serde(rename = "information")]
2103    Information,
2104    /// A hint.
2105    #[serde(rename = "hint")]
2106    Hint,
2107    /// Custom or future diagnostic severity.
2108    ///
2109    /// Values beginning with `_` are reserved for implementation-specific
2110    /// extensions. Unknown values that do not begin with `_` are reserved for
2111    /// future ACP variants.
2112    #[serde(untagged)]
2113    Other(String),
2114}
2115
2116// NES suggest response
2117
2118/// Response to `nes/suggest`.
2119#[serde_as]
2120#[skip_serializing_none]
2121#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2122#[schemars(extend("x-side" = "agent", "x-method" = NES_SUGGEST_METHOD_NAME))]
2123#[serde(rename_all = "camelCase")]
2124#[non_exhaustive]
2125pub struct SuggestNesResponse {
2126    /// The list of suggestions.
2127    #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, SkipListener>>")]
2128    #[schemars(extend("x-deserialize-default-on-error" = true, "x-deserialize-skip-invalid-items" = true))]
2129    pub suggestions: Vec<NesSuggestion>,
2130    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2131    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2132    /// these keys.
2133    ///
2134    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2135    #[serde(rename = "_meta")]
2136    pub meta: Option<Meta>,
2137}
2138
2139impl SuggestNesResponse {
2140    /// Builds [`SuggestNesResponse`] with the required response fields set; optional fields start unset or empty.
2141    #[must_use]
2142    pub fn new(suggestions: Vec<NesSuggestion>) -> Self {
2143        Self {
2144            suggestions,
2145            meta: None,
2146        }
2147    }
2148
2149    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2150    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2151    /// these keys.
2152    ///
2153    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2154    #[must_use]
2155    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2156        self.meta = meta.into_option();
2157        self
2158    }
2159}
2160
2161/// A suggestion returned by the agent.
2162#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2163#[serde(tag = "kind", rename_all = "camelCase")]
2164#[schemars(extend("discriminator" = {"propertyName": "kind"}))]
2165#[non_exhaustive]
2166pub enum NesSuggestion {
2167    /// A text edit suggestion.
2168    Edit(NesEditSuggestion),
2169    /// A jump-to-location suggestion.
2170    Jump(NesJumpSuggestion),
2171    /// A rename symbol suggestion.
2172    Rename(NesRenameSuggestion),
2173    /// A search-and-replace suggestion.
2174    SearchAndReplace(NesSearchAndReplaceSuggestion),
2175    /// Custom or future NES suggestion.
2176    ///
2177    /// Values beginning with `_` are reserved for implementation-specific
2178    /// extensions. Unknown values that do not begin with `_` are reserved for
2179    /// future ACP variants.
2180    ///
2181    /// Receivers that do not understand this suggestion kind should preserve
2182    /// the raw payload when storing, replaying, proxying, or forwarding
2183    /// suggestions, and otherwise ignore it or display it generically.
2184    #[serde(untagged)]
2185    Other(OtherNesSuggestion),
2186}
2187
2188/// Custom or future NES suggestion payload.
2189#[derive(Debug, Clone, Serialize, JsonSchema, PartialEq, Eq)]
2190#[schemars(inline)]
2191#[schemars(transform = other_nes_suggestion_schema)]
2192#[serde(rename_all = "camelCase")]
2193#[non_exhaustive]
2194pub struct OtherNesSuggestion {
2195    /// Custom or future NES suggestion kind.
2196    ///
2197    /// Values beginning with `_` are reserved for implementation-specific
2198    /// extensions. Unknown values that do not begin with `_` are reserved for
2199    /// future ACP variants.
2200    pub kind: String,
2201    /// Additional fields from the unknown NES suggestion payload.
2202    #[serde(flatten)]
2203    pub fields: BTreeMap<String, serde_json::Value>,
2204}
2205
2206impl OtherNesSuggestion {
2207    /// Builds [`OtherNesSuggestion`] from an unknown discriminator and preserves the remaining extension fields.
2208    #[must_use]
2209    pub fn new(kind: impl Into<String>, mut fields: BTreeMap<String, serde_json::Value>) -> Self {
2210        fields.remove("kind");
2211        Self {
2212            kind: kind.into(),
2213            fields,
2214        }
2215    }
2216}
2217
2218impl<'de> Deserialize<'de> for OtherNesSuggestion {
2219    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2220    where
2221        D: serde::Deserializer<'de>,
2222    {
2223        let mut fields = BTreeMap::<String, serde_json::Value>::deserialize(deserializer)?;
2224        let kind = fields
2225            .remove("kind")
2226            .ok_or_else(|| serde::de::Error::missing_field("kind"))?;
2227        let serde_json::Value::String(kind) = kind else {
2228            return Err(serde::de::Error::custom("`kind` must be a string"));
2229        };
2230
2231        if is_known_nes_suggestion_kind(&kind) {
2232            return Err(serde::de::Error::custom(format!(
2233                "known NES suggestion `{kind}` did not match its schema"
2234            )));
2235        }
2236
2237        Ok(Self { kind, fields })
2238    }
2239}
2240
2241fn is_known_nes_suggestion_kind(kind: &str) -> bool {
2242    matches!(kind, "edit" | "jump" | "rename" | "searchAndReplace")
2243}
2244
2245fn other_nes_suggestion_schema(schema: &mut Schema) {
2246    super::schema_util::reject_known_string_discriminators(
2247        schema,
2248        "kind",
2249        &["edit", "jump", "rename", "searchAndReplace"],
2250    );
2251}
2252
2253/// A text edit suggestion.
2254#[serde_as]
2255#[skip_serializing_none]
2256#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2257#[serde(rename_all = "camelCase")]
2258#[non_exhaustive]
2259pub struct NesEditSuggestion {
2260    /// Unique identifier for accept/reject tracking.
2261    pub id: String,
2262    /// The URI of the file to edit.
2263    pub uri: String,
2264    /// The text edits to apply.
2265    pub edits: Vec<NesTextEdit>,
2266    /// Optional suggested cursor position after applying edits.
2267    #[serde_as(deserialize_as = "DefaultOnError")]
2268    #[schemars(extend("x-deserialize-default-on-error" = true))]
2269    #[serde(default)]
2270    pub cursor_position: Option<Position>,
2271    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2272    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2273    /// these keys.
2274    ///
2275    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2276    #[serde(rename = "_meta")]
2277    pub meta: Option<Meta>,
2278}
2279
2280impl NesEditSuggestion {
2281    /// Builds [`NesEditSuggestion`] with the required fields set; optional fields start unset or empty.
2282    #[must_use]
2283    pub fn new(id: impl Into<String>, uri: impl Into<String>, edits: Vec<NesTextEdit>) -> Self {
2284        Self {
2285            id: id.into(),
2286            uri: uri.into(),
2287            edits,
2288            cursor_position: None,
2289            meta: None,
2290        }
2291    }
2292
2293    /// Sets or clears the optional `cursorPosition` field.
2294    #[must_use]
2295    pub fn cursor_position(mut self, cursor_position: impl IntoOption<Position>) -> Self {
2296        self.cursor_position = cursor_position.into_option();
2297        self
2298    }
2299
2300    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2301    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2302    /// these keys.
2303    ///
2304    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2305    #[must_use]
2306    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2307        self.meta = meta.into_option();
2308        self
2309    }
2310}
2311
2312/// A text edit within a suggestion.
2313#[skip_serializing_none]
2314#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2315#[serde(rename_all = "camelCase")]
2316#[non_exhaustive]
2317pub struct NesTextEdit {
2318    /// The range to replace.
2319    pub range: Range,
2320    /// The replacement text.
2321    pub new_text: String,
2322    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2323    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2324    /// these keys.
2325    ///
2326    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2327    #[serde(rename = "_meta")]
2328    pub meta: Option<Meta>,
2329}
2330
2331impl NesTextEdit {
2332    /// Builds [`NesTextEdit`] with the required fields set; optional fields start unset or empty.
2333    #[must_use]
2334    pub fn new(range: Range, new_text: impl Into<String>) -> Self {
2335        Self {
2336            range,
2337            new_text: new_text.into(),
2338            meta: None,
2339        }
2340    }
2341
2342    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2343    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2344    /// these keys.
2345    ///
2346    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2347    #[must_use]
2348    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2349        self.meta = meta.into_option();
2350        self
2351    }
2352}
2353
2354/// A jump-to-location suggestion.
2355#[skip_serializing_none]
2356#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2357#[serde(rename_all = "camelCase")]
2358#[non_exhaustive]
2359pub struct NesJumpSuggestion {
2360    /// Unique identifier for accept/reject tracking.
2361    pub id: String,
2362    /// The file to navigate to.
2363    pub uri: String,
2364    /// The target position within the file.
2365    pub position: Position,
2366    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2367    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2368    /// these keys.
2369    ///
2370    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2371    #[serde(rename = "_meta")]
2372    pub meta: Option<Meta>,
2373}
2374
2375impl NesJumpSuggestion {
2376    /// Builds [`NesJumpSuggestion`] with the required fields set; optional fields start unset or empty.
2377    #[must_use]
2378    pub fn new(id: impl Into<String>, uri: impl Into<String>, position: Position) -> Self {
2379        Self {
2380            id: id.into(),
2381            uri: uri.into(),
2382            position,
2383            meta: None,
2384        }
2385    }
2386
2387    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2388    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2389    /// these keys.
2390    ///
2391    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2392    #[must_use]
2393    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2394        self.meta = meta.into_option();
2395        self
2396    }
2397}
2398
2399/// A rename symbol suggestion.
2400#[skip_serializing_none]
2401#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2402#[serde(rename_all = "camelCase")]
2403#[non_exhaustive]
2404pub struct NesRenameSuggestion {
2405    /// Unique identifier for accept/reject tracking.
2406    pub id: String,
2407    /// The file URI containing the symbol.
2408    pub uri: String,
2409    /// The position of the symbol to rename.
2410    pub position: Position,
2411    /// The new name for the symbol.
2412    pub new_name: String,
2413    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2414    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2415    /// these keys.
2416    ///
2417    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2418    #[serde(rename = "_meta")]
2419    pub meta: Option<Meta>,
2420}
2421
2422impl NesRenameSuggestion {
2423    /// Builds [`NesRenameSuggestion`] with the required fields set; optional fields start unset or empty.
2424    #[must_use]
2425    pub fn new(
2426        id: impl Into<String>,
2427        uri: impl Into<String>,
2428        position: Position,
2429        new_name: impl Into<String>,
2430    ) -> Self {
2431        Self {
2432            id: id.into(),
2433            uri: uri.into(),
2434            position,
2435            new_name: new_name.into(),
2436            meta: None,
2437        }
2438    }
2439
2440    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2441    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2442    /// these keys.
2443    ///
2444    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2445    #[must_use]
2446    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2447        self.meta = meta.into_option();
2448        self
2449    }
2450}
2451
2452/// A search-and-replace suggestion.
2453#[skip_serializing_none]
2454#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2455#[serde(rename_all = "camelCase")]
2456#[non_exhaustive]
2457pub struct NesSearchAndReplaceSuggestion {
2458    /// Unique identifier for accept/reject tracking.
2459    pub id: String,
2460    /// The file URI to search within.
2461    pub uri: String,
2462    /// The text or pattern to find.
2463    pub search: String,
2464    /// The replacement text.
2465    pub replace: String,
2466    /// Whether `search` is a regular expression. Defaults to `false`.
2467    pub is_regex: Option<bool>,
2468    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2469    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2470    /// these keys.
2471    ///
2472    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2473    #[serde(rename = "_meta")]
2474    pub meta: Option<Meta>,
2475}
2476
2477impl NesSearchAndReplaceSuggestion {
2478    /// Builds [`NesSearchAndReplaceSuggestion`] with the required fields set; optional fields start unset or empty.
2479    #[must_use]
2480    pub fn new(
2481        id: impl Into<String>,
2482        uri: impl Into<String>,
2483        search: impl Into<String>,
2484        replace: impl Into<String>,
2485    ) -> Self {
2486        Self {
2487            id: id.into(),
2488            uri: uri.into(),
2489            search: search.into(),
2490            replace: replace.into(),
2491            is_regex: None,
2492            meta: None,
2493        }
2494    }
2495
2496    /// Sets or clears the optional `isRegex` field.
2497    #[must_use]
2498    pub fn is_regex(mut self, is_regex: impl IntoOption<bool>) -> Self {
2499        self.is_regex = is_regex.into_option();
2500        self
2501    }
2502
2503    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2504    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2505    /// these keys.
2506    ///
2507    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2508    #[must_use]
2509    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2510        self.meta = meta.into_option();
2511        self
2512    }
2513}
2514
2515// NES accept/reject notifications
2516
2517/// Notification sent when a suggestion is accepted.
2518#[skip_serializing_none]
2519#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2520#[schemars(extend("x-side" = "agent", "x-method" = NES_ACCEPT_METHOD_NAME))]
2521#[serde(rename_all = "camelCase")]
2522#[non_exhaustive]
2523pub struct AcceptNesNotification {
2524    /// The session ID for this notification.
2525    pub session_id: SessionId,
2526    /// The ID of the accepted suggestion.
2527    pub id: String,
2528    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2529    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2530    /// these keys.
2531    ///
2532    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2533    #[serde(rename = "_meta")]
2534    pub meta: Option<Meta>,
2535}
2536
2537impl AcceptNesNotification {
2538    /// Builds [`AcceptNesNotification`] with the required notification fields set; optional fields start unset or empty.
2539    #[must_use]
2540    pub fn new(session_id: impl Into<SessionId>, id: impl Into<String>) -> Self {
2541        Self {
2542            session_id: session_id.into(),
2543            id: id.into(),
2544            meta: None,
2545        }
2546    }
2547
2548    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2549    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2550    /// these keys.
2551    ///
2552    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2553    #[must_use]
2554    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2555        self.meta = meta.into_option();
2556        self
2557    }
2558}
2559
2560/// Notification sent when a suggestion is rejected.
2561#[serde_as]
2562#[skip_serializing_none]
2563#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2564#[schemars(extend("x-side" = "agent", "x-method" = NES_REJECT_METHOD_NAME))]
2565#[serde(rename_all = "camelCase")]
2566#[non_exhaustive]
2567pub struct RejectNesNotification {
2568    /// The session ID for this notification.
2569    pub session_id: SessionId,
2570    /// The ID of the rejected suggestion.
2571    pub id: String,
2572    /// The reason for rejection.
2573    #[serde_as(deserialize_as = "DefaultOnError")]
2574    #[schemars(extend("x-deserialize-default-on-error" = true))]
2575    #[serde(default)]
2576    pub reason: Option<NesRejectReason>,
2577    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2578    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2579    /// these keys.
2580    ///
2581    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2582    #[serde(rename = "_meta")]
2583    pub meta: Option<Meta>,
2584}
2585
2586impl RejectNesNotification {
2587    /// Builds [`RejectNesNotification`] with the required notification fields set; optional fields start unset or empty.
2588    #[must_use]
2589    pub fn new(session_id: impl Into<SessionId>, id: impl Into<String>) -> Self {
2590        Self {
2591            session_id: session_id.into(),
2592            id: id.into(),
2593            reason: None,
2594            meta: None,
2595        }
2596    }
2597
2598    /// Sets or clears the optional `reason` field.
2599    #[must_use]
2600    pub fn reason(mut self, reason: impl IntoOption<NesRejectReason>) -> Self {
2601        self.reason = reason.into_option();
2602        self
2603    }
2604
2605    /// The _meta property is reserved by ACP to allow clients and agents to attach additional
2606    /// metadata to their interactions. Implementations MUST NOT make assumptions about values at
2607    /// these keys.
2608    ///
2609    /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)
2610    #[must_use]
2611    pub fn meta(mut self, meta: impl IntoOption<Meta>) -> Self {
2612        self.meta = meta.into_option();
2613        self
2614    }
2615}
2616
2617/// The reason a suggestion was rejected.
2618#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
2619#[non_exhaustive]
2620pub enum NesRejectReason {
2621    /// The user explicitly dismissed the suggestion.
2622    #[serde(rename = "rejected")]
2623    Rejected,
2624    /// The suggestion was shown but the user continued editing without interacting.
2625    #[serde(rename = "ignored")]
2626    Ignored,
2627    /// The suggestion was superseded by a newer suggestion.
2628    #[serde(rename = "replaced")]
2629    Replaced,
2630    /// The request was cancelled before the agent returned a response.
2631    #[serde(rename = "cancelled")]
2632    Cancelled,
2633    /// Custom or future rejection reason.
2634    ///
2635    /// Values beginning with `_` are reserved for implementation-specific
2636    /// extensions. Unknown values that do not begin with `_` are reserved for
2637    /// future ACP variants.
2638    #[serde(untagged)]
2639    Other(String),
2640}
2641
2642#[cfg(test)]
2643mod tests {
2644    use super::*;
2645    use serde_json::json;
2646
2647    #[test]
2648    fn test_position_encoding_kind_serialization() {
2649        assert_eq!(
2650            serde_json::to_value(&PositionEncodingKind::Utf16).unwrap(),
2651            json!("utf-16")
2652        );
2653        assert_eq!(
2654            serde_json::to_value(&PositionEncodingKind::Utf32).unwrap(),
2655            json!("utf-32")
2656        );
2657        assert_eq!(
2658            serde_json::to_value(&PositionEncodingKind::Utf8).unwrap(),
2659            json!("utf-8")
2660        );
2661
2662        assert_eq!(
2663            serde_json::from_value::<PositionEncodingKind>(json!("utf-16")).unwrap(),
2664            PositionEncodingKind::Utf16
2665        );
2666        assert_eq!(
2667            serde_json::from_value::<PositionEncodingKind>(json!("utf-32")).unwrap(),
2668            PositionEncodingKind::Utf32
2669        );
2670        assert_eq!(
2671            serde_json::from_value::<PositionEncodingKind>(json!("utf-8")).unwrap(),
2672            PositionEncodingKind::Utf8
2673        );
2674        assert!(serde_json::from_value::<PositionEncodingKind>(json!("_future")).is_err());
2675    }
2676
2677    #[test]
2678    fn test_client_capabilities_skip_unknown_position_encodings() {
2679        let caps: crate::v2::ClientCapabilities = serde_json::from_value(json!({
2680            "positionEncodings": ["_future", "utf-8", "utf-16"]
2681        }))
2682        .unwrap();
2683
2684        assert_eq!(
2685            caps.position_encodings,
2686            vec![PositionEncodingKind::Utf8, PositionEncodingKind::Utf16]
2687        );
2688    }
2689
2690    #[test]
2691    fn test_agent_nes_capabilities_serialization() {
2692        let caps = NesCapabilities::new()
2693            .events(
2694                NesEventCapabilities::new().document(
2695                    NesDocumentEventCapabilities::new()
2696                        .did_open(NesDocumentDidOpenCapabilities::default())
2697                        .did_change(NesDocumentDidChangeCapabilities::new(
2698                            TextDocumentSyncKind::Incremental,
2699                        ))
2700                        .did_close(NesDocumentDidCloseCapabilities::default())
2701                        .did_save(NesDocumentDidSaveCapabilities::default())
2702                        .did_focus(NesDocumentDidFocusCapabilities::default()),
2703                ),
2704            )
2705            .context(
2706                NesContextCapabilities::new()
2707                    .recent_files(NesRecentFilesCapabilities {
2708                        max_count: Some(10),
2709                        meta: None,
2710                    })
2711                    .related_snippets(NesRelatedSnippetsCapabilities::default())
2712                    .edit_history(NesEditHistoryCapabilities {
2713                        max_count: Some(6),
2714                        meta: None,
2715                    })
2716                    .user_actions(NesUserActionsCapabilities {
2717                        max_count: Some(16),
2718                        meta: None,
2719                    })
2720                    .open_files(NesOpenFilesCapabilities::default())
2721                    .diagnostics(NesDiagnosticsCapabilities::default()),
2722            );
2723
2724        let json = serde_json::to_value(&caps).unwrap();
2725        assert_eq!(
2726            json,
2727            json!({
2728                "events": {
2729                    "document": {
2730                        "didOpen": {},
2731                        "didChange": {
2732                            "syncKind": "incremental"
2733                        },
2734                        "didClose": {},
2735                        "didSave": {},
2736                        "didFocus": {}
2737                    }
2738                },
2739                "context": {
2740                    "recentFiles": {
2741                        "maxCount": 10
2742                    },
2743                    "relatedSnippets": {},
2744                    "editHistory": {
2745                        "maxCount": 6
2746                    },
2747                    "userActions": {
2748                        "maxCount": 16
2749                    },
2750                    "openFiles": {},
2751                    "diagnostics": {}
2752                }
2753            })
2754        );
2755
2756        // Round-trip
2757        let deserialized: NesCapabilities = serde_json::from_value(json).unwrap();
2758        assert_eq!(deserialized, caps);
2759    }
2760
2761    #[test]
2762    fn test_client_nes_capabilities_serialization() {
2763        let caps = ClientNesCapabilities::new()
2764            .jump(NesJumpCapabilities::default())
2765            .rename(NesRenameCapabilities::default())
2766            .search_and_replace(NesSearchAndReplaceCapabilities::default());
2767
2768        let json = serde_json::to_value(&caps).unwrap();
2769        assert_eq!(
2770            json,
2771            json!({
2772                "jump": {},
2773                "rename": {},
2774                "searchAndReplace": {}
2775            })
2776        );
2777
2778        let deserialized: ClientNesCapabilities = serde_json::from_value(json).unwrap();
2779        assert_eq!(deserialized, caps);
2780    }
2781
2782    #[test]
2783    fn test_document_did_open_serialization() {
2784        let notification = DidOpenDocumentNotification::new(
2785            "session_123",
2786            "file:///path/to/file.rs",
2787            "rust",
2788            1,
2789            "fn main() {\n    println!(\"hello\");\n}\n",
2790        );
2791
2792        let json = serde_json::to_value(&notification).unwrap();
2793        assert_eq!(
2794            json,
2795            json!({
2796                "sessionId": "session_123",
2797                "uri": "file:///path/to/file.rs",
2798                "languageId": "rust",
2799                "version": 1,
2800                "text": "fn main() {\n    println!(\"hello\");\n}\n"
2801            })
2802        );
2803
2804        let deserialized: DidOpenDocumentNotification = serde_json::from_value(json).unwrap();
2805        assert_eq!(deserialized, notification);
2806    }
2807
2808    #[test]
2809    fn test_document_did_change_incremental_serialization() {
2810        let notification = DidChangeDocumentNotification::new(
2811            "session_123",
2812            "file:///path/to/file.rs",
2813            2,
2814            vec![TextDocumentContentChangeEvent::incremental(
2815                Range::new(Position::new(1, 4), Position::new(1, 4)),
2816                "let x = 42;\n    ",
2817            )],
2818        );
2819
2820        let json = serde_json::to_value(&notification).unwrap();
2821        assert_eq!(
2822            json,
2823            json!({
2824                "sessionId": "session_123",
2825                "uri": "file:///path/to/file.rs",
2826                "version": 2,
2827                "contentChanges": [
2828                    {
2829                        "range": {
2830                            "start": { "line": 1, "character": 4 },
2831                            "end": { "line": 1, "character": 4 }
2832                        },
2833                        "text": "let x = 42;\n    "
2834                    }
2835                ]
2836            })
2837        );
2838    }
2839
2840    #[test]
2841    fn test_document_did_change_full_serialization() {
2842        let notification = DidChangeDocumentNotification::new(
2843            "session_123",
2844            "file:///path/to/file.rs",
2845            2,
2846            vec![TextDocumentContentChangeEvent::full(
2847                "fn main() {\n    let x = 42;\n    println!(\"hello\");\n}\n",
2848            )],
2849        );
2850
2851        let json = serde_json::to_value(&notification).unwrap();
2852        assert_eq!(
2853            json,
2854            json!({
2855                "sessionId": "session_123",
2856                "uri": "file:///path/to/file.rs",
2857                "version": 2,
2858                "contentChanges": [
2859                    {
2860                        "text": "fn main() {\n    let x = 42;\n    println!(\"hello\");\n}\n"
2861                    }
2862                ]
2863            })
2864        );
2865    }
2866
2867    #[test]
2868    fn test_document_did_close_serialization() {
2869        let notification =
2870            DidCloseDocumentNotification::new("session_123", "file:///path/to/file.rs");
2871        let json = serde_json::to_value(&notification).unwrap();
2872        assert_eq!(
2873            json,
2874            json!({ "sessionId": "session_123", "uri": "file:///path/to/file.rs" })
2875        );
2876    }
2877
2878    #[test]
2879    fn test_document_did_save_serialization() {
2880        let notification =
2881            DidSaveDocumentNotification::new("session_123", "file:///path/to/file.rs");
2882        let json = serde_json::to_value(&notification).unwrap();
2883        assert_eq!(
2884            json,
2885            json!({ "sessionId": "session_123", "uri": "file:///path/to/file.rs" })
2886        );
2887    }
2888
2889    #[test]
2890    fn test_document_did_focus_serialization() {
2891        let notification = DidFocusDocumentNotification::new(
2892            "session_123",
2893            "file:///path/to/file.rs",
2894            2,
2895            Position::new(5, 12),
2896            Range::new(Position::new(0, 0), Position::new(45, 0)),
2897        );
2898
2899        let json = serde_json::to_value(&notification).unwrap();
2900        assert_eq!(
2901            json,
2902            json!({
2903                "sessionId": "session_123",
2904                "uri": "file:///path/to/file.rs",
2905                "version": 2,
2906                "position": { "line": 5, "character": 12 },
2907                "visibleRange": {
2908                    "start": { "line": 0, "character": 0 },
2909                    "end": { "line": 45, "character": 0 }
2910                }
2911            })
2912        );
2913    }
2914
2915    #[test]
2916    fn test_nes_suggestion_edit_serialization() {
2917        let suggestion = NesSuggestion::Edit(
2918            NesEditSuggestion::new(
2919                "sugg_001",
2920                "file:///path/to/other_file.rs",
2921                vec![NesTextEdit::new(
2922                    Range::new(Position::new(5, 0), Position::new(5, 10)),
2923                    "let result = helper();",
2924                )],
2925            )
2926            .cursor_position(Position::new(5, 22)),
2927        );
2928
2929        let json = serde_json::to_value(&suggestion).unwrap();
2930        assert_eq!(
2931            json,
2932            json!({
2933                "kind": "edit",
2934                "id": "sugg_001",
2935                "uri": "file:///path/to/other_file.rs",
2936                "edits": [
2937                    {
2938                        "range": {
2939                            "start": { "line": 5, "character": 0 },
2940                            "end": { "line": 5, "character": 10 }
2941                        },
2942                        "newText": "let result = helper();"
2943                    }
2944                ],
2945                "cursorPosition": { "line": 5, "character": 22 }
2946            })
2947        );
2948
2949        let deserialized: NesSuggestion = serde_json::from_value(json).unwrap();
2950        assert_eq!(deserialized, suggestion);
2951    }
2952
2953    #[test]
2954    fn test_nes_suggestion_unknown_variant() {
2955        let suggestion: NesSuggestion = serde_json::from_value(json!({
2956            "kind": "_preview",
2957            "id": "sugg_001",
2958            "label": "Preview generated file"
2959        }))
2960        .unwrap();
2961
2962        let NesSuggestion::Other(unknown) = suggestion else {
2963            panic!("expected unknown NES suggestion");
2964        };
2965
2966        assert_eq!(unknown.kind, "_preview");
2967        assert_eq!(unknown.fields.get("id"), Some(&json!("sugg_001")));
2968        assert_eq!(
2969            serde_json::to_value(NesSuggestion::Other(unknown)).unwrap(),
2970            json!({
2971                "kind": "_preview",
2972                "id": "sugg_001",
2973                "label": "Preview generated file"
2974            })
2975        );
2976    }
2977
2978    #[test]
2979    fn test_nes_suggestion_unknown_does_not_hide_malformed_known_variant() {
2980        assert!(
2981            serde_json::from_value::<NesSuggestion>(json!({
2982                "kind": "edit"
2983            }))
2984            .is_err()
2985        );
2986    }
2987
2988    #[test]
2989    fn test_nes_suggestion_jump_serialization() {
2990        let suggestion = NesSuggestion::Jump(NesJumpSuggestion::new(
2991            "sugg_002",
2992            "file:///path/to/other_file.rs",
2993            Position::new(15, 4),
2994        ));
2995
2996        let json = serde_json::to_value(&suggestion).unwrap();
2997        assert_eq!(
2998            json,
2999            json!({
3000                "kind": "jump",
3001                "id": "sugg_002",
3002                "uri": "file:///path/to/other_file.rs",
3003                "position": { "line": 15, "character": 4 }
3004            })
3005        );
3006
3007        let deserialized: NesSuggestion = serde_json::from_value(json).unwrap();
3008        assert_eq!(deserialized, suggestion);
3009    }
3010
3011    #[test]
3012    fn test_nes_suggestion_rename_serialization() {
3013        let suggestion = NesSuggestion::Rename(NesRenameSuggestion::new(
3014            "sugg_003",
3015            "file:///path/to/file.rs",
3016            Position::new(5, 10),
3017            "calculateTotal",
3018        ));
3019
3020        let json = serde_json::to_value(&suggestion).unwrap();
3021        assert_eq!(
3022            json,
3023            json!({
3024                "kind": "rename",
3025                "id": "sugg_003",
3026                "uri": "file:///path/to/file.rs",
3027                "position": { "line": 5, "character": 10 },
3028                "newName": "calculateTotal"
3029            })
3030        );
3031
3032        let deserialized: NesSuggestion = serde_json::from_value(json).unwrap();
3033        assert_eq!(deserialized, suggestion);
3034    }
3035
3036    #[test]
3037    fn test_nes_suggestion_search_and_replace_serialization() {
3038        let suggestion = NesSuggestion::SearchAndReplace(
3039            NesSearchAndReplaceSuggestion::new(
3040                "sugg_004",
3041                "file:///path/to/file.rs",
3042                "oldFunction",
3043                "newFunction",
3044            )
3045            .is_regex(false),
3046        );
3047
3048        let json = serde_json::to_value(&suggestion).unwrap();
3049        assert_eq!(
3050            json,
3051            json!({
3052                "kind": "searchAndReplace",
3053                "id": "sugg_004",
3054                "uri": "file:///path/to/file.rs",
3055                "search": "oldFunction",
3056                "replace": "newFunction",
3057                "isRegex": false
3058            })
3059        );
3060
3061        let deserialized: NesSuggestion = serde_json::from_value(json).unwrap();
3062        assert_eq!(deserialized, suggestion);
3063    }
3064
3065    #[test]
3066    fn test_nes_start_request_serialization() {
3067        let request = StartNesRequest::new()
3068            .workspace_uri("file:///Users/alice/projects/my-app")
3069            .workspace_folders(vec![WorkspaceFolder::new(
3070                "file:///Users/alice/projects/my-app",
3071                "my-app",
3072            )])
3073            .repository(NesRepository::new(
3074                "my-app",
3075                "alice",
3076                "https://github.com/alice/my-app.git",
3077            ));
3078
3079        let json = serde_json::to_value(&request).unwrap();
3080        assert_eq!(
3081            json,
3082            json!({
3083                "workspaceUri": "file:///Users/alice/projects/my-app",
3084                "workspaceFolders": [
3085                    {
3086                        "uri": "file:///Users/alice/projects/my-app",
3087                        "name": "my-app"
3088                    }
3089                ],
3090                "repository": {
3091                    "name": "my-app",
3092                    "owner": "alice",
3093                    "remoteUrl": "https://github.com/alice/my-app.git"
3094                }
3095            })
3096        );
3097    }
3098
3099    #[test]
3100    fn test_nes_start_response_serialization() {
3101        let response = StartNesResponse::new("session_abc123");
3102        let json = serde_json::to_value(&response).unwrap();
3103        assert_eq!(json, json!({ "sessionId": "session_abc123" }));
3104    }
3105
3106    #[test]
3107    fn test_nes_trigger_kind_serialization() {
3108        assert_eq!(
3109            serde_json::to_value(&NesTriggerKind::Automatic).unwrap(),
3110            json!("automatic")
3111        );
3112        assert_eq!(
3113            serde_json::to_value(&NesTriggerKind::Diagnostic).unwrap(),
3114            json!("diagnostic")
3115        );
3116        assert_eq!(
3117            serde_json::to_value(&NesTriggerKind::Manual).unwrap(),
3118            json!("manual")
3119        );
3120    }
3121
3122    #[test]
3123    fn test_nes_reject_reason_serialization() {
3124        assert_eq!(
3125            serde_json::to_value(&NesRejectReason::Rejected).unwrap(),
3126            json!("rejected")
3127        );
3128        assert_eq!(
3129            serde_json::to_value(&NesRejectReason::Ignored).unwrap(),
3130            json!("ignored")
3131        );
3132        assert_eq!(
3133            serde_json::to_value(&NesRejectReason::Replaced).unwrap(),
3134            json!("replaced")
3135        );
3136        assert_eq!(
3137            serde_json::to_value(&NesRejectReason::Cancelled).unwrap(),
3138            json!("cancelled")
3139        );
3140    }
3141
3142    #[test]
3143    fn test_nes_accept_notification_serialization() {
3144        let notification = AcceptNesNotification::new("session_123", "sugg_001");
3145        let json = serde_json::to_value(&notification).unwrap();
3146        assert_eq!(
3147            json,
3148            json!({ "sessionId": "session_123", "id": "sugg_001" })
3149        );
3150    }
3151
3152    #[test]
3153    fn test_nes_reject_notification_serialization() {
3154        let notification =
3155            RejectNesNotification::new("session_123", "sugg_001").reason(NesRejectReason::Rejected);
3156        let json = serde_json::to_value(&notification).unwrap();
3157        assert_eq!(
3158            json,
3159            json!({ "sessionId": "session_123", "id": "sugg_001", "reason": "rejected" })
3160        );
3161    }
3162
3163    #[test]
3164    fn test_nes_suggest_request_with_context_serialization() {
3165        let request = SuggestNesRequest::new(
3166            "session_123",
3167            "file:///path/to/file.rs",
3168            2,
3169            Position::new(5, 12),
3170            NesTriggerKind::Automatic,
3171        )
3172        .selection(Range::new(Position::new(5, 4), Position::new(5, 12)))
3173        .context(
3174            NesSuggestContext::new()
3175                .recent_files(vec![NesRecentFile::new(
3176                    "file:///path/to/utils.rs",
3177                    "rust",
3178                    "pub fn helper() -> i32 { 42 }\n",
3179                )])
3180                .diagnostics(vec![NesDiagnostic::new(
3181                    "file:///path/to/file.rs",
3182                    Range::new(Position::new(5, 0), Position::new(5, 10)),
3183                    NesDiagnosticSeverity::Error,
3184                    "cannot find value `foo` in this scope",
3185                )]),
3186        );
3187
3188        let json = serde_json::to_value(&request).unwrap();
3189        assert_eq!(json["sessionId"], "session_123");
3190        assert_eq!(json["uri"], "file:///path/to/file.rs");
3191        assert_eq!(json["version"], 2);
3192        assert_eq!(json["triggerKind"], "automatic");
3193        assert_eq!(
3194            json["context"]["recentFiles"][0]["uri"],
3195            "file:///path/to/utils.rs"
3196        );
3197        assert_eq!(json["context"]["diagnostics"][0]["severity"], "error");
3198    }
3199
3200    #[test]
3201    fn test_text_document_sync_kind_serialization() {
3202        assert_eq!(
3203            serde_json::to_value(&TextDocumentSyncKind::Full).unwrap(),
3204            json!("full")
3205        );
3206        assert_eq!(
3207            serde_json::to_value(&TextDocumentSyncKind::Incremental).unwrap(),
3208            json!("incremental")
3209        );
3210        assert!(serde_json::from_value::<TextDocumentSyncKind>(json!("_future")).is_err());
3211    }
3212
3213    #[test]
3214    fn test_document_event_capabilities_drop_unknown_did_change_sync_kind() {
3215        let caps: NesDocumentEventCapabilities = serde_json::from_value(json!({
3216            "didChange": {
3217                "syncKind": "_future"
3218            }
3219        }))
3220        .unwrap();
3221
3222        assert_eq!(caps.did_change, None);
3223    }
3224
3225    #[test]
3226    fn test_document_did_change_capabilities_requires_sync_kind() {
3227        assert!(serde_json::from_value::<NesDocumentDidChangeCapabilities>(json!({})).is_err());
3228    }
3229
3230    #[test]
3231    fn test_nes_suggest_response_serialization() {
3232        let response = SuggestNesResponse::new(vec![
3233            NesSuggestion::Edit(NesEditSuggestion::new(
3234                "sugg_001",
3235                "file:///path/to/file.rs",
3236                vec![NesTextEdit::new(
3237                    Range::new(Position::new(5, 0), Position::new(5, 10)),
3238                    "let result = helper();",
3239                )],
3240            )),
3241            NesSuggestion::Jump(NesJumpSuggestion::new(
3242                "sugg_002",
3243                "file:///path/to/other.rs",
3244                Position::new(10, 0),
3245            )),
3246        ]);
3247
3248        let json = serde_json::to_value(&response).unwrap();
3249        assert_eq!(json["suggestions"].as_array().unwrap().len(), 2);
3250        assert_eq!(json["suggestions"][0]["kind"], "edit");
3251        assert_eq!(json["suggestions"][1]["kind"], "jump");
3252    }
3253}