Skip to content

feat: multi-agent engine on the Claude Agent SDK#1

Merged
efenocchi merged 38 commits into
mainfrom
feat/claude-agent-sdk
Jul 1, 2026
Merged

feat: multi-agent engine on the Claude Agent SDK#1
efenocchi merged 38 commits into
mainfrom
feat/claude-agent-sdk

Conversation

@efenocchi

@efenocchi efenocchi commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

Summary

Replaces the hand-rolled multi-agent layer (Vercel AI SDK) with an engine built on Anthropic's Claude Agent SDK (@anthropic-ai/claude-agent-sdk). Spec: PRD-agent-sdk-migration.md.

  • New provider: 'claude' (default), subscription-first auth (each self-hosted user uses their own Claude plan), with ANTHROPIC_API_KEY as the pay-as-you-go fallback.
  • Per-role model tiers: Haiku (planner), Sonnet (reviewer), Opus (orchestrator).
  • OpenRouter/Codex paths untouched (the existing AI-SDK engine still runs them).

Architecture (validated by the real-run e2e)

The end-to-end test against a real Claude subscription proved that an in-process MCP server is only reachable from the main thread — delegated subagents that try to write files are unreliable ("Stream closed", non-deterministic writes). So the shipped design is:

  • Orchestrator (main thread) does the coding through the in-process MCP tools (write_file/run_command/run_app) — deterministic, goes through workspace.writeFile + probeApp, fires the live-editor watcher, and drives the run → fix self-healing loop.
  • Planner / Reviewer are read-only advisory subagents (built-in Read/Glob/Grep[/Bash]) — their output is text, so async delegation can't corrupt file state.

Verification

End-to-end (real subscription, builds + runs a Flask todo app) — PASS on all criteria:

AC1 delegation   PASS  (planner, reviewer delegated)
AC1 live files   PASS  (files on disk: app.py, templates, static, requirements.txt, todo.db, .venv)
AC2 self-heal    PASS  (independent probe ok: http://localhost:5000)
AC3 stop         PASS  (abort in ~2s)
AC4 usage        PASS  (planUnits recorded, subscription mode)

Run it: npx tsx scripts/check-claude-engine.ts (skips cleanly without a Claude credential).

  • Typecheck: clean (tsc --noEmit).
  • Unit tests: 26 new tests for roles + event mapping (vitest), co-located 1:1.
  • Codex review: 4 findings, all fixed — SDK-native orchestrator prompt (was using the legacy delegate_to_* tools), shared isCommandAllowed that rejects shell-operator chaining past the allowlist (&&/;/|), cwd anchored to the workspace, and env inheritance instead of spreading host config.

Notes

  • Pre-existing test debt (not introduced here): 10 tests in packages/server/test/ fail on main too (demo provider removed, mock drift) — verified by running them against the unmodified base. Out of scope for this PR.
  • Internal planning docs (STATUS/PLAN/BACKLOG/...) are intentionally kept out of the public repo.

Summary by CodeRabbit

  • New Features
    • Added Claude (subscription) provider option and expanded Claude model selections in the UI.
    • Increased default maximum run steps to support longer tasks.
  • Bug Fixes
    • Improved agent tool permissioning so delegated work uses reliable read-only tools, while the main orchestrator handles execution.
    • Hardened command execution with allowlisting and improved handling of partial/aborted runs and stop/cancellation.
  • Tests / Chores
    • Added end-to-end Claude engine verification and expanded permission/message/role tests.
    • Updated CI with typecheck/build, duplication checks, and added CodeQL scanning.

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5734d3a9-ae18-47d0-b429-63628fd04b72

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds claude as a first-class provider with dedicated SDK execution, tool/role separation, session branching, UI updates, and validation automation. Default configuration now targets Claude, and the repo includes an end-to-end check for the Claude engine path.

Changes

Claude Agent SDK Integration

Layer / File(s) Summary
Contracts, defaults, and provider registration
packages/shared/src/protocol.ts, packages/server/src/agent/engine.ts, packages/server/package.json, packages/server/src/config.ts, packages/server/src/providers/claude.ts, packages/server/src/providers/registry.ts, packages/server/src/providers/models.ts
Adds claude to the provider contract, defines the engine input/output types, updates default provider/model settings, adds the Claude provider, registers it, and returns Claude model aliases from listModels().
Tool access and role contracts
packages/server/src/agent/claude/tools.ts, packages/server/src/agent/claude/roles.ts, packages/server/src/agent/claude/engine.ts, packages/server/src/agent/claude/roles.test.ts, packages/server/src/agent/claude/engine.test.ts
Defines the in-process OpenREPL MCP tools, command allowlisting, role-specific tool sets and prompts, and the gate used by the Claude engine. Tests cover tool permissions, loop guards, role tiers, tool lists, and prompt constraints.
SDK message mapping
packages/server/src/agent/claude/map-messages.ts, packages/server/src/agent/claude/map-messages.test.ts
Maps Claude Agent SDK messages into UI events and extracts terminal token/cost/result data, with tests for assistant text, tool calls/results, result detection, and usage parsing.
Abort-aware command and probe execution
packages/server/src/agent/tools.ts, packages/server/src/agent/probe.ts, packages/server/src/runner.ts
Threads AbortSignal through command execution and app probing, and updates process cleanup so Stop can cancel in-flight work.
Claude session branch and UI wiring
packages/server/src/session.ts, packages/web/src/App.tsx, packages/web/src/store.ts, packages/server/test/models.test.ts, packages/server/test/run-app-e2e.test.ts, packages/server/test/session.test.ts
Routes Claude runs through ClaudeAgentEngine, updates provider defaults on provider changes, exposes Claude in the UI, and adjusts server tests for the new provider and run-app flow.
Repository automation and end-to-end checks
scripts/check-claude-engine.ts, .github/workflows/ci.yaml, .github/workflows/codeql.yaml, .coderabbit.yaml, .github/pull_request_template.md, package.json, .jscpd.json, .gitignore, vitest.config.ts, PRD-agent-sdk-migration.md
Adds the Claude engine verification script, CI and CodeQL workflows, duplicate-code tooling, review configuration, test discovery changes, and PRD updates.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WebSocketClient
  participant Session
  participant ClaudeAgentEngine
  participant SDKquery
  participant OpenReplMcpServer

  WebSocketClient->>Session: run / stop messages
  Session->>ClaudeAgentEngine: run(EngineRun)
  ClaudeAgentEngine->>SDKquery: query(prompt, Options)
  SDKquery->>OpenReplMcpServer: tool call
  OpenReplMcpServer-->>SDKquery: tool result
  SDKquery-->>ClaudeAgentEngine: streamed SDK messages
  ClaudeAgentEngine->>Session: emit UiEvent
  ClaudeAgentEngine-->>Session: RunResult
  Session->>WebSocketClient: usage_update / done
Loading

Poem

A rabbit hopped through Claude’s new gate,
With tools and roles set straight.
Read-only paws and run-app flair,
Then stop could land right in the air.
🐇✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly reflects the main change: a new multi-agent engine built on the Claude Agent SDK.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/claude-agent-sdk

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.

@efenocchi

Copy link
Copy Markdown
Collaborator Author

Codex review — findings addressed

A final independent Codex review of the shipped code surfaced 4 findings; all addressed:

  • High — reviewer was not actually read-only (had built-in Bash; with the default allow-all commandAllowlist, a "read-only" reviewer could run mutating shell commands). Fixed: removed built-in Bash from every role — the orchestrator runs commands only through the gated run_command MCP tool; planner/reviewer are Read/Glob/Grep only.
  • MediumsettingSources: [] isolates settings but host/project .mcp.json could still load. Fixed: added strictMcpConfig: true (only the in-process openrepl server connects — verified it doesn't block our own server).
  • Medium — allowlist prefix-match can be bypassed by an allowed binary that re-shells (npm exec -- sh -c …). Documented as a coarse defense-in-depth guard (true isolation needs OS-level sandboxing, which OpenREPL defers); the built-in Bash removal shrinks this surface to the gated run_command only.
  • Low — usage was recorded under config.model (sonnet) while the main coding agent runs on models.orchestrator (opus). Fixed: Claude usage is now recorded under the orchestrator tier.

Re-verified after the fixes: tsc --noEmit clean, 26 unit tests pass, and a focused probe confirms the in-process MCP server still works under strictMcpConfig. The full end-to-end (real subscription, builds + serves a Flask app) passed on all 5 acceptance criteria prior to this round; these are read-only/isolation/accounting hardening that don't touch the proven coding path.

(An earlier Codex pass was derailed by this environment's deeplake/hivemind SessionStart hooks failing inside the Codex subprocess; the review above is the recovered, complete one.)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/server/src/agent/claude/engine.ts`:
- Around line 138-140: The early-return in claude engine should not synthesize
placeholder text for aborts that produced no output, because finishRun will
persist it as assistant memory. Update the aborted/partial-result branch in
engine.ts so it only preserves actual generated text (or a real partial result)
and leaves result.text empty when the run was stopped before any assistant
content was produced; use the existing result, finalText, and signal.aborted
checks to keep this behavior localized.

In `@packages/server/src/agent/claude/roles.ts`:
- Around line 54-55: The reviewer role is currently not actually read-only
because REVIEWER_BUILTIN includes Bash, which is controlled by the same
commandAllowlist used for orchestrator commands in engine.ts. Update roles.ts so
the reviewer subagent does not get Bash by default, or introduce a separate
read-only command gate for the reviewer path and wire it through the reviewer
role setup. Use the REVIEWER_BUILTIN and READONLY_BUILTIN definitions in
roles.ts to locate the change and keep the reviewer advisory-only.

In `@packages/server/src/agent/claude/tools.ts`:
- Around line 33-37: The command allowlist check in isCommandAllowed currently
uses startsWith(), which can mistakenly approve commands that only share a
prefix with an allowed entry. Update the matching logic in isCommandAllowed so
an allowlist item is accepted only when it matches the full command exactly or
is followed by a command-boundary whitespace, while still preserving the
existing trim() and SHELL_OPERATORS guard.

In `@packages/server/src/config.ts`:
- Around line 26-38: DEFAULT_CONFIG.model/models in config.ts are currently
Claude-specific, but Session.handle('set_provider') only updates
m.config.provider, so the shared config can leak Claude aliases into
OpenRouter/Codex. Update the config flow in Session.handle('set_provider') and
the model resolution path (including DEFAULT_CONFIG.model/models) so each
provider has its own defaults, or clear/reset provider-incompatible model
selections whenever provider changes. Make sure the fix uses the existing
provider/model config symbols so the OpenRouter/Codex path no longer reads
Claude-only aliases.

In `@packages/server/src/providers/registry.ts`:
- Around line 14-16: The ClaudeProvider wiring in registry.ts is using
ANTHROPIC_API_KEY as the active credential, which implicitly routes Claude runs
to PAYG; update the initialization so ClaudeProvider does not automatically fall
back to getSecret('ANTHROPIC_API_KEY') or process.env.ANTHROPIC_API_KEY unless
PAYG is explicitly opted in. Adjust the credential selection in the registry
constructor to use a subscription-first auth strategy for ClaudeProvider, and
keep the key lookup gated behind an explicit opt-in path.

In `@packages/server/src/session.ts`:
- Around line 302-312: The Claude branch in session handling is still inheriting
the generic non-OpenRouter auth fallback, so Claude users see the wrong `codex
login` guidance. Update the auth/error path in `session.ts` around the
`m.config.provider === 'claude'` flow so Claude requests emit Claude-specific
not-ready messaging instead of the earlier shared message, using the existing
`ClaudeAgentEngine`/provider checks to branch the copy correctly.
- Around line 292-296: The session tooling ignores abort state, so stopped turns
can leave shell commands and app probes running. Update the `ToolDeps` wiring in
`session.ts` so `runCommand` and `runApp` receive and pass along
`controller.signal`, then thread that signal through `CommandRunner.run` and
`probeApp` so both command execution and probing can be cancelled cleanly when
Stop is triggered.

In `@PRD-agent-sdk-migration.md`:
- Around line 101-107: Update this design note to match the implemented role
contract in roles.test and the actual agent setup: remove the unsupported
planner/coder/reviewer subagent split and the claim that allowedTools includes
the six built-ins plus mcp__openrepl__run_app. Rephrase it so the orchestrator
remains the only role with Agent plus the six MCP tools, while planner and
reviewer stay read-only and reviewer keeps Bash only if that is what the
contract enforces. Reference the roles contract and the tool-selection wording
around createSdkMcpServer, allowedTools, and canUseTool so the doc reflects the
current implementation instead of the reverted proposal.

In `@scripts/check-claude-engine.ts`:
- Around line 149-153: Phase A timeout handling in the waitFor catch only marks
process.exitCode, so the script continues into the probe and Phase B with a
potentially still-running build. Update the failure path around waitFor in
check-claude-engine.ts to stop execution immediately after reporting the
timeout, using the existing report and credential context so the script does not
proceed to later phases when the initial run did not finish.
- Around line 128-131: The readiness check is too broad and can pick up a stale
`ready` event from `session.init()` instead of the one for the newly opened
project. Update the `waitFor` predicate in `check-claude-engine.ts` so it
matches both `e.type === 'ready'` and the `workspaceDir` for `projectDir`, using
the existing `UiEvent.ready` shape to ensure Phase A runs against the correct
workspace.
- Around line 85-99: The waitFor helper currently only resolves on a matching
message or rejects on timeout, so it can hang until the full timeout if the
WebSocket closes or errors and it leaves the temporary message listener attached
on failure. Update waitFor(ws, events, pred, ms) to also listen for the socket’s
close/error events and reject immediately when the connection dies, and make
sure every exit path (match, timeout, close, error) removes the registered
message listener and any auxiliary listeners to avoid leaks.
- Around line 31-39: The output-directory creation in report is currently using
the async fs.mkdir call inside a try/catch, so it does not block before
writeFileSync runs. Update report to use the synchronous mkdir API for .test-tmp
(or otherwise ensure the directory exists synchronously) before writing OUT,
keeping the existing best-effort error handling around the directory creation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 53a65466-ccfe-486d-97e9-a07258fc3a67

📥 Commits

Reviewing files that changed from the base of the PR and between 5cc3ebc and 77276c4.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (16)
  • PRD-agent-sdk-migration.md
  • packages/server/package.json
  • packages/server/src/agent/claude/engine.ts
  • packages/server/src/agent/claude/map-messages.test.ts
  • packages/server/src/agent/claude/map-messages.ts
  • packages/server/src/agent/claude/roles.test.ts
  • packages/server/src/agent/claude/roles.ts
  • packages/server/src/agent/claude/tools.ts
  • packages/server/src/agent/engine.ts
  • packages/server/src/config.ts
  • packages/server/src/providers/claude.ts
  • packages/server/src/providers/registry.ts
  • packages/server/src/session.ts
  • packages/shared/src/protocol.ts
  • scripts/check-claude-engine.ts
  • vitest.config.ts

Comment thread packages/server/src/agent/claude/engine.ts
Comment thread packages/server/src/agent/claude/roles.ts Outdated
Comment thread packages/server/src/agent/claude/tools.ts Outdated
Comment thread packages/server/src/config.ts
Comment thread packages/server/src/providers/registry.ts
Comment thread PRD-agent-sdk-migration.md Outdated
Comment thread scripts/check-claude-engine.ts
Comment thread scripts/check-claude-engine.ts
Comment thread scripts/check-claude-engine.ts Outdated
Comment thread scripts/check-claude-engine.ts Outdated
@github-advanced-security

Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

Comment thread scripts/check-claude-engine.ts Fixed
@efenocchi

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@efenocchi

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/server/src/agent/probe.ts (1)

36-41: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Thread signal into dependency installation.

If Stop happens while det.install is running, this code only observes the abort after runOnce returns, so installs can keep running and delay turn cancellation.

Proposed direction
-    const r = await runOnce(dir, det.install, env);
+    const r = await runOnce(dir, det.install, env, signal);

Also update runOnce to terminate its spawned process on abort.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/server/src/agent/probe.ts` around lines 36 - 41, Thread the abort
signal through dependency installation in probe.ts: the install path in probe()
currently waits for runOnce(dir, det.install, env) to finish before checking
signal?.aborted, so Stop cannot cancel an in-flight install. Update probe() to
pass the signal into runOnce, and change runOnce to accept that signal and
terminate its spawned process promptly when aborted, using the existing
probe/runOnce symbols to locate the flow.
🧹 Nitpick comments (3)
packages/server/src/agent/claude/engine.test.ts (1)

40-64: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Loop-guard coverage limited to one tool.

The loop-guard tests only exercise mcp__openrepl__list_dir. READONLY_FOR_LOOP_GUARD also includes Read, Glob, Grep, read_file, and search_repo (per engine.ts), none of which are exercised in a repeated-call scenario here. A regression that breaks dedup logic for those tools specifically wouldn't be caught by this file.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/server/src/agent/claude/engine.test.ts` around lines 40 - 64, The
loop-guard tests in engine.test.ts only cover repeated mcp__openrepl__list_dir
calls, but READONLY_FOR_LOOP_GUARD in canUseTool also applies to Read, Glob,
Grep, read_file, and search_repo. Extend the read-only loop-guard coverage by
adding repeated-call cases for those tool names against makeCanUseTool so the
same dedup/deny behavior is verified for each read-only tool, while keeping
side-effecting tools like mcp__openrepl__write_file and mcp__openrepl__run_app
expected to always allow.
packages/server/test/session.test.ts (1)

23-30: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the emitted ready payload, not just that one exists.

This still passes if Session emits a ready event for the wrong workspace. Matching workspaceDir here makes the test protect the actual open_project contract.

Suggested change
-      expect(events.some((e) => e.type === 'ready')).toBe(true);
-      expect(events.some((e) => e.type === 'tree')).toBe(true);
+      expect(events).toContainEqual(expect.objectContaining({ type: 'ready', workspaceDir: dir }));
+      expect(events).toContainEqual(expect.objectContaining({ type: 'tree' }));

As per path instructions, "Prefer asserting on specific values (paths, tool names, model tiers) over generic substrings."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/server/test/session.test.ts` around lines 23 - 30, The `opening a
project emits ready + tree` test in `session.test.ts` only checks that a `ready`
event exists, so tighten it to assert the emitted `ready` payload includes the
expected `workspaceDir` for the opened project. Update the assertions around
`makeSession`, `session.handle({ type: 'open_project', path: dir })`, and the
`events` array so the test verifies the specific `ready` event data rather than
a generic match.

Source: Path instructions

.github/workflows/ci.yaml (1)

13-33: 📐 Maintainability & Code Quality | 🔵 Trivial

Wire the Claude end-to-end check into automation.

This workflow never invokes scripts/check-claude-engine.ts, so the new provider path stays covered only by manual runs. Add a secret-gated job, or at least a scheduled/manual one, so Claude session regressions are caught before merge.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yaml around lines 13 - 33, The CI workflow currently
runs typecheck, tests, and build but never executes
scripts/check-claude-engine.ts, so the Claude provider path is not covered in
automation. Add a new job or step in the existing workflow that runs the Claude
end-to-end check, gated by the required secret and/or exposed as a scheduled or
manual trigger, and keep it separate from the main npm ci/typecheck/test flow so
it only runs when credentials are available.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/codeql.yaml:
- Around line 12-16: The CodeQL workflow is only triggering for pull requests
targeting main, so stacked/intermediate PRs are missing SAST scans. Update the
pull_request trigger in the CodeQL workflow to run on every PR instead of
filtering by branches, while keeping the existing schedule intact. Use the
workflow’s pull_request event definition in codeql.yaml as the place to adjust
this trigger.

In `@packages/server/src/runner.ts`:
- Around line 52-57: The abort handling in runner.ts only terminates the shell
wrapper and still reports a successful exit on cancel. Update the logic around
onAbort and close in the runner execution path to kill the entire spawned
process tree using the same detached process-group approach as workflow.ts, and
make sure an aborted command returns a non-zero exit code instead of defaulting
to 0. Keep the fix centered on the process spawn/kill flow in the runner
function and the p.kill/close handling.
- Line 72: The close handler in runner logic is treating aborted child processes
as success by defaulting `code` to 0. Update the `p.on('close')` callback in
`runner.ts` so it checks the `signal` argument as well as `code`, and returns a
non-zero exit status when the process was terminated by the abort path (for
example after `SIGTERM`) instead of mapping it to 0. Keep the fix localized to
the close/abort handling around `done(...)`.

In `@packages/server/src/session.ts`:
- Around line 103-115: The set_provider branch in session.ts is emitting
provider_status as ok unconditionally, even when the selected provider may not
be ready. Update the provider switch flow in the Session command handling to run
the same readiness check used by provider.isReady() before emitting status, and
emit provider_status with ok only when that check passes, otherwise emit error
so the UI reflects the real precondition. Keep the existing model/default
updates and sendModels behavior in place, but base the status payload on the
readiness result for the selected provider.

In `@packages/server/test/models.test.ts`:
- Around line 35-38: The static catalog test in listModels should assert exact
model IDs instead of loose presence checks, so regressions like missing or
renamed entries are caught. Tighten the expectations in the models.test.ts case
for listModels('codex') and listModels('claude') to compare the full returned id
lists exactly, using the listModels helper and the existing static catalog names
(for example codex, claude, haiku, sonnet, opus, gpt-5-mini, fable) as the
unique anchors.

In `@packages/server/test/run-app-e2e.test.ts`:
- Around line 18-23: The e2e server setup in createServer should not hard-code a
fixed port because it can collide during parallel test runs or when the port is
in use. Update the server initialization in run-app-e2e.test.ts to use an
ephemeral port via createServer, and keep relying on server.url for the chosen
port so the rest of the test flow remains unchanged.

---

Outside diff comments:
In `@packages/server/src/agent/probe.ts`:
- Around line 36-41: Thread the abort signal through dependency installation in
probe.ts: the install path in probe() currently waits for runOnce(dir,
det.install, env) to finish before checking signal?.aborted, so Stop cannot
cancel an in-flight install. Update probe() to pass the signal into runOnce, and
change runOnce to accept that signal and terminate its spawned process promptly
when aborted, using the existing probe/runOnce symbols to locate the flow.

---

Nitpick comments:
In @.github/workflows/ci.yaml:
- Around line 13-33: The CI workflow currently runs typecheck, tests, and build
but never executes scripts/check-claude-engine.ts, so the Claude provider path
is not covered in automation. Add a new job or step in the existing workflow
that runs the Claude end-to-end check, gated by the required secret and/or
exposed as a scheduled or manual trigger, and keep it separate from the main npm
ci/typecheck/test flow so it only runs when credentials are available.

In `@packages/server/src/agent/claude/engine.test.ts`:
- Around line 40-64: The loop-guard tests in engine.test.ts only cover repeated
mcp__openrepl__list_dir calls, but READONLY_FOR_LOOP_GUARD in canUseTool also
applies to Read, Glob, Grep, read_file, and search_repo. Extend the read-only
loop-guard coverage by adding repeated-call cases for those tool names against
makeCanUseTool so the same dedup/deny behavior is verified for each read-only
tool, while keeping side-effecting tools like mcp__openrepl__write_file and
mcp__openrepl__run_app expected to always allow.

In `@packages/server/test/session.test.ts`:
- Around line 23-30: The `opening a project emits ready + tree` test in
`session.test.ts` only checks that a `ready` event exists, so tighten it to
assert the emitted `ready` payload includes the expected `workspaceDir` for the
opened project. Update the assertions around `makeSession`, `session.handle({
type: 'open_project', path: dir })`, and the `events` array so the test verifies
the specific `ready` event data rather than a generic match.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e5ae0ada-c531-4b0a-8e53-8067e37a5fa5

📥 Commits

Reviewing files that changed from the base of the PR and between 77276c4 and c7868ca.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (30)
  • .coderabbit.yaml
  • .github/pull_request_template.md
  • .github/workflows/ci.yaml
  • .github/workflows/codeql.yaml
  • .gitignore
  • .jscpd.json
  • PRD-agent-sdk-migration.md
  • package.json
  • packages/server/src/agent/claude/engine.test.ts
  • packages/server/src/agent/claude/engine.ts
  • packages/server/src/agent/claude/roles.test.ts
  • packages/server/src/agent/claude/roles.ts
  • packages/server/src/agent/claude/tools.ts
  • packages/server/src/agent/probe.ts
  • packages/server/src/agent/tools.ts
  • packages/server/src/config.ts
  • packages/server/src/providers/demo.ts
  • packages/server/src/providers/models.ts
  • packages/server/src/providers/openai-compatible.ts
  • packages/server/src/runner.ts
  • packages/server/src/session.ts
  • packages/server/test/demo-provider.test.ts
  • packages/server/test/models.test.ts
  • packages/server/test/openai-compatible.test.ts
  • packages/server/test/orchestrator.test.ts
  • packages/server/test/run-app-e2e.test.ts
  • packages/server/test/session.test.ts
  • packages/web/src/App.tsx
  • packages/web/src/store.ts
  • scripts/check-claude-engine.ts
💤 Files with no reviewable changes (5)
  • packages/server/test/orchestrator.test.ts
  • packages/server/src/providers/demo.ts
  • packages/server/src/providers/openai-compatible.ts
  • packages/server/test/demo-provider.test.ts
  • packages/server/test/openai-compatible.test.ts
✅ Files skipped from review due to trivial changes (5)
  • .github/pull_request_template.md
  • .jscpd.json
  • .gitignore
  • packages/web/src/App.tsx
  • PRD-agent-sdk-migration.md
🚧 Files skipped from review as they are similar to previous changes (6)
  • packages/server/src/agent/claude/engine.ts
  • packages/server/src/agent/claude/roles.ts
  • packages/server/src/agent/claude/roles.test.ts
  • packages/server/src/agent/claude/tools.ts
  • packages/server/src/config.ts
  • scripts/check-claude-engine.ts

Comment thread .github/workflows/codeql.yaml
Comment thread packages/server/src/runner.ts
Comment thread packages/server/src/runner.ts Outdated
Comment thread packages/server/src/session.ts
Comment thread packages/server/test/models.test.ts Outdated
Comment thread packages/server/test/run-app-e2e.test.ts
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 84.29% (🎯 90%) 1438 / 1706
🔵 Statements 84.29% (🎯 90%) 1438 / 1706
🔵 Functions 92.04% (🎯 90%) 162 / 176
🔵 Branches 80.07% (🎯 80%) 446 / 557
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/server/src/config.ts 98.07% 88.88% 100% 98.07% 89
packages/server/src/runner.ts 87.65% 71.87% 100% 87.65% 38, 62, 64-69, 87-88, 98
packages/server/src/session.ts 58.95% 74.32% 85.71% 58.95% 64, 84, 89, 91, 102, 122, 140, 142, 183-185, 220-221, 228-231, 273-274, 279-394, 397-412
packages/server/src/workflow.ts 80.95% 84.93% 83.33% 80.95% 44-45, 123-139, 155-166
packages/server/src/agent/engine.ts 100% 100% 100% 100%
packages/server/src/agent/probe.ts 52.27% 68.18% 71.42% 52.27% 37-40, 54-55, 78-116
packages/server/src/agent/tools.ts 95.45% 75% 75% 95.45% 59-60, 71-72
packages/server/src/agent/claude/map-messages.ts 100% 81.48% 100% 100%
packages/server/src/agent/claude/roles.ts 100% 80% 100% 100%
packages/server/src/agent/claude/tools.ts 100% 96.15% 100% 100%
packages/server/src/providers/claude.ts 94.44% 83.33% 100% 94.44% 35
packages/server/src/providers/models.ts 95.74% 64.7% 100% 95.74% 44-45
packages/server/src/providers/registry.ts 100% 93.33% 100% 100%
packages/shared/src/protocol.ts 0% 100% 100% 0% 48
Generated in workflow #14 for commit d31c6e4 by the Vitest Coverage Report Action

@efenocchi efenocchi merged commit 9b156d8 into main Jul 1, 2026
5 checks passed
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