cmux NEXT Cloud: an agent's start-session card in cmux agents dialog list|answer, answered at once as pending, person-only delegation

Merged
TE
2d ago
+1056-207

An agent's cmux cloud start-session no longer blocks for 2 minutes in silence on a card nobody can see from the CLI. Its Start / Not now card shows up in cmux agents dialog list (and the MCP agents_dialog_list), the person can answer it from their own terminal, and the agent's run says at once that it waits for the person.

Leo (2026-11-02): "we need to improve the docs/instructions/commands and guidance and whatnot to make this all smoother faster more efficient more exposed / cuz chief will be using this kind of flow". His brief: "Expose pending questions to the CLI. ... Never auto-approve a permission the person must give. Agents may answer only the questions the person delegated."

What happened on the nightly: an agent ran cmux cloud start-session --target machine:vm-… --agent claude --json, blocked 2 minutes, then failed "Nobody answered the question in the chat within 2 minutes, so claude didn't start". cmux agents dialog list --all did not show the card.

What changes

  1. Listed. feed.list (behind cmux agents dialog list, agents wait --until dialog, agents events and their MCP tools) now includes each waiting Cloud start ask: id cloud-start-<uuid>, kind: question, the asking chat (tab_id, agent_session), its text, machine, agent, the choices start / notNow, start_by (person, or person_or_agent when delegated) and answer_commands.
  2. Answered, by whom may. cmux agents dialog answer ID --selection start|notNow (also --mode allow|deny). The CLI now sends its origin (cli or mcp) and, from an agent's environment, its caller (agent session or Chief turn). The rule is in Rust, cmux-cloud-start-core::ask (FFI cloud_ask_answer, cloud_ask_first); the app only reads the facts:
    • Not now: anyone (it only takes the ask away).
    • Start: the person, meaning a CLI request that names no agent session (their own terminal); or an agent only when the person turned on the delegation. MCP, a named agent session, a Chief turn, or no origin at all (an older CLI) is an agent. A refused Start answers person_required with how the person answers, and leaves the card asking.
    • The FFI not answering takes nothing (fail closed).
  3. Delegation, person-only. New setting cloud.agentsStartWithoutAsking (Settings > Machines, "Let Agents Start Cloud Sessions Without Asking"), default off. It is in the agent-refused table (userOnly), so an agent's settings_set / cmux settings set is refused (the --confirm path still asks the person on a native sheet). With it on, an agent's start runs as the person's Start would, with no card.
  4. Never silent. The run that shows the card answers right away: {pending: true, waiting_for: "person", dialog_id, answer_command, text}, the text naming the command to run again and the person's answer command. Running the same command again joins the run and waits for the answer (Start, Not now or the 2-minute expiry), then returns machine_id / session_id or the refusal. The 2-minute card is unchanged.

Consent rule kept

Only the person's Start starts a Cloud machine for an agent, unless the person themselves delegated it in Settings. Nothing auto-approves. The caller rule trusts the caller's claim like every control-socket caller rule; it adds no way around the card that the start itself did not already have (the start already treats a run that names no agent session as a person's or a script's run). acpmux tool permissions are untouched: the desk still only denies those.

Commands an agent uses

cmux cloud start-session --target machine:<id> --agent claude --json   # pending + dialog_id when the person must answer
cmux agents dialog list                                                # the waiting ask, its choices and answer commands
cmux cloud start-session --target machine:<id> --agent claude --json   # again: waits for the answer, returns machine_id/session_id
cmux agents dialog answer <dialog_id> --selection notNow               # an agent may withdraw its own ask
cmux agents dialog answer <dialog_id> --selection start                # the person's terminal, or an agent once delegated

Tests

  • Rust cmux-cloud-start-core ask::tests: agent refused Start without delegation, the person allowed, a delegated agent allowed, anyone may decline, caller classification, choice words.
  • FFI cloud_harness_login::cloud_ask cases through the C ABI.
  • cmux-tui dialog_answers_name_who_answers: the CLI's origin and caller on an answer.
  • Swift CloudAskDialogsTests: listing, refusal of an agent / MCP / Chief / no-origin Start, the person's Start, a delegated agent's Start, decline and a gone dialog, the early pending answer, and the setting being person-only and off by default.

Not in this PR: the Move to Cloud card (CloudMoveAsks) does not block the agent's run and has its own Always grant; listing it is a follow-up.

Agent session: agent session

🤖 Generated with Claude Code


Autofix with [code]smith
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

An agent's cmux cloud start-session no longer blocks in silence on a Start / Not now card the CLI never surfaced: the card now shows in cmux agents dialog list (and MCP agents_dialog_list), anyone can answer it, and the agent's run reports pending at once.

  • cmux agents dialog answer <id> --selection start|notNow answers the card; a listed label in any language answers as its option.
  • Not now is anyone's; Start is the person's or a delegated agent's. Who may answer is decided in cmux-cloud-start-core ask (FFI cloud_ask_answer, cloud_ask_first); a refused Start leaves the card asking, and the FFI never answers on its own.
  • New person-only setting cloud.agentsStartWithoutAsking (Settings > Machines, off by default) lets an agent start without asking; agents cannot set it.
  • Only the first answer to a dialog is taken; a later answer reports the ask gone.
  • Re-running the same start-session command joins the run and waits, then returns machine_id/session_id or the refusal.
  • The runs registry (CloudStartRuns) is generic in CmuxNextCloud, and the dialog and pending shapes (CloudAskDialog) live in CmuxNextControl.
  • Pins the app FFI to a build carrying the ask ops.