mirror of
https://github.com/stablyai/orca.git
synced 2026-10-12 16:03:03 +00:00
merge: PR #19736 into integration stack
This commit is contained in:
@@ -4,6 +4,10 @@ import { useAppStore, type AppState } from '@/store'
|
||||
import { resumeSleepingAgentSessionsForWorktree } from './resume-sleeping-agent-session'
|
||||
import { makeCreatedAgentWorktree } from '@/lib/worktree-activation-created-agent-test-state'
|
||||
import { makePaneKey } from '../../../shared/stable-pane-id'
|
||||
import {
|
||||
RESUMABLE_TUI_AGENTS,
|
||||
type AgentProviderSessionMetadata
|
||||
} from '../../../shared/agent-session-resume'
|
||||
import {
|
||||
hasHostSessionMirrorHydrated,
|
||||
markHostSessionMirrorHydrated,
|
||||
@@ -194,6 +198,72 @@ describe('parked mirrored-pane resume replay', () => {
|
||||
expect(Object.keys(after.automaticAgentResumeClaimsByTabId)).toHaveLength(0)
|
||||
})
|
||||
|
||||
it.each(
|
||||
RESUMABLE_TUI_AGENTS.flatMap((agent) =>
|
||||
(['working', 'done'] as const).flatMap((checkpointState) =>
|
||||
[
|
||||
{ livePtyIds: ['pty-host-old-1'], expectedLaunches: 0 },
|
||||
{ livePtyIds: [], expectedLaunches: 1 },
|
||||
{ livePtyIds: ['other-pane-pty'], expectedLaunches: 1 }
|
||||
].map((scenario) => ({ agent, checkpointState, ...scenario }))
|
||||
)
|
||||
)
|
||||
)(
|
||||
'checks exact live ownership before resuming $agent $checkpointState with $livePtyIds',
|
||||
({ agent, checkpointState, livePtyIds, expectedLaunches }) => {
|
||||
const worktree = makeRuntimeOwnedWorktree()
|
||||
seedState(worktree)
|
||||
const paneKey = seedSleepingRecord(worktree.id, 'completed-session')
|
||||
const providerSession: AgentProviderSessionMetadata = {
|
||||
key: agent === 'antigravity' ? 'conversation_id' : 'session_id',
|
||||
id: 'completed-session',
|
||||
...(agent === 'pi' || agent === 'prime-agent'
|
||||
? { transcriptPath: `/remote/${agent}/session.jsonl` }
|
||||
: {})
|
||||
}
|
||||
const record = {
|
||||
...useAppStore.getState().sleepingAgentSessionsByPaneKey[paneKey]!,
|
||||
agent,
|
||||
providerSession
|
||||
}
|
||||
const stalePaneKey = makePaneKey('old-tab', LEAF_ID)
|
||||
useAppStore.setState({
|
||||
ptyIdsByTabId: { [WEB_TAB_ID]: [...livePtyIds] },
|
||||
sleepingAgentSessionsByPaneKey: {
|
||||
[stalePaneKey]: {
|
||||
...record,
|
||||
paneKey: stalePaneKey,
|
||||
tabId: 'old-tab',
|
||||
state: checkpointState,
|
||||
origin: 'quit'
|
||||
}
|
||||
},
|
||||
agentStatusByPaneKey: {
|
||||
[paneKey]: {
|
||||
paneKey,
|
||||
tabId: WEB_TAB_ID,
|
||||
worktreeId: worktree.id,
|
||||
agentType: record.agent,
|
||||
providerSession: record.providerSession,
|
||||
state: 'done',
|
||||
prompt: '',
|
||||
updatedAt: 2000,
|
||||
stateStartedAt: 2000,
|
||||
stateHistory: []
|
||||
}
|
||||
}
|
||||
})
|
||||
markHostSessionMirrorHydrated(RUNTIME_ENV_ID)
|
||||
|
||||
expect(resumeSleepingAgentSessionsForWorktree(worktree.id)).toBe(expectedLaunches)
|
||||
const after = useAppStore.getState()
|
||||
expect(after.tabsByWorktree[worktree.id]).toHaveLength(1 + expectedLaunches)
|
||||
expect(after.tabsByWorktree[worktree.id]?.[0]?.id).toBe(WEB_TAB_ID)
|
||||
expect(after.sleepingAgentSessionsByPaneKey[stalePaneKey]).toBeUndefined()
|
||||
expect(Object.keys(after.pendingStartupByTabId)).toHaveLength(expectedLaunches)
|
||||
}
|
||||
)
|
||||
|
||||
it('drops the hydrated verdict when the host reconnects under a new runtime id', () => {
|
||||
connectRuntime('runtime-a')
|
||||
markHostSessionMirrorHydrated(RUNTIME_ENV_ID)
|
||||
|
||||
@@ -4,10 +4,12 @@ import {
|
||||
type SleepingAgentSessionRecord
|
||||
} from '../../../shared/agent-session-resume'
|
||||
import { AGENT_STATUS_STALE_AFTER_MS } from '../../../shared/agent-status-types'
|
||||
import { parsePaneKey } from '../../../shared/stable-pane-id'
|
||||
import {
|
||||
getProviderSessionClaimKey,
|
||||
isPassiveCompletedHibernationEvidence,
|
||||
recordPaneIsOwnedByPreservedPane
|
||||
recordPaneIsOwnedByPreservedPane,
|
||||
stablePaneHasLivePty
|
||||
} from './sleeping-agent-pane-ownership'
|
||||
import {
|
||||
launchSleepingAgentSession,
|
||||
@@ -100,11 +102,25 @@ function activeOrQueuedResumeClaimsProviderSession(
|
||||
if (samePaneOwnsRecovery && entry.paneKey === record.paneKey) {
|
||||
continue
|
||||
}
|
||||
const tabId = getAgentStatusTabId(entry)
|
||||
const pane = parsePaneKey(entry.paneKey)
|
||||
// A completed turn still owns its transcript while its exact PTY is live.
|
||||
const completedPaneIsLive = Boolean(
|
||||
entry.state === 'done' &&
|
||||
pane &&
|
||||
tabId === pane.tabId &&
|
||||
stablePaneHasLivePty(
|
||||
pane.tabId,
|
||||
pane.leafId,
|
||||
state.ptyIdsByTabId,
|
||||
state.terminalLayoutsByTabId[pane.tabId]
|
||||
)
|
||||
)
|
||||
if (
|
||||
worktreeTabIds.has(getAgentStatusTabId(entry) ?? '') &&
|
||||
worktreeTabIds.has(tabId ?? '') &&
|
||||
entry.worktreeId === record.worktreeId &&
|
||||
entry.agentType === record.agent &&
|
||||
entry.state !== 'done' &&
|
||||
(entry.state !== 'done' || completedPaneIsLive) &&
|
||||
agentProviderSessionsEqual(record.agent, entry.providerSession, record.providerSession)
|
||||
) {
|
||||
return true
|
||||
|
||||
@@ -94,7 +94,7 @@ function hasRestorableStablePanePty(
|
||||
// the pane that reconnects on activation. Liveness comes from the runtime
|
||||
// live-PTY map (ptyIdsByTabId), not the layout's ptyIdsByLeafId snapshot, which
|
||||
// persists stale across sleep/restart.
|
||||
function stablePaneHasLivePty(
|
||||
export function stablePaneHasLivePty(
|
||||
tabId: string,
|
||||
leafId: string,
|
||||
ptyIdsByTabId: Record<string, string[]>,
|
||||
|
||||
Reference in New Issue
Block a user