Skip to content

Align with RFD #533 (multi-client session attach)#3

Open
lsaether wants to merge 1 commit into
mainfrom
rfd-533-alignment
Open

Align with RFD #533 (multi-client session attach)#3
lsaether wants to merge 1 commit into
mainfrom
rfd-533-alignment

Conversation

@lsaether
Copy link
Copy Markdown
Owner

Summary

Implements agentclientprotocol/agent-client-protocol#533 — Multi-Client Session Attach — on top of amux's existing multi-subscriber proxy. amux already solved ~80% of the RFD; this PR adds the standard method names, the session/update variants, the capability advertisement, and the pending-permission re-issue.

Existing amux clients keep working unchanged via dual-emit. RFD-aware clients can use the standard methods and frames directly without knowing anything about the amux/* namespace.

What changed

New methods (proxy-handled, never forwarded to the agent)

  • session/attach { sessionId?, historyPolicy?, clientId?, clientInfo? }{ sessionId, clientId, historyPolicy, connectedClients[], history? }. Re-issues unresolved session/request_permission to the attaching client so it's actionable.
  • session/detach{ sessionId, status: \"detached\" }, then closes the WS with code 1000.

New session/update siblings of amux/* (dual-emit, fire only when an ACP session id is cached)

RFD frame (update.type) Existing amux sibling
prompt_received amux/turn_started
turn_complete amux/turn_complete
permission_resolved amux/agent_request_resolved
client_disconnected amux/peer_left

Distinguished from agent-emitted updates by update.type (proxy) vs update.kind (agent).

initialize mutation

The proxy now injects agentCapabilities.sessionCapabilities.attach: true into the upstream agent's reply — both on first cache and on the cached short-circuit path — so RFD-aware clients can detect multi-client support without knowing about amux.

historyPolicy

  • full (default) — broadcast log projected to { method, params } entries
  • pending_only — just the unresolved session/request_permission frames
  • none — omits the field
  • after_message — accepted but falls back to full until the Message ID RFD is adopted

Internals

  • New protocol::attach and protocol::session_update modules with typed builders + the RFD error codes.
  • SessionInner.pending_permission_frames: HashMap<Id, Bytes> keeps the original frame for every InFlight permission so it can be re-delivered to a late attacher with the same id (the existing first-writer-wins gate handles the reply normally).
  • route_agent_request now takes the method so the permission map is only populated for actionable requests.
  • inject_attach_capability is idempotent and runs on both code paths.

What amux still has beyond the RFD

  • The full amux/* namespace including amuxTurnId, amux/session_busy { heldBy }, role.
  • JSON-RPC id rewriting (per-subscriber mux_id).
  • initialize / session/new caching for late joiners.
  • Turn serialization with -32001 and amux/session_busy.
  • Driving-subscriber attribution in /debug/sessions.
  • TTL reconnect grace (--session-ttl-seconds).
  • WS query attach (?session=&peer_id=&peer_name=&role=).
  • Turn-end sweep of abandoned agent-initiated requests.
  • /healthz, /debug/sessions, --replay-turns, --agent-cmd.

Caveats

  • history entries are raw { method, params } rather than the RFD's typed shape — keeps amux envelope-only; documented as a deviation.
  • after_message is accepted but falls back to full (depends on the Message ID RFD).
  • Transport remains WebSocket-only; the Streamable-HTTP transport RFD is out of scope for this PR.

Test plan

  • cargo test — 67 tests pass (5 new RFD-#533 alignment tests on top of the existing 62)
  • cargo clippy --all-targets -- -D warnings clean
  • cargo fmt --check clean
  • Manual smoke against a real ACP agent (e.g. hermes acp) to confirm initialize capability injection and session/attach end-to-end

Implement the proposed Multi-Client Session Attach RFD on top of amux's
existing multi-subscriber proxy. Existing amux clients keep working
unchanged via dual-emit; RFD-aware clients can use the standard methods
and frames directly.

New surface:

- session/attach and session/detach intercepted by the proxy; never
  reach the agent. Attach returns { sessionId, clientId, historyPolicy,
  connectedClients[], history? } and re-issues unresolved
  session/request_permission to the attaching client so it's actionable.
- historyPolicy: full (default), pending_only, none, after_message
  (falls back to full until the Message ID RFD is adopted).
- session/update siblings of every amux/* metadata frame, emitted when
  an ACP session id is known: prompt_received, turn_complete,
  permission_resolved, client_disconnected. Distinguished from
  agent-emitted updates by update.type vs update.kind.
- initialize advertises agentCapabilities.sessionCapabilities.attach,
  synthesized by the proxy on top of the upstream agent's reply.

Internals:

- New protocol::attach module with typed AttachParams/AttachResult and
  RFD error codes.
- New protocol::session_update module with builders for the four
  proxy-emitted update variants.
- SessionInner gains pending_permission_frames: HashMap<Id, Bytes> so
  in-flight session/request_permission frames can be re-issued to a
  late attacher.
- route_agent_request now takes the method so it can populate the
  permission map only for actionable requests.
- inject_attach_capability mutates the cached initialize result; the
  helper is idempotent and runs on both the first-time send and the
  cached short-circuit path.
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.

1 participant