@opengeni/codex 0.2.24 → 0.2.25-canary.35916963870001

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.
@@ -18,6 +18,7 @@
18
18
  * runtime request seam and the database history boundary call the same pure
19
19
  * function, so replayed conversation truth is identical to live model input.
20
20
  */
21
+ import { type HostedCallStatusItemType } from "./hosted-call-status.js";
21
22
  export { MODEL_TOOL_OUTPUT_OVERSIZED_IMAGE_CARD_DATA_URL } from "./oversized-image-card.js";
22
23
  export type ModelHistoryItem = Record<string, unknown>;
23
24
  type WithoutOutputOnlyProviderDataFields<T> = T extends ModelHistoryItem ? Omit<T, "status"> : T;
@@ -28,15 +29,18 @@ type WithoutOutputOnlyProviderDataField<T extends ModelHistoryItem> = "providerD
28
29
  } : {
29
30
  providerData: WithoutOutputOnlyProviderDataFields<T["providerData"]>;
30
31
  } : object;
31
- type WithoutOutputOnlyHistoryItemFields<T extends ModelHistoryItem> = T extends unknown ? Omit<T, "status" | "providerData"> & WithoutOutputOnlyProviderDataField<T> : never;
32
+ type WithoutOutputOnlyHistoryItemFields<T extends ModelHistoryItem> = T extends unknown ? T extends {
33
+ type: HostedCallStatusItemType;
34
+ } ? T : Omit<T, "status" | "providerData"> & WithoutOutputOnlyProviderDataField<T> : never;
32
35
  /**
33
36
  * Responses output items carry `status` (`in_progress` / `completed` /
34
37
  * `incomplete`). That field is not conversation meaning — pairing is `call_id`
35
38
  * — and Codex's input schema 400s it (`Unknown parameter: 'input[N].status'`).
36
39
  * SuperGrok accepts items with or without it. The SDK also nests `status` on
37
40
  * `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.
41
+ * Canonical history omits both for those items so portable sessions can cross
42
+ * Responses providers. Hosted tool calls are different: the provider requires
43
+ * their status on replay, and the SDK reads it to reconstruct the wire item.
40
44
  */
41
45
  export declare function omitOutputOnlyHistoryItemFields<T extends ModelHistoryItem>(item: T): WithoutOutputOnlyHistoryItemFields<T>;
42
46
  /** Persist/replay boundary: drop output-only fields, then bound tool output. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/codex",
3
- "version": "0.2.24",
3
+ "version": "0.2.25-canary.35916963870001",
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,8 @@
41
41
  "prepublishOnly": "bash ../../scripts/prepublish-guard"
42
42
  },
43
43
  "dependencies": {
44
- "@opengeni/network": "^0.3.1",
44
+ "@opengeni/contracts": "^5.1.0-canary.35916963870001",
45
+ "@opengeni/network": "^0.3.1-canary.35916963870001",
45
46
  "zod": "^4.2.1"
46
47
  },
47
48
  "engines": {
package/src/constants.ts CHANGED
@@ -25,16 +25,10 @@ export const CODEX_PROVIDER_ID = "codex-subscription";
25
25
  export const CODEX_PROVIDER_BASE_URL = "https://chatgpt.com/backend-api";
26
26
  export const CODEX_MODEL_ID_PREFIX = "codex/";
27
27
 
28
- // The only Codex subscription models OpenGeni exposes. Astra is deliberately
29
- // pre-advertised before an account receives provider rollout access so the
30
- // picker becomes runnable without another OpenGeni release once access arrives.
31
- // Older or internal live models never broaden this product allowlist.
32
- export const CODEX_FALLBACK_MODEL_SLUGS = [
33
- "gpt-5.6-sol",
34
- "gpt-5.6-terra",
35
- "gpt-5.6-luna",
36
- "gpt-6-astra",
37
- ] as const;
28
+ // The only Codex subscription models OpenGeni exposes. Older families are not
29
+ // advertised. A slug stays in this list before every account has rollout access
30
+ // so the picker can offer it without another OpenGeni release.
31
+ export const CODEX_FALLBACK_MODEL_SLUGS = ["gpt-6-astra", "gpt-6-sol", "gpt-6-luna"] as const;
38
32
 
39
33
  // Live Codex model-catalog values for every exposed subscription slug.
40
34
  // Verified 2026-09-04 against Codex CLI 0.153.2's bundled model catalog:
@@ -57,7 +51,7 @@ export const CODEX_MODEL_AUTO_COMPACT_TOKEN_LIMIT = Math.floor(
57
51
  // 2026-07-09: 0.142.4 filtered every GPT-5.6 slug out of GET /models. Keep this
58
52
  // pinned to the latest stable Codex release whose bundled catalog and transport
59
53
  // contract have been reviewed here.
60
- export const CODEX_CLIENT_VERSION = "0.153.2";
54
+ export const CODEX_CLIENT_VERSION = "0.156.0";
61
55
 
62
56
  // Public OpenGeni selector for native ChatGPT/Codex subscription WebRTC. This
63
57
  // remains stable for persisted sessions; the provider's remotely configured
@@ -0,0 +1,17 @@
1
+ /** Hosted Responses calls require status on replay, unlike function/message annotations. */
2
+ export type HostedCallStatusItemType =
3
+ | "hosted_tool_call"
4
+ | "web_search_call"
5
+ | "file_search_call"
6
+ | "code_interpreter_call"
7
+ | "image_generation_call";
8
+
9
+ export function preservesHostedCallStatus(type: unknown): type is HostedCallStatusItemType {
10
+ return (
11
+ type === "hosted_tool_call" ||
12
+ type === "web_search_call" ||
13
+ type === "file_search_call" ||
14
+ type === "code_interpreter_call" ||
15
+ type === "image_generation_call"
16
+ );
17
+ }
@@ -20,6 +20,8 @@
20
20
  */
21
21
 
22
22
  import { MODEL_TOOL_OUTPUT_OVERSIZED_IMAGE_CARD_DATA_URL } from "./oversized-image-card";
23
+ import { RetainedArtifactMetadataSchema } from "@opengeni/contracts";
24
+ import { preservesHostedCallStatus, type HostedCallStatusItemType } from "./hosted-call-status";
23
25
 
24
26
  export { MODEL_TOOL_OUTPUT_OVERSIZED_IMAGE_CARD_DATA_URL } from "./oversized-image-card";
25
27
 
@@ -42,7 +44,9 @@ type WithoutOutputOnlyProviderDataField<T extends ModelHistoryItem> = "providerD
42
44
  : object;
43
45
 
44
46
  type WithoutOutputOnlyHistoryItemFields<T extends ModelHistoryItem> = T extends unknown
45
- ? Omit<T, "status" | "providerData"> & WithoutOutputOnlyProviderDataField<T>
47
+ ? T extends { type: HostedCallStatusItemType }
48
+ ? T
49
+ : Omit<T, "status" | "providerData"> & WithoutOutputOnlyProviderDataField<T>
46
50
  : never;
47
51
 
48
52
  /**
@@ -51,8 +55,9 @@ type WithoutOutputOnlyHistoryItemFields<T extends ModelHistoryItem> = T extends
51
55
  * — and Codex's input schema 400s it (`Unknown parameter: 'input[N].status'`).
52
56
  * SuperGrok accepts items with or without it. The SDK also nests `status` on
53
57
  * `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.
58
+ * Canonical history omits both for those items so portable sessions can cross
59
+ * Responses providers. Hosted tool calls are different: the provider requires
60
+ * their status on replay, and the SDK reads it to reconstruct the wire item.
56
61
  */
57
62
  export function omitOutputOnlyHistoryItemFields<T extends ModelHistoryItem>(
58
63
  item: T,
@@ -60,6 +65,9 @@ export function omitOutputOnlyHistoryItemFields<T extends ModelHistoryItem>(
60
65
  if (!item || typeof item !== "object") {
61
66
  return item as unknown as WithoutOutputOnlyHistoryItemFields<T>;
62
67
  }
68
+ if (preservesHostedCallStatus(item.type)) {
69
+ return item as unknown as WithoutOutputOnlyHistoryItemFields<T>;
70
+ }
63
71
  const providerData =
64
72
  item.providerData && typeof item.providerData === "object"
65
73
  ? (item.providerData as Record<string, unknown>)
@@ -402,6 +410,16 @@ function boundTextLeaves(
402
410
  return bounded;
403
411
  }
404
412
  if (!value || typeof value !== "object") return value;
413
+ // Screenshot receipts are image protocol, not model-visible text. They are
414
+ // compact, schema-bounded references whose fields must survive a zero text
415
+ // budget so a resumed turn can materialize the image from durable storage.
416
+ if (
417
+ !Array.isArray(value) &&
418
+ (value as Record<string, unknown>).type === "retained_artifact" &&
419
+ RetainedArtifactMetadataSchema.safeParse((value as Record<string, unknown>).artifact).success
420
+ ) {
421
+ return value;
422
+ }
405
423
  if (depth >= MODEL_TOOL_OUTPUT_MAX_DEPTH) return DEPTH_OMISSION_MARKER;
406
424
  if (state.seen.has(value)) return CYCLE_OMISSION_MARKER;
407
425
  state.seen.add(value);
package/src/normalize.ts CHANGED
@@ -7,7 +7,8 @@
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` and `status` but PRESERVE `call_id`
10
+ // - strip every item `id` and output-only `status` but PRESERVE `call_id`
11
+ // and the required status of hosted tool calls
11
12
  // We do NOT filter item_reference (the SDK never emits it) and do NOT convert
12
13
  // orphaned tool outputs (the SDK's runner already prunes by call_id).
13
14
  //
@@ -16,6 +17,8 @@
16
17
  // schema 400s `Unknown parameter: 'input[N].status'` — observed live on a
17
18
  // portable SuperGrok → Codex switch. Pairing uses `call_id`, never `status`.
18
19
 
20
+ import { preservesHostedCallStatus } from "./hosted-call-status";
21
+
19
22
  const MINIMAL = "minimal";
20
23
 
21
24
  // The ChatGPT/Codex backend is a STRICT ALLOWLIST: it 400s on ANY top-level field
@@ -71,13 +74,13 @@ export function normalizeCodexRequestBody(
71
74
  body.model = resolveModel(body.model);
72
75
  }
73
76
 
74
- // strip every item id and status; PRESERVE call_id. spec §1.6 / verdict §0(b)
77
+ // Strip item ids and output-only status; preserve call_id and hosted status.
75
78
  // (This also covers tool_search items: the backend accepts an id-less
76
79
  // tool_search_call/output pair correlated by call_id — verified live — and
77
80
  // 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.
81
+ // Hosted web/file search, code interpreter and image-generation items require
82
+ // their actual status on replay. Never turn an absent status into a fabricated
83
+ // completion. Other items retain the portable-history compatibility strip.
81
84
  if (Array.isArray(body.input)) {
82
85
  for (const item of body.input as unknown[]) {
83
86
  if (!item || typeof item !== "object") {
@@ -87,7 +90,7 @@ export function normalizeCodexRequestBody(
87
90
  if ("id" in record) {
88
91
  delete record.id;
89
92
  }
90
- if ("status" in record) {
93
+ if ("status" in record && !preservesHostedCallStatus(record.type)) {
91
94
  delete record.status;
92
95
  }
93
96
  // A replayed tool_search_call must carry `arguments` as an OBJECT — the
@@ -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. Astra is deliberately\n// pre-advertised before an account receives provider rollout access so the\n// picker becomes runnable without another OpenGeni release once access arrives.\n// Older or internal live models never broaden this product allowlist.\nexport const CODEX_FALLBACK_MODEL_SLUGS = [\n \"gpt-5.6-sol\",\n \"gpt-5.6-terra\",\n \"gpt-5.6-luna\",\n \"gpt-6-astra\",\n] as const;\n\n// Live Codex model-catalog values for every exposed subscription slug.\n// Verified 2026-09-04 against Codex CLI 0.153.2's bundled model catalog:\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. Keep this\n// pinned to the latest stable Codex release whose bundled catalog and transport\n// contract have been reviewed here.\nexport const CODEX_CLIENT_VERSION = \"0.153.2\";\n\n// Public OpenGeni selector for native ChatGPT/Codex subscription WebRTC. This\n// remains stable for persisted sessions; the provider's remotely configured\n// model is resolved separately before call creation.\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;\nexport const CODEX_REALTIME_CONFIG_TIMEOUT_MS = 5_000;\nexport const CODEX_REALTIME_CONFIG_ID = \"3566525122\";\n// Verified live on 2026-08-23. Used only when the authenticated remote config\n// is transiently unavailable or malformed; it is not the persisted model id.\nexport const CODEX_REALTIME_PROVIDER_MODEL_FALLBACK = \"gpt-live-1-codex\";\nexport const CODEX_REALTIME_PROVIDER_ARCHITECTURE_FALLBACK = \"avas\";\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;AAM9B,IAAM,6BAA6B;AAAA,EACxC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AASO,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;AAK7B,IAAM,uBAAuB;AAC7B,IAAM,yBAAyB;AAC/B,IAAM,+BAA+B;AACrC,IAAM,iCAAiC;AACvC,IAAM,mCAAmC;AACzC,IAAM,2BAA2B;AAGjC,IAAM,yCAAyC;AAC/C,IAAM,gDAAgD;AAEtD,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":[]}