Repository navigation
feat(workspace): send an agent to another open window - #1484
Open
Juliusolsson05 wants to merge 16 commits into
Open
Juliusolsson05 wants to merge 16 commits into
Juliusolsson05 wants to merge 16 commits into
Conversation
Refs #1481 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Seeding a window's view of sessions that are already alive and routed to it (snapshot before paint, a runtime for every session, condition re-seed after the seed, history only for live backends) was inline in useWorkspaceAdoption. Moving agents between live windows needs exactly the same steps, and each one is a regression fix (#895, #1083). One shared copy keeps them in force for both paths instead of a second copy that would have to rediscover them. Refs #1481 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…them Adds the protocol for moving an agent and its descendants from one live window to another. Routing moves to the target first. The target merges the rows, shows the agent in its focused lane, and confirms only after a durable save. Only then does main tell the source to drop its rows, which it does without killing anything. A refused or expired move hands routing back (only the exact claims it made), and the target removes whatever it merged. The ordering prefers a transient duplicate row over a lost agent: between the target's save and the source's save both slices list the session, while the reverse order would have a gap where no slice lists a live agent. A new project tab in the target gets a fresh id, because tab ids are window-local and a shared id would make a later window-close adoption refuse the whole workspace. Refs #1481 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A picker of the other open windows, each labelled with its project titles and session count, because every Agent Code window has the same OS title. It captures the target session when it opens, so from the right-click menu it moves the clicked row even if focus changes while the user chooses. The row drop on both sides now reuses workspaceWithoutSessions directly. It already clears lanes and scrubs row metadata, so the move-specific wrapper was a duplicate. Refs #1481 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
agents.moveToWindow (ac_agents_move_to_window) waits for the commit, so the caller's next read already sees the agent in the target window. A failure is not_started when main refused or rolled back the move. It is unknown only when the renderer stopped waiting without a verdict, so an operator observes before retrying. Refs #1481 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main: only the target's current renderer may settle a move, and a rollback undoes only the exact claims it made. Renderer: the target confirms only through autosave, refuses collisions, and holds offers until bootstrap. The source drops rows only on main's commit and never kills. Refs #1481 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refs #1481 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… wait Main's deadline can fire while the target is still awaiting backend snapshots. The cancel found nothing registered yet and was dropped, so the merge then published rows whose routing had already gone back to the source: dead panes in the target. The cancel is now remembered and the merge stops before publishing. Also drops an early-verdict buffer that could never be reached, since main's invoke reply always precedes its verdict events. Refs #1481 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
While the picker is open the focused window is always the sender, so marking the OS-focused window never highlighted anything useful. Main now reports the other window the user was in most recently, from the registry's focus order. Refs #1481 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The autosave success callback confirmed every move and adoption queued by the time the write resolved, including ones that landed while that write was in flight and so were not in it. A move confirmation makes the source window drop its rows, and an adoption confirmation makes main delete a closed window's slice, so a crash before the next save could lose the agent entirely. Only ids queued before the snapshot was serialized are confirmed now. The adoption half of this bug predates #1481. Refs #1481 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…iled move Waking a parked agent in the target's focused lane re-claims it, which rotates the lease. The rollback's identity check then skipped it while the target dropped its rows, stranding the agent where no window displayed it. A rollback now also takes back a newer claim by the target window itself (any other window's claim is still left alone). After routing returns, it marks a routing gap so the source's existing repair re-reads readiness, conditions and history it missed while the target held the claim. The move picker also leaves out windows that are closing or still loading, instead of offering them and then failing. Refs #1481 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…cket A pocket id moves with its session to another window. The target registers a new guest under it, and the source then retires its old guest and unregistered the same id, deleting the target's live registration. Browser tools then answered no_pocket while the pocket stayed on screen. Only the renderer that holds the current registration may release it. Refs #1481 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
agents.moveToWindow accepted extension views, which the command hides and the plan excludes. The rule now lives in the packet builder, so every entry point refuses the same thing. Sending a child agent on its own leaves its parent in the old window, where the parent's orchestration tools can no longer reach it. The picker and the capability description now say so before the user commits. Adds tests for the capability's not_started/unknown outcome split, which is its contract to operators, and for appending moved rows after the target's own rows even when the clock is behind. Refs #1481 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refs #1481 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # src/renderer/src/features/command-palette/catalog.test.ts
6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
With several Agent Code windows open (one per monitor, say), you can't move a running agent from one window to another. The only cross-window handover today happens when a window closes. Root Agent Code Management agents (#906) can rearrange lanes inside a window but can't move work between windows either.
What this adds
Send to Window… (palette, plus the Sessions right-click menu) moves the focused lane's agent or terminal to another open window you pick:
Window N · <project titles> · <n> sessions, because every window has the same OS title. The picker starts on the window you used last.agents.moveToWindowis published asac_agents_move_to_windowto root-management agents and the external operator. It returns once the move has committed.How it works (ordering is the safety property)
transfer), so events emitted mid-handoff reach the window about to show them. Parked agents are claimed for the target, so the source can't wake them mid-move.Design decisions
Refactors and fixes along the way
seedIncomingSessions.ts, shared by adoption and moves. These steps carry the bug(workspace): window adoption drops pending permission and question conditions #895/fix(workspace): seed pending conditions when a window adopts, restores or wakes a session #1083 fixes.forgetClosedSessionDebugStatebecameforgetSessionViewStateand now also releases the bootstrap timer that close used to clear inline.catalog.test.tsnow records the step c882a41 made without extending the comment.Tests
main/window/sessionMove.test.ts:workspace/sessionMove.test.ts: transitive and cycle-safe subtree; packet round trip; same-folder join versus a fresh tab id; collision refusal; source drop; target cancel restoring the original state.hook/ipc/useSessionMoves.renderer.test.tsx:storage/workspaceFile.test.ts: picker labels.Verification
npx tsc -b: clean.npm run check:keybindingsandnpm run test:contract: OK.npm test: 994 files, 8259 passed, 1 skipped (Node 24, local). The two follow-up fixes after that run (cancel during merge, picker default) were verified with their own suites plustsc -b.Known limitations
Review dispositions (one round: Codex on correctness, Claude on integration)
856eb5e0: confirmations are captured at serialization; a regression test fails on the old coded883ef63: rollback also takes back a newer claim by the target window; any other window's claim is still left aloned883ef63: rollback marks a router gap, so the source's existing routing-gap repair resyncs2d13863d: only the renderer holding the current registration may unregister itagents.moveToWindowaccepted extension views45d00581: the rule now lives in the packet builder and is shared by every entry point45d00581: the picker and the capability description warn. Redirecting the move to the root owner is a product call, left UNCONFIRMEDd883ef63movingmap never cleared, outcome split, append orderingnot_started/unknown, clock behind the target's rows)session:inputanddeliver-promptdo not check display-window ownershipFixes #1481
🤖 Generated with Claude Code