@opengeni/codex 0.2.9 → 0.2.10

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
@@ -21,6 +21,10 @@ import {
21
21
  CODEX_ORIGINATOR,
22
22
  CODEX_PROVIDER_BASE_URL,
23
23
  CODEX_PROVIDER_ID,
24
+ CODEX_REALTIME_CALL_TIMEOUT_MS,
25
+ CODEX_REALTIME_DEFAULT_VOICE,
26
+ CODEX_REALTIME_MODEL,
27
+ CODEX_REALTIME_VERSION,
24
28
  CODEX_REFRESH_FALLBACK_MS,
25
29
  CODEX_REFRESH_WINDOW_MS,
26
30
  CODEX_RESPONSES_BASE,
@@ -32,7 +36,19 @@ import {
32
36
  CODEX_RESPONSE_WHOLE_TIMEOUT_MS,
33
37
  CODEX_TOKEN_URL,
34
38
  CODEX_WHAM_BASE
35
- } from "./chunk-YGKQDUY7.js";
39
+ } from "./chunk-HT463QWA.js";
40
+ import {
41
+ CODEX_REALTIME_CONTEXT_APPEND_MAX_BYTES,
42
+ CODEX_REALTIME_INITIAL_ITEMS_MAX_COUNT,
43
+ CODEX_REALTIME_INITIAL_ITEMS_MAX_TOKENS,
44
+ CODEX_REALTIME_V3_MAX_EVENT_BYTES,
45
+ CODEX_REALTIME_V3_MAX_IDENTIFIER_BYTES,
46
+ CODEX_REALTIME_V3_MAX_TEXT_BYTES,
47
+ contextAppendChunks,
48
+ encodeCodexRealtimeV3DelegationContextAppend,
49
+ encodeCodexRealtimeV3SessionContextAppend,
50
+ parseCodexRealtimeV3Event
51
+ } from "./chunk-MFVGCR3N.js";
36
52
 
37
53
  // src/billing.ts
38
54
  function isCodexBilledModel(model) {
@@ -567,7 +583,7 @@ function normalizeCodexUsage(httpStatus, rawPayload) {
567
583
  var CODEX_READ_TIMEOUT_MS = 5e3;
568
584
  var RESET_CREDIT_DETAILS_TIMEOUT_MS = 5e3;
569
585
  var RESET_CREDIT_CONSUME_TIMEOUT_MS = 1e4;
570
- function subscriptionHeaders(a) {
586
+ function codexSubscriptionHeaders(a) {
571
587
  return {
572
588
  Authorization: `Bearer ${a.accessToken}`,
573
589
  ...a.chatgptAccountId ? { "ChatGPT-Account-ID": a.chatgptAccountId } : {},
@@ -581,7 +597,7 @@ async function fetchCodexModels(a, fetchImpl = fetch, timeoutMs = CODEX_READ_TIM
581
597
  const fetched = await runBoundedCodexOperation(async (signal) => {
582
598
  const res = await fetchImpl(
583
599
  `${CODEX_RESPONSES_BASE}/models?client_version=${encodeURIComponent(a.clientVersion)}`,
584
- { method: "GET", headers: subscriptionHeaders(a), signal }
600
+ { method: "GET", headers: codexSubscriptionHeaders(a), signal }
585
601
  );
586
602
  if (!res.ok) {
587
603
  await res.arrayBuffer().catch(() => void 0);
@@ -597,7 +613,7 @@ async function fetchCodexUsage(a, fetchImpl = fetch, timeoutMs = CODEX_READ_TIME
597
613
  const fetched = await runBoundedCodexOperation(async (signal) => {
598
614
  const res = await fetchImpl(`${CODEX_WHAM_BASE}/wham/usage`, {
599
615
  method: "GET",
600
- headers: subscriptionHeaders(a),
616
+ headers: codexSubscriptionHeaders(a),
601
617
  signal
602
618
  });
603
619
  const payload = res.ok || res.status === 404 ? await res.json().catch(() => null) : null;
@@ -611,15 +627,23 @@ async function fetchCodexRateLimitResetCredits(a, fetchImpl = fetch, timeoutMs =
611
627
  const fetched = await runBoundedCodexOperation(async (signal) => {
612
628
  const res = await fetchImpl(`${CODEX_WHAM_BASE}/wham/rate-limit-reset-credits`, {
613
629
  method: "GET",
614
- headers: subscriptionHeaders(a),
630
+ headers: codexSubscriptionHeaders(a),
615
631
  signal
616
632
  });
617
633
  if (!res.ok) {
618
634
  await res.arrayBuffer().catch(() => void 0);
619
- return { ok: false, status: res.status, reason: "http_error" };
635
+ return {
636
+ ok: false,
637
+ status: res.status,
638
+ reason: "http_error"
639
+ };
620
640
  }
621
641
  const details = parseCodexRateLimitResetCreditsDetails(await res.json().catch(() => null));
622
- return details ? { ok: true, status: res.status, details } : { ok: false, status: res.status, reason: "invalid_response" };
642
+ return details ? { ok: true, status: res.status, details } : {
643
+ ok: false,
644
+ status: res.status,
645
+ reason: "invalid_response"
646
+ };
623
647
  }, timeoutMs);
624
648
  return fetched.ok ? fetched.value : { ok: false, status: 0, reason: fetched.reason };
625
649
  }
@@ -631,7 +655,7 @@ async function consumeCodexRateLimitResetCredit(a, input, fetchImpl = fetch, tim
631
655
  const res = await fetchImpl(`${CODEX_WHAM_BASE}/wham/rate-limit-reset-credits/consume`, {
632
656
  method: "POST",
633
657
  headers: {
634
- ...subscriptionHeaders(a),
658
+ ...codexSubscriptionHeaders(a),
635
659
  "content-type": "application/json"
636
660
  },
637
661
  body: JSON.stringify({
@@ -642,10 +666,18 @@ async function consumeCodexRateLimitResetCredit(a, input, fetchImpl = fetch, tim
642
666
  });
643
667
  if (!res.ok) {
644
668
  await res.arrayBuffer().catch(() => void 0);
645
- return { ok: false, status: res.status, reason: "http_error" };
669
+ return {
670
+ ok: false,
671
+ status: res.status,
672
+ reason: "http_error"
673
+ };
646
674
  }
647
675
  const result = parseCodexRateLimitResetConsumeResponse(await res.json().catch(() => null));
648
- return result ? { ok: true, status: res.status, result } : { ok: false, status: res.status, reason: "invalid_response" };
676
+ return result ? { ok: true, status: res.status, result } : {
677
+ ok: false,
678
+ status: res.status,
679
+ reason: "invalid_response"
680
+ };
649
681
  }, timeoutMs);
650
682
  return fetched.ok ? fetched.value : { ok: false, status: 0, reason: fetched.reason };
651
683
  }
@@ -2219,6 +2251,269 @@ function isResponsesProtocolContentPrefix(output) {
2219
2251
  function isImageDataUrl(value) {
2220
2252
  return /^data:image\/[a-z0-9.+-]+;base64,/i.test(value);
2221
2253
  }
2254
+
2255
+ // src/realtime.ts
2256
+ var CODEX_REALTIME_CALL_URL = `${CODEX_RESPONSES_BASE}/realtime/calls?intent=quicksilver&architecture=avas`;
2257
+ var MAX_REALTIME_SDP_BYTES = 1024 * 1024;
2258
+ var REALTIME_CALL_ID = /^(?:rtc_.+|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i;
2259
+ var CODEX_REALTIME_VOICES = [
2260
+ "juniper",
2261
+ "maple",
2262
+ "spruce",
2263
+ "ember",
2264
+ "vale",
2265
+ "breeze",
2266
+ "arbor",
2267
+ "sol",
2268
+ "cove"
2269
+ ];
2270
+ var CodexRealtimeError = class extends Error {
2271
+ constructor(code, message, providerStatus = null) {
2272
+ super(message);
2273
+ this.code = code;
2274
+ this.providerStatus = providerStatus;
2275
+ this.name = "CodexRealtimeError";
2276
+ }
2277
+ };
2278
+ async function createCodexRealtimeCall(auth, input, fetchImpl = fetch, options = {}) {
2279
+ validateRealtimeInput(input);
2280
+ const timeoutMs = options.timeoutMs ?? CODEX_REALTIME_CALL_TIMEOUT_MS;
2281
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
2282
+ throw new CodexRealtimeError("invalid_request", "Codex realtime timeout must be positive");
2283
+ }
2284
+ if (options.signal?.aborted) {
2285
+ throw new CodexRealtimeError("cancelled", "Codex realtime request cancelled");
2286
+ }
2287
+ const controller = new AbortController();
2288
+ let timedOut = false;
2289
+ let timeout;
2290
+ let rejectCancellation;
2291
+ const cancellation = new Promise((_resolve, reject) => {
2292
+ rejectCancellation = reject;
2293
+ });
2294
+ const onAbort = () => {
2295
+ controller.abort(options.signal?.reason);
2296
+ rejectCancellation?.(new CodexRealtimeError("cancelled", "Codex realtime request cancelled"));
2297
+ };
2298
+ options.signal?.addEventListener("abort", onAbort, { once: true });
2299
+ const deadline = new Promise((_resolve, reject) => {
2300
+ timeout = setTimeout(() => {
2301
+ timedOut = true;
2302
+ controller.abort();
2303
+ reject(new CodexRealtimeError("timeout", "Codex realtime request timed out"));
2304
+ }, timeoutMs);
2305
+ });
2306
+ const request = (async () => {
2307
+ const response = await fetchImpl(CODEX_REALTIME_CALL_URL, {
2308
+ method: "POST",
2309
+ headers: {
2310
+ ...codexSubscriptionHeaders(auth),
2311
+ "content-type": "application/json",
2312
+ "openai-alpha": "quicksilver=v2",
2313
+ "session-id": input.sessionId,
2314
+ "thread-id": input.sessionId
2315
+ },
2316
+ body: JSON.stringify({
2317
+ sdp: input.sdp,
2318
+ session: {
2319
+ instructions: input.instructions ?? "",
2320
+ audio: {
2321
+ output: { voice: input.voice ?? CODEX_REALTIME_DEFAULT_VOICE }
2322
+ },
2323
+ delegation: { type: "client" },
2324
+ model: CODEX_REALTIME_MODEL,
2325
+ ...input.initialItems?.length ? {
2326
+ initial_items: input.initialItems.map((item) => ({
2327
+ type: "message",
2328
+ role: item.role,
2329
+ content: [
2330
+ {
2331
+ type: item.role === "assistant" ? "output_text" : "input_text",
2332
+ text: item.text
2333
+ }
2334
+ ]
2335
+ }))
2336
+ } : {}
2337
+ }
2338
+ }),
2339
+ signal: controller.signal
2340
+ });
2341
+ if (!response.ok) {
2342
+ await response.body?.cancel().catch(() => void 0);
2343
+ throw providerHttpError(response.status);
2344
+ }
2345
+ const location = response.headers.get("location");
2346
+ if (!location || !validRealtimeLocation(location)) {
2347
+ await response.body?.cancel().catch(() => void 0);
2348
+ throw new CodexRealtimeError(
2349
+ "invalid_response",
2350
+ "Codex realtime response did not identify a compatible call",
2351
+ response.status
2352
+ );
2353
+ }
2354
+ const sdp = await readBoundedSdp(response);
2355
+ if (!isAudioSdp(sdp)) {
2356
+ throw new CodexRealtimeError(
2357
+ "invalid_response",
2358
+ "Codex realtime response was not an audio SDP answer",
2359
+ response.status
2360
+ );
2361
+ }
2362
+ return {
2363
+ sdp,
2364
+ version: CODEX_REALTIME_VERSION,
2365
+ model: CODEX_REALTIME_MODEL
2366
+ };
2367
+ })();
2368
+ try {
2369
+ return await Promise.race([request, cancellation, deadline]);
2370
+ } catch (error) {
2371
+ if (error instanceof CodexRealtimeError) throw error;
2372
+ if (options.signal?.aborted) {
2373
+ throw new CodexRealtimeError("cancelled", "Codex realtime request cancelled");
2374
+ }
2375
+ if (timedOut || controller.signal.aborted) {
2376
+ throw new CodexRealtimeError("timeout", "Codex realtime request timed out");
2377
+ }
2378
+ throw new CodexRealtimeError("network", "Codex realtime provider request failed");
2379
+ } finally {
2380
+ if (timeout) clearTimeout(timeout);
2381
+ options.signal?.removeEventListener("abort", onAbort);
2382
+ }
2383
+ }
2384
+ function selectCodexCredentialId(args) {
2385
+ if (args.sessionPinnedCredentialId && args.connectedIds.has(args.sessionPinnedCredentialId)) {
2386
+ return args.sessionPinnedCredentialId;
2387
+ }
2388
+ if (args.activeCredentialId && args.connectedIds.has(args.activeCredentialId)) {
2389
+ return args.activeCredentialId;
2390
+ }
2391
+ return null;
2392
+ }
2393
+ function validateRealtimeInput(input) {
2394
+ if (input.version !== CODEX_REALTIME_VERSION) {
2395
+ throw new CodexRealtimeError(
2396
+ "incompatible",
2397
+ `Codex realtime requires ${CODEX_REALTIME_VERSION}`
2398
+ );
2399
+ }
2400
+ if (!input.sessionId || input.sessionId.length > 128) {
2401
+ throw new CodexRealtimeError("invalid_request", "Codex realtime session id is invalid");
2402
+ }
2403
+ if (new TextEncoder().encode(input.sdp).byteLength > MAX_REALTIME_SDP_BYTES) {
2404
+ throw new CodexRealtimeError("invalid_request", "Codex realtime SDP offer is too large");
2405
+ }
2406
+ if (!isAudioSdp(input.sdp)) {
2407
+ throw new CodexRealtimeError("invalid_request", "Codex realtime requires an audio SDP offer");
2408
+ }
2409
+ if (input.voice !== void 0 && !CODEX_REALTIME_VOICES.includes(input.voice)) {
2410
+ throw new CodexRealtimeError("invalid_request", "Codex realtime voice is unsupported");
2411
+ }
2412
+ const initialItems = input.initialItems ?? [];
2413
+ if (initialItems.length > CODEX_REALTIME_INITIAL_ITEMS_MAX_COUNT) {
2414
+ throw new CodexRealtimeError(
2415
+ "invalid_request",
2416
+ `Codex realtime history exceeds ${CODEX_REALTIME_INITIAL_ITEMS_MAX_COUNT} items`
2417
+ );
2418
+ }
2419
+ let estimatedTokens = 0;
2420
+ for (const item of initialItems) {
2421
+ if (item.role !== "user" && item.role !== "developer" && item.role !== "assistant" || typeof item.text !== "string") {
2422
+ throw new CodexRealtimeError("invalid_request", "Codex realtime history item is invalid");
2423
+ }
2424
+ const itemTokens = Math.ceil(new TextEncoder().encode(item.text).byteLength / 4);
2425
+ if (itemTokens > CODEX_REALTIME_INITIAL_ITEMS_MAX_TOKENS) {
2426
+ throw new CodexRealtimeError("invalid_request", "Codex realtime history item is too large");
2427
+ }
2428
+ estimatedTokens += itemTokens;
2429
+ }
2430
+ if (estimatedTokens > CODEX_REALTIME_INITIAL_ITEMS_MAX_TOKENS) {
2431
+ throw new CodexRealtimeError("invalid_request", "Codex realtime history is too large");
2432
+ }
2433
+ }
2434
+ function providerHttpError(status) {
2435
+ if (status === 401) {
2436
+ return new CodexRealtimeError(
2437
+ "authentication",
2438
+ "Codex subscription rejected authentication",
2439
+ status
2440
+ );
2441
+ }
2442
+ if (status === 403) {
2443
+ return new CodexRealtimeError(
2444
+ "entitlement",
2445
+ "Codex subscription lacks realtime entitlement",
2446
+ status
2447
+ );
2448
+ }
2449
+ if (status === 404) {
2450
+ return new CodexRealtimeError(
2451
+ "incompatible",
2452
+ "Codex subscription realtime is unavailable",
2453
+ status
2454
+ );
2455
+ }
2456
+ if (status === 429) {
2457
+ return new CodexRealtimeError("rate_limited", "Codex realtime is rate limited", status);
2458
+ }
2459
+ return new CodexRealtimeError("provider", "Codex realtime provider request failed", status);
2460
+ }
2461
+ function validRealtimeLocation(location) {
2462
+ const path = location.split("?", 1)[0] ?? "";
2463
+ const segment = path.split("/").filter(Boolean).at(-1) ?? "";
2464
+ return REALTIME_CALL_ID.test(segment);
2465
+ }
2466
+ function isAudioSdp(sdp) {
2467
+ return /^v=0(?:\r?\n)/.test(sdp) && /(?:^|\r?\n)m=audio\s/m.test(sdp);
2468
+ }
2469
+ async function readBoundedSdp(response) {
2470
+ const declared = Number(response.headers.get("content-length"));
2471
+ if (Number.isFinite(declared) && declared > MAX_REALTIME_SDP_BYTES) {
2472
+ await response.body?.cancel().catch(() => void 0);
2473
+ throw new CodexRealtimeError(
2474
+ "invalid_response",
2475
+ "Codex realtime SDP answer is too large",
2476
+ response.status
2477
+ );
2478
+ }
2479
+ if (!response.body) return "";
2480
+ const reader = response.body.getReader();
2481
+ const chunks = [];
2482
+ let total = 0;
2483
+ try {
2484
+ while (true) {
2485
+ const next = await reader.read();
2486
+ if (next.done) break;
2487
+ total += next.value.byteLength;
2488
+ if (total > MAX_REALTIME_SDP_BYTES) {
2489
+ await reader.cancel();
2490
+ throw new CodexRealtimeError(
2491
+ "invalid_response",
2492
+ "Codex realtime SDP answer is too large",
2493
+ response.status
2494
+ );
2495
+ }
2496
+ chunks.push(next.value);
2497
+ }
2498
+ } finally {
2499
+ reader.releaseLock();
2500
+ }
2501
+ const bytes = new Uint8Array(total);
2502
+ let offset = 0;
2503
+ for (const chunk of chunks) {
2504
+ bytes.set(chunk, offset);
2505
+ offset += chunk.byteLength;
2506
+ }
2507
+ try {
2508
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
2509
+ } catch {
2510
+ throw new CodexRealtimeError(
2511
+ "invalid_response",
2512
+ "Codex realtime SDP answer was not valid UTF-8",
2513
+ response.status
2514
+ );
2515
+ }
2516
+ }
2222
2517
  export {
2223
2518
  CODEX_APPS_MCP_SERVER_ID,
2224
2519
  CODEX_APPS_MCP_SERVER_NAME,
@@ -2245,6 +2540,17 @@ export {
2245
2540
  CODEX_PROVIDER_BASE_URL,
2246
2541
  CODEX_PROVIDER_ID,
2247
2542
  CODEX_RATE_LIMIT_RESET_OUTCOMES,
2543
+ CODEX_REALTIME_CALL_TIMEOUT_MS,
2544
+ CODEX_REALTIME_CONTEXT_APPEND_MAX_BYTES,
2545
+ CODEX_REALTIME_DEFAULT_VOICE,
2546
+ CODEX_REALTIME_INITIAL_ITEMS_MAX_COUNT,
2547
+ CODEX_REALTIME_INITIAL_ITEMS_MAX_TOKENS,
2548
+ CODEX_REALTIME_MODEL,
2549
+ CODEX_REALTIME_V3_MAX_EVENT_BYTES,
2550
+ CODEX_REALTIME_V3_MAX_IDENTIFIER_BYTES,
2551
+ CODEX_REALTIME_V3_MAX_TEXT_BYTES,
2552
+ CODEX_REALTIME_VERSION,
2553
+ CODEX_REALTIME_VOICES,
2248
2554
  CODEX_REFRESH_FALLBACK_MS,
2249
2555
  CODEX_REFRESH_WINDOW_MS,
2250
2556
  CODEX_RESPONSES_BASE,
@@ -2262,6 +2568,7 @@ export {
2262
2568
  CODEX_WEEKLY_WINDOW_SECONDS,
2263
2569
  CODEX_WHAM_BASE,
2264
2570
  CodexDeviceError,
2571
+ CodexRealtimeError,
2265
2572
  CodexRefreshTransient,
2266
2573
  CodexReloginRequired,
2267
2574
  CodexResponseTimeoutError,
@@ -2283,8 +2590,13 @@ export {
2283
2590
  codexAppsSanitizingFetch,
2284
2591
  codexRequestStorage,
2285
2592
  codexSubscriptionFetch,
2593
+ codexSubscriptionHeaders,
2286
2594
  consumeCodexRateLimitResetCredit,
2595
+ contextAppendChunks,
2596
+ createCodexRealtimeCall,
2287
2597
  decodeJwtPayload,
2598
+ encodeCodexRealtimeV3DelegationContextAppend,
2599
+ encodeCodexRealtimeV3SessionContextAppend,
2288
2600
  exchangeDeviceCode,
2289
2601
  fetchCodexModels,
2290
2602
  fetchCodexRateLimitResetCredits,
@@ -2298,6 +2610,7 @@ export {
2298
2610
  parseCodexRateLimitResetConsumeResponse,
2299
2611
  parseCodexRateLimitResetCreditsDetails,
2300
2612
  parseCodexRateLimitResetCreditsSummary,
2613
+ parseCodexRealtimeV3Event,
2301
2614
  parseCodexUsageHeaders,
2302
2615
  parseIdToken,
2303
2616
  pollDeviceCode,
@@ -2306,6 +2619,7 @@ export {
2306
2619
  resolveCodexResponseTimeoutPolicy,
2307
2620
  sanitizeMcpJsonBody,
2308
2621
  sanitizeMcpSseBody,
2622
+ selectCodexCredentialId,
2309
2623
  startDeviceCode,
2310
2624
  truncateMiddleWithTokenBudget,
2311
2625
  withCodexRequestOverrides