Skip to content

Chief: run DeepInfra models with your own API key - #21280

Draft
austinywang wants to merge 24 commits into
agent-cmux-instinct-b77acc19from
chief-deepinfra-byok
Draft

austinywang wants to merge 24 commits into
agent-cmux-instinct-b77acc19from
chief-deepinfra-byok

Conversation

@austinywang

@austinywang austinywang commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #21063 (Chief). This targets that branch, not main.

Summary

Chief could only run Anthropic models through Claude Code or OpenAI models through Codex. This PR adds a DeepInfra harness, so you can pick open models from Chief Settings and pay DeepInfra with your own key:

  • DeepSeek V4 Pro (the default)
  • Kimi K3
  • GLM-5.3
  • DeepSeek V4 Flash

How it works: DeepInfra serves an Anthropic-compatible Messages API at https://api.deepinfra.com/anthropic. The DeepInfra harness is the existing ClaudeCodeBrain with a new ChiefModelEndpoint:

  • Settings: everything reaches Claude Code through --settings. Flag settings outrank the project settings that Chief's claude loads from its working directory, which is ~/.claude/settings.json when Chief runs from the home folder. The JSON:
    • sets the base URL;
    • points every model slot at a DeepInfra model (Opus, Sonnet, Fable, the subagent model, and Haiku for background calls);
    • blanks Anthropic credentials, custom headers and provider switches.
  • No secret in transit: the JSON carries no secret. An apiKeyHelper reads the saved key file, falling back to DEEPINFRA_API_KEY, so the key is never in argv, in the app's configuration, or in the environment the model's tools inherit.
  • Environment stripping: the app's own ANTHROPIC_*, CLAUDE_CODE_USE_* and Claude Code token/descriptor variables are removed from claude's environment.
  • Effort: DeepInfra models offer no effort levels.
  • Key storage: the key is saved in a new chief.deepInfraAPIKey SecretFileKey, the same 0600 secret-file store as the socket password, never cmux.json. You can enter it in two places:
    • the in-pane Chief Settings, as a row under the harness picker, shown only for DeepInfra;
    • Settings > Automation > Chief.
  • Key changes: saving or removing the key restarts the brain for the next turn.
  • Errors: a missing key fails the turn before launch, with a pointer to Chief Settings. A rejected key (authentication_error, DeepInfra's 401) and an unknown model both name DeepInfra.

This is milestone 1, bring-your-own-key. Milestone 2 is usage credits included with cmux plans, with paid top-ups, through coderouter. It only changes the endpoint's base URL and token, so this harness and UI carry over.

Testing

  • Added ChiefDeepInfraTests and DeepInfra rows in ChiefModelCatalogTests. They cover:
    • the endpoint environment and model slots;
    • removal of Anthropic credentials from a polluted environment;
    • the token never appearing in description or debugDescription;
    • the missing-key failure, with no launch;
    • rejected-key and unknown-model messages;
    • harness parsing.
  • Red then green: the test commit (1995a0c) comes before the fix. The red run was not executed: the build host cmux-lawrence-2 does not resolve from this Mac (REPAIR.md row for nx-remote), and local swift test is off-limits by policy. Red holds by construction, since the tests reference ChiefModelEndpoint, .deepInfra and missingCredential, which do not exist before the fix. Green is the CmuxChief package-test lane on this PR.
  • Ran locally:
    • python3 scripts/verify-local.py --affected <base> --swift-changed <base> passed 10/10. That covers Swift syntax, xcstrings, localization parity and defaults, pbxproj, the embedded config schema, app-source wiring, test wiring, package groups and feature flags.
    • Native compilation was not checked locally.
  • Live check against the claude 2.1.302 binary, with local listeners and project settings that set a different base URL, an Anthropic key, a token and an apiKeyHelper. With this PR's --settings shape, only the endpoint is contacted, and both auth headers carry only the key from the file (whose path contains a space). Without it, the settings' URL and credentials win. See the review comment.
  • Endpoint probe: an invalid key against DeepInfra's Anthropic endpoint returns 401 {"type":"error","error":{"type":"authentication_error",…}}, which the rejected-key mapping matches.
  • Not yet verified: a live Chief turn against DeepInfra with a real key in a tagged build, i.e. tool use through Claude Code on these models. No DeepInfra key was available.

Localization audit: 15 new keys, each translated into all 9 macOS locales. The two interpolated chief.brain.* strings were added by hand with %@. DeepInfra, the harness name, has an identity-locale record like the Claude Code and Codex names. localization_catalog.py check reports 0 parity errors.

Changelog

Added: Chief can run DeepInfra models (DeepSeek, Kimi, GLM) with your own DeepInfra API key, set in Chief Settings

Proof

No screenshot yet. It will be attached after the tagged-build dogfood with a real key.

Checklist

  • Behavior changes have added or updated tests, or Testing says why not
  • UI, settings, menu, schema, help-text or user-facing docs change: localization audited, and the result is stated above
  • Reviewed with a subagent before merge; findings and resolutions are posted on the PR

🤖 Generated with Claude Code

austinywang and others added 2 commits October 16, 2026 20:40
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Chief gains a DeepInfra harness: Claude Code against DeepInfra's
Anthropic-compatible API, with DeepSeek V4 Pro (default), Kimi K3,
GLM-5.3 and DeepSeek V4 Flash in the model picker.

The key is saved from Chief Settings or Settings > Automation into its
own 0600 secret file (chief.deepInfraAPIKey, never cmux.json), falling
back to DEEPINFRA_API_KEY. Saving or removing it installs a new brain
for the next turn. The app's Anthropic credentials, custom headers and
Bedrock/Vertex switches are stripped before claude starts, so they never
reach DeepInfra. A missing or rejected key fails the turn with a pointer
to Chief Settings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 17, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@blacksmith-sh

This comment has been minimized.

@github-actions

github-actions Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Passes: CI passes on bc42236cd3.

CI passes on bc42236cd3 (run 39116868069 attempt 1).

Written by scripts/ci/classify_failures.py (ci-failure-attribution.yml); signatures are its SIGNATURES table. A machine verdict is the runner's fault, not this PR's; yours means the failing file is one this PR changes, also red on main that main's latest full suite fails the same way, seen on other PRs that it failed on another pull request's run lately.

@github-actions

github-actions Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

CI fast guards passes on bc42236cd3 (https://github.com/manaflow-ai/cmux/actions/runs/39116868069). This covers only the fast guards, not CI: CI's result is the ci-status check, and the CI failure attribution comment names any failing test.

@github-actions

github-actions Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Dogfood tours of bc42236c

chief-home-tour at bc42236c: not run

skipped: CI built this head on a runner pool whose products the UI test Macs cannot load, and media never compiles one; gh workflow run pr-media.yml -f pr=&lt;n&gt; -f allow_compile=true does

Tours are picked by the paths globs in dogfood/scenarios/*.json; a Dogfood-tours: a, b line in the description picks them instead (none turns this off). Look at every frame before merging: a green tour only means no step failed.

… env

Review found that Chief's `claude` reads ~/.claude/settings.json as
project settings (cwd is the home folder), and a settings `env` block
replaces the process environment: a local repro sent the settings'
Anthropic key and token to the settings' URL instead of the endpoint.

The endpoint now reaches Claude Code through `--settings`, which
outranks project settings: base URL, model slots, and blanked Anthropic
credentials and provider switches. The JSON carries no secret; an
apiKeyHelper reads the saved 0600 key file (else DEEPINFRA_API_KEY), so
the key is never in argv, in this process's configuration, or in the
environment the model's tools inherit. The key is read when claude
starts, which also removes the race where a turn right after launch
missed a saved key. Anthropic variables are stripped by prefix
(ANTHROPIC_*, CLAUDE_CODE_USE_*, CLAUDE_CODE_*_TOKEN/_FILE_DESCRIPTOR).

Also: a failed removal says so, the Settings subtitle mentions the
DEEPINFRA_API_KEY fallback, and the key rows keep their spacing when
Remove Key is hidden.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@austinywang

Copy link
Copy Markdown
Contributor Author

Correctness review (subagent), findings and resolution

# Severity Finding Resolution
1 High Chief runs claude from the home folder with --setting-sources project, so ~/.claude/settings.json is loaded as project settings, and its env / apiKeyHelper override the process environment. Confirmed and fixed in ca4ac5f. Local repro against claude 2.1.302 with a listener as the base URL: requests went to the settings' URL carrying the settings' x-api-key and Bearer token. The endpoint now reaches Claude Code via --settings, which outranks project settings. It carries the base URL, the model slots, and blanked Anthropic credential and switch variables. The key comes from an apiKeyHelper that reads the 0600 secret file, falling back to DEEPINFRA_API_KEY. Re-run of the same repro: only the endpoint is contacted, and both headers carry only the file key.
2 Medium The denylist of Anthropic variables was incomplete (ANTHROPIC_UNIX_SOCKET, CLAUDE_CODE_USE_MANTLE, gateway tokens, key file descriptors, …). Fixed: strip by prefix (ANTHROPIC_*, CLAUDE_CODE_USE_*, CLAUDE_CODE_*_TOKEN / _FILE_DESCRIPTOR / _API_KEY). Covered by a test.
3 Medium-low A turn right after launch could miss a saved key, because the first brain was built before the secret stream yielded. Fixed by construction: the key file is read when claude starts, not when the brain is built.
4 Low The token was reachable through Mirror / dump. Resolved: the configuration no longer holds the key, only the file path.
5 Low The key was inherited by the model's tool subprocesses through ANTHROPIC_AUTH_TOKEN. Resolved: no key-bearing variable is set anymore; the key is only read by apiKeyHelper. (A DEEPINFRA_API_KEY the user exported into the app's environment is still inherited, as before this PR.)
6 Nit Removal error said "save"; Settings subtitle ignored the env fallback; spacing collapsed when Remove Key was hidden. Fixed.

Pre-existing failures on base #21063 (same on its own CI run 38845456157), not from this PR: ChiefConversationRecoveryTests.transcriptTextIsLocalized, SettingsRowAnchorResolution.everyCuratedSettingEntryIsReachable (chief-dangerous-permissions), package-conventions-lint (ChiefSearchPath), web complexity.

The schema guard assumed every SecretFileKey id is a cmux.json path.
The DeepInfra key is entered only in Settings and never read from
cmux.json, so it is listed as an explicit exception instead of being
advertised in the schema.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@austinywang

austinywang commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor Author

CI on ca4ac5f: every DeepInfra and catalog test passed in the CmuxChief package lane. One failure was from this PR: workflow-guard-tests flagged chief.deepInfraAPIKey as a SecretFileKey missing from the cmux.json schema. That key is deliberately never read from cmux.json, so e93fc7c lists it as an explicit, documented exception in tests/test_cmux_schema_parity.py (passes locally, 7/7). The remaining failures come from the base branch or are unrelated to this PR: devbox-image web tests, web complexity, ChiefSearchPath iOS lint, and the chief-dangerous-permissions anchor test.

The ternary between a closure literal and a function reference did not
type-check in CI's Swift toolchain.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@austinywang

Copy link
Copy Markdown
Contributor Author

CI on 8f27031: on ca4ac5f/e93fc7c, CmuxChief didn't compile in CI (ClaudeCodeBrain.swift:270, a ternary between a closure literal and a function reference was ambiguous). 8f27031 fixes it, and all 7 ChiefDeepInfraTests now pass. The schema guard passes too. The remaining Swift package failures are the same ones failing on #21063: transcriptTextIsLocalized and SettingsRowAnchorResolution chief-dangerous-permissions.

austinywang and others added 5 commits October 17, 2026 15:27
Claude Code retries a 401 up to ten times with growing delays, so a
Chief turn with a bad key spins for minutes before it fails.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude Code reports each 401 retry as a system api_retry line. For a
model endpoint, stop claude there and show the invalid-key message.
Claude Code's own sign-in keeps its retries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A 403 can mean balance or model access, not a bad key. The turn test
now ignores the session event that precedes the failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@austinywang

Copy link
Copy Markdown
Contributor Author

A rejected key failed slowly; fixed and reviewed

A dry run of the real claude against DeepInfra with a fake key showed the problem. Claude Code retries a 401 up to 10 times with backoff, reporting each attempt as system/api_retry with error_status: 401. The Chief turn kept spinning for minutes before failing.

  • Red: 6e20fde. In CI run 39076149335, aRetriedAuthenticationFailureIsReported failed (the parser returned []) and aRejectedKeyEndsTheTurnWithoutWaitingForRetries hit its 60 s time limit.
  • Fix: a46bcae. For a model endpoint only, the first retried 401 stops claude and the turn ends with the invalid-key message pointing to Chief Settings. Claude Code's own sign-in keeps its retries.

A correctness-first review subagent made three findings, all fixed in 2cbc644:

  • High: the turn test would see the .session event before the failure. It now ignores session events.
  • Medium: error == "authentication_failed" also matched a 403, which providers use for balance or model access. Now only error_status == 401 counts, and a test covers the 403 case.
  • Low: open approvals weren't cleared when the process stops on this path. They are now.

The reviewer also confirmed: subagent retry lines are ignored, end can't run twice, the next turn relaunches (a resume that was never saved falls back once through retryFreshSession), the default Claude harness is unchanged, and the fake claude process doesn't leak.

The live check against DeepInfra with a fake key reached the endpoint and got 401 User is not authorized, which confirms that the URL and the key file reach DeepInfra. A real-key turn is still pending.

@austinywang

Copy link
Copy Markdown
Contributor Author

Dogfood: tagged build chief-deepinfra at 2cbc644

Fleet job ca0bf24b2aa4aba5626232a6 built and published the app. I drove Chief through the tagged app's own socket (CMUX_TAG=chief-deepinfra scripts/cmux-debug-cli.sh chief …), with the harness set to DeepInfra in that app's own preferences domain only:

  • chief status reports harness DeepInfra and model deepseek-ai/DeepSeek-V4-Pro-0813, the default.
  • No key saved: the reply came back at once: "Add your DeepInfra API key in Chief Settings (click the model name above the conversation) to use its models."
  • Fake key saved: the reply took 3 s: "DeepInfra did not accept the saved API key. Check it in Chief Settings…". So the app reached api.deepinfra.com with the saved key, got a 401, and stopped at the first retry. Before 2cbc644 this took minutes. I deleted the fake key afterward.

Not verified yet: a turn with a real key, which is waiting on a key. Settings UI automation also didn't run, because computer use isn't set up on this Mac, so the Settings key field hasn't been driven by automation.

austinywang and others added 6 commits October 17, 2026 21:17
Diagnostic only, to find the CmuxHomeCore signal 11 on CI virtual Macs.
Drop before merge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diagnostic only, for the signal 11 on CI's macOS 15 VMs. Drop before merge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…to chief-deepinfra-byok

# Conflicts:
#	Packages/Shared/CmuxHomeCore/Tests/CmuxHomeCoreTests/AttachmentTests.swift
@teamleaderleo

Copy link
Copy Markdown
Collaborator

For awareness: #27058 (NEXT app) offers "Add API Key…" and "Add Credits…" when a model can't be used. Add API Key… opens Settings > Accounts at the provider's key field, which stores the key in the Mac Keychain; Add Credits… uses the existing paid-plan Checkout. It touches none of this PR's files (classic Chief, DeepInfra key). If NEXT later gets a DeepInfra provider in Accounts, the same Add API Key… row can open it.

This branch has not been deployed

No deployments
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.

2 participants