@opengeni/codex 0.2.11 → 0.2.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -326,6 +326,20 @@ function normalizeCodexRequestBody(body, resolveModel) {
326
326
  }
327
327
  return body;
328
328
  }
329
+ function normalizedCodexRequestBody(body, resolveModel) {
330
+ const projected = { ...body };
331
+ if (body.reasoning && typeof body.reasoning === "object" && !Array.isArray(body.reasoning)) {
332
+ projected.reasoning = { ...body.reasoning };
333
+ }
334
+ if (Array.isArray(body.input)) {
335
+ projected.input = body.input.map((item) => {
336
+ if (!item || typeof item !== "object" || Array.isArray(item)) return item;
337
+ const record = item;
338
+ return "id" in record || record.type === "tool_search_call" && typeof record.arguments === "string" ? { ...record } : item;
339
+ });
340
+ }
341
+ return normalizeCodexRequestBody(projected, resolveModel);
342
+ }
329
343
  function buildModelResolver(liveSlugs, fallbackSlug) {
330
344
  return (requested) => {
331
345
  const stripped = requested.includes("/") ? requested.slice(requested.indexOf("/") + 1) : requested;
@@ -808,6 +822,12 @@ function opaqueProviderArtifactFingerprints(input) {
808
822
 
809
823
  // src/fetch.ts
810
824
  var CODEX_TRANSPORT_ERROR_HEADER = "x-opengeni-codex-transport-error";
825
+ var CODEX_REQUEST_BODY_NORMALIZED_HEADER = "x-opengeni-request-body-normalized";
826
+ var REPLAYABLE_REQUEST_BODY_FACTORY = Symbol.for("opengeni.replayable-request-body-factory");
827
+ var CODEX_REQUEST_MODEL_HEADER = "x-opengeni-request-model";
828
+ var CODEX_REQUEST_ID_HEADER = "x-opengeni-request-id";
829
+ var CODEX_REQUEST_CALLER_STREAM_HEADER = "x-opengeni-request-caller-stream";
830
+ var MAX_CODEX_ERROR_BODY_BYTES = 64 * 1024;
811
831
  function headersCarryCodexTransportMarker(headers) {
812
832
  if (!headers || typeof headers !== "object") return false;
813
833
  const getter = headers.get;
@@ -1036,16 +1056,20 @@ async function observedResponse(res, audit, externalSignal) {
1036
1056
  controller.close();
1037
1057
  return;
1038
1058
  }
1039
- armIdle();
1040
1059
  if (!firstByte) {
1041
1060
  firstByte = true;
1061
+ if (idleTimer) clearTimeout(idleTimer);
1062
+ controller.enqueue(chunk.value);
1042
1063
  await emitRequestEvent(audit, {
1043
1064
  phase: "first_byte",
1044
1065
  responseObserved: true,
1045
1066
  status: res.status,
1046
1067
  ...requestId ? { providerRequestId: requestId } : {}
1047
1068
  });
1069
+ if (!terminal) armIdle();
1070
+ return;
1048
1071
  }
1072
+ armIdle();
1049
1073
  controller.enqueue(chunk.value);
1050
1074
  } catch (error) {
1051
1075
  if (terminal) return;
@@ -1113,11 +1137,19 @@ function codexSubscriptionFetch(base = globalThis.fetch) {
1113
1137
  const rawUrl = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
1114
1138
  const rewritten = rawUrl.replace(/(?<!\/codex)\/responses(\b|$)/, "/codex/responses$1");
1115
1139
  const policy = resolveCodexResponseTimeoutPolicy(ctx.responseTimeoutPolicy);
1116
- const requestId = ctx.nextRequestId?.() ?? randomUUID();
1140
+ const handedRequestId = new Headers(init?.headers).get(CODEX_REQUEST_ID_HEADER);
1141
+ const requestId = handedRequestId ?? ctx.nextRequestId?.() ?? randomUUID();
1117
1142
  const logicalStartedAt = Date.now();
1118
1143
  let transportAttempt = 0;
1119
1144
  const attempt = async (auth, authenticationAttempt) => {
1120
1145
  const headers = new Headers(init?.headers);
1146
+ const bodyAlreadyNormalized = headers.get(CODEX_REQUEST_BODY_NORMALIZED_HEADER) === "1";
1147
+ const normalizedModel = headers.get(CODEX_REQUEST_MODEL_HEADER) ?? void 0;
1148
+ const normalizedCallerStream = headers.get(CODEX_REQUEST_CALLER_STREAM_HEADER);
1149
+ headers.delete(CODEX_REQUEST_BODY_NORMALIZED_HEADER);
1150
+ headers.delete(CODEX_REQUEST_MODEL_HEADER);
1151
+ headers.delete(CODEX_REQUEST_ID_HEADER);
1152
+ headers.delete(CODEX_REQUEST_CALLER_STREAM_HEADER);
1121
1153
  headers.set("Authorization", `Bearer ${auth.accessToken}`);
1122
1154
  if (auth.chatgptAccountId) {
1123
1155
  headers.set("ChatGPT-Account-ID", auth.chatgptAccountId);
@@ -1141,11 +1173,16 @@ function codexSubscriptionFetch(base = globalThis.fetch) {
1141
1173
  if (ctx.turnMetadata && Object.keys(ctx.turnMetadata).length > 0) {
1142
1174
  headers.set("x-codex-turn-metadata", JSON.stringify(ctx.turnMetadata));
1143
1175
  }
1144
- let callerWantsStream = true;
1145
- let model;
1176
+ let callerWantsStream = bodyAlreadyNormalized ? normalizedCallerStream !== "0" : true;
1177
+ let model = normalizedModel;
1146
1178
  let requestOpaqueArtifacts = [];
1147
- const nextInit = { ...init, headers };
1148
- if (typeof init?.body === "string") {
1179
+ const replayableBodyFactory = init?.[REPLAYABLE_REQUEST_BODY_FACTORY];
1180
+ const nextInit = {
1181
+ ...init,
1182
+ headers,
1183
+ ...replayableBodyFactory ? { body: replayableBodyFactory() } : {}
1184
+ };
1185
+ if (!bodyAlreadyNormalized && typeof init?.body === "string") {
1149
1186
  try {
1150
1187
  const parsed = JSON.parse(init.body);
1151
1188
  callerWantsStream = parsed.stream === true;
@@ -1154,24 +1191,25 @@ function codexSubscriptionFetch(base = globalThis.fetch) {
1154
1191
  nextInit.body = JSON.stringify(normalized);
1155
1192
  requestOpaqueArtifacts = opaqueProviderArtifactFingerprints(normalized.input);
1156
1193
  } catch {
1194
+ throw new Error("Model request could not be prepared");
1157
1195
  }
1196
+ } else if (!bodyAlreadyNormalized) {
1197
+ throw new Error("Model request could not be prepared");
1198
+ }
1199
+ if (!bodyAlreadyNormalized) {
1200
+ ctx.onRequestOpaqueArtifacts?.({ requestId, fingerprints: requestOpaqueArtifacts });
1158
1201
  }
1159
- ctx.onRequestOpaqueArtifacts?.({ requestId, fingerprints: requestOpaqueArtifacts });
1160
1202
  headers.set(
1161
1203
  "Idempotency-Key",
1162
1204
  authenticationAttempt === 0 ? requestId : `${requestId}:auth-${authenticationAttempt}`
1163
1205
  );
1164
1206
  if (process.env.CODEX_DEBUG) {
1165
- let keys = [];
1166
- if (typeof nextInit.body === "string") {
1167
- try {
1168
- keys = Object.keys(JSON.parse(nextInit.body));
1169
- } catch {
1170
- }
1171
- }
1172
- console.error(
1173
- `[codex-debug] POST ${rewritten} stream=${callerWantsStream} bodyKeys=[${keys.join(",")}]`
1174
- );
1207
+ console.error("[codex-debug] request dispatched", {
1208
+ method: "POST",
1209
+ origin: "codex-subscription",
1210
+ route: "codex_responses",
1211
+ stream: callerWantsStream
1212
+ });
1175
1213
  }
1176
1214
  let res;
1177
1215
  transportAttempt += 1;
@@ -1228,14 +1266,16 @@ function codexSubscriptionFetch(base = globalThis.fetch) {
1228
1266
  ctx.onUsageHeaders?.(usage);
1229
1267
  }
1230
1268
  if (process.env.CODEX_DEBUG && !res.ok) {
1231
- console.error(
1232
- `[codex-debug] <- ${res.status} requestId=${res.headers.get("x-request-id") ?? "unknown"}`
1233
- );
1269
+ console.error("[codex-debug] request failed", {
1270
+ origin: "codex-subscription",
1271
+ route: "codex_responses",
1272
+ status: res.status
1273
+ });
1234
1274
  }
1235
1275
  if (!res.ok) {
1236
1276
  return await bufferCodexErrorResponse(res);
1237
1277
  }
1238
- return callerWantsStream ? repairCodexStream(res) : await sseToJsonResponse(res);
1278
+ return callerWantsStream ? validateCodexStream(res) : await sseToJsonResponse(res);
1239
1279
  };
1240
1280
  try {
1241
1281
  let res = await attempt(await ctx.getToken(), 0);
@@ -1273,27 +1313,75 @@ function classifyCodexUsageLimitError(error) {
1273
1313
  return null;
1274
1314
  }
1275
1315
  async function bufferCodexErrorResponse(res) {
1276
- const bodyText = await res.text().catch(() => "");
1316
+ const { text: bodyText, truncated } = await readBoundedResponseText(
1317
+ res,
1318
+ MAX_CODEX_ERROR_BODY_BYTES
1319
+ );
1277
1320
  const headers = new Headers(res.headers);
1278
1321
  headers.set("content-type", "application/json");
1279
1322
  headers.set(CODEX_TRANSPORT_ERROR_HEADER, "1");
1280
1323
  headers.delete("content-length");
1281
1324
  headers.delete("content-encoding");
1282
1325
  let errorType;
1326
+ let responseBody = bodyText;
1283
1327
  try {
1284
1328
  const parsed = JSON.parse(bodyText);
1285
1329
  errorType = typeof parsed.error?.type === "string" ? parsed.error.type : void 0;
1286
1330
  } catch {
1287
1331
  }
1332
+ if (truncated) {
1333
+ responseBody = JSON.stringify({
1334
+ error: {
1335
+ type: "provider_error_body_too_large",
1336
+ code: "provider_error_body_too_large",
1337
+ message: `The provider returned an error body larger than ${MAX_CODEX_ERROR_BODY_BYTES} bytes`
1338
+ }
1339
+ });
1340
+ headers.set("x-opengeni-provider-error-truncated", "1");
1341
+ }
1288
1342
  if (errorType === CODEX_USAGE_LIMIT_ERROR_TYPE) {
1289
1343
  headers.set("x-should-retry", "false");
1290
1344
  }
1291
- return new Response(bodyText, {
1345
+ return new Response(responseBody, {
1292
1346
  status: res.status,
1293
1347
  statusText: res.statusText,
1294
1348
  headers
1295
1349
  });
1296
1350
  }
1351
+ async function readBoundedResponseText(response, maxBytes) {
1352
+ if (!response.body) return { text: "", truncated: false };
1353
+ const reader = response.body.getReader();
1354
+ const decoder = new TextDecoder();
1355
+ const parts = [];
1356
+ let bytes = 0;
1357
+ let truncated = false;
1358
+ try {
1359
+ while (bytes < maxBytes) {
1360
+ const next = await reader.read();
1361
+ if (next.done) {
1362
+ parts.push(decoder.decode());
1363
+ return { text: parts.join(""), truncated };
1364
+ }
1365
+ const remaining = maxBytes - bytes;
1366
+ const accepted = next.value.byteLength > remaining ? next.value.subarray(0, remaining) : next.value;
1367
+ bytes += accepted.byteLength;
1368
+ parts.push(decoder.decode(accepted, { stream: true }));
1369
+ if (accepted.byteLength !== next.value.byteLength) {
1370
+ truncated = true;
1371
+ break;
1372
+ }
1373
+ if (bytes >= maxBytes) {
1374
+ truncated = true;
1375
+ break;
1376
+ }
1377
+ }
1378
+ } catch {
1379
+ truncated = true;
1380
+ } finally {
1381
+ if (truncated) void reader.cancel().catch(() => void 0);
1382
+ }
1383
+ return { text: parts.join(""), truncated };
1384
+ }
1297
1385
  async function sseToJsonResponse(res) {
1298
1386
  const text = await res.text();
1299
1387
  let final = null;
@@ -1492,8 +1580,8 @@ var CodexStreamingTerminalError = class extends Error {
1492
1580
  responseStatus;
1493
1581
  headers;
1494
1582
  error;
1495
- constructor(projection, publicMessage) {
1496
- super(publicMessage);
1583
+ constructor(projection) {
1584
+ super(projection.error.message);
1497
1585
  this.name = "CodexStreamingTerminalError";
1498
1586
  this.status = projection.status;
1499
1587
  this.code = projection.error.code;
@@ -1511,6 +1599,8 @@ var CodexStreamingTerminalError = class extends Error {
1511
1599
  this.error = {
1512
1600
  type: projection.error.type,
1513
1601
  code: projection.error.code,
1602
+ message: projection.error.message,
1603
+ ...projection.error.param ? { param: projection.error.param } : {},
1514
1604
  ...projection.error.event_type ? { event_type: projection.error.event_type } : {},
1515
1605
  ...projection.error.response_id ? { response_id: projection.error.response_id } : {},
1516
1606
  ...projection.error.response_status ? { response_status: projection.error.response_status } : {},
@@ -1520,11 +1610,10 @@ var CodexStreamingTerminalError = class extends Error {
1520
1610
  };
1521
1611
  function codexSseFailureError(source, rawError, fallbackCode, publicMessage, metadata = {}) {
1522
1612
  return new CodexStreamingTerminalError(
1523
- codexSseFailureProjection(source, rawError, fallbackCode, publicMessage, metadata),
1524
- publicMessage
1613
+ codexSseFailureProjection(source, rawError, fallbackCode, publicMessage, metadata)
1525
1614
  );
1526
1615
  }
1527
- function repairCodexStream(res) {
1616
+ function validateCodexStream(res) {
1528
1617
  if (!res.body) {
1529
1618
  const error = codexSseFailureError(
1530
1619
  res,
@@ -1545,7 +1634,6 @@ function repairCodexStream(res) {
1545
1634
  headers: headers2
1546
1635
  });
1547
1636
  }
1548
- const items = [];
1549
1637
  const decoder = new TextDecoder();
1550
1638
  const encoder = new TextEncoder();
1551
1639
  let buffer = "";
@@ -1556,9 +1644,8 @@ function repairCodexStream(res) {
1556
1644
  const block = buffer.slice(0, boundary.start);
1557
1645
  const separator = buffer.slice(boundary.start, boundary.end);
1558
1646
  buffer = buffer.slice(boundary.end);
1559
- const patched = patchSseBlock(block, items, res);
1560
- successfulTerminalSeen ||= patched.successfulTerminal;
1561
- controller.enqueue(encoder.encode(`${patched.block}${separator}`));
1647
+ successfulTerminalSeen ||= inspectCodexSseBlock(block, res);
1648
+ controller.enqueue(encoder.encode(`${block}${separator}`));
1562
1649
  boundary = findSseBlockBoundary(buffer, final);
1563
1650
  }
1564
1651
  };
@@ -1571,9 +1658,8 @@ function repairCodexStream(res) {
1571
1658
  buffer += decoder.decode();
1572
1659
  emitCompleteBlocks(controller, true);
1573
1660
  if (buffer.length > 0) {
1574
- const patched = patchSseBlock(buffer, items, res);
1575
- successfulTerminalSeen ||= patched.successfulTerminal;
1576
- controller.enqueue(encoder.encode(patched.block));
1661
+ successfulTerminalSeen ||= inspectCodexSseBlock(buffer, res);
1662
+ controller.enqueue(encoder.encode(buffer));
1577
1663
  buffer = "";
1578
1664
  }
1579
1665
  if (!successfulTerminalSeen) {
@@ -1614,21 +1700,28 @@ function sseLineEndingEnd(value, index, final) {
1614
1700
  }
1615
1701
  return final ? index + 1 : null;
1616
1702
  }
1617
- function patchSseBlock(block, items, source) {
1703
+ var CODEX_TERMINAL_TYPE_HINTS = [
1704
+ '"response.completed"',
1705
+ '"response.done"',
1706
+ '"response.failed"',
1707
+ '"response.incomplete"',
1708
+ '"response.error"',
1709
+ '"error"'
1710
+ ];
1711
+ function inspectCodexSseBlock(block, source) {
1618
1712
  const lines = block.split(/\r\n|\r|\n/);
1619
1713
  const dataStr = lines.filter((l) => l.startsWith("data:")).map((l) => l.slice(5).trim()).join("\n");
1620
1714
  if (!dataStr || dataStr === "[DONE]") {
1621
- return { block, successfulTerminal: false };
1715
+ return false;
1716
+ }
1717
+ if (!CODEX_TERMINAL_TYPE_HINTS.some((terminalType) => dataStr.includes(terminalType))) {
1718
+ return false;
1622
1719
  }
1623
1720
  let ev;
1624
1721
  try {
1625
1722
  ev = JSON.parse(dataStr);
1626
1723
  } catch {
1627
- return { block, successfulTerminal: false };
1628
- }
1629
- if (ev.type === "response.output_item.done" && ev.item !== void 0) {
1630
- items.push(ev.item);
1631
- return { block, successfulTerminal: false };
1724
+ return false;
1632
1725
  }
1633
1726
  if (ev.type === "response.failed") {
1634
1727
  throw codexSseFailureError(
@@ -1675,7 +1768,7 @@ function patchSseBlock(block, items, source) {
1675
1768
  );
1676
1769
  }
1677
1770
  if ((ev.type === "response.completed" || ev.type === "response.done") && ev.response) {
1678
- if (ev.response.status === "failed" || ev.response.status === "incomplete" || ev.response.error !== null && ev.response.error !== void 0) {
1771
+ if (ev.response.status !== void 0 && ev.response.status !== "completed" || ev.response.error !== null && ev.response.error !== void 0) {
1679
1772
  throw codexSseFailureError(
1680
1773
  source,
1681
1774
  ev.response.error,
@@ -1688,19 +1781,9 @@ function patchSseBlock(block, items, source) {
1688
1781
  }
1689
1782
  );
1690
1783
  }
1691
- const out = ev.response.output;
1692
- if ((!Array.isArray(out) || out.length === 0) && items.length > 0) {
1693
- ev.response = { ...ev.response, output: items };
1694
- const nonData = lines.filter((l) => !l.startsWith("data:"));
1695
- const lineEnding = block.match(/\r\n|\r|\n/)?.[0] ?? "\n";
1696
- return {
1697
- block: [...nonData, `data: ${JSON.stringify(ev)}`].join(lineEnding),
1698
- successfulTerminal: true
1699
- };
1700
- }
1701
- return { block, successfulTerminal: true };
1784
+ return true;
1702
1785
  }
1703
- return { block, successfulTerminal: false };
1786
+ return false;
1704
1787
  }
1705
1788
 
1706
1789
  // src/mcp-sanitize.ts
@@ -1786,13 +1869,15 @@ function inlineStructuredContentInRpcMessage(message) {
1786
1869
  return;
1787
1870
  }
1788
1871
  const structured = record.structuredContent;
1789
- if (structured === void 0 || structured === null) {
1790
- return;
1872
+ if (structured !== void 0 && structured !== null) {
1873
+ const text = typeof structured === "string" ? structured : JSON.stringify(structured);
1874
+ const content = Array.isArray(record.content) ? [...record.content] : [];
1875
+ content.push({ type: "text", text });
1876
+ record.content = content;
1877
+ }
1878
+ if (typeof structured !== "object" || structured === null || Array.isArray(structured)) {
1879
+ delete record.structuredContent;
1791
1880
  }
1792
- const text = typeof structured === "string" ? structured : JSON.stringify(structured);
1793
- const content = Array.isArray(record.content) ? [...record.content] : [];
1794
- content.push({ type: "text", text });
1795
- record.content = content;
1796
1881
  }
1797
1882
  function sanitizeMcpJsonBody(text, mapper = new ToolNameMapper(), namespaceSink) {
1798
1883
  try {
@@ -1961,7 +2046,13 @@ function boundModelToolOutputItem(item, policyTokens = DEFAULT_MODEL_TOOL_OUTPUT
1961
2046
  return boundedOutput === item.output ? item : { ...item, output: boundedOutput };
1962
2047
  }
1963
2048
  function boundModelToolOutputItems(items, policyTokens = DEFAULT_MODEL_TOOL_OUTPUT_TRUNCATION_TOKENS) {
1964
- return items.map((item) => boundModelToolOutputItem(item, policyTokens));
2049
+ let bounded = null;
2050
+ for (const [index, item] of items.entries()) {
2051
+ const next = boundModelToolOutputItem(item, policyTokens);
2052
+ if (next !== item && bounded === null) bounded = items.slice(0, index);
2053
+ bounded?.push(next);
2054
+ }
2055
+ return bounded ?? items;
1965
2056
  }
1966
2057
  function boundToolOutputValue(output, budgetTokens) {
1967
2058
  const state = modelOutputBoundState(budgetTokens);
@@ -2276,6 +2367,161 @@ function isImageDataUrl(value) {
2276
2367
  return /^data:image\/[a-z0-9.+-]+;base64,/i.test(value);
2277
2368
  }
2278
2369
 
2370
+ // src/images.ts
2371
+ import { pinnedFetch, readJsonBase64Field, readResponseTextBounded } from "@opengeni/network";
2372
+ var CODEX_IMAGE_MODEL = "gpt-image-2";
2373
+ var CODEX_IMAGE_RESPONSE_MAX_BYTES = 90 * 1024 * 1024;
2374
+ var CODEX_IMAGE_ERROR_MAX_BYTES = 64 * 1024;
2375
+ var CODEX_IMAGE_MAX_BYTES = 64 * 1024 * 1024;
2376
+ var CODEX_IMAGE_REQUEST_TIMEOUT_MS = 5 * 6e4;
2377
+ var CODEX_IMAGE_MAX_REFERENCES = 5;
2378
+ var codexImageFetch = async (input, init) => await pinnedFetch(
2379
+ input,
2380
+ init,
2381
+ {
2382
+ environment: "production",
2383
+ integrationsAllowPrivateNetworkTargets: false
2384
+ },
2385
+ {
2386
+ label: "Codex image generation",
2387
+ requireHttpsOutsideLocalTest: true
2388
+ }
2389
+ );
2390
+ var CodexImageApiError = class extends Error {
2391
+ constructor(status, message) {
2392
+ super(message);
2393
+ this.status = status;
2394
+ this.name = "CodexImageApiError";
2395
+ }
2396
+ };
2397
+ var CodexImageRequestTimeoutError = class extends Error {
2398
+ constructor(timeoutMs) {
2399
+ super(`Codex image generation timed out after ${Math.ceil(timeoutMs / 1e3)} seconds`);
2400
+ this.timeoutMs = timeoutMs;
2401
+ this.name = "CodexImageRequestTimeoutError";
2402
+ }
2403
+ };
2404
+ async function generateCodexSubscriptionImage(input) {
2405
+ const fetchImpl = input.fetch ?? codexImageFetch;
2406
+ const timeoutMs = input.requestTimeoutMs ?? CODEX_IMAGE_REQUEST_TIMEOUT_MS;
2407
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0) {
2408
+ throw new RangeError("Codex image request timeout must be a positive safe integer");
2409
+ }
2410
+ const references = input.references ?? [];
2411
+ if (references.length > CODEX_IMAGE_MAX_REFERENCES) {
2412
+ throw new RangeError(
2413
+ `Codex image editing accepts at most ${CODEX_IMAGE_MAX_REFERENCES} images`
2414
+ );
2415
+ }
2416
+ for (const reference of references) {
2417
+ if (reference.bytes.byteLength === 0) throw new Error("Codex image reference is empty");
2418
+ }
2419
+ const deadline = new AbortController();
2420
+ const timer = setTimeout(
2421
+ () => deadline.abort(new CodexImageRequestTimeoutError(timeoutMs)),
2422
+ timeoutMs
2423
+ );
2424
+ const signal = input.abortSignal ? AbortSignal.any([input.abortSignal, deadline.signal]) : deadline.signal;
2425
+ const request = async (auth) => {
2426
+ const headers = codexImageHeaders(auth, input.context.clientVersion, input.turnId);
2427
+ return await fetchImpl(
2428
+ `${CODEX_RESPONSES_BASE}/${references.length > 0 ? "images/edits" : "images/generations"}`,
2429
+ {
2430
+ method: "POST",
2431
+ redirect: "error",
2432
+ headers,
2433
+ body: JSON.stringify(
2434
+ references.length > 0 ? {
2435
+ images: references.map((reference) => ({
2436
+ image_url: `data:${reference.mediaType};base64,${Buffer.from(reference.bytes).toString("base64")}`
2437
+ })),
2438
+ prompt: input.prompt,
2439
+ background: "auto",
2440
+ model: CODEX_IMAGE_MODEL,
2441
+ quality: "auto",
2442
+ size: "auto"
2443
+ } : {
2444
+ prompt: input.prompt,
2445
+ background: "auto",
2446
+ model: CODEX_IMAGE_MODEL,
2447
+ quality: "auto",
2448
+ size: "auto"
2449
+ }
2450
+ ),
2451
+ signal
2452
+ }
2453
+ );
2454
+ };
2455
+ const operation = (async () => {
2456
+ let response = await request(await input.context.getToken());
2457
+ if (response.status === 401) {
2458
+ await response.body?.cancel().catch(() => void 0);
2459
+ response = await request(await input.context.refresh());
2460
+ }
2461
+ if (!response.ok) {
2462
+ const detail = await readResponseTextBounded(
2463
+ response,
2464
+ CODEX_IMAGE_ERROR_MAX_BYTES,
2465
+ "Codex image error",
2466
+ { signal }
2467
+ ).catch(() => "");
2468
+ throw new CodexImageApiError(
2469
+ response.status,
2470
+ detail ? `Codex image generation failed (${response.status}): ${boundedErrorMessage(detail)}` : `Codex image generation failed (${response.status})`
2471
+ );
2472
+ }
2473
+ const bytes = await readJsonBase64Field(response, {
2474
+ fieldName: "b64_json",
2475
+ shape: "string",
2476
+ maxResponseBytes: CODEX_IMAGE_RESPONSE_MAX_BYTES,
2477
+ maxDecodedBytes: CODEX_IMAGE_MAX_BYTES,
2478
+ label: "Codex image generation",
2479
+ signal
2480
+ });
2481
+ return { bytes, declaredMediaType: "image/png" };
2482
+ })();
2483
+ let removeAbortListener = () => void 0;
2484
+ const aborted = new Promise((_resolve, reject) => {
2485
+ const onAbort = () => reject(signal.reason);
2486
+ if (signal.aborted) {
2487
+ onAbort();
2488
+ return;
2489
+ }
2490
+ signal.addEventListener("abort", onAbort, { once: true });
2491
+ removeAbortListener = () => signal.removeEventListener("abort", onAbort);
2492
+ });
2493
+ try {
2494
+ return await Promise.race([operation, aborted]);
2495
+ } finally {
2496
+ removeAbortListener();
2497
+ clearTimeout(timer);
2498
+ }
2499
+ }
2500
+ function codexImageHeaders(auth, clientVersion, turnId) {
2501
+ const headers = new Headers({
2502
+ Authorization: `Bearer ${auth.accessToken}`,
2503
+ accept: "application/json",
2504
+ "content-type": "application/json",
2505
+ originator: CODEX_ORIGINATOR,
2506
+ "User-Agent": `${CODEX_ORIGINATOR}/${clientVersion || CODEX_CLIENT_VERSION}`,
2507
+ version: clientVersion || CODEX_CLIENT_VERSION,
2508
+ "x-codex-image-turn-id": turnId
2509
+ });
2510
+ if (auth.chatgptAccountId) headers.set("ChatGPT-Account-ID", auth.chatgptAccountId);
2511
+ if (auth.isFedramp) headers.set("X-OpenAI-Fedramp", "true");
2512
+ return headers;
2513
+ }
2514
+ function boundedErrorMessage(body) {
2515
+ let message = body;
2516
+ try {
2517
+ const value = JSON.parse(body);
2518
+ const candidate = value.error?.message ?? value.message;
2519
+ if (typeof candidate === "string") message = candidate;
2520
+ } catch {
2521
+ }
2522
+ return message.replace(/\s+/g, " ").trim().slice(0, 1e3);
2523
+ }
2524
+
2279
2525
  // src/realtime.ts
2280
2526
  var CODEX_REALTIME_CALL_URL = `${CODEX_RESPONSES_BASE}/realtime/calls?intent=quicksilver&architecture=avas`;
2281
2527
  var MAX_REALTIME_SDP_BYTES = 1024 * 1024;
@@ -2577,6 +2823,10 @@ export {
2577
2823
  CODEX_REALTIME_VOICES,
2578
2824
  CODEX_REFRESH_FALLBACK_MS,
2579
2825
  CODEX_REFRESH_WINDOW_MS,
2826
+ CODEX_REQUEST_BODY_NORMALIZED_HEADER,
2827
+ CODEX_REQUEST_CALLER_STREAM_HEADER,
2828
+ CODEX_REQUEST_ID_HEADER,
2829
+ CODEX_REQUEST_MODEL_HEADER,
2580
2830
  CODEX_RESPONSES_BASE,
2581
2831
  CODEX_RESPONSE_HEADERS_TIMEOUT_MS,
2582
2832
  CODEX_RESPONSE_NO_BYTE_RETRIES,
@@ -2592,6 +2842,8 @@ export {
2592
2842
  CODEX_WEEKLY_WINDOW_SECONDS,
2593
2843
  CODEX_WHAM_BASE,
2594
2844
  CodexDeviceError,
2845
+ CodexImageApiError,
2846
+ CodexImageRequestTimeoutError,
2595
2847
  CodexRealtimeError,
2596
2848
  CodexRefreshTransient,
2597
2849
  CodexReloginRequired,
@@ -2625,12 +2877,14 @@ export {
2625
2877
  fetchCodexModels,
2626
2878
  fetchCodexRateLimitResetCredits,
2627
2879
  fetchCodexUsage,
2880
+ generateCodexSubscriptionImage,
2628
2881
  isCodexBilledModel,
2629
2882
  isCodexTransportError,
2630
2883
  isPreHeadersTimeoutError,
2631
2884
  modelToolOutputSerializationBudgetTokens,
2632
2885
  normalizeCodexRequestBody,
2633
2886
  normalizeCodexUsage,
2887
+ normalizedCodexRequestBody,
2634
2888
  opaqueProviderArtifactFingerprint,
2635
2889
  opaqueProviderArtifactFingerprints,
2636
2890
  parseCodexRateLimitResetConsumeResponse,