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:
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-pollsandbox.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.