fix(anthropic): ping-aware stream watchdog — eliminate NonZeroAgentExitCodeError on large-context agentic runs#733
Closed
ericleepi314 wants to merge 331 commits into
Closed
fix(anthropic): ping-aware stream watchdog — eliminate NonZeroAgentExitCodeError on large-context agentic runs#733ericleepi314 wants to merge 331 commits into
ericleepi314 wants to merge 331 commits into
Conversation
Port openclaude's /stickers (typescript/src/commands/stickers — it opens stickermule.com/claudecode). Shows the sticker URL. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Port the original's global content search (inventory §6 GlobalSearchDialog) as a /search <query> command: runs ripgrep in cwd (smart-case, capped) and shows matching file:line results. Distinct from @ (filenames) and Ctrl+R (history). Query is single-quoted for shell safety. Verified: /search CompanionSprite returns the .tsx matches. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Port the original's clickable refs (inventory §8). hyperlink() wraps known refs in OSC 8 escapes with terminal capability detection (iTerm2/WezTerm/kitty/ghostty/ vscode/Hyper/rio/VTE) — no-op elsewhere so unsupported terminals show no garbage. Tool entries now link web args (URL) and file-op args (file:// resolved to cwd). This reverses my "fiddly-marginal" call — the clean version wraps *known* refs (not heuristic detection) + gates on capability. Verified: supported terminal wraps file.ts in OSC 8; unsupported returns plain text. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Port the original's MessageSelector (inventory §6) as the no-arg /rewind: opens a
picker of past prompts ("Rewind to before <preview>") and rewinds the
conversation to that point. /rewind <N> still drops N turns directly. Shared
requestRewind() helper; picker kind 'rewindpick' maps each restore point to the
turn count to drop.
This coexists with /rewind <N> exactly as MessageSelector coexists with simple
rewind in the original (so it's faithful parity, not redundancy).
Verified: 3 prompts → /rewind opens the picker → selecting "before second task"
sends rewind turns=2.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…484) Port the original's HistorySearchDialog (inventory §6) as /history: opens a list-picker of submitted prompts (newest first) → recalls the chosen one into the input. Distinct from Ctrl+R (inline cycle) — a browsable list view. Reuses the picker (kind 'historyrecall'). Verified: after 2 prompts, /history lists them; selecting "beta query" recalls it to the input. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Port the original's Settings dialog (inventory §6) as /settings: a picker of configurable areas (model/mode/theme/effort/provider) that routes the selection to the matching setter command. Picker Enter now closes before applyPick so a selection can open a nested picker. Verified: /settings lists the areas; selecting "model" opens the model picker. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Port the original's multiline input (inventory §1). A line ending in `\` + Enter inserts a newline and keeps editing instead of submitting (terminal-independent, unlike Shift/Meta+Enter). The input value renders multiline; the next plain Enter submits the whole thing. Verified: "line1\" + Enter does not submit (becomes "line1\n"); "line2" + Enter submits "line1\nline2". Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Port the original's Shift+Tab mode cycle (inventory §5/§8) — previously deferred. Shift+Tab (\x1b[Z) cycles default → acceptEdits → plan → bypassPermissions, sends set_permission_mode, and updates the mode state (footer reflects it). Verified: 3× Shift+Tab → acceptEdits → plan → bypassPermissions controls sent; footer shows the mode. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Port the original's "Yes, and don't ask again" permission option (inventory §5). Pressing 'a' (option 2) at a permission prompt remembers the tool in a session allow-set and allows it; future requests for that tool are auto-allowed (an effect responds + dequeues without showing the dialog). Client-side — no backend protocol change. Dialog now lists Yes / Yes-don't-ask-again / No. Verified: 'a' allows + remembers Bash; a second Bash request auto-allows with no keypress and no dialog. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Complete the permission UX (inventory §5). At a prompt, Tab opens a feedback field — typing "what to do differently" and pressing Enter denies the tool with that message sent back to the agent (Esc cancels the field). The dialog now shows the "tab to amend · esc to cancel" hint. Permission UX is now Yes / Yes-don't-ask / No / Tab-amend + Shift+Tab mode cycle. Verified: Tab opens the field; "use sed instead" + Enter → deny with that message. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Port the original's CompactSummary (inventory §3): after /compact, show a divider-style "── ✻ Summarized conversation · N → M messages · saved Xk tokens ──" boundary marking the summarization point + direction, instead of a plain line. Verified: /compact → "── ✻ Summarized conversation · 20 → 4 messages · saved 12.0k tokens ──". Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Port the original's DiffDialog (inventory §4). /diff now lists changed files (git diff --name-only): 0 → "no changes", 1 → its diff, >1 → a file picker (kind 'difffile') → selecting shows that file's diff. Replaces the single dump with a navigable per-file browser. Verified: with 2 changed files, /diff shows the picker; selecting renders that file's diff. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Port the original's /tasks (inventory §2/§9) as a safe status view: shows the in-flight turn (with its current tool activity) and the count of queued prompts, or "no active tasks" when idle. Read-only snapshot of the async work the agent-server already does (no concurrency change — true Ctrl+B turn-backgrounding would need a multi-session backend, deferred). Verified: /tasks → "Tasks: no active tasks" when idle. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Port the readline undo (inventory §1/§8) missing from the input. Ctrl+_ (\x1f) reverts the last edit. An effect snapshots each value change onto an undo stack (capped 200); Ctrl+_ pops it back, with an isUndoing guard so the undo's own change isn't re-recorded. Works in both insert and vim modes. Verified: typing "hello" then Ctrl+_ twice → "hell" → "hel". Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…#494) Complete the readline op set (inventory §1/§8). Ctrl+T transposes the two chars around the cursor; Alt+D kills from the cursor to the next word end (into the kill-ring, yankable with Ctrl+Y). Joins the existing Ctrl+A/E/W/U/K/Y + word-motion + undo. Verified: "ab" + Ctrl+T → "ba"; "foo bar" at col 0 + Alt+D → "bar". Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Port the original's external-editor keybinding (inventory §8). Ctrl+G writes the
current prompt to a temp file, opens it in $VISUAL/$EDITOR (default vi), and reads
the edited text back into the input. Raw mode is dropped around the editor (with
a finally to always restore it) so the editor owns the terminal; a repaint
follows. editInEditor() is a pure helper for testability.
Verified: editInEditor("hello world") with a non-interactive uppercasing $EDITOR
→ "HELLO WORLD"; in-app Ctrl+G on "edit me" → "EDIT ME".
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Close much of the vim gap (inventory §1). Adds motions e (end-of-word), ^ (first non-blank), G (last col); edits ~ (toggle case + advance), C (change to end), s (substitute char); and an unnamed register (filled by x/D/s/C) pasted by p/P. Joins the existing h/l/0/$/w/b + i/a/I/A + x/D. Verified: ~ toggles "hello"→"Hello" at col 0; C from mid → "Hi"; 0 x → "bc"; p pastes the deleted char → "bac". Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Add operator-pending mode (inventory §1) — the core of vim editing. d/c/y wait for a motion, then apply over the range: dw/db/de/d$/d0/d^/dG/dh/dl (delete), c… (change → insert), y… (yank). Doubled form dd/cc/yy acts on the whole line. All fill the unnamed register (pasteable with p/P). Verified: "hello world" dw → "world"; d$ from col2 of "abcdef" → "ab"; cc then "Q" → "Q"; yy then p → "heyhey". Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Add the char-pending vim commands (inventory §1): f<c>/F<c> jump to the next/prev occurrence of <c>, t<c>/T<c> stop just before/after it, and r<c> replaces the char under the cursor. Implemented via a pendingFind state that captures the next key. Verified: "hello" → r J → "Jello"; fl then x → "Jelo". Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Add text objects to operator-pending mode (inventory §1). After d/c/y, the i/a
prefix selects an object: w (word), " ' ` (quotes), () [] {} <> (pairs) — inner
(i) or around (a). Enables diw/ciw, ci"/da", ci(/da(, etc. via a pendingTextObj
state + textObjectRange().
Verified: "foo bar" diw → " bar"; 'say "hi" ok' ci" then X → 'say "X" ok'.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Add count prefixes (inventory §1): digits accumulate into a count that repeats the next motion (h/l/w/b/e) or x N times. A leading 0 stays the line-start motion; 0 mid-count is a digit. (Operators consume the count as 1 for now.) Verified: "abcdefgh" 3x → "defgh"; "abcdef" 0 2l x → "abdef". Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Port the original's inline ghost-text (inventory §1): while typing a slash command, the dim suffix of the top match is shown inline after the cursor (e.g. "/sett" → "/sett" + dim "ings"). Tab/Enter accepts it via the existing menu. Shown only for a single partial /token in non-vim mode. Verified: typing "/sett" renders dim "ings" on the input line (→ /settings). Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Wire the output-style end-to-end (inventory §2/§6; was non-functional). Backend: new set_output_style control sets tool_context.output_style_name and rebuilds the session system prompt (idle-guarded) so the style's "# Output Style" section is appended on the next turn; validates against VALID_OUTPUT_STYLES. TUI: /output-style opens a picker (default/concise/verbose/markdown) → set_output_style. Verified: backend e2e — "concise" accepted, "bogus" rejected; TUI — picker → set_output_style sent → "output style → concise". Server suite green. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Add the . repeat command (inventory §1) for buffer changes: x, D, ~, r, p/P, and d-operator (motion + text object) record a pure replay closure (current value/cursor → new), and . re-applies it at the cursor. Extracted motionTarget() so the operator and the recorder share motion logic. Insert-changes (c/i/s) are not yet recorded — a follow-up. Verified: "one two three four" dw → "two three four", . → "three four"; x then . chains single-char deletes. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Complete vim . (inventory §1): insert-entering changes now record a `.`-repeatable change too. enterInsert(pre, start) arms capture on i/a/I/A/s/C/cc/c-motion/c-obj; on Esc the typed text (insertStart..cursor) plus the pre-delete closure are stored as lastChange, so . replays the whole change (delete + retype) at the cursor. Verified: "foo bar baz" ciw X esc → "X bar baz", w . → "X X baz"; iZ esc → +Z, . → repeats; dw . non-insert path still works. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Complete the readline kill-ring (inventory §1). Kills (Ctrl+W/U/K, Alt+D) now also push to a kill-history; after Ctrl+Y, Meta+Y replaces the just-yanked text with the next-older kill, cycling. Adjacency is tracked via a yankActive ref reset each keystroke, so Meta+Y only cycles immediately after a yank. Ctrl+Y behavior is otherwise unchanged. Verified: kill "beta" then "alpha " → Ctrl+Y yanks "alpha " → Meta+Y → "beta". Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…sion (#506) Port the original's Knowledge Graph engine (/knowledge) as a functional store. Backend: src/knowledge (KnowledgeGraph — entities keyed by type:name with count/last_seen, heuristic record_from_text for files/`symbols`/urls, JSON persistence at ~/.clawcodex/knowledge/graph.json). The agent-server auto-records from the latest exchange at each turn end (gated by _knowledge_enabled, default on) and exposes a `knowledge` control (status/list/clear/enable/disable). TUI: /knowledge [list|clear|enable|disable] shows counts + top entities. Heuristic extraction now; the original's model-based semantic extraction + @orama search is a follow-up. Store, persistence, recording, control, and command are functional + tested end-to-end. Verified: store unit tests (extract/count/persist/clear); backend e2e (status/ disable/list round-trip); TUI /knowledge list renders counts + entities. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Port the original's /wiki as a file-based project wiki under .clawcodex/wiki. Backend: src/wiki (init creates pages/, sources/, index.md, log.md, schema.md + a starter architecture.md; status counts pages/sources; ingest copies a file into sources/ and logs it). agent-server `wiki` control routes init/status/ingest. TUI: /wiki [init|status|ingest <path>]. Verified: wiki unit tests (init/idempotent/status/ingest/missing); backend e2e (status→init→status); TUI /wiki init + status render. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
#508) Make the knowledge graph faithful to the original's model-based extraction, opt-in. src/knowledge/extract.py asks the provider for a JSON entity list ([{name,type}], fenced/prose tolerant, []-on-error). agent-server gains _knowledge_semantic (default off); when on, _record_knowledge uses model extraction and falls back to the heuristic if it yields nothing. /knowledge semantic|heuristic toggles it; status/list now report the mode and the TUI shows "· semantic". Verified: extract unit tests (json/fences/unknown-type/bad-json/none); full knowledge+wiki+server suite (101) green; TUI /knowledge semantic shows the mode. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
#509) Port the concurrent core of the original's backgroundable runs (inventory §9). Backend: src/background BackgroundTasks (thread-safe registry of detached shell commands in subprocesses — start/list/output/kill, captured output, no conversation race). agent-server bg_run/bg_list/bg_kill controls. TUI: /bg <cmd> starts a background task, /bg kill <id> terminates, and /tasks now lists live background tasks (icon + status + command) alongside the running turn + queue. Full turn/agent backgrounding (concurrent conversations) remains a larger multi-session effort; this lands the concurrent bash-task variant end-to-end. Verified: registry unit tests (complete/failed/list/output/kill); backend e2e (bg_run → poll bg_list → done + output); TUI /bg + /tasks render. Suite green. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…codex paths (#710) _prepare_subscription_request's clean() rewrote EVERY case-insensitive "clawcodex" in the system prompt to "Claude Code" — including filesystem paths. On Claude-subscription logins the model was told its memory lives at ~/.Claude Code/projects/<slug>/memory and its history at ~/.Claude Code/sessions — literal nonexistent locations it then searched verbatim. This is the real source of the "guessed wrong paths" #706/#707 addressed: those fixes emit correct paths, and this rewrite corrupted them after prompt assembly, at the provider layer. Constrain the rewrite to standalone brand mentions: matches inside path segments (~/.clawcodex, /etc/clawcodex), env vars ($CLAWCODEX_CONFIG_DIR), module/file names (clawcodex_dirs), and domains (clawcodex.app) survive verbatim. Prose disguise and the official-prefix block are unchanged. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…nfigs (#711) Add the two latest Anthropic models across every model-config surface: - models/configs.py: ModelConfig rows (1M context; 32K first-attempt wire max_tokens with the 128K true cap noted; Opus 4.8 at $5/$25, Fable 5 at $10/$50 per MTok with matching cache rates). Placed after the legacy opus row so prefix fallback for opus-4-1..4-7 is unchanged. - services/pricing.py: new 10/50 tier for Fable 5; exact PRICING and _FAMILY_PREFIXES entries for both models (dated snapshots included). - providers: both added to the anthropic available_models lists; `fable` added to the large-max-tokens pattern so Fable 5 isn't capped at 4096. - query.py: opus-4-8 and fable-5 added to the adaptive-thinking and effort allowlists, fable to the thinking-eligible pattern — without this, opus-4-8 would take the budget_tokens path, a hard 400 on 4.8+. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: octo-patch <266937838+octo-patch@users.noreply.github.com>
…ase (#712) Replicate the experiment behind RTK's README savings table (https://github.com/rtk-ai/rtk estimates -80% over a modeled 30-minute session) with real measurements, and showcase the results in the READMEs. eval/eco/ benchmark: - capture_corpus.py (stdlib-only) captures 27 real operations: failing + green pytest/go/jest runs from sample projects with genuine bugs (RTK's "never synthetic" fixture rule), npm/pip installs, git clone/status/ commit/push --progress, repo-scale log/diff/ls/find/cat/grep, a ~35k-line macOS system log, and small must-pass-through rows. Marker-file guard refuses to rmtree a --workdir it didn't create; missing toolchains skip; partial failure exits non-zero. - measure.py replays each capture through the production path (baseline = _assemble_bash_body(truncate_output(...)); compress_bash_output; the mapper's returnCodeInterpretation suffix on both sides) and counts tiktoken cl100k_base tokens of the exact wire content. Tee-filename entropy (time_ns/pid/counter) is pinned and the tee dir fixed, so reruns on an unchanged corpus are byte-identical. Never-worse asserted corpus-wide. Percentages floor so display never overstates savings. - Committed results: corpus total 92,989 -> 17,767 tokens (-80%), filter hits -88%, and an honestly conservative recompute of RTK's own session model at -19% (eco compresses results only; small outputs pass through). READMEs: new /eco section (before/after pytest example, measured table, RTK-model comparison in <details>, safety guarantees, reproduce commands) in README.md and README_ZH.md, compact translated sections in AR/FR/HI/PT/ RU, /eco rows in the REPL command tables, eval/README.md pointer. src/eco: npm >=9 lowercased its log prefixes — noise pattern now matches "npm warn" alongside WARN/notice (npm error still always survives); found via the real npm-install capture. New regression test pins both casings. Critic-reviewed: two REVISE rounds (determinism + rmtree guard majors, wire-exactness and display-floor nits) then APPROVE with independent re-execution of the capture and byte-identical measure verification. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…713) Replace the DeepSeek Prefix Cache banner with the measured /eco story (same session, 80% fewer Bash-output tokens; 92,989 -> 17,767 across 27 real operations), with an explicit #eco-benchmark anchor down to the full benchmark section. The DeepSeek economics remain in the 2026-06-18 news item, which the banner and the /eco section now point at. Add a 2026-07-13 news item (#708 + #712) to README.md, docs/NEWS.md, and README_ZH.md; both README news lists stay at the 10 most recent (the GLM-5.2 item rolls off — already archived in docs/NEWS.md). Wording is engine-exact per critic review: failure summaries keep the error lines that matter (kept lines never rewritten), every LOSSY compression tees the full output to disk (safe-loss ceremony strips do not claim recoverability), and the RTK-model recompute names its -19%. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
PR #713 swapped the DeepSeek Prefix Cache banner out for the new /eco Token Compression one. Bring both back: /eco stays as the newer banner, the original DeepSeek banner (restored byte-for-byte from before it was ever touched, git show 22c4be0^:README.md) follows immediately after. Repoints the two DeepSeek-prefix-cache cross-references that had been redirected at the News section back to a direct #-deepseek-prefix-cache anchor now that the heading exists again. Softens "now THE headline" to "now A headline" in the 2026-07-13 news item (README, docs/NEWS.md, README_ZH) since there are two banners now, not an exclusive one. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Updates the requirements on [openai](https://github.com/openai/openai-python) to permit the latest version. - [Release notes](https://github.com/openai/openai-python/releases) - [Changelog](https://github.com/openai/openai-python/blob/main/CHANGELOG.md) - [Commits](openai/openai-python@v1.0.0...v1.109.1) --- updated-dependencies: - dependency-name: openai dependency-version: 1.109.1 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…52 (#716) Updates the requirements on [prompt-toolkit](https://github.com/prompt-toolkit/python-prompt-toolkit) to permit the latest version. - [Release notes](https://github.com/prompt-toolkit/python-prompt-toolkit/releases) - [Changelog](https://github.com/prompt-toolkit/python-prompt-toolkit/blob/main/CHANGELOG) - [Commits](prompt-toolkit/python-prompt-toolkit@3.0.0...3.0.52) --- updated-dependencies: - dependency-name: prompt-toolkit dependency-version: 3.0.52 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* chore(release): prepare PyPI publishing * test(bridge): avoid shutdown grace delay
* feat(eval): Harbor agent adapter for terminal-bench 2.0 Adds eval/harbor/clawcodex_agent.py, a Harbor (harbor-framework) installed-agent that bootstraps uv + clawcodex-cli from PyPI inside each task container and runs it headless (--print --output-format stream-json --dangerously-skip-permissions, IS_SANDBOX=1 for the root safety gate). Model names use Harbor's provider/model convention and are split into --provider/--model. Token totals are backfilled from the final stream-json result event. Smoke-verified on terminal-bench@2.0 fix-git + openssl-selfsigned-cert with deepseek/deepseek-v4-flash: 2/2 reward 1.0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(eval): docker credsStore fix is the durable path, DOCKER_CONFIG the fallback The credential-helper hang is best fixed by switching config.json to the osxkeychain helper (verified on this machine, auths empty so nothing to migrate); keep the DOCKER_CONFIG=~/.docker-nocreds recipe as the non-invasive alternative. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(eval): critic follow-ups for the harbor adapter - Scope API-key forwarding to the provider under eval (unknown/absent provider falls back to the full allowlist; --agent-env unaffected) - Test for an actual CA bundle instead of [ -d /etc/ssl ] - Drop unused bash from the apk sieve; document CLAWCODEX_MAX_TURNS - Note the unquoted build_cli_flags() splice hazard for future flags - README: harbor view has no trajectory pane without ATIF Critic verdict on the branch: APPROVE (these are its suggestions applied). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2.1 (harbor-framework/terminal-bench-2-1) is the verified iteration of 2.0 (89 tasks, 26 fixed for bugs/timeouts/reward-hacking robustness). It resolves from Harbor Hub as `terminal-bench/terminal-bench-2-1` — no @Version — and hub datasets namespace task names, so include filters need the full `terminal-bench/<task>` form (or a glob). Smoke-verified with the existing adapter + deepseek/deepseek-v4-flash: 4 trials, 0 infra errors; fix-git -k 2 scored pass@2=1.0 (mean 0.5). Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…tion mode (#722) * feat(query): wire --effort end-to-end; harbor adapter subscription mode Effort wiring (port of TS effort.ts / main.tsx:995): - `clawcodex --effort {low,medium,high,max}` on the print path, threaded HeadlessOptions -> run_query_as_agent_loop -> QueryParams. - QueryParams.thinking_effort becomes None=auto; resolve_thinking_effort() resolves explicit > settings.effort > "medium" at the wire boundary, so the persisted /effort setting is now live on all three QueryParams sites (the documented-inert pipeline in effort_command.py is gone). - "max" clamps to "high" off the max-effort allowlist (TS modelSupportsMaxEffort: opus-4-6 only) instead of 400ing. - Capability matrix extended with opus-4-8/fable-5 rows + 11 new tests. Harbor adapter (eval/harbor): - `--ak subscription=true`: authenticate the Anthropic provider with a Claude Pro/Max subscription — host-side refresh of ~/.clawcodex/anthropic-oauth.json (5-min slack, locked, atomic save), injected per-trial via exec env into a config dir OUTSIDE the synced /logs tree; ANTHROPIC_API_KEY deliberately not forwarded (it would win over OAuth inside clawcodex). Sessions/transcripts still sync back, minus the token file. - `--ak effort=...` -> clawcodex --effort (CLAWCODEX_EFFORT fallback). - `--ak source=git+...` installs clawcodex from a git spec instead of PyPI (evaluating unreleased code); adds git to the container package sieve; mutually exclusive with version=. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * feat(effort): full Claude ladder low|medium|high|xhigh|max, wire-probed gating The Claude effort ladder includes xhigh (between high and max). Wire probes on the first-party API (2026-07-18, subscription auth): opus-4-8: xhigh OK, max OK sonnet-4-6: xhigh -> 400 ("Supported levels: high, low, max, medium"), max OK opus-4-6: xhigh -> 400 (same message), max OK (also documented) So the vendored TS snapshot's modelSupportsMaxEffort (max = opus-4-6 only) is stale: max is broadly accepted and it's xhigh that needs gating. Replaced with _model_supports_xhigh_effort (opus-4-8, fable-5 by analogy); resolve_thinking_effort degrades xhigh->high off-allowlist and passes max through. --effort/settings/VALID_EFFORT_VALUES/ /effort picker+help all accept the 5-level ladder; adapter CliFlag likewise. Tests updated to the probed semantics (127 passing across effort/ settings/query/cli/compat files), including flipping the deliberate "xhigh is invalid in Python" divergence tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: critic round — parity auto-effort, token containment, subagent seed src (effort): - Auto path now OMITS output_config entirely (TS configureEffortParams parity: undefined -> no param; the API applies its model default, "high" per TS docs) instead of forcing clawcodex's historical hardcoded "medium". Tests updated; ladder-sync test added. - xhigh added to frontmatter EFFORT_LEVELS; stale 4-level comments and "inert pipeline" notes corrected; --effort help documents main-loop scope (subagents follow settings.effort). adapter (critic blockers/majors): - BLOCKER: /logs/agent is a live bind mount of the host trial dir — copy-back switched from copy-all-then-delete to an ALLOWLIST (sessions/transcripts/todos), so the OAuth token never transits /logs. - Containers now receive a refresh-token-free credential copy (empty refresh_token; host-side refresh raised to 30-min runway, above any agent timeout) — no rotation fan-out hazard. - Token write hardened against symlink planting in the 1777 config dir (rm -f + set -C noclobber); refresh POST moved off the event loop (asyncio.to_thread). - subscription=true now rejects --ae ANTHROPIC_API_KEY (harbor injects agent-env into every exec, bypassing _build_env; the key would outrank OAuth and silently bill the API). - _oauth_file honors CLAWCODEX_OAUTH_FILE > $CLAWCODEX_CONFIG_DIR > ~. - effort kwarg additionally seeds settings.effort in the container's global config so SUBAGENTS inherit the level (--effort alone is main-loop only). - README/docstrings updated to match. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(eval): include projects/ in the copy-back allowlist Headless runs write per-project session transcripts under <config-dir>/projects (CC-style layout), not sessions/ — the allowlist missed it, so trial dirs synced no session artifacts. Token exclusion unchanged (config-dir root still never copied). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix: critic re-review leftovers — skills-loader xhigh + warning grammar Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
The _headless_args SimpleNamespace simulates argparse output; _run_print_mode now reads args.effort (PR #722), so the fixture needs the attribute like every other flag it mirrors. Fixes the only 2 CI failures (8609 passed otherwise). Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
) Runs the vendored TypeScript Claude Code implementation (typescript/ dist/cli.mjs, openclaude 0.13.0) through the same harbor harness as clawcodex, for apples-to-apples terminal-bench scoring: - Uploads the host-built single-file bundle (bun run build) per container + npm-installs its 7 unbundled runtime externals (pinned to the working host tree; native sharp/ripgrep need platform-correct binaries, and 3 further externals are lazy per-provider imports that never load on the Anthropic path). - Node >= 22 bootstrap: system node if suitable, apk on alpine, else the official nodejs.org tarball. - Claude-subscription auth is ENV-ONLY: the shared host-side refresh (clawcodex_agent.fresh_subscription_credentials) feeds CLAUDE_CODE_OAUTH_TOKEN — the fork treats it as an inference-only token (no refresh, never persisted; write-paths audited), so the config dir can live under the synced /logs tree for free session-log syncback. CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1 turns on the fork's own Bash-subprocess secret scrub so a prompt-injecting task can't `env` the token into transcripts (critic catch). - --provider anthropic always pinned: the any-LLM fork otherwise auto-routes to detected credentials (observed live: a ChatGPT/Codex profile 400ing an opus request). - --max-turns 300 (flag exists but is hidden from --help — critic catch) and --effort low|medium|high|max mirror the clawcodex adapter for comparability. Live-verified on terminal-bench 2.1 with anthropic/claude-opus-4-8 + subscription + effort=high: 3/3 trials reward 1.0, 0 errors; token/cost metrics populated. Critic verdict: REQUEST CHANGES -> fixes applied. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ns (#725) Adds eval/harbor/claude_code_subscription.py — a thin subclass of Harbor's own claude-code agent (bootstrap-installs the latest official CLI in-container; pin with --ak version=) that injects a per-trial refreshed Claude-subscription access token (shared 30-min-runway host helper) via CLAUDE_CODE_OAUTH_TOKEN, with CLAUDE_FORCE_OAUTH so a stray ANTHROPIC_API_KEY can never bill the API. Subprocess-env scrub: modern claude-code implements CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1 via bubblewrap and HARD-FAILS without bwrap (observed live — it also crashed the bootstrap when sprayed across install via the Trial extra_env overlay, which Trial snapshots BEFORE run()). Shipped as an ENV_VARS descriptor that reaches the run exec only, defaulted OFF ("0") to match stock harbor/ leaderboard posture; opt in with --ak subprocess_env_scrub=true on bubblewrap-equipped images. NOTE: the vendored openclaude fork's same var is plain env filtering (=1 correct there) — do not harmonize. Live e2e on terminal-bench 2.1 (anthropic/claude-opus-4-8 + reasoning_effort=high): 3 passing trials reward 1.0, 0 errors; agent_info auto-detected claude-code 2.1.215; ATIF/cost/token accounting inherited from the parent. Critic: REQUEST CHANGES (scrub-snapshot channel) -> ENV_VARS redesign + bwrap discovery -> APPROVE. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
The vendored typescript/ tree moved 0.13.0 -> 0.24.0. Adapter deltas: - Runtime externals repinned to the 0.24.0 host tree — now the full 13-package COMMON_EXTERNALS set (adds @aws-sdk/client-bedrock, client-sts, credential-provider-node, @smithy/core, @smithy/node-http-handler, @azure/identity; bumps aws-sdk/google-auth versions). At 0.13.0 three of these were absent lazy imports. - --effort choices gain xhigh (0.24.0 ladder; "ultracode" deliberately excluded — workflow mode, not a reasoning level). - Docstrings: the opus-4-8 conservative-128k caveat is FIXED in 0.24.0 (proper 1M-context/128K-output metadata registered), which also removes the compaction handicap the 0.13.0 comparison runs carried. Live-verified on terminal-bench 2.1 (opus-4-8 + subscription + effort=high): 2/2 trials reward 1.0, 0 errors, agent version auto-detected 0.24.0, the 128k metadata warning gone from both trial streams, host preflight PONG green. Design unchanged from the twice-critic-APPROVED adapter; this is a mechanical pin/enum refresh. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…#727) Port of typescript/vscode-extension/openclaude-vscode to clawcodex at vscode-extension/clawcodex-vscode: Control Center webview (runtime / provider / project-settings status, project-aware terminal launch), chat sidebar + editor panel (streaming text and thinking deltas, tool cards, per-turn usage), session history with resume, theme, and activity-bar icon. The chat backend is `clawcodex agent-server --stdio` (the same backend the Ink TUI spawns) rather than headless -p stream-json, because headless auto-denies permission asks and lacks the control_request protocol and per-turn results. Interactive permission prompts work end to end: can_use_tool cards with warning / session_label / plan payloads, allow-session persisted via chosen_updates, deny honored. Client-side RPCs (resume, list_sessions) correlate control_response replies by request_id; abort is the interrupt control (SIGINT would kill the session). Sessions are read from ~/.clawcodex/sessions with a realpath-normalized workspace filter (unbounded scan, output-capped — an input-side cap can silently empty Resume). The Azure/Foundry env subsystem of the reference is replaced with clawcodex-native provider detection from ~/.clawcodex/config.json; every divergence is documented in PORT_NOTES.md. Backend fix shipped with it: an agent-server `interrupt` during a live Anthropic stream stopped the deltas but never emitted result/cancelled — response.close() from the abort listener cannot wake a reader parked in ssl.read(), so the worker hung (reproduced on the installed build too; even the stream watchdog's own timeout close was ineffective). force_close_response() now shuts the underlying socket down (SHUT_RDWR via the httpcore network_stream extension) before closing, used by both the StreamAbortGuard and the watchdog. Regression tests include a real-socketpair blocked-recv unblock. Tests: 84 node --test cases behind a local vscode stub plus a live e2e script (scripts/e2e-agent-server.mjs, 8 checks incl. permission round-trip, interrupt -> cancelled, and post-interrupt turn); Python abort/watchdog/server/provider suites green. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Follow-up to #727: remove every openclaude mention from the shipped extension. PORT_NOTES.md becomes BACKEND_NOTES.md, rewritten as self-contained backend/wire-contract documentation (agent-server protocol, RPC correlation, permission persistence, session store, provider integration, streaming derivation, stderr policy) with the same technical content and no provenance framing. README pointer and package.json files list updated; two source comments reworded to stand alone. Also gitignore packaged *.vsix artifacts. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…de to non-streaming
Root cause of 18/89 NonZeroAgentExitCodeError trials in the 2026-07-19
terminal-bench run (mean 0.584 vs claude-code 0.719): the stream idle
watchdog (90s, TS-parity default) recovered by re-issuing the request
NON-streaming via chat() — which the Anthropic Python SDK refuses
outright for opus-class max_tokens ("Streaming is required for
operations that may take longer than 10 minutes"). Every watchdog fire
on an agentic call was therefore fatal: exit 1, trial zeroed, including
runs 13 minutes in. The referenced TS recovery
(streamLatencyWatchdog.ts:resumeViaChatCompletion) does not exist in
the vendored tree — real Claude Code aborts the timed-out stream and
RETRIES THE STREAM (claude.ts abortTimedOutStream + retry re-issue),
and had zero such crashes on the same tasks. The #730 1M-context fix
made large prompts (and >90s first-event latencies) common enough to
surface this latent landmine.
- chat_stream_response: bounded re-stream loop (stream_idle_max_attempts,
default 3 total attempts, env CLAUDE_STREAM_IDLE_MAX_RETRIES); on
exhaustion raise StreamIdleTimeout with harness-classifiable
"Connection timed out" phrasing. Attempt body extracted to
_stream_attempt (returns None on watchdog fire). _fallback_to_chat
deleted. Same x-client-request-id across attempts.
- chat(): explicit per-request timeout (API_TIMEOUT_MS env, ms, default
600s — TS getApiTimeoutMs parity), which also disarms the SDK's
>10-min guard for the legitimate non-streaming callers (compaction
summarize, session title, judges).
- stream_watchdog: stream_idle_max_attempts helper; stale
"falls back to non-streaming" docs corrected.
- tests: retry-then-succeed, exhaustion-raises (chat() never called),
retry-budget env override, chat timeout default/env/caller-override.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The retry loop (prior commit) removed the fatal non-streaming downgrade but the 18 terminal-bench trials STILL failed: a live re-run turned the crash into StreamIdleTimeout after 3x90s. Real root cause, traced to the SDK: in the agentic loop every turn re-sends the growing conversation, so every stream call pays PROMPT-PROCESSING time-to-first-event, which scales toward minutes as context approaches opus-4-8's 1M window (the #730 fix made this common). The Anthropic Python SDK DROPS the ping keepalives the API sends during that window (_streaming.py: `if sse.event == "ping": continue`), so the watchdog — which iterates the TYPED event stream — sees dead air during a healthy-but-processing request and fires. Retrying re-incurs the same TTFT. The crash duration_ms clustered at 94-100s (just past the flat 90s window) is the fingerprint. claude-code (0.719, same tasks) never crashes because the TS watchdog survives this window. Fix: two-phase deadline in StreamWatchdog. The FIRST event gets a longer grace (stream_first_event_timeout_seconds, CLAUDE_STREAM_FIRST_EVENT_ TIMEOUT_MS, default 300s — above realistic 1M-context TTFT, below the 600s non-streaming request ceiling that still catches a truly dead socket); reset() flips to the 90s inter-event idle once events flow. _effective_timeout_locked drives both the deadline and half-time-warning timers off the current phase. Complements the retry loop (transient mid-stream stalls) and the explicit chat() timeout. Tests: two-phase grace/fire/dead-socket coverage + env resolution; pre-existing short-timeout unit tests pass first_event_timeout_s so they still exercise first-fire. 82 passing across watchdog/provider/ch04. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The two-phase grace (prior commit) fixed only the tasks whose stall was the initial time-to-first-event (e.g. regex-log: crash -> reward 1.0). A live re-run showed the rest still failing: their >90s dead-air windows occur AFTER message_start (so the 90s inter-event deadline governs, not the 300s first-event grace) — the model does heavy extended-thinking / internal work mid-stream while the API sends only ping keepalives, which the Anthropic Python SDK drops from the typed stream. duration_ms ~282s = 3 attempts x ~94s = the 90s inter-event firing three times. Real fix, matching the ping-aware TS watchdog: the dropped pings are still BYTES on the wire that httpx.Response.num_bytes_downloaded counts. On each deadline the watchdog now checks byte progress and RE-ARMS instead of firing when bytes advanced since the deadline was set — so it fires only on true dead air (no bytes at all), never on a healthy stream the SDK simply isn't surfacing typed events for. Zero changes to the delicate accumulation path (the counter is read off stream.response). The two-phase grace stays as the time-based fallback when the counter is unavailable (mocked/non-httpx transport). Tests: byte-progress-prevents-fire + no-progress-still-fires (real timers, MagicMock byte counter). 84 passing across watchdog/provider/ch04. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…sition test - Document that first_event_timeout_s resolves independently of timeout_s (a caller passing only timeout_s still gets the 300s env/default grace). - Widen the phase-transition test's first-event grace 400ms->800ms so a CI scheduling stall can't flip the "must not fire during grace" assert. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…h paths (critic nit) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Collaborator
Author
|
Superseded by a clean-based branch (main history was rewritten under this branch, making its merge-base stale). Reopening as an identical change from current main. |
Merged
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes the dominant failure class in the terminal-bench 2.1 eval: 18/89 trials died with
NonZeroAgentExitCodeError(mean 0.58 vs claude-code 0.72, which had zero such crashes). Root-caused to the stream idle watchdog + a Python-SDK behavior.Root cause (verified at SDK source)
The Anthropic Python SDK drops
pingkeepalive events —_streaming.py:151if sse.event == "ping": continue— so the watchdog, which iterates the typed event stream, sees dead air whenever the model works >90s between typed events. In the agentic loop every turn re-sends the growing conversation, so every stream call pays prompt-processing time-to-first-event, which scales toward minutes as context approaches opus-4-8's 1M window (the #730 1M-context fix made this common). The crashduration_msclustered at 94–100s (just past the flat 90s window) is the fingerprint. Worse, the watchdog's recovery re-issued the request non-streaming, which the SDK refuses outright for opus-classmax_tokens("Streaming is required for operations that may take longer than 10 minutes") → fatal exit 1.Fix (three complementary layers; TS-parity)
httpx.Response.num_bytes_downloadedcounts (httpx/_models.py:952, incremented per raw chunk initer_bytesbefore SSE-decoding; the SDK consumesresponse.iter_bytes()). On each deadline the watchdog checks byte progress and re-arms instead of firing when bytes advanced — firing only on true dead air. Matches the ping-aware TS watchdog. Zero changes to the accumulation path._fallback_to_chatis deleted; watchdog fire → bounded stream retry (stream_idle_max_attempts, default 3) →StreamIdleTimeout(harness-retryable phrasing) on exhaustion.Plus: explicit
chat()per-request timeout (API_TIMEOUT_MS, default 600s, TSgetApiTimeoutMs) which also disarms the SDK's >10-min guard for legit non-streaming callers (compaction, session title, judges).Test plan
NonZeroAgentExitCodeErrorin the 0.58 run:dna-assembly→ 1.0 PASS,feal-linear-cryptanalysis→ 1.0 PASSfilter-js-from-html,adaptive-rejection-sampler→ complete, reward 0.0, no exception (genuine model misses, not crashes)write-compressor→AgentTimeoutError(the legit slow-task mode claude-code also hits)regex-log(a former crash) → 1.0 PASS in an earlier stagetest_stream_watchdog/test_anthropic_thinking_keeps_stream_alive/test_providers/test_ch04_api_round4/test_query_extended_thinking/test_context_runtime_limits(byte-progress re-arm + dead-air fire, retry-then-succeed, exhaustion-raises, chat timeout, two-phase transition)🤖 Generated with Claude Code