Skip to content

feat: --auto option, unlock auto/bypassPermissions permission modes via canonical claude-* model names#20

Closed
BenSheridanEdwards wants to merge 8 commits into
aattaran:mainfrom
BenSheridanEdwards:auto-mode-no-thinking-strip
Closed

feat: --auto option, unlock auto/bypassPermissions permission modes via canonical claude-* model names#20
BenSheridanEdwards wants to merge 8 commits into
aattaran:mainfrom
BenSheridanEdwards:auto-mode-no-thinking-strip

Conversation

@BenSheridanEdwards
Copy link
Copy Markdown

@BenSheridanEdwards BenSheridanEdwards commented May 7, 2026

Claude Code disables auto and bypassPermissions modes when ANTHROPIC_DEFAULT_*_MODEL is not a claude-* value. Today deepclaude exports the resolved backend model names (e.g. deepseek-v4-pro) directly, which trips that gate — Shift+Tab reports "auto mode isn't available for this model".

This PR adds a --auto opt-in that swaps the env vars to canonical claude-* names so the gate unlocks. Default behavior is unchanged: backend-native names in env vars and TUI, no auto/bypassPermissions. The bulk of the diff is proxy hardening that's needed regardless of which mode you launch in.

Behavior

deepclaude (default, no --auto):

  • Proxy starts; request flows claude → proxy → deepseek (or or/fw).
  • Env vars set to backend names (deepseek-v4-pro, deepseek-v4-flash, etc.).
  • TUI shows the real backend model.
  • Shift+Tab cannot reach auto / bypassPermissions.
  • Launch banner prints:
    Auto mode: OFF (running deepseek; TUI will show 'deepseek-v4-pro')
    Tip: pass --auto to enable auto mode — TUI will show 'claude-opus-4-7' instead
    

deepclaude --auto:

  • Same proxy path; env vars switched to canonical claude-opus-4-7 / claude-sonnet-4-6 / claude-haiku-4-5-20251001.
  • Proxy MODEL_REMAP translates them back to backend names on the wire (#N model remap: claude-opus-4-7 → deepseek-v4-pro in $PROXY_LOG).
  • TUI shows claude-opus-4-7 (it lies — actual routing is still the configured backend).
  • Shift+Tab cycles default → auto → plan → bypassPermissions.

Caveat (auto mode only): Claude Code's in-session UI (status bar, /model) shows the canonical claude-* name rather than the backend model — the trade-off for unlocking auto-mode. The launch banner still prints the resolved backend name, and the proxy logs every remap, so the actual routing is verifiable from outside the TUI.

What's in the diff

Behavior gate:

  • New --auto flag (default off). set_model_env branches on it: auto = canonical claude-* names, default = $RESOLVED_OPUS / $RESOLVED_SONNET / $RESOLVED_HAIKU / $RESOLVED_SUBAGENT.
  • New print_auto_mode_tip helper called by both launch_claude and launch_remote so the trade-off is visible at launch in either mode.
  • --help documents --auto.

Proxy / launch hardening (active in both modes):

  • Route the default launch_claude path through the local proxy (mirrors what launch_remote already did), so requests go through the same code path regardless of mode.
  • start-proxy.js legacy mode accepts an optional 3rd-positional defaultMode, so state.mode resolves to deepseek / openrouter / fireworks (not _single or anthropic) and MODEL_REMAP[state.mode] actually fires. This also fixes a latent bug in launch_remote, which previously left the proxy in 'anthropic' mode and forwarded requests to api.anthropic.com regardless of the configured backend.
  • Add Fireworks entry to MODEL_REMAP to match deepseek/openrouter.
  • Extract a shared start_proxy helper used by both launch_claude and launch_remote. Symlink-resolve SCRIPT_DIR so deepclaude works when installed via a /usr/local/bin/deepclaude symlink.
  • Call start_proxy directly (not via $()) so the PROXY_PID it sets actually reaches the parent shell — the EXIT trap was previously a silent no-op, leaving the node proxy orphaned on exit. start_proxy now sets PROXY_PID, PROXY_PORT, and PROXY_LOG as script globals.
  • Align RESOLVED_HAIKU / RESOLVED_SONNET / RESOLVED_SUBAGENT with what MODEL_REMAP actually produces, so the launch banner matches the wire instead of advertising the pro model for slots the proxy routes to flash.
  • Use a per-instance $PROXY_LOG path (/tmp/deepclaude-proxy.$$.log) so concurrent invocations don't truncate each other's logs. The resolved path is printed at launch.
  • On a non-zero, non-signal claude exit, dump the tail of $PROXY_LOG to stderr so upstream proxy errors and remap warnings surface immediately. Clean exits and Ctrl+C stay silent.
  • Warn in the proxy when a request arrives with a claude-* model name not in MODEL_REMAP[state.mode] — catches drift between the canonical-name list in deepclaude.sh and the remap table the next time Anthropic ships a model bump. Backend-native names (default-mode wire format) are passed silently.
  • Document the start-proxy banner-ordering invariant that grep -E '^[0-9]+$' depends on, plus a sync-required note above set_model_env.

Notes for reviewers

BenSheridanEdwards and others added 8 commits May 5, 2026 21:10
…aude-* model names

Claude Code disables `auto` and `bypassPermissions` modes when
ANTHROPIC_DEFAULT_*_MODEL is not a `claude-*` value. Today deepclaude
exports the resolved backend model names (e.g. `deepseek-v4-pro`) directly,
which trips that gate — `Shift+Tab` reports "auto mode isn't available
for this model".

Fix:
- Route the default `launch_claude` path through the local proxy (mirrors
  what `launch_remote` already does), so Claude Code talks to a Claude
  endpoint and the proxy translates model names on the wire via the
  existing MODEL_REMAP table.
- Export canonical `claude-opus-4-7` / `claude-sonnet-4-6` /
  `claude-haiku-4-5-20251001` from `set_model_env` instead of the
  resolved backend names.
- Extract a shared `start_proxy` helper used by both `launch_claude` and
  `launch_remote`. Symlink-resolve SCRIPT_DIR so deepclaude works when
  installed via `/usr/local/bin/deepclaude` symlink.
- `start-proxy.js` legacy mode accepts an optional 3rd-positional
  `defaultMode`, so `state.mode` resolves to `deepseek` / `openrouter` /
  `fireworks` (not `_single` or `anthropic`) and `MODEL_REMAP[state.mode]`
  actually fires.
- Add Fireworks entry to `MODEL_REMAP` to match deepseek/openrouter.

After this change `deepclaude` shows `>> auto mode on (shift+tab to cycle)`
at launch and Shift+Tab cycles default → auto → plan → bypassPermissions.

Notes for reviewers:
- This overlaps with draft PR aattaran#9 on the proxy-routing piece. PR aattaran#9 fixes
  cost-tracking but does not switch model env vars to canonical claude-*
  names, so PR aattaran#9 alone does not unlock auto mode.
- This branch deliberately does NOT alter the existing thinking-strip
  policy from 70518b6. A separate experiment branch tests scoping that
  strip down for DeepSeek; results will inform a follow-up.
The launch banner advertised pro-tier models for slots the proxy
actually routes to flash via MODEL_REMAP. Align RESOLVED_SONNET /
RESOLVED_HAIKU / RESOLVED_SUBAGENT for ds, or, fw with the flash
targets in proxy/model-proxy.js so the banner matches the wire.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The PR's EXIT trap (cleanup_proxy) was a silent no-op: start_proxy
ran inside $() — a subshell — so PROXY_PID never reached the parent,
leaving the node proxy orphaned on exit. Call start_proxy directly
and have it set PROXY_PID/PROXY_PORT/PROXY_LOG as script globals.

PROXY_LOG default also moves from /tmp/deepclaude-proxy.log to
/tmp/deepclaude-proxy.\$\$.log so concurrent invocations don't
truncate each other's logs (the file is opened with `: > "\$PROXY_LOG"`
on every start). Resolved path is printed at launch so users can
find it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…el_env

If Claude Code starts requesting a model name that isn't in
MODEL_REMAP[state.mode] (e.g. after Anthropic ships a model bump and
the canonical-name list in deepclaude.sh drifts from the remap
table), the proxy used to silently forward the unrecognized name and
the backend would 404 with no diagnostic. Now warn loudly so the
mismatch surfaces in the proxy log.

Add an IMPORTANT note above set_model_env so future maintainers know
the canonical names there must remain keys in every MODEL_REMAP
backend block.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Now that the proxy logs to a file rather than the terminal,
upstream errors and remap warnings (including the new unmapped-model
warn from the previous commit) never reach the user mid-session. On
non-zero, non-signal claude exit, dump the last 20 lines of
\$PROXY_LOG to stderr so the cause is visible without having to
hunt for the log path. Signal exits (>=128, e.g. 130 SIGINT) are
suppressed so Ctrl+C stays silent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The grep -E '^[0-9]+\$' port-extraction in start_proxy depends on
start-proxy.js emitting its "[MODEL-PROXY] Listening on ..." banner
before its final console.log(port). Document the invariant so a
future tidy-up of the proxy startup logs doesn't quietly break it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The port-polling loop runs grep -E '^[0-9]+$' against \$PROXY_LOG to
extract the bare-numeric port line emitted by start-proxy.js. On
early iterations the proxy hasn't written its port yet, so grep finds
zero matches and exits 1. With set -o pipefail, the grep|head
pipeline propagates that failure; with set -e, the var=\$(...)
assignment exits the script. Net effect: deepclaude died silently
during proxy startup (no error message, just "Proxy stopped." from
the EXIT trap) on any machine where the polling loop ran before the
proxy emitted its port line — i.e. always, on a slow enough boot.

Trail \`|| true\` on the pipeline so an empty match is OK; the loop's
own \`[[ -z \"\$proxy_port\" ]]\` test handles "keep waiting" already.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Default mode now exports backend-native model names (deepseek-v4-pro etc.)
so the TUI shows the real model — auto/bypassPermissions stay locked.
--auto opts into canonical claude-* env vars to unlock auto mode; the
launch banner prints a tip explaining the trade-off either way.

Quiet the proxy's "no remap" warning for non-claude-* model names so it
doesn't fire on every default-mode request (backend-native names are
deliberate there).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@BenSheridanEdwards BenSheridanEdwards changed the title feat: unlock auto/bypassPermissions permission modes via canonical claude-* model names feat: --auto option, unlock auto/bypassPermissions permission modes via canonical claude-* model names May 7, 2026
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