Agent browser commands target the browser nearest the caller, not the focused one
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 (fleet721756195624cbe15eb1c4f3). At56c2a294c1845a5e83eea02e6c9a236573892ec4, all 9 browser-caller tests and 4 home-chat tests passed (fleet8ceee3e2d70d0908102231fd). - Regression baseline
9f656f95de06fb703c5509bb247f9720568ed299ran 13 browser-caller tests and reported 12 expected assertion failures (fleete9ab34f244b45bb7eefb2eea). Repaired77171184c79c440b68631a0159198d0ecf4d81ffpassed 32 tests in all 6 selected suites (fleetdacafdb25714d84e1849c366): 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 withcargo 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 heade4e32cbb9548a1a4ef4a7951080626f0a9021d51. A fresh review found the merged base addedbrowser.editSourcewithout focus requirements; current head includes its routing repair and a separately committed regression at8e49aaf27f69f061a75460d1bc376598c53dbd0f. Fleet redd0cfc5d262ec9eb663f2157ccompiled and ran the 13 browser-caller tests, failing only the edit-source case with 2 expected assertions. Exact-head green0c3396501724fc530c54f0f9passed all 32 tests in all 6 selected suites, including that case; the earlier queuedebf3c086Swift 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.pyagainstnightly.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.