Repository navigation
Fix review findings and restructure the gateway and harness modules - #43
Merged
Merged
Conversation
Add isCmdSafeArgument, argvSafe and promptArgvSafe so providers can send prompt text over stdin when a non-shim .cmd/.bat would re-parse it. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
… total output Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
… per session The policy route can hold its reply until discovery ends (bounded), so a hook asks once instead of polling. Unconsumed policy jobs expire after a minute, and the policy and snapshot tables evict their least recently used idle session instead of refusing new ones until restart. Display tools are acknowledged per session, and ToolSearch tool_reference blocks naming them are stripped from forwarded history. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
…on uninstall Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
…nge cap; document Windows behavior Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Grok and Antigravity send prompts through a file or stdin when a non-shim .cmd launcher cannot carry them, and classify failures with the shared classifier. Codex app-server flag no longer embeds quotes. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Session cleanup moves from session.detach (any client leaving the roster) to session.end, which covers /clear, exit and resume; detach is no longer hooked. Tool-call reviewer attribution moves to tool.call, which carries the loop's agentId, so provider workers in a Claude session are attributed with the mode from the prompt snapshot. Agent types are classified native only on a Claude model, built-ins locally, and a definition pinned to a provider model keeps gateway registration; the Agent description names provider models only while a provider worker is offered. Display tools register lazily (at a harness prompt, before a harness worker spawns, before a harness step) and re-register after a /clear. The streamed Agent input rewrite is gone. Hook state (policy snapshot, agent and spawn models, offer classification, display tools, usage panes) lives in $state and survives a hot reload, and drawings subscribe to it instead of invalidating. Every gateway call goes through one client with an explicit method, a byte bound and a $clock deadline; the status poll is a $clock timer cancelled at turn.complete and session.end; the policy wait is one held request. An unchanged Claude prompt snapshot is not reposted, Claude steps send no telemetry, and the usage pane honours isPlaced. Types build against .claude-plugin/types and include the hook tests; test:mod forces the Mods rollout flag locally. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
…ad record() A main-loop compaction boundary is no longer written into the main snapshot, so workers are no longer resolved as toolless compaction turns, and a failed or cancelled compaction clears it. The permission context tables evict their oldest entry at the cap instead of refusing new work. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
…ncher Nested claude runs get the start environment back through a PATH shim and the bootstrap; a caller ANTHROPIC_BASE_URL and proxy are honored; discovery runs in parallel; a Claude that exits early ends the launch with its exit code; a failing plugin listing no longer aborts it; agent view is scoped through the session settings alone. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Claude's own classifier goes to Anthropic unless a provider action matches, non-streamed provider replies keep their connection alive, raw routes cover every non-Multi path, bodyless requests stay bodyless, encodings are kept, observation is bounded per line, failures leave a diagnostic, the mod control plane requires a per-session key once the mod holds one, and the dead agent catalog rewriting is removed. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
A worktree's node_modules symlink was committed because node_modules/ only matches directories. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Drop the early-access framing (opt-in variable, /plugin-types as the type source, $store), record the engine's static rules for hooks files, explain why test:mod forces the rollout flag locally, and update the API map for the changed hooks. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Zen and OpenAI now share gateway/responses.ts; forAnthropic takes the provider signature prefixes to strip instead of hard-coding Zen's. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
harness-cli.ts owns handle, exchange keying, leases, eviction, close and the bounded wait (Cursor reuses it with its own timeout). Providers supply model selection, key fingerprint, error type and the turn. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
…ng helpers OpenAI and Zen request construction, endpoints, model lookup and auth help move behind a PreparedRequest interface in their provider folders. One providerOf table replaces the duplicated prefix routing. The shared settings check and permission merge get neutral names in core. handleModRoute takes a context object, and cancelling a compaction now also clears the main loop's permission boundary. isRecord and the bounded-map eviction helpers each have one definition. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
…s shim, harden mod keys The gateway imports the shared Responses translation directly and gets each provider's signature prefixes from its own request module. Detaching a session now drops its mod key, and cap eviction removes unconfirmed entries before confirmed ones so a token holder cannot evict a real session's key. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
…dules Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
…launch, Windows shim, proxy, keepalive failure Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01659JR7q9uk8UxjbNTAr3R3
Two claude-multi launches started together both reinstall the Antigravity hook under lockStateFile, which refused a live owner at once. The hook install now waits up to 5 s (new waitMs option; session files keep failing fast), and a marker released between lstat and read is retried. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdKRYTowK4F5p9JdLb6Ac6
The gateway did not review the report, so it reports the verdict as unavailable rather than not_flagged; Claude Code's classifier decides. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdKRYTowK4F5p9JdLb6Ac6
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdKRYTowK4F5p9JdLb6Ac6
- An unreadable classifier request (Claude 2.1.283 omits the severity tag) went to Anthropic even while an OpenAI worker's action awaited review; it fails closed again in that case. - The Grok routing fixture ended with stopReason 'stop'; real Grok ends with 'end_turn', which the fail-closed check now requires. - The Grok [1m] wire drives a session of its own and follows the mod-key protocol, since the Claude session's key now guards mod writes. - The Windows install test passes a settings path, not quoted JSON, to its .cmd fake Claude, which the launcher now correctly refuses. - The e2e rejecting proxy ignores client resets of refused tunnels, which crashed local runs. - Codex now serves gpt-6.1-sol on ChatGPT plans, so the OpenAI Sol row runs it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdKRYTowK4F5p9JdLb6Ac6
npm links Claude Code's bin/claude.exe through claude.cmd. The shim resolver only unwrapped .js targets, so the gateway's plugin-permission probe ran it through cmd.exe and the metacharacter refusal rejected its inline --settings JSON: every harness worker failed on Windows with an npm-installed Claude. An .exe shim target now runs directly. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdKRYTowK4F5p9JdLb6Ac6
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.
Fixes from the October 2026 full-codebase review (Mods usage, native-behavior alteration, gateway, providers), a regression-review pass on top of them, and a cleanup refactor of the gateway and harness structure. 46 commits, 155 files.
CHANGELOG.mdhas 29 Unreleased entries with the details.Behavior fixes
multi/openaiormulti/zen, and workers that inherit a provider model, are now registered with the gateway and attributed throughtool.call.Hostare refused (DNS rebinding). Mods control-plane calls need a per-session mod key. Unknownmulti/provider prefixes are reviewed.NO_PROXYgains the loopback names. Slow non-streamed replies are kept alive, and a failure after the keepalive is reported as an assistant message.claude. Nested runs stay native, and nestedmultilaunches forward to the user's upstream.end_turnstops. Cursor recovers its state across restarts. Zen Chat blocks are sequential, and OpenAI bills stop-sequence replies..cmdlaunchers refuse cmd.exe metacharacters and unsafe prompts. Process trees stop gracefully. Writes are atomic with fsync. Taking over a stale state lock tolerates hostname changes. The nested shim works in Git Bash.SubagentHandbackcall in auto mode. The gateway now issues that call for Cursor, Antigravity, and Grok workers. It answers bounce and result-only requests itself, and strips the handback from history on resume. The report's review is left to Claude Code's own classifier.gpt-6.1-solon ChatGPT plans, so the OpenAI Sol row runs it in place ofgpt-6-sol.Refactor
$.state,$.clock, and a single gateway client (hooks/gateway.ts).harness-cli.ts) and a failure classifier (harness-failure.ts).gateway/responses.ts).gateway-request.ts), with the contract inprovider-request.ts.isRecordandnativeSpellingwere consolidated, and the responses shim was removed.Verification
npm run check: banner, Biome, Knip,tsc, 645 unit tests all pass, with noDEP0190warnings (Node 24.18).npm run test:e2eagainst the pinned Claude Code 2.1.283: 105 pass, 7 known todos. The first CI run caught a regression: an unreadable classifier request passed to Anthropic while an OpenAI worker's action awaited review. It now fails closed again. The run also caught three stale fixtures. Windows e2e then caught that npm'sclaude.cmdshim forclaude.exewent through cmd.exe, where the new metacharacter refusal broke every harness worker. Such shims now run the binary directly.npm run test:mod: 95/95 hook tests pass.claude-multiin tmux):npm testpassed. After the handback fix, both harness reports arrived with no re-prompts.acceptEditsran an Antigravity worker.Not yet verified live
taskkill, and the agy hook guard. CI covers the cmd.exe refusal and the npm shim in Windows unit and e2e jobs.🤖 Generated with Claude Code
https://claude.ai/code/session_01VdKRYTowK4F5p9JdLb6Ac6