@opengeni/codex 0.2.17 → 0.2.19-canary.0

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.
@@ -20,6 +20,27 @@
20
20
  */
21
21
  export { MODEL_TOOL_OUTPUT_OVERSIZED_IMAGE_CARD_DATA_URL } from "./oversized-image-card.js";
22
22
  export type ModelHistoryItem = Record<string, unknown>;
23
+ type WithoutOutputOnlyProviderDataFields<T> = T extends ModelHistoryItem ? Omit<T, "status"> : T;
24
+ type WithoutOutputOnlyProviderDataField<T extends ModelHistoryItem> = "providerData" extends keyof T ? string extends keyof T ? {
25
+ providerData?: WithoutOutputOnlyProviderDataFields<T["providerData"]>;
26
+ } : object extends Pick<T, Extract<keyof T, "providerData">> ? {
27
+ providerData?: WithoutOutputOnlyProviderDataFields<T["providerData"]>;
28
+ } : {
29
+ providerData: WithoutOutputOnlyProviderDataFields<T["providerData"]>;
30
+ } : object;
31
+ type WithoutOutputOnlyHistoryItemFields<T extends ModelHistoryItem> = T extends unknown ? Omit<T, "status" | "providerData"> & WithoutOutputOnlyProviderDataField<T> : never;
32
+ /**
33
+ * Responses output items carry `status` (`in_progress` / `completed` /
34
+ * `incomplete`). That field is not conversation meaning — pairing is `call_id`
35
+ * — and Codex's input schema 400s it (`Unknown parameter: 'input[N].status'`).
36
+ * SuperGrok accepts items with or without it. The SDK also nests `status` on
37
+ * `providerData` (reasoning items) and flattens it back onto the request.
38
+ * Canonical history therefore omits both at persist so portable sessions can
39
+ * cross Responses providers.
40
+ */
41
+ export declare function omitOutputOnlyHistoryItemFields<T extends ModelHistoryItem>(item: T): WithoutOutputOnlyHistoryItemFields<T>;
42
+ /** Persist/replay boundary: drop output-only fields, then bound tool output. */
43
+ export declare function canonicalizePersistedHistoryItem<T extends ModelHistoryItem>(item: T, policyTokens?: number): WithoutOutputOnlyHistoryItemFields<T>;
23
44
  export declare const CODEX_MODEL_TOOL_OUTPUT_TRUNCATION_TOKENS = 10000;
24
45
  export declare const CODEX_TOOL_OUTPUT_SERIALIZATION_ALLOWANCE = 1.2;
25
46
  export declare const DEFAULT_MODEL_TOOL_OUTPUT_TRUNCATION_TOKENS = 10000;
@@ -31,7 +31,22 @@ export declare class CodexRealtimeError extends Error {
31
31
  export type CodexRealtimeCallOptions = {
32
32
  signal?: AbortSignal | undefined;
33
33
  timeoutMs?: number | undefined;
34
+ providerConfig?: CodexRealtimeProviderConfig | undefined;
34
35
  };
36
+ export type CodexRealtimeProviderConfig = {
37
+ architecture: string;
38
+ model: string;
39
+ version: typeof CODEX_REALTIME_VERSION;
40
+ };
41
+ /**
42
+ * Resolve the provider-controlled Codex voice model without changing the
43
+ * stable OpenGeni model id. ChatGPT rotates this config independently of Codex
44
+ * releases; pinning the old value caused deterministic call-creation failures.
45
+ */
46
+ export declare function fetchCodexRealtimeProviderConfig(auth: CodexAuthHeaders, fetchImpl?: CodexFetch, options?: {
47
+ signal?: AbortSignal | undefined;
48
+ timeoutMs?: number | undefined;
49
+ }): Promise<CodexRealtimeProviderConfig>;
35
50
  /**
36
51
  * Create one native subscription-authenticated Codex GPT-Live V3 WebRTC call.
37
52
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/codex",
3
- "version": "0.2.17",
3
+ "version": "0.2.19-canary.0",
4
4
  "description": "ChatGPT/Codex subscription auth + transport: device-code login, token refresh, and the Responses-backend fetch. Pure HTTP + transforms; no database dependency.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -41,7 +41,7 @@
41
41
  "prepublishOnly": "bash ../../scripts/prepublish-guard"
42
42
  },
43
43
  "dependencies": {
44
- "@opengeni/network": "^0.2.2",
44
+ "@opengeni/network": "^0.2.3-canary.0",
45
45
  "zod": "^4.2.1"
46
46
  },
47
47
  "engines": {
package/src/constants.ts CHANGED
@@ -54,12 +54,19 @@ export const CODEX_MODEL_AUTO_COMPACT_TOKEN_LIMIT = Math.floor(
54
54
  // this pinned to the latest stable Codex release we have verified end-to-end.
55
55
  export const CODEX_CLIENT_VERSION = "0.145.0";
56
56
 
57
- // Native ChatGPT/Codex subscription WebRTC call creation. Verified against
58
- // openai/codex 0.145.0 commit bd2de422aa287b97b06ca6425a10935bcf1b3731.
57
+ // Public OpenGeni selector for native ChatGPT/Codex subscription WebRTC. This
58
+ // remains stable for persisted sessions; the provider's remotely configured
59
+ // model is resolved separately before call creation.
59
60
  export const CODEX_REALTIME_MODEL = "gpt-live-1-boulder-alpha";
60
61
  export const CODEX_REALTIME_VERSION = "v3";
61
62
  export const CODEX_REALTIME_DEFAULT_VOICE = "cove";
62
63
  export const CODEX_REALTIME_CALL_TIMEOUT_MS = 15_000;
64
+ export const CODEX_REALTIME_CONFIG_TIMEOUT_MS = 5_000;
65
+ export const CODEX_REALTIME_CONFIG_ID = "3566525122";
66
+ // Verified live on 2026-08-23. Used only when the authenticated remote config
67
+ // is transiently unavailable or malformed; it is not the persisted model id.
68
+ export const CODEX_REALTIME_PROVIDER_MODEL_FALLBACK = "gpt-live-1-codex";
69
+ export const CODEX_REALTIME_PROVIDER_ARCHITECTURE_FALLBACK = "avas";
63
70
 
64
71
  export const CODEX_REFRESH_WINDOW_MS = 5 * 60 * 1000; // proactive refresh when within 5 min of exp (spec §1.1)
65
72
  export const CODEX_REFRESH_FALLBACK_MS = 8 * 24 * 60 * 60 * 1000; // 8 days when exp is unparseable
@@ -25,6 +25,67 @@ export { MODEL_TOOL_OUTPUT_OVERSIZED_IMAGE_CARD_DATA_URL } from "./oversized-ima
25
25
 
26
26
  export type ModelHistoryItem = Record<string, unknown>;
27
27
 
28
+ type WithoutOutputOnlyProviderDataFields<T> = T extends ModelHistoryItem ? Omit<T, "status"> : T;
29
+
30
+ type WithoutOutputOnlyProviderDataField<T extends ModelHistoryItem> = "providerData" extends keyof T
31
+ ? string extends keyof T
32
+ ? {
33
+ providerData?: WithoutOutputOnlyProviderDataFields<T["providerData"]>;
34
+ }
35
+ : object extends Pick<T, Extract<keyof T, "providerData">>
36
+ ? {
37
+ providerData?: WithoutOutputOnlyProviderDataFields<T["providerData"]>;
38
+ }
39
+ : {
40
+ providerData: WithoutOutputOnlyProviderDataFields<T["providerData"]>;
41
+ }
42
+ : object;
43
+
44
+ type WithoutOutputOnlyHistoryItemFields<T extends ModelHistoryItem> = T extends unknown
45
+ ? Omit<T, "status" | "providerData"> & WithoutOutputOnlyProviderDataField<T>
46
+ : never;
47
+
48
+ /**
49
+ * Responses output items carry `status` (`in_progress` / `completed` /
50
+ * `incomplete`). That field is not conversation meaning — pairing is `call_id`
51
+ * — and Codex's input schema 400s it (`Unknown parameter: 'input[N].status'`).
52
+ * SuperGrok accepts items with or without it. The SDK also nests `status` on
53
+ * `providerData` (reasoning items) and flattens it back onto the request.
54
+ * Canonical history therefore omits both at persist so portable sessions can
55
+ * cross Responses providers.
56
+ */
57
+ export function omitOutputOnlyHistoryItemFields<T extends ModelHistoryItem>(
58
+ item: T,
59
+ ): WithoutOutputOnlyHistoryItemFields<T> {
60
+ if (!item || typeof item !== "object") {
61
+ return item as unknown as WithoutOutputOnlyHistoryItemFields<T>;
62
+ }
63
+ const providerData =
64
+ item.providerData && typeof item.providerData === "object"
65
+ ? (item.providerData as Record<string, unknown>)
66
+ : null;
67
+ const hasTopStatus = "status" in item;
68
+ const hasNestedStatus = Boolean(providerData && "status" in providerData);
69
+ if (!hasTopStatus && !hasNestedStatus) {
70
+ return item as unknown as WithoutOutputOnlyHistoryItemFields<T>;
71
+ }
72
+ const next = { ...item };
73
+ if (hasTopStatus) delete (next as Record<string, unknown>).status;
74
+ if (hasNestedStatus && providerData) {
75
+ const { status: _dropped, ...rest } = providerData;
76
+ (next as Record<string, unknown>).providerData = rest;
77
+ }
78
+ return next as unknown as WithoutOutputOnlyHistoryItemFields<T>;
79
+ }
80
+
81
+ /** Persist/replay boundary: drop output-only fields, then bound tool output. */
82
+ export function canonicalizePersistedHistoryItem<T extends ModelHistoryItem>(
83
+ item: T,
84
+ policyTokens = DEFAULT_MODEL_TOOL_OUTPUT_TRUNCATION_TOKENS,
85
+ ): WithoutOutputOnlyHistoryItemFields<T> {
86
+ return boundModelToolOutputItem(omitOutputOnlyHistoryItemFields(item), policyTokens);
87
+ }
88
+
28
89
  export const CODEX_MODEL_TOOL_OUTPUT_TRUNCATION_TOKENS = 10_000;
29
90
  export const CODEX_TOOL_OUTPUT_SERIALIZATION_ALLOWANCE = 1.2;
30
91
  export const DEFAULT_MODEL_TOOL_OUTPUT_TRUNCATION_TOKENS =
package/src/normalize.ts CHANGED
@@ -7,9 +7,14 @@
7
7
  // - strip max_output_tokens / max_completion_tokens
8
8
  // - reasoning effort minimal -> low
9
9
  // - normalize the model slug (longest-prefix against the live catalog)
10
- // - strip every item `id` but PRESERVE `call_id`
10
+ // - strip every item `id` and `status` but PRESERVE `call_id`
11
11
  // We do NOT filter item_reference (the SDK never emits it) and do NOT convert
12
12
  // orphaned tool outputs (the SDK's runner already prunes by call_id).
13
+ //
14
+ // `status` is an output annotation SuperGrok (and some Responses items) persist
15
+ // on messages / function_call / function_call_output. Codex's strict input
16
+ // schema 400s `Unknown parameter: 'input[N].status'` — observed live on a
17
+ // portable SuperGrok → Codex switch. Pairing uses `call_id`, never `status`.
13
18
 
14
19
  const MINIMAL = "minimal";
15
20
 
@@ -66,10 +71,13 @@ export function normalizeCodexRequestBody(
66
71
  body.model = resolveModel(body.model);
67
72
  }
68
73
 
69
- // strip every item id; PRESERVE call_id. spec §1.6 / verdict §0(b)
74
+ // strip every item id and status; PRESERVE call_id. spec §1.6 / verdict §0(b)
70
75
  // (This also covers tool_search items: the backend accepts an id-less
71
76
  // tool_search_call/output pair correlated by call_id — verified live — and
72
77
  // stripping the provider-stored `tsc_…` id here sanitizes BOTH replay paths.)
78
+ // `status` is output-only on Codex input items. New rows omit it at persist;
79
+ // this wire strip remains defense for already-stored SuperGrok rows and
80
+ // mid-turn SDK items.
73
81
  if (Array.isArray(body.input)) {
74
82
  for (const item of body.input as unknown[]) {
75
83
  if (!item || typeof item !== "object") {
@@ -79,6 +87,9 @@ export function normalizeCodexRequestBody(
79
87
  if ("id" in record) {
80
88
  delete record.id;
81
89
  }
90
+ if ("status" in record) {
91
+ delete record.status;
92
+ }
82
93
  // A replayed tool_search_call must carry `arguments` as an OBJECT — the
83
94
  // backend 400s a string ("Invalid type for 'input[N].arguments': expected
84
95
  // an object", verified live). The live wire emits an object (the SDK's
@@ -133,6 +144,7 @@ export function normalizedCodexRequestBody(
133
144
  if (!item || typeof item !== "object" || Array.isArray(item)) return item;
134
145
  const record = item as Record<string, unknown>;
135
146
  return "id" in record ||
147
+ "status" in record ||
136
148
  (record.type === "tool_search_call" && typeof record.arguments === "string")
137
149
  ? { ...record }
138
150
  : item;
package/src/realtime.ts CHANGED
@@ -1,10 +1,15 @@
1
1
  import { codexSubscriptionHeaders, type CodexAuthHeaders } from "./api-client";
2
2
  import {
3
3
  CODEX_REALTIME_CALL_TIMEOUT_MS,
4
+ CODEX_REALTIME_CONFIG_ID,
5
+ CODEX_REALTIME_CONFIG_TIMEOUT_MS,
4
6
  CODEX_REALTIME_DEFAULT_VOICE,
5
7
  CODEX_REALTIME_MODEL,
8
+ CODEX_REALTIME_PROVIDER_ARCHITECTURE_FALLBACK,
9
+ CODEX_REALTIME_PROVIDER_MODEL_FALLBACK,
6
10
  CODEX_REALTIME_VERSION,
7
11
  CODEX_RESPONSES_BASE,
12
+ CODEX_WHAM_BASE,
8
13
  } from "./constants";
9
14
  import type { CodexFetch } from "./device-code";
10
15
  import {
@@ -13,8 +18,9 @@ import {
13
18
  type CodexRealtimeInitialItem,
14
19
  } from "./realtime-v3";
15
20
 
16
- const CODEX_REALTIME_CALL_URL = `${CODEX_RESPONSES_BASE}/realtime/calls?intent=quicksilver&architecture=avas`;
17
21
  const MAX_REALTIME_SDP_BYTES = 1024 * 1024;
22
+ const MAX_REALTIME_PROVIDER_VALUE_LENGTH = 128;
23
+ const REALTIME_PROVIDER_VALUE = /^[a-z0-9][a-z0-9._-]*$/i;
18
24
  const REALTIME_CALL_ID =
19
25
  /^(?:rtc_.+|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i;
20
26
 
@@ -78,8 +84,108 @@ export class CodexRealtimeError extends Error {
78
84
  export type CodexRealtimeCallOptions = {
79
85
  signal?: AbortSignal | undefined;
80
86
  timeoutMs?: number | undefined;
87
+ providerConfig?: CodexRealtimeProviderConfig | undefined;
81
88
  };
82
89
 
90
+ export type CodexRealtimeProviderConfig = {
91
+ architecture: string;
92
+ model: string;
93
+ version: typeof CODEX_REALTIME_VERSION;
94
+ };
95
+
96
+ const FALLBACK_REALTIME_PROVIDER_CONFIG: CodexRealtimeProviderConfig = {
97
+ architecture: CODEX_REALTIME_PROVIDER_ARCHITECTURE_FALLBACK,
98
+ model: CODEX_REALTIME_PROVIDER_MODEL_FALLBACK,
99
+ version: CODEX_REALTIME_VERSION,
100
+ };
101
+
102
+ /**
103
+ * Resolve the provider-controlled Codex voice model without changing the
104
+ * stable OpenGeni model id. ChatGPT rotates this config independently of Codex
105
+ * releases; pinning the old value caused deterministic call-creation failures.
106
+ */
107
+ export async function fetchCodexRealtimeProviderConfig(
108
+ auth: CodexAuthHeaders,
109
+ fetchImpl: CodexFetch = fetch,
110
+ options: {
111
+ signal?: AbortSignal | undefined;
112
+ timeoutMs?: number | undefined;
113
+ } = {},
114
+ ): Promise<CodexRealtimeProviderConfig> {
115
+ if (options.signal?.aborted) {
116
+ throw new CodexRealtimeError("cancelled", "Codex realtime request cancelled");
117
+ }
118
+ const controller = new AbortController();
119
+ const timeoutMs = options.timeoutMs ?? CODEX_REALTIME_CONFIG_TIMEOUT_MS;
120
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
121
+ const onAbort = (): void => controller.abort(options.signal?.reason);
122
+ options.signal?.addEventListener("abort", onAbort, { once: true });
123
+ try {
124
+ const response = await fetchImpl(`${CODEX_WHAM_BASE}/wham/statsig/bootstrap`, {
125
+ method: "POST",
126
+ headers: {
127
+ ...codexSubscriptionHeaders(auth),
128
+ "content-type": "application/json",
129
+ },
130
+ body: JSON.stringify({
131
+ app_session_id: crypto.randomUUID(),
132
+ app_version: auth.clientVersion,
133
+ brand_name: "Codex",
134
+ build_flavor: "stable",
135
+ locale: "en-US",
136
+ stable_id: auth.chatgptAccountId ?? "opengeni-server",
137
+ system_name: "OpenGeni",
138
+ system_version: "server",
139
+ window_type: "local",
140
+ }),
141
+ signal: controller.signal,
142
+ });
143
+ if (response.status === 401) {
144
+ await response.body?.cancel().catch(() => undefined);
145
+ throw new CodexRealtimeError(
146
+ "authentication",
147
+ "Codex subscription rejected authentication",
148
+ response.status,
149
+ );
150
+ }
151
+ if (!response.ok) {
152
+ await response.body?.cancel().catch(() => undefined);
153
+ return FALLBACK_REALTIME_PROVIDER_CONFIG;
154
+ }
155
+ const outer = (await response.json().catch(() => null)) as {
156
+ statsigPayload?: unknown;
157
+ } | null;
158
+ if (typeof outer?.statsigPayload !== "string") return FALLBACK_REALTIME_PROVIDER_CONFIG;
159
+ const payload = JSON.parse(outer.statsigPayload) as {
160
+ dynamic_configs?: Record<string, { value?: Record<string, unknown> }>;
161
+ };
162
+ const value = payload.dynamic_configs?.[CODEX_REALTIME_CONFIG_ID]?.value;
163
+ const version = value?.version;
164
+ if (version !== undefined && version !== CODEX_REALTIME_VERSION) {
165
+ throw new CodexRealtimeError(
166
+ "incompatible",
167
+ `Codex realtime remote configuration requires ${String(version)}`,
168
+ );
169
+ }
170
+ const architecture = validProviderValue(value?.architecture)
171
+ ? value.architecture
172
+ : FALLBACK_REALTIME_PROVIDER_CONFIG.architecture;
173
+ const model = validProviderValue(value?.model)
174
+ ? value.model
175
+ : FALLBACK_REALTIME_PROVIDER_CONFIG.model;
176
+ return { architecture, model, version: CODEX_REALTIME_VERSION };
177
+ } catch (error) {
178
+ if (error instanceof CodexRealtimeError) throw error;
179
+ if (options.signal?.aborted) {
180
+ throw new CodexRealtimeError("cancelled", "Codex realtime request cancelled");
181
+ }
182
+ return FALLBACK_REALTIME_PROVIDER_CONFIG;
183
+ } finally {
184
+ clearTimeout(timeout);
185
+ options.signal?.removeEventListener("abort", onAbort);
186
+ }
187
+ }
188
+
83
189
  /**
84
190
  * Create one native subscription-authenticated Codex GPT-Live V3 WebRTC call.
85
191
  *
@@ -95,12 +201,28 @@ export async function createCodexRealtimeCall(
95
201
  ): Promise<CodexRealtimeCallResult> {
96
202
  validateRealtimeInput(input);
97
203
  const timeoutMs = options.timeoutMs ?? CODEX_REALTIME_CALL_TIMEOUT_MS;
204
+ const providerConfig = options.providerConfig ?? FALLBACK_REALTIME_PROVIDER_CONFIG;
98
205
  if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
99
206
  throw new CodexRealtimeError("invalid_request", "Codex realtime timeout must be positive");
100
207
  }
101
208
  if (options.signal?.aborted) {
102
209
  throw new CodexRealtimeError("cancelled", "Codex realtime request cancelled");
103
210
  }
211
+ if (providerConfig.version !== CODEX_REALTIME_VERSION) {
212
+ throw new CodexRealtimeError(
213
+ "incompatible",
214
+ `Codex realtime provider configuration requires ${providerConfig.version}`,
215
+ );
216
+ }
217
+ if (
218
+ !validProviderValue(providerConfig.architecture) ||
219
+ !validProviderValue(providerConfig.model)
220
+ ) {
221
+ throw new CodexRealtimeError(
222
+ "invalid_request",
223
+ "Codex realtime provider configuration is invalid",
224
+ );
225
+ }
104
226
 
105
227
  const controller = new AbortController();
106
228
  let timedOut = false;
@@ -124,42 +246,45 @@ export async function createCodexRealtimeCall(
124
246
  });
125
247
 
126
248
  const request = (async (): Promise<CodexRealtimeCallResult> => {
127
- const response = await fetchImpl(CODEX_REALTIME_CALL_URL, {
128
- method: "POST",
129
- headers: {
130
- ...codexSubscriptionHeaders(auth),
131
- "content-type": "application/json",
132
- "openai-alpha": "quicksilver=v2",
133
- "session-id": input.sessionId,
134
- "thread-id": input.sessionId,
135
- },
136
- body: JSON.stringify({
137
- sdp: input.sdp,
138
- session: {
139
- instructions: input.instructions ?? "",
140
- audio: {
141
- output: { voice: input.voice ?? CODEX_REALTIME_DEFAULT_VOICE },
142
- },
143
- delegation: { type: "client" },
144
- model: CODEX_REALTIME_MODEL,
145
- ...(input.initialItems?.length
146
- ? {
147
- initial_items: input.initialItems.map((item) => ({
148
- type: "message",
149
- role: item.role,
150
- content: [
151
- {
152
- type: item.role === "assistant" ? "output_text" : "input_text",
153
- text: item.text,
154
- },
155
- ],
156
- })),
157
- }
158
- : {}),
249
+ const response = await fetchImpl(
250
+ `${CODEX_RESPONSES_BASE}/realtime/calls?intent=quicksilver&architecture=${encodeURIComponent(providerConfig.architecture)}`,
251
+ {
252
+ method: "POST",
253
+ headers: {
254
+ ...codexSubscriptionHeaders(auth),
255
+ "content-type": "application/json",
256
+ "openai-alpha": "quicksilver=v2",
257
+ "session-id": input.sessionId,
258
+ "thread-id": input.sessionId,
159
259
  },
160
- }),
161
- signal: controller.signal,
162
- });
260
+ body: JSON.stringify({
261
+ sdp: input.sdp,
262
+ session: {
263
+ instructions: input.instructions ?? "",
264
+ audio: {
265
+ output: { voice: input.voice ?? CODEX_REALTIME_DEFAULT_VOICE },
266
+ },
267
+ delegation: { type: "client" },
268
+ model: providerConfig.model,
269
+ ...(input.initialItems?.length
270
+ ? {
271
+ initial_items: input.initialItems.map((item) => ({
272
+ type: "message",
273
+ role: item.role,
274
+ content: [
275
+ {
276
+ type: item.role === "assistant" ? "output_text" : "input_text",
277
+ text: item.text,
278
+ },
279
+ ],
280
+ })),
281
+ }
282
+ : {}),
283
+ },
284
+ }),
285
+ signal: controller.signal,
286
+ },
287
+ );
163
288
 
164
289
  if (!response.ok) {
165
290
  await response.body?.cancel().catch(() => undefined);
@@ -208,6 +333,14 @@ export async function createCodexRealtimeCall(
208
333
  }
209
334
  }
210
335
 
336
+ function validProviderValue(value: unknown): value is string {
337
+ return (
338
+ typeof value === "string" &&
339
+ value.length <= MAX_REALTIME_PROVIDER_VALUE_LENGTH &&
340
+ REALTIME_PROVIDER_VALUE.test(value)
341
+ );
342
+ }
343
+
211
344
  /** Shared pin→active selection for worker turns and direct realtime calls. */
212
345
  export function selectCodexCredentialId(args: {
213
346
  sessionPinnedCredentialId: string | null;
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/constants.ts"],"sourcesContent":["// Wire constants for the ChatGPT/Codex subscription backend.\n// Source: CODEX-SUBSCRIPTION-SPEC.md (verified against openai/codex codex-rs).\n\nexport const CODEX_ISSUER = \"https://auth.openai.com\";\nexport const CODEX_CLIENT_ID = \"app_EMoamEEZ73f0CkXaXp7hrann\"; // spec §1.1 (manager.rs:1444)\nexport const CODEX_AUTH_BASE = `${CODEX_ISSUER}/api/accounts`; // device endpoints (device_code_auth.rs:164)\nexport const CODEX_TOKEN_URL = `${CODEX_ISSUER}/oauth/token`; // exchange (form) + refresh (json)\nexport const CODEX_DEVICE_VERIFICATION_URL = `${CODEX_ISSUER}/codex/device`;\nexport const CODEX_DEVICE_REDIRECT_URI = `${CODEX_ISSUER}/deviceauth/callback`;\n\n// Model requests: base already includes /codex; client appends /responses, /models.\nexport const CODEX_RESPONSES_BASE = \"https://chatgpt.com/backend-api/codex\";\n// Usage lives on the WHAM base — NOT under /codex (verified, spec §1.8a).\nexport const CODEX_WHAM_BASE = \"https://chatgpt.com/backend-api\";\n\nexport const CODEX_ORIGINATOR = \"codex_cli_rs\"; // whitelisted originator (spec §1.2)\nexport const CODEX_ID_TOKEN_AUTH_CLAIM = \"https://api.openai.com/auth\";\n\n// Synthetic registry-provider identity. The provider's baseURL is the bare\n// /backend-api (NOT /codex) — codexSubscriptionFetch rewrites /responses ->\n// /codex/responses. Codex model ids are namespaced `codex/<slug>` so they never\n// collide with the built-in OpenAI provider's model ids; the fetch's resolveModel\n// strips the prefix before the slug reaches the backend.\nexport const CODEX_PROVIDER_ID = \"codex-subscription\";\nexport const CODEX_PROVIDER_BASE_URL = \"https://chatgpt.com/backend-api\";\nexport const CODEX_MODEL_ID_PREFIX = \"codex/\";\n\n// The only Codex subscription models OpenGeni exposes. The live GET /models\n// catalog must contain every exact slug; older or internal models never broaden\n// this product allowlist.\nexport const CODEX_FALLBACK_MODEL_SLUGS = [\"gpt-5.6-sol\", \"gpt-5.6-terra\", \"gpt-5.6-luna\"] as const;\n\n// Live Codex model-catalog values for every exposed gpt-5.6 subscription slug.\n// Verified 2026-07-18 against Codex CLI 0.144.6's freshly fetched\n// ~/.codex/models_cache.json and the matching openai/codex core derivations:\n// raw context window = 272,000\n// effective input window (95%) = 258,400\n// automatic compaction limit (90%) = 244,800\n// Keep all three explicit: the effective ceiling is a hard input guard while\n// the lower auto-compact limit is the proactive checkpoint trigger.\nexport const CODEX_MODEL_CONTEXT_WINDOW_TOKENS = 272_000;\nexport const CODEX_EFFECTIVE_CONTEXT_WINDOW_PERCENT = 95;\nexport const CODEX_MODEL_EFFECTIVE_CONTEXT_WINDOW_TOKENS = Math.floor(\n (CODEX_MODEL_CONTEXT_WINDOW_TOKENS * CODEX_EFFECTIVE_CONTEXT_WINDOW_PERCENT) / 100,\n);\nexport const CODEX_AUTO_COMPACTION_PERCENT = 90;\nexport const CODEX_MODEL_AUTO_COMPACT_TOKEN_LIMIT = Math.floor(\n (CODEX_MODEL_CONTEXT_WINDOW_TOKENS * CODEX_AUTO_COMPACTION_PERCENT) / 100,\n);\n\n// Sent as the `version` header and inside the User-Agent. Staging-proven on\n// 2026-07-09: 0.142.4 filtered every GPT-5.6 slug out of GET /models, while the\n// official Codex 0.144.0+ releases return all three exact slugs above. Keep\n// this pinned to the latest stable Codex release we have verified end-to-end.\nexport const CODEX_CLIENT_VERSION = \"0.145.0\";\n\n// Native ChatGPT/Codex subscription WebRTC call creation. Verified against\n// openai/codex 0.145.0 commit bd2de422aa287b97b06ca6425a10935bcf1b3731.\nexport const CODEX_REALTIME_MODEL = \"gpt-live-1-boulder-alpha\";\nexport const CODEX_REALTIME_VERSION = \"v3\";\nexport const CODEX_REALTIME_DEFAULT_VOICE = \"cove\";\nexport const CODEX_REALTIME_CALL_TIMEOUT_MS = 15_000;\n\nexport const CODEX_REFRESH_WINDOW_MS = 5 * 60 * 1000; // proactive refresh when within 5 min of exp (spec §1.1)\nexport const CODEX_REFRESH_FALLBACK_MS = 8 * 24 * 60 * 60 * 1000; // 8 days when exp is unparseable\n\n// Codex Responses transport deadlines. The OpenAI SDK's own timeout only covers\n// the wait for response headers and erases the underlying timeout class into the\n// bare `Request timed out.` error. Keep the provider-specific budgets here so\n// the transport can enforce and durably report them without enabling the SDK's\n// blind request replay.\nexport const CODEX_RESPONSE_HEADERS_TIMEOUT_MS = 4 * 60_000;\nexport const CODEX_RESPONSE_STREAM_IDLE_TIMEOUT_MS = 5 * 60_000;\nexport const CODEX_RESPONSE_WHOLE_TIMEOUT_MS = 30 * 60_000;\n// Kept as a compatibility-shaped policy field, but automatic replay is disabled\n// until a provider-specific operation receipt can prove non-acceptance or resume\n// the same operation identity. An absent response does not prove that the\n// provider never accepted the request.\nexport const CODEX_RESPONSE_NO_BYTE_RETRIES = 0;\nexport const CODEX_RESPONSE_RETRY_BACKOFF_MS = 1_000;\n// Must exceed the transport-owned whole-response deadline. This SDK guard is a\n// last-resort envelope; the inner transport emits the typed/durable failure.\nexport const CODEX_RESPONSE_SDK_OUTER_TIMEOUT_MS = 35 * 60_000;\n\n// ── Apps / connectors MCP (spec §1.10, §E) ───────────────────────────────────\n// One server-side MCP exposes ALL the user's ChatGPT/Codex connectors\n// (gmail/github/linear/slack/sentry/drive/calendar/…). Streamable HTTP, always.\nexport const CODEX_APPS_MCP_SERVER_ID = \"codex_apps\"; // tools surface as mcp__codex_apps__<tool>\nexport const CODEX_APPS_MCP_SERVER_NAME = \"codex_apps\"; // MCP `name` — MUST equal the id so the SDK namespaces tools as mcp__codex_apps__*\nexport const CODEX_APPS_MCP_URL = \"https://chatgpt.com/backend-api/ps/mcp\"; // live URL (NOT /codex, NOT the legacy /wham/apps)\nexport const CODEX_APPS_STARTUP_TIMEOUT_MS = 30_000; // startup_timeout 30s (spec §1.10) — maps to timeoutMs on this server only\n// Connector scopes that the apps MCP requires. Present ONLY when granted at\n// browser-authorize time; the device-code path CANNOT be confirmed to grant\n// them, so treat connector availability as runtime-discovered (spec §1.10 / §E).\nexport const CODEX_APPS_REQUIRED_SCOPES = [\"api.connectors.read\", \"api.connectors.invoke\"] as const;\n"],"mappings":";AAGO,IAAM,eAAe;AACrB,IAAM,kBAAkB;AACxB,IAAM,kBAAkB,GAAG,YAAY;AACvC,IAAM,kBAAkB,GAAG,YAAY;AACvC,IAAM,gCAAgC,GAAG,YAAY;AACrD,IAAM,4BAA4B,GAAG,YAAY;AAGjD,IAAM,uBAAuB;AAE7B,IAAM,kBAAkB;AAExB,IAAM,mBAAmB;AACzB,IAAM,4BAA4B;AAOlC,IAAM,oBAAoB;AAC1B,IAAM,0BAA0B;AAChC,IAAM,wBAAwB;AAK9B,IAAM,6BAA6B,CAAC,eAAe,iBAAiB,cAAc;AAUlF,IAAM,oCAAoC;AAC1C,IAAM,yCAAyC;AAC/C,IAAM,8CAA8C,KAAK;AAAA,EAC7D,oCAAoC,yCAA0C;AACjF;AACO,IAAM,gCAAgC;AACtC,IAAM,uCAAuC,KAAK;AAAA,EACtD,oCAAoC,gCAAiC;AACxE;AAMO,IAAM,uBAAuB;AAI7B,IAAM,uBAAuB;AAC7B,IAAM,yBAAyB;AAC/B,IAAM,+BAA+B;AACrC,IAAM,iCAAiC;AAEvC,IAAM,0BAA0B,IAAI,KAAK;AACzC,IAAM,4BAA4B,IAAI,KAAK,KAAK,KAAK;AAOrD,IAAM,oCAAoC,IAAI;AAC9C,IAAM,wCAAwC,IAAI;AAClD,IAAM,kCAAkC,KAAK;AAK7C,IAAM,iCAAiC;AACvC,IAAM,kCAAkC;AAGxC,IAAM,sCAAsC,KAAK;AAKjD,IAAM,2BAA2B;AACjC,IAAM,6BAA6B;AACnC,IAAM,qBAAqB;AAC3B,IAAM,gCAAgC;AAItC,IAAM,6BAA6B,CAAC,uBAAuB,uBAAuB;","names":[]}