phi-code-ai 0.56.3 → 0.74.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 +258 -73
- package/dist/api-registry.d.ts.map +1 -1
- package/dist/api-registry.js.map +1 -1
- package/dist/bedrock-provider.d.ts.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/env-api-keys.d.ts +9 -0
- package/dist/env-api-keys.d.ts.map +1 -1
- package/dist/env-api-keys.js +96 -30
- package/dist/env-api-keys.js.map +1 -1
- package/dist/image-models.d.ts +10 -0
- package/dist/image-models.d.ts.map +1 -0
- package/dist/image-models.generated.d.ts +305 -0
- package/dist/image-models.generated.d.ts.map +1 -0
- package/dist/image-models.generated.js +307 -0
- package/dist/image-models.generated.js.map +1 -0
- package/dist/image-models.js +23 -0
- package/dist/image-models.js.map +1 -0
- package/dist/images-api-registry.d.ts +14 -0
- package/dist/images-api-registry.d.ts.map +1 -0
- package/dist/images-api-registry.js +22 -0
- package/dist/images-api-registry.js.map +1 -0
- package/dist/images.d.ts +4 -0
- package/dist/images.d.ts.map +1 -0
- package/dist/images.js +14 -0
- package/dist/images.js.map +1 -0
- package/dist/index.d.ts +20 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -9
- package/dist/index.js.map +1 -1
- package/dist/models.d.ts +3 -9
- package/dist/models.d.ts.map +1 -1
- package/dist/models.generated.d.ts +6525 -2231
- package/dist/models.generated.d.ts.map +1 -1
- package/dist/models.generated.js +8992 -5524
- package/dist/models.generated.js.map +1 -1
- package/dist/models.js +28 -12
- package/dist/models.js.map +1 -1
- package/dist/oauth.d.ts.map +1 -1
- package/dist/providers/amazon-bedrock.d.ts +23 -0
- package/dist/providers/amazon-bedrock.d.ts.map +1 -1
- package/dist/providers/amazon-bedrock.js +206 -44
- package/dist/providers/amazon-bedrock.js.map +1 -1
- package/dist/providers/anthropic.d.ts +23 -2
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +294 -63
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/azure-openai-responses.d.ts.map +1 -1
- package/dist/providers/azure-openai-responses.js +47 -23
- package/dist/providers/azure-openai-responses.js.map +1 -1
- package/dist/providers/cloudflare.d.ts +13 -0
- package/dist/providers/cloudflare.d.ts.map +1 -0
- package/dist/providers/cloudflare.js +26 -0
- package/dist/providers/cloudflare.js.map +1 -0
- package/dist/providers/faux.d.ts +56 -0
- package/dist/providers/faux.d.ts.map +1 -0
- package/dist/providers/faux.js +368 -0
- package/dist/providers/faux.js.map +1 -0
- package/dist/providers/github-copilot-headers.d.ts.map +1 -1
- package/dist/providers/github-copilot-headers.js.map +1 -1
- package/dist/providers/google-shared.d.ts +7 -2
- package/dist/providers/google-shared.d.ts.map +1 -1
- package/dist/providers/google-shared.js +53 -24
- package/dist/providers/google-shared.js.map +1 -1
- package/dist/providers/google-vertex.d.ts +1 -1
- package/dist/providers/google-vertex.d.ts.map +1 -1
- package/dist/providers/google-vertex.js +87 -16
- package/dist/providers/google-vertex.js.map +1 -1
- package/dist/providers/google.d.ts +1 -1
- package/dist/providers/google.d.ts.map +1 -1
- package/dist/providers/google.js +57 -9
- package/dist/providers/google.js.map +1 -1
- package/dist/providers/images/openrouter.d.ts +3 -0
- package/dist/providers/images/openrouter.d.ts.map +1 -0
- package/dist/providers/images/openrouter.js +129 -0
- package/dist/providers/images/openrouter.js.map +1 -0
- package/dist/providers/images/register-builtins.d.ts +4 -0
- package/dist/providers/images/register-builtins.d.ts.map +1 -0
- package/dist/providers/images/register-builtins.js +34 -0
- package/dist/providers/images/register-builtins.js.map +1 -0
- package/dist/providers/mistral.d.ts +3 -0
- package/dist/providers/mistral.d.ts.map +1 -1
- package/dist/providers/mistral.js +49 -9
- package/dist/providers/mistral.js.map +1 -1
- package/dist/providers/openai-codex-responses.d.ts +21 -0
- package/dist/providers/openai-codex-responses.d.ts.map +1 -1
- package/dist/providers/openai-codex-responses.js +443 -86
- package/dist/providers/openai-codex-responses.js.map +1 -1
- package/dist/providers/openai-completions.d.ts +5 -1
- package/dist/providers/openai-completions.d.ts.map +1 -1
- package/dist/providers/openai-completions.js +460 -225
- package/dist/providers/openai-completions.js.map +1 -1
- package/dist/providers/openai-responses-shared.d.ts +1 -0
- package/dist/providers/openai-responses-shared.d.ts.map +1 -1
- package/dist/providers/openai-responses-shared.js +95 -45
- 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 +66 -44
- package/dist/providers/openai-responses.js.map +1 -1
- package/dist/providers/register-builtins.d.ts +27 -2
- package/dist/providers/register-builtins.d.ts.map +1 -1
- package/dist/providers/register-builtins.js +157 -52
- package/dist/providers/register-builtins.js.map +1 -1
- package/dist/providers/simple-options.d.ts.map +1 -1
- package/dist/providers/simple-options.js +5 -1
- package/dist/providers/simple-options.js.map +1 -1
- package/dist/providers/transform-messages.d.ts.map +1 -1
- package/dist/providers/transform-messages.js +63 -34
- package/dist/providers/transform-messages.js.map +1 -1
- package/dist/session-resources.d.ts +4 -0
- package/dist/session-resources.d.ts.map +1 -0
- package/dist/session-resources.js +22 -0
- package/dist/session-resources.js.map +1 -0
- package/dist/stream.d.ts.map +1 -1
- package/dist/stream.js.map +1 -1
- package/dist/types.d.ts +219 -15
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/diagnostics.d.ts +19 -0
- package/dist/utils/diagnostics.d.ts.map +1 -0
- package/dist/utils/diagnostics.js +25 -0
- package/dist/utils/diagnostics.js.map +1 -0
- package/dist/utils/event-stream.d.ts.map +1 -1
- package/dist/utils/event-stream.js +7 -3
- package/dist/utils/event-stream.js.map +1 -1
- package/dist/utils/hash.d.ts.map +1 -1
- package/dist/utils/hash.js.map +1 -1
- package/dist/utils/headers.d.ts +2 -0
- package/dist/utils/headers.d.ts.map +1 -0
- package/dist/utils/headers.js +8 -0
- package/dist/utils/headers.js.map +1 -0
- package/dist/utils/json-parse.d.ts +8 -1
- package/dist/utils/json-parse.d.ts.map +1 -1
- package/dist/utils/json-parse.js +89 -5
- package/dist/utils/json-parse.js.map +1 -1
- package/dist/utils/oauth/anthropic.d.ts +14 -6
- package/dist/utils/oauth/anthropic.d.ts.map +1 -1
- package/dist/utils/oauth/anthropic.js +288 -57
- 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 +23 -12
- package/dist/utils/oauth/github-copilot.js.map +1 -1
- package/dist/utils/oauth/index.d.ts +0 -4
- package/dist/utils/oauth/index.d.ts.map +1 -1
- package/dist/utils/oauth/index.js +0 -10
- package/dist/utils/oauth/index.js.map +1 -1
- package/dist/utils/oauth/oauth-page.d.ts +3 -0
- package/dist/utils/oauth/oauth-page.d.ts.map +1 -0
- package/dist/utils/oauth/oauth-page.js +105 -0
- package/dist/utils/oauth/oauth-page.js.map +1 -0
- package/dist/utils/oauth/openai-codex.d.ts.map +1 -1
- package/dist/utils/oauth/openai-codex.js +51 -46
- package/dist/utils/oauth/openai-codex.js.map +1 -1
- package/dist/utils/oauth/pkce.d.ts.map +1 -1
- package/dist/utils/oauth/pkce.js.map +1 -1
- package/dist/utils/oauth/types.d.ts +10 -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 +7 -3
- package/dist/utils/overflow.d.ts.map +1 -1
- package/dist/utils/overflow.js +46 -13
- package/dist/utils/overflow.js.map +1 -1
- package/dist/utils/sanitize-unicode.d.ts.map +1 -1
- package/dist/utils/sanitize-unicode.js.map +1 -1
- package/dist/utils/typebox-helpers.d.ts +1 -1
- package/dist/utils/typebox-helpers.d.ts.map +1 -1
- package/dist/utils/typebox-helpers.js +1 -1
- package/dist/utils/typebox-helpers.js.map +1 -1
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +247 -38
- package/dist/utils/validation.js.map +1 -1
- package/package.json +44 -14
- package/bedrock-provider.d.ts +0 -1
- package/bedrock-provider.js +0 -1
- package/dist/providers/google-gemini-cli.d.ts +0 -74
- package/dist/providers/google-gemini-cli.d.ts.map +0 -1
- package/dist/providers/google-gemini-cli.js +0 -754
- package/dist/providers/google-gemini-cli.js.map +0 -1
- package/dist/utils/oauth/google-antigravity.d.ts +0 -26
- package/dist/utils/oauth/google-antigravity.d.ts.map +0 -1
- package/dist/utils/oauth/google-antigravity.js +0 -373
- package/dist/utils/oauth/google-antigravity.js.map +0 -1
- package/dist/utils/oauth/google-gemini-cli.d.ts +0 -26
- package/dist/utils/oauth/google-gemini-cli.d.ts.map +0 -1
- package/dist/utils/oauth/google-gemini-cli.js +0 -478
- package/dist/utils/oauth/google-gemini-cli.js.map +0 -1
|
@@ -8,10 +8,13 @@ if (typeof process !== "undefined" && (process.versions?.node || process.version
|
|
|
8
8
|
});
|
|
9
9
|
}
|
|
10
10
|
import { getEnvApiKey } from "../env-api-keys.js";
|
|
11
|
-
import {
|
|
11
|
+
import { clampThinkingLevel } from "../models.js";
|
|
12
|
+
import { registerSessionResourceCleanup } from "../session-resources.js";
|
|
13
|
+
import { appendAssistantMessageDiagnostic, createAssistantMessageDiagnostic, formatThrownValue, } from "../utils/diagnostics.js";
|
|
12
14
|
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
|
15
|
+
import { headersToRecord } from "../utils/headers.js";
|
|
13
16
|
import { convertResponsesMessages, convertResponsesTools, processResponsesStream } from "./openai-responses-shared.js";
|
|
14
|
-
import { buildBaseOptions
|
|
17
|
+
import { buildBaseOptions } from "./simple-options.js";
|
|
15
18
|
// ============================================================================
|
|
16
19
|
// Configuration
|
|
17
20
|
// ============================================================================
|
|
@@ -20,6 +23,7 @@ const JWT_CLAIM_PATH = "https://api.openai.com/auth";
|
|
|
20
23
|
const MAX_RETRIES = 3;
|
|
21
24
|
const BASE_DELAY_MS = 1000;
|
|
22
25
|
const CODEX_TOOL_CALL_PROVIDERS = new Set(["openai", "openai-codex", "opencode"]);
|
|
26
|
+
const WEBSOCKET_MESSAGE_TOO_BIG_CLOSE_CODE = 1009;
|
|
23
27
|
const CODEX_RESPONSE_STATUSES = new Set([
|
|
24
28
|
"completed",
|
|
25
29
|
"incomplete",
|
|
@@ -79,15 +83,24 @@ export const streamOpenAICodexResponses = (model, context, options) => {
|
|
|
79
83
|
throw new Error(`No API key for provider: ${model.provider}`);
|
|
80
84
|
}
|
|
81
85
|
const accountId = extractAccountId(apiKey);
|
|
82
|
-
|
|
83
|
-
options?.onPayload?.(body);
|
|
84
|
-
|
|
86
|
+
let body = buildRequestBody(model, context, options);
|
|
87
|
+
const nextBody = await options?.onPayload?.(body, model);
|
|
88
|
+
if (nextBody !== undefined) {
|
|
89
|
+
body = nextBody;
|
|
90
|
+
}
|
|
91
|
+
const websocketRequestId = options?.sessionId || createCodexRequestId();
|
|
92
|
+
const sseHeaders = buildSSEHeaders(model.headers, options?.headers, accountId, apiKey, options?.sessionId);
|
|
93
|
+
const websocketHeaders = buildWebSocketHeaders(model.headers, options?.headers, accountId, apiKey, websocketRequestId);
|
|
85
94
|
const bodyJson = JSON.stringify(body);
|
|
86
|
-
const transport = options?.transport || "
|
|
87
|
-
|
|
95
|
+
const transport = options?.transport || "auto";
|
|
96
|
+
const websocketDisabledForSession = transport !== "sse" && isWebSocketSseFallbackActive(options?.sessionId);
|
|
97
|
+
if (websocketDisabledForSession) {
|
|
98
|
+
recordWebSocketSseFallback(options?.sessionId);
|
|
99
|
+
}
|
|
100
|
+
if (transport !== "sse" && !websocketDisabledForSession) {
|
|
88
101
|
let websocketStarted = false;
|
|
89
102
|
try {
|
|
90
|
-
await processWebSocketStream(resolveCodexWebSocketUrl(model.baseUrl), body,
|
|
103
|
+
await processWebSocketStream(resolveCodexWebSocketUrl(model.baseUrl), body, websocketHeaders, output, stream, model, () => {
|
|
91
104
|
websocketStarted = true;
|
|
92
105
|
}, options);
|
|
93
106
|
if (options?.signal?.aborted) {
|
|
@@ -102,9 +115,22 @@ export const streamOpenAICodexResponses = (model, context, options) => {
|
|
|
102
115
|
return;
|
|
103
116
|
}
|
|
104
117
|
catch (error) {
|
|
105
|
-
|
|
118
|
+
const aborted = options?.signal?.aborted;
|
|
119
|
+
if (aborted || isCodexNonTransportError(error)) {
|
|
120
|
+
throw error;
|
|
121
|
+
}
|
|
122
|
+
appendAssistantMessageDiagnostic(output, createAssistantMessageDiagnostic("provider_transport_failure", error, {
|
|
123
|
+
configuredTransport: transport,
|
|
124
|
+
fallbackTransport: websocketStarted ? undefined : "sse",
|
|
125
|
+
eventsEmitted: websocketStarted,
|
|
126
|
+
phase: websocketStarted ? "after_message_stream_start" : "before_message_stream_start",
|
|
127
|
+
requestBytes: new TextEncoder().encode(bodyJson).byteLength,
|
|
128
|
+
}));
|
|
129
|
+
recordWebSocketFailure(options?.sessionId, error);
|
|
130
|
+
if (websocketStarted) {
|
|
106
131
|
throw error;
|
|
107
132
|
}
|
|
133
|
+
recordWebSocketSseFallback(options?.sessionId);
|
|
108
134
|
}
|
|
109
135
|
}
|
|
110
136
|
// Fetch with retry logic for rate limits and transient errors
|
|
@@ -117,10 +143,11 @@ export const streamOpenAICodexResponses = (model, context, options) => {
|
|
|
117
143
|
try {
|
|
118
144
|
response = await fetch(resolveCodexUrl(model.baseUrl), {
|
|
119
145
|
method: "POST",
|
|
120
|
-
headers,
|
|
146
|
+
headers: sseHeaders,
|
|
121
147
|
body: bodyJson,
|
|
122
148
|
signal: options?.signal,
|
|
123
149
|
});
|
|
150
|
+
await options?.onResponse?.({ status: response.status, headers: headersToRecord(response.headers) }, model);
|
|
124
151
|
if (response.ok) {
|
|
125
152
|
break;
|
|
126
153
|
}
|
|
@@ -161,7 +188,7 @@ export const streamOpenAICodexResponses = (model, context, options) => {
|
|
|
161
188
|
throw new Error("No response body");
|
|
162
189
|
}
|
|
163
190
|
stream.push({ type: "start", partial: output });
|
|
164
|
-
await processStream(response, output, stream, model);
|
|
191
|
+
await processStream(response, output, stream, model, options);
|
|
165
192
|
if (options?.signal?.aborted) {
|
|
166
193
|
throw new Error("Request was aborted");
|
|
167
194
|
}
|
|
@@ -169,6 +196,10 @@ export const streamOpenAICodexResponses = (model, context, options) => {
|
|
|
169
196
|
stream.end();
|
|
170
197
|
}
|
|
171
198
|
catch (error) {
|
|
199
|
+
for (const block of output.content) {
|
|
200
|
+
// partialJson is only a streaming scratch buffer; never persist it.
|
|
201
|
+
delete block.partialJson;
|
|
202
|
+
}
|
|
172
203
|
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
173
204
|
output.errorMessage = error instanceof Error ? error.message : String(error);
|
|
174
205
|
stream.push({ type: "error", reason: output.stopReason, error: output });
|
|
@@ -183,7 +214,8 @@ export const streamSimpleOpenAICodexResponses = (model, context, options) => {
|
|
|
183
214
|
throw new Error(`No API key for provider: ${model.provider}`);
|
|
184
215
|
}
|
|
185
216
|
const base = buildBaseOptions(model, options, apiKey);
|
|
186
|
-
const
|
|
217
|
+
const clampedReasoning = options?.reasoning ? clampThinkingLevel(model, options.reasoning) : undefined;
|
|
218
|
+
const reasoningEffort = clampedReasoning === "off" ? undefined : clampedReasoning;
|
|
187
219
|
return streamOpenAICodexResponses(model, context, {
|
|
188
220
|
...base,
|
|
189
221
|
reasoningEffort,
|
|
@@ -200,9 +232,9 @@ function buildRequestBody(model, context, options) {
|
|
|
200
232
|
model: model.id,
|
|
201
233
|
store: false,
|
|
202
234
|
stream: true,
|
|
203
|
-
instructions: context.systemPrompt,
|
|
235
|
+
instructions: context.systemPrompt || "You are a helpful assistant.",
|
|
204
236
|
input: messages,
|
|
205
|
-
text: { verbosity: options?.textVerbosity || "
|
|
237
|
+
text: { verbosity: options?.textVerbosity || "low" },
|
|
206
238
|
include: ["reasoning.encrypted_content"],
|
|
207
239
|
prompt_cache_key: options?.sessionId,
|
|
208
240
|
tool_choice: "auto",
|
|
@@ -211,26 +243,50 @@ function buildRequestBody(model, context, options) {
|
|
|
211
243
|
if (options?.temperature !== undefined) {
|
|
212
244
|
body.temperature = options.temperature;
|
|
213
245
|
}
|
|
214
|
-
if (
|
|
246
|
+
if (options?.serviceTier !== undefined) {
|
|
247
|
+
body.service_tier = options.serviceTier;
|
|
248
|
+
}
|
|
249
|
+
if (context.tools && context.tools.length > 0) {
|
|
215
250
|
body.tools = convertResponsesTools(context.tools, { strict: null });
|
|
216
251
|
}
|
|
217
252
|
if (options?.reasoningEffort !== undefined) {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
253
|
+
const effort = options.reasoningEffort === "none"
|
|
254
|
+
? (model.thinkingLevelMap?.off ?? "none")
|
|
255
|
+
: (model.thinkingLevelMap?.[options.reasoningEffort] ?? options.reasoningEffort);
|
|
256
|
+
if (effort !== null) {
|
|
257
|
+
body.reasoning = {
|
|
258
|
+
effort,
|
|
259
|
+
summary: options.reasoningSummary ?? "auto",
|
|
260
|
+
};
|
|
261
|
+
}
|
|
222
262
|
}
|
|
223
263
|
return body;
|
|
224
264
|
}
|
|
225
|
-
function
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
265
|
+
function getServiceTierCostMultiplier(model, serviceTier) {
|
|
266
|
+
switch (serviceTier) {
|
|
267
|
+
case "flex":
|
|
268
|
+
return 0.5;
|
|
269
|
+
case "priority":
|
|
270
|
+
return model.id === "gpt-5.5" ? 2.5 : 2;
|
|
271
|
+
default:
|
|
272
|
+
return 1;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
function applyServiceTierPricing(usage, serviceTier, model) {
|
|
276
|
+
const multiplier = getServiceTierCostMultiplier(model, serviceTier);
|
|
277
|
+
if (multiplier === 1)
|
|
278
|
+
return;
|
|
279
|
+
usage.cost.input *= multiplier;
|
|
280
|
+
usage.cost.output *= multiplier;
|
|
281
|
+
usage.cost.cacheRead *= multiplier;
|
|
282
|
+
usage.cost.cacheWrite *= multiplier;
|
|
283
|
+
usage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;
|
|
284
|
+
}
|
|
285
|
+
function resolveCodexServiceTier(responseServiceTier, requestServiceTier) {
|
|
286
|
+
if (responseServiceTier === "default" && (requestServiceTier === "flex" || requestServiceTier === "priority")) {
|
|
287
|
+
return requestServiceTier;
|
|
288
|
+
}
|
|
289
|
+
return responseServiceTier ?? requestServiceTier;
|
|
234
290
|
}
|
|
235
291
|
function resolveCodexUrl(baseUrl) {
|
|
236
292
|
const raw = baseUrl && baseUrl.trim().length > 0 ? baseUrl : DEFAULT_CODEX_BASE_URL;
|
|
@@ -252,8 +308,35 @@ function resolveCodexWebSocketUrl(baseUrl) {
|
|
|
252
308
|
// ============================================================================
|
|
253
309
|
// Response Processing
|
|
254
310
|
// ============================================================================
|
|
255
|
-
async function processStream(response, output, stream, model) {
|
|
256
|
-
await processResponsesStream(mapCodexEvents(parseSSE(response)), output, stream, model
|
|
311
|
+
async function processStream(response, output, stream, model, options) {
|
|
312
|
+
await processResponsesStream(mapCodexEvents(parseSSE(response)), output, stream, model, {
|
|
313
|
+
serviceTier: options?.serviceTier,
|
|
314
|
+
resolveServiceTier: resolveCodexServiceTier,
|
|
315
|
+
applyServiceTierPricing: (usage, serviceTier) => applyServiceTierPricing(usage, serviceTier, model),
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
class CodexApiError extends Error {
|
|
319
|
+
code;
|
|
320
|
+
payload;
|
|
321
|
+
constructor(message, options) {
|
|
322
|
+
super(message);
|
|
323
|
+
this.name = "CodexApiError";
|
|
324
|
+
this.code = options?.code;
|
|
325
|
+
this.payload = options?.payload;
|
|
326
|
+
this.cause = options?.cause;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
class CodexProtocolError extends Error {
|
|
330
|
+
payload;
|
|
331
|
+
constructor(message, options) {
|
|
332
|
+
super(message);
|
|
333
|
+
this.name = "CodexProtocolError";
|
|
334
|
+
this.payload = options?.payload;
|
|
335
|
+
this.cause = options?.cause;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
function isCodexNonTransportError(error) {
|
|
339
|
+
return error instanceof CodexApiError || error instanceof CodexProtocolError;
|
|
257
340
|
}
|
|
258
341
|
async function* mapCodexEvents(events) {
|
|
259
342
|
for await (const event of events) {
|
|
@@ -263,19 +346,24 @@ async function* mapCodexEvents(events) {
|
|
|
263
346
|
if (type === "error") {
|
|
264
347
|
const code = event.code || "";
|
|
265
348
|
const message = event.message || "";
|
|
266
|
-
throw new
|
|
349
|
+
throw new CodexApiError(`Codex error: ${message || code || JSON.stringify(event)}`, {
|
|
350
|
+
code: code || undefined,
|
|
351
|
+
payload: event,
|
|
352
|
+
});
|
|
267
353
|
}
|
|
268
354
|
if (type === "response.failed") {
|
|
269
|
-
const
|
|
270
|
-
|
|
355
|
+
const response = event.response;
|
|
356
|
+
const code = response?.error?.code;
|
|
357
|
+
const message = response?.error?.message;
|
|
358
|
+
throw new CodexApiError(message || "Codex response failed", { code, payload: event });
|
|
271
359
|
}
|
|
272
|
-
if (type === "response.done" || type === "response.completed") {
|
|
360
|
+
if (type === "response.done" || type === "response.completed" || type === "response.incomplete") {
|
|
273
361
|
const response = event.response;
|
|
274
362
|
const normalizedResponse = response
|
|
275
363
|
? { ...response, status: normalizeCodexStatus(response.status) }
|
|
276
364
|
: response;
|
|
277
365
|
yield { ...event, type: "response.completed", response: normalizedResponse };
|
|
278
|
-
|
|
366
|
+
return;
|
|
279
367
|
}
|
|
280
368
|
yield event;
|
|
281
369
|
}
|
|
@@ -294,31 +382,48 @@ async function* parseSSE(response) {
|
|
|
294
382
|
const reader = response.body.getReader();
|
|
295
383
|
const decoder = new TextDecoder();
|
|
296
384
|
let buffer = "";
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
385
|
+
try {
|
|
386
|
+
while (true) {
|
|
387
|
+
const { done, value } = await reader.read();
|
|
388
|
+
if (done)
|
|
389
|
+
break;
|
|
390
|
+
buffer += decoder.decode(value, { stream: true });
|
|
391
|
+
let idx = buffer.indexOf("\n\n");
|
|
392
|
+
while (idx !== -1) {
|
|
393
|
+
const chunk = buffer.slice(0, idx);
|
|
394
|
+
buffer = buffer.slice(idx + 2);
|
|
395
|
+
const dataLines = chunk
|
|
396
|
+
.split("\n")
|
|
397
|
+
.filter((l) => l.startsWith("data:"))
|
|
398
|
+
.map((l) => l.slice(5).trim());
|
|
399
|
+
if (dataLines.length > 0) {
|
|
400
|
+
const data = dataLines.join("\n").trim();
|
|
401
|
+
if (data && data !== "[DONE]") {
|
|
402
|
+
try {
|
|
403
|
+
yield JSON.parse(data);
|
|
404
|
+
}
|
|
405
|
+
catch (cause) {
|
|
406
|
+
throw new CodexProtocolError(`Invalid Codex SSE JSON: ${formatThrownValue(cause)}`, {
|
|
407
|
+
cause,
|
|
408
|
+
payload: data,
|
|
409
|
+
});
|
|
410
|
+
}
|
|
315
411
|
}
|
|
316
|
-
catch { }
|
|
317
412
|
}
|
|
413
|
+
idx = buffer.indexOf("\n\n");
|
|
318
414
|
}
|
|
319
|
-
idx = buffer.indexOf("\n\n");
|
|
320
415
|
}
|
|
321
416
|
}
|
|
417
|
+
finally {
|
|
418
|
+
try {
|
|
419
|
+
await reader.cancel();
|
|
420
|
+
}
|
|
421
|
+
catch { }
|
|
422
|
+
try {
|
|
423
|
+
reader.releaseLock();
|
|
424
|
+
}
|
|
425
|
+
catch { }
|
|
426
|
+
}
|
|
322
427
|
}
|
|
323
428
|
// ============================================================================
|
|
324
429
|
// WebSocket Parsing
|
|
@@ -326,18 +431,119 @@ async function* parseSSE(response) {
|
|
|
326
431
|
const OPENAI_BETA_RESPONSES_WEBSOCKETS = "responses_websockets=2026-02-06";
|
|
327
432
|
const SESSION_WEBSOCKET_CACHE_TTL_MS = 5 * 60 * 1000;
|
|
328
433
|
const websocketSessionCache = new Map();
|
|
329
|
-
|
|
434
|
+
const websocketDebugStats = new Map();
|
|
435
|
+
const websocketSseFallbackSessions = new Set();
|
|
436
|
+
function getOrCreateWebSocketDebugStats(sessionId) {
|
|
437
|
+
let stats = websocketDebugStats.get(sessionId);
|
|
438
|
+
if (!stats) {
|
|
439
|
+
stats = {
|
|
440
|
+
requests: 0,
|
|
441
|
+
connectionsCreated: 0,
|
|
442
|
+
connectionsReused: 0,
|
|
443
|
+
cachedContextRequests: 0,
|
|
444
|
+
storeTrueRequests: 0,
|
|
445
|
+
fullContextRequests: 0,
|
|
446
|
+
deltaRequests: 0,
|
|
447
|
+
lastInputItems: 0,
|
|
448
|
+
websocketFailures: 0,
|
|
449
|
+
sseFallbacks: 0,
|
|
450
|
+
};
|
|
451
|
+
websocketDebugStats.set(sessionId, stats);
|
|
452
|
+
}
|
|
453
|
+
return stats;
|
|
454
|
+
}
|
|
455
|
+
export function getOpenAICodexWebSocketDebugStats(sessionId) {
|
|
456
|
+
const stats = websocketDebugStats.get(sessionId);
|
|
457
|
+
return stats ? { ...stats } : undefined;
|
|
458
|
+
}
|
|
459
|
+
export function resetOpenAICodexWebSocketDebugStats(sessionId) {
|
|
460
|
+
if (sessionId) {
|
|
461
|
+
websocketDebugStats.delete(sessionId);
|
|
462
|
+
websocketSseFallbackSessions.delete(sessionId);
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
websocketDebugStats.clear();
|
|
466
|
+
websocketSseFallbackSessions.clear();
|
|
467
|
+
}
|
|
468
|
+
export function closeOpenAICodexWebSocketSessions(sessionId) {
|
|
469
|
+
const closeEntry = (entry) => {
|
|
470
|
+
if (entry.idleTimer)
|
|
471
|
+
clearTimeout(entry.idleTimer);
|
|
472
|
+
closeWebSocketSilently(entry.socket, 1000, "debug_close");
|
|
473
|
+
};
|
|
474
|
+
if (sessionId) {
|
|
475
|
+
const entry = websocketSessionCache.get(sessionId);
|
|
476
|
+
if (entry)
|
|
477
|
+
closeEntry(entry);
|
|
478
|
+
websocketSessionCache.delete(sessionId);
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
for (const entry of websocketSessionCache.values()) {
|
|
482
|
+
closeEntry(entry);
|
|
483
|
+
}
|
|
484
|
+
websocketSessionCache.clear();
|
|
485
|
+
}
|
|
486
|
+
registerSessionResourceCleanup(closeOpenAICodexWebSocketSessions);
|
|
487
|
+
function isWebSocketSseFallbackActive(sessionId) {
|
|
488
|
+
return sessionId ? websocketSseFallbackSessions.has(sessionId) : false;
|
|
489
|
+
}
|
|
490
|
+
function recordWebSocketSseFallback(sessionId) {
|
|
491
|
+
if (!sessionId)
|
|
492
|
+
return;
|
|
493
|
+
const stats = getOrCreateWebSocketDebugStats(sessionId);
|
|
494
|
+
stats.sseFallbacks++;
|
|
495
|
+
stats.websocketFallbackActive = isWebSocketSseFallbackActive(sessionId);
|
|
496
|
+
}
|
|
497
|
+
function recordWebSocketFailure(sessionId, error) {
|
|
498
|
+
if (!sessionId)
|
|
499
|
+
return;
|
|
500
|
+
websocketSseFallbackSessions.add(sessionId);
|
|
501
|
+
const stats = getOrCreateWebSocketDebugStats(sessionId);
|
|
502
|
+
stats.websocketFailures++;
|
|
503
|
+
stats.lastWebSocketError = formatThrownValue(error);
|
|
504
|
+
stats.websocketFallbackActive = true;
|
|
505
|
+
}
|
|
506
|
+
let _cachedWebsocket = null;
|
|
507
|
+
async function getWebSocketConstructor() {
|
|
508
|
+
if (_cachedWebsocket)
|
|
509
|
+
return _cachedWebsocket;
|
|
510
|
+
// bun doesn't respect http proxy envs, ref: https://github.com/oven-sh/bun/issues/15489
|
|
511
|
+
// TODO: remove this when bun supports proxy envs in websocket.
|
|
512
|
+
if (process?.versions?.bun &&
|
|
513
|
+
(process.env.HTTP_PROXY || process.env.HTTPS_PROXY || process.env.http_proxy || process.env.https_proxy)) {
|
|
514
|
+
const m = await dynamicImport("proxy-from-env");
|
|
515
|
+
const getProxyForUrl = m.getProxyForUrl;
|
|
516
|
+
_cachedWebsocket = class extends WebSocket {
|
|
517
|
+
constructor(url, options) {
|
|
518
|
+
let _opts = {};
|
|
519
|
+
if (Array.isArray(options) || typeof options === "string") {
|
|
520
|
+
_opts = { protocols: options };
|
|
521
|
+
}
|
|
522
|
+
else {
|
|
523
|
+
_opts = { ...options };
|
|
524
|
+
}
|
|
525
|
+
const proxy = getProxyForUrl(url.toString().replace(/^wss:/, "https:").replace(/^ws:/, "http:"));
|
|
526
|
+
super(url, { ..._opts, ...(proxy ? { proxy } : {}) });
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
return _cachedWebsocket;
|
|
530
|
+
}
|
|
330
531
|
const ctor = globalThis.WebSocket;
|
|
331
532
|
if (typeof ctor !== "function")
|
|
332
533
|
return null;
|
|
333
534
|
return ctor;
|
|
334
535
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
536
|
+
class WebSocketCloseError extends Error {
|
|
537
|
+
code;
|
|
538
|
+
reason;
|
|
539
|
+
wasClean;
|
|
540
|
+
constructor(message, options) {
|
|
541
|
+
super(message);
|
|
542
|
+
this.name = "WebSocketCloseError";
|
|
543
|
+
this.code = options?.code;
|
|
544
|
+
this.reason = options?.reason;
|
|
545
|
+
this.wasClean = options?.wasClean;
|
|
339
546
|
}
|
|
340
|
-
return out;
|
|
341
547
|
}
|
|
342
548
|
function getWebSocketReadyState(socket) {
|
|
343
549
|
const readyState = socket.readyState;
|
|
@@ -366,12 +572,12 @@ function scheduleSessionWebSocketExpiry(sessionId, entry) {
|
|
|
366
572
|
}, SESSION_WEBSOCKET_CACHE_TTL_MS);
|
|
367
573
|
}
|
|
368
574
|
async function connectWebSocket(url, headers, signal) {
|
|
369
|
-
const WebSocketCtor = getWebSocketConstructor();
|
|
575
|
+
const WebSocketCtor = await getWebSocketConstructor();
|
|
370
576
|
if (!WebSocketCtor) {
|
|
371
577
|
throw new Error("WebSocket transport is not available in this runtime");
|
|
372
578
|
}
|
|
373
579
|
const wsHeaders = headersToRecord(headers);
|
|
374
|
-
wsHeaders["OpenAI-Beta"]
|
|
580
|
+
delete wsHeaders["OpenAI-Beta"];
|
|
375
581
|
return new Promise((resolve, reject) => {
|
|
376
582
|
let settled = false;
|
|
377
583
|
let socket;
|
|
@@ -390,18 +596,20 @@ async function connectWebSocket(url, headers, signal) {
|
|
|
390
596
|
resolve(socket);
|
|
391
597
|
};
|
|
392
598
|
const onError = (event) => {
|
|
599
|
+
const error = extractWebSocketError(event);
|
|
393
600
|
if (settled)
|
|
394
601
|
return;
|
|
395
602
|
settled = true;
|
|
396
603
|
cleanup();
|
|
397
|
-
reject(
|
|
604
|
+
reject(error);
|
|
398
605
|
};
|
|
399
606
|
const onClose = (event) => {
|
|
607
|
+
const error = extractWebSocketCloseError(event);
|
|
400
608
|
if (settled)
|
|
401
609
|
return;
|
|
402
610
|
settled = true;
|
|
403
611
|
cleanup();
|
|
404
|
-
reject(
|
|
612
|
+
reject(error);
|
|
405
613
|
};
|
|
406
614
|
const onAbort = () => {
|
|
407
615
|
if (settled)
|
|
@@ -428,6 +636,7 @@ async function acquireWebSocket(url, headers, sessionId, signal) {
|
|
|
428
636
|
const socket = await connectWebSocket(url, headers, signal);
|
|
429
637
|
return {
|
|
430
638
|
socket,
|
|
639
|
+
reused: false,
|
|
431
640
|
release: ({ keep } = {}) => {
|
|
432
641
|
if (keep === false) {
|
|
433
642
|
closeWebSocketSilently(socket);
|
|
@@ -447,6 +656,8 @@ async function acquireWebSocket(url, headers, sessionId, signal) {
|
|
|
447
656
|
cached.busy = true;
|
|
448
657
|
return {
|
|
449
658
|
socket: cached.socket,
|
|
659
|
+
entry: cached,
|
|
660
|
+
reused: true,
|
|
450
661
|
release: ({ keep } = {}) => {
|
|
451
662
|
if (!keep || !isWebSocketReusable(cached.socket)) {
|
|
452
663
|
closeWebSocketSilently(cached.socket);
|
|
@@ -462,6 +673,7 @@ async function acquireWebSocket(url, headers, sessionId, signal) {
|
|
|
462
673
|
const socket = await connectWebSocket(url, headers, signal);
|
|
463
674
|
return {
|
|
464
675
|
socket,
|
|
676
|
+
reused: false,
|
|
465
677
|
release: () => {
|
|
466
678
|
closeWebSocketSilently(socket);
|
|
467
679
|
},
|
|
@@ -477,6 +689,8 @@ async function acquireWebSocket(url, headers, sessionId, signal) {
|
|
|
477
689
|
websocketSessionCache.set(sessionId, entry);
|
|
478
690
|
return {
|
|
479
691
|
socket,
|
|
692
|
+
entry,
|
|
693
|
+
reused: false,
|
|
480
694
|
release: ({ keep } = {}) => {
|
|
481
695
|
if (!keep || !isWebSocketReusable(entry.socket)) {
|
|
482
696
|
closeWebSocketSilently(entry.socket);
|
|
@@ -493,11 +707,21 @@ async function acquireWebSocket(url, headers, sessionId, signal) {
|
|
|
493
707
|
};
|
|
494
708
|
}
|
|
495
709
|
function extractWebSocketError(event) {
|
|
496
|
-
if (event && typeof event === "object"
|
|
497
|
-
const message = event.message;
|
|
710
|
+
if (event && typeof event === "object") {
|
|
711
|
+
const message = "message" in event ? event.message : undefined;
|
|
498
712
|
if (typeof message === "string" && message.length > 0) {
|
|
499
713
|
return new Error(message);
|
|
500
714
|
}
|
|
715
|
+
const nestedError = "error" in event ? event.error : undefined;
|
|
716
|
+
if (nestedError instanceof Error && nestedError.message.length > 0) {
|
|
717
|
+
return nestedError;
|
|
718
|
+
}
|
|
719
|
+
if (nestedError && typeof nestedError === "object" && "message" in nestedError) {
|
|
720
|
+
const nestedMessage = nestedError.message;
|
|
721
|
+
if (typeof nestedMessage === "string" && nestedMessage.length > 0) {
|
|
722
|
+
return new Error(nestedMessage);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
501
725
|
}
|
|
502
726
|
return new Error("WebSocket error");
|
|
503
727
|
}
|
|
@@ -505,9 +729,17 @@ function extractWebSocketCloseError(event) {
|
|
|
505
729
|
if (event && typeof event === "object") {
|
|
506
730
|
const code = "code" in event ? event.code : undefined;
|
|
507
731
|
const reason = "reason" in event ? event.reason : undefined;
|
|
732
|
+
const wasClean = "wasClean" in event ? event.wasClean : undefined;
|
|
508
733
|
const codeText = typeof code === "number" ? ` ${code}` : "";
|
|
509
|
-
|
|
510
|
-
|
|
734
|
+
let reasonText = typeof reason === "string" && reason.length > 0 ? ` ${reason}` : "";
|
|
735
|
+
if (!reasonText && code === WEBSOCKET_MESSAGE_TOO_BIG_CLOSE_CODE) {
|
|
736
|
+
reasonText = " message too big";
|
|
737
|
+
}
|
|
738
|
+
return new WebSocketCloseError(`WebSocket closed${codeText}${reasonText}`.trim(), {
|
|
739
|
+
code: typeof code === "number" ? code : undefined,
|
|
740
|
+
reason: typeof reason === "string" && reason.length > 0 ? reason : undefined,
|
|
741
|
+
wasClean: typeof wasClean === "boolean" ? wasClean : undefined,
|
|
742
|
+
});
|
|
511
743
|
}
|
|
512
744
|
return new Error("WebSocket closed");
|
|
513
745
|
}
|
|
@@ -543,22 +775,30 @@ async function* parseWebSocket(socket, signal) {
|
|
|
543
775
|
};
|
|
544
776
|
const onMessage = (event) => {
|
|
545
777
|
void (async () => {
|
|
546
|
-
|
|
547
|
-
return;
|
|
548
|
-
const text = await decodeWebSocketData(event.data);
|
|
549
|
-
if (!text)
|
|
550
|
-
return;
|
|
778
|
+
let text = null;
|
|
551
779
|
try {
|
|
780
|
+
if (!event || typeof event !== "object" || !("data" in event))
|
|
781
|
+
return;
|
|
782
|
+
text = await decodeWebSocketData(event.data);
|
|
783
|
+
if (!text)
|
|
784
|
+
return;
|
|
552
785
|
const parsed = JSON.parse(text);
|
|
553
786
|
const type = typeof parsed.type === "string" ? parsed.type : "";
|
|
554
|
-
if (type === "response.completed" || type === "response.done") {
|
|
787
|
+
if (type === "response.completed" || type === "response.done" || type === "response.incomplete") {
|
|
555
788
|
sawCompletion = true;
|
|
556
789
|
done = true;
|
|
557
790
|
}
|
|
558
791
|
queue.push(parsed);
|
|
559
792
|
wake();
|
|
560
793
|
}
|
|
561
|
-
catch {
|
|
794
|
+
catch (cause) {
|
|
795
|
+
failed = new CodexProtocolError(`Invalid Codex WebSocket JSON: ${formatThrownValue(cause)}`, {
|
|
796
|
+
cause,
|
|
797
|
+
payload: text,
|
|
798
|
+
});
|
|
799
|
+
done = true;
|
|
800
|
+
wake();
|
|
801
|
+
}
|
|
562
802
|
})();
|
|
563
803
|
};
|
|
564
804
|
const onError = (event) => {
|
|
@@ -616,19 +856,114 @@ async function* parseWebSocket(socket, signal) {
|
|
|
616
856
|
signal?.removeEventListener("abort", onAbort);
|
|
617
857
|
}
|
|
618
858
|
}
|
|
859
|
+
function requestBodyWithoutInput(body) {
|
|
860
|
+
const { input: _input, previous_response_id: _previousResponseId, ...rest } = body;
|
|
861
|
+
return rest;
|
|
862
|
+
}
|
|
863
|
+
function responseInputsEqual(a, b) {
|
|
864
|
+
return JSON.stringify(a ?? []) === JSON.stringify(b ?? []);
|
|
865
|
+
}
|
|
866
|
+
function requestBodiesMatchExceptInput(a, b) {
|
|
867
|
+
return JSON.stringify(requestBodyWithoutInput(a)) === JSON.stringify(requestBodyWithoutInput(b));
|
|
868
|
+
}
|
|
869
|
+
function getCachedWebSocketInputDelta(body, continuation) {
|
|
870
|
+
if (!requestBodiesMatchExceptInput(body, continuation.lastRequestBody)) {
|
|
871
|
+
return undefined;
|
|
872
|
+
}
|
|
873
|
+
const currentInput = body.input ?? [];
|
|
874
|
+
const baseline = [...(continuation.lastRequestBody.input ?? []), ...continuation.lastResponseItems];
|
|
875
|
+
if (currentInput.length < baseline.length) {
|
|
876
|
+
return undefined;
|
|
877
|
+
}
|
|
878
|
+
const prefix = currentInput.slice(0, baseline.length);
|
|
879
|
+
if (!responseInputsEqual(prefix, baseline)) {
|
|
880
|
+
return undefined;
|
|
881
|
+
}
|
|
882
|
+
return currentInput.slice(baseline.length);
|
|
883
|
+
}
|
|
884
|
+
function buildCachedWebSocketRequestBody(entry, body) {
|
|
885
|
+
const continuation = entry.continuation;
|
|
886
|
+
if (!continuation) {
|
|
887
|
+
return body;
|
|
888
|
+
}
|
|
889
|
+
const delta = getCachedWebSocketInputDelta(body, continuation);
|
|
890
|
+
if (!delta || !continuation.lastResponseId) {
|
|
891
|
+
entry.continuation = undefined;
|
|
892
|
+
return body;
|
|
893
|
+
}
|
|
894
|
+
return {
|
|
895
|
+
...body,
|
|
896
|
+
previous_response_id: continuation.lastResponseId,
|
|
897
|
+
input: delta,
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
async function* startWebSocketOutputOnFirstEvent(events, output, stream, onStart) {
|
|
901
|
+
let started = false;
|
|
902
|
+
for await (const event of events) {
|
|
903
|
+
if (!started) {
|
|
904
|
+
started = true;
|
|
905
|
+
onStart();
|
|
906
|
+
stream.push({ type: "start", partial: output });
|
|
907
|
+
}
|
|
908
|
+
yield event;
|
|
909
|
+
}
|
|
910
|
+
}
|
|
619
911
|
async function processWebSocketStream(url, body, headers, output, stream, model, onStart, options) {
|
|
620
|
-
const { socket, release } = await acquireWebSocket(url, headers, options?.sessionId, options?.signal);
|
|
912
|
+
const { socket, entry, reused, release } = await acquireWebSocket(url, headers, options?.sessionId, options?.signal);
|
|
621
913
|
let keepConnection = true;
|
|
914
|
+
const useCachedContext = options?.transport === "websocket-cached" || options?.transport === "auto";
|
|
915
|
+
// ChatGPT Codex Responses rejects `store: true` ("Store must be set to false").
|
|
916
|
+
// WebSocket continuation still works via connection-scoped previous_response_id state.
|
|
917
|
+
const fullBody = body;
|
|
918
|
+
const requestBody = useCachedContext && entry ? buildCachedWebSocketRequestBody(entry, fullBody) : fullBody;
|
|
919
|
+
const stats = options?.sessionId ? getOrCreateWebSocketDebugStats(options.sessionId) : undefined;
|
|
920
|
+
if (stats) {
|
|
921
|
+
stats.requests++;
|
|
922
|
+
if (reused)
|
|
923
|
+
stats.connectionsReused++;
|
|
924
|
+
else
|
|
925
|
+
stats.connectionsCreated++;
|
|
926
|
+
if (useCachedContext)
|
|
927
|
+
stats.cachedContextRequests++;
|
|
928
|
+
if (requestBody.store === true)
|
|
929
|
+
stats.storeTrueRequests++;
|
|
930
|
+
stats.lastInputItems = requestBody.input?.length ?? 0;
|
|
931
|
+
if (requestBody.previous_response_id) {
|
|
932
|
+
stats.deltaRequests++;
|
|
933
|
+
stats.lastDeltaInputItems = requestBody.input?.length ?? 0;
|
|
934
|
+
stats.lastPreviousResponseId = requestBody.previous_response_id;
|
|
935
|
+
}
|
|
936
|
+
else {
|
|
937
|
+
stats.fullContextRequests++;
|
|
938
|
+
stats.lastDeltaInputItems = undefined;
|
|
939
|
+
stats.lastPreviousResponseId = undefined;
|
|
940
|
+
}
|
|
941
|
+
}
|
|
622
942
|
try {
|
|
623
|
-
socket.send(JSON.stringify({ type: "response.create", ...
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
943
|
+
socket.send(JSON.stringify({ type: "response.create", ...requestBody }));
|
|
944
|
+
await processResponsesStream(startWebSocketOutputOnFirstEvent(mapCodexEvents(parseWebSocket(socket, options?.signal)), output, stream, onStart), output, stream, model, {
|
|
945
|
+
serviceTier: options?.serviceTier,
|
|
946
|
+
resolveServiceTier: resolveCodexServiceTier,
|
|
947
|
+
applyServiceTierPricing: (usage, serviceTier) => applyServiceTierPricing(usage, serviceTier, model),
|
|
948
|
+
});
|
|
627
949
|
if (options?.signal?.aborted) {
|
|
628
950
|
keepConnection = false;
|
|
629
951
|
}
|
|
952
|
+
else if (useCachedContext && entry && output.responseId) {
|
|
953
|
+
const responseItems = convertResponsesMessages(model, { messages: [output] }, CODEX_TOOL_CALL_PROVIDERS, {
|
|
954
|
+
includeSystemPrompt: false,
|
|
955
|
+
}).filter((item) => item.type !== "function_call_output");
|
|
956
|
+
entry.continuation = {
|
|
957
|
+
lastRequestBody: fullBody,
|
|
958
|
+
lastResponseId: output.responseId,
|
|
959
|
+
lastResponseItems: responseItems,
|
|
960
|
+
};
|
|
961
|
+
}
|
|
630
962
|
}
|
|
631
963
|
catch (error) {
|
|
964
|
+
if (entry) {
|
|
965
|
+
entry.continuation = undefined;
|
|
966
|
+
}
|
|
632
967
|
keepConnection = false;
|
|
633
968
|
throw error;
|
|
634
969
|
}
|
|
@@ -680,22 +1015,44 @@ function extractAccountId(token) {
|
|
|
680
1015
|
throw new Error("Failed to extract accountId from token");
|
|
681
1016
|
}
|
|
682
1017
|
}
|
|
683
|
-
function
|
|
1018
|
+
function createCodexRequestId() {
|
|
1019
|
+
if (typeof globalThis.crypto?.randomUUID === "function") {
|
|
1020
|
+
return globalThis.crypto.randomUUID();
|
|
1021
|
+
}
|
|
1022
|
+
return `codex_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
1023
|
+
}
|
|
1024
|
+
function buildBaseCodexHeaders(initHeaders, additionalHeaders, accountId, token) {
|
|
684
1025
|
const headers = new Headers(initHeaders);
|
|
1026
|
+
for (const [key, value] of Object.entries(additionalHeaders || {})) {
|
|
1027
|
+
headers.set(key, value);
|
|
1028
|
+
}
|
|
685
1029
|
headers.set("Authorization", `Bearer ${token}`);
|
|
686
1030
|
headers.set("chatgpt-account-id", accountId);
|
|
687
|
-
headers.set("OpenAI-Beta", "responses=experimental");
|
|
688
1031
|
headers.set("originator", "pi");
|
|
689
1032
|
const userAgent = _os ? `pi (${_os.platform()} ${_os.release()}; ${_os.arch()})` : "pi (browser)";
|
|
690
1033
|
headers.set("User-Agent", userAgent);
|
|
1034
|
+
return headers;
|
|
1035
|
+
}
|
|
1036
|
+
function buildSSEHeaders(initHeaders, additionalHeaders, accountId, token, sessionId) {
|
|
1037
|
+
const headers = buildBaseCodexHeaders(initHeaders, additionalHeaders, accountId, token);
|
|
1038
|
+
headers.set("OpenAI-Beta", "responses=experimental");
|
|
691
1039
|
headers.set("accept", "text/event-stream");
|
|
692
1040
|
headers.set("content-type", "application/json");
|
|
693
|
-
for (const [key, value] of Object.entries(additionalHeaders || {})) {
|
|
694
|
-
headers.set(key, value);
|
|
695
|
-
}
|
|
696
1041
|
if (sessionId) {
|
|
697
1042
|
headers.set("session_id", sessionId);
|
|
1043
|
+
headers.set("x-client-request-id", sessionId);
|
|
698
1044
|
}
|
|
699
1045
|
return headers;
|
|
700
1046
|
}
|
|
1047
|
+
function buildWebSocketHeaders(initHeaders, additionalHeaders, accountId, token, requestId) {
|
|
1048
|
+
const headers = buildBaseCodexHeaders(initHeaders, additionalHeaders, accountId, token);
|
|
1049
|
+
headers.delete("accept");
|
|
1050
|
+
headers.delete("content-type");
|
|
1051
|
+
headers.delete("OpenAI-Beta");
|
|
1052
|
+
headers.delete("openai-beta");
|
|
1053
|
+
headers.set("OpenAI-Beta", OPENAI_BETA_RESPONSES_WEBSOCKETS);
|
|
1054
|
+
headers.set("x-client-request-id", requestId);
|
|
1055
|
+
headers.set("session_id", requestId);
|
|
1056
|
+
return headers;
|
|
1057
|
+
}
|
|
701
1058
|
//# sourceMappingURL=openai-codex-responses.js.map
|