diff --git a/src/main/agent-hooks/restored-status-dead-terminal-sweep.test.ts b/src/main/agent-hooks/restored-status-dead-terminal-sweep.test.ts index 682d30ad32b..6c78921cd8f 100644 --- a/src/main/agent-hooks/restored-status-dead-terminal-sweep.test.ts +++ b/src/main/agent-hooks/restored-status-dead-terminal-sweep.test.ts @@ -3,13 +3,13 @@ import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' import type { AgentSubagentSnapshot } from '../../shared/agent-status-types' +import { isLocalExecutionHost } from '../../shared/execution-host' import { makePaneKey } from '../../shared/stable-pane-id' import { toAppSshPtyId } from '../../shared/ssh-pty-id' import { AgentHookServer } from './server' import { buildBody, postHookEvent } from './server.test-fixtures' import { indexPersistedPaneKeyPtyIds, - isLocalExecutionHost, resolveAgentWorkspaceExecutionHostId, retireRestoredStatusesWithDeadTerminals } from './restored-status-dead-terminal-sweep' diff --git a/src/main/agent-hooks/restored-status-dead-terminal-sweep.ts b/src/main/agent-hooks/restored-status-dead-terminal-sweep.ts index 94472280ff9..97838602df2 100644 --- a/src/main/agent-hooks/restored-status-dead-terminal-sweep.ts +++ b/src/main/agent-hooks/restored-status-dead-terminal-sweep.ts @@ -136,7 +136,3 @@ export function resolveAgentWorkspaceExecutionHostId( const repo = deps.getRepo(getRepoIdFromWorktreeId(worktreeId)) return repo ? resolveDeclaredExecutionHost(repo) : null } - -export function isLocalExecutionHost(hostId: string | null | undefined): boolean { - return parseExecutionHostId(hostId)?.kind === 'local' -} diff --git a/src/main/startup/main-process-pty-startup.ts b/src/main/startup/main-process-pty-startup.ts index 5a639a7fa57..2e4a6dbfab7 100644 --- a/src/main/startup/main-process-pty-startup.ts +++ b/src/main/startup/main-process-pty-startup.ts @@ -1,4 +1,5 @@ import { app } from 'electron' +import { isLocalExecutionHost } from '../../shared/execution-host' import { classifyError } from '../telemetry/classify-error' import { track } from '../telemetry/client' import { getPtyIdForPaneKey } from '../ipc/pty/pane/key-state' @@ -24,7 +25,6 @@ import { agentHookServer } from '../agent-hooks/server' import { createLocalTmuxManagedPtyResolver } from '../agent-hooks/local-tmux-managed-pty' import { indexPersistedPaneKeyPtyIds, - isLocalExecutionHost, resolveAgentWorkspaceExecutionHostId, retireRestoredStatusesWithDeadTerminals } from '../agent-hooks/restored-status-dead-terminal-sweep' diff --git a/src/renderer/src/components/terminal-pane/pty-connection/agent-idle-working-handlers.ts b/src/renderer/src/components/terminal-pane/pty-connection/agent-idle-working-handlers.ts index be141d5d809..0ad6747aa16 100644 --- a/src/renderer/src/components/terminal-pane/pty-connection/agent-idle-working-handlers.ts +++ b/src/renderer/src/components/terminal-pane/pty-connection/agent-idle-working-handlers.ts @@ -3,7 +3,7 @@ import { getWorktreeMapFromState } from '@/store/selectors' import { parseWorkspaceKey } from '../../../../../shared/workspace-scope' import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../../../shared/constants' import { isEphemeralSetupTerminalWorktreeId } from '../../../../../shared/ephemeral-setup-terminal-worktree-id' -import { parseExecutionHostId } from '../../../../../shared/execution-host' +import { isLocalExecutionHost } from '../../../../../shared/execution-host' import { resolveTerminalWorktreeRoute } from '@/lib/terminal-worktree-route' import { getConnectionId } from '@/lib/connection-context' import { getRemoteRuntimePtyEnvironmentId } from '@/runtime/runtime-terminal-stream' @@ -118,7 +118,7 @@ export function installAgentIdleWorkingHandlers(session: ConnectPanePtySession): session.hostAgnosticTerminalWorktree = session.deps.worktreeId === FLOATING_TERMINAL_WORKTREE_ID || isEphemeralSetupTerminalWorktreeId(session.deps.worktreeId) - session.worktreeProvesLocalHost = parseExecutionHostId(session.worktree?.hostId)?.kind === 'local' + session.worktreeProvesLocalHost = isLocalExecutionHost(session.worktree?.hostId) session.connectionOwnerHydrating = !session.terminalOwnerUnresolved && !session.hostAgnosticTerminalWorktree && diff --git a/src/renderer/src/hooks/editor-external-watch-targets.ts b/src/renderer/src/hooks/editor-external-watch-targets.ts index 53bda088cde..e88c2605018 100644 --- a/src/renderer/src/hooks/editor-external-watch-targets.ts +++ b/src/renderer/src/hooks/editor-external-watch-targets.ts @@ -7,6 +7,7 @@ import { isLocalWindowsDesktopClient } from '@/lib/desktop-window-chrome' import { getRuntimeEnvironmentIdForWorktree } from '@/lib/worktree-runtime-owner' import { isWindowsAbsolutePathLike } from '../../../shared/cross-platform-path' import { + isLocalExecutionHost, parseExecutionHostId, toRuntimeExecutionHostId, toSshExecutionHostId, @@ -126,10 +127,6 @@ function addWatchConsumer( consumers.set(`${consumer.owner ?? ''}::${consumer.hostId ?? ''}`, consumer) } -function isLocalHostStamp(value: string | null | undefined): boolean { - return parseExecutionHostId(value)?.kind === 'local' -} - function canWatchLocalWindowsWslAliases(args: { worktreePath: string runtimeEnvironmentId: string | null @@ -150,14 +147,14 @@ function canWatchLocalWindowsWslAliases(args: { return ( !!args.repo && !args.worktree.runtimeOwnerEnvironmentId?.trim() && - isLocalHostStamp(args.worktree.hostId) && - isLocalHostStamp(args.repo.executionHostId) + isLocalExecutionHost(args.worktree.hostId) && + isLocalExecutionHost(args.repo.executionHostId) ) } return ( !!args.folderWorkspace && - isLocalHostStamp(args.folderWorkspace.executionHostId) && - isLocalHostStamp(args.projectGroup?.executionHostId) + isLocalExecutionHost(args.folderWorkspace.executionHostId) && + isLocalExecutionHost(args.projectGroup?.executionHostId) ) } diff --git a/src/renderer/src/lib/agent-session-launch-plan.ts b/src/renderer/src/lib/agent-session-launch-plan.ts index b7de4c6da88..23ee62c06da 100644 --- a/src/renderer/src/lib/agent-session-launch-plan.ts +++ b/src/renderer/src/lib/agent-session-launch-plan.ts @@ -1,6 +1,10 @@ import type { StructuredAgentSessionResumeSource } from '../../../shared/structured-agent-session-create' import type { TuiAgent } from '../../../shared/tui-agent' -import { parseExecutionHostId, type ExecutionHostId } from '../../../shared/execution-host' +import { + isLocalExecutionHost, + parseExecutionHostId, + type ExecutionHostId +} from '../../../shared/execution-host' import { STRUCTURED_AGENT_SESSION_REGISTERED_AGENTS_RUNTIME_CAPABILITY } from '../../../shared/protocol-version' import { toast } from 'sonner' import { translate } from '@/i18n/i18n' @@ -216,8 +220,7 @@ function structuredRouteAwaits( ) { return 'host-agents' } - return downgrade === 'runtime-capability-unknown' && - parseExecutionHostId(input.executionHostId)?.kind === 'local' + return downgrade === 'runtime-capability-unknown' && isLocalExecutionHost(input.executionHostId) ? 'local-capabilities' : null } @@ -245,7 +248,7 @@ export function awaitStructuredRouteHostAnswer( let capabilities = input.hostCapabilities // Either route may be missing this computer's capabilities: wait for the probe that lands // them, since one that failed at startup answers null at once. - if (capabilities === null && parseExecutionHostId(input.executionHostId)?.kind === 'local') { + if (capabilities === null && isLocalExecutionHost(input.executionHostId)) { const wait = awaitLocalRuntimeCapabilities() stopCapabilitiesWait = wait.stop capabilities = await Promise.race([wait.known, deadline]) diff --git a/src/shared/execution-host.ts b/src/shared/execution-host.ts index 409dd2986b6..604271226a5 100644 --- a/src/shared/execution-host.ts +++ b/src/shared/execution-host.ts @@ -127,6 +127,10 @@ export function parseExecutionHostId(value: string | null | undefined): ParsedEx } /** Whether a host id names another machine; an absent or unparsable id reads as this one. */ +export function isLocalExecutionHost(value: string | null | undefined): boolean { + return parseExecutionHostId(value)?.kind === 'local' +} + export function isRemoteExecutionHostId(value: string | null | undefined): boolean { return (parseExecutionHostId(value)?.kind ?? 'local') !== 'local' } diff --git a/src/shared/worktree/host-context-labels.ts b/src/shared/worktree/host-context-labels.ts index 2171f00d76a..e68bbe722a9 100644 --- a/src/shared/worktree/host-context-labels.ts +++ b/src/shared/worktree/host-context-labels.ts @@ -2,7 +2,7 @@ import { getExecutionHostLabel, getLocalExecutionHostLabel, normalizeExecutionHostId, - parseExecutionHostId, + isLocalExecutionHost, toSshExecutionHostId, type ExecutionHostId } from '../execution-host' @@ -26,7 +26,7 @@ export function getHostContextLabel( if (override) { return override } - if (parseExecutionHostId(hostId)?.kind === 'local') { + if (isLocalExecutionHost(hostId)) { // An explicit null means the paired host platform is unknown (mobile); an // omitted platform means use the current process (desktop). if (sources.hostPlatform === null) {