|
|
@@ -11,7 +11,7 @@ import { SessionId } from '@deepseek-ai/dsh-session'
|
|
|
import * as agentCore from '@deepseek-ai/dsh-agent-spine-demo'
|
|
|
import SessionPersistenceJsonl from '@deepseek-ai/dsh-session-persistence-jsonl'
|
|
|
import * as LlmDeepSeek from '@deepseek-ai/dsh-llm-deepseek'
|
|
|
-import SubagentService, { type SubagentRunEndInfo } from '@deepseek-ai/dsh-subagent'
|
|
|
+import SubagentService, { type SubagentResult, type SubagentRunEndInfo } from '@deepseek-ai/dsh-subagent'
|
|
|
import { HarnessSdkServer, type JsonRpcTransportPeer } from '../src/index.ts'
|
|
|
|
|
|
class FakeTransport implements JsonRpcTransportPeer {
|
|
|
@@ -67,7 +67,13 @@ async function makeHarness(storageDir: string) {
|
|
|
}
|
|
|
|
|
|
/** Drive the owning service so test lifecycle events carry the real parent scope. */
|
|
|
-async function settleSubagent(ctx: Context, parent: Agent, info: SubagentRunEndInfo): Promise<void> {
|
|
|
+async function settleSubagent(
|
|
|
+ ctx: Context,
|
|
|
+ parent: Agent,
|
|
|
+ info: Omit<SubagentRunEndInfo, 'runId' | 'local'> & { localAgent: Agent | undefined },
|
|
|
+ beforeSettle?: () => Promise<void>,
|
|
|
+): Promise<void> {
|
|
|
+ const result = Promise.withResolvers<SubagentResult>()
|
|
|
const disposeProvider = ctx.subagents.registerProvider({
|
|
|
name: info.provider,
|
|
|
capabilities: { outputSchema: false, depthLimit: false, toolFilter: false, persona: false },
|
|
|
@@ -75,9 +81,8 @@ async function settleSubagent(ctx: Context, parent: Agent, info: SubagentRunEndI
|
|
|
async start() {
|
|
|
return {
|
|
|
id: info.id,
|
|
|
- result: info.lastAssistantMessage === undefined
|
|
|
- ? Promise.reject(new Error('synthetic infrastructure failure'))
|
|
|
- : Promise.resolve({ output: info.lastAssistantMessage, stopReason: info.stopReason }),
|
|
|
+ localAgent: info.localAgent,
|
|
|
+ result: result.promise,
|
|
|
dispose: () => Promise.resolve(),
|
|
|
}
|
|
|
},
|
|
|
@@ -88,6 +93,12 @@ async function settleSubagent(ctx: Context, parent: Agent, info: SubagentRunEndI
|
|
|
prompt: [],
|
|
|
signal: new AbortController().signal,
|
|
|
})
|
|
|
+ await beforeSettle?.()
|
|
|
+ if (info.lastAssistantMessage === undefined) {
|
|
|
+ result.reject(new Error('synthetic infrastructure failure'))
|
|
|
+ } else {
|
|
|
+ result.resolve({ output: info.lastAssistantMessage, stopReason: info.stopReason })
|
|
|
+ }
|
|
|
await run.result.then(() => undefined, () => undefined)
|
|
|
await run.dispose()
|
|
|
} finally {
|
|
|
@@ -268,17 +279,32 @@ describe('HarnessSdkServer', () => {
|
|
|
meta: { cwd: storageDir },
|
|
|
agentOptions: { provider: 'deepseek', model: 'deepseek' },
|
|
|
})
|
|
|
+ // A custom in-process provider may own its child at the provider/root
|
|
|
+ // scope while preserving durable parent lineage.
|
|
|
const handle = await ctx.agents.create({
|
|
|
sessionId: SessionId('child-session'),
|
|
|
meta: { cwd: storageDir, parentSession: SessionId('main') },
|
|
|
agentOptions: { provider: 'deepseek', model: 'deepseek' },
|
|
|
})
|
|
|
+ expect(ctx.agents.roots()).toContain(handle.agent)
|
|
|
+ const parentlessHandle = await parentHandle.agent.ctx.agents.create({
|
|
|
+ sessionId: SessionId('parentless-child-session'),
|
|
|
+ meta: { cwd: storageDir },
|
|
|
+ agentOptions: { model: 'deepseek' },
|
|
|
+ })
|
|
|
await settleSubagent(ctx, parentHandle.agent, {
|
|
|
provider: 'spawn',
|
|
|
id: SessionId('child-session'),
|
|
|
+ localAgent: handle.agent,
|
|
|
stopReason: 'completed',
|
|
|
lastAssistantMessage: [{ type: 'text', text: 'child done' }],
|
|
|
- })
|
|
|
+ }, () => handle.dispose())
|
|
|
+ await settleSubagent(ctx, parentHandle.agent, {
|
|
|
+ provider: 'spawn',
|
|
|
+ id: SessionId('parentless-child-session'),
|
|
|
+ localAgent: parentlessHandle.agent,
|
|
|
+ stopReason: 'error',
|
|
|
+ }, () => parentlessHandle.dispose())
|
|
|
|
|
|
expect(transport.notifications).toContainEqual({
|
|
|
method: 'subagent.finished',
|
|
|
@@ -292,8 +318,102 @@ describe('HarnessSdkServer', () => {
|
|
|
lastAssistantMessage: [{ type: 'text', text: 'child done' }],
|
|
|
},
|
|
|
})
|
|
|
+ expect(transport.notifications).toContainEqual({
|
|
|
+ method: 'subagent.finished',
|
|
|
+ params: {
|
|
|
+ provider: 'spawn',
|
|
|
+ agentId: 'parentless-child-session',
|
|
|
+ parentSessionId: 'main',
|
|
|
+ childSessionId: 'parentless-child-session',
|
|
|
+ status: 'error',
|
|
|
+ stopReason: 'error',
|
|
|
+ },
|
|
|
+ })
|
|
|
+
|
|
|
+ await parentHandle.dispose()
|
|
|
+ await server.shutdown()
|
|
|
+ } finally {
|
|
|
+ await ctx.fiber.dispose()
|
|
|
+ await rm(storageDir, { recursive: true, force: true })
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ it('ignores a remote run id that collides with a local child of the same parent', async () => {
|
|
|
+ const storageDir = await mkdtemp(join(tmpdir(), 'dsh-jsonrpc-subagent-remote-collision-'))
|
|
|
+ const ctx = await makeHarness(storageDir)
|
|
|
+ try {
|
|
|
+ const transport = new FakeTransport()
|
|
|
+ const server = new HarnessSdkServer(ctx, transport)
|
|
|
+ const parentHandle = await ctx.agents.create({
|
|
|
+ sessionId: SessionId('collision-parent'),
|
|
|
+ meta: { cwd: storageDir },
|
|
|
+ agentOptions: { model: 'deepseek' },
|
|
|
+ })
|
|
|
+ const collidingChild = await parentHandle.agent.ctx.agents.create({
|
|
|
+ sessionId: SessionId('remote-run-id'),
|
|
|
+ meta: { cwd: storageDir, parentSession: SessionId('collision-parent') },
|
|
|
+ agentOptions: { model: 'deepseek' },
|
|
|
+ })
|
|
|
+
|
|
|
+ await settleSubagent(ctx, parentHandle.agent, {
|
|
|
+ provider: 'remote',
|
|
|
+ id: SessionId('remote-run-id'),
|
|
|
+ localAgent: undefined,
|
|
|
+ stopReason: 'completed',
|
|
|
+ lastAssistantMessage: [],
|
|
|
+ })
|
|
|
+
|
|
|
+ expect(transport.notifications.some(notification =>
|
|
|
+ notification.method === 'subagent.finished'
|
|
|
+ && notification.params?.agentId === 'remote-run-id',
|
|
|
+ )).toBe(false)
|
|
|
+
|
|
|
+ await collidingChild.dispose()
|
|
|
+ await parentHandle.dispose()
|
|
|
+ await server.shutdown()
|
|
|
+ } finally {
|
|
|
+ await ctx.fiber.dispose()
|
|
|
+ await rm(storageDir, { recursive: true, force: true })
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ it('retains locality across continuation runs on one live child', async () => {
|
|
|
+ const storageDir = await mkdtemp(join(tmpdir(), 'dsh-jsonrpc-subagent-continuation-'))
|
|
|
+ const ctx = await makeHarness(storageDir)
|
|
|
+ try {
|
|
|
+ const transport = new FakeTransport()
|
|
|
+ const server = new HarnessSdkServer(ctx, transport)
|
|
|
+ const parentHandle = await ctx.agents.create({
|
|
|
+ sessionId: SessionId('continuation-parent'),
|
|
|
+ meta: { cwd: storageDir },
|
|
|
+ agentOptions: { model: 'deepseek' },
|
|
|
+ })
|
|
|
+ const childHandle = await parentHandle.agent.ctx.agents.create({
|
|
|
+ sessionId: SessionId('continuation-child'),
|
|
|
+ meta: { cwd: storageDir, parentSession: SessionId('continuation-parent') },
|
|
|
+ agentOptions: { model: 'deepseek' },
|
|
|
+ })
|
|
|
+
|
|
|
+ await settleSubagent(ctx, parentHandle.agent, {
|
|
|
+ provider: 'continuation',
|
|
|
+ id: SessionId('continuation-child'),
|
|
|
+ localAgent: childHandle.agent,
|
|
|
+ stopReason: 'completed',
|
|
|
+ lastAssistantMessage: [{ type: 'text', text: 'first' }],
|
|
|
+ })
|
|
|
+ await settleSubagent(ctx, parentHandle.agent, {
|
|
|
+ provider: 'continuation',
|
|
|
+ id: SessionId('continuation-child'),
|
|
|
+ localAgent: childHandle.agent,
|
|
|
+ stopReason: 'completed',
|
|
|
+ lastAssistantMessage: [{ type: 'text', text: 'second' }],
|
|
|
+ }, () => childHandle.dispose())
|
|
|
+
|
|
|
+ expect(transport.notifications.filter(notification =>
|
|
|
+ notification.method === 'subagent.finished'
|
|
|
+ && notification.params?.childSessionId === 'continuation-child',
|
|
|
+ )).toHaveLength(2)
|
|
|
|
|
|
- await handle.dispose()
|
|
|
await parentHandle.dispose()
|
|
|
await server.shutdown()
|
|
|
} finally {
|
|
|
@@ -302,7 +422,198 @@ describe('HarnessSdkServer', () => {
|
|
|
}
|
|
|
})
|
|
|
|
|
|
- it('falls back to live agent lineage for uncached subagent end events', async () => {
|
|
|
+ it('correlates reused local ids by parent scope when runs settle out of order', async () => {
|
|
|
+ const storageDir = await mkdtemp(join(tmpdir(), 'dsh-jsonrpc-subagent-reuse-'))
|
|
|
+ const ctx = await makeHarness(storageDir)
|
|
|
+ try {
|
|
|
+ const transport = new FakeTransport()
|
|
|
+ const server = new HarnessSdkServer(ctx, transport)
|
|
|
+ const oldParent = await ctx.agents.create({
|
|
|
+ sessionId: SessionId('old-parent'),
|
|
|
+ meta: { cwd: storageDir },
|
|
|
+ agentOptions: { model: 'deepseek' },
|
|
|
+ })
|
|
|
+ const oldChild = await oldParent.agent.ctx.agents.create({
|
|
|
+ sessionId: SessionId('reused-child'),
|
|
|
+ meta: { cwd: storageDir, parentSession: SessionId('old-parent') },
|
|
|
+ agentOptions: { model: 'deepseek' },
|
|
|
+ })
|
|
|
+ const first = Promise.withResolvers<SubagentResult>()
|
|
|
+ const sameLifetime = Promise.withResolvers<SubagentResult>()
|
|
|
+ const replacement = Promise.withResolvers<SubagentResult>()
|
|
|
+ const results = [first.promise, sameLifetime.promise, replacement.promise]
|
|
|
+ let starts = 0
|
|
|
+ let currentLocalAgent = oldChild.agent
|
|
|
+ const disposeProvider = ctx.subagents.registerProvider({
|
|
|
+ name: 'reused',
|
|
|
+ capabilities: { outputSchema: false, depthLimit: false, toolFilter: false, persona: false },
|
|
|
+ inheritsParentContext: false,
|
|
|
+ start() {
|
|
|
+ const result = results[starts]
|
|
|
+ starts += 1
|
|
|
+ if (result === undefined) throw new Error('unexpected fourth reused-id run')
|
|
|
+ return Promise.resolve({ id: SessionId('reused-child'), localAgent: currentLocalAgent, result, dispose: () => Promise.resolve() })
|
|
|
+ },
|
|
|
+ })
|
|
|
+
|
|
|
+ const firstRun = await ctx.subagents.start('reused', {
|
|
|
+ parent: oldParent.agent,
|
|
|
+ prompt: [],
|
|
|
+ signal: new AbortController().signal,
|
|
|
+ })
|
|
|
+ const sameLifetimeRun = await ctx.subagents.start('reused', {
|
|
|
+ parent: oldParent.agent,
|
|
|
+ prompt: [],
|
|
|
+ signal: new AbortController().signal,
|
|
|
+ })
|
|
|
+ sameLifetime.resolve({ output: [{ type: 'text', text: 'same lifetime' }], stopReason: 'completed' })
|
|
|
+ await sameLifetimeRun.result
|
|
|
+ await oldChild.dispose()
|
|
|
+ const newParent = await ctx.agents.create({
|
|
|
+ sessionId: SessionId('new-parent'),
|
|
|
+ meta: { cwd: storageDir },
|
|
|
+ agentOptions: { model: 'deepseek' },
|
|
|
+ })
|
|
|
+ const newChild = await newParent.agent.ctx.agents.create({
|
|
|
+ sessionId: SessionId('reused-child'),
|
|
|
+ meta: { cwd: storageDir, parentSession: SessionId('new-parent') },
|
|
|
+ agentOptions: { model: 'deepseek' },
|
|
|
+ })
|
|
|
+ currentLocalAgent = newChild.agent
|
|
|
+ const secondRun = await ctx.subagents.start('reused', {
|
|
|
+ parent: newParent.agent,
|
|
|
+ prompt: [],
|
|
|
+ signal: new AbortController().signal,
|
|
|
+ })
|
|
|
+
|
|
|
+ replacement.resolve({ output: [{ type: 'text', text: 'new lifetime' }], stopReason: 'completed' })
|
|
|
+ await secondRun.result
|
|
|
+ first.resolve({ output: [{ type: 'text', text: 'old lifetime' }], stopReason: 'completed' })
|
|
|
+ await firstRun.result
|
|
|
+ await Promise.resolve()
|
|
|
+
|
|
|
+ const finished = transport.notifications.filter(notification =>
|
|
|
+ notification.method === 'subagent.finished'
|
|
|
+ && notification.params?.childSessionId === 'reused-child',
|
|
|
+ )
|
|
|
+ expect(finished.map(notification => notification.params?.lastAssistantMessage)).toEqual([
|
|
|
+ [{ type: 'text', text: 'same lifetime' }],
|
|
|
+ [{ type: 'text', text: 'new lifetime' }],
|
|
|
+ [{ type: 'text', text: 'old lifetime' }],
|
|
|
+ ])
|
|
|
+ expect(finished.map(notification => notification.params?.parentSessionId)).toEqual([
|
|
|
+ 'old-parent',
|
|
|
+ 'new-parent',
|
|
|
+ 'old-parent',
|
|
|
+ ])
|
|
|
+
|
|
|
+ await firstRun.dispose()
|
|
|
+ await sameLifetimeRun.dispose()
|
|
|
+ await secondRun.dispose()
|
|
|
+ disposeProvider()
|
|
|
+ await newChild.dispose()
|
|
|
+ await oldParent.dispose()
|
|
|
+ await newParent.dispose()
|
|
|
+ await server.shutdown()
|
|
|
+ } finally {
|
|
|
+ await ctx.fiber.dispose()
|
|
|
+ await rm(storageDir, { recursive: true, force: true })
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ it('keeps locality bound to the accepted run across provider re-registration', async () => {
|
|
|
+ const storageDir = await mkdtemp(join(tmpdir(), 'dsh-jsonrpc-subagent-provider-reuse-'))
|
|
|
+ const ctx = await makeHarness(storageDir)
|
|
|
+ try {
|
|
|
+ const transport = new FakeTransport()
|
|
|
+ const server = new HarnessSdkServer(ctx, transport)
|
|
|
+ const parent = await ctx.agents.create({
|
|
|
+ sessionId: SessionId('provider-reuse-parent'),
|
|
|
+ meta: { cwd: storageDir },
|
|
|
+ agentOptions: { model: 'deepseek' },
|
|
|
+ })
|
|
|
+ const child = await parent.agent.ctx.agents.create({
|
|
|
+ sessionId: SessionId('provider-reuse-child'),
|
|
|
+ meta: { cwd: storageDir, parentSession: SessionId('provider-reuse-parent') },
|
|
|
+ agentOptions: { model: 'deepseek' },
|
|
|
+ })
|
|
|
+ const localResult = Promise.withResolvers<SubagentResult>()
|
|
|
+ const remoteResult = Promise.withResolvers<SubagentResult>()
|
|
|
+ const unregisterLocal = ctx.subagents.registerProvider({
|
|
|
+ name: 'reused-provider',
|
|
|
+ capabilities: { outputSchema: false, depthLimit: false, toolFilter: false, persona: false },
|
|
|
+ inheritsParentContext: false,
|
|
|
+ start: () => Promise.resolve({
|
|
|
+ id: SessionId('provider-reuse-child'),
|
|
|
+ localAgent: child.agent,
|
|
|
+ result: localResult.promise,
|
|
|
+ dispose: () => Promise.resolve(),
|
|
|
+ }),
|
|
|
+ })
|
|
|
+ const localRun = await ctx.subagents.start('reused-provider', {
|
|
|
+ parent: parent.agent,
|
|
|
+ prompt: [],
|
|
|
+ signal: new AbortController().signal,
|
|
|
+ })
|
|
|
+ unregisterLocal()
|
|
|
+
|
|
|
+ const unregisterRemote = ctx.subagents.registerProvider({
|
|
|
+ name: 'reused-provider',
|
|
|
+ capabilities: { outputSchema: false, depthLimit: false, toolFilter: false, persona: false },
|
|
|
+ inheritsParentContext: false,
|
|
|
+ start: () => Promise.resolve({
|
|
|
+ id: SessionId('provider-reuse-child'),
|
|
|
+ localAgent: undefined,
|
|
|
+ result: remoteResult.promise,
|
|
|
+ dispose: () => Promise.resolve(),
|
|
|
+ }),
|
|
|
+ })
|
|
|
+ const remoteRun = await ctx.subagents.start('reused-provider', {
|
|
|
+ parent: parent.agent,
|
|
|
+ prompt: [],
|
|
|
+ signal: new AbortController().signal,
|
|
|
+ })
|
|
|
+
|
|
|
+ remoteResult.resolve({ output: [{ type: 'text', text: 'remote' }], stopReason: 'completed' })
|
|
|
+ await remoteRun.result
|
|
|
+ await Promise.resolve()
|
|
|
+ expect(transport.notifications.some(notification =>
|
|
|
+ notification.method === 'subagent.finished'
|
|
|
+ && notification.params?.lastAssistantMessage !== undefined,
|
|
|
+ )).toBe(false)
|
|
|
+
|
|
|
+ await child.dispose()
|
|
|
+ localResult.resolve({ output: [{ type: 'text', text: 'local' }], stopReason: 'completed' })
|
|
|
+ await localRun.result
|
|
|
+ await Promise.resolve()
|
|
|
+ expect(transport.notifications.filter(notification =>
|
|
|
+ notification.method === 'subagent.finished'
|
|
|
+ && notification.params?.childSessionId === 'provider-reuse-child',
|
|
|
+ )).toEqual([{
|
|
|
+ method: 'subagent.finished',
|
|
|
+ params: {
|
|
|
+ provider: 'reused-provider',
|
|
|
+ agentId: 'provider-reuse-child',
|
|
|
+ parentSessionId: 'provider-reuse-parent',
|
|
|
+ childSessionId: 'provider-reuse-child',
|
|
|
+ status: 'ok',
|
|
|
+ stopReason: 'completed',
|
|
|
+ lastAssistantMessage: [{ type: 'text', text: 'local' }],
|
|
|
+ },
|
|
|
+ }])
|
|
|
+
|
|
|
+ await localRun.dispose()
|
|
|
+ await remoteRun.dispose()
|
|
|
+ unregisterRemote()
|
|
|
+ await parent.dispose()
|
|
|
+ await server.shutdown()
|
|
|
+ } finally {
|
|
|
+ await ctx.fiber.dispose()
|
|
|
+ await rm(storageDir, { recursive: true, force: true })
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ it('uses explicit local provenance when start was missed and ignores remote runs', async () => {
|
|
|
const storageDir = await mkdtemp(join(tmpdir(), 'dsh-jsonrpc-subagent-fallback-'))
|
|
|
const ctx = await makeHarness(storageDir)
|
|
|
let parentHandle: AgentHandle | undefined
|
|
|
@@ -314,33 +625,63 @@ describe('HarnessSdkServer', () => {
|
|
|
meta: { cwd: storageDir },
|
|
|
agentOptions: { provider: 'deepseek', model: 'deepseek' },
|
|
|
})
|
|
|
- handle = await ctx.agents.create({
|
|
|
+ handle = await parentHandle.agent.ctx.agents.create({
|
|
|
sessionId: SessionId('fallback-child-session'),
|
|
|
meta: { cwd: storageDir, parentSession: SessionId('fallback-parent') },
|
|
|
agentOptions: { provider: 'deepseek', model: 'deepseek' },
|
|
|
})
|
|
|
- failedHandle = await ctx.agents.create({
|
|
|
+ const fallbackChild = handle.agent
|
|
|
+ failedHandle = await parentHandle.agent.ctx.agents.create({
|
|
|
sessionId: SessionId('failed-child-session'),
|
|
|
meta: { cwd: storageDir },
|
|
|
agentOptions: { provider: 'deepseek', model: 'deepseek' },
|
|
|
})
|
|
|
+ const missedStartResult = Promise.withResolvers<SubagentResult>()
|
|
|
+ const disposeMissedStartProvider = ctx.subagents.registerProvider({
|
|
|
+ name: 'fork',
|
|
|
+ capabilities: { outputSchema: false, depthLimit: false, toolFilter: false, persona: false },
|
|
|
+ inheritsParentContext: true,
|
|
|
+ start: () => Promise.resolve({
|
|
|
+ id: SessionId('fallback-child-session'),
|
|
|
+ localAgent: fallbackChild,
|
|
|
+ result: missedStartResult.promise,
|
|
|
+ dispose: () => Promise.resolve(),
|
|
|
+ }),
|
|
|
+ })
|
|
|
+ // Start before the server subscribes. The terminal payload still carries
|
|
|
+ // this run's exact local child without reconstructing it from ids.
|
|
|
+ const missedStartRun = await ctx.subagents.start('fork', {
|
|
|
+ parent: parentHandle.agent,
|
|
|
+ prompt: [],
|
|
|
+ signal: new AbortController().signal,
|
|
|
+ })
|
|
|
const transport = new FakeTransport()
|
|
|
const server = new HarnessSdkServer(ctx, transport)
|
|
|
|
|
|
+ missedStartResult.resolve({ output: [], stopReason: 'max-tokens' })
|
|
|
+ await missedStartRun.result
|
|
|
+ await Promise.resolve()
|
|
|
+ await missedStartRun.dispose()
|
|
|
+ disposeMissedStartProvider()
|
|
|
+ // The server also missed this agent's creation but sees the exact child
|
|
|
+ // on the run lifecycle payload.
|
|
|
await settleSubagent(ctx, parentHandle.agent, {
|
|
|
- provider: 'fork',
|
|
|
+ provider: 'fork-live-fallback',
|
|
|
id: SessionId('fallback-child-session'),
|
|
|
- stopReason: 'max-tokens',
|
|
|
+ localAgent: fallbackChild,
|
|
|
+ stopReason: 'completed',
|
|
|
lastAssistantMessage: [],
|
|
|
})
|
|
|
await settleSubagent(ctx, parentHandle.agent, {
|
|
|
provider: 'fork',
|
|
|
id: SessionId('failed-child-session'),
|
|
|
+ localAgent: failedHandle.agent,
|
|
|
stopReason: 'error',
|
|
|
})
|
|
|
await settleSubagent(ctx, parentHandle.agent, {
|
|
|
provider: 'fork',
|
|
|
id: SessionId('missing-child-agent'),
|
|
|
+ localAgent: undefined,
|
|
|
stopReason: 'error',
|
|
|
})
|
|
|
|
|
|
@@ -361,6 +702,7 @@ describe('HarnessSdkServer', () => {
|
|
|
params: {
|
|
|
provider: 'fork',
|
|
|
agentId: 'failed-child-session',
|
|
|
+ parentSessionId: 'fallback-parent',
|
|
|
childSessionId: 'failed-child-session',
|
|
|
status: 'error',
|
|
|
stopReason: 'error',
|
|
|
@@ -564,6 +906,6 @@ describe('HarnessSdkServer', () => {
|
|
|
const server = new HarnessSdkServer(ctx, new FakeTransport())
|
|
|
|
|
|
await expect(server.shutdown()).rejects.toBe(listenerFailure)
|
|
|
- expect(on).toHaveBeenCalledTimes(4)
|
|
|
+ expect(on).toHaveBeenCalledTimes(3)
|
|
|
})
|
|
|
})
|