acpmux: a chat runs at most maxRunning of its sub-sessions at once (default 16); more wait queued, changed live

Merged
TE
15d ago
+1007-51

A chat's agent can start as many sub-sessions as its open limit allows (50), and they all run at once. Now acpmux runs at most maxRunning of a chat's sub-sessions at the same time (default 16). A start past it still creates the chat, and its first prompt waits queued (waitsForSlot) until a running one ends. A message to such a chat waits the same way.

It changes live, no restart:

  • Raising the limit starts waiting sub-sessions at once.
  • Lowering it stops nothing: running turns keep their slot; new ones wait.
  • A settings file change wakes the waiting ones too, so agents.subsessions.maxRunning applies at once.

Where the value comes from, like the other sub-session limits: the chat's own value, an ancestor's, the chat's workspace's, the agents.subsessions.maxRunning setting, then the default. The workspace level is new: _acpmux/subsession/limits {workspaceId, maxRunning} (person only), kept in ~/.acpmux/subsession-workspaces.json.

Who sees and sets it:

  • _acpmux/info's subsessionLimits gains maxRunning, maxRunningSource, running and queued; a session summary gains waitsForSlot (left out when false).
  • _acpmux/subsession/limits {sessionId, maxRunning} sets or clears (null) a chat's value: the person for any chat, or an ancestor within its own limit. A session never raises its own.
  • The agent: set_session_limits takes maxRunning; list_sessions shows maxRunning, running and queued under self; start_session says when the new sub-session is queued.
  • CLI: acpmux session limits NAME [--max N | --clear].

The person's own prompts, and chats the person started from a suggested task, never wait for a slot and hold none. The settings row in Settings and the app's stepper and context menu come in follow-up PRs; until the catalog row lands the daemon uses 16 (range 1 to 1000).

tests/subsession_runs.rs covers the queued start and its start when a slot frees, raising (a waiting one starts), lowering (running ones keep going, the next waits), the person's prompt passing the limit, the refusals (a session for itself, out of range, a workspace set by a session), and the workspace fallback and its saved file.

Changelog

Added: acpmux runs at most 16 of a chat's sub-sessions at once by default; more wait their turn, and the limit can be changed per chat, per workspace or in settings without a restart.

Observed / Not run

  • Ran on huge-blue (Linux) at 62ee5f6 merged with feat-cmux-next f76419f: cargo test -p acpmux --test subsession_runs (3 of 3 pass), cargo test -p acpmux --lib (563 pass), cargo clippy -p acpmux --all-targets -- -D warnings (clean). Protocol IR re-blessed (waitsForSlot) and the web types regenerated.
  • tests/subsessions.rs:254 ("disconnected" vs "closed") is a base flake: it failed 5 of 6 runs at base f76419f and 3 of 6 at this head.
  • Ran here: rustfmt, scripts/cmux-next/prepush-gate.py (no new violations).
  • Not run: the macOS acpmux tests and a live app. The app's stepper (#23990) and Settings/context menu (#24061) come separately.

Review fixes at 62ee5f6: a sub-session waiting for a run slot is neither ready nor done for wait_for_sessions (and is waited on by default); stopping or closing it withdraws its queued start, so it never runs later; the workspace file is written under its lock; a request naming both workspaceId and sessionId is refused.

Reds at 0bc7720 that are not this PR's: the SDK protocol contract (test_sdk_publish_conformance_runs_live_against_exact_built_binary, also red on #23990), release-notary, cmux-next checks, same-tree cmux-tui, Guard status and release rails (also red on #24061 and other PRs on the same base).

No capture: daemon-only change. The one webview file is the regenerated protocol type (waitsForSlot); no UI renders it in this PR. The stepper in #23990 has the screenshots.

Agent session: cosmic-gray

šŸ¤– Generated with Claude Code