From 8585c65fa40b7409ecbeae95ee1aa51657161306 Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Sun, 30 Aug 2026 15:16:56 -0700 Subject: [PATCH] Respect explicit server placement for owner-pinned remote browser links (#17361) When a remote browser pane opens a link with an explicit placementPreference, honor that override rather than applying the generic browser client policy. Links opened from remote panes may require specific host placement to respect execution boundaries. --- .../BrowserPane.remote-link-routing.test.ts | 1 + .../remote-browser-page-pane.tsx | 3 +- .../lib/workspace-browser-tab-open.test.ts | 60 +++++++++++++++++++ .../src/lib/workspace-browser-tab-open.ts | 10 +++- 4 files changed, 72 insertions(+), 2 deletions(-) diff --git a/src/renderer/src/components/browser-pane/assemble-chrome/BrowserPane.remote-link-routing.test.ts b/src/renderer/src/components/browser-pane/assemble-chrome/BrowserPane.remote-link-routing.test.ts index 511b1eb064c..856ac24c725 100644 --- a/src/renderer/src/components/browser-pane/assemble-chrome/BrowserPane.remote-link-routing.test.ts +++ b/src/renderer/src/components/browser-pane/assemble-chrome/BrowserPane.remote-link-routing.test.ts @@ -15,5 +15,6 @@ describe('remote browser link routing', () => { expect(openRequest).toContain('workspaceId: worktreeId') expect(openRequest).toContain('expectedRuntimeEnvironmentId: runtimeEnvironmentId') + expect(openRequest).toContain("placementPreference: 'server'") }) }) diff --git a/src/renderer/src/components/browser-pane/stream-remote/remote-browser-page-pane.tsx b/src/renderer/src/components/browser-pane/stream-remote/remote-browser-page-pane.tsx index 443264c1f1b..91995117b16 100644 --- a/src/renderer/src/components/browser-pane/stream-remote/remote-browser-page-pane.tsx +++ b/src/renderer/src/components/browser-pane/stream-remote/remote-browser-page-pane.tsx @@ -359,7 +359,8 @@ export function RemoteBrowserPagePane({ workspaceId: worktreeId, url: linkUrl, intent: { kind: 'url' }, - expectedRuntimeEnvironmentId: runtimeEnvironmentId + expectedRuntimeEnvironmentId: runtimeEnvironmentId, + placementPreference: 'server' }).catch((error) => { setPaneNotice({ kind: 'direct', diff --git a/src/renderer/src/lib/workspace-browser-tab-open.test.ts b/src/renderer/src/lib/workspace-browser-tab-open.test.ts index 59ce6bac8d8..5ae73fa7c12 100644 --- a/src/renderer/src/lib/workspace-browser-tab-open.test.ts +++ b/src/renderer/src/lib/workspace-browser-tab-open.test.ts @@ -6,6 +6,7 @@ import { } from '../../../shared/execution-host' import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../shared/constants' import { BROWSER_SCREENCAST_RUNTIME_CAPABILITY } from '../../../shared/protocol-version' +import * as clientCreationActionPolicy from './client-creation-action-policy' import { canOpenWorkspaceBrowserTabOnRuntime, canOpenWorkspaceBrowserTabOnSsh, @@ -241,6 +242,65 @@ describe('openWorkspaceBrowserTab', () => { expect(createBrowserTab).not.toHaveBeenCalled() }) + it('keeps an asserted runtime link on its owner when policy selects the local client', async () => { + const createBrowserTab = vi.fn() + mocks.state = { + ...ownerState(toRuntimeExecutionHostId('hub-a')), + ...browserCapableRuntime('hub-a'), + createBrowserTab, + defaultBrowserSessionProfileId: 'client-profile', + defaultBrowserSessionProfileIdByHostId: {} + } + const policySpy = vi + .spyOn(clientCreationActionPolicy, 'getClientCreationActionPolicy') + .mockReturnValue({ + 'managed-browser': { state: 'enabled', provider: 'local-client' }, + 'mobile-emulator': { state: 'enabled', provider: 'local-client' } + }) + + try { + await openWorkspaceBrowserTab({ + workspaceId: WORKSPACE_ID, + url: 'https://example.com/pinned', + intent: { kind: 'url' }, + expectedRuntimeEnvironmentId: 'hub-a' + }) + + expect(mocks.createRemote).toHaveBeenCalledWith( + expect.objectContaining({ + environmentId: 'hub-a', + waitForRegistration: true, + worktreeId: WORKSPACE_ID + }) + ) + expect(createBrowserTab).not.toHaveBeenCalled() + } finally { + policySpy.mockRestore() + } + }) + + it('forwards an explicit server placement for owner-pinned remote panes', async () => { + mocks.state = { + ...ownerState(toRuntimeExecutionHostId('hub-a')), + ...browserCapableRuntime('hub-a'), + createBrowserTab: vi.fn(), + defaultBrowserSessionProfileId: 'client-profile', + defaultBrowserSessionProfileIdByHostId: {} + } + + await openWorkspaceBrowserTab({ + workspaceId: WORKSPACE_ID, + url: 'https://example.com/pinned', + intent: { kind: 'url' }, + expectedRuntimeEnvironmentId: 'hub-a', + placementPreference: 'server' + }) + + expect(mocks.createRemote).toHaveBeenCalledWith( + expect.objectContaining({ placementPreference: 'server' }) + ) + }) + it('fails closed when the workspace route swaps away from the pane runtime before opening', async () => { mocks.state = { ...ownerState(toRuntimeExecutionHostId('hub-a')), diff --git a/src/renderer/src/lib/workspace-browser-tab-open.ts b/src/renderer/src/lib/workspace-browser-tab-open.ts index d159d47aab3..18149a98bbc 100644 --- a/src/renderer/src/lib/workspace-browser-tab-open.ts +++ b/src/renderer/src/lib/workspace-browser-tab-open.ts @@ -8,6 +8,7 @@ import { type ExecutionHostId } from '../../../shared/execution-host' import { SEARCH_ENGINE_LABELS, type SearchEngine } from '../../../shared/browser-url' +import type { BrowserClientHostPlacementPreference } from '../../../shared/browser-client-host-placement' import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../shared/constants' import { BROWSER_SCREENCAST_RUNTIME_CAPABILITY } from '../../../shared/protocol-version' import { @@ -31,6 +32,8 @@ export type OpenWorkspaceBrowserTabRequest = { selectWorktree?: boolean expectedRuntimeEnvironmentId?: string expectedSshConnectionId?: string + /** Override placement for links whose pane explicitly requires server ownership. */ + placementPreference?: BrowserClientHostPlacementPreference } function isExpectedRuntimeBrowserRoute( @@ -272,7 +275,9 @@ export async function openWorkspaceBrowserTab( `host ${route.executionHostId} does not own runtime ${environmentId}` ) } - if (availability.provider === 'local-client') { + // An asserted runtime owns links opened from remote panes; provider policy may describe the + // viewing client's generic browser surface rather than that pane's execution host. + if (expectedEnvironmentId === null && availability.provider === 'local-client') { const localHostId = host && host.kind !== 'runtime' ? host.id : LOCAL_EXECUTION_HOST_ID createClientBrowserTab(state, request, localHostId, presentation) return @@ -286,6 +291,9 @@ export async function openWorkspaceBrowserTab( targetGroupId: request.targetGroupId, // Owner-pinned links need the host tab published before client reconciliation. ...(expectedEnvironmentId !== null ? { waitForRegistration: true } : {}), + ...(request.placementPreference !== undefined + ? { placementPreference: request.placementPreference } + : {}), // Why: the tab is opened from this workspace's tab bar, so surface that // workspace — otherwise a background worktree looks like nothing happened. ...(request.focusOnCreate !== undefined ? { focusOnCreate: request.focusOnCreate } : {}),