From 373b183ddbca476f609195058d4723a643ef8adb Mon Sep 17 00:00:00 2001 From: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> Date: Wed, 2 Sep 2026 15:54:27 -0300 Subject: [PATCH] fix(responses): preserve safe output text schema --- open-sse/utils/responsesFailureOutput.ts | 3 +++ tests/unit/request-log-payloads.test.ts | 7 ++++--- tests/unit/stream-passthrough-error-redaction.test.ts | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/open-sse/utils/responsesFailureOutput.ts b/open-sse/utils/responsesFailureOutput.ts index c08938333f..e085ba3b69 100644 --- a/open-sse/utils/responsesFailureOutput.ts +++ b/open-sse/utils/responsesFailureOutput.ts @@ -38,6 +38,9 @@ export function projectResponsesFailureOutput( content.push({ type: "output_text", text: projectString("text", contentPart.text), + // Preserve the required Responses schema without forwarding any + // untrusted citation/file metadata supplied by the provider. + annotations: [], }); } else if (contentPart.type === "refusal" && typeof contentPart.refusal === "string") { content.push({ diff --git a/tests/unit/request-log-payloads.test.ts b/tests/unit/request-log-payloads.test.ts index 999e302045..098eaf12e8 100644 --- a/tests/unit/request-log-payloads.test.ts +++ b/tests/unit/request-log-payloads.test.ts @@ -335,7 +335,7 @@ test("sanitizes response.failed messages without rewriting unrelated deep diagno { type: "message", role: "assistant", - content: [{ type: "output_text", text: "safe direct partial output" }], + content: [{ type: "output_text", text: "safe direct partial output", annotations: [] }], }, ]); assert.doesNotMatch(serialized, /private direct reasoning/); @@ -409,7 +409,7 @@ test("sanitizes response.completed failed siblings in objects, SSE, and NDJSON", role: "assistant", status: "in_progress", content: [ - { type: "output_text", text: "partial safe output" }, + { type: "output_text", text: "partial safe output", annotations: [] }, { type: "refusal", refusal: "safe refusal" }, ], }, @@ -440,7 +440,8 @@ test("sanitizes response.completed failed siblings in objects, SSE, and NDJSON", serialized, /completed-failed-secret|srv\/private|completed-failed\.ts|private commentary|private roleless|private chain|private encrypted|private tool/i ); - assert.doesNotMatch(serialized, /"annotations"|"diagnostics"|"function_call"|"reasoning"/); + assert.match(serialized, /"annotations":\[\]/); + assert.doesNotMatch(serialized, /"url_citation"|"diagnostics"|"function_call"|"reasoning"/); assert.match(serialized, /partial safe output/); assert.match(serialized, /safe refusal/); assert.deepEqual( diff --git a/tests/unit/stream-passthrough-error-redaction.test.ts b/tests/unit/stream-passthrough-error-redaction.test.ts index 4082bbd001..9da6457def 100644 --- a/tests/unit/stream-passthrough-error-redaction.test.ts +++ b/tests/unit/stream-passthrough-error-redaction.test.ts @@ -285,7 +285,7 @@ test("Responses response.failed is projected before forwarding, logging, and onF assert.match(result.output, /"last_error":\{/); assert.match(result.output, /safe partial output/); assert.match(result.output, /safe refusal/); - assert.doesNotMatch(result.output, /"annotations"/); + assert.match(result.output, /"annotations":\[\]/); assert.doesNotMatch(result.output, /hidden nested commentary must not be public/); assert.doesNotMatch(result.output, /roleless output must not be public/); assert.match(result.output, /"cached_tokens":1/);