Repository navigation
Add a cross-workspace live agent session list (cmux sessions live) - #17772
azooz2003-bit wants to merge 13 commits into
Conversation
cmux can already tell you about one workspace at a time. Nothing answers "which of my agents is blocked on me right now" across every window, so you go looking window by window. Three surfaces had grown their own inline ranking (the mobile chat list, the JS agents panel) and they disagreed on tie-breaks. This adds one shared triage order and one read that uses it: - AgentSessionAttention in CmuxMobileHost ranks by bucket (needs input, working, idle, ended), then oldest-first inside the two timed buckets and newest-activity-first inside the settled ones, with sessionID as the final tie-break so the order is total. The registry iterates a dictionary, so a total order is what makes the output stable. - AgentSessionListPayload projects records onto the wire, omitting absent optionals rather than sending null, and applies the shared order itself so every client of the verb gets the same triage. - agent.sessions.list returns that payload from the live registry. - cmux sessions live prints it, with --state, --agent, --needs-me, --limit, --all and --json. The verb is deliberately unscoped. registry.sessions(workspaceID:) filters on a stored workspace id that cmux re-mints on relaunch while surface bindings survive, so scoping here would silently drop pre-relaunch sessions. Callers filter on the workspace_id they can see. It is deliberately not relay-exported either. The reply spans every workspace and carries titles, working directories, transcript paths and pids, which is local state a remote session has no business reading. RemoteRelayCommandPolicy defaults to deny and the remote capabilities reply is filtered through permittedMethods(from:), so listing the method in capabilities does not expose it. No policy change, so no policy test. state_confirmed carries the one caveat worth surfacing: process-table discovery proves a session exists but not that it is idle, so an unconfirmed idle is not reported as plain idle. The logic lives in the package, not the app target, so unit tests cover it without a GUI build. The one thing unit tests cannot catch is the method name agreeing between the CLI and the app, so a CLI product test drives the real binary against a stub socket and asserts the method it asks for. ## Changelog Added: `cmux sessions live` lists agent sessions across every workspace, ranked by which ones are waiting on you. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Catch-up merge by scripts/ci/catch_up_pr.py (RFC #14631). Merged by scripts/merge-main.sh: origin/main at 3887653, the newest commit with green CI fast guards (1 newer skipped). Resolved conflicts: - cmux.xcodeproj/project.pbxproj: union of added entries, then normalize-pbxproj.py Catch-up-previous-head: 610f1ac Catch-up-base: 3887653
Review and CI on the first pass turned up four things worth fixing before this ships. A flag-shaped filter value was accepted as a value: `cmux sessions live --agent --needs-me` filtered on the literal agent name "--needs-me", printed "No live agent sessions matched." and exited 0, so a typo looked like an empty queue. Value-taking flags now reject a `--`-prefixed value and say which flag needs one; `--limit -5` still reaches the existing positive-integer error. The limit was applied before counting, so the summary and `--json` described only the rows that survived truncation. Matching now happens once, the tally covers every match, JSON reports `total_matches`, `total_live` and the effective `limit`, and a truncated text list ends with the existing translated `... N more.` footer. `sessions live --agent claude-code` matched nothing while `sessions list --agent claude-code` worked, because only the sibling resolved aliases. Both now go through `sessionsCanonicalAgentName`, and an unknown agent is an error instead of an empty result. `AgentSessionAttention` was an all-static namespace type, which the package-conventions lint rejects repo-wide. The ordering now hangs off the types it describes: `AgentSessionAttentionRank`, `AgentSessionAttentionCounts`, and extensions on `ChatAgentState`, `AgentChatSessionRecord` and `Collection<AgentChatSessionRecord>`. The unused `needingAttention` helper is gone; the rule it protected (a long-running `working` session must not satisfy a "needs me" filter) is now covered at the payload level. The text summary is `key=value` tokens rather than a sentence, matching the invariant tokens already in this output, so it needs no plural selection in any locale. Every other new string is in the catalog for all nine macOS locales. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The payload spelled out the four wire names next to the four stored properties, so a renamed rank would have left the JSON keys behind. It now iterates the ranks and asks each for its `wireName`, which is the same name the session's own `state` field reports and the same one `--state` accepts. The new test pins that: the key set is exactly the ranks plus `total`. This also gives `CaseIterable` and the counts subscript a production caller, which the review noted they lacked. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`cmux --help` now prints `sessions [list|live] [options]`, so the probe list in docs/cli-contract.md still expected the old `sessions [list]` text and `tests/test_cli_contract_help.py` failed in the macOS compile admission job. Also add a dogfood tour that builds four synthetic agent sessions over `cmux hooks enqueue` and runs `cmux sessions live` against them, so the ordering, the summary counts, the filters and the two argument errors can be seen from CI without an authenticated agent. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… summary keys The --help probe still expected the old synopsis, which failed the early CLI smoke check. The untranslated locales of cli.sessions.command also kept the list-only synopsis. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Two conflicts, both keep-both: - `Packages/macOS/CmuxMobileHost/Package.swift`: main added `swiftSettings: [.swiftLanguageMode(.v6)]` to the test target, this branch added the `CmuxAgentChat` product dependency it needs. Both stay. - `docs/cli-contract.md`: main extended the `sessions [list]` row with the cmux-owned scratch root metadata, this branch added the `sessions live` row below it. Both stay. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
# Conflicts: # cmux.xcodeproj/project.pbxproj
Main assigned C117570000000000000000C1 to ClaudeHookSessionStoreRecoveryTests' build file. This branch used the same ID for CLISessionsLiveTests.swift, so the merged project had one object ID for two objects. Move the file reference and build file to unused IDs (…E1, …E2). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 51 minutes. View limit detailsLimit details: You’ve used all 10 included reviews currently available. Review configuration: ⚙️ Run configuration
📒 Files selected for processing (17)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Note Pull Request opener @azooz2003-bit is not an author or co-author of any commit in this PR (commit identities: All contributors have signed the CLA ✍️ ✅ |
What this is
cmux can tell you about one workspace at a time. Nothing answers "which of my agents is blocked on me right now" across every window, so today you go looking window by window. This adds one shared triage order and one read that uses it.
This is the gap I hit every day: a session list with status marks is the first thing comparable agent UIs offer, and cmux has no cross-workspace equivalent.
No UI in this PR. It is the cross-workspace projection plus a JSON read over the existing CLI and socket surface. Nothing in the sidebar, so nothing here conflicts with the in-flight UI set (#14838 #14855 #14903 #14877 #14893 #14958 #14450 #14875 #14876).
The shape
Filters:
--state,--agent,--needs-me,--limit,--all,--json. Unknown flags and unknown state names fail closed without touching the socket.Design notes
One ranking, not a fourth copy.
AgentSessionAttentionranks by bucket (needs input, working, idle, ended), then oldest-first inside the two timed buckets, newest-activity-first inside the settled ones, andsessionIDas the final tie-break. The registry iterates a dictionary, so a total order is what makes output stable; there is a test that shuffles the input 32 times and asserts one order. Three surfaces had already grown their own inline comparators and disagreed on tie-breaks; this is where the next one should read from.Deliberately unscoped.
registry.sessions(workspaceID:)filters on a stored workspace id, and cmux re-mints workspace ids on relaunch while surface bindings survive. Scoping here would silently drop every pre-relaunch session.mobile.chat.sessionssolves that properly by resolving live surface ids first; until this verb does the same, it returns everything and callers filter on theworkspace_idthey can see. Follow-up noted.state_confirmed. Process-table discovery proves a session exists but not that it is idle. An unconfirmedidleis not reported as plain idle, in the JSON or in the text output.Relay authorization analysis
Required by CLAUDE.md for any new v2 method.
Conclusion: do not allowlist.
RemoteRelayCommandPolicydefaults to deny (RemoteRelayCommandPolicy.swift:79), so the safe outcome is the default one and there is no policy change to test. Adding the method to the capabilities list does not leak it either: the remote-scope capabilities reply is filtered throughpermittedMethods(from:). Documented in the verb's doc comment and indocs/cli-contract.mdso the next person does not have to re-derive it.Tests
JSONSerialization.isValidJSONObject.["agent.sessions.list"], which is the one integration risk unit tests cannot catch. The third asserts a bad--statenever contacts the socket, against a live-but-unawaited server so the assertion is not vacuous.docs/cli-contract.mdhelp probes, so the usage text is contract-checked.Packages/, not the app target, so all of this runs in CI without a GUI build.Scoped verification:
python3 scripts/verify-local.py, 15/15 green. Native compilation and app tests are CI's.Open design call
The
agent.sessions.list/cmux sessions livenamespace is parked on #13742 with a recommendation. That wants resolving before merge.🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.Summary by cubic
Adds
cmux sessions live, a cross-workspace live agent session list that answers "which agent is waiting on me right now" across every window — previously cmux only reported one workspace at a time.AgentSessionAttentionRankplus extensions onChatAgentStateand session records), replacing three inline comparators that disagreed on tie-breaks. It buckets needs-input, working, idle, ended; timed buckets sort oldest-first, settled buckets newest-activity-first, with session ID as the final tie-break for a total order.agent.sessions.list(also surfaced in client capabilities) and CLI filters--state,--agent,--needs-me,--limit,--all,--json. The reply is deliberately unscoped (cmux re-mints workspace IDs on relaunch, so scoping would drop pre-relaunch sessions) and not relay-exported (the reply spans all workspaces and carries titles, paths, and pids; relay policy defaults to deny).state_confirmedflags sessions discovered from the process table whose idleness was never hook-confirmed, so an unconfirmedidleis never reported as plain idle.---prefixed value, an unknown--agentis an error instead of an empty result, and agent aliases resolve consistently withsessions list.total_matches,total_live, and the effectivelimit, the truncated text list ends with the existing "... N more." footer, andstate_countsis built from the ranks' own wire names so keys can't drift from the names--stateaccepts.sessions [list|live] [options]across all locales and the docs contract probe, and adds a dogfood tour building four synthetic sessions overcmux hooks enqueueso ordering, counts, filters, and argument errors are visible from CI without an authenticated agent.state_countskey set (including a 32-shuffle order-stability check), 3 CLI product tests asserting the exact socket method name and that bad flags fail closed before touching the socket (mock server command reads are lock-protected), and docs contract probes.Written for commit 98f4d6c. Summary will update on new commits.
Summary by CodeRabbit
cmux sessions liveto view agent sessions across workspaces, with filters for agent and state, a needs-attention option, result limits, and JSON output.Migrated from #15319 after correcting the PR author identity. The head branch and commit history are preserved.