Revert "fix(terminal): launch prior/default agent when a woken terminal can’t resume" (#7524)
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
parent
3dd3cc9f68
commit
d9103d08c1
|
|
@ -137,7 +137,7 @@ type StoreState = {
|
|||
| { kind: 'windows-host' }
|
||||
| { kind: 'wsl'; distro: string }
|
||||
}[]
|
||||
sshConnectionStates: Map<string, { status: string; remotePlatform?: NodeJS.Platform }>
|
||||
sshConnectionStates: Map<string, { status: string }>
|
||||
cacheTimerByKey: Record<string, number | null>
|
||||
settings: {
|
||||
theme?: 'system' | 'dark' | 'light'
|
||||
|
|
@ -5620,285 +5620,6 @@ describe('connectPanePty', () => {
|
|||
expect(mockStoreState.clearSleepingAgentSession).toHaveBeenCalledWith(paneKey)
|
||||
})
|
||||
|
||||
// #4557: an agent terminal that can't be resumed should come back as a fresh
|
||||
// agent (prior agent, else default) instead of a blank shell.
|
||||
const findReattachConnect = (transport: ReturnType<typeof createMockTransport>) => {
|
||||
const call = transport.connect.mock.calls.find(
|
||||
(args) => (args[0] as { sessionId?: string })?.sessionId === 'lost-pty'
|
||||
)
|
||||
return call?.[0] as
|
||||
| {
|
||||
command?: string
|
||||
launchAgent?: string
|
||||
launchConfig?: {
|
||||
agentCommand?: string
|
||||
agentArgs?: string
|
||||
agentEnv?: Record<string, string>
|
||||
}
|
||||
env?: Record<string, string>
|
||||
}
|
||||
| undefined
|
||||
}
|
||||
|
||||
const mountColdRestore = async (
|
||||
transport: ReturnType<typeof createMockTransport>,
|
||||
depsOverrides: Record<string, unknown> = {}
|
||||
) => {
|
||||
const { connectPanePty } = await import('./pty-connection')
|
||||
transport.connect.mockImplementation(async ({ sessionId }: { sessionId?: string }) => {
|
||||
if (sessionId) {
|
||||
return { id: 'fresh-pty', coldRestore: { scrollback: 'cold-payload', cwd: '/tmp/wt-1' } }
|
||||
}
|
||||
return 'fresh-pty'
|
||||
})
|
||||
transportFactoryQueue.push(transport)
|
||||
const pane = createPane(1)
|
||||
const manager = createManager(1)
|
||||
const deps = createDeps({
|
||||
restoredLeafId: LEAF_1,
|
||||
restoredPtyIdByLeafId: { [LEAF_1]: 'lost-pty' },
|
||||
...depsOverrides
|
||||
})
|
||||
connectPanePty(pane as never, manager as never, deps as never)
|
||||
await flushAsyncTicks(20)
|
||||
await new Promise((resolve) => setTimeout(resolve, 70))
|
||||
return deps
|
||||
}
|
||||
|
||||
it('cold-restores a fresh agent from the sleeping record when its session is unresumable', async () => {
|
||||
const transport = createMockTransport('fresh-pty')
|
||||
const paneKey = makePaneKey('tab-1', LEAF_1)
|
||||
mockStoreState = {
|
||||
...mockStoreState,
|
||||
tabsByWorktree: { 'wt-1': [{ id: 'tab-1', ptyId: 'lost-pty' }] },
|
||||
settings: { ...mockStoreState.settings, agentCmdOverrides: {} },
|
||||
agentStatusByPaneKey: {},
|
||||
sleepingAgentSessionsByPaneKey: {
|
||||
// Agent known, but no resumable provider session id.
|
||||
[paneKey]: {
|
||||
paneKey,
|
||||
tabId: 'tab-1',
|
||||
worktreeId: 'wt-1',
|
||||
agent: 'codex',
|
||||
prompt: 'finish the task',
|
||||
state: 'working',
|
||||
capturedAt: 1,
|
||||
updatedAt: 1
|
||||
}
|
||||
}
|
||||
} as StoreState
|
||||
|
||||
await mountColdRestore(transport)
|
||||
|
||||
const reattach = findReattachConnect(transport)
|
||||
expect(reattach?.command).toMatch(/^codex /)
|
||||
expect(reattach?.command).not.toContain('resume')
|
||||
expect(reattach?.launchAgent).toBe('codex')
|
||||
expect(reattach?.env?.ORCA_AGENT_LAUNCH_TOKEN).toMatch(new RegExp(`^${UUID_RE}$`))
|
||||
})
|
||||
|
||||
it('cold-restores a fresh agent with the sleeping record launch config when resume is unavailable', async () => {
|
||||
const transport = createMockTransport('fresh-pty')
|
||||
const paneKey = makePaneKey('tab-1', LEAF_1)
|
||||
mockStoreState = {
|
||||
...mockStoreState,
|
||||
tabsByWorktree: { 'wt-1': [{ id: 'tab-1', ptyId: 'lost-pty' }] },
|
||||
settings: {
|
||||
...mockStoreState.settings,
|
||||
agentCmdOverrides: { codex: 'current-codex' },
|
||||
agentDefaultArgs: { codex: '--current-args' }
|
||||
},
|
||||
agentStatusByPaneKey: {},
|
||||
sleepingAgentSessionsByPaneKey: {
|
||||
[paneKey]: {
|
||||
paneKey,
|
||||
tabId: 'tab-1',
|
||||
worktreeId: 'wt-1',
|
||||
agent: 'codex',
|
||||
prompt: 'finish the task',
|
||||
state: 'working',
|
||||
capturedAt: 1,
|
||||
updatedAt: 1,
|
||||
launchConfig: {
|
||||
agentCommand: 'sleeping-codex --stored-args',
|
||||
agentArgs: '--stored-args',
|
||||
agentEnv: { CODEX_HOME: '/tmp/orca-stored-codex' }
|
||||
}
|
||||
}
|
||||
}
|
||||
} as StoreState
|
||||
|
||||
await mountColdRestore(transport)
|
||||
|
||||
const reattach = findReattachConnect(transport)
|
||||
expect(reattach?.command).toBe('sleeping-codex --stored-args')
|
||||
expect(reattach?.launchAgent).toBe('codex')
|
||||
expect(reattach?.launchConfig).toMatchObject({
|
||||
agentCommand: 'sleeping-codex --stored-args',
|
||||
agentArgs: '--stored-args',
|
||||
agentEnv: { CODEX_HOME: '/tmp/orca-stored-codex' }
|
||||
})
|
||||
expect(reattach?.env).toMatchObject({
|
||||
CODEX_HOME: '/tmp/orca-stored-codex',
|
||||
ORCA_AGENT_LAUNCH_TOKEN: expect.stringMatching(new RegExp(`^${UUID_RE}$`))
|
||||
})
|
||||
})
|
||||
|
||||
it('uses the known SSH remote platform when fresh-launching an unresumable agent', async () => {
|
||||
const transport = createMockTransport('fresh-pty')
|
||||
mockStoreState = {
|
||||
...mockStoreState,
|
||||
tabsByWorktree: {
|
||||
'wt-1': [{ id: 'tab-1', ptyId: 'lost-pty', launchAgent: 'claude-agent-teams' }]
|
||||
},
|
||||
repos: [{ id: 'repo1', connectionId: 'ssh-win' }],
|
||||
sshConnectionStates: new Map([['ssh-win', { status: 'connected', remotePlatform: 'win32' }]]),
|
||||
settings: { ...mockStoreState.settings, agentCmdOverrides: {} },
|
||||
agentStatusByPaneKey: {},
|
||||
sleepingAgentSessionsByPaneKey: {}
|
||||
} as StoreState
|
||||
|
||||
await mountColdRestore(transport)
|
||||
|
||||
const reattach = findReattachConnect(transport)
|
||||
expect(reattach?.command).toMatch(/^orca\.cmd claude-teams/)
|
||||
expect(reattach?.command).not.toContain('orca-ide')
|
||||
expect(reattach?.launchAgent).toBe('claude-agent-teams')
|
||||
})
|
||||
|
||||
it('cold-restores the prior agent from the persisted tab launchAgent after a restart', async () => {
|
||||
const transport = createMockTransport('fresh-pty')
|
||||
mockStoreState = {
|
||||
...mockStoreState,
|
||||
// After a restart there is no live status nor sleeping record — only the
|
||||
// persisted tab.launchAgent tells us this terminal ran an agent.
|
||||
tabsByWorktree: { 'wt-1': [{ id: 'tab-1', ptyId: 'lost-pty', launchAgent: 'claude' }] },
|
||||
settings: { ...mockStoreState.settings, agentCmdOverrides: {} },
|
||||
agentStatusByPaneKey: {},
|
||||
sleepingAgentSessionsByPaneKey: {}
|
||||
} as StoreState
|
||||
|
||||
await mountColdRestore(transport)
|
||||
|
||||
const reattach = findReattachConnect(transport)
|
||||
expect(reattach?.command).toContain('claude')
|
||||
expect(reattach?.command).not.toContain('resume')
|
||||
expect(reattach?.launchAgent).toBe('claude')
|
||||
})
|
||||
|
||||
it('cold-restores the default agent when an agent terminal has no recoverable agent', async () => {
|
||||
const transport = createMockTransport('fresh-pty')
|
||||
const paneKey = makePaneKey('tab-1', LEAF_1)
|
||||
mockStoreState = {
|
||||
...mockStoreState,
|
||||
tabsByWorktree: { 'wt-1': [{ id: 'tab-1', ptyId: 'lost-pty' }] },
|
||||
settings: { ...mockStoreState.settings, agentCmdOverrides: {}, defaultTuiAgent: 'codex' },
|
||||
agentStatusByPaneKey: {},
|
||||
// A record exists (it was an agent terminal) but carries no usable agent id.
|
||||
sleepingAgentSessionsByPaneKey: {
|
||||
[paneKey]: { paneKey, tabId: 'tab-1', worktreeId: 'wt-1', capturedAt: 1, updatedAt: 1 }
|
||||
}
|
||||
} as StoreState
|
||||
|
||||
await mountColdRestore(transport)
|
||||
|
||||
const reattach = findReattachConnect(transport)
|
||||
expect(reattach?.command).toMatch(/^codex /)
|
||||
expect(reattach?.command).not.toContain('resume')
|
||||
expect(reattach?.launchAgent).toBe('codex')
|
||||
})
|
||||
|
||||
it('does not cold-restore a fresh agent from only a completed live status row', async () => {
|
||||
const transport = createMockTransport('fresh-pty')
|
||||
const paneKey = makePaneKey('tab-1', LEAF_1)
|
||||
mockStoreState = {
|
||||
...mockStoreState,
|
||||
tabsByWorktree: { 'wt-1': [{ id: 'tab-1', ptyId: 'lost-pty' }] },
|
||||
settings: { ...mockStoreState.settings, agentCmdOverrides: {}, defaultTuiAgent: 'codex' },
|
||||
agentStatusByPaneKey: {
|
||||
[paneKey]: {
|
||||
agentType: 'codex',
|
||||
paneKey,
|
||||
prompt: 'done task',
|
||||
state: 'done',
|
||||
stateHistory: [],
|
||||
stateStartedAt: 1,
|
||||
updatedAt: 1
|
||||
}
|
||||
},
|
||||
sleepingAgentSessionsByPaneKey: {}
|
||||
} as StoreState
|
||||
|
||||
await mountColdRestore(transport)
|
||||
|
||||
const reattach = findReattachConnect(transport)
|
||||
expect(reattach?.command).toBeUndefined()
|
||||
expect(reattach?.launchAgent).toBeUndefined()
|
||||
})
|
||||
|
||||
it('ignores malformed sleeping launch config when a live resumable status exists', async () => {
|
||||
const transport = createMockTransport('fresh-pty')
|
||||
const paneKey = makePaneKey('tab-1', LEAF_1)
|
||||
mockStoreState = {
|
||||
...mockStoreState,
|
||||
tabsByWorktree: { 'wt-1': [{ id: 'tab-1', ptyId: 'lost-pty' }] },
|
||||
settings: { ...mockStoreState.settings, agentCmdOverrides: {} },
|
||||
agentStatusByPaneKey: {
|
||||
[paneKey]: {
|
||||
agentType: 'codex',
|
||||
paneKey,
|
||||
prompt: 'live task',
|
||||
providerSession: { key: 'session_id', id: 'live-session' },
|
||||
state: 'working',
|
||||
stateHistory: [],
|
||||
stateStartedAt: 1,
|
||||
updatedAt: 1
|
||||
}
|
||||
},
|
||||
sleepingAgentSessionsByPaneKey: {
|
||||
[paneKey]: {
|
||||
paneKey,
|
||||
tabId: 'tab-1',
|
||||
worktreeId: 'wt-1',
|
||||
agent: 'codex',
|
||||
prompt: 'malformed sleeping task',
|
||||
state: 'working',
|
||||
capturedAt: 1,
|
||||
updatedAt: 1,
|
||||
launchConfig: {
|
||||
agentCommand: 'malformed-sleeping-codex',
|
||||
agentArgs: '',
|
||||
agentEnv: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
} as StoreState
|
||||
|
||||
await mountColdRestore(transport)
|
||||
|
||||
const reattach = findReattachConnect(transport)
|
||||
expect(reattach?.command).toContain("'resume' 'live-session'")
|
||||
expect(reattach?.command).not.toBe('malformed-sleeping-codex')
|
||||
expect(reattach?.launchAgent).toBe('codex')
|
||||
})
|
||||
|
||||
it('leaves a plain shell blank on cold restore (no prior agent, no default)', async () => {
|
||||
const transport = createMockTransport('fresh-pty')
|
||||
mockStoreState = {
|
||||
...mockStoreState,
|
||||
tabsByWorktree: { 'wt-1': [{ id: 'tab-1', ptyId: 'lost-pty' }] },
|
||||
settings: { ...mockStoreState.settings, agentCmdOverrides: {} },
|
||||
agentStatusByPaneKey: {},
|
||||
sleepingAgentSessionsByPaneKey: {}
|
||||
} as StoreState
|
||||
|
||||
await mountColdRestore(transport)
|
||||
|
||||
const reattach = findReattachConnect(transport)
|
||||
expect(reattach?.command).toBeUndefined()
|
||||
expect(reattach?.launchAgent).toBeUndefined()
|
||||
})
|
||||
|
||||
it('resumes from an unambiguous legacy sleeping record when cold-restoring a preserved pane', async () => {
|
||||
const { connectPanePty } = await import('./pty-connection')
|
||||
const transport = createMockTransport('fresh-pty')
|
||||
|
|
|
|||
|
|
@ -149,17 +149,16 @@ import {
|
|||
getRuntimeEnvironmentIdForWorktree
|
||||
} from '@/lib/worktree-runtime-owner'
|
||||
import { CLIENT_PLATFORM } from '@/lib/new-workspace'
|
||||
import { buildAgentResumeStartupPlan, buildAgentStartupPlan } from '@/lib/tui-agent-startup'
|
||||
import { buildAgentResumeStartupPlan } from '@/lib/tui-agent-startup'
|
||||
import { resolveAgentStatusTerminalTitle } from '@/lib/agent-status-terminal-title'
|
||||
import {
|
||||
resolveTuiAgentLaunchArgs,
|
||||
resolveTuiAgentLaunchEnv
|
||||
} from '../../../../shared/tui-agent-launch-defaults'
|
||||
import { isTuiAgent } from '../../../../shared/tui-agent-config'
|
||||
import { isTuiAgentEnabled } from '../../../../shared/tui-agent-selection'
|
||||
import {
|
||||
isResumableTuiAgent,
|
||||
normalizeAgentProviderSession,
|
||||
type ResumableTuiAgent,
|
||||
type SleepingAgentSessionRecord
|
||||
} from '../../../../shared/agent-session-resume'
|
||||
import {
|
||||
|
|
@ -373,9 +372,7 @@ type FreshSpawnOptions = {
|
|||
}
|
||||
|
||||
type ColdRestoreAgentResumeStartup = PendingStartupCommand & {
|
||||
// TuiAgent (not just ResumableTuiAgent): the fresh-launch fallback can spawn any
|
||||
// agent the terminal previously ran, including ones without resume support.
|
||||
agent: TuiAgent
|
||||
agent: ResumableTuiAgent
|
||||
launchConfig: NonNullable<ReturnType<typeof buildAgentResumeStartupPlan>>['launchConfig']
|
||||
launchToken: string
|
||||
useLiveEntry: boolean
|
||||
|
|
@ -1129,34 +1126,32 @@ export function connectPanePty(
|
|||
)
|
||||
}
|
||||
)
|
||||
const legacyProviderSessionKeys = legacyMatches.map(([, record]) => {
|
||||
const providerSession = normalizeAgentProviderSession(record.providerSession)
|
||||
return providerSession
|
||||
? [record.worktreeId, record.agent, providerSession.key, providerSession.id].join('\0')
|
||||
: null
|
||||
})
|
||||
const exactLegacyMatch = legacyMatches.find(([paneKey]) => {
|
||||
const legacy = parseLegacyNumericPaneKey(paneKey)
|
||||
return legacy?.numericPaneId === String(pane.id)
|
||||
})
|
||||
const providerSessionKeys = new Set(
|
||||
legacyProviderSessionKeys.filter((key): key is string => key !== null)
|
||||
legacyMatches.map(([, record]) =>
|
||||
[
|
||||
record.worktreeId,
|
||||
record.agent,
|
||||
record.providerSession.key,
|
||||
record.providerSession.id
|
||||
].join('\0')
|
||||
)
|
||||
)
|
||||
const oldestLegacyMatch = legacyMatches
|
||||
.slice()
|
||||
.sort(([, a], [, b]) => a.capturedAt - b.capturedAt || a.updatedAt - b.updatedAt)[0]
|
||||
// Why: duplicate legacy aliases can point at one provider session; consume
|
||||
// the oldest capture as canonical and clear its aliases after resume.
|
||||
const allLegacyMatchesHaveProviderSession = legacyProviderSessionKeys.every(
|
||||
(key) => key !== null
|
||||
)
|
||||
const selectedLegacyMatch =
|
||||
exactLegacyMatch ??
|
||||
(legacyMatches.length === 1
|
||||
? legacyMatches[0]
|
||||
: allLegacyMatchesHaveProviderSession && providerSessionKeys.size === 1
|
||||
? oldestLegacyMatch
|
||||
: null)
|
||||
(providerSessionKeys.size === 1
|
||||
? legacyMatches.length === 1
|
||||
? legacyMatches[0]
|
||||
: oldestLegacyMatch
|
||||
: null)
|
||||
if (!selectedLegacyMatch) {
|
||||
return null
|
||||
}
|
||||
|
|
@ -1168,19 +1163,13 @@ export function connectPanePty(
|
|||
consumed: { paneKey: string; record: SleepingAgentSessionRecord }
|
||||
): void => {
|
||||
state.clearSleepingAgentSession(consumed.paneKey)
|
||||
const consumedSession = normalizeAgentProviderSession(consumed.record.providerSession)
|
||||
// No provider session (e.g. a fresh-launch fallback record) means there is no
|
||||
// shared session to alias, so there is nothing else to clear.
|
||||
if (!consumedSession) {
|
||||
return
|
||||
}
|
||||
for (const [paneKey, record] of Object.entries(state.sleepingAgentSessionsByPaneKey)) {
|
||||
if (
|
||||
paneKey !== consumed.paneKey &&
|
||||
record.worktreeId === consumed.record.worktreeId &&
|
||||
record.agent === consumed.record.agent &&
|
||||
record.providerSession?.key === consumedSession.key &&
|
||||
record.providerSession?.id === consumedSession.id
|
||||
record.providerSession.key === consumed.record.providerSession.key &&
|
||||
record.providerSession.id === consumed.record.providerSession.id
|
||||
) {
|
||||
// Why: legacy pane aliases can leave multiple sleeping rows for one
|
||||
// provider session; once this pane resumes it, every alias is stale.
|
||||
|
|
@ -3495,10 +3484,6 @@ export function connectPanePty(
|
|||
if (projectRuntime?.status === 'resolved' && projectRuntime.runtime.kind === 'wsl') {
|
||||
return 'linux'
|
||||
}
|
||||
const sshRemotePlatform = getTerminalPasteSshRemotePlatform(connectionId)
|
||||
if (sshRemotePlatform) {
|
||||
return sshRemotePlatform
|
||||
}
|
||||
if (connectionId || (worktree?.path && isWslUncPath(worktree.path))) {
|
||||
return 'linux'
|
||||
}
|
||||
|
|
@ -3512,123 +3497,27 @@ export function connectPanePty(
|
|||
const entry = state.agentStatusByPaneKey[cacheKey]
|
||||
const sleepingRecordEntry = getSleepingRecordForPane(state)
|
||||
const sleepingRecord = sleepingRecordEntry?.record
|
||||
const liveEntry = entry && entry.state !== 'done' ? entry : null
|
||||
const useLiveEntry = Boolean(liveEntry)
|
||||
|
||||
// When the provider session can't be resumed, an agent terminal should still
|
||||
// come back as its agent (or the default), not a blank shell (#4557). A plain
|
||||
// shell - no agent ever ran here - stays blank (buildFreshFallback returns null).
|
||||
const buildFreshFallback = (): ColdRestoreAgentResumeStartup | null => {
|
||||
const tab = (state.tabsByWorktree[deps.worktreeId] ?? []).find(
|
||||
(candidate) => candidate.id === deps.tabId
|
||||
)
|
||||
const liveAgent =
|
||||
liveEntry && isTuiAgent(liveEntry.agentType) ? liveEntry.agentType : undefined
|
||||
const priorAgent =
|
||||
liveAgent ?? sleepingRecord?.agent ?? tab?.launchAgent ?? paneStartup?.launchAgent
|
||||
const defaultPref = state.settings?.defaultTuiAgent
|
||||
const defaultAgent =
|
||||
defaultPref &&
|
||||
defaultPref !== 'blank' &&
|
||||
isTuiAgentEnabled(defaultPref, state.settings?.disabledTuiAgents)
|
||||
? defaultPref
|
||||
: undefined
|
||||
// Fall back to the default agent only when this was an active/sleeping
|
||||
// agent terminal whose specific agent is unrecoverable - never for a
|
||||
// genuine plain shell or a stale completed live row.
|
||||
const fallbackAgent = priorAgent ?? (liveEntry || sleepingRecord ? defaultAgent : undefined)
|
||||
if (!fallbackAgent) {
|
||||
return null
|
||||
}
|
||||
const liveLaunchConfig =
|
||||
liveEntry && liveAgent === fallbackAgent
|
||||
? state.getAgentLaunchConfigForStatusEntry(liveEntry)
|
||||
: undefined
|
||||
const fallbackLaunchConfig =
|
||||
liveLaunchConfig ??
|
||||
(sleepingRecord?.agent === fallbackAgent ? sleepingRecord.launchConfig : undefined) ??
|
||||
(paneStartup?.launchAgent === fallbackAgent ? paneStartup.launchConfig : undefined)
|
||||
if (fallbackLaunchConfig?.agentCommand?.trim()) {
|
||||
const freshLaunchToken = createBrowserUuid()
|
||||
return {
|
||||
agent: fallbackAgent,
|
||||
command: fallbackLaunchConfig.agentCommand.trim(),
|
||||
env: {
|
||||
...fallbackLaunchConfig.agentEnv,
|
||||
ORCA_AGENT_LAUNCH_TOKEN: freshLaunchToken
|
||||
},
|
||||
launchConfig: {
|
||||
agentCommand: fallbackLaunchConfig.agentCommand.trim(),
|
||||
agentArgs: fallbackLaunchConfig.agentArgs,
|
||||
agentEnv: { ...fallbackLaunchConfig.agentEnv }
|
||||
},
|
||||
launchToken: freshLaunchToken,
|
||||
useLiveEntry,
|
||||
// A fresh launch is a new session, not a restored one, so no
|
||||
// "restored" banner; the stale sleeping record is still cleared
|
||||
// after the spawn.
|
||||
hasSleepingRecord: false,
|
||||
sleepingRecordEntry
|
||||
}
|
||||
}
|
||||
const freshPlan = buildAgentStartupPlan({
|
||||
agent: fallbackAgent,
|
||||
prompt: '',
|
||||
cmdOverrides: state.settings?.agentCmdOverrides ?? {},
|
||||
agentArgs:
|
||||
fallbackLaunchConfig !== undefined
|
||||
? fallbackLaunchConfig.agentArgs
|
||||
: resolveTuiAgentLaunchArgs(fallbackAgent, state.settings?.agentDefaultArgs),
|
||||
agentEnv:
|
||||
fallbackLaunchConfig !== undefined
|
||||
? fallbackLaunchConfig.agentEnv
|
||||
: resolveTuiAgentLaunchEnv(fallbackAgent, state.settings?.agentDefaultEnv),
|
||||
platform: getColdRestoreAgentResumePlatform(),
|
||||
allowEmptyPromptLaunch: true
|
||||
})
|
||||
if (!freshPlan) {
|
||||
return null
|
||||
}
|
||||
const freshLaunchToken = createBrowserUuid()
|
||||
return {
|
||||
agent: fallbackAgent,
|
||||
command: freshPlan.launchCommand,
|
||||
env: {
|
||||
...freshPlan.env,
|
||||
ORCA_AGENT_LAUNCH_TOKEN: freshLaunchToken
|
||||
},
|
||||
launchConfig: freshPlan.launchConfig,
|
||||
launchToken: freshLaunchToken,
|
||||
useLiveEntry,
|
||||
// A fresh launch is a new session, not a restored one, so no
|
||||
// "restored" banner; the stale sleeping record is still cleared after
|
||||
// the spawn.
|
||||
hasSleepingRecord: false,
|
||||
sleepingRecordEntry
|
||||
}
|
||||
}
|
||||
|
||||
const agent = liveEntry ? liveEntry.agentType : sleepingRecord?.agent
|
||||
const useLiveEntry = entry && entry.state !== 'done'
|
||||
const agent = useLiveEntry ? entry.agentType : sleepingRecord?.agent
|
||||
if (!agent || !isResumableTuiAgent(agent)) {
|
||||
return buildFreshFallback()
|
||||
return null
|
||||
}
|
||||
const providerSession = normalizeAgentProviderSession(
|
||||
liveEntry ? liveEntry.providerSession : sleepingRecord?.providerSession
|
||||
useLiveEntry ? entry.providerSession : sleepingRecord?.providerSession
|
||||
)
|
||||
if (!providerSession) {
|
||||
return buildFreshFallback()
|
||||
return null
|
||||
}
|
||||
const sleepingProviderSession = normalizeAgentProviderSession(sleepingRecord?.providerSession)
|
||||
const matchingSleepingLaunchConfig =
|
||||
sleepingRecord?.launchConfig &&
|
||||
(!useLiveEntry ||
|
||||
(sleepingRecord.agent === agent &&
|
||||
sleepingProviderSession?.key === providerSession.key &&
|
||||
sleepingProviderSession?.id === providerSession.id))
|
||||
sleepingRecord.providerSession.key === providerSession.key &&
|
||||
sleepingRecord.providerSession.id === providerSession.id))
|
||||
? sleepingRecord.launchConfig
|
||||
: undefined
|
||||
const launchConfig =
|
||||
(liveEntry ? state.getAgentLaunchConfigForStatusEntry(liveEntry) : undefined) ??
|
||||
(useLiveEntry && entry ? state.getAgentLaunchConfigForStatusEntry(entry) : undefined) ??
|
||||
matchingSleepingLaunchConfig
|
||||
const resumePlatform = getColdRestoreAgentResumePlatform()
|
||||
const startupPlan = buildAgentResumeStartupPlan({
|
||||
|
|
@ -3647,7 +3536,7 @@ export function connectPanePty(
|
|||
platform: resumePlatform
|
||||
})
|
||||
if (!startupPlan) {
|
||||
return buildFreshFallback()
|
||||
return null
|
||||
}
|
||||
const coldRestoreLaunchToken = createBrowserUuid()
|
||||
// Why: cold restore means the PTY process is gone but the agent provider
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ function fileNameFromUrl(url: string): string | null {
|
|||
}
|
||||
try {
|
||||
const parsed = new URL(url)
|
||||
const last = parsed.pathname.split('/').filter(Boolean).pop()
|
||||
const last = parsed.pathname.split('/').findLast(Boolean)
|
||||
return last ? decodeURIComponent(last) : null
|
||||
} catch {
|
||||
return null
|
||||
|
|
|
|||
Loading…
Reference in New Issue