fix(terminal): scope account restart metadata to its execution host (#34214)

This commit is contained in:
Neil
2026-10-26 20:39:47 -07:00
committed by GitHub
parent 3347d47a7d
commit c5f48b5cd3
2 changed files with 71 additions and 4 deletions
@@ -10,7 +10,12 @@ import {
type ProjectExecutionRuntimeResolution
} from '../../../shared/project-execution-runtime'
import type * as localPreflightContext from '@/lib/local-preflight-context'
import { makeTerminalTab, makeWorktree } from '@/store/slices/worktrees-slice-test-fixtures'
import { resolveAgentResumeLaunchTarget } from '@/lib/agent-resume-launch-target'
import {
makeTerminalTab,
makeWorktree,
TEST_REPO
} from '@/store/slices/worktrees-slice-test-fixtures'
import {
buildPaneAccountRestartStartup,
resolveAgentAccountRestartResumeStartup,
@@ -19,6 +24,8 @@ import {
let projectRuntimeContext: ProjectExecutionRuntimeResolution | undefined
vi.mock('@/lib/agent-resume-launch-target', { spy: true })
vi.mock('@/lib/local-preflight-context', async (importOriginal) => ({
...(await importOriginal<typeof localPreflightContext>()),
getLocalProjectExecutionRuntimeContext: () => projectRuntimeContext
@@ -306,6 +313,60 @@ describe('buildPaneAccountRestartStartup for Claude', () => {
)
})
it.each([
['workspace folder', undefined, "cd '/recorded/session' && "],
['saved tab folder', '/recorded/session', '']
] as const)('uses the selected host and respects the %s on resume', (_, startupCwd, prefix) => {
const original = useAppStore.getState()
try {
seedAgentStatus({
agentType: 'claude',
state: 'done',
providerSession: { key: 'session_id', id: SESSION_ID, cwd: '/recorded/session' }
})
useAppStore.setState({
repos: [
TEST_REPO,
{ ...TEST_REPO, executionHostId: 'runtime:remote-host', connectionId: 'selected-ssh' }
],
worktreesByRepo: {
[TEST_REPO.id]: [
makeWorktree({
id: WORKTREE_ID,
repoId: TEST_REPO.id,
path: '/recorded/session',
hostId: 'local'
}),
makeWorktree({
id: WORKTREE_ID,
repoId: TEST_REPO.id,
path: '/remote/workspace',
hostId: 'runtime:remote-host'
})
]
},
detectedWorktreesByRepo: {},
activeWorktreeId: WORKTREE_ID,
activeWorkspaceExecutionHostId: 'runtime:remote-host',
tabsByWorktree: {
[WORKTREE_ID]: [makeTerminalTab({ id: TAB_ID, worktreeId: WORKTREE_ID, startupCwd })]
}
})
expect(buildPaneAccountRestartStartup('claude', pane)?.command).toBe(
`${prefix}claude '--resume' '${SESSION_ID}'`
)
expect(resolveAgentResumeLaunchTarget).toHaveBeenLastCalledWith(
expect.objectContaining({
executionHostId: 'runtime:remote-host',
connectionId: 'selected-ssh'
})
)
} finally {
useAppStore.setState(original)
}
})
it('reports no resume when the Claude pane has no session id', () => {
seedAgentStatus({ agentType: 'claude', state: 'done' })
@@ -8,6 +8,7 @@ import {
import { getExecutionHostIdForWorktree } from '@/lib/worktree-runtime-owner'
import { runtimeEnvironmentIdUnlessTwin } from '@/lib/resolve-owner'
import { getLocalProjectExecutionRuntimeContext } from '@/lib/local-preflight-context'
import { repoMatchesHostIdentity } from '@/store/slices/repo-host-identity'
import { makePaneKey } from '../../../shared/stable-pane-id'
import { normalizeAgentProviderSession } from '../../../shared/agent-session-resume'
import {
@@ -103,7 +104,10 @@ export function resolveAgentAccountRestartResumeStartup(
// Both restart executors spawn in the tab's start folder.
launchCwd:
state.tabsByWorktree[args.worktreeId]?.find((tab) => tab.id === args.tabId)?.startupCwd ??
state.getKnownWorktreeById(args.worktreeId)?.path
state.getKnownWorktreeById(
args.worktreeId,
getExecutionHostIdForWorktree(state, args.worktreeId)
)?.path
})
if (!startupPlan) {
return null
@@ -158,9 +162,11 @@ function resolveAccountRestartLaunchTarget(
args: AccountRestartTarget,
projectRuntime: ProjectExecutionRuntimeResolution | undefined
): AgentResumeLaunchTarget {
const worktree = state.getKnownWorktreeById(args.worktreeId)
const repo = worktree ? state.repos.find((entry) => entry.id === worktree.repoId) : null
const executionHostId = getExecutionHostIdForWorktree(state, args.worktreeId)
const worktree = state.getKnownWorktreeById(args.worktreeId, executionHostId)
const repo = worktree
? state.repos.find((entry) => repoMatchesHostIdentity(entry, worktree.repoId, executionHostId))
: null
return resolveAgentResumeLaunchTarget({
projectRuntime,
connectionId: repo?.connectionId,