diff --git a/src/protocol/iterator.ts b/src/protocol/iterator.ts index ec157ad..238e890 100644 --- a/src/protocol/iterator.ts +++ b/src/protocol/iterator.ts @@ -435,12 +435,15 @@ export function parseResponse( } return events; } - case "response.completed": { + // `response.done` is an alias some backends emit for `response.completed`; + // both carry the turn's usage on the terminal envelope. + case "response.completed": + case "response.done": { const validated = CompletedEvent(parsed); if (validated instanceof type.errors) { throw protocolMismatch( provider, - `response.completed failed schema validation: ${validated.summary}`, + `${eventType} failed schema validation: ${validated.summary}`, parsed, ); } diff --git a/src/responses.test.ts b/src/responses.test.ts index 52f90ca..50066a0 100644 --- a/src/responses.test.ts +++ b/src/responses.test.ts @@ -384,6 +384,17 @@ describe("Responses parser — event schema validation", () => { expect(events.map((e) => e.type)).toEqual(["inference.usage"]); }); + test("response.done reports usage like response.completed", () => { + const adapter = createOpenAIResponsesAdapter(source, {}); + adapter.buildRequest(turns, "model", {}); + const done = JSON.stringify({ + type: "response.done", + response: { usage: { input_tokens: 10, output_tokens: 20 } }, + }); + const events = adapter.parseResponse(done); + expect(events.map((e) => e.type)).toEqual(["inference.usage"]); + }); + // A function_call_arguments.delta is only routable to the tool_call.start // the harness already saw; one for an item_id that never arrived via // output_item.added is an orphan fragment, not a fresh block to