Skip to content

feat(providers): named provider instances (#1512 I1, #453) - #1516

Merged
currentbits merged 7 commits into
mainfrom
coder/1512-i1-named-provider-instances-1294ea
Oct 6, 2026
Merged

currentbits merged 7 commits into
mainfrom
coder/1512-i1-named-provider-instances-1294ea

Conversation

@currentbits

Copy link
Copy Markdown
Owner

Item I1 of #1512, also #453.

What changed: you can add named instances of claude and codex, such as "Claude (work)" or "Codex (personal)", each with its own config dir (CLAUDE_CONFIG_DIR / CODEX_HOME, both verified against the real CLIs) and extra env. Instances can be picked anywhere a provider can: the model picker, Settings › Agents, agent profiles, per-project defaults (#1503) and failover chains.

How it works: a thread keeps provider (the base id) and gains providerInstance; single-string refs use claude:<id>. Every provider path resolves through the instance:

  • Spawn env: the instance's config dir and env are applied per spawn only. process.env is never touched, and a test proves a plain thread running beside an instance thread gets none of it.
  • Session resume: switching instance drops the session, because a different config dir is a different login.
  • Accounting: usage and quota are recorded per instance, and sign-in probes (feat: provider sign-in status with one-click Sign in; onboarding finds recent repos (#1501 G1) #1505) run under the instance env.
  • Sign in: the sign-in terminal carries only the config dir.
  • Guardrail hooks: Claude's work as-is; Codex's run through a per-thread copy of the instance home. Also fixed here: a stale auth.json link on instance switch, which would have run the next turn on the old account (tested).
  • Secrets: instance env is sealed like MCP server env and never logged.
  • Safety rules: a thread whose instance was deleted fails loudly rather than falling back to the default account. Instances are refused on remote and WSL projects.

Not covered yet:

  • workflow phases, automations, and skill or harness imports (these use the base default);
  • opencode, grok, kimi and muse;
  • CLAUDE_CODE_PROJECT_DIR_NAME, deliberately skipped because it would break session import.

Verified independently, on main merged with this branch:

  • typecheck exits 0 and vite build passes.
  • test:renderer: 2863 pass, 0 fail, 0 cancelled.
  • test:electron: 4309 pass, 0 fail, 0 cancelled.

Merge note: conflicts with #1513 (I3); I'll merge main and resolve after it lands.

🤖 Generated with Claude Code

Willem and others added 5 commits October 6, 2026 18:12
settings.providerInstances holds extra configs of claude and codex, each
with its own config folder (CLAUDE_CONFIG_DIR / CODEX_HOME) and env.
Threads keep the base provider id and gain providerInstance; places that
store a provider as one string use the ref <provider>:<instance>.
Instance env values are sealed at rest like MCP env.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Spawn env: claude and codex children get the instance overlay per spawn,
never via process.env; the kept-alive claude key includes it, so switching
instance respawns. A removed instance or a remote project fails the send
instead of running on the default account.

Also: the codex guardrail overlay drops links into a previous home (a
thread moving instance kept the old auth.json), session resume and the
eject command use the instance's folder, forks keep the instance, spend
is recorded per instance, the failover chain and project defaults take
refs, sign-in probes and quota fetches run under each instance's env.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Settings › Agents › Providers lists each instance under its base with its
own sign-in state, plus Add instance / Edit / Delete: provider, name,
config folder, and env rows with masked values. The model picker shows an
instance as its own group; the thread's current pick is its ref, so the
fresh-session warning covers switching accounts too. Marks and colours
follow the base provider.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#453)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-06T16:39:07.305011Z c00e306 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c00e3064f2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// patch config + provenance. sessionId stays null (fresh session).
const forkPatch = {
provider: nextProvider,
...(nextInstance ? { providerInstance: nextInstance } : {}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Clear the default instance when forking to a base provider

Always include providerInstance in this patch, even when nextInstance is null. If the global default is a named instance, createThread() initially assigns that instance; a fork from a base-provider thread, or an explicit handoff to another base provider, then omits this field and retains the default instance. The fork can consequently run on the wrong account or fail because its base provider and retained instance do not match.

Useful? React with 👍 / 👎.

Comment on lines +176 to +179
if (Object.prototype.hasOwnProperty.call(patch, "providerInstances")) {
this.data.settings.providerInstances = validateProviderInstances(
patch.providerInstances,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Invalidate sessions when an instance configuration changes

When an existing instance's config directory or account-related environment is edited, this replacement leaves every thread on that instance with its old sessionId. The next run respawns the CLI under the new configuration but passes the session ID from the previous account/config directory, causing resume failures or potentially attaching to an unrelated session with the same ID. Treat execution-affecting edits like an instance switch by clearing affected sessions, or make those fields immutable while the instance ID is retained.

Useful? React with 👍 / 👎.

Willem added 2 commits October 6, 2026 18:40
# Conflicts:
#	electron/test/auto-settle-settings.test.js
#	electron/test/budget-spend.test.js
@currentbits

Copy link
Copy Markdown
Owner Author

Merged main into this branch (now includes #1513, #1514 and #1515). Resolved the expected-settings fixtures in auto-settle-settings.test.js and budget-spend.test.js: main's resumeInterruptedRuns: false plus this branch's providerInstances: [].

Verified on the merged tree:

  • typecheck exits 0 and vite build passes.
  • test:renderer: 2882 pass, 0 fail, 0 cancelled.
  • test:electron: 4346 pass. The 1 failure was provider-switch-context under full-suite load, a known flake that reads a half-written log file; it passes 12 of 12 alone. Fixing that test separately.

@currentbits
currentbits merged commit 3e1a578 into main Oct 6, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant