|
|
@@ -9,6 +9,7 @@ import AgentLoop from '@deepseek-ai/dsh-agent-loop'
|
|
|
import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-testkit'
|
|
|
import SessionStore, { SESSION_FORMAT_VERSION, SessionId } from '@deepseek-ai/dsh-session'
|
|
|
import type { SessionEvent, SessionHeader } from '@deepseek-ai/dsh-session'
|
|
|
+import type { SessionObservation } from '@deepseek-ai/dsh-session-query'
|
|
|
import JsonlSessionPersistence from '@deepseek-ai/dsh-session-persistence-jsonl'
|
|
|
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
|
|
|
import type { ProjectionDefinition } from '@deepseek-ai/dsh-session-projection'
|
|
|
@@ -23,6 +24,7 @@ import SubagentRuntime, {
|
|
|
import * as SubagentSpawn from '@deepseek-ai/dsh-subagent-spawn-in-process'
|
|
|
import * as SubagentFork from '@deepseek-ai/dsh-subagent-fork-in-process'
|
|
|
import { MockAdapter, textResponse } from '../../../core/agent-loop/tests/mock-adapter.ts'
|
|
|
+import { TestSessionQuery } from './test-session-query.ts'
|
|
|
|
|
|
type Script = ConstructorParameters<typeof MockAdapter>[0]
|
|
|
|
|
|
@@ -51,6 +53,7 @@ async function setup(
|
|
|
ctx.provide('storageDomain', facility)
|
|
|
await ctx.plugin(SessionProjectionCache, { writeEveryEvents: 100, writeIntervalMs: 60_000 })
|
|
|
}
|
|
|
+ await ctx.plugin(TestSessionQuery)
|
|
|
await ctx.plugin(SubagentRuntime)
|
|
|
await ctx.plugin(SubagentSpawn, { providerName: 'spawn' })
|
|
|
await ctx.plugin(SubagentFork, { providerName: 'fork' })
|
|
|
@@ -152,10 +155,11 @@ const hostileProjectionDefinition = {
|
|
|
} satisfies ProjectionDefinition<'subagentListHostileProbe', { poisoned?: boolean | undefined }>
|
|
|
|
|
|
describe('SubagentRuntime.listChildren', () => {
|
|
|
- it('lists live children without persistence, query services, or the continuation runtime', async () => {
|
|
|
+ it('lists live children without persistence or the continuation runtime', async () => {
|
|
|
const ctx = new Context()
|
|
|
await ctx.plugin(SessionStore)
|
|
|
await ctx.plugin(SessionProjectionRegistry)
|
|
|
+ await ctx.plugin(TestSessionQuery)
|
|
|
await ctx.plugin(SubagentRuntime)
|
|
|
expect(ctx.get('jobs')).toBeUndefined()
|
|
|
expect(ctx.get('agents')).toBeUndefined()
|
|
|
@@ -196,6 +200,17 @@ describe('SubagentRuntime.listChildren', () => {
|
|
|
)
|
|
|
})
|
|
|
|
|
|
+ it('fails loud when the Session query service is not mounted', async () => {
|
|
|
+ const ctx = new Context()
|
|
|
+ await ctx.plugin(SessionStore)
|
|
|
+ await ctx.plugin(SessionProjectionRegistry)
|
|
|
+ await ctx.plugin(SubagentRuntime)
|
|
|
+
|
|
|
+ await expect(ctx.subagents.listChildren(SessionId('no-query-parent'))).rejects.toThrow(
|
|
|
+ expect.objectContaining({ code: 'SUBAGENT_CONTROL_QUERY_UNAVAILABLE' }) as Error,
|
|
|
+ )
|
|
|
+ })
|
|
|
+
|
|
|
it('lists a persisted continuable child as inactive with its durable label', async () => {
|
|
|
const { ctx, parent } = await setup([textResponse('done')])
|
|
|
const childId = await startChild(ctx, parent, 'summarize the doc')
|
|
|
@@ -301,6 +316,76 @@ describe('SubagentRuntime.listChildren', () => {
|
|
|
await expect(ctx.subagents.listChildren(parent.id)).resolves.toEqual([])
|
|
|
})
|
|
|
|
|
|
+ it('contains a live child projection failure as one corrupt diagnostic', async () => {
|
|
|
+ const { ctx, parent } = await setup([])
|
|
|
+ const childId = SessionId('live-projection-failure')
|
|
|
+ const child = ctx.sessions.create(childId, {
|
|
|
+ meta: { parentSession: parent.id, origin: 'subagent' },
|
|
|
+ })
|
|
|
+ child.append('subagent/descriptor', descriptorPayload('broken live child'))
|
|
|
+ const snapshot = ctx.sessionProjections.snapshot.bind(ctx.sessionProjections)
|
|
|
+ vi.spyOn(ctx.sessionProjections, 'snapshot').mockImplementation((session, keys) => {
|
|
|
+ if (session.id === childId) throw new Error('projection failed')
|
|
|
+ return snapshot(session, keys)
|
|
|
+ })
|
|
|
+
|
|
|
+ await expect(ctx.subagents.listChildren(parent.id)).resolves.toContainEqual({
|
|
|
+ kind: 'diagnostic', id: childId, reason: 'corrupt',
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ it('maps a non-Error cold observation failure to unavailable', async () => {
|
|
|
+ const { ctx, parent } = await setup([])
|
|
|
+ const childId = await authorChild(ctx, '00000000-0000-4000-8000-00000000aa01', {
|
|
|
+ parentSession: parent.id,
|
|
|
+ origin: 'subagent',
|
|
|
+ }, childEvents(descriptorPayload('unreadable child')))
|
|
|
+ const observe = ctx.sessionQuery.observeSession.bind(ctx.sessionQuery)
|
|
|
+ vi.spyOn(ctx.sessionQuery, 'observeSession').mockImplementation((id, options) => {
|
|
|
+ if (id === childId) {
|
|
|
+ return Promise.reject('backend unavailable') // oxlint-disable-line typescript/prefer-promise-reject-errors
|
|
|
+ }
|
|
|
+ return observe(id, options)
|
|
|
+ })
|
|
|
+
|
|
|
+ await expect(ctx.subagents.listChildren(parent.id)).resolves.toContainEqual({
|
|
|
+ kind: 'diagnostic', id: childId, reason: 'unavailable',
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ it('releases a cold observation when cancellation lands after its read', async () => {
|
|
|
+ const { ctx, parent } = await setup([])
|
|
|
+ const controller = new AbortController()
|
|
|
+ const childId = await authorChild(ctx, '00000000-0000-4000-8000-00000000aa02', {
|
|
|
+ parentSession: parent.id,
|
|
|
+ origin: 'subagent',
|
|
|
+ }, childEvents(descriptorPayload('cancelled child')))
|
|
|
+ const dispose = vi.fn()
|
|
|
+ vi.spyOn(ctx.sessionQuery, 'observeSession').mockImplementation((id) => {
|
|
|
+ if (id !== childId) throw new Error(`unexpected observation: ${id}`)
|
|
|
+ controller.abort(new Error('cancelled after observation'))
|
|
|
+ return Promise.resolve({
|
|
|
+ source: 'prepared',
|
|
|
+ header: {
|
|
|
+ version: SESSION_FORMAT_VERSION,
|
|
|
+ id: childId,
|
|
|
+ createdAt: 1,
|
|
|
+ parentSession: parent.id,
|
|
|
+ origin: 'subagent',
|
|
|
+ },
|
|
|
+ events: [],
|
|
|
+ cursor: -1,
|
|
|
+ projections: { asOfSeq: -1, values: {} },
|
|
|
+ retain: vi.fn(),
|
|
|
+ [Symbol.dispose]: dispose,
|
|
|
+ } as unknown as SessionObservation)
|
|
|
+ })
|
|
|
+
|
|
|
+ await expect(ctx.subagents.listChildren(parent.id, controller.signal))
|
|
|
+ .rejects.toMatchObject({ code: 'CANCELLED' })
|
|
|
+ expect(dispose).toHaveBeenCalledOnce()
|
|
|
+ })
|
|
|
+
|
|
|
it('lists a one-shot child with its durable creation label', async () => {
|
|
|
const { ctx, parent } = await setup([])
|
|
|
const labeled = await authorChild(ctx, '00000000-0000-4000-8000-00000000ab02', {
|
|
|
@@ -428,7 +513,7 @@ describe('SubagentRuntime.listChildren', () => {
|
|
|
asOfSeq: 2,
|
|
|
values: { subagent: { mode: 'continuable', label: 'cached own', seq: 2 } },
|
|
|
})
|
|
|
- const inspect = vi.spyOn(ctx.sessionPersistence, 'inspect')
|
|
|
+ const inspect = vi.spyOn(ctx.sessionPersistence, 'borrowSession')
|
|
|
await expect(ctx.subagents.listChildren(parent.id)).resolves.toEqual([{
|
|
|
kind: 'child', id: child, label: 'cached own', mode: 'continuable',
|
|
|
activity: 'inactive', hasChildren: false,
|
|
|
@@ -458,7 +543,7 @@ describe('SubagentRuntime.listChildren', () => {
|
|
|
asOfSeq: 2,
|
|
|
values: { subagent: { mode: 'continuable', label: 'ancestor label', seq: 2 } },
|
|
|
})
|
|
|
- const inspect = vi.spyOn(ctx.sessionPersistence, 'inspect')
|
|
|
+ const inspect = vi.spyOn(ctx.sessionPersistence, 'borrowSession')
|
|
|
await expect(ctx.subagents.listChildren(parent.id)).resolves.toEqual([{
|
|
|
kind: 'child', id: forkChild, label: 'own label', mode: 'continuable',
|
|
|
activity: 'inactive', hasChildren: false,
|
|
|
@@ -481,12 +566,12 @@ describe('SubagentRuntime.listChildren', () => {
|
|
|
parentSession: parent.id,
|
|
|
origin: 'subagent',
|
|
|
}, childEvents(descriptorPayload('reborn child')))
|
|
|
- const original = ctx.sessionPersistence.inspect.bind(ctx.sessionPersistence)
|
|
|
- ctx.sessionPersistence.inspect = async (sessionId, signal) => {
|
|
|
+ const original = ctx.sessionPersistence.borrowSession.bind(ctx.sessionPersistence)
|
|
|
+ ctx.sessionPersistence.borrowSession = async (sessionId, signal) => {
|
|
|
const result = await original(sessionId, signal)
|
|
|
if (sessionId !== reborn) return result
|
|
|
// The id was re-published as a different lifecycle after enumeration.
|
|
|
- return { ...result, meta: mutate(result.meta) }
|
|
|
+ return { ...result, inspection: { ...result.inspection, meta: mutate(result.inspection.meta) } }
|
|
|
}
|
|
|
const entries = await ctx.subagents.listChildren(parent.id)
|
|
|
expect(entries).toContainEqual({ kind: 'diagnostic', id: reborn, reason: 'corrupt' })
|
|
|
@@ -504,7 +589,7 @@ describe('SubagentRuntime.listChildren', () => {
|
|
|
}, childEvents(descriptorPayload('actually valid')))
|
|
|
// A stale cached sentinel must not out-rank the authoritative re-fold.
|
|
|
ctx.sessionProjectionCache.cachedSnapshot = () => ({ asOfSeq: 0, values: { subagent: null } })
|
|
|
- const inspect = vi.spyOn(ctx.sessionPersistence, 'inspect')
|
|
|
+ const inspect = vi.spyOn(ctx.sessionPersistence, 'borrowSession')
|
|
|
await expect(ctx.subagents.listChildren(parent.id)).resolves.toEqual([{
|
|
|
kind: 'child', id: healthy, label: 'actually valid', mode: 'continuable',
|
|
|
activity: 'inactive', hasChildren: false,
|
|
|
@@ -512,7 +597,7 @@ describe('SubagentRuntime.listChildren', () => {
|
|
|
expect(inspect).toHaveBeenCalledTimes(1)
|
|
|
})
|
|
|
|
|
|
- it('maps a child rejected by persistence inspection to unavailable', async () => {
|
|
|
+ it('maps a child rejected by persistence validation to corrupt', async () => {
|
|
|
const { ctx, parent } = await setup([])
|
|
|
// The surface-eligible user/message lacks its required surfaceOp, so the
|
|
|
// first-party inspection rejects before any projection fold can run.
|
|
|
@@ -530,7 +615,7 @@ describe('SubagentRuntime.listChildren', () => {
|
|
|
{ type: 'subagent/descriptor', seq: 2, time: 3, data: descriptorPayload('broken surface') },
|
|
|
] as SessionEvent[])
|
|
|
const entries = await ctx.subagents.listChildren(parent.id)
|
|
|
- expect(entries).toEqual([{ kind: 'diagnostic', id: invalid, reason: 'unavailable' }])
|
|
|
+ expect(entries).toEqual([{ kind: 'diagnostic', id: invalid, reason: 'corrupt' }])
|
|
|
})
|
|
|
|
|
|
it('diagnoses a malformed descriptor payload as corrupt', async () => {
|
|
|
@@ -556,10 +641,10 @@ describe('SubagentRuntime.listChildren', () => {
|
|
|
expect(entries).toEqual([{ kind: 'diagnostic', id: future, reason: 'corrupt' }])
|
|
|
})
|
|
|
|
|
|
- it('lists a fork whose seed replays an ancestor descriptor under that identity', async () => {
|
|
|
+ it('rejects a fork whose only descriptor belongs to its inherited seed', async () => {
|
|
|
const { ctx, parent } = await setup([])
|
|
|
- // The last-wins fold serves a seed-replayed ancestor descriptor until the
|
|
|
- // child's own descriptor overrides it (known deviation #1 in the design).
|
|
|
+ // A seed-replayed descriptor predates this child's own suffix and cannot
|
|
|
+ // identify the fork as a resumable child.
|
|
|
const seed = childEvents(descriptorPayload('ancestor label'))
|
|
|
const forkChild = await authorChild(ctx, '00000000-0000-4000-8000-0000000000f0', {
|
|
|
parentSession: parent.id,
|
|
|
@@ -567,12 +652,7 @@ describe('SubagentRuntime.listChildren', () => {
|
|
|
origin: 'subagent',
|
|
|
}, seed)
|
|
|
const entries = await ctx.subagents.listChildren(parent.id)
|
|
|
- expect(entries).toEqual([
|
|
|
- {
|
|
|
- kind: 'child', id: forkChild, label: 'ancestor label', mode: 'continuable',
|
|
|
- activity: 'inactive', hasChildren: false,
|
|
|
- },
|
|
|
- ])
|
|
|
+ expect(entries).toEqual([{ kind: 'diagnostic', id: forkChild, reason: 'corrupt' }])
|
|
|
})
|
|
|
|
|
|
it('does not filter by provider availability: children of unmounted providers stay listed', async () => {
|
|
|
@@ -613,7 +693,7 @@ describe('SubagentRuntime.listChildren', () => {
|
|
|
})
|
|
|
})
|
|
|
|
|
|
- it('contains a foreign unit failure during a live snapshot to that child as corrupt', async () => {
|
|
|
+ it('does not evaluate an unrelated wire view when exposing a live child identity', async () => {
|
|
|
const { ctx, parent } = await setup([])
|
|
|
ctx.sessionProjections.register(hostileProjectionDefinition)
|
|
|
const poisonedId = SessionId('live-poisoned-child')
|
|
|
@@ -629,7 +709,10 @@ describe('SubagentRuntime.listChildren', () => {
|
|
|
healthy.append('turn/start', { turn: 1 })
|
|
|
healthy.append('subagent/descriptor', descriptorPayload('live healthy'))
|
|
|
const entries = await ctx.subagents.listChildren(parent.id)
|
|
|
- expect(entries).toContainEqual({ kind: 'diagnostic', id: poisonedId, reason: 'corrupt' })
|
|
|
+ expect(entries).toContainEqual({
|
|
|
+ kind: 'child', id: poisonedId, label: 'poison me', mode: 'continuable',
|
|
|
+ activity: 'running', hasChildren: false,
|
|
|
+ })
|
|
|
expect(entries).toContainEqual({
|
|
|
kind: 'child', id: healthyId, label: 'live healthy', mode: 'continuable',
|
|
|
activity: 'running', hasChildren: false,
|
|
|
@@ -652,8 +735,8 @@ describe('SubagentRuntime.listChildren', () => {
|
|
|
parentSession: parent.id,
|
|
|
origin: 'subagent',
|
|
|
}, childEvents(descriptorPayload('flaky storage')))
|
|
|
- const original = ctx.sessionPersistence.inspect.bind(ctx.sessionPersistence)
|
|
|
- ctx.sessionPersistence.inspect = (sessionId, signal) => {
|
|
|
+ const original = ctx.sessionPersistence.borrowSession.bind(ctx.sessionPersistence)
|
|
|
+ ctx.sessionPersistence.borrowSession = (sessionId, signal) => {
|
|
|
if (sessionId === flaky) {
|
|
|
return Promise.reject(new Error('backend read failed'))
|
|
|
}
|
|
|
@@ -669,7 +752,7 @@ describe('SubagentRuntime.listChildren', () => {
|
|
|
})
|
|
|
// Nothing is memoized: with the backend healthy again, the next listing
|
|
|
// folds the same child to its identity.
|
|
|
- ctx.sessionPersistence.inspect = original
|
|
|
+ ctx.sessionPersistence.borrowSession = original
|
|
|
await expect(ctx.subagents.listChildren(parent.id)).resolves.toContainEqual({
|
|
|
kind: 'child', id: flaky, label: 'flaky storage', mode: 'continuable',
|
|
|
activity: 'inactive', hasChildren: false,
|
|
|
@@ -723,8 +806,8 @@ describe('SubagentRuntime.listChildren', () => {
|
|
|
origin: 'subagent',
|
|
|
}, childEvents(descriptorPayload('grandchild')))
|
|
|
const inspected: SessionId[] = []
|
|
|
- const original = ctx.sessionPersistence.inspect.bind(ctx.sessionPersistence)
|
|
|
- ctx.sessionPersistence.inspect = (sessionId, signal) => {
|
|
|
+ const original = ctx.sessionPersistence.borrowSession.bind(ctx.sessionPersistence)
|
|
|
+ ctx.sessionPersistence.borrowSession = (sessionId, signal) => {
|
|
|
inspected.push(sessionId)
|
|
|
return original(sessionId, signal)
|
|
|
}
|
|
|
@@ -755,8 +838,8 @@ describe('SubagentRuntime.listChildren', () => {
|
|
|
live.append('subagent/descriptor', descriptorPayload('live mixed child'))
|
|
|
|
|
|
const inspected: SessionId[] = []
|
|
|
- const original = ctx.sessionPersistence.inspect.bind(ctx.sessionPersistence)
|
|
|
- ctx.sessionPersistence.inspect = (sessionId, signal) => {
|
|
|
+ const original = ctx.sessionPersistence.borrowSession.bind(ctx.sessionPersistence)
|
|
|
+ ctx.sessionPersistence.borrowSession = (sessionId, signal) => {
|
|
|
inspected.push(sessionId)
|
|
|
return original(sessionId, signal)
|
|
|
}
|
|
|
@@ -778,7 +861,7 @@ describe('SubagentRuntime.listChildren', () => {
|
|
|
await vi.waitFor(() => {
|
|
|
expect(ctx.sessionProjectionCache.cachedSnapshot(header!)?.values.subagent).toBeDefined()
|
|
|
}, { timeout: 5_000 })
|
|
|
- const inspect = vi.spyOn(ctx.sessionPersistence, 'inspect')
|
|
|
+ const inspect = vi.spyOn(ctx.sessionPersistence, 'borrowSession')
|
|
|
await expect(ctx.subagents.listChildren(parent.id)).resolves.toEqual([{
|
|
|
kind: 'child', id: childId, label: 'cached child', mode: 'continuable',
|
|
|
activity: 'inactive', hasChildren: false,
|
|
|
@@ -797,7 +880,7 @@ describe('SubagentRuntime.listChildren', () => {
|
|
|
activity: 'inactive', hasChildren: false,
|
|
|
}]
|
|
|
// No stored row at all for a foreign child this process never ran.
|
|
|
- const inspect = vi.spyOn(ctx.sessionPersistence, 'inspect')
|
|
|
+ const inspect = vi.spyOn(ctx.sessionPersistence, 'borrowSession')
|
|
|
await expect(ctx.subagents.listChildren(parent.id)).resolves.toEqual(expected)
|
|
|
expect(inspect).toHaveBeenCalledTimes(1)
|
|
|
// A stored row whose cut predates the descriptor: the subagent key is
|
|
|
@@ -814,7 +897,7 @@ describe('SubagentRuntime.listChildren', () => {
|
|
|
parentSession: parent.id,
|
|
|
origin: 'subagent',
|
|
|
}, childEvents(descriptorPayload('uncacheable child')))
|
|
|
- const inspect = vi.spyOn(ctx.sessionPersistence, 'inspect')
|
|
|
+ const inspect = vi.spyOn(ctx.sessionPersistence, 'borrowSession')
|
|
|
await expect(ctx.subagents.listChildren(parent.id)).resolves.toEqual([{
|
|
|
kind: 'child', id: foreign, label: 'uncacheable child', mode: 'continuable',
|
|
|
activity: 'inactive', hasChildren: false,
|
|
|
@@ -833,7 +916,7 @@ describe('SubagentRuntime.listChildren', () => {
|
|
|
// is derived data, so its failure must not become a verdict.
|
|
|
throw new Error('poisoned cache row')
|
|
|
}
|
|
|
- const inspect = vi.spyOn(ctx.sessionPersistence, 'inspect')
|
|
|
+ const inspect = vi.spyOn(ctx.sessionPersistence, 'borrowSession')
|
|
|
await expect(ctx.subagents.listChildren(parent.id)).resolves.toEqual([{
|
|
|
kind: 'child', id: recovered, label: 'recovered child', mode: 'continuable',
|
|
|
activity: 'inactive', hasChildren: false,
|
|
|
@@ -912,7 +995,7 @@ describe('SubagentRuntime.listChildren', () => {
|
|
|
}, childEvents(descriptorPayload('cancelled cold read')))
|
|
|
const controller = new AbortController()
|
|
|
const entered = Promise.withResolvers<undefined>()
|
|
|
- ctx.sessionPersistence.inspect = (_sessionId, signal) => {
|
|
|
+ ctx.sessionPersistence.borrowSession = (_sessionId, signal) => {
|
|
|
entered.resolve(undefined)
|
|
|
return new Promise((_resolve, reject) => {
|
|
|
signal?.addEventListener('abort', () => {
|
|
|
@@ -935,8 +1018,8 @@ describe('SubagentRuntime.listChildren', () => {
|
|
|
origin: 'subagent',
|
|
|
}, childEvents(descriptorPayload('cancelled mid-listing')))
|
|
|
const controller = new AbortController()
|
|
|
- const original = ctx.sessionPersistence.inspect.bind(ctx.sessionPersistence)
|
|
|
- ctx.sessionPersistence.inspect = async (sessionId, signal) => {
|
|
|
+ const original = ctx.sessionPersistence.borrowSession.bind(ctx.sessionPersistence)
|
|
|
+ ctx.sessionPersistence.borrowSession = async (sessionId, signal) => {
|
|
|
const result = await original(sessionId, signal)
|
|
|
controller.abort()
|
|
|
return result
|
|
|
@@ -954,7 +1037,7 @@ describe('SubagentRuntime.listChildren', () => {
|
|
|
origin: 'subagent',
|
|
|
}, childEvents(descriptorPayload('aborted behind a failure')))
|
|
|
const controller = new AbortController()
|
|
|
- ctx.sessionPersistence.inspect = () => {
|
|
|
+ ctx.sessionPersistence.borrowSession = () => {
|
|
|
// The read fails while the caller aborts: cancellation normalization
|
|
|
// must fail the listing rather than return a one-diagnostic success.
|
|
|
controller.abort()
|
|
|
@@ -1188,11 +1271,17 @@ describe('SubagentRuntime.listDescendants', () => {
|
|
|
createdAt: 1,
|
|
|
origin: 'subagent',
|
|
|
}, childEvents(descriptorPayload('lineage checked')))
|
|
|
- const realInspect = ctx.sessionPersistence.inspect.bind(ctx.sessionPersistence)
|
|
|
- ctx.sessionPersistence.inspect = async (sessionId, signal) => {
|
|
|
+ const realInspect = ctx.sessionPersistence.borrowSession.bind(ctx.sessionPersistence)
|
|
|
+ ctx.sessionPersistence.borrowSession = async (sessionId, signal) => {
|
|
|
const inspected = await realInspect(sessionId, signal)
|
|
|
// The exact read reports a different durable parent than enumeration did.
|
|
|
- return { ...inspected, meta: { ...inspected.meta, parentSession: SessionId('someone-else') } }
|
|
|
+ return {
|
|
|
+ ...inspected,
|
|
|
+ inspection: {
|
|
|
+ ...inspected.inspection,
|
|
|
+ meta: { ...inspected.inspection.meta, parentSession: SessionId('someone-else') },
|
|
|
+ },
|
|
|
+ }
|
|
|
}
|
|
|
await expect(ctx.subagents.listDescendants(parent.id)).resolves.toEqual([
|
|
|
{ kind: 'diagnostic', id: childId, reason: 'corrupt', parentId: parent.id, depth: 1 },
|