Repository navigation
Bridge: deliver to Claude Code and Pi on Windows, and let them report back - #19
Closed
ahmad-ajmal wants to merge 4 commits into
Closed
ahmad-ajmal wants to merge 4 commits into
ahmad-ajmal wants to merge 4 commits into
Conversation
On Windows, `bridge start` could never deliver to Claude Code, Codex or Gemini: npm installs them behind a .cmd wrapper, and Node refuses to spawn a batch file without a shell (EINVAL, CVE-2024-27980). The throw happened synchronously inside deliverHeadless, outside the 'error' handler, so the poll loop logged "spawn EINVAL" and left the task claimed with nobody running it until the app redelivered it to exhaustion (~5 minutes, then `redelivery_exhausted`). - launchSpec(): read an npm cmd-shim and spawn what it starts (node + the CLI script, or the native .exe) with the prompt still an argument array. Any other .cmd/.bat is refused with a reason; nothing is ever run through cmd.exe, so the "harness is not a shell" contract holds. - The ladder reports a batch file it cannot start as unavailable, so `bridge start` refuses before claiming anything. - A synchronous spawn() throw now fails the task at once as `harness_spawn_failed`. - Tests: shim parsing; on Windows, delivery through an npm-style shim with a payload of cmd.exe metacharacters, and a non-shim .bat refused before any claim; everywhere, a spawn that throws fails the task immediately. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The built-in Claude profile ran `claude -p <prompt>` with no permissions. In headless mode nobody can approve a command, so every `a2app .` call the prompt asks for came back "This command requires approval". Observed in a real run: the agent did the work, then went looking for another way to report it — reading `.agent-token`, trying curl, and finally editing the adapter's state file by hand — while the task sat `working`. Grant exactly the CLI the prompt names (`Bash(a2app:*)`) and deny file edits: a delivered task is done through the app's API, never its files. The variadic tool lists come before `-p` so they cannot swallow the prompt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The delivery heartbeat sent `step: "running in <harness>"` every 20 s, so an
agent that reported its own progress ("Writing triage notes…") had it
replaced within seconds, and the app's View flipped between the two for the
whole run. Seen in a real Claude Code run. The heartbeat now sends no step;
every adapter keeps the last one, so the agent's own words (or "delivered
to …" until it says anything) stay on screen.
BridgeContext takes an optional heartbeatMs (default HEARTBEAT_MS) so the
test can watch several beats in under a second.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Pi plugin registers `pi -p {prompt}`, and on Windows `pi` resolves to
pi.cmd, which is not npm-generated: it is the single line
`node "%~dp0pi-launcher.js" %*`. The npm-shim parser did not know that
shape, so the bridge refused Pi outright.
Recognise it, strictly: after dropping `@echo off`, blank lines, comments
and setlocal/endlocal, that line must be the whole file, so running its
target directly skips nothing the batch file would have done. Anything
more is still refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Found while testing #18 end to end with a real agent. On Windows,
agent-app <dir> bridge startcould not complete a single task with Claude Code or Pi. Four problems, one commit each:1. The harness never started:
spawn EINVAL.claude,codexandgeminibehind a.cmdwrapper, and Node refuses to spawn a batch file without a shell (CVE-2024-27980).'error'handler. The task sat claimed with nobody running it until the app redelivered it to exhaustion (~5 min, thenredelivery_exhausted).launchSpec()reads the npm shim and spawns what it starts (node <cli.js>or the native.exe), with the prompt still an argument array. The "harness is not a shell" contract holds..cmd/.batis reported unavailable on the ladder and refused before anything is claimed.harness_spawn_failed.2. The agent couldn't use the a2app CLI.
claude -phas nobody to approve a command, so everya2app .call came back "This command requires approval"..agent-token, tried curl, and started editing the adapter's state file by hand.Bash(a2app:*)and deniesEdit/Write/NotebookEdit. A delivered task is done through the app's API, not its files.3. The heartbeat overwrote the agent's own step.
step: "running in claude", so the app flipped between the agent's real step and that filler.4. The Pi plugin route was refused.
pi -p {prompt}, and on Windowspiispi.cmd. That file is not npm-generated: it is the single linenode "%~dp0pi-launcher.js" %*.@echo off, blank lines, comments and setlocal/endlocal, it must be the whole file. A wrapper that does anything more is still refused.Verified
framework/cli/test/bridge.test.mjs:.batrefused before any claimmain, the new tests fail exactly as the real bug did (tasks stuckworking).bridge start. It reported its own steps and completed with summaries, and the View showed them live.pi -p {prompt}→ pi.cmd) and a directnode pi-launcher.jsroute. Both completed with summaries, writing notes, a due date and a status change that the View showed live. The Pi plugin build, test and typecheck also pass.🤖 Generated with Claude Code