agentHost: adding a Claude credential while gated at "Sign in to use Agents" needs a restart
- Dominant language
- TypeScript
- Stars
- 193k
- Forks
- 42.4k
- PR merge metrics
- PR metrics pending
Description
### Symptom
A signed-out user with no Anthropic credential opens the Agents window and hits the **"Sign in to use Agents"** gate. They then set up a Claude credential (an `ANTHROPIC_API_KEY` / `CLAUDE_CODE_OAUTH_TOKEN` in `~/.claude/settings.json` or the environment) and return to the window.
Nothing happens. The gate stays up until the app is restarted — even though the user now has exactly the setup the signed-out feature exists to serve.
### Why
The gate is `resolveAgentAuthRequirement`, which needs **both** halves to flip, and neither is reactive at agent level while no session exists:
1. **`protectedResources[].required`** — `ClaudeAgent.getProtectedResources()` reads the credential on demand, but agent infos are only republished on the `agents` observable, `onDidCustomizationsChange`, or `RootConfigChanged`. A credential file appearing triggers none of them.
2. **`models.length`** — `_refreshModels()` gates the native catalog on `detectExistingClaudeSetup`, so it would pick the credential up, but nothing calls it. `AgentModelRefreshScheduler._refreshActive` only ticks providers in `_activeSinceLastRefresh`, which is populated exclusively by `onDidStartTurn`. A gated user has never started a turn, so Claude is never refreshed. Catch-22.
`ClaudeCustomizationWatcher` does watch `/.claude` recursively, but it's constructed per **session** (`claudeAgentSession.ts`) — and the gated scenario has zero sessions.
### Note on scope
Half of this is long-standing: before #329718 the same user was already stuck, via `models: 0` → `Unusable`. #329718 made `required` a second stale input but did not create the stuck state, and fixing only `required` would not unstick anyone, because the model count would still be `0`. **Both halves have to move together**, which is why it wasn't folded into that PR.
### Sketch
- Add an optional `onDidChangeProtectedResources?: Event` to `IAgent`, wired in `AgentSideEffects._registerAgentListeners` next to `onDidCustomizationsChange` so it calls `_publishAgentInfos`. (Reusing `onDidCustomizationsChange` for this would work mechanically but is the wrong signal, and the repo guidance discourages events driving cross-component control flow.)
- Give `ClaudeAgent` an agent-lifetime watcher on the credential sources, firing that event **and** kicking `_startModelRefresh()` when usability actually flips, so both gate inputs update together.
- Prefer a correlated watcher (`fileService.createWatcher`) per the repo guideline, debounced like `ClaudeCustomizationWatcher`.
Worth checking whether Codex has the same shape for its own native providers.
Found during review of #329718 (thanks @copilot-pull-request-reviewer for spotting the reactivity gap).
Contributor guide
Assessment
This issue has not been assessed yet.