Skip to content

acpmux: move a chat to another daemon with its history, queue and draft (acpmux.transfer.v1, cx-xtg0.6) - #19359

Merged
teamleaderleo merged 9 commits into
feat-cmux-nextfrom
op-next-steering/chat-transfer
Oct 11, 2026
Merged

teamleaderleo merged 9 commits into
feat-cmux-nextfrom
op-next-steering/chat-transfer

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 11, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

An agent chat could not move between daemons. _acpmux/export / import only wrote and read bundle directories on one disk. Import never made the queue live, and nothing retired the source, so a queued prompt could run on both daemons or on neither.

This adds acpmux.transfer.v1: any client moves a chat from one acpmux daemon to another, carrying its history, queue (held state included), draft and agent store. The other daemon can be a sibling build's (tagged builds each run their own daemon) or a remote machine's. The client pumps the bundle, so neither daemon needs to reach the other. This is the base for the Swift session.transfer chat slice (cx-xtg0.7) and for Move to Cloud.

Source

  • _acpmux/transfer_begin {sessionId, interrupt?}:
    • Freezes the chat: no prompt, steer, queue change, draft or policy change, and nothing queued starts.
    • A running turn refuses the move (transfer.busy) unless interrupt stops it.
    • Answers the manifest: {schema, transferId, sessionId, files:[{path,size}], session}.
  • _acpmux/transfer_read {transferId, path, offset, limit≤4 MiB}: returns {data (base64), next, eof}.
  • _acpmux/transfer_commit {sessionId, transferId, to?}: withdraws the queue (it runs on the target), clears the draft and closes the chat.
  • _acpmux/transfer_abort {sessionId, transferId}: gives the chat back as it was.

Target

  • _acpmux/transfer_receive {manifest}: returns {receiveId}. Refused with transfer.exists (that chat is here) or transfer.harness (agent not configured here). A chat that moved away from here can come back.
  • _acpmux/transfer_write {receiveId, path, offset, data}: pages, in order.
  • _acpmux/transfer_finish {receiveId, cwd?, name?, from?}:
    • imports the chat, in cwd if its folder has another path here;
    • its queue is back before any client sees it, and runs at once;
    • logs transferred_in {transferId} and answers the session summary.

Exactly once: the move is in the log (transfer_out, transfer_aborted, transferred). A daemon that restarts mid-move keeps the chat frozen until commit or abort. A client aborts only when the target lacks the move. If a finish answer is lost, the client commits only when the target's log has transferred_in with this transfer id.

Reference client: acpmux transfer SESSION --to SOCKET [--cwd] [--name] [--interrupt].

Limits:

  • Local socket only. A bundle holds the whole chat, and its queue runs on the target under the rules it was sent with. A remote daemon needs its own guarded receive; that is follow-up work with Move to Cloud, which the continuity lane is building on this.
  • Size: at most 4 GiB in 10,000 files per move. Moves left a day without a finish, commit or abort are swept.
  • Summary: the session summary gains moving, and initialize features gain sessionTransfer.

closes cx-xtg0.6

Testing

tests/hub_integration/transfer.rs runs two in-process daemons with the fake agent; the test is the client that moves the chat.

  • The moved chat keeps its history, its queue and its draft. Each queued prompt runs once, on the target. The source runs nothing after begin and is closed after commit.
  • A chat being moved refuses a busy move without interrupt, and refuses a prompt, a queue edit and a draft change. An aborted move gives it back: its queue runs and it takes prompts again.
Commit Command Result
Red, tests only cmux-tui-rust-check.sh test transfer:: (fleet) 2 fail (no transfer methods)
Green cmux-tui-rust-check.sh all transfer (fleet: fmt, clippy, tests) both pass; fmt and clippy clean. The only failure is cmux-remote interactive_echo_stays_responsive_during_bidirectional_bulk_transfer, which the filter also matches; it is unrelated and failed the same way on another worker.
Regression test draft (fleet) 13 pass
Regression test queue (fleet) 17 acpmux queue tests pass, including the durable restart ones (aws worker; on the cmux11s-13s minis those durable tests fail at daemon start, also without this change). Only failure: chatmux-relay writer_queue_preserves_complete_fifo_lines, unrelated and seen on base runs before

Two review subagents read this (correctness and security); their findings are fixed above. Notable ones:

  • a prompt waiting on the turn lock as the move began;
  • peers bypassing remote_guard, now local socket only;
  • edits and steers mid-move;
  • a lost finish answer leading to an abort and a double run;
  • disk caps;
  • return moves after a restart.

God-file check: no file over budget grew. requests.rs has two one-line changes; hub_integration.rs is unchanged in length.

🤖 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 acpmux.transfer.v1, letting any client move a chat from one acpmux daemon to another—carrying its history, queue (including held prompts), draft, and agent store. The client pumps the bundle between the two daemons, so neither needs to reach the other.

Closes cx-xtg0.6.

Key points

  • transfer_begin freezes the chat; a running turn refuses the move unless interrupt is set.
  • transfer_read/transfer_write page the bundle; transfer_finish imports it with its queue live.
  • transfer_commit withdraws the queue and closes the source; transfer_abort gives the chat back.
  • The move is logged, so a daemon restart keeps the chat frozen until commit or abort; each queued prompt runs exactly once.
  • A move is capped at 4 GiB in 10,000 files; stale moves are swept after a day.
  • Local socket only; acpmux transfer SESSION --to SOCKET is the reference client.

Written for commit be4fd57. Summary will update on new commits.

View guided diff Turn on auto-fix

teamleaderleo and others added 6 commits October 11, 2026 02:52
…and draft (cx-xtg0.6)

Red: acpmux has no transfer protocol. Two daemons; the test moves a chat
between them through _acpmux/transfer_*: each queued prompt runs once, on
the target; the source runs nothing once the move began, takes no prompt
while it moves, and an aborted move gives the chat back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd draft (cx-xtg0.6)

acpmux.transfer.v1: any client moves a chat between two daemons (a
sibling build's, or a remote machine's) through _acpmux/transfer_*,
so neither daemon needs to reach the other.

- Source transfer_begin freezes the chat (no prompt, no queued start;
  a running turn refuses the move unless interrupt), makes its bundle
  and answers the manifest; transfer_read pages it.
- Target transfer_receive / transfer_write / transfer_finish import it,
  in another folder path if given, and run its queue there at once.
- Source transfer_commit withdraws the queue, clears the draft and
  closes the chat; transfer_abort gives it back.
- The move is in the log, so a restart keeps the chat frozen: each
  queued prompt runs once, on one daemon.
- Local socket and peers only. acpmux transfer SESSION --to SOCKET is
  the reference client. The summary says moving.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…x-xtg0.6)

Review:
- A prompt that waited for the turn lock as the move began runs nowhere.
- The queue (edit, send now, remove) and the draft are refused while the
  chat moves.
- Only the local socket moves a chat; a remote daemon will take one
  through its own guarded entry.
- The moved queue is back before the chat is visible on the target, so
  no new prompt runs ahead of it.
- The reference client commits when a lost finish answer had landed, and
  aborts only when the target lacks the chat.
- A move carries at most 4 GiB in 10,000 files; moves left a day are swept.
- A chat that moved away can come back; a read never reaches a receive.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-xtg0.6)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e (cx-xtg0.6)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The reference client commits a move whose finish answer was lost only
  when the target logged transferred_in with this transfer id; a chat
  with the same id there may be another copy (its queue would be lost).
- A chat that moved away is known from its transferred record, so it
  can come back after a restart too; its closed copy gives way only
  once the chat's folder is there.
- A chat being moved takes no steer, and a queued prompt sent now waits
  instead of steering; its policy cannot change; an abort after the
  commit is refused.
- begin refuses a move while a turn recovered from an agent host runs.
- A receive's pages keep it from the sweep; reads refuse IN- as in-.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository: manaflow-ai/cmux/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 6fbb6248-a43e-46ee-ab90-95b1632b8562

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · 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.

…steering/chat-transfer

# Conflicts:
#	cmux-tui/crates/acpmux/src/hub/queue.rs
@cursor

cursor Bot commented Oct 11, 2026

Copy link
Copy Markdown

Bugbot is paused — on-demand spend limit reached

Bugbot uses usage-based billing for this team and has hit its on-demand spend limit.

A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue.

This branch was previously deployed

1 inactive deployment
artifacts — be4fd57f Deployed Oct 11, 2026 by teamleaderleo via publish tree-addressed cmux-tui #4871
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