mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
refactor(renderer): name the owner adapters for what they do (resolveOwnerOrLocal) (#31626)
* refactor(renderer): name the owner adapters for what they do: resolveOwnerOrLocal, runtimeEnvironmentIdOrLocalUnlessTwin The Active Server focus fallback they were named for is gone (#31058, #31119); what is left reads an id no row places as this app's (a folder's saved-session server, a web client's only server). * docs(renderer): resolveOwnerOrLocal keeps a named host's miss; baseline note names the setup-terminal read
This commit is contained in:
@@ -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
|
||||
|
||||
+1
-1
@@ -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', () => ({
|
||||
|
||||
+1
-1
@@ -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', () => ({
|
||||
|
||||
@@ -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)
|
||||
|
||||
+2
-5
@@ -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
|
||||
|
||||
+1
-1
@@ -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 = (
|
||||
|
||||
+2
-2
@@ -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,
|
||||
|
||||
+1
-1
@@ -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 }) => {
|
||||
|
||||
@@ -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,
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ vi.mock('@/lib/connection-owner-resolution', async (importOriginal) => ({
|
||||
}))
|
||||
vi.mock('@/lib/worktree-runtime-owner', async (importOriginal) => ({
|
||||
...(await importOriginal<typeof WorktreeRuntimeOwnerModule>()),
|
||||
runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () => 'env-1'
|
||||
runtimeEnvironmentIdOrLocalUnlessTwin: () => 'env-1'
|
||||
}))
|
||||
|
||||
import { NativeChatInlineVisual } from './NativeChatInlineVisual'
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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' })
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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 })
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
|
||||
+2
-2
@@ -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<AppState['settings']>(
|
||||
() =>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
+1
-1
@@ -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) => ({
|
||||
|
||||
+2
-2
@@ -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 })
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
+1
-1
@@ -107,7 +107,7 @@ export function tabBarRuntimeModelStubs(): Record<string, () => Record<string, u
|
||||
detectedAgents: () => ({ 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 }),
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
+3
-4
@@ -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 })
|
||||
})
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ const mocks = vi.hoisted(() => ({ state: {} as Record<string, unknown> }))
|
||||
|
||||
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() }))
|
||||
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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', () => ({
|
||||
|
||||
@@ -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 } }
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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', () => ({
|
||||
|
||||
@@ -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', () => ({
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) =>
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/** Hand-reviewed owner per matrix case for `resolveOwnerWithLegacyFallback` (round-3 design §5). */
|
||||
export const LEGACY_FALLBACK_OWNER_ACCEPTANCE: Record<string, string> = {
|
||||
/** Hand-reviewed owner per matrix case for `resolveOwnerOrLocal` (round-3 design §5). */
|
||||
export const OWNER_OR_LOCAL_ACCEPTANCE: Record<string, string> = {
|
||||
'focused | global-floating-terminal': 'self@local',
|
||||
'focused | local-repo::wt': 'self@local',
|
||||
'focused | legacy-repo::wt': 'self@local',
|
||||
|
||||
@@ -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<string, string> = {
|
||||
'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 = {
|
||||
|
||||
@@ -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 }))
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ const mocks = vi.hoisted(() => ({
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/worktree-runtime-owner', () => ({
|
||||
runtimeEnvironmentIdWithLegacyFallbackUnlessTwin: () => mocks.environmentId
|
||||
runtimeEnvironmentIdOrLocalUnlessTwin: () => mocks.environmentId
|
||||
}))
|
||||
|
||||
vi.mock('@/store', () => ({
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
|
||||
|
||||
@@ -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 })
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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')
|
||||
})
|
||||
|
||||
|
||||
@@ -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<HostAuthority> {
|
||||
@@ -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
|
||||
|
||||
@@ -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<string, { orderedIds: string[]; rowHostIds: string[] }>()
|
||||
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
|
||||
|
||||
@@ -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', () => ({
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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')
|
||||
|
||||
Reference in New Issue
Block a user