fix(omp): ignore nested task session ownership (#21663)

This commit is contained in:
Neil
2026-09-19 05:25:16 -07:00
committed by GitHub
parent 6abd1ce53b
commit 5cf65d79fc
4 changed files with 38 additions and 1 deletions
@@ -221,6 +221,35 @@ describe('getPiAgentStatusExtensionSource', () => {
])
})
it('does not let a nested OMP session replace the root resume identity', async () => {
const harness = createHarness({ kind: 'omp' })
const root = {
getSessionId: () => 'root-session',
getSessionFile: () => '/tmp/root.jsonl',
getHeader: () => ({ parentSession: undefined })
}
const child = {
getSessionId: () => 'child-session',
getSessionFile: () => '/tmp/root-artifacts/worker.jsonl',
getHeader: () => ({ parentSession: '/tmp/root.jsonl' })
}
await harness.callHook('agent_start', undefined, { sessionManager: child })
await harness.callHook('agent_start', undefined, { sessionManager: root })
await vi.waitFor(() => expect(harness.fetchMock).toHaveBeenCalledTimes(1))
const payloads = harness.fetchMock.mock.calls.map(
([_event, init]) => JSON.parse(String(init?.body)).payload
)
expect(payloads).toEqual([
{
hook_event_name: 'agent_start',
session_id: 'root-session',
session_file: '/tmp/root.jsonl'
}
])
})
it.each([
['OMP extension', { kind: 'omp' as const }],
['runtime-routed OMP', { kind: 'pi' as const, title: 'omp' }]
@@ -15,6 +15,7 @@ export type HookContext = {
sessionManager?: {
getSessionId?: () => unknown
getSessionFile?: () => unknown
getHeader?: () => unknown
}
}
@@ -30,6 +30,9 @@ export function getOmpSessionOwnerHandlerSourceLines(): string[] {
' if (ctx?.agentKind === "sub") return false',
' const current = sessionProvenance(ctx)',
' if (!current) return true',
" // A task transcript is never the pane's resumable root, even when its",
' // callback arrives before the root session reports on the shared hook.',
' if (current.parent) return false',
' // Keep ownership through module reload and shutdown while child sessions drain.',
" const key = Symbol.for('orca.omp.status-session-owners')",
' let owners = Reflect.get(globalThis, key)',
+5 -1
View File
@@ -62,11 +62,16 @@ try {
await new Promise((resolve) => setTimeout(resolve, 60))
}
const extension = await load()
const childExtension = await load()
await emit(extension, 'session_start', root)
for (const phase of ['initial', 'new']) {
if (phase === 'new') {
await root.newSession()
}
await child.newSession({ parentSession: root.getSessionFile() })
const beforeChildFirst = posts.length
await emit(childExtension, 'agent_start', child)
assert.equal(posts.length, beforeChildFirst)
assert.equal(root.isSessionOnDisk(), false)
await emit(extension, 'agent_start', root)
const session = extractAgentProviderSession('omp', posts.at(-1))
@@ -94,7 +99,6 @@ try {
assert.ok(JSON.stringify(transcript.messages).includes(`Transcript proof ${kind} ${phase}`))
transcripts.push({ kind, phase, sessionId: session.id, messages: transcript.messages })
const beforeChild = posts.length
const childExtension = await load()
await emit(childExtension, 'session_start', child)
await emit(childExtension, 'agent_start', child)
await emit(childExtension, 'agent_end', child)