Skip to content

Add a cross-workspace live agent session list (cmux sessions live) - #17772

Closed
azooz2003-bit wants to merge 13 commits into
mainfrom
parity/sessions-live
Closed

azooz2003-bit wants to merge 13 commits into
mainfrom
parity/sessions-live

Conversation

@azooz2003-bit

Copy link
Copy Markdown
Collaborator

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

$ cmux sessions live
3 live agent sessions (1 needs input, 1 working)
needs_input  12m  claude  sess-needs  Fix the parser
    cwd=/src/cmux surface=surface-1 workspace=ws-1
working       3m  claude  sess-work   Rework the diff view
    cwd=/src/cmux surface=surface-2 subagents=2
idle           -  codex   sess-idle   Read the relay policy
    cwd=/src/cmux surface=surface-3 state=unconfirmed

$ cmux sessions live --needs-me --json
{"sessions":[...],"count":1,"state_counts":{...},"matched_of_total":3,"generated_at":"..."}

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. AgentSessionAttention ranks by bucket (needs input, working, idle, ended), then oldest-first inside the two timed buckets, newest-activity-first inside the settled ones, and sessionID as 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.sessions solves that properly by resolving live surface ids first; until this verb does the same, it returns everything and callers filter on the workspace_id they can see. Follow-up noted.

state_confirmed. Process-table discovery proves a session exists but not that it is idle. An unconfirmed idle is 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.

  • Local command or content execution: none. The verb reads registry records and formats them. It takes no parameters at all.
  • Access to objects the caller does not own: yes, by design, and that is exactly why it stays local. The reply spans every workspace, not the caller's session.
  • Local-state exposure: yes. Conversation titles, working directories, transcript paths and pids. A remote session has no business reading any of it.

Conclusion: do not allowlist. RemoteRelayCommandPolicy defaults 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 through permittedMethods(from:). Documented in the verb's doc comment and in docs/cli-contract.md so the next person does not have to re-derive it.

Tests

  • 19 package tests on the ranking and the payload, including the 32-shuffle order-stability test, clamp-to-zero for a future timestamp, omitted-vs-null optionals, and JSONSerialization.isValidJSONObject.
  • 3 CLI product tests driving the built binary against a stub unix socket. The first asserts the method asked for is exactly ["agent.sessions.list"], which is the one integration risk unit tests cannot catch. The third asserts a bad --state never contacts the socket, against a live-but-unawaited server so the assertion is not vacuous.
  • 3 new docs/cli-contract.md help probes, so the usage text is contract-checked.
  • The logic lives in 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 live namespace is parked on #13742 with a recommendation. That wants resolving before merge.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with 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.

  • Introduces one shared triage order (AgentSessionAttentionRank plus extensions on ChatAgentState and 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.
  • Adds socket v2 method 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_confirmed flags sessions discovered from the process table whose idleness was never hook-confirmed, so an unconfirmed idle is never reported as plain idle.
  • Value-taking flags reject a ---prefixed value, an unknown --agent is an error instead of an empty result, and agent aliases resolve consistently with sessions list.
  • Tallying happens before the limit truncates; JSON reports total_matches, total_live, and the effective limit, the truncated text list ends with the existing "... N more." footer, and state_counts is built from the ranks' own wire names so keys can't drift from the names --state accepts.
  • Updates the help usage line to sessions [list|live] [options] across all locales and the docs contract probe, and adds a dogfood tour building four synthetic sessions over cmux hooks enqueue so ordering, counts, filters, and argument errors are visible from CI without an authenticated agent.
  • Tests: package tests covering ordering, payload, and the rank-derived state_counts key 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.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Added cmux sessions live to view agent sessions across workspaces, with filters for agent and state, a needs-attention option, result limits, and JSON output.
    • Sessions are ordered by attention state for triage, with state counts and activity details included in the results.
    • Integrations connected to the running app can now retrieve the live session list.
  • Documentation
    • Expanded CLI help and documentation with live-session options, ordering, output details, and the requirement to run the app.

Migrated from #15319 after correcting the PR author identity. The head branch and commit history are preserved.

teamleaderleo and others added 13 commits September 28, 2026 03:35
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>
@coderabbitai

coderabbitai Bot commented Oct 6, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You'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.

Check out review usage here.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Repository: manaflow-ai/cmux/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 81953d28-8386-44c2-aeb5-368b89c1a812
📥 Commits

Reviewing files that changed from the base of the PR and between a8c4861 and 98f4d6c.

📒 Files selected for processing (17)
  • CLI/CMUXCLI+SessionsList.swift
  • CLI/CMUXCLI+SessionsLive.swift
  • CLI/CMUXCLI+TaskHelp.swift
  • CLI/cmux.swift
  • Packages/macOS/CmuxMobileHost/Package.swift
  • Packages/macOS/CmuxMobileHost/Sources/CmuxMobileHost/AgentChat/AgentSessionAttention.swift
  • Packages/macOS/CmuxMobileHost/Sources/CmuxMobileHost/AgentChat/AgentSessionListPayload.swift
  • Packages/macOS/CmuxMobileHost/Tests/CmuxMobileHostTests/AgentSessionAttentionTests.swift
  • Resources/Localizable.xcstrings
  • Sources/TerminalController+AgentSessionsCommands.swift
  • Sources/TerminalController+Capabilities.swift
  • Sources/TerminalController.swift
  • cmux.xcodeproj/project.pbxproj
  • cmuxCLITests/CLISessionsLiveTests.swift
  • docs/cli-contract.md
  • dogfood/scenarios/agent-sessions-live-tour.json
  • scripts/localization-allowed-omissions.json
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

Note

Pull Request opener @azooz2003-bit is not an author or co-author of any commit in this PR (commit identities: teamleaderleo, claude). The CLA check will still proceed and requires every listed identity plus @azooz2003-bit to have signed.

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants