VS Code/code-server extension: resumed/new tabs silently ignore claudeCode.initialPermissionMode after extension update — permissionMode stuck undefined
Summary
After the extension auto-updates (observed: 2.1.225 → 2.1.226), previously-open and even brand-new chat tabs can silently lose "bypass permissions" mode and start prompting for approval on every single tool call — despite claudeCode.initialPermissionMode: "bypassPermissions" and claudeCode.allowDangerouslySkipPermissions: true being correctly set. There is no user-visible settings change; it looks like random breakage after an update, and there is no supported way to recover it project-wide.
Environment
- code-server 4.115.0, extension
anthropic.claude-code2.1.226, linux x86_64 ~/.claude/settings.json→permissions.defaultMode: "bypassPermissions"- VS Code settings →
claudeCode.initialPermissionMode: "bypassPermissions",claudeCode.allowDangerouslySkipPermissions: true
What the extension's own debug log shows
.../exthostN/Anthropic.claude-code/Claude VSCode.log, same extension host, same settings, different tabs:
Spawning Claude with SDK query function - ... permission mode: bypassPermissions, resume: <sessionA>
Spawning Claude with SDK query function - ... permission mode: undefined, resume: <sessionB>
Spawning Claude with SDK query function - ... permission mode: undefined, resume: undefined <- brand-new conversation, first message ever
A genuinely brand-new conversation (resume: undefined) can already come up with permission mode: undefined.
Root cause (traced through the shipped, minified extension.js / webview/index.js)
- In the webview bundle, each conversation view-model initializes its permission-mode field to
undefined(permissionMode=ct(void 0)in the minified build). It only gets populated fromconfig.initialPermissionModein specific construction paths (new-session creation, "teleport"). The path used when the webview reconnects and restores/resumes an already-open tab does not apply this fallback, so the field staysundefinedfor that tab's entire remaining lifetime. launchClaude(...)always includespermissionModeas an explicit key in thelaunch_claudemessage (even whenundefined), which flows into the SDK query options (permissionMode:oinsidespawnClaude). This appears to suppress the CLI's own--setting-sources-based resolution ofpermissions.defaultModefrom~/.claude/settings.json. Confirmed independently: invoking the packagedclaudebinary directly with no--permission-modeflag (same--setting-sources user,project,local) correctly resolvesbypassPermissionsand does not prompt — so the CLI-side resolution itself is fine; only the SDK-driven launch path from the extension is affected.- Separately,
migrateAllSettings()calls a one-time migration,clearPersistedPermissionModeOnce()(guarded by apermissionModeCleared20260804globalState flag), which unconditionally clears any previously-persisteddefaultPermissionModefrom extensionglobalStatethe first time this code runs on a given machine/profile — with no re-derivation from current config afterward. On our machine it fired for the first time on today's auto-update to 2.1.226, immediately breaking every already-open tab that had implicitly been relying on that persisted value.
Impact
Any long-running or mostly-non-interactive workflow that keeps chat tabs open across days (common for background/automation-style usage of the extension) silently reverts to prompting on every tool call the moment the extension auto-updates — with no corresponding, visible settings change, so it presents as unexplained regression. We found no supported recovery short of manually re-toggling permission mode inside each affected tab's own UI one at a time.
Expected
Restoring/resuming an existing tab, or creating a brand-new conversation, should derive its initial permission mode from claudeCode.initialPermissionMode (subject to claudeCode.allowDangerouslySkipPermissions) whenever the user hasn't explicitly overridden it for that specific conversation — consistently, across all construction/restore paths, not just some of them.
Repro
- Set
claudeCode.initialPermissionMode: "bypassPermissions"andclaudeCode.allowDangerouslySkipPermissions: true. - Open a chat tab, do some work.
- Reload the window / let the extension host restart (e.g. a code-server reconnect), or just leave the tab backgrounded and switch away and back.
- Tool calls in that tab now prompt for permission despite the settings above, and a brand-new tab created afterward is not guaranteed to be unaffected either.