diff --git a/Cargo.toml b/Cargo.toml index 86160b45..1b429a39 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ unstable = [ "unstable_cancel_request", "unstable_elicitation", "unstable_logout", + "unstable_session_additional_directories", "unstable_session_fork", "unstable_session_model", "unstable_session_resume", @@ -31,6 +32,7 @@ unstable_auth_methods = [] unstable_cancel_request = [] unstable_elicitation = [] unstable_logout = [] +unstable_session_additional_directories = [] unstable_session_fork = [] unstable_session_model = [] unstable_session_resume = [] diff --git a/docs/docs.json b/docs/docs.json index a8e43fd1..e5156612 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -82,6 +82,9 @@ "group": "Draft: In Progress and May Change", "hidden": true, "pages": [ + "protocol/draft/session-setup", + "protocol/draft/session-list", + "protocol/draft/file-system", "protocol/draft/cancellation", "protocol/draft/schema" ] diff --git a/docs/protocol/draft/file-system.mdx b/docs/protocol/draft/file-system.mdx new file mode 100644 index 00000000..98c6e312 --- /dev/null +++ b/docs/protocol/draft/file-system.mdx @@ -0,0 +1,131 @@ +--- +title: "File System" +description: "Client filesystem access methods" +--- + +The filesystem methods allow Agents to read and write text files within the Client's environment. These methods enable Agents to access unsaved editor state and allow Clients to track file modifications made during agent execution. + +## Session Root Scope + +Filesystem methods always operate on absolute paths, but the Client **MUST** +authorize those paths against the session's effective root set. + +- by default, the effective root set is just `cwd` +- if the unstable `sessionCapabilities.additionalDirectories` capability is in use, the effective root set becomes `[cwd, ...additionalDirectories]` +- `cwd` remains the base for relative paths; additional roots only expand filesystem scope +- when a Client discovers a session through `session/list`, it SHOULD treat `cwd` plus `SessionInfo.additionalDirectories` as the authoritative current root set until a later lifecycle request changes it + +Because ACP filesystem methods are client-mediated, the Client remains +responsible for enforcing those root boundaries. + +## Checking Support + +Before attempting to use filesystem methods, Agents **MUST** verify that the Client supports these capabilities by checking the [Client Capabilities](/protocol/initialization#client-capabilities) field in the `initialize` response: + +```json highlight={8,9} +{ + "jsonrpc": "2.0", + "id": 0, + "result": { + "protocolVersion": 1, + "clientCapabilities": { + "fs": { + "readTextFile": true, + "writeTextFile": true + } + } + } +} +``` + +If `readTextFile` or `writeTextFile` is `false` or not present, the Agent **MUST NOT** attempt to call the corresponding filesystem method. + +## Reading Files + +The `fs/read_text_file` method allows Agents to read text file contents from the Client's filesystem, including unsaved changes in the editor. + +```json +{ + "jsonrpc": "2.0", + "id": 3, + "method": "fs/read_text_file", + "params": { + "sessionId": "sess_abc123def456", + "path": "/home/user/project/src/main.py", + "line": 10, + "limit": 50 + } +} +``` + + + The [Session ID](/protocol/session-setup#session-id) for this request. The + session determines which filesystem roots are in scope for the path. + + + + Absolute path to the file to read + + + + Optional line number to start reading from (1-based) + + + + Optional maximum number of lines to read + + +The Client responds with the file contents: + +```json +{ + "jsonrpc": "2.0", + "id": 3, + "result": { + "content": "def hello_world():\n print('Hello, world!')\n" + } +} +``` + +## Writing Files + +The `fs/write_text_file` method allows Agents to write or update text files in the Client's filesystem. + +```json +{ + "jsonrpc": "2.0", + "id": 4, + "method": "fs/write_text_file", + "params": { + "sessionId": "sess_abc123def456", + "path": "/home/user/project/config.json", + "content": "{\n \"debug\": true,\n \"version\": \"1.0.0\"\n}" + } +} +``` + + + The [Session ID](/protocol/session-setup#session-id) for this request. The + session determines which filesystem roots are in scope for the path. + + + +Absolute path to the file to write. + +The Client **MUST** create the file if it doesn't exist. + + + + + The text content to write to the file + + +The Client responds with an empty result on success: + +```json +{ + "jsonrpc": "2.0", + "id": 4, + "result": null +} +``` diff --git a/docs/protocol/draft/schema.mdx b/docs/protocol/draft/schema.mdx index d9a81347..92466bcf 100644 --- a/docs/protocol/draft/schema.mdx +++ b/docs/protocol/draft/schema.mdx @@ -355,6 +355,18 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + +"string"[]} > + **UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +Additional workspace roots to activate for this session. Each path must be absolute. + +When omitted or empty, no additional roots are activated. When non-empty, +this is the complete resulting additional-root list for the forked +session. + The working directory for this session. @@ -433,6 +445,17 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + +"string"[]} > + **UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +Filter sessions by the exact ordered additional workspace roots. Each path must be absolute. + +This filter applies only when the field is present and non-empty. When +omitted or empty, no additional-root filter is applied. + Opaque cursor token from a previous response's nextCursor field for cursor-based pagination @@ -499,6 +522,18 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + +"string"[]} > + **UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +Additional workspace roots to activate for this session. Each path must be absolute. + +When omitted or empty, no additional roots are activated. When non-empty, +this is the complete resulting additional-root list for the loaded +session. + The working directory for this session. @@ -578,6 +613,18 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + +"string"[]} > + **UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +Additional workspace roots for this session. Each path must be absolute. + +These expand the session's filesystem scope without changing `cwd`, which +remains the base for relative paths. When omitted or empty, no +additional roots are activated for the new session. + The working directory for this session. Must be an absolute path. @@ -776,6 +823,18 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + +"string"[]} > + **UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +Additional workspace roots to activate for this session. Each path must be absolute. + +When omitted or empty, no additional roots are activated. When non-empty, +this is the complete resulting additional-root list for the resumed +session. + The working directory for this session. @@ -4165,6 +4224,30 @@ See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/exte The ID of the option the user selected. +## SessionAdditionalDirectoriesCapabilities + +**UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +Capabilities for additional session directories support. + +By supplying `\{\}` it means that the agent supports the `additionalDirectories` field on +supported session lifecycle requests and `session/list`. + +**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) + + + ## SessionCapabilities Session capabilities supported by the agent. @@ -4188,6 +4271,14 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + +SessionAdditionalDirectoriesCapabilities | null} > + **UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +Whether the agent supports `additionalDirectories` on supported session lifecycle requests and `session/list`. + SessionCloseCapabilities | null} > **UNSTABLE** @@ -4493,6 +4584,16 @@ these keys. See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + +"string"[]} > + **UNSTABLE** + +This capability is not part of the spec yet, and may be removed or changed at any point. + +Authoritative ordered additional workspace roots for this session. Each path must be absolute. + +When omitted or empty, there are no additional roots for the session. + The working directory for this session. Must be an absolute path. diff --git a/docs/protocol/draft/session-list.mdx b/docs/protocol/draft/session-list.mdx new file mode 100644 index 00000000..8e1be790 --- /dev/null +++ b/docs/protocol/draft/session-list.mdx @@ -0,0 +1,228 @@ +--- +title: "Session List" +description: "Discovering existing sessions" +--- + +The `session/list` method allows Clients to discover sessions known to an Agent. Clients can use this to display session history and switch between sessions. + +Agents can also push session metadata updates to Clients in real-time via the `session_info_update` notification, keeping session titles and metadata in sync without polling. + +Before listing sessions, Clients **MUST** first complete the [initialization](/protocol/initialization) phase to verify the Agent supports this capability. + +
+ +```mermaid +sequenceDiagram + participant Client + participant Agent + + Note over Agent,Client: Initialized + + Client->>Agent: session/list + Agent-->>Client: session/list response (sessions) + + alt User selects a session + Client->>Agent: session/load (sessionId) + Note over Agent,Client: Replay conversation history... + Agent-->>Client: session/load response + end + + Note over Client,Agent: Ready for prompts +``` + +
+ +## Checking Support + +Before attempting to list sessions, Clients **MUST** verify that the Agent supports this capability by checking the `sessionCapabilities.list` field in the `initialize` response: + +```json highlight={7-9} +{ + "jsonrpc": "2.0", + "id": 0, + "result": { + "protocolVersion": 1, + "agentCapabilities": { + "sessionCapabilities": { + "list": {} + } + } + } +} +``` + +If `sessionCapabilities.list` is not present, the Agent does not support listing sessions and Clients **MUST NOT** attempt to call `session/list`. + + + If the Agent also advertises the unstable + `sessionCapabilities.additionalDirectories` capability, `session/list` + supports filtering by `additionalDirectories`, and any returned + `SessionInfo.additionalDirectories` value is the authoritative additional-root + list for that session. + + +## Listing Sessions + +Clients discover existing sessions by calling the `session/list` method with optional filtering and pagination parameters: + +```json +{ + "jsonrpc": "2.0", + "id": 2, + "method": "session/list", + "params": { + "cwd": "/home/user/project", + "cursor": "eyJwYWdlIjogMn0=" + } +} +``` + +All parameters are optional. A request with an empty `params` object returns the first page of sessions. + + + Filter sessions by working directory. Must be an absolute path. Only sessions + with a matching `cwd` are returned. + + + + If the Agent advertises `sessionCapabilities.additionalDirectories`, filter + sessions by the exact ordered additional-root list when this field is present + and non-empty. Omitting the field or providing an empty array means no + additional-root filter is applied. + + + + Opaque cursor token from a previous response's `nextCursor` field for + cursor-based pagination. See [Pagination](#pagination). + + +The Agent **MUST** respond with a list of sessions and optional pagination metadata: + +```json +{ + "jsonrpc": "2.0", + "id": 2, + "result": { + "sessions": [ + { + "sessionId": "sess_abc123def456", + "cwd": "/home/user/project", + "title": "Implement session list API", + "updatedAt": "2025-10-29T14:22:15Z", + "_meta": { + "messageCount": 12, + "hasErrors": false + } + }, + { + "sessionId": "sess_xyz789ghi012", + "cwd": "/home/user/another-project", + "title": "Debug authentication flow", + "updatedAt": "2025-10-28T16:45:30Z" + }, + { + "sessionId": "sess_uvw345rst678", + "cwd": "/home/user/project", + "updatedAt": "2025-10-27T15:30:00Z" + } + ], + "nextCursor": "eyJwYWdlIjogM30=" + } +} +``` + + + Array of session information objects. + + + + Unique identifier for the session. + + + Working directory for the session. Always an absolute path. + + + If the Agent advertises `sessionCapabilities.additionalDirectories`, this + is the authoritative ordered additional-root list for the session when + present. Omitting the field or returning an empty array means there are no + additional roots. + + + + Human-readable title for the session. May be auto-generated from the first prompt. + + + ISO 8601 timestamp of the last activity in the session. + + + Agent-specific metadata. See [Extensibility](/protocol/extensibility). + + + + + + + Opaque cursor token. If present, pass this in the next request's `cursor` + parameter to fetch the next page. If absent, there are no more results. + + +When no sessions match the criteria, the Agent **MUST** return an empty `sessions` array. + +## Pagination + +`session/list` uses cursor-based pagination. The request includes an optional `cursor`, and the response includes `nextCursor` when more results are available. + +- Clients **MUST** treat a missing `nextCursor` as the end of results +- Clients **MUST** treat cursors as opaque tokens — do not parse, modify, or persist them +- Agents **SHOULD** return an error if the cursor is invalid +- Agents **SHOULD** enforce reasonable page sizes internally + +## Updating Session Metadata + +Agents can update session metadata in real-time by sending a `session_info_update` notification via `session/update`. This follows the same pattern as other session notifications like [`available_commands_update`](/protocol/slash-commands) and [`current_mode_update`](/protocol/session-modes). + +```json +{ + "jsonrpc": "2.0", + "method": "session/update", + "params": { + "sessionId": "sess_abc123def456", + "update": { + "sessionUpdate": "session_info_update", + "title": "Implement user authentication", + "_meta": { + "tags": ["feature", "auth"], + "priority": "high" + } + } + } +} +``` + +All fields are optional. Only include fields that have changed — omitted fields are left unchanged. + + + Human-readable title for the session. Set to `null` to clear. + + + + ISO 8601 timestamp of last activity. Set to `null` to clear. + + + + Agent-specific metadata. See [Extensibility](/protocol/extensibility). + + +The `sessionId`, `cwd`, and `additionalDirectories` fields are **not** included +in the update. `sessionId` is already in the notification's `params`, `cwd` is +immutable after session setup, and ACP does not currently define mid-session +mutation for `additionalDirectories`. Agents typically send this notification +after the first meaningful exchange to auto-generate a title. + +## Interaction with Other Session Methods + +`session/list` is a discovery mechanism only — it does **not** restore or modify sessions: + +1. Client calls `session/list` to discover available sessions +2. User selects a session from the list +3. Client calls [`session/load`](/protocol/session-setup#loading-sessions) with the chosen `sessionId` to resume the conversation diff --git a/docs/protocol/draft/session-setup.mdx b/docs/protocol/draft/session-setup.mdx new file mode 100644 index 00000000..0e19536a --- /dev/null +++ b/docs/protocol/draft/session-setup.mdx @@ -0,0 +1,419 @@ +--- +title: "Session Setup" +description: "Creating and loading sessions" +--- + +Sessions represent a specific conversation or thread between the [Client](/protocol/overview#client) and [Agent](/protocol/overview#agent). Each session maintains its own context, conversation history, and state, allowing multiple independent interactions with the same Agent. + +Before creating a session, Clients **MUST** first complete the [initialization](/protocol/initialization) phase to establish protocol compatibility and capabilities. + +
+ +```mermaid +sequenceDiagram + participant Client + participant Agent + + Note over Agent,Client: Initialized + + alt + Client->>Agent: session/new + Note over Agent: Create session context + Note over Agent: Connect to MCP servers + Agent-->>Client: session/new response (sessionId) + else + Client->>Agent: session/load (sessionId) + Note over Agent: Restore session context + Note over Agent: Connect to MCP servers + Note over Agent,Client: Replay conversation history... + Agent->>Client: session/update + Agent->>Client: session/update + Note over Agent,Client: All content streamed + Agent-->>Client: session/load response + end + + Note over Client,Agent: Ready for prompts +``` + +
+ +## Creating a Session + +Clients create a new session by calling the `session/new` method with: + +- The [working directory](#working-directory) for the session +- A list of [MCP servers](#mcp-servers) the Agent should connect to + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "session/new", + "params": { + "cwd": "/home/user/project", + "mcpServers": [ + { + "name": "filesystem", + "command": "/path/to/mcp-server", + "args": ["--stdio"], + "env": [] + } + ] + } +} +``` + +The Agent **MUST** respond with a unique [Session ID](#session-id) that identifies this conversation: + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "sessionId": "sess_abc123def456" + } +} +``` + +## Loading Sessions + +Agents that support the `loadSession` capability allow Clients to resume previous conversations. This feature enables persistence across restarts and sharing sessions between different Client instances. + +### Checking Support + +Before attempting to load a session, Clients **MUST** verify that the Agent supports this capability by checking the `loadSession` field in the `initialize` response: + +```json highlight={7} +{ + "jsonrpc": "2.0", + "id": 0, + "result": { + "protocolVersion": 1, + "agentCapabilities": { + "loadSession": true + } + } +} +``` + +If `loadSession` is `false` or not present, the Agent does not support loading sessions and Clients **MUST NOT** attempt to call `session/load`. + +### Loading a Session + +To load an existing session, Clients **MUST** call the `session/load` method with: + +- The [Session ID](#session-id) to resume +- [MCP servers](#mcp-servers) to connect to +- The [working directory](#working-directory) + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "session/load", + "params": { + "sessionId": "sess_789xyz", + "cwd": "/home/user/project", + "mcpServers": [ + { + "name": "filesystem", + "command": "/path/to/mcp-server", + "args": ["--mode", "filesystem"], + "env": [] + } + ] + } +} +``` + +The Agent **MUST** replay the entire conversation to the Client in the form of `session/update` notifications (like `session/prompt`). + +For example, a user message from the conversation history: + +```json +{ + "jsonrpc": "2.0", + "method": "session/update", + "params": { + "sessionId": "sess_789xyz", + "update": { + "sessionUpdate": "user_message_chunk", + "content": { + "type": "text", + "text": "What's the capital of France?" + } + } + } +} +``` + +Followed by the agent's response: + +```json +{ + "jsonrpc": "2.0", + "method": "session/update", + "params": { + "sessionId": "sess_789xyz", + "update": { + "sessionUpdate": "agent_message_chunk", + "content": { + "type": "text", + "text": "The capital of France is Paris." + } + } + } +} +``` + +When **all** the conversation entries have been streamed to the Client, the Agent **MUST** respond to the original `session/load` request. + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "result": null +} +``` + +The Client can then continue sending prompts as if the session was never interrupted. + +## Additional Workspace Roots + + + This section describes the unstable + `sessionCapabilities.additionalDirectories` capability. Clients MUST gate + usage on that capability being present during initialization. + + +When advertised, Clients MAY include `additionalDirectories` on supported +session lifecycle requests to expand the session's effective filesystem root +set. In the stable lifecycle pages, that means `session/new` and `session/load`. +The same unstable field is also available on `session/resume` and +`session/fork` when those methods are supported. + +```json +{ + "jsonrpc": "2.0", + "id": 2, + "method": "session/load", + "params": { + "sessionId": "sess_789xyz", + "cwd": "/home/user/project", + "additionalDirectories": [ + "/home/user/shared-lib", + "/home/user/product-docs" + ], + "mcpServers": [] + } +} +``` + +When present, `additionalDirectories` has the following behavior: + +- `cwd` remains the primary working directory and the base for relative paths +- each `additionalDirectories` entry **MUST** be an absolute path +- omitting the field or providing an empty array activates no additional roots for the resulting session +- on `session/load`, Clients must send the full intended additional-root list again because omitting the field or providing an empty array does not restore stored roots implicitly + +## Session ID + +The session ID returned by `session/new` is a unique identifier for the conversation context. + +Clients use this ID to: + +- Send prompt requests via `session/prompt` +- Cancel ongoing operations via `session/cancel` +- Load previous sessions via `session/load` (if the Agent supports the `loadSession` capability) + +## Working Directory + +The `cwd` (current working directory) parameter establishes the primary file +system context for the session. This directory: + +- **MUST** be an absolute path +- **MUST** be used for the session regardless of where the Agent subprocess was spawned +- **MUST** remain the base for relative-path resolution +- **MUST** be part of the session's effective root set + +When the unstable `sessionCapabilities.additionalDirectories` capability is in +use, the session's effective root set is `[cwd, ...additionalDirectories]`. +Otherwise, the effective root set is just `cwd`. + +## MCP Servers + +The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) allows Agents to access external tools and data sources. When creating a session, Clients **MAY** include connection details for MCP servers that the Agent should connect to. + +MCP servers can be connected to using different transports. All Agents **MUST** support the stdio transport, while HTTP and SSE transports are optional capabilities that can be checked during initialization. + +While they are not required to by the spec, new Agents **SHOULD** support the HTTP transport to ensure compatibility with modern MCP servers. + +### Transport Types + +#### Stdio Transport + +All Agents **MUST** support connecting to MCP servers via stdio (standard input/output). This is the default transport mechanism. + + + A human-readable identifier for the server + + + + The absolute path to the MCP server executable + + + + Command-line arguments to pass to the server + + + + Environment variables to set when launching the server + + + + The name of the environment variable. + + + The value of the environment variable. + + + + +Example stdio transport configuration: + +```json +{ + "name": "filesystem", + "command": "/path/to/mcp-server", + "args": ["--stdio"], + "env": [ + { + "name": "API_KEY", + "value": "secret123" + } + ] +} +``` + +#### HTTP Transport + +When the Agent supports `mcpCapabilities.http`, Clients can specify MCP servers configurations using the HTTP transport. + + + Must be `"http"` to indicate HTTP transport + + + + A human-readable identifier for the server + + + + The URL of the MCP server + + + + HTTP headers to include in requests to the server + + + + The name of the HTTP header. + + + The value to set for the HTTP header. + + + + +Example HTTP transport configuration: + +```json +{ + "type": "http", + "name": "api-server", + "url": "https://api.example.com/mcp", + "headers": [ + { + "name": "Authorization", + "value": "Bearer token123" + }, + { + "name": "Content-Type", + "value": "application/json" + } + ] +} +``` + +#### SSE Transport + +When the Agent supports `mcpCapabilities.sse`, Clients can specify MCP servers configurations using the SSE transport. + +This transport was deprecated by the MCP spec. + + + Must be `"sse"` to indicate SSE transport + + + + A human-readable identifier for the server + + + + The URL of the SSE endpoint + + + + HTTP headers to include when establishing the SSE connection + + + + The name of the HTTP header. + + + The value to set for the HTTP header. + + + + +Example SSE transport configuration: + +```json +{ + "type": "sse", + "name": "event-stream", + "url": "https://events.example.com/mcp", + "headers": [ + { + "name": "X-API-Key", + "value": "apikey456" + } + ] +} +``` + +### Checking Transport Support + +Before using HTTP or SSE transports, Clients **MUST** verify the Agent's capabilities during initialization: + +```json highlight={7-10} +{ + "jsonrpc": "2.0", + "id": 0, + "result": { + "protocolVersion": 1, + "agentCapabilities": { + "mcpCapabilities": { + "http": true, + "sse": true + } + } + } +} +``` + +If `mcpCapabilities.http` is `false` or not present, the Agent does not support HTTP transport. +If `mcpCapabilities.sse` is `false` or not present, the Agent does not support SSE transport. + +Agents **SHOULD** connect to all MCP servers specified by the Client. + +Clients **MAY** use this ability to provide tools directly to the underlying language model by including their own MCP server. diff --git a/docs/rfds/additional-directories.mdx b/docs/rfds/additional-directories.mdx index 061987e6..fb2f512c 100644 --- a/docs/rfds/additional-directories.mdx +++ b/docs/rfds/additional-directories.mdx @@ -54,7 +54,7 @@ The proposal is scoped to session lifecycle requests, session discovery requests Agents advertise support with `sessionCapabilities.additionalDirectories`. Clients MUST gate usage on that capability. -This proposal also extends `SessionInfo` returned by `session/list` with authoritative `additionalDirectories` state, and allows `session/list` to filter on `cwd` plus `additionalDirectories`, so clients that support session discovery can recover and query the active root set for listed sessions. `session/load` and `session/resume` nevertheless remain explicit-list only: omitted `additionalDirectories` means no additional roots are activated for the resulting session, while a provided `additionalDirectories` value becomes the complete resulting additional-root list for that request. +This proposal also extends `SessionInfo` returned by `session/list` with authoritative `additionalDirectories` state, and allows `session/list` to filter on `cwd` plus `additionalDirectories`, so clients that support session discovery can recover and query the active root set for listed sessions. `session/load` and `session/resume` nevertheless remain explicit-list only: omitting `additionalDirectories` or supplying an empty array means no additional roots are activated for the resulting session, while a non-empty array-valued `additionalDirectories` becomes the complete resulting additional-root list for that request. ## Shiny future @@ -62,7 +62,7 @@ This proposal also extends `SessionInfo` returned by `session/list` with authori Clients can declare multi-root workspace scope in a standard way while preserving existing `cwd` behavior. -Agents can treat a session as an ordered root set (`[cwd, ...additionalDirectories]`) and apply the same discovery and resource-handling behavior they already apply under `cwd` to the other declared roots. +Agents can treat a session as an ordered root set starting with `cwd` and followed by any array-valued `additionalDirectories` entries, and apply the same discovery and resource-handling behavior they already apply under `cwd` to the other declared roots. Security boundaries remain explicit: declared roots communicate intended scope, while sandboxing, approvals, and OS permissions continue to govern actual access. @@ -117,9 +117,9 @@ additionalDirectories?: {} Clients MUST send `additionalDirectories` only when `sessionCapabilities.additionalDirectories` is present. -If an agent advertises `sessionCapabilities.additionalDirectories` and also supports `session/list`, each `SessionInfo` it returns MUST include `additionalDirectories` as the authoritative ordered additional-root list for that session. When a listed session has no additional roots, the field MUST be present as `[]`. +If an agent advertises `sessionCapabilities.additionalDirectories` and also supports `session/list`, any `SessionInfo.additionalDirectories` value it returns is the authoritative ordered additional-root list for that session. Agents MAY omit the field when there are no additional roots, and clients MUST treat an omitted field the same as `[]`. -If an agent advertises `sessionCapabilities.additionalDirectories` and also supports `session/list`, `ListSessionsRequest.additionalDirectories` filters sessions by exact match on the authoritative ordered additional-root list. When both `cwd` and `additionalDirectories` are present on `session/list`, both filters apply. An empty `additionalDirectories` filter matches only sessions whose authoritative additional-root list is `[]`. +If an agent advertises `sessionCapabilities.additionalDirectories` and also supports `session/list`, `ListSessionsRequest.additionalDirectories` filters sessions by exact match on the authoritative ordered additional-root list when the field is present and non-empty. When both `cwd` and a non-empty `additionalDirectories` are present on `session/list`, both filters apply. Omitting the field or supplying an empty array means no additional-root filter is applied. If adopted, the session-setup, session-list, and filesystem documentation will need corresponding updates so they describe the effective root set rather than `cwd` alone as the session filesystem context or boundary. @@ -150,8 +150,11 @@ When an agent supports both `sessionCapabilities.additionalDirectories` and `ses "id": 5, "method": "session/list", "params": { - "cwd": "/workspace/app", - "additionalDirectories": ["/workspace/libs/shared", "/workspace/skills"] + "cwd": "/home/user/project", + "additionalDirectories": [ + "/home/user/shared-lib", + "/home/user/product-docs" + ] } } ``` @@ -164,10 +167,10 @@ When an agent supports both `sessionCapabilities.additionalDirectories` and `ses "sessions": [ { "sessionId": "sess_abc123", - "cwd": "/workspace/app", + "cwd": "/home/user/project", "additionalDirectories": [ - "/workspace/libs/shared", - "/workspace/skills" + "/home/user/shared-lib", + "/home/user/product-docs" ], "updatedAt": "2026-03-24T12:00:00Z" } @@ -187,7 +190,7 @@ When an agent supports both `sessionCapabilities.additionalDirectories` and `ses ACP does not define a wire-level lexical normalization algorithm for `cwd` or `additionalDirectories`. -Clients SHOULD remove exact duplicate path strings before sending the request. Clients SHOULD also avoid entries whose path string exactly matches `cwd`. Overlapping or nested roots are not semantically redundant for ACP purposes: because discovery across `[cwd, ...additionalDirectories]` is ordered and implementation-defined, such entries MAY affect behavior even when they do not change the union of accessible paths. Agents MAY remove exact duplicate path strings, including entries identical to `cwd`, provided doing so preserves the first occurrence order of the remaining entries and does not expand scope. +Clients SHOULD remove exact duplicate path strings before sending the request. Clients SHOULD also avoid entries whose path string exactly matches `cwd`. Overlapping or nested roots are not semantically redundant for ACP purposes: because discovery across the effective ordered root list is ordered and implementation-defined, such entries MAY affect behavior even when they do not change the union of accessible paths. Agents MAY remove exact duplicate path strings, including entries identical to `cwd`, provided doing so preserves the first occurrence order of the remaining entries and does not expand scope. For `session/list` filtering, matching is against the session's authoritative ordered additional-root list as surfaced in `SessionInfo.additionalDirectories`. Implementations that normalize or canonicalize path strings for comparison SHOULD apply the same platform-appropriate rule consistently to stored session state, `SessionInfo.additionalDirectories`, and `ListSessionsRequest.additionalDirectories`. @@ -203,9 +206,8 @@ For `session/list` filtering, matching is against the session's authoritative or The effective ordered root list for a session is: -```ts -[cwd, ...additionalDirectories]; -``` +- `[cwd]` when `additionalDirectories` is omitted or is an empty array +- `[cwd, ...additionalDirectories]` when `additionalDirectories` is a non-empty array This ordered root list has the following semantics: @@ -221,20 +223,20 @@ A file path is in scope for the session if, after platform-appropriate path reso For `NewSessionRequest`, the resulting additional root list is: -- `additionalDirectories` when present; +- `additionalDirectories` when present with a non-empty array value; - otherwise `[]`. For `LoadSessionRequest` and `ResumeSessionRequest`: -- when `additionalDirectories` is present, it is the complete resulting list of additional roots for the active session, even if that list differs from the session's previously stored additional roots; -- when omitted, the resulting additional root list is `[]`. +- when `additionalDirectories` is present with a non-empty array value, it is the complete resulting list of additional roots for the active session, even if that list differs from the session's previously stored additional roots; +- when omitted or present as an empty array, the resulting additional root list is `[]`. Agents MUST NOT implicitly reactivate stored additional roots that were not supplied on the `session/load` or `session/resume` request. Supplying `additionalDirectories` on `session/load` or `session/resume` is always allowed when the capability is advertised, and doing so may preserve, replace, reduce, or expand the session's previously stored additional-root list for the resulting active session, subject to validation and policy checks. Clients that need to preserve a session's additional roots across restarts or across client instances MUST obtain, persist, or reconstruct the full list and resend it on load or resume. When `session/list` is available, `SessionInfo.additionalDirectories` provides the authoritative current additional-root list for that purpose. For `ForkSessionRequest`: -- when `additionalDirectories` is present, it is a full replacement list for the forked session; -- when omitted, the resulting additional root list is `[]`. +- when `additionalDirectories` is present with a non-empty array value, it is a full replacement list for the forked session; +- when omitted or present as an empty array, the resulting additional root list is `[]`. Agents MUST NOT implicitly inherit additional roots from the source session unless the implementation can prove that the active root list is already authoritative to the requesting client on the current connection. Otherwise, the agent MUST fail the request or require the client to provide the full list explicitly. @@ -248,7 +250,7 @@ Agents MUST treat paths under `additionalDirectories` as part of the session's a For any discovery, indexing, or resource-loading behavior an agent applies under `cwd` such as instructions, skills, configuration, or other agent-specific artifacts, the agent SHOULD apply the same behavior to each declared root in `additionalDirectories`, subject to the same capabilities and permissions. -If an implementation resolves conflicts across multiple roots, earlier roots in `[cwd, ...additionalDirectories]` SHOULD take precedence over later roots. Conflict resolution within a single root remains implementation-defined. +If an implementation resolves conflicts across multiple roots, earlier roots in the effective ordered root list SHOULD take precedence over later roots. Conflict resolution within a single root remains implementation-defined. ACP does not define: @@ -303,8 +305,11 @@ If a client launches an agent with direct filesystem access, `additionalDirector "id": 1, "method": "session/new", "params": { - "cwd": "/workspace/app", - "additionalDirectories": ["/workspace/libs/shared", "/workspace/skills"], + "cwd": "/home/user/project", + "additionalDirectories": [ + "/home/user/shared-lib", + "/home/user/product-docs" + ], "mcpServers": [] } } @@ -319,8 +324,11 @@ If a client launches an agent with direct filesystem access, `additionalDirector "method": "session/load", "params": { "sessionId": "sess_abc123", - "cwd": "/workspace/app", - "additionalDirectories": ["/workspace/libs/shared", "/workspace/skills"], + "cwd": "/home/user/project", + "additionalDirectories": [ + "/home/user/shared-lib", + "/home/user/product-docs" + ], "mcpServers": [] } } @@ -335,8 +343,11 @@ If a client launches an agent with direct filesystem access, `additionalDirector "method": "session/resume", "params": { "sessionId": "sess_abc123", - "cwd": "/workspace/app", - "additionalDirectories": ["/workspace/libs/shared", "/workspace/skills"], + "cwd": "/home/user/project", + "additionalDirectories": [ + "/home/user/shared-lib", + "/home/user/product-docs" + ], "mcpServers": [] } } @@ -351,8 +362,11 @@ If a client launches an agent with direct filesystem access, `additionalDirector "method": "session/fork", "params": { "sessionId": "sess_abc123", - "cwd": "/workspace/app", - "additionalDirectories": ["/workspace/libs/shared", "/workspace/skills"], + "cwd": "/home/user/project", + "additionalDirectories": [ + "/home/user/shared-lib", + "/home/user/product-docs" + ], "mcpServers": [] } } @@ -415,7 +429,7 @@ Clients MUST gate `additionalDirectories` on `sessionCapabilities.additionalDire ### Why not restore stored roots on `session/load` or `session/resume` when the field is omitted? -Even with `SessionInfo.additionalDirectories` available through `session/list`, implicit restoration would still let an agent reactivate filesystem scope that the current request did not state explicitly. That is undesirable for clients that do not use `session/list`, for clients resuming a session by ID without first listing it, and for clients that want request-time control over the active root set. This proposal therefore keeps load and resume explicit-list only for additional roots: omitting the field activates none, while supplying it is explicitly allowed and sets the complete resulting additional-root list for that request. +Even with `SessionInfo.additionalDirectories` available through `session/list`, implicit restoration would still let an agent reactivate filesystem scope that the current request did not state explicitly. That is undesirable for clients that do not use `session/list`, for clients resuming a session by ID without first listing it, and for clients that want request-time control over the active root set. This proposal therefore keeps load and resume explicit-list only for additional roots: omitting the field or supplying an empty array activates none, while supplying a non-empty array is explicitly allowed and sets the complete resulting additional-root list for that request. ### What alternative approaches did you consider, and why did you settle on this one? diff --git a/schema/schema.unstable.json b/schema/schema.unstable.json index 081e1a1f..b93adf5f 100644 --- a/schema/schema.unstable.json +++ b/schema/schema.unstable.json @@ -2126,6 +2126,13 @@ "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"] }, + "additionalDirectories": { + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nAdditional workspace roots to activate for this session. Each path must be absolute.\n\nWhen omitted or empty, no additional roots are activated. When non-empty,\nthis is the complete resulting additional-root list for the forked\nsession.", + "items": { + "type": "string" + }, + "type": "array" + }, "cwd": { "description": "The working directory for this session.", "type": "string" @@ -2467,6 +2474,13 @@ "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"] }, + "additionalDirectories": { + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nFilter sessions by the exact ordered additional workspace roots. Each path must be absolute.\n\nThis filter applies only when the field is present and non-empty. When\nomitted or empty, no additional-root filter is applied.", + "items": { + "type": "string" + }, + "type": "array" + }, "cursor": { "description": "Opaque cursor token from a previous response's nextCursor field for cursor-based pagination", "type": ["string", "null"] @@ -2513,6 +2527,13 @@ "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"] }, + "additionalDirectories": { + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nAdditional workspace roots to activate for this session. Each path must be absolute.\n\nWhen omitted or empty, no additional roots are activated. When non-empty,\nthis is the complete resulting additional-root list for the loaded\nsession.", + "items": { + "type": "string" + }, + "type": "array" + }, "cwd": { "description": "The working directory for this session.", "type": "string" @@ -2877,6 +2898,13 @@ "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"] }, + "additionalDirectories": { + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nAdditional workspace roots for this session. Each path must be absolute.\n\nThese expand the session's filesystem scope without changing `cwd`, which\nremains the base for relative paths. When omitted or empty, no\nadditional roots are activated for the new session.", + "items": { + "type": "string" + }, + "type": "array" + }, "cwd": { "description": "The working directory for this session. Must be an absolute path.", "type": "string" @@ -3484,6 +3512,13 @@ "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"] }, + "additionalDirectories": { + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nAdditional workspace roots to activate for this session. Each path must be absolute.\n\nWhen omitted or empty, no additional roots are activated. When non-empty,\nthis is the complete resulting additional-root list for the resumed\nsession.", + "items": { + "type": "string" + }, + "type": "array" + }, "cwd": { "description": "The working directory for this session.", "type": "string" @@ -3576,6 +3611,17 @@ "required": ["optionId"], "type": "object" }, + "SessionAdditionalDirectoriesCapabilities": { + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nCapabilities for additional session directories support.\n\nBy supplying `{}` it means that the agent supports the `additionalDirectories` field on\nsupported session lifecycle requests and `session/list`.", + "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" + }, "SessionCapabilities": { "description": "Session capabilities supported by the agent.\n\nAs a baseline, all Agents **MUST** support `session/new`, `session/prompt`, `session/cancel`, and `session/update`.\n\nOptionally, they **MAY** support other session methods and notifications by specifying additional capabilities.\n\nNote: `session/load` is still handled by the top-level `load_session` capability. This will be unified in future versions of the protocol.\n\nSee protocol docs: [Session Capabilities](https://agentclientprotocol.com/protocol/initialization#session-capabilities)", "properties": { @@ -3584,6 +3630,17 @@ "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"] }, + "additionalDirectories": { + "anyOf": [ + { + "$ref": "#/$defs/SessionAdditionalDirectoriesCapabilities" + }, + { + "type": "null" + } + ], + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nWhether the agent supports `additionalDirectories` on supported session lifecycle requests and `session/list`." + }, "close": { "anyOf": [ { @@ -3892,6 +3949,13 @@ "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"] }, + "additionalDirectories": { + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nAuthoritative ordered additional workspace roots for this session. Each path must be absolute.\n\nWhen omitted or empty, there are no additional roots for the session.", + "items": { + "type": "string" + }, + "type": "array" + }, "cwd": { "description": "The working directory for this session. Must be an absolute path.", "type": "string" diff --git a/src/agent.rs b/src/agent.rs index b8ce81de..d67a6698 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -893,6 +893,18 @@ impl AuthMethodTerminal { pub struct NewSessionRequest { /// The working directory for this session. Must be an absolute path. pub cwd: PathBuf, + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// Additional workspace roots for this session. Each path must be absolute. + /// + /// These expand the session's filesystem scope without changing `cwd`, which + /// remains the base for relative paths. When omitted or empty, no + /// additional roots are activated for the new session. + #[cfg(feature = "unstable_session_additional_directories")] + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub additional_directories: Vec, /// List of MCP (Model Context Protocol) servers the agent should connect to. pub mcp_servers: Vec, /// The _meta property is reserved by ACP to allow clients and agents to attach additional @@ -909,11 +921,25 @@ impl NewSessionRequest { pub fn new(cwd: impl Into) -> Self { Self { cwd: cwd.into(), + #[cfg(feature = "unstable_session_additional_directories")] + additional_directories: vec![], mcp_servers: vec![], meta: None, } } + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// Additional workspace roots for this session. Each path must be absolute. + #[cfg(feature = "unstable_session_additional_directories")] + #[must_use] + pub fn additional_directories(mut self, additional_directories: Vec) -> Self { + self.additional_directories = additional_directories; + self + } + /// List of MCP (Model Context Protocol) servers the agent should connect to. #[must_use] pub fn mcp_servers(mut self, mcp_servers: Vec) -> Self { @@ -1042,6 +1068,18 @@ pub struct LoadSessionRequest { pub mcp_servers: Vec, /// The working directory for this session. pub cwd: PathBuf, + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// Additional workspace roots to activate for this session. Each path must be absolute. + /// + /// When omitted or empty, no additional roots are activated. When non-empty, + /// this is the complete resulting additional-root list for the loaded + /// session. + #[cfg(feature = "unstable_session_additional_directories")] + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub additional_directories: Vec, /// The ID of the session to load. pub session_id: SessionId, /// The _meta property is reserved by ACP to allow clients and agents to attach additional @@ -1059,11 +1097,25 @@ impl LoadSessionRequest { Self { mcp_servers: vec![], cwd: cwd.into(), + #[cfg(feature = "unstable_session_additional_directories")] + additional_directories: vec![], session_id: session_id.into(), meta: None, } } + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// Additional workspace roots to activate for this session. Each path must be absolute. + #[cfg(feature = "unstable_session_additional_directories")] + #[must_use] + pub fn additional_directories(mut self, additional_directories: Vec) -> Self { + self.additional_directories = additional_directories; + self + } + /// List of MCP servers to connect to for this session. #[must_use] pub fn mcp_servers(mut self, mcp_servers: Vec) -> Self { @@ -1185,6 +1237,18 @@ pub struct ForkSessionRequest { pub session_id: SessionId, /// The working directory for this session. pub cwd: PathBuf, + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// Additional workspace roots to activate for this session. Each path must be absolute. + /// + /// When omitted or empty, no additional roots are activated. When non-empty, + /// this is the complete resulting additional-root list for the forked + /// session. + #[cfg(feature = "unstable_session_additional_directories")] + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub additional_directories: Vec, /// List of MCP servers to connect to for this session. #[serde(default, skip_serializing_if = "Vec::is_empty")] pub mcp_servers: Vec, @@ -1204,11 +1268,25 @@ impl ForkSessionRequest { Self { session_id: session_id.into(), cwd: cwd.into(), + #[cfg(feature = "unstable_session_additional_directories")] + additional_directories: vec![], mcp_servers: vec![], meta: None, } } + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// Additional workspace roots to activate for this session. Each path must be absolute. + #[cfg(feature = "unstable_session_additional_directories")] + #[must_use] + pub fn additional_directories(mut self, additional_directories: Vec) -> Self { + self.additional_directories = additional_directories; + self + } + /// List of MCP servers to connect to for this session. #[must_use] pub fn mcp_servers(mut self, mcp_servers: Vec) -> Self { @@ -1345,6 +1423,18 @@ pub struct ResumeSessionRequest { pub session_id: SessionId, /// The working directory for this session. pub cwd: PathBuf, + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// Additional workspace roots to activate for this session. Each path must be absolute. + /// + /// When omitted or empty, no additional roots are activated. When non-empty, + /// this is the complete resulting additional-root list for the resumed + /// session. + #[cfg(feature = "unstable_session_additional_directories")] + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub additional_directories: Vec, /// List of MCP servers to connect to for this session. #[serde(default, skip_serializing_if = "Vec::is_empty")] pub mcp_servers: Vec, @@ -1364,11 +1454,25 @@ impl ResumeSessionRequest { Self { session_id: session_id.into(), cwd: cwd.into(), + #[cfg(feature = "unstable_session_additional_directories")] + additional_directories: vec![], mcp_servers: vec![], meta: None, } } + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// Additional workspace roots to activate for this session. Each path must be absolute. + #[cfg(feature = "unstable_session_additional_directories")] + #[must_use] + pub fn additional_directories(mut self, additional_directories: Vec) -> Self { + self.additional_directories = additional_directories; + self + } + /// List of MCP servers to connect to for this session. #[must_use] pub fn mcp_servers(mut self, mcp_servers: Vec) -> Self { @@ -1578,6 +1682,17 @@ pub struct ListSessionsRequest { /// Filter sessions by working directory. Must be an absolute path. #[serde(skip_serializing_if = "Option::is_none")] pub cwd: Option, + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// Filter sessions by the exact ordered additional workspace roots. Each path must be absolute. + /// + /// This filter applies only when the field is present and non-empty. When + /// omitted or empty, no additional-root filter is applied. + #[cfg(feature = "unstable_session_additional_directories")] + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub additional_directories: Vec, /// Opaque cursor token from a previous response's nextCursor field for cursor-based pagination #[serde(skip_serializing_if = "Option::is_none")] pub cursor: Option, @@ -1603,6 +1718,18 @@ impl ListSessionsRequest { self } + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// Filter sessions by the exact ordered additional workspace roots. Each path must be absolute. + #[cfg(feature = "unstable_session_additional_directories")] + #[must_use] + pub fn additional_directories(mut self, additional_directories: Vec) -> Self { + self.additional_directories = additional_directories; + self + } + /// Opaque cursor token from a previous response's nextCursor field for cursor-based pagination #[must_use] pub fn cursor(mut self, cursor: impl IntoOption) -> Self { @@ -1680,6 +1807,17 @@ pub struct SessionInfo { pub session_id: SessionId, /// The working directory for this session. Must be an absolute path. pub cwd: PathBuf, + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// Authoritative ordered additional workspace roots for this session. Each path must be absolute. + /// + /// When omitted or empty, there are no additional roots for the session. + #[cfg(feature = "unstable_session_additional_directories")] + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub additional_directories: Vec, + /// Human-readable title for the session #[serde(skip_serializing_if = "Option::is_none")] pub title: Option, @@ -1701,12 +1839,26 @@ impl SessionInfo { Self { session_id: session_id.into(), cwd: cwd.into(), + #[cfg(feature = "unstable_session_additional_directories")] + additional_directories: vec![], title: None, updated_at: None, meta: None, } } + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// Authoritative ordered additional workspace roots for this session. Each path must be absolute. + #[cfg(feature = "unstable_session_additional_directories")] + #[must_use] + pub fn additional_directories(mut self, additional_directories: Vec) -> Self { + self.additional_directories = additional_directories; + self + } + /// Human-readable title for the session #[must_use] pub fn title(mut self, title: impl IntoOption) -> Self { @@ -3320,6 +3472,7 @@ impl AgentCapabilities { /// /// See protocol docs: [Session Capabilities](https://agentclientprotocol.com/protocol/initialization#session-capabilities) #[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] #[non_exhaustive] pub struct SessionCapabilities { /// Whether the agent supports `session/list`. @@ -3329,6 +3482,14 @@ pub struct SessionCapabilities { /// /// This capability is not part of the spec yet, and may be removed or changed at any point. /// + /// Whether the agent supports `additionalDirectories` on supported session lifecycle requests and `session/list`. + #[cfg(feature = "unstable_session_additional_directories")] + #[serde(skip_serializing_if = "Option::is_none")] + pub additional_directories: Option, + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// /// Whether the agent supports `session/fork`. #[cfg(feature = "unstable_session_fork")] #[serde(skip_serializing_if = "Option::is_none")] @@ -3371,6 +3532,21 @@ impl SessionCapabilities { self } + /// **UNSTABLE** + /// + /// This capability is not part of the spec yet, and may be removed or changed at any point. + /// + /// Whether the agent supports `additionalDirectories` on supported session lifecycle requests and `session/list`. + #[cfg(feature = "unstable_session_additional_directories")] + #[must_use] + pub fn additional_directories( + mut self, + additional_directories: impl IntoOption, + ) -> Self { + self.additional_directories = additional_directories.into_option(); + self + } + #[cfg(feature = "unstable_session_fork")] /// Whether the agent supports `session/fork`. #[must_use] @@ -3427,6 +3603,47 @@ impl SessionListCapabilities { 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 + } +} + +/// **UNSTABLE** +/// +/// This capability is not part of the spec yet, and may be removed or changed at any point. +/// +/// Capabilities for additional session directories support. +/// +/// By supplying `{}` it means that the agent supports the `additionalDirectories` field on +/// supported session lifecycle requests and `session/list`. +#[cfg(feature = "unstable_session_additional_directories")] +#[derive(Default, Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[non_exhaustive] +pub struct SessionAdditionalDirectoriesCapabilities { + /// 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, +} + +#[cfg(feature = "unstable_session_additional_directories")] +impl SessionAdditionalDirectoriesCapabilities { + #[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. @@ -4333,6 +4550,106 @@ mod test_serialization { assert!(matches!(deserialized, AuthMethod::Agent(_))); } + #[cfg(feature = "unstable_session_additional_directories")] + #[test] + fn test_session_additional_directories_serialization() { + assert_eq!( + serde_json::to_value(NewSessionRequest::new("/home/user/project")).unwrap(), + json!({ + "cwd": "/home/user/project", + "mcpServers": [] + }) + ); + assert_eq!( + serde_json::to_value( + NewSessionRequest::new("/home/user/project").additional_directories(vec![ + PathBuf::from("/home/user/shared-lib"), + PathBuf::from("/home/user/product-docs"), + ]) + ) + .unwrap(), + json!({ + "cwd": "/home/user/project", + "additionalDirectories": [ + "/home/user/shared-lib", + "/home/user/product-docs" + ], + "mcpServers": [] + }) + ); + assert_eq!( + serde_json::to_value( + ListSessionsRequest::new().additional_directories(Vec::::new()) + ) + .unwrap(), + json!({}) + ); + assert_eq!( + serde_json::to_value(SessionInfo::new("sess_abc123", "/home/user/project")).unwrap(), + json!({ + "sessionId": "sess_abc123", + "cwd": "/home/user/project" + }) + ); + assert_eq!( + serde_json::to_value( + SessionInfo::new("sess_abc123", "/home/user/project").additional_directories(vec![ + PathBuf::from("/home/user/shared-lib"), + PathBuf::from("/home/user/product-docs"), + ]) + ) + .unwrap(), + json!({ + "sessionId": "sess_abc123", + "cwd": "/home/user/project", + "additionalDirectories": [ + "/home/user/shared-lib", + "/home/user/product-docs" + ] + }) + ); + assert_eq!( + serde_json::from_value::(json!({ + "sessionId": "sess_abc123", + "cwd": "/home/user/project" + })) + .unwrap() + .additional_directories, + Vec::::new() + ); + + assert_eq!( + serde_json::from_value::(json!({})) + .unwrap() + .additional_directories, + Vec::::new() + ); + + assert_eq!( + serde_json::from_value::(json!({ + "additionalDirectories": [] + })) + .unwrap() + .additional_directories, + Vec::::new() + ); + } + + #[cfg(feature = "unstable_session_additional_directories")] + #[test] + fn test_session_additional_directories_capabilities_serialization() { + assert_eq!( + serde_json::to_value( + SessionCapabilities::new() + .additional_directories(SessionAdditionalDirectoriesCapabilities::new()) + ) + .unwrap(), + json!({ + "additionalDirectories": {} + }) + ); + } + #[cfg(feature = "unstable_auth_methods")] #[test] fn test_auth_method_env_var_serialization() {