Compare commits

..

5 Commits

16 changed files with 573 additions and 383 deletions

View File

@@ -97,10 +97,6 @@ _Living section — cycle opened at the v3.8.50 freeze (parallel-cycle model). B
### 🐛 Bug Fixes
- **security(streaming):** sanitize generic mid-stream error messages before emitting OpenAI,
Responses, or Claude SSE failure frames and before diagnostic logging, while preserving raw
failures for internal classification and keeping client disconnects out of provider failure state.
### 📝 Maintenance
---

View File

@@ -38,6 +38,7 @@ import { applyReasoningInputPolicy } from "../services/reasoningInputPolicy.ts";
import { normalizeCodexVerbosity } from "../services/codexVerbosity.ts";
import { getThinkingBudgetConfig, ThinkingMode } from "../services/thinkingBudget.ts";
import { CORS_HEADERS } from "../utils/cors.ts";
import { projectCodexPublicError } from "../utils/codexPublicError.ts";
import { errorResponse } from "../utils/error.ts";
import { normalizeCodexResponsesInput } from "../utils/responsesInputNormalization.ts";
import * as prl from "../utils/providerRequestLogging.ts";
@@ -493,7 +494,6 @@ function toCodexResponseFailedEvent(parsed: Record<string, unknown>): Record<str
typeof upstreamError.message === "string" && upstreamError.message.trim()
? upstreamError.message
: "Codex upstream error";
const error: Record<string, unknown> = { code, message };
const explicitStatus =
toStatusCode(parsed.status_code) ??
toStatusCode(parsed.status) ??
@@ -503,8 +503,10 @@ function toCodexResponseFailedEvent(parsed: Record<string, unknown>): Record<str
toStatusCode(upstreamError.status);
const statusCode =
explicitStatus ?? (looksLikeQuotaOrRateLimit(code, type, message) ? 429 : null);
const error: Record<string, unknown> = {
...projectCodexPublicError({ status: statusCode, code, type }),
};
if (type) error.type = type;
if (statusCode !== null) error.status_code = statusCode;
return {
@@ -951,7 +953,7 @@ export class CodexExecutor extends BaseExecutor {
}
};
const failController = (code: string, message: string) => {
const failController = (code: string, _message: string) => {
if (closed) return;
const controller = streamController;
const payload = JSON.stringify({
@@ -959,7 +961,7 @@ export class CodexExecutor extends BaseExecutor {
response: {
id: null,
status: "failed",
error: { code, message },
error: projectCodexPublicError({ status: 502, code, type: "provider_error" }),
},
});
try {

View File

@@ -23,7 +23,7 @@
* The non-secret STRUCTURAL fields (appVersion, ctxKey, header names) carry safe
* defaults so a transient parse miss can't break an otherwise-working signer.
*/
import { createHmac, createHash, createCipheriv, randomBytes, randomInt } from "node:crypto";
import { createHmac, createHash, createCipheriv, randomBytes } from "node:crypto";
import type { MaxaiSigningConstants, MaxaiHeaderNames } from "./constants.ts";
import { MAXAI_DEFAULT_HEADER_NAMES } from "./constants.ts";
@@ -39,22 +39,8 @@ const BLANK_USER_ROUTES = new Set([
const MAGIC = Buffer.from("Salted__", "ascii");
/**
* The wire `X-Random` slot: a 6-digit decimal string (100000-999999).
*
* Uses `crypto.randomInt`, which rejection-samples internally, instead of
* `randomBytes(4) % 900000` — a plain modulo over a 32-bit draw does not divide
* evenly by 900000, so the low ~4772 values of the range came out marginally
* more often. The emitted shape is unchanged (always exactly 6 digits).
*/
export function maxaiRandomSlot(): string {
return String(randomInt(100000, 1000000));
}
function hmacSha1Hex(message: string, key: string): string {
return createHmac("sha1", Buffer.from(key, "utf8"))
.update(Buffer.from(message, "utf8"))
.digest("hex");
return createHmac("sha1", Buffer.from(key, "utf8")).update(Buffer.from(message, "utf8")).digest("hex");
}
function sm3Hex(message: string): string {
@@ -72,9 +58,7 @@ function evpBytesToKey(
let block = Buffer.alloc(0);
const pass = Buffer.from(passphrase, "utf8");
while (derived.length < keyLen + ivLen) {
block = createHash("md5")
.update(Buffer.concat([block, pass, salt]))
.digest();
block = createHash("md5").update(Buffer.concat([block, pass, salt])).digest();
derived = Buffer.concat([derived, block]);
}
return { key: derived.subarray(0, keyLen), iv: derived.subarray(keyLen, keyLen + ivLen) };
@@ -140,7 +124,8 @@ export function buildMaxaiSignedHeaders(
constants: MaxaiSigningConstants
): Record<string, string> {
const reqTime = (input.now ?? (() => Date.now()))();
const random = input.random?.() ?? maxaiRandomSlot();
const random =
input.random?.() ?? String((randomBytes(4).readUInt32BE(0) % 900000) + 100000);
const h: MaxaiHeaderNames = { ...MAXAI_DEFAULT_HEADER_NAMES, ...constants.headerNames };
const ctxKey = constants.ctxKey;
const appVersion = constants.appVersion;

View File

@@ -0,0 +1,110 @@
import { sanitizeErrorMessage } from "./error.ts";
export const CODEX_PUBLIC_ERROR_MESSAGE = sanitizeErrorMessage("Codex provider request failed");
export interface CodexPublicError {
message: string;
type: string;
code: string;
}
interface CodexPublicErrorInput {
status?: number | null;
type?: unknown;
code?: unknown;
}
interface CodexPublicErrorRule {
type: string;
allowsStatus: (status: number) => boolean;
}
const exactStatuses =
(...statuses: number[]) =>
(status: number): boolean =>
statuses.includes(status);
const CODEX_PUBLIC_ERROR_RULES = new Map<string, CodexPublicErrorRule>([
["browser_stream_inconsistent", { type: "server_error", allowsStatus: exactStatuses(502) }],
["chatgpt_session_expired", { type: "authentication_error", allowsStatus: exactStatuses(401) }],
["chatgpt_submission_ambiguous", { type: "server_error", allowsStatus: exactStatuses(502) }],
["chatgpt_submitted_turn_failed", { type: "server_error", allowsStatus: exactStatuses(502) }],
["chatgpt_subscription_unavailable", { type: "server_error", allowsStatus: exactStatuses(503) }],
["client_cancelled", { type: "invalid_request_error", allowsStatus: exactStatuses(499) }],
["client_closed_request", { type: "invalid_request_error", allowsStatus: exactStatuses(499) }],
["codex_app_server_turn_failed", { type: "provider_error", allowsStatus: exactStatuses(502) }],
[
"compaction_control_unavailable",
{ type: "invalid_request_error", allowsStatus: exactStatuses(409) },
],
[
"compaction_handoff_failed",
{ type: "invalid_request_error", allowsStatus: exactStatuses(409) },
],
[
"compaction_source_unavailable",
{ type: "invalid_request_error", allowsStatus: exactStatuses(409) },
],
["connector_not_found", { type: "connector_error", allowsStatus: exactStatuses(424) }],
[
"context_length_exceeded",
{ type: "invalid_request_error", allowsStatus: exactStatuses(400, 413) },
],
["insufficient_quota", { type: "insufficient_quota", allowsStatus: exactStatuses(429) }],
["invalid_api_key", { type: "authentication_error", allowsStatus: exactStatuses(401) }],
["invalid_output_schema", { type: "invalid_request_error", allowsStatus: exactStatuses(400) }],
["invalid_request_error", { type: "invalid_request_error", allowsStatus: exactStatuses(400) }],
["multipart_protocol_violation", { type: "server_error", allowsStatus: exactStatuses(502) }],
["origin_rejected", { type: "invalid_request_error", allowsStatus: exactStatuses(403) }],
["permission_denied", { type: "permission_error", allowsStatus: exactStatuses(403) }],
["prompt_attachment_integrity", { type: "server_error", allowsStatus: exactStatuses(502) }],
["rate_limit_exceeded", { type: "rate_limit_error", allowsStatus: exactStatuses(429) }],
["server_is_overloaded", { type: "server_error", allowsStatus: exactStatuses(503) }],
[
"structured_output_validation_failed",
{ type: "server_error", allowsStatus: exactStatuses(502) },
],
["subscription_required", { type: "permission_error", allowsStatus: exactStatuses(403) }],
[
"upstream_server_error",
{
type: "server_error",
allowsStatus: (status) => status >= 500 && status <= 599 && status !== 503,
},
],
[
"upstream_websocket_connect_failed",
{ type: "provider_error", allowsStatus: exactStatuses(502) },
],
["upstream_websocket_error", { type: "provider_error", allowsStatus: exactStatuses(502) }],
["usage_limit_reached", { type: "rate_limit_error", allowsStatus: exactStatuses(429) }],
]);
function defaultPublicClassification(status: number): Pick<CodexPublicError, "type" | "code"> {
if (status === 429) return { type: "rate_limit_error", code: "rate_limit_exceeded" };
if (status === 401) return { type: "authentication_error", code: "invalid_api_key" };
if (status === 403) return { type: "permission_error", code: "permission_denied" };
if (status === 499) return { type: "invalid_request_error", code: "client_closed_request" };
if (status === 503) return { type: "server_error", code: "server_is_overloaded" };
if (status >= 500) return { type: "server_error", code: "upstream_server_error" };
return { type: "invalid_request_error", code: "invalid_request_error" };
}
/**
* Project an internally classified Codex failure onto its public Responses contract.
*
* Upstream message, code, and type fields are untrusted. The public message is fixed,
* while code/type retain only closed, protocol-level identifiers already produced by
* OmniRoute. Everything else falls back to the HTTP status classification.
*/
export function projectCodexPublicError(input: CodexPublicErrorInput): CodexPublicError {
const status =
typeof input.status === "number" && Number.isInteger(input.status) ? input.status : 502;
const fallback = defaultPublicClassification(status);
const rule =
typeof input.code === "string" ? CODEX_PUBLIC_ERROR_RULES.get(input.code) : undefined;
if (!rule || !rule.allowsStatus(status)) {
return { message: CODEX_PUBLIC_ERROR_MESSAGE, ...fallback };
}
return { message: CODEX_PUBLIC_ERROR_MESSAGE, type: rule.type, code: input.code as string };
}

View File

@@ -1,7 +1,6 @@
import { trackPendingRequest } from "@/lib/usageDb";
import { STREAM_IDLE_TIMEOUT_MS } from "../config/constants.ts";
import { FORMATS } from "../translator/formats.ts";
import { buildErrorBody } from "./error.ts";
import { PENDING_REQUEST_CLEARED_MARKER } from "./stream.ts";
import { createCompletedResponsesToolHandoffWatcher } from "./responsesToolHandoff.ts";
import { createStreamContentWatcher, type StreamContentWatcher } from "./streamReadiness.ts";
@@ -188,10 +187,6 @@ function getErrorStatusCode(error: unknown): number {
return 502;
}
function getPublicErrorMessage(errorMsg: string, statusCode: number): string {
return buildErrorBody(statusCode, errorMsg).error.message;
}
function isDeadlineAbortReason(reason: unknown): reason is Error {
return (
reason instanceof Error &&
@@ -411,7 +406,7 @@ export function createStreamController({
}
if (error instanceof Error) {
logStream(`error: ${getPublicErrorMessage(error.message, getErrorStatusCode(error))}`);
logStream(`error: ${error.message}`);
return;
}
logStream("error: unknown");
@@ -457,7 +452,6 @@ export function buildStreamErrorChunks(
clientResponseFormat?: string | null
) {
const statusMapping = getStreamErrorStatusMapping(statusCode);
const publicErrorMessage = getPublicErrorMessage(errorMsg, statusCode);
if (isResponsesClientFormat(clientResponseFormat)) {
const errorEvent = {
@@ -466,7 +460,7 @@ export function buildStreamErrorChunks(
id: null,
status: "failed",
error: {
message: publicErrorMessage,
message: errorMsg,
type: statusMapping.responses.type,
code: statusMapping.responses.code,
},
@@ -481,7 +475,7 @@ export function buildStreamErrorChunks(
type: "error",
error: {
type: statusMapping.claude.type,
message: publicErrorMessage,
message: errorMsg,
},
};
@@ -504,7 +498,7 @@ export function buildStreamErrorChunks(
},
],
error: {
message: publicErrorMessage,
message: errorMsg,
type: statusMapping.responses.type,
code: statusMapping.responses.code,
},

View File

@@ -5,6 +5,7 @@ import type {
CodexProviderContinuationState,
CodexUsage,
} from "./types";
import { projectCodexPublicError } from "../../utils/codexPublicError";
import { adapterFailureFromMessage, classifyError, type CodexErrorPayload } from "./lib/errors";
import { encodeCompactionSummary } from "./responses/compaction";
import { encodeReasoningEnvelope, type ReasoningEnvelope } from "./responses/reasoning-envelope";
@@ -46,7 +47,8 @@ function responsesUsage(usage: CodexUsage | undefined): Record<string, unknown>
}
function responseError(status: number, type: string, message: string): CodexErrorPayload {
return classifyError(status, type, message);
const classified = classifyError(status, type, message);
return projectCodexPublicError({ status, type: classified.type, code: classified.code });
}
function adapterFailureFromEvent(event: Extract<AdapterEvent, { type: "error" }>): {
@@ -54,14 +56,25 @@ function adapterFailureFromEvent(event: Extract<AdapterEvent, { type: "error" }>
error: CodexErrorPayload;
} {
if (event.status === undefined && event.errorType === undefined && event.code === undefined) {
return adapterFailureFromMessage(event.message);
const fallback = adapterFailureFromMessage(event.message);
return {
httpStatus: fallback.httpStatus,
error: projectCodexPublicError({
status: fallback.httpStatus,
type: fallback.error.type,
code: fallback.error.code,
}),
};
}
const fallback = adapterFailureFromMessage(event.message);
const httpStatus = event.status ?? fallback.httpStatus;
const error = classifyError(httpStatus, event.errorType ?? fallback.error.type, event.message);
if (event.errorType !== undefined) error.type = event.errorType;
if (event.code !== undefined) error.code = event.code;
return { httpStatus, error };
return {
httpStatus,
error: projectCodexPublicError({ status: httpStatus, type: error.type, code: error.code }),
};
}
export { adapterFailureFromMessage } from "./lib/errors";
@@ -1314,7 +1327,7 @@ export function buildResponseJSON(
}
export function formatErrorResponse(status: number, type: string, message: string): Response {
return new Response(JSON.stringify({ error: classifyError(status, type, message) }), {
return new Response(JSON.stringify({ error: responseError(status, type, message) }), {
status,
headers: { "Content-Type": "application/json" },
});

View File

@@ -0,0 +1,376 @@
// This suite intentionally owns process-wide DATA_DIR, plugin, and DB state. It must run only
// inside the subprocess launched by tests/unit/codex-response-failed-boundary.test.ts.
import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import test from "node:test";
import type { CodexWreqWebSocket } from "../../open-sse/executors/codex/appServerClient.ts";
import type { AdapterEvent } from "../../open-sse/vendor/codex-chatgpt-web/types.ts";
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-codex-boundary-data-"));
const TEST_PLUGINS_DIR = fs.mkdtempSync(
path.join(os.tmpdir(), "omniroute-codex-boundary-plugins-")
);
process.env.DATA_DIR = TEST_DATA_DIR;
process.env.OMNIROUTE_PLUGINS_DIR = TEST_PLUGINS_DIR;
process.env.APP_LOG_TO_FILE = "false";
const { CodexExecutor, __setCodexWebSocketTransportForTesting, encodeResponseSseEvent } =
await import("../../open-sse/executors/codex.ts");
const { CodexAppServerExecutor } = await import("../../open-sse/executors/codex-app-server.ts");
const { bridgeToResponsesSSE, buildResponseJSON } =
await import("../../open-sse/vendor/codex-chatgpt-web/bridge.ts");
const { resetDbInstance } = await import("../../src/lib/db/core.ts");
const PUBLIC_MESSAGE = "Codex provider request failed";
const HOSTILE_MESSAGE =
"token=codex-secret-value at /srv/omniroute/private/config.json\nforged-log: admin=true";
type FailedPayload = {
type: "response.failed";
response: {
error: {
code: string | null;
message: string;
status_code?: number;
type?: string;
};
};
};
function responseFailedPayload(sse: string): FailedPayload {
for (const line of sse.split("\n")) {
if (!line.startsWith("data: ") || line === "data: [DONE]") continue;
const parsed = JSON.parse(line.slice("data: ".length)) as Record<string, unknown>;
if (parsed.type === "response.failed") return parsed as FailedPayload;
}
assert.fail(`response.failed frame missing from: ${sse}`);
}
function assertPublicFailure(
payload: FailedPayload,
expected: { code: string; type: string; statusCode?: number }
): void {
assert.equal(payload.response.error.message, PUBLIC_MESSAGE);
assert.equal(payload.response.error.code, expected.code);
assert.equal(payload.response.error.type, expected.type);
if (expected.statusCode !== undefined) {
assert.equal(payload.response.error.status_code, expected.statusCode);
}
assert.ok(!JSON.stringify(payload).includes(HOSTILE_MESSAGE));
assert.ok(!JSON.stringify(payload).includes("codex-secret-value"));
assert.ok(!JSON.stringify(payload).includes("/srv/omniroute/private"));
}
async function executeCodexWebSocketFailure(
websocket: Parameters<typeof __setCodexWebSocketTransportForTesting>[0]
): Promise<string> {
__setCodexWebSocketTransportForTesting(websocket);
try {
const result = await new CodexExecutor().execute({
model: "gpt-5.5",
body: { model: "gpt-5.5", input: "hello" },
stream: true,
credentials: {
accessToken: "test-token",
providerSpecificData: { codexTransport: "websocket" },
},
});
return await result.response.text();
} finally {
__setCodexWebSocketTransportForTesting(undefined);
}
}
async function executeAppServerFailure(stream: boolean): Promise<Response> {
const socket: CodexWreqWebSocket = {
send(data: string) {
const frame = JSON.parse(data) as Record<string, unknown>;
if (frame.id == null || typeof frame.method !== "string") return;
queueMicrotask(() => {
if (frame.method === "thread/start") {
socket.onmessage?.({
data: JSON.stringify({
jsonrpc: "2.0",
id: frame.id,
result: { thread: { id: "thread-public-boundary" } },
}),
});
return;
}
if (frame.method === "turn/start") {
socket.onmessage?.({
data: JSON.stringify({
jsonrpc: "2.0",
id: frame.id,
result: { turn: { id: "turn-public-boundary", status: "inProgress" } },
}),
});
setTimeout(() => {
socket.onmessage?.({
data: JSON.stringify({
jsonrpc: "2.0",
method: "error",
params: { error: { message: HOSTILE_MESSAGE } },
}),
});
}, 0);
return;
}
socket.onmessage?.({
data: JSON.stringify({ jsonrpc: "2.0", id: frame.id, result: {} }),
});
});
},
close() {},
onmessage: null,
onerror: null,
onclose: null,
};
const executor = new CodexAppServerExecutor({ websocketFn: async () => socket });
const result = await executor.execute({
model: "gpt-5.5",
body: { input: "hello" },
stream,
credentials: {
providerSpecificData: {
codexTransport: "app-server",
codexAppServerUrl: "ws://codex-app-server.test:1456",
codexAppServerToken: "test-app-server-token",
},
},
});
return result.response;
}
test.after(() => {
__setCodexWebSocketTransportForTesting(undefined);
resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.rmSync(TEST_PLUGINS_DIR, { recursive: true, force: true });
});
test("Codex same-format error event emits only a fixed public failure contract", () => {
const result = encodeResponseSseEvent(
JSON.stringify({
type: "error",
status_code: 502,
error: {
code: "secret_backend_code_9182",
type: "secret_backend_type_7731",
message: HOSTILE_MESSAGE,
},
})
);
assert.equal(result.terminal, true);
assertPublicFailure(responseFailedPayload(result.sse), {
code: "upstream_server_error",
type: "server_error",
statusCode: 502,
});
});
test("Codex same-format quota classification survives while its raw message does not", () => {
const result = encodeResponseSseEvent(
JSON.stringify({
type: "response.failed",
response: {
status: "failed",
error: { code: "usage_limit_reached", message: HOSTILE_MESSAGE },
},
})
);
assertPublicFailure(responseFailedPayload(result.sse), {
code: "usage_limit_reached",
type: "rate_limit_error",
statusCode: 429,
});
});
test("Codex same-format failures reject contradictory allowlisted status, code and type", () => {
const wrongStatus = responseFailedPayload(
encodeResponseSseEvent(
JSON.stringify({
type: "response.failed",
status_code: 502,
response: {
status: "failed",
error: {
code: "invalid_api_key",
type: "rate_limit_error",
message: HOSTILE_MESSAGE,
},
},
})
).sse
);
assertPublicFailure(wrongStatus, {
code: "upstream_server_error",
type: "server_error",
statusCode: 502,
});
const wrongType = responseFailedPayload(
encodeResponseSseEvent(
JSON.stringify({
type: "response.failed",
status_code: 401,
response: {
status: "failed",
error: {
code: "invalid_api_key",
type: "rate_limit_error",
message: HOSTILE_MESSAGE,
},
},
})
).sse
);
assertPublicFailure(wrongType, {
code: "invalid_api_key",
type: "authentication_error",
statusCode: 401,
});
});
test("Codex WebSocket in-flight error event cannot expose transport details", async () => {
const socket = {
send() {
queueMicrotask(() => socket.onerror?.({ message: HOSTILE_MESSAGE }));
},
close() {},
onmessage: null as ((event: { data: unknown }) => void) | null,
onerror: null as ((event: { message?: string }) => void) | null,
onclose: null as (() => void) | null,
};
const sse = await executeCodexWebSocketFailure(async () => socket);
assertPublicFailure(responseFailedPayload(sse), {
code: "upstream_websocket_error",
type: "provider_error",
});
});
test("Codex WebSocket connection failure cannot expose exception details", async () => {
const sse = await executeCodexWebSocketFailure(async () => {
throw new Error(HOSTILE_MESSAGE);
});
assertPublicFailure(responseFailedPayload(sse), {
code: "upstream_websocket_connect_failed",
type: "provider_error",
});
});
test("Codex App Server streaming failure is projected before the HTTP 200 SSE boundary", async () => {
const response = await executeAppServerFailure(true);
assert.equal(response.status, 200);
assertPublicFailure(responseFailedPayload(await response.text()), {
code: "codex_app_server_turn_failed",
type: "provider_error",
});
});
test("Codex App Server non-streaming failure is projected before the HTTP 200 JSON boundary", async () => {
const response = await executeAppServerFailure(false);
assert.equal(response.status, 200);
const body = (await response.json()) as FailedPayload["response"] & { status: string };
assert.equal(body.status, "failed");
assertPublicFailure(
{ type: "response.failed", response: body },
{
code: "codex_app_server_turn_failed",
type: "provider_error",
}
);
});
test("ChatGPT Web Playwright adapter failures keep safe routing metadata without raw text", async () => {
async function* browserEvents(): AsyncGenerator<AdapterEvent> {
yield {
type: "error",
message: HOSTILE_MESSAGE,
status: 502,
errorType: "server_error",
code: "chatgpt_submission_ambiguous",
retryable: false,
};
}
const sse = await new Response(bridgeToResponsesSSE(browserEvents(), "gpt-5.5")).text();
assertPublicFailure(responseFailedPayload(sse), {
code: "chatgpt_submission_ambiguous",
type: "server_error",
});
});
test("Codex bridge projects message-only adapter failures before SSE serialization", async () => {
async function* messageOnlyEvents(): AsyncGenerator<AdapterEvent> {
yield { type: "error", message: HOSTILE_MESSAGE };
}
const sse = await new Response(bridgeToResponsesSSE(messageOnlyEvents(), "gpt-5.5")).text();
assertPublicFailure(responseFailedPayload(sse), {
code: "upstream_server_error",
type: "server_error",
});
});
test("Codex batch bridge projects message-only adapter failures before JSON serialization", () => {
const body = buildResponseJSON(
[{ type: "error", message: HOSTILE_MESSAGE }],
"gpt-5.5"
) as FailedPayload["response"] & { status: string };
assert.equal(body.status, "failed");
assertPublicFailure(
{ type: "response.failed", response: body },
{
code: "upstream_server_error",
type: "server_error",
}
);
});
test("Codex bridge exceptions cannot serialize raw exception messages", async () => {
async function* throwingEvents(): AsyncGenerator<AdapterEvent> {
throw new Error(HOSTILE_MESSAGE);
}
const sse = await new Response(bridgeToResponsesSSE(throwingEvents(), "gpt-5.5")).text();
assertPublicFailure(responseFailedPayload(sse), {
code: "upstream_server_error",
type: "server_error",
});
});
test("Codex batch bridge applies the same public failure projector", () => {
const body = buildResponseJSON(
[
{
type: "error",
message: HOSTILE_MESSAGE,
status: 502,
errorType: "server_error",
code: "chatgpt_submitted_turn_failed",
retryable: false,
},
],
"gpt-5.5"
) as FailedPayload["response"] & { status: string };
assert.equal(body.status, "failed");
assertPublicFailure(
{ type: "response.failed", response: body },
{
code: "chatgpt_submitted_turn_failed",
type: "server_error",
}
);
});

View File

@@ -1,211 +0,0 @@
// This suite owns process-wide DATA_DIR, plugin, logger, and DB state. It must run only inside
// the subprocess launched by tests/unit/stream-handler-public-error-boundary.test.ts.
import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import test from "node:test";
const originalDataDir = process.env.DATA_DIR;
const originalPluginsDir = process.env.OMNIROUTE_PLUGINS_DIR;
const testRoot = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-stream-public-error-"));
const TEST_DATA_DIR = path.join(testRoot, "data");
const TEST_PLUGINS_DIR = path.join(testRoot, "plugins");
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
fs.mkdirSync(TEST_PLUGINS_DIR, { recursive: true });
process.env.DATA_DIR = TEST_DATA_DIR;
process.env.OMNIROUTE_PLUGINS_DIR = TEST_PLUGINS_DIR;
const [core, callLogs, artifactWriter, loggerResource, streamHandler, { FORMATS }] =
await Promise.all([
import("../../src/lib/db/core.ts"),
import("../../src/lib/usage/callLogs.ts"),
import("../../src/lib/usage/callLogArtifactWriter.ts"),
import("../../src/shared/utils/loggerResource.ts"),
import("../../open-sse/utils/streamHandler.ts"),
import("../../open-sse/translator/formats.ts"),
]);
const { createStreamController, pipeWithDisconnect } = streamHandler;
const SECRET = "sk-live-streamhandler-secret-123456";
const API_KEY = "provider-key-streamhandler-654321";
const PRIVATE_PATH = "/srv/omniroute/private/provider.ts:42:9";
const RAW_MESSAGE =
`Upstream failed at ${PRIVATE_PATH} Authorization: Bearer ${SECRET} api_key=${API_KEY}` +
`\n at dispatch (/srv/omniroute/private/dispatcher.ts:88:3)`;
test.after(async () => {
assert.equal(await callLogs.waitForCallLogSaves(3_000), true);
await artifactWriter.closeCallLogArtifactWriter();
core.resetDbInstance();
await loggerResource.closeSharedLoggerResource();
if (originalDataDir === undefined) delete process.env.DATA_DIR;
else process.env.DATA_DIR = originalDataDir;
if (originalPluginsDir === undefined) delete process.env.OMNIROUTE_PLUGINS_DIR;
else process.env.OMNIROUTE_PLUGINS_DIR = originalPluginsDir;
fs.rmSync(testRoot, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("fixture binds all persistent state to its process-owned directories", () => {
assert.equal(core.DATA_DIR, TEST_DATA_DIR);
assert.equal(core.SQLITE_FILE, path.join(TEST_DATA_DIR, "storage.sqlite"));
assert.equal(process.env.DATA_DIR, TEST_DATA_DIR);
assert.equal(process.env.OMNIROUTE_PLUGINS_DIR, TEST_PLUGINS_DIR);
assert.equal(fs.existsSync(TEST_DATA_DIR), true);
assert.equal(fs.existsSync(TEST_PLUGINS_DIR), true);
});
test("OpenAI stream failures keep raw diagnostics internal and sanitize the public wire", async () => {
const upstreamError = Object.assign(new Error(RAW_MESSAGE), { statusCode: 502 });
const source = new ReadableStream<Uint8Array>({
start(controller) {
controller.error(upstreamError);
},
});
let internalMessage = "";
const stream = pipeWithDisconnect(
new Response(source),
new TransformStream<Uint8Array, Uint8Array>(),
createStreamController({
clientResponseFormat: FORMATS.OPENAI,
onError(event) {
internalMessage = event.message;
return true;
},
}),
{ stallTimeoutMs: 0 }
);
const publicWire = await new Response(stream).text();
assert.equal(internalMessage, RAW_MESSAGE, "failure classification must retain the raw message");
assert.match(publicWire, /"finish_reason":"error"/);
assert.match(publicWire, /"code":"server_error"/);
assert.match(publicWire, /\[DONE\]/);
assert.doesNotMatch(publicWire, new RegExp(SECRET));
assert.doesNotMatch(publicWire, new RegExp(API_KEY));
assert.doesNotMatch(publicWire, /\/srv\/omniroute\/private/);
assert.doesNotMatch(publicWire, /dispatcher\.ts/);
assert.match(publicWire, /Authorization: \[REDACTED\]/);
assert.match(publicWire, /<path>/);
});
test("Responses stream failures preserve the failure event shape without leaking diagnostics", async () => {
const upstreamError = Object.assign(new Error(RAW_MESSAGE), { statusCode: 429 });
const source = new ReadableStream<Uint8Array>({
start(controller) {
controller.error(upstreamError);
},
});
let internalError: unknown;
const stream = pipeWithDisconnect(
new Response(source),
new TransformStream<Uint8Array, Uint8Array>(),
createStreamController({
clientResponseFormat: FORMATS.OPENAI_RESPONSES,
onError(event) {
internalError = event.error;
return true;
},
}),
{ stallTimeoutMs: 0 }
);
const publicWire = await new Response(stream).text();
assert.equal(internalError, upstreamError, "the original error object must reach classification");
assert.match(publicWire, /event: response\.failed/);
assert.match(publicWire, /"type":"response\.failed"/);
assert.match(publicWire, /"type":"rate_limit_error"/);
assert.match(publicWire, /"code":"rate_limit_exceeded"/);
assert.doesNotMatch(publicWire, new RegExp(SECRET));
assert.doesNotMatch(publicWire, new RegExp(API_KEY));
assert.doesNotMatch(publicWire, /\/srv\/omniroute\/private/);
assert.doesNotMatch(publicWire, /dispatcher\.ts/);
assert.match(publicWire, /Authorization: \[REDACTED\]/);
assert.match(publicWire, /<path>/);
});
test("Claude stream failures preserve error and stop events without leaking diagnostics", async () => {
const upstreamError = Object.assign(new Error(RAW_MESSAGE), { statusCode: 403 });
const source = new ReadableStream<Uint8Array>({
start(controller) {
controller.error(upstreamError);
},
});
let internalStatusCode = 0;
const stream = pipeWithDisconnect(
new Response(source),
new TransformStream<Uint8Array, Uint8Array>(),
createStreamController({
clientResponseFormat: FORMATS.CLAUDE,
onError(event) {
internalStatusCode = event.statusCode;
return true;
},
}),
{ stallTimeoutMs: 0 }
);
const publicWire = await new Response(stream).text();
assert.equal(internalStatusCode, 403);
assert.match(publicWire, /event: error/);
assert.match(publicWire, /"type":"permission_error"/);
assert.match(publicWire, /event: message_stop/);
assert.doesNotMatch(publicWire, new RegExp(SECRET));
assert.doesNotMatch(publicWire, new RegExp(API_KEY));
assert.doesNotMatch(publicWire, /\/srv\/omniroute\/private/);
assert.doesNotMatch(publicWire, /dispatcher\.ts/);
assert.match(publicWire, /Authorization: \[REDACTED\]/);
assert.match(publicWire, /<path>/);
});
test("stream diagnostics sanitize logs while callbacks retain the original failure", () => {
const upstreamError = Object.assign(new Error(RAW_MESSAGE), { statusCode: 502 });
const originalLog = console.log;
const logLines: string[] = [];
let internalError: unknown;
console.log = (...args: unknown[]) => {
logLines.push(args.map(String).join(" "));
};
try {
createStreamController({
provider: "test-provider",
model: "test-model",
onError(event) {
internalError = event.error;
return true;
},
}).handleError(upstreamError);
} finally {
console.log = originalLog;
}
const logs = logLines.join("\n");
assert.equal(internalError, upstreamError);
assert.match(logs, /error: Upstream failed at <path>/);
assert.match(logs, /Authorization: \[REDACTED\]/);
assert.doesNotMatch(logs, new RegExp(SECRET));
assert.doesNotMatch(logs, new RegExp(API_KEY));
assert.doesNotMatch(logs, /\/srv\/omniroute\/private/);
assert.doesNotMatch(logs, /dispatcher\.ts/);
});
test("client disconnects stay outside the provider-failure callback", () => {
let providerFailureRecorded = false;
const controller = createStreamController({
onError() {
providerFailureRecorded = true;
return true;
},
});
controller.handleError(new DOMException("request_signal_aborted", "AbortError"));
assert.equal(providerFailureRecorded, false);
assert.equal(controller.signal.aborted, false);
});

View File

@@ -5,7 +5,7 @@ import test from "node:test";
const REPO_ROOT = fileURLToPath(new URL("../..", import.meta.url));
const FIXTURE = fileURLToPath(
new URL("../fixtures/stream-handler-public-error-boundary.fixture.ts", import.meta.url)
new URL("../fixtures/codex-response-failed-boundary.fixture.ts", import.meta.url)
);
const CHILD_RUNTIME_ENV_KEYS = [
@@ -25,9 +25,8 @@ function buildFixtureEnv(): NodeJS.ProcessEnv {
const env: NodeJS.ProcessEnv = {
NODE_ENV: "test",
APP_LOG_TO_FILE: "false",
API_KEY_SECRET: "stream-handler-boundary-fixture-secret-20260902",
API_KEY_SECRET: "codex-boundary-fixture-api-key-secret-20260902",
DISABLE_SQLITE_AUTO_BACKUP: "true",
NO_COLOR: "1",
};
for (const key of CHILD_RUNTIME_ENV_KEYS) {
@@ -35,28 +34,40 @@ function buildFixtureEnv(): NodeJS.ProcessEnv {
if (value !== undefined) env[key] = value;
}
// Nested test runners must not inherit the parent runner's recursion marker.
// A nested test runner must receive its own context instead of inheriting the parent's.
delete env.NODE_TEST_CONTEXT;
return env;
}
test("generic stream public error boundaries pass in an isolated process", () => {
test("Codex public failure boundaries pass in an isolated process", () => {
const result = spawnSync(
process.execPath,
["--import", "tsx/esm", "--import", "./open-sse/utils/setupPolyfill.ts", "--test", FIXTURE],
[
"--import",
"tsx/esm",
"--import",
"./open-sse/utils/setupPolyfill.ts",
"--test",
"--test-force-exit",
FIXTURE,
],
{
cwd: REPO_ROOT,
encoding: "utf8",
env: buildFixtureEnv(),
timeout: 120_000,
timeout: 60_000,
}
);
const output = `${result.stdout}\n${result.stderr}`;
assert.ifError(result.error);
assert.equal(result.signal, null, output.slice(-12_000));
assert.equal(result.status, 0, output.slice(-12_000));
assert.match(output, /(?:^|\s)tests\s+6(?:\s|$)/m);
assert.match(output, /(?:^|\s)pass\s+6(?:\s|$)/m);
assert.match(output, /(?:^|\s)fail\s+0(?:\s|$)/m);
assert.equal(
result.signal,
null,
`isolated Codex boundary fixture terminated by ${result.signal}\n${result.stdout}\n${result.stderr}`
);
assert.equal(
result.status,
0,
`isolated Codex boundary fixture failed\n${result.stdout}\n${result.stderr}`
);
});

View File

@@ -95,14 +95,10 @@ test("handleChat names the shadowed custom node when the built-in prefix has no
/prefix "of" is reserved by the built-in provider "openference"/,
`runtime error must explain that the prefix resolved to the built-in, got: ${message}`
);
// Exact substring, not a hand-escaped RegExp: the name carries regex
// metacharacters (parentheses) and the previous `.replace(/[()]/g, …)` escaped
// only those, so any other metachar in a future name would have been
// interpreted instead of matched literally (CodeQL js/incomplete-sanitization).
const expectedNodeMention = `"${SHADOWED_NODE_NAME}" (${SHADOWED_NODE_ID})`;
assert.ok(
message.includes(expectedNodeMention),
`runtime error must name the shadowed node and its id (${expectedNodeMention}), got: ${message}`
assert.match(
message,
new RegExp(`"${SHADOWED_NODE_NAME.replace(/[()]/g, "\\$&")}" \\(${SHADOWED_NODE_ID}\\)`),
`runtime error must name the shadowed node and its id, got: ${message}`
);
assert.match(message, /Rename that node's prefix/);
});

View File

@@ -1,31 +0,0 @@
/**
* Strict recognizer for the UC (uncensored.com) Clerk session-token mint call,
* shared by the uc-image / uc-video mock `fetch` routers.
*
* The mock routers used to dispatch on `url.includes("clerk.uncensored.com")`.
* That is a substring test over a whole URL, so ANY host answers as long as the
* name appears somewhere in it — `https://evil.example/?next=clerk.uncensored.com`
* would have been served the mint response. A test whose router accepts a
* malformed URL cannot fail when the executor builds one, which is exactly the
* regression such a test exists to catch (and CodeQL flags it as
* `js/incomplete-url-substring-sanitization`).
*
* This matches the real shape instead:
* POST https://clerk.uncensored.com/v1/client/sessions/{sid}/tokens?_clerk_js_version=…
* comparing the parsed origin against the production constant and pinning the
* path shape.
*/
import { UC_CLERK_FAPI } from "../../../open-sse/executors/uc/constants.ts";
const MINT_PATH = /^\/v1\/client\/sessions\/[^/]+\/tokens$/;
/** True only for the Clerk mint endpoint on the real Clerk FAPI origin. */
export function isUcClerkMintUrl(raw: unknown): boolean {
let parsed: URL;
try {
parsed = new URL(String(raw));
} catch {
return false;
}
return parsed.origin === UC_CLERK_FAPI && MINT_PATH.test(parsed.pathname);
}

View File

@@ -9,7 +9,6 @@ import {
} from "../../open-sse/handlers/imageGeneration/providers/maxaiImage.ts";
import { IMAGE_PROVIDERS } from "../../open-sse/config/imageRegistry.ts";
import { __setMaxaiConstantsForTest } from "../../open-sse/executors/maxai/constantsStore.ts";
import { MAXAI_BASE_URL } from "../../open-sse/executors/maxai/protocol.ts";
import { MOCK_CONSTANTS } from "./helpers/maxaiMockConstants.ts";
// Image generation signs like any request; seed the in-process constants memo
@@ -29,9 +28,7 @@ const CRED = {
// --- Registry ------------------------------------------------------------
test("maxai is registered in IMAGE_PROVIDERS with the maxai-image format + 6 models", () => {
const entry = (
IMAGE_PROVIDERS as Record<string, { format?: string; baseUrl?: string; models?: unknown[] }>
)["maxai"];
const entry = (IMAGE_PROVIDERS as Record<string, { format?: string; baseUrl?: string; models?: unknown[] }>)["maxai"];
assert.ok(entry, "maxai must exist in IMAGE_PROVIDERS");
assert.equal(entry.format, "maxai-image");
assert.match(String(entry.baseUrl), /api\.maxai\.me\/gpt\/get_image_generate_response/);
@@ -96,10 +93,7 @@ test("handleMaxaiImageGeneration returns OpenAI image data on success", async ()
ok: true,
status: 200,
async json() {
return {
status: "OK",
data: [{ png_url: "https://cdn/x.png", webp_url: "https://cdn/x.webp" }],
};
return { status: "OK", data: [{ png_url: "https://cdn/x.png", webp_url: "https://cdn/x.webp" }] };
},
async text() {
return "";
@@ -117,12 +111,8 @@ test("handleMaxaiImageGeneration returns OpenAI image data on success", async ()
assert.equal(result.success, true);
assert.deepEqual(result.data?.data, [{ url: "https://cdn/x.png" }]);
// Hit the image endpoint with the signed body. Exact URL equality instead of a
// hand-escaped RegExp over the path — the old `.replace(/\//g, "\\/")` escaped
// only slashes (which need no escaping in a RegExp anyway) and would have let
// any other metacharacter through (CodeQL js/incomplete-sanitization), while
// also accepting the path appearing anywhere in a wrong URL.
assert.equal(capturedUrl, MAXAI_BASE_URL + MAXAI_IMAGE_PATH);
// Hit the image endpoint with the signed body.
assert.match(capturedUrl, new RegExp(MAXAI_IMAGE_PATH.replace(/\//g, "\\/")));
assert.equal(capturedBody.model_name, "flux-1-schnell");
assert.equal(capturedBody.size, "512x512"); // flux passes size through
assert.equal(capturedBody.n, 2);

View File

@@ -12,7 +12,6 @@ import {
computeMaxaiProof,
maxaiAesEncrypt,
buildMaxaiSignedHeaders,
maxaiRandomSlot,
} from "../../open-sse/executors/maxai/signing.ts";
import {
assembleMaxaiContext,
@@ -104,13 +103,7 @@ test("computeMaxaiProof blanks the user id only on /oauth/* routes", () => {
// A blank-user route yields a different proof than the same route with a uid,
// proving the uid is dropped for /oauth/* (and only there).
const t = 1784594159681;
const oauthWithUid = computeMaxaiProof(
"/oauth/signin_with_email",
t,
USER_ID,
HMAC_KEY,
APP_VERSION
);
const oauthWithUid = computeMaxaiProof("/oauth/signin_with_email", t, USER_ID, HMAC_KEY, APP_VERSION);
const oauthNoUid = computeMaxaiProof("/oauth/signin_with_email", t, "", HMAC_KEY, APP_VERSION);
assert.equal(oauthWithUid, oauthNoUid); // uid ignored for /oauth/*
const chatWithUid = computeMaxaiProof("/gpt/cwc/chat", t, USER_ID, HMAC_KEY, APP_VERSION);
@@ -313,28 +306,7 @@ test("buildMaxaiSignedHeaders emits the X-App/X-Browser companions + X-Authoriza
assert.equal(h["X-App-Version"], MOCK_APP_VERSION);
assert.equal(h["X-App-Env"], "MaxAI-Browser-Extension");
assert.ok(h["X-Authorization"].length > 0);
assert.equal(
Buffer.from(h["X-Authorization"], "base64").subarray(0, 8).toString("ascii"),
"Salted__"
);
});
test("maxaiRandomSlot emits an unbiased 6-digit X-Random slot", () => {
// The wire slot is always exactly 6 decimal digits, i.e. 100000-999999.
const samples = Array.from({ length: 4000 }, () => maxaiRandomSlot());
for (const s of samples) {
assert.match(s, /^\d{6}$/, `X-Random must be 6 digits, got: ${s}`);
const n = Number(s);
assert.ok(n >= 100000 && n <= 999999, `X-Random out of range: ${s}`);
}
// Regression guard for the modulo bias the previous
// `randomBytes(4).readUInt32BE(0) % 900000` draw introduced: the value must
// still spread across the whole range, not collapse onto its low end.
assert.ok(new Set(samples).size > samples.length * 0.9, "X-Random must not repeat heavily");
assert.ok(
samples.some((s) => Number(s) < 550000) && samples.some((s) => Number(s) >= 550000),
"X-Random must cover both halves of the 100000-999999 range"
);
assert.equal(Buffer.from(h["X-Authorization"], "base64").subarray(0, 8).toString("ascii"), "Salted__");
});
// ── Context assembly ─────────────────────────────────────────────────────────
@@ -392,12 +364,7 @@ test("contentToText flattens multipart content, dropping non-text parts", () =>
});
test("buildMaxaiChatBody pins field order + constants", () => {
const body = buildMaxaiChatBody({
conversationId: "conv-1",
text: "hi",
modelName: "gpt-5.6",
appVersion: APP_VERSION,
});
const body = buildMaxaiChatBody({ conversationId: "conv-1", text: "hi", modelName: "gpt-5.6", appVersion: APP_VERSION });
const keys = Object.keys(body);
assert.equal(keys[0], "chat_mode");
assert.equal(keys[3], "message_content");
@@ -412,12 +379,7 @@ test("buildMaxaiChatBody pins field order + constants", () => {
// ── Vision input (image_url parts) ───────────────────────────────────────────
test("buildMaxaiChatBody text-only path is unchanged (no imageUrls)", () => {
const body = buildMaxaiChatBody({
conversationId: "c",
text: "hi",
modelName: "gpt-5.6",
appVersion: APP_VERSION,
});
const body = buildMaxaiChatBody({ conversationId: "c", text: "hi", modelName: "gpt-5.6", appVersion: APP_VERSION });
// Byte-identical to the pre-vision shape: a single text part.
assert.deepEqual(body.message_content, [{ type: "text", text: "hi" }]);
assert.deepEqual(body.doc_list, []);
@@ -601,7 +563,8 @@ test("maxaiRefreshAccessToken sends the exact web-app request + parses data.acce
test("maxaiRefreshAccessToken returns a structured error on non-200 (no throw)", async () => {
const nowSec = Math.floor(Date.now() / 1000);
const fakeFetch = (async () => new Response("nope", { status: 418 })) as unknown as typeof fetch;
const fakeFetch = (async () =>
new Response("nope", { status: 418 })) as unknown as typeof fetch;
const result = await maxaiRefreshAccessToken({
refreshToken: fakeJwt(nowSec + 1000, USER_ID),
deviceId: "dev",
@@ -724,9 +687,7 @@ test("verifyMaxaiEmailCode maps code 10119 to an expired-code message", async ()
test("verifyMaxaiEmailCode defaults to an invalid-code message otherwise", async () => {
const fakeFetch = (async () =>
new Response(JSON.stringify({ data: { status: "FAIL" } }), {
status: 200,
})) as unknown as typeof fetch;
new Response(JSON.stringify({ data: { status: "FAIL" } }), { status: 200 })) as unknown as typeof fetch;
const r = await verifyMaxaiEmailCode({
email: "x@y.z",
code: "999999",
@@ -1048,9 +1009,10 @@ test("discoverMaxaiModels drops deprecated, non-chat, and non-curated models", a
test("discoverMaxaiModels falls back to the catalog window when max_tokens is absent", async () => {
const fakeFetch = (async () =>
new Response(modelsConfigBody([{ model_name: "claude-5-sonnet", type: "chat" }]), {
status: 200,
})) as unknown as typeof fetch;
new Response(
modelsConfigBody([{ model_name: "claude-5-sonnet", type: "chat" }]),
{ status: 200 }
)) as unknown as typeof fetch;
const { models } = await discoverMaxaiModels({
providerSpecificData: DISCOVERY_CRED.providerSpecificData,
accessToken: DISCOVERY_CRED.accessToken,

View File

@@ -256,8 +256,7 @@ test("createDisconnectAwareStream emits Responses API failure events for Respons
assert.match(text, /event: response\.failed/);
assert.match(text, /"type":"response\.failed"/);
assert.match(text, /"message":"responses stream"/);
assert.doesNotMatch(text, /died/);
assert.match(text, /"message":"responses stream\\ndied"/);
assert.match(text, /"type":"server_error"/);
assert.match(text, /"code":"server_error"/);
assert.doesNotMatch(text, /chat\.completion\.chunk/);
@@ -265,7 +264,7 @@ test("createDisconnectAwareStream emits Responses API failure events for Respons
assert.doesNotMatch(text, /\[DONE\]/);
});
test("createDisconnectAwareStream strips multiline diagnostic tails from Responses errors", async () => {
test("createDisconnectAwareStream keeps newlines escaped inside SSE data fields", async () => {
const upstreamError = Object.assign(new Error("line one\nline two\rline three"), {
statusCode: 400,
});
@@ -291,9 +290,9 @@ test("createDisconnectAwareStream strips multiline diagnostic tails from Respons
const text = await readStreamText(stream);
assert.match(text, /^event: response\.failed\ndata: \{"type":"response\.failed"/);
assert.match(text, /"message":"line one"/);
assert.doesNotMatch(text, /line two/);
assert.doesNotMatch(text, /line three/);
assert.match(text, /"message":"line one\\nline two\\rline three"/);
assert.doesNotMatch(text, /^line two/m);
assert.doesNotMatch(text, /^line three/m);
});
test("createDisconnectAwareStream treats legacy OpenAI response format alias as Responses", async () => {
@@ -361,7 +360,7 @@ test("createDisconnectAwareStream emits Claude SSE errors for Claude clients", a
assert.doesNotMatch(text, /\[DONE\]/);
});
test("createDisconnectAwareStream strips multiline diagnostic tails from Claude errors", async () => {
test("createDisconnectAwareStream keeps newlines escaped for Claude SSE errors", async () => {
const upstreamError = Object.assign(new Error("claude line one\nclaude line two"), {
statusCode: 502,
});
@@ -387,8 +386,8 @@ test("createDisconnectAwareStream strips multiline diagnostic tails from Claude
const text = await readStreamText(stream);
assert.match(text, /^event: error\ndata: \{"type":"error"/);
assert.match(text, /"message":"claude line one"/);
assert.doesNotMatch(text, /claude line two/);
assert.match(text, /"message":"claude line one\\nclaude line two"/);
assert.doesNotMatch(text, /^claude line two/m);
});
// #7699/#7816 — heuristic is scoped to FORMATS.CLAUDE (/v1/messages); a

View File

@@ -9,7 +9,6 @@ import {
UC_DIRECT_IMAGE_URL,
} from "../../open-sse/handlers/imageGeneration/providers/ucImage.ts";
import { IMAGE_PROVIDERS, parseImageModel } from "../../open-sse/config/imageRegistry.ts";
import { isUcClerkMintUrl } from "./helpers/ucClerkUrl.ts";
// A valid PERSONA credential (durable Clerk cookie + sid + uid in psd). No API
// key, so the handler takes the persona web path (mint -> POST -> poll).
@@ -145,7 +144,7 @@ function personaFetch(opts: {
let pollsSeen = 0;
return (async (url: string, init: RequestInit = {}) => {
// 1) Clerk mint
if (isUcClerkMintUrl(url)) {
if (url.includes("clerk.uncensored.com")) {
return {
ok: true,
status: 200,
@@ -266,7 +265,7 @@ test("handleUcImageGeneration (persona) times out with 504 when the result never
test("handleUcImageGeneration (persona) surfaces a Clerk mint failure", async () => {
const fetchImpl = (async (url: string) => {
if (isUcClerkMintUrl(url)) {
if (url.includes("clerk.uncensored.com")) {
return {
ok: false,
status: 401,

View File

@@ -13,7 +13,6 @@ import {
UC_DIRECT_VIDEO_URL,
} from "../../open-sse/handlers/videoGeneration/providers/ucVideo.ts";
import { VIDEO_PROVIDERS } from "../../open-sse/config/videoRegistry.ts";
import { isUcClerkMintUrl } from "./helpers/ucClerkUrl.ts";
// A valid PERSONA credential (durable Clerk cookie + sid + uid in psd). No API
// key, so the handler takes the persona web path (mint -> generate -> poll).
@@ -149,7 +148,7 @@ function personaFetch(opts: {
let pollsSeen = 0;
return (async (url: string, init: RequestInit = {}) => {
// Clerk mint
if (isUcClerkMintUrl(url)) {
if (url.includes("clerk.uncensored.com")) {
return {
ok: true,
status: 200,
@@ -340,7 +339,7 @@ test("handleUcVideoGeneration (persona) times out with 504 when never ready", as
test("handleUcVideoGeneration (persona) surfaces a Clerk mint failure", async () => {
const fetchImpl = (async (url: string) => {
if (isUcClerkMintUrl(url)) {
if (url.includes("clerk.uncensored.com")) {
return {
ok: false,
status: 401,