From 1fb7a3b051ca8989203bd6b3e6aca84abaf0608b Mon Sep 17 00:00:00 2001 From: "Anna.Zhdan" Date: Sun, 15 Mar 2026 13:44:58 +0100 Subject: [PATCH 01/13] initial implementation: nes --- Cargo.toml | 2 + docs/protocol/draft/schema.mdx | 1689 +++++++++++++++++++++++-- schema/meta.unstable.json | 9 + schema/schema.unstable.json | 1539 +++++++++++++++++++++- src/agent.rs | 170 +++ src/bin/generate.rs | 9 + src/client.rs | 39 + src/lib.rs | 4 + src/nes.rs | 2172 ++++++++++++++++++++++++++++++++ src/rpc.rs | 185 +++ 10 files changed, 5708 insertions(+), 110 deletions(-) create mode 100644 src/nes.rs diff --git a/Cargo.toml b/Cargo.toml index 86160b45..e0555ddf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ include = ["/src/**/*.rs", "/README.md", "/LICENSE", "/Cargo.toml"] unstable = [ "unstable_auth_methods", "unstable_cancel_request", + "unstable_nes", "unstable_elicitation", "unstable_logout", "unstable_session_fork", @@ -29,6 +30,7 @@ unstable = [ ] unstable_auth_methods = [] unstable_cancel_request = [] +unstable_nes = [] unstable_elicitation = [] unstable_logout = [] unstable_session_fork = [] diff --git a/docs/protocol/draft/schema.mdx b/docs/protocol/draft/schema.mdx index d9a81347..28f07940 100644 --- a/docs/protocol/draft/schema.mdx +++ b/docs/protocol/draft/schema.mdx @@ -62,6 +62,148 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte + +### document/didChange + +**UNSTABLE** + +Notification sent when a file is edited. + +#### DocumentDidChangeNotification + +Notification sent when a file is edited. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP. + + + TextDocumentContentChangeEvent[] + + } + required +> + The content changes. + + + The URI of the changed document. + + + The new version number of the document. + + + +### document/didClose + +**UNSTABLE** + +Notification sent when a file is closed. + +#### DocumentDidCloseNotification + +Notification sent when a file is closed. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP. + + + The URI of the closed document. + + + +### document/didFocus + +**UNSTABLE** + +Notification sent when a file becomes the active editor tab. + +#### DocumentDidFocusNotification + +Notification sent when a file becomes the active editor tab. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP. + +Position} required> + The current cursor position. + + + The URI of the focused document. + + + The version number of the document. + +Range} required> + The portion of the file currently visible in the editor viewport. + + + +### document/didOpen + +**UNSTABLE** + +Notification sent when a file is opened in the editor. + +#### DocumentDidOpenNotification + +Notification sent when a file is opened in the editor. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP. + + + The language identifier of the document (e.g., "rust", "python"). + + + The full text content of the document. + + + The URI of the opened document. + + + The version number of the document. + + + +### document/didSave + +**UNSTABLE** + +Notification sent when a file is saved. + +#### DocumentDidSaveNotification + +Notification sent when a file is saved. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP. + + + The URI of the saved document. + + ### initialize Establishes the connection with a client and negotiates protocol capabilities. @@ -213,6 +355,214 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte + +### nes/accept + +**UNSTABLE** + +Notification sent when a suggestion is accepted. + +#### NesAcceptNotification + +Notification sent when a suggestion is accepted. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP. + + + The ID of the accepted suggestion. + + + +### nes/reject + +**UNSTABLE** + +Notification sent when a suggestion is rejected. + +#### NesRejectNotification + +Notification sent when a suggestion is rejected. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP. + + + The ID of the rejected suggestion. + + + + NesRejectReason + + | null + + } +> + The reason for rejection. + + + +### nes/start + +**UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +Starts an NES session. + +#### NesStartRequest + +Request to start an NES session. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP. + + + + NesRepository + + | null + + } +> + Repository metadata, if the workspace is a git repository. + + + + WorkspaceFolder[] + + | null + + } +> + The workspace folders. + + + The root URI of the workspace. + + +#### NesStartResponse + +Response to `nes/start`. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP. + +NesSessionId} + required +> + The session ID for the newly started NES session. + + + +### nes/suggest + +**UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +Requests a code suggestion. + +#### NesSuggestRequest + +Request for a code suggestion. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP. + + + + NesSuggestContext + + | null + + } +> + Context for the suggestion, included based on agent capabilities. + +Position} required> + The current cursor position. + + + + Range + + | null + + } +> + The current text selection range, if any. + +NesTriggerKind} + required +> + What triggered this suggestion request. + + + The URI of the document to suggest for. + + + The version number of the document. + + +#### NesSuggestResponse + +Response to `nes/suggest`. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP. + +NesSuggestion[]} + required +> + The list of suggestions. + + ### session/cancel @@ -1799,6 +2149,22 @@ Authentication-related capabilities supported by the agent. - Default: `{"http":false,"sse":false}` + +NesCapabilities | null} > + **UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +NES (Next Edit Suggestions) capabilities supported by the agent. + + +PositionEncodingKind | null} > + **UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +The position encoding selected by the agent from the client's supported encodings. + PromptCapabilities} > Prompt capabilities supported by the agent. @@ -2311,6 +2677,22 @@ Determines which file operations the agent can request. - Default: `{"readTextFile":false,"writeTextFile":false}` + +GeneralCapabilities | null} > + **UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +General client capabilities (e.g. position encodings). + + +ClientNesCapabilities | null} > + **UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +NES (Next Edit Suggestions) capabilities supported by the client. + Whether the Client support all `terminal/*` methods. @@ -2319,6 +2701,31 @@ Determines which file operations the agent can request. +## ClientNesCapabilities + +NES capabilities advertised by the client during initialization. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP. + + + + NesIdeActionsCapabilities + + | null + + } +> + IDE actions the client supports. + + ## ConfigOptionUpdate Session configuration options have been updated. @@ -3317,21 +3724,47 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte -## HttpHeader +## GeneralCapabilities -An HTTP header to set when making requests to the MCP server. +General client capabilities (not specific to a particular feature). **Type:** Object **Properties:** - - The _meta property is reserved by ACP to allow clients and agents to attach additional -metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. - -See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) - + + The _meta property is reserved by ACP. + + + + PositionEncodingKind[] + + | null + + } +> + Position encodings supported by the client, in order of preference. If + omitted, defaults to UTF-16. + + +## HttpHeader + +An HTTP header to set when making requests to the MCP server. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + The name of the HTTP header. @@ -3631,156 +4064,1132 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte URL to the MCP server. -## McpServerStdio +## McpServerStdio + +Stdio transport configuration for MCP. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + + +"string"[]} required> + Command-line arguments to pass to the MCP server. + + + Path to the MCP server executable. + +EnvVariable[]} required> + Environment variables to set when launching the MCP server. + + + Human-readable name identifying this MCP server. + + +## ModelId + +**UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +A unique identifier for a model. + +**Type:** `string` + +## ModelInfo + +**UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +Information about a selectable model. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + + + + Optional description of the model. + +ModelId} required> + Unique identifier for the model. + + + Human-readable name of the model. + + +## MultiSelectItems + +Items for a multi-select (array) property schema. + +**Type:** Union + + +Untitled multi-select items with plain string values. + + + + + "string" + [] + + } + required +> + Allowed enum values. + +ElicitationStringType} + required +> + Item type discriminator. Must be `"string"`. + + + + + + +Titled multi-select items with human-readable labels. + + + +EnumOption[]} + required +> + Titled enum options. + + + + + +## MultiSelectPropertySchema + +Schema for multi-select (array) properties in an elicitation form. + +**Type:** Object + +**Properties:** + +<>"string"[] | null} > + Default selected values. + + + Human-readable description. + +MultiSelectItems} required> + The items definition describing allowed values. + + + Maximum number of items to select. + + - Minimum: `0` + + + + Minimum number of items to select. + + - Minimum: `0` + + + + Optional title for the property. + + +## NesActionSuggestion + +An IDE action suggestion. + +**Type:** Object + +**Properties:** + + + The IDE action to perform (must match a client-advertised action). + + + Action parameters matching the schema from the client's capability. + + + Unique identifier for accept/reject tracking. + + +## NesCapabilities + +NES capabilities advertised by the agent during initialization. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP to allow clients and agents to attach + additional metadata to their interactions. + + + + NesContextCapabilities + + | null + + } +> + Context the agent wants attached to each suggestion request. + + + + NesEventCapabilities + + | null + + } +> + Events the agent wants to receive. + + +## NesContextCapabilities + +Context capabilities the agent wants attached to each suggestion request. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP. + + + + NesDiagnosticsCapabilities + + | null + + } +> + Whether the agent wants diagnostics context. + + + + NesEditHistoryCapabilities + + | null + + } +> + Whether the agent wants edit history context. + + + + NesOpenFilesCapabilities + + | null + + } +> + Whether the agent wants open files context. + + + + NesRecentFilesCapabilities + + | null + + } +> + Whether the agent wants recent files context. + + + + + NesRelatedSnippetsCapabilities + + + | null + + } +> + Whether the agent wants related snippets context. + + + + NesUserActionsCapabilities + + | null + + } +> + Whether the agent wants user actions context. + + +## NesDiagnostic + +A diagnostic (error, warning, etc.). + +**Type:** Object + +**Properties:** + + + The diagnostic message. + +Range} required> + The range of the diagnostic. + +NesDiagnosticSeverity} + required +> + The severity of the diagnostic. + + + The URI of the file containing the diagnostic. + + +## NesDiagnosticSeverity + +Severity of a diagnostic. + +**Type:** Union + + + An error. + + + + A warning. + + + + An informational message. + + + + A hint. + + +## NesDiagnosticsCapabilities + +Capabilities for diagnostics context. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP. + + +## NesDocumentDidChangeCapabilities + +Capabilities for `document/didChange` events. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP. + + + + TextDocumentSyncKind + + | null + + } +> + The sync kind the agent wants: `"full"` or `"incremental"`. + + +## NesDocumentDidCloseCapabilities + +Marker for `document/didClose` capability support. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP. + + +## NesDocumentDidFocusCapabilities + +Marker for `document/didFocus` capability support. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP. + + +## NesDocumentDidOpenCapabilities + +Marker for `document/didOpen` capability support. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP. + + +## NesDocumentDidSaveCapabilities + +Marker for `document/didSave` capability support. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP. + + +## NesDocumentEventCapabilities + +Document event capabilities the agent wants to receive. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP. + + + + + NesDocumentDidChangeCapabilities + + + | null + + } +> + Whether the agent wants `document/didChange` events, and the sync kind. + + + + + NesDocumentDidCloseCapabilities + + + | null + + } +> + Whether the agent wants `document/didClose` events. + + + + + NesDocumentDidFocusCapabilities + + + | null + + } +> + Whether the agent wants `document/didFocus` events. + + + + + NesDocumentDidOpenCapabilities + + + | null + + } +> + Whether the agent wants `document/didOpen` events. + + + + + NesDocumentDidSaveCapabilities + + + | null + + } +> + Whether the agent wants `document/didSave` events. + + +## NesEditHistoryCapabilities + +Capabilities for edit history context. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP. + + + Maximum number of edit history entries the agent can use. + + - Minimum: `0` + + + +## NesEditHistoryEntry + +An entry in the edit history. + +**Type:** Object + +**Properties:** + + + A diff representing the edit. + + + The URI of the edited file. + + +## NesEditSuggestion + +A text edit suggestion. + +**Type:** Object + +**Properties:** + + + + Position + + | null + + } +> + Optional suggested cursor position after applying edits. + +NesTextEdit[]} + required +> + The text edits to apply. + + + Unique identifier for accept/reject tracking. + + + The URI of the file to edit. + + +## NesEventCapabilities + +Event capabilities the agent can consume. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP. + + + + NesDocumentEventCapabilities + + | null + + } +> + Document event capabilities. + + +## NesExcerpt + +A code excerpt from a file. + +**Type:** Object + +**Properties:** + + + The end line of the excerpt (zero-based). + + - Minimum: `0` + + + + The start line of the excerpt (zero-based). + + - Minimum: `0` + + + + The text content of the excerpt. + + +## NesIdeActionsCapabilities + +IDE actions the client can perform. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP. + + + + NesRenameActionCapabilities + + | null + + } +> + Whether the client supports the `rename` action. + + + + + NesSearchAndReplaceActionCapabilities + + + | null + + } +> + Whether the client supports the `searchAndReplace` action. + + +## NesJumpSuggestion + +A jump-to-location suggestion. + +**Type:** Object + +**Properties:** + + + Unique identifier for accept/reject tracking. + +Position} required> + The target position within the file. + + + The file to navigate to. + + +## NesOpenFile + +An open file in the editor. + +**Type:** Object + +**Properties:** + + + The language identifier. + + + Timestamp in milliseconds since epoch of when the file was last focused. + + - Minimum: `0` + + + + The URI of the file. + +Range | null} > + The visible range in the editor, if any. + + +## NesOpenFilesCapabilities + +Capabilities for open files context. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP. + + +## NesRecentFile + +A recently accessed file. + +**Type:** Object + +**Properties:** + + + The language identifier. + + + The full text content of the file. + + + The URI of the file. + + +## NesRecentFilesCapabilities + +Capabilities for recent files context. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP. + + + Maximum number of recent files the agent can use. + + - Minimum: `0` + + + +## NesRejectReason + +The reason a suggestion was rejected. + +**Type:** Union + + + The user explicitly dismissed the suggestion. + + + + The suggestion was shown but the user continued editing without interacting. + + + + The suggestion was superseded by a newer suggestion. + + + + The request was cancelled before the agent returned a response. + + +## NesRelatedSnippet + +A related code snippet from a file. + +**Type:** Object + +**Properties:** + +NesExcerpt[]} + required +> + The code excerpts. + + + The URI of the file containing the snippets. + + +## NesRelatedSnippetsCapabilities + +Capabilities for related snippets context. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP. + + +## NesRenameActionCapabilities -Stdio transport configuration for MCP. +Marker for rename action support. **Type:** Object **Properties:** - - The _meta property is reserved by ACP to allow clients and agents to attach additional -metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. + + The _meta property is reserved by ACP. + -See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) +## NesRepository +Repository metadata for an NES session. + +**Type:** Object + +**Properties:** + + + The repository name. -"string"[]} required> - Command-line arguments to pass to the MCP server. - - - Path to the MCP server executable. - -EnvVariable[]} required> - Environment variables to set when launching the MCP server. + + The repository owner. - - Human-readable name identifying this MCP server. + + The remote URL of the repository. -## ModelId +## NesSearchAndReplaceActionCapabilities -**UNSTABLE** +Marker for search and replace action support. -This capability is not part of the spec yet, and may be removed or changed at any point. +**Type:** Object -A unique identifier for a model. +**Properties:** -**Type:** `string` + + The _meta property is reserved by ACP. + -## ModelInfo +## NesSessionId -**UNSTABLE** +A unique identifier for an NES session, distinct from chat `SessionId`. -This capability is not part of the spec yet, and may be removed or changed at any point. +**Type:** `string` -Information about a selectable model. +## NesSuggestContext + +Context attached to a suggestion request. **Type:** Object **Properties:** - - The _meta property is reserved by ACP to allow clients and agents to attach additional -metadata to their interactions. Implementations MUST NOT make assumptions about values at -these keys. - -See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) - + + The _meta property is reserved by ACP. - - Optional description of the model. + + + NesDiagnostic[] + + | null + + } +> + Current diagnostics (errors, warnings). -ModelId} required> - Unique identifier for the model. + + + NesEditHistoryEntry[] + + | null + + } +> + Recent edit history. - - Human-readable name of the model. + + + NesOpenFile[] + + | null + + } +> + Currently open files in the editor. + + + + NesRecentFile[] + + | null + + } +> + Recently accessed files. + + + + NesRelatedSnippet[] + + | null + + } +> + Related code snippets. + + + + NesUserAction[] + + | null + + } +> + Recent user actions (typing, navigation, etc.). -## MultiSelectItems +## NesSuggestion -Items for a multi-select (array) property schema. +A suggestion returned by the agent. **Type:** Union - -Untitled multi-select items with plain string values. + +A text edit suggestion. - "string" - [] + + Position + + | null } - required > - Allowed enum values. + Optional suggested cursor position after applying edits. ElicitationStringType} + name="edits" + type={NesTextEdit[]} required > - Item type discriminator. Must be `"string"`. + The text edits to apply. + + + Unique identifier for accept/reject tracking. + + + The discriminator value. Must be `"edit"`. + + + The URI of the file to edit. - -Titled multi-select items with human-readable labels. + +A jump-to-location suggestion. -EnumOption[]} - required -> - Titled enum options. + + Unique identifier for accept/reject tracking. + + + The discriminator value. Must be `"jump"`. + +Position} required> + The target position within the file. + + + The file to navigate to. -## MultiSelectPropertySchema + +An IDE action suggestion. -Schema for multi-select (array) properties in an elicitation form. + + + + The IDE action to perform (must match a client-advertised action). + + + Action parameters matching the schema from the client's capability. + + + Unique identifier for accept/reject tracking. + + + The discriminator value. Must be `"action"`. + + + + + +## NesTextEdit + +A text edit within a suggestion. **Type:** Object **Properties:** -<>"string"[] | null} > - Default selected values. + + The replacement text. - - Human-readable description. +Range} required> + The range to replace. -MultiSelectItems} required> - The items definition describing allowed values. + +## NesTriggerKind + +What triggered the suggestion request. + +**Type:** Union + + + Triggered by user typing or cursor movement. - - Maximum number of items to select. + + + Triggered by a diagnostic appearing at or near the cursor. + + + + Triggered by an explicit user action (keyboard shortcut). + + +## NesUserAction + +A user action (typing, cursor movement, etc.). + +**Type:** Object + +**Properties:** + + + The kind of action (e.g., "insertChar", "cursorMovement"). + + + The line number where the action occurred. - Minimum: `0` - - Minimum number of items to select. + + The character offset where the action occurred. - Minimum: `0` - - Optional title for the property. + + Timestamp in milliseconds since epoch. + + - Minimum: `0` + + + + The URI of the file where the action occurred. + + +## NesUserActionsCapabilities + +Capabilities for user actions context. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP. + + + Maximum number of user actions the agent can use. + + - Minimum: `0` + ## NumberPropertySchema @@ -3967,6 +5376,49 @@ See protocol docs: [Plan Entries](https://agentclientprotocol.com/protocol/agent The task has been successfully completed. +## Position + +A zero-based position in a text document. + +The meaning of `character` depends on the negotiated position encoding. + +**Type:** Object + +**Properties:** + + + Zero-based character offset (encoding-dependent). + + - Minimum: `0` + + + + Zero-based line number. + + - Minimum: `0` + + + +## PositionEncodingKind + +The encoding used for character offsets in positions. + +Follows the same conventions as LSP 3.17. The default is UTF-16. + +**Type:** Union + + + Character offsets count UTF-16 code units. This is the default. + + + + Character offsets count Unicode code points. + + + + Character offsets count UTF-8 code units (bytes). + + ## PromptCapabilities Prompt capabilities supported by the agent in `session/prompt` requests. @@ -4030,6 +5482,21 @@ Non-breaking changes should be introduced via capabilities. | Minimum | `0` | | Maximum | `65535` | +## Range + +A range in a text document, expressed as start and end positions. + +**Type:** Object + +**Properties:** + +Position} required> + The end position (exclusive). + +Position} required> + The start position (inclusive). + + ## RequestId JSON RPC Request Id @@ -5199,6 +6666,49 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte +## TextDocumentContentChangeEvent + +A content change event for a document. + +When `range` is `None`, `text` is the full content of the document. +When `range` is `Some`, `text` replaces the given range. + +**Type:** Object + +**Properties:** + + + + Range + + | null + + } +> + The range of the document that changed. If `None`, the entire content is + replaced. + + + The new text for the range, or the full document content if `range` is `None`. + + +## TextDocumentSyncKind + +How the agent wants document changes delivered. + +**Type:** Union + + + Client sends the entire file content on each change. + + + + Client sends only the changed ranges. + + ## TextResourceContents Text-based resource contents. @@ -5668,3 +7178,18 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte - Minimum: `0` + +## WorkspaceFolder + +A workspace folder. + +**Type:** Object + +**Properties:** + + + The display name of the folder. + + + The URI of the folder. + diff --git a/schema/meta.unstable.json b/schema/meta.unstable.json index 6198214a..3c00e464 100644 --- a/schema/meta.unstable.json +++ b/schema/meta.unstable.json @@ -1,8 +1,17 @@ { "agentMethods": { "authenticate": "authenticate", + "document_did_change": "document/didChange", + "document_did_close": "document/didClose", + "document_did_focus": "document/didFocus", + "document_did_open": "document/didOpen", + "document_did_save": "document/didSave", "initialize": "initialize", "logout": "logout", + "nes_accept": "nes/accept", + "nes_reject": "nes/reject", + "nes_start": "nes/start", + "nes_suggest": "nes/suggest", "session_cancel": "session/cancel", "session_close": "session/close", "session_fork": "session/fork", diff --git a/schema/schema.unstable.json b/schema/schema.unstable.json index 081e1a1f..1c5bc667 100644 --- a/schema/schema.unstable.json +++ b/schema/schema.unstable.json @@ -56,6 +56,28 @@ }, "description": "MCP capabilities supported by the agent." }, + "nes": { + "anyOf": [ + { + "$ref": "#/$defs/NesCapabilities" + }, + { + "type": "null" + } + ], + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nNES (Next Edit Suggestions) capabilities supported by the agent." + }, + "positionEncoding": { + "anyOf": [ + { + "$ref": "#/$defs/PositionEncodingKind" + }, + { + "type": "null" + } + ], + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nThe position encoding selected by the agent from the client's supported encodings." + }, "promptCapabilities": { "allOf": [ { @@ -357,6 +379,22 @@ ], "title": "SetSessionModelResponse" }, + { + "allOf": [ + { + "$ref": "#/$defs/NesStartResponse" + } + ], + "title": "NesStartResponse" + }, + { + "allOf": [ + { + "$ref": "#/$defs/NesSuggestResponse" + } + ], + "title": "NesSuggestResponse" + }, { "allOf": [ { @@ -850,6 +888,28 @@ }, "description": "File system capabilities supported by the client.\nDetermines which file operations the agent can request." }, + "general": { + "anyOf": [ + { + "$ref": "#/$defs/GeneralCapabilities" + }, + { + "type": "null" + } + ], + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nGeneral client capabilities (e.g. position encodings)." + }, + "nes": { + "anyOf": [ + { + "$ref": "#/$defs/ClientNesCapabilities" + }, + { + "type": "null" + } + ], + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nNES (Next Edit Suggestions) capabilities supported by the client." + }, "terminal": { "default": false, "description": "Whether the Client support all `terminal/*` methods.", @@ -858,6 +918,28 @@ }, "type": "object" }, + "ClientNesCapabilities": { + "description": "NES capabilities advertised by the client during initialization.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + }, + "ideActions": { + "anyOf": [ + { + "$ref": "#/$defs/NesIdeActionsCapabilities" + }, + { + "type": "null" + } + ], + "description": "IDE actions the client supports." + } + }, + "type": "object" + }, "ClientNotification": { "properties": { "method": { @@ -876,6 +958,69 @@ "description": "Cancels ongoing operations for a session.\n\nThis is a notification sent by the client to cancel an ongoing prompt turn.\n\nUpon receiving this notification, the Agent SHOULD:\n- Stop all language model requests as soon as possible\n- Abort all tool call invocations in progress\n- Send any pending `session/update` notifications\n- Respond to the original `session/prompt` request with `StopReason::Cancelled`\n\nSee protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/prompt-turn#cancellation)", "title": "CancelNotification" }, + { + "allOf": [ + { + "$ref": "#/$defs/DocumentDidOpenNotification" + } + ], + "description": "**UNSTABLE**\n\nNotification sent when a file is opened in the editor.", + "title": "DocumentDidOpenNotification" + }, + { + "allOf": [ + { + "$ref": "#/$defs/DocumentDidChangeNotification" + } + ], + "description": "**UNSTABLE**\n\nNotification sent when a file is edited.", + "title": "DocumentDidChangeNotification" + }, + { + "allOf": [ + { + "$ref": "#/$defs/DocumentDidCloseNotification" + } + ], + "description": "**UNSTABLE**\n\nNotification sent when a file is closed.", + "title": "DocumentDidCloseNotification" + }, + { + "allOf": [ + { + "$ref": "#/$defs/DocumentDidSaveNotification" + } + ], + "description": "**UNSTABLE**\n\nNotification sent when a file is saved.", + "title": "DocumentDidSaveNotification" + }, + { + "allOf": [ + { + "$ref": "#/$defs/DocumentDidFocusNotification" + } + ], + "description": "**UNSTABLE**\n\nNotification sent when a file becomes the active editor tab.", + "title": "DocumentDidFocusNotification" + }, + { + "allOf": [ + { + "$ref": "#/$defs/NesAcceptNotification" + } + ], + "description": "**UNSTABLE**\n\nNotification sent when a suggestion is accepted.", + "title": "NesAcceptNotification" + }, + { + "allOf": [ + { + "$ref": "#/$defs/NesRejectNotification" + } + ], + "description": "**UNSTABLE**\n\nNotification sent when a suggestion is rejected.", + "title": "NesRejectNotification" + }, { "allOf": [ { @@ -1027,6 +1172,24 @@ "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nSelect a model for a given session.", "title": "SetSessionModelRequest" }, + { + "allOf": [ + { + "$ref": "#/$defs/NesStartRequest" + } + ], + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nStarts an NES session.", + "title": "NesStartRequest" + }, + { + "allOf": [ + { + "$ref": "#/$defs/NesSuggestRequest" + } + ], + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nRequests a code suggestion.", + "title": "NesSuggestRequest" + }, { "allOf": [ { @@ -1476,6 +1639,142 @@ "required": ["path", "newText"], "type": "object" }, + "DocumentDidChangeNotification": { + "description": "Notification sent when a file is edited.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + }, + "contentChanges": { + "description": "The content changes.", + "items": { + "$ref": "#/$defs/TextDocumentContentChangeEvent" + }, + "type": "array" + }, + "uri": { + "description": "The URI of the changed document.", + "type": "string" + }, + "version": { + "description": "The new version number of the document.", + "format": "int64", + "type": "integer" + } + }, + "required": ["uri", "version", "contentChanges"], + "type": "object", + "x-method": "document/didChange", + "x-side": "agent" + }, + "DocumentDidCloseNotification": { + "description": "Notification sent when a file is closed.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + }, + "uri": { + "description": "The URI of the closed document.", + "type": "string" + } + }, + "required": ["uri"], + "type": "object", + "x-method": "document/didClose", + "x-side": "agent" + }, + "DocumentDidFocusNotification": { + "description": "Notification sent when a file becomes the active editor tab.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + }, + "position": { + "allOf": [ + { + "$ref": "#/$defs/Position" + } + ], + "description": "The current cursor position." + }, + "uri": { + "description": "The URI of the focused document.", + "type": "string" + }, + "version": { + "description": "The version number of the document.", + "format": "int64", + "type": "integer" + }, + "visibleRange": { + "allOf": [ + { + "$ref": "#/$defs/Range" + } + ], + "description": "The portion of the file currently visible in the editor viewport." + } + }, + "required": ["uri", "version", "position", "visibleRange"], + "type": "object", + "x-method": "document/didFocus", + "x-side": "agent" + }, + "DocumentDidOpenNotification": { + "description": "Notification sent when a file is opened in the editor.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + }, + "languageId": { + "description": "The language identifier of the document (e.g., \"rust\", \"python\").", + "type": "string" + }, + "text": { + "description": "The full text content of the document.", + "type": "string" + }, + "uri": { + "description": "The URI of the opened document.", + "type": "string" + }, + "version": { + "description": "The version number of the document.", + "format": "int64", + "type": "integer" + } + }, + "required": ["uri", "languageId", "version", "text"], + "type": "object", + "x-method": "document/didOpen", + "x-side": "agent" + }, + "DocumentDidSaveNotification": { + "description": "Notification sent when a file is saved.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + }, + "uri": { + "description": "The URI of the saved document.", + "type": "string" + } + }, + "required": ["uri"], + "type": "object", + "x-method": "document/didSave", + "x-side": "agent" + }, "ElicitationAcceptAction": { "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nThe user accepted the elicitation and provided content.", "properties": { @@ -2202,6 +2501,24 @@ "x-method": "session/fork", "x-side": "agent" }, + "GeneralCapabilities": { + "description": "General client capabilities (not specific to a particular feature).", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + }, + "positionEncodings": { + "description": "Position encodings supported by the client, in order of preference.\nIf omitted, defaults to UTF-16.", + "items": { + "$ref": "#/$defs/PositionEncodingKind" + }, + "type": ["array", "null"] + } + }, + "type": "object" + }, "HttpHeader": { "description": "An HTTP header to set when making requests to the MCP server.", "properties": { @@ -2869,58 +3186,1110 @@ "required": ["items"], "type": "object" }, - "NewSessionRequest": { - "description": "Request parameters for creating a new session.\n\nSee protocol docs: [Creating a Session](https://agentclientprotocol.com/protocol/session-setup#creating-a-session)", + "NesAcceptNotification": { + "description": "Notification sent when a suggestion is accepted.", "properties": { "_meta": { "additionalProperties": true, - "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", + "description": "The _meta property is reserved by ACP.", "type": ["object", "null"] }, - "cwd": { - "description": "The working directory for this session. Must be an absolute path.", + "id": { + "description": "The ID of the accepted suggestion.", "type": "string" - }, - "mcpServers": { - "description": "List of MCP (Model Context Protocol) servers the agent should connect to.", - "items": { - "$ref": "#/$defs/McpServer" - }, - "type": "array" } }, - "required": ["cwd", "mcpServers"], + "required": ["id"], "type": "object", - "x-method": "session/new", + "x-method": "nes/accept", "x-side": "agent" }, - "NewSessionResponse": { - "description": "Response from creating a new session.\n\nSee protocol docs: [Creating a Session](https://agentclientprotocol.com/protocol/session-setup#creating-a-session)", + "NesActionSuggestion": { + "description": "An IDE action suggestion.", + "properties": { + "actionId": { + "description": "The IDE action to perform (must match a client-advertised action).", + "type": "string" + }, + "arguments": { + "description": "Action parameters matching the schema from the client's capability." + }, + "id": { + "description": "Unique identifier for accept/reject tracking.", + "type": "string" + } + }, + "required": ["id", "actionId"], + "type": "object" + }, + "NesCapabilities": { + "description": "NES capabilities advertised by the agent during initialization.", "properties": { "_meta": { "additionalProperties": true, - "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions.", "type": ["object", "null"] }, - "configOptions": { - "description": "Initial session configuration options if supported by the Agent.", - "items": { - "$ref": "#/$defs/SessionConfigOption" - }, - "type": ["array", "null"] - }, - "models": { + "context": { "anyOf": [ { - "$ref": "#/$defs/SessionModelState" + "$ref": "#/$defs/NesContextCapabilities" }, { "type": "null" } ], - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nInitial model state if supported by the Agent" + "description": "Context the agent wants attached to each suggestion request." }, - "modes": { + "events": { + "anyOf": [ + { + "$ref": "#/$defs/NesEventCapabilities" + }, + { + "type": "null" + } + ], + "description": "Events the agent wants to receive." + } + }, + "type": "object" + }, + "NesContextCapabilities": { + "description": "Context capabilities the agent wants attached to each suggestion request.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + }, + "diagnostics": { + "anyOf": [ + { + "$ref": "#/$defs/NesDiagnosticsCapabilities" + }, + { + "type": "null" + } + ], + "description": "Whether the agent wants diagnostics context." + }, + "editHistory": { + "anyOf": [ + { + "$ref": "#/$defs/NesEditHistoryCapabilities" + }, + { + "type": "null" + } + ], + "description": "Whether the agent wants edit history context." + }, + "openFiles": { + "anyOf": [ + { + "$ref": "#/$defs/NesOpenFilesCapabilities" + }, + { + "type": "null" + } + ], + "description": "Whether the agent wants open files context." + }, + "recentFiles": { + "anyOf": [ + { + "$ref": "#/$defs/NesRecentFilesCapabilities" + }, + { + "type": "null" + } + ], + "description": "Whether the agent wants recent files context." + }, + "relatedSnippets": { + "anyOf": [ + { + "$ref": "#/$defs/NesRelatedSnippetsCapabilities" + }, + { + "type": "null" + } + ], + "description": "Whether the agent wants related snippets context." + }, + "userActions": { + "anyOf": [ + { + "$ref": "#/$defs/NesUserActionsCapabilities" + }, + { + "type": "null" + } + ], + "description": "Whether the agent wants user actions context." + } + }, + "type": "object" + }, + "NesDiagnostic": { + "description": "A diagnostic (error, warning, etc.).", + "properties": { + "message": { + "description": "The diagnostic message.", + "type": "string" + }, + "range": { + "allOf": [ + { + "$ref": "#/$defs/Range" + } + ], + "description": "The range of the diagnostic." + }, + "severity": { + "allOf": [ + { + "$ref": "#/$defs/NesDiagnosticSeverity" + } + ], + "description": "The severity of the diagnostic." + }, + "uri": { + "description": "The URI of the file containing the diagnostic.", + "type": "string" + } + }, + "required": ["uri", "range", "severity", "message"], + "type": "object" + }, + "NesDiagnosticSeverity": { + "description": "Severity of a diagnostic.", + "oneOf": [ + { + "const": "error", + "description": "An error.", + "type": "string" + }, + { + "const": "warning", + "description": "A warning.", + "type": "string" + }, + { + "const": "information", + "description": "An informational message.", + "type": "string" + }, + { + "const": "hint", + "description": "A hint.", + "type": "string" + } + ] + }, + "NesDiagnosticsCapabilities": { + "description": "Capabilities for diagnostics context.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + } + }, + "type": "object" + }, + "NesDocumentDidChangeCapabilities": { + "description": "Capabilities for `document/didChange` events.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + }, + "syncKind": { + "anyOf": [ + { + "$ref": "#/$defs/TextDocumentSyncKind" + }, + { + "type": "null" + } + ], + "description": "The sync kind the agent wants: `\"full\"` or `\"incremental\"`." + } + }, + "type": "object" + }, + "NesDocumentDidCloseCapabilities": { + "description": "Marker for `document/didClose` capability support.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + } + }, + "type": "object" + }, + "NesDocumentDidFocusCapabilities": { + "description": "Marker for `document/didFocus` capability support.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + } + }, + "type": "object" + }, + "NesDocumentDidOpenCapabilities": { + "description": "Marker for `document/didOpen` capability support.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + } + }, + "type": "object" + }, + "NesDocumentDidSaveCapabilities": { + "description": "Marker for `document/didSave` capability support.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + } + }, + "type": "object" + }, + "NesDocumentEventCapabilities": { + "description": "Document event capabilities the agent wants to receive.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + }, + "didChange": { + "anyOf": [ + { + "$ref": "#/$defs/NesDocumentDidChangeCapabilities" + }, + { + "type": "null" + } + ], + "description": "Whether the agent wants `document/didChange` events, and the sync kind." + }, + "didClose": { + "anyOf": [ + { + "$ref": "#/$defs/NesDocumentDidCloseCapabilities" + }, + { + "type": "null" + } + ], + "description": "Whether the agent wants `document/didClose` events." + }, + "didFocus": { + "anyOf": [ + { + "$ref": "#/$defs/NesDocumentDidFocusCapabilities" + }, + { + "type": "null" + } + ], + "description": "Whether the agent wants `document/didFocus` events." + }, + "didOpen": { + "anyOf": [ + { + "$ref": "#/$defs/NesDocumentDidOpenCapabilities" + }, + { + "type": "null" + } + ], + "description": "Whether the agent wants `document/didOpen` events." + }, + "didSave": { + "anyOf": [ + { + "$ref": "#/$defs/NesDocumentDidSaveCapabilities" + }, + { + "type": "null" + } + ], + "description": "Whether the agent wants `document/didSave` events." + } + }, + "type": "object" + }, + "NesEditHistoryCapabilities": { + "description": "Capabilities for edit history context.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + }, + "maxCount": { + "description": "Maximum number of edit history entries the agent can use.", + "format": "uint32", + "minimum": 0, + "type": ["integer", "null"] + } + }, + "type": "object" + }, + "NesEditHistoryEntry": { + "description": "An entry in the edit history.", + "properties": { + "diff": { + "description": "A diff representing the edit.", + "type": "string" + }, + "uri": { + "description": "The URI of the edited file.", + "type": "string" + } + }, + "required": ["uri", "diff"], + "type": "object" + }, + "NesEditSuggestion": { + "description": "A text edit suggestion.", + "properties": { + "cursorPosition": { + "anyOf": [ + { + "$ref": "#/$defs/Position" + }, + { + "type": "null" + } + ], + "description": "Optional suggested cursor position after applying edits." + }, + "edits": { + "description": "The text edits to apply.", + "items": { + "$ref": "#/$defs/NesTextEdit" + }, + "type": "array" + }, + "id": { + "description": "Unique identifier for accept/reject tracking.", + "type": "string" + }, + "uri": { + "description": "The URI of the file to edit.", + "type": "string" + } + }, + "required": ["id", "uri", "edits"], + "type": "object" + }, + "NesEventCapabilities": { + "description": "Event capabilities the agent can consume.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + }, + "document": { + "anyOf": [ + { + "$ref": "#/$defs/NesDocumentEventCapabilities" + }, + { + "type": "null" + } + ], + "description": "Document event capabilities." + } + }, + "type": "object" + }, + "NesExcerpt": { + "description": "A code excerpt from a file.", + "properties": { + "endLine": { + "description": "The end line of the excerpt (zero-based).", + "format": "uint32", + "minimum": 0, + "type": "integer" + }, + "startLine": { + "description": "The start line of the excerpt (zero-based).", + "format": "uint32", + "minimum": 0, + "type": "integer" + }, + "text": { + "description": "The text content of the excerpt.", + "type": "string" + } + }, + "required": ["startLine", "endLine", "text"], + "type": "object" + }, + "NesIdeActionsCapabilities": { + "description": "IDE actions the client can perform.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + }, + "rename": { + "anyOf": [ + { + "$ref": "#/$defs/NesRenameActionCapabilities" + }, + { + "type": "null" + } + ], + "description": "Whether the client supports the `rename` action." + }, + "searchAndReplace": { + "anyOf": [ + { + "$ref": "#/$defs/NesSearchAndReplaceActionCapabilities" + }, + { + "type": "null" + } + ], + "description": "Whether the client supports the `searchAndReplace` action." + } + }, + "type": "object" + }, + "NesJumpSuggestion": { + "description": "A jump-to-location suggestion.", + "properties": { + "id": { + "description": "Unique identifier for accept/reject tracking.", + "type": "string" + }, + "position": { + "allOf": [ + { + "$ref": "#/$defs/Position" + } + ], + "description": "The target position within the file." + }, + "uri": { + "description": "The file to navigate to.", + "type": "string" + } + }, + "required": ["id", "uri", "position"], + "type": "object" + }, + "NesOpenFile": { + "description": "An open file in the editor.", + "properties": { + "languageId": { + "description": "The language identifier.", + "type": "string" + }, + "lastFocusedMs": { + "description": "Timestamp in milliseconds since epoch of when the file was last focused.", + "format": "uint64", + "minimum": 0, + "type": ["integer", "null"] + }, + "uri": { + "description": "The URI of the file.", + "type": "string" + }, + "visibleRange": { + "anyOf": [ + { + "$ref": "#/$defs/Range" + }, + { + "type": "null" + } + ], + "description": "The visible range in the editor, if any." + } + }, + "required": ["uri", "languageId"], + "type": "object" + }, + "NesOpenFilesCapabilities": { + "description": "Capabilities for open files context.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + } + }, + "type": "object" + }, + "NesRecentFile": { + "description": "A recently accessed file.", + "properties": { + "languageId": { + "description": "The language identifier.", + "type": "string" + }, + "text": { + "description": "The full text content of the file.", + "type": "string" + }, + "uri": { + "description": "The URI of the file.", + "type": "string" + } + }, + "required": ["uri", "languageId", "text"], + "type": "object" + }, + "NesRecentFilesCapabilities": { + "description": "Capabilities for recent files context.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + }, + "maxCount": { + "description": "Maximum number of recent files the agent can use.", + "format": "uint32", + "minimum": 0, + "type": ["integer", "null"] + } + }, + "type": "object" + }, + "NesRejectNotification": { + "description": "Notification sent when a suggestion is rejected.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + }, + "id": { + "description": "The ID of the rejected suggestion.", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "$ref": "#/$defs/NesRejectReason" + }, + { + "type": "null" + } + ], + "description": "The reason for rejection." + } + }, + "required": ["id"], + "type": "object", + "x-method": "nes/reject", + "x-side": "agent" + }, + "NesRejectReason": { + "description": "The reason a suggestion was rejected.", + "oneOf": [ + { + "const": "rejected", + "description": "The user explicitly dismissed the suggestion.", + "type": "string" + }, + { + "const": "ignored", + "description": "The suggestion was shown but the user continued editing without interacting.", + "type": "string" + }, + { + "const": "replaced", + "description": "The suggestion was superseded by a newer suggestion.", + "type": "string" + }, + { + "const": "cancelled", + "description": "The request was cancelled before the agent returned a response.", + "type": "string" + } + ] + }, + "NesRelatedSnippet": { + "description": "A related code snippet from a file.", + "properties": { + "excerpts": { + "description": "The code excerpts.", + "items": { + "$ref": "#/$defs/NesExcerpt" + }, + "type": "array" + }, + "uri": { + "description": "The URI of the file containing the snippets.", + "type": "string" + } + }, + "required": ["uri", "excerpts"], + "type": "object" + }, + "NesRelatedSnippetsCapabilities": { + "description": "Capabilities for related snippets context.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + } + }, + "type": "object" + }, + "NesRenameActionCapabilities": { + "description": "Marker for rename action support.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + } + }, + "type": "object" + }, + "NesRepository": { + "description": "Repository metadata for an NES session.", + "properties": { + "name": { + "description": "The repository name.", + "type": "string" + }, + "owner": { + "description": "The repository owner.", + "type": "string" + }, + "remoteUrl": { + "description": "The remote URL of the repository.", + "type": "string" + } + }, + "required": ["name", "owner", "remoteUrl"], + "type": "object" + }, + "NesSearchAndReplaceActionCapabilities": { + "description": "Marker for search and replace action support.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + } + }, + "type": "object" + }, + "NesSessionId": { + "description": "A unique identifier for an NES session, distinct from chat `SessionId`.", + "type": "string" + }, + "NesStartRequest": { + "description": "Request to start an NES session.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + }, + "repository": { + "anyOf": [ + { + "$ref": "#/$defs/NesRepository" + }, + { + "type": "null" + } + ], + "description": "Repository metadata, if the workspace is a git repository." + }, + "workspaceFolders": { + "description": "The workspace folders.", + "items": { + "$ref": "#/$defs/WorkspaceFolder" + }, + "type": ["array", "null"] + }, + "workspaceUri": { + "description": "The root URI of the workspace.", + "type": ["string", "null"] + } + }, + "type": "object", + "x-method": "nes/start", + "x-side": "agent" + }, + "NesStartResponse": { + "description": "Response to `nes/start`.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + }, + "sessionId": { + "allOf": [ + { + "$ref": "#/$defs/NesSessionId" + } + ], + "description": "The session ID for the newly started NES session." + } + }, + "required": ["sessionId"], + "type": "object", + "x-method": "nes/start", + "x-side": "agent" + }, + "NesSuggestContext": { + "description": "Context attached to a suggestion request.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + }, + "diagnostics": { + "description": "Current diagnostics (errors, warnings).", + "items": { + "$ref": "#/$defs/NesDiagnostic" + }, + "type": ["array", "null"] + }, + "editHistory": { + "description": "Recent edit history.", + "items": { + "$ref": "#/$defs/NesEditHistoryEntry" + }, + "type": ["array", "null"] + }, + "openFiles": { + "description": "Currently open files in the editor.", + "items": { + "$ref": "#/$defs/NesOpenFile" + }, + "type": ["array", "null"] + }, + "recentFiles": { + "description": "Recently accessed files.", + "items": { + "$ref": "#/$defs/NesRecentFile" + }, + "type": ["array", "null"] + }, + "relatedSnippets": { + "description": "Related code snippets.", + "items": { + "$ref": "#/$defs/NesRelatedSnippet" + }, + "type": ["array", "null"] + }, + "userActions": { + "description": "Recent user actions (typing, navigation, etc.).", + "items": { + "$ref": "#/$defs/NesUserAction" + }, + "type": ["array", "null"] + } + }, + "type": "object" + }, + "NesSuggestRequest": { + "description": "Request for a code suggestion.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + }, + "context": { + "anyOf": [ + { + "$ref": "#/$defs/NesSuggestContext" + }, + { + "type": "null" + } + ], + "description": "Context for the suggestion, included based on agent capabilities." + }, + "position": { + "allOf": [ + { + "$ref": "#/$defs/Position" + } + ], + "description": "The current cursor position." + }, + "selection": { + "anyOf": [ + { + "$ref": "#/$defs/Range" + }, + { + "type": "null" + } + ], + "description": "The current text selection range, if any." + }, + "triggerKind": { + "allOf": [ + { + "$ref": "#/$defs/NesTriggerKind" + } + ], + "description": "What triggered this suggestion request." + }, + "uri": { + "description": "The URI of the document to suggest for.", + "type": "string" + }, + "version": { + "description": "The version number of the document.", + "format": "int64", + "type": "integer" + } + }, + "required": ["uri", "version", "position", "triggerKind"], + "type": "object", + "x-method": "nes/suggest", + "x-side": "agent" + }, + "NesSuggestResponse": { + "description": "Response to `nes/suggest`.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + }, + "suggestions": { + "description": "The list of suggestions.", + "items": { + "$ref": "#/$defs/NesSuggestion" + }, + "type": "array" + } + }, + "required": ["suggestions"], + "type": "object", + "x-method": "nes/suggest", + "x-side": "agent" + }, + "NesSuggestion": { + "description": "A suggestion returned by the agent.", + "oneOf": [ + { + "allOf": [ + { + "$ref": "#/$defs/NesEditSuggestion" + } + ], + "description": "A text edit suggestion.", + "properties": { + "kind": { + "const": "edit", + "type": "string" + } + }, + "required": ["kind"], + "type": "object" + }, + { + "allOf": [ + { + "$ref": "#/$defs/NesJumpSuggestion" + } + ], + "description": "A jump-to-location suggestion.", + "properties": { + "kind": { + "const": "jump", + "type": "string" + } + }, + "required": ["kind"], + "type": "object" + }, + { + "allOf": [ + { + "$ref": "#/$defs/NesActionSuggestion" + } + ], + "description": "An IDE action suggestion.", + "properties": { + "kind": { + "const": "action", + "type": "string" + } + }, + "required": ["kind"], + "type": "object" + } + ] + }, + "NesTextEdit": { + "description": "A text edit within a suggestion.", + "properties": { + "newText": { + "description": "The replacement text.", + "type": "string" + }, + "range": { + "allOf": [ + { + "$ref": "#/$defs/Range" + } + ], + "description": "The range to replace." + } + }, + "required": ["range", "newText"], + "type": "object" + }, + "NesTriggerKind": { + "description": "What triggered the suggestion request.", + "oneOf": [ + { + "const": "automatic", + "description": "Triggered by user typing or cursor movement.", + "type": "string" + }, + { + "const": "diagnostic", + "description": "Triggered by a diagnostic appearing at or near the cursor.", + "type": "string" + }, + { + "const": "manual", + "description": "Triggered by an explicit user action (keyboard shortcut).", + "type": "string" + } + ] + }, + "NesUserAction": { + "description": "A user action (typing, cursor movement, etc.).", + "properties": { + "action": { + "description": "The kind of action (e.g., \"insertChar\", \"cursorMovement\").", + "type": "string" + }, + "line": { + "description": "The line number where the action occurred.", + "format": "uint32", + "minimum": 0, + "type": "integer" + }, + "offset": { + "description": "The character offset where the action occurred.", + "format": "uint32", + "minimum": 0, + "type": "integer" + }, + "timestampMs": { + "description": "Timestamp in milliseconds since epoch.", + "format": "uint64", + "minimum": 0, + "type": "integer" + }, + "uri": { + "description": "The URI of the file where the action occurred.", + "type": "string" + } + }, + "required": ["action", "uri", "line", "offset", "timestampMs"], + "type": "object" + }, + "NesUserActionsCapabilities": { + "description": "Capabilities for user actions context.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP.", + "type": ["object", "null"] + }, + "maxCount": { + "description": "Maximum number of user actions the agent can use.", + "format": "uint32", + "minimum": 0, + "type": ["integer", "null"] + } + }, + "type": "object" + }, + "NewSessionRequest": { + "description": "Request parameters for creating a new session.\n\nSee protocol docs: [Creating a Session](https://agentclientprotocol.com/protocol/session-setup#creating-a-session)", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", + "type": ["object", "null"] + }, + "cwd": { + "description": "The working directory for this session. Must be an absolute path.", + "type": "string" + }, + "mcpServers": { + "description": "List of MCP (Model Context Protocol) servers the agent should connect to.", + "items": { + "$ref": "#/$defs/McpServer" + }, + "type": "array" + } + }, + "required": ["cwd", "mcpServers"], + "type": "object", + "x-method": "session/new", + "x-side": "agent" + }, + "NewSessionResponse": { + "description": "Response from creating a new session.\n\nSee protocol docs: [Creating a Session](https://agentclientprotocol.com/protocol/session-setup#creating-a-session)", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", + "type": ["object", "null"] + }, + "configOptions": { + "description": "Initial session configuration options if supported by the Agent.", + "items": { + "$ref": "#/$defs/SessionConfigOption" + }, + "type": ["array", "null"] + }, + "models": { + "anyOf": [ + { + "$ref": "#/$defs/SessionModelState" + }, + { + "type": "null" + } + ], + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nInitial model state if supported by the Agent" + }, + "modes": { "anyOf": [ { "$ref": "#/$defs/SessionModeState" @@ -3126,6 +4495,45 @@ } ] }, + "Position": { + "description": "A zero-based position in a text document.\n\nThe meaning of `character` depends on the negotiated position encoding.", + "properties": { + "character": { + "description": "Zero-based character offset (encoding-dependent).", + "format": "uint32", + "minimum": 0, + "type": "integer" + }, + "line": { + "description": "Zero-based line number.", + "format": "uint32", + "minimum": 0, + "type": "integer" + } + }, + "required": ["line", "character"], + "type": "object" + }, + "PositionEncodingKind": { + "description": "The encoding used for character offsets in positions.\n\nFollows the same conventions as LSP 3.17. The default is UTF-16.", + "oneOf": [ + { + "const": "utf-16", + "description": "Character offsets count UTF-16 code units. This is the default.", + "type": "string" + }, + { + "const": "utf-32", + "description": "Character offsets count Unicode code points.", + "type": "string" + }, + { + "const": "utf-8", + "description": "Character offsets count UTF-8 code units (bytes).", + "type": "string" + } + ] + }, "PromptCapabilities": { "description": "Prompt capabilities supported by the agent in `session/prompt` requests.\n\nBaseline agent functionality requires support for [`ContentBlock::Text`]\nand [`ContentBlock::ResourceLink`] in prompt requests.\n\nOther variants must be explicitly opted in to.\nCapabilities for different types of content in prompt requests.\n\nIndicates which content types beyond the baseline (text and resource links)\nthe agent can process.\n\nSee protocol docs: [Prompt Capabilities](https://agentclientprotocol.com/protocol/initialization#prompt-capabilities)", "properties": { @@ -3229,6 +4637,29 @@ "minimum": 0, "type": "integer" }, + "Range": { + "description": "A range in a text document, expressed as start and end positions.", + "properties": { + "end": { + "allOf": [ + { + "$ref": "#/$defs/Position" + } + ], + "description": "The end position (exclusive)." + }, + "start": { + "allOf": [ + { + "$ref": "#/$defs/Position" + } + ], + "description": "The start position (inclusive)." + } + }, + "required": ["start", "end"], + "type": "object" + }, "ReadTextFileRequest": { "description": "Request to read content from a text file.\n\nOnly available if the client supports the `fs.readTextFile` capability.", "properties": { @@ -4654,6 +6085,43 @@ "required": ["text"], "type": "object" }, + "TextDocumentContentChangeEvent": { + "description": "A content change event for a document.\n\nWhen `range` is `None`, `text` is the full content of the document.\nWhen `range` is `Some`, `text` replaces the given range.", + "properties": { + "range": { + "anyOf": [ + { + "$ref": "#/$defs/Range" + }, + { + "type": "null" + } + ], + "description": "The range of the document that changed. If `None`, the entire content is replaced." + }, + "text": { + "description": "The new text for the range, or the full document content if `range` is `None`.", + "type": "string" + } + }, + "required": ["text"], + "type": "object" + }, + "TextDocumentSyncKind": { + "description": "How the agent wants document changes delivered.", + "oneOf": [ + { + "const": "full", + "description": "Client sends the entire file content on each change.", + "type": "string" + }, + { + "const": "incremental", + "description": "Client sends only the changed ranges.", + "type": "string" + } + ] + }, "TextResourceContents": { "description": "Text-based resource contents.", "properties": { @@ -5142,6 +6610,21 @@ "x-method": "terminal/wait_for_exit", "x-side": "client" }, + "WorkspaceFolder": { + "description": "A workspace folder.", + "properties": { + "name": { + "description": "The display name of the folder.", + "type": "string" + }, + "uri": { + "description": "The URI of the folder.", + "type": "string" + } + }, + "required": ["uri", "name"], + "type": "object" + }, "WriteTextFileRequest": { "description": "Request to write content to a text file.\n\nOnly available if the client supports the `fs.writeTextFile` capability.", "properties": { diff --git a/src/agent.rs b/src/agent.rs index b8ce81de..acee2bd6 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -17,6 +17,14 @@ use crate::{ ProtocolVersion, SessionId, }; +#[cfg(feature = "unstable_nes")] +use crate::{ + DocumentDidChangeNotification, DocumentDidCloseNotification, DocumentDidFocusNotification, + DocumentDidOpenNotification, DocumentDidSaveNotification, NesAcceptNotification, + NesCapabilities, NesRejectNotification, NesStartRequest, NesStartResponse, NesSuggestRequest, + NesSuggestResponse, PositionEncodingKind, +}; + // Initialize /// Request parameters for the initialize method. @@ -3243,6 +3251,22 @@ pub struct AgentCapabilities { #[cfg(feature = "unstable_logout")] #[serde(default)] pub auth: AgentAuthCapabilities, + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// NES (Next Edit Suggestions) capabilities supported by the agent. + #[cfg(feature = "unstable_nes")] + #[serde(skip_serializing_if = "Option::is_none")] + pub nes: Option, + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// The position encoding selected by the agent from the client's supported encodings. + #[cfg(feature = "unstable_nes")] + #[serde(skip_serializing_if = "Option::is_none")] + pub position_encoding: Option, /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at /// these keys. @@ -3298,6 +3322,29 @@ impl AgentCapabilities { self } + /// **UNSTABLE** + /// + /// NES (Next Edit Suggestions) capabilities supported by the agent. + #[cfg(feature = "unstable_nes")] + #[must_use] + pub fn nes(mut self, nes: impl IntoOption) -> Self { + self.nes = nes.into_option(); + self + } + + /// **UNSTABLE** + /// + /// The position encoding selected by the agent. + #[cfg(feature = "unstable_nes")] + #[must_use] + pub fn position_encoding( + mut self, + position_encoding: impl IntoOption, + ) -> Self { + self.position_encoding = position_encoding.into_option(); + self + } + /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at /// these keys. @@ -3728,6 +3775,33 @@ pub struct AgentMethodNames { /// Method for logging out of an authenticated session. #[cfg(feature = "unstable_logout")] pub logout: &'static str, + /// Method for starting an NES session. + #[cfg(feature = "unstable_nes")] + pub nes_start: &'static str, + /// Method for requesting a suggestion. + #[cfg(feature = "unstable_nes")] + pub nes_suggest: &'static str, + /// Notification for accepting a suggestion. + #[cfg(feature = "unstable_nes")] + pub nes_accept: &'static str, + /// Notification for rejecting a suggestion. + #[cfg(feature = "unstable_nes")] + pub nes_reject: &'static str, + /// Notification for document open events. + #[cfg(feature = "unstable_nes")] + pub document_did_open: &'static str, + /// Notification for document change events. + #[cfg(feature = "unstable_nes")] + pub document_did_change: &'static str, + /// Notification for document close events. + #[cfg(feature = "unstable_nes")] + pub document_did_close: &'static str, + /// Notification for document save events. + #[cfg(feature = "unstable_nes")] + pub document_did_save: &'static str, + /// Notification for document focus events. + #[cfg(feature = "unstable_nes")] + pub document_did_focus: &'static str, } /// Constant containing all agent method names. @@ -3751,6 +3825,31 @@ pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames { session_close: SESSION_CLOSE_METHOD_NAME, #[cfg(feature = "unstable_logout")] logout: LOGOUT_METHOD_NAME, + #[cfg(feature = "unstable_nes")] + nes_start: NES_START_METHOD_NAME, + #[cfg(feature = "unstable_nes")] + nes_suggest: NES_SUGGEST_METHOD_NAME, + #[cfg(feature = "unstable_nes")] + nes_accept: NES_ACCEPT_METHOD_NAME, + #[cfg(feature = "unstable_nes")] + nes_reject: NES_REJECT_METHOD_NAME, + #[cfg(feature = "unstable_nes")] + document_did_open: DOCUMENT_DID_OPEN_METHOD_NAME, + #[cfg(feature = "unstable_nes")] + document_did_change: DOCUMENT_DID_CHANGE_METHOD_NAME, + #[cfg(feature = "unstable_nes")] + document_did_close: DOCUMENT_DID_CLOSE_METHOD_NAME, + #[cfg(feature = "unstable_nes")] + document_did_save: DOCUMENT_DID_SAVE_METHOD_NAME, + #[cfg(feature = "unstable_nes")] + document_did_focus: DOCUMENT_DID_FOCUS_METHOD_NAME, +}; + +#[cfg(feature = "unstable_nes")] +use crate::nes::{ + DOCUMENT_DID_CHANGE_METHOD_NAME, DOCUMENT_DID_CLOSE_METHOD_NAME, + DOCUMENT_DID_FOCUS_METHOD_NAME, DOCUMENT_DID_OPEN_METHOD_NAME, DOCUMENT_DID_SAVE_METHOD_NAME, + NES_ACCEPT_METHOD_NAME, NES_REJECT_METHOD_NAME, NES_START_METHOD_NAME, NES_SUGGEST_METHOD_NAME, }; /// Method name for the initialize request. @@ -3932,6 +4031,20 @@ pub enum ClientRequest { /// /// Select a model for a given session. SetSessionModelRequest(SetSessionModelRequest), + #[cfg(feature = "unstable_nes")] + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// Starts an NES session. + NesStartRequest(NesStartRequest), + #[cfg(feature = "unstable_nes")] + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// Requests a code suggestion. + NesSuggestRequest(NesSuggestRequest), /// Handles extension method requests from the client. /// /// Extension methods provide a way to add custom functionality while maintaining @@ -3964,6 +4077,10 @@ impl ClientRequest { Self::PromptRequest(_) => AGENT_METHOD_NAMES.session_prompt, #[cfg(feature = "unstable_session_model")] Self::SetSessionModelRequest(_) => AGENT_METHOD_NAMES.session_set_model, + #[cfg(feature = "unstable_nes")] + Self::NesStartRequest(_) => AGENT_METHOD_NAMES.nes_start, + #[cfg(feature = "unstable_nes")] + Self::NesSuggestRequest(_) => AGENT_METHOD_NAMES.nes_suggest, Self::ExtMethodRequest(ext_request) => &ext_request.method, } } @@ -3999,6 +4116,10 @@ pub enum AgentResponse { PromptResponse(PromptResponse), #[cfg(feature = "unstable_session_model")] SetSessionModelResponse(#[serde(default)] SetSessionModelResponse), + #[cfg(feature = "unstable_nes")] + NesStartResponse(NesStartResponse), + #[cfg(feature = "unstable_nes")] + NesSuggestResponse(NesSuggestResponse), ExtMethodResponse(ExtResponse), } @@ -4025,6 +4146,41 @@ pub enum ClientNotification { /// /// See protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/prompt-turn#cancellation) CancelNotification(CancelNotification), + #[cfg(feature = "unstable_nes")] + /// **UNSTABLE** + /// + /// Notification sent when a file is opened in the editor. + DocumentDidOpenNotification(DocumentDidOpenNotification), + #[cfg(feature = "unstable_nes")] + /// **UNSTABLE** + /// + /// Notification sent when a file is edited. + DocumentDidChangeNotification(DocumentDidChangeNotification), + #[cfg(feature = "unstable_nes")] + /// **UNSTABLE** + /// + /// Notification sent when a file is closed. + DocumentDidCloseNotification(DocumentDidCloseNotification), + #[cfg(feature = "unstable_nes")] + /// **UNSTABLE** + /// + /// Notification sent when a file is saved. + DocumentDidSaveNotification(DocumentDidSaveNotification), + #[cfg(feature = "unstable_nes")] + /// **UNSTABLE** + /// + /// Notification sent when a file becomes the active editor tab. + DocumentDidFocusNotification(DocumentDidFocusNotification), + #[cfg(feature = "unstable_nes")] + /// **UNSTABLE** + /// + /// Notification sent when a suggestion is accepted. + NesAcceptNotification(NesAcceptNotification), + #[cfg(feature = "unstable_nes")] + /// **UNSTABLE** + /// + /// Notification sent when a suggestion is rejected. + NesRejectNotification(NesRejectNotification), /// Handles extension notifications from the client. /// /// Extension notifications provide a way to send one-way messages for custom functionality @@ -4040,6 +4196,20 @@ impl ClientNotification { pub fn method(&self) -> &str { match self { Self::CancelNotification(_) => AGENT_METHOD_NAMES.session_cancel, + #[cfg(feature = "unstable_nes")] + Self::DocumentDidOpenNotification(_) => AGENT_METHOD_NAMES.document_did_open, + #[cfg(feature = "unstable_nes")] + Self::DocumentDidChangeNotification(_) => AGENT_METHOD_NAMES.document_did_change, + #[cfg(feature = "unstable_nes")] + Self::DocumentDidCloseNotification(_) => AGENT_METHOD_NAMES.document_did_close, + #[cfg(feature = "unstable_nes")] + Self::DocumentDidSaveNotification(_) => AGENT_METHOD_NAMES.document_did_save, + #[cfg(feature = "unstable_nes")] + Self::DocumentDidFocusNotification(_) => AGENT_METHOD_NAMES.document_did_focus, + #[cfg(feature = "unstable_nes")] + Self::NesAcceptNotification(_) => AGENT_METHOD_NAMES.nes_accept, + #[cfg(feature = "unstable_nes")] + Self::NesRejectNotification(_) => AGENT_METHOD_NAMES.nes_reject, Self::ExtNotification(ext_notification) => &ext_notification.method, } } diff --git a/src/bin/generate.rs b/src/bin/generate.rs index 3051d30f..ddfdc034 100644 --- a/src/bin/generate.rs +++ b/src/bin/generate.rs @@ -1032,6 +1032,15 @@ starting with '$/' it is free to ignore the notification." "session/set_model" => self.agent.get("SetSessionModelRequest").unwrap(), "session/close" => self.agent.get("CloseSessionRequest").unwrap(), "logout" => self.agent.get("LogoutRequest").unwrap(), + "nes/start" => self.agent.get("NesStartRequest").unwrap(), + "nes/suggest" => self.agent.get("NesSuggestRequest").unwrap(), + "nes/accept" => self.agent.get("NesAcceptNotification").unwrap(), + "nes/reject" => self.agent.get("NesRejectNotification").unwrap(), + "document/didOpen" => self.agent.get("DocumentDidOpenNotification").unwrap(), + "document/didChange" => self.agent.get("DocumentDidChangeNotification").unwrap(), + "document/didClose" => self.agent.get("DocumentDidCloseNotification").unwrap(), + "document/didSave" => self.agent.get("DocumentDidSaveNotification").unwrap(), + "document/didFocus" => self.agent.get("DocumentDidFocusNotification").unwrap(), _ => panic!("Introduced a method? Add it here :)"), } } diff --git a/src/client.rs b/src/client.rs index ef81f671..f960c369 100644 --- a/src/client.rs +++ b/src/client.rs @@ -20,6 +20,9 @@ use crate::{ }; use crate::{IntoMaybeUndefined, MaybeUndefined}; +#[cfg(feature = "unstable_nes")] +use crate::{ClientNesCapabilities, GeneralCapabilities}; + // Session updates /// Notification containing a session update from the agent. @@ -1500,6 +1503,22 @@ pub struct ClientCapabilities { #[cfg(feature = "unstable_elicitation")] #[serde(default, skip_serializing_if = "Option::is_none")] pub elicitation: Option, + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// NES (Next Edit Suggestions) capabilities supported by the client. + #[cfg(feature = "unstable_nes")] + #[serde(skip_serializing_if = "Option::is_none")] + pub nes: Option, + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// General client capabilities (e.g. position encodings). + #[cfg(feature = "unstable_nes")] + #[serde(skip_serializing_if = "Option::is_none")] + pub general: Option, /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at /// these keys. @@ -1557,6 +1576,26 @@ impl ClientCapabilities { self } + /// **UNSTABLE** + /// + /// NES (Next Edit Suggestions) capabilities supported by the client. + #[cfg(feature = "unstable_nes")] + #[must_use] + pub fn nes(mut self, nes: impl IntoOption) -> Self { + self.nes = nes.into_option(); + self + } + + /// **UNSTABLE** + /// + /// General client capabilities (e.g. position encodings). + #[cfg(feature = "unstable_nes")] + #[must_use] + pub fn general(mut self, general: impl IntoOption) -> Self { + self.general = general.into_option(); + self + } + /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at /// these keys. diff --git a/src/lib.rs b/src/lib.rs index f6cdb638..1be0f6c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,6 +58,8 @@ mod elicitation; mod error; mod ext; mod maybe_undefined; +#[cfg(feature = "unstable_nes")] +mod nes; mod plan; #[cfg(feature = "unstable_cancel_request")] mod protocol_level; @@ -74,6 +76,8 @@ pub use elicitation::*; pub use error::*; pub use ext::*; pub use maybe_undefined::*; +#[cfg(feature = "unstable_nes")] +pub use nes::*; pub use plan::*; #[cfg(feature = "unstable_cancel_request")] pub use protocol_level::*; diff --git a/src/nes.rs b/src/nes.rs new file mode 100644 index 00000000..645c323c --- /dev/null +++ b/src/nes.rs @@ -0,0 +1,2172 @@ +//! Next Edit Suggestions (NES) types and constants. +//! +//! NES allows agents to provide predictive code edits via capability negotiation, +//! document events, and a suggestion request/response flow. NES sessions are +//! independent of chat sessions and have their own lifecycle. + +use std::sync::Arc; + +use derive_more::{Display, From}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use crate::{IntoOption, Meta}; + +// Method name constants + +/// Method name for starting an NES session. +pub(crate) const NES_START_METHOD_NAME: &str = "nes/start"; +/// Method name for requesting a suggestion. +pub(crate) const NES_SUGGEST_METHOD_NAME: &str = "nes/suggest"; +/// Method name for accepting a suggestion. +pub(crate) const NES_ACCEPT_METHOD_NAME: &str = "nes/accept"; +/// Method name for rejecting a suggestion. +pub(crate) const NES_REJECT_METHOD_NAME: &str = "nes/reject"; +/// Notification name for document open events. +pub(crate) const DOCUMENT_DID_OPEN_METHOD_NAME: &str = "document/didOpen"; +/// Notification name for document change events. +pub(crate) const DOCUMENT_DID_CHANGE_METHOD_NAME: &str = "document/didChange"; +/// Notification name for document close events. +pub(crate) const DOCUMENT_DID_CLOSE_METHOD_NAME: &str = "document/didClose"; +/// Notification name for document save events. +pub(crate) const DOCUMENT_DID_SAVE_METHOD_NAME: &str = "document/didSave"; +/// Notification name for document focus events. +pub(crate) const DOCUMENT_DID_FOCUS_METHOD_NAME: &str = "document/didFocus"; + +/// Names of all NES-related methods. +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[non_exhaustive] +pub struct NesMethodNames { + /// Method for starting an NES session. + pub nes_start: &'static str, + /// Method for requesting a suggestion. + pub nes_suggest: &'static str, + /// Notification for accepting a suggestion. + pub nes_accept: &'static str, + /// Notification for rejecting a suggestion. + pub nes_reject: &'static str, + /// Notification for document open events. + pub document_did_open: &'static str, + /// Notification for document change events. + pub document_did_change: &'static str, + /// Notification for document close events. + pub document_did_close: &'static str, + /// Notification for document save events. + pub document_did_save: &'static str, + /// Notification for document focus events. + pub document_did_focus: &'static str, +} + +/// Constant containing all NES method names. +pub const NES_METHOD_NAMES: NesMethodNames = NesMethodNames { + nes_start: NES_START_METHOD_NAME, + nes_suggest: NES_SUGGEST_METHOD_NAME, + nes_accept: NES_ACCEPT_METHOD_NAME, + nes_reject: NES_REJECT_METHOD_NAME, + document_did_open: DOCUMENT_DID_OPEN_METHOD_NAME, + document_did_change: DOCUMENT_DID_CHANGE_METHOD_NAME, + document_did_close: DOCUMENT_DID_CLOSE_METHOD_NAME, + document_did_save: DOCUMENT_DID_SAVE_METHOD_NAME, + document_did_focus: DOCUMENT_DID_FOCUS_METHOD_NAME, +}; + +// Position primitives + +/// The encoding used for character offsets in positions. +/// +/// Follows the same conventions as LSP 3.17. The default is UTF-16. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[non_exhaustive] +pub enum PositionEncodingKind { + /// Character offsets count UTF-16 code units. This is the default. + #[serde(rename = "utf-16")] + Utf16, + /// Character offsets count Unicode code points. + #[serde(rename = "utf-32")] + Utf32, + /// Character offsets count UTF-8 code units (bytes). + #[serde(rename = "utf-8")] + Utf8, +} + +/// A zero-based position in a text document. +/// +/// The meaning of `character` depends on the negotiated position encoding. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct Position { + /// Zero-based line number. + pub line: u32, + /// Zero-based character offset (encoding-dependent). + pub character: u32, +} + +impl Position { + #[must_use] + pub fn new(line: u32, character: u32) -> Self { + Self { line, character } + } +} + +/// A range in a text document, expressed as start and end positions. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct Range { + /// The start position (inclusive). + pub start: Position, + /// The end position (exclusive). + pub end: Position, +} + +impl Range { + #[must_use] + pub fn new(start: Position, end: Position) -> Self { + Self { start, end } + } +} + +// Agent NES capabilities + +/// NES capabilities advertised by the agent during initialization. +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesCapabilities { + /// Events the agent wants to receive. + #[serde(skip_serializing_if = "Option::is_none")] + pub events: Option, + /// Context the agent wants attached to each suggestion request. + #[serde(skip_serializing_if = "Option::is_none")] + pub context: Option, + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +impl NesCapabilities { + #[must_use] + pub fn new() -> Self { + Self::default() + } + + #[must_use] + pub fn events(mut self, events: impl IntoOption) -> Self { + self.events = events.into_option(); + self + } + + #[must_use] + pub fn context(mut self, context: impl IntoOption) -> Self { + self.context = context.into_option(); + self + } + + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + +/// Event capabilities the agent can consume. +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesEventCapabilities { + /// Document event capabilities. + #[serde(skip_serializing_if = "Option::is_none")] + pub document: Option, + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +impl NesEventCapabilities { + #[must_use] + pub fn new() -> Self { + Self::default() + } + + #[must_use] + pub fn document(mut self, document: impl IntoOption) -> Self { + self.document = document.into_option(); + self + } + + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + +/// Document event capabilities the agent wants to receive. +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesDocumentEventCapabilities { + /// Whether the agent wants `document/didOpen` events. + #[serde(skip_serializing_if = "Option::is_none")] + pub did_open: Option, + /// Whether the agent wants `document/didChange` events, and the sync kind. + #[serde(skip_serializing_if = "Option::is_none")] + pub did_change: Option, + /// Whether the agent wants `document/didClose` events. + #[serde(skip_serializing_if = "Option::is_none")] + pub did_close: Option, + /// Whether the agent wants `document/didSave` events. + #[serde(skip_serializing_if = "Option::is_none")] + pub did_save: Option, + /// Whether the agent wants `document/didFocus` events. + #[serde(skip_serializing_if = "Option::is_none")] + pub did_focus: Option, + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +impl NesDocumentEventCapabilities { + #[must_use] + pub fn new() -> Self { + Self::default() + } + + #[must_use] + pub fn did_open(mut self, did_open: impl IntoOption) -> Self { + self.did_open = did_open.into_option(); + self + } + + #[must_use] + pub fn did_change( + mut self, + did_change: impl IntoOption, + ) -> Self { + self.did_change = did_change.into_option(); + self + } + + #[must_use] + pub fn did_close( + mut self, + did_close: impl IntoOption, + ) -> Self { + self.did_close = did_close.into_option(); + self + } + + #[must_use] + pub fn did_save(mut self, did_save: impl IntoOption) -> Self { + self.did_save = did_save.into_option(); + self + } + + #[must_use] + pub fn did_focus( + mut self, + did_focus: impl IntoOption, + ) -> Self { + self.did_focus = did_focus.into_option(); + self + } + + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + +/// Marker for `document/didOpen` capability support. +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesDocumentDidOpenCapabilities { + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +/// Capabilities for `document/didChange` events. +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesDocumentDidChangeCapabilities { + /// The sync kind the agent wants: `"full"` or `"incremental"`. + #[serde(skip_serializing_if = "Option::is_none")] + pub sync_kind: Option, + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +impl NesDocumentDidChangeCapabilities { + #[must_use] + pub fn new() -> Self { + Self::default() + } + + #[must_use] + pub fn sync_kind(mut self, sync_kind: impl IntoOption) -> Self { + self.sync_kind = sync_kind.into_option(); + self + } + + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + +/// How the agent wants document changes delivered. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[non_exhaustive] +pub enum TextDocumentSyncKind { + /// Client sends the entire file content on each change. + #[serde(rename = "full")] + Full, + /// Client sends only the changed ranges. + #[serde(rename = "incremental")] + Incremental, +} + +/// Marker for `document/didClose` capability support. +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesDocumentDidCloseCapabilities { + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +/// Marker for `document/didSave` capability support. +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesDocumentDidSaveCapabilities { + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +/// Marker for `document/didFocus` capability support. +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesDocumentDidFocusCapabilities { + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +/// Context capabilities the agent wants attached to each suggestion request. +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesContextCapabilities { + /// Whether the agent wants recent files context. + #[serde(skip_serializing_if = "Option::is_none")] + pub recent_files: Option, + /// Whether the agent wants related snippets context. + #[serde(skip_serializing_if = "Option::is_none")] + pub related_snippets: Option, + /// Whether the agent wants edit history context. + #[serde(skip_serializing_if = "Option::is_none")] + pub edit_history: Option, + /// Whether the agent wants user actions context. + #[serde(skip_serializing_if = "Option::is_none")] + pub user_actions: Option, + /// Whether the agent wants open files context. + #[serde(skip_serializing_if = "Option::is_none")] + pub open_files: Option, + /// Whether the agent wants diagnostics context. + #[serde(skip_serializing_if = "Option::is_none")] + pub diagnostics: Option, + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +impl NesContextCapabilities { + #[must_use] + pub fn new() -> Self { + Self::default() + } + + #[must_use] + pub fn recent_files( + mut self, + recent_files: impl IntoOption, + ) -> Self { + self.recent_files = recent_files.into_option(); + self + } + + #[must_use] + pub fn related_snippets( + mut self, + related_snippets: impl IntoOption, + ) -> Self { + self.related_snippets = related_snippets.into_option(); + self + } + + #[must_use] + pub fn edit_history( + mut self, + edit_history: impl IntoOption, + ) -> Self { + self.edit_history = edit_history.into_option(); + self + } + + #[must_use] + pub fn user_actions( + mut self, + user_actions: impl IntoOption, + ) -> Self { + self.user_actions = user_actions.into_option(); + self + } + + #[must_use] + pub fn open_files(mut self, open_files: impl IntoOption) -> Self { + self.open_files = open_files.into_option(); + self + } + + #[must_use] + pub fn diagnostics(mut self, diagnostics: impl IntoOption) -> Self { + self.diagnostics = diagnostics.into_option(); + self + } + + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + +/// Capabilities for recent files context. +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesRecentFilesCapabilities { + /// Maximum number of recent files the agent can use. + #[serde(skip_serializing_if = "Option::is_none")] + pub max_count: Option, + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +/// Capabilities for related snippets context. +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesRelatedSnippetsCapabilities { + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +/// Capabilities for edit history context. +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesEditHistoryCapabilities { + /// Maximum number of edit history entries the agent can use. + #[serde(skip_serializing_if = "Option::is_none")] + pub max_count: Option, + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +/// Capabilities for user actions context. +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesUserActionsCapabilities { + /// Maximum number of user actions the agent can use. + #[serde(skip_serializing_if = "Option::is_none")] + pub max_count: Option, + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +/// Capabilities for open files context. +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesOpenFilesCapabilities { + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +/// Capabilities for diagnostics context. +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesDiagnosticsCapabilities { + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +// Client NES capabilities + +/// NES capabilities advertised by the client during initialization. +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct ClientNesCapabilities { + /// IDE actions the client supports. + #[serde(skip_serializing_if = "Option::is_none")] + pub ide_actions: Option, + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +impl ClientNesCapabilities { + #[must_use] + pub fn new() -> Self { + Self::default() + } + + #[must_use] + pub fn ide_actions(mut self, ide_actions: impl IntoOption) -> Self { + self.ide_actions = ide_actions.into_option(); + self + } + + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + +/// IDE actions the client can perform. +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesIdeActionsCapabilities { + /// Whether the client supports the `rename` action. + #[serde(skip_serializing_if = "Option::is_none")] + pub rename: Option, + /// Whether the client supports the `searchAndReplace` action. + #[serde(skip_serializing_if = "Option::is_none")] + pub search_and_replace: Option, + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +impl NesIdeActionsCapabilities { + #[must_use] + pub fn new() -> Self { + Self::default() + } + + #[must_use] + pub fn rename(mut self, rename: impl IntoOption) -> Self { + self.rename = rename.into_option(); + self + } + + #[must_use] + pub fn search_and_replace( + mut self, + search_and_replace: impl IntoOption, + ) -> Self { + self.search_and_replace = search_and_replace.into_option(); + self + } + + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + +/// Marker for rename action support. +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesRenameActionCapabilities { + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +/// Marker for search and replace action support. +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesSearchAndReplaceActionCapabilities { + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +// General client capabilities + +/// General client capabilities (not specific to a particular feature). +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct GeneralCapabilities { + /// Position encodings supported by the client, in order of preference. + /// If omitted, defaults to UTF-16. + #[serde(skip_serializing_if = "Option::is_none")] + pub position_encodings: Option>, + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +impl GeneralCapabilities { + #[must_use] + pub fn new() -> Self { + Self::default() + } + + #[must_use] + pub fn position_encodings( + mut self, + position_encodings: impl IntoOption>, + ) -> Self { + self.position_encodings = position_encodings.into_option(); + self + } + + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + +// Document event notifications (client -> agent) + +/// Notification sent when a file is opened in the editor. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[schemars(extend("x-side" = "agent", "x-method" = DOCUMENT_DID_OPEN_METHOD_NAME))] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct DocumentDidOpenNotification { + /// The URI of the opened document. + pub uri: String, + /// The language identifier of the document (e.g., "rust", "python"). + pub language_id: String, + /// The version number of the document. + pub version: i64, + /// The full text content of the document. + pub text: String, + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +impl DocumentDidOpenNotification { + #[must_use] + pub fn new( + uri: impl Into, + language_id: impl Into, + version: i64, + text: impl Into, + ) -> Self { + Self { + uri: uri.into(), + language_id: language_id.into(), + version, + text: text.into(), + meta: None, + } + } + + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + +/// Notification sent when a file is edited. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[schemars(extend("x-side" = "agent", "x-method" = DOCUMENT_DID_CHANGE_METHOD_NAME))] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct DocumentDidChangeNotification { + /// The URI of the changed document. + pub uri: String, + /// The new version number of the document. + pub version: i64, + /// The content changes. + pub content_changes: Vec, + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +impl DocumentDidChangeNotification { + #[must_use] + pub fn new( + uri: impl Into, + version: i64, + content_changes: Vec, + ) -> Self { + Self { + uri: uri.into(), + version, + content_changes, + meta: None, + } + } + + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + +/// A content change event for a document. +/// +/// When `range` is `None`, `text` is the full content of the document. +/// When `range` is `Some`, `text` replaces the given range. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct TextDocumentContentChangeEvent { + /// The range of the document that changed. If `None`, the entire content is replaced. + #[serde(skip_serializing_if = "Option::is_none")] + pub range: Option, + /// The new text for the range, or the full document content if `range` is `None`. + pub text: String, +} + +impl TextDocumentContentChangeEvent { + #[must_use] + pub fn full(text: impl Into) -> Self { + Self { + range: None, + text: text.into(), + } + } + + #[must_use] + pub fn incremental(range: Range, text: impl Into) -> Self { + Self { + range: Some(range), + text: text.into(), + } + } +} + +/// Notification sent when a file is closed. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[schemars(extend("x-side" = "agent", "x-method" = DOCUMENT_DID_CLOSE_METHOD_NAME))] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct DocumentDidCloseNotification { + /// The URI of the closed document. + pub uri: String, + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +impl DocumentDidCloseNotification { + #[must_use] + pub fn new(uri: impl Into) -> Self { + Self { + uri: uri.into(), + meta: None, + } + } + + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + +/// Notification sent when a file is saved. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[schemars(extend("x-side" = "agent", "x-method" = DOCUMENT_DID_SAVE_METHOD_NAME))] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct DocumentDidSaveNotification { + /// The URI of the saved document. + pub uri: String, + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +impl DocumentDidSaveNotification { + #[must_use] + pub fn new(uri: impl Into) -> Self { + Self { + uri: uri.into(), + meta: None, + } + } + + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + +/// Notification sent when a file becomes the active editor tab. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[schemars(extend("x-side" = "agent", "x-method" = DOCUMENT_DID_FOCUS_METHOD_NAME))] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct DocumentDidFocusNotification { + /// The URI of the focused document. + pub uri: String, + /// The version number of the document. + pub version: i64, + /// The current cursor position. + pub position: Position, + /// The portion of the file currently visible in the editor viewport. + pub visible_range: Range, + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +impl DocumentDidFocusNotification { + #[must_use] + pub fn new( + uri: impl Into, + version: i64, + position: Position, + visible_range: Range, + ) -> Self { + Self { + uri: uri.into(), + version, + position, + visible_range, + meta: None, + } + } + + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + +// NES session start + +/// A unique identifier for an NES session, distinct from chat `SessionId`. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)] +#[serde(transparent)] +#[from(Arc, String, &'static str)] +#[non_exhaustive] +pub struct NesSessionId(pub Arc); + +impl NesSessionId { + #[must_use] + pub fn new(id: impl Into>) -> Self { + Self(id.into()) + } +} + +/// Request to start an NES session. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[schemars(extend("x-side" = "agent", "x-method" = NES_START_METHOD_NAME))] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesStartRequest { + /// The root URI of the workspace. + #[serde(skip_serializing_if = "Option::is_none")] + pub workspace_uri: Option, + /// The workspace folders. + #[serde(skip_serializing_if = "Option::is_none")] + pub workspace_folders: Option>, + /// Repository metadata, if the workspace is a git repository. + #[serde(skip_serializing_if = "Option::is_none")] + pub repository: Option, + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +impl NesStartRequest { + #[must_use] + pub fn new() -> Self { + Self { + workspace_uri: None, + workspace_folders: None, + repository: None, + meta: None, + } + } + + #[must_use] + pub fn workspace_uri(mut self, workspace_uri: impl IntoOption) -> Self { + self.workspace_uri = workspace_uri.into_option(); + self + } + + #[must_use] + pub fn workspace_folders( + mut self, + workspace_folders: impl IntoOption>, + ) -> Self { + self.workspace_folders = workspace_folders.into_option(); + self + } + + #[must_use] + pub fn repository(mut self, repository: impl IntoOption) -> Self { + self.repository = repository.into_option(); + self + } + + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + +impl Default for NesStartRequest { + fn default() -> Self { + Self::new() + } +} + +/// A workspace folder. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct WorkspaceFolder { + /// The URI of the folder. + pub uri: String, + /// The display name of the folder. + pub name: String, +} + +impl WorkspaceFolder { + #[must_use] + pub fn new(uri: impl Into, name: impl Into) -> Self { + Self { + uri: uri.into(), + name: name.into(), + } + } +} + +/// Repository metadata for an NES session. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesRepository { + /// The repository name. + pub name: String, + /// The repository owner. + pub owner: String, + /// The remote URL of the repository. + pub remote_url: String, +} + +impl NesRepository { + #[must_use] + pub fn new( + name: impl Into, + owner: impl Into, + remote_url: impl Into, + ) -> Self { + Self { + name: name.into(), + owner: owner.into(), + remote_url: remote_url.into(), + } + } +} + +/// Response to `nes/start`. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[schemars(extend("x-side" = "agent", "x-method" = NES_START_METHOD_NAME))] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesStartResponse { + /// The session ID for the newly started NES session. + pub session_id: NesSessionId, + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +impl NesStartResponse { + #[must_use] + pub fn new(session_id: impl Into) -> Self { + Self { + session_id: session_id.into(), + meta: None, + } + } + + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + +// NES suggest request + +/// What triggered the suggestion request. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[non_exhaustive] +pub enum NesTriggerKind { + /// Triggered by user typing or cursor movement. + #[serde(rename = "automatic")] + Automatic, + /// Triggered by a diagnostic appearing at or near the cursor. + #[serde(rename = "diagnostic")] + Diagnostic, + /// Triggered by an explicit user action (keyboard shortcut). + #[serde(rename = "manual")] + Manual, +} + +/// Request for a code suggestion. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[schemars(extend("x-side" = "agent", "x-method" = NES_SUGGEST_METHOD_NAME))] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesSuggestRequest { + /// The URI of the document to suggest for. + pub uri: String, + /// The version number of the document. + pub version: i64, + /// The current cursor position. + pub position: Position, + /// The current text selection range, if any. + #[serde(skip_serializing_if = "Option::is_none")] + pub selection: Option, + /// What triggered this suggestion request. + pub trigger_kind: NesTriggerKind, + /// Context for the suggestion, included based on agent capabilities. + #[serde(skip_serializing_if = "Option::is_none")] + pub context: Option, + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +impl NesSuggestRequest { + #[must_use] + pub fn new( + uri: impl Into, + version: i64, + position: Position, + trigger_kind: NesTriggerKind, + ) -> Self { + Self { + uri: uri.into(), + version, + position, + selection: None, + trigger_kind, + context: None, + meta: None, + } + } + + #[must_use] + pub fn selection(mut self, selection: impl IntoOption) -> Self { + self.selection = selection.into_option(); + self + } + + #[must_use] + pub fn context(mut self, context: impl IntoOption) -> Self { + self.context = context.into_option(); + self + } + + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + +/// Context attached to a suggestion request. +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesSuggestContext { + /// Recently accessed files. + #[serde(skip_serializing_if = "Option::is_none")] + pub recent_files: Option>, + /// Related code snippets. + #[serde(skip_serializing_if = "Option::is_none")] + pub related_snippets: Option>, + /// Recent edit history. + #[serde(skip_serializing_if = "Option::is_none")] + pub edit_history: Option>, + /// Recent user actions (typing, navigation, etc.). + #[serde(skip_serializing_if = "Option::is_none")] + pub user_actions: Option>, + /// Currently open files in the editor. + #[serde(skip_serializing_if = "Option::is_none")] + pub open_files: Option>, + /// Current diagnostics (errors, warnings). + #[serde(skip_serializing_if = "Option::is_none")] + pub diagnostics: Option>, + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +impl NesSuggestContext { + #[must_use] + pub fn new() -> Self { + Self::default() + } + + #[must_use] + pub fn recent_files(mut self, recent_files: impl IntoOption>) -> Self { + self.recent_files = recent_files.into_option(); + self + } + + #[must_use] + pub fn related_snippets( + mut self, + related_snippets: impl IntoOption>, + ) -> Self { + self.related_snippets = related_snippets.into_option(); + self + } + + #[must_use] + pub fn edit_history(mut self, edit_history: impl IntoOption>) -> Self { + self.edit_history = edit_history.into_option(); + self + } + + #[must_use] + pub fn user_actions(mut self, user_actions: impl IntoOption>) -> Self { + self.user_actions = user_actions.into_option(); + self + } + + #[must_use] + pub fn open_files(mut self, open_files: impl IntoOption>) -> Self { + self.open_files = open_files.into_option(); + self + } + + #[must_use] + pub fn diagnostics(mut self, diagnostics: impl IntoOption>) -> Self { + self.diagnostics = diagnostics.into_option(); + self + } + + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + +/// A recently accessed file. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesRecentFile { + /// The URI of the file. + pub uri: String, + /// The language identifier. + pub language_id: String, + /// The full text content of the file. + pub text: String, +} + +impl NesRecentFile { + #[must_use] + pub fn new( + uri: impl Into, + language_id: impl Into, + text: impl Into, + ) -> Self { + Self { + uri: uri.into(), + language_id: language_id.into(), + text: text.into(), + } + } +} + +/// A related code snippet from a file. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesRelatedSnippet { + /// The URI of the file containing the snippets. + pub uri: String, + /// The code excerpts. + pub excerpts: Vec, +} + +impl NesRelatedSnippet { + #[must_use] + pub fn new(uri: impl Into, excerpts: Vec) -> Self { + Self { + uri: uri.into(), + excerpts, + } + } +} + +/// A code excerpt from a file. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesExcerpt { + /// The start line of the excerpt (zero-based). + pub start_line: u32, + /// The end line of the excerpt (zero-based). + pub end_line: u32, + /// The text content of the excerpt. + pub text: String, +} + +impl NesExcerpt { + #[must_use] + pub fn new(start_line: u32, end_line: u32, text: impl Into) -> Self { + Self { + start_line, + end_line, + text: text.into(), + } + } +} + +/// An entry in the edit history. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesEditHistoryEntry { + /// The URI of the edited file. + pub uri: String, + /// A diff representing the edit. + pub diff: String, +} + +impl NesEditHistoryEntry { + #[must_use] + pub fn new(uri: impl Into, diff: impl Into) -> Self { + Self { + uri: uri.into(), + diff: diff.into(), + } + } +} + +/// A user action (typing, cursor movement, etc.). +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesUserAction { + /// The kind of action (e.g., "insertChar", "cursorMovement"). + pub action: String, + /// The URI of the file where the action occurred. + pub uri: String, + /// The line number where the action occurred. + pub line: u32, + /// The character offset where the action occurred. + pub offset: u32, + /// Timestamp in milliseconds since epoch. + pub timestamp_ms: u64, +} + +impl NesUserAction { + #[must_use] + pub fn new( + action: impl Into, + uri: impl Into, + line: u32, + offset: u32, + timestamp_ms: u64, + ) -> Self { + Self { + action: action.into(), + uri: uri.into(), + line, + offset, + timestamp_ms, + } + } +} + +/// An open file in the editor. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesOpenFile { + /// The URI of the file. + pub uri: String, + /// The language identifier. + pub language_id: String, + /// The visible range in the editor, if any. + pub visible_range: Option, + /// Timestamp in milliseconds since epoch of when the file was last focused. + pub last_focused_ms: Option, +} + +impl NesOpenFile { + #[must_use] + pub fn new(uri: impl Into, language_id: impl Into) -> Self { + Self { + uri: uri.into(), + language_id: language_id.into(), + visible_range: None, + last_focused_ms: None, + } + } + + #[must_use] + pub fn visible_range(mut self, visible_range: impl IntoOption) -> Self { + self.visible_range = visible_range.into_option(); + self + } + + #[must_use] + pub fn last_focused_ms(mut self, last_focused_ms: impl IntoOption) -> Self { + self.last_focused_ms = last_focused_ms.into_option(); + self + } +} + +/// A diagnostic (error, warning, etc.). +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesDiagnostic { + /// The URI of the file containing the diagnostic. + pub uri: String, + /// The range of the diagnostic. + pub range: Range, + /// The severity of the diagnostic. + pub severity: NesDiagnosticSeverity, + /// The diagnostic message. + pub message: String, +} + +impl NesDiagnostic { + #[must_use] + pub fn new( + uri: impl Into, + range: Range, + severity: NesDiagnosticSeverity, + message: impl Into, + ) -> Self { + Self { + uri: uri.into(), + range, + severity, + message: message.into(), + } + } +} + +/// Severity of a diagnostic. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[non_exhaustive] +pub enum NesDiagnosticSeverity { + /// An error. + #[serde(rename = "error")] + Error, + /// A warning. + #[serde(rename = "warning")] + Warning, + /// An informational message. + #[serde(rename = "information")] + Information, + /// A hint. + #[serde(rename = "hint")] + Hint, +} + +// NES suggest response + +/// Response to `nes/suggest`. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[schemars(extend("x-side" = "agent", "x-method" = NES_SUGGEST_METHOD_NAME))] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesSuggestResponse { + /// The list of suggestions. + pub suggestions: Vec, + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +impl NesSuggestResponse { + #[must_use] + pub fn new(suggestions: Vec) -> Self { + Self { + suggestions, + meta: None, + } + } + + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + +/// A suggestion returned by the agent. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(tag = "kind", rename_all = "camelCase")] +#[non_exhaustive] +pub enum NesSuggestion { + /// A text edit suggestion. + Edit(NesEditSuggestion), + /// A jump-to-location suggestion. + Jump(NesJumpSuggestion), + /// An IDE action suggestion. + Action(NesActionSuggestion), +} + +/// A text edit suggestion. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesEditSuggestion { + /// Unique identifier for accept/reject tracking. + pub id: String, + /// The URI of the file to edit. + pub uri: String, + /// The text edits to apply. + pub edits: Vec, + /// Optional suggested cursor position after applying edits. + #[serde(skip_serializing_if = "Option::is_none")] + pub cursor_position: Option, +} + +impl NesEditSuggestion { + #[must_use] + pub fn new(id: impl Into, uri: impl Into, edits: Vec) -> Self { + Self { + id: id.into(), + uri: uri.into(), + edits, + cursor_position: None, + } + } + + #[must_use] + pub fn cursor_position(mut self, cursor_position: impl IntoOption) -> Self { + self.cursor_position = cursor_position.into_option(); + self + } +} + +/// A text edit within a suggestion. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesTextEdit { + /// The range to replace. + pub range: Range, + /// The replacement text. + pub new_text: String, +} + +impl NesTextEdit { + #[must_use] + pub fn new(range: Range, new_text: impl Into) -> Self { + Self { + range, + new_text: new_text.into(), + } + } +} + +/// A jump-to-location suggestion. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesJumpSuggestion { + /// Unique identifier for accept/reject tracking. + pub id: String, + /// The file to navigate to. + pub uri: String, + /// The target position within the file. + pub position: Position, +} + +impl NesJumpSuggestion { + #[must_use] + pub fn new(id: impl Into, uri: impl Into, position: Position) -> Self { + Self { + id: id.into(), + uri: uri.into(), + position, + } + } +} + +/// An IDE action suggestion. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesActionSuggestion { + /// Unique identifier for accept/reject tracking. + pub id: String, + /// The IDE action to perform (must match a client-advertised action). + pub action_id: String, + /// Action parameters matching the schema from the client's capability. + #[serde(skip_serializing_if = "Option::is_none")] + pub arguments: Option, +} + +impl NesActionSuggestion { + #[must_use] + pub fn new(id: impl Into, action_id: impl Into) -> Self { + Self { + id: id.into(), + action_id: action_id.into(), + arguments: None, + } + } + + #[must_use] + pub fn arguments(mut self, arguments: impl IntoOption) -> Self { + self.arguments = arguments.into_option(); + self + } +} + +// NES accept/reject notifications + +/// Notification sent when a suggestion is accepted. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[schemars(extend("x-side" = "agent", "x-method" = NES_ACCEPT_METHOD_NAME))] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesAcceptNotification { + /// The ID of the accepted suggestion. + pub id: String, + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +impl NesAcceptNotification { + #[must_use] + pub fn new(id: impl Into) -> Self { + Self { + id: id.into(), + meta: None, + } + } + + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + +/// Notification sent when a suggestion is rejected. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[schemars(extend("x-side" = "agent", "x-method" = NES_REJECT_METHOD_NAME))] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesRejectNotification { + /// The ID of the rejected suggestion. + pub id: String, + /// The reason for rejection. + #[serde(skip_serializing_if = "Option::is_none")] + pub reason: Option, + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +impl NesRejectNotification { + #[must_use] + pub fn new(id: impl Into) -> Self { + Self { + id: id.into(), + reason: None, + meta: None, + } + } + + #[must_use] + pub fn reason(mut self, reason: impl IntoOption) -> Self { + self.reason = reason.into_option(); + self + } + + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + +/// The reason a suggestion was rejected. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[non_exhaustive] +pub enum NesRejectReason { + /// The user explicitly dismissed the suggestion. + #[serde(rename = "rejected")] + Rejected, + /// The suggestion was shown but the user continued editing without interacting. + #[serde(rename = "ignored")] + Ignored, + /// The suggestion was superseded by a newer suggestion. + #[serde(rename = "replaced")] + Replaced, + /// The request was cancelled before the agent returned a response. + #[serde(rename = "cancelled")] + Cancelled, +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + #[test] + fn test_position_encoding_kind_serialization() { + assert_eq!( + serde_json::to_value(&PositionEncodingKind::Utf16).unwrap(), + json!("utf-16") + ); + assert_eq!( + serde_json::to_value(&PositionEncodingKind::Utf32).unwrap(), + json!("utf-32") + ); + assert_eq!( + serde_json::to_value(&PositionEncodingKind::Utf8).unwrap(), + json!("utf-8") + ); + + assert_eq!( + serde_json::from_value::(json!("utf-16")).unwrap(), + PositionEncodingKind::Utf16 + ); + assert_eq!( + serde_json::from_value::(json!("utf-32")).unwrap(), + PositionEncodingKind::Utf32 + ); + assert_eq!( + serde_json::from_value::(json!("utf-8")).unwrap(), + PositionEncodingKind::Utf8 + ); + } + + #[test] + fn test_agent_nes_capabilities_serialization() { + let caps = NesCapabilities::new() + .events( + NesEventCapabilities::new().document( + NesDocumentEventCapabilities::new() + .did_open(NesDocumentDidOpenCapabilities::default()) + .did_change( + NesDocumentDidChangeCapabilities::new() + .sync_kind(TextDocumentSyncKind::Incremental), + ) + .did_close(NesDocumentDidCloseCapabilities::default()) + .did_save(NesDocumentDidSaveCapabilities::default()) + .did_focus(NesDocumentDidFocusCapabilities::default()), + ), + ) + .context( + NesContextCapabilities::new() + .recent_files(NesRecentFilesCapabilities { + max_count: Some(10), + meta: None, + }) + .related_snippets(NesRelatedSnippetsCapabilities::default()) + .edit_history(NesEditHistoryCapabilities { + max_count: Some(6), + meta: None, + }) + .user_actions(NesUserActionsCapabilities { + max_count: Some(16), + meta: None, + }) + .open_files(NesOpenFilesCapabilities::default()) + .diagnostics(NesDiagnosticsCapabilities::default()), + ); + + let json = serde_json::to_value(&caps).unwrap(); + assert_eq!( + json, + json!({ + "events": { + "document": { + "didOpen": {}, + "didChange": { + "syncKind": "incremental" + }, + "didClose": {}, + "didSave": {}, + "didFocus": {} + } + }, + "context": { + "recentFiles": { + "maxCount": 10 + }, + "relatedSnippets": {}, + "editHistory": { + "maxCount": 6 + }, + "userActions": { + "maxCount": 16 + }, + "openFiles": {}, + "diagnostics": {} + } + }) + ); + + // Round-trip + let deserialized: NesCapabilities = serde_json::from_value(json).unwrap(); + assert_eq!(deserialized, caps); + } + + #[test] + fn test_client_nes_capabilities_serialization() { + let caps = ClientNesCapabilities::new().ide_actions( + NesIdeActionsCapabilities::new() + .rename(NesRenameActionCapabilities::default()) + .search_and_replace(NesSearchAndReplaceActionCapabilities::default()), + ); + + let json = serde_json::to_value(&caps).unwrap(); + assert_eq!( + json, + json!({ + "ideActions": { + "rename": {}, + "searchAndReplace": {} + } + }) + ); + + let deserialized: ClientNesCapabilities = serde_json::from_value(json).unwrap(); + assert_eq!(deserialized, caps); + } + + #[test] + fn test_document_did_open_serialization() { + let notification = DocumentDidOpenNotification::new( + "file:///path/to/file.rs", + "rust", + 1, + "fn main() {\n println!(\"hello\");\n}\n", + ); + + let json = serde_json::to_value(¬ification).unwrap(); + assert_eq!( + json, + json!({ + "uri": "file:///path/to/file.rs", + "languageId": "rust", + "version": 1, + "text": "fn main() {\n println!(\"hello\");\n}\n" + }) + ); + + let deserialized: DocumentDidOpenNotification = serde_json::from_value(json).unwrap(); + assert_eq!(deserialized, notification); + } + + #[test] + fn test_document_did_change_incremental_serialization() { + let notification = DocumentDidChangeNotification::new( + "file:///path/to/file.rs", + 2, + vec![TextDocumentContentChangeEvent::incremental( + Range::new(Position::new(1, 4), Position::new(1, 4)), + "let x = 42;\n ", + )], + ); + + let json = serde_json::to_value(¬ification).unwrap(); + assert_eq!( + json, + json!({ + "uri": "file:///path/to/file.rs", + "version": 2, + "contentChanges": [ + { + "range": { + "start": { "line": 1, "character": 4 }, + "end": { "line": 1, "character": 4 } + }, + "text": "let x = 42;\n " + } + ] + }) + ); + } + + #[test] + fn test_document_did_change_full_serialization() { + let notification = DocumentDidChangeNotification::new( + "file:///path/to/file.rs", + 2, + vec![TextDocumentContentChangeEvent::full( + "fn main() {\n let x = 42;\n println!(\"hello\");\n}\n", + )], + ); + + let json = serde_json::to_value(¬ification).unwrap(); + assert_eq!( + json, + json!({ + "uri": "file:///path/to/file.rs", + "version": 2, + "contentChanges": [ + { + "text": "fn main() {\n let x = 42;\n println!(\"hello\");\n}\n" + } + ] + }) + ); + } + + #[test] + fn test_document_did_close_serialization() { + let notification = DocumentDidCloseNotification::new("file:///path/to/file.rs"); + let json = serde_json::to_value(¬ification).unwrap(); + assert_eq!(json, json!({ "uri": "file:///path/to/file.rs" })); + } + + #[test] + fn test_document_did_save_serialization() { + let notification = DocumentDidSaveNotification::new("file:///path/to/file.rs"); + let json = serde_json::to_value(¬ification).unwrap(); + assert_eq!(json, json!({ "uri": "file:///path/to/file.rs" })); + } + + #[test] + fn test_document_did_focus_serialization() { + let notification = DocumentDidFocusNotification::new( + "file:///path/to/file.rs", + 2, + Position::new(5, 12), + Range::new(Position::new(0, 0), Position::new(45, 0)), + ); + + let json = serde_json::to_value(¬ification).unwrap(); + assert_eq!( + json, + json!({ + "uri": "file:///path/to/file.rs", + "version": 2, + "position": { "line": 5, "character": 12 }, + "visibleRange": { + "start": { "line": 0, "character": 0 }, + "end": { "line": 45, "character": 0 } + } + }) + ); + } + + #[test] + fn test_nes_suggestion_edit_serialization() { + let suggestion = NesSuggestion::Edit( + NesEditSuggestion::new( + "sugg_001", + "file:///path/to/other_file.rs", + vec![NesTextEdit::new( + Range::new(Position::new(5, 0), Position::new(5, 10)), + "let result = helper();", + )], + ) + .cursor_position(Position::new(5, 22)), + ); + + let json = serde_json::to_value(&suggestion).unwrap(); + assert_eq!( + json, + json!({ + "kind": "edit", + "id": "sugg_001", + "uri": "file:///path/to/other_file.rs", + "edits": [ + { + "range": { + "start": { "line": 5, "character": 0 }, + "end": { "line": 5, "character": 10 } + }, + "newText": "let result = helper();" + } + ], + "cursorPosition": { "line": 5, "character": 22 } + }) + ); + + let deserialized: NesSuggestion = serde_json::from_value(json).unwrap(); + assert_eq!(deserialized, suggestion); + } + + #[test] + fn test_nes_suggestion_jump_serialization() { + let suggestion = NesSuggestion::Jump(NesJumpSuggestion::new( + "sugg_002", + "file:///path/to/other_file.rs", + Position::new(15, 4), + )); + + let json = serde_json::to_value(&suggestion).unwrap(); + assert_eq!( + json, + json!({ + "kind": "jump", + "id": "sugg_002", + "uri": "file:///path/to/other_file.rs", + "position": { "line": 15, "character": 4 } + }) + ); + + let deserialized: NesSuggestion = serde_json::from_value(json).unwrap(); + assert_eq!(deserialized, suggestion); + } + + #[test] + fn test_nes_suggestion_action_serialization() { + let suggestion = NesSuggestion::Action( + NesActionSuggestion::new("sugg_003", "rename").arguments(json!({ + "uri": "file:///path/to/file.rs", + "position": { "line": 5, "character": 10 }, + "newName": "calculateTotal" + })), + ); + + let json = serde_json::to_value(&suggestion).unwrap(); + assert_eq!( + json, + json!({ + "kind": "action", + "id": "sugg_003", + "actionId": "rename", + "arguments": { + "uri": "file:///path/to/file.rs", + "position": { "line": 5, "character": 10 }, + "newName": "calculateTotal" + } + }) + ); + + let deserialized: NesSuggestion = serde_json::from_value(json).unwrap(); + assert_eq!(deserialized, suggestion); + } + + #[test] + fn test_nes_start_request_serialization() { + let request = NesStartRequest::new() + .workspace_uri("file:///Users/alice/projects/my-app") + .workspace_folders(vec![WorkspaceFolder::new( + "file:///Users/alice/projects/my-app", + "my-app", + )]) + .repository(NesRepository::new( + "my-app", + "alice", + "https://github.com/alice/my-app.git", + )); + + let json = serde_json::to_value(&request).unwrap(); + assert_eq!( + json, + json!({ + "workspaceUri": "file:///Users/alice/projects/my-app", + "workspaceFolders": [ + { + "uri": "file:///Users/alice/projects/my-app", + "name": "my-app" + } + ], + "repository": { + "name": "my-app", + "owner": "alice", + "remoteUrl": "https://github.com/alice/my-app.git" + } + }) + ); + } + + #[test] + fn test_nes_start_response_serialization() { + let response = NesStartResponse::new("nes_abc123"); + let json = serde_json::to_value(&response).unwrap(); + assert_eq!(json, json!({ "sessionId": "nes_abc123" })); + } + + #[test] + fn test_nes_trigger_kind_serialization() { + assert_eq!( + serde_json::to_value(&NesTriggerKind::Automatic).unwrap(), + json!("automatic") + ); + assert_eq!( + serde_json::to_value(&NesTriggerKind::Diagnostic).unwrap(), + json!("diagnostic") + ); + assert_eq!( + serde_json::to_value(&NesTriggerKind::Manual).unwrap(), + json!("manual") + ); + } + + #[test] + fn test_nes_reject_reason_serialization() { + assert_eq!( + serde_json::to_value(&NesRejectReason::Rejected).unwrap(), + json!("rejected") + ); + assert_eq!( + serde_json::to_value(&NesRejectReason::Ignored).unwrap(), + json!("ignored") + ); + assert_eq!( + serde_json::to_value(&NesRejectReason::Replaced).unwrap(), + json!("replaced") + ); + assert_eq!( + serde_json::to_value(&NesRejectReason::Cancelled).unwrap(), + json!("cancelled") + ); + } + + #[test] + fn test_nes_accept_notification_serialization() { + let notification = NesAcceptNotification::new("sugg_001"); + let json = serde_json::to_value(¬ification).unwrap(); + assert_eq!(json, json!({ "id": "sugg_001" })); + } + + #[test] + fn test_nes_reject_notification_serialization() { + let notification = NesRejectNotification::new("sugg_001").reason(NesRejectReason::Rejected); + let json = serde_json::to_value(¬ification).unwrap(); + assert_eq!(json, json!({ "id": "sugg_001", "reason": "rejected" })); + } + + #[test] + fn test_nes_suggest_request_with_context_serialization() { + let request = NesSuggestRequest::new( + "file:///path/to/file.rs", + 2, + Position::new(5, 12), + NesTriggerKind::Automatic, + ) + .selection(Range::new(Position::new(5, 4), Position::new(5, 12))) + .context( + NesSuggestContext::new() + .recent_files(vec![NesRecentFile::new( + "file:///path/to/utils.rs", + "rust", + "pub fn helper() -> i32 { 42 }\n", + )]) + .diagnostics(vec![NesDiagnostic::new( + "file:///path/to/file.rs", + Range::new(Position::new(5, 0), Position::new(5, 10)), + NesDiagnosticSeverity::Error, + "cannot find value `foo` in this scope", + )]), + ); + + let json = serde_json::to_value(&request).unwrap(); + assert_eq!(json["uri"], "file:///path/to/file.rs"); + assert_eq!(json["version"], 2); + assert_eq!(json["triggerKind"], "automatic"); + assert_eq!( + json["context"]["recentFiles"][0]["uri"], + "file:///path/to/utils.rs" + ); + assert_eq!(json["context"]["diagnostics"][0]["severity"], "error"); + } + + #[test] + fn test_general_capabilities_serialization() { + let caps = GeneralCapabilities::new().position_encodings(vec![ + PositionEncodingKind::Utf32, + PositionEncodingKind::Utf16, + ]); + + let json = serde_json::to_value(&caps).unwrap(); + assert_eq!( + json, + json!({ + "positionEncodings": ["utf-32", "utf-16"] + }) + ); + } + + #[test] + fn test_text_document_sync_kind_serialization() { + assert_eq!( + serde_json::to_value(&TextDocumentSyncKind::Full).unwrap(), + json!("full") + ); + assert_eq!( + serde_json::to_value(&TextDocumentSyncKind::Incremental).unwrap(), + json!("incremental") + ); + } + + #[test] + fn test_nes_suggest_response_serialization() { + let response = NesSuggestResponse::new(vec![ + NesSuggestion::Edit(NesEditSuggestion::new( + "sugg_001", + "file:///path/to/file.rs", + vec![NesTextEdit::new( + Range::new(Position::new(5, 0), Position::new(5, 10)), + "let result = helper();", + )], + )), + NesSuggestion::Jump(NesJumpSuggestion::new( + "sugg_002", + "file:///path/to/other.rs", + Position::new(10, 0), + )), + ]); + + let json = serde_json::to_value(&response).unwrap(); + assert_eq!(json["suggestions"].as_array().unwrap().len(), 2); + assert_eq!(json["suggestions"][0]["kind"], "edit"); + assert_eq!(json["suggestions"][1]["kind"], "jump"); + } +} diff --git a/src/rpc.rs b/src/rpc.rs index 868087ee..7fe151aa 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -10,6 +10,9 @@ use crate::{ ClientNotification, ClientRequest, ClientResponse, Error, ExtNotification, ExtRequest, Result, }; +#[cfg(feature = "unstable_nes")] +use crate::NES_METHOD_NAMES; + /// JSON RPC Request Id /// /// An identifier established by the Client that MUST contain a String, Number, or NULL value if included. If it is not included it is assumed to be a notification. The value SHOULD normally not be Null [1] and Numbers SHOULD NOT contain fractional parts [2] @@ -324,6 +327,14 @@ impl Side for AgentSide { m if m == AGENT_METHOD_NAMES.session_prompt => serde_json::from_str(params.get()) .map(ClientRequest::PromptRequest) .map_err(Into::into), + #[cfg(feature = "unstable_nes")] + m if m == NES_METHOD_NAMES.nes_start => serde_json::from_str(params.get()) + .map(ClientRequest::NesStartRequest) + .map_err(Into::into), + #[cfg(feature = "unstable_nes")] + m if m == NES_METHOD_NAMES.nes_suggest => serde_json::from_str(params.get()) + .map(ClientRequest::NesSuggestRequest) + .map_err(Into::into), _ => { if let Some(custom_method) = method.strip_prefix('_') { Ok(ClientRequest::ExtMethodRequest(ExtRequest { @@ -344,6 +355,34 @@ impl Side for AgentSide { m if m == AGENT_METHOD_NAMES.session_cancel => serde_json::from_str(params.get()) .map(ClientNotification::CancelNotification) .map_err(Into::into), + #[cfg(feature = "unstable_nes")] + m if m == NES_METHOD_NAMES.document_did_open => serde_json::from_str(params.get()) + .map(ClientNotification::DocumentDidOpenNotification) + .map_err(Into::into), + #[cfg(feature = "unstable_nes")] + m if m == NES_METHOD_NAMES.document_did_change => serde_json::from_str(params.get()) + .map(ClientNotification::DocumentDidChangeNotification) + .map_err(Into::into), + #[cfg(feature = "unstable_nes")] + m if m == NES_METHOD_NAMES.document_did_close => serde_json::from_str(params.get()) + .map(ClientNotification::DocumentDidCloseNotification) + .map_err(Into::into), + #[cfg(feature = "unstable_nes")] + m if m == NES_METHOD_NAMES.document_did_save => serde_json::from_str(params.get()) + .map(ClientNotification::DocumentDidSaveNotification) + .map_err(Into::into), + #[cfg(feature = "unstable_nes")] + m if m == NES_METHOD_NAMES.document_did_focus => serde_json::from_str(params.get()) + .map(ClientNotification::DocumentDidFocusNotification) + .map_err(Into::into), + #[cfg(feature = "unstable_nes")] + m if m == NES_METHOD_NAMES.nes_accept => serde_json::from_str(params.get()) + .map(ClientNotification::NesAcceptNotification) + .map_err(Into::into), + #[cfg(feature = "unstable_nes")] + m if m == NES_METHOD_NAMES.nes_reject => serde_json::from_str(params.get()) + .map(ClientNotification::NesRejectNotification) + .map_err(Into::into), _ => { if let Some(custom_method) = method.strip_prefix('_') { Ok(ClientNotification::ExtNotification(ExtNotification { @@ -412,6 +451,152 @@ mod tests { } } +#[cfg(feature = "unstable_nes")] +#[cfg(test)] +mod nes_rpc_tests { + use super::*; + use serde_json::json; + + #[test] + fn test_decode_nes_start_request() { + let params = serde_json::to_string(&json!({ + "workspaceUri": "file:///Users/alice/projects/my-app", + "workspaceFolders": [ + { "uri": "file:///Users/alice/projects/my-app", "name": "my-app" } + ] + })) + .unwrap(); + let raw = serde_json::value::RawValue::from_string(params).unwrap(); + let request = AgentSide::decode_request("nes/start", Some(&raw)).unwrap(); + assert!(matches!(request, ClientRequest::NesStartRequest(_))); + } + + #[test] + fn test_decode_nes_suggest_request() { + let params = serde_json::to_string(&json!({ + "uri": "file:///path/to/file.rs", + "version": 2, + "position": { "line": 5, "character": 12 }, + "triggerKind": "automatic" + })) + .unwrap(); + let raw = serde_json::value::RawValue::from_string(params).unwrap(); + let request = AgentSide::decode_request("nes/suggest", Some(&raw)).unwrap(); + assert!(matches!(request, ClientRequest::NesSuggestRequest(_))); + } + + #[test] + fn test_decode_document_did_open_notification() { + let params = serde_json::to_string(&json!({ + "uri": "file:///path/to/file.rs", + "languageId": "rust", + "version": 1, + "text": "fn main() {}" + })) + .unwrap(); + let raw = serde_json::value::RawValue::from_string(params).unwrap(); + let notification = AgentSide::decode_notification("document/didOpen", Some(&raw)).unwrap(); + assert!(matches!( + notification, + ClientNotification::DocumentDidOpenNotification(_) + )); + } + + #[test] + fn test_decode_document_did_change_notification() { + let params = serde_json::to_string(&json!({ + "uri": "file:///path/to/file.rs", + "version": 2, + "contentChanges": [{ "text": "fn main() { let x = 1; }" }] + })) + .unwrap(); + let raw = serde_json::value::RawValue::from_string(params).unwrap(); + let notification = + AgentSide::decode_notification("document/didChange", Some(&raw)).unwrap(); + assert!(matches!( + notification, + ClientNotification::DocumentDidChangeNotification(_) + )); + } + + #[test] + fn test_decode_document_did_close_notification() { + let params = serde_json::to_string(&json!({ + "uri": "file:///path/to/file.rs" + })) + .unwrap(); + let raw = serde_json::value::RawValue::from_string(params).unwrap(); + let notification = AgentSide::decode_notification("document/didClose", Some(&raw)).unwrap(); + assert!(matches!( + notification, + ClientNotification::DocumentDidCloseNotification(_) + )); + } + + #[test] + fn test_decode_document_did_save_notification() { + let params = serde_json::to_string(&json!({ + "uri": "file:///path/to/file.rs" + })) + .unwrap(); + let raw = serde_json::value::RawValue::from_string(params).unwrap(); + let notification = AgentSide::decode_notification("document/didSave", Some(&raw)).unwrap(); + assert!(matches!( + notification, + ClientNotification::DocumentDidSaveNotification(_) + )); + } + + #[test] + fn test_decode_document_did_focus_notification() { + let params = serde_json::to_string(&json!({ + "uri": "file:///path/to/file.rs", + "version": 2, + "position": { "line": 5, "character": 12 }, + "visibleRange": { + "start": { "line": 0, "character": 0 }, + "end": { "line": 45, "character": 0 } + } + })) + .unwrap(); + let raw = serde_json::value::RawValue::from_string(params).unwrap(); + let notification = AgentSide::decode_notification("document/didFocus", Some(&raw)).unwrap(); + assert!(matches!( + notification, + ClientNotification::DocumentDidFocusNotification(_) + )); + } + + #[test] + fn test_decode_nes_accept_notification() { + let params = serde_json::to_string(&json!({ + "id": "sugg_001" + })) + .unwrap(); + let raw = serde_json::value::RawValue::from_string(params).unwrap(); + let notification = AgentSide::decode_notification("nes/accept", Some(&raw)).unwrap(); + assert!(matches!( + notification, + ClientNotification::NesAcceptNotification(_) + )); + } + + #[test] + fn test_decode_nes_reject_notification() { + let params = serde_json::to_string(&json!({ + "id": "sugg_001", + "reason": "rejected" + })) + .unwrap(); + let raw = serde_json::value::RawValue::from_string(params).unwrap(); + let notification = AgentSide::decode_notification("nes/reject", Some(&raw)).unwrap(); + assert!(matches!( + notification, + ClientNotification::NesRejectNotification(_) + )); + } +} + #[test] fn test_notification_wire_format() { use super::*; From 7db01b6df3f836df59633439fdece083f5e476be Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Fri, 27 Mar 2026 11:47:22 +0100 Subject: [PATCH 02/13] Small fixes --- Cargo.toml | 4 ++-- src/client.rs | 15 ++++++++------ src/nes.rs | 54 --------------------------------------------------- 3 files changed, 11 insertions(+), 62 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e0555ddf..f56ea16e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,9 +17,9 @@ include = ["/src/**/*.rs", "/README.md", "/LICENSE", "/Cargo.toml"] unstable = [ "unstable_auth_methods", "unstable_cancel_request", - "unstable_nes", "unstable_elicitation", "unstable_logout", + "unstable_nes", "unstable_session_fork", "unstable_session_model", "unstable_session_resume", @@ -30,9 +30,9 @@ unstable = [ ] unstable_auth_methods = [] unstable_cancel_request = [] -unstable_nes = [] unstable_elicitation = [] unstable_logout = [] +unstable_nes = [] unstable_session_fork = [] unstable_session_model = [] unstable_session_resume = [] diff --git a/src/client.rs b/src/client.rs index f960c369..bb0dfe29 100644 --- a/src/client.rs +++ b/src/client.rs @@ -21,7 +21,7 @@ use crate::{ use crate::{IntoMaybeUndefined, MaybeUndefined}; #[cfg(feature = "unstable_nes")] -use crate::{ClientNesCapabilities, GeneralCapabilities}; +use crate::{ClientNesCapabilities, PositionEncodingKind}; // Session updates @@ -1515,10 +1515,10 @@ pub struct ClientCapabilities { /// /// This capability is not part of the spec yet, and may be removed or changed at any point. /// - /// General client capabilities (e.g. position encodings). + /// The position encoding selected by the agent from the client's supported encodings. #[cfg(feature = "unstable_nes")] #[serde(skip_serializing_if = "Option::is_none")] - pub general: Option, + pub position_encoding: Option, /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at /// these keys. @@ -1588,11 +1588,14 @@ impl ClientCapabilities { /// **UNSTABLE** /// - /// General client capabilities (e.g. position encodings). + /// The position encoding selected by the agent. #[cfg(feature = "unstable_nes")] #[must_use] - pub fn general(mut self, general: impl IntoOption) -> Self { - self.general = general.into_option(); + pub fn position_encoding( + mut self, + position_encoding: impl IntoOption, + ) -> Self { + self.position_encoding = position_encoding.into_option(); self } diff --git a/src/nes.rs b/src/nes.rs index 645c323c..c8a9d2e3 100644 --- a/src/nes.rs +++ b/src/nes.rs @@ -620,44 +620,6 @@ pub struct NesSearchAndReplaceActionCapabilities { pub meta: Option, } -// General client capabilities - -/// General client capabilities (not specific to a particular feature). -#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -#[serde(rename_all = "camelCase")] -#[non_exhaustive] -pub struct GeneralCapabilities { - /// Position encodings supported by the client, in order of preference. - /// If omitted, defaults to UTF-16. - #[serde(skip_serializing_if = "Option::is_none")] - pub position_encodings: Option>, - /// The _meta property is reserved by ACP. - #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] - pub meta: Option, -} - -impl GeneralCapabilities { - #[must_use] - pub fn new() -> Self { - Self::default() - } - - #[must_use] - pub fn position_encodings( - mut self, - position_encodings: impl IntoOption>, - ) -> Self { - self.position_encodings = position_encodings.into_option(); - self - } - - #[must_use] - pub fn meta(mut self, meta: impl IntoOption) -> Self { - self.meta = meta.into_option(); - self - } -} - // Document event notifications (client -> agent) /// Notification sent when a file is opened in the editor. @@ -2118,22 +2080,6 @@ mod tests { assert_eq!(json["context"]["diagnostics"][0]["severity"], "error"); } - #[test] - fn test_general_capabilities_serialization() { - let caps = GeneralCapabilities::new().position_encodings(vec![ - PositionEncodingKind::Utf32, - PositionEncodingKind::Utf16, - ]); - - let json = serde_json::to_value(&caps).unwrap(); - assert_eq!( - json, - json!({ - "positionEncodings": ["utf-32", "utf-16"] - }) - ); - } - #[test] fn test_text_document_sync_kind_serialization() { assert_eq!( From 95d83ab070419ee4e564cd8af6739d021d450cb0 Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Fri, 27 Mar 2026 12:08:04 +0100 Subject: [PATCH 03/13] Add session ids to requests --- docs/protocol/draft/schema.mdx | 98 +++++++++++++++++----------- schema/schema.unstable.json | 116 ++++++++++++++++++++++----------- src/nes.rs | 100 +++++++++++++++++++--------- src/rpc.rs | 8 +++ 4 files changed, 216 insertions(+), 106 deletions(-) diff --git a/docs/protocol/draft/schema.mdx b/docs/protocol/draft/schema.mdx index 28f07940..ea2cb810 100644 --- a/docs/protocol/draft/schema.mdx +++ b/docs/protocol/draft/schema.mdx @@ -91,6 +91,13 @@ Notification sent when a file is edited. > The content changes. +SessionId} + required +> + The session ID for this notification. + The URI of the changed document. @@ -116,6 +123,13 @@ Notification sent when a file is closed. The _meta property is reserved by ACP. +SessionId} + required +> + The session ID for this notification. + The URI of the closed document. @@ -141,6 +155,13 @@ Notification sent when a file becomes the active editor tab. Position} required> The current cursor position. +SessionId} + required +> + The session ID for this notification. + The URI of the focused document. @@ -172,6 +193,13 @@ Notification sent when a file is opened in the editor. The language identifier of the document (e.g., "rust", "python"). +SessionId} + required +> + The session ID for this notification. + The full text content of the document. @@ -200,6 +228,13 @@ Notification sent when a file is saved. The _meta property is reserved by ACP. +SessionId} + required +> + The session ID for this notification. + The URI of the saved document. @@ -376,6 +411,13 @@ Notification sent when a suggestion is accepted. The ID of the accepted suggestion. +SessionId} + required +> + The session ID for this notification. + ### nes/reject @@ -411,6 +453,13 @@ Notification sent when a suggestion is rejected. > The reason for rejection. +SessionId} + required +> + The session ID for this notification. + ### nes/start @@ -475,7 +524,7 @@ Response to `nes/start`. NesSessionId} + type={SessionId} required > The session ID for the newly started NES session. @@ -530,6 +579,13 @@ Request for a code suggestion. > The current text selection range, if any. +SessionId} + required +> + The session ID for this request. + NesTriggerKind} @@ -2678,20 +2734,20 @@ Determines which file operations the agent can request. - Default: `{"readTextFile":false,"writeTextFile":false}` -GeneralCapabilities | null} > +ClientNesCapabilities | null} > **UNSTABLE** This capability is not part of the spec yet, and may be removed or changed at any point. -General client capabilities (e.g. position encodings). +NES (Next Edit Suggestions) capabilities supported by the client. -ClientNesCapabilities | null} > +PositionEncodingKind | null} > **UNSTABLE** This capability is not part of the spec yet, and may be removed or changed at any point. -NES (Next Edit Suggestions) capabilities supported by the client. +The position encoding selected by the agent from the client's supported encodings. @@ -3724,32 +3780,6 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte -## GeneralCapabilities - -General client capabilities (not specific to a particular feature). - -**Type:** Object - -**Properties:** - - - The _meta property is reserved by ACP. - - - - PositionEncodingKind[] - - | null - - } -> - Position encodings supported by the client, in order of preference. If - omitted, defaults to UTF-16. - - ## HttpHeader An HTTP header to set when making requests to the MCP server. @@ -4926,12 +4956,6 @@ Marker for search and replace action support. The _meta property is reserved by ACP. -## NesSessionId - -A unique identifier for an NES session, distinct from chat `SessionId`. - -**Type:** `string` - ## NesSuggestContext Context attached to a suggestion request. diff --git a/schema/schema.unstable.json b/schema/schema.unstable.json index 1c5bc667..f72a7652 100644 --- a/schema/schema.unstable.json +++ b/schema/schema.unstable.json @@ -888,27 +888,27 @@ }, "description": "File system capabilities supported by the client.\nDetermines which file operations the agent can request." }, - "general": { + "nes": { "anyOf": [ { - "$ref": "#/$defs/GeneralCapabilities" + "$ref": "#/$defs/ClientNesCapabilities" }, { "type": "null" } ], - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nGeneral client capabilities (e.g. position encodings)." + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nNES (Next Edit Suggestions) capabilities supported by the client." }, - "nes": { + "positionEncoding": { "anyOf": [ { - "$ref": "#/$defs/ClientNesCapabilities" + "$ref": "#/$defs/PositionEncodingKind" }, { "type": "null" } ], - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nNES (Next Edit Suggestions) capabilities supported by the client." + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nThe position encoding selected by the agent from the client's supported encodings." }, "terminal": { "default": false, @@ -1654,6 +1654,14 @@ }, "type": "array" }, + "sessionId": { + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], + "description": "The session ID for this notification." + }, "uri": { "description": "The URI of the changed document.", "type": "string" @@ -1664,7 +1672,7 @@ "type": "integer" } }, - "required": ["uri", "version", "contentChanges"], + "required": ["sessionId", "uri", "version", "contentChanges"], "type": "object", "x-method": "document/didChange", "x-side": "agent" @@ -1677,12 +1685,20 @@ "description": "The _meta property is reserved by ACP.", "type": ["object", "null"] }, + "sessionId": { + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], + "description": "The session ID for this notification." + }, "uri": { "description": "The URI of the closed document.", "type": "string" } }, - "required": ["uri"], + "required": ["sessionId", "uri"], "type": "object", "x-method": "document/didClose", "x-side": "agent" @@ -1703,6 +1719,14 @@ ], "description": "The current cursor position." }, + "sessionId": { + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], + "description": "The session ID for this notification." + }, "uri": { "description": "The URI of the focused document.", "type": "string" @@ -1721,7 +1745,7 @@ "description": "The portion of the file currently visible in the editor viewport." } }, - "required": ["uri", "version", "position", "visibleRange"], + "required": ["sessionId", "uri", "version", "position", "visibleRange"], "type": "object", "x-method": "document/didFocus", "x-side": "agent" @@ -1738,6 +1762,14 @@ "description": "The language identifier of the document (e.g., \"rust\", \"python\").", "type": "string" }, + "sessionId": { + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], + "description": "The session ID for this notification." + }, "text": { "description": "The full text content of the document.", "type": "string" @@ -1752,7 +1784,7 @@ "type": "integer" } }, - "required": ["uri", "languageId", "version", "text"], + "required": ["sessionId", "uri", "languageId", "version", "text"], "type": "object", "x-method": "document/didOpen", "x-side": "agent" @@ -1765,12 +1797,20 @@ "description": "The _meta property is reserved by ACP.", "type": ["object", "null"] }, + "sessionId": { + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], + "description": "The session ID for this notification." + }, "uri": { "description": "The URI of the saved document.", "type": "string" } }, - "required": ["uri"], + "required": ["sessionId", "uri"], "type": "object", "x-method": "document/didSave", "x-side": "agent" @@ -2501,24 +2541,6 @@ "x-method": "session/fork", "x-side": "agent" }, - "GeneralCapabilities": { - "description": "General client capabilities (not specific to a particular feature).", - "properties": { - "_meta": { - "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", - "type": ["object", "null"] - }, - "positionEncodings": { - "description": "Position encodings supported by the client, in order of preference.\nIf omitted, defaults to UTF-16.", - "items": { - "$ref": "#/$defs/PositionEncodingKind" - }, - "type": ["array", "null"] - } - }, - "type": "object" - }, "HttpHeader": { "description": "An HTTP header to set when making requests to the MCP server.", "properties": { @@ -3197,9 +3219,17 @@ "id": { "description": "The ID of the accepted suggestion.", "type": "string" + }, + "sessionId": { + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], + "description": "The session ID for this notification." } }, - "required": ["id"], + "required": ["sessionId", "id"], "type": "object", "x-method": "nes/accept", "x-side": "agent" @@ -3798,9 +3828,17 @@ } ], "description": "The reason for rejection." + }, + "sessionId": { + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], + "description": "The session ID for this notification." } }, - "required": ["id"], + "required": ["sessionId", "id"], "type": "object", "x-method": "nes/reject", "x-side": "agent" @@ -3900,10 +3938,6 @@ }, "type": "object" }, - "NesSessionId": { - "description": "A unique identifier for an NES session, distinct from chat `SessionId`.", - "type": "string" - }, "NesStartRequest": { "description": "Request to start an NES session.", "properties": { @@ -3950,7 +3984,7 @@ "sessionId": { "allOf": [ { - "$ref": "#/$defs/NesSessionId" + "$ref": "#/$defs/SessionId" } ], "description": "The session ID for the newly started NES session." @@ -4052,6 +4086,14 @@ ], "description": "The current text selection range, if any." }, + "sessionId": { + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], + "description": "The session ID for this request." + }, "triggerKind": { "allOf": [ { @@ -4070,7 +4112,7 @@ "type": "integer" } }, - "required": ["uri", "version", "position", "triggerKind"], + "required": ["sessionId", "uri", "version", "position", "triggerKind"], "type": "object", "x-method": "nes/suggest", "x-side": "agent" diff --git a/src/nes.rs b/src/nes.rs index c8a9d2e3..cba783e0 100644 --- a/src/nes.rs +++ b/src/nes.rs @@ -4,13 +4,10 @@ //! document events, and a suggestion request/response flow. NES sessions are //! independent of chat sessions and have their own lifecycle. -use std::sync::Arc; - -use derive_more::{Display, From}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use crate::{IntoOption, Meta}; +use crate::{IntoOption, Meta, SessionId}; // Method name constants @@ -628,6 +625,8 @@ pub struct NesSearchAndReplaceActionCapabilities { #[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct DocumentDidOpenNotification { + /// The session ID for this notification. + pub session_id: SessionId, /// The URI of the opened document. pub uri: String, /// The language identifier of the document (e.g., "rust", "python"). @@ -644,12 +643,14 @@ pub struct DocumentDidOpenNotification { impl DocumentDidOpenNotification { #[must_use] pub fn new( + session_id: impl Into, uri: impl Into, language_id: impl Into, version: i64, text: impl Into, ) -> Self { Self { + session_id: session_id.into(), uri: uri.into(), language_id: language_id.into(), version, @@ -671,6 +672,8 @@ impl DocumentDidOpenNotification { #[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct DocumentDidChangeNotification { + /// The session ID for this notification. + pub session_id: SessionId, /// The URI of the changed document. pub uri: String, /// The new version number of the document. @@ -685,11 +688,13 @@ pub struct DocumentDidChangeNotification { impl DocumentDidChangeNotification { #[must_use] pub fn new( + session_id: impl Into, uri: impl Into, version: i64, content_changes: Vec, ) -> Self { Self { + session_id: session_id.into(), uri: uri.into(), version, content_changes, @@ -743,6 +748,8 @@ impl TextDocumentContentChangeEvent { #[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct DocumentDidCloseNotification { + /// The session ID for this notification. + pub session_id: SessionId, /// The URI of the closed document. pub uri: String, /// The _meta property is reserved by ACP. @@ -752,8 +759,9 @@ pub struct DocumentDidCloseNotification { impl DocumentDidCloseNotification { #[must_use] - pub fn new(uri: impl Into) -> Self { + pub fn new(session_id: impl Into, uri: impl Into) -> Self { Self { + session_id: session_id.into(), uri: uri.into(), meta: None, } @@ -772,6 +780,8 @@ impl DocumentDidCloseNotification { #[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct DocumentDidSaveNotification { + /// The session ID for this notification. + pub session_id: SessionId, /// The URI of the saved document. pub uri: String, /// The _meta property is reserved by ACP. @@ -781,8 +791,9 @@ pub struct DocumentDidSaveNotification { impl DocumentDidSaveNotification { #[must_use] - pub fn new(uri: impl Into) -> Self { + pub fn new(session_id: impl Into, uri: impl Into) -> Self { Self { + session_id: session_id.into(), uri: uri.into(), meta: None, } @@ -801,6 +812,8 @@ impl DocumentDidSaveNotification { #[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct DocumentDidFocusNotification { + /// The session ID for this notification. + pub session_id: SessionId, /// The URI of the focused document. pub uri: String, /// The version number of the document. @@ -817,12 +830,14 @@ pub struct DocumentDidFocusNotification { impl DocumentDidFocusNotification { #[must_use] pub fn new( + session_id: impl Into, uri: impl Into, version: i64, position: Position, visible_range: Range, ) -> Self { Self { + session_id: session_id.into(), uri: uri.into(), version, position, @@ -840,20 +855,6 @@ impl DocumentDidFocusNotification { // NES session start -/// A unique identifier for an NES session, distinct from chat `SessionId`. -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Hash, Display, From)] -#[serde(transparent)] -#[from(Arc, String, &'static str)] -#[non_exhaustive] -pub struct NesSessionId(pub Arc); - -impl NesSessionId { - #[must_use] - pub fn new(id: impl Into>) -> Self { - Self(id.into()) - } -} - /// Request to start an NES session. #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[schemars(extend("x-side" = "agent", "x-method" = NES_START_METHOD_NAME))] @@ -975,7 +976,7 @@ impl NesRepository { #[non_exhaustive] pub struct NesStartResponse { /// The session ID for the newly started NES session. - pub session_id: NesSessionId, + pub session_id: SessionId, /// The _meta property is reserved by ACP. #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, @@ -983,7 +984,7 @@ pub struct NesStartResponse { impl NesStartResponse { #[must_use] - pub fn new(session_id: impl Into) -> Self { + pub fn new(session_id: impl Into) -> Self { Self { session_id: session_id.into(), meta: None, @@ -1020,6 +1021,8 @@ pub enum NesTriggerKind { #[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct NesSuggestRequest { + /// The session ID for this request. + pub session_id: SessionId, /// The URI of the document to suggest for. pub uri: String, /// The version number of the document. @@ -1042,12 +1045,14 @@ pub struct NesSuggestRequest { impl NesSuggestRequest { #[must_use] pub fn new( + session_id: impl Into, uri: impl Into, version: i64, position: Position, trigger_kind: NesTriggerKind, ) -> Self { Self { + session_id: session_id.into(), uri: uri.into(), version, position, @@ -1538,6 +1543,8 @@ impl NesActionSuggestion { #[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct NesAcceptNotification { + /// The session ID for this notification. + pub session_id: SessionId, /// The ID of the accepted suggestion. pub id: String, /// The _meta property is reserved by ACP. @@ -1547,8 +1554,9 @@ pub struct NesAcceptNotification { impl NesAcceptNotification { #[must_use] - pub fn new(id: impl Into) -> Self { + pub fn new(session_id: impl Into, id: impl Into) -> Self { Self { + session_id: session_id.into(), id: id.into(), meta: None, } @@ -1567,6 +1575,8 @@ impl NesAcceptNotification { #[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct NesRejectNotification { + /// The session ID for this notification. + pub session_id: SessionId, /// The ID of the rejected suggestion. pub id: String, /// The reason for rejection. @@ -1579,8 +1589,9 @@ pub struct NesRejectNotification { impl NesRejectNotification { #[must_use] - pub fn new(id: impl Into) -> Self { + pub fn new(session_id: impl Into, id: impl Into) -> Self { Self { + session_id: session_id.into(), id: id.into(), reason: None, meta: None, @@ -1750,6 +1761,7 @@ mod tests { #[test] fn test_document_did_open_serialization() { let notification = DocumentDidOpenNotification::new( + "session_123", "file:///path/to/file.rs", "rust", 1, @@ -1760,6 +1772,7 @@ mod tests { assert_eq!( json, json!({ + "sessionId": "session_123", "uri": "file:///path/to/file.rs", "languageId": "rust", "version": 1, @@ -1774,6 +1787,7 @@ mod tests { #[test] fn test_document_did_change_incremental_serialization() { let notification = DocumentDidChangeNotification::new( + "session_123", "file:///path/to/file.rs", 2, vec![TextDocumentContentChangeEvent::incremental( @@ -1786,6 +1800,7 @@ mod tests { assert_eq!( json, json!({ + "sessionId": "session_123", "uri": "file:///path/to/file.rs", "version": 2, "contentChanges": [ @@ -1804,6 +1819,7 @@ mod tests { #[test] fn test_document_did_change_full_serialization() { let notification = DocumentDidChangeNotification::new( + "session_123", "file:///path/to/file.rs", 2, vec![TextDocumentContentChangeEvent::full( @@ -1815,6 +1831,7 @@ mod tests { assert_eq!( json, json!({ + "sessionId": "session_123", "uri": "file:///path/to/file.rs", "version": 2, "contentChanges": [ @@ -1828,21 +1845,30 @@ mod tests { #[test] fn test_document_did_close_serialization() { - let notification = DocumentDidCloseNotification::new("file:///path/to/file.rs"); + let notification = + DocumentDidCloseNotification::new("session_123", "file:///path/to/file.rs"); let json = serde_json::to_value(¬ification).unwrap(); - assert_eq!(json, json!({ "uri": "file:///path/to/file.rs" })); + assert_eq!( + json, + json!({ "sessionId": "session_123", "uri": "file:///path/to/file.rs" }) + ); } #[test] fn test_document_did_save_serialization() { - let notification = DocumentDidSaveNotification::new("file:///path/to/file.rs"); + let notification = + DocumentDidSaveNotification::new("session_123", "file:///path/to/file.rs"); let json = serde_json::to_value(¬ification).unwrap(); - assert_eq!(json, json!({ "uri": "file:///path/to/file.rs" })); + assert_eq!( + json, + json!({ "sessionId": "session_123", "uri": "file:///path/to/file.rs" }) + ); } #[test] fn test_document_did_focus_serialization() { let notification = DocumentDidFocusNotification::new( + "session_123", "file:///path/to/file.rs", 2, Position::new(5, 12), @@ -1853,6 +1879,7 @@ mod tests { assert_eq!( json, json!({ + "sessionId": "session_123", "uri": "file:///path/to/file.rs", "version": 2, "position": { "line": 5, "character": 12 }, @@ -2033,21 +2060,29 @@ mod tests { #[test] fn test_nes_accept_notification_serialization() { - let notification = NesAcceptNotification::new("sugg_001"); + let notification = NesAcceptNotification::new("session_123", "sugg_001"); let json = serde_json::to_value(¬ification).unwrap(); - assert_eq!(json, json!({ "id": "sugg_001" })); + assert_eq!( + json, + json!({ "sessionId": "session_123", "id": "sugg_001" }) + ); } #[test] fn test_nes_reject_notification_serialization() { - let notification = NesRejectNotification::new("sugg_001").reason(NesRejectReason::Rejected); + let notification = + NesRejectNotification::new("session_123", "sugg_001").reason(NesRejectReason::Rejected); let json = serde_json::to_value(¬ification).unwrap(); - assert_eq!(json, json!({ "id": "sugg_001", "reason": "rejected" })); + assert_eq!( + json, + json!({ "sessionId": "session_123", "id": "sugg_001", "reason": "rejected" }) + ); } #[test] fn test_nes_suggest_request_with_context_serialization() { let request = NesSuggestRequest::new( + "session_123", "file:///path/to/file.rs", 2, Position::new(5, 12), @@ -2070,6 +2105,7 @@ mod tests { ); let json = serde_json::to_value(&request).unwrap(); + assert_eq!(json["sessionId"], "session_123"); assert_eq!(json["uri"], "file:///path/to/file.rs"); assert_eq!(json["version"], 2); assert_eq!(json["triggerKind"], "automatic"); diff --git a/src/rpc.rs b/src/rpc.rs index 7fe151aa..9fd45700 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -474,6 +474,7 @@ mod nes_rpc_tests { #[test] fn test_decode_nes_suggest_request() { let params = serde_json::to_string(&json!({ + "sessionId": "session_123", "uri": "file:///path/to/file.rs", "version": 2, "position": { "line": 5, "character": 12 }, @@ -488,6 +489,7 @@ mod nes_rpc_tests { #[test] fn test_decode_document_did_open_notification() { let params = serde_json::to_string(&json!({ + "sessionId": "session_123", "uri": "file:///path/to/file.rs", "languageId": "rust", "version": 1, @@ -505,6 +507,7 @@ mod nes_rpc_tests { #[test] fn test_decode_document_did_change_notification() { let params = serde_json::to_string(&json!({ + "sessionId": "session_123", "uri": "file:///path/to/file.rs", "version": 2, "contentChanges": [{ "text": "fn main() { let x = 1; }" }] @@ -522,6 +525,7 @@ mod nes_rpc_tests { #[test] fn test_decode_document_did_close_notification() { let params = serde_json::to_string(&json!({ + "sessionId": "session_123", "uri": "file:///path/to/file.rs" })) .unwrap(); @@ -536,6 +540,7 @@ mod nes_rpc_tests { #[test] fn test_decode_document_did_save_notification() { let params = serde_json::to_string(&json!({ + "sessionId": "session_123", "uri": "file:///path/to/file.rs" })) .unwrap(); @@ -550,6 +555,7 @@ mod nes_rpc_tests { #[test] fn test_decode_document_did_focus_notification() { let params = serde_json::to_string(&json!({ + "sessionId": "session_123", "uri": "file:///path/to/file.rs", "version": 2, "position": { "line": 5, "character": 12 }, @@ -570,6 +576,7 @@ mod nes_rpc_tests { #[test] fn test_decode_nes_accept_notification() { let params = serde_json::to_string(&json!({ + "sessionId": "session_123", "id": "sugg_001" })) .unwrap(); @@ -584,6 +591,7 @@ mod nes_rpc_tests { #[test] fn test_decode_nes_reject_notification() { let params = serde_json::to_string(&json!({ + "sessionId": "session_123", "id": "sugg_001", "reason": "rejected" })) From 1942f569bc34cc849110e50a7a052d566ccc9dab Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Fri, 27 Mar 2026 12:21:20 +0100 Subject: [PATCH 04/13] align stylization --- src/nes.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/nes.rs b/src/nes.rs index cba783e0..3e54df73 100644 --- a/src/nes.rs +++ b/src/nes.rs @@ -1302,8 +1302,10 @@ pub struct NesOpenFile { /// The language identifier. pub language_id: String, /// The visible range in the editor, if any. + #[serde(skip_serializing_if = "Option::is_none")] pub visible_range: Option, /// Timestamp in milliseconds since epoch of when the file was last focused. + #[serde(skip_serializing_if = "Option::is_none")] pub last_focused_ms: Option, } From fa4c54494670e8d205759d3643a5189fac8fc770 Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Fri, 27 Mar 2026 12:37:58 +0100 Subject: [PATCH 05/13] Update rfd --- docs/rfds/next-edit-suggestions.mdx | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/docs/rfds/next-edit-suggestions.mdx b/docs/rfds/next-edit-suggestions.mdx index ece81a25..cc7d06d2 100644 --- a/docs/rfds/next-edit-suggestions.mdx +++ b/docs/rfds/next-edit-suggestions.mdx @@ -29,11 +29,11 @@ The client inspects these declarations and provides only what was requested, min ### Capability advertisement -During `initialize`, the agent includes a `nes` field in its capabilities: +During `initialize`, the agent includes a `nes` field in `agentCapabilities`: ```json { - "capabilities": { + "agentCapabilities": { "nes": { "events": { "document": { @@ -69,11 +69,11 @@ All fields under `events` and `context` are optional — an agent advertises onl #### Client capabilities -The **client** advertises its own NES-related capabilities in the `initialize` request. Currently, the client can declare which well-known IDE actions it supports by listing their IDs. The agent reads these and may later include `"action"` kind suggestions that reference them. +The **client** advertises its own NES-related capabilities in the `initialize` request. Currently, the client can declare which well-known IDE actions it supports by advertising them as keys under `clientCapabilities.nes.ideActions`. The agent reads these and may later include `"action"` kind suggestions that reference them. ```json { - "capabilities": { + "clientCapabilities": { "nes": { "ideActions": { "rename": {}, @@ -84,7 +84,7 @@ The **client** advertises its own NES-related capabilities in the `initialize` r } ``` -Each entry in `ideActions` is the ID of a well-known action (see [Well-known IDE actions](#well-known-ide-actions) below). Agents should only suggest actions that the client has advertised. +Each key in `ideActions` is the ID of a well-known action (see [Well-known IDE actions](#well-known-ide-actions) below). Agents should only suggest actions that the client has advertised. ### Session lifecycle @@ -112,7 +112,7 @@ Client `initialize` request (excerpt): ```json { - "capabilities": { + "clientCapabilities": { "general": { "positionEncodings": ["utf-32", "utf-16"] } @@ -124,7 +124,7 @@ Agent `initialize` response (excerpt): ```json { - "capabilities": { + "agentCapabilities": { "nes": { ... }, "positionEncoding": "utf-32" } @@ -135,7 +135,7 @@ The negotiated encoding applies to all `Position` and `Range` values exchanged w ### Events -Events are fire-and-forget notifications from client to agent. The client sends them only if the corresponding key is present in the agent's advertised `events` capability (e.g. `nes.events.document` for NES). +Events are fire-and-forget notifications from client to agent. Every event is scoped to the NES session identified by the `sessionId` returned from `nes/start`. The client sends them only if the corresponding key is present in the agent's advertised `events` capability (e.g. `nes.events.document` for NES). #### `document/didOpen` @@ -146,6 +146,7 @@ Sent when a file is opened in the editor. "jsonrpc": "2.0", "method": "document/didOpen", "params": { + "sessionId": "session_abc123", "uri": "file:///path/to/file.rs", "languageId": "rust", "version": 1, @@ -168,6 +169,7 @@ Sent when a file is edited. Supports two sync modes declared by the agent: "jsonrpc": "2.0", "method": "document/didChange", "params": { + "sessionId": "session_abc123", "uri": "file:///path/to/file.rs", "version": 2, "contentChanges": [ @@ -190,6 +192,7 @@ Sent when a file is edited. Supports two sync modes declared by the agent: "jsonrpc": "2.0", "method": "document/didChange", "params": { + "sessionId": "session_abc123", "uri": "file:///path/to/file.rs", "version": 2, "contentChanges": [ @@ -210,6 +213,7 @@ Sent when a file is closed. "jsonrpc": "2.0", "method": "document/didClose", "params": { + "sessionId": "session_abc123", "uri": "file:///path/to/file.rs" } } @@ -224,6 +228,7 @@ Sent when a file is saved. "jsonrpc": "2.0", "method": "document/didSave", "params": { + "sessionId": "session_abc123", "uri": "file:///path/to/file.rs" } } @@ -238,6 +243,7 @@ Sent when a file becomes the active editor tab. Unlike `document/didOpen` (which "jsonrpc": "2.0", "method": "document/didFocus", "params": { + "sessionId": "session_abc123", "uri": "file:///path/to/file.rs", "version": 2, "position": { "line": 5, "character": 12 }, @@ -261,6 +267,7 @@ The client requests a suggestion by calling `nes/suggest`. Context fields are in "id": 42, "method": "nes/suggest", "params": { + "sessionId": "session_abc123", "uri": "file:///path/to/file.rs", "version": 2, "position": { "line": 5, "character": 12 }, @@ -460,6 +467,7 @@ Action suggestions additionally contain: "jsonrpc": "2.0", "method": "nes/accept", "params": { + "sessionId": "session_abc123", "id": "sugg_001" } } @@ -470,6 +478,7 @@ Action suggestions additionally contain: "jsonrpc": "2.0", "method": "nes/reject", "params": { + "sessionId": "session_abc123", "id": "sugg_001", "reason": "rejected" } @@ -520,11 +529,13 @@ Response: "jsonrpc": "2.0", "id": 1, "result": { - "sessionId": "nes_abc123" + "sessionId": "session_abc123" } } ``` +The returned `sessionId` scopes all subsequent NES events, requests, and notifications for that session. + #### Error handling The agent may reject `nes/start` with an error. In particular, agents that require authentication may return an `auth_required` error: From b74bb7eb57391973a95706f9fe10f6fbc9cef7b4 Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Fri, 27 Mar 2026 13:14:18 +0100 Subject: [PATCH 06/13] Clean up capabilities --- docs/protocol/draft/schema.mdx | 14 ++++---------- docs/rfds/next-edit-suggestions.mdx | 6 ++---- schema/schema.unstable.json | 22 ++++++++------------- src/agent.rs | 2 +- src/client.rs | 30 ++++++++++++++++++++--------- src/nes.rs | 30 ++++++++++++++--------------- 6 files changed, 51 insertions(+), 53 deletions(-) diff --git a/docs/protocol/draft/schema.mdx b/docs/protocol/draft/schema.mdx index ea2cb810..0ac5a0c2 100644 --- a/docs/protocol/draft/schema.mdx +++ b/docs/protocol/draft/schema.mdx @@ -2742,12 +2742,12 @@ This capability is not part of the spec yet, and may be removed or changed at an NES (Next Edit Suggestions) capabilities supported by the client. -PositionEncodingKind | null} > +PositionEncodingKind[]} > **UNSTABLE** This capability is not part of the spec yet, and may be removed or changed at any point. -The position encoding selected by the agent from the client's supported encodings. +The position encodings supported by the client, in order of preference. @@ -4467,14 +4467,8 @@ Capabilities for `document/didChange` events. - - TextDocumentSyncKind - - | null - - } + type={TextDocumentSyncKind} + required > The sync kind the agent wants: `"full"` or `"incremental"`. diff --git a/docs/rfds/next-edit-suggestions.mdx b/docs/rfds/next-edit-suggestions.mdx index cc7d06d2..4e78a38a 100644 --- a/docs/rfds/next-edit-suggestions.mdx +++ b/docs/rfds/next-edit-suggestions.mdx @@ -106,16 +106,14 @@ Three encoding kinds are supported: - `"utf-32"` — character offsets count Unicode code points. - `"utf-8"` — character offsets count UTF-8 code units (bytes). -**Negotiation:** The client declares which encodings it supports in the `initialize` request via `general.positionEncodings`, listed in order of preference. The agent selects one from the client's list and declares it in its `initialize` response as `positionEncoding`. If the client omits `positionEncodings`, or the agent omits `positionEncoding` in its response, both sides default to `"utf-16"`. +**Negotiation:** The client may declare the position encodings it supports in the `initialize` request via `clientCapabilities.positionEncodings`, listed in order of preference. The agent selects one from the client's list and declares it in its `initialize` response as `agentCapabilities.positionEncoding`. If the client omits `positionEncodings`, or the agent omits `positionEncoding` in its response, both sides default to `"utf-16"`. Client `initialize` request (excerpt): ```json { "clientCapabilities": { - "general": { - "positionEncodings": ["utf-32", "utf-16"] - } + "positionEncodings": ["utf-32", "utf-16"] } } ``` diff --git a/schema/schema.unstable.json b/schema/schema.unstable.json index f72a7652..1695cc4e 100644 --- a/schema/schema.unstable.json +++ b/schema/schema.unstable.json @@ -899,16 +899,12 @@ ], "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nNES (Next Edit Suggestions) capabilities supported by the client." }, - "positionEncoding": { - "anyOf": [ - { - "$ref": "#/$defs/PositionEncodingKind" - }, - { - "type": "null" - } - ], - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nThe position encoding selected by the agent from the client's supported encodings." + "positionEncodings": { + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nThe position encodings supported by the client, in order of preference.", + "items": { + "$ref": "#/$defs/PositionEncodingKind" + }, + "type": "array" }, "terminal": { "default": false, @@ -3438,17 +3434,15 @@ "type": ["object", "null"] }, "syncKind": { - "anyOf": [ + "allOf": [ { "$ref": "#/$defs/TextDocumentSyncKind" - }, - { - "type": "null" } ], "description": "The sync kind the agent wants: `\"full\"` or `\"incremental\"`." } }, + "required": ["syncKind"], "type": "object" }, "NesDocumentDidCloseCapabilities": { diff --git a/src/agent.rs b/src/agent.rs index acee2bd6..6af4d83e 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -3334,7 +3334,7 @@ impl AgentCapabilities { /// **UNSTABLE** /// - /// The position encoding selected by the agent. + /// The position encoding selected by the agent from the client's supported encodings. #[cfg(feature = "unstable_nes")] #[must_use] pub fn position_encoding( diff --git a/src/client.rs b/src/client.rs index bb0dfe29..0a34cfe3 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1515,10 +1515,11 @@ pub struct ClientCapabilities { /// /// This capability is not part of the spec yet, and may be removed or changed at any point. /// - /// The position encoding selected by the agent from the client's supported encodings. + /// The position encodings supported by the client, in order of preference. #[cfg(feature = "unstable_nes")] - #[serde(skip_serializing_if = "Option::is_none")] - pub position_encoding: Option, + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub position_encodings: Vec, + /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at /// these keys. @@ -1588,14 +1589,11 @@ impl ClientCapabilities { /// **UNSTABLE** /// - /// The position encoding selected by the agent. + /// The position encodings supported by the client, in order of preference. #[cfg(feature = "unstable_nes")] #[must_use] - pub fn position_encoding( - mut self, - position_encoding: impl IntoOption, - ) -> Self { - self.position_encoding = position_encoding.into_option(); + pub fn position_encodings(mut self, position_encodings: Vec) -> Self { + self.position_encodings = position_encodings; self } @@ -2062,4 +2060,18 @@ mod tests { json!({}) ); } + + #[cfg(feature = "unstable_nes")] + #[test] + fn test_client_capabilities_position_encodings_serialization() { + use serde_json::json; + + let capabilities = ClientCapabilities::new().position_encodings(vec![ + PositionEncodingKind::Utf32, + PositionEncodingKind::Utf16, + ]); + let json = serde_json::to_value(&capabilities).unwrap(); + + assert_eq!(json["positionEncodings"], json!(["utf-32", "utf-16"])); + } } diff --git a/src/nes.rs b/src/nes.rs index 3e54df73..2bc12884 100644 --- a/src/nes.rs +++ b/src/nes.rs @@ -288,13 +288,12 @@ pub struct NesDocumentDidOpenCapabilities { } /// Capabilities for `document/didChange` events. -#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct NesDocumentDidChangeCapabilities { /// The sync kind the agent wants: `"full"` or `"incremental"`. - #[serde(skip_serializing_if = "Option::is_none")] - pub sync_kind: Option, + pub sync_kind: TextDocumentSyncKind, /// The _meta property is reserved by ACP. #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, @@ -302,14 +301,11 @@ pub struct NesDocumentDidChangeCapabilities { impl NesDocumentDidChangeCapabilities { #[must_use] - pub fn new() -> Self { - Self::default() - } - - #[must_use] - pub fn sync_kind(mut self, sync_kind: impl IntoOption) -> Self { - self.sync_kind = sync_kind.into_option(); - self + pub fn new(sync_kind: TextDocumentSyncKind) -> Self { + Self { + sync_kind, + meta: None, + } } #[must_use] @@ -1672,10 +1668,9 @@ mod tests { NesEventCapabilities::new().document( NesDocumentEventCapabilities::new() .did_open(NesDocumentDidOpenCapabilities::default()) - .did_change( - NesDocumentDidChangeCapabilities::new() - .sync_kind(TextDocumentSyncKind::Incremental), - ) + .did_change(NesDocumentDidChangeCapabilities::new( + TextDocumentSyncKind::Incremental, + )) .did_close(NesDocumentDidCloseCapabilities::default()) .did_save(NesDocumentDidSaveCapabilities::default()) .did_focus(NesDocumentDidFocusCapabilities::default()), @@ -2130,6 +2125,11 @@ mod tests { ); } + #[test] + fn test_document_did_change_capabilities_requires_sync_kind() { + assert!(serde_json::from_value::(json!({})).is_err()); + } + #[test] fn test_nes_suggest_response_serialization() { let response = NesSuggestResponse::new(vec![ From adad43e2f198bf4b41f31cf1f9f8d6628306a347 Mon Sep 17 00:00:00 2001 From: "Anna.Zhdan" Date: Fri, 27 Mar 2026 13:42:19 +0100 Subject: [PATCH 07/13] fixed ide actions --- docs/rfds/next-edit-suggestions.mdx | 93 ++++++++------ src/nes.rs | 190 ++++++++++++++++++---------- 2 files changed, 173 insertions(+), 110 deletions(-) diff --git a/docs/rfds/next-edit-suggestions.mdx b/docs/rfds/next-edit-suggestions.mdx index 4e78a38a..2c6a357c 100644 --- a/docs/rfds/next-edit-suggestions.mdx +++ b/docs/rfds/next-edit-suggestions.mdx @@ -69,22 +69,21 @@ All fields under `events` and `context` are optional — an agent advertises onl #### Client capabilities -The **client** advertises its own NES-related capabilities in the `initialize` request. Currently, the client can declare which well-known IDE actions it supports by advertising them as keys under `clientCapabilities.nes.ideActions`. The agent reads these and may later include `"action"` kind suggestions that reference them. +The **client** advertises its own NES-related capabilities in the `initialize` request. The client declares which suggestion kinds it supports beyond the basic `edit` kind. Agents should only suggest kinds that the client has advertised. ```json { "clientCapabilities": { "nes": { - "ideActions": { - "rename": {}, - "searchAndReplace": {} - } + "jump": {}, + "rename": {}, + "searchAndReplace": {} } } } ``` -Each key in `ideActions` is the ID of a well-known action (see [Well-known IDE actions](#well-known-ide-actions) below). Agents should only suggest actions that the client has advertised. +Each entry corresponds to a suggestion kind (see [Suggestion kinds](#suggestion-kinds) below). If a kind is absent, the agent must not produce suggestions of that kind. ### Session lifecycle @@ -359,7 +358,7 @@ The client requests a suggestion by calling `nes/suggest`. Context fields are in ### Suggestion response -A suggestion is one of three kinds: an **edit** (text changes), a **jump** (navigate to a different file), or an **action** (trigger an IDE action). +A suggestion is one of several kinds, each identified by the `kind` field. The `edit` kind is always supported; other kinds (`jump`, `rename`, `searchAndReplace`) require the client to advertise support in its capabilities. **Edit suggestion:** @@ -408,7 +407,7 @@ A suggestion is one of three kinds: an **edit** (text changes), a **jump** (navi } ``` -**Action suggestion:** +**Rename suggestion:** ```json { @@ -418,33 +417,47 @@ A suggestion is one of three kinds: an **edit** (text changes), a **jump** (navi "suggestions": [ { "id": "sugg_003", - "kind": "action", - "actionId": "rename", - "arguments": { - "uri": "file:///path/to/file.rs", - "position": { "line": 5, "character": 10 }, - "newName": "calculateTotal" - } + "kind": "rename", + "uri": "file:///path/to/file.rs", + "position": { "line": 5, "character": 10 }, + "newName": "calculateTotal" } ] } } ``` -Action suggestions reference an IDE action that the client previously advertised in its capabilities: +**Search-and-replace suggestion:** -- `actionId` — matches an `id` from the client's advertised `ideActions`. -- `arguments` — matches the parameter schema declared by the client for that action. +```json +{ + "jsonrpc": "2.0", + "id": 42, + "result": { + "suggestions": [ + { + "id": "sugg_004", + "kind": "searchAndReplace", + "uri": "file:///path/to/file.rs", + "search": "oldFunction", + "replace": "newFunction", + "isRegex": false + } + ] + } +} +``` -A response may contain a mix of edit, jump, and action suggestions. The client decides how to present them (e.g. inline ghost text for edits, a navigation hint for jumps). +A response may contain a mix of suggestion kinds. The client decides how to present them (e.g. inline ghost text for edits, a navigation hint for jumps). Agents must only include suggestion kinds that the client has advertised in its capabilities (except `edit`, which is always supported). Each suggestion contains: - `id` — unique identifier for accept/reject tracking. -- `kind` — `"edit"`, `"jump"`, or `"action"`. +- `kind` — the suggestion kind (see [Suggestion kinds](#suggestion-kinds) below). Edit suggestions additionally contain: +- `uri` — the file to edit. - `edits` — one or more text edits to apply. - `cursorPosition` — optional suggested cursor position after applying edits. @@ -453,10 +466,18 @@ Jump suggestions additionally contain: - `uri` — the file to navigate to. - `position` — the target position within that file. -Action suggestions additionally contain: +Rename suggestions additionally contain: -- `actionId` — the IDE action to perform (must match a client-advertised action `id`). -- `arguments` — action parameters matching the schema from the client's capability. +- `uri` — the file URI containing the symbol. +- `position` — the position of the symbol to rename. +- `newName` — the new name for the symbol. + +Search-and-replace suggestions additionally contain: + +- `uri` — the file URI to search within. Can be a folder, then operation is performed in all the files in this folder. +- `search` — the text or pattern to find. +- `replace` — the replacement text. +- `isRegex` (`boolean`, optional) — whether `search` is a regular expression. Defaults to `false`. ### Accept / Reject @@ -554,28 +575,16 @@ The agent may reject `nes/start` with an error. In particular, agents that requi Clients **must** be prepared to handle `auth_required` errors on `nes/start`. The recommended behavior is to prompt the user to authenticate (e.g. sign in or provide credentials) and retry the `nes/start` call after authentication succeeds. Clients should not silently ignore this error or assume NES is unavailable — the agent may be fully functional once the user authenticates. -### Well-known IDE actions +### Suggestion kinds -The following actions are well-known and have standardized parameter schemas. Clients that support these actions should use the IDs and parameter shapes defined here. +The `kind` field in each suggestion identifies its type. The following kinds are defined: -**`rename`** — Rename a symbol across the workspace. - -Parameters: - -- `uri` (`string`) — the file URI containing the symbol. -- `position` (`Position`) — the position of the symbol to rename. -- `newName` (`string`) — the new name for the symbol. - -**`searchAndReplace`** — Search and replace text within a file. - -Parameters: - -- `uri` (`string`) — the file URI to search within. -- `search` (`string`) — the text or pattern to find. -- `replace` (`string`) — the replacement text. -- `isRegex` (`boolean`, optional) — whether `search` is a regular expression. Defaults to `false`. +- **`edit`** — A text edit suggestion. Always supported; does not require a client capability. +- **`jump`** — Navigate to a different file/position. Requires `jump` in client capabilities. +- **`rename`** — Rename a symbol across the workspace. Requires `rename` in client capabilities. +- **`searchAndReplace`** — Search and replace text within a file/folder. Requires `searchAndReplace` in client capabilities. -Additional well-known actions may be added to the protocol in the future. Agents should only suggest actions whose `id` matches an entry the client has advertised. +Additional suggestion kinds may be added to the protocol in the future. Agents must only produce suggestions whose `kind` the client has advertised (except `edit`, which is always supported). ### Config options diff --git a/src/nes.rs b/src/nes.rs index 2bc12884..66f80b81 100644 --- a/src/nes.rs +++ b/src/nes.rs @@ -522,9 +522,15 @@ pub struct NesDiagnosticsCapabilities { #[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct ClientNesCapabilities { - /// IDE actions the client supports. + /// Whether the client supports the `jump` suggestion kind. #[serde(skip_serializing_if = "Option::is_none")] - pub ide_actions: Option, + pub jump: Option, + /// Whether the client supports the `rename` suggestion kind. + #[serde(skip_serializing_if = "Option::is_none")] + pub rename: Option, + /// Whether the client supports the `searchAndReplace` suggestion kind. + #[serde(skip_serializing_if = "Option::is_none")] + pub search_and_replace: Option, /// The _meta property is reserved by ACP. #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, @@ -537,40 +543,11 @@ impl ClientNesCapabilities { } #[must_use] - pub fn ide_actions(mut self, ide_actions: impl IntoOption) -> Self { - self.ide_actions = ide_actions.into_option(); + pub fn jump(mut self, jump: impl IntoOption) -> Self { + self.jump = jump.into_option(); self } - #[must_use] - pub fn meta(mut self, meta: impl IntoOption) -> Self { - self.meta = meta.into_option(); - self - } -} - -/// IDE actions the client can perform. -#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] -#[serde(rename_all = "camelCase")] -#[non_exhaustive] -pub struct NesIdeActionsCapabilities { - /// Whether the client supports the `rename` action. - #[serde(skip_serializing_if = "Option::is_none")] - pub rename: Option, - /// Whether the client supports the `searchAndReplace` action. - #[serde(skip_serializing_if = "Option::is_none")] - pub search_and_replace: Option, - /// The _meta property is reserved by ACP. - #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] - pub meta: Option, -} - -impl NesIdeActionsCapabilities { - #[must_use] - pub fn new() -> Self { - Self::default() - } - #[must_use] pub fn rename(mut self, rename: impl IntoOption) -> Self { self.rename = rename.into_option(); @@ -593,6 +570,16 @@ impl NesIdeActionsCapabilities { } } +/// Marker for jump suggestion support. +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesJumpCapabilities { + /// The _meta property is reserved by ACP. + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + /// Marker for rename action support. #[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "camelCase")] @@ -1419,8 +1406,10 @@ pub enum NesSuggestion { Edit(NesEditSuggestion), /// A jump-to-location suggestion. Jump(NesJumpSuggestion), - /// An IDE action suggestion. - Action(NesActionSuggestion), + /// A rename symbol suggestion. + Rename(NesRenameActionSuggestion), + /// A search-and-replace suggestion. + SearchAndReplace(NesSearchAndReplaceActionSuggestion), } /// A text edit suggestion. @@ -1502,33 +1491,76 @@ impl NesJumpSuggestion { } } -/// An IDE action suggestion. +/// A rename symbol suggestion. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesRenameActionSuggestion { + /// Unique identifier for accept/reject tracking. + pub id: String, + /// The file URI containing the symbol. + pub uri: String, + /// The position of the symbol to rename. + pub position: Position, + /// The new name for the symbol. + pub new_name: String, +} + +impl NesRenameActionSuggestion { + #[must_use] + pub fn new( + id: impl Into, + uri: impl Into, + position: Position, + new_name: impl Into, + ) -> Self { + Self { + id: id.into(), + uri: uri.into(), + position, + new_name: new_name.into(), + } + } +} + +/// A search-and-replace suggestion. #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "camelCase")] #[non_exhaustive] -pub struct NesActionSuggestion { +pub struct NesSearchAndReplaceActionSuggestion { /// Unique identifier for accept/reject tracking. pub id: String, - /// The IDE action to perform (must match a client-advertised action). - pub action_id: String, - /// Action parameters matching the schema from the client's capability. + /// The file URI to search within. + pub uri: String, + /// The text or pattern to find. + pub search: String, + /// The replacement text. + pub replace: String, + /// Whether `search` is a regular expression. Defaults to `false`. #[serde(skip_serializing_if = "Option::is_none")] - pub arguments: Option, + pub is_regex: Option, } -impl NesActionSuggestion { +impl NesSearchAndReplaceActionSuggestion { #[must_use] - pub fn new(id: impl Into, action_id: impl Into) -> Self { + pub fn new( + id: impl Into, + uri: impl Into, + search: impl Into, + replace: impl Into, + ) -> Self { Self { id: id.into(), - action_id: action_id.into(), - arguments: None, + uri: uri.into(), + search: search.into(), + replace: replace.into(), + is_regex: None, } } #[must_use] - pub fn arguments(mut self, arguments: impl IntoOption) -> Self { - self.arguments = arguments.into_option(); + pub fn is_regex(mut self, is_regex: impl IntoOption) -> Self { + self.is_regex = is_regex.into_option(); self } } @@ -1734,20 +1766,18 @@ mod tests { #[test] fn test_client_nes_capabilities_serialization() { - let caps = ClientNesCapabilities::new().ide_actions( - NesIdeActionsCapabilities::new() - .rename(NesRenameActionCapabilities::default()) - .search_and_replace(NesSearchAndReplaceActionCapabilities::default()), - ); + let caps = ClientNesCapabilities::new() + .jump(NesJumpCapabilities::default()) + .rename(NesRenameActionCapabilities::default()) + .search_and_replace(NesSearchAndReplaceActionCapabilities::default()); let json = serde_json::to_value(&caps).unwrap(); assert_eq!( json, json!({ - "ideActions": { - "rename": {}, - "searchAndReplace": {} - } + "jump": {}, + "rename": {}, + "searchAndReplace": {} }) ); @@ -1950,27 +1980,51 @@ mod tests { } #[test] - fn test_nes_suggestion_action_serialization() { - let suggestion = NesSuggestion::Action( - NesActionSuggestion::new("sugg_003", "rename").arguments(json!({ + fn test_nes_suggestion_rename_action_serialization() { + let suggestion = NesSuggestion::Rename(NesRenameActionSuggestion::new( + "sugg_003", + "file:///path/to/file.rs", + Position::new(5, 10), + "calculateTotal", + )); + + let json = serde_json::to_value(&suggestion).unwrap(); + assert_eq!( + json, + json!({ + "kind": "rename", + "id": "sugg_003", "uri": "file:///path/to/file.rs", "position": { "line": 5, "character": 10 }, "newName": "calculateTotal" - })), + }) ); + let deserialized: NesSuggestion = serde_json::from_value(json).unwrap(); + assert_eq!(deserialized, suggestion); + } + + #[test] + fn test_nes_suggestion_search_and_replace_action_serialization() { + let suggestion = + NesSuggestion::SearchAndReplace(NesSearchAndReplaceActionSuggestion::new( + "sugg_004", + "file:///path/to/file.rs", + "oldFunction", + "newFunction", + ) + .is_regex(false)); + let json = serde_json::to_value(&suggestion).unwrap(); assert_eq!( json, json!({ - "kind": "action", - "id": "sugg_003", - "actionId": "rename", - "arguments": { - "uri": "file:///path/to/file.rs", - "position": { "line": 5, "character": 10 }, - "newName": "calculateTotal" - } + "kind": "searchAndReplace", + "id": "sugg_004", + "uri": "file:///path/to/file.rs", + "search": "oldFunction", + "replace": "newFunction", + "isRegex": false }) ); From b999f13b7da2f4083eb6f3e556ce58ba77aa83f8 Mon Sep 17 00:00:00 2001 From: "Anna.Zhdan" Date: Fri, 27 Mar 2026 13:46:52 +0100 Subject: [PATCH 08/13] fixed ide actions --- docs/protocol/draft/schema.mdx | 173 ++++++++++++++++++++++----------- schema/schema.unstable.json | 148 +++++++++++++++++++--------- src/nes.rs | 7 +- 3 files changed, 219 insertions(+), 109 deletions(-) diff --git a/docs/protocol/draft/schema.mdx b/docs/protocol/draft/schema.mdx index 0ac5a0c2..1d1ad188 100644 --- a/docs/protocol/draft/schema.mdx +++ b/docs/protocol/draft/schema.mdx @@ -2769,17 +2769,45 @@ NES capabilities advertised by the client during initialization. The _meta property is reserved by ACP. - NesIdeActionsCapabilities + NesJumpCapabilities | null } > - IDE actions the client supports. + Whether the client supports the `jump` suggestion kind. + + + + NesRenameActionCapabilities + + | null + + } +> + Whether the client supports the `rename` suggestion kind. + + + + + NesSearchAndReplaceActionCapabilities + + + | null + + } +> + Whether the client supports the `searchAndReplace` suggestion kind. ## ConfigOptionUpdate @@ -4246,24 +4274,6 @@ Schema for multi-select (array) properties in an elicitation form. Optional title for the property. -## NesActionSuggestion - -An IDE action suggestion. - -**Type:** Object - -**Properties:** - - - The IDE action to perform (must match a client-advertised action). - - - Action parameters matching the schema from the client's capability. - - - Unique identifier for accept/reject tracking. - - ## NesCapabilities NES capabilities advertised by the agent during initialization. @@ -4725,9 +4735,9 @@ A code excerpt from a file. The text content of the excerpt. -## NesIdeActionsCapabilities +## NesJumpCapabilities -IDE actions the client can perform. +Marker for jump suggestion support. **Type:** Object @@ -4736,34 +4746,6 @@ IDE actions the client can perform. The _meta property is reserved by ACP. - - - NesRenameActionCapabilities - - | null - - } -> - Whether the client supports the `rename` action. - - - - - NesSearchAndReplaceActionCapabilities - - - | null - - } -> - Whether the client supports the `searchAndReplace` action. - ## NesJumpSuggestion @@ -4920,6 +4902,27 @@ Marker for rename action support. The _meta property is reserved by ACP. +## NesRenameActionSuggestion + +A rename symbol suggestion. + +**Type:** Object + +**Properties:** + + + Unique identifier for accept/reject tracking. + + + The new name for the symbol. + +Position} required> + The position of the symbol to rename. + + + The file URI containing the symbol. + + ## NesRepository Repository metadata for an NES session. @@ -4950,6 +4953,30 @@ Marker for search and replace action support. The _meta property is reserved by ACP. +## NesSearchAndReplaceActionSuggestion + +A search-and-replace suggestion. + +**Type:** Object + +**Properties:** + + + Unique identifier for accept/reject tracking. + + + Whether `search` is a regular expression. Defaults to `false`. + + + The replacement text. + + + The text or pattern to find. + + + The file URI to search within. + + ## NesSuggestContext Context attached to a suggestion request. @@ -5105,22 +5132,52 @@ A jump-to-location suggestion. - -An IDE action suggestion. + +A rename symbol suggestion. - - The IDE action to perform (must match a client-advertised action). + + Unique identifier for accept/reject tracking. + + + The discriminator value. Must be `"rename"`. - - Action parameters matching the schema from the client's capability. + + The new name for the symbol. +Position} required> + The position of the symbol to rename. + + + The file URI containing the symbol. + + + + + + +A search-and-replace suggestion. + + + Unique identifier for accept/reject tracking. + + Whether `search` is a regular expression. Defaults to `false`. + - The discriminator value. Must be `"action"`. + The discriminator value. Must be `"searchAndReplace"`. + + + The replacement text. + + + The text or pattern to find. + + + The file URI to search within. diff --git a/schema/schema.unstable.json b/schema/schema.unstable.json index 1695cc4e..0f75a717 100644 --- a/schema/schema.unstable.json +++ b/schema/schema.unstable.json @@ -922,16 +922,38 @@ "description": "The _meta property is reserved by ACP.", "type": ["object", "null"] }, - "ideActions": { + "jump": { "anyOf": [ { - "$ref": "#/$defs/NesIdeActionsCapabilities" + "$ref": "#/$defs/NesJumpCapabilities" }, { "type": "null" } ], - "description": "IDE actions the client supports." + "description": "Whether the client supports the `jump` suggestion kind." + }, + "rename": { + "anyOf": [ + { + "$ref": "#/$defs/NesRenameActionCapabilities" + }, + { + "type": "null" + } + ], + "description": "Whether the client supports the `rename` suggestion kind." + }, + "searchAndReplace": { + "anyOf": [ + { + "$ref": "#/$defs/NesSearchAndReplaceActionCapabilities" + }, + { + "type": "null" + } + ], + "description": "Whether the client supports the `searchAndReplace` suggestion kind." } }, "type": "object" @@ -3230,24 +3252,6 @@ "x-method": "nes/accept", "x-side": "agent" }, - "NesActionSuggestion": { - "description": "An IDE action suggestion.", - "properties": { - "actionId": { - "description": "The IDE action to perform (must match a client-advertised action).", - "type": "string" - }, - "arguments": { - "description": "Action parameters matching the schema from the client's capability." - }, - "id": { - "description": "Unique identifier for accept/reject tracking.", - "type": "string" - } - }, - "required": ["id", "actionId"], - "type": "object" - }, "NesCapabilities": { "description": "NES capabilities advertised by the agent during initialization.", "properties": { @@ -3665,35 +3669,13 @@ "required": ["startLine", "endLine", "text"], "type": "object" }, - "NesIdeActionsCapabilities": { - "description": "IDE actions the client can perform.", + "NesJumpCapabilities": { + "description": "Marker for jump suggestion support.", "properties": { "_meta": { "additionalProperties": true, "description": "The _meta property is reserved by ACP.", "type": ["object", "null"] - }, - "rename": { - "anyOf": [ - { - "$ref": "#/$defs/NesRenameActionCapabilities" - }, - { - "type": "null" - } - ], - "description": "Whether the client supports the `rename` action." - }, - "searchAndReplace": { - "anyOf": [ - { - "$ref": "#/$defs/NesSearchAndReplaceActionCapabilities" - }, - { - "type": "null" - } - ], - "description": "Whether the client supports the `searchAndReplace` action." } }, "type": "object" @@ -3902,6 +3884,33 @@ }, "type": "object" }, + "NesRenameActionSuggestion": { + "description": "A rename symbol suggestion.", + "properties": { + "id": { + "description": "Unique identifier for accept/reject tracking.", + "type": "string" + }, + "newName": { + "description": "The new name for the symbol.", + "type": "string" + }, + "position": { + "allOf": [ + { + "$ref": "#/$defs/Position" + } + ], + "description": "The position of the symbol to rename." + }, + "uri": { + "description": "The file URI containing the symbol.", + "type": "string" + } + }, + "required": ["id", "uri", "position", "newName"], + "type": "object" + }, "NesRepository": { "description": "Repository metadata for an NES session.", "properties": { @@ -3932,6 +3941,33 @@ }, "type": "object" }, + "NesSearchAndReplaceActionSuggestion": { + "description": "A search-and-replace suggestion.", + "properties": { + "id": { + "description": "Unique identifier for accept/reject tracking.", + "type": "string" + }, + "isRegex": { + "description": "Whether `search` is a regular expression. Defaults to `false`.", + "type": ["boolean", "null"] + }, + "replace": { + "description": "The replacement text.", + "type": "string" + }, + "search": { + "description": "The text or pattern to find.", + "type": "string" + }, + "uri": { + "description": "The file URI to search within.", + "type": "string" + } + }, + "required": ["id", "uri", "search", "replace"], + "type": "object" + }, "NesStartRequest": { "description": "Request to start an NES session.", "properties": { @@ -4170,13 +4206,29 @@ { "allOf": [ { - "$ref": "#/$defs/NesActionSuggestion" + "$ref": "#/$defs/NesRenameActionSuggestion" + } + ], + "description": "A rename symbol suggestion.", + "properties": { + "kind": { + "const": "rename", + "type": "string" + } + }, + "required": ["kind"], + "type": "object" + }, + { + "allOf": [ + { + "$ref": "#/$defs/NesSearchAndReplaceActionSuggestion" } ], - "description": "An IDE action suggestion.", + "description": "A search-and-replace suggestion.", "properties": { "kind": { - "const": "action", + "const": "searchAndReplace", "type": "string" } }, diff --git a/src/nes.rs b/src/nes.rs index 66f80b81..b8ee2260 100644 --- a/src/nes.rs +++ b/src/nes.rs @@ -2006,14 +2006,15 @@ mod tests { #[test] fn test_nes_suggestion_search_and_replace_action_serialization() { - let suggestion = - NesSuggestion::SearchAndReplace(NesSearchAndReplaceActionSuggestion::new( + let suggestion = NesSuggestion::SearchAndReplace( + NesSearchAndReplaceActionSuggestion::new( "sugg_004", "file:///path/to/file.rs", "oldFunction", "newFunction", ) - .is_regex(false)); + .is_regex(false), + ); let json = serde_json::to_value(&suggestion).unwrap(); assert_eq!( From 026d019a2b586c08f70cb58c55e6f7df7760c759 Mon Sep 17 00:00:00 2001 From: "Anna.Zhdan" Date: Fri, 27 Mar 2026 13:51:56 +0100 Subject: [PATCH 09/13] replace line\offset with position --- docs/protocol/draft/schema.mdx | 13 ++----------- docs/rfds/next-edit-suggestions.mdx | 6 ++---- schema/schema.unstable.json | 20 ++++++++------------ src/nes.rs | 12 ++++-------- 4 files changed, 16 insertions(+), 35 deletions(-) diff --git a/docs/protocol/draft/schema.mdx b/docs/protocol/draft/schema.mdx index 1d1ad188..670c783a 100644 --- a/docs/protocol/draft/schema.mdx +++ b/docs/protocol/draft/schema.mdx @@ -5227,17 +5227,8 @@ A user action (typing, cursor movement, etc.). The kind of action (e.g., "insertChar", "cursorMovement"). - - The line number where the action occurred. - - - Minimum: `0` - - - - The character offset where the action occurred. - - - Minimum: `0` - +Position} required> + The position where the action occurred. Timestamp in milliseconds since epoch. diff --git a/docs/rfds/next-edit-suggestions.mdx b/docs/rfds/next-edit-suggestions.mdx index 2c6a357c..8c5eb273 100644 --- a/docs/rfds/next-edit-suggestions.mdx +++ b/docs/rfds/next-edit-suggestions.mdx @@ -303,15 +303,13 @@ The client requests a suggestion by calling `nes/suggest`. Context fields are in { "action": "insertChar", "uri": "file:///path/to/file.rs", - "line": 5, - "offset": 12, + "position": { "line": 5, "character": 12 }, "timestampMs": 1719400000000 }, { "action": "cursorMovement", "uri": "file:///path/to/file.rs", - "line": 10, - "offset": 0, + "position": { "line": 10, "character": 0 }, "timestampMs": 1719400001200 } ], diff --git a/schema/schema.unstable.json b/schema/schema.unstable.json index 0f75a717..bb198c99 100644 --- a/schema/schema.unstable.json +++ b/schema/schema.unstable.json @@ -4283,17 +4283,13 @@ "description": "The kind of action (e.g., \"insertChar\", \"cursorMovement\").", "type": "string" }, - "line": { - "description": "The line number where the action occurred.", - "format": "uint32", - "minimum": 0, - "type": "integer" - }, - "offset": { - "description": "The character offset where the action occurred.", - "format": "uint32", - "minimum": 0, - "type": "integer" + "position": { + "allOf": [ + { + "$ref": "#/$defs/Position" + } + ], + "description": "The position where the action occurred." }, "timestampMs": { "description": "Timestamp in milliseconds since epoch.", @@ -4306,7 +4302,7 @@ "type": "string" } }, - "required": ["action", "uri", "line", "offset", "timestampMs"], + "required": ["action", "uri", "position", "timestampMs"], "type": "object" }, "NesUserActionsCapabilities": { diff --git a/src/nes.rs b/src/nes.rs index b8ee2260..474b0c05 100644 --- a/src/nes.rs +++ b/src/nes.rs @@ -1248,10 +1248,8 @@ pub struct NesUserAction { pub action: String, /// The URI of the file where the action occurred. pub uri: String, - /// The line number where the action occurred. - pub line: u32, - /// The character offset where the action occurred. - pub offset: u32, + /// The position where the action occurred. + pub position: Position, /// Timestamp in milliseconds since epoch. pub timestamp_ms: u64, } @@ -1261,15 +1259,13 @@ impl NesUserAction { pub fn new( action: impl Into, uri: impl Into, - line: u32, - offset: u32, + position: Position, timestamp_ms: u64, ) -> Self { Self { action: action.into(), uri: uri.into(), - line, - offset, + position, timestamp_ms, } } From 376da050424058be5dbc524e528d91acc6dd0967 Mon Sep 17 00:00:00 2001 From: "Anna.Zhdan" Date: Fri, 27 Mar 2026 14:37:22 +0100 Subject: [PATCH 10/13] added nes/close method --- docs/protocol/draft/schema.mdx | 52 ++++++++++++++++++++++ schema/meta.unstable.json | 1 + schema/schema.unstable.json | 52 ++++++++++++++++++++++ src/agent.rs | 26 +++++++++-- src/bin/generate.rs | 1 + src/nes.rs | 81 ++++++++++++++++++++++++++++++++++ src/rpc.rs | 4 ++ 7 files changed, 214 insertions(+), 3 deletions(-) diff --git a/docs/protocol/draft/schema.mdx b/docs/protocol/draft/schema.mdx index 670c783a..8dc97309 100644 --- a/docs/protocol/draft/schema.mdx +++ b/docs/protocol/draft/schema.mdx @@ -419,6 +419,58 @@ Notification sent when a suggestion is accepted. The session ID for this notification. + +### nes/close + +**UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +Closes an active NES session and frees up any resources associated with it. + +The agent must cancel any ongoing work and then free up any resources +associated with the NES session. + +#### NesCloseRequest + +Request to close an NES session. + +The agent **must** cancel any ongoing work related to the NES session +and then free up any resources associated with the session. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + + +SessionId} required> + The ID of the NES session to close. + + +#### NesCloseResponse + +Response from closing an NES session. + +**Type:** Object + +**Properties:** + + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + + + ### nes/reject diff --git a/schema/meta.unstable.json b/schema/meta.unstable.json index 3c00e464..167bba2f 100644 --- a/schema/meta.unstable.json +++ b/schema/meta.unstable.json @@ -9,6 +9,7 @@ "initialize": "initialize", "logout": "logout", "nes_accept": "nes/accept", + "nes_close": "nes/close", "nes_reject": "nes/reject", "nes_start": "nes/start", "nes_suggest": "nes/suggest", diff --git a/schema/schema.unstable.json b/schema/schema.unstable.json index bb198c99..49088669 100644 --- a/schema/schema.unstable.json +++ b/schema/schema.unstable.json @@ -395,6 +395,14 @@ ], "title": "NesSuggestResponse" }, + { + "allOf": [ + { + "$ref": "#/$defs/NesCloseResponse" + } + ], + "title": "NesCloseResponse" + }, { "allOf": [ { @@ -1208,6 +1216,15 @@ "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nRequests a code suggestion.", "title": "NesSuggestRequest" }, + { + "allOf": [ + { + "$ref": "#/$defs/NesCloseRequest" + } + ], + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nCloses an active NES session and frees up any resources associated with it.\n\nThe agent must cancel any ongoing work and then free up any resources\nassociated with the NES session.", + "title": "NesCloseRequest" + }, { "allOf": [ { @@ -3285,6 +3302,41 @@ }, "type": "object" }, + "NesCloseRequest": { + "description": "Request to close an NES session.\n\nThe agent **must** cancel any ongoing work related to the NES session\nand then free up any resources associated with the session.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", + "type": ["object", "null"] + }, + "sessionId": { + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], + "description": "The ID of the NES session to close." + } + }, + "required": ["sessionId"], + "type": "object", + "x-method": "nes/close", + "x-side": "agent" + }, + "NesCloseResponse": { + "description": "Response from closing an NES session.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", + "type": ["object", "null"] + } + }, + "type": "object", + "x-method": "nes/close", + "x-side": "agent" + }, "NesContextCapabilities": { "description": "Context capabilities the agent wants attached to each suggestion request.", "properties": { diff --git a/src/agent.rs b/src/agent.rs index 6af4d83e..d51a68f7 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -21,8 +21,8 @@ use crate::{ use crate::{ DocumentDidChangeNotification, DocumentDidCloseNotification, DocumentDidFocusNotification, DocumentDidOpenNotification, DocumentDidSaveNotification, NesAcceptNotification, - NesCapabilities, NesRejectNotification, NesStartRequest, NesStartResponse, NesSuggestRequest, - NesSuggestResponse, PositionEncodingKind, + NesCapabilities, NesCloseRequest, NesCloseResponse, NesRejectNotification, NesStartRequest, + NesStartResponse, NesSuggestRequest, NesSuggestResponse, PositionEncodingKind, }; // Initialize @@ -3787,6 +3787,9 @@ pub struct AgentMethodNames { /// Notification for rejecting a suggestion. #[cfg(feature = "unstable_nes")] pub nes_reject: &'static str, + /// Method for closing an NES session. + #[cfg(feature = "unstable_nes")] + pub nes_close: &'static str, /// Notification for document open events. #[cfg(feature = "unstable_nes")] pub document_did_open: &'static str, @@ -3834,6 +3837,8 @@ pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames { #[cfg(feature = "unstable_nes")] nes_reject: NES_REJECT_METHOD_NAME, #[cfg(feature = "unstable_nes")] + nes_close: NES_CLOSE_METHOD_NAME, + #[cfg(feature = "unstable_nes")] document_did_open: DOCUMENT_DID_OPEN_METHOD_NAME, #[cfg(feature = "unstable_nes")] document_did_change: DOCUMENT_DID_CHANGE_METHOD_NAME, @@ -3849,7 +3854,8 @@ pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames { use crate::nes::{ DOCUMENT_DID_CHANGE_METHOD_NAME, DOCUMENT_DID_CLOSE_METHOD_NAME, DOCUMENT_DID_FOCUS_METHOD_NAME, DOCUMENT_DID_OPEN_METHOD_NAME, DOCUMENT_DID_SAVE_METHOD_NAME, - NES_ACCEPT_METHOD_NAME, NES_REJECT_METHOD_NAME, NES_START_METHOD_NAME, NES_SUGGEST_METHOD_NAME, + NES_ACCEPT_METHOD_NAME, NES_CLOSE_METHOD_NAME, NES_REJECT_METHOD_NAME, NES_START_METHOD_NAME, + NES_SUGGEST_METHOD_NAME, }; /// Method name for the initialize request. @@ -4045,6 +4051,16 @@ pub enum ClientRequest { /// /// Requests a code suggestion. NesSuggestRequest(NesSuggestRequest), + #[cfg(feature = "unstable_nes")] + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// Closes an active NES session and frees up any resources associated with it. + /// + /// The agent must cancel any ongoing work and then free up any resources + /// associated with the NES session. + NesCloseRequest(NesCloseRequest), /// Handles extension method requests from the client. /// /// Extension methods provide a way to add custom functionality while maintaining @@ -4081,6 +4097,8 @@ impl ClientRequest { Self::NesStartRequest(_) => AGENT_METHOD_NAMES.nes_start, #[cfg(feature = "unstable_nes")] Self::NesSuggestRequest(_) => AGENT_METHOD_NAMES.nes_suggest, + #[cfg(feature = "unstable_nes")] + Self::NesCloseRequest(_) => AGENT_METHOD_NAMES.nes_close, Self::ExtMethodRequest(ext_request) => &ext_request.method, } } @@ -4120,6 +4138,8 @@ pub enum AgentResponse { NesStartResponse(NesStartResponse), #[cfg(feature = "unstable_nes")] NesSuggestResponse(NesSuggestResponse), + #[cfg(feature = "unstable_nes")] + NesCloseResponse(#[serde(default)] NesCloseResponse), ExtMethodResponse(ExtResponse), } diff --git a/src/bin/generate.rs b/src/bin/generate.rs index ddfdc034..859b0f5d 100644 --- a/src/bin/generate.rs +++ b/src/bin/generate.rs @@ -1034,6 +1034,7 @@ starting with '$/' it is free to ignore the notification." "logout" => self.agent.get("LogoutRequest").unwrap(), "nes/start" => self.agent.get("NesStartRequest").unwrap(), "nes/suggest" => self.agent.get("NesSuggestRequest").unwrap(), + "nes/close" => self.agent.get("NesCloseRequest").unwrap(), "nes/accept" => self.agent.get("NesAcceptNotification").unwrap(), "nes/reject" => self.agent.get("NesRejectNotification").unwrap(), "document/didOpen" => self.agent.get("DocumentDidOpenNotification").unwrap(), diff --git a/src/nes.rs b/src/nes.rs index 474b0c05..a6ba01a0 100644 --- a/src/nes.rs +++ b/src/nes.rs @@ -19,6 +19,8 @@ pub(crate) const NES_SUGGEST_METHOD_NAME: &str = "nes/suggest"; pub(crate) const NES_ACCEPT_METHOD_NAME: &str = "nes/accept"; /// Method name for rejecting a suggestion. pub(crate) const NES_REJECT_METHOD_NAME: &str = "nes/reject"; +/// Method name for closing an NES session. +pub(crate) const NES_CLOSE_METHOD_NAME: &str = "nes/close"; /// Notification name for document open events. pub(crate) const DOCUMENT_DID_OPEN_METHOD_NAME: &str = "document/didOpen"; /// Notification name for document change events. @@ -42,6 +44,8 @@ pub struct NesMethodNames { pub nes_accept: &'static str, /// Notification for rejecting a suggestion. pub nes_reject: &'static str, + /// Method for closing an NES session. + pub nes_close: &'static str, /// Notification for document open events. pub document_did_open: &'static str, /// Notification for document change events. @@ -60,6 +64,7 @@ pub const NES_METHOD_NAMES: NesMethodNames = NesMethodNames { nes_suggest: NES_SUGGEST_METHOD_NAME, nes_accept: NES_ACCEPT_METHOD_NAME, nes_reject: NES_REJECT_METHOD_NAME, + nes_close: NES_CLOSE_METHOD_NAME, document_did_open: DOCUMENT_DID_OPEN_METHOD_NAME, document_did_change: DOCUMENT_DID_CHANGE_METHOD_NAME, document_did_close: DOCUMENT_DID_CLOSE_METHOD_NAME, @@ -981,6 +986,82 @@ impl NesStartResponse { } } +// NES session close + +/// Request to close an NES session. +/// +/// The agent **must** cancel any ongoing work related to the NES session +/// and then free up any resources associated with the session. +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[schemars(extend("x-side" = "agent", "x-method" = NES_CLOSE_METHOD_NAME))] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesCloseRequest { + /// The ID of the NES session to close. + pub session_id: SessionId, + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +impl NesCloseRequest { + #[must_use] + pub fn new(session_id: impl Into) -> Self { + Self { + session_id: session_id.into(), + meta: None, + } + } + + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + +/// Response from closing an NES session. +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[schemars(extend("x-side" = "agent", "x-method" = NES_CLOSE_METHOD_NAME))] +#[serde(rename_all = "camelCase")] +#[non_exhaustive] +pub struct NesCloseResponse { + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] + pub meta: Option, +} + +impl NesCloseResponse { + #[must_use] + pub fn new() -> Self { + Self::default() + } + + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + #[must_use] + pub fn meta(mut self, meta: impl IntoOption) -> Self { + self.meta = meta.into_option(); + self + } +} + // NES suggest request /// What triggered the suggestion request. diff --git a/src/rpc.rs b/src/rpc.rs index 9fd45700..47c4ec1e 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -335,6 +335,10 @@ impl Side for AgentSide { m if m == NES_METHOD_NAMES.nes_suggest => serde_json::from_str(params.get()) .map(ClientRequest::NesSuggestRequest) .map_err(Into::into), + #[cfg(feature = "unstable_nes")] + m if m == NES_METHOD_NAMES.nes_close => serde_json::from_str(params.get()) + .map(ClientRequest::NesCloseRequest) + .map_err(Into::into), _ => { if let Some(custom_method) = method.strip_prefix('_') { Ok(ClientRequest::ExtMethodRequest(ExtRequest { From cbabf93c17e7a769cc08ff44ceb9e8e8564fb32b Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Sat, 28 Mar 2026 07:30:14 +0100 Subject: [PATCH 11/13] Final final --- docs/protocol/draft/schema.mdx | 724 ++++++++++++--------------------- schema/schema.unstable.json | 572 +++++++++++++------------- src/agent.rs | 50 +-- src/bin/generate.rs | 10 +- src/nes.rs | 371 ++++++++++++----- src/rpc.rs | 41 +- 6 files changed, 876 insertions(+), 892 deletions(-) diff --git a/docs/protocol/draft/schema.mdx b/docs/protocol/draft/schema.mdx index 8dc97309..8eb2f2ac 100644 --- a/docs/protocol/draft/schema.mdx +++ b/docs/protocol/draft/schema.mdx @@ -77,25 +77,18 @@ Notification sent when a file is edited. **Properties:** - - The _meta property is reserved by ACP. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + - - TextDocumentContentChangeEvent[] - - } - required -> +TextDocumentContentChangeEvent[]} required> The content changes. -SessionId} - required -> +SessionId} required> The session ID for this notification. @@ -120,14 +113,15 @@ Notification sent when a file is closed. **Properties:** - - The _meta property is reserved by ACP. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + -SessionId} - required -> +SessionId} required> The session ID for this notification. @@ -149,17 +143,18 @@ Notification sent when a file becomes the active editor tab. **Properties:** - - The _meta property is reserved by ACP. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + Position} required> The current cursor position. -SessionId} - required -> +SessionId} required> The session ID for this notification. @@ -187,17 +182,18 @@ Notification sent when a file is opened in the editor. **Properties:** - - The _meta property is reserved by ACP. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + The language identifier of the document (e.g., "rust", "python"). -SessionId} - required -> +SessionId} required> The session ID for this notification. @@ -225,14 +221,15 @@ Notification sent when a file is saved. **Properties:** - - The _meta property is reserved by ACP. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + -SessionId} - required -> +SessionId} required> The session ID for this notification. @@ -397,7 +394,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte Notification sent when a suggestion is accepted. -#### NesAcceptNotification +#### AcceptNesNotification Notification sent when a suggestion is accepted. @@ -405,17 +402,18 @@ Notification sent when a suggestion is accepted. **Properties:** - - The _meta property is reserved by ACP. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + The ID of the accepted suggestion. -SessionId} - required -> +SessionId} required> The session ID for this notification. @@ -431,7 +429,7 @@ Closes an active NES session and frees up any resources associated with it. The agent must cancel any ongoing work and then free up any resources associated with the NES session. -#### NesCloseRequest +#### CloseNesRequest Request to close an NES session. @@ -454,7 +452,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte The ID of the NES session to close. -#### NesCloseResponse +#### CloseNesResponse Response from closing an NES session. @@ -478,7 +476,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte Notification sent when a suggestion is rejected. -#### NesRejectNotification +#### RejectNesNotification Notification sent when a suggestion is rejected. @@ -486,30 +484,21 @@ Notification sent when a suggestion is rejected. **Properties:** - - The _meta property is reserved by ACP. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + The ID of the rejected suggestion. - - - NesRejectReason - - | null - - } -> +NesRejectReason | null} > The reason for rejection. -SessionId} - required -> +SessionId} required> The session ID for this notification. @@ -522,7 +511,7 @@ This capability is not part of the spec yet, and may be removed or changed at an Starts an NES session. -#### NesStartRequest +#### StartNesRequest Request to start an NES session. @@ -530,40 +519,25 @@ Request to start an NES session. **Properties:** - - The _meta property is reserved by ACP. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + - - - NesRepository - - | null - - } -> +NesRepository | null} > Repository metadata, if the workspace is a git repository. - - - WorkspaceFolder[] - - | null - - } -> +WorkspaceFolder[] | null} > The workspace folders. - + The root URI of the workspace. -#### NesStartResponse +#### StartNesResponse Response to `nes/start`. @@ -571,14 +545,15 @@ Response to `nes/start`. **Properties:** - - The _meta property is reserved by ACP. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + -SessionId} - required -> +SessionId} required> The session ID for the newly started NES session. @@ -591,7 +566,7 @@ This capability is not part of the spec yet, and may be removed or changed at an Requests a code suggestion. -#### NesSuggestRequest +#### SuggestNesRequest Request for a code suggestion. @@ -599,50 +574,27 @@ Request for a code suggestion. **Properties:** - - The _meta property is reserved by ACP. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + - - - NesSuggestContext - - | null - - } -> +NesSuggestContext | null} > Context for the suggestion, included based on agent capabilities. Position} required> The current cursor position. - - - Range - - | null - - } -> +Range | null} > The current text selection range, if any. -SessionId} - required -> +SessionId} required> The session ID for this request. -NesTriggerKind} - required -> +NesTriggerKind} required> What triggered this suggestion request. @@ -652,7 +604,7 @@ Request for a code suggestion. The version number of the document. -#### NesSuggestResponse +#### SuggestNesResponse Response to `nes/suggest`. @@ -660,14 +612,15 @@ Response to `nes/suggest`. **Properties:** - - The _meta property is reserved by ACP. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + -NesSuggestion[]} - required -> +NesSuggestion[]} required> The list of suggestions. @@ -2817,48 +2770,21 @@ NES capabilities advertised by the client during initialization. **Properties:** - - The _meta property is reserved by ACP. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + - - - NesJumpCapabilities - - | null - - } -> +NesJumpCapabilities | null} > Whether the client supports the `jump` suggestion kind. - - - NesRenameActionCapabilities - - | null - - } -> +NesRenameActionCapabilities | null} > Whether the client supports the `rename` suggestion kind. - - - - NesSearchAndReplaceActionCapabilities - - - | null - - } -> +NesSearchAndReplaceActionCapabilities | null} > Whether the client supports the `searchAndReplace` suggestion kind. @@ -4334,34 +4260,18 @@ NES capabilities advertised by the agent during initialization. **Properties:** - - The _meta property is reserved by ACP to allow clients and agents to attach - additional metadata to their interactions. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + - - - NesContextCapabilities - - | null - - } -> +NesContextCapabilities | null} > Context the agent wants attached to each suggestion request. - - - NesEventCapabilities - - | null - - } -> +NesEventCapabilities | null} > Events the agent wants to receive. @@ -4373,87 +4283,30 @@ Context capabilities the agent wants attached to each suggestion request. **Properties:** - - The _meta property is reserved by ACP. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + - - - NesDiagnosticsCapabilities - - | null - - } -> +NesDiagnosticsCapabilities | null} > Whether the agent wants diagnostics context. - - - NesEditHistoryCapabilities - - | null - - } -> +NesEditHistoryCapabilities | null} > Whether the agent wants edit history context. - - - NesOpenFilesCapabilities - - | null - - } -> +NesOpenFilesCapabilities | null} > Whether the agent wants open files context. - - - NesRecentFilesCapabilities - - | null - - } -> +NesRecentFilesCapabilities | null} > Whether the agent wants recent files context. - - - - NesRelatedSnippetsCapabilities - - - | null - - } -> +NesRelatedSnippetsCapabilities | null} > Whether the agent wants related snippets context. - - - NesUserActionsCapabilities - - | null - - } -> +NesUserActionsCapabilities | null} > Whether the agent wants user actions context. @@ -4512,8 +4365,13 @@ Capabilities for diagnostics context. **Properties:** - - The _meta property is reserved by ACP. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + ## NesDocumentDidChangeCapabilities @@ -4524,14 +4382,15 @@ Capabilities for `document/didChange` events. **Properties:** - - The _meta property is reserved by ACP. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + -TextDocumentSyncKind} - required -> +TextDocumentSyncKind} required> The sync kind the agent wants: `"full"` or `"incremental"`. @@ -4543,8 +4402,13 @@ Marker for `document/didClose` capability support. **Properties:** - - The _meta property is reserved by ACP. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + ## NesDocumentDidFocusCapabilities @@ -4555,8 +4419,13 @@ Marker for `document/didFocus` capability support. **Properties:** - - The _meta property is reserved by ACP. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + ## NesDocumentDidOpenCapabilities @@ -4567,8 +4436,13 @@ Marker for `document/didOpen` capability support. **Properties:** - - The _meta property is reserved by ACP. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + ## NesDocumentDidSaveCapabilities @@ -4579,8 +4453,13 @@ Marker for `document/didSave` capability support. **Properties:** - - The _meta property is reserved by ACP. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + ## NesDocumentEventCapabilities @@ -4591,82 +4470,27 @@ Document event capabilities the agent wants to receive. **Properties:** - - The _meta property is reserved by ACP. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + - - - - NesDocumentDidChangeCapabilities - - - | null - - } -> +NesDocumentDidChangeCapabilities | null} > Whether the agent wants `document/didChange` events, and the sync kind. - - - - NesDocumentDidCloseCapabilities - - - | null - - } -> +NesDocumentDidCloseCapabilities | null} > Whether the agent wants `document/didClose` events. - - - - NesDocumentDidFocusCapabilities - - - | null - - } -> +NesDocumentDidFocusCapabilities | null} > Whether the agent wants `document/didFocus` events. - - - - NesDocumentDidOpenCapabilities - - - | null - - } -> +NesDocumentDidOpenCapabilities | null} > Whether the agent wants `document/didOpen` events. - - - - NesDocumentDidSaveCapabilities - - - | null - - } -> +NesDocumentDidSaveCapabilities | null} > Whether the agent wants `document/didSave` events. @@ -4679,7 +4503,12 @@ Capabilities for edit history context. **Properties:** - The _meta property is reserved by ACP. + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + Maximum number of edit history entries the agent can use. @@ -4746,20 +4575,15 @@ Event capabilities the agent can consume. **Properties:** - - The _meta property is reserved by ACP. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + - - - NesDocumentEventCapabilities - - | null - - } -> +NesDocumentEventCapabilities | null} > Document event capabilities. @@ -4795,8 +4619,13 @@ Marker for jump suggestion support. **Properties:** - - The _meta property is reserved by ACP. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + ## NesJumpSuggestion @@ -4849,8 +4678,13 @@ Capabilities for open files context. **Properties:** - - The _meta property is reserved by ACP. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + ## NesRecentFile @@ -4880,7 +4714,12 @@ Capabilities for recent files context. **Properties:** - The _meta property is reserved by ACP. + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + Maximum number of recent files the agent can use. @@ -4938,8 +4777,13 @@ Capabilities for related snippets context. **Properties:** - - The _meta property is reserved by ACP. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + ## NesRenameActionCapabilities @@ -4950,8 +4794,13 @@ Marker for rename action support. **Properties:** - - The _meta property is reserved by ACP. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + ## NesRenameActionSuggestion @@ -5001,8 +4850,13 @@ Marker for search and replace action support. **Properties:** - - The _meta property is reserved by ACP. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + ## NesSearchAndReplaceActionSuggestion @@ -5037,85 +4891,30 @@ Context attached to a suggestion request. **Properties:** - - The _meta property is reserved by ACP. + + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + - - - NesDiagnostic[] - - | null - - } -> +NesDiagnostic[] | null} > Current diagnostics (errors, warnings). - - - NesEditHistoryEntry[] - - | null - - } -> +NesEditHistoryEntry[] | null} > Recent edit history. - - - NesOpenFile[] - - | null - - } -> +NesOpenFile[] | null} > Currently open files in the editor. - - - NesRecentFile[] - - | null - - } -> +NesRecentFile[] | null} > Recently accessed files. - - - NesRelatedSnippet[] - - | null - - } -> +NesRelatedSnippet[] | null} > Related code snippets. - - - NesUserAction[] - - | null - - } -> +NesUserAction[] | null} > Recent user actions (typing, navigation, etc.). @@ -5301,7 +5100,12 @@ Capabilities for user actions context. **Properties:** - The _meta property is reserved by ACP. + The _meta property is reserved by ACP to allow clients and agents to attach additional +metadata to their interactions. Implementations MUST NOT make assumptions about values at +these keys. + +See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + Maximum number of user actions the agent can use. diff --git a/schema/schema.unstable.json b/schema/schema.unstable.json index 49088669..ce9be475 100644 --- a/schema/schema.unstable.json +++ b/schema/schema.unstable.json @@ -1,5 +1,31 @@ { "$defs": { + "AcceptNesNotification": { + "description": "Notification sent when a suggestion is accepted.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", + "type": ["object", "null"] + }, + "id": { + "description": "The ID of the accepted suggestion.", + "type": "string" + }, + "sessionId": { + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], + "description": "The session ID for this notification." + } + }, + "required": ["sessionId", "id"], + "type": "object", + "x-method": "nes/accept", + "x-side": "agent" + }, "AgentAuthCapabilities": { "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nAuthentication-related capabilities supported by the agent.", "properties": { @@ -382,26 +408,26 @@ { "allOf": [ { - "$ref": "#/$defs/NesStartResponse" + "$ref": "#/$defs/StartNesResponse" } ], - "title": "NesStartResponse" + "title": "StartNesResponse" }, { "allOf": [ { - "$ref": "#/$defs/NesSuggestResponse" + "$ref": "#/$defs/SuggestNesResponse" } ], - "title": "NesSuggestResponse" + "title": "SuggestNesResponse" }, { "allOf": [ { - "$ref": "#/$defs/NesCloseResponse" + "$ref": "#/$defs/CloseNesResponse" } ], - "title": "NesCloseResponse" + "title": "CloseNesResponse" }, { "allOf": [ @@ -927,7 +953,7 @@ "properties": { "_meta": { "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", "type": ["object", "null"] }, "jump": { @@ -1032,20 +1058,20 @@ { "allOf": [ { - "$ref": "#/$defs/NesAcceptNotification" + "$ref": "#/$defs/AcceptNesNotification" } ], "description": "**UNSTABLE**\n\nNotification sent when a suggestion is accepted.", - "title": "NesAcceptNotification" + "title": "AcceptNesNotification" }, { "allOf": [ { - "$ref": "#/$defs/NesRejectNotification" + "$ref": "#/$defs/RejectNesNotification" } ], "description": "**UNSTABLE**\n\nNotification sent when a suggestion is rejected.", - "title": "NesRejectNotification" + "title": "RejectNesNotification" }, { "allOf": [ @@ -1201,29 +1227,29 @@ { "allOf": [ { - "$ref": "#/$defs/NesStartRequest" + "$ref": "#/$defs/StartNesRequest" } ], "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nStarts an NES session.", - "title": "NesStartRequest" + "title": "StartNesRequest" }, { "allOf": [ { - "$ref": "#/$defs/NesSuggestRequest" + "$ref": "#/$defs/SuggestNesRequest" } ], "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nRequests a code suggestion.", - "title": "NesSuggestRequest" + "title": "SuggestNesRequest" }, { "allOf": [ { - "$ref": "#/$defs/NesCloseRequest" + "$ref": "#/$defs/CloseNesRequest" } ], "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nCloses an active NES session and frees up any resources associated with it.\n\nThe agent must cancel any ongoing work and then free up any resources\nassociated with the NES session.", - "title": "NesCloseRequest" + "title": "CloseNesRequest" }, { "allOf": [ @@ -1360,6 +1386,41 @@ ], "x-docs-ignore": true }, + "CloseNesRequest": { + "description": "Request to close an NES session.\n\nThe agent **must** cancel any ongoing work related to the NES session\nand then free up any resources associated with the session.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", + "type": ["object", "null"] + }, + "sessionId": { + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], + "description": "The ID of the NES session to close." + } + }, + "required": ["sessionId"], + "type": "object", + "x-method": "nes/close", + "x-side": "agent" + }, + "CloseNesResponse": { + "description": "Response from closing an NES session.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", + "type": ["object", "null"] + } + }, + "type": "object", + "x-method": "nes/close", + "x-side": "agent" + }, "CloseSessionRequest": { "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nRequest parameters for closing an active session.\n\nIf supported, the agent **must** cancel any ongoing work related to the session\n(treat it as if `session/cancel` was called) and then free up any resources\nassociated with the session.\n\nOnly available if the Agent supports the `session.close` capability.", "properties": { @@ -1679,7 +1740,7 @@ "properties": { "_meta": { "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", "type": ["object", "null"] }, "contentChanges": { @@ -1717,7 +1778,7 @@ "properties": { "_meta": { "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", "type": ["object", "null"] }, "sessionId": { @@ -1743,7 +1804,7 @@ "properties": { "_meta": { "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", "type": ["object", "null"] }, "position": { @@ -1790,7 +1851,7 @@ "properties": { "_meta": { "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", "type": ["object", "null"] }, "languageId": { @@ -1829,7 +1890,7 @@ "properties": { "_meta": { "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", "type": ["object", "null"] }, "sessionId": { @@ -3243,38 +3304,12 @@ "required": ["items"], "type": "object" }, - "NesAcceptNotification": { - "description": "Notification sent when a suggestion is accepted.", - "properties": { - "_meta": { - "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", - "type": ["object", "null"] - }, - "id": { - "description": "The ID of the accepted suggestion.", - "type": "string" - }, - "sessionId": { - "allOf": [ - { - "$ref": "#/$defs/SessionId" - } - ], - "description": "The session ID for this notification." - } - }, - "required": ["sessionId", "id"], - "type": "object", - "x-method": "nes/accept", - "x-side": "agent" - }, "NesCapabilities": { "description": "NES capabilities advertised by the agent during initialization.", "properties": { "_meta": { "additionalProperties": true, - "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions.", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", "type": ["object", "null"] }, "context": { @@ -3302,47 +3337,12 @@ }, "type": "object" }, - "NesCloseRequest": { - "description": "Request to close an NES session.\n\nThe agent **must** cancel any ongoing work related to the NES session\nand then free up any resources associated with the session.", - "properties": { - "_meta": { - "additionalProperties": true, - "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", - "type": ["object", "null"] - }, - "sessionId": { - "allOf": [ - { - "$ref": "#/$defs/SessionId" - } - ], - "description": "The ID of the NES session to close." - } - }, - "required": ["sessionId"], - "type": "object", - "x-method": "nes/close", - "x-side": "agent" - }, - "NesCloseResponse": { - "description": "Response from closing an NES session.", - "properties": { - "_meta": { - "additionalProperties": true, - "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", - "type": ["object", "null"] - } - }, - "type": "object", - "x-method": "nes/close", - "x-side": "agent" - }, "NesContextCapabilities": { "description": "Context capabilities the agent wants attached to each suggestion request.", "properties": { "_meta": { "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", "type": ["object", "null"] }, "diagnostics": { @@ -3475,7 +3475,7 @@ "properties": { "_meta": { "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", "type": ["object", "null"] } }, @@ -3486,7 +3486,7 @@ "properties": { "_meta": { "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", "type": ["object", "null"] }, "syncKind": { @@ -3506,7 +3506,7 @@ "properties": { "_meta": { "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", "type": ["object", "null"] } }, @@ -3517,7 +3517,7 @@ "properties": { "_meta": { "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", "type": ["object", "null"] } }, @@ -3528,7 +3528,7 @@ "properties": { "_meta": { "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", "type": ["object", "null"] } }, @@ -3539,7 +3539,7 @@ "properties": { "_meta": { "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", "type": ["object", "null"] } }, @@ -3550,7 +3550,7 @@ "properties": { "_meta": { "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", "type": ["object", "null"] }, "didChange": { @@ -3616,7 +3616,7 @@ "properties": { "_meta": { "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", "type": ["object", "null"] }, "maxCount": { @@ -3681,7 +3681,7 @@ "properties": { "_meta": { "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", "type": ["object", "null"] }, "document": { @@ -3726,7 +3726,7 @@ "properties": { "_meta": { "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", "type": ["object", "null"] } }, @@ -3792,7 +3792,7 @@ "properties": { "_meta": { "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", "type": ["object", "null"] } }, @@ -3822,7 +3822,7 @@ "properties": { "_meta": { "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", "type": ["object", "null"] }, "maxCount": { @@ -3834,43 +3834,6 @@ }, "type": "object" }, - "NesRejectNotification": { - "description": "Notification sent when a suggestion is rejected.", - "properties": { - "_meta": { - "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", - "type": ["object", "null"] - }, - "id": { - "description": "The ID of the rejected suggestion.", - "type": "string" - }, - "reason": { - "anyOf": [ - { - "$ref": "#/$defs/NesRejectReason" - }, - { - "type": "null" - } - ], - "description": "The reason for rejection." - }, - "sessionId": { - "allOf": [ - { - "$ref": "#/$defs/SessionId" - } - ], - "description": "The session ID for this notification." - } - }, - "required": ["sessionId", "id"], - "type": "object", - "x-method": "nes/reject", - "x-side": "agent" - }, "NesRejectReason": { "description": "The reason a suggestion was rejected.", "oneOf": [ @@ -3919,7 +3882,7 @@ "properties": { "_meta": { "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", "type": ["object", "null"] } }, @@ -3930,7 +3893,7 @@ "properties": { "_meta": { "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", "type": ["object", "null"] } }, @@ -3987,7 +3950,7 @@ "properties": { "_meta": { "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", "type": ["object", "null"] } }, @@ -4020,69 +3983,12 @@ "required": ["id", "uri", "search", "replace"], "type": "object" }, - "NesStartRequest": { - "description": "Request to start an NES session.", - "properties": { - "_meta": { - "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", - "type": ["object", "null"] - }, - "repository": { - "anyOf": [ - { - "$ref": "#/$defs/NesRepository" - }, - { - "type": "null" - } - ], - "description": "Repository metadata, if the workspace is a git repository." - }, - "workspaceFolders": { - "description": "The workspace folders.", - "items": { - "$ref": "#/$defs/WorkspaceFolder" - }, - "type": ["array", "null"] - }, - "workspaceUri": { - "description": "The root URI of the workspace.", - "type": ["string", "null"] - } - }, - "type": "object", - "x-method": "nes/start", - "x-side": "agent" - }, - "NesStartResponse": { - "description": "Response to `nes/start`.", - "properties": { - "_meta": { - "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", - "type": ["object", "null"] - }, - "sessionId": { - "allOf": [ - { - "$ref": "#/$defs/SessionId" - } - ], - "description": "The session ID for the newly started NES session." - } - }, - "required": ["sessionId"], - "type": "object", - "x-method": "nes/start", - "x-side": "agent" - }, "NesSuggestContext": { "description": "Context attached to a suggestion request.", "properties": { "_meta": { "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", "type": ["object", "null"] }, "diagnostics": { @@ -4130,96 +4036,6 @@ }, "type": "object" }, - "NesSuggestRequest": { - "description": "Request for a code suggestion.", - "properties": { - "_meta": { - "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", - "type": ["object", "null"] - }, - "context": { - "anyOf": [ - { - "$ref": "#/$defs/NesSuggestContext" - }, - { - "type": "null" - } - ], - "description": "Context for the suggestion, included based on agent capabilities." - }, - "position": { - "allOf": [ - { - "$ref": "#/$defs/Position" - } - ], - "description": "The current cursor position." - }, - "selection": { - "anyOf": [ - { - "$ref": "#/$defs/Range" - }, - { - "type": "null" - } - ], - "description": "The current text selection range, if any." - }, - "sessionId": { - "allOf": [ - { - "$ref": "#/$defs/SessionId" - } - ], - "description": "The session ID for this request." - }, - "triggerKind": { - "allOf": [ - { - "$ref": "#/$defs/NesTriggerKind" - } - ], - "description": "What triggered this suggestion request." - }, - "uri": { - "description": "The URI of the document to suggest for.", - "type": "string" - }, - "version": { - "description": "The version number of the document.", - "format": "int64", - "type": "integer" - } - }, - "required": ["sessionId", "uri", "version", "position", "triggerKind"], - "type": "object", - "x-method": "nes/suggest", - "x-side": "agent" - }, - "NesSuggestResponse": { - "description": "Response to `nes/suggest`.", - "properties": { - "_meta": { - "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", - "type": ["object", "null"] - }, - "suggestions": { - "description": "The list of suggestions.", - "items": { - "$ref": "#/$defs/NesSuggestion" - }, - "type": "array" - } - }, - "required": ["suggestions"], - "type": "object", - "x-method": "nes/suggest", - "x-side": "agent" - }, "NesSuggestion": { "description": "A suggestion returned by the agent.", "oneOf": [ @@ -4362,7 +4178,7 @@ "properties": { "_meta": { "additionalProperties": true, - "description": "The _meta property is reserved by ACP.", + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", "type": ["object", "null"] }, "maxCount": { @@ -4851,6 +4667,43 @@ "x-method": "fs/read_text_file", "x-side": "client" }, + "RejectNesNotification": { + "description": "Notification sent when a suggestion is rejected.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", + "type": ["object", "null"] + }, + "id": { + "description": "The ID of the rejected suggestion.", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "$ref": "#/$defs/NesRejectReason" + }, + { + "type": "null" + } + ], + "description": "The reason for rejection." + }, + "sessionId": { + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], + "description": "The session ID for this notification." + } + }, + "required": ["sessionId", "id"], + "type": "object", + "x-method": "nes/reject", + "x-side": "agent" + }, "ReleaseTerminalRequest": { "description": "Request to release a terminal and free its resources.", "properties": { @@ -5987,6 +5840,63 @@ "x-method": "session/set_model", "x-side": "agent" }, + "StartNesRequest": { + "description": "Request to start an NES session.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", + "type": ["object", "null"] + }, + "repository": { + "anyOf": [ + { + "$ref": "#/$defs/NesRepository" + }, + { + "type": "null" + } + ], + "description": "Repository metadata, if the workspace is a git repository." + }, + "workspaceFolders": { + "description": "The workspace folders.", + "items": { + "$ref": "#/$defs/WorkspaceFolder" + }, + "type": ["array", "null"] + }, + "workspaceUri": { + "description": "The root URI of the workspace.", + "type": ["string", "null"] + } + }, + "type": "object", + "x-method": "nes/start", + "x-side": "agent" + }, + "StartNesResponse": { + "description": "Response to `nes/start`.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", + "type": ["object", "null"] + }, + "sessionId": { + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], + "description": "The session ID for the newly started NES session." + } + }, + "required": ["sessionId"], + "type": "object", + "x-method": "nes/start", + "x-side": "agent" + }, "StopReason": { "description": "Reasons why an agent stops processing a prompt turn.\n\nSee protocol docs: [Stop Reasons](https://agentclientprotocol.com/protocol/prompt-turn#stop-reasons)", "oneOf": [ @@ -6101,6 +6011,96 @@ }, "type": "object" }, + "SuggestNesRequest": { + "description": "Request for a code suggestion.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", + "type": ["object", "null"] + }, + "context": { + "anyOf": [ + { + "$ref": "#/$defs/NesSuggestContext" + }, + { + "type": "null" + } + ], + "description": "Context for the suggestion, included based on agent capabilities." + }, + "position": { + "allOf": [ + { + "$ref": "#/$defs/Position" + } + ], + "description": "The current cursor position." + }, + "selection": { + "anyOf": [ + { + "$ref": "#/$defs/Range" + }, + { + "type": "null" + } + ], + "description": "The current text selection range, if any." + }, + "sessionId": { + "allOf": [ + { + "$ref": "#/$defs/SessionId" + } + ], + "description": "The session ID for this request." + }, + "triggerKind": { + "allOf": [ + { + "$ref": "#/$defs/NesTriggerKind" + } + ], + "description": "What triggered this suggestion request." + }, + "uri": { + "description": "The URI of the document to suggest for.", + "type": "string" + }, + "version": { + "description": "The version number of the document.", + "format": "int64", + "type": "integer" + } + }, + "required": ["sessionId", "uri", "version", "position", "triggerKind"], + "type": "object", + "x-method": "nes/suggest", + "x-side": "agent" + }, + "SuggestNesResponse": { + "description": "Response to `nes/suggest`.", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", + "type": ["object", "null"] + }, + "suggestions": { + "description": "The list of suggestions.", + "items": { + "$ref": "#/$defs/NesSuggestion" + }, + "type": "array" + } + }, + "required": ["suggestions"], + "type": "object", + "x-method": "nes/suggest", + "x-side": "agent" + }, "Terminal": { "description": "Embed a terminal created with `terminal/create` by its id.\n\nThe terminal must be added before calling `terminal/release`.\n\nSee protocol docs: [Terminal](https://agentclientprotocol.com/protocol/terminals)", "properties": { diff --git a/src/agent.rs b/src/agent.rs index d51a68f7..162c0161 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -19,10 +19,18 @@ use crate::{ #[cfg(feature = "unstable_nes")] use crate::{ - DocumentDidChangeNotification, DocumentDidCloseNotification, DocumentDidFocusNotification, - DocumentDidOpenNotification, DocumentDidSaveNotification, NesAcceptNotification, - NesCapabilities, NesCloseRequest, NesCloseResponse, NesRejectNotification, NesStartRequest, - NesStartResponse, NesSuggestRequest, NesSuggestResponse, PositionEncodingKind, + AcceptNesNotification, CloseNesRequest, CloseNesResponse, DocumentDidChangeNotification, + DocumentDidCloseNotification, DocumentDidFocusNotification, DocumentDidOpenNotification, + DocumentDidSaveNotification, NesCapabilities, PositionEncodingKind, RejectNesNotification, + StartNesRequest, StartNesResponse, SuggestNesRequest, SuggestNesResponse, +}; + +#[cfg(feature = "unstable_nes")] +use crate::nes::{ + DOCUMENT_DID_CHANGE_METHOD_NAME, DOCUMENT_DID_CLOSE_METHOD_NAME, + DOCUMENT_DID_FOCUS_METHOD_NAME, DOCUMENT_DID_OPEN_METHOD_NAME, DOCUMENT_DID_SAVE_METHOD_NAME, + NES_ACCEPT_METHOD_NAME, NES_CLOSE_METHOD_NAME, NES_REJECT_METHOD_NAME, NES_START_METHOD_NAME, + NES_SUGGEST_METHOD_NAME, }; // Initialize @@ -3850,14 +3858,6 @@ pub const AGENT_METHOD_NAMES: AgentMethodNames = AgentMethodNames { document_did_focus: DOCUMENT_DID_FOCUS_METHOD_NAME, }; -#[cfg(feature = "unstable_nes")] -use crate::nes::{ - DOCUMENT_DID_CHANGE_METHOD_NAME, DOCUMENT_DID_CLOSE_METHOD_NAME, - DOCUMENT_DID_FOCUS_METHOD_NAME, DOCUMENT_DID_OPEN_METHOD_NAME, DOCUMENT_DID_SAVE_METHOD_NAME, - NES_ACCEPT_METHOD_NAME, NES_CLOSE_METHOD_NAME, NES_REJECT_METHOD_NAME, NES_START_METHOD_NAME, - NES_SUGGEST_METHOD_NAME, -}; - /// Method name for the initialize request. pub(crate) const INITIALIZE_METHOD_NAME: &str = "initialize"; /// Method name for the authenticate request. @@ -4043,14 +4043,14 @@ pub enum ClientRequest { /// This capability is not part of the spec yet, and may be removed or changed at any point. /// /// Starts an NES session. - NesStartRequest(NesStartRequest), + StartNesRequest(StartNesRequest), #[cfg(feature = "unstable_nes")] /// **UNSTABLE** /// /// This capability is not part of the spec yet, and may be removed or changed at any point. /// /// Requests a code suggestion. - NesSuggestRequest(NesSuggestRequest), + SuggestNesRequest(SuggestNesRequest), #[cfg(feature = "unstable_nes")] /// **UNSTABLE** /// @@ -4060,7 +4060,7 @@ pub enum ClientRequest { /// /// The agent must cancel any ongoing work and then free up any resources /// associated with the NES session. - NesCloseRequest(NesCloseRequest), + CloseNesRequest(CloseNesRequest), /// Handles extension method requests from the client. /// /// Extension methods provide a way to add custom functionality while maintaining @@ -4094,11 +4094,11 @@ impl ClientRequest { #[cfg(feature = "unstable_session_model")] Self::SetSessionModelRequest(_) => AGENT_METHOD_NAMES.session_set_model, #[cfg(feature = "unstable_nes")] - Self::NesStartRequest(_) => AGENT_METHOD_NAMES.nes_start, + Self::StartNesRequest(_) => AGENT_METHOD_NAMES.nes_start, #[cfg(feature = "unstable_nes")] - Self::NesSuggestRequest(_) => AGENT_METHOD_NAMES.nes_suggest, + Self::SuggestNesRequest(_) => AGENT_METHOD_NAMES.nes_suggest, #[cfg(feature = "unstable_nes")] - Self::NesCloseRequest(_) => AGENT_METHOD_NAMES.nes_close, + Self::CloseNesRequest(_) => AGENT_METHOD_NAMES.nes_close, Self::ExtMethodRequest(ext_request) => &ext_request.method, } } @@ -4135,11 +4135,11 @@ pub enum AgentResponse { #[cfg(feature = "unstable_session_model")] SetSessionModelResponse(#[serde(default)] SetSessionModelResponse), #[cfg(feature = "unstable_nes")] - NesStartResponse(NesStartResponse), + StartNesResponse(StartNesResponse), #[cfg(feature = "unstable_nes")] - NesSuggestResponse(NesSuggestResponse), + SuggestNesResponse(SuggestNesResponse), #[cfg(feature = "unstable_nes")] - NesCloseResponse(#[serde(default)] NesCloseResponse), + CloseNesResponse(#[serde(default)] CloseNesResponse), ExtMethodResponse(ExtResponse), } @@ -4195,12 +4195,12 @@ pub enum ClientNotification { /// **UNSTABLE** /// /// Notification sent when a suggestion is accepted. - NesAcceptNotification(NesAcceptNotification), + AcceptNesNotification(AcceptNesNotification), #[cfg(feature = "unstable_nes")] /// **UNSTABLE** /// /// Notification sent when a suggestion is rejected. - NesRejectNotification(NesRejectNotification), + RejectNesNotification(RejectNesNotification), /// Handles extension notifications from the client. /// /// Extension notifications provide a way to send one-way messages for custom functionality @@ -4227,9 +4227,9 @@ impl ClientNotification { #[cfg(feature = "unstable_nes")] Self::DocumentDidFocusNotification(_) => AGENT_METHOD_NAMES.document_did_focus, #[cfg(feature = "unstable_nes")] - Self::NesAcceptNotification(_) => AGENT_METHOD_NAMES.nes_accept, + Self::AcceptNesNotification(_) => AGENT_METHOD_NAMES.nes_accept, #[cfg(feature = "unstable_nes")] - Self::NesRejectNotification(_) => AGENT_METHOD_NAMES.nes_reject, + Self::RejectNesNotification(_) => AGENT_METHOD_NAMES.nes_reject, Self::ExtNotification(ext_notification) => &ext_notification.method, } } diff --git a/src/bin/generate.rs b/src/bin/generate.rs index 859b0f5d..914fb258 100644 --- a/src/bin/generate.rs +++ b/src/bin/generate.rs @@ -1032,11 +1032,11 @@ starting with '$/' it is free to ignore the notification." "session/set_model" => self.agent.get("SetSessionModelRequest").unwrap(), "session/close" => self.agent.get("CloseSessionRequest").unwrap(), "logout" => self.agent.get("LogoutRequest").unwrap(), - "nes/start" => self.agent.get("NesStartRequest").unwrap(), - "nes/suggest" => self.agent.get("NesSuggestRequest").unwrap(), - "nes/close" => self.agent.get("NesCloseRequest").unwrap(), - "nes/accept" => self.agent.get("NesAcceptNotification").unwrap(), - "nes/reject" => self.agent.get("NesRejectNotification").unwrap(), + "nes/start" => self.agent.get("StartNesRequest").unwrap(), + "nes/suggest" => self.agent.get("SuggestNesRequest").unwrap(), + "nes/close" => self.agent.get("CloseNesRequest").unwrap(), + "nes/accept" => self.agent.get("AcceptNesNotification").unwrap(), + "nes/reject" => self.agent.get("RejectNesNotification").unwrap(), "document/didOpen" => self.agent.get("DocumentDidOpenNotification").unwrap(), "document/didChange" => self.agent.get("DocumentDidChangeNotification").unwrap(), "document/didClose" => self.agent.get("DocumentDidCloseNotification").unwrap(), diff --git a/src/nes.rs b/src/nes.rs index a6ba01a0..7e57ed12 100644 --- a/src/nes.rs +++ b/src/nes.rs @@ -32,46 +32,6 @@ pub(crate) const DOCUMENT_DID_SAVE_METHOD_NAME: &str = "document/didSave"; /// Notification name for document focus events. pub(crate) const DOCUMENT_DID_FOCUS_METHOD_NAME: &str = "document/didFocus"; -/// Names of all NES-related methods. -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -#[non_exhaustive] -pub struct NesMethodNames { - /// Method for starting an NES session. - pub nes_start: &'static str, - /// Method for requesting a suggestion. - pub nes_suggest: &'static str, - /// Notification for accepting a suggestion. - pub nes_accept: &'static str, - /// Notification for rejecting a suggestion. - pub nes_reject: &'static str, - /// Method for closing an NES session. - pub nes_close: &'static str, - /// Notification for document open events. - pub document_did_open: &'static str, - /// Notification for document change events. - pub document_did_change: &'static str, - /// Notification for document close events. - pub document_did_close: &'static str, - /// Notification for document save events. - pub document_did_save: &'static str, - /// Notification for document focus events. - pub document_did_focus: &'static str, -} - -/// Constant containing all NES method names. -pub const NES_METHOD_NAMES: NesMethodNames = NesMethodNames { - nes_start: NES_START_METHOD_NAME, - nes_suggest: NES_SUGGEST_METHOD_NAME, - nes_accept: NES_ACCEPT_METHOD_NAME, - nes_reject: NES_REJECT_METHOD_NAME, - nes_close: NES_CLOSE_METHOD_NAME, - document_did_open: DOCUMENT_DID_OPEN_METHOD_NAME, - document_did_change: DOCUMENT_DID_CHANGE_METHOD_NAME, - document_did_close: DOCUMENT_DID_CLOSE_METHOD_NAME, - document_did_save: DOCUMENT_DID_SAVE_METHOD_NAME, - document_did_focus: DOCUMENT_DID_FOCUS_METHOD_NAME, -}; - // Position primitives /// The encoding used for character offsets in positions. @@ -143,7 +103,10 @@ pub struct NesCapabilities { #[serde(skip_serializing_if = "Option::is_none")] pub context: Option, /// The _meta property is reserved by ACP to allow clients and agents to attach additional - /// metadata to their interactions. + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } @@ -166,6 +129,11 @@ impl NesCapabilities { self } + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[must_use] pub fn meta(mut self, meta: impl IntoOption) -> Self { self.meta = meta.into_option(); @@ -181,7 +149,11 @@ pub struct NesEventCapabilities { /// Document event capabilities. #[serde(skip_serializing_if = "Option::is_none")] pub document: Option, - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } @@ -198,6 +170,11 @@ impl NesEventCapabilities { self } + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[must_use] pub fn meta(mut self, meta: impl IntoOption) -> Self { self.meta = meta.into_option(); @@ -225,7 +202,11 @@ pub struct NesDocumentEventCapabilities { /// Whether the agent wants `document/didFocus` events. #[serde(skip_serializing_if = "Option::is_none")] pub did_focus: Option, - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } @@ -275,6 +256,11 @@ impl NesDocumentEventCapabilities { self } + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[must_use] pub fn meta(mut self, meta: impl IntoOption) -> Self { self.meta = meta.into_option(); @@ -287,7 +273,11 @@ impl NesDocumentEventCapabilities { #[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct NesDocumentDidOpenCapabilities { - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } @@ -299,7 +289,11 @@ pub struct NesDocumentDidOpenCapabilities { pub struct NesDocumentDidChangeCapabilities { /// The sync kind the agent wants: `"full"` or `"incremental"`. pub sync_kind: TextDocumentSyncKind, - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } @@ -313,6 +307,11 @@ impl NesDocumentDidChangeCapabilities { } } + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[must_use] pub fn meta(mut self, meta: impl IntoOption) -> Self { self.meta = meta.into_option(); @@ -337,7 +336,11 @@ pub enum TextDocumentSyncKind { #[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct NesDocumentDidCloseCapabilities { - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } @@ -347,7 +350,11 @@ pub struct NesDocumentDidCloseCapabilities { #[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct NesDocumentDidSaveCapabilities { - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } @@ -357,7 +364,11 @@ pub struct NesDocumentDidSaveCapabilities { #[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct NesDocumentDidFocusCapabilities { - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } @@ -385,7 +396,11 @@ pub struct NesContextCapabilities { /// Whether the agent wants diagnostics context. #[serde(skip_serializing_if = "Option::is_none")] pub diagnostics: Option, - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } @@ -444,6 +459,11 @@ impl NesContextCapabilities { self } + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[must_use] pub fn meta(mut self, meta: impl IntoOption) -> Self { self.meta = meta.into_option(); @@ -459,7 +479,11 @@ pub struct NesRecentFilesCapabilities { /// Maximum number of recent files the agent can use. #[serde(skip_serializing_if = "Option::is_none")] pub max_count: Option, - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } @@ -469,7 +493,11 @@ pub struct NesRecentFilesCapabilities { #[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct NesRelatedSnippetsCapabilities { - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } @@ -482,7 +510,11 @@ pub struct NesEditHistoryCapabilities { /// Maximum number of edit history entries the agent can use. #[serde(skip_serializing_if = "Option::is_none")] pub max_count: Option, - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } @@ -495,7 +527,11 @@ pub struct NesUserActionsCapabilities { /// Maximum number of user actions the agent can use. #[serde(skip_serializing_if = "Option::is_none")] pub max_count: Option, - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } @@ -505,7 +541,11 @@ pub struct NesUserActionsCapabilities { #[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct NesOpenFilesCapabilities { - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } @@ -515,7 +555,11 @@ pub struct NesOpenFilesCapabilities { #[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct NesDiagnosticsCapabilities { - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } @@ -536,7 +580,11 @@ pub struct ClientNesCapabilities { /// Whether the client supports the `searchAndReplace` suggestion kind. #[serde(skip_serializing_if = "Option::is_none")] pub search_and_replace: Option, - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } @@ -568,6 +616,11 @@ impl ClientNesCapabilities { self } + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[must_use] pub fn meta(mut self, meta: impl IntoOption) -> Self { self.meta = meta.into_option(); @@ -580,7 +633,11 @@ impl ClientNesCapabilities { #[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct NesJumpCapabilities { - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } @@ -590,7 +647,11 @@ pub struct NesJumpCapabilities { #[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct NesRenameActionCapabilities { - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } @@ -600,7 +661,11 @@ pub struct NesRenameActionCapabilities { #[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct NesSearchAndReplaceActionCapabilities { - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } @@ -623,7 +688,11 @@ pub struct DocumentDidOpenNotification { pub version: i64, /// The full text content of the document. pub text: String, - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } @@ -647,6 +716,11 @@ impl DocumentDidOpenNotification { } } + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[must_use] pub fn meta(mut self, meta: impl IntoOption) -> Self { self.meta = meta.into_option(); @@ -668,7 +742,11 @@ pub struct DocumentDidChangeNotification { pub version: i64, /// The content changes. pub content_changes: Vec, - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } @@ -690,6 +768,11 @@ impl DocumentDidChangeNotification { } } + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[must_use] pub fn meta(mut self, meta: impl IntoOption) -> Self { self.meta = meta.into_option(); @@ -740,7 +823,11 @@ pub struct DocumentDidCloseNotification { pub session_id: SessionId, /// The URI of the closed document. pub uri: String, - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } @@ -755,6 +842,11 @@ impl DocumentDidCloseNotification { } } + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[must_use] pub fn meta(mut self, meta: impl IntoOption) -> Self { self.meta = meta.into_option(); @@ -772,7 +864,11 @@ pub struct DocumentDidSaveNotification { pub session_id: SessionId, /// The URI of the saved document. pub uri: String, - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } @@ -787,6 +883,11 @@ impl DocumentDidSaveNotification { } } + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[must_use] pub fn meta(mut self, meta: impl IntoOption) -> Self { self.meta = meta.into_option(); @@ -810,7 +911,11 @@ pub struct DocumentDidFocusNotification { pub position: Position, /// The portion of the file currently visible in the editor viewport. pub visible_range: Range, - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } @@ -834,6 +939,11 @@ impl DocumentDidFocusNotification { } } + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[must_use] pub fn meta(mut self, meta: impl IntoOption) -> Self { self.meta = meta.into_option(); @@ -848,7 +958,7 @@ impl DocumentDidFocusNotification { #[schemars(extend("x-side" = "agent", "x-method" = NES_START_METHOD_NAME))] #[serde(rename_all = "camelCase")] #[non_exhaustive] -pub struct NesStartRequest { +pub struct StartNesRequest { /// The root URI of the workspace. #[serde(skip_serializing_if = "Option::is_none")] pub workspace_uri: Option, @@ -858,12 +968,16 @@ pub struct NesStartRequest { /// Repository metadata, if the workspace is a git repository. #[serde(skip_serializing_if = "Option::is_none")] pub repository: Option, - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } -impl NesStartRequest { +impl StartNesRequest { #[must_use] pub fn new() -> Self { Self { @@ -895,6 +1009,11 @@ impl NesStartRequest { self } + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[must_use] pub fn meta(mut self, meta: impl IntoOption) -> Self { self.meta = meta.into_option(); @@ -902,7 +1021,7 @@ impl NesStartRequest { } } -impl Default for NesStartRequest { +impl Default for StartNesRequest { fn default() -> Self { Self::new() } @@ -962,15 +1081,19 @@ impl NesRepository { #[schemars(extend("x-side" = "agent", "x-method" = NES_START_METHOD_NAME))] #[serde(rename_all = "camelCase")] #[non_exhaustive] -pub struct NesStartResponse { +pub struct StartNesResponse { /// The session ID for the newly started NES session. pub session_id: SessionId, - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } -impl NesStartResponse { +impl StartNesResponse { #[must_use] pub fn new(session_id: impl Into) -> Self { Self { @@ -979,6 +1102,11 @@ impl NesStartResponse { } } + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[must_use] pub fn meta(mut self, meta: impl IntoOption) -> Self { self.meta = meta.into_option(); @@ -996,7 +1124,7 @@ impl NesStartResponse { #[schemars(extend("x-side" = "agent", "x-method" = NES_CLOSE_METHOD_NAME))] #[serde(rename_all = "camelCase")] #[non_exhaustive] -pub struct NesCloseRequest { +pub struct CloseNesRequest { /// The ID of the NES session to close. pub session_id: SessionId, /// The _meta property is reserved by ACP to allow clients and agents to attach additional @@ -1008,7 +1136,7 @@ pub struct NesCloseRequest { pub meta: Option, } -impl NesCloseRequest { +impl CloseNesRequest { #[must_use] pub fn new(session_id: impl Into) -> Self { Self { @@ -1017,6 +1145,11 @@ impl NesCloseRequest { } } + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at /// these keys. @@ -1034,7 +1167,7 @@ impl NesCloseRequest { #[schemars(extend("x-side" = "agent", "x-method" = NES_CLOSE_METHOD_NAME))] #[serde(rename_all = "camelCase")] #[non_exhaustive] -pub struct NesCloseResponse { +pub struct CloseNesResponse { /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at /// these keys. @@ -1044,12 +1177,17 @@ pub struct NesCloseResponse { pub meta: Option, } -impl NesCloseResponse { +impl CloseNesResponse { #[must_use] pub fn new() -> Self { Self::default() } + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at /// these keys. @@ -1084,7 +1222,7 @@ pub enum NesTriggerKind { #[schemars(extend("x-side" = "agent", "x-method" = NES_SUGGEST_METHOD_NAME))] #[serde(rename_all = "camelCase")] #[non_exhaustive] -pub struct NesSuggestRequest { +pub struct SuggestNesRequest { /// The session ID for this request. pub session_id: SessionId, /// The URI of the document to suggest for. @@ -1101,12 +1239,16 @@ pub struct NesSuggestRequest { /// Context for the suggestion, included based on agent capabilities. #[serde(skip_serializing_if = "Option::is_none")] pub context: Option, - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } -impl NesSuggestRequest { +impl SuggestNesRequest { #[must_use] pub fn new( session_id: impl Into, @@ -1139,6 +1281,11 @@ impl NesSuggestRequest { self } + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[must_use] pub fn meta(mut self, meta: impl IntoOption) -> Self { self.meta = meta.into_option(); @@ -1169,7 +1316,11 @@ pub struct NesSuggestContext { /// Current diagnostics (errors, warnings). #[serde(skip_serializing_if = "Option::is_none")] pub diagnostics: Option>, - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } @@ -1219,6 +1370,11 @@ impl NesSuggestContext { self } + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[must_use] pub fn meta(mut self, meta: impl IntoOption) -> Self { self.meta = meta.into_option(); @@ -1450,15 +1606,19 @@ pub enum NesDiagnosticSeverity { #[schemars(extend("x-side" = "agent", "x-method" = NES_SUGGEST_METHOD_NAME))] #[serde(rename_all = "camelCase")] #[non_exhaustive] -pub struct NesSuggestResponse { +pub struct SuggestNesResponse { /// The list of suggestions. pub suggestions: Vec, - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } -impl NesSuggestResponse { +impl SuggestNesResponse { #[must_use] pub fn new(suggestions: Vec) -> Self { Self { @@ -1467,6 +1627,11 @@ impl NesSuggestResponse { } } + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[must_use] pub fn meta(mut self, meta: impl IntoOption) -> Self { self.meta = meta.into_option(); @@ -1649,17 +1814,21 @@ impl NesSearchAndReplaceActionSuggestion { #[schemars(extend("x-side" = "agent", "x-method" = NES_ACCEPT_METHOD_NAME))] #[serde(rename_all = "camelCase")] #[non_exhaustive] -pub struct NesAcceptNotification { +pub struct AcceptNesNotification { /// The session ID for this notification. pub session_id: SessionId, /// The ID of the accepted suggestion. pub id: String, - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } -impl NesAcceptNotification { +impl AcceptNesNotification { #[must_use] pub fn new(session_id: impl Into, id: impl Into) -> Self { Self { @@ -1669,6 +1838,11 @@ impl NesAcceptNotification { } } + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[must_use] pub fn meta(mut self, meta: impl IntoOption) -> Self { self.meta = meta.into_option(); @@ -1681,7 +1855,7 @@ impl NesAcceptNotification { #[schemars(extend("x-side" = "agent", "x-method" = NES_REJECT_METHOD_NAME))] #[serde(rename_all = "camelCase")] #[non_exhaustive] -pub struct NesRejectNotification { +pub struct RejectNesNotification { /// The session ID for this notification. pub session_id: SessionId, /// The ID of the rejected suggestion. @@ -1689,12 +1863,16 @@ pub struct NesRejectNotification { /// The reason for rejection. #[serde(skip_serializing_if = "Option::is_none")] pub reason: Option, - /// The _meta property is reserved by ACP. + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[serde(skip_serializing_if = "Option::is_none", rename = "_meta")] pub meta: Option, } -impl NesRejectNotification { +impl RejectNesNotification { #[must_use] pub fn new(session_id: impl Into, id: impl Into) -> Self { Self { @@ -1711,6 +1889,11 @@ impl NesRejectNotification { self } + /// The _meta property is reserved by ACP to allow clients and agents to attach additional + /// metadata to their interactions. Implementations MUST NOT make assumptions about values at + /// these keys. + /// + /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) #[must_use] pub fn meta(mut self, meta: impl IntoOption) -> Self { self.meta = meta.into_option(); @@ -2112,7 +2295,7 @@ mod tests { #[test] fn test_nes_start_request_serialization() { - let request = NesStartRequest::new() + let request = StartNesRequest::new() .workspace_uri("file:///Users/alice/projects/my-app") .workspace_folders(vec![WorkspaceFolder::new( "file:///Users/alice/projects/my-app", @@ -2146,7 +2329,7 @@ mod tests { #[test] fn test_nes_start_response_serialization() { - let response = NesStartResponse::new("nes_abc123"); + let response = StartNesResponse::new("nes_abc123"); let json = serde_json::to_value(&response).unwrap(); assert_eq!(json, json!({ "sessionId": "nes_abc123" })); } @@ -2189,7 +2372,7 @@ mod tests { #[test] fn test_nes_accept_notification_serialization() { - let notification = NesAcceptNotification::new("session_123", "sugg_001"); + let notification = AcceptNesNotification::new("session_123", "sugg_001"); let json = serde_json::to_value(¬ification).unwrap(); assert_eq!( json, @@ -2200,7 +2383,7 @@ mod tests { #[test] fn test_nes_reject_notification_serialization() { let notification = - NesRejectNotification::new("session_123", "sugg_001").reason(NesRejectReason::Rejected); + RejectNesNotification::new("session_123", "sugg_001").reason(NesRejectReason::Rejected); let json = serde_json::to_value(¬ification).unwrap(); assert_eq!( json, @@ -2210,7 +2393,7 @@ mod tests { #[test] fn test_nes_suggest_request_with_context_serialization() { - let request = NesSuggestRequest::new( + let request = SuggestNesRequest::new( "session_123", "file:///path/to/file.rs", 2, @@ -2264,7 +2447,7 @@ mod tests { #[test] fn test_nes_suggest_response_serialization() { - let response = NesSuggestResponse::new(vec![ + let response = SuggestNesResponse::new(vec![ NesSuggestion::Edit(NesEditSuggestion::new( "sugg_001", "file:///path/to/file.rs", diff --git a/src/rpc.rs b/src/rpc.rs index 47c4ec1e..cee866d3 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -10,9 +10,6 @@ use crate::{ ClientNotification, ClientRequest, ClientResponse, Error, ExtNotification, ExtRequest, Result, }; -#[cfg(feature = "unstable_nes")] -use crate::NES_METHOD_NAMES; - /// JSON RPC Request Id /// /// An identifier established by the Client that MUST contain a String, Number, or NULL value if included. If it is not included it is assumed to be a notification. The value SHOULD normally not be Null [1] and Numbers SHOULD NOT contain fractional parts [2] @@ -328,16 +325,16 @@ impl Side for AgentSide { .map(ClientRequest::PromptRequest) .map_err(Into::into), #[cfg(feature = "unstable_nes")] - m if m == NES_METHOD_NAMES.nes_start => serde_json::from_str(params.get()) - .map(ClientRequest::NesStartRequest) + m if m == AGENT_METHOD_NAMES.nes_start => serde_json::from_str(params.get()) + .map(ClientRequest::StartNesRequest) .map_err(Into::into), #[cfg(feature = "unstable_nes")] - m if m == NES_METHOD_NAMES.nes_suggest => serde_json::from_str(params.get()) - .map(ClientRequest::NesSuggestRequest) + m if m == AGENT_METHOD_NAMES.nes_suggest => serde_json::from_str(params.get()) + .map(ClientRequest::SuggestNesRequest) .map_err(Into::into), #[cfg(feature = "unstable_nes")] - m if m == NES_METHOD_NAMES.nes_close => serde_json::from_str(params.get()) - .map(ClientRequest::NesCloseRequest) + m if m == AGENT_METHOD_NAMES.nes_close => serde_json::from_str(params.get()) + .map(ClientRequest::CloseNesRequest) .map_err(Into::into), _ => { if let Some(custom_method) = method.strip_prefix('_') { @@ -360,32 +357,32 @@ impl Side for AgentSide { .map(ClientNotification::CancelNotification) .map_err(Into::into), #[cfg(feature = "unstable_nes")] - m if m == NES_METHOD_NAMES.document_did_open => serde_json::from_str(params.get()) + m if m == AGENT_METHOD_NAMES.document_did_open => serde_json::from_str(params.get()) .map(ClientNotification::DocumentDidOpenNotification) .map_err(Into::into), #[cfg(feature = "unstable_nes")] - m if m == NES_METHOD_NAMES.document_did_change => serde_json::from_str(params.get()) + m if m == AGENT_METHOD_NAMES.document_did_change => serde_json::from_str(params.get()) .map(ClientNotification::DocumentDidChangeNotification) .map_err(Into::into), #[cfg(feature = "unstable_nes")] - m if m == NES_METHOD_NAMES.document_did_close => serde_json::from_str(params.get()) + m if m == AGENT_METHOD_NAMES.document_did_close => serde_json::from_str(params.get()) .map(ClientNotification::DocumentDidCloseNotification) .map_err(Into::into), #[cfg(feature = "unstable_nes")] - m if m == NES_METHOD_NAMES.document_did_save => serde_json::from_str(params.get()) + m if m == AGENT_METHOD_NAMES.document_did_save => serde_json::from_str(params.get()) .map(ClientNotification::DocumentDidSaveNotification) .map_err(Into::into), #[cfg(feature = "unstable_nes")] - m if m == NES_METHOD_NAMES.document_did_focus => serde_json::from_str(params.get()) + m if m == AGENT_METHOD_NAMES.document_did_focus => serde_json::from_str(params.get()) .map(ClientNotification::DocumentDidFocusNotification) .map_err(Into::into), #[cfg(feature = "unstable_nes")] - m if m == NES_METHOD_NAMES.nes_accept => serde_json::from_str(params.get()) - .map(ClientNotification::NesAcceptNotification) + m if m == AGENT_METHOD_NAMES.nes_accept => serde_json::from_str(params.get()) + .map(ClientNotification::AcceptNesNotification) .map_err(Into::into), #[cfg(feature = "unstable_nes")] - m if m == NES_METHOD_NAMES.nes_reject => serde_json::from_str(params.get()) - .map(ClientNotification::NesRejectNotification) + m if m == AGENT_METHOD_NAMES.nes_reject => serde_json::from_str(params.get()) + .map(ClientNotification::RejectNesNotification) .map_err(Into::into), _ => { if let Some(custom_method) = method.strip_prefix('_') { @@ -472,7 +469,7 @@ mod nes_rpc_tests { .unwrap(); let raw = serde_json::value::RawValue::from_string(params).unwrap(); let request = AgentSide::decode_request("nes/start", Some(&raw)).unwrap(); - assert!(matches!(request, ClientRequest::NesStartRequest(_))); + assert!(matches!(request, ClientRequest::StartNesRequest(_))); } #[test] @@ -487,7 +484,7 @@ mod nes_rpc_tests { .unwrap(); let raw = serde_json::value::RawValue::from_string(params).unwrap(); let request = AgentSide::decode_request("nes/suggest", Some(&raw)).unwrap(); - assert!(matches!(request, ClientRequest::NesSuggestRequest(_))); + assert!(matches!(request, ClientRequest::SuggestNesRequest(_))); } #[test] @@ -588,7 +585,7 @@ mod nes_rpc_tests { let notification = AgentSide::decode_notification("nes/accept", Some(&raw)).unwrap(); assert!(matches!( notification, - ClientNotification::NesAcceptNotification(_) + ClientNotification::AcceptNesNotification(_) )); } @@ -604,7 +601,7 @@ mod nes_rpc_tests { let notification = AgentSide::decode_notification("nes/reject", Some(&raw)).unwrap(); assert!(matches!( notification, - ClientNotification::NesRejectNotification(_) + ClientNotification::RejectNesNotification(_) )); } } From 808cb54af3dc53addcfa3a4eea5a1278eb003678 Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Sat, 28 Mar 2026 07:44:32 +0100 Subject: [PATCH 12/13] Some more --- AGENTS.md | 2 - docs/protocol/draft/schema.mdx | 10 +- docs/rfds/next-edit-suggestions.mdx | 29 +++++- schema/schema.unstable.json | 81 +++++++++-------- src/agent.rs | 26 +++--- src/bin/generate.rs | 10 +- src/nes.rs | 136 ++++++++++++++++++++++------ src/rpc.rs | 20 ++-- 8 files changed, 211 insertions(+), 103 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 341acb32..38ecb09f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,9 +19,7 @@ All paths in the protocol should be absolute - Add constants for the method names - Add variants to {Agent|Client}{Request|Response} enums -- Add the methods to the Client/Agent impl of {Agent|Client}SideConnection in src/acp.rs - Handle the new method in the `Side::decode_request`/`Side::decode_notification` implementation -- Handle the new request in the blanket impl of MessageHandler<{Agent|Client}Side> - Add the method to markdown_generator.rs SideDocs functions - Run `npm run generate` and fix any issues that appear - Run `npm run check` diff --git a/docs/protocol/draft/schema.mdx b/docs/protocol/draft/schema.mdx index f1ca6e3a..33e6f59b 100644 --- a/docs/protocol/draft/schema.mdx +++ b/docs/protocol/draft/schema.mdx @@ -69,7 +69,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte Notification sent when a file is edited. -#### DocumentDidChangeNotification +#### DidChangeDocumentNotification Notification sent when a file is edited. @@ -105,7 +105,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte Notification sent when a file is closed. -#### DocumentDidCloseNotification +#### DidCloseDocumentNotification Notification sent when a file is closed. @@ -135,7 +135,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte Notification sent when a file becomes the active editor tab. -#### DocumentDidFocusNotification +#### DidFocusDocumentNotification Notification sent when a file becomes the active editor tab. @@ -174,7 +174,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte Notification sent when a file is opened in the editor. -#### DocumentDidOpenNotification +#### DidOpenDocumentNotification Notification sent when a file is opened in the editor. @@ -213,7 +213,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte Notification sent when a file is saved. -#### DocumentDidSaveNotification +#### DidSaveDocumentNotification Notification sent when a file is saved. diff --git a/docs/rfds/next-edit-suggestions.mdx b/docs/rfds/next-edit-suggestions.mdx index 8c5eb273..335ed747 100644 --- a/docs/rfds/next-edit-suggestions.mdx +++ b/docs/rfds/next-edit-suggestions.mdx @@ -87,7 +87,7 @@ Each entry corresponds to a suggestion kind (see [Suggestion kinds](#suggestion- ### Session lifecycle -If the `nes` capability is present, the client may call `nes/start` to begin an NES session. An NES session is **separate from and independent of** the ACP chat session — it has its own session ID, its own lifecycle, and its own stream of events and requests. A single ACP connection may have any number of active NES sessions alongside any number of chat sessions. The NES session is started and stopped independently via `nes/start`; it does not inherit state from, or share context with, chat sessions. +If the `nes` capability is present, the client may call `nes/start` to begin an NES session. An NES session is **separate from and independent of** the ACP chat session — it has its own session ID, its own lifecycle, and its own stream of events and requests. A single ACP connection may have any number of active NES sessions alongside any number of chat sessions. The NES session is started via `nes/start` and closed via `nes/close`; it does not inherit state from, or share context with, chat sessions. The agent can also use the existing `configOptions` mechanism to expose NES-related settings (model selection, debounce preferences, enabled/disabled state, etc.). @@ -562,7 +562,7 @@ The agent may reject `nes/start` with an error. In particular, agents that requi "jsonrpc": "2.0", "id": 1, "error": { - "code": -32001, + "code": -32000, "message": "Authentication required", "data": { "reason": "auth_required" @@ -573,6 +573,31 @@ The agent may reject `nes/start` with an error. In particular, agents that requi Clients **must** be prepared to handle `auth_required` errors on `nes/start`. The recommended behavior is to prompt the user to authenticate (e.g. sign in or provide credentials) and retry the `nes/start` call after authentication succeeds. Clients should not silently ignore this error or assume NES is unavailable — the agent may be fully functional once the user authenticates. +### NES session close + +The client closes an NES session by calling `nes/close`. The agent **must** cancel any ongoing work related to the NES session and free up any resources associated with it. + +```json +{ + "jsonrpc": "2.0", + "id": 2, + "method": "nes/close", + "params": { + "sessionId": "session_abc123" + } +} +``` + +Response: + +```json +{ + "jsonrpc": "2.0", + "id": 2, + "result": {} +} +``` + ### Suggestion kinds The `kind` field in each suggestion identifies its type. The following kinds are defined: diff --git a/schema/schema.unstable.json b/schema/schema.unstable.json index 38cad569..02032b60 100644 --- a/schema/schema.unstable.json +++ b/schema/schema.unstable.json @@ -1013,47 +1013,47 @@ { "allOf": [ { - "$ref": "#/$defs/DocumentDidOpenNotification" + "$ref": "#/$defs/DidOpenDocumentNotification" } ], "description": "**UNSTABLE**\n\nNotification sent when a file is opened in the editor.", - "title": "DocumentDidOpenNotification" + "title": "DidOpenDocumentNotification" }, { "allOf": [ { - "$ref": "#/$defs/DocumentDidChangeNotification" + "$ref": "#/$defs/DidChangeDocumentNotification" } ], "description": "**UNSTABLE**\n\nNotification sent when a file is edited.", - "title": "DocumentDidChangeNotification" + "title": "DidChangeDocumentNotification" }, { "allOf": [ { - "$ref": "#/$defs/DocumentDidCloseNotification" + "$ref": "#/$defs/DidCloseDocumentNotification" } ], "description": "**UNSTABLE**\n\nNotification sent when a file is closed.", - "title": "DocumentDidCloseNotification" + "title": "DidCloseDocumentNotification" }, { "allOf": [ { - "$ref": "#/$defs/DocumentDidSaveNotification" + "$ref": "#/$defs/DidSaveDocumentNotification" } ], "description": "**UNSTABLE**\n\nNotification sent when a file is saved.", - "title": "DocumentDidSaveNotification" + "title": "DidSaveDocumentNotification" }, { "allOf": [ { - "$ref": "#/$defs/DocumentDidFocusNotification" + "$ref": "#/$defs/DidFocusDocumentNotification" } ], "description": "**UNSTABLE**\n\nNotification sent when a file becomes the active editor tab.", - "title": "DocumentDidFocusNotification" + "title": "DidFocusDocumentNotification" }, { "allOf": [ @@ -1711,31 +1711,7 @@ "required": ["currentModeId"], "type": "object" }, - "Diff": { - "description": "A diff representing file modifications.\n\nShows changes to files in a format suitable for display in the client UI.\n\nSee protocol docs: [Content](https://agentclientprotocol.com/protocol/tool-calls#content)", - "properties": { - "_meta": { - "additionalProperties": true, - "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", - "type": ["object", "null"] - }, - "newText": { - "description": "The new content after modification.", - "type": "string" - }, - "oldText": { - "description": "The original content (None for new files).", - "type": ["string", "null"] - }, - "path": { - "description": "The file path being modified.", - "type": "string" - } - }, - "required": ["path", "newText"], - "type": "object" - }, - "DocumentDidChangeNotification": { + "DidChangeDocumentNotification": { "description": "Notification sent when a file is edited.", "properties": { "_meta": { @@ -1773,7 +1749,7 @@ "x-method": "document/didChange", "x-side": "agent" }, - "DocumentDidCloseNotification": { + "DidCloseDocumentNotification": { "description": "Notification sent when a file is closed.", "properties": { "_meta": { @@ -1799,7 +1775,7 @@ "x-method": "document/didClose", "x-side": "agent" }, - "DocumentDidFocusNotification": { + "DidFocusDocumentNotification": { "description": "Notification sent when a file becomes the active editor tab.", "properties": { "_meta": { @@ -1846,7 +1822,7 @@ "x-method": "document/didFocus", "x-side": "agent" }, - "DocumentDidOpenNotification": { + "DidOpenDocumentNotification": { "description": "Notification sent when a file is opened in the editor.", "properties": { "_meta": { @@ -1885,7 +1861,7 @@ "x-method": "document/didOpen", "x-side": "agent" }, - "DocumentDidSaveNotification": { + "DidSaveDocumentNotification": { "description": "Notification sent when a file is saved.", "properties": { "_meta": { @@ -1911,6 +1887,30 @@ "x-method": "document/didSave", "x-side": "agent" }, + "Diff": { + "description": "A diff representing file modifications.\n\nShows changes to files in a format suitable for display in the client UI.\n\nSee protocol docs: [Content](https://agentclientprotocol.com/protocol/tool-calls#content)", + "properties": { + "_meta": { + "additionalProperties": true, + "description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)", + "type": ["object", "null"] + }, + "newText": { + "description": "The new content after modification.", + "type": "string" + }, + "oldText": { + "description": "The original content (None for new files).", + "type": ["string", "null"] + }, + "path": { + "description": "The file path being modified.", + "type": "string" + } + }, + "required": ["path", "newText"], + "type": "object" + }, "ElicitationAcceptAction": { "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nThe user accepted the elicitation and provided content.", "properties": { @@ -4059,6 +4059,9 @@ }, "NesSuggestion": { "description": "A suggestion returned by the agent.", + "discriminator": { + "propertyName": "kind" + }, "oneOf": [ { "allOf": [ diff --git a/src/agent.rs b/src/agent.rs index e9f1fb5e..8166c4a6 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -19,9 +19,9 @@ use crate::{ #[cfg(feature = "unstable_nes")] use crate::{ - AcceptNesNotification, CloseNesRequest, CloseNesResponse, DocumentDidChangeNotification, - DocumentDidCloseNotification, DocumentDidFocusNotification, DocumentDidOpenNotification, - DocumentDidSaveNotification, NesCapabilities, PositionEncodingKind, RejectNesNotification, + AcceptNesNotification, CloseNesRequest, CloseNesResponse, DidChangeDocumentNotification, + DidCloseDocumentNotification, DidFocusDocumentNotification, DidOpenDocumentNotification, + DidSaveDocumentNotification, NesCapabilities, PositionEncodingKind, RejectNesNotification, StartNesRequest, StartNesResponse, SuggestNesRequest, SuggestNesResponse, }; @@ -4387,27 +4387,27 @@ pub enum ClientNotification { /// **UNSTABLE** /// /// Notification sent when a file is opened in the editor. - DocumentDidOpenNotification(DocumentDidOpenNotification), + DidOpenDocumentNotification(DidOpenDocumentNotification), #[cfg(feature = "unstable_nes")] /// **UNSTABLE** /// /// Notification sent when a file is edited. - DocumentDidChangeNotification(DocumentDidChangeNotification), + DidChangeDocumentNotification(DidChangeDocumentNotification), #[cfg(feature = "unstable_nes")] /// **UNSTABLE** /// /// Notification sent when a file is closed. - DocumentDidCloseNotification(DocumentDidCloseNotification), + DidCloseDocumentNotification(DidCloseDocumentNotification), #[cfg(feature = "unstable_nes")] /// **UNSTABLE** /// /// Notification sent when a file is saved. - DocumentDidSaveNotification(DocumentDidSaveNotification), + DidSaveDocumentNotification(DidSaveDocumentNotification), #[cfg(feature = "unstable_nes")] /// **UNSTABLE** /// /// Notification sent when a file becomes the active editor tab. - DocumentDidFocusNotification(DocumentDidFocusNotification), + DidFocusDocumentNotification(DidFocusDocumentNotification), #[cfg(feature = "unstable_nes")] /// **UNSTABLE** /// @@ -4434,15 +4434,15 @@ impl ClientNotification { match self { Self::CancelNotification(_) => AGENT_METHOD_NAMES.session_cancel, #[cfg(feature = "unstable_nes")] - Self::DocumentDidOpenNotification(_) => AGENT_METHOD_NAMES.document_did_open, + Self::DidOpenDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_open, #[cfg(feature = "unstable_nes")] - Self::DocumentDidChangeNotification(_) => AGENT_METHOD_NAMES.document_did_change, + Self::DidChangeDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_change, #[cfg(feature = "unstable_nes")] - Self::DocumentDidCloseNotification(_) => AGENT_METHOD_NAMES.document_did_close, + Self::DidCloseDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_close, #[cfg(feature = "unstable_nes")] - Self::DocumentDidSaveNotification(_) => AGENT_METHOD_NAMES.document_did_save, + Self::DidSaveDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_save, #[cfg(feature = "unstable_nes")] - Self::DocumentDidFocusNotification(_) => AGENT_METHOD_NAMES.document_did_focus, + Self::DidFocusDocumentNotification(_) => AGENT_METHOD_NAMES.document_did_focus, #[cfg(feature = "unstable_nes")] Self::AcceptNesNotification(_) => AGENT_METHOD_NAMES.nes_accept, #[cfg(feature = "unstable_nes")] diff --git a/src/bin/generate.rs b/src/bin/generate.rs index 94d442cc..fd93616b 100644 --- a/src/bin/generate.rs +++ b/src/bin/generate.rs @@ -1038,11 +1038,11 @@ starting with '$/' it is free to ignore the notification." "nes/close" => self.agent.get("CloseNesRequest").unwrap(), "nes/accept" => self.agent.get("AcceptNesNotification").unwrap(), "nes/reject" => self.agent.get("RejectNesNotification").unwrap(), - "document/didOpen" => self.agent.get("DocumentDidOpenNotification").unwrap(), - "document/didChange" => self.agent.get("DocumentDidChangeNotification").unwrap(), - "document/didClose" => self.agent.get("DocumentDidCloseNotification").unwrap(), - "document/didSave" => self.agent.get("DocumentDidSaveNotification").unwrap(), - "document/didFocus" => self.agent.get("DocumentDidFocusNotification").unwrap(), + "document/didOpen" => self.agent.get("DidOpenDocumentNotification").unwrap(), + "document/didChange" => self.agent.get("DidChangeDocumentNotification").unwrap(), + "document/didClose" => self.agent.get("DidCloseDocumentNotification").unwrap(), + "document/didSave" => self.agent.get("DidSaveDocumentNotification").unwrap(), + "document/didFocus" => self.agent.get("DidFocusDocumentNotification").unwrap(), _ => panic!("Introduced a method? Add it here :)"), } } diff --git a/src/nes.rs b/src/nes.rs index 7e57ed12..1d8bd0ea 100644 --- a/src/nes.rs +++ b/src/nes.rs @@ -282,6 +282,13 @@ pub struct NesDocumentDidOpenCapabilities { pub meta: Option, } +impl NesDocumentDidOpenCapabilities { + #[must_use] + pub fn new() -> Self { + Self::default() + } +} + /// Capabilities for `document/didChange` events. #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "camelCase")] @@ -345,6 +352,13 @@ pub struct NesDocumentDidCloseCapabilities { pub meta: Option, } +impl NesDocumentDidCloseCapabilities { + #[must_use] + pub fn new() -> Self { + Self::default() + } +} + /// Marker for `document/didSave` capability support. #[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "camelCase")] @@ -359,6 +373,13 @@ pub struct NesDocumentDidSaveCapabilities { pub meta: Option, } +impl NesDocumentDidSaveCapabilities { + #[must_use] + pub fn new() -> Self { + Self::default() + } +} + /// Marker for `document/didFocus` capability support. #[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "camelCase")] @@ -373,6 +394,13 @@ pub struct NesDocumentDidFocusCapabilities { pub meta: Option, } +impl NesDocumentDidFocusCapabilities { + #[must_use] + pub fn new() -> Self { + Self::default() + } +} + /// Context capabilities the agent wants attached to each suggestion request. #[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "camelCase")] @@ -488,6 +516,13 @@ pub struct NesRecentFilesCapabilities { pub meta: Option, } +impl NesRecentFilesCapabilities { + #[must_use] + pub fn new() -> Self { + Self::default() + } +} + /// Capabilities for related snippets context. #[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "camelCase")] @@ -502,6 +537,13 @@ pub struct NesRelatedSnippetsCapabilities { pub meta: Option, } +impl NesRelatedSnippetsCapabilities { + #[must_use] + pub fn new() -> Self { + Self::default() + } +} + /// Capabilities for edit history context. #[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "camelCase")] @@ -519,6 +561,13 @@ pub struct NesEditHistoryCapabilities { pub meta: Option, } +impl NesEditHistoryCapabilities { + #[must_use] + pub fn new() -> Self { + Self::default() + } +} + /// Capabilities for user actions context. #[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "camelCase")] @@ -536,6 +585,13 @@ pub struct NesUserActionsCapabilities { pub meta: Option, } +impl NesUserActionsCapabilities { + #[must_use] + pub fn new() -> Self { + Self::default() + } +} + /// Capabilities for open files context. #[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "camelCase")] @@ -550,6 +606,13 @@ pub struct NesOpenFilesCapabilities { pub meta: Option, } +impl NesOpenFilesCapabilities { + #[must_use] + pub fn new() -> Self { + Self::default() + } +} + /// Capabilities for diagnostics context. #[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "camelCase")] @@ -564,6 +627,13 @@ pub struct NesDiagnosticsCapabilities { pub meta: Option, } +impl NesDiagnosticsCapabilities { + #[must_use] + pub fn new() -> Self { + Self::default() + } +} + // Client NES capabilities /// NES capabilities advertised by the client during initialization. @@ -642,6 +712,13 @@ pub struct NesJumpCapabilities { pub meta: Option, } +impl NesJumpCapabilities { + #[must_use] + pub fn new() -> Self { + Self::default() + } +} + /// Marker for rename action support. #[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "camelCase")] @@ -656,6 +733,13 @@ pub struct NesRenameActionCapabilities { pub meta: Option, } +impl NesRenameActionCapabilities { + #[must_use] + pub fn new() -> Self { + Self::default() + } +} + /// Marker for search and replace action support. #[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "camelCase")] @@ -670,6 +754,13 @@ pub struct NesSearchAndReplaceActionCapabilities { pub meta: Option, } +impl NesSearchAndReplaceActionCapabilities { + #[must_use] + pub fn new() -> Self { + Self::default() + } +} + // Document event notifications (client -> agent) /// Notification sent when a file is opened in the editor. @@ -677,7 +768,7 @@ pub struct NesSearchAndReplaceActionCapabilities { #[schemars(extend("x-side" = "agent", "x-method" = DOCUMENT_DID_OPEN_METHOD_NAME))] #[serde(rename_all = "camelCase")] #[non_exhaustive] -pub struct DocumentDidOpenNotification { +pub struct DidOpenDocumentNotification { /// The session ID for this notification. pub session_id: SessionId, /// The URI of the opened document. @@ -697,7 +788,7 @@ pub struct DocumentDidOpenNotification { pub meta: Option, } -impl DocumentDidOpenNotification { +impl DidOpenDocumentNotification { #[must_use] pub fn new( session_id: impl Into, @@ -733,7 +824,7 @@ impl DocumentDidOpenNotification { #[schemars(extend("x-side" = "agent", "x-method" = DOCUMENT_DID_CHANGE_METHOD_NAME))] #[serde(rename_all = "camelCase")] #[non_exhaustive] -pub struct DocumentDidChangeNotification { +pub struct DidChangeDocumentNotification { /// The session ID for this notification. pub session_id: SessionId, /// The URI of the changed document. @@ -751,7 +842,7 @@ pub struct DocumentDidChangeNotification { pub meta: Option, } -impl DocumentDidChangeNotification { +impl DidChangeDocumentNotification { #[must_use] pub fn new( session_id: impl Into, @@ -818,7 +909,7 @@ impl TextDocumentContentChangeEvent { #[schemars(extend("x-side" = "agent", "x-method" = DOCUMENT_DID_CLOSE_METHOD_NAME))] #[serde(rename_all = "camelCase")] #[non_exhaustive] -pub struct DocumentDidCloseNotification { +pub struct DidCloseDocumentNotification { /// The session ID for this notification. pub session_id: SessionId, /// The URI of the closed document. @@ -832,7 +923,7 @@ pub struct DocumentDidCloseNotification { pub meta: Option, } -impl DocumentDidCloseNotification { +impl DidCloseDocumentNotification { #[must_use] pub fn new(session_id: impl Into, uri: impl Into) -> Self { Self { @@ -859,7 +950,7 @@ impl DocumentDidCloseNotification { #[schemars(extend("x-side" = "agent", "x-method" = DOCUMENT_DID_SAVE_METHOD_NAME))] #[serde(rename_all = "camelCase")] #[non_exhaustive] -pub struct DocumentDidSaveNotification { +pub struct DidSaveDocumentNotification { /// The session ID for this notification. pub session_id: SessionId, /// The URI of the saved document. @@ -873,7 +964,7 @@ pub struct DocumentDidSaveNotification { pub meta: Option, } -impl DocumentDidSaveNotification { +impl DidSaveDocumentNotification { #[must_use] pub fn new(session_id: impl Into, uri: impl Into) -> Self { Self { @@ -900,7 +991,7 @@ impl DocumentDidSaveNotification { #[schemars(extend("x-side" = "agent", "x-method" = DOCUMENT_DID_FOCUS_METHOD_NAME))] #[serde(rename_all = "camelCase")] #[non_exhaustive] -pub struct DocumentDidFocusNotification { +pub struct DidFocusDocumentNotification { /// The session ID for this notification. pub session_id: SessionId, /// The URI of the focused document. @@ -920,7 +1011,7 @@ pub struct DocumentDidFocusNotification { pub meta: Option, } -impl DocumentDidFocusNotification { +impl DidFocusDocumentNotification { #[must_use] pub fn new( session_id: impl Into, @@ -1145,11 +1236,6 @@ impl CloseNesRequest { } } - /// The _meta property is reserved by ACP to allow clients and agents to attach additional - /// metadata to their interactions. Implementations MUST NOT make assumptions about values at - /// these keys. - /// - /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at /// these keys. @@ -1183,11 +1269,6 @@ impl CloseNesResponse { Self::default() } - /// The _meta property is reserved by ACP to allow clients and agents to attach additional - /// metadata to their interactions. Implementations MUST NOT make assumptions about values at - /// these keys. - /// - /// See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at /// these keys. @@ -1642,6 +1723,7 @@ impl SuggestNesResponse { /// A suggestion returned by the agent. #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(tag = "kind", rename_all = "camelCase")] +#[schemars(extend("discriminator" = {"propertyName": "kind"}))] #[non_exhaustive] pub enum NesSuggestion { /// A text edit suggestion. @@ -2047,7 +2129,7 @@ mod tests { #[test] fn test_document_did_open_serialization() { - let notification = DocumentDidOpenNotification::new( + let notification = DidOpenDocumentNotification::new( "session_123", "file:///path/to/file.rs", "rust", @@ -2067,13 +2149,13 @@ mod tests { }) ); - let deserialized: DocumentDidOpenNotification = serde_json::from_value(json).unwrap(); + let deserialized: DidOpenDocumentNotification = serde_json::from_value(json).unwrap(); assert_eq!(deserialized, notification); } #[test] fn test_document_did_change_incremental_serialization() { - let notification = DocumentDidChangeNotification::new( + let notification = DidChangeDocumentNotification::new( "session_123", "file:///path/to/file.rs", 2, @@ -2105,7 +2187,7 @@ mod tests { #[test] fn test_document_did_change_full_serialization() { - let notification = DocumentDidChangeNotification::new( + let notification = DidChangeDocumentNotification::new( "session_123", "file:///path/to/file.rs", 2, @@ -2133,7 +2215,7 @@ mod tests { #[test] fn test_document_did_close_serialization() { let notification = - DocumentDidCloseNotification::new("session_123", "file:///path/to/file.rs"); + DidCloseDocumentNotification::new("session_123", "file:///path/to/file.rs"); let json = serde_json::to_value(¬ification).unwrap(); assert_eq!( json, @@ -2144,7 +2226,7 @@ mod tests { #[test] fn test_document_did_save_serialization() { let notification = - DocumentDidSaveNotification::new("session_123", "file:///path/to/file.rs"); + DidSaveDocumentNotification::new("session_123", "file:///path/to/file.rs"); let json = serde_json::to_value(¬ification).unwrap(); assert_eq!( json, @@ -2154,7 +2236,7 @@ mod tests { #[test] fn test_document_did_focus_serialization() { - let notification = DocumentDidFocusNotification::new( + let notification = DidFocusDocumentNotification::new( "session_123", "file:///path/to/file.rs", 2, diff --git a/src/rpc.rs b/src/rpc.rs index cee866d3..960c0dd5 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -358,23 +358,23 @@ impl Side for AgentSide { .map_err(Into::into), #[cfg(feature = "unstable_nes")] m if m == AGENT_METHOD_NAMES.document_did_open => serde_json::from_str(params.get()) - .map(ClientNotification::DocumentDidOpenNotification) + .map(ClientNotification::DidOpenDocumentNotification) .map_err(Into::into), #[cfg(feature = "unstable_nes")] m if m == AGENT_METHOD_NAMES.document_did_change => serde_json::from_str(params.get()) - .map(ClientNotification::DocumentDidChangeNotification) + .map(ClientNotification::DidChangeDocumentNotification) .map_err(Into::into), #[cfg(feature = "unstable_nes")] m if m == AGENT_METHOD_NAMES.document_did_close => serde_json::from_str(params.get()) - .map(ClientNotification::DocumentDidCloseNotification) + .map(ClientNotification::DidCloseDocumentNotification) .map_err(Into::into), #[cfg(feature = "unstable_nes")] m if m == AGENT_METHOD_NAMES.document_did_save => serde_json::from_str(params.get()) - .map(ClientNotification::DocumentDidSaveNotification) + .map(ClientNotification::DidSaveDocumentNotification) .map_err(Into::into), #[cfg(feature = "unstable_nes")] m if m == AGENT_METHOD_NAMES.document_did_focus => serde_json::from_str(params.get()) - .map(ClientNotification::DocumentDidFocusNotification) + .map(ClientNotification::DidFocusDocumentNotification) .map_err(Into::into), #[cfg(feature = "unstable_nes")] m if m == AGENT_METHOD_NAMES.nes_accept => serde_json::from_str(params.get()) @@ -501,7 +501,7 @@ mod nes_rpc_tests { let notification = AgentSide::decode_notification("document/didOpen", Some(&raw)).unwrap(); assert!(matches!( notification, - ClientNotification::DocumentDidOpenNotification(_) + ClientNotification::DidOpenDocumentNotification(_) )); } @@ -519,7 +519,7 @@ mod nes_rpc_tests { AgentSide::decode_notification("document/didChange", Some(&raw)).unwrap(); assert!(matches!( notification, - ClientNotification::DocumentDidChangeNotification(_) + ClientNotification::DidChangeDocumentNotification(_) )); } @@ -534,7 +534,7 @@ mod nes_rpc_tests { let notification = AgentSide::decode_notification("document/didClose", Some(&raw)).unwrap(); assert!(matches!( notification, - ClientNotification::DocumentDidCloseNotification(_) + ClientNotification::DidCloseDocumentNotification(_) )); } @@ -549,7 +549,7 @@ mod nes_rpc_tests { let notification = AgentSide::decode_notification("document/didSave", Some(&raw)).unwrap(); assert!(matches!( notification, - ClientNotification::DocumentDidSaveNotification(_) + ClientNotification::DidSaveDocumentNotification(_) )); } @@ -570,7 +570,7 @@ mod nes_rpc_tests { let notification = AgentSide::decode_notification("document/didFocus", Some(&raw)).unwrap(); assert!(matches!( notification, - ClientNotification::DocumentDidFocusNotification(_) + ClientNotification::DidFocusDocumentNotification(_) )); } From d779ce24ed4c4f606779c046db8755190c9ca366 Mon Sep 17 00:00:00 2001 From: Ben Brandt Date: Sat, 28 Mar 2026 07:53:34 +0100 Subject: [PATCH 13/13] Final final final --- docs/protocol/draft/schema.mdx | 16 ++++++------ schema/schema.unstable.json | 20 +++++++------- src/nes.rs | 48 +++++++++++++++++----------------- src/rpc.rs | 11 ++++++++ 4 files changed, 53 insertions(+), 42 deletions(-) diff --git a/docs/protocol/draft/schema.mdx b/docs/protocol/draft/schema.mdx index 33e6f59b..ebc6338c 100644 --- a/docs/protocol/draft/schema.mdx +++ b/docs/protocol/draft/schema.mdx @@ -2840,10 +2840,10 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte NesJumpCapabilities | null} > Whether the client supports the `jump` suggestion kind. -NesRenameActionCapabilities | null} > +NesRenameCapabilities | null} > Whether the client supports the `rename` suggestion kind. -NesSearchAndReplaceActionCapabilities | null} > +NesSearchAndReplaceCapabilities | null} > Whether the client supports the `searchAndReplace` suggestion kind. @@ -4845,9 +4845,9 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte -## NesRenameActionCapabilities +## NesRenameCapabilities -Marker for rename action support. +Marker for rename suggestion support. **Type:** Object @@ -4862,7 +4862,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte -## NesRenameActionSuggestion +## NesRenameSuggestion A rename symbol suggestion. @@ -4901,9 +4901,9 @@ Repository metadata for an NES session. The remote URL of the repository. -## NesSearchAndReplaceActionCapabilities +## NesSearchAndReplaceCapabilities -Marker for search and replace action support. +Marker for search and replace suggestion support. **Type:** Object @@ -4918,7 +4918,7 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte -## NesSearchAndReplaceActionSuggestion +## NesSearchAndReplaceSuggestion A search-and-replace suggestion. diff --git a/schema/schema.unstable.json b/schema/schema.unstable.json index 02032b60..0a6fb847 100644 --- a/schema/schema.unstable.json +++ b/schema/schema.unstable.json @@ -970,7 +970,7 @@ "rename": { "anyOf": [ { - "$ref": "#/$defs/NesRenameActionCapabilities" + "$ref": "#/$defs/NesRenameCapabilities" }, { "type": "null" @@ -981,7 +981,7 @@ "searchAndReplace": { "anyOf": [ { - "$ref": "#/$defs/NesSearchAndReplaceActionCapabilities" + "$ref": "#/$defs/NesSearchAndReplaceCapabilities" }, { "type": "null" @@ -3909,8 +3909,8 @@ }, "type": "object" }, - "NesRenameActionCapabilities": { - "description": "Marker for rename action support.", + "NesRenameCapabilities": { + "description": "Marker for rename suggestion support.", "properties": { "_meta": { "additionalProperties": true, @@ -3920,7 +3920,7 @@ }, "type": "object" }, - "NesRenameActionSuggestion": { + "NesRenameSuggestion": { "description": "A rename symbol suggestion.", "properties": { "id": { @@ -3966,8 +3966,8 @@ "required": ["name", "owner", "remoteUrl"], "type": "object" }, - "NesSearchAndReplaceActionCapabilities": { - "description": "Marker for search and replace action support.", + "NesSearchAndReplaceCapabilities": { + "description": "Marker for search and replace suggestion support.", "properties": { "_meta": { "additionalProperties": true, @@ -3977,7 +3977,7 @@ }, "type": "object" }, - "NesSearchAndReplaceActionSuggestion": { + "NesSearchAndReplaceSuggestion": { "description": "A search-and-replace suggestion.", "properties": { "id": { @@ -4098,7 +4098,7 @@ { "allOf": [ { - "$ref": "#/$defs/NesRenameActionSuggestion" + "$ref": "#/$defs/NesRenameSuggestion" } ], "description": "A rename symbol suggestion.", @@ -4114,7 +4114,7 @@ { "allOf": [ { - "$ref": "#/$defs/NesSearchAndReplaceActionSuggestion" + "$ref": "#/$defs/NesSearchAndReplaceSuggestion" } ], "description": "A search-and-replace suggestion.", diff --git a/src/nes.rs b/src/nes.rs index 1d8bd0ea..58323d22 100644 --- a/src/nes.rs +++ b/src/nes.rs @@ -646,10 +646,10 @@ pub struct ClientNesCapabilities { pub jump: Option, /// Whether the client supports the `rename` suggestion kind. #[serde(skip_serializing_if = "Option::is_none")] - pub rename: Option, + pub rename: Option, /// Whether the client supports the `searchAndReplace` suggestion kind. #[serde(skip_serializing_if = "Option::is_none")] - pub search_and_replace: Option, + pub search_and_replace: Option, /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at /// these keys. @@ -672,7 +672,7 @@ impl ClientNesCapabilities { } #[must_use] - pub fn rename(mut self, rename: impl IntoOption) -> Self { + pub fn rename(mut self, rename: impl IntoOption) -> Self { self.rename = rename.into_option(); self } @@ -680,7 +680,7 @@ impl ClientNesCapabilities { #[must_use] pub fn search_and_replace( mut self, - search_and_replace: impl IntoOption, + search_and_replace: impl IntoOption, ) -> Self { self.search_and_replace = search_and_replace.into_option(); self @@ -719,11 +719,11 @@ impl NesJumpCapabilities { } } -/// Marker for rename action support. +/// Marker for rename suggestion support. #[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "camelCase")] #[non_exhaustive] -pub struct NesRenameActionCapabilities { +pub struct NesRenameCapabilities { /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at /// these keys. @@ -733,18 +733,18 @@ pub struct NesRenameActionCapabilities { pub meta: Option, } -impl NesRenameActionCapabilities { +impl NesRenameCapabilities { #[must_use] pub fn new() -> Self { Self::default() } } -/// Marker for search and replace action support. +/// Marker for search and replace suggestion support. #[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "camelCase")] #[non_exhaustive] -pub struct NesSearchAndReplaceActionCapabilities { +pub struct NesSearchAndReplaceCapabilities { /// The _meta property is reserved by ACP to allow clients and agents to attach additional /// metadata to their interactions. Implementations MUST NOT make assumptions about values at /// these keys. @@ -754,7 +754,7 @@ pub struct NesSearchAndReplaceActionCapabilities { pub meta: Option, } -impl NesSearchAndReplaceActionCapabilities { +impl NesSearchAndReplaceCapabilities { #[must_use] pub fn new() -> Self { Self::default() @@ -1731,9 +1731,9 @@ pub enum NesSuggestion { /// A jump-to-location suggestion. Jump(NesJumpSuggestion), /// A rename symbol suggestion. - Rename(NesRenameActionSuggestion), + Rename(NesRenameSuggestion), /// A search-and-replace suggestion. - SearchAndReplace(NesSearchAndReplaceActionSuggestion), + SearchAndReplace(NesSearchAndReplaceSuggestion), } /// A text edit suggestion. @@ -1819,7 +1819,7 @@ impl NesJumpSuggestion { #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "camelCase")] #[non_exhaustive] -pub struct NesRenameActionSuggestion { +pub struct NesRenameSuggestion { /// Unique identifier for accept/reject tracking. pub id: String, /// The file URI containing the symbol. @@ -1830,7 +1830,7 @@ pub struct NesRenameActionSuggestion { pub new_name: String, } -impl NesRenameActionSuggestion { +impl NesRenameSuggestion { #[must_use] pub fn new( id: impl Into, @@ -1851,7 +1851,7 @@ impl NesRenameActionSuggestion { #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "camelCase")] #[non_exhaustive] -pub struct NesSearchAndReplaceActionSuggestion { +pub struct NesSearchAndReplaceSuggestion { /// Unique identifier for accept/reject tracking. pub id: String, /// The file URI to search within. @@ -1865,7 +1865,7 @@ pub struct NesSearchAndReplaceActionSuggestion { pub is_regex: Option, } -impl NesSearchAndReplaceActionSuggestion { +impl NesSearchAndReplaceSuggestion { #[must_use] pub fn new( id: impl Into, @@ -2110,8 +2110,8 @@ mod tests { fn test_client_nes_capabilities_serialization() { let caps = ClientNesCapabilities::new() .jump(NesJumpCapabilities::default()) - .rename(NesRenameActionCapabilities::default()) - .search_and_replace(NesSearchAndReplaceActionCapabilities::default()); + .rename(NesRenameCapabilities::default()) + .search_and_replace(NesSearchAndReplaceCapabilities::default()); let json = serde_json::to_value(&caps).unwrap(); assert_eq!( @@ -2322,8 +2322,8 @@ mod tests { } #[test] - fn test_nes_suggestion_rename_action_serialization() { - let suggestion = NesSuggestion::Rename(NesRenameActionSuggestion::new( + fn test_nes_suggestion_rename_serialization() { + let suggestion = NesSuggestion::Rename(NesRenameSuggestion::new( "sugg_003", "file:///path/to/file.rs", Position::new(5, 10), @@ -2347,9 +2347,9 @@ mod tests { } #[test] - fn test_nes_suggestion_search_and_replace_action_serialization() { + fn test_nes_suggestion_search_and_replace_serialization() { let suggestion = NesSuggestion::SearchAndReplace( - NesSearchAndReplaceActionSuggestion::new( + NesSearchAndReplaceSuggestion::new( "sugg_004", "file:///path/to/file.rs", "oldFunction", @@ -2411,9 +2411,9 @@ mod tests { #[test] fn test_nes_start_response_serialization() { - let response = StartNesResponse::new("nes_abc123"); + let response = StartNesResponse::new("session_abc123"); let json = serde_json::to_value(&response).unwrap(); - assert_eq!(json, json!({ "sessionId": "nes_abc123" })); + assert_eq!(json, json!({ "sessionId": "session_abc123" })); } #[test] diff --git a/src/rpc.rs b/src/rpc.rs index 960c0dd5..4df1d484 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -487,6 +487,17 @@ mod nes_rpc_tests { assert!(matches!(request, ClientRequest::SuggestNesRequest(_))); } + #[test] + fn test_decode_nes_close_request() { + let params = serde_json::to_string(&json!({ + "sessionId": "session_123" + })) + .unwrap(); + let raw = serde_json::value::RawValue::from_string(params).unwrap(); + let request = AgentSide::decode_request("nes/close", Some(&raw)).unwrap(); + assert!(matches!(request, ClientRequest::CloseNesRequest(_))); + } + #[test] fn test_decode_document_did_open_notification() { let params = serde_json::to_string(&json!({