@jecruz/merc-ai 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -7
- package/dist/env-api-keys.d.ts.map +1 -1
- package/dist/env-api-keys.js +1 -1
- package/dist/env-api-keys.js.map +1 -1
- package/dist/image-models.generated.d.ts +345 -0
- package/dist/image-models.generated.d.ts.map +1 -1
- package/dist/image-models.generated.js +348 -3
- package/dist/image-models.generated.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/models.d.ts.map +1 -1
- package/dist/models.generated.d.ts +15282 -3882
- package/dist/models.generated.d.ts.map +1 -1
- package/dist/models.generated.js +18368 -10964
- package/dist/models.generated.js.map +1 -1
- package/dist/models.js +4 -1
- package/dist/models.js.map +1 -1
- package/dist/providers/amazon-bedrock.d.ts.map +1 -1
- package/dist/providers/amazon-bedrock.js +188 -19
- package/dist/providers/amazon-bedrock.js.map +1 -1
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +101 -34
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/azure-openai-responses.d.ts +2 -0
- package/dist/providers/azure-openai-responses.d.ts.map +1 -1
- package/dist/providers/azure-openai-responses.js +8 -3
- package/dist/providers/azure-openai-responses.js.map +1 -1
- package/dist/providers/google-shared.d.ts +15 -4
- package/dist/providers/google-shared.d.ts.map +1 -1
- package/dist/providers/google-shared.js +80 -15
- package/dist/providers/google-shared.js.map +1 -1
- package/dist/providers/google-vertex.d.ts.map +1 -1
- package/dist/providers/google-vertex.js +23 -60
- package/dist/providers/google-vertex.js.map +1 -1
- package/dist/providers/google.d.ts.map +1 -1
- package/dist/providers/google.js +19 -68
- package/dist/providers/google.js.map +1 -1
- package/dist/providers/mistral.d.ts +1 -1
- package/dist/providers/mistral.d.ts.map +1 -1
- package/dist/providers/mistral.js +44 -60
- package/dist/providers/mistral.js.map +1 -1
- package/dist/providers/openai-codex-responses.d.ts.map +1 -1
- package/dist/providers/openai-codex-responses.js +250 -124
- package/dist/providers/openai-codex-responses.js.map +1 -1
- package/dist/providers/openai-completions.d.ts.map +1 -1
- package/dist/providers/openai-completions.js +94 -46
- package/dist/providers/openai-completions.js.map +1 -1
- package/dist/providers/openai-prompt-cache.d.ts +3 -0
- package/dist/providers/openai-prompt-cache.d.ts.map +1 -0
- package/dist/providers/openai-prompt-cache.js +10 -0
- package/dist/providers/openai-prompt-cache.js.map +1 -0
- package/dist/providers/openai-responses-shared.d.ts.map +1 -1
- package/dist/providers/openai-responses-shared.js +154 -211
- package/dist/providers/openai-responses-shared.js.map +1 -1
- package/dist/providers/openai-responses.d.ts.map +1 -1
- package/dist/providers/openai-responses.js +15 -9
- package/dist/providers/openai-responses.js.map +1 -1
- package/dist/providers/openrouter-images.d.ts.map +1 -1
- package/dist/providers/openrouter-images.js +5 -2
- package/dist/providers/openrouter-images.js.map +1 -1
- package/dist/providers/simple-options.d.ts +15 -3
- package/dist/providers/simple-options.d.ts.map +1 -1
- package/dist/providers/simple-options.js +36 -4
- package/dist/providers/simple-options.js.map +1 -1
- package/dist/types.d.ts +35 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/error-body.d.ts +5 -0
- package/dist/utils/error-body.d.ts.map +1 -1
- package/dist/utils/error-body.js +53 -5
- package/dist/utils/error-body.js.map +1 -1
- package/dist/utils/estimate.d.ts +22 -0
- package/dist/utils/estimate.d.ts.map +1 -0
- package/dist/utils/estimate.js +104 -0
- package/dist/utils/estimate.js.map +1 -0
- package/dist/utils/event-stream.d.ts.map +1 -1
- package/dist/utils/event-stream.js +25 -7
- package/dist/utils/event-stream.js.map +1 -1
- package/dist/utils/json-parse.d.ts +3 -0
- package/dist/utils/json-parse.d.ts.map +1 -1
- package/dist/utils/json-parse.js +15 -0
- package/dist/utils/json-parse.js.map +1 -1
- package/dist/utils/oauth/anthropic.d.ts.map +1 -1
- package/dist/utils/oauth/anthropic.js +16 -10
- package/dist/utils/oauth/anthropic.js.map +1 -1
- package/dist/utils/oauth/github-copilot.d.ts.map +1 -1
- package/dist/utils/oauth/github-copilot.js +57 -21
- package/dist/utils/oauth/github-copilot.js.map +1 -1
- package/dist/utils/oauth/openai-codex.d.ts.map +1 -1
- package/dist/utils/oauth/openai-codex.js +73 -16
- package/dist/utils/oauth/openai-codex.js.map +1 -1
- package/dist/utils/oauth/types.d.ts +2 -0
- package/dist/utils/oauth/types.d.ts.map +1 -1
- package/dist/utils/oauth/types.js.map +1 -1
- package/dist/utils/overflow.d.ts +10 -1
- package/dist/utils/overflow.d.ts.map +1 -1
- package/dist/utils/overflow.js +47 -7
- package/dist/utils/overflow.js.map +1 -1
- package/dist/utils/provider-env.d.ts.map +1 -1
- package/dist/utils/provider-env.js +45 -10
- package/dist/utils/provider-env.js.map +1 -1
- package/dist/utils/tool-call-id.d.ts +11 -0
- package/dist/utils/tool-call-id.d.ts.map +1 -0
- package/dist/utils/tool-call-id.js +96 -0
- package/dist/utils/tool-call-id.js.map +1 -0
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +9 -0
- package/dist/utils/validation.js.map +1 -1
- package/package.json +6 -3
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
// NEVER convert to top-level runtime imports - breaks browser/Vite builds (web-ui)
|
|
2
|
-
let _os = null;
|
|
3
1
|
const dynamicImport = (specifier) => import(specifier);
|
|
4
|
-
|
|
5
|
-
if (typeof process
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
2
|
+
function loadNodeOs() {
|
|
3
|
+
if (typeof process === "undefined" || !(process.versions?.node || process.versions?.bun)) {
|
|
4
|
+
return null;
|
|
5
|
+
}
|
|
6
|
+
return process.getBuiltinModule?.("node:os") ?? null;
|
|
9
7
|
}
|
|
8
|
+
// NEVER convert to top-level runtime imports - breaks browser/Vite builds (web-ui)
|
|
9
|
+
const _os = loadNodeOs();
|
|
10
|
+
import { v7 as uuidv7 } from "uuid";
|
|
10
11
|
import { getEnvApiKey } from "../env-api-keys.js";
|
|
11
12
|
import { clampThinkingLevel } from "../models.js";
|
|
12
13
|
import { registerSessionResourceCleanup } from "../session-resources.js";
|
|
13
14
|
import { splitDeferredTools } from "../utils/deferred-tools.js";
|
|
14
15
|
import { appendAssistantMessageDiagnostic, createAssistantMessageDiagnostic, formatThrownValue, } from "../utils/diagnostics.js";
|
|
15
|
-
import { formatProviderError, normalizeProviderError } from "../utils/error-body.js";
|
|
16
|
+
import { formatProviderError, isDeterministicProviderError, normalizeProviderError } from "../utils/error-body.js";
|
|
16
17
|
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
|
17
18
|
import { headersToRecord } from "../utils/headers.js";
|
|
18
19
|
import { resolveProviderProxyUrl } from "../utils/provider-env.js";
|
|
20
|
+
import { clampOpenAIPromptCacheKey } from "./openai-prompt-cache.js";
|
|
19
21
|
import { convertResponsesMessages, convertResponsesTools, processResponsesStream } from "./openai-responses-shared.js";
|
|
20
22
|
import { buildBaseOptions } from "./simple-options.js";
|
|
21
23
|
// ============================================================================
|
|
@@ -23,11 +25,14 @@ import { buildBaseOptions } from "./simple-options.js";
|
|
|
23
25
|
// ============================================================================
|
|
24
26
|
const DEFAULT_CODEX_BASE_URL = "https://chatgpt.com/backend-api";
|
|
25
27
|
const JWT_CLAIM_PATH = "https://api.openai.com/auth";
|
|
26
|
-
const DEFAULT_MAX_RETRIES =
|
|
28
|
+
const DEFAULT_MAX_RETRIES = 0;
|
|
27
29
|
const BASE_DELAY_MS = 1000;
|
|
30
|
+
const DEFAULT_MAX_RETRY_DELAY_MS = 60_000;
|
|
28
31
|
const DEFAULT_SSE_HEADER_TIMEOUT_MS = 20_000;
|
|
32
|
+
const DEFAULT_WEBSOCKET_CONNECT_TIMEOUT_MS = 15_000;
|
|
29
33
|
const CODEX_TOOL_CALL_PROVIDERS = new Set(["openai", "openai-codex", "opencode"]);
|
|
30
34
|
const WEBSOCKET_MESSAGE_TOO_BIG_CLOSE_CODE = 1009;
|
|
35
|
+
const WEBSOCKET_CONNECTION_LIMIT_REACHED_CODE = "websocket_connection_limit_reached";
|
|
31
36
|
const CODEX_RESPONSE_STATUSES = new Set([
|
|
32
37
|
"completed",
|
|
33
38
|
"incomplete",
|
|
@@ -36,6 +41,7 @@ const CODEX_RESPONSE_STATUSES = new Set([
|
|
|
36
41
|
"queued",
|
|
37
42
|
"in_progress",
|
|
38
43
|
]);
|
|
44
|
+
// Codex requests never carry max_output_tokens, so supportsMaxOutputTokens does not apply here.
|
|
39
45
|
function getCompat(model) {
|
|
40
46
|
return {
|
|
41
47
|
supportsDeveloperRole: model.compat?.supportsDeveloperRole ?? true,
|
|
@@ -47,17 +53,32 @@ function getCompat(model) {
|
|
|
47
53
|
function defaultSupportsToolSearch(model) {
|
|
48
54
|
if (model.provider !== "openai" && model.provider !== "openai-codex")
|
|
49
55
|
return false;
|
|
50
|
-
return /gpt-5\.
|
|
56
|
+
return /(?:^|\/)gpt-(?:5\.[456]|6)(?:[.-]|$)/.test(model.id);
|
|
51
57
|
}
|
|
52
58
|
// ============================================================================
|
|
53
59
|
// Retry Helpers
|
|
54
60
|
// ============================================================================
|
|
61
|
+
function isTerminalRateLimitError(errorText) {
|
|
62
|
+
return /GoUsageLimitError|FreeUsageLimitError|Monthly usage limit reached|available balance|insufficient_quota|out of budget|quota exceeded|billing/i.test(errorText);
|
|
63
|
+
}
|
|
55
64
|
function isRetryableError(status, errorText) {
|
|
65
|
+
if (status === 429 && isTerminalRateLimitError(errorText)) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
56
68
|
if (status === 429 || status === 500 || status === 502 || status === 503 || status === 504) {
|
|
57
69
|
return true;
|
|
58
70
|
}
|
|
59
71
|
return /rate.?limit|overloaded|service.?unavailable|upstream.?connect|connection.?refused/i.test(errorText);
|
|
60
72
|
}
|
|
73
|
+
class RetryDelayExceededError extends Error {
|
|
74
|
+
}
|
|
75
|
+
function validateRetryDelayMs(delayMs, options) {
|
|
76
|
+
const maxRetryDelayMs = options?.maxRetryDelayMs ?? DEFAULT_MAX_RETRY_DELAY_MS;
|
|
77
|
+
if (maxRetryDelayMs > 0 && delayMs > maxRetryDelayMs) {
|
|
78
|
+
throw new RetryDelayExceededError(`Server requested ${Math.ceil(delayMs / 1000)}s retry delay (max: ${Math.ceil(maxRetryDelayMs / 1000)}s)`);
|
|
79
|
+
}
|
|
80
|
+
return delayMs;
|
|
81
|
+
}
|
|
61
82
|
function sleep(ms, signal) {
|
|
62
83
|
return new Promise((resolve, reject) => {
|
|
63
84
|
if (signal?.aborted) {
|
|
@@ -93,6 +114,14 @@ function parseRetryAfterDelay(response) {
|
|
|
93
114
|
}
|
|
94
115
|
return undefined;
|
|
95
116
|
}
|
|
117
|
+
function normalizeTimeoutMs(value) {
|
|
118
|
+
if (value === undefined)
|
|
119
|
+
return undefined;
|
|
120
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
121
|
+
throw new Error(`Invalid timeoutMs: ${String(value)}`);
|
|
122
|
+
}
|
|
123
|
+
return Math.floor(value);
|
|
124
|
+
}
|
|
96
125
|
// ============================================================================
|
|
97
126
|
// Main Stream Function
|
|
98
127
|
// ============================================================================
|
|
@@ -122,58 +151,82 @@ export const streamOpenAICodexResponses = (model, context, options) => {
|
|
|
122
151
|
throw new Error(`No API key for provider: ${model.provider}`);
|
|
123
152
|
}
|
|
124
153
|
const accountId = extractAccountId(apiKey);
|
|
125
|
-
|
|
154
|
+
const cacheSessionId = options?.cacheRetention === "none" ? undefined : options?.sessionId;
|
|
155
|
+
let body = buildRequestBody(model, context, options, cacheSessionId);
|
|
126
156
|
const nextBody = await options?.onPayload?.(body, model);
|
|
127
157
|
if (nextBody !== undefined) {
|
|
128
158
|
body = nextBody;
|
|
129
159
|
}
|
|
130
|
-
const websocketRequestId =
|
|
131
|
-
const sseHeaders = buildSSEHeaders(model.headers, options?.headers, accountId, apiKey,
|
|
160
|
+
const websocketRequestId = cacheSessionId || uuidv7();
|
|
161
|
+
const sseHeaders = buildSSEHeaders(model.headers, options?.headers, accountId, apiKey, cacheSessionId);
|
|
132
162
|
const websocketHeaders = buildWebSocketHeaders(model.headers, options?.headers, accountId, apiKey, websocketRequestId);
|
|
133
163
|
const bodyJson = JSON.stringify(body);
|
|
164
|
+
const idleTimeoutMs = normalizeTimeoutMs(options?.timeoutMs);
|
|
165
|
+
const websocketConnectTimeoutMs = normalizeTimeoutMs(options?.websocketConnectTimeoutMs);
|
|
134
166
|
const transport = options?.transport || "auto";
|
|
135
|
-
const websocketDisabledForSession = transport !== "sse" && isWebSocketSseFallbackActive(
|
|
167
|
+
const websocketDisabledForSession = transport !== "sse" && isWebSocketSseFallbackActive(cacheSessionId);
|
|
136
168
|
if (websocketDisabledForSession) {
|
|
137
|
-
recordWebSocketSseFallback(
|
|
169
|
+
recordWebSocketSseFallback(cacheSessionId);
|
|
138
170
|
}
|
|
139
171
|
if (transport !== "sse") {
|
|
140
172
|
let websocketReceivedEvent = false;
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
173
|
+
let retriedWebSocketConnectionLimit = false;
|
|
174
|
+
let websocketStartPushed = false;
|
|
175
|
+
while (true) {
|
|
176
|
+
websocketReceivedEvent = false;
|
|
177
|
+
try {
|
|
178
|
+
await processWebSocketStream(resolveCodexWebSocketUrl(model.baseUrl), body, websocketHeaders, output, stream, model, () => {
|
|
179
|
+
if (websocketStartPushed)
|
|
180
|
+
return;
|
|
181
|
+
websocketStartPushed = true;
|
|
182
|
+
stream.push({ type: "start", partial: output });
|
|
183
|
+
}, () => {
|
|
184
|
+
websocketReceivedEvent = true;
|
|
185
|
+
}, cacheSessionId, idleTimeoutMs, websocketConnectTimeoutMs, options);
|
|
186
|
+
if (options?.signal?.aborted) {
|
|
187
|
+
throw new Error("Request was aborted");
|
|
188
|
+
}
|
|
189
|
+
stream.push({
|
|
190
|
+
type: "done",
|
|
191
|
+
reason: output.stopReason,
|
|
192
|
+
message: output,
|
|
193
|
+
});
|
|
194
|
+
stream.end();
|
|
195
|
+
return;
|
|
164
196
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
197
|
+
catch (error) {
|
|
198
|
+
// ChatGPT rejects a new WebSocket with websocket_connection_limit_reached when the
|
|
199
|
+
// account already holds its maximum number of connections. Nothing was streamed yet,
|
|
200
|
+
// so reconnect once instead of surfacing the error or dropping to SSE.
|
|
201
|
+
if (!options?.signal?.aborted &&
|
|
202
|
+
!websocketReceivedEvent &&
|
|
203
|
+
!retriedWebSocketConnectionLimit &&
|
|
204
|
+
isWebSocketConnectionLimitReachedError(error)) {
|
|
205
|
+
retriedWebSocketConnectionLimit = true;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
// In auto mode, ChatGPT's Codex WebSocket can occasionally close
|
|
209
|
+
// abnormally (1006) after `response.create` is sent but before any
|
|
210
|
+
// response event arrives. That is still safe to retry over SSE using
|
|
211
|
+
// the original full request body. Once any response event has arrived,
|
|
212
|
+
// do not retry: replaying could duplicate partial assistant/tool output.
|
|
213
|
+
if (transport === "websocket" || transport === "websocket-cached" || websocketReceivedEvent) {
|
|
214
|
+
throw error;
|
|
215
|
+
}
|
|
216
|
+
appendAssistantMessageDiagnostic(output, createAssistantMessageDiagnostic("provider_transport_failure", error, {
|
|
217
|
+
configuredTransport: transport,
|
|
218
|
+
fallbackTransport: websocketReceivedEvent ? undefined : "sse",
|
|
219
|
+
eventsEmitted: websocketReceivedEvent,
|
|
220
|
+
phase: websocketReceivedEvent ? "after_message_stream_start" : "before_message_stream_start",
|
|
221
|
+
requestBytes: new TextEncoder().encode(bodyJson).byteLength,
|
|
222
|
+
}));
|
|
223
|
+
recordWebSocketFailure(cacheSessionId, error);
|
|
224
|
+
if (websocketReceivedEvent) {
|
|
225
|
+
throw error;
|
|
226
|
+
}
|
|
227
|
+
recordWebSocketSseFallback(cacheSessionId);
|
|
228
|
+
break;
|
|
175
229
|
}
|
|
176
|
-
recordWebSocketSseFallback(options?.sessionId);
|
|
177
230
|
}
|
|
178
231
|
}
|
|
179
232
|
// Fetch with retry logic for rate limits and transient errors
|
|
@@ -186,10 +239,11 @@ export const streamOpenAICodexResponses = (model, context, options) => {
|
|
|
186
239
|
}
|
|
187
240
|
const headerTimeoutController = new AbortController();
|
|
188
241
|
let timedOut = false;
|
|
242
|
+
const headerTimeoutMs = options?.timeoutMs ?? DEFAULT_SSE_HEADER_TIMEOUT_MS;
|
|
189
243
|
const headerTimeout = setTimeout(() => {
|
|
190
244
|
timedOut = true;
|
|
191
245
|
headerTimeoutController.abort();
|
|
192
|
-
},
|
|
246
|
+
}, headerTimeoutMs);
|
|
193
247
|
const onAbort = () => {
|
|
194
248
|
headerTimeoutController.abort();
|
|
195
249
|
};
|
|
@@ -206,22 +260,25 @@ export const streamOpenAICodexResponses = (model, context, options) => {
|
|
|
206
260
|
break;
|
|
207
261
|
}
|
|
208
262
|
const errorText = await response.text();
|
|
209
|
-
|
|
210
|
-
|
|
263
|
+
const info = await parseErrorResponse(new Response(errorText, {
|
|
264
|
+
status: response.status,
|
|
265
|
+
statusText: response.statusText,
|
|
266
|
+
}));
|
|
267
|
+
if (attempt < maxRetries &&
|
|
268
|
+
!isDeterministicProviderError(info.errorMetadata) &&
|
|
269
|
+
isRetryableError(response.status, errorText)) {
|
|
270
|
+
const retryAfterDelayMs = parseRetryAfterDelay(response);
|
|
271
|
+
const delayMs = retryAfterDelayMs === undefined
|
|
272
|
+
? BASE_DELAY_MS * 2 ** attempt
|
|
273
|
+
: validateRetryDelayMs(retryAfterDelayMs, options);
|
|
211
274
|
await sleep(delayMs, options?.signal);
|
|
212
275
|
continue;
|
|
213
276
|
}
|
|
214
|
-
|
|
215
|
-
const fakeResponse = new Response(errorText, {
|
|
216
|
-
status: response.status,
|
|
217
|
-
statusText: response.statusText,
|
|
218
|
-
});
|
|
219
|
-
const info = await parseErrorResponse(fakeResponse);
|
|
220
|
-
throw new Error(info.friendlyMessage || info.message);
|
|
277
|
+
throw new CodexApiError(info.friendlyMessage || info.message, info.errorMetadata);
|
|
221
278
|
}
|
|
222
279
|
catch (error) {
|
|
223
280
|
if (timedOut && !options?.signal?.aborted) {
|
|
224
|
-
throw new Error(`Codex SSE response headers timed out after ${
|
|
281
|
+
throw new Error(`Codex SSE response headers timed out after ${headerTimeoutMs}ms`);
|
|
225
282
|
}
|
|
226
283
|
if (error instanceof Error) {
|
|
227
284
|
if (error.name === "AbortError" || error.message === "Request was aborted") {
|
|
@@ -230,7 +287,10 @@ export const streamOpenAICodexResponses = (model, context, options) => {
|
|
|
230
287
|
}
|
|
231
288
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
232
289
|
// Network errors are retryable
|
|
233
|
-
if (attempt < maxRetries &&
|
|
290
|
+
if (attempt < maxRetries &&
|
|
291
|
+
!(lastError instanceof RetryDelayExceededError) &&
|
|
292
|
+
!isDeterministicProviderError(normalizeProviderError(lastError).errorMetadata) &&
|
|
293
|
+
!lastError.message.includes("usage limit")) {
|
|
234
294
|
const delayMs = BASE_DELAY_MS * 2 ** attempt;
|
|
235
295
|
await sleep(delayMs, options?.signal);
|
|
236
296
|
continue;
|
|
@@ -262,7 +322,9 @@ export const streamOpenAICodexResponses = (model, context, options) => {
|
|
|
262
322
|
delete block.partialJson;
|
|
263
323
|
}
|
|
264
324
|
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
265
|
-
|
|
325
|
+
const normalized = normalizeProviderError(error);
|
|
326
|
+
output.errorMetadata = normalized.errorMetadata;
|
|
327
|
+
output.errorMessage = formatProviderError(normalized);
|
|
266
328
|
stream.push({ type: "error", reason: output.stopReason, error: output });
|
|
267
329
|
stream.end();
|
|
268
330
|
}
|
|
@@ -274,7 +336,7 @@ export const streamSimpleOpenAICodexResponses = (model, context, options) => {
|
|
|
274
336
|
if (!apiKey) {
|
|
275
337
|
throw new Error(`No API key for provider: ${model.provider}`);
|
|
276
338
|
}
|
|
277
|
-
const base = buildBaseOptions(model, options, apiKey);
|
|
339
|
+
const base = buildBaseOptions(model, context, options, apiKey);
|
|
278
340
|
const clampedReasoning = options?.reasoning ? clampThinkingLevel(model, options.reasoning) : undefined;
|
|
279
341
|
const reasoningEffort = clampedReasoning === "off" ? undefined : clampedReasoning;
|
|
280
342
|
return streamOpenAICodexResponses(model, context, {
|
|
@@ -285,7 +347,7 @@ export const streamSimpleOpenAICodexResponses = (model, context, options) => {
|
|
|
285
347
|
// ============================================================================
|
|
286
348
|
// Request Building
|
|
287
349
|
// ============================================================================
|
|
288
|
-
function buildRequestBody(model, context, options) {
|
|
350
|
+
function buildRequestBody(model, context, options, cacheSessionId) {
|
|
289
351
|
const compat = getCompat(model);
|
|
290
352
|
const toolPlacement = splitDeferredTools(context, compat.supportsToolSearch);
|
|
291
353
|
const messages = convertResponsesMessages(model, context, CODEX_TOOL_CALL_PROVIDERS, {
|
|
@@ -300,7 +362,7 @@ function buildRequestBody(model, context, options) {
|
|
|
300
362
|
input: messages,
|
|
301
363
|
text: { verbosity: options?.textVerbosity || "low" },
|
|
302
364
|
include: ["reasoning.encrypted_content"],
|
|
303
|
-
prompt_cache_key:
|
|
365
|
+
prompt_cache_key: clampOpenAIPromptCacheKey(cacheSessionId),
|
|
304
366
|
tool_choice: options?.toolChoice ?? "auto",
|
|
305
367
|
parallel_tool_calls: true,
|
|
306
368
|
};
|
|
@@ -315,7 +377,9 @@ function buildRequestBody(model, context, options) {
|
|
|
315
377
|
}
|
|
316
378
|
if (options?.reasoningEffort !== undefined) {
|
|
317
379
|
const effort = options.reasoningEffort === "none"
|
|
318
|
-
?
|
|
380
|
+
? model.thinkingLevelMap?.off === undefined
|
|
381
|
+
? "none"
|
|
382
|
+
: model.thinkingLevelMap.off
|
|
319
383
|
: (model.thinkingLevelMap?.[options.reasoningEffort] ?? options.reasoningEffort);
|
|
320
384
|
if (effort !== null) {
|
|
321
385
|
body.reasoning = {
|
|
@@ -324,6 +388,9 @@ function buildRequestBody(model, context, options) {
|
|
|
324
388
|
};
|
|
325
389
|
}
|
|
326
390
|
}
|
|
391
|
+
else if (model.reasoning && model.thinkingLevelMap?.off !== null) {
|
|
392
|
+
body.reasoning = { effort: model.thinkingLevelMap?.off ?? "none" };
|
|
393
|
+
}
|
|
327
394
|
return body;
|
|
328
395
|
}
|
|
329
396
|
function getServiceTierCostMultiplier(model, serviceTier) {
|
|
@@ -381,11 +448,13 @@ async function processStream(response, output, stream, model, options) {
|
|
|
381
448
|
}
|
|
382
449
|
class CodexApiError extends Error {
|
|
383
450
|
code;
|
|
451
|
+
type;
|
|
384
452
|
payload;
|
|
385
453
|
constructor(message, options) {
|
|
386
454
|
super(message);
|
|
387
455
|
this.name = "CodexApiError";
|
|
388
456
|
this.code = options?.code;
|
|
457
|
+
this.type = options?.type;
|
|
389
458
|
this.payload = options?.payload;
|
|
390
459
|
this.cause = options?.cause;
|
|
391
460
|
}
|
|
@@ -402,24 +471,43 @@ class CodexProtocolError extends Error {
|
|
|
402
471
|
function _isCodexNonTransportError(error) {
|
|
403
472
|
return error instanceof CodexApiError || error instanceof CodexProtocolError;
|
|
404
473
|
}
|
|
474
|
+
function isWebSocketConnectionLimitReachedError(error) {
|
|
475
|
+
return error instanceof CodexApiError && error.code === WEBSOCKET_CONNECTION_LIMIT_REACHED_CODE;
|
|
476
|
+
}
|
|
477
|
+
function isWebSocketConnectionLimitReachedEvent(event) {
|
|
478
|
+
return event.type === "error" && extractCodexEventError(event).code === WEBSOCKET_CONNECTION_LIMIT_REACHED_CODE;
|
|
479
|
+
}
|
|
480
|
+
function extractCodexEventError(event) {
|
|
481
|
+
const nested = event.error && typeof event.error === "object" ? event.error : undefined;
|
|
482
|
+
return {
|
|
483
|
+
...normalizeProviderError({ code: event.code, error: nested }).errorMetadata,
|
|
484
|
+
message: typeof event.message === "string"
|
|
485
|
+
? event.message
|
|
486
|
+
: typeof nested?.message === "string"
|
|
487
|
+
? nested.message
|
|
488
|
+
: undefined,
|
|
489
|
+
};
|
|
490
|
+
}
|
|
405
491
|
async function* mapCodexEvents(events) {
|
|
406
492
|
for await (const event of events) {
|
|
407
493
|
const type = typeof event.type === "string" ? event.type : undefined;
|
|
408
494
|
if (!type)
|
|
409
495
|
continue;
|
|
410
496
|
if (type === "error") {
|
|
411
|
-
const code = event
|
|
412
|
-
const message = event.message || "";
|
|
497
|
+
const { code, type: errorType, message } = extractCodexEventError(event);
|
|
413
498
|
throw new CodexApiError(`Codex error: ${message || code || JSON.stringify(event)}`, {
|
|
414
|
-
code
|
|
499
|
+
code,
|
|
500
|
+
type: errorType,
|
|
415
501
|
payload: event,
|
|
416
502
|
});
|
|
417
503
|
}
|
|
418
504
|
if (type === "response.failed") {
|
|
419
505
|
const response = event.response;
|
|
420
|
-
const code = response?.error?.code;
|
|
421
506
|
const message = response?.error?.message;
|
|
422
|
-
throw new CodexApiError(message || "Codex response failed", {
|
|
507
|
+
throw new CodexApiError(message || "Codex response failed", {
|
|
508
|
+
...normalizeProviderError(response?.error).errorMetadata,
|
|
509
|
+
payload: event,
|
|
510
|
+
});
|
|
423
511
|
}
|
|
424
512
|
if (type === "response.done" || type === "response.completed" || type === "response.incomplete") {
|
|
425
513
|
const response = event.response;
|
|
@@ -459,9 +547,10 @@ async function* parseSSE(response, signal) {
|
|
|
459
547
|
if (signal?.aborted) {
|
|
460
548
|
throw new Error("Request was aborted");
|
|
461
549
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
550
|
+
buffer += done ? decoder.decode() : decoder.decode(value, { stream: true });
|
|
551
|
+
// Treat EOF as terminating the residual SSE frame.
|
|
552
|
+
if (done && buffer.trim())
|
|
553
|
+
buffer += "\n\n";
|
|
465
554
|
let idx = buffer.indexOf("\n\n");
|
|
466
555
|
while (idx !== -1) {
|
|
467
556
|
const chunk = buffer.slice(0, idx);
|
|
@@ -486,6 +575,8 @@ async function* parseSSE(response, signal) {
|
|
|
486
575
|
}
|
|
487
576
|
idx = buffer.indexOf("\n\n");
|
|
488
577
|
}
|
|
578
|
+
if (done)
|
|
579
|
+
break;
|
|
489
580
|
}
|
|
490
581
|
}
|
|
491
582
|
finally {
|
|
@@ -682,7 +773,7 @@ function scheduleSessionWebSocketExpiry(sessionId, entry) {
|
|
|
682
773
|
websocketSessionCache.delete(sessionId);
|
|
683
774
|
}, SESSION_WEBSOCKET_CACHE_TTL_MS);
|
|
684
775
|
}
|
|
685
|
-
async function connectWebSocket(url, headers, signal, env) {
|
|
776
|
+
async function connectWebSocket(url, headers, signal, env, connectTimeoutMs = DEFAULT_WEBSOCKET_CONNECT_TIMEOUT_MS) {
|
|
686
777
|
const WebSocketCtor = await getWebSocketConstructor(env);
|
|
687
778
|
if (!WebSocketCtor) {
|
|
688
779
|
throw new Error("WebSocket transport is not available in this runtime");
|
|
@@ -691,6 +782,7 @@ async function connectWebSocket(url, headers, signal, env) {
|
|
|
691
782
|
delete wsHeaders["OpenAI-Beta"];
|
|
692
783
|
return new Promise((resolve, reject) => {
|
|
693
784
|
let settled = false;
|
|
785
|
+
let timeout;
|
|
694
786
|
let socket;
|
|
695
787
|
try {
|
|
696
788
|
socket = new WebSocketCtor(url, { headers: wsHeaders });
|
|
@@ -699,52 +791,59 @@ async function connectWebSocket(url, headers, signal, env) {
|
|
|
699
791
|
reject(error instanceof Error ? error : new Error(String(error)));
|
|
700
792
|
return;
|
|
701
793
|
}
|
|
702
|
-
const
|
|
703
|
-
if (
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
794
|
+
const cleanup = () => {
|
|
795
|
+
if (timeout) {
|
|
796
|
+
clearTimeout(timeout);
|
|
797
|
+
timeout = undefined;
|
|
798
|
+
}
|
|
799
|
+
socket.removeEventListener("open", onOpen);
|
|
800
|
+
socket.removeEventListener("error", onError);
|
|
801
|
+
socket.removeEventListener("close", onClose);
|
|
802
|
+
signal?.removeEventListener("abort", onAbort);
|
|
708
803
|
};
|
|
709
|
-
const
|
|
710
|
-
const error = extractWebSocketError(event);
|
|
804
|
+
const fail = (error, closeReason) => {
|
|
711
805
|
if (settled)
|
|
712
806
|
return;
|
|
713
807
|
settled = true;
|
|
714
808
|
cleanup();
|
|
809
|
+
if (closeReason) {
|
|
810
|
+
closeWebSocketSilently(socket, 1000, closeReason);
|
|
811
|
+
}
|
|
715
812
|
reject(error);
|
|
716
813
|
};
|
|
717
|
-
const
|
|
718
|
-
const error = extractWebSocketCloseError(event);
|
|
814
|
+
const onOpen = () => {
|
|
719
815
|
if (settled)
|
|
720
816
|
return;
|
|
721
817
|
settled = true;
|
|
722
818
|
cleanup();
|
|
723
|
-
|
|
819
|
+
resolve(socket);
|
|
724
820
|
};
|
|
725
|
-
const
|
|
726
|
-
|
|
727
|
-
return;
|
|
728
|
-
settled = true;
|
|
729
|
-
cleanup();
|
|
730
|
-
socket.close(1000, "aborted");
|
|
731
|
-
reject(new Error("Request was aborted"));
|
|
821
|
+
const onError = (event) => {
|
|
822
|
+
fail(extractWebSocketError(event));
|
|
732
823
|
};
|
|
733
|
-
const
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
824
|
+
const onClose = (event) => {
|
|
825
|
+
fail(extractWebSocketCloseError(event));
|
|
826
|
+
};
|
|
827
|
+
const onAbort = () => {
|
|
828
|
+
fail(new Error("Request was aborted"), "aborted");
|
|
738
829
|
};
|
|
739
830
|
socket.addEventListener("open", onOpen);
|
|
740
831
|
socket.addEventListener("error", onError);
|
|
741
832
|
socket.addEventListener("close", onClose);
|
|
742
833
|
signal?.addEventListener("abort", onAbort);
|
|
834
|
+
if (connectTimeoutMs > 0) {
|
|
835
|
+
timeout = setTimeout(() => {
|
|
836
|
+
fail(new Error(`WebSocket connect timeout after ${connectTimeoutMs}ms`), "connect_timeout");
|
|
837
|
+
}, connectTimeoutMs);
|
|
838
|
+
}
|
|
839
|
+
if (signal?.aborted) {
|
|
840
|
+
onAbort();
|
|
841
|
+
}
|
|
743
842
|
});
|
|
744
843
|
}
|
|
745
|
-
async function acquireWebSocket(url, headers, sessionId, signal, env) {
|
|
844
|
+
async function acquireWebSocket(url, headers, sessionId, signal, env, connectTimeoutMs) {
|
|
746
845
|
if (!sessionId) {
|
|
747
|
-
const socket = await connectWebSocket(url, headers, signal, env);
|
|
846
|
+
const socket = await connectWebSocket(url, headers, signal, env, connectTimeoutMs);
|
|
748
847
|
return {
|
|
749
848
|
socket,
|
|
750
849
|
reused: false,
|
|
@@ -758,7 +857,20 @@ async function acquireWebSocket(url, headers, sessionId, signal, env) {
|
|
|
758
857
|
};
|
|
759
858
|
}
|
|
760
859
|
const proxyKey = getScopedProxyKey(env);
|
|
860
|
+
const accountId = headers.get("chatgpt-account-id") ?? undefined;
|
|
761
861
|
const cached = websocketSessionCache.get(sessionId);
|
|
862
|
+
if (cached && cached.accountId !== accountId) {
|
|
863
|
+
// A cached socket is authenticated for one account; never reuse it for another
|
|
864
|
+
// account sharing the session id. Leave the cached entry untouched.
|
|
865
|
+
const socket = await connectWebSocket(url, headers, signal, env, connectTimeoutMs);
|
|
866
|
+
return {
|
|
867
|
+
socket,
|
|
868
|
+
reused: false,
|
|
869
|
+
release: () => {
|
|
870
|
+
closeWebSocketSilently(socket);
|
|
871
|
+
},
|
|
872
|
+
};
|
|
873
|
+
}
|
|
762
874
|
if (cached) {
|
|
763
875
|
if (cached.proxyKey !== proxyKey) {
|
|
764
876
|
closeWebSocketSilently(cached.socket, 1000, "proxy_config_changed");
|
|
@@ -793,7 +905,7 @@ async function acquireWebSocket(url, headers, sessionId, signal, env) {
|
|
|
793
905
|
};
|
|
794
906
|
}
|
|
795
907
|
if (cached.busy) {
|
|
796
|
-
const socket = await connectWebSocket(url, headers, signal, env);
|
|
908
|
+
const socket = await connectWebSocket(url, headers, signal, env, connectTimeoutMs);
|
|
797
909
|
return {
|
|
798
910
|
socket,
|
|
799
911
|
reused: false,
|
|
@@ -808,8 +920,8 @@ async function acquireWebSocket(url, headers, sessionId, signal, env) {
|
|
|
808
920
|
}
|
|
809
921
|
}
|
|
810
922
|
}
|
|
811
|
-
const socket = await connectWebSocket(url, headers, signal, env);
|
|
812
|
-
const entry = { socket, busy: true, createdAt: Date.now(), proxyKey };
|
|
923
|
+
const socket = await connectWebSocket(url, headers, signal, env, connectTimeoutMs);
|
|
924
|
+
const entry = { socket, busy: true, createdAt: Date.now(), proxyKey, accountId };
|
|
813
925
|
websocketSessionCache.set(sessionId, entry);
|
|
814
926
|
return {
|
|
815
927
|
socket,
|
|
@@ -884,7 +996,7 @@ async function decodeWebSocketData(data) {
|
|
|
884
996
|
}
|
|
885
997
|
return null;
|
|
886
998
|
}
|
|
887
|
-
async function* parseWebSocket(socket, signal) {
|
|
999
|
+
async function* parseWebSocket(socket, signal, idleTimeoutMs) {
|
|
888
1000
|
const queue = [];
|
|
889
1001
|
let pending = null;
|
|
890
1002
|
let done = false;
|
|
@@ -962,8 +1074,23 @@ async function* parseWebSocket(socket, signal) {
|
|
|
962
1074
|
}
|
|
963
1075
|
if (done)
|
|
964
1076
|
break;
|
|
965
|
-
|
|
1077
|
+
let timeout;
|
|
1078
|
+
await new Promise((resolve, reject) => {
|
|
966
1079
|
pending = resolve;
|
|
1080
|
+
if (idleTimeoutMs !== undefined && idleTimeoutMs > 0) {
|
|
1081
|
+
timeout = setTimeout(() => {
|
|
1082
|
+
const error = new Error(`WebSocket idle timeout after ${idleTimeoutMs}ms`);
|
|
1083
|
+
failed = error;
|
|
1084
|
+
done = true;
|
|
1085
|
+
pending = null;
|
|
1086
|
+
closeWebSocketSilently(socket, 1000, "idle_timeout");
|
|
1087
|
+
reject(error);
|
|
1088
|
+
}, idleTimeoutMs);
|
|
1089
|
+
}
|
|
1090
|
+
}).finally(() => {
|
|
1091
|
+
if (timeout) {
|
|
1092
|
+
clearTimeout(timeout);
|
|
1093
|
+
}
|
|
967
1094
|
});
|
|
968
1095
|
}
|
|
969
1096
|
if (failed) {
|
|
@@ -1032,15 +1159,15 @@ async function* _startWebSocketOutputOnFirstEvent(events, output, stream, onStar
|
|
|
1032
1159
|
yield event;
|
|
1033
1160
|
}
|
|
1034
1161
|
}
|
|
1035
|
-
async function processWebSocketStream(url, body, headers, output, stream, model, onStart, onResponseEvent, options) {
|
|
1036
|
-
const { socket, entry, reused, release } = await acquireWebSocket(url, headers,
|
|
1162
|
+
async function processWebSocketStream(url, body, headers, output, stream, model, onStart, onResponseEvent, cacheSessionId, idleTimeoutMs, websocketConnectTimeoutMs, options) {
|
|
1163
|
+
const { socket, entry, reused, release } = await acquireWebSocket(url, headers, cacheSessionId, options?.signal, options?.env, websocketConnectTimeoutMs);
|
|
1037
1164
|
let keepConnection = true;
|
|
1038
1165
|
const useCachedContext = options?.transport === "websocket-cached" || options?.transport === "auto";
|
|
1039
1166
|
// ChatGPT Codex Responses rejects `store: true` ("Store must be set to false").
|
|
1040
1167
|
// WebSocket continuation still works via connection-scoped previous_response_id state.
|
|
1041
1168
|
const fullBody = body;
|
|
1042
1169
|
const requestBody = useCachedContext && entry ? buildCachedWebSocketRequestBody(entry, fullBody) : fullBody;
|
|
1043
|
-
const stats =
|
|
1170
|
+
const stats = cacheSessionId ? getOrCreateWebSocketDebugStats(cacheSessionId) : undefined;
|
|
1044
1171
|
if (stats) {
|
|
1045
1172
|
stats.requests++;
|
|
1046
1173
|
if (reused)
|
|
@@ -1066,10 +1193,11 @@ async function processWebSocketStream(url, body, headers, output, stream, model,
|
|
|
1066
1193
|
try {
|
|
1067
1194
|
socket.send(JSON.stringify({ type: "response.create", ...requestBody }));
|
|
1068
1195
|
onStart();
|
|
1069
|
-
stream.push({ type: "start", partial: output });
|
|
1070
1196
|
async function* observedWebSocketEvents() {
|
|
1071
|
-
for await (const event of parseWebSocket(socket, options?.signal)) {
|
|
1072
|
-
|
|
1197
|
+
for await (const event of parseWebSocket(socket, options?.signal, idleTimeoutMs)) {
|
|
1198
|
+
// A connection-limit rejection carries no response data, so the request can be retried.
|
|
1199
|
+
if (!isWebSocketConnectionLimitReachedEvent(event))
|
|
1200
|
+
onResponseEvent();
|
|
1073
1201
|
yield event;
|
|
1074
1202
|
}
|
|
1075
1203
|
}
|
|
@@ -1110,9 +1238,11 @@ async function parseErrorResponse(response) {
|
|
|
1110
1238
|
const raw = await response.text();
|
|
1111
1239
|
let message = raw || response.statusText || "Request failed";
|
|
1112
1240
|
let friendlyMessage;
|
|
1241
|
+
let errorMetadata;
|
|
1113
1242
|
try {
|
|
1114
1243
|
const parsed = JSON.parse(raw);
|
|
1115
1244
|
const err = parsed?.error;
|
|
1245
|
+
errorMetadata = normalizeProviderError(parsed).errorMetadata;
|
|
1116
1246
|
if (err) {
|
|
1117
1247
|
const code = err.code || err.type || "";
|
|
1118
1248
|
if (/usage_limit_reached|usage_not_included|rate_limit_exceeded/i.test(code) || response.status === 429) {
|
|
@@ -1127,7 +1257,7 @@ async function parseErrorResponse(response) {
|
|
|
1127
1257
|
}
|
|
1128
1258
|
}
|
|
1129
1259
|
catch { }
|
|
1130
|
-
return { message, friendlyMessage };
|
|
1260
|
+
return { message, friendlyMessage, errorMetadata };
|
|
1131
1261
|
}
|
|
1132
1262
|
// ============================================================================
|
|
1133
1263
|
// Auth & Headers
|
|
@@ -1147,22 +1277,18 @@ function extractAccountId(token) {
|
|
|
1147
1277
|
throw new Error("Failed to extract accountId from token");
|
|
1148
1278
|
}
|
|
1149
1279
|
}
|
|
1150
|
-
function createCodexRequestId() {
|
|
1151
|
-
if (typeof globalThis.crypto?.randomUUID === "function") {
|
|
1152
|
-
return globalThis.crypto.randomUUID();
|
|
1153
|
-
}
|
|
1154
|
-
return `codex_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
1155
|
-
}
|
|
1156
1280
|
function buildBaseCodexHeaders(initHeaders, additionalHeaders, accountId, token) {
|
|
1157
|
-
const
|
|
1281
|
+
const userAgent = _os ? `pi (${_os.platform()} ${_os.release()}; ${_os.arch()})` : "pi (browser)";
|
|
1282
|
+
// Defaults first so model and caller headers can override them, matching the other providers.
|
|
1283
|
+
const headers = new Headers({ originator: "pi", "User-Agent": userAgent });
|
|
1284
|
+
for (const [key, value] of Object.entries(initHeaders || {})) {
|
|
1285
|
+
headers.set(key, value);
|
|
1286
|
+
}
|
|
1158
1287
|
for (const [key, value] of Object.entries(additionalHeaders || {})) {
|
|
1159
1288
|
headers.set(key, value);
|
|
1160
1289
|
}
|
|
1161
1290
|
headers.set("Authorization", `Bearer ${token}`);
|
|
1162
1291
|
headers.set("chatgpt-account-id", accountId);
|
|
1163
|
-
headers.set("originator", "pi");
|
|
1164
|
-
const userAgent = _os ? `pi (${_os.platform()} ${_os.release()}; ${_os.arch()})` : "pi (browser)";
|
|
1165
|
-
headers.set("User-Agent", userAgent);
|
|
1166
1292
|
return headers;
|
|
1167
1293
|
}
|
|
1168
1294
|
function buildSSEHeaders(initHeaders, additionalHeaders, accountId, token, sessionId) {
|
|
@@ -1171,7 +1297,7 @@ function buildSSEHeaders(initHeaders, additionalHeaders, accountId, token, sessi
|
|
|
1171
1297
|
headers.set("accept", "text/event-stream");
|
|
1172
1298
|
headers.set("content-type", "application/json");
|
|
1173
1299
|
if (sessionId) {
|
|
1174
|
-
headers.set("
|
|
1300
|
+
headers.set("session-id", sessionId);
|
|
1175
1301
|
headers.set("x-client-request-id", sessionId);
|
|
1176
1302
|
}
|
|
1177
1303
|
return headers;
|
|
@@ -1184,7 +1310,7 @@ function buildWebSocketHeaders(initHeaders, additionalHeaders, accountId, token,
|
|
|
1184
1310
|
headers.delete("openai-beta");
|
|
1185
1311
|
headers.set("OpenAI-Beta", OPENAI_BETA_RESPONSES_WEBSOCKETS);
|
|
1186
1312
|
headers.set("x-client-request-id", requestId);
|
|
1187
|
-
headers.set("
|
|
1313
|
+
headers.set("session-id", requestId);
|
|
1188
1314
|
return headers;
|
|
1189
1315
|
}
|
|
1190
1316
|
//# sourceMappingURL=openai-codex-responses.js.map
|