import { createServer } from 'node:http' import type { IncomingMessage, Server, ServerResponse } from 'node:http' import { mkdtemp, rm } from 'node:fs/promises' import { join } from 'node:path' import { tmpdir } from 'node:os' import { afterEach, describe, expect, it, vi } from 'vitest' import { Context } from 'cordis' import { AgentMessageId, type Agent, type AgentHandle } from '@deepseek-ai/dsh-agent' import SessionStore, { 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 SubagentResult, type SubagentRunEndInfo } from '@deepseek-ai/dsh-subagent' import { HarnessSdkServer, type JsonRpcTransportPeer } from '../src/index.ts' class FakeTransport implements JsonRpcTransportPeer { notifications: { method: string; params?: Record }[] = [] async request(method: string, params: Record): Promise { throw new Error(`the SDK server should not call host JSON-RPC method ${method} with ${JSON.stringify(params)}`) } notify(method: string, params?: Record): void { this.notifications.push(params === undefined ? { method } : { method, params }) } } const servers: Server[] = [] afterEach(async () => { await Promise.all(servers.splice(0).map(server => new Promise(resolve => server.close(resolve)))) vi.unstubAllEnvs() }) async function mockCompletionServer(): Promise<{ url: string; requests: unknown[]; headers: IncomingMessage['headers'][] }> { const requests: unknown[] = [] const headers: IncomingMessage['headers'][] = [] const server = createServer((request: IncomingMessage, response: ServerResponse) => { let body = '' request.on('data', (chunk: Buffer) => { body += chunk.toString('utf8') }) request.on('end', () => { requests.push(JSON.parse(body)) headers.push(request.headers) response.writeHead(200, { 'content-type': 'text/event-stream' }) response.write('data: {"choices":[{"delta":{"role":"assistant","content":null,"reasoning_content":""}}]}\n\n') response.write('data: {"choices":[{"delta":{"content":"done"}}]}\n\n') response.write('data: {"choices":[{"delta":{"content":""},"finish_reason":"stop"}],"usage":{"prompt_tokens":3,"completion_tokens":1}}\n\n') response.write('data: [DONE]\n\n') response.end() }) }) servers.push(server) await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)) const address = server.address() if (address === null || typeof address === 'string') throw new Error('no port') return { url: `http://127.0.0.1:${address.port}`, requests, headers } } async function makeHarness(storageDir: string) { const ctx = new Context() await ctx.plugin(agentCore, { workspaceContext: false }) await ctx.plugin(SubagentService) await ctx.plugin(SessionPersistenceJsonl, { root: storageDir }) await new Promise(resolve => setTimeout(resolve, 50)) return ctx } /** Drive the owning service so test lifecycle events carry the real parent scope. */ async function settleSubagent( ctx: Context, parent: Agent, info: Omit & { localAgent: Agent | undefined }, beforeSettle?: () => Promise, ): Promise { const result = Promise.withResolvers() const disposeProvider = ctx.subagents.registerProvider({ name: info.provider, capabilities: { outputSchema: false, depthLimit: false, toolFilter: false, persona: false }, inheritsParentContext: false, async start() { return { id: info.id, localAgent: info.localAgent, result: result.promise, dispose: () => Promise.resolve(), } }, }) try { const run = await ctx.subagents.start(info.provider, { parent, 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 { disposeProvider() } } describe('HarnessSdkServer', () => { it('creates a harness agent and calls the configured OpenAI-compatible endpoint', async () => { const storageDir = await mkdtemp(join(tmpdir(), 'dsh-jsonrpc-')) const llmServer = await mockCompletionServer() vi.stubEnv('DEEPSEEK_API_KEY', 'test-key') vi.stubEnv('DEEPSEEK_BASE_URL', llmServer.url) const ctx = await makeHarness(storageDir) try { const transport = new FakeTransport() const server = new HarnessSdkServer(ctx, transport) const init = await server.handleRequest('initialize', { cwd: storageDir, provider: 'deepseek', model: 'dsagent-model', }) as { serverInfo: { name: string } } expect(init.serverInfo.name).toBe('deepseek-harness-sdk-runtime') await server.handleRequest('session/prompt', { sessionId: 'main', contentBlocks: [{ type: 'text', text: 'fix it' }], }) expect(llmServer.requests).toHaveLength(1) const body = llmServer.requests[0] as { model: string; messages: { role: string }[] } expect(body.model).toBe('dsagent-model') expect(body.messages[0]?.role).toBe('system') expect(body.messages.at(-1)?.role).toBe('user') expect(llmServer.headers[0]?.authorization).toBe('Bearer test-key') expect(transport.notifications.some(n => n.method === 'session.event')).toBe(true) expect(transport.notifications.at(-1)).toMatchObject({ method: 'session.finished', params: { sessionId: 'main', status: 'ok' }, }) await server.handleRequest('session/prompt', { sessionId: 'main', contentBlocks: [{ type: 'text', text: 'again' }], }) expect(llmServer.requests).toHaveLength(2) const orphanHandle = await ctx.agents.create({ sessionId: SessionId('orphan-session'), meta: { cwd: storageDir }, agentOptions: { provider: 'deepseek', model: 'dsagent-model' }, }) orphanHandle.agent.followup([{ type: 'text', text: 'outside the sdk session map' }]) await orphanHandle.agent.whenIdle() await orphanHandle.dispose() expect(llmServer.requests).toHaveLength(3) await server.handleRequest('shutdown', undefined) } finally { await ctx.fiber.dispose() await rm(storageDir, { recursive: true, force: true }) } }) it('rejects overlapping prompts for one session without serializing other sessions', async () => { let releaseMain: (() => void) | undefined const firstMainIdle = new Promise((resolve) => { releaseMain = resolve }) const mainWhenIdle = vi.fn<() => Promise>() .mockReturnValueOnce(firstMainIdle) .mockResolvedValue(undefined) const mainFollowup = vi.fn().mockReturnValue(AgentMessageId('main-followup')) const mainAgent = ({ followup: mainFollowup, whenIdle: mainWhenIdle, } satisfies Pick) as unknown as Agent const otherFollowup = vi.fn().mockReturnValue(AgentMessageId('other-followup')) const otherAgent = ({ followup: otherFollowup, whenIdle: vi.fn(() => Promise.resolve()), } satisfies Pick) as unknown as Agent const mainHandle = { agent: mainAgent, dispose: vi.fn(() => Promise.resolve()) } const otherHandle = { agent: otherAgent, dispose: vi.fn(() => Promise.resolve()) } const create = vi.fn(async (options: { sessionId: SessionId }) => String(options.sessionId) === 'main' ? mainHandle : otherHandle) const ctx = { on: vi.fn(() => () => undefined), agents: { create, get: () => undefined }, get: () => undefined, } as unknown as Context const server = new HarnessSdkServer(ctx, new FakeTransport()) const prompt = (sessionId: string, text: string) => server.prompt({ sessionId, contentBlocks: [{ type: 'text', text }], }) const first = prompt('main', 'first') await vi.waitFor(() => { expect(mainFollowup).toHaveBeenCalledOnce() }) await expect(prompt('main', 'overlap')).rejects.toThrow('session already has an active prompt: main') await expect(prompt('other', 'independent')).resolves.toEqual({ accepted: true }) releaseMain?.() await expect(first).resolves.toEqual({ accepted: true }) await expect(prompt('main', 'sequential')).resolves.toEqual({ accepted: true }) mainWhenIdle.mockRejectedValueOnce(new Error('turn wait failed')) await expect(prompt('main', 'failing')).rejects.toThrow('turn wait failed') await expect(prompt('main', 'after failure')).resolves.toEqual({ accepted: true }) expect(mainFollowup).toHaveBeenCalledTimes(4) expect(otherFollowup).toHaveBeenCalledOnce() await server.shutdown() expect(mainHandle.dispose).toHaveBeenCalledOnce() expect(otherHandle.dispose).toHaveBeenCalledOnce() }) it('reports the message-turn outcome when a later non-message turn settles before idle', async () => { const ctx = new Context() await ctx.plugin(SessionStore) const transport = new FakeTransport() const server = new HarnessSdkServer(ctx, transport) as unknown as { prompt(params: { sessionId: string; contentBlocks: { type: 'text'; text: string }[] }): Promise sessions: Map shutdown(): Promise> } const session = ctx.sessions.create(SessionId('message-outcome')) const agent = ({ session, followup(content: { type: 'text'; text: string }[]) { session.append('turn/start', { turn: 1, trigger: { kind: 'message', source: { kind: 'user' } }, }) session.append('user/message', { content, source: { kind: 'user' }, }, { surfaceOp: 'append' }) session.append('turn/end', { turn: 1, reason: { kind: 'max-tokens' } }) session.append('turn/start', { turn: 2, trigger: { kind: 'injection', source: { kind: 'plugin', plugin: 'late-metadata' } }, }) session.append('user/message', { content: [{ type: 'text', text: 'late metadata' }], source: { kind: 'plugin', plugin: 'late-metadata' }, }, { surfaceOp: 'append' }) session.append('turn/end', { turn: 2, reason: { kind: 'completed' } }) return AgentMessageId('message-outcome') }, whenIdle: () => Promise.resolve(), } satisfies Pick) as unknown as Agent server.sessions.set('message-outcome', { handle: { agent, dispose: () => Promise.resolve() }, lastTurnEnd: undefined, activePrompt: false, }) await server.prompt({ sessionId: 'message-outcome', contentBlocks: [{ type: 'text', text: 'bounded prompt' }], }) expect(transport.notifications.findLast(notification => notification.method === 'session.finished')) .toEqual({ method: 'session.finished', params: { sessionId: 'message-outcome', status: 'error', reason: { kind: 'max-tokens' }, }, }) await server.shutdown() await ctx.fiber.dispose() }) it('notifies the host when a child session is created with parent lineage', async () => { const storageDir = await mkdtemp(join(tmpdir(), 'dsh-jsonrpc-subagent-')) const ctx = await makeHarness(storageDir) try { const transport = new FakeTransport() const server = new HarnessSdkServer(ctx, transport) ctx.sessions.create(SessionId('root-session'), { meta: { cwd: storageDir }, }) ctx.sessions.create(SessionId('child-session'), { meta: { cwd: storageDir, parentSession: SessionId('main') }, }) expect(transport.notifications).toContainEqual({ method: 'subagent.started', params: { parentSessionId: 'main', childSessionId: 'child-session', }, }) await server.shutdown() } finally { await ctx.fiber.dispose() await rm(storageDir, { recursive: true, force: true }) } }) it('creates an SDK session without an optional system prompt', async () => { const storageDir = await mkdtemp(join(tmpdir(), 'dsh-jsonrpc-no-system-')) const llmServer = await mockCompletionServer() vi.stubEnv('DEEPSEEK_API_KEY', 'test-key') vi.stubEnv('DEEPSEEK_BASE_URL', llmServer.url) const ctx = await makeHarness(storageDir) try { const server = new HarnessSdkServer(ctx, new FakeTransport()) await server.initialize({ cwd: storageDir, provider: 'deepseek', model: 'plain-model' }) await server.prompt({ sessionId: 'plain', contentBlocks: [{ type: 'text', text: 'hello' }], }) expect(llmServer.requests).toHaveLength(1) await server.shutdown() } finally { await ctx.fiber.dispose() await rm(storageDir, { recursive: true, force: true }) } }) it('notifies the host when a subagent run settles', async () => { const storageDir = await mkdtemp(join(tmpdir(), 'dsh-jsonrpc-subagent-end-')) const ctx = await makeHarness(storageDir) try { const transport = new FakeTransport() const server = new HarnessSdkServer(ctx, transport) const parentHandle = await ctx.agents.create({ sessionId: SessionId('main'), 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', params: { provider: 'spawn', agentId: 'child-session', parentSessionId: 'main', childSessionId: 'child-session', status: 'ok', stopReason: 'completed', 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 parentHandle.dispose() await server.shutdown() } finally { await ctx.fiber.dispose() await rm(storageDir, { recursive: true, force: true }) } }) 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() const sameLifetime = Promise.withResolvers() const replacement = Promise.withResolvers() 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() const remoteResult = Promise.withResolvers() 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 let handle: AgentHandle | undefined let failedHandle: AgentHandle | undefined try { parentHandle = await ctx.agents.create({ sessionId: SessionId('fallback-parent'), meta: { cwd: storageDir }, agentOptions: { provider: 'deepseek', model: 'deepseek' }, }) handle = await parentHandle.agent.ctx.agents.create({ sessionId: SessionId('fallback-child-session'), meta: { cwd: storageDir, parentSession: SessionId('fallback-parent') }, agentOptions: { provider: 'deepseek', model: 'deepseek' }, }) 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() 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, { maxTokensAsSuccess: true }) 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-live-fallback', id: SessionId('fallback-child-session'), 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', }) expect(transport.notifications).toContainEqual({ method: 'subagent.finished', params: { provider: 'fork', agentId: 'fallback-child-session', parentSessionId: 'fallback-parent', childSessionId: 'fallback-child-session', status: 'ok', stopReason: 'max-tokens', lastAssistantMessage: [], }, }) expect(transport.notifications).toContainEqual({ method: 'subagent.finished', params: { provider: 'fork', agentId: 'failed-child-session', parentSessionId: 'fallback-parent', childSessionId: 'failed-child-session', status: 'error', stopReason: 'error', }, }) expect(transport.notifications.some(n => n.method === 'subagent.finished' && n.params?.agentId === 'missing-child-agent', )).toBe(false) await server.shutdown() } finally { await handle?.dispose() await failedHandle?.dispose() await parentHandle?.dispose() await ctx.fiber.dispose() await rm(storageDir, { recursive: true, force: true }) } }) it('does not re-register an LLM adapter whose provider already has an owner', async () => { const storageDir = await mkdtemp(join(tmpdir(), 'dsh-jsonrpc-existing-llm-')) const ctx = await makeHarness(storageDir) vi.stubEnv('DEEPSEEK_API_KEY', 'test-key') await ctx.plugin(LlmDeepSeek) try { const server = new HarnessSdkServer(ctx, new FakeTransport()) const inspect = server as unknown as { hasAdapterFor(provider: string): boolean } expect(inspect.hasAdapterFor('deepseek')).toBe(true) expect(inspect.hasAdapterFor('missing-provider')).toBe(false) await server.initialize({ cwd: storageDir, provider: 'deepseek', model: 'preinstalled-model' }) expect(ctx.get('llm')?.listProviders().filter(provider => provider.id === 'deepseek')).toEqual([{ id: 'deepseek', name: 'DeepSeek' }]) await server.shutdown() } finally { await ctx.fiber.dispose() await rm(storageDir, { recursive: true, force: true }) } }) it('rejects a missing non-DeepSeek provider when an LLM service already exists', async () => { const storageDir = await mkdtemp(join(tmpdir(), 'dsh-jsonrpc-new-llm-')) const ctx = await makeHarness(storageDir) vi.stubEnv('DEEPSEEK_API_KEY', 'test-key') await ctx.plugin(LlmDeepSeek) try { const server = new HarnessSdkServer(ctx, new FakeTransport()) await expect(server.initialize({ cwd: storageDir, provider: 'private', model: 'new-model' })) .rejects.toThrow('no adapter registered for provider "private"') expect(ctx.get('llm')?.listProviders()).toEqual([{ id: 'deepseek', name: 'DeepSeek' }]) await server.shutdown() } finally { await ctx.fiber.dispose() await rm(storageDir, { recursive: true, force: true }) } }) it('classifies defensive finish states', async () => { const storageDir = await mkdtemp(join(tmpdir(), 'dsh-jsonrpc-finish-states-')) const ctx = await makeHarness(storageDir) try { const server = new HarnessSdkServer(ctx, new FakeTransport()) as unknown as { finishedStatus(reason: unknown): 'ok' | 'error' shutdown(): Promise> } expect(server.finishedStatus(undefined)).toBe('error') expect(server.finishedStatus({ kind: 'max-tokens' })).toBe('error') expect(server.finishedStatus({ kind: 'error' })).toBe('error') await server.shutdown() } finally { await ctx.fiber.dispose() await rm(storageDir, { recursive: true, force: true }) } }) it('can report max-token turn termination as an accepted evaluation result', async () => { const storageDir = await mkdtemp(join(tmpdir(), 'dsh-jsonrpc-max-tokens-success-')) const ctx = await makeHarness(storageDir) try { const server = new HarnessSdkServer(ctx, new FakeTransport(), { maxTokensAsSuccess: true }) as unknown as { finishedStatus(reason: unknown): 'ok' | 'error' shutdown(): Promise> } expect(server.finishedStatus({ kind: 'max-tokens' })).toBe('ok') expect(server.finishedStatus({ kind: 'error' })).toBe('error') await server.shutdown() } finally { await ctx.fiber.dispose() await rm(storageDir, { recursive: true, force: true }) } }) it('reports no adapter when the LLM service is absent', async () => { const ctx = new Context() try { const server = new HarnessSdkServer(ctx, new FakeTransport()) as unknown as { hasAdapterFor(model: string): boolean shutdown(): Promise> } expect(server.hasAdapterFor('missing-model')).toBe(false) await server.shutdown() } finally { await ctx.fiber.dispose() } }) it('rejects unknown JSON-RPC runtime methods', async () => { const storageDir = await mkdtemp(join(tmpdir(), 'dsh-jsonrpc-unknown-')) const ctx = await makeHarness(storageDir) try { const server = new HarnessSdkServer(ctx, new FakeTransport()) await expect(server.handleRequest('does/not/exist', {})) .rejects .toThrow('unknown DeepSeek Harness SDK runtime method: does/not/exist') await server.shutdown() } finally { await ctx.fiber.dispose() await rm(storageDir, { recursive: true, force: true }) } }) it('coalesces concurrent session creation and retries a failed creation', async () => { let resolveShared: ((handle: AgentHandle) => void) | undefined const sharedCreation = new Promise((resolve) => { resolveShared = resolve }) const sharedHandle = { agent: {} as Agent, dispose: vi.fn(() => Promise.resolve()) } const retryHandle = { agent: {} as Agent, dispose: vi.fn(() => Promise.resolve()) } const create = vi.fn<(options: unknown) => Promise>() .mockReturnValueOnce(sharedCreation) .mockRejectedValueOnce(new Error('creation failed')) .mockResolvedValueOnce(retryHandle) const ctx = { on: vi.fn(() => () => undefined), agents: { create, get: () => undefined }, get: () => undefined, } as unknown as Context const server = new HarnessSdkServer(ctx, new FakeTransport()) as unknown as { getOrCreateSession(sessionId: string): Promise<{ handle: AgentHandle }> shutdown(): Promise> } const first = server.getOrCreateSession('shared') const second = server.getOrCreateSession('shared') expect(create).toHaveBeenCalledTimes(1) resolveShared?.(sharedHandle) const [firstRecord, secondRecord] = await Promise.all([first, second]) expect(firstRecord).toBe(secondRecord) await expect(server.getOrCreateSession('retry')).rejects.toThrow('creation failed') await expect(server.getOrCreateSession('retry')).resolves.toMatchObject({ handle: retryHandle }) expect(create).toHaveBeenCalledTimes(3) await server.shutdown() expect(sharedHandle.dispose).toHaveBeenCalledOnce() expect(retryHandle.dispose).toHaveBeenCalledOnce() await expect(server.getOrCreateSession('after-shutdown')).rejects.toThrow('SDK server is shutting down') }) it('resolves a relative cwd before creating the session', async () => { const create = vi.fn<(options: unknown) => Promise>() .mockResolvedValue({ agent: {} as Agent, dispose: () => Promise.resolve() }) const ctx = { on: vi.fn(() => () => undefined), agents: { create, get: () => undefined }, get: () => ({ listProviders: () => [{ id: 'mock', name: 'Mock' }] }), } as unknown as Context const server = new HarnessSdkServer(ctx, new FakeTransport()) as unknown as { initialize(params: { cwd: string; provider: string; model: string }): Promise getOrCreateSession(sessionId: string): Promise shutdown(): Promise> } await server.initialize({ cwd: '.', provider: 'mock', model: 'model' }) await server.getOrCreateSession('relative') expect(create).toHaveBeenCalledWith(expect.objectContaining({ meta: { cwd: process.cwd() } })) await server.shutdown() }) it('settles every teardown and aggregates multiple failures', async () => { const firstDispose = vi.fn(() => { throw new Error('first teardown failed') }) const secondDispose = vi.fn(() => Promise.reject(new Error('second teardown failed'))) const ctx = { on: vi.fn(() => () => undefined), agents: { create: vi.fn(), get: () => undefined }, get: () => undefined, } as unknown as Context const server = new HarnessSdkServer(ctx, new FakeTransport()) as unknown as { sessions: Map shutdown(): Promise> } server.sessions.set('first', { handle: { agent: {} as Agent, dispose: firstDispose }, lastTurnEnd: undefined, activePrompt: false }) server.sessions.set('second', { handle: { agent: {} as Agent, dispose: secondDispose }, lastTurnEnd: undefined, activePrompt: false }) await expect(server.shutdown()).rejects.toThrow('SDK server teardown failed') expect(firstDispose).toHaveBeenCalledOnce() expect(secondDispose).toHaveBeenCalledOnce() }) it('continues teardown after a subscription disposer fails', async () => { let subscription = 0 const listenerFailure = new Error('listener teardown failed') const on = vi.fn(() => { subscription += 1 return subscription === 1 ? () => { throw listenerFailure } : () => undefined }) const ctx = { on, agents: { create: vi.fn(), get: () => undefined }, get: () => undefined, } as unknown as Context const server = new HarnessSdkServer(ctx, new FakeTransport()) await expect(server.shutdown()).rejects.toBe(listenerFailure) expect(on).toHaveBeenCalledTimes(3) }) })