microsoft / microsoft/vscode

Agent host: restored Claude sessions lose their slash commands, skills and agents until the next message

Open
#332,047 0 comments 0 reactions 1 assignee Claimed by @roblourens View on GitHub
Dominant language
TypeScript
Stars
193k
Forks
42.4k
PR merge metrics
PR metrics pending

Description

When an agent-host Claude session's live runtime is no longer resident, `getChatCustomizations` returns an empty list, so the session loses every customization-derived surface: project slash commands, plugin and built-in skills, custom agents, rules and hooks. A session created in the same window has all of them.

## Steps to reproduce

1. Open a Claude agent-host session in a workspace that has `.claude/commands/*.md` and send at least one message.
2. Put the session's runtime out of residency: close the window and reopen it, restart the host, or leave the session unsubscribed until idle eviction releases it.
3. Select the restored session and type `/` in the chat input.

**Expected:** the completions a session created in this window offers, namely the project's commands plus the built-in skills.

**Actual:** only host-level commands appear (`/rename`). Sending any message restores the full list.

Observed on a live host, two sessions in the same workspace seconds apart, with the identical request `{"kind":"userMessage","text":"/","offset":1}`:

| session | state | completion items |
|---|---|---|
| created in this window | live runtime | 18 (5 project commands + 13 built-in skills) |
| restored into this window | backing only | 1 (`/rename`) |

The restored session is missing the read-only built-ins (`agent-builtin:/skill/*`) as well as the project commands, which is what distinguishes this from a directory scan that failed to read the workspace.

## Root cause

`ClaudeAgent.getChatCustomizations` resolves the chat through its **live** runtime:

```ts
const sess = this._findChatByUri(chat);
if (!sess) {
return [];
}
```

`_findChatByUri` maps the chat to its backing's `sdkSessionId` and looks that up in `_chatEntriesBySdkId`, which holds only live `ClaudeAgentSession` wrappers. Two ordinary states have a backing but no wrapper:

- `_releaseChat` disposes the live session on idle eviction and deliberately keeps the backing: "`_chatBackings` retains the backing across release so the chat resolves uniformly on the next cold resume-on-send".
- `materializeChat` re-attaches the backing for a session restored into a fresh process, and registers no wrapper by design.

In both, the read returns `[]`.

Nothing downstream recovers from that:

- `AgentService._doRestoreSession` calls `getChatCustomizations` on exactly this path and seeds state only `if (restoredCustomizations && restoredCustomizations.length > 0)`, so `SessionState.customizations` stays `undefined`.
- `AgentHostSkillCompletionProvider._getCandidates` resolves `/` completions through the same call, so the completion list carries no commands or skills.
- `AgentSideEffects._publishSessionCustomizations` returns early when the resolved set matches session state, so no `SessionCustomizationsChanged` is dispatched either. Confirmed on the live host: the restored session received zero of those actions over its lifetime, while the sessions created in the window received theirs.

The empty list is not the intended pre-materialize behaviour. `ClaudeAgentSession.getSessionCustomizations` supports a session with no SDK Query explicitly, and documents it: "Pre-materialize there is no Query, so the full disk set is shown". A restored chat never reaches that code.

The sibling providers do not share the problem. `CopilotAgent.getChatCustomizations` resolves from `resolveAgentChatContext(context, chat).configurationResource` and handles the "Provisional (pre-send) or pre-resume" case explicitly; `CodexAgent.getChatCustomizations` resolves through `_resolveConversationSession`. `ClaudeAgent` is the only provider keyed on a live runtime, and the only one that ignores its `context` argument.

## Suggested fix

Bring the chat's runtime up from its exact backing when it is not resident, reusing `_createProvisionalChatSession`, which recovers the working directories from the SDK transcript `cwd` or the persisted overlay and starts no SDK Query. That reaches the pre-materialize path `getSessionCustomizations` already implements, and leaves the live-runtime case untouched. I have this working locally with a regression test and am happy to open a PR.

## Version

Visual Studio Code 1.1.3 (stable), server commit `110a328ea54b42367b803ec53ee0bf52ef26b419`, dev container on linux-arm64. Confirmed against `main` in the source refs quoted above.

*AI disclosure: this issue and the related code were written with the assistance of AI.*

### Public patches and patcher scripts

[Public patch catalog and patcher scripts](https://github.com/RyanEwen/vscode-patches/blob/main/CATALOG.md) · [Source patch index](https://github.com/RyanEwen/vscode-patches/blob/main/SOURCE-PATCHES.md). The [public collection](https://github.com/RyanEwen/vscode-patches) includes the maintained patchers, rollback instructions, regression scripts, and historical snapshots. Build restrictions and exact installer coverage are documented there.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.