@pure01fx/dsh-openai-codex-auth 0.5.0 → 0.6.1

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.
@@ -0,0 +1,189 @@
1
+ /** Bounded parsing for Codex subscription quota side channels. */
2
+ const MAX_TEXT_LENGTH = 128;
3
+ const MAX_LIMITS = 32;
4
+ const DEFAULT_LIMIT_ID = 'codex';
5
+ function boundedText(value) {
6
+ if (typeof value !== 'string')
7
+ return undefined;
8
+ const text = value.trim();
9
+ return text.length > 0 && text.length <= MAX_TEXT_LENGTH ? text : undefined;
10
+ }
11
+ function finiteNumber(value) {
12
+ const number = typeof value === 'number' ? value
13
+ : typeof value === 'string' && value.trim() !== '' ? Number(value) : undefined;
14
+ return number !== undefined && Number.isFinite(number) ? number : undefined;
15
+ }
16
+ function safeNonNegativeInteger(value) {
17
+ const number = finiteNumber(value);
18
+ return number !== undefined && Number.isSafeInteger(number) && number >= 0 ? number : undefined;
19
+ }
20
+ function optionalBoolean(value) {
21
+ if (typeof value === 'boolean')
22
+ return value;
23
+ if (value === '1' || (typeof value === 'string' && value.toLowerCase() === 'true'))
24
+ return true;
25
+ if (value === '0' || (typeof value === 'string' && value.toLowerCase() === 'false'))
26
+ return false;
27
+ return undefined;
28
+ }
29
+ function normalizedLimitId(value) {
30
+ if (value === undefined || value === null)
31
+ return DEFAULT_LIMIT_ID;
32
+ const text = boundedText(value);
33
+ return text?.toLowerCase().replaceAll('-', '_');
34
+ }
35
+ function rateLimitWindow(value, duration) {
36
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
37
+ return undefined;
38
+ const row = value;
39
+ const used = finiteNumber(row.used_percent ?? row.usedPercent);
40
+ if (used === undefined)
41
+ return undefined;
42
+ const rawWindow = duration === 'minutes'
43
+ ? safeNonNegativeInteger(row.window_minutes ?? row.windowMinutes)
44
+ : safeNonNegativeInteger(row.limit_window_seconds ?? row.windowDurationSecs);
45
+ const windowSeconds = rawWindow === undefined ? undefined
46
+ : duration === 'minutes' && rawWindow <= Math.floor(Number.MAX_SAFE_INTEGER / 60)
47
+ ? rawWindow * 60
48
+ : duration === 'seconds' ? rawWindow : undefined;
49
+ const resetAt = safeNonNegativeInteger(row.reset_at ?? row.resetsAt);
50
+ return {
51
+ usedPercent: Math.max(0, Math.min(100, used)),
52
+ ...windowSeconds === undefined ? {} : { windowSeconds },
53
+ ...resetAt === undefined ? {} : { resetAt },
54
+ };
55
+ }
56
+ export function parseCodexRateLimitCredits(value) {
57
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
58
+ return undefined;
59
+ const row = value;
60
+ const hasCredits = optionalBoolean(row.has_credits ?? row.hasCredits);
61
+ const unlimited = optionalBoolean(row.unlimited);
62
+ if (hasCredits === undefined || unlimited === undefined)
63
+ return undefined;
64
+ const balance = boundedText(row.balance);
65
+ return {
66
+ hasCredits,
67
+ unlimited,
68
+ ...balance === undefined ? {} : { balance },
69
+ };
70
+ }
71
+ /** Parse a `codex.rate_limits` WebSocket v2 event without trusting provider text. */
72
+ export function parseCodexRateLimitEvent(value) {
73
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
74
+ return undefined;
75
+ const event = value;
76
+ if (event.type !== 'codex.rate_limits')
77
+ return undefined;
78
+ const limits = event.rate_limits !== null && typeof event.rate_limits === 'object'
79
+ && !Array.isArray(event.rate_limits)
80
+ ? event.rate_limits : {};
81
+ const primary = limits.primary === null ? null : rateLimitWindow(limits.primary, 'minutes');
82
+ const secondary = limits.secondary === null ? null : rateLimitWindow(limits.secondary, 'minutes');
83
+ const parsedCredits = parseCodexRateLimitCredits(event.credits);
84
+ const planType = boundedText(event.plan_type ?? event.planType);
85
+ const explicitReached = optionalBoolean(limits.limit_reached ?? limits.limitReached);
86
+ const allowed = optionalBoolean(limits.allowed);
87
+ const derivedReached = primary === undefined && secondary === undefined
88
+ ? undefined : [primary, secondary].some(window => window?.usedPercent === 100);
89
+ const limitReached = explicitReached
90
+ ?? (allowed === undefined ? derivedReached : !allowed);
91
+ if (primary === undefined && secondary === undefined && parsedCredits === undefined
92
+ && planType === undefined && limitReached === undefined)
93
+ return undefined;
94
+ const rawLimitId = event.metered_limit_name ?? event.meteredLimitName
95
+ ?? event.limit_name ?? event.limitName;
96
+ const limitId = normalizedLimitId(rawLimitId);
97
+ if (limitId === undefined)
98
+ return undefined;
99
+ const limitName = boundedText(event.limit_name ?? event.limitName);
100
+ return {
101
+ limitId,
102
+ ...limitName === undefined ? {} : { limitName },
103
+ ...planType === undefined ? {} : { planType },
104
+ ...primary === undefined ? {} : { primary },
105
+ ...secondary === undefined ? {} : { secondary },
106
+ ...limitReached === undefined ? {} : { limitReached },
107
+ ...parsedCredits === undefined ? {} : { credits: parsedCredits },
108
+ };
109
+ }
110
+ function headerEntries(source) {
111
+ if (source instanceof Headers) {
112
+ const entries = [];
113
+ source.forEach((value, name) => { entries.push([name.toLowerCase(), value]); });
114
+ return entries;
115
+ }
116
+ return Object.entries(source).flatMap(([name, raw]) => {
117
+ const value = Array.isArray(raw) ? raw[0] : raw;
118
+ return typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean'
119
+ ? [[name.toLowerCase(), String(value)]] : [];
120
+ });
121
+ }
122
+ /** Parse every `x-<limit>-primary-*` quota header family on an HTTP or wrapped WS response. */
123
+ export function parseCodexRateLimitHeaders(source) {
124
+ if (source === undefined)
125
+ return [];
126
+ const headers = new Map(headerEntries(source));
127
+ const globalCredits = parseCodexRateLimitCredits({
128
+ has_credits: headers.get('x-codex-credits-has-credits'),
129
+ unlimited: headers.get('x-codex-credits-unlimited'),
130
+ balance: headers.get('x-codex-credits-balance'),
131
+ });
132
+ const prefixes = new Set();
133
+ for (const name of headers.keys()) {
134
+ for (const suffix of ['-primary-used-percent', '-secondary-used-percent']) {
135
+ if (name.startsWith('x-') && name.endsWith(suffix)) {
136
+ prefixes.add(name.slice(0, -suffix.length));
137
+ }
138
+ }
139
+ }
140
+ if (globalCredits !== undefined)
141
+ prefixes.add('x-codex');
142
+ const updates = [];
143
+ for (const prefix of [...prefixes].sort().slice(0, MAX_LIMITS)) {
144
+ const primary = rateLimitWindow({
145
+ used_percent: headers.get(`${prefix}-primary-used-percent`),
146
+ window_minutes: headers.get(`${prefix}-primary-window-minutes`),
147
+ reset_at: headers.get(`${prefix}-primary-reset-at`),
148
+ }, 'minutes');
149
+ const secondary = rateLimitWindow({
150
+ used_percent: headers.get(`${prefix}-secondary-used-percent`),
151
+ window_minutes: headers.get(`${prefix}-secondary-window-minutes`),
152
+ reset_at: headers.get(`${prefix}-secondary-reset-at`),
153
+ }, 'minutes');
154
+ const parsedCredits = prefix === 'x-codex' ? globalCredits : undefined;
155
+ if (primary === undefined && secondary === undefined && parsedCredits === undefined)
156
+ continue;
157
+ const limitId = normalizedLimitId(prefix.slice(2));
158
+ if (limitId === undefined)
159
+ continue;
160
+ const limitName = boundedText(headers.get(`${prefix}-limit-name`));
161
+ const limitReached = primary === undefined && secondary === undefined
162
+ ? undefined : [primary, secondary].some(window => window?.usedPercent === 100);
163
+ updates.push({
164
+ limitId,
165
+ ...limitName === undefined ? {} : { limitName },
166
+ ...primary === undefined ? {} : { primary },
167
+ ...secondary === undefined ? {} : { secondary },
168
+ ...limitReached === undefined ? {} : { limitReached },
169
+ ...parsedCredits === undefined ? {} : { credits: parsedCredits },
170
+ });
171
+ }
172
+ return updates;
173
+ }
174
+ /** Publish optional quota metadata without letting diagnostics break a model stream. */
175
+ export function publishCodexRateLimits(accountId, updates, callback, warn) {
176
+ if (updates.length === 0 || callback === undefined)
177
+ return;
178
+ try {
179
+ callback({ accountId, updates });
180
+ }
181
+ catch {
182
+ try {
183
+ warn?.('native Codex rate-limit update could not be published');
184
+ }
185
+ catch {
186
+ // Quota diagnostics are observational and must never fail generation.
187
+ }
188
+ }
189
+ }
@@ -0,0 +1,49 @@
1
+ /** Bounded, versioned Codex Responses continuation state. */
2
+ import { type ContentBlock } from '@deepseek-ai/dsh-llm';
3
+ export declare const NATIVE_CODEX_REPLAY_KIND = "openai-codex-native.responses-replay";
4
+ export declare const NATIVE_CODEX_REPLAY_VERSION = 1;
5
+ export type NativeCodexReplayDescriptor = {
6
+ type: 'message';
7
+ id?: string;
8
+ blocks: number[];
9
+ } | {
10
+ type: 'reasoning';
11
+ id?: string;
12
+ blocks: number[];
13
+ encryptedContent?: string;
14
+ } | {
15
+ type: 'function_call';
16
+ id?: string;
17
+ block: number;
18
+ };
19
+ export interface NativeCodexReplayState {
20
+ kind: typeof NATIVE_CODEX_REPLAY_KIND;
21
+ version: typeof NATIVE_CODEX_REPLAY_VERSION;
22
+ provider: string;
23
+ model: string;
24
+ items: NativeCodexReplayDescriptor[];
25
+ }
26
+ export interface NativeCodexReplaySource {
27
+ provider: string;
28
+ model: string;
29
+ replayState: unknown;
30
+ }
31
+ /** Preserve only server item IDs that Codex itself would replay. */
32
+ export declare function replayableItemId(value: string | undefined): string | undefined;
33
+ /** True only for state emitted by this package; foreign adapters degrade to visible history. */
34
+ export declare function hasNativeCodexReplayKind(value: unknown): boolean;
35
+ /** Attempt-local bounded accumulator; no ciphertext can grow unchecked before completion. */
36
+ export declare class NativeCodexReplayCapture {
37
+ private readonly provider;
38
+ private readonly model;
39
+ private readonly descriptors;
40
+ private references;
41
+ private stateBytes;
42
+ constructor(provider: string, model: string);
43
+ add(item: NativeCodexReplayDescriptor): void;
44
+ finish(): NativeCodexReplayState | undefined;
45
+ }
46
+ /** Create state only for a successful response with completed replay descriptors. */
47
+ export declare function createNativeCodexReplayState(provider: string, model: string, items: readonly NativeCodexReplayDescriptor[]): NativeCodexReplayState | undefined;
48
+ /** Reconstruct provider items without duplicating durable visible block payloads in state. */
49
+ export declare function replayAssistantInput(content: readonly ContentBlock[], source: NativeCodexReplaySource): Record<string, unknown>[];
package/lib/replay.js ADDED
@@ -0,0 +1,240 @@
1
+ /** Bounded, versioned Codex Responses continuation state. */
2
+ import { LlmError } from '@deepseek-ai/dsh-llm';
3
+ export const NATIVE_CODEX_REPLAY_KIND = 'openai-codex-native.responses-replay';
4
+ export const NATIVE_CODEX_REPLAY_VERSION = 1;
5
+ const MAX_REPLAY_DESCRIPTORS = 128;
6
+ const MAX_REPLAY_BLOCK_REFS = 256;
7
+ const MAX_REPLAY_ITEM_ID_BYTES = 256;
8
+ const MAX_REPLAY_CIPHERTEXT_BYTES = 1024 * 1024;
9
+ const MAX_REPLAY_STATE_BYTES = 4 * 1024 * 1024;
10
+ function failure(message, code = 'INVALID_REPLAY_STATE') {
11
+ return new LlmError(message, code);
12
+ }
13
+ function object(value) {
14
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
15
+ ? value
16
+ : undefined;
17
+ }
18
+ function onlyKeys(row, keys) {
19
+ const allowed = new Set(keys);
20
+ return Object.keys(row).every(key => allowed.has(key));
21
+ }
22
+ function boundedString(value, maximum = 256) {
23
+ return typeof value === 'string' && value.length > 0
24
+ && Buffer.byteLength(value) <= maximum ? value : undefined;
25
+ }
26
+ /** Preserve only server item IDs that Codex itself would replay. */
27
+ export function replayableItemId(value) {
28
+ if (value === undefined)
29
+ return undefined;
30
+ if (Buffer.byteLength(value) > MAX_REPLAY_ITEM_ID_BYTES) {
31
+ throw failure('native Codex response item identity exceeded the replay limit', 'MALFORMED_RESPONSE');
32
+ }
33
+ const split = value.indexOf('_');
34
+ return split > 0 && split < value.length - 1 ? value : undefined;
35
+ }
36
+ function safeStateSize(value, code) {
37
+ let serialized;
38
+ try {
39
+ serialized = JSON.stringify(value);
40
+ }
41
+ catch {
42
+ throw failure('native Codex replay state is not lossless JSON', code);
43
+ }
44
+ if (Buffer.byteLength(serialized) > MAX_REPLAY_STATE_BYTES) {
45
+ throw failure('native Codex replay state exceeded the size limit', code);
46
+ }
47
+ }
48
+ function validateBlockArray(value) {
49
+ if (!Array.isArray(value))
50
+ return undefined;
51
+ const blocks = [];
52
+ for (const item of value) {
53
+ if (!Number.isSafeInteger(item) || Number(item) < 0)
54
+ return undefined;
55
+ blocks.push(Number(item));
56
+ }
57
+ return blocks;
58
+ }
59
+ function parseDescriptor(value) {
60
+ const row = object(value);
61
+ if (row === undefined || typeof row.type !== 'string') {
62
+ throw failure('native Codex replay descriptor is invalid');
63
+ }
64
+ const id = row.id === undefined ? undefined : boundedString(row.id, MAX_REPLAY_ITEM_ID_BYTES);
65
+ const split = id?.indexOf('_') ?? -1;
66
+ if (row.id !== undefined && (id === undefined || split <= 0 || split >= id.length - 1)) {
67
+ throw failure('native Codex replay item identity is invalid');
68
+ }
69
+ if (row.type === 'message') {
70
+ const blocks = validateBlockArray(row.blocks);
71
+ if (blocks === undefined || !onlyKeys(row, ['type', 'id', 'blocks'])) {
72
+ throw failure('native Codex message replay descriptor is invalid');
73
+ }
74
+ return { type: 'message', ...(id === undefined ? {} : { id }), blocks };
75
+ }
76
+ if (row.type === 'reasoning') {
77
+ const blocks = validateBlockArray(row.blocks);
78
+ const encryptedContent = row.encryptedContent === undefined
79
+ ? undefined : boundedString(row.encryptedContent, MAX_REPLAY_CIPHERTEXT_BYTES);
80
+ if (blocks === undefined
81
+ || (row.encryptedContent !== undefined && encryptedContent === undefined)
82
+ || !onlyKeys(row, ['type', 'id', 'blocks', 'encryptedContent'])) {
83
+ throw failure('native Codex reasoning replay descriptor is invalid');
84
+ }
85
+ return {
86
+ type: 'reasoning', ...(id === undefined ? {} : { id }), blocks,
87
+ ...(encryptedContent === undefined ? {} : { encryptedContent }),
88
+ };
89
+ }
90
+ if (row.type === 'function_call') {
91
+ if (!Number.isSafeInteger(row.block) || Number(row.block) < 0
92
+ || !onlyKeys(row, ['type', 'id', 'block'])) {
93
+ throw failure('native Codex function replay descriptor is invalid');
94
+ }
95
+ return { type: 'function_call', ...(id === undefined ? {} : { id }), block: Number(row.block) };
96
+ }
97
+ throw failure('native Codex replay descriptor type is unsupported');
98
+ }
99
+ /** True only for state emitted by this package; foreign adapters degrade to visible history. */
100
+ export function hasNativeCodexReplayKind(value) {
101
+ return object(value)?.kind === NATIVE_CODEX_REPLAY_KIND;
102
+ }
103
+ function parseState(value) {
104
+ safeStateSize(value, 'INVALID_REPLAY_STATE');
105
+ const row = object(value);
106
+ if (row === undefined || row.kind !== NATIVE_CODEX_REPLAY_KIND
107
+ || row.version !== NATIVE_CODEX_REPLAY_VERSION
108
+ || !onlyKeys(row, ['kind', 'version', 'provider', 'model', 'items'])) {
109
+ throw failure('native Codex replay state kind or version is invalid');
110
+ }
111
+ const provider = boundedString(row.provider);
112
+ const model = boundedString(row.model, 512);
113
+ if (provider === undefined || model === undefined || !Array.isArray(row.items)
114
+ || row.items.length === 0 || row.items.length > MAX_REPLAY_DESCRIPTORS) {
115
+ throw failure('native Codex replay state metadata is invalid');
116
+ }
117
+ const items = row.items.map(parseDescriptor);
118
+ const refs = items.reduce((total, item) => total + (item.type === 'function_call' ? 1 : item.blocks.length), 0);
119
+ if (refs > MAX_REPLAY_BLOCK_REFS)
120
+ throw failure('native Codex replay state has too many block references');
121
+ return {
122
+ kind: NATIVE_CODEX_REPLAY_KIND,
123
+ version: NATIVE_CODEX_REPLAY_VERSION,
124
+ provider,
125
+ model,
126
+ items,
127
+ };
128
+ }
129
+ /** Attempt-local bounded accumulator; no ciphertext can grow unchecked before completion. */
130
+ export class NativeCodexReplayCapture {
131
+ provider;
132
+ model;
133
+ descriptors = [];
134
+ references = 0;
135
+ stateBytes;
136
+ constructor(provider, model) {
137
+ this.provider = provider;
138
+ this.model = model;
139
+ this.stateBytes = Buffer.byteLength(JSON.stringify({
140
+ kind: NATIVE_CODEX_REPLAY_KIND,
141
+ version: NATIVE_CODEX_REPLAY_VERSION,
142
+ provider,
143
+ model,
144
+ items: [],
145
+ }));
146
+ }
147
+ add(item) {
148
+ if (this.descriptors.length >= MAX_REPLAY_DESCRIPTORS) {
149
+ throw failure('native Codex response has too many replay descriptors', 'MALFORMED_RESPONSE');
150
+ }
151
+ const addedReferences = item.type === 'function_call' ? 1 : item.blocks.length;
152
+ if (this.references + addedReferences > MAX_REPLAY_BLOCK_REFS) {
153
+ throw failure('native Codex response has too many replay block references', 'MALFORMED_RESPONSE');
154
+ }
155
+ if (item.type === 'reasoning' && item.encryptedContent !== undefined
156
+ && Buffer.byteLength(item.encryptedContent) > MAX_REPLAY_CIPHERTEXT_BYTES) {
157
+ throw failure('native Codex encrypted reasoning exceeded the replay limit', 'MALFORMED_RESPONSE');
158
+ }
159
+ const itemBytes = Buffer.byteLength(JSON.stringify(item));
160
+ const nextBytes = this.stateBytes + itemBytes + (this.descriptors.length === 0 ? 0 : 1);
161
+ if (nextBytes > MAX_REPLAY_STATE_BYTES) {
162
+ throw failure('native Codex replay state exceeded the size limit', 'REPLAY_STATE_TOO_LARGE');
163
+ }
164
+ this.descriptors.push(item);
165
+ this.references += addedReferences;
166
+ this.stateBytes = nextBytes;
167
+ }
168
+ finish() {
169
+ return createNativeCodexReplayState(this.provider, this.model, this.descriptors);
170
+ }
171
+ }
172
+ /** Create state only for a successful response with completed replay descriptors. */
173
+ export function createNativeCodexReplayState(provider, model, items) {
174
+ if (items.length === 0)
175
+ return undefined;
176
+ const state = {
177
+ kind: NATIVE_CODEX_REPLAY_KIND,
178
+ version: NATIVE_CODEX_REPLAY_VERSION,
179
+ provider,
180
+ model,
181
+ items: items.map(item => ({ ...item })),
182
+ };
183
+ safeStateSize(state, 'REPLAY_STATE_TOO_LARGE');
184
+ try {
185
+ return parseState(state);
186
+ }
187
+ catch {
188
+ throw failure('native Codex completed items cannot form replay state', 'MALFORMED_RESPONSE');
189
+ }
190
+ }
191
+ function blockAt(content, used, index, expected) {
192
+ const block = content[index];
193
+ if (block === undefined || block.type !== expected || used.has(index)) {
194
+ throw failure('native Codex replay block reference is invalid');
195
+ }
196
+ used.add(index);
197
+ return block;
198
+ }
199
+ /** Reconstruct provider items without duplicating durable visible block payloads in state. */
200
+ export function replayAssistantInput(content, source) {
201
+ const state = parseState(source.replayState);
202
+ if (state.provider !== source.provider || state.model !== source.model) {
203
+ throw failure('native Codex replay provenance does not match its assistant message');
204
+ }
205
+ const used = new Set();
206
+ const input = [];
207
+ for (const item of state.items) {
208
+ if (item.type === 'message') {
209
+ const parts = item.blocks.map((index) => {
210
+ const block = blockAt(content, used, index, 'text');
211
+ return { type: 'output_text', text: block.text };
212
+ });
213
+ input.push({
214
+ type: 'message', ...(item.id === undefined ? {} : { id: item.id }),
215
+ role: 'assistant', content: parts,
216
+ });
217
+ }
218
+ else if (item.type === 'reasoning') {
219
+ const summary = item.blocks.map((index) => {
220
+ const block = blockAt(content, used, index, 'reasoning');
221
+ return { type: 'summary_text', text: block.text };
222
+ });
223
+ input.push({
224
+ type: 'reasoning', ...(item.id === undefined ? {} : { id: item.id }), summary,
225
+ ...(item.encryptedContent === undefined ? {} : { encrypted_content: item.encryptedContent }),
226
+ });
227
+ }
228
+ else {
229
+ const block = blockAt(content, used, item.block, 'tool-call');
230
+ input.push({
231
+ type: 'function_call', ...(item.id === undefined ? {} : { id: item.id }),
232
+ call_id: String(block.id), name: block.name, arguments: block.arguments,
233
+ });
234
+ }
235
+ }
236
+ if (used.size !== content.length) {
237
+ throw failure('native Codex replay state does not cover every assistant block');
238
+ }
239
+ return input;
240
+ }
@@ -0,0 +1,14 @@
1
+ /** Bounded per-response usage metadata emitted by the Codex Responses API. */
2
+ export interface CodexResponseUsageMetadata {
3
+ /** Exact provider representation; never coerce this high-precision value to a number. */
4
+ amount: string;
5
+ }
6
+ export interface CodexResponseUsageObservation {
7
+ accountId: string;
8
+ metadata: CodexResponseUsageMetadata;
9
+ }
10
+ export type CodexResponseUsageCallback = (observation: CodexResponseUsageObservation) => void;
11
+ /** Parse only response.completed usage_metadata.amount and preserve its exact string value. */
12
+ export declare function parseCodexResponseUsageMetadata(value: unknown): CodexResponseUsageMetadata | undefined;
13
+ /** Publish optional response usage without allowing diagnostics to fail generation. */
14
+ export declare function publishCodexResponseUsage(accountId: string, metadata: CodexResponseUsageMetadata | undefined, callback: CodexResponseUsageCallback | undefined, warn: ((message: string) => void) | undefined): void;
@@ -0,0 +1,35 @@
1
+ /** Bounded per-response usage metadata emitted by the Codex Responses API. */
2
+ const MAX_AMOUNT_BYTES = 256;
3
+ function record(value) {
4
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
5
+ ? value : undefined;
6
+ }
7
+ /** Parse only response.completed usage_metadata.amount and preserve its exact string value. */
8
+ export function parseCodexResponseUsageMetadata(value) {
9
+ const event = record(value);
10
+ if (event?.type !== 'response.completed')
11
+ return undefined;
12
+ const response = record(event.response);
13
+ const metadata = record(response?.usage_metadata);
14
+ const amount = metadata?.amount;
15
+ if (typeof amount !== 'string' || amount.length === 0
16
+ || Buffer.byteLength(amount) > MAX_AMOUNT_BYTES)
17
+ return undefined;
18
+ return { amount };
19
+ }
20
+ /** Publish optional response usage without allowing diagnostics to fail generation. */
21
+ export function publishCodexResponseUsage(accountId, metadata, callback, warn) {
22
+ if (metadata === undefined || callback === undefined)
23
+ return;
24
+ try {
25
+ callback({ accountId, metadata });
26
+ }
27
+ catch {
28
+ try {
29
+ warn?.('native Codex response usage metadata could not be published');
30
+ }
31
+ catch {
32
+ // Per-response usage is observational and must never fail generation.
33
+ }
34
+ }
35
+ }
@@ -0,0 +1,126 @@
1
+ import { CallId, LlmError, type ContentBlock, type GenerateOptions, type StreamChunk, type TokenUsage, type ToolSchema } from '@deepseek-ai/dsh-llm';
2
+ import { type ParseSseOptions } from './sse.js';
3
+ import { type NativeCodexReplaySource } from './replay.js';
4
+ export declare const DEFAULT_CODEX_INSTRUCTIONS = "You are Codex, an AI coding agent. Help the user with software engineering tasks.";
5
+ export interface ResolvedImagePart {
6
+ type: 'image';
7
+ mediaType: string;
8
+ dataBase64: string;
9
+ }
10
+ export interface ResolvedToolResultPart {
11
+ type: 'tool-result';
12
+ toolCallId: CallId;
13
+ content: readonly ResolvedContentPart[];
14
+ isError?: boolean;
15
+ }
16
+ export type ResolvedContentPart = Exclude<ContentBlock, {
17
+ type: 'image' | 'tool-result';
18
+ }> | ResolvedImagePart | ResolvedToolResultPart;
19
+ export interface ResolvedMessage {
20
+ role: 'system' | 'user' | 'assistant';
21
+ content: readonly ResolvedContentPart[];
22
+ replaySource?: NativeCodexReplaySource;
23
+ }
24
+ export interface ResponsesRequestInput {
25
+ instructions?: string;
26
+ input: Record<string, unknown>[];
27
+ }
28
+ export declare function toResponsesTools(tools: readonly ToolSchema[]): Record<string, unknown>[];
29
+ /** Convert resolved DSH messages into Responses instructions and ordered input items. */
30
+ export declare function toResponsesInput(messages: readonly ResolvedMessage[], system?: string): ResponsesRequestInput;
31
+ /** Bound call ids while preserving every function call/result correlation. */
32
+ export declare function normalizeCodexCallIds(input: readonly Record<string, unknown>[]): Record<string, unknown>[];
33
+ export interface ResponsesRequestMode {
34
+ serviceTier?: 'priority';
35
+ }
36
+ /** Build the canonical Standard/Fast HTTP Responses body. */
37
+ export declare function codexRequestBody(options: GenerateOptions, messages: readonly ResolvedMessage[], mode?: ResponsesRequestMode): Record<string, unknown>;
38
+ export interface ResponsesUsage {
39
+ input_tokens: number;
40
+ output_tokens: number;
41
+ input_tokens_details?: {
42
+ cached_tokens?: number;
43
+ cache_write_tokens?: number;
44
+ };
45
+ output_tokens_details?: {
46
+ reasoning_tokens?: number;
47
+ };
48
+ }
49
+ /** Map provider totals to DSH's strict disjoint token counts. */
50
+ export declare function mapResponsesUsage(usage: ResponsesUsage): TokenUsage;
51
+ /** Classify in-band failure data without reflecting provider text. */
52
+ export declare function responsesFailure(code?: string, message?: string): LlmError;
53
+ interface ResponsesOutputItem {
54
+ type?: string;
55
+ id?: string;
56
+ call_id?: string;
57
+ name?: string;
58
+ arguments?: string;
59
+ encrypted_content?: string;
60
+ summary?: unknown[];
61
+ status?: string;
62
+ content?: Array<{
63
+ type?: string;
64
+ text?: string;
65
+ }>;
66
+ }
67
+ export interface ResponsesStreamEvent {
68
+ type: string;
69
+ item_id?: string;
70
+ output_index?: number;
71
+ content_index?: number;
72
+ summary_index?: number;
73
+ delta?: string;
74
+ text?: string;
75
+ call_id?: string;
76
+ name?: string;
77
+ item?: ResponsesOutputItem;
78
+ response?: {
79
+ status?: string;
80
+ usage?: ResponsesUsage;
81
+ error?: {
82
+ code?: string;
83
+ message?: string;
84
+ };
85
+ incomplete_details?: {
86
+ reason?: string;
87
+ };
88
+ };
89
+ code?: string;
90
+ message?: string;
91
+ }
92
+ export interface ResponsesReplayContext {
93
+ provider: string;
94
+ model: string;
95
+ }
96
+ /** Stateful, transport-free Responses event to DSH chunk translator. */
97
+ export declare class ResponsesStreamTranslator {
98
+ private readonly replayContext?;
99
+ private readonly blocks;
100
+ private readonly order;
101
+ private readonly replayCapture;
102
+ private nextIndex;
103
+ private sawToolCall;
104
+ terminated: boolean;
105
+ constructor(replayContext?: ResponsesReplayContext | undefined);
106
+ private open;
107
+ private close;
108
+ private closeItem;
109
+ private closeAll;
110
+ push(event: ResponsesStreamEvent): StreamChunk[];
111
+ endOfStream(): never;
112
+ }
113
+ export interface StreamResponsesOptions extends ParseSseOptions {
114
+ onMalformedEvent?: () => void;
115
+ onEvent?: (event: ResponsesStreamEvent) => void;
116
+ replayContext?: ResponsesReplayContext;
117
+ maxResponseBytes?: number;
118
+ maxResponseEvents?: number;
119
+ }
120
+ /** Validate one opaque sticky turn token before retaining or forwarding it. */
121
+ export declare function boundedCodexTurnState(value: unknown): string | undefined;
122
+ /** Extract the bounded sticky turn token from provider metadata/event shapes. */
123
+ export declare function codexResponseTurnState(event: ResponsesStreamEvent): string | undefined;
124
+ /** Consume framed SSE JSON into DSH chunks. */
125
+ export declare function streamResponses(stream: ReadableStream<Uint8Array>, options?: StreamResponsesOptions): AsyncGenerator<StreamChunk>;
126
+ export {};