Skip to content

feat(flow): code-as-action agent for compile-agent run 1 - #1305

Open
abrichr wants to merge 15 commits into
claude/crystallize-from-prompt-89950efrom
claude/code-as-action
Open

abrichr wants to merge 15 commits into
claude/crystallize-from-prompt-89950efrom
claude/code-as-action

Conversation

@abrichr

@abrichr abrichr commented Oct 18, 2026 •

Copy link
Copy Markdown
Member

Stacked on #1277 (compile-agent). Merge that first; this PR then retargets to main.

What

ClaudeAgent(vision="code"): the run-1 agent acts through code instead of a screenshot after every click. It gets one run tool that takes a short script:

  • read the structure: tree, find, text, read (the DOM of the page and every frame; a desktop accessibility tree; OCR text on RDP and Citrix);
  • act by role and name: click, fill, select, press, scroll;
  • screenshot only when it asks;
  • sh: a read-only shell in a working directory (allow-listed text tools, | only, no path outside the directory, sandbox-exec with network denied on macOS), and save FILE to keep state on disk.

Every action is a structured step (element role, name and row; action; value) in code_action_trace.json, checked against the locator the recorder stored and then against the compiled program (check_trace_against_ir; steps distill dropped are reported as distilled). The identity the agent read before the save and the inputs it read back after it go to the person who confirms the record checks.

Design follows CUAWright (arXiv:2610.04116), OpenGCUA (arXiv:2610.22200) and ANIS (arXiv:2610.15765), with one difference: the script is not general code. Each line is a read or one recordable action, so every action goes through the save gate and becomes a program step. Docs: packages/flow/docs/CODE_AS_ACTION.md.

Save-gate fixes (all compile-agent agents)

Found running OpenEMR. The first three are also in #1277 (ported there):

  • a Save inside an iframe was never gated; the harness now reads through the backend's frame chain;
  • Enter with focus inside an iframe read the top page;
  • a click on a "Postal code" field was gated as a save ("post");
  • Enter on a native drop-down was gated as a save;
  • a layout table row with many controls was taken as the record's scope.

Measured

Quotable figures will be scoreboard arm 7 (#1307), once the scoreboard's single runner has run it.

The earlier exploratory measurement in packages/flow/benchmark/code_as_action/ (its own runner, before the scoreboard) is kept as raw data: OpenEMR referral code 8/8 at $0.055 against the screenshot loop's 6/8 at $0.33 (both stops by the save gate, no wrong save); MockMed code $0.012, structure-first $0.014, screenshot loop $0.075. Spend there: $4.06 of its $15 cap.

Not done here

Tests

tests/test_code_action.py (shell sandbox, script language on a fake desktop surface, gate hints, trace check, the Claude loop with a fake client) and tests/e2e/test_code_action_e2e.py (MockMed through the whole pipeline with a scripted agent, wrong patient refused, a form inside an iframe), added to the flow-critical CI job. No paid calls.

🤖 Generated with Claude Code

abrichr and others added 10 commits October 18, 2026 15:45
ClaudeAgent(vision="code") acts through one run tool that takes a short
script: read the DOM, accessibility tree or OCR text, act by role and
name, scroll, screenshot only on request, and a sandboxed read-only shell
in a working directory. Every action is a structured step checked against
the locator the recorder stored and the compiled program.

The save gate now reads inside iframes, takes a tool's hint for frames and
desktop surfaces, and no longer treats a click on a text field such as
Postal code as a save.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ocus in frames

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ed OpenEMR runs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, not missing

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… result files

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- shell: per-tool flag allow-list, no long options, attached values checked,
  quoted operators are words, process-group kill; macOS profile denies other
  programs and reads outside the working and system directories
- gate: on a surface with no page, Enter and Space are always gated; select
  only on lists, and a drop-down that submits on change is gated
- a bad number is a line error; a line that acted before failing keeps its
  step in the trace and reports a possible commit; error text is scrubbed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…save

Any onchange counted before, so OpenEMR's Title drop-down was gated like a
save and refused every run at the first select.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
abrichr and others added 2 commits October 18, 2026 17:50
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
abrichr and others added 2 commits October 18, 2026 20:18
…a path not on this branch

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

1 participant