Skip to main content
The client-neutral core: launch over SSH with merv_run <label> -- <command>, then observe the durable receipt with sandbox.runs. What differs per client is how the agent waits without burning its context or a tool-call timeout.

The watcher

merv-runs-wait (installed with the plugin as bin/merv-runs-wait) blocks until a run settles. Its exit is the wake signal. stdout carries exactly one line:
Two modes:
A watcher killed outright exits with no line at all; treat a missing line exactly like poll_error.

wait_url

Each sandbox.runs row carries a wait_url signed for exactly one sandbox + label. It’s served by an auth-exempt route, so an agent can wait without holding a credential; it reveals only that the run ended and how (status, exit_code) — logs and receipts stay behind authenticated tools. It expires about six hours after the brain last saw a terminal run, or at sandbox lease + one day. Treat it like a status pager, not a secret: fine for a local background process, not for public paste. Direct/library callers with no reachable base URL get rows without a wait_url — use keyed mode.

Long-polling instead

Where no watcher can run (Claude Desktop and similar MCP-only clients), long-poll sandbox.runs with wait_seconds — recommended ≤ 45 (most MCP clients cut tool calls near 60 s; the server cap is 300 s), never tighter than 60 s apart.

Per client

Not yet documented per client — the generic pattern applies: GitHub Copilot CLI, Gemini CLI, Qwen Code, OpenCode run the watcher in a background shell and treat its exit as the wake signal; OpenHands, Replit Agent long-poll sandbox.runs. Concept: Sandboxes.