fix(repl): make local interactive max-turns configurable (#2086)

* fix(repl): make interactive max-turns configurable

Wire --max-turns into interactive sessionConfig and honor
OPENCLAUDE_MAX_TURNS / CLAUDE_CODE_MAX_TURNS so long autonomous REPL
sessions can raise the default 50-turn per-prompt cap (fixes #2079).

* fix(repl): forward --max-turns on connect/ssh/remote launches

sessionConfig covered the normal interactive paths; connect, SSH,
assistant, and --remote built REPL props without spreading it, so the
CLI override was dropped despite help advertising interactive support.

* fix(repl): scope interactive max-turns to local query loops

Remote-backed sessions bypass local query(), so forwarding --max-turns
into those REPL props over-claimed enforcement. Clarify help/docs and
match OPENCLAUDE_MAX_RETRIES precedence when OPENCLAUDE_MAX_TURNS is set
but invalid.

* feat(config): add interactive max turns under /config

Expose replMaxTurns in the Config panel (50/100/200/500) and resolve it
after CLI/env so local interactive sessions can raise the per-prompt
cap without restarting. Resolve at query time so mid-session /config
changes apply on the next prompt.

* docs(repl): clarify invalid OPENCLAUDE_MAX_TURNS precedence

Match the OPENCLAUDE_MAX_RETRIES contract: a set-but-invalid primary
env var uses the default and does not fall through to legacy or /config.

* fix(repl): address PR review on max-turns help and web version gate

Share the --max-turns Commander description via an imported constant so
help and tests stay in sync without breaking the CLI bundle, replace
source-only help assertions with Commander behavior coverage, and add
the published 0.27.0 entry so web verify-dist passes.

* fix(repl): typecheck Commander maxTurns opts and warn on invalid env

Avoid TS2339 on untyped Commander opts, log invalid OPENCLAUDE_MAX_TURNS
like MAX_RETRIES, and clarify that /config shows the persisted preference.

* fix(repl): warn when max turns is unlimited

* fix(repl): scope unlimited-turn warning locally

* fix(repl): preserve interactive turn caps across backgrounding

* fix(repl): preserve turn caps when backgrounding

* fix(repl): share turn budget across background handoff

* fix(repl): reserve turns at provider dispatch

* fix(repl): snapshot background handoff transcript

* fix(tasks): avoid phantom background session task

* fix(repl): preserve handoff lifecycle state

* fix(repl): own pending background handoffs

* test(tasks): isolate background session task storage

* fix(repl): refresh background task title and test cleanup

* test(repl): cover max-turn CLI dispatch paths

* test(queue): cover prepend notification and priority

* fix(repl): skip background handoff after foreground query throws

Rebased onto main and gate Ctrl+B continuation on !didThrow so a faulted
foreground turn cannot start a background session from partial state.

* fix(repl): address PR review findings on notifications and handoff

Dedupe background task notifications by embedded task id, gate background
continuation on preflight veto, scope queue removal to main-thread notifications,
and forward maxTurns through all launchRepl entry points.

* fix(repl): resolve latest CodeRabbit inline review findings

Dedupe claimed notification batches by task id, restore notifications on
pre-registration abort, tighten test isolation, and replace remaining brittle
source-text assertions with behavioral coverage.

* fix(repl): keep notification restore active until provider dispatch

Stop clearing notification ownership when preparation succeeds so pre-dispatch
aborts can restore claimed queue items, and commit ownership once the provider
starts. Add regression coverage for the abort path and headless max-turns zero.

* test(repl): cover post-dispatch ownership and headless max-turns 0

Add regression tests for notification restore after provider dispatch commits
ownership, and assert headless --max-turns 0 reaches query() without interactive
resolution stripping the value.

* fix(repl): restore only embeddable notifications on Ctrl+B handoff abort

Track the deduped successor subset when restoring claimed main-thread task
notifications so items already in the settled foreground transcript are not
re-queued. Clarify that agent-scoped notifications intentionally stay on their
owner drain path (issue #2079 scope is interactive turn caps only).

* fix(repl): address review findings on background handoff

Commit notification ownership when background sessions complete without
provider dispatch, forward all task notifications on Ctrl+B again, and
restore deferred max-turn cap attachments when continuation is cancelled.

* fix(repl): guard deferred cap restore and skip remote turn limits

Anchor deferred max-turn restoration to the handed-off transcript tail so a
cancelled Ctrl+B handoff cannot attach the prior prompt's cap to a newer turn.
Apply the interactive turn cap only in local sessions and align remote-session
docs/help wording.

* fix(repl): use messagesRef for deferred cap transcript anchor

persistentMessages is block-scoped inside onQuery try; read the settled
tail from messagesRef in finally so typecheck passes.

* test: harden context fallback warning assertion after max-turns tests

Scope the unknown-model context test to [context] warnings only so unrelated
import-time debug logs do not fail CI, and clear turn env vars in both that
test and replMaxTurnsProp setup to avoid cross-file pollution.

* test: address PR review findings on headless max-turns boundary

Add a runHeadless-to-ask regression that asserts maxTurns 0 is forwarded
through the headless print path, and restore OPENCLAUDE_MAX_TURNS env vars
in context.test.ts after the unknown-model fallback test mutates them.

* test: tidy headless max-turns boundary test and env isolation

Mock headless stdout so runHeadless completes cleanly without leaking
output, restore spies in finally, and centralize turn-env cleanup in
context.test beforeEach.

* fix: address PR review findings for max-turns background handoff

Separate model-request lifecycle from provider dispatch acceptance so
interruption correction arms before async prep, notification ownership
commits only after dispatch, deferred turn caps restore on every abort
path, and foreground work stays blocked while handoff preparation runs.
This commit is contained in:
JATMN
2026-08-06 20:24:34 +08:00
committed by GitHub
parent 2c42a325d9
commit b0cbfe1100
22 changed files with 3000 additions and 229 deletions
+1
View File
@@ -478,6 +478,7 @@ host. Without this variable the behavior is unchanged.
| `CODEX_AUTH_JSON_PATH` | Codex only | Path to a Codex CLI `auth.json` file |
| `CODEX_HOME` | Codex only | Alternative Codex home directory |
| `OPENCLAUDE_MAX_RETRIES` | No | Maximum retry attempts for retryable API failures, capped at 100 (default: 10). Set to `0` to disable retries after the initial request. If unset, deprecated `CLAUDE_CODE_MAX_RETRIES` is still honored for compatibility. |
| `OPENCLAUDE_MAX_TURNS` | No | Per-prompt **local** interactive REPL turn cap for the in-process query loop. Defaults to `50`. Set a larger positive integer for long autonomous local interactive sessions (for example models that take many small tool steps). CLI `--max-turns 0` explicitly disables this cap and prints a cautionary warning. Precedence for a valid override: CLI `--max-turns` → this env var → legacy `CLAUDE_CODE_MAX_TURNS` (only when this var is unset/empty) → `/config` → Max turns (interactive) → `50`. If this env var is set but invalid (zero, negative, non-integer), the default `50` is used and lower layers are not consulted — same pattern as `OPENCLAUDE_MAX_RETRIES`. Does not apply to remote-backed interactive sessions (`connect` / `ssh` / `--remote`). |
| `OPENCLAUDE_RETRY_DELAY_MS` | No | Base retry delay in milliseconds for APIs that do not send `Retry-After`; exponential backoff starts from this value, capped at 60000 (default: 500) |
| `OPENCLAUDE_QUERY_HARD_MAX_MS` | No | Foreground query hard maximum in milliseconds. Defaults to 1800000 (30 minutes). Use a larger positive integer for long autonomous sessions; invalid, zero, negative, fractional, or timer-overflow values are ignored with a warning. |
| `OPENCLAUDE_DISABLE_CO_AUTHORED_BY` | No | Suppress the default `Co-Authored-By` trailer in generated git commits |