acpmux resolve: a PR footer's agent link finds its session, here or on a peer, with its metadata

Merged
TE
13d ago
+820-2

Leo, 2026-10-24: "shouldnt the deeplinks and such be able to tell you a decent amount of metadata and be able to find things? likewise, what about beads? linking beads to agents and vice versa?"

This PR is the deeplink half. The beads half comes in a second PR.

What changed

acpmux resolve LINK|ID|NAME (alias find) and the RPC _acpmux/resolve {query}:

  • It takes a PR footer's https://cmux.com/deeplink/agent?harness=…&session=…&host=… (the bare URL or the whole markdown footer line), the native cmux://agent/<harness>/<session>?…, an acpmux session id, a qualified id, a harness session id, HOST/NAME, a name, or @.
  • A link's session= is the harness's own session id, so it is matched against agentSessionId, plus the family when the session has one. Matches on the host the link names come first, then this daemon's own sessions, then newest first.
  • It searches this daemon's own sessions and the session lists it already mirrors from its peers (all_session_summaries). It sends no new request to any peer.
  • Each match returns: host, peer, sessionId, qualifiedSessionId, agentSessionId, name, harness, family, model, cwd, gitBranch, status, lane, title, pullRequests, lastTurn, lastPrompt, lastMessage, createdAt, updatedAt, tags, peerState, stale, and the session's own link.
  • When nothing matches, the error says where it looked: this machine and the names of its peers. It also says if the link names a host that is neither this machine nor a peer, and which peers are not connected right now.
  • The link reader is src/agent_link.rs. Its test runs every vector in schemas/deeplinks/agent-link.json, the same vectors the web page and the app's parser run, so the three readers can't drift apart. The schema's comment now lists this reader as well.
  • Registered in rpc.rs, the protocol registry, the committed IR (spec/acpmux-protocol.json), the generated web method list, the initialize extensions list, and peer_forward's daemon-scoped list. The acpmux guide (section 6) gains one line about it.
  • No MCP tool. acpmux has no MCP surface for arbitrary sessions: the cmux-subsessions server only covers your own children. Agents use the CLI, which the guide documents.

Tests

  • Red on base: tests/peer_integration.rs an_agent_link_resolves_to_its_session_here_or_on_a_peer, run with this test on the base code, fails with "Method not found: _acpmux/resolve".
  • Green on this head, run via cmux-ci test --setup rust:
    • that peer test passes. It covers a local session from a whole footer line, a peer's session from the native link with host=b ranked first, a bare name found on the peer, an unknown link that names a foreign host (the error lists the peers and the host), and a link with no session.
    • cargo test -p acpmux --lib agent_link locate: 25 tests, including the schema vectors.
    • cargo fmt --check and cargo clippy --all-targets -D warnings are clean.
  • protocol_ir_is_current and protocol_registry_names_every_served_method also fail on base (ce5e5c3b9f7: stale titleSource in the IR, and "_acpmux/unknown" in a server/mod.rs test). Re-blessing the IR on this head changes only the titleSource drift, so the _acpmux/resolve entry matches what the registry emits. Re-running the webviews codegen on this head gives no diff.

Review

Independent Codex review (gpt-6-astra, xhigh, its own read-only checkout).

  • Round 1, at c6da1dc: BLOCK with five findings.
    1. A peer-origin resolve returned unmasked session text: the card lacked lastSeq, so server/peer_text.rs did not see it as a summary.
    2. A session with no family could match a link for a different harness.
    3. Read-only paired devices are refused _acpmux/resolve.
    4. The link reader accepted /deeplink/agent on any host or path.
    5. The CLI printed the last prompt instead of the latest reply.
  • Fixed with tests: 1 (the Origin::Peer integration test a_peer_resolves_a_sessions_text_masked was red at c6da1dc and is green now), 2, 4 and 5.
  • Not changed: 3. The read-only allowlist is an access-control list, which is the owner's policy; read-only devices still get _acpmux/sessions. The reviewer agreed it is not a blocker and it is tracked as a follow-up.
  • Round 2: SHIP, reviewed head d8b5b0ee00fc7491970d72066d9f7b8e842745d2.
  • After that, two CI reds were this PR's own, and both are fixed: the crash ratchet (two .expect( on the regex statics, now Option and checked by the vectors test) and tree-key-embeds (the schema was added to scripts/cmux-next/cmux-tui-tree-inputs.txt).
  • Round 3: SHIP, reviewed head fb82c2c1d013b82f9c15e2d51ba8729b4cd6a2f6.
  • The reds left at that head also fail on base: No new SwiftUI and Checks job structure (Swift, base job 121072285982), gallery health (2)/(3) (base run 40160510139), release-notary and Guard status. The visual-evidence check is advisory.
  • Fleet results at that head: the whole tests/peer_integration.rs passes (35 tests); the lib tests for agent_link, locate, cli::resolve, peer_text and pairing pass; fmt and clippy -D warnings are clean.

Stop condition: merged at a head the Codex reviewer said SHIP to. No new timer, observer or loop.

No capture: CLI and RPC only, nothing visible changed.

Agent session: big-red
šŸ¤– Generated with Claude Code