@opengeni/codex 0.2.25 → 0.2.26-canary.36199476632001
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 ?
|
|
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
|
|
39
|
-
*
|
|
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.
|
|
3
|
+
"version": "0.2.26-canary.36199476632001",
|
|
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/
|
|
44
|
+
"@opengeni/contracts": "^5.1.1-canary.36199476632001",
|
|
45
|
+
"@opengeni/network": "^0.3.1-canary.36199476632001",
|
|
45
46
|
"zod": "^4.2.1"
|
|
46
47
|
},
|
|
47
48
|
"engines": {
|
|
@@ -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
|
-
?
|
|
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
|
|
55
|
-
*
|
|
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
|
-
//
|
|
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
|
-
//
|
|
79
|
-
//
|
|
80
|
-
//
|
|
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
|