import { describe, expect, it } from 'vitest' import { anthropicMessageToResponses, openAiChatToResponses, responsesToAnthropicMessages, responsesToOpenAiChat, } from '../../packages/server/src/services/agent-runner/adapters/responses' import { anthropicToOpenAiChat, anthropicToOpenAiResponses, openAiResponsesToAnthropicMessage, openAiToAnthropicMessage, } from '../../packages/server/src/services/agent-runner/adapters/anthropic' import { openAiChatSseToAnthropicEvents, openAiResponsesSseToAnthropicEvents, type AnthropicStreamEvent, } from '../../packages/server/src/services/agent-runner/adapters/anthropic-stream' import { anthropicMessagesSseToResponsesEvents, openAiChatSseToResponsesEvents, openAiResponsesSseToResponsesEvents, type CanonicalResponsesEvent, } from '../../packages/server/src/services/agent-runner/adapters/responses-stream' const target = { model: 'test-model' } const codexTarget = { model: 'test-model', annotateMcpToolNamespaces: true } const anthropicTarget = { provider: 'deepseek', model: 'deepseek-reasoner', baseUrl: 'https://api.deepseek.com/v1' } describe('agent runner Responses adapters', () => { it('converts Responses input to OpenAI Chat messages and tools', () => { const body = { instructions: 'be terse', max_output_tokens: 16, temperature: 0.2, top_p: 0.9, input: [ { role: 'user', content: [{ type: 'input_text', text: 'hello' }] }, { role: 'developer', content: [{ type: 'input_text', text: 'rules' }] }, { type: 'function_call', call_id: 'call_1', name: 'search', arguments: '{"q":"x"}' }, { type: 'function_call_output', call_id: 'call_1', output: 'found' }, ], tools: [{ type: 'function', name: 'search', description: 'Search', parameters: { type: 'object' } }], } expect(responsesToOpenAiChat(body, target)).toMatchObject({ model: 'test-model', max_tokens: 16, temperature: 0.2, top_p: 0.9, stream: false, messages: [ { role: 'system', content: 'be terse' }, { role: 'user', content: 'hello' }, { role: 'system', content: 'rules' }, { role: 'assistant', content: null, tool_calls: [{ id: 'call_1', type: 'function', function: { name: 'search', arguments: '{"q":"x"}' }, }], }, { role: 'tool', tool_call_id: 'call_1', content: 'found' }, ], tools: [{ type: 'function', function: { name: 'search', description: 'Search', parameters: { type: 'object' } }, }], }) }) it('groups parallel Responses function calls before Chat tool results', () => { const body = { input: [ { role: 'user', content: [{ type: 'input_text', text: 'check repo' }] }, { type: 'function_call', call_id: 'call_1', name: 'read_file', arguments: '{"path":"a.ts"}' }, { type: 'function_call', call_id: 'call_2', name: 'search', arguments: '{"q":"todo"}' }, { type: 'function_call_output', call_id: 'call_2', output: 'matches' }, { type: 'function_call_output', call_id: 'call_1', output: 'file text' }, { role: 'user', content: [{ type: 'input_text', text: 'continue' }] }, ], } expect(responsesToOpenAiChat(body, target).messages).toEqual([ { role: 'user', content: 'check repo' }, { role: 'assistant', content: null, tool_calls: [ { id: 'call_1', type: 'function', function: { name: 'read_file', arguments: '{"path":"a.ts"}' }, }, { id: 'call_2', type: 'function', function: { name: 'search', arguments: '{"q":"todo"}' }, }, ], }, { role: 'tool', tool_call_id: 'call_1', content: 'file text' }, { role: 'tool', tool_call_id: 'call_2', content: 'matches' }, { role: 'user', content: 'continue' }, ]) }) it('drops incomplete Responses function call history for Chat providers', () => { const body = { input: [ { role: 'user', content: [{ type: 'input_text', text: 'hello' }] }, { type: 'function_call', call_id: 'call_missing', name: 'search', arguments: '{"q":"x"}' }, { role: 'user', content: [{ type: 'input_text', text: 'next turn' }] }, { type: 'function_call_output', call_id: 'orphan_call', output: 'orphan' }, ], } expect(responsesToOpenAiChat(body, target).messages).toEqual([ { role: 'user', content: 'hello' }, { role: 'user', content: 'next turn' }, ]) }) it('converts Responses input to Anthropic messages', () => { const body = { instructions: 'system text', input: [ { role: 'user', content: [{ type: 'input_text', text: 'hello' }] }, { type: 'function_call', call_id: 'call_1', name: 'lookup', arguments: '{"id":1}' }, { type: 'function_call_output', call_id: 'call_1', output: [{ text: 'ok' }] }, ], tools: [{ type: 'function', name: 'lookup', description: 'Lookup', parameters: { type: 'object' } }], } expect(responsesToAnthropicMessages(body, target, true)).toMatchObject({ model: 'test-model', system: 'system text', max_tokens: 4096, stream: true, messages: [ { role: 'user', content: [{ type: 'text', text: 'hello' }] }, { role: 'assistant', content: [{ type: 'tool_use', id: 'call_1', name: 'lookup', input: { id: 1 } }] }, { role: 'user', content: [{ type: 'tool_result', tool_use_id: 'call_1', content: 'ok' }] }, ], tools: [{ name: 'lookup', description: 'Lookup', input_schema: { type: 'object' } }], }) }) it('expands Hermes MCP namespace tools for Chat and Anthropic providers', () => { const body = { input: [{ role: 'user', content: [{ type: 'input_text', text: 'list devices' }] }], tools: [{ type: 'namespace', name: 'mcp__hermes_studio', description: 'Hermes tools' }], } expect(responsesToOpenAiChat(body, target).tools).toEqual(expect.arrayContaining([ expect.objectContaining({ type: 'function', function: expect.objectContaining({ name: 'hermes_studio_lan_devices_scan', parameters: expect.objectContaining({ properties: expect.objectContaining({ profile: expect.any(Object), token: expect.any(Object), }), }), }), }), ])) expect(responsesToAnthropicMessages(body, target).tools).toEqual(expect.arrayContaining([ expect.objectContaining({ name: 'hermes_studio_lan_devices_scan', input_schema: expect.objectContaining({ properties: expect.objectContaining({ profile: expect.any(Object), token: expect.any(Object), }), }), }), ])) }) it('keeps unknown MCP namespaces callable through a generic function fallback', () => { const body = { input: [{ role: 'user', content: [{ type: 'input_text', text: 'call custom mcp' }] }], tools: [{ type: 'namespace', name: 'mcp__custom_server', description: 'Custom server tools' }], } expect(responsesToOpenAiChat(body, target).tools).toEqual(expect.arrayContaining([ expect.objectContaining({ type: 'function', function: expect.objectContaining({ name: 'mcp__custom_server', parameters: expect.objectContaining({ required: ['tool', 'arguments'], }), }), }), ])) }) it('converts OpenAI Chat responses to Responses output', () => { expect(openAiChatToResponses({ id: 'chatcmpl_1', created: 123, choices: [{ message: { reasoning_content: 'think', content: 'hi', tool_calls: [{ id: 'call_1', function: { name: 'lookup', arguments: '{"id":1}' }, }], }, }], usage: { prompt_tokens: 2, completion_tokens: 3, total_tokens: 5 }, }, target)).toMatchObject({ id: 'chatcmpl_1', object: 'response', created_at: 123, model: 'test-model', output: [ { type: 'reasoning', summary: [{ type: 'summary_text', text: 'think' }] }, { type: 'message', role: 'assistant', content: [{ type: 'output_text', text: 'hi', annotations: [] }] }, { type: 'function_call', call_id: 'call_1', name: 'lookup', arguments: '{"id":1}' }, ], usage: { input_tokens: 2, output_tokens: 3, total_tokens: 5 }, }) }) it('marks expanded Hermes MCP Chat tool calls with their Responses namespace', () => { expect(openAiChatToResponses({ id: 'chatcmpl_1', created: 123, choices: [{ message: { tool_calls: [{ id: 'call_1', function: { name: 'hermes_studio_lan_devices_scan', arguments: '{"profile":"default"}' }, }], }, }], }, target)).toMatchObject({ output: [{ type: 'function_call', call_id: 'call_1', name: 'hermes_studio_lan_devices_scan', namespace: 'mcp__hermes_studio', }], }) }) it('normalizes generic MCP namespace function calls back to Responses MCP calls', () => { expect(openAiChatToResponses({ id: 'chatcmpl_1', created: 123, choices: [{ message: { tool_calls: [{ id: 'call_1', function: { name: 'mcp__custom_server', arguments: '{"tool":"custom_lookup","arguments":{"id":1}}', }, }], }, }], }, target)).toMatchObject({ output: [{ type: 'function_call', call_id: 'call_1', name: 'custom_lookup', arguments: '{"id":1}', namespace: 'mcp__custom_server', }], }) }) it('converts Anthropic messages to Responses output', () => { expect(anthropicMessageToResponses({ id: 'msg_1', content: [ { type: 'thinking', thinking: 'anthropic think' }, { type: 'text', text: 'hi' }, { type: 'tool_use', id: 'toolu_1', name: 'lookup', input: { id: 1 } }, ], usage: { input_tokens: 4, output_tokens: 5 }, }, target)).toMatchObject({ id: 'msg_1', object: 'response', model: 'test-model', output: [ { type: 'reasoning', summary: [{ type: 'summary_text', text: 'anthropic think' }] }, { type: 'message', role: 'assistant', content: [{ type: 'output_text', text: 'hi', annotations: [] }] }, { type: 'function_call', call_id: 'toolu_1', name: 'lookup', arguments: '{"id":1}' }, ], usage: { input_tokens: 4, output_tokens: 5, total_tokens: 9 }, }) }) it('marks expanded Hermes MCP Anthropic tool calls with their Responses namespace', () => { expect(anthropicMessageToResponses({ id: 'msg_1', content: [ { type: 'tool_use', id: 'toolu_1', name: 'hermes_studio_lan_devices_list', input: { profile: 'default' } }, ], usage: { input_tokens: 1, output_tokens: 1 }, }, target)).toMatchObject({ output: [{ type: 'function_call', call_id: 'toolu_1', name: 'hermes_studio_lan_devices_list', namespace: 'mcp__hermes_studio', }], }) }) }) async function* encodedChunks(chunks: string[]): AsyncGenerator { const encoder = new TextEncoder() for (const chunk of chunks) yield encoder.encode(chunk) } async function collectEvents(events: AsyncIterable): Promise { const collected: CanonicalResponsesEvent[] = [] for await (const event of events) collected.push(event) return collected } async function collectAnthropicEvents(events: AsyncIterable): Promise { const collected: AnthropicStreamEvent[] = [] for await (const event of events) collected.push(event) return collected } describe('agent runner Responses stream adapters', () => { it('normalizes OpenAI Chat SSE text and tool calls to Responses events', async () => { const events = await collectEvents(openAiChatSseToResponsesEvents(encodedChunks([ 'data: {"choices":[{"delta":{"reasoning_content":"think"}}]}\n\n', 'data: {"choices":[{"delta":{"content":"he"}}]}\n\n', 'data: {"choices":[{"delta":{"content":"llo"}}]}\r\n\r\n', 'data: {"choices":[{"delta":{"tool_calls":[{"index":0,"id":"call_1","function":{"name":"lookup","arguments":"{\\"id\\":"}}]}}]}\n\n', 'data: {"choices":[{"delta":{"tool_calls":[{"index":0,"function":{"arguments":"1}"}}]}}]}\n\n', 'data: [DONE]\n\n', ]), codexTarget)) expect(events.map(event => event.type)).toEqual([ 'response.created', 'response.reasoning.delta', 'response.output_item.added', 'response.content_part.added', 'response.output_text.delta', 'response.output_text.delta', 'response.output_item.added', 'response.function_call_arguments.delta', 'response.function_call_arguments.delta', 'response.output_text.done', 'response.content_part.done', 'response.output_item.done', 'response.output_item.done', 'response.completed', ]) expect(events[1].data).toMatchObject({ delta: 'think' }) expect(events[4].data).toMatchObject({ delta: 'he' }) expect(events[5].data).toMatchObject({ delta: 'llo' }) expect(events[6].data).toMatchObject({ item: { type: 'function_call', call_id: 'call_1', name: 'lookup' }, }) expect(events[13].data).toMatchObject({ response: { model: 'test-model', status: 'completed', output: [ { type: 'reasoning', summary: [{ type: 'summary_text', text: 'think' }] }, { type: 'message', content: [{ type: 'output_text', text: 'hello' }] }, { type: 'function_call', call_id: 'call_1', name: 'lookup', arguments: '{"id":1}' }, ], }, }) }) it('marks expanded Hermes MCP Chat SSE tool calls with their Responses namespace', async () => { const events = await collectEvents(openAiChatSseToResponsesEvents(encodedChunks([ 'data: {"choices":[{"delta":{"tool_calls":[{"index":0,"id":"call_1","function":{"name":"hermes_studio_lan_devices_scan","arguments":"{}"}}]}}]}\n\n', 'data: [DONE]\n\n', ]), codexTarget)) expect(events).toEqual(expect.arrayContaining([ expect.objectContaining({ type: 'response.output_item.done', data: expect.objectContaining({ item: expect.objectContaining({ type: 'function_call', call_id: 'call_1', name: 'hermes_studio_lan_devices_scan', namespace: 'mcp__hermes_studio', }), }), }), ])) }) it('normalizes Anthropic Messages SSE text and tool calls to Responses events', async () => { const events = await collectEvents(anthropicMessagesSseToResponsesEvents(encodedChunks([ 'event: message_start\ndata: {"type":"message_start","message":{"id":"msg_1"}}\n\n', 'event: content_block_delta\ndata: {"type":"content_block_delta","index":0,"delta":{"type":"thinking_delta","thinking":"think"}}\n\n', 'event: content_block_delta\ndata: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"hi"}}\n\n', 'event: content_block_start\ndata: {"type":"content_block_start","index":1,"content_block":{"type":"tool_use","id":"toolu_1","name":"lookup","input":{}}}\r\n\r\n', 'event: content_block_delta\ndata: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"{\\"id\\":1}"}}\n\n', ]), codexTarget)) expect(events.map(event => event.type)).toEqual([ 'response.created', 'response.reasoning.delta', 'response.output_item.added', 'response.content_part.added', 'response.output_text.delta', 'response.output_item.added', 'response.function_call_arguments.delta', 'response.output_text.done', 'response.content_part.done', 'response.output_item.done', 'response.output_item.done', 'response.completed', ]) expect(events[1].data).toMatchObject({ delta: 'think' }) expect(events[2].data).toMatchObject({ item: { id: 'msg_msg_1' } }) expect(events[5].data).toMatchObject({ item: { type: 'function_call', call_id: 'toolu_1', name: 'lookup' }, }) expect(events[11].data).toMatchObject({ response: { id: 'msg_1', output: [ { type: 'reasoning', summary: [{ type: 'summary_text', text: 'think' }] }, { type: 'message', content: [{ type: 'output_text', text: 'hi' }] }, { type: 'function_call', call_id: 'toolu_1', name: 'lookup', arguments: '{"id":1}' }, ], }, }) }) it('marks expanded Hermes MCP Anthropic SSE tool calls with their Responses namespace', async () => { const events = await collectEvents(anthropicMessagesSseToResponsesEvents(encodedChunks([ 'event: message_start\ndata: {"type":"message_start","message":{"id":"msg_1"}}\n\n', 'event: content_block_start\ndata: {"type":"content_block_start","index":0,"content_block":{"type":"tool_use","id":"toolu_1","name":"hermes_studio_lan_devices_list","input":{}}}\n\n', 'event: content_block_delta\ndata: {"type":"content_block_delta","index":0,"delta":{"type":"input_json_delta","partial_json":"{\\"profile\\":\\"default\\"}"}}\n\n', 'event: message_stop\ndata: {"type":"message_stop"}\n\n', ]), codexTarget)) expect(events).toEqual(expect.arrayContaining([ expect.objectContaining({ type: 'response.output_item.done', data: expect.objectContaining({ item: expect.objectContaining({ type: 'function_call', call_id: 'toolu_1', name: 'hermes_studio_lan_devices_list', namespace: 'mcp__hermes_studio', }), }), }), ])) }) it('passes native Responses SSE events through as canonical events', async () => { const events = await collectEvents(openAiResponsesSseToResponsesEvents(encodedChunks([ 'event: response.created\r\ndata: {"response":{"id":"resp_1"}}\r\n\r\n', 'data: {"type":"response.output_text.delta","delta":"hi"}\n\n', 'data: [DONE]\n\n', ]))) expect(events).toEqual([ { type: 'response.created', data: { type: 'response.created', response: { id: 'resp_1' } }, }, { type: 'response.output_text.delta', data: { type: 'response.output_text.delta', delta: 'hi' }, }, ]) }) }) describe('agent runner Anthropic adapters', () => { it('converts Anthropic messages to OpenAI Chat with reasoning_content', () => { const body = { system: 'system text', max_tokens: 32, temperature: 0.1, messages: [ { role: 'user', content: [{ type: 'text', text: 'hello' }] }, { role: 'assistant', content: [ { type: 'thinking', thinking: 'need tool' }, { type: 'tool_use', id: 'toolu_1', name: 'lookup', input: { id: 1 } }, ], }, { role: 'user', content: [{ type: 'tool_result', tool_use_id: 'toolu_1', content: 'ok' }] }, ], tools: [{ name: 'lookup', description: 'Lookup', input_schema: { type: 'object' } }], } expect(anthropicToOpenAiChat(body, anthropicTarget)).toMatchObject({ model: 'deepseek-reasoner', max_tokens: 32, temperature: 0.1, stream: false, messages: [ { role: 'system', content: 'system text' }, { role: 'user', content: 'hello' }, { role: 'assistant', content: null, reasoning_content: 'need tool', tool_calls: [{ id: 'toolu_1', type: 'function', function: { name: 'lookup', arguments: '{"id":1}' }, }], }, { role: 'tool', tool_call_id: 'toolu_1', content: 'ok' }, ], tools: [{ type: 'function', function: { name: 'lookup', description: 'Lookup', parameters: { type: 'object' } }, }], }) }) it('converts Anthropic messages to Responses input', () => { expect(anthropicToOpenAiResponses({ system: 'system text', max_tokens: 64, messages: [ { role: 'user', content: 'hello' }, { role: 'assistant', content: [{ type: 'tool_use', id: 'toolu_1', name: 'lookup', input: { id: 1 } }] }, { role: 'user', content: [{ type: 'tool_result', tool_use_id: 'toolu_1', content: 'ok' }] }, ], tools: [{ name: 'lookup', input_schema: { type: 'object' } }], }, anthropicTarget, true)).toMatchObject({ model: 'deepseek-reasoner', instructions: 'system text', max_output_tokens: 64, stream: true, store: false, input: [ { role: 'user', content: 'hello' }, { type: 'function_call', call_id: 'toolu_1', name: 'lookup', arguments: '{"id":1}' }, { type: 'function_call_output', call_id: 'toolu_1', output: 'ok' }, ], tools: [{ type: 'function', name: 'lookup', parameters: { type: 'object' } }], }) }) it('converts OpenAI Chat responses to Anthropic messages', () => { expect(openAiToAnthropicMessage({ id: 'chatcmpl_1', choices: [{ finish_reason: 'tool_calls', message: { reasoning_content: 'thinking', content: 'hi', tool_calls: [{ id: 'call_1', function: { name: 'lookup', arguments: '{"id":1}' } }], }, }], usage: { prompt_tokens: 3, completion_tokens: 4 }, }, anthropicTarget)).toMatchObject({ id: 'chatcmpl_1', type: 'message', role: 'assistant', model: 'deepseek-reasoner', content: [ { type: 'thinking', thinking: 'thinking' }, { type: 'text', text: 'hi' }, { type: 'tool_use', id: 'call_1', name: 'lookup', input: { id: 1 } }, ], stop_reason: 'tool_use', usage: { input_tokens: 3, output_tokens: 4 }, }) }) it('converts Responses output to Anthropic messages', () => { expect(openAiResponsesToAnthropicMessage({ id: 'resp_1', status: 'completed', output: [ { type: 'message', content: [{ type: 'output_text', text: 'hi' }] }, { type: 'function_call', call_id: 'call_1', name: 'lookup', arguments: '{"id":1}' }, ], usage: { input_tokens: 5, output_tokens: 6 }, }, anthropicTarget)).toMatchObject({ id: 'resp_1', type: 'message', role: 'assistant', model: 'deepseek-reasoner', content: [ { type: 'text', text: 'hi' }, { type: 'tool_use', id: 'call_1', name: 'lookup', input: { id: 1 } }, ], stop_reason: 'tool_use', usage: { input_tokens: 5, output_tokens: 6 }, }) }) }) describe('agent runner Anthropic stream adapters', () => { it('normalizes OpenAI Chat SSE to Anthropic Messages events', async () => { const events = await collectAnthropicEvents(openAiChatSseToAnthropicEvents(encodedChunks([ 'data: {"choices":[{"delta":{"reasoning_content":"think"}}]}\n\n', 'data: {"choices":[{"delta":{"content":"hi"}}]}\n\n', 'data: {"choices":[{"delta":{"tool_calls":[{"index":0,"id":"call_1","function":{"name":"lookup","arguments":"{\\"id\\":"}}]}}]}\r\n\r\n', 'data: {"choices":[{"delta":{"tool_calls":[{"index":0,"function":{"arguments":"1}"}}]},"finish_reason":"tool_calls"}],"usage":{"completion_tokens":7}}\n\n', ]), anthropicTarget)) expect(events.map(event => event.type)).toEqual([ 'message_start', 'content_block_start', 'content_block_delta', 'content_block_stop', 'content_block_start', 'content_block_delta', 'content_block_stop', 'content_block_start', 'content_block_delta', 'content_block_delta', 'content_block_stop', 'message_delta', 'message_stop', ]) expect(events[1].data).toMatchObject({ content_block: { type: 'thinking' } }) expect(events[2].data).toMatchObject({ delta: { type: 'thinking_delta', thinking: 'think' } }) expect(events[5].data).toMatchObject({ delta: { type: 'text_delta', text: 'hi' } }) expect(events[7].data).toMatchObject({ content_block: { type: 'tool_use', id: 'call_1', name: 'lookup' } }) expect(events[11].data).toMatchObject({ delta: { stop_reason: 'tool_use', stop_sequence: null }, usage: { output_tokens: 7 }, }) }) it('normalizes Responses SSE to Anthropic Messages events', async () => { const events = await collectAnthropicEvents(openAiResponsesSseToAnthropicEvents(encodedChunks([ 'data: {"type":"response.created","response":{"id":"resp_1"}}\n\n', 'data: {"type":"response.output_text.delta","delta":"hi"}\n\n', 'data: {"type":"response.output_text.done"}\n\n', 'data: {"type":"response.output_item.added","output_index":1,"item":{"type":"function_call","call_id":"call_1","name":"lookup"}}\n\n', 'data: {"type":"response.function_call_arguments.delta","item_id":"call_1","delta":"{\\"id\\":1}"}\n\n', 'data: {"type":"response.output_item.done","item":{"type":"function_call","call_id":"call_1","name":"lookup","arguments":"{\\"id\\":1}"}}\n\n', 'data: {"type":"response.completed","response":{"status":"completed","usage":{"output_tokens":3}}}\n\n', ]), anthropicTarget)) expect(events.map(event => event.type)).toEqual([ 'message_start', 'content_block_start', 'content_block_delta', 'content_block_stop', 'content_block_start', 'content_block_delta', 'content_block_stop', 'message_delta', 'message_stop', ]) expect(events[2].data).toMatchObject({ delta: { type: 'text_delta', text: 'hi' } }) expect(events[4].data).toMatchObject({ content_block: { type: 'tool_use', id: 'call_1', name: 'lookup' } }) expect(events[5].data).toMatchObject({ delta: { type: 'input_json_delta', partial_json: '{"id":1}' } }) expect(events[7].data).toMatchObject({ delta: { stop_reason: 'tool_use', stop_sequence: null }, usage: { output_tokens: 3 }, }) }) })