fix(agent-status): keep the conversation id across hookless claude turns (#36208)

Claude reports slash commands like /context over OSC 7501 with no hooks, so
idle panes lost their session and mobile chat could not find it.

Fixes #36033
This commit is contained in:
Kelvin Amoaba
2026-10-30 03:36:07 +00:00
committed by GitHub
parent 0ded87821d
commit 93ca82a2a7
2 changed files with 31 additions and 4 deletions
@@ -215,7 +215,31 @@ describe('AgentHookServer ingestTerminalStatus', () => {
}
)
it('drops the cached provider session when an OSC status starts a new turn', () => {
// Claude reports hookless slash commands (`/context`) as OSC 7501 working then done; the session lives on (#36033).
it('keeps the cached provider session when an OSC status starts a turn after done', () => {
const server = new AgentHookServer()
const providerSession = { key: 'session_id' as const, id: 'claude-session-1' }
server.ingestRemote(
{
paneKey: PANE,
providerSession,
payload: { state: 'done', prompt: 'first', agentType: 'claude' }
},
'conn-1'
)
for (const state of ['working', 'done'] as const) {
server.ingestTerminalStatus({
paneKey: PANE,
connectionId: 'conn-1',
payload: { state, prompt: '', agentType: 'claude' }
})
}
expect(server.getStatusSnapshot()[0]).toMatchObject({ state: 'done', providerSession })
})
it('drops the cached provider session when a relaunched agent starts a turn after the old one exited', () => {
const server = new AgentHookServer()
server.ingestRemote(
@@ -226,10 +250,12 @@ describe('AgentHookServer ingestTerminalStatus', () => {
},
'conn-1'
)
server.reconcileEndedProcessForPaneKeys([PANE], { preserveResumeIdentity: true })
expect(server.getStatusSnapshot()[0]).toMatchObject({ providerSessionOnly: true })
server.ingestTerminalStatus({
paneKey: PANE,
connectionId: 'conn-1',
payload: { state: 'working', prompt: 'second', agentType: 'claude' }
payload: { state: 'working', prompt: '', agentType: 'claude' }
})
expect(server.getStatusSnapshot()[0]?.providerSession).toBeUndefined()
@@ -280,13 +280,14 @@ export abstract class AgentHookServerIngestTerminal extends AgentHookServerInges
event.payload.agentType && event.payload.agentType !== 'unknown'
? event.payload.agentType
: undefined
// Launch seeds are not completed turns; real completed sessions must not leak into reused panes.
// A live agent's turn after `done` keeps its session (OSC 7501 reports hookless slash commands); a new run or an exited agent's resume-only row does not. Launch seeds are not completed turns.
const preservedProviderSession =
previous?.providerSession &&
(claimedAgentType === undefined || claimedAgentType === previous.payload.agentType) &&
(previous.payload.state !== 'done' ||
event.payload.state === 'done' ||
previous.observation?.origin === 'launch')
previous.observation?.origin === 'launch' ||
(!newRun && previous.providerSessionOnly !== true))
? previous.providerSession
: undefined
// Without a new main-agent fact, preserve it only while the row's state and agent still match.