Agent browser commands target the browser nearest the caller, not the focused one

AU
5d ago
+1398-44

Fixes https://github.com/manaflow-ai/cmux/issues/26648

Browser commands from an agent or terminal now use the browser nearest that caller. An agent can work in its own workspace while the person keeps another window focused.

The shared resolver chooses the caller's workspace, then its sidebar group, then the rest of its window, always within the caller's session. It serves page verbs, targetless browser actions, and system.identify / snapshot.get; the CLI and MCP use the identified browser for a current selector. Explicit selectors keep their meaning. Calls without a known caller retain the focused-browser fallback.

With no nearby browser, page commands refuse instead of driving another window. page new-tab creates the caller's first browser: beside an agent chat, or behind a terminal in its own pane. Identical keyed retries replay before looking up a target, including after the caller or browser disappears. Mirrored lookup stays within the session and refuses duplicate matches; actions without a target require the same focused browser location, including a focused mirror.

Known limitation: browser actions still require a browser in the person's focus when the action catalog requires it. Availability follows app focus, as it already does with explicit action targets. Page verbs do not have that limitation.

Changelog

Fixed: Agent browser commands target the browser nearest their workspace instead of another window's focused browser.

Verification

Focused Swift command (run through the fleet, never on the submitting Mac):

cmux-ci run --class light --script scripts/ci/package-test-lane.sh \
  --ref <sha> --arg suite --arg Packages/macOS/CmuxNext \
  --arg BrowserCallerTargetTests,HomeChatCallerTests \
  --env CMUX_CI_XCODE_APP=/Applications/Xcode_26.6.app
  • Mirror regression: at 6fcddf25ce3e794f31fc08cfb037548782780114, the new cross-session/duplicate-ID cases failed with 10 expected assertions (fleet 721756195624cbe15eb1c4f3). At 56c2a294c1845a5e83eea02e6c9a236573892ec4, all 9 browser-caller tests and 4 home-chat tests passed (fleet 8ceee3e2d70d0908102231fd).
  • Regression baseline 9f656f95de06fb703c5509bb247f9720568ed299 ran 13 browser-caller tests and reported 12 expected assertion failures (fleet e9ab34f244b45bb7eefb2eea). Repaired 77171184c79c440b68631a0159198d0ecf4d81ff passed 32 tests in all 6 selected suites (fleet dacafdb25714d84e1849c366): browser-caller, home-chat caller, page tabs, and action-result/created/not-run behavior. The earlier compile-only baseline attempt is not used as behavioral evidence.
  • At ebf3c086d249e2abbc1dd93719ba0b5982069f9e, after the latest base merge and checked Rust caller lookups, rbx: 136 CLI/MCP tests passed with cargo nextest run --locked -p cmux-tui --no-fail-fast -E 'test(cli::mcp::tests::) | test(cli::caller_browser::) | test(page_verbs_send_the_caller)'. Remote Clippy reports no indexing warnings in the caller-routing additions. The Rust subtree is unchanged on current head e4e32cbb9548a1a4ef4a7951080626f0a9021d51. A fresh review found the merged base added browser.editSource without focus requirements; current head includes its routing repair and a separately committed regression at 8e49aaf27f69f061a75460d1bc376598c53dbd0f. Fleet red d0cfc5d262ec9eb663f2157c compiled and ran the 13 browser-caller tests, failing only the edit-source case with 2 expected assertions. Exact-head green 0c3396501724fc530c54f0f9 passed all 32 tests in all 6 selected suites, including that case; the earlier queued ebf3c086 Swift run was cancelled before execution.
  • Static pre-push gate: no new violations. Swift syntax passes. Release digest: 5 tests passed. Browser skill contract and 10 regression checks passed.
  • Localization: the two added control errors have 38 reviewed translations and no unresolved scoped entries. Full catalog parity remains over its inherited debt baseline.
  • Fresh independent correctness-first review: SHIP at e4e32cbb9548a1a4ef4a7951080626f0a9021d51, covering the full PR, base interactions and edit-source repair. No remaining correctness findings.

Observed / Not run

The exact-head tagged fleet build succeeded: job fade78f0d370d12a1f347235, artifact sha256:1bdfb0cc70f4e15b08b5d6225d2134498a8d49c747a7f3e27d1d2fe1cdb66820, tag issue-26648-browser-caller-target, local backend mode. The submission and terminal receipts were saved. It is published for dogfooding; its Tag Opener link responds, and it was not installed or launched on the person's Mac.

Live two-window e2e was not run: the remote GUI host refused SSH authentication. scripts/cmux-next/browser-caller-target-e2e.py is included for that live check, but its presence is not execution evidence.

No capture: this changes control routing, CLI and MCP behavior without a visible native UI change.

The latest fetched base 4f4a95744816d7ca6b7d87d19acdf49ec4698c41 was merged without rewriting history. Current-head failure classification (pending checks do not hold a reviewed NEXT PR):

  • browser-skill: browser contract checks pass; the settings inventory test reports the two terminal.linkOpener.* paths. The same test fails against extracted base files, and this PR changes none of its settings inputs.

  • Clippy panic ratchet: this run and the base both report cmux-tui indexing 835 > 831 and acpmux indexing 692 > 686. The current-head artifact has zero diagnostics on added Rust lines. The earlier added caller index was repaired in 332e5cfa88. The strict classifier rejects the different aggregate crate list in the error line.

  • release-notary: two runner-fallback assertions fail in test_ci_runs_on_fallback.py against nightly.yml. Both inputs are identical to the merged base. The base release-notary check is also red. The release-digest tests now pass.

  • Crash-class selectors: three missing runtime probes in OmnibarFieldEditor.swift, introduced by the advancing base and outside this PR's diff. The base check is also red.

The local current-head cmux-next static gate passes. Exact-head Swift fleet execution passed all 32 tests in six suites. Autoland called the settings-inventory failure an own red because this workflow has no completed feat-cmux-next run to compare. Its settings inputs are byte-identical on head and the base tip, and the same failure appears on an unrelated PR run. The current-head comparison receipts document these base findings. No unrelated settings or guard files have been changed here.