cmux NEXT: list agent runtimes (cmux agents runtimes, RFC #20149 phase 1)

Closed
TE
23d ago
+1938-46

First slice of RFC #20149, phase 1: cmux can now list the coding-agent processes running for this user. That includes ones no open tab owns, along with what cmux can prove about who started each one. It is read-only. Nothing is stopped or changed.

cmux agents runtimes          (also: cmux agent runtime list)

What changed

  • Scan (cmux-tui-platform/src/process_inventory.rs): one scan of this user's processes per request. It reads /proc on Linux, and libproc plus KERN_PROCARGS2 on macOS.
    • Each process gets pid, ppid, process group, session, controlling tty, start time, executable path, CPU time and memory.
    • Other users' processes are skipped.
    • The scan is bounded to 4096 processes, and 32 arguments per process (4 KiB in all). The environment is never read.
    • A cut or partly unreadable scan reports itself as incomplete.
    • No timer or cache. A viewer that wants fresh numbers asks again.
  • Classifier (cmux-tui-core/src/runtime_inventory/): a pure function from one scan plus the live cmux terminals.
    • Adapters recognize Codex, Claude Code and OpenCode by executable name, or by the script a node/bun launcher runs.
    • Each gets a role: cli, task, server, shared_server (Codex's managed app-server), updater or launcher.
    • Attribution:
      • verified: an unbroken parent chain from a live cmux terminal's PTY child, where each process started no earlier than its parent. A reused pid never adopts an older process.
      • probable: reparented, but still in the session of a live cmux terminal whose leader is that same process generation.
      • unattributed: everything else. A shared controlling terminal is never evidence on its own (#11004).
    • For a server, known_cmux_terminal_ids lists the cmux terminals running a same-vendor client. total_clients is always null: cmux sees processes, not connections, so zero known cmux clients never means zero clients.
    • can_stop_safely is always false in phase 1.
  • Privacy (redact.rs):
    • Arguments keep their shape only: the program name, flag names (--model=<redacted>), and up to two leading subcommand words (app-server, mcp serve). Every other argument is <redacted>, since it may be a flag value or part of a prompt.
    • The home directory in paths shows as ~.
  • API: a new resource API v2 read operation, agent.runtime.list, scoped like agent.list (machine + session).
    • Pages are refused it (page access ScreenRead).
    • cmux apps are refused it (app-host NEVER).
    • MCP doesn't expose it (CLI_ONLY), so a provider's tools never see host process data.
    • The remote relay already refuses every v2 frame.
    • The catalog, envelope schema, inventory, class tables and seven SDK descriptors are updated, and the app-host files are regenerated.

Review fixes:

  • A surface's pid outlives its child until the exit is noticed. So a terminal counts only while its child's parent is the daemon or a terminal host. A pid reused by another app can't adopt that app's tree; when lineage can't be shown, the result is a missing attribution, never a false one.
  • argv[0] is kept only when it looks like a program name, since a process can rewrite it into any text.
  • Short flags are kept only as one letter: -Xsecret becomes <redacted>.
  • Bare words are kept only when they are an adapter subcommand: claude hello becomes claude <redacted>.
  • On Linux, /proc/<pid>/cmdline is read only up to the argument bound.

Known misses (never wrong attributions):

  • A node/bun launcher with flags before the script path isn't recognized.
  • macOS zombies count as unreadable, so complete can be false on a busy Mac.

Not in this PR (later slices of phase 1):

  • A launch ledger, so a runtime keeps a verified attribution after its tab closes. Today such a runtime reads unattributed, or probable while its session leader lives.
  • The Background Runtimes section in the app.
  • Phase 2 (stop actions) waits for a decision on #20149.

Testing

On fleet macOS hosts with scripts/ci/cmux-tui-rust-check.sh:

  • all inventory at 38edddd07931: cargo fmt --check, cargo clippy --workspace --all-targets -D warnings, and 19 tests pass.
    • 7 process_inventory tests, including a live scan that must find the test process as one generation (pid plus start time), and the scan cap.
    • 12 runtime_inventory tests covering the RFC's cases plus redaction:
      • a shared server with clients in two terminals
      • zero known cmux clients is not zero clients
      • a reparented agent in the terminal session is only probable
      • an agent that left with setsid is unattributed
      • a same-tty PPID-1 process is not owned
      • a reused terminal pid doesn't adopt an older process
      • an incomplete scan is marked
      • adapter roles and versions
      • argument and path redaction
  • test agent_runtimes, a daemon test (tests/agent_runtimes.rs): a stand-in codex started in a cmux terminal must come back verified to that terminal, with redacted arguments. The same program started outside cmux must come back unattributed.
    • The first run (4a003391ac8b) failed in a useful way. The host was running a real Codex app-server --listen … (ppid 1), which the inventory correctly reported as an unattributed server, but the test took it for the stand-in. Fixed in the test, which now matches the stand-in by path.
    • Result at 38edddd07931: passes. A second fix along the way: public terminal ids start with term_.
  • test noun_first / test mcp:: (every operation has a CLI path, every read is an MCP tool or excluded): at 4a003391ac8b, noun_first (5 tests) and mcp:: (25 tests) pass.
  • Locally: check-resource-api-boundary.py, check-spec-inventory.py, check-sdk-schema.py and scripts/cmux-next/crash_ratchet.py pass.

Windows: the usual Rust builder for the Windows check wasn't reachable from my machine, so this PR's build x86_64-pc-windows-gnu CI job is the Windows check. The scan returns nothing on Windows.

Not verified: manually, which identities each adapter finds on a real Mac with Codex, Claude Code and OpenCode installed. The RFC asks for that list, and I'll add it to #20149 once the CLI is in a nightly.

Changelog

Added: cmux agents runtimes lists the coding-agent processes running for you (Codex, Claude Code, OpenCode), including ones no open tab owns, and shows whether cmux launched them.

🤖 Generated with Claude Code