Skip to content

fix(agent-launch): a phone's launch leaves the desktop window where it is - #26025

Merged
brennanb2025 merged 4 commits into
mainfrom
brennanb2025/paired-launch-leaves-desktop
Oct 7, 2026
Merged

brennanb2025 merged 4 commits into
mainfrom
brennanb2025/paired-launch-leaves-desktop

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 3 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​103 $\color{#cf222e}{\Huge{\mathbf{−}}}$​2 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​101
Prod 4 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​40 $\color{#cf222e}{\Huge{\mathbf{−}}}$​12 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​28

ELI5

When you start an agent from your phone, the desktop app reacts too: its sidebar scrolls to that workspace, expands it and highlights it, even if you were working somewhere else on the desktop.

After this change, a launch from your phone (or from another paired device) moves only the phone. The phone still opens the agent's tab; the desktop just gets a new tab in that workspace, with no sidebar scroll and no highlight. Launches from the desktop itself and from the orca CLI behave exactly as before.

Not covered yet: on the phone, "new workspace from an issue, PR or Linear item, with an agent" still moves the desktop to the new workspace, on this PR as on main. That flow uses worktree.create, not agent.launch. It gets the same rule when those launches move onto agent.launch (the next steps of the agent-launch work).

What Changed

Before: a paired device's agent.launch revealed the workspace on the host desktop in two places:

  • The tab Orca shows before the agent starts (feat(agent-launch): show an agent's tab at once, where the caller asked #25430) carried the viewer rule reveal-owner for any caller whose navigation isn't the host (a phone, a paired desktop or web client). That rule makes the desktop window reveal the workspace in the sidebar.
  • When there was no such early tab (an older phone that doesn't read it, a launch without an operation id, or a skipped early tab), the agent's terminal was spawned without surfaceOwner: false. The window treats that as "reveal the owner" and does the same reveal.

A third path was open but unused: an agent.launch that creates a workspace passed the caller's activate / runHooks straight through, so a paired client that sent activate: true would have switched the desktop to the new workspace. No shipped client sends that through agent.launch today.

After: one predicate, agentLaunchMovesHostWindow, answers "may this launch move the host window?". It's true for the desktop's own window and for in-process callers (the CLI), and false for any caller whose navigation is its own (clientKind set without being the desktop). All three paths ask it:

  • The early tab's viewer rule returns none for those callers: the tab is created in place and nothing moves.
  • The spawn passes surfaceOwner: false for those callers, so the no-early-tab path matches.
  • A workspace-creating launch from those callers creates without host activation (activate: false, runHooks: false, and runHooks' setup half kept as setupDecision: 'run'). Setup and default tabs are provisioned in the background, the same path today's phones already take.

Unchanged:

  • The phone's own selection: selectAgentLaunchTabForCaller still selects the new tab for that device.
  • Prompt delivery.
  • Chat activate. For a chat it only sets the active tab in the runtime's tab snapshot that the phone mirrors. The desktop never switches workspace from that snapshot (verified in code and in live QA row 7).
  • worktree.create itself (see "Not covered yet" above).

Why

The host's navigation model already says a paired device steers only its own view (resolveRuntimeNavigationTarget: "treating that as navigation lets one device steer every UI"), and the shared viewer-rule type says "a paired device never moves the host window". agent.launch was the exception: before #25430 its spawn revealed the desktop for every caller, and #25430 kept that reveal for paired callers so as not to change behaviour in the same PR. This PR removes the exception.

Alternatives considered:

  • Change only the viewer rule (the one-line version). It leaves the desktop jumping whenever the early tab is skipped (older phones, launches without an operation id, a workspace the early tab can't resolve). Users would see the desktop jump only some of the time.
  • Also stop chat activate for paired callers. Not needed: it moves only the phone's mirrored snapshot, which a phone launching into a new workspace relies on to open the chat.

Difference from the common pattern (intended): in the common pattern, creating a session never moves the host's view at all; each client navigates its own view after its create call. Here the host still reveals on create, and paired callers opt out. This is intended for now: desktop-window and CLI launches rely on that reveal (a CLI launch with presentation: 'focused' moves the window by design), and this PR's decision is only about paired devices. Moving the reveal out of the shared launch path would change CLI and desktop behaviour, which is out of scope here.

Linked Issue

Follow-up to #25430 (split out of it at review so that PR kept today's reveal for paired callers).

Visual Proof

Live QA on an M4 MacBook Air: two hidden, isolated dev hosts (this PR at 1fd740f6dd4, and origin/main), a stub agent only, Orca Mobile on the iOS simulator driven with orca emulator. A store watcher on each desktop recorded the active workspace, each workspace's tabs and active tab, the sidebar reveal (pendingRevealWorktree) and keyboard focus around each action.

Row This PR main
Phone "+" → Claude while the desktop shows another workspace tab added; sidebar reveal never set; active workspace and focus unchanged; phone on its new tab sidebar reveal set (twice)
Phone launch while the desktop shows the same workspace tab added; active tab stays Terminal 1; no reveal reveal set; active tab stays
Paired RPC launch with no operation id and no early-tab capability (the old-phone path) no reveal; active workspace and focus unchanged reveal set
CLI launch, default and presentation: 'focused' default reveals; focused moves the window to the new tab same
Desktop's own launch new tab becomes the workspace's active tab same
Phone chat launch phone on the chat; desktop unmoved same
Phone create-workspace-with-agent phone on the agent tab; desktop unmoved same (this flow didn't send activate, so it can't separate the arms)

Phone after the launch (this PR), on its new claude tab:

phone-pr-r1-after.jpg

Desktop after the phone launch, this PR then main. In this small fixture both workspaces already fit in the sidebar, so main's reveal doesn't show in a still frame. The difference is the reveal recorded by the store watcher (table above).

pr-r1-desk.jpg
main-r1-desk.jpg

Testing

  • agent-launch-instant-tab.test.ts:

    • The viewer rule is none for a phone (focused and default) and for a paired desktop. It's unchanged for the CLI (focus-window / reveal-owner), the desktop (focus-in-workspace) and background (none).
    • With no early tab, the spawn carries surfaceOwner: false for a phone that reads no early tab, and for a phone launch with no operation id. A CLI launch with no early tab still spawns and still reveals (no surfaceOwner).
  • agent-launch.test.ts, "whose window a new workspace moves": a phone and a paired desktop, in chat and terminal mode, sending activate: true, runHooks: true create with activate: false, runHooks: false, setupDecision: 'run'; a paired setupDecision: 'skip' is kept; a CLI caller's activate / runHooks are unchanged.

  • The expected createTerminal arguments for phone-caller launches in agent-launch.test.ts and agent-launch-floating-workspace.test.ts now include surfaceOwner: false.

  • Checked against the old code: with the source changes reverted, the new tests fail; with the change, all pass.

  • pnpm tc:node, oxlint and oxfmt pass on the changed files; CI is green.

  • I manually tested these changes locally (see Visual Proof)

  • Automated tests added/updated, or explained why not below

Review

Agent skill upstream boundary

  • Not applicable, or this change follows docs/reference/agent-skill-sharing-upstream-boundary.md and copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.

Notes

  • Platforms: host-side only (main process). No platform-specific code; Windows, macOS and Linux behave the same.
  • SSH and remote: the rule depends on who called, not where the workspace runs. A remote host's own desktop window is that host's window, so it's treated the same.
  • Mobile and wire compatibility: no wire change, nothing under mobile/ or src/shared touched. Old and new phones both get "the desktop stays put". Old phones never read the early tab, so for them the spawn-path half is what applies.
  • Backward compatibility: the only behaviour change is that paired callers no longer reveal on the desktop.
  • Performance: one extra boolean per launch.

Checklist

  • This PR is small and focused
  • I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

… where it is

A launch from the phone (or any paired client) revealed the workspace on the
host desktop: the early tab's viewer rule said `reveal-owner` for callers whose
navigation is their own, and with no early tab the spawn surfaced the owner by
default. One predicate, `agentLaunchMovesHostWindow`, now decides both: paired
callers get `none` for the shown tab and `surfaceOwner: false` at spawn. The
device still selects the tab for itself; the CLI and the desktop are unchanged.
…desktop window where it is

An agent.launch that creates its workspace never asked agentLaunchMovesHostWindow:
the create payload's activate/runHooks passed straight through, so a paired
caller that set either would switch the desktop to the new workspace and reveal
the startup terminal there. No shipped client sends them through agent.launch
yet, so nothing changes today; it closes the gap before a phone's work-item
creates move here.

For a caller whose launch does not move the host window, the create now runs
unactivated, which provisions setup and default tabs in the background. runHooks
keeps its setup half as setupDecision 'run'. CLI and desktop creates are
unchanged. Also makes the CLI "still reveals" test assert that a terminal was
actually created, so it cannot pass with none.
…host to run setup

The caller-selection header said a launch that creates its workspace relies on
worktree.create's host activation to run setup. Since the previous commit that
holds only for in-process callers: a paired client's create never activates the
host window, and its setup and default tabs are provisioned in the background.
Comment only.
@brennanb2025

Copy link
Copy Markdown
Contributor Author

Review summary

The problem. Starting an agent from the phone (or any paired device) also moved the desktop app: its sidebar scrolled to that workspace, expanded it and highlighted it, even when you were working in another workspace on the desktop. The decision for this work is that a phone or paired launch leaves the desktop where it is.

What changes for you. A launch from a phone or another paired device now moves only that device. The phone still lands on its new agent tab (terminal or chat); the desktop just gets the tab in that workspace, with no sidebar scroll or highlight. Launches from the desktop itself and from the orca CLI are unchanged. One phone flow is not covered yet: "new workspace from an issue, PR or Linear item, with an agent" still moves the desktop, as on main, because it uses worktree.create rather than agent.launch. It gets the same rule when those launches move onto agent.launch.

What review fixed (pushed to this branch):

  • f63ef7834cf: an agent.launch that creates a workspace passed the caller's activate / runHooks straight through, so a paired client sending activate: true would still have switched the desktop to the new workspace. Now that path asks the same rule: a paired caller's create skips host activation and keeps setup (runHooks becomes setupDecision: 'run'). No shipped client sends activate through agent.launch today, so nothing changes for current phones. It closes the gap before the issue/PR create flow moves over.
  • Same commit: the CLI "still reveals" test could pass without spawning a terminal; it now asserts the terminal was created.
  • b173ea15eae: a header comment in agent-launch-caller-selection.ts that the fix made untrue.
  • The PR description's claim that workspace-creating launches already used the phone's own navigation was wrong for phones; the description is corrected.

Deferred (small, none user-visible today):

  • The new rule identifies paired callers by clientKind, while nearby checks use caller.kind. They agree for every connection type today; worth unifying in a later cleanup.
  • A CLI reaching the host over a paired connection would count as a paired device. No CLI sends agent.launch that way today.
  • Test gaps: no spawn-path test for the desktop window or a paired desktop with no early tab, no pin for a paired chat's activate: false, and the chat and terminal rows of the new create test assert the same fields.
  • A paired caller that asks for navigation: 'clients' or 'all' together with activate on create no longer broadcasts that activation to other clients. No shipped client sends it.
  • Not from this PR: a CLI launch with presentation: 'background' and no early tab still reveals.

Verified:

  • Code review (adversarial reviewers by area, a from-scratch architecture comparison, a delta review of the fix, and a release-readiness pass). The rule covers every paired path into an existing workspace: early tab shown, no early tab (old phone, no operation id, skipped tab), and chat. Desktop-window, CLI, SSH-bridge and orchestration callers resolve exactly as on main. Chat activate changes only the phone's mirrored tab snapshot, never the desktop's workspace.
  • Live QA on an M4 MacBook Air, this PR at 1fd740f6dd4 vs main, phone on the iOS simulator. On main a phone launch set the desktop's sidebar reveal; on this PR it never did, including the old-phone path with no early tab. The phone landed on its tab, and CLI and desktop launches behaved the same on both. Details and screenshots are in the description.
  • Typecheck (tc:node), the related unit test files, oxlint and oxfmt, and CI (all green on b173ea15eae; one Windows SQLite timing test and one runner shutdown failed first and passed on rerun, neither in files this PR touches).

Not verified:

  • The workspace-creating fix (f63ef7834cf) was not exercised live. QA ran on the commit before it, and the phone's create flow doesn't send activate, so it can't show that change. It's covered by unit tests that fail without it.
  • Android and Windows/Linux hosts were not run live; the change is host-side and has no platform-specific code.
  • On a hidden QA desktop, keyboard focus is only weakly observable. The evidence is the store watcher's reveal signal; the screenshots look the same in both arms because the test fixture's sidebar fits on screen.

@brennanb2025
brennanb2025 marked this pull request as ready for review October 7, 2026 03:23
@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: cd211395-0ffb-4b51-b356-391feca016fc
📥 Commits

Reviewing files that changed from the base of the PR and between b173ea1 and 6ce3959.

📒 Files selected for processing (1)
  • src/main/runtime/rpc/methods/agent-launch.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 4 remain after this review.


📝 Walkthrough

Walkthrough

Agent launch handling now uses a shared decision to determine whether a launch moves the host window. That decision affects tab viewer selection, workspace activation and hooks, and terminal surface ownership. Tests cover paired phone and desktop launches, local launches, and CLI launches.

Priority: ⬇️ Low

Merge Risk: ⚪ Minimal · up to 6ce39

The reviewed paired-launch behavior preserves the desktop workspace selection, and no concrete merge-blocking regression was established.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 7 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely identifies the main change: phone launches no longer move the desktop window.
Description check ✅ Passed The description covers the required sections with clear behavior changes, rationale, visual proof, testing, and compatibility notes. It identifies the related follow-up to #25430 and marks the applica…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
  • 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.

…aunch-leaves-desktop

# Conflicts:
#	src/main/runtime/rpc/methods/agent-launch.test.ts
@brennanb2025
brennanb2025 merged commit 3427450 into main Oct 7, 2026
25 of 27 checks passed
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