diff --git a/config/focus-setting-read-baseline.txt b/config/focus-setting-read-baseline.txt index 6ddd89b6570..93fb2d5732d 100644 --- a/config/focus-setting-read-baseline.txt +++ b/config/focus-setting-read-baseline.txt @@ -257,7 +257,7 @@ 4 src/renderer/src/lib/workspace-browser-tab-open.ts 1 src/renderer/src/lib/worktree-activation.ts 1 src/renderer/src/lib/worktree-jump-navigation.ts -1 src/renderer/src/lib/worktree-runtime-owner.ts # typed owner resolver and the legacy-fallback adapter the folds moved to (round 3 F0); F8 deleted the deprecated pair +1 src/renderer/src/lib/worktree-runtime-owner.ts # a setup terminal with no row spawns on the focused runtime 17 src/renderer/src/runtime/runtime-jira-client.ts 2 src/renderer/src/runtime/runtime-jira-summary-client.ts 1 src/renderer/src/runtime/runtime-jira-target.ts diff --git a/src/renderer/src/components/browser-pane/assemble-chrome/browser-deferred-lifecycle.test.tsx b/src/renderer/src/components/browser-pane/assemble-chrome/browser-deferred-lifecycle.test.tsx index ffaddf3cff8..d6212a7e14d 100644 --- a/src/renderer/src/components/browser-pane/assemble-chrome/browser-deferred-lifecycle.test.tsx +++ b/src/renderer/src/components/browser-pane/assemble-chrome/browser-deferred-lifecycle.test.tsx @@ -35,7 +35,7 @@ vi.mock('@/store', async () => { } }) vi.mock('@/lib/worktree-runtime-owner', () => ({ - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () => null, + runtimeEnvironmentIdOrLocalUnlessTwin: () => null, getExecutionHostIdForWorktree: () => mocks.executionHostId })) vi.mock('@/components/contextual-tours/use-contextual-tour', () => ({ diff --git a/src/renderer/src/components/browser-pane/assemble-chrome/browser-workspace-pane.retention-props.test.tsx b/src/renderer/src/components/browser-pane/assemble-chrome/browser-workspace-pane.retention-props.test.tsx index d5f592246fe..36a691b3731 100644 --- a/src/renderer/src/components/browser-pane/assemble-chrome/browser-workspace-pane.retention-props.test.tsx +++ b/src/renderer/src/components/browser-pane/assemble-chrome/browser-workspace-pane.retention-props.test.tsx @@ -33,7 +33,7 @@ vi.mock('@/store', () => ({ })) vi.mock('@/lib/worktree-runtime-owner', () => ({ - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () => null + runtimeEnvironmentIdOrLocalUnlessTwin: () => null })) vi.mock('@/components/contextual-tours/use-contextual-tour', () => ({ diff --git a/src/renderer/src/components/browser-pane/assemble-chrome/browser-workspace-pane.tsx b/src/renderer/src/components/browser-pane/assemble-chrome/browser-workspace-pane.tsx index 852ef860d55..aa822bc00dc 100644 --- a/src/renderer/src/components/browser-pane/assemble-chrome/browser-workspace-pane.tsx +++ b/src/renderer/src/components/browser-pane/assemble-chrome/browser-workspace-pane.tsx @@ -1,6 +1,6 @@ import { useCallback, useEffect, useMemo } from 'react' import { useAppStore } from '@/store' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { isClientHostedBrowserPageHandle } from '@/store/slices/browser' import type { BrowserWorkspace as BrowserWorkspaceState } from '../../../../../shared/browser-workspace-types' import { destroyPersistentWebview } from '../host-guest/webview-registry' @@ -41,7 +41,7 @@ export default function BrowserPane({ }): React.JSX.Element { const resolvedChromeShortcutScope = chromeShortcutScope ?? (isActive ? 'focused' : 'inactive') const activeRuntimeEnvironmentId = useAppStore((s) => - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(s, browserTab.worktreeId) + runtimeEnvironmentIdOrLocalUnlessTwin(s, browserTab.worktreeId) ) const browserPages = useAppStore((s) => getBrowserPagesForWorkspace(s.browserPagesByWorkspace, browserTab.id) diff --git a/src/renderer/src/components/browser-pane/stream-remote/use-remote-browser-page-lifecycle.ts b/src/renderer/src/components/browser-pane/stream-remote/use-remote-browser-page-lifecycle.ts index 349699dda97..88ecd769542 100644 --- a/src/renderer/src/components/browser-pane/stream-remote/use-remote-browser-page-lifecycle.ts +++ b/src/renderer/src/components/browser-pane/stream-remote/use-remote-browser-page-lifecycle.ts @@ -1,6 +1,6 @@ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react' import { useAppStore } from '@/store' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { RemoteBrowserStreamLifecycle } from './remote-browser-stream-lifecycle' import type { RemoteBrowserOperationToken, @@ -237,10 +237,7 @@ export function useRemoteBrowserPageLifecycle({ return } const state = useAppStore.getState() - const currentEnvironmentId = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin( - state, - worktreeId - ) + const currentEnvironmentId = runtimeEnvironmentIdOrLocalUnlessTwin(state, worktreeId) const pageStillExists = browserPageExists(browserTab.id) if (currentEnvironmentId === activeRuntimeEnvironmentId && pageStillExists) { return diff --git a/src/renderer/src/components/browser-pane/workspace-doc/doc-preview-document-actions.test.ts b/src/renderer/src/components/browser-pane/workspace-doc/doc-preview-document-actions.test.ts index 66526126f0b..a60f2dcc7de 100644 --- a/src/renderer/src/components/browser-pane/workspace-doc/doc-preview-document-actions.test.ts +++ b/src/renderer/src/components/browser-pane/workspace-doc/doc-preview-document-actions.test.ts @@ -28,7 +28,7 @@ vi.mock('@/lib/connection-owner-resolution', () => ({ getConnectionIdForFileFromState: () => mocks.connectionIdForFile })) vi.mock('@/lib/worktree-runtime-owner', () => ({ - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () => mocks.worktreeRuntimeOwnerId + runtimeEnvironmentIdOrLocalUnlessTwin: () => mocks.worktreeRuntimeOwnerId })) vi.mock('@/lib/file-owner-runtime-target', () => { const getRuntimeTargetForFileOwner = ( diff --git a/src/renderer/src/components/browser-pane/workspace-doc/doc-preview-document-actions.ts b/src/renderer/src/components/browser-pane/workspace-doc/doc-preview-document-actions.ts index 9208e9619b9..097062aa0e4 100644 --- a/src/renderer/src/components/browser-pane/workspace-doc/doc-preview-document-actions.ts +++ b/src/renderer/src/components/browser-pane/workspace-doc/doc-preview-document-actions.ts @@ -2,7 +2,7 @@ import { LOCAL_EXECUTION_HOST_ID } from '../../../../../shared/execution-host' import { toast } from 'sonner' import { translate } from '@/i18n/i18n' import { getConnectionIdForFileFromState } from '@/lib/connection-owner-resolution' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { isTwinWorkspace } from '@/lib/resolve-owner' import { detectLanguage } from '@/lib/language-detect' import { @@ -71,7 +71,7 @@ export function openDocPreviewExternally(document: DocPreviewDocument): void { // renders through was minted against the worktree's owner at render time, and a tab opened or // restored before that owner was known still carries null — which reads as local. const runtimeEnvironmentId = - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, document.worktreeId) ?? + runtimeEnvironmentIdOrLocalUnlessTwin(state, document.worktreeId) ?? document.runtimeEnvironmentId const fileContext = buildWorkspaceFileContextForFile( document.worktreeId, diff --git a/src/renderer/src/components/browser-pane/workspace-doc/workspace-doc-page-pane.test.tsx b/src/renderer/src/components/browser-pane/workspace-doc/workspace-doc-page-pane.test.tsx index 0b535961469..becffb91763 100644 --- a/src/renderer/src/components/browser-pane/workspace-doc/workspace-doc-page-pane.test.tsx +++ b/src/renderer/src/components/browser-pane/workspace-doc/workspace-doc-page-pane.test.tsx @@ -23,7 +23,7 @@ vi.mock('@/store', () => ({ useAppStore: (selector: (state: unknown) => unknown) => selector(mocks.storeState) })) vi.mock('@/lib/worktree-runtime-owner', () => ({ - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () => null + runtimeEnvironmentIdOrLocalUnlessTwin: () => null })) vi.mock('./HtmlDocPreview', () => ({ HtmlDocPreview: ({ holdsGuestFocus }: { holdsGuestFocus?: boolean }) => { diff --git a/src/renderer/src/components/browser-pane/workspace-doc/workspace-doc-page-pane.tsx b/src/renderer/src/components/browser-pane/workspace-doc/workspace-doc-page-pane.tsx index 0367d15040f..f6b2dd8f977 100644 --- a/src/renderer/src/components/browser-pane/workspace-doc/workspace-doc-page-pane.tsx +++ b/src/renderer/src/components/browser-pane/workspace-doc/workspace-doc-page-pane.tsx @@ -1,6 +1,6 @@ import { useMemo } from 'react' import { getRelativePathInsideRoot } from '@/lib/path' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { useAppStore } from '@/store' import { HtmlDocPreview } from './HtmlDocPreview' import type { BrowserPage } from '../../../../../shared/browser-workspace-types' @@ -24,7 +24,7 @@ export function WorkspaceDocPagePane({ // Why resolved here rather than stored on the page: ownership moves. A page persisted before a // pairing or an SSH reconnect would otherwise route its document actions at yesterday's host. const runtimeEnvironmentId = useAppStore((store) => - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(store, worktreeId) + runtimeEnvironmentIdOrLocalUnlessTwin(store, worktreeId) ) const relativePath = useMemo( () => getRelativePathInsideRoot(filePath, worktreeRoot) ?? filePath, diff --git a/src/renderer/src/components/native-chat/NativeChatInlineVisual.open-as-file.test.tsx b/src/renderer/src/components/native-chat/NativeChatInlineVisual.open-as-file.test.tsx index 6ea1e48c877..3c1699cb077 100644 --- a/src/renderer/src/components/native-chat/NativeChatInlineVisual.open-as-file.test.tsx +++ b/src/renderer/src/components/native-chat/NativeChatInlineVisual.open-as-file.test.tsx @@ -22,7 +22,7 @@ vi.mock('@/lib/connection-owner-resolution', async (importOriginal) => ({ })) vi.mock('@/lib/worktree-runtime-owner', async (importOriginal) => ({ ...(await importOriginal()), - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () => 'env-1' + runtimeEnvironmentIdOrLocalUnlessTwin: () => 'env-1' })) import { NativeChatInlineVisual } from './NativeChatInlineVisual' diff --git a/src/renderer/src/components/native-chat/native-chat-file-link.ts b/src/renderer/src/components/native-chat/native-chat-file-link.ts index c8a23104b89..b7aeea15717 100644 --- a/src/renderer/src/components/native-chat/native-chat-file-link.ts +++ b/src/renderer/src/components/native-chat/native-chat-file-link.ts @@ -4,7 +4,7 @@ import { resolveExplicitFileLinkTarget, resolveExplicitFileLinkTargetPath } from '@/lib/explicit-file-link-target' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import type { AppState } from '@/store/types' import { resolveNativeChatTabDirectory } from './native-chat-tab-directory' @@ -122,7 +122,7 @@ function resolveNativeChatFileLinkContextForOwner( return { worktreeId, worktreePath, - runtimeEnvironmentId: runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, worktreeId), + runtimeEnvironmentId: runtimeEnvironmentIdOrLocalUnlessTwin(state, worktreeId), sourceTabId: terminalTabId } } diff --git a/src/renderer/src/components/native-chat/native-chat-http-link-source-owner.test.ts b/src/renderer/src/components/native-chat/native-chat-http-link-source-owner.test.ts index aad334a46fe..86ea93e03bb 100644 --- a/src/renderer/src/components/native-chat/native-chat-http-link-source-owner.test.ts +++ b/src/renderer/src/components/native-chat/native-chat-http-link-source-owner.test.ts @@ -6,15 +6,14 @@ import { } from './native-chat-http-link-source-owner' const mocks = vi.hoisted(() => ({ - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: vi.fn(), + runtimeEnvironmentIdOrLocalUnlessTwin: vi.fn(), getConnectionIdFromState: vi.fn(), canOpenWorkspaceBrowserTabOnRuntime: vi.fn(), canOpenWorkspaceBrowserTabOnSsh: vi.fn() })) vi.mock('@/lib/worktree-runtime-owner', () => ({ - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: - mocks.runtimeEnvironmentIdWithLegacyFallbackUnlessTwin + runtimeEnvironmentIdOrLocalUnlessTwin: mocks.runtimeEnvironmentIdOrLocalUnlessTwin })) vi.mock('@/lib/connection-owner-resolution', () => ({ getConnectionIdFromState: mocks.getConnectionIdFromState @@ -32,7 +31,7 @@ afterEach(() => { describe('resolveNativeChatHttpLinkSourceOwner', () => { it('prefers the workspace runtime owner', () => { - mocks.runtimeEnvironmentIdWithLegacyFallbackUnlessTwin.mockReturnValue('env-1') + mocks.runtimeEnvironmentIdOrLocalUnlessTwin.mockReturnValue('env-1') expect(resolveNativeChatHttpLinkSourceOwner(state, 'wt-1')).toEqual({ kind: 'runtime', @@ -42,7 +41,7 @@ describe('resolveNativeChatHttpLinkSourceOwner', () => { }) it('falls back to the SSH connection that owns the workspace', () => { - mocks.runtimeEnvironmentIdWithLegacyFallbackUnlessTwin.mockReturnValue(null) + mocks.runtimeEnvironmentIdOrLocalUnlessTwin.mockReturnValue(null) mocks.getConnectionIdFromState.mockReturnValue('ssh-1') expect(resolveNativeChatHttpLinkSourceOwner(state, 'wt-1')).toEqual({ @@ -52,7 +51,7 @@ describe('resolveNativeChatHttpLinkSourceOwner', () => { }) it('reads a null connection as local', () => { - mocks.runtimeEnvironmentIdWithLegacyFallbackUnlessTwin.mockReturnValue(null) + mocks.runtimeEnvironmentIdOrLocalUnlessTwin.mockReturnValue(null) mocks.getConnectionIdFromState.mockReturnValue(null) expect(resolveNativeChatHttpLinkSourceOwner(state, 'wt-1')).toEqual({ kind: 'local' }) @@ -61,7 +60,7 @@ describe('resolveNativeChatHttpLinkSourceOwner', () => { // An unresolved owner must not be mistaken for local: a remote link would then // open against the wrong host. it('reports an unresolved owner as unknown', () => { - mocks.runtimeEnvironmentIdWithLegacyFallbackUnlessTwin.mockReturnValue(null) + mocks.runtimeEnvironmentIdOrLocalUnlessTwin.mockReturnValue(null) mocks.getConnectionIdFromState.mockReturnValue(undefined) expect(resolveNativeChatHttpLinkSourceOwner(state, 'wt-1')).toEqual({ kind: 'unknown' }) diff --git a/src/renderer/src/components/native-chat/native-chat-http-link-source-owner.ts b/src/renderer/src/components/native-chat/native-chat-http-link-source-owner.ts index b5fc881bb42..9b8798f2f26 100644 --- a/src/renderer/src/components/native-chat/native-chat-http-link-source-owner.ts +++ b/src/renderer/src/components/native-chat/native-chat-http-link-source-owner.ts @@ -4,7 +4,7 @@ import { canOpenWorkspaceBrowserTabOnRuntime, canOpenWorkspaceBrowserTabOnSsh } from '@/lib/workspace-browser-tab-open' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import type { AppState } from '@/store/types' /** The chat transcript has no PTY, so link ownership comes from the session's @@ -14,7 +14,7 @@ export function resolveNativeChatHttpLinkSourceOwner( state: AppState, worktreeId: string ): HttpLinkSourceOwner { - const runtimeEnvironmentId = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, worktreeId) + const runtimeEnvironmentId = runtimeEnvironmentIdOrLocalUnlessTwin(state, worktreeId) if (runtimeEnvironmentId) { return { kind: 'runtime', runtimeEnvironmentId } } diff --git a/src/renderer/src/components/native-chat/native-chat-runtime-owner.ts b/src/renderer/src/components/native-chat/native-chat-runtime-owner.ts index dd5b6558119..3eecf88a1c7 100644 --- a/src/renderer/src/components/native-chat/native-chat-runtime-owner.ts +++ b/src/renderer/src/components/native-chat/native-chat-runtime-owner.ts @@ -1,4 +1,4 @@ -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import type { WorktreeRuntimeOwnerState } from '@/lib/worktree-runtime-owner-state' import type { AppState } from '@/store/types' import { @@ -26,7 +26,7 @@ export function selectNativeChatRuntimeEnvironmentId( state: NativeChatRuntimeOwnerState, terminalTabId: string ): string | null { - return runtimeEnvironmentIdWithLegacyFallbackUnlessTwin( + return runtimeEnvironmentIdOrLocalUnlessTwin( state, findTerminalTabWorktreeId(state.tabsByWorktree, terminalTabId) ) @@ -35,7 +35,7 @@ export function selectNativeChatRuntimeEnvironmentId( export function createNativeChatRuntimeSelector(terminalTabId: string) { const selectOwner = createNativeChatTabOwnerSelector(terminalTabId) return (state: NativeChatRuntimeOwnerState): string | null => { - return runtimeEnvironmentIdWithLegacyFallbackUnlessTwin( + return runtimeEnvironmentIdOrLocalUnlessTwin( state, selectOwner({ tabsByWorktree: state.tabsByWorktree }) ) diff --git a/src/renderer/src/components/right-sidebar/FileExplorer.tsx b/src/renderer/src/components/right-sidebar/FileExplorer.tsx index 3e9157e04f1..24ffbbe80c4 100644 --- a/src/renderer/src/components/right-sidebar/FileExplorer.tsx +++ b/src/renderer/src/components/right-sidebar/FileExplorer.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useMemo } from 'react' import { useAppStore } from '@/store' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { getWorkspaceFileRevealOwner } from '@/lib/reveal-in-file-manager' import { getRouteForLocalPathOpenOwner } from '@/lib/local-path-open-guard' import { useActiveWorktree, useRepoById } from '@/store/selectors' @@ -51,7 +51,7 @@ function FileExplorerFiles(): React.JSX.Element { const searchPanel = useFileSearchPanel(explorerView) const activeWorktreeId = useAppStore((s) => s.activeWorktreeId) const runtimeEnvironmentId = useAppStore((s) => - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(s, activeWorktreeId) + runtimeEnvironmentIdOrLocalUnlessTwin(s, activeWorktreeId) ) const wslHostUnavailable = useWslHostUnavailableReason(activeWorktreeId) const activeWorktree = useActiveWorktree() diff --git a/src/renderer/src/components/right-sidebar/checks-panel/use-checks-panel-controller-state.tsx b/src/renderer/src/components/right-sidebar/checks-panel/use-checks-panel-controller-state.tsx index 0864da3c5b8..acffc243b97 100644 --- a/src/renderer/src/components/right-sidebar/checks-panel/use-checks-panel-controller-state.tsx +++ b/src/renderer/src/components/right-sidebar/checks-panel/use-checks-panel-controller-state.tsx @@ -9,7 +9,7 @@ import { useMountedRef } from '@/hooks/useMountedRef' import { useConfirmationDialog } from '@/components/confirmation-dialog-context' import { getWorktreeGitIdentityDisplay } from '../../../../../shared/worktree/git-identity-display' import { resolveRepoWorktreeLaunchPlatform } from '@/lib/execution-host-facts' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { normalizeGlobalWindowsRuntimeDefault } from '../../../../../shared/project-execution-runtime' import { saveSourceControlActionRecipe, @@ -223,7 +223,7 @@ export function useChecksPanelControllerState() { ? resolveRepoWorktreeLaunchPlatform(useAppStore.getState(), repo, activeWorktree) : undefined const runtimeEnvironmentId = useAppStore((s) => - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(s, activeWorktreeId) + runtimeEnvironmentIdOrLocalUnlessTwin(s, activeWorktreeId) ) const ownerSettings = useMemo( () => diff --git a/src/renderer/src/components/right-sidebar/file-explorer-row-context-menu.tsx b/src/renderer/src/components/right-sidebar/file-explorer-row-context-menu.tsx index e65c21ae124..30ff70084be 100644 --- a/src/renderer/src/components/right-sidebar/file-explorer-row-context-menu.tsx +++ b/src/renderer/src/components/right-sidebar/file-explorer-row-context-menu.tsx @@ -34,7 +34,7 @@ import { revealInFileManager } from '@/lib/reveal-in-file-manager' import { isRevealOrEditorOpenBlocked } from '@/lib/local-path-open-guard' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import type { FileExplorerRowProps } from './FileExplorerRow' import { shouldShowCollapseFolderAction, @@ -123,7 +123,7 @@ export function FileExplorerRowContextMenu({ const revealOwner = useAppStore((s) => getWorkspaceFileRevealOwner(s, s.activeWorktreeId, { connectionId, - runtimeEnvironmentId: runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(s, s.activeWorktreeId) + runtimeEnvironmentId: runtimeEnvironmentIdOrLocalUnlessTwin(s, s.activeWorktreeId) }) ) const revealBlocked = isRevealOrEditorOpenBlocked(revealOwner) diff --git a/src/renderer/src/components/right-sidebar/git-status-file-watch-refresh.ts b/src/renderer/src/components/right-sidebar/git-status-file-watch-refresh.ts index ad8e9f014d4..f9ff00af392 100644 --- a/src/renderer/src/components/right-sidebar/git-status-file-watch-refresh.ts +++ b/src/renderer/src/components/right-sidebar/git-status-file-watch-refresh.ts @@ -2,7 +2,7 @@ import { useEffect, useRef } from 'react' import { useAppStore } from '@/store' import { shouldPollActiveGitStatus } from '@/lib/passive-macos-app-data-access' import { isWindowVisible } from '@/lib/window-visibility-interval' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { normalizeRuntimePathForComparison, relativePathInsideRoot @@ -72,7 +72,7 @@ export function useGitStatusFileWatchRefresh({ worktreePath }: UseGitStatusFileWatchRefreshParams): void { const activeRuntimeEnvironmentId = useAppStore((state) => - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, activeWorktreeId) + runtimeEnvironmentIdOrLocalUnlessTwin(state, activeWorktreeId) ) const fetchStatusRef = useRef(fetchStatus) fetchStatusRef.current = fetchStatus diff --git a/src/renderer/src/components/right-sidebar/source-control-entry-context-menu.test.tsx b/src/renderer/src/components/right-sidebar/source-control-entry-context-menu.test.tsx index 3b5ab76bf4d..7d1a729a122 100644 --- a/src/renderer/src/components/right-sidebar/source-control-entry-context-menu.test.tsx +++ b/src/renderer/src/components/right-sidebar/source-control-entry-context-menu.test.tsx @@ -48,7 +48,7 @@ vi.mock('@/store', () => ({ })) vi.mock('@/lib/worktree-runtime-owner', () => ({ - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () => ownerRuntime.environmentId + runtimeEnvironmentIdOrLocalUnlessTwin: () => ownerRuntime.environmentId })) vi.mock(import('@/lib/reveal-in-file-manager'), async (importOriginal) => ({ diff --git a/src/renderer/src/components/right-sidebar/source-control/listing/entry-context-menu.tsx b/src/renderer/src/components/right-sidebar/source-control/listing/entry-context-menu.tsx index 8581d9b2102..55b01910d0e 100644 --- a/src/renderer/src/components/right-sidebar/source-control/listing/entry-context-menu.tsx +++ b/src/renderer/src/components/right-sidebar/source-control/listing/entry-context-menu.tsx @@ -25,7 +25,7 @@ import { getRouteForLocalPathOpenOwner, isRevealOrEditorOpenBlocked } from '@/lib/local-path-open-guard' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { NO_OPEN_IN_APPLICATIONS } from '@/lib/open-in-application-selection' import { getOpenInEntryAvailability, @@ -63,7 +63,7 @@ export function SourceControlEntryContextMenu({ // Why: a repo can belong to a runtime other than the focused one, and the OS reveal // cannot tell that host's path from a local one of the same name. const runtimeEnvironmentId = useAppStore((s) => - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(s, currentWorktreeId) + runtimeEnvironmentIdOrLocalUnlessTwin(s, currentWorktreeId) ) const revealOwner = useAppStore((s) => getWorkspaceFileRevealOwner(s, currentWorktreeId, { connectionId, runtimeEnvironmentId }) diff --git a/src/renderer/src/components/right-sidebar/use-file-explorer-tree-pane-state.ts b/src/renderer/src/components/right-sidebar/use-file-explorer-tree-pane-state.ts index 6767c75d53e..abf594a6d65 100644 --- a/src/renderer/src/components/right-sidebar/use-file-explorer-tree-pane-state.ts +++ b/src/renderer/src/components/right-sidebar/use-file-explorer-tree-pane-state.ts @@ -1,7 +1,7 @@ import type { Dispatch, RefObject, SetStateAction } from 'react' import { useEffect, useMemo, useRef } from 'react' import { useAppStore } from '@/store' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { useWorkspaceFileBrowserActionPredicate } from '@/lib/file-preview' import { openWorkspaceFile } from '@/lib/workspace-file-open' import type { RuntimeFileOperationArgs } from '@/runtime/runtime-file-client-types' @@ -130,7 +130,7 @@ export function useFileExplorerTreePaneState({ : false }) const ownerRuntimeEnvironmentId = useAppStore((s) => - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(s, activeWorktreeId) + runtimeEnvironmentIdOrLocalUnlessTwin(s, activeWorktreeId) ) const toggleDir = useAppStore((s) => s.toggleDir) const makePreviewFilePermanent = useAppStore((s) => s.makePreviewFilePermanent) diff --git a/src/renderer/src/components/right-sidebar/useFileSearchRunner.ts b/src/renderer/src/components/right-sidebar/useFileSearchRunner.ts index 1002ac2e93b..cfdc485071d 100644 --- a/src/renderer/src/components/right-sidebar/useFileSearchRunner.ts +++ b/src/renderer/src/components/right-sidebar/useFileSearchRunner.ts @@ -14,7 +14,7 @@ import { searchRuntimeFiles } from '@/runtime/runtime-file-search-client' import { useAppStore } from '@/store' import { getExecutionHostIdForWorktree, - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin + runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { isTwinWorkspace } from '@/lib/resolve-owner' import type { SearchResult } from '../../../../shared/code-search-types' @@ -45,7 +45,7 @@ export function useFileSearchRunner({ cancelPendingSearch: () => void } { const runtimeEnvironmentId = useAppStore((state) => - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, activeWorktreeId) + runtimeEnvironmentIdOrLocalUnlessTwin(state, activeWorktreeId) ) const executionHostId = useAppStore((state) => getExecutionHostIdForWorktree(state, activeWorktreeId) diff --git a/src/renderer/src/components/right-sidebar/useFileSearchScope.ts b/src/renderer/src/components/right-sidebar/useFileSearchScope.ts index afc52fa393e..e206d04aac6 100644 --- a/src/renderer/src/components/right-sidebar/useFileSearchScope.ts +++ b/src/renderer/src/components/right-sidebar/useFileSearchScope.ts @@ -2,7 +2,7 @@ import { useCallback, useEffect } from 'react' import { useAppStore } from '@/store' import { getExecutionHostIdForWorktree, - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin + runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { isFileSearchResultOwnerCurrent, @@ -25,7 +25,7 @@ export function useFileSearchScope({ updateActiveSearchState: (updates: { results: null; resultOwner: null; error: null }) => void }) { const runtimeEnvironmentId = useAppStore((state) => - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, activeWorktreeId) + runtimeEnvironmentIdOrLocalUnlessTwin(state, activeWorktreeId) ) const executionHostId = useAppStore((state) => getExecutionHostIdForWorktree(state, activeWorktreeId) diff --git a/src/renderer/src/components/tab-bar/use-tab-bar-runtime-model-worktree-write-probe.ts b/src/renderer/src/components/tab-bar/use-tab-bar-runtime-model-worktree-write-probe.ts index 2c208ef79e2..4cc17f03ad7 100644 --- a/src/renderer/src/components/tab-bar/use-tab-bar-runtime-model-worktree-write-probe.ts +++ b/src/renderer/src/components/tab-bar/use-tab-bar-runtime-model-worktree-write-probe.ts @@ -107,7 +107,7 @@ export function tabBarRuntimeModelStubs(): Record Record ({ useDetectedAgents: () => DETECTED_AGENTS }), detectionTarget: () => ({ useAgentDetectionTargetForWorktree: () => null }), connectionContext: () => ({ getConnectionIdFromState: () => null }), - runtimeOwner: () => ({ runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () => null }), + runtimeOwner: () => ({ runtimeEnvironmentIdOrLocalUnlessTwin: () => null }), nativeChatReadability: () => ({ isNativeChatTranscriptLocalReadable: () => false }), creationPolicy: () => ({ getClientCreationActionPolicy: () => CREATION_POLICY }), agentProjections: () => ({ selectTabBarAgentProjections: () => AGENT_PROJECTIONS }), diff --git a/src/renderer/src/components/tab-bar/use-tab-bar-runtime-model.ts b/src/renderer/src/components/tab-bar/use-tab-bar-runtime-model.ts index ff979fb25d8..096853d867b 100644 --- a/src/renderer/src/components/tab-bar/use-tab-bar-runtime-model.ts +++ b/src/renderer/src/components/tab-bar/use-tab-bar-runtime-model.ts @@ -11,7 +11,7 @@ import { useAgentDetectionTargetForWorktree } from '@/hooks/useAgentDetectionTar import { getConnectionIdFromState } from '@/lib/connection-context' import { getLocalProjectExecutionRuntimeContext } from '@/lib/local-preflight-context' import { isNativeChatTranscriptLocalReadable } from '@/lib/native-chat-transcript-readability' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { runtimeTargetForOwnerEnvironment } from '@/runtime/runtime-client-target' import { useOptionalShortcutLabel, useShortcutLabel } from '@/hooks/useShortcutLabel' import { @@ -126,7 +126,7 @@ export function useTabBarRuntimeModel({ const settings = useAppStore((s) => s.settings) // Why: use the worktree's owning host so offered Windows shells match the host that actually runs the terminal. const activeRuntimeEnvironmentId = useAppStore((s) => - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(s, worktreeId) + runtimeEnvironmentIdOrLocalUnlessTwin(s, worktreeId) ) // Why: retained tab strips rerun selectors on every store write; reuse canonical indexes, don't flatten both slices here. const worktreeConnectionId = useAppStore( diff --git a/src/renderer/src/components/tab-bar/web-runtime-tab-move-mirror.ts b/src/renderer/src/components/tab-bar/web-runtime-tab-move-mirror.ts index f15aedab71e..5d40f6da463 100644 --- a/src/renderer/src/components/tab-bar/web-runtime-tab-move-mirror.ts +++ b/src/renderer/src/components/tab-bar/web-runtime-tab-move-mirror.ts @@ -1,5 +1,5 @@ import type { RuntimeMobileSessionTabMove } from '../../../../shared/runtime-types' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { useAppStore } from '../../store' import { isWebRuntimeSessionActive } from '../../runtime/web-runtime-session-environment' import { moveWebRuntimeSessionTab } from '../../runtime/web-runtime-session-tab-move' @@ -9,7 +9,7 @@ export function mirrorWebRuntimeTabMove( worktreeId: string } ): void { - const environmentId = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin( + const environmentId = runtimeEnvironmentIdOrLocalUnlessTwin( useAppStore.getState(), args.worktreeId ) diff --git a/src/renderer/src/components/tab-group/useTabGroupActivationCommands.ts b/src/renderer/src/components/tab-group/useTabGroupActivationCommands.ts index 7a7ec61aa57..6580676a42f 100644 --- a/src/renderer/src/components/tab-group/useTabGroupActivationCommands.ts +++ b/src/renderer/src/components/tab-group/useTabGroupActivationCommands.ts @@ -5,7 +5,7 @@ import { focusTerminalTabSurface } from '../../lib/focus-terminal-tab-surface' import { TOGGLE_TERMINAL_PANE_EXPAND_EVENT } from '@/constants/terminal' import { activateWebRuntimeSessionTab } from '../../runtime/web-runtime-session-tab-lifecycle' import { isWebRuntimeSessionActive } from '../../runtime/web-runtime-session-environment' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { browserWorkspaceHasRemoteOwner } from '@/runtime/remote-browser-tab-ownership' import { activateStructuredAgentSessionTab } from '@/lib/structured-agent-session-tab-activation' import type { TabGroupWorktreeSnapshot } from './useTabGroupItemProjections' @@ -38,7 +38,7 @@ export function useTabGroupActivationCommands({ } focusGroup(worktreeId, groupId) activateTab(item.id) - const runtimeEnvironmentId = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin( + const runtimeEnvironmentId = runtimeEnvironmentIdOrLocalUnlessTwin( useAppStore.getState(), worktreeId ) @@ -117,7 +117,7 @@ export function useTabGroupActivationCommands({ } focusGroup(worktreeId, groupId) activateTab(item.id) - const runtimeEnvironmentId = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin( + const runtimeEnvironmentId = runtimeEnvironmentIdOrLocalUnlessTwin( useAppStore.getState(), worktreeId ) diff --git a/src/renderer/src/components/tab-group/useTabGroupCreationCommands.local-shell.test.ts b/src/renderer/src/components/tab-group/useTabGroupCreationCommands.local-shell.test.ts index 4649d82be95..d7d3f37e858 100644 --- a/src/renderer/src/components/tab-group/useTabGroupCreationCommands.local-shell.test.ts +++ b/src/renderer/src/components/tab-group/useTabGroupCreationCommands.local-shell.test.ts @@ -13,7 +13,7 @@ const mocks = vi.hoisted(() => ({ openNewBrowserTabInActiveWorkspace: vi.fn(), openNewMarkdownInActiveWorkspace: vi.fn(), openNewTerminalTabInActiveWorkspace: vi.fn(), - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: vi.fn(), + runtimeEnvironmentIdOrLocalUnlessTwin: vi.fn(), focusTerminalTabSurface: vi.fn(), runtimeCall: vi.fn() })) @@ -21,8 +21,7 @@ const mocks = vi.hoisted(() => ({ vi.mock('react', async () => await stubHeadlessReact()) vi.mock('@/lib/worktree-runtime-owner', () => ({ - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: - mocks.runtimeEnvironmentIdWithLegacyFallbackUnlessTwin + runtimeEnvironmentIdOrLocalUnlessTwin: mocks.runtimeEnvironmentIdOrLocalUnlessTwin })) vi.mock('@/lib/runtime-session-mirror-owners', () => ({ getRuntimeSessionMirrorEnvironmentIds: () => [] @@ -77,7 +76,7 @@ describe('tab group "+" menu shell launch on a locally-owned workspace', () => { beforeEach(() => { vi.clearAllMocks() storeState.activeWorkspaceExecutionHostId = 'local' - mocks.runtimeEnvironmentIdWithLegacyFallbackUnlessTwin.mockReturnValue(null) + mocks.runtimeEnvironmentIdOrLocalUnlessTwin.mockReturnValue(null) stubRuntimeEnvironmentBridge({ call: mocks.runtimeCall }) }) diff --git a/src/renderer/src/components/tab-group/useTabGroupCreationCommands.ts b/src/renderer/src/components/tab-group/useTabGroupCreationCommands.ts index f72d1ae2e0a..edf25e933ae 100644 --- a/src/renderer/src/components/tab-group/useTabGroupCreationCommands.ts +++ b/src/renderer/src/components/tab-group/useTabGroupCreationCommands.ts @@ -9,7 +9,7 @@ import { openTabBarEntry, type TabCreateEntryArgs } from '../tab-bar/tab-create- import { openMobileEmulatorTab } from '@/lib/open-mobile-emulator-tab' import { ensureSimulatorTab, getSimulatorTabForWorktree } from '@/lib/ensure-simulator-tab' import { buildDuplicatedBrowserTabOptions } from '@/lib/duplicate-browser-tab-options' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { browserWorkspaceHasRemoteOwner } from '@/runtime/remote-browser-tab-ownership' import { getClientCreationActionPolicy } from '@/lib/client-creation-action-policy' import type { TabGroupWorktreeSnapshot } from './useTabGroupItemProjections' @@ -70,10 +70,7 @@ export function useTabGroupCreationCommands({ if (!source) { return } - const runtimeEnvironmentId = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin( - state, - worktreeId - ) + const runtimeEnvironmentId = runtimeEnvironmentIdOrLocalUnlessTwin(state, worktreeId) const browserAvailability = getClientCreationActionPolicy(state, worktreeId)[ 'managed-browser' ] @@ -116,7 +113,7 @@ export function useTabGroupCreationCommands({ }, newTerminalWithShell: (shellOverride: string) => { void (async () => { - const environmentId = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin( + const environmentId = runtimeEnvironmentIdOrLocalUnlessTwin( useAppStore.getState(), worktreeId ) diff --git a/src/renderer/src/components/terminal-pane/terminal-input-host-platform.ts b/src/renderer/src/components/terminal-pane/terminal-input-host-platform.ts index 61e4abf8393..ea9a26f200a 100644 --- a/src/renderer/src/components/terminal-pane/terminal-input-host-platform.ts +++ b/src/renderer/src/components/terminal-pane/terminal-input-host-platform.ts @@ -4,7 +4,7 @@ import { isWslUncPath } from '../../../../shared/wsl-paths' import { getConnectionIdFromState } from '@/lib/connection-context' import { getExecutionHostIdForWorktree, - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin + runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { getRemoteRuntimePtyEnvironmentId } from '@/runtime/runtime-terminal-stream' import type { AppState } from '@/store/types' @@ -90,10 +90,7 @@ export function resolveTerminalInputHostPlatform(args: { const host = parseExecutionHostId(getExecutionHostIdForWorktree(args.state, args.worktreeId)) if (host?.kind === 'ssh') { - const ownerEnvironmentId = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin( - args.state, - args.worktreeId - ) + const ownerEnvironmentId = runtimeEnvironmentIdOrLocalUnlessTwin(args.state, args.worktreeId) return ownerEnvironmentId ? (args.state.sshStateByEnvironment .get(ownerEnvironmentId) diff --git a/src/renderer/src/components/terminal-pane/terminal-paste-runtime.ts b/src/renderer/src/components/terminal-pane/terminal-paste-runtime.ts index 983ad7f5e85..9cd6aec6d34 100644 --- a/src/renderer/src/components/terminal-pane/terminal-paste-runtime.ts +++ b/src/renderer/src/components/terminal-pane/terminal-paste-runtime.ts @@ -4,7 +4,7 @@ import { isRemoteRuntimePtyId, parseRemoteRuntimePtyId } from '../../../../shared/remote-runtime-pty-id' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import type { WorktreeRuntimeOwnerState } from '@/lib/worktree-runtime-owner-state' import type { PtyTransport } from './pty-transport-types' @@ -122,6 +122,6 @@ export function resolveTerminalPasteRuntimeEnvironmentId( const ptyId = transport?.getPtyId() ?? null return ( (ptyId ? parseRemoteRuntimePtyId(ptyId)?.environmentId : null) ?? - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, worktreeId) + runtimeEnvironmentIdOrLocalUnlessTwin(state, worktreeId) ) } diff --git a/src/renderer/src/components/terminal-workspace-model.ts b/src/renderer/src/components/terminal-workspace-model.ts index 360ae69ced8..68ba7ee9728 100644 --- a/src/renderer/src/components/terminal-workspace-model.ts +++ b/src/renderer/src/components/terminal-workspace-model.ts @@ -1,7 +1,7 @@ import type { Tab } from '../../../shared/tab-types' import type { KeybindingContext } from '../../../shared/keybindings' import { useAppStore } from '../store' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { isEditorTabContentType } from '@/store/slices/editor/tabs/editor-tab-content-type' export const CLOSE_DIALOG_DEBOUNCE_MS = 200 @@ -56,7 +56,7 @@ export function isPinnedVisibleTab( } export function getActiveWorktreeRuntimeEnvironmentId(worktreeId: string | null): string | null { - return runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(useAppStore.getState(), worktreeId) + return runtimeEnvironmentIdOrLocalUnlessTwin(useAppStore.getState(), worktreeId) } export function isPinnedActiveEditorTab( diff --git a/src/renderer/src/components/worktree-jump-palette-quick-action-availability.test.tsx b/src/renderer/src/components/worktree-jump-palette-quick-action-availability.test.tsx index b641cc6d3e7..b8d9102e352 100644 --- a/src/renderer/src/components/worktree-jump-palette-quick-action-availability.test.tsx +++ b/src/renderer/src/components/worktree-jump-palette-quick-action-availability.test.tsx @@ -12,7 +12,7 @@ const mocks = vi.hoisted(() => ({ state: {} as Record })) vi.mock('@/store', () => ({ useAppStore: { getState: () => mocks.state } })) vi.mock('@/lib/worktree-runtime-owner', () => ({ - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () => RUNTIME_ID + runtimeEnvironmentIdOrLocalUnlessTwin: () => RUNTIME_ID })) vi.mock('@/components/sidebar/delete-worktree-flow', () => ({ runWorktreeDelete: vi.fn() })) diff --git a/src/renderer/src/hooks/editor-external-watch-targets.ts b/src/renderer/src/hooks/editor-external-watch-targets.ts index 46c18de44a0..7f36f0ec7b2 100644 --- a/src/renderer/src/hooks/editor-external-watch-targets.ts +++ b/src/renderer/src/hooks/editor-external-watch-targets.ts @@ -4,7 +4,7 @@ import { findWorktreeById } from '@/store/slices/worktree-helpers' import { isVirtualEditorFile } from '@/store/slices/editor/tabs/editor-tab-content-type' import { findRepoForHost } from '@/store/slices/repo-host-identity' import { isLocalWindowsDesktopClient } from '@/lib/desktop-window-chrome' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { parseExecutionHostId, toRuntimeExecutionHostId, @@ -123,7 +123,7 @@ function getSidebarWatchConsumer( worktreeId: string ): WatchConsumer { // Why: sidebar watcher must follow the selected worktree's host owner, not the host currently focused in the UI. - const owner = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, worktreeId) + const owner = runtimeEnvironmentIdOrLocalUnlessTwin(state, worktreeId) if (owner) { return { owner, hostId: toRuntimeExecutionHostId(owner) } } diff --git a/src/renderer/src/hooks/ipc-events/browser-state-capture-paint-hold.test.ts b/src/renderer/src/hooks/ipc-events/browser-state-capture-paint-hold.test.ts index 853de2315ab..93edfbf7a3d 100644 --- a/src/renderer/src/hooks/ipc-events/browser-state-capture-paint-hold.test.ts +++ b/src/renderer/src/hooks/ipc-events/browser-state-capture-paint-hold.test.ts @@ -4,7 +4,7 @@ vi.mock('../../store', () => ({ useAppStore: { getState: () => ({}) } })) vi.mock('@/lib/worktree-runtime-owner', () => ({ - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () => null + runtimeEnvironmentIdOrLocalUnlessTwin: () => null })) vi.mock('@/components/browser-pane/describe-page/live-browser-url-registry', () => ({ rememberLiveBrowserUrl: vi.fn() diff --git a/src/renderer/src/hooks/ipc-events/browser-state-ipc-bridge.ts b/src/renderer/src/hooks/ipc-events/browser-state-ipc-bridge.ts index a84bb1c3db7..17caab7c151 100644 --- a/src/renderer/src/hooks/ipc-events/browser-state-ipc-bridge.ts +++ b/src/renderer/src/hooks/ipc-events/browser-state-ipc-bridge.ts @@ -1,5 +1,5 @@ import { rememberLiveBrowserUrl } from '@/components/browser-pane/describe-page/live-browser-url-registry' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { resolveBrowserSourceUnifiedTab } from '@/lib/browser-workspace-source-resolution' import { redactKagiSessionToken } from '../../../../shared/browser-url' import { useAppStore } from '../../store' @@ -138,10 +138,7 @@ export function registerBrowserStateIpcBridge( const sourcePage = Object.values(store.browserPagesByWorkspace) .flat() .find((page) => page.id === browserPageId) - if ( - !sourcePage || - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(store, sourcePage.worktreeId) - ) { + if (!sourcePage || runtimeEnvironmentIdOrLocalUnlessTwin(store, sourcePage.worktreeId)) { return } // Why: the link inherits the opener's cookie jar. Falling back to the default profile would let diff --git a/src/renderer/src/hooks/ipc-events/browser-state-open-link-profile.test.ts b/src/renderer/src/hooks/ipc-events/browser-state-open-link-profile.test.ts index 75094ad2854..7512ec8bd4b 100644 --- a/src/renderer/src/hooks/ipc-events/browser-state-open-link-profile.test.ts +++ b/src/renderer/src/hooks/ipc-events/browser-state-open-link-profile.test.ts @@ -11,7 +11,7 @@ vi.mock('../../store', () => ({ useAppStore: { getState: () => storeState.value } })) vi.mock('@/lib/worktree-runtime-owner', () => ({ - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () => null + runtimeEnvironmentIdOrLocalUnlessTwin: () => null })) vi.mock('@/components/browser-pane/describe-page/live-browser-url-registry', () => ({ rememberLiveBrowserUrl: vi.fn() diff --git a/src/renderer/src/lib/activate-ai-vault-structured-session-reveal.test.ts b/src/renderer/src/lib/activate-ai-vault-structured-session-reveal.test.ts index 081b0b31cd8..19e17c1555f 100644 --- a/src/renderer/src/lib/activate-ai-vault-structured-session-reveal.test.ts +++ b/src/renderer/src/lib/activate-ai-vault-structured-session-reveal.test.ts @@ -21,7 +21,7 @@ vi.mock('@/runtime/runtime-rpc-client', () => ({ })) vi.mock('./worktree-runtime-owner', () => ({ - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: mocks.environmentIdFor + runtimeEnvironmentIdOrLocalUnlessTwin: mocks.environmentIdFor })) import { revealStructuredSession } from './activate-ai-vault-structured-session' diff --git a/src/renderer/src/lib/activate-ai-vault-structured-session.ts b/src/renderer/src/lib/activate-ai-vault-structured-session.ts index 29013528a19..33a1932697f 100644 --- a/src/renderer/src/lib/activate-ai-vault-structured-session.ts +++ b/src/renderer/src/lib/activate-ai-vault-structured-session.ts @@ -4,7 +4,7 @@ import { translate } from '@/i18n/i18n' import { activateAndRevealWorktree } from './worktree-activation' import { activateStructuredAgentSessionById } from './structured-agent-session-tab-activation' import { useAppStore } from '@/store' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from './worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from './worktree-runtime-owner' import { callRuntimeRpc, runtimeEnvironmentSupportsCapability } from '@/runtime/runtime-rpc-client' import type { RuntimeClientTarget } from '../../../shared/runtime-client-target' import { runtimeTargetForOwnerEnvironment } from '@/runtime/runtime-client-target' @@ -105,10 +105,7 @@ export async function activateAiVaultStructuredSession( const hostKey = executionHostIdForStructuredTarget( target ?? runtimeTargetForOwnerEnvironment( - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin( - useAppStore.getState(), - structured.workspaceId - ) + runtimeEnvironmentIdOrLocalUnlessTwin(useAppStore.getState(), structured.workspaceId) ) ) const key = `${hostKey}\0${structured.workspaceId}\0${structured.sessionId}` @@ -199,7 +196,7 @@ export async function revealStructuredSession(target: { const host = target.target ?? runtimeTargetForOwnerEnvironment( - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(useAppStore.getState(), target.worktreeId) + runtimeEnvironmentIdOrLocalUnlessTwin(useAppStore.getState(), target.worktreeId) ) // Negotiated against the host that will answer this call, not the local one: a paired host runs // its own build, and its method-not-found is indistinguishable from a refusal we should surface. @@ -259,9 +256,7 @@ async function refreshStructuredSessionTabs( const state = useAppStore.getState() const host = target ?? - runtimeTargetForOwnerEnvironment( - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, worktreeId) - ) + runtimeTargetForOwnerEnvironment(runtimeEnvironmentIdOrLocalUnlessTwin(state, worktreeId)) if (host.kind === 'local') { await withStructuredSessionRestoreTimeout(refreshLocalStructuredSessionWorktreeTabs(worktreeId)) return diff --git a/src/renderer/src/lib/active-agent-note-send.ts b/src/renderer/src/lib/active-agent-note-send.ts index 7ac569f10f6..ffd4807da15 100644 --- a/src/renderer/src/lib/active-agent-note-send.ts +++ b/src/renderer/src/lib/active-agent-note-send.ts @@ -3,7 +3,7 @@ import { runtimeTargetForOwnerEnvironment } from '@/runtime/runtime-client-targe import type { RuntimeClientTarget } from '../../../shared/runtime-client-target' import { useAppStore } from '@/store' import { callRuntimeRpc } from '@/runtime/runtime-rpc-client' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { findActiveRuntimeTerminal, getActiveTerminalNoteTarget } from './active-agent-note-target' import type { ActiveTerminalNoteTarget } from './active-agent-note-target' import type { ActiveAgentNotesSendResult } from './active-agent-note-send-result' @@ -74,7 +74,7 @@ async function sendNotesToActiveAgentSessionInternal({ return reportNoteSendFailure({ status: 'no-active-terminal', code: 'no-note-target' }, null) } const runtimeTarget = runtimeTargetForOwnerEnvironment( - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, worktreeId) + runtimeEnvironmentIdOrLocalUnlessTwin(state, worktreeId) ) const terminal = await findActiveRuntimeTerminal( runtimeTarget, diff --git a/src/renderer/src/lib/active-agent-note-target.ts b/src/renderer/src/lib/active-agent-note-target.ts index bf8ca66b0b3..f492a55f2c9 100644 --- a/src/renderer/src/lib/active-agent-note-target.ts +++ b/src/renderer/src/lib/active-agent-note-target.ts @@ -8,7 +8,7 @@ import { } from '../../../shared/agent-status-types' import type { AppState } from '@/store/types' import { callRuntimeRpc } from '@/runtime/runtime-rpc-client' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import type { WorktreeRuntimeOwnerState } from '@/lib/worktree-runtime-owner-state' import { toRuntimeWorktreeSelector } from '@/runtime/runtime-worktree-selector' import { isTerminalLeafId, makePaneKey } from '../../../shared/stable-pane-id' @@ -122,7 +122,7 @@ export function getActiveAgentRuntimeProbeDescriptor( // Route by the worktree's owner host so the probe targets the host that runs // this worktree's agent terminal, not the focused runtime. const runtimeTarget = runtimeTargetForOwnerEnvironment( - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, worktreeId) + runtimeEnvironmentIdOrLocalUnlessTwin(state, worktreeId) ) const runtimeKey = runtimeTarget.kind === 'environment' ? `env:${runtimeTarget.environmentId}` : 'local' diff --git a/src/renderer/src/lib/agent-launch-caller-permissions.test.ts b/src/renderer/src/lib/agent-launch-caller-permissions.test.ts index 1cc03e20753..0af713a2d0b 100644 --- a/src/renderer/src/lib/agent-launch-caller-permissions.test.ts +++ b/src/renderer/src/lib/agent-launch-caller-permissions.test.ts @@ -30,7 +30,7 @@ vi.mock('@/runtime/web-runtime-session-environment', () => ({ })) vi.mock('@/lib/worktree-runtime-owner', () => ({ getExecutionHostIdForWorktree: () => 'local', - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () => null + runtimeEnvironmentIdOrLocalUnlessTwin: () => null })) vi.mock('@/components/tab-bar/reconcile-order', () => ({ reconcileTabOrder: (_stored: unknown, terminalIds: string[]) => terminalIds diff --git a/src/renderer/src/lib/agent-launch-caller-placement.test.ts b/src/renderer/src/lib/agent-launch-caller-placement.test.ts index 750367973c1..7f6409e3dd5 100644 --- a/src/renderer/src/lib/agent-launch-caller-placement.test.ts +++ b/src/renderer/src/lib/agent-launch-caller-placement.test.ts @@ -31,7 +31,7 @@ vi.mock('@/runtime/web-runtime-session-environment', () => ({ })) vi.mock('@/lib/worktree-runtime-owner', () => ({ getExecutionHostIdForWorktree: () => 'local', - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () => null + runtimeEnvironmentIdOrLocalUnlessTwin: () => null })) vi.mock('@/components/tab-bar/reconcile-order', () => ({ reconcileTabOrder: (_stored: unknown, terminalIds: string[]) => terminalIds diff --git a/src/renderer/src/lib/agent-launch-caller-prompt-transport.test.ts b/src/renderer/src/lib/agent-launch-caller-prompt-transport.test.ts index dc0b109115e..6a9abcfecca 100644 --- a/src/renderer/src/lib/agent-launch-caller-prompt-transport.test.ts +++ b/src/renderer/src/lib/agent-launch-caller-prompt-transport.test.ts @@ -32,7 +32,7 @@ vi.mock('@/runtime/web-runtime-session-environment', () => ({ })) vi.mock('@/lib/worktree-runtime-owner', () => ({ getExecutionHostIdForWorktree: () => 'local', - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () => null + runtimeEnvironmentIdOrLocalUnlessTwin: () => null })) vi.mock('@/components/tab-bar/reconcile-order', () => ({ reconcileTabOrder: (_stored: unknown, terminalIds: string[]) => terminalIds diff --git a/src/renderer/src/lib/agent-launch-caller-routing.test.ts b/src/renderer/src/lib/agent-launch-caller-routing.test.ts index 8dcac1b7e97..af6282f07a7 100644 --- a/src/renderer/src/lib/agent-launch-caller-routing.test.ts +++ b/src/renderer/src/lib/agent-launch-caller-routing.test.ts @@ -37,7 +37,7 @@ vi.mock('@/runtime/web-runtime-session-environment', () => ({ })) vi.mock('@/lib/worktree-runtime-owner', () => ({ getExecutionHostIdForWorktree: () => mockExecutionHostId(), - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () => 'web-runtime' + runtimeEnvironmentIdOrLocalUnlessTwin: () => 'web-runtime' })) vi.mock('@/lib/launch-agent-web-host-tab', () => ({ launchAgentInWebHostTab: mockLaunchAgentInWebHostTab diff --git a/src/renderer/src/lib/client-creation-action-policy.ts b/src/renderer/src/lib/client-creation-action-policy.ts index a73564a2908..0979b4c1acd 100644 --- a/src/renderer/src/lib/client-creation-action-policy.ts +++ b/src/renderer/src/lib/client-creation-action-policy.ts @@ -3,7 +3,7 @@ import { BROWSER_SCREENCAST_RUNTIME_CAPABILITY } from '../../../shared/protocol- import type { RuntimeStatus } from '../../../shared/runtime-types' import type { AppState } from '@/store/types' import { isPairedWebClientWindow } from './desktop-window-chrome' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from './worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from './worktree-runtime-owner' import { expectsBrowserClientHosting } from '../../../shared/browser-client-hosting-eligibility' import { getOrcadSshDeployment, @@ -77,7 +77,7 @@ export function getClientCreationActionPolicy( const runtimeEnvironmentId = floatingWorkspace ? null : worktreeId - ? runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, worktreeId) + ? runtimeEnvironmentIdOrLocalUnlessTwin(state, worktreeId) : (state.settings?.activeRuntimeEnvironmentId?.trim() ?? null) const runtimeStatus = runtimeEnvironmentId ? (state.runtimeStatusByEnvironmentId?.get(runtimeEnvironmentId)?.status ?? null) diff --git a/src/renderer/src/lib/doc-preview-grants.test.ts b/src/renderer/src/lib/doc-preview-grants.test.ts index 308760c184d..b53ff82a799 100644 --- a/src/renderer/src/lib/doc-preview-grants.test.ts +++ b/src/renderer/src/lib/doc-preview-grants.test.ts @@ -12,8 +12,7 @@ vi.mock('@/lib/connection-owner-resolution', () => ({ getConnectionIdForFileFromState: () => mocks.connectionId })) vi.mock('@/lib/worktree-runtime-owner', () => ({ - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () => - mocks.ambiguous ? null : mocks.environmentId + runtimeEnvironmentIdOrLocalUnlessTwin: () => (mocks.ambiguous ? null : mocks.environmentId) })) import { diff --git a/src/renderer/src/lib/doc-preview-grants.ts b/src/renderer/src/lib/doc-preview-grants.ts index 5129830f3fd..86aa2296c13 100644 --- a/src/renderer/src/lib/doc-preview-grants.ts +++ b/src/renderer/src/lib/doc-preview-grants.ts @@ -1,7 +1,7 @@ import type { DocPreviewGrantRequest } from '../../../preload/api/doc-preview-api' import { basename, dirname, getRelativePathInsideRoot } from '@/lib/path' import { getConnectionIdForFileFromState } from '@/lib/connection-owner-resolution' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { toRuntimeWorktreeSelector } from '@/runtime/runtime-worktree-selector' import { chatScratchFileAddress } from '@/runtime/runtime-chat-scratch-file-address' import type { AppState } from '@/store/types' @@ -44,7 +44,7 @@ export function buildDocPreviewGrantRequest( return { owner: { kind: 'ssh', connectionId }, requestBase, root, entryRelativePath } } // Why: a twin names no server to mint against, so it gets no grant. - const environmentId = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, worktreeId) + const environmentId = runtimeEnvironmentIdOrLocalUnlessTwin(state, worktreeId) // Outside the workspace, a chat's scratch folder stands in for it: its host serves it as a root. const base = worktreeRoot && worktreeRelativePath diff --git a/src/renderer/src/lib/execute-open-editor-path-move.ts b/src/renderer/src/lib/execute-open-editor-path-move.ts index 703a1cf14c7..d7f4c453650 100644 --- a/src/renderer/src/lib/execute-open-editor-path-move.ts +++ b/src/renderer/src/lib/execute-open-editor-path-move.ts @@ -12,7 +12,7 @@ import { } from '@/lib/remap-open-editor-tabs-for-path-change' import { verifyLatchedMoveDestinations } from '@/hooks/useEditorExternalWatch' import { notifyHostOfMirroredEditorClose } from '@/runtime/close-mirrored-editor-tab' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { editorMoveOwnerFromContext, isOpenFileOnMoveOwner } from '@/lib/open-editor-move-owner' import { documentFolderAccess } from '@/lib/local-file-access' import { contentRekeyOutcome } from '@/runtime/layout-owner-content-rekeys' @@ -55,7 +55,7 @@ export async function executeOpenEditorPathMove(args: { const addScope = (wtId: string, owner: string | null): void => { scopes.set(`${wtId}::${owner ?? 'local'}`, { worktreeId: wtId, owner }) } - addScope(worktreeId, runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(moveState, worktreeId)) + addScope(worktreeId, runtimeEnvironmentIdOrLocalUnlessTwin(moveState, worktreeId)) for (const f of affected) { addScope(f.worktreeId, f.runtimeEnvironmentId?.trim() || null) } diff --git a/src/renderer/src/lib/execution-host-facts.ts b/src/renderer/src/lib/execution-host-facts.ts index 7836e9db404..2096a7d1c57 100644 --- a/src/renderer/src/lib/execution-host-facts.ts +++ b/src/renderer/src/lib/execution-host-facts.ts @@ -14,7 +14,7 @@ import { getLocalProjectExecutionRuntimeContext } from './local-preflight-contex import { CLIENT_PLATFORM } from './new-workspace' import { getExecutionHostIdForWorktree, - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin + runtimeEnvironmentIdOrLocalUnlessTwin } from './worktree-runtime-owner' /** @@ -148,9 +148,7 @@ export function resolveWorktreeExecutionHostPlatform( nestedSshTargetId: host?.kind === 'runtime' ? (getConnectionIdFromState(state, worktreeId) ?? null) : null, sshOwnerEnvironmentId: - host?.kind === 'ssh' - ? runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, worktreeId) - : null, + host?.kind === 'ssh' ? runtimeEnvironmentIdOrLocalUnlessTwin(state, worktreeId) : null, workspacePath, projectRuntime: () => getLocalProjectExecutionRuntimeContext(state, worktreeId) }) diff --git a/src/renderer/src/lib/file-preview-action-visibility.test.tsx b/src/renderer/src/lib/file-preview-action-visibility.test.tsx index e393b624de8..20feffe9508 100644 --- a/src/renderer/src/lib/file-preview-action-visibility.test.tsx +++ b/src/renderer/src/lib/file-preview-action-visibility.test.tsx @@ -18,7 +18,7 @@ vi.mock('@/lib/client-creation-action-policy', () => ({ getClientCreationActionPolicy: () => ({ 'managed-browser': mocks.browserAvailability }) })) vi.mock('@/lib/worktree-runtime-owner', () => ({ - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () => mocks.environmentId + runtimeEnvironmentIdOrLocalUnlessTwin: () => mocks.environmentId })) const storeState = { diff --git a/src/renderer/src/lib/file-preview.test.ts b/src/renderer/src/lib/file-preview.test.ts index fb5e2b3fef8..02fe5d55cac 100644 --- a/src/renderer/src/lib/file-preview.test.ts +++ b/src/renderer/src/lib/file-preview.test.ts @@ -52,7 +52,7 @@ vi.mock('@/lib/client-creation-action-policy', () => ({ })) vi.mock('@/lib/worktree-runtime-owner', () => ({ - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () => mocks.environmentId + runtimeEnvironmentIdOrLocalUnlessTwin: () => mocks.environmentId })) vi.mock('@/store', () => ({ diff --git a/src/renderer/src/lib/file-preview.ts b/src/renderer/src/lib/file-preview.ts index 8735fc98d94..45029639a2d 100644 --- a/src/renderer/src/lib/file-preview.ts +++ b/src/renderer/src/lib/file-preview.ts @@ -7,7 +7,7 @@ import { useShallow } from 'zustand/react/shallow' import { basename, getRelativePathInsideRoot } from '@/lib/path' import { getConnectionIdForFileFromState } from '@/lib/connection-owner-resolution' import { activateBrowserWorkspaceTab } from '@/lib/browser-workspace-tab-activation' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { isTwinWorkspace } from '@/lib/resolve-owner' import { REMOTE_FILE_BROWSER_UNSUPPORTED_MESSAGE, @@ -99,7 +99,7 @@ export function getWorkspaceFilePreviewPlan( } // Why: the doc preview needs no browser at all, so a paired runtime without the // screencast capability still previews documents. - if (runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, worktreeId)) { + if (runtimeEnvironmentIdOrLocalUnlessTwin(state, worktreeId)) { const worktreeRoot = state.getKnownWorktreeById(worktreeId)?.path ?? null const outsideWorktree = worktreeRoot && !getRelativePathInsideRoot(filePath, worktreeRoot) // Why: the host's files.read is worktree-scoped (plus chat scratch folders, served by path), so @@ -140,7 +140,7 @@ export function useWorkspaceFileBrowserActionPredicate( ? getClientCreationActionPolicy(state, worktreeId)['managed-browser'].state : null, runtimeEnvironmentId: worktreeId - ? runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, worktreeId) + ? runtimeEnvironmentIdOrLocalUnlessTwin(state, worktreeId) : null, folderWorkspaces: selectFolderWorkspaces(state), projectGroups: state.projectGroups, @@ -292,7 +292,7 @@ export function openFilePreviewToSide(params: { // Why the unfocused split on a paired workspace: the preview opens in the background, and a host // snapshot reads an activated empty group as a terminal pane. - const activate = !runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, worktreeId) + const activate = !runtimeEnvironmentIdOrLocalUnlessTwin(state, worktreeId) const placement = existingSibling ? { targetGroupId: existingSibling } : { split: { sourceGroupId, splitDirection: 'right' as const, activate } } diff --git a/src/renderer/src/lib/launch-agent-background-session.ts b/src/renderer/src/lib/launch-agent-background-session.ts index 0cd9c1277ca..21ff60be960 100644 --- a/src/renderer/src/lib/launch-agent-background-session.ts +++ b/src/renderer/src/lib/launch-agent-background-session.ts @@ -19,7 +19,7 @@ import { } from '@/components/terminal-pane/pty-dispatcher' import { subscribeToPtyData } from '@/components/terminal-pane/pty-data-sidecar-subscriptions' import { callRuntimeRpc } from '@/runtime/runtime-rpc-client' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { retireProvider } from '@/lib/retire-unowned-background-terminal' import { createRuntimeAgentBackgroundTerminal } from '@/lib/runtime-agent-background-create' import { subscribeToRuntimeTerminalData } from '@/runtime/runtime-terminal-stream' @@ -57,7 +57,7 @@ export async function launchAgentBackgroundSession( const { platform: launchPlatform, isRemote, isLocalHost } = launchHost // Route by the worktree's owner host, not the focused runtime. const runtimeTarget = runtimeTargetForOwnerEnvironment( - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(store, worktreeId) + runtimeEnvironmentIdOrLocalUnlessTwin(store, worktreeId) ) if (hasExtraAgentArgs(args.extraAgentArgs) && runtimeTarget.kind === 'environment') { // Why: that server rebuilds the command from its own settings and would drop the extras. diff --git a/src/renderer/src/lib/launch-agent-in-new-tab-cwd.test.ts b/src/renderer/src/lib/launch-agent-in-new-tab-cwd.test.ts index 7b3620e5be0..b07c2a5aacc 100644 --- a/src/renderer/src/lib/launch-agent-in-new-tab-cwd.test.ts +++ b/src/renderer/src/lib/launch-agent-in-new-tab-cwd.test.ts @@ -44,7 +44,7 @@ vi.mock('@/runtime/web-runtime-session-environment', () => ({ vi.mock('@/lib/worktree-runtime-owner', () => ({ getExecutionHostIdForWorktree: () => 'local', - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () => 'web-runtime' + runtimeEnvironmentIdOrLocalUnlessTwin: () => 'web-runtime' })) vi.mock('@/lib/launch-agent-web-host-tab', () => ({ diff --git a/src/renderer/src/lib/launch-agent-in-new-tab-placement.test.ts b/src/renderer/src/lib/launch-agent-in-new-tab-placement.test.ts index d083aab34d2..249149b5a9b 100644 --- a/src/renderer/src/lib/launch-agent-in-new-tab-placement.test.ts +++ b/src/renderer/src/lib/launch-agent-in-new-tab-placement.test.ts @@ -69,7 +69,7 @@ vi.mock('@/runtime/web-runtime-session-environment', () => ({ vi.mock('@/lib/worktree-runtime-owner', () => ({ getExecutionHostIdForWorktree: () => 'local', - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () => null + runtimeEnvironmentIdOrLocalUnlessTwin: () => null })) vi.mock('@/components/tab-bar/reconcile-order', () => ({ diff --git a/src/renderer/src/lib/launch-agent-in-new-tab.ts b/src/renderer/src/lib/launch-agent-in-new-tab.ts index 8e2a5ad6b0f..63a755f643d 100644 --- a/src/renderer/src/lib/launch-agent-in-new-tab.ts +++ b/src/renderer/src/lib/launch-agent-in-new-tab.ts @@ -13,7 +13,7 @@ import { launchNewTabPromptThroughHost, newTabPromptLaunchesThroughHost } from '@/lib/launch-agent-new-tab-host-route' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { isWebRuntimeSessionActive } from '@/runtime/web-runtime-session-environment' import { launchAgentInWebHostTab } from '@/lib/launch-agent-web-host-tab' import { @@ -222,7 +222,7 @@ function launchAgentInNewTabInternal(args: LaunchAgentInNewTabArgs): LaunchAgent } args.focusIntent?.cancel() - const runtimeEnvironmentId = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(store, worktreeId) + const runtimeEnvironmentId = runtimeEnvironmentIdOrLocalUnlessTwin(store, worktreeId) if (isWebRuntimeSessionActive(runtimeEnvironmentId)) { if (beforeSurfaceOpen?.({ kind: 'host-published' }) === false) { return null diff --git a/src/renderer/src/lib/launch-agent-structured-chat-guard.test.ts b/src/renderer/src/lib/launch-agent-structured-chat-guard.test.ts index 10983d53a64..c1cbba55acf 100644 --- a/src/renderer/src/lib/launch-agent-structured-chat-guard.test.ts +++ b/src/renderer/src/lib/launch-agent-structured-chat-guard.test.ts @@ -180,7 +180,7 @@ vi.mock('@/runtime/local-runtime-capabilities', () => ({ vi.mock('@/lib/worktree-runtime-owner', () => ({ getExecutionHostIdForWorktree: () => store.repos[0]?.connectionId ? `ssh:${store.repos[0].connectionId}` : 'local', - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () => null + runtimeEnvironmentIdOrLocalUnlessTwin: () => null })) vi.mock('@/runtime/structured-agent-session-client', () => ({ callStructuredAgentSession: (target: unknown, method: string, params: unknown) => diff --git a/src/renderer/src/lib/launch-ai-vault-session.test.ts b/src/renderer/src/lib/launch-ai-vault-session.test.ts index 5e8ed72b57f..967d1f92a44 100644 --- a/src/renderer/src/lib/launch-ai-vault-session.test.ts +++ b/src/renderer/src/lib/launch-ai-vault-session.test.ts @@ -11,7 +11,7 @@ const mockSetActiveTabType = vi.fn() const mockSetTabBarOrder = vi.fn() const runtimeMocks = vi.hoisted(() => ({ createWebRuntimeSessionTerminal: vi.fn(), - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: vi.fn<() => string | null>(() => null), + runtimeEnvironmentIdOrLocalUnlessTwin: vi.fn<() => string | null>(() => null), isWebRuntimeSessionActive: vi.fn(() => false) })) @@ -47,8 +47,7 @@ vi.mock('@/lib/telemetry', () => ({ })) vi.mock('@/lib/worktree-runtime-owner', () => ({ - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: - runtimeMocks.runtimeEnvironmentIdWithLegacyFallbackUnlessTwin + runtimeEnvironmentIdOrLocalUnlessTwin: runtimeMocks.runtimeEnvironmentIdOrLocalUnlessTwin })) vi.mock('@/runtime/web-runtime-terminal-creation', () => ({ @@ -63,7 +62,7 @@ import { launchAiVaultSessionInNewTab } from './launch-ai-vault-session' describe('launchAiVaultSessionInNewTab', () => { beforeEach(() => { vi.clearAllMocks() - runtimeMocks.runtimeEnvironmentIdWithLegacyFallbackUnlessTwin.mockReturnValue(null) + runtimeMocks.runtimeEnvironmentIdOrLocalUnlessTwin.mockReturnValue(null) runtimeMocks.isWebRuntimeSessionActive.mockReturnValue(false) runtimeMocks.createWebRuntimeSessionTerminal.mockResolvedValue({ status: 'created' }) mockState.tabsByWorktree = {} @@ -159,7 +158,7 @@ describe('launchAiVaultSessionInNewTab', () => { }) it('creates runtime-hosted resume terminals through the paired host', async () => { - runtimeMocks.runtimeEnvironmentIdWithLegacyFallbackUnlessTwin.mockReturnValue('env-1') + runtimeMocks.runtimeEnvironmentIdOrLocalUnlessTwin.mockReturnValue('env-1') runtimeMocks.isWebRuntimeSessionActive.mockReturnValue(true) const result = launchAiVaultSessionInNewTab({ @@ -211,7 +210,7 @@ describe('launchAiVaultSessionInNewTab', () => { // #22791: a drop on a pane edge in a paired-runtime worktree opened a tab instead of a split. it('splits the pane before a runtime-hosted resume and targets the new group', async () => { - runtimeMocks.runtimeEnvironmentIdWithLegacyFallbackUnlessTwin.mockReturnValue('env-1') + runtimeMocks.runtimeEnvironmentIdOrLocalUnlessTwin.mockReturnValue('env-1') runtimeMocks.isWebRuntimeSessionActive.mockReturnValue(true) const result = launchAiVaultSessionInNewTab({ @@ -234,7 +233,7 @@ describe('launchAiVaultSessionInNewTab', () => { }) it('removes the still-empty split group when the runtime create does not succeed', async () => { - runtimeMocks.runtimeEnvironmentIdWithLegacyFallbackUnlessTwin.mockReturnValue('env-1') + runtimeMocks.runtimeEnvironmentIdOrLocalUnlessTwin.mockReturnValue('env-1') runtimeMocks.isWebRuntimeSessionActive.mockReturnValue(true) runtimeMocks.createWebRuntimeSessionTerminal.mockResolvedValue({ status: 'failed', diff --git a/src/renderer/src/lib/launch-ai-vault-session.ts b/src/renderer/src/lib/launch-ai-vault-session.ts index b630483e51a..0827b314878 100644 --- a/src/renderer/src/lib/launch-ai-vault-session.ts +++ b/src/renderer/src/lib/launch-ai-vault-session.ts @@ -1,7 +1,7 @@ import { useAppStore } from '@/store' import { persistAgentLaunchTabOrder } from '@/lib/launch-agent-tab-order' import { tuiAgentToAgentKind } from '@/lib/telemetry' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { createWebRuntimeSessionTerminal } from '@/runtime/web-runtime-terminal-creation' import { isWebRuntimeSessionActive } from '@/runtime/web-runtime-session-environment' import type { AiVaultAgent } from '../../../shared/ai-vault-types' @@ -34,10 +34,7 @@ export function launchAiVaultSessionInNewTab(args: { args.splitDirection && args.targetGroupId ? { sourceGroupId: args.targetGroupId, splitDirection: args.splitDirection } : undefined - const runtimeEnvironmentId = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin( - store, - args.worktreeId - ) + const runtimeEnvironmentId = runtimeEnvironmentIdOrLocalUnlessTwin(store, args.worktreeId) if (isWebRuntimeSessionActive(runtimeEnvironmentId)) { // Why an empty group first: the paired host creates the tab after a round trip. const splitGroupId = split diff --git a/src/renderer/src/lib/paired-web-client-host.test.ts b/src/renderer/src/lib/paired-web-client-host.test.ts index 8ccabd33fc3..ec3ddb05e34 100644 --- a/src/renderer/src/lib/paired-web-client-host.test.ts +++ b/src/renderer/src/lib/paired-web-client-host.test.ts @@ -1,5 +1,5 @@ import { afterEach, describe, expect, it, vi } from 'vitest' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from './worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from './worktree-runtime-owner' import type { WorktreeRuntimeOwnerState } from './worktree-runtime-owner-state' import { resolveWorktreeOperationRoute } from './worktree-operation-route' import { resolveTerminalHostOwnership } from './terminal-worktree-route' @@ -42,7 +42,7 @@ describe('a paired web client routes every owner through its server (#9047)', () webState({ activeWorktreeId: WT, activeWorkspaceExecutionHostId: 'local' }), webState(UNSTAMPED_LOCAL_ROW) ]) { - expect(runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, WT)).toBe(ENV) + expect(runtimeEnvironmentIdOrLocalUnlessTwin(state, WT)).toBe(ENV) expect(resolveTerminalHostOwnership(state, WT, 'spawn')).toEqual({ kind: 'runtime', runtimeEnvironmentId: ENV @@ -64,9 +64,9 @@ describe('a paired web client routes every owner through its server (#9047)', () { id: 'folder-1', projectGroupId: 'group-1', executionHostId: 'local', connectionId: null } ] }) - expect( - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(folderState, folderWorkspaceKey('folder-1')) - ).toBe(ENV) + expect(runtimeEnvironmentIdOrLocalUnlessTwin(folderState, folderWorkspaceKey('folder-1'))).toBe( + ENV + ) }) it('keeps an SSH target as the place and the server as the transport', () => { @@ -88,12 +88,12 @@ describe('a paired web client routes every owner through its server (#9047)', () ] } }) - expect(runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, WT)).toBeNull() + expect(runtimeEnvironmentIdOrLocalUnlessTwin(state, WT)).toBeNull() }) it('leaves a desktop window local workspace on this computer', () => { const state = webState(UNSTAMPED_LOCAL_ROW) - expect(runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, WT)).toBeNull() + expect(runtimeEnvironmentIdOrLocalUnlessTwin(state, WT)).toBeNull() expect(resolveTerminalHostOwnership(state, WT, 'spawn').kind).toBe('local-or-ssh') }) }) diff --git a/src/renderer/src/lib/resolve-owner-acceptance.test-fixture.ts b/src/renderer/src/lib/resolve-owner-acceptance.test-fixture.ts index b36ba137248..92779b3574b 100644 --- a/src/renderer/src/lib/resolve-owner-acceptance.test-fixture.ts +++ b/src/renderer/src/lib/resolve-owner-acceptance.test-fixture.ts @@ -1,5 +1,5 @@ -/** Hand-reviewed owner per matrix case for `resolveOwnerWithLegacyFallback` (round-3 design §5). */ -export const LEGACY_FALLBACK_OWNER_ACCEPTANCE: Record = { +/** Hand-reviewed owner per matrix case for `resolveOwnerOrLocal` (round-3 design §5). */ +export const OWNER_OR_LOCAL_ACCEPTANCE: Record = { 'focused | global-floating-terminal': 'self@local', 'focused | local-repo::wt': 'self@local', 'focused | legacy-repo::wt': 'self@local', diff --git a/src/renderer/src/lib/resolve-owner-acceptance.test.ts b/src/renderer/src/lib/resolve-owner-acceptance.test.ts index 4678ac46eda..4a5fd92a990 100644 --- a/src/renderer/src/lib/resolve-owner-acceptance.test.ts +++ b/src/renderer/src/lib/resolve-owner-acceptance.test.ts @@ -1,9 +1,9 @@ import { describe, expect, it } from 'vitest' import type { ExecutionHostOwnerMatch } from '../../../shared/worktree-execution-host-resolution' import type { HostAuthority } from '../../../shared/host-authority' -import { resolveOwnerWithLegacyFallback } from './worktree-runtime-owner' +import { resolveOwnerOrLocal } from './worktree-runtime-owner' import { OWNER_RESOLUTION_CASES } from './runtime-owner-alias-matrix.test-fixture' -import { LEGACY_FALLBACK_OWNER_ACCEPTANCE } from './resolve-owner-acceptance.test-fixture' +import { OWNER_OR_LOCAL_ACCEPTANCE } from './resolve-owner-acceptance.test-fixture' import { LEGACY_RUNTIME_OWNER_GOLDEN } from './legacy-runtime-owner-answers.test-fixture' import type { WorktreeRuntimeOwnerState } from './worktree-runtime-owner-state' @@ -33,20 +33,20 @@ const DELIBERATE_CHANGES: Record = { 'folder:missing-folder': 'same' } -describe('resolveOwnerWithLegacyFallback acceptance (round-3 design §5)', () => { +describe('resolveOwnerOrLocal acceptance (round-3 design §5)', () => { it('pins the owner of every matrix case', () => { const actual = Object.fromEntries( casesWithIds.map((c) => [ c.name, - describeMatch(resolveOwnerWithLegacyFallback(c.state, { workspaceId: c.worktreeId })) + describeMatch(resolveOwnerOrLocal(c.state, { workspaceId: c.worktreeId })) ]) ) - expect(actual).toEqual(LEGACY_FALLBACK_OWNER_ACCEPTANCE) + expect(actual).toEqual(OWNER_OR_LOCAL_ACCEPTANCE) }) it('differs from the deprecated resolver only in the deliberate cases', () => { const changed = casesWithIds.filter((c) => { - const owner = resolveOwnerWithLegacyFallback(c.state, { workspaceId: c.worktreeId }) + const owner = resolveOwnerOrLocal(c.state, { workspaceId: c.worktreeId }) const environmentId = owner.kind === 'resolved' && owner.owner.endpoint.kind === 'environment' ? owner.owner.endpoint.environmentId @@ -64,13 +64,13 @@ describe('resolveOwnerWithLegacyFallback acceptance (round-3 design §5)', () => }) }) -describe('resolveOwnerWithLegacyFallback probes', () => { +describe('resolveOwnerOrLocal probes', () => { const base = OWNER_RESOLUTION_CASES.find((c) => c.name === 'focused | stale-hub-repo::wt')!.state const single = OWNER_RESOLUTION_CASES.find( (c) => c.name === 'single focus | legacy-repo::wt' )!.state const ownerOf = (state: WorktreeRuntimeOwnerState, workspaceId: string, hostId?: 'local') => - describeMatch(resolveOwnerWithLegacyFallback(state, { workspaceId, hostId })) + describeMatch(resolveOwnerOrLocal(state, { workspaceId, hostId })) it('reads a genuine twin as ambiguous', () => { const withDirect = { diff --git a/src/renderer/src/lib/structured-agent-session-tab-activation.test.ts b/src/renderer/src/lib/structured-agent-session-tab-activation.test.ts index 6cc3a85abb6..d07effc7414 100644 --- a/src/renderer/src/lib/structured-agent-session-tab-activation.test.ts +++ b/src/renderer/src/lib/structured-agent-session-tab-activation.test.ts @@ -19,7 +19,7 @@ vi.mock('@/components/native-chat/use-native-chat-composer-reveal-focus', () => })) vi.mock('./worktree-runtime-owner', () => ({ - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () => 'env-1' + runtimeEnvironmentIdOrLocalUnlessTwin: () => 'env-1' })) vi.mock('@/runtime/runtime-rpc-client', () => ({ callRuntimeRpc: mocks.callRuntimeRpc })) diff --git a/src/renderer/src/lib/structured-agent-session-tab-activation.ts b/src/renderer/src/lib/structured-agent-session-tab-activation.ts index c14d0525e05..317e37a7a63 100644 --- a/src/renderer/src/lib/structured-agent-session-tab-activation.ts +++ b/src/renderer/src/lib/structured-agent-session-tab-activation.ts @@ -1,4 +1,4 @@ -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from './worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from './worktree-runtime-owner' import { requestNativeChatComposerFocus } from '@/components/native-chat/use-native-chat-composer-reveal-focus' import { useAppStore } from '@/store' import { callRuntimeRpc } from '@/runtime/runtime-rpc-client' @@ -49,7 +49,7 @@ export function activateStructuredAgentSessionTab(args: { state.activateTab(tab.id, { worktreeId: args.worktreeId }) state.setActiveTabType('agent-session', args.worktreeId) requestNativeChatComposerFocus(tab.id) - const environmentId = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, args.worktreeId) + const environmentId = runtimeEnvironmentIdOrLocalUnlessTwin(state, args.worktreeId) void callRuntimeRpc( args.target ?? runtimeTargetForOwnerEnvironment(environmentId), 'session.tabs.activate', diff --git a/src/renderer/src/lib/tab-number-shortcuts.ts b/src/renderer/src/lib/tab-number-shortcuts.ts index 17562c906cf..75905046bd4 100644 --- a/src/renderer/src/lib/tab-number-shortcuts.ts +++ b/src/renderer/src/lib/tab-number-shortcuts.ts @@ -1,7 +1,7 @@ import { focusTerminalTabSurface } from '@/lib/focus-terminal-tab-surface' import { useAppStore } from '@/store' import type { AppState } from '@/store/types' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { dedupeTabOrder } from '../../../shared/workspace-layout/tab-order' import type { Tab } from '../../../shared/tab-types' import { activateWebRuntimeSessionTab } from '@/runtime/web-runtime-session-tab-lifecycle' @@ -60,7 +60,7 @@ export function activateTabNumberShortcut(index: number): boolean { } const worktreeId = target.worktreeId - const runtimeEnvironmentId = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(store, worktreeId) + const runtimeEnvironmentId = runtimeEnvironmentIdOrLocalUnlessTwin(store, worktreeId) store.focusGroup(worktreeId, target.groupId) store.activateTab(target.id) diff --git a/src/renderer/src/lib/terminal-tab-owner-activation.test.ts b/src/renderer/src/lib/terminal-tab-owner-activation.test.ts index efe26bb3943..269d85be365 100644 --- a/src/renderer/src/lib/terminal-tab-owner-activation.test.ts +++ b/src/renderer/src/lib/terminal-tab-owner-activation.test.ts @@ -2,14 +2,13 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' const mocks = vi.hoisted(() => ({ activateWebRuntimeSessionTab: vi.fn(), - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: vi.fn(), + runtimeEnvironmentIdOrLocalUnlessTwin: vi.fn(), resolveWebSessionVisibleTabId: vi.fn() })) vi.mock('@/store', () => ({ useAppStore: { getState: () => ({}) } })) vi.mock('@/lib/worktree-runtime-owner', () => ({ - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: - mocks.runtimeEnvironmentIdWithLegacyFallbackUnlessTwin + runtimeEnvironmentIdOrLocalUnlessTwin: mocks.runtimeEnvironmentIdOrLocalUnlessTwin })) vi.mock('@/runtime/web-runtime-session-tab-lifecycle', () => ({ activateWebRuntimeSessionTab: mocks.activateWebRuntimeSessionTab @@ -31,12 +30,12 @@ describe('activateTerminalTabOnOwner', () => { }) it('tells the paired server that owns the workspace, not the focused one', () => { - mocks.runtimeEnvironmentIdWithLegacyFallbackUnlessTwin.mockReturnValue('env-owner') + mocks.runtimeEnvironmentIdOrLocalUnlessTwin.mockReturnValue('env-owner') mocks.resolveWebSessionVisibleTabId.mockReturnValue('unified-visible') activateTerminalTabOnOwner('wt-1', 'web-terminal-host-tab', 'leaf-b') - expect(mocks.runtimeEnvironmentIdWithLegacyFallbackUnlessTwin).toHaveBeenCalledWith({}, 'wt-1') + expect(mocks.runtimeEnvironmentIdOrLocalUnlessTwin).toHaveBeenCalledWith({}, 'wt-1') expect(mocks.activateWebRuntimeSessionTab).toHaveBeenCalledWith({ worktreeId: 'wt-1', tabId: 'web-terminal-host-tab', @@ -48,7 +47,7 @@ describe('activateTerminalTabOnOwner', () => { }) it('makes no owner round trip for a workspace this client runs', () => { - mocks.runtimeEnvironmentIdWithLegacyFallbackUnlessTwin.mockReturnValue(null) + mocks.runtimeEnvironmentIdOrLocalUnlessTwin.mockReturnValue(null) activateTerminalTabOnOwner('wt-local', 'tab-1', null) diff --git a/src/renderer/src/lib/terminal-tab-owner-activation.ts b/src/renderer/src/lib/terminal-tab-owner-activation.ts index 5bf816fb685..bef6a384018 100644 --- a/src/renderer/src/lib/terminal-tab-owner-activation.ts +++ b/src/renderer/src/lib/terminal-tab-owner-activation.ts @@ -1,5 +1,5 @@ import { useAppStore } from '@/store' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { activateWebRuntimeSessionTab } from '@/runtime/web-runtime-session-tab-lifecycle' import { isWebRuntimeSessionActive } from '@/runtime/web-runtime-session-environment' import { resolveWebSessionVisibleTabId } from '@/runtime/web-session-focus-intent' @@ -17,7 +17,7 @@ export function activateTerminalTabOnOwner( leafId: string | null ): void { const state = useAppStore.getState() - const environmentId = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, worktreeId) + const environmentId = runtimeEnvironmentIdOrLocalUnlessTwin(state, worktreeId) if (!environmentId || !isWebRuntimeSessionActive(environmentId)) { return } diff --git a/src/renderer/src/lib/terminal-worktree-route.ts b/src/renderer/src/lib/terminal-worktree-route.ts index 53160cd7de9..09e81e06a16 100644 --- a/src/renderer/src/lib/terminal-worktree-route.ts +++ b/src/renderer/src/lib/terminal-worktree-route.ts @@ -3,7 +3,7 @@ import { isEphemeralSetupTerminalWorktreeId } from '../../../shared/ephemeral-se import { parseExecutionHostId } from '../../../shared/execution-host' import { parseWorkspaceKey } from '../../../shared/workspace-scope' import type { AppState } from '@/store/types' -import { resolveOwnerWithLegacyFallback } from './worktree-runtime-owner' +import { resolveOwnerOrLocal } from './worktree-runtime-owner' import { namedHostForOwnerRef, runtimeEnvironmentIdUnlessTwin } from './resolve-owner' import type { WorktreeRuntimeOwnerState } from './worktree-runtime-owner-state' import { @@ -69,7 +69,7 @@ export function resolveTerminalHostOwnership( worktreeId === FLOATING_TERMINAL_WORKTREE_ID || parseWorkspaceKey(worktreeId)?.type === 'folder' ) { - const owner = resolveOwnerWithLegacyFallback(state, { workspaceId: worktreeId }) + const owner = resolveOwnerOrLocal(state, { workspaceId: worktreeId }) if (owner.kind !== 'resolved') { // Why: spawn must not pick a twin; a twin's runtime PTYs carry `remote:` ids, so a raw id is // local or SSH on either copy. diff --git a/src/renderer/src/lib/web-runtime-worktree-terminal-after-wake.ts b/src/renderer/src/lib/web-runtime-worktree-terminal-after-wake.ts index ec3652b8bf7..173ca689f45 100644 --- a/src/renderer/src/lib/web-runtime-worktree-terminal-after-wake.ts +++ b/src/renderer/src/lib/web-runtime-worktree-terminal-after-wake.ts @@ -8,7 +8,7 @@ import { beginWebRuntimeWakeTerminalRespawn, endWebRuntimeWakeTerminalRespawn } from '@/runtime/web-runtime-wake-terminal-respawn' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import type { WorktreeStartupPayload } from '@/lib/worktree-startup-payload' import type { TuiAgent } from '../../../shared/tui-agent' import { toast } from 'sonner' @@ -35,7 +35,7 @@ export function ensureWebRuntimeWorktreeTerminalAfterWake( const runtimeEnvironmentId = opts && 'runtimeEnvironmentId' in opts ? (opts.runtimeEnvironmentId ?? null) - : runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, worktreeId) + : runtimeEnvironmentIdOrLocalUnlessTwin(state, worktreeId) if (!runtimeEnvironmentId || !isWebRuntimeSessionActive(runtimeEnvironmentId)) { return } diff --git a/src/renderer/src/lib/workspace-file-browser-open-target.test.ts b/src/renderer/src/lib/workspace-file-browser-open-target.test.ts index 254e4a1a391..76b7d4f2b78 100644 --- a/src/renderer/src/lib/workspace-file-browser-open-target.test.ts +++ b/src/renderer/src/lib/workspace-file-browser-open-target.test.ts @@ -13,7 +13,7 @@ const mocks = vi.hoisted(() => ({ })) vi.mock('@/lib/worktree-runtime-owner', () => ({ - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () => mocks.environmentId + runtimeEnvironmentIdOrLocalUnlessTwin: () => mocks.environmentId })) vi.mock('@/store', () => ({ diff --git a/src/renderer/src/lib/workspace-file-browser-open-target.ts b/src/renderer/src/lib/workspace-file-browser-open-target.ts index 5cf848dd31d..655b8e970c9 100644 --- a/src/renderer/src/lib/workspace-file-browser-open-target.ts +++ b/src/renderer/src/lib/workspace-file-browser-open-target.ts @@ -2,7 +2,7 @@ import { absolutePathToFileUri } from '@/components/editor/markdown-internal-lin import { getConnectionIdForFile } from '@/lib/connection-context' import { basename } from '@/lib/path' import { isTwinWorkspace } from '@/lib/resolve-owner' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { useAppStore } from '@/store' /** Still the answer for flows that need a real `file://` URL (e.g. dropping a file on a browser pane). */ @@ -39,7 +39,7 @@ export function getWorkspaceFileBrowserOpenTarget(params: { } if ( getConnectionIdForFile(params.worktreeId, params.filePath) !== null || - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, params.worktreeId) + runtimeEnvironmentIdOrLocalUnlessTwin(state, params.worktreeId) ) { // Why: Chromium resolves file:// URLs on the local machine. Remote files // need an Orca-served URL before the browser can render them correctly. diff --git a/src/renderer/src/lib/workspace-tab-palette-activation.test.ts b/src/renderer/src/lib/workspace-tab-palette-activation.test.ts index 1c98f3f261b..58135459fd5 100644 --- a/src/renderer/src/lib/workspace-tab-palette-activation.test.ts +++ b/src/renderer/src/lib/workspace-tab-palette-activation.test.ts @@ -63,7 +63,7 @@ const mocks = vi.hoisted(() => { activateAndRevealWorkspace: vi.fn(), activateWebRuntimeSessionTab: vi.fn(), focusTerminalTabSurface: vi.fn(), - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: vi.fn(), + runtimeEnvironmentIdOrLocalUnlessTwin: vi.fn(), isWebRuntimeSessionActive: vi.fn() } }) @@ -79,8 +79,7 @@ vi.mock('@/lib/focus-terminal-tab-surface', () => ({ })) vi.mock('@/lib/worktree-runtime-owner', () => ({ - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: - mocks.runtimeEnvironmentIdWithLegacyFallbackUnlessTwin + runtimeEnvironmentIdOrLocalUnlessTwin: mocks.runtimeEnvironmentIdOrLocalUnlessTwin })) vi.mock('@/runtime/web-runtime-session-tab-lifecycle', () => ({ @@ -173,7 +172,7 @@ describe('activateWorkspaceTabPaletteResult', () => { .find((worktree) => worktree.id === worktreeId) ) mocks.activateAndRevealWorkspace.mockReturnValue(true) - mocks.runtimeEnvironmentIdWithLegacyFallbackUnlessTwin.mockReturnValue('runtime-1') + mocks.runtimeEnvironmentIdOrLocalUnlessTwin.mockReturnValue('runtime-1') mocks.isWebRuntimeSessionActive.mockReturnValue(false) }) diff --git a/src/renderer/src/lib/workspace-tab-palette-activation.ts b/src/renderer/src/lib/workspace-tab-palette-activation.ts index 9a3dc6d92fd..43ca1bccb5a 100644 --- a/src/renderer/src/lib/workspace-tab-palette-activation.ts +++ b/src/renderer/src/lib/workspace-tab-palette-activation.ts @@ -1,5 +1,5 @@ import { focusTerminalTabSurface } from '@/lib/focus-terminal-tab-surface' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { activateWebRuntimeSessionTab } from '@/runtime/web-runtime-session-tab-lifecycle' import { isWebRuntimeSessionActive } from '@/runtime/web-runtime-session-environment' import { useAppStore } from '@/store' @@ -119,10 +119,7 @@ export function activateWorkspaceTabPaletteResult( return { status: 'failed', reason: finalFailure } } - const runtimeEnvironmentId = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin( - state, - result.worktreeId - ) + const runtimeEnvironmentId = runtimeEnvironmentIdOrLocalUnlessTwin(state, result.worktreeId) state.focusGroup(result.worktreeId, result.groupId) state.activateTab(result.tabId, { worktreeId: result.worktreeId }) diff --git a/src/renderer/src/lib/worktree-activation-pty-inventory.ts b/src/renderer/src/lib/worktree-activation-pty-inventory.ts index a91a78fa24f..2018f8578a1 100644 --- a/src/renderer/src/lib/worktree-activation-pty-inventory.ts +++ b/src/renderer/src/lib/worktree-activation-pty-inventory.ts @@ -2,7 +2,7 @@ import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../shared/constants' import { parseExecutionHostId } from '../../../shared/execution-host' import type { PtyListedSession, PtySessionListScope } from '../../../shared/pty-listed-session' import { getRepoIdFromWorktreeId } from '../../../shared/worktree/id' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from './worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from './worktree-runtime-owner' import { resolveIndexedRepoOwner, resolveIndexedWorktreeOwner @@ -21,10 +21,7 @@ export function resolveActivationPtyListScope( return { connectionId: null } } const resolution = resolveWorktreeOperationRouteResult(state, worktreeId) - if ( - resolution.kind === 'missing' && - !runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, worktreeId) - ) { + if (resolution.kind === 'missing' && !runtimeEnvironmentIdOrLocalUnlessTwin(state, worktreeId)) { const repo = resolveIndexedRepoOwner(state.repos, getRepoIdFromWorktreeId(worktreeId)) const worktree = resolveIndexedWorktreeOwner(state.worktreesByRepo, worktreeId) // A known native repo remains usable while the unrelated runtime catalog hydrates. diff --git a/src/renderer/src/lib/worktree-activation.ts b/src/renderer/src/lib/worktree-activation.ts index f611e66ed5b..83eef2b096f 100644 --- a/src/renderer/src/lib/worktree-activation.ts +++ b/src/renderer/src/lib/worktree-activation.ts @@ -7,7 +7,7 @@ import { registerWorktreeActivation } from '@/lib/worktree-activation-nav-regist import { workspaceHasSleepingAgentSessions } from '@/lib/worktree-agent-activation-gate' import { resumeSleepingAgentSessionsForWorktree } from '@/lib/resume-sleeping-agent-session' import { shouldAutoCreateInitialTerminal } from '@/components/terminal/initial-terminal' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { folderWorkspaceKey, parseWorkspaceKey } from '../../../shared/workspace-scope' import { folderWorkspaceActivationBlocked, @@ -211,7 +211,7 @@ export function activateAndRevealWorktree( // 3. Core activation: setActiveWorktree also restores per-worktree state, clears unread, bumps dead PTY generations, refreshes GitHub state.setActiveWorktree(worktreeId, opts?.executionHostId) const postActivationState = useAppStore.getState() - const ownerRuntimeEnvironmentId = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin( + const ownerRuntimeEnvironmentId = runtimeEnvironmentIdOrLocalUnlessTwin( postActivationState, wt.id ) diff --git a/src/renderer/src/lib/worktree-creation-agent-seeds.ts b/src/renderer/src/lib/worktree-creation-agent-seeds.ts index 81e7549c132..0ae0aab02eb 100644 --- a/src/renderer/src/lib/worktree-creation-agent-seeds.ts +++ b/src/renderer/src/lib/worktree-creation-agent-seeds.ts @@ -2,7 +2,7 @@ import { useAppStore } from '@/store' import { seedNativeChatAppliedSessionOptions } from '@/components/native-chat/native-chat-session-option-cache' import { seedNativeChatLaunchDraftForAgentTab } from '@/lib/agent-launch-prompt-delivery' import { queueHookCommandsForFirstWorktreeTab } from '@/lib/hook-command-delayed-delivery' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { toWebTerminalSurfaceTabId } from '../../../shared/terminal-surface-id' import type { WorktreeCreationRequest } from '@/lib/pending-worktree-creation' import type { TuiAgent } from '../../../shared/tui-agent' @@ -31,7 +31,7 @@ function resolveLaunchAgentTabId( const worktreeTabs = state.tabsByWorktree[args.worktreeId] ?? [] // Main spawned the agent itself, so its startup tab is authoritative. if (args.backendSpawned && args.startupTerminalTabId) { - return runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, args.worktreeId) + return runtimeEnvironmentIdOrLocalUnlessTwin(state, args.worktreeId) ? toWebTerminalSurfaceTabId(args.startupTerminalTabId) : args.startupTerminalTabId } diff --git a/src/renderer/src/lib/worktree-reactivation-preserved-pane-replacement.test.ts b/src/renderer/src/lib/worktree-reactivation-preserved-pane-replacement.test.ts index bbc135af0d1..41cd31c2f1f 100644 --- a/src/renderer/src/lib/worktree-reactivation-preserved-pane-replacement.test.ts +++ b/src/renderer/src/lib/worktree-reactivation-preserved-pane-replacement.test.ts @@ -7,7 +7,7 @@ import { waitForWorktreeAgentActivationGateForTests } from './worktree-agent-act import { makeCreatedAgentWorktree as makeWorktree } from '@/lib/worktree-activation-created-agent.test-fixture' import { makePaneKey } from '../../../shared/stable-pane-id' import type { ExecutionHostId } from '../../../shared/execution-host' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from './worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from './worktree-runtime-owner' import { markHostSessionMirrorHydrated, resetHostSessionMirrorHydrationForTests @@ -331,9 +331,9 @@ describe('preserved-pane replacement contract on workspace activation', () => { } })) - expect( - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(useAppStore.getState(), worktree.id) - ).toBe(RUNTIME_ENV_ID) + expect(runtimeEnvironmentIdOrLocalUnlessTwin(useAppStore.getState(), worktree.id)).toBe( + RUNTIME_ENV_ID + ) activateAndRevealWorktree(worktree.id, { notifyHostRuntime: false }) await waitForWorktreeAgentActivationGateForTests(worktree.id) diff --git a/src/renderer/src/lib/worktree-row-open-in-owner.ts b/src/renderer/src/lib/worktree-row-open-in-owner.ts index 8c95361f0a1..30cdce3589f 100644 --- a/src/renderer/src/lib/worktree-row-open-in-owner.ts +++ b/src/renderer/src/lib/worktree-row-open-in-owner.ts @@ -1,6 +1,6 @@ import { parseExecutionHostId } from '../../../shared/execution-host' import type { Worktree } from '../../../shared/worktree/types' -import { resolveOwnerWithLegacyFallback } from './worktree-runtime-owner' +import { resolveOwnerOrLocal } from './worktree-runtime-owner' import type { WorktreeRuntimeOwnerState } from './worktree-runtime-owner-state' import { resolveWorktreeOperationRouteResultForHost, @@ -46,7 +46,7 @@ export function resolveWorktreeRowOpenInRuntimeOwner( } if (!worktree.hostId) { // Why: a twin names no owner, and Open in must not launch it as this desktop's checkout. - const owner = resolveOwnerWithLegacyFallback(state, { workspaceId: worktree.id }) + const owner = resolveOwnerOrLocal(state, { workspaceId: worktree.id }) if (owner.kind === 'ambiguous') { return UNRESOLVED } diff --git a/src/renderer/src/lib/worktree-runtime-owner.test.ts b/src/renderer/src/lib/worktree-runtime-owner.test.ts index 4c614d22185..c93a5a27181 100644 --- a/src/renderer/src/lib/worktree-runtime-owner.test.ts +++ b/src/renderer/src/lib/worktree-runtime-owner.test.ts @@ -3,7 +3,7 @@ import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../shared/constants' import { getExecutionHostIdForWorktree, getKnownExecutionHostIdForWorktree, - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin + runtimeEnvironmentIdOrLocalUnlessTwin } from './worktree-runtime-owner' import { resolveTerminalPtyOwner } from './terminal-worktree-route' import { getRuntimeSessionMirrorEnvironmentIds } from './runtime-session-mirror-owners' @@ -44,15 +44,13 @@ const state: WorktreeRuntimeOwnerState = { ] } -describe('runtimeEnvironmentIdWithLegacyFallbackUnlessTwin', () => { +describe('runtimeEnvironmentIdOrLocalUnlessTwin', () => { it('routes to the runtime owner of the worktree', () => { - expect(runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, 'runtime-repo::wt-b')).toBe( - 'owner-env' - ) + expect(runtimeEnvironmentIdOrLocalUnlessTwin(state, 'runtime-repo::wt-b')).toBe('owner-env') }) it('keeps explicit-local worktrees local even while a runtime is focused', () => { - expect(runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, 'local-repo::wt-a')).toBeNull() + expect(runtimeEnvironmentIdOrLocalUnlessTwin(state, 'local-repo::wt-a')).toBeNull() }) it('keeps the synthetic floating workspace local while a runtime is focused', () => { @@ -62,7 +60,7 @@ describe('runtimeEnvironmentIdWithLegacyFallbackUnlessTwin', () => { activeWorkspaceExecutionHostId: 'runtime:focused-env' } expect( - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(floatingState, FLOATING_TERMINAL_WORKTREE_ID) + runtimeEnvironmentIdOrLocalUnlessTwin(floatingState, FLOATING_TERMINAL_WORKTREE_ID) ).toBeNull() expect(getKnownExecutionHostIdForWorktree(floatingState, FLOATING_TERMINAL_WORKTREE_ID)).toBe( 'local' @@ -73,9 +71,7 @@ describe('runtimeEnvironmentIdWithLegacyFallbackUnlessTwin', () => { }) it('routes folder workspaces to their project group runtime owner', () => { - expect(runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, 'folder:runtime-folder')).toBe( - 'folder-env' - ) + expect(runtimeEnvironmentIdOrLocalUnlessTwin(state, 'folder:runtime-folder')).toBe('folder-env') expect(getExecutionHostIdForWorktree(state, 'folder:runtime-folder')).toBe('runtime:folder-env') }) @@ -111,10 +107,7 @@ describe('runtimeEnvironmentIdWithLegacyFallbackUnlessTwin', () => { } expect( - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin( - restoredFolderState, - 'folder:restored-folder' - ) + runtimeEnvironmentIdOrLocalUnlessTwin(restoredFolderState, 'folder:restored-folder') ).toBe('restored-env') expect(ownerEnvironmentId(restoredFolderState, 'folder:restored-folder')).toBe('restored-env') expect(getExecutionHostIdForWorktree(restoredFolderState, 'folder:restored-folder')).toBe( @@ -123,9 +116,7 @@ describe('runtimeEnvironmentIdWithLegacyFallbackUnlessTwin', () => { }) it('keeps explicit-local folder workspaces local even while a runtime is focused', () => { - expect( - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, 'folder:local-folder') - ).toBeNull() + expect(runtimeEnvironmentIdOrLocalUnlessTwin(state, 'folder:local-folder')).toBeNull() expect(getExecutionHostIdForWorktree(state, 'folder:local-folder')).toBe('local') const restoredOwnerState: WorktreeRuntimeOwnerState = { @@ -135,7 +126,7 @@ describe('runtimeEnvironmentIdWithLegacyFallbackUnlessTwin', () => { } } expect( - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(restoredOwnerState, 'folder:local-folder') + runtimeEnvironmentIdOrLocalUnlessTwin(restoredOwnerState, 'folder:local-folder') ).toBeNull() expect(getExecutionHostIdForWorktree(restoredOwnerState, 'folder:local-folder')).toBe('local') }) @@ -150,7 +141,7 @@ describe('runtimeEnvironmentIdWithLegacyFallbackUnlessTwin', () => { } expect( - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(folderConnectionState, 'folder:folder-ssh') + runtimeEnvironmentIdOrLocalUnlessTwin(folderConnectionState, 'folder:folder-ssh') ).toBeNull() expect(getExecutionHostIdForWorktree(folderConnectionState, 'folder:folder-ssh')).toBe( 'ssh:folder-remote' @@ -166,10 +157,7 @@ describe('runtimeEnvironmentIdWithLegacyFallbackUnlessTwin', () => { } expect( - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin( - staleFolderConnectionState, - 'folder:runtime-folder' - ) + runtimeEnvironmentIdOrLocalUnlessTwin(staleFolderConnectionState, 'folder:runtime-folder') ).toBe('folder-env') expect(getExecutionHostIdForWorktree(staleFolderConnectionState, 'folder:runtime-folder')).toBe( 'runtime:folder-env' @@ -200,7 +188,7 @@ describe('resolveTerminalPtyOwner', () => { kind: 'ambiguous' }) // Why: read-only callers keep the old collapse to "no server". - expect(runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(twinState, 'twin-repo::wt')).toBeNull() + expect(runtimeEnvironmentIdOrLocalUnlessTwin(twinState, 'twin-repo::wt')).toBeNull() }) }) @@ -228,9 +216,9 @@ describe('the explicit owner environment (resolveOwner)', () => { } expect(ownerEnvironmentId(nestedState, 'nested-repo::remote-worktree')).toBe('owner-hub') - expect( - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(nestedState, 'nested-repo::remote-worktree') - ).toBe('owner-hub') + expect(runtimeEnvironmentIdOrLocalUnlessTwin(nestedState, 'nested-repo::remote-worktree')).toBe( + 'owner-hub' + ) expect(getExecutionHostIdForWorktree(nestedState, 'nested-repo::remote-worktree')).toBe( 'ssh:hub-private-ssh' ) @@ -349,12 +337,12 @@ describe('the explicit owner environment (resolveOwner)', () => { } } - expect( - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(multiHubState, 'repo-a::ssh-worktree') - ).toBe('hub-a') - expect( - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(multiHubState, 'repo-b::ssh-worktree') - ).toBe('hub-b') + expect(runtimeEnvironmentIdOrLocalUnlessTwin(multiHubState, 'repo-a::ssh-worktree')).toBe( + 'hub-a' + ) + expect(runtimeEnvironmentIdOrLocalUnlessTwin(multiHubState, 'repo-b::ssh-worktree')).toBe( + 'hub-b' + ) expect(getExecutionHostIdForWorktree(multiHubState, 'repo-a::ssh-worktree')).toBe( 'ssh:private-a' ) @@ -364,9 +352,7 @@ describe('the explicit owner environment (resolveOwner)', () => { }) it('does not treat the focused runtime as ownership for legacy-local worktrees', () => { - expect( - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, 'legacy-repo::wt-legacy') - ).toBeNull() + expect(runtimeEnvironmentIdOrLocalUnlessTwin(state, 'legacy-repo::wt-legacy')).toBeNull() expect(ownerEnvironmentId(state, 'legacy-repo::wt-legacy')).toBeNull() expect(getExecutionHostIdForWorktree(state, 'legacy-repo::wt-legacy')).toBe('local') expect(getExecutionHostIdForWorktree(state, 'unknown-repo::wt')).toBe('local') @@ -401,10 +387,7 @@ describe('the explicit owner environment (resolveOwner)', () => { expect(ownerEnvironmentId(hostOverrideState, 'runtime-repo::wt-local-override')).toBeNull() expect( - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin( - hostOverrideState, - 'runtime-repo::wt-local-override' - ) + runtimeEnvironmentIdOrLocalUnlessTwin(hostOverrideState, 'runtime-repo::wt-local-override') ).toBeNull() expect( getExecutionHostIdForWorktree(hostOverrideState, 'runtime-repo::wt-local-override') @@ -413,10 +396,7 @@ describe('the explicit owner environment (resolveOwner)', () => { 'worktree-env' ) expect( - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin( - hostOverrideState, - 'runtime-repo::wt-runtime-override' - ) + runtimeEnvironmentIdOrLocalUnlessTwin(hostOverrideState, 'runtime-repo::wt-runtime-override') ).toBe('worktree-env') expect( getExecutionHostIdForWorktree(hostOverrideState, 'runtime-repo::wt-runtime-override') @@ -503,13 +483,10 @@ describe('runtime owner identity indexes', () => { } for (let lookup = 0; lookup < 200; lookup += 1) { - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(indexedState, targetWorktreeId) + runtimeEnvironmentIdOrLocalUnlessTwin(indexedState, targetWorktreeId) ownerEnvironmentId(indexedState, targetWorktreeId) getExecutionHostIdForWorktree(indexedState, targetWorktreeId) - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin( - indexedState, - `folder:${targetFolderWorkspaceId}` - ) + runtimeEnvironmentIdOrLocalUnlessTwin(indexedState, `folder:${targetFolderWorkspaceId}`) ownerEnvironmentId(indexedState, `folder:${targetFolderWorkspaceId}`) getExecutionHostIdForWorktree(indexedState, `folder:${targetFolderWorkspaceId}`) } @@ -519,9 +496,7 @@ describe('runtime owner identity indexes', () => { expect(repoIdReads).toBe(200) expect(folderWorkspaceIdReads).toBe(1_000) expect(projectGroupIdReads).toBe(1_000) - expect(runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(indexedState, targetWorktreeId)).toBe( - 'repo-owner' - ) + expect(runtimeEnvironmentIdOrLocalUnlessTwin(indexedState, targetWorktreeId)).toBe('repo-owner') expect(getExecutionHostIdForWorktree(indexedState, `folder:${targetFolderWorkspaceId}`)).toBe( 'runtime:folder-owner' ) @@ -534,10 +509,8 @@ describe('runtime owner identity indexes', () => { folderWorkspaces: [{ id: 'folder', projectGroupId: 'group' }], projectGroups: [{ id: 'group', connectionId: null, executionHostId: 'runtime:folder-one' }] } - expect(runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(first, 'worktree')).toBe('repo-one') - expect(runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(first, 'folder:folder')).toBe( - 'folder-one' - ) + expect(runtimeEnvironmentIdOrLocalUnlessTwin(first, 'worktree')).toBe('repo-one') + expect(runtimeEnvironmentIdOrLocalUnlessTwin(first, 'folder:folder')).toBe('folder-one') const second: WorktreeRuntimeOwnerState = { repos: [{ id: 'repo', connectionId: null, executionHostId: 'runtime:repo-two' }], @@ -547,12 +520,8 @@ describe('runtime owner identity indexes', () => { folderWorkspaces: [{ id: 'folder', projectGroupId: 'group' }], projectGroups: [{ id: 'group', connectionId: null, executionHostId: 'runtime:folder-two' }] } - expect(runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(second, 'worktree')).toBe( - 'worktree-two' - ) - expect(runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(second, 'folder:folder')).toBe( - 'folder-two' - ) + expect(runtimeEnvironmentIdOrLocalUnlessTwin(second, 'worktree')).toBe('worktree-two') + expect(runtimeEnvironmentIdOrLocalUnlessTwin(second, 'folder:folder')).toBe('folder-two') }) }) @@ -654,9 +623,9 @@ describe('active workspace host selection', () => { } it('keeps the HUB transport for the active paired SSH worktree', () => { - expect( - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(pairedHubState, PAIRED_HUB_WORKTREE_ID) - ).toBe('hub-a') + expect(runtimeEnvironmentIdOrLocalUnlessTwin(pairedHubState, PAIRED_HUB_WORKTREE_ID)).toBe( + 'hub-a' + ) expect(ownerEnvironmentId(pairedHubState, PAIRED_HUB_WORKTREE_ID)).toBe('hub-a') }) diff --git a/src/renderer/src/lib/worktree-runtime-owner.ts b/src/renderer/src/lib/worktree-runtime-owner.ts index 39810bbe26a..7ab24f74c5b 100644 --- a/src/renderer/src/lib/worktree-runtime-owner.ts +++ b/src/renderer/src/lib/worktree-runtime-owner.ts @@ -123,10 +123,10 @@ export function getExecutionHostIdForWorktree( } /** - * {@link resolveOwner}, reading an id no row places as this app's — or, for a folder, the server - * its saved session names, or a web client's only server. + * {@link resolveOwner}, except an id no row places and no caller names a host resolves to this app + * instead of `missing` — or, for a folder, to its saved session's server or a web client's only one. */ -export function resolveOwnerWithLegacyFallback( +export function resolveOwnerOrLocal( state: WorktreeRuntimeOwnerState, ref: WorkspaceOwnerRef ): ExecutionHostOwnerMatch { @@ -148,17 +148,18 @@ export function resolveOwnerWithLegacyFallback( } /** - * The paired server behind {@link resolveOwnerWithLegacyFallback}, or `null` for this app, a - * directly dialed SSH target or a twin: for readers whose twin answer is "no server", as before. + * The paired server {@link resolveOwnerOrLocal} names, or `null` for this app, a directly dialed + * SSH target or a twin. Unlike `runtimeEnvironmentIdUnlessTwin`, an unplaced folder on a web + * client names that client's server. */ -export function runtimeEnvironmentIdWithLegacyFallbackUnlessTwin( +export function runtimeEnvironmentIdOrLocalUnlessTwin( state: WorktreeRuntimeOwnerState, workspaceId: string | null | undefined ): string | null { if (!workspaceId) { return null } - const match = resolveOwnerWithLegacyFallback(state, { workspaceId }) + const match = resolveOwnerOrLocal(state, { workspaceId }) return match.kind === 'resolved' && match.owner.endpoint.kind === 'environment' ? match.owner.endpoint.environmentId : null diff --git a/src/renderer/src/lib/worktree-sort-order-host-split.ts b/src/renderer/src/lib/worktree-sort-order-host-split.ts index 0e47942364b..bbfcd046eec 100644 --- a/src/renderer/src/lib/worktree-sort-order-host-split.ts +++ b/src/renderer/src/lib/worktree-sort-order-host-split.ts @@ -1,6 +1,6 @@ import { LOCAL_EXECUTION_HOST_ID, toRuntimeExecutionHostId } from '../../../shared/execution-host' import { isUnprovenServerSshRoute } from './resolve-owner' -import { resolveOwnerWithLegacyFallback } from './worktree-runtime-owner' +import { resolveOwnerOrLocal } from './worktree-runtime-owner' import type { WorktreeRuntimeOwnerState } from './worktree-runtime-owner-state' export type WorktreeSortOrderHostGroup = { @@ -25,7 +25,7 @@ export function splitWorktreeSortOrderByHost( ): WorktreeSortOrderHostGroup[] { const groups = new Map() for (const id of orderedIds) { - const owner = resolveOwnerWithLegacyFallback(state, { workspaceId: id }) + const owner = resolveOwnerOrLocal(state, { workspaceId: id }) // Why: a twin names no host to write to, and an unproven server route may be another machine. if (owner.kind !== 'resolved' || isUnprovenServerSshRoute(state, id, owner.owner)) { continue diff --git a/src/renderer/src/runtime/web-runtime-session.test.ts b/src/renderer/src/runtime/web-runtime-session.test.ts index 1e2964eef76..f04b3abfe2c 100644 --- a/src/renderer/src/runtime/web-runtime-session.test.ts +++ b/src/renderer/src/runtime/web-runtime-session.test.ts @@ -31,7 +31,7 @@ const mocks = vi.hoisted(() => ({ trackTerminalPaneSplit: vi.fn(), deliverLaunchPromptToAgentTab: vi.fn(), seedNativeChatLaunchDraftForAgentTab: vi.fn(), - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: vi.fn(), + runtimeEnvironmentIdOrLocalUnlessTwin: vi.fn(), hasMaterializedWebRuntimeBrowserPage: vi.fn(), recoverWebSessionTerminalOrphansBeforeApply: vi.fn( async (_state: unknown, snapshot: unknown) => snapshot @@ -76,8 +76,7 @@ vi.mock('@/lib/feature-education-telemetry', () => ({ })) vi.mock('@/lib/worktree-runtime-owner', () => ({ - runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: - mocks.runtimeEnvironmentIdWithLegacyFallbackUnlessTwin + runtimeEnvironmentIdOrLocalUnlessTwin: mocks.runtimeEnvironmentIdOrLocalUnlessTwin })) vi.mock('@/lib/agent-launch-prompt-delivery', () => ({ diff --git a/src/renderer/src/store/slices/browser/browser-page-conversion-actions.ts b/src/renderer/src/store/slices/browser/browser-page-conversion-actions.ts index 857b6a85e86..59aca0e2cc1 100644 --- a/src/renderer/src/store/slices/browser/browser-page-conversion-actions.ts +++ b/src/renderer/src/store/slices/browser/browser-page-conversion-actions.ts @@ -8,7 +8,7 @@ import type { BrowserPage } from '../../../../../shared/browser-workspace-types' import { planBrowserPageConversion } from '../browser-page-conversion' import { findPage, findWorkspace, mirrorWorkspaceFromActivePage } from '../browser-page-records' import { assertManagedBrowserMaterializationAllowed } from '@/lib/client-creation-action-policy' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { closeRemoteBrowserPageInOwningEnvironment } from './browser-remote-close' import { releaseDocPreviewGrant } from '@/lib/doc-preview-grants' import { isLocalBrowserPageOwner } from './browser-host-state' @@ -37,10 +37,7 @@ export function createBrowserPageConversionActions( declaredOwnership !== undefined ? declaredOwnership : oldPageForOwnership - ? runtimeEnvironmentIdWithLegacyFallbackUnlessTwin( - get(), - oldPageForOwnership.worktreeId - ) + ? runtimeEnvironmentIdOrLocalUnlessTwin(get(), oldPageForOwnership.worktreeId) : null ) let converted: BrowserPage | null = null diff --git a/src/renderer/src/store/slices/browser/browser-tab-actions.ts b/src/renderer/src/store/slices/browser/browser-tab-actions.ts index fb57dc7f400..23cd123ed5d 100644 --- a/src/renderer/src/store/slices/browser/browser-tab-actions.ts +++ b/src/renderer/src/store/slices/browser/browser-tab-actions.ts @@ -16,7 +16,7 @@ import { findWorkspace } from '../browser-page-records' import { getBrowserSessionProfileHostId } from './browser-host-state' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { admitBrowserPageMount } from '@/components/browser-pane/host-guest/browser-page-mount-admission' import { ownsGlobalSelection } from '../../global-selection-owner' import { @@ -197,10 +197,7 @@ export function createBrowserTabActions( throw new Error(browserAvailability.reason) } const defaultUrl = state.browserDefaultUrl ?? 'about:blank' - const runtimeEnvironmentId = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin( - state, - worktreeId - ) + const runtimeEnvironmentId = runtimeEnvironmentIdOrLocalUnlessTwin(state, worktreeId) if (browserAvailability.provider === 'paired-runtime') { if (!runtimeEnvironmentId) { throw new Error('The paired runtime browser provider is unavailable.') @@ -253,10 +250,7 @@ export function createBrowserTabActions( if (browserAvailability.state !== 'enabled') { return false } - const runtimeEnvironmentId = runtimeEnvironmentIdWithLegacyFallbackUnlessTwin( - state, - worktreeId - ) + const runtimeEnvironmentId = runtimeEnvironmentIdOrLocalUnlessTwin(state, worktreeId) if (browserAvailability.provider === 'paired-runtime') { if (!runtimeEnvironmentId) { return false diff --git a/src/renderer/src/store/slices/tabs/tabs-host-mirroring.ts b/src/renderer/src/store/slices/tabs/tabs-host-mirroring.ts index b81e883e4b1..8b778eafefc 100644 --- a/src/renderer/src/store/slices/tabs/tabs-host-mirroring.ts +++ b/src/renderer/src/store/slices/tabs/tabs-host-mirroring.ts @@ -1,7 +1,7 @@ import type { AppState } from '../../types' import type { TerminalTab } from '../../../../../shared/terminal-tab-types' import { findTabAndWorktree } from '../tab-group-state' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { locateTerminalTab } from '../../terminals/terminal-tab-location' /** @@ -33,7 +33,7 @@ export function mirrorTabPinnedToHost(state: AppState, tabId: string, isPinned: if ( !found || found.tab.contentType !== 'terminal' || - !runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, found.worktreeId) + !runtimeEnvironmentIdOrLocalUnlessTwin(state, found.worktreeId) ) { return } @@ -55,7 +55,7 @@ export function mirrorTabViewModeToHost( if ( !found || found.tab.contentType !== 'terminal' || - !runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, found.worktreeId) + !runtimeEnvironmentIdOrLocalUnlessTwin(state, found.worktreeId) ) { return } diff --git a/src/renderer/src/store/terminals/terminal-active-workspace-creation.ts b/src/renderer/src/store/terminals/terminal-active-workspace-creation.ts index 7d11bc7a9f4..c1636f3cd70 100644 --- a/src/renderer/src/store/terminals/terminal-active-workspace-creation.ts +++ b/src/renderer/src/store/terminals/terminal-active-workspace-creation.ts @@ -1,7 +1,7 @@ import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../../shared/constants' import { parseWorkspaceKey } from '../../../../shared/workspace-scope' import { focusTerminalTabSurface } from '@/lib/focus-terminal-tab-surface' -import { runtimeEnvironmentIdWithLegacyFallbackUnlessTwin } from '@/lib/worktree-runtime-owner' +import { runtimeEnvironmentIdOrLocalUnlessTwin } from '@/lib/worktree-runtime-owner' import { resolveWorktreeOperationRouteResult } from '@/lib/worktree-operation-route' import { isWebClientLocation } from '@/lib/web-client-location' import type { TerminalSlice, TerminalStoreGet, TerminalStoreSet } from './terminal-state' @@ -27,7 +27,7 @@ export function createActiveWorkspaceTerminalActions( } const runtimeEnvironmentId = worktreeRoute ? worktreeRoute.route.runtimeEnvironmentId - : runtimeEnvironmentIdWithLegacyFallbackUnlessTwin(state, worktreeId) + : runtimeEnvironmentIdOrLocalUnlessTwin(state, worktreeId) if (runtimeEnvironmentId) { const { createWebRuntimeSessionTerminal } = await import('@/runtime/web-runtime-terminal-creation')