cmux NEXT: Debug toggle turns on suggested tasks for agents (cx-lo9yf)

Merged
TE
21d ago
+170-2

A new Debug Settings toggle, Suggested tasks (agent.suggestedTasks), lets agents in cmux NEXT suggest follow-up tasks that you start as child chats beside the current one. Debug Settings exist only in DEV and NIGHTLY builds, and the toggle is off by default. With it on, the acpmux daemon the app starts gets ACPMUX_OFFER_TOOLS=1. acpmux then gives every local session, whatever the harness (Claude, Codex or any ACP agent), the cmux-sessions MCP server with offer_session (and suggest_tasks once acpmux ships it). With it off, the daemon starts exactly as before. There is no Labs setting involved. Bead cx-lo9yf; this is the app half of the suggested tasks and nested child sessions contract.

How it works

  • AgentPaneTunables.suggestedTasks is a Tunable<Bool> toggle in the agent pane's Debug Settings section, listed in AgentPaneTunables.all.
  • AcpmuxHost takes an offersSessionTools closure and reads it at each daemon start, the same way it reads the Computer Use variables. By default the closure reads AgentPaneTunables.suggestedTasks.value, so CmuxNextApp has no net change, which keeps it under its size cap. AcpmuxEnvironment.offersSessionTools then puts ACPMUX_OFFER_TOOLS=1 into the daemon's spawn environment (AcpmuxDaemonLauncher.spawnEnvironment).
  • Each agent page gets the toggle the same way it gets preview features. On the old host that's window.cmuxAcpmuxBridge?.applySuggestedTasks?.(on). On the page host it's a suggestedTasks event. The page gets it at load, at handshake and whenever the toggle changes. A single AgentPaneSuggestedTasks instance in CmuxNextAgentPane observes the tunable and pushes to every page it has seen, holding them weakly. The page side of applySuggestedTasks is in the web half.

When a toggle takes effect

acpmux reads ACPMUX_OFFER_TOOLS from its own process environment, which is fixed when the daemon starts. A toggle applies to the next daemon this app starts: after acpmux stops or crashes, or when an app update hands off the old daemon. The running daemon keeps the value it started with. The app never restarts the daemon on a toggle, so running chats are left alone. Relaunching the app on its own isn't enough if the daemon outlives the app. The page push, by contrast, is live.

Not changed

  • The pane allowlist. _acpmux/offer_respond already allows outcome as a plain param, and neither the Swift knownParams nor cmux-agent-pane-policy/policy.json lists outcome values, so outcome "here" passes as is.
  • Settings strings and the settings export. The toggle is a Debug Settings tunable, not a cmux.json setting.
  • The Chief home's daemon, which the Chief host starts separately.

Verification

AcpmuxOfferToolsTests drives AcpmuxHost.handshake against a stand-in acpmux that records the ACPMUX_OFFER_TOOLS it was started with. The host reads the toggle from a TunableStore the way the app reads it. The test checks three things: with the toggle on the daemon gets 1, by default it doesn't get the variable, and a toggle between two daemon starts applies to the second start.

  • Red, from the first version of this PR, which used the same spawn boundary with a Labs flag: on 63b40a6b27a6 (test commit 7bd808674d2e plus its test isolation fix), the "on" case recorded ["unset"] == ["1"] and the toggle case recorded ["unset", "unset"] == ["unset", "1"]. Every other suite passed. The fix commit 1eed1730bad7 added the spawn environment line, and the test passed at 7969e0f84d9a.
  • After the Labs-to-Debug-toggle rework, the test was rewritten to use the tunable in 45201e1123ec. That commit was green on arrival, because the spawn plumbing it tests already existed, so it has no separate red.
  • Green, at a5e75db451b0 before the move out of CmuxNextApp: AcpmuxOfferToolsTests passed in a full suite run (fleet step df502b5caa4c). The step exited 1 only for SidebarInsertWhileDisplayAsleepTests. That sidebar motion suite runs only on hosts with an awake display and fails there at this branch's base too. This PR doesn't touch it, and full Swift suites no longer gate merge.
  • At head 5d24d635c85d, after the move: AcpmuxOfferToolsTests passed (fleet step df33a5d8ff6b), and the Release compile succeeded (fleet step 10d367112d3c). The suite step exited 1 only for RapidWorkspaceCreateTests, which this PR doesn't touch.
  • Local checks: check-app-size.sh is ok, with no net CmuxNextApp lines. lint-ios-package-conventions.sh is clean. check-no-godfiles.sh --base <merge base> passed on every touched file, crash_ratchet.py is ok, and check-concurrency.sh reports only two existing violations in files this PR doesn't touch.
  • Not run: a live DEV build. Nothing here is visible until an agent calls offer_session, and the page side is the web half.

Changelog

none

🤖 Generated with Claude Code