@opengeni/codex 0.2.7 → 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/api-client.d.ts +57 -0
- package/dist/billing.d.ts +11 -0
- package/dist/bounded-operation.d.ts +16 -0
- package/dist/{chunk-YGKQDUY7.js → chunk-HT463QWA.js} +10 -2
- package/dist/chunk-HT463QWA.js.map +1 -0
- package/dist/chunk-MFVGCR3N.js +236 -0
- package/dist/chunk-MFVGCR3N.js.map +1 -0
- package/dist/constants.d.ts +37 -35
- package/dist/constants.js +9 -1
- package/dist/device-code.d.ts +36 -0
- package/dist/fetch.d.ts +84 -0
- package/dist/index.d.ts +15 -515
- package/dist/index.js +373 -14
- package/dist/index.js.map +1 -1
- package/dist/mcp-sanitize.d.ts +33 -0
- package/dist/model-output-truncation.d.ts +36 -0
- package/dist/normalize.d.ts +8 -0
- package/dist/oversized-image-card.d.ts +17 -0
- package/dist/realtime-v3.d.ts +86 -0
- package/dist/realtime-v3.js +25 -0
- package/dist/realtime-v3.js.map +1 -0
- package/dist/realtime.d.ts +48 -0
- package/dist/refresh.d.ts +28 -0
- package/dist/request-context.d.ts +99 -0
- package/dist/reset-credits.d.ts +33 -0
- package/dist/response-timeout.d.ts +28 -0
- package/dist/usage-normalize.d.ts +61 -0
- package/package.json +7 -3
- package/src/api-client.ts +26 -9
- package/src/constants.ts +8 -1
- package/src/fetch.ts +67 -2
- package/src/index.ts +2 -0
- package/src/normalize.ts +6 -3
- package/src/realtime-v3.ts +349 -0
- package/src/realtime.ts +356 -0
- package/src/request-context.ts +20 -0
- package/dist/chunk-YGKQDUY7.js.map +0 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { FetchLike } from "./fetch";
|
|
2
|
+
/**
|
|
3
|
+
* Maps connector tool names to a Responses-API-legal charset and back. One
|
|
4
|
+
* instance per codex_apps transport (i.e. per turn): tools/list populates it,
|
|
5
|
+
* tools/call reads it. Idempotent across repeat listings.
|
|
6
|
+
*/
|
|
7
|
+
export declare class ToolNameMapper {
|
|
8
|
+
private readonly sanitizedToOriginal;
|
|
9
|
+
private readonly used;
|
|
10
|
+
/** Return a legal, unique name (<= EFFECTIVE_MAX_TOOL_NAME_LEN, so `<prefix>__name` <= 64) for `original`, recording the reverse mapping. */
|
|
11
|
+
sanitize(original: string): string;
|
|
12
|
+
/** Reverse a sanitized name back to the MCP server's original, if known. */
|
|
13
|
+
toOriginal(sanitized: string): string | undefined;
|
|
14
|
+
}
|
|
15
|
+
/** Sanitize a single JSON body (application/json MCP response). */
|
|
16
|
+
export declare function sanitizeMcpJsonBody(text: string, mapper?: ToolNameMapper, namespaceSink?: Set<string>): string;
|
|
17
|
+
/** Sanitize an SSE body: each JSON-RPC message rides on a `data:` line. */
|
|
18
|
+
export declare function sanitizeMcpSseBody(text: string, mapper?: ToolNameMapper, namespaceSink?: Set<string>): string;
|
|
19
|
+
/** Reverse a sanitized tools/call name back to the original; returns null if no rewrite is needed. */
|
|
20
|
+
export declare function remapToolCallRequestBody(body: string, mapper: ToolNameMapper): string | null;
|
|
21
|
+
/**
|
|
22
|
+
* Wrap a base fetch so the codex_apps MCP transport is Responses-API-compatible:
|
|
23
|
+
* tools/list responses get their names sanitized + bad outputSchemas dropped (and
|
|
24
|
+
* the name mapping recorded), and tools/call requests get their name reversed back
|
|
25
|
+
* to the MCP server's original. Only the POST request/response is buffered; the
|
|
26
|
+
* long-lived GET notification SSE stream is passed through untouched.
|
|
27
|
+
*
|
|
28
|
+
* P4 (Part B.1): an optional `namespaceSink` Set accumulates the ORIGINAL-dotted
|
|
29
|
+
* connector namespaces seen across every tools/list this turn (captured before the
|
|
30
|
+
* dot is sanitized away). The worker reads the (live, by-reference) Set after the
|
|
31
|
+
* turn to cache the serving account's connector set — packages/codex stays db-free.
|
|
32
|
+
*/
|
|
33
|
+
export declare function codexAppsSanitizingFetch(base?: FetchLike, namespaceSink?: Set<string>): FetchLike;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical model-facing tool-output truncation.
|
|
3
|
+
*
|
|
4
|
+
* Ported from openai/codex `rust-v0.144.6` (commit
|
|
5
|
+
* 5d1fbf26c43abc65a203928b2e31561cb039e06d):
|
|
6
|
+
*
|
|
7
|
+
* - `codex-rs/utils/string/src/truncate.rs`
|
|
8
|
+
* - `codex-rs/utils/output-truncation/src/lib.rs`
|
|
9
|
+
* - `codex-rs/core/src/context_manager/history.rs`
|
|
10
|
+
*
|
|
11
|
+
* The live gpt-5.6 model catalog declares a 10,000-token truncation policy.
|
|
12
|
+
* Codex applies a 1.2x allowance before serializing a function-call output, so
|
|
13
|
+
* the effective textual payload budget is 12,000 approximate tokens. Images,
|
|
14
|
+
* files, and encrypted content are preserved; textual content shares one
|
|
15
|
+
* sequential budget and carries an explicit head/tail truncation marker.
|
|
16
|
+
*
|
|
17
|
+
* This module deliberately has no database or Agents SDK dependency. Both the
|
|
18
|
+
* runtime request seam and the database history boundary call the same pure
|
|
19
|
+
* function, so replayed conversation truth is identical to live model input.
|
|
20
|
+
*/
|
|
21
|
+
export { MODEL_TOOL_OUTPUT_OVERSIZED_IMAGE_CARD_DATA_URL } from "./oversized-image-card";
|
|
22
|
+
export type ModelHistoryItem = Record<string, unknown>;
|
|
23
|
+
export declare const CODEX_MODEL_TOOL_OUTPUT_TRUNCATION_TOKENS = 10000;
|
|
24
|
+
export declare const CODEX_TOOL_OUTPUT_SERIALIZATION_ALLOWANCE = 1.2;
|
|
25
|
+
export declare const DEFAULT_MODEL_TOOL_OUTPUT_TRUNCATION_TOKENS = 10000;
|
|
26
|
+
export declare const MODEL_TOOL_OUTPUT_OPAQUE_PAYLOAD_MAX_BYTES: number;
|
|
27
|
+
export declare function modelToolOutputSerializationBudgetTokens(policyTokens?: number): number;
|
|
28
|
+
export declare function approximateTokenCount(value: string): number;
|
|
29
|
+
/** Exact Codex-style middle truncation for a token policy. */
|
|
30
|
+
export declare function truncateMiddleWithTokenBudget(value: string, maxTokens: number): string;
|
|
31
|
+
/**
|
|
32
|
+
* Bound every model-visible tool-result item. Non-result items are returned by
|
|
33
|
+
* reference. Result items are cloned only when their textual output changes.
|
|
34
|
+
*/
|
|
35
|
+
export declare function boundModelToolOutputItem<T extends ModelHistoryItem>(item: T, policyTokens?: number): T;
|
|
36
|
+
export declare function boundModelToolOutputItems<T extends ModelHistoryItem>(items: readonly T[], policyTokens?: number): T[];
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Mutates a parsed Responses request body in place and returns it. Pure + synchronous + unit-testable. */
|
|
2
|
+
export declare function normalizeCodexRequestBody(body: Record<string, unknown>, resolveModel: (slug: string) => string): Record<string, unknown>;
|
|
3
|
+
/**
|
|
4
|
+
* Build a longest-prefix model resolver. Catalog slugs come from GET /models
|
|
5
|
+
* (api-client.ts). One leading `namespace/` segment is stripped first; an
|
|
6
|
+
* unknown slug returns the fallback (caller should log — spec §1.4 step 4).
|
|
7
|
+
*/
|
|
8
|
+
export declare function buildModelResolver(liveSlugs: readonly string[], fallbackSlug: string): (slug: string) => string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Protocol-valid image shown to the model when an inline tool image exceeds the
|
|
3
|
+
* hard model-input allowance. The image channel cannot carry a text marker, so
|
|
4
|
+
* the omission itself is rendered as a legible PNG instead of corrupting the
|
|
5
|
+
* original base64 or pretending the placeholder is the real screenshot.
|
|
6
|
+
*
|
|
7
|
+
* Generated with the dependency-free bitmap/PNG encoder documented in
|
|
8
|
+
* `scripts/gen-screenshot-error-card.mjs`; the source image is 1,076x284 RGBA
|
|
9
|
+
* and 5,255 bytes. Rendered text:
|
|
10
|
+
*
|
|
11
|
+
* SCREEN CAPTURE OMITTED
|
|
12
|
+
* THE SCREEN CAPTURE IS TOO LARGE.
|
|
13
|
+
* THIS IS A PLACEHOLDER, NOT THE REAL SCREEN.
|
|
14
|
+
* DO NOT SAY THIS IS THE REAL SCREEN.
|
|
15
|
+
* TELL THE USER TO TAKE A SMALLER CAPTURE.
|
|
16
|
+
*/
|
|
17
|
+
export declare const MODEL_TOOL_OUTPUT_OVERSIZED_IMAGE_CARD_DATA_URL = "data:image/png;base64,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";
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex GPT-Live Frameless Bidi (V3) wire adapter.
|
|
3
|
+
*
|
|
4
|
+
* These shapes are pinned to openai/codex@fa1d4c40. V3 delegates work with
|
|
5
|
+
* `delegation.created`; it does not expose the ordinary Realtime V2
|
|
6
|
+
* `function_call` protocol.
|
|
7
|
+
*/
|
|
8
|
+
export declare const CODEX_REALTIME_INITIAL_ITEMS_MAX_COUNT = 128;
|
|
9
|
+
export declare const CODEX_REALTIME_INITIAL_ITEMS_MAX_TOKENS = 8192;
|
|
10
|
+
export declare const CODEX_REALTIME_CONTEXT_APPEND_MAX_BYTES = 500;
|
|
11
|
+
export declare const CODEX_REALTIME_V3_MAX_EVENT_BYTES = 1048576;
|
|
12
|
+
export declare const CODEX_REALTIME_V3_MAX_IDENTIFIER_BYTES = 1024;
|
|
13
|
+
export declare const CODEX_REALTIME_V3_MAX_TEXT_BYTES = 131072;
|
|
14
|
+
export type CodexRealtimeInitialItem = {
|
|
15
|
+
role: "user" | "developer" | "assistant";
|
|
16
|
+
text: string;
|
|
17
|
+
};
|
|
18
|
+
type ProviderEventIdentity = {
|
|
19
|
+
providerEventId: string | null;
|
|
20
|
+
};
|
|
21
|
+
export type CodexRealtimeV3Event = (ProviderEventIdentity & {
|
|
22
|
+
type: "session.started" | "session.updated";
|
|
23
|
+
sessionId: string;
|
|
24
|
+
instructions: string | null;
|
|
25
|
+
}) | (ProviderEventIdentity & {
|
|
26
|
+
type: "input_transcript.added" | "output_transcript.added";
|
|
27
|
+
itemId: string | null;
|
|
28
|
+
text: string;
|
|
29
|
+
}) | (ProviderEventIdentity & {
|
|
30
|
+
type: "output_audio.delta";
|
|
31
|
+
audio: string;
|
|
32
|
+
startMs: number | null;
|
|
33
|
+
endMs: number | null;
|
|
34
|
+
}) | (ProviderEventIdentity & {
|
|
35
|
+
type: "turn.done";
|
|
36
|
+
turnId: string;
|
|
37
|
+
role: "user" | "assistant";
|
|
38
|
+
transcript: string;
|
|
39
|
+
}) | (ProviderEventIdentity & {
|
|
40
|
+
type: "delegation.created";
|
|
41
|
+
delegationItemId: string;
|
|
42
|
+
inputTranscript: string;
|
|
43
|
+
offsetMs: number | null;
|
|
44
|
+
}) | (ProviderEventIdentity & {
|
|
45
|
+
type: "error";
|
|
46
|
+
message: string;
|
|
47
|
+
});
|
|
48
|
+
export type CodexRealtimeV3ParseFailure = {
|
|
49
|
+
ok: false;
|
|
50
|
+
reason: "invalid_json" | "missing_type" | "unsupported_type" | "invalid_shape" | "oversized_event" | "oversized_field";
|
|
51
|
+
eventType: string | null;
|
|
52
|
+
};
|
|
53
|
+
export type CodexRealtimeV3ParseResult = {
|
|
54
|
+
ok: true;
|
|
55
|
+
event: CodexRealtimeV3Event;
|
|
56
|
+
} | CodexRealtimeV3ParseFailure;
|
|
57
|
+
export type CodexRealtimeV3ContextAppendChannel = "speakable" | "commentary";
|
|
58
|
+
export type CodexRealtimeV3DelegationContextAppend = {
|
|
59
|
+
type: "delegation.context.append";
|
|
60
|
+
delegation_item_id: string;
|
|
61
|
+
channel?: CodexRealtimeV3ContextAppendChannel | undefined;
|
|
62
|
+
content: Array<{
|
|
63
|
+
type: "input_text";
|
|
64
|
+
text: string;
|
|
65
|
+
}>;
|
|
66
|
+
};
|
|
67
|
+
export type CodexRealtimeV3SessionContextAppend = {
|
|
68
|
+
type: "session.context.append";
|
|
69
|
+
channel?: CodexRealtimeV3ContextAppendChannel | undefined;
|
|
70
|
+
content: Array<{
|
|
71
|
+
type: "input_text";
|
|
72
|
+
text: string;
|
|
73
|
+
}>;
|
|
74
|
+
};
|
|
75
|
+
export declare function parseCodexRealtimeV3Event(payload: string): CodexRealtimeV3ParseResult;
|
|
76
|
+
export declare function encodeCodexRealtimeV3DelegationContextAppend(input: {
|
|
77
|
+
delegationItemId: string;
|
|
78
|
+
text: string;
|
|
79
|
+
channel?: CodexRealtimeV3ContextAppendChannel | undefined;
|
|
80
|
+
}): CodexRealtimeV3DelegationContextAppend[];
|
|
81
|
+
export declare function encodeCodexRealtimeV3SessionContextAppend(input: {
|
|
82
|
+
text: string;
|
|
83
|
+
channel?: CodexRealtimeV3ContextAppendChannel | undefined;
|
|
84
|
+
}): CodexRealtimeV3SessionContextAppend[];
|
|
85
|
+
export declare function contextAppendChunks(text: string): string[];
|
|
86
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CODEX_REALTIME_CONTEXT_APPEND_MAX_BYTES,
|
|
3
|
+
CODEX_REALTIME_INITIAL_ITEMS_MAX_COUNT,
|
|
4
|
+
CODEX_REALTIME_INITIAL_ITEMS_MAX_TOKENS,
|
|
5
|
+
CODEX_REALTIME_V3_MAX_EVENT_BYTES,
|
|
6
|
+
CODEX_REALTIME_V3_MAX_IDENTIFIER_BYTES,
|
|
7
|
+
CODEX_REALTIME_V3_MAX_TEXT_BYTES,
|
|
8
|
+
contextAppendChunks,
|
|
9
|
+
encodeCodexRealtimeV3DelegationContextAppend,
|
|
10
|
+
encodeCodexRealtimeV3SessionContextAppend,
|
|
11
|
+
parseCodexRealtimeV3Event
|
|
12
|
+
} from "./chunk-MFVGCR3N.js";
|
|
13
|
+
export {
|
|
14
|
+
CODEX_REALTIME_CONTEXT_APPEND_MAX_BYTES,
|
|
15
|
+
CODEX_REALTIME_INITIAL_ITEMS_MAX_COUNT,
|
|
16
|
+
CODEX_REALTIME_INITIAL_ITEMS_MAX_TOKENS,
|
|
17
|
+
CODEX_REALTIME_V3_MAX_EVENT_BYTES,
|
|
18
|
+
CODEX_REALTIME_V3_MAX_IDENTIFIER_BYTES,
|
|
19
|
+
CODEX_REALTIME_V3_MAX_TEXT_BYTES,
|
|
20
|
+
contextAppendChunks,
|
|
21
|
+
encodeCodexRealtimeV3DelegationContextAppend,
|
|
22
|
+
encodeCodexRealtimeV3SessionContextAppend,
|
|
23
|
+
parseCodexRealtimeV3Event
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=realtime-v3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type CodexAuthHeaders } from "./api-client";
|
|
2
|
+
import { CODEX_REALTIME_MODEL, CODEX_REALTIME_VERSION } from "./constants";
|
|
3
|
+
import type { CodexFetch } from "./device-code";
|
|
4
|
+
import { type CodexRealtimeInitialItem } from "./realtime-v3";
|
|
5
|
+
export declare const CODEX_REALTIME_VOICES: readonly ["juniper", "maple", "spruce", "ember", "vale", "breeze", "arbor", "sol", "cove"];
|
|
6
|
+
export type CodexRealtimeVoice = (typeof CODEX_REALTIME_VOICES)[number];
|
|
7
|
+
export type CodexRealtimeCallInput = {
|
|
8
|
+
/** Browser-created WebRTC offer. It must negotiate an audio media section. */
|
|
9
|
+
sdp: string;
|
|
10
|
+
/** This transport intentionally supports only Codex's Frameless/V3 protocol. */
|
|
11
|
+
version: typeof CODEX_REALTIME_VERSION;
|
|
12
|
+
/** Server-owned session/thread binding; sent upstream but never returned. */
|
|
13
|
+
sessionId: string;
|
|
14
|
+
/** Server-projected ordinary-session history for Frameless V3 bootstrap. */
|
|
15
|
+
initialItems?: CodexRealtimeInitialItem[] | undefined;
|
|
16
|
+
instructions?: string | undefined;
|
|
17
|
+
voice?: CodexRealtimeVoice | undefined;
|
|
18
|
+
};
|
|
19
|
+
export type CodexRealtimeCallResult = {
|
|
20
|
+
sdp: string;
|
|
21
|
+
version: typeof CODEX_REALTIME_VERSION;
|
|
22
|
+
model: typeof CODEX_REALTIME_MODEL;
|
|
23
|
+
};
|
|
24
|
+
export type CodexRealtimeErrorCode = "invalid_request" | "incompatible" | "authentication" | "entitlement" | "rate_limited" | "provider" | "invalid_response" | "network" | "timeout" | "cancelled";
|
|
25
|
+
/** Safe provider failure: it contains no response body, credential, or account identity. */
|
|
26
|
+
export declare class CodexRealtimeError extends Error {
|
|
27
|
+
readonly code: CodexRealtimeErrorCode;
|
|
28
|
+
readonly providerStatus: number | null;
|
|
29
|
+
constructor(code: CodexRealtimeErrorCode, message: string, providerStatus?: number | null);
|
|
30
|
+
}
|
|
31
|
+
export type CodexRealtimeCallOptions = {
|
|
32
|
+
signal?: AbortSignal | undefined;
|
|
33
|
+
timeoutMs?: number | undefined;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Create one native subscription-authenticated Codex GPT-Live V3 WebRTC call.
|
|
37
|
+
*
|
|
38
|
+
* There is deliberately no API-key or WebSocket fallback and no transport
|
|
39
|
+
* retry. A caller may refresh the same connected subscription after one 401,
|
|
40
|
+
* but this adapter always performs exactly one provider request.
|
|
41
|
+
*/
|
|
42
|
+
export declare function createCodexRealtimeCall(auth: CodexAuthHeaders, input: CodexRealtimeCallInput, fetchImpl?: CodexFetch, options?: CodexRealtimeCallOptions): Promise<CodexRealtimeCallResult>;
|
|
43
|
+
/** Shared pin→active selection for worker turns and direct realtime calls. */
|
|
44
|
+
export declare function selectCodexCredentialId(args: {
|
|
45
|
+
sessionPinnedCredentialId: string | null;
|
|
46
|
+
activeCredentialId: string | null;
|
|
47
|
+
connectedIds: ReadonlySet<string>;
|
|
48
|
+
}): string | null;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { CodexFetch } from "./device-code";
|
|
2
|
+
/** Permanent — the workspace must reconnect (status => needs_relogin). */
|
|
3
|
+
export declare class CodexReloginRequired extends Error {
|
|
4
|
+
constructor(message: string);
|
|
5
|
+
}
|
|
6
|
+
/** Transient — safe to retry later. */
|
|
7
|
+
export declare class CodexRefreshTransient extends Error {
|
|
8
|
+
constructor(message: string);
|
|
9
|
+
}
|
|
10
|
+
/** Only present fields are returned (the server may rotate any subset). */
|
|
11
|
+
export type CodexRefreshTokens = {
|
|
12
|
+
idToken?: string | undefined;
|
|
13
|
+
accessToken?: string | undefined;
|
|
14
|
+
refreshToken?: string | undefined;
|
|
15
|
+
};
|
|
16
|
+
/** POST {issuer}/oauth/token JSON {client_id, grant_type:"refresh_token", refresh_token}. manager.rs:1336-1340 */
|
|
17
|
+
export declare function refreshCodexToken(refreshToken: string, fetchImpl?: CodexFetch, timeoutMs?: number): Promise<CodexRefreshTokens>;
|
|
18
|
+
/** Decode a JWT payload (base64url, no signature check). */
|
|
19
|
+
export declare function decodeJwtPayload(jwt: string): Record<string, unknown> | null;
|
|
20
|
+
/** access-token `exp` claim -> Date | null. token_data.rs:101-105 */
|
|
21
|
+
export declare function accessTokenExpiry(accessToken: string): Date | null;
|
|
22
|
+
/** id_token -> {chatgptAccountId, planType, isFedramp}. server.rs:827-832; token_data.rs:71-99 */
|
|
23
|
+
export declare function parseIdToken(idToken: string): {
|
|
24
|
+
chatgptAccountId: string | null;
|
|
25
|
+
planType: string | null;
|
|
26
|
+
isFedramp: boolean;
|
|
27
|
+
email: string | null;
|
|
28
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
export type CodexTokenSnapshot = {
|
|
3
|
+
accessToken: string;
|
|
4
|
+
chatgptAccountId: string | null;
|
|
5
|
+
isFedramp: boolean;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Multi-account P4 (Part A): a full usage snapshot scraped FOR FREE from the
|
|
9
|
+
* `x-codex-primary-*` / `x-codex-secondary-*` response headers the codex backend
|
|
10
|
+
* stamps on every `/codex/responses` turn (success AND 429 hard-cap). Integer-
|
|
11
|
+
* identical to GET /wham/usage but with zero extra round-trip. parseCodexUsageHeaders
|
|
12
|
+
* returns this only when BOTH windows parse, so a write is always a full 5-column
|
|
13
|
+
* snapshot (no partial-window clobber). Shape mirrors db's CodexAccountUsageSnapshot
|
|
14
|
+
* (non-null here: a partial read is filtered to null upstream, never half-written).
|
|
15
|
+
*/
|
|
16
|
+
export type CodexUsageHeaderSnapshot = {
|
|
17
|
+
primaryUsedPercent: number;
|
|
18
|
+
primaryResetAt: Date;
|
|
19
|
+
secondaryUsedPercent: number;
|
|
20
|
+
secondaryResetAt: Date;
|
|
21
|
+
checkedAt: Date;
|
|
22
|
+
};
|
|
23
|
+
export type CodexResponseTimeoutClass = "connect" | "headers" | "idle_stream" | "whole_request";
|
|
24
|
+
export type CodexResponseTimeoutPolicy = {
|
|
25
|
+
/** Maximum wait for response headers, including DNS/TCP/TLS establishment. */
|
|
26
|
+
headersTimeoutMs: number;
|
|
27
|
+
/** Maximum silence between response-body chunks after headers arrive. */
|
|
28
|
+
streamIdleTimeoutMs: number;
|
|
29
|
+
/** Maximum wall time for one logical Responses request. */
|
|
30
|
+
wholeRequestTimeoutMs: number;
|
|
31
|
+
/**
|
|
32
|
+
* Reserved compatibility field. It is currently normalized to zero because
|
|
33
|
+
* an absent response does not prove that the provider never accepted a
|
|
34
|
+
* request, so automatic replay is not safe without an operation receipt.
|
|
35
|
+
*/
|
|
36
|
+
noByteRetries: number;
|
|
37
|
+
retryBackoffMs: number;
|
|
38
|
+
};
|
|
39
|
+
export type CodexModelRequestEvent = {
|
|
40
|
+
requestId: string;
|
|
41
|
+
transportAttempt: number;
|
|
42
|
+
phase: "started" | "headers" | "first_byte" | "completed" | "failed" | "timed_out";
|
|
43
|
+
model?: string;
|
|
44
|
+
durationMs: number;
|
|
45
|
+
responseObserved: boolean;
|
|
46
|
+
timeoutPolicy: CodexResponseTimeoutPolicy;
|
|
47
|
+
timeoutClass?: CodexResponseTimeoutClass;
|
|
48
|
+
providerRequestId?: string;
|
|
49
|
+
status?: number;
|
|
50
|
+
willRetry?: boolean;
|
|
51
|
+
};
|
|
52
|
+
export type CodexRequestContext = {
|
|
53
|
+
clientVersion: string;
|
|
54
|
+
/**
|
|
55
|
+
* Stable per-session affinity id, sent as the `session_id` header on every
|
|
56
|
+
* request. This is the backend's STICKY CACHE-ROUTING key — measured
|
|
57
|
+
* 2026-07-12 with byte-identical ~99k-token gpt-5.6-sol requests on one idle
|
|
58
|
+
* account: without the header, repeat requests hit the prompt cache ~50% of
|
|
59
|
+
* the time (a per-request routing lottery across cache shards; matches the
|
|
60
|
+
* prod fleet's 48.6%); with a stable session_id, 10/10 requests hit at the
|
|
61
|
+
* 99.0% ceiling — Codex CLI parity (the CLI always sends it; its own last-3d
|
|
62
|
+
* token-weighted rate here is 94%). `prompt_cache_key` in the body only
|
|
63
|
+
* influences routing and does NOT pin it. Use the SAME value as
|
|
64
|
+
* prompt_cache_key (the OpenGeni sessionId) so routing and cache key agree.
|
|
65
|
+
*/
|
|
66
|
+
sessionId?: string;
|
|
67
|
+
/** Worker-supplied: proactive refresh + single-flight + db persist. */
|
|
68
|
+
getToken: () => Promise<CodexTokenSnapshot>;
|
|
69
|
+
/** Forced refresh used for the 401 retry. */
|
|
70
|
+
refresh: () => Promise<CodexTokenSnapshot>;
|
|
71
|
+
/** Model-slug resolver (longest-prefix against the live catalog). */
|
|
72
|
+
resolveModel: (slug: string) => string;
|
|
73
|
+
/**
|
|
74
|
+
* Multi-account P4 (Part A): fire-and-forget usage-header sink. Called by
|
|
75
|
+
* codexSubscriptionFetch on EVERY response (sync, non-throwing, never awaited)
|
|
76
|
+
* with the parsed full-window snapshot. The worker records the latest into the
|
|
77
|
+
* P2 usage cache once per turn in its `finally` — packages/codex stays db-free.
|
|
78
|
+
*/
|
|
79
|
+
onUsageHeaders?: (snapshot: CodexUsageHeaderSnapshot) => void;
|
|
80
|
+
/** Optional per-run override, primarily for deterministic transport tests. */
|
|
81
|
+
responseTimeoutPolicy?: Partial<CodexResponseTimeoutPolicy>;
|
|
82
|
+
/** Worker-owned durable audit sink; payloads never contain request bodies or auth. */
|
|
83
|
+
onModelRequestEvent?: (event: CodexModelRequestEvent) => Promise<void> | void;
|
|
84
|
+
/** Stable request identity supplied by the owning durable execution. */
|
|
85
|
+
nextRequestId?: () => string;
|
|
86
|
+
/**
|
|
87
|
+
* Optional Codex beta feature flags advertised as `x-codex-beta-features`
|
|
88
|
+
* (comma-separated). Used for remote compaction v2 (`remote_compaction_v2`).
|
|
89
|
+
*/
|
|
90
|
+
betaFeatures?: readonly string[];
|
|
91
|
+
/**
|
|
92
|
+
* Optional turn analytics / routing metadata sent as `x-codex-turn-metadata`
|
|
93
|
+
* (JSON). Body `metadata` is stripped by normalize — never put request_kind there.
|
|
94
|
+
*/
|
|
95
|
+
turnMetadata?: Record<string, unknown>;
|
|
96
|
+
};
|
|
97
|
+
export declare const codexRequestStorage: AsyncLocalStorage<CodexRequestContext>;
|
|
98
|
+
/** Nest a Codex ALS scope with header overrides (e.g. remote compaction v2). */
|
|
99
|
+
export declare function withCodexRequestOverrides<T>(overrides: Pick<CodexRequestContext, "betaFeatures" | "turnMetadata">, fn: () => T): T;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export declare const CODEX_RATE_LIMIT_RESET_OUTCOMES: readonly ["reset", "nothingToReset", "noCredit", "alreadyRedeemed"];
|
|
2
|
+
export type CodexRateLimitResetOutcome = (typeof CODEX_RATE_LIMIT_RESET_OUTCOMES)[number];
|
|
3
|
+
export type CodexRateLimitResetType = "codexRateLimits" | "unknown";
|
|
4
|
+
export type CodexRateLimitResetCreditStatus = "available" | "redeeming" | "redeemed" | "unknown";
|
|
5
|
+
export type CodexRateLimitResetCredit = {
|
|
6
|
+
id: string;
|
|
7
|
+
resetType: CodexRateLimitResetType;
|
|
8
|
+
status: CodexRateLimitResetCreditStatus;
|
|
9
|
+
/** Unix seconds, matching account/rateLimits/read in Codex v0.144.6. */
|
|
10
|
+
grantedAt: number;
|
|
11
|
+
/** Unix seconds, or null when the provider says the credit does not expire. */
|
|
12
|
+
expiresAt: number | null;
|
|
13
|
+
title: string | null;
|
|
14
|
+
description: string | null;
|
|
15
|
+
};
|
|
16
|
+
export type CodexRateLimitResetCreditsDetails = {
|
|
17
|
+
availableCount: number;
|
|
18
|
+
credits: CodexRateLimitResetCredit[];
|
|
19
|
+
};
|
|
20
|
+
export type CodexRateLimitResetCreditsSummary = {
|
|
21
|
+
availableCount: number;
|
|
22
|
+
/** null means the provider supplied an authoritative count but no detail rows. */
|
|
23
|
+
credits: null;
|
|
24
|
+
};
|
|
25
|
+
export type CodexRateLimitResetConsumeResponse = {
|
|
26
|
+
outcome: CodexRateLimitResetOutcome;
|
|
27
|
+
};
|
|
28
|
+
/** Parse the exact detailed-credit backend response. Unknown rows stay view-only. */
|
|
29
|
+
export declare function parseCodexRateLimitResetCreditsDetails(payload: unknown): CodexRateLimitResetCreditsDetails | null;
|
|
30
|
+
/** Parse the count-only summary carried by GET /wham/usage. */
|
|
31
|
+
export declare function parseCodexRateLimitResetCreditsSummary(payload: unknown): CodexRateLimitResetCreditsSummary | null;
|
|
32
|
+
/** Parse one of the exact four v0.144.6 consume outcomes. Unknowns fail closed. */
|
|
33
|
+
export declare function parseCodexRateLimitResetConsumeResponse(payload: unknown): CodexRateLimitResetConsumeResponse | null;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { CodexResponseTimeoutClass, CodexResponseTimeoutPolicy } from "./request-context";
|
|
2
|
+
export declare const CODEX_RESPONSE_TIMEOUT_ERROR_TYPE = "opengeni_codex_response_timeout";
|
|
3
|
+
export declare const DEFAULT_CODEX_RESPONSE_TIMEOUT_POLICY: CodexResponseTimeoutPolicy;
|
|
4
|
+
export declare function resolveCodexResponseTimeoutPolicy(override: Partial<CodexResponseTimeoutPolicy> | undefined): CodexResponseTimeoutPolicy;
|
|
5
|
+
export declare class CodexResponseTimeoutError extends Error {
|
|
6
|
+
readonly timeoutClass: CodexResponseTimeoutClass;
|
|
7
|
+
readonly requestId: string;
|
|
8
|
+
readonly responseObserved: boolean;
|
|
9
|
+
readonly code = "opengeni_codex_response_timeout";
|
|
10
|
+
readonly type = "opengeni_codex_response_timeout";
|
|
11
|
+
constructor(timeoutClass: CodexResponseTimeoutClass, requestId: string, responseObserved: boolean, message?: string);
|
|
12
|
+
}
|
|
13
|
+
export type CodexResponseTimeoutInfo = {
|
|
14
|
+
timeoutClass: CodexResponseTimeoutClass;
|
|
15
|
+
requestId: string | null;
|
|
16
|
+
responseObserved: boolean;
|
|
17
|
+
message: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Recover structured transport timeouts through SDK wrapping. The optional
|
|
21
|
+
* legacy match is deliberately opt-in: `Request timed out.` alone has no
|
|
22
|
+
* provider provenance and the worker enables it only for a confirmed Codex
|
|
23
|
+
* subscription turn.
|
|
24
|
+
*/
|
|
25
|
+
export declare function classifyCodexResponseTimeoutError(error: unknown, options?: {
|
|
26
|
+
allowLegacyRequestTimeout?: boolean;
|
|
27
|
+
}): CodexResponseTimeoutInfo | null;
|
|
28
|
+
export declare function isPreHeadersTimeoutError(error: unknown): CodexResponseTimeoutClass | null;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { type CodexRateLimitResetCreditsSummary } from "./reset-credits";
|
|
2
|
+
/** The 5-hour (primary) window's `limit_window_seconds`. */
|
|
3
|
+
export declare const CODEX_FIVE_HOUR_WINDOW_SECONDS = 18000;
|
|
4
|
+
/** The weekly (secondary) window's `limit_window_seconds`. */
|
|
5
|
+
export declare const CODEX_WEEKLY_WINDOW_SECONDS = 604800;
|
|
6
|
+
/** One normalized usage window (applied to BOTH primary_window and secondary_window). */
|
|
7
|
+
export type CodexUsageWindow = {
|
|
8
|
+
used: number;
|
|
9
|
+
limit: number;
|
|
10
|
+
remaining: number;
|
|
11
|
+
percent: number;
|
|
12
|
+
resetAt: string | null;
|
|
13
|
+
resetAfterSeconds: number | null;
|
|
14
|
+
limitWindowSeconds: number;
|
|
15
|
+
};
|
|
16
|
+
/** One additional (per-feature) limit (forward-compat; P2 renders nothing from it). */
|
|
17
|
+
export type CodexAdditionalLimit = {
|
|
18
|
+
limitName: string;
|
|
19
|
+
meteredFeature: string;
|
|
20
|
+
fiveHour: CodexUsageWindow | null;
|
|
21
|
+
weekly: CodexUsageWindow | null;
|
|
22
|
+
};
|
|
23
|
+
export type CodexUsageStatus = "ok" | "limit_reached" | "error" | "no-data";
|
|
24
|
+
/** The normalized usage payload — the P2/P3 contract. */
|
|
25
|
+
export type CodexUsagePayload = {
|
|
26
|
+
status: CodexUsageStatus;
|
|
27
|
+
planType: string | null;
|
|
28
|
+
fiveHour: CodexUsageWindow | null;
|
|
29
|
+
weekly: CodexUsageWindow | null;
|
|
30
|
+
limitReached: boolean;
|
|
31
|
+
fetchedAt: string;
|
|
32
|
+
/**
|
|
33
|
+
* Authoritative count-only reset-credit summary from the usage response.
|
|
34
|
+
* Detail rows are fetched separately and are never synthesized from this.
|
|
35
|
+
*/
|
|
36
|
+
rateLimitResetCredits: CodexRateLimitResetCreditsSummary | null;
|
|
37
|
+
/** Present only on a refresh/auth failure path; carries the precise reason. */
|
|
38
|
+
reason?: "needs_relogin" | undefined;
|
|
39
|
+
additionalLimits?: CodexAdditionalLimit[] | undefined;
|
|
40
|
+
credits?: {
|
|
41
|
+
hasCredits: boolean;
|
|
42
|
+
unlimited: boolean;
|
|
43
|
+
overageLimitReached: boolean;
|
|
44
|
+
balance: string;
|
|
45
|
+
} | undefined;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Build a normalized window from the PERSISTED cache columns (used_percent +
|
|
49
|
+
* absolute reset timestamp). The same 0–100 synthesis as the live path, with the
|
|
50
|
+
* skew-free countdown derived from `resetAt − now` at read time. Returns null when
|
|
51
|
+
* there is no cached percent yet. `limitWindowSeconds` is the constant that
|
|
52
|
+
* identifies the window (18000 ⇒ 5h, 604800 ⇒ weekly).
|
|
53
|
+
*/
|
|
54
|
+
export declare function buildCodexUsageWindowFromCache(usedPercent: number | null | undefined, resetAt: Date | string | null | undefined, limitWindowSeconds: number): CodexUsageWindow | null;
|
|
55
|
+
/**
|
|
56
|
+
* Normalize a /wham/usage fetch result into the P2/P3 contract.
|
|
57
|
+
*
|
|
58
|
+
* @param httpStatus the HTTP status from fetchCodexUsage (404 ⇒ a limit body)
|
|
59
|
+
* @param rawPayload the parsed JSON body (or null when the body was unreadable)
|
|
60
|
+
*/
|
|
61
|
+
export declare function normalizeCodexUsage(httpStatus: number, rawPayload: unknown): CodexUsagePayload;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/codex",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.10",
|
|
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": {
|
|
@@ -25,6 +25,10 @@
|
|
|
25
25
|
"./constants": {
|
|
26
26
|
"types": "./dist/constants.d.ts",
|
|
27
27
|
"import": "./dist/constants.js"
|
|
28
|
+
},
|
|
29
|
+
"./realtime-v3": {
|
|
30
|
+
"types": "./dist/realtime-v3.d.ts",
|
|
31
|
+
"import": "./dist/realtime-v3.js"
|
|
28
32
|
}
|
|
29
33
|
},
|
|
30
34
|
"publishConfig": {
|
|
@@ -32,8 +36,8 @@
|
|
|
32
36
|
"provenance": true
|
|
33
37
|
},
|
|
34
38
|
"scripts": {
|
|
35
|
-
"typecheck": "
|
|
36
|
-
"build": "
|
|
39
|
+
"typecheck": "tsc --noEmit",
|
|
40
|
+
"build": "bun ../../scripts/build-typescript-package.ts",
|
|
37
41
|
"prepublishOnly": "bash ../../scripts/prepublish-guard"
|
|
38
42
|
},
|
|
39
43
|
"dependencies": {
|
package/src/api-client.ts
CHANGED
|
@@ -28,7 +28,8 @@ export type ResetCreditFetchFailureReason =
|
|
|
28
28
|
| "network_error"
|
|
29
29
|
| "timeout";
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
/** Server-only headers shared by every ChatGPT/Codex subscription transport. */
|
|
32
|
+
export function codexSubscriptionHeaders(a: CodexAuthHeaders): Record<string, string> {
|
|
32
33
|
return {
|
|
33
34
|
Authorization: `Bearer ${a.accessToken}`,
|
|
34
35
|
...(a.chatgptAccountId ? { "ChatGPT-Account-ID": a.chatgptAccountId } : {}),
|
|
@@ -48,7 +49,7 @@ export async function fetchCodexModels(
|
|
|
48
49
|
const fetched = await runBoundedCodexOperation(async (signal) => {
|
|
49
50
|
const res = await fetchImpl(
|
|
50
51
|
`${CODEX_RESPONSES_BASE}/models?client_version=${encodeURIComponent(a.clientVersion)}`,
|
|
51
|
-
{ method: "GET", headers:
|
|
52
|
+
{ method: "GET", headers: codexSubscriptionHeaders(a), signal },
|
|
52
53
|
);
|
|
53
54
|
if (!res.ok) {
|
|
54
55
|
await res.arrayBuffer().catch(() => undefined);
|
|
@@ -72,7 +73,7 @@ export async function fetchCodexUsage(
|
|
|
72
73
|
const fetched = await runBoundedCodexOperation(async (signal) => {
|
|
73
74
|
const res = await fetchImpl(`${CODEX_WHAM_BASE}/wham/usage`, {
|
|
74
75
|
method: "GET",
|
|
75
|
-
headers:
|
|
76
|
+
headers: codexSubscriptionHeaders(a),
|
|
76
77
|
signal,
|
|
77
78
|
});
|
|
78
79
|
// A 404 may carry a usage-limit body; the route layer normalizes it to a limits state (spec §1.8c).
|
|
@@ -102,19 +103,27 @@ export async function fetchCodexRateLimitResetCredits(
|
|
|
102
103
|
const fetched = await runBoundedCodexOperation(async (signal) => {
|
|
103
104
|
const res = await fetchImpl(`${CODEX_WHAM_BASE}/wham/rate-limit-reset-credits`, {
|
|
104
105
|
method: "GET",
|
|
105
|
-
headers:
|
|
106
|
+
headers: codexSubscriptionHeaders(a),
|
|
106
107
|
signal,
|
|
107
108
|
});
|
|
108
109
|
if (!res.ok) {
|
|
109
110
|
// Drain the body without retaining/logging it. Provider error bodies may
|
|
110
111
|
// contain account-specific details and are not part of this contract.
|
|
111
112
|
await res.arrayBuffer().catch(() => undefined);
|
|
112
|
-
return {
|
|
113
|
+
return {
|
|
114
|
+
ok: false as const,
|
|
115
|
+
status: res.status,
|
|
116
|
+
reason: "http_error" as const,
|
|
117
|
+
};
|
|
113
118
|
}
|
|
114
119
|
const details = parseCodexRateLimitResetCreditsDetails(await res.json().catch(() => null));
|
|
115
120
|
return details
|
|
116
121
|
? { ok: true as const, status: res.status, details }
|
|
117
|
-
: {
|
|
122
|
+
: {
|
|
123
|
+
ok: false as const,
|
|
124
|
+
status: res.status,
|
|
125
|
+
reason: "invalid_response" as const,
|
|
126
|
+
};
|
|
118
127
|
}, timeoutMs);
|
|
119
128
|
return fetched.ok ? fetched.value : { ok: false, status: 0, reason: fetched.reason };
|
|
120
129
|
}
|
|
@@ -146,7 +155,7 @@ export async function consumeCodexRateLimitResetCredit(
|
|
|
146
155
|
const res = await fetchImpl(`${CODEX_WHAM_BASE}/wham/rate-limit-reset-credits/consume`, {
|
|
147
156
|
method: "POST",
|
|
148
157
|
headers: {
|
|
149
|
-
...
|
|
158
|
+
...codexSubscriptionHeaders(a),
|
|
150
159
|
"content-type": "application/json",
|
|
151
160
|
},
|
|
152
161
|
body: JSON.stringify({
|
|
@@ -157,12 +166,20 @@ export async function consumeCodexRateLimitResetCredit(
|
|
|
157
166
|
});
|
|
158
167
|
if (!res.ok) {
|
|
159
168
|
await res.arrayBuffer().catch(() => undefined);
|
|
160
|
-
return {
|
|
169
|
+
return {
|
|
170
|
+
ok: false as const,
|
|
171
|
+
status: res.status,
|
|
172
|
+
reason: "http_error" as const,
|
|
173
|
+
};
|
|
161
174
|
}
|
|
162
175
|
const result = parseCodexRateLimitResetConsumeResponse(await res.json().catch(() => null));
|
|
163
176
|
return result
|
|
164
177
|
? { ok: true as const, status: res.status, result }
|
|
165
|
-
: {
|
|
178
|
+
: {
|
|
179
|
+
ok: false as const,
|
|
180
|
+
status: res.status,
|
|
181
|
+
reason: "invalid_response" as const,
|
|
182
|
+
};
|
|
166
183
|
}, timeoutMs);
|
|
167
184
|
return fetched.ok ? fetched.value : { ok: false, status: 0, reason: fetched.reason };
|
|
168
185
|
}
|