cmux NEXT CLI: progress while waiting for the app, and a timeout that says what to run next

Merged
TE
22d ago
+310-4

Summary

cmux action run, cmux <noun> <verb>, cmux open, page waits and screenshots wait up to 45 s (35 s for screenshots) for the cmux app with no output, then fail with "the cmux app did not answer within 45 s". An agent can't tell a slow action from a hung app, or what to do next.

Now, for the long waits (the 45 s run wait and the 35 s screenshot wait, not 5 s reads like snapshot.get or identify), the CLI prints waiting for the cmux app to run window.new (2 s of 45 s) to stderr every 5 s. Notes print in human output, and in --json/--jsonl only when stderr is a terminal, so piped JSON errors stay one object per line. Quiet output prints none. A small ticker thread blocks on a channel and stops when the request returns. If that thread can't start, the request runs without notes. Notes count against the first timeout's total, and stop there even when a busy retry waits longer. Timeouts are unchanged.

When the app took the request and never answered, the error keeps code app.transport and exit code 3, names the socket and the next command. That command keeps the route the call took: cmux --app-socket <path> identify, cmux --socket <path> identify or cmux --session <name> identify, and bare cmux identify only when the call named no route, and adds details: owner: "app", socket, method, waiting_for, timeout_seconds, waited_seconds, next_command. Other transport errors are unchanged.

A test build (debug assertions) honors CMUX_TEST_APP_WAIT_TIMEOUT_MS so the boundary test runs in 3.5 s; release builds ignore it.

Not changed here:

  • link/agent_cli/daemon.rs (CALL_TIMEOUT 45 s, BEGIN_TIMEOUT 60 s, FINISH_TIMEOUT 90 s): cmux link agent transfer is a JSON-only machine command, and its errors already name the side (<side>_unreachable).
  • The "55 s permission stall" from the lane brief is in the cmux-cua engine (manaflow-ai/cmux-cua, see plans/cmux-next/computer-use.md section 0), not in this CLI.
  • cmux mcp app tools also use the 45 s wait; MCP has no stderr reader, so they're left as is.

Proof

Boundary test tests/app_wait_progress.rs: runs the real cmux-tui binary against a socket that is bound but never accepts. It runs a waiting action run once in human output and once with --json, plus identify, a short read that must stay quiet.

  • First red (a35a0045ee675, test only): scripts/verify-cmux-tui-hosted.sh --filter a_silent_app_shows_progress, run 38554135963. test (linux) failed after 45.6 s: no progress line: the cmux app did not answer within 45 s.
  • First green (4aa722fd9a910): same command, run 38564534125, test (linux) passed. I cancelled the run when the macOS job had sat in the hosted runner queue for over an hour.
  • Review red (f51ebbbaec75c, test now asserts the routed next command and a quiet identify): same command, run 38577601751. test (linux) failed: the message still said bare cmux identify (no routed next command).
  • Review green (4a60542459b5b): scripts/verify-cmux-tui-hosted.sh --full, run 38579273594. test (linux), which runs every cmux-tui test including this one, and test (windows) passed. valgrind-leak-check (startup) failed again on its exact-test lookup for config::tests::ghostty_config_files::load_uses_file_ghostty_defaults_without_invoking_external_resolver, which this PR doesn't touch. The macOS jobs were still queued for a hosted runner, so macOS is not verified.

Changelog

Changed: cmux NEXT CLI commands that wait for the app show progress on stderr and, on timeout, name the socket and the next command to run.

🤖 Generated with Claude Code