feat(cli): ensure the daemon explicitly before attaching in the CLI and MCP

TH
26m ago
+538-5

Summary

Layer 3 of 3 for #3754 (umbrella #3747); based on #3765. The CLI and MCP stop managing the daemon implicitly.

  • withEnsuredDaemon wraps the CLI's default transport and each MCP tool call's client. The first request that needs a local daemon calls ensureDaemon() once; every request then attaches. A command that never reaches the daemon starts none.
  • Intentional CLI behavior change: a daemon from another version or build is replaced only if idle. A busy one fails the command with daemon-busy instead of losing its sessions; one without the quiesce capability fails with daemon-incompatible. Recorded in the changelog and CLI help, and in the client docs.
  • agent-device proxy is unchanged and stays on the managed path.
  • A request that finds its ensured daemon gone ensures again and retries once; nothing was sent.
  • Pass-through, unchanged: remote --daemon-base-url connections and a one-shot replay/test on its private ephemeral daemon. A timeout no longer retires an unresponsive daemon from the CLI or MCP (changelog).

11 files incl. docs. Closes #3754.

Validation

pnpm check:affected --run --head e1f0bca89a: passed. Tests cover ensure-once, idle replacement, busy refusal before send, remote and private-replay pass-through, and re-ensure after the daemon vanishes. An independent review of layers 1-3 found 7 issues. Five are fixed. Uploads and lease-less holds being outside the census, and quiesce over HTTP with an auth hook, are documented in ADR 0006. A hung but connectable daemon is no longer retired by a CLI timeout; the changelog says so.

🤖 Generated with Claude Code