import { createServer } from 'node:http' import type { IncomingHttpHeaders, IncomingMessage, Server, ServerResponse, } from 'node:http' /** One request observed by the package-private Responses fixture. */ interface RecordedResponsesRequest { readonly method: string | undefined readonly path: string | undefined readonly headers: IncomingHttpHeaders readonly body: Record } /** Behavior consumed by one Responses request. */ export type ResponsesBehavior = | { readonly kind: 'complete'; readonly text: string } | { readonly kind: 'functionCall' readonly name: string readonly arguments: Record } | { readonly kind: 'hold' } /** Running package-private Responses fixture. */ export interface ResponsesFixture { readonly baseUrl: string readonly requests: RecordedResponsesRequest[] readonly requestStarted: Promise close(): Promise } function responseObject(text: string): Record { const message = { id: 'msg_fixture', type: 'message', status: 'completed', role: 'assistant', content: [{ type: 'output_text', annotations: [], logprobs: [], text, }], } return { id: 'resp_fixture', object: 'response', created_at: 1, status: 'completed', background: false, error: null, incomplete_details: null, instructions: null, max_output_tokens: null, max_tool_calls: null, model: 'fixture-model', output: [message], parallel_tool_calls: true, previous_response_id: null, prompt_cache_key: null, prompt_cache_retention: null, reasoning: { effort: null, summary: null }, safety_identifier: null, service_tier: 'default', store: false, temperature: null, text: { format: { type: 'text' }, verbosity: 'medium' }, tool_choice: 'auto', tools: [], top_logprobs: 0, top_p: null, truncation: 'disabled', usage: { input_tokens: 10, input_tokens_details: { cached_tokens: 0 }, output_tokens: 1, output_tokens_details: { reasoning_tokens: 0 }, total_tokens: 11, }, user: null, metadata: {}, } } /** * Build the minimal Responses SSE event sequence consumed by Codex 0.146.0. * @param text - exact assistant answer. * @returns ordered response lifecycle events. */ export function completeResponsesEvents(text: string): Record[] { const completed = responseObject(text) const message = (completed.output as Record[])[0]! const part = (message.content as Record[])[0]! return [ { type: 'response.created', response: { ...completed, status: 'in_progress', output: [] }, }, { type: 'response.output_item.added', output_index: 0, item: { ...message, status: 'in_progress', content: [] }, }, { type: 'response.content_part.added', item_id: message.id, output_index: 0, content_index: 0, part: { ...part, text: '' }, }, { type: 'response.output_text.delta', item_id: message.id, output_index: 0, content_index: 0, delta: text, logprobs: [], }, { type: 'response.output_text.done', item_id: message.id, output_index: 0, content_index: 0, text, logprobs: [], }, { type: 'response.content_part.done', item_id: message.id, output_index: 0, content_index: 0, part, }, { type: 'response.output_item.done', output_index: 0, item: message, }, { type: 'response.completed', response: completed }, ] } function functionCallEvents( name: string, argumentsValue: Record, ): Record[] { const argumentsText = JSON.stringify(argumentsValue) const item = { id: 'fc_fixture', type: 'function_call', status: 'completed', name, arguments: argumentsText, call_id: 'call_fixture', } const completed = { ...responseObject(''), output: [item], usage: { input_tokens: 10, input_tokens_details: { cached_tokens: 0 }, output_tokens: 5, output_tokens_details: { reasoning_tokens: 0 }, total_tokens: 15, }, } return [ { type: 'response.created', response: { ...completed, status: 'in_progress', output: [] }, }, { type: 'response.output_item.added', output_index: 0, item: { ...item, status: 'in_progress', arguments: '' }, }, { type: 'response.function_call_arguments.delta', item_id: item.id, output_index: 0, delta: argumentsText, }, { type: 'response.function_call_arguments.done', item_id: item.id, output_index: 0, arguments: argumentsText, }, { type: 'response.output_item.done', output_index: 0, item, }, { type: 'response.completed', response: completed }, ] } function readRequest(request: IncomingMessage): Promise { return new Promise((resolve, reject) => { let body = '' request.setEncoding('utf8') request.on('data', (chunk: string) => { body += chunk }) request.on('end', () => { resolve(body) }) request.on('error', reject) }) } function closeServer(server: Server): Promise { return new Promise((resolve, reject) => { server.close((error) => { if (error !== undefined) reject(error) else resolve() }) server.closeAllConnections() }) } /** * Start a loopback-only Responses SSE fixture. * @param script - one behavior per expected Responses request. * @returns the running fixture and its observed requests. */ export async function startResponsesFixture( script: readonly ResponsesBehavior[], ): Promise { const behaviors = [...script] const requests: RecordedResponsesRequest[] = [] const started = Promise.withResolvers() const openResponses = new Set() const server = createServer((request, response) => { openResponses.add(response) response.on('close', () => { openResponses.delete(response) }) void readRequest(request).then((body) => { requests.push({ method: request.method, path: request.url, headers: request.headers, body: JSON.parse(body) as Record, }) started.resolve(undefined) const behavior = behaviors.shift() if (behavior === undefined) { response.writeHead(500, { 'content-type': 'application/json' }) response.end(JSON.stringify({ error: { message: 'fixture script exhausted' } })) return } response.writeHead(200, { 'content-type': 'text/event-stream', 'cache-control': 'no-cache', connection: 'keep-alive', 'x-request-id': 'req_fixture', }) if (behavior.kind === 'hold') return const events = behavior.kind === 'complete' ? completeResponsesEvents(behavior.text) : functionCallEvents(behavior.name, behavior.arguments) for (const event of events) { response.write(`data: ${JSON.stringify(event)}\n\n`) } response.end('data: [DONE]\n\n') }).catch((error: unknown) => { response.destroy(error instanceof Error ? error : new Error(String(error))) }) }) await new Promise((resolve, reject) => { server.once('error', reject) server.listen(0, '127.0.0.1', () => { server.off('error', reject) resolve() }) }) const address = server.address() if (address === null || typeof address === 'string') { throw new Error('responses fixture did not acquire a TCP port') } return { baseUrl: `http://127.0.0.1:${address.port}/v1`, requests, requestStarted: started.promise, async close(): Promise { for (const response of openResponses) response.destroy() await closeServer(server) }, } }