Skip to content

feat(desktop): batch execute — one round-trip for N actions - #24

Merged
rrader26 merged 1 commit into
mainfrom
feat/desktop-execute-batch
May 12, 2026
Merged

feat(desktop): batch execute — one round-trip for N actions#24
rrader26 merged 1 commit into
mainfrom
feat/desktop-execute-batch

Conversation

@rrader26

Copy link
Copy Markdown
Contributor

Summary

  • New tool `agentmark_desktop_execute_batch` — pass an array of actions, get them executed inside the bridge in one stdio round-trip.
  • New backend method `executeBatch?()` — optional; backends without it fall through to a loop of `execute()` in the plugin handler (still saves the MCP dispatch overhead).
  • Native batched implementation in both bridges:
    • Windows: `HandleExecuteBatch` in Program.cs (one STA invocation processes the whole array).
    • macOS: `handleExecuteBatch` in Dispatcher.swift (one AXAPI invocation loop).

Why

The audit yesterday showed both bridges already use the fast SetValue/AXSetValue path for `type` actions — the per-key keystroke synthesis was already only a fallback. The actual bottleneck for bulk-input workflows ("fill 10k Excel cells", "populate a 500-row form") is the per-call dispatch overhead: each `agentmark_desktop_execute` is one MCP dispatch + one stdio round-trip + one ValuePattern.SetValue. SetValue itself is microseconds; the round-trips dominate.

Result for a 1000-action workflow:

Old New
MCP dispatches 1000 1
Bridge stdio round-trips 1000 1
ValuePattern.SetValue calls 1000 1000 (same)

What's not in this PR

  • Grid-aware addressing (`grid_set` action, GridPattern-direct cell access without binding entries) — Phase 2, follow-up. This batch tool composes cleanly with that: when grid_set lands, you can batch 10k grid_set ops without needing 10k action_ids in the snapshot.

Fail-fast semantics

Every `action_id` is resolved against the cached snapshot binding before any backend call. An unknown action_id in position N aborts the entire batch before touching the bridge — guards against partially-corrupted state on ordered workflows. Tested explicitly.

`on_error` controls behavior past the first runtime failure (e.g. element disabled, no longer in tree):

  • `"stop"` (default): abort the rest, return results so far + the failing one.
  • `"continue"`: run every action regardless. For independent bulk fills.

Test plan

  • `pnpm build` clean (TS + Swift compilation paths)
  • `pnpm test` — 342 pass / 10 skip (4 new desktop dispatcher tests)
  • Manual on Windows VM: snapshot Notepad, batch-type a 100-line text block, confirm latency <1s
  • Manual on macOS: snapshot TextEdit, same drill via macos_axapi backend

🤖 Generated with Claude Code

The MCP/bridge dispatch overhead, not the SetValue call itself, is the
real bottleneck for bulk-input workflows (form fills, Excel cell writes).
ValuePattern.SetValue / AXSetValue are already the fast path on both
bridges; the cost is paying for N MCP dispatches + N stdio round-trips
when an agent drives many cells in sequence.

This adds `agentmark_desktop_execute_batch` — pass an array of actions,
get them executed inside the bridge in one stdio call. For a 1000-action
batch that's ~1 dispatch + ~1 stdio call instead of 1000 of each.

Changes:
- `DesktopCaptureBackend.executeBatch?()` — optional method; backends
  without it fall through to a loop of execute() in the plugin handler.
- `FixtureBackend.executeBatch` — runs the array; honours on_error.
- `WindowsUiaBackend.executeBatch` — calls bridge `execute_batch` method.
- `Program.cs HandleExecuteBatch` — single STA invocation processes
  the entire array; `onError: stop | continue`.
- `MacosAxapiBackend.executeBatch` — same pattern, bridge `execute_batch`.
- `Dispatcher.swift handleExecuteBatch` — loops actions inside one
  AXAPI invocation.
- Plugin tool `agentmark_desktop_execute_batch` validates every
  action_id up front; fail-fast on unknown IDs so a bad batch never
  reaches the backend.

Tests (4 new, 330 total):
- Batch type-action sequence updates multiple snapshot values.
- Fail-fast on unknown action_id does NOT touch the backend.
- Empty actions array is rejected.
- No snapshot → clear error.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@rrader26
rrader26 merged commit c835199 into main May 12, 2026
4 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