mirror of
https://github.com/stablyai/orca.git
synced 2026-10-09 00:02:39 +00:00
Gate session ID liveness by shell foreground state
Once OSC 133;D proves a pane is back at the shell, don't return the session ID even if a durable record survived the exit. This prevents treating exited sessions as still active when the user is typing at the prompt.
This commit is contained in:
@@ -7,11 +7,13 @@ const PANE_KEY = 'tab-1:11111111-1111-4111-8111-111111111111'
|
||||
|
||||
function state(
|
||||
live?: AgentStatusEntry,
|
||||
sleeping?: SleepingAgentSessionRecord
|
||||
sleeping?: SleepingAgentSessionRecord,
|
||||
shellForeground = false
|
||||
): PaneAgentSessionIdState {
|
||||
return {
|
||||
agentStatusByPaneKey: live ? { [PANE_KEY]: live } : {},
|
||||
sleepingAgentSessionsByPaneKey: sleeping ? { [PANE_KEY]: sleeping } : {}
|
||||
sleepingAgentSessionsByPaneKey: sleeping ? { [PANE_KEY]: sleeping } : {},
|
||||
paneForegroundAgentByPaneKey: { [PANE_KEY]: { agent: 'claude', shellForeground } }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +70,39 @@ describe('resolvePaneAgentSessionId', () => {
|
||||
).toBe('confirmed-session')
|
||||
})
|
||||
|
||||
describe('liveness', () => {
|
||||
it('is absent once the pane is proven back at the shell', () => {
|
||||
expect(
|
||||
resolvePaneAgentSessionId(state(live('live-session'), undefined, true), PANE_KEY)
|
||||
).toBe(null)
|
||||
})
|
||||
|
||||
it('is absent at the shell even when a durable record survives the exit', () => {
|
||||
expect(
|
||||
resolvePaneAgentSessionId(state(undefined, sleeping('sleeping-session'), true), PANE_KEY)
|
||||
).toBeNull()
|
||||
})
|
||||
|
||||
it('keeps a session whose foreground evidence is only that an agent runs', () => {
|
||||
expect(
|
||||
resolvePaneAgentSessionId(state(live('live-session'), undefined, false), PANE_KEY)
|
||||
).toBe('live-session')
|
||||
})
|
||||
|
||||
it('keeps a session for a pane with no foreground evidence at all', () => {
|
||||
expect(
|
||||
resolvePaneAgentSessionId(
|
||||
{
|
||||
agentStatusByPaneKey: { [PANE_KEY]: live('live-session') },
|
||||
sleepingAgentSessionsByPaneKey: {},
|
||||
paneForegroundAgentByPaneKey: {}
|
||||
},
|
||||
PANE_KEY
|
||||
)
|
||||
).toBe('live-session')
|
||||
})
|
||||
})
|
||||
|
||||
it('does not read identity from a sibling pane', () => {
|
||||
const sibling = 'tab-1:22222222-2222-4222-8222-222222222222'
|
||||
expect(resolvePaneAgentSessionId(state(undefined, sleeping('session-1')), sibling)).toBeNull()
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
import type { AgentStatusEntry } from '../../../../shared/agent-status-types'
|
||||
import type { SleepingAgentSessionRecord } from '../../../../shared/agent-session-resume'
|
||||
import type { PaneForegroundAgentEntry } from '../../store/slices/pane-foreground-agent'
|
||||
|
||||
export type PaneAgentSessionIdState = {
|
||||
agentStatusByPaneKey: Record<string, AgentStatusEntry | undefined>
|
||||
sleepingAgentSessionsByPaneKey: Record<string, SleepingAgentSessionRecord | undefined>
|
||||
paneForegroundAgentByPaneKey: Record<string, PaneForegroundAgentEntry | undefined>
|
||||
}
|
||||
|
||||
/** Resolves the provider session owned by one exact terminal pane. */
|
||||
/** Resolves the provider session owned by one exact terminal pane, while its agent is still live. */
|
||||
export function resolvePaneAgentSessionId(
|
||||
state: PaneAgentSessionIdState,
|
||||
paneKey: string
|
||||
): string | null {
|
||||
// OSC 133;D proves the pane is back at the shell. The durable record outlives that exit on
|
||||
// purpose (cold restore resumes from it), so gate it here too — otherwise the gate would only
|
||||
// hold for panes whose agent has no resumable record.
|
||||
if (state.paneForegroundAgentByPaneKey[paneKey]?.shellForeground === true) {
|
||||
return null
|
||||
}
|
||||
const live = state.agentStatusByPaneKey[paneKey]
|
||||
if (live && live.restoredUnconfirmed !== true) {
|
||||
return live.providerSession?.id ?? null
|
||||
|
||||
Reference in New Issue
Block a user