mirror of
https://github.com/agentclientprotocol/claude-agent-acp.git
synced 2026-11-11 03:41:17 +00:00
Implements the [`session/set_title` RFD](https://agentclientprotocol.com/rfds/session-set-title) on ACP v1 and v2. ## What clients get - v1 advertises `agentCapabilities.sessionCapabilities.title: {}` and v2 advertises `capabilities.session.title: {}`. Both serve `session/set_title` for every client. - The session does not need to be loaded. A session that runs on this connection is renamed through its CLI (`rename_session`). Any other session gets the `custom-title` and `agent-name` records in every copy of its transcript, plus the `custom-title.json` sidecar, as the CLI's `/rename` writes them. - The title is normalized the way `_session/rename` normalizes it: whitespace collapsed, cut to the CLI's 200 characters. The response carries the stored title, as that client's `session/list` shows it. On v1 an AIR client gets it without the `[archived] ` prefix. An archived session stays archived. - A loaded session publishes `session_info_update { title }`. A title generation in flight finishes first, and no generated title replaces the client's. - Errors: a title that is empty after normalization returns `-32602`. A session that is not loaded and has no transcript returns `-32002`. On v2, a subagent session is refused, as close and delete refuse it. ## Structure - The transcript writer behind `_session/rename` lived in `src/v1/session-index/`, which v2 cannot import. Everything there except `connection.ts` moves to the shared `src/session-index/` with no change beyond import paths. `connection.ts` becomes `src/v1/session-index.ts`. - `src/session-index/rename.ts` holds the rename logic that used to be in `SessionIndexConnection.rename`/`retitle`. `_session/rename` and `session/set_title` on both versions call it. - Clients without `sessionIndex` (and every v2 client) use a `SessionIndexService` with no list and no watcher (`transcriptWriter`). Their `session/delete` now runs inside that service's per-session `exclusive()`, so a delete cannot race a rename that is still appending records. - If a restart or resume starts a new query of a v2 session while a rename runs, v2 renames through the new query too. Its CLI could otherwise hold the old title and write it back. This doesn't use the SDK's top-level `renameSession()`. It writes only a `custom-title` record to the first copy of the transcript it finds, with no `agent-name` and no sidecar, so a session renamed earlier through `/rename` would keep its old name in the CLI and in AIR. ## Tests - v1 (`session-index.test.ts`): a stored session for a client without the session index; an archived session for three clients (AIR with `sessionIndex`, AIR without it, a client that is not AIR); unknown session and empty title. - v2 (`acp-v2.test.ts`): an active session through its CLI with the published update; a stored session's records; the rename that a resume overtakes; unknown session and empty title. - Golden files: only the `initialize` capability line changes. `compare.ts` now allows the `title` capability against the origin/main baseline. - Not run: `npm run test:integration` against the real CLI. The delete/rename ordering has no test, since it is a timing race.