@corbat-tech/coco 2.11.1 → 2.13.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/dist/cli/index.js +475 -134
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +439 -119
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -1424,7 +1424,7 @@ var init_openai = __esm({
|
|
|
1424
1424
|
"src/providers/openai.ts"() {
|
|
1425
1425
|
init_errors();
|
|
1426
1426
|
init_retry();
|
|
1427
|
-
DEFAULT_MODEL2 = "gpt-5.
|
|
1427
|
+
DEFAULT_MODEL2 = "gpt-5.4-codex";
|
|
1428
1428
|
CONTEXT_WINDOWS2 = {
|
|
1429
1429
|
// OpenAI models
|
|
1430
1430
|
"gpt-4o": 128e3,
|
|
@@ -1447,6 +1447,7 @@ var init_openai = __esm({
|
|
|
1447
1447
|
"gpt-5.2-instant": 4e5,
|
|
1448
1448
|
"gpt-5.2-pro": 4e5,
|
|
1449
1449
|
"gpt-5.3-codex": 4e5,
|
|
1450
|
+
"gpt-5.4-codex": 4e5,
|
|
1450
1451
|
// Kimi/Moonshot models
|
|
1451
1452
|
"kimi-k2.5": 262144,
|
|
1452
1453
|
"kimi-k2-0324": 131072,
|
|
@@ -1503,7 +1504,7 @@ var init_openai = __esm({
|
|
|
1503
1504
|
"microsoft/Phi-4": 16384,
|
|
1504
1505
|
// OpenRouter model IDs
|
|
1505
1506
|
"anthropic/claude-opus-4-6": 2e5,
|
|
1506
|
-
"openai/gpt-5.
|
|
1507
|
+
"openai/gpt-5.4-codex": 4e5,
|
|
1507
1508
|
"google/gemini-3-flash-preview": 1e6,
|
|
1508
1509
|
"meta-llama/llama-3.3-70b-instruct": 128e3
|
|
1509
1510
|
};
|
|
@@ -1755,7 +1756,7 @@ var init_openai = __esm({
|
|
|
1755
1756
|
once: true
|
|
1756
1757
|
});
|
|
1757
1758
|
const providerName = this.name;
|
|
1758
|
-
const
|
|
1759
|
+
const parseArguments2 = (builder) => {
|
|
1759
1760
|
let input = {};
|
|
1760
1761
|
try {
|
|
1761
1762
|
input = builder.arguments ? JSON.parse(builder.arguments) : {};
|
|
@@ -1836,7 +1837,7 @@ var init_openai = __esm({
|
|
|
1836
1837
|
toolCall: {
|
|
1837
1838
|
id: builder.id,
|
|
1838
1839
|
name: builder.name,
|
|
1839
|
-
input:
|
|
1840
|
+
input: parseArguments2(builder)
|
|
1840
1841
|
}
|
|
1841
1842
|
};
|
|
1842
1843
|
}
|
|
@@ -1849,7 +1850,7 @@ var init_openai = __esm({
|
|
|
1849
1850
|
toolCall: {
|
|
1850
1851
|
id: builder.id,
|
|
1851
1852
|
name: builder.name,
|
|
1852
|
-
input:
|
|
1853
|
+
input: parseArguments2(builder)
|
|
1853
1854
|
}
|
|
1854
1855
|
};
|
|
1855
1856
|
}
|
|
@@ -4060,8 +4061,6 @@ var init_auth = __esm({
|
|
|
4060
4061
|
init_gcloud();
|
|
4061
4062
|
}
|
|
4062
4063
|
});
|
|
4063
|
-
|
|
4064
|
-
// src/providers/codex.ts
|
|
4065
4064
|
function parseJwtClaims(token) {
|
|
4066
4065
|
const parts = token.split(".");
|
|
4067
4066
|
if (parts.length !== 3 || !parts[1]) return void 0;
|
|
@@ -4077,6 +4076,21 @@ function extractAccountId(accessToken) {
|
|
|
4077
4076
|
const auth = claims["https://api.openai.com/auth"];
|
|
4078
4077
|
return claims["chatgpt_account_id"] || auth?.["chatgpt_account_id"] || claims["organizations"]?.[0]?.id;
|
|
4079
4078
|
}
|
|
4079
|
+
function parseArguments(args) {
|
|
4080
|
+
try {
|
|
4081
|
+
return args ? JSON.parse(args) : {};
|
|
4082
|
+
} catch {
|
|
4083
|
+
try {
|
|
4084
|
+
if (args) {
|
|
4085
|
+
const repaired = jsonrepair(args);
|
|
4086
|
+
return JSON.parse(repaired);
|
|
4087
|
+
}
|
|
4088
|
+
} catch {
|
|
4089
|
+
console.error(`[Codex] Cannot parse tool arguments: ${args.slice(0, 200)}`);
|
|
4090
|
+
}
|
|
4091
|
+
return {};
|
|
4092
|
+
}
|
|
4093
|
+
}
|
|
4080
4094
|
function createCodexProvider(config) {
|
|
4081
4095
|
const provider = new CodexProvider();
|
|
4082
4096
|
if (config) {
|
|
@@ -4085,14 +4099,16 @@ function createCodexProvider(config) {
|
|
|
4085
4099
|
}
|
|
4086
4100
|
return provider;
|
|
4087
4101
|
}
|
|
4088
|
-
var CODEX_API_ENDPOINT, DEFAULT_MODEL3, CONTEXT_WINDOWS3, CodexProvider;
|
|
4102
|
+
var CODEX_API_ENDPOINT, DEFAULT_MODEL3, CONTEXT_WINDOWS3, STREAM_TIMEOUT_MS, CodexProvider;
|
|
4089
4103
|
var init_codex = __esm({
|
|
4090
4104
|
"src/providers/codex.ts"() {
|
|
4091
4105
|
init_errors();
|
|
4092
4106
|
init_auth();
|
|
4107
|
+
init_retry();
|
|
4093
4108
|
CODEX_API_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses";
|
|
4094
|
-
DEFAULT_MODEL3 = "gpt-5.
|
|
4109
|
+
DEFAULT_MODEL3 = "gpt-5.4-codex";
|
|
4095
4110
|
CONTEXT_WINDOWS3 = {
|
|
4111
|
+
"gpt-5.4-codex": 2e5,
|
|
4096
4112
|
"gpt-5.3-codex": 2e5,
|
|
4097
4113
|
"gpt-5.2-codex": 2e5,
|
|
4098
4114
|
"gpt-5-codex": 2e5,
|
|
@@ -4101,12 +4117,14 @@ var init_codex = __esm({
|
|
|
4101
4117
|
"gpt-5.2": 2e5,
|
|
4102
4118
|
"gpt-5.1": 2e5
|
|
4103
4119
|
};
|
|
4120
|
+
STREAM_TIMEOUT_MS = 12e4;
|
|
4104
4121
|
CodexProvider = class {
|
|
4105
4122
|
id = "codex";
|
|
4106
4123
|
name = "OpenAI Codex (ChatGPT Plus/Pro)";
|
|
4107
4124
|
config = {};
|
|
4108
4125
|
accessToken = null;
|
|
4109
4126
|
accountId;
|
|
4127
|
+
retryConfig = DEFAULT_RETRY_CONFIG;
|
|
4110
4128
|
/**
|
|
4111
4129
|
* Initialize the provider with OAuth tokens
|
|
4112
4130
|
*/
|
|
@@ -4191,166 +4209,466 @@ var init_codex = __esm({
|
|
|
4191
4209
|
/**
|
|
4192
4210
|
* Extract text content from a message
|
|
4193
4211
|
*/
|
|
4194
|
-
|
|
4195
|
-
if (typeof
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
if (Array.isArray(msg.content)) {
|
|
4199
|
-
return msg.content.map((part) => {
|
|
4200
|
-
if (part.type === "text") return part.text;
|
|
4201
|
-
if (part.type === "tool_result") return `Tool result: ${JSON.stringify(part.content)}`;
|
|
4202
|
-
return "";
|
|
4203
|
-
}).join("\n");
|
|
4212
|
+
contentToString(content) {
|
|
4213
|
+
if (typeof content === "string") return content;
|
|
4214
|
+
if (Array.isArray(content)) {
|
|
4215
|
+
return content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
|
|
4204
4216
|
}
|
|
4205
4217
|
return "";
|
|
4206
4218
|
}
|
|
4207
4219
|
/**
|
|
4208
|
-
* Convert messages to
|
|
4209
|
-
* Codex uses a different format than Chat Completions:
|
|
4210
|
-
* {
|
|
4211
|
-
* "input": [
|
|
4212
|
-
* { "type": "message", "role": "developer|user", "content": [{ "type": "input_text", "text": "..." }] },
|
|
4213
|
-
* { "type": "message", "role": "assistant", "content": [{ "type": "output_text", "text": "..." }] }
|
|
4214
|
-
* ]
|
|
4215
|
-
* }
|
|
4220
|
+
* Convert messages to Responses API input format.
|
|
4216
4221
|
*
|
|
4217
|
-
*
|
|
4222
|
+
* Handles:
|
|
4223
|
+
* - system messages → extracted as instructions
|
|
4224
|
+
* - user text messages → { role: "user", content: "..." }
|
|
4225
|
+
* - user tool_result messages → function_call_output items
|
|
4226
|
+
* - assistant text → { role: "assistant", content: "..." }
|
|
4227
|
+
* - assistant tool_use → function_call items
|
|
4218
4228
|
*/
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4229
|
+
convertToResponsesInput(messages, systemPrompt) {
|
|
4230
|
+
const input = [];
|
|
4231
|
+
let instructions = systemPrompt ?? null;
|
|
4232
|
+
for (const msg of messages) {
|
|
4233
|
+
if (msg.role === "system") {
|
|
4234
|
+
instructions = (instructions ? instructions + "\n\n" : "") + this.contentToString(msg.content);
|
|
4235
|
+
} else if (msg.role === "user") {
|
|
4236
|
+
if (Array.isArray(msg.content) && msg.content.some((b) => b.type === "tool_result")) {
|
|
4237
|
+
for (const block of msg.content) {
|
|
4238
|
+
if (block.type === "tool_result") {
|
|
4239
|
+
const tr = block;
|
|
4240
|
+
input.push({
|
|
4241
|
+
type: "function_call_output",
|
|
4242
|
+
call_id: tr.tool_use_id,
|
|
4243
|
+
output: tr.content
|
|
4244
|
+
});
|
|
4245
|
+
}
|
|
4246
|
+
}
|
|
4247
|
+
} else {
|
|
4248
|
+
input.push({
|
|
4249
|
+
role: "user",
|
|
4250
|
+
content: this.contentToString(msg.content)
|
|
4251
|
+
});
|
|
4252
|
+
}
|
|
4253
|
+
} else if (msg.role === "assistant") {
|
|
4254
|
+
if (typeof msg.content === "string") {
|
|
4255
|
+
input.push({ role: "assistant", content: msg.content });
|
|
4256
|
+
} else if (Array.isArray(msg.content)) {
|
|
4257
|
+
const textParts = [];
|
|
4258
|
+
for (const block of msg.content) {
|
|
4259
|
+
if (block.type === "text") {
|
|
4260
|
+
textParts.push(block.text);
|
|
4261
|
+
} else if (block.type === "tool_use") {
|
|
4262
|
+
if (textParts.length > 0) {
|
|
4263
|
+
input.push({ role: "assistant", content: textParts.join("") });
|
|
4264
|
+
textParts.length = 0;
|
|
4265
|
+
}
|
|
4266
|
+
const tu = block;
|
|
4267
|
+
input.push({
|
|
4268
|
+
type: "function_call",
|
|
4269
|
+
call_id: tu.id,
|
|
4270
|
+
name: tu.name,
|
|
4271
|
+
arguments: JSON.stringify(tu.input)
|
|
4272
|
+
});
|
|
4273
|
+
}
|
|
4274
|
+
}
|
|
4275
|
+
if (textParts.length > 0) {
|
|
4276
|
+
input.push({ role: "assistant", content: textParts.join("") });
|
|
4277
|
+
}
|
|
4278
|
+
}
|
|
4279
|
+
}
|
|
4280
|
+
}
|
|
4281
|
+
return { input, instructions };
|
|
4230
4282
|
}
|
|
4231
4283
|
/**
|
|
4232
|
-
*
|
|
4284
|
+
* Convert tool definitions to Responses API function tool format
|
|
4233
4285
|
*/
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4286
|
+
convertTools(tools) {
|
|
4287
|
+
return tools.map((tool) => ({
|
|
4288
|
+
type: "function",
|
|
4289
|
+
name: tool.name,
|
|
4290
|
+
description: tool.description ?? void 0,
|
|
4291
|
+
parameters: tool.input_schema ?? null,
|
|
4292
|
+
strict: false
|
|
4293
|
+
}));
|
|
4294
|
+
}
|
|
4295
|
+
/**
|
|
4296
|
+
* Build the request body for the Codex Responses API
|
|
4297
|
+
*/
|
|
4298
|
+
buildRequestBody(model, input, instructions, options) {
|
|
4239
4299
|
const body = {
|
|
4240
4300
|
model,
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4301
|
+
input,
|
|
4302
|
+
instructions: instructions ?? "You are a helpful coding assistant.",
|
|
4303
|
+
max_output_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
|
|
4304
|
+
temperature: options?.temperature ?? this.config.temperature ?? 0,
|
|
4244
4305
|
store: false,
|
|
4245
4306
|
stream: true
|
|
4246
4307
|
// Codex API requires streaming
|
|
4247
4308
|
};
|
|
4248
|
-
|
|
4309
|
+
if (options?.tools && options.tools.length > 0) {
|
|
4310
|
+
body.tools = this.convertTools(options.tools);
|
|
4311
|
+
}
|
|
4312
|
+
return body;
|
|
4313
|
+
}
|
|
4314
|
+
/**
|
|
4315
|
+
* Read SSE stream and call handler for each parsed event.
|
|
4316
|
+
* Returns when stream ends.
|
|
4317
|
+
*/
|
|
4318
|
+
async readSSEStream(response, onEvent) {
|
|
4249
4319
|
if (!response.body) {
|
|
4250
|
-
throw new ProviderError("No response body from Codex API", {
|
|
4251
|
-
provider: this.id
|
|
4252
|
-
});
|
|
4320
|
+
throw new ProviderError("No response body from Codex API", { provider: this.id });
|
|
4253
4321
|
}
|
|
4254
4322
|
const reader = response.body.getReader();
|
|
4255
4323
|
const decoder = new TextDecoder();
|
|
4256
4324
|
let buffer = "";
|
|
4257
|
-
let
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4325
|
+
let lastActivityTime = Date.now();
|
|
4326
|
+
const timeoutController = new AbortController();
|
|
4327
|
+
const timeoutInterval = setInterval(() => {
|
|
4328
|
+
if (Date.now() - lastActivityTime > STREAM_TIMEOUT_MS) {
|
|
4329
|
+
clearInterval(timeoutInterval);
|
|
4330
|
+
timeoutController.abort();
|
|
4331
|
+
}
|
|
4332
|
+
}, 5e3);
|
|
4262
4333
|
try {
|
|
4263
4334
|
while (true) {
|
|
4335
|
+
if (timeoutController.signal.aborted) break;
|
|
4264
4336
|
const { done, value } = await reader.read();
|
|
4265
4337
|
if (done) break;
|
|
4338
|
+
lastActivityTime = Date.now();
|
|
4266
4339
|
buffer += decoder.decode(value, { stream: true });
|
|
4267
4340
|
const lines = buffer.split("\n");
|
|
4268
4341
|
buffer = lines.pop() ?? "";
|
|
4269
4342
|
for (const line of lines) {
|
|
4270
|
-
if (line.startsWith("data: "))
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
responseId = parsed.id;
|
|
4277
|
-
}
|
|
4278
|
-
if (parsed.type === "response.output_text.delta" && parsed.delta) {
|
|
4279
|
-
content += parsed.delta;
|
|
4280
|
-
} else if (parsed.type === "response.completed" && parsed.response) {
|
|
4281
|
-
if (parsed.response.usage) {
|
|
4282
|
-
inputTokens = parsed.response.usage.input_tokens ?? 0;
|
|
4283
|
-
outputTokens = parsed.response.usage.output_tokens ?? 0;
|
|
4284
|
-
}
|
|
4285
|
-
status = parsed.response.status ?? "completed";
|
|
4286
|
-
} else if (parsed.type === "response.output_text.done" && parsed.text) {
|
|
4287
|
-
content = parsed.text;
|
|
4288
|
-
}
|
|
4289
|
-
} catch {
|
|
4290
|
-
}
|
|
4343
|
+
if (!line.startsWith("data: ")) continue;
|
|
4344
|
+
const data = line.slice(6).trim();
|
|
4345
|
+
if (!data || data === "[DONE]") continue;
|
|
4346
|
+
try {
|
|
4347
|
+
onEvent(JSON.parse(data));
|
|
4348
|
+
} catch {
|
|
4291
4349
|
}
|
|
4292
4350
|
}
|
|
4293
4351
|
}
|
|
4294
4352
|
} finally {
|
|
4353
|
+
clearInterval(timeoutInterval);
|
|
4295
4354
|
reader.releaseLock();
|
|
4296
4355
|
}
|
|
4297
|
-
if (
|
|
4298
|
-
throw new
|
|
4299
|
-
|
|
4300
|
-
|
|
4356
|
+
if (timeoutController.signal.aborted) {
|
|
4357
|
+
throw new Error(
|
|
4358
|
+
`Stream timeout: No response from Codex API for ${STREAM_TIMEOUT_MS / 1e3}s`
|
|
4359
|
+
);
|
|
4301
4360
|
}
|
|
4302
|
-
const stopReason = status === "completed" ? "end_turn" : status === "incomplete" ? "max_tokens" : "end_turn";
|
|
4303
|
-
return {
|
|
4304
|
-
id: responseId,
|
|
4305
|
-
content,
|
|
4306
|
-
stopReason,
|
|
4307
|
-
model,
|
|
4308
|
-
usage: {
|
|
4309
|
-
inputTokens,
|
|
4310
|
-
outputTokens
|
|
4311
|
-
}
|
|
4312
|
-
};
|
|
4313
4361
|
}
|
|
4314
4362
|
/**
|
|
4315
|
-
* Send a chat message
|
|
4316
|
-
|
|
4317
|
-
|
|
4363
|
+
* Send a chat message using Codex Responses API format
|
|
4364
|
+
*/
|
|
4365
|
+
async chat(messages, options) {
|
|
4366
|
+
return withRetry(async () => {
|
|
4367
|
+
const model = options?.model ?? this.config.model ?? DEFAULT_MODEL3;
|
|
4368
|
+
const { input, instructions } = this.convertToResponsesInput(messages, options?.system);
|
|
4369
|
+
const body = this.buildRequestBody(model, input, instructions, {
|
|
4370
|
+
maxTokens: options?.maxTokens,
|
|
4371
|
+
temperature: options?.temperature
|
|
4372
|
+
});
|
|
4373
|
+
const response = await this.makeRequest(body);
|
|
4374
|
+
let content = "";
|
|
4375
|
+
let responseId = `codex-${Date.now()}`;
|
|
4376
|
+
let inputTokens = 0;
|
|
4377
|
+
let outputTokens = 0;
|
|
4378
|
+
let status = "completed";
|
|
4379
|
+
await this.readSSEStream(response, (event) => {
|
|
4380
|
+
if (event.id) responseId = event.id;
|
|
4381
|
+
if (event.type === "response.output_text.delta" && event.delta) {
|
|
4382
|
+
content += event.delta;
|
|
4383
|
+
} else if (event.type === "response.output_text.done" && event.text) {
|
|
4384
|
+
content = event.text;
|
|
4385
|
+
} else if (event.type === "response.completed" && event.response) {
|
|
4386
|
+
const resp = event.response;
|
|
4387
|
+
const usage = resp.usage;
|
|
4388
|
+
if (usage) {
|
|
4389
|
+
inputTokens = usage.input_tokens ?? 0;
|
|
4390
|
+
outputTokens = usage.output_tokens ?? 0;
|
|
4391
|
+
}
|
|
4392
|
+
status = resp.status ?? "completed";
|
|
4393
|
+
}
|
|
4394
|
+
});
|
|
4395
|
+
const stopReason = status === "completed" ? "end_turn" : status === "incomplete" ? "max_tokens" : "end_turn";
|
|
4396
|
+
return {
|
|
4397
|
+
id: responseId,
|
|
4398
|
+
content,
|
|
4399
|
+
stopReason,
|
|
4400
|
+
model,
|
|
4401
|
+
usage: { inputTokens, outputTokens }
|
|
4402
|
+
};
|
|
4403
|
+
}, this.retryConfig);
|
|
4404
|
+
}
|
|
4405
|
+
/**
|
|
4406
|
+
* Send a chat message with tool use via Responses API
|
|
4318
4407
|
*/
|
|
4319
4408
|
async chatWithTools(messages, options) {
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4409
|
+
return withRetry(async () => {
|
|
4410
|
+
const model = options?.model ?? this.config.model ?? DEFAULT_MODEL3;
|
|
4411
|
+
const { input, instructions } = this.convertToResponsesInput(messages, options?.system);
|
|
4412
|
+
const body = this.buildRequestBody(model, input, instructions, {
|
|
4413
|
+
tools: options.tools,
|
|
4414
|
+
maxTokens: options?.maxTokens
|
|
4415
|
+
});
|
|
4416
|
+
const response = await this.makeRequest(body);
|
|
4417
|
+
let content = "";
|
|
4418
|
+
let responseId = `codex-${Date.now()}`;
|
|
4419
|
+
let inputTokens = 0;
|
|
4420
|
+
let outputTokens = 0;
|
|
4421
|
+
const toolCalls = [];
|
|
4422
|
+
const fnCallBuilders = /* @__PURE__ */ new Map();
|
|
4423
|
+
await this.readSSEStream(response, (event) => {
|
|
4424
|
+
if (event.id) responseId = event.id;
|
|
4425
|
+
switch (event.type) {
|
|
4426
|
+
case "response.output_text.delta":
|
|
4427
|
+
content += event.delta ?? "";
|
|
4428
|
+
break;
|
|
4429
|
+
case "response.output_text.done":
|
|
4430
|
+
content = event.text ?? content;
|
|
4431
|
+
break;
|
|
4432
|
+
case "response.output_item.added": {
|
|
4433
|
+
const item = event.item;
|
|
4434
|
+
if (item.type === "function_call") {
|
|
4435
|
+
const itemKey = item.id ?? item.call_id;
|
|
4436
|
+
fnCallBuilders.set(itemKey, {
|
|
4437
|
+
callId: item.call_id,
|
|
4438
|
+
name: item.name,
|
|
4439
|
+
arguments: ""
|
|
4440
|
+
});
|
|
4441
|
+
}
|
|
4442
|
+
break;
|
|
4443
|
+
}
|
|
4444
|
+
case "response.function_call_arguments.delta": {
|
|
4445
|
+
const builder = fnCallBuilders.get(event.item_id);
|
|
4446
|
+
if (builder) builder.arguments += event.delta ?? "";
|
|
4447
|
+
break;
|
|
4448
|
+
}
|
|
4449
|
+
case "response.function_call_arguments.done": {
|
|
4450
|
+
const builder = fnCallBuilders.get(event.item_id);
|
|
4451
|
+
if (builder) {
|
|
4452
|
+
toolCalls.push({
|
|
4453
|
+
id: builder.callId,
|
|
4454
|
+
name: builder.name,
|
|
4455
|
+
input: parseArguments(event.arguments)
|
|
4456
|
+
});
|
|
4457
|
+
fnCallBuilders.delete(event.item_id);
|
|
4458
|
+
}
|
|
4459
|
+
break;
|
|
4460
|
+
}
|
|
4461
|
+
case "response.completed": {
|
|
4462
|
+
const resp = event.response;
|
|
4463
|
+
const usage = resp.usage;
|
|
4464
|
+
if (usage) {
|
|
4465
|
+
inputTokens = usage.input_tokens ?? 0;
|
|
4466
|
+
outputTokens = usage.output_tokens ?? 0;
|
|
4467
|
+
}
|
|
4468
|
+
for (const [, builder] of fnCallBuilders) {
|
|
4469
|
+
toolCalls.push({
|
|
4470
|
+
id: builder.callId,
|
|
4471
|
+
name: builder.name,
|
|
4472
|
+
input: parseArguments(builder.arguments)
|
|
4473
|
+
});
|
|
4474
|
+
}
|
|
4475
|
+
fnCallBuilders.clear();
|
|
4476
|
+
break;
|
|
4477
|
+
}
|
|
4478
|
+
}
|
|
4479
|
+
});
|
|
4480
|
+
return {
|
|
4481
|
+
id: responseId,
|
|
4482
|
+
content,
|
|
4483
|
+
stopReason: toolCalls.length > 0 ? "tool_use" : "end_turn",
|
|
4484
|
+
model,
|
|
4485
|
+
usage: { inputTokens, outputTokens },
|
|
4486
|
+
toolCalls
|
|
4487
|
+
};
|
|
4488
|
+
}, this.retryConfig);
|
|
4326
4489
|
}
|
|
4327
4490
|
/**
|
|
4328
|
-
* Stream a chat response
|
|
4329
|
-
* Note: True streaming with Codex Responses API is complex.
|
|
4330
|
-
* For now, we make a non-streaming call and simulate streaming by emitting chunks.
|
|
4491
|
+
* Stream a chat response (no tools)
|
|
4331
4492
|
*/
|
|
4332
4493
|
async *stream(messages, options) {
|
|
4333
|
-
const
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4494
|
+
const model = options?.model ?? this.config.model ?? DEFAULT_MODEL3;
|
|
4495
|
+
const { input, instructions } = this.convertToResponsesInput(messages, options?.system);
|
|
4496
|
+
const body = this.buildRequestBody(model, input, instructions, {
|
|
4497
|
+
maxTokens: options?.maxTokens
|
|
4498
|
+
});
|
|
4499
|
+
const response = await this.makeRequest(body);
|
|
4500
|
+
if (!response.body) {
|
|
4501
|
+
throw new ProviderError("No response body from Codex API", { provider: this.id });
|
|
4502
|
+
}
|
|
4503
|
+
const reader = response.body.getReader();
|
|
4504
|
+
const decoder = new TextDecoder();
|
|
4505
|
+
let buffer = "";
|
|
4506
|
+
let lastActivityTime = Date.now();
|
|
4507
|
+
const timeoutController = new AbortController();
|
|
4508
|
+
const timeoutInterval = setInterval(() => {
|
|
4509
|
+
if (Date.now() - lastActivityTime > STREAM_TIMEOUT_MS) {
|
|
4510
|
+
clearInterval(timeoutInterval);
|
|
4511
|
+
timeoutController.abort();
|
|
4512
|
+
}
|
|
4513
|
+
}, 5e3);
|
|
4514
|
+
try {
|
|
4515
|
+
while (true) {
|
|
4516
|
+
if (timeoutController.signal.aborted) break;
|
|
4517
|
+
const { done, value } = await reader.read();
|
|
4518
|
+
if (done) break;
|
|
4519
|
+
lastActivityTime = Date.now();
|
|
4520
|
+
buffer += decoder.decode(value, { stream: true });
|
|
4521
|
+
const lines = buffer.split("\n");
|
|
4522
|
+
buffer = lines.pop() ?? "";
|
|
4523
|
+
for (const line of lines) {
|
|
4524
|
+
if (!line.startsWith("data: ")) continue;
|
|
4525
|
+
const data = line.slice(6).trim();
|
|
4526
|
+
if (!data || data === "[DONE]") continue;
|
|
4527
|
+
try {
|
|
4528
|
+
const event = JSON.parse(data);
|
|
4529
|
+
if (event.type === "response.output_text.delta" && event.delta) {
|
|
4530
|
+
yield { type: "text", text: event.delta };
|
|
4531
|
+
} else if (event.type === "response.completed") {
|
|
4532
|
+
yield { type: "done", stopReason: "end_turn" };
|
|
4533
|
+
}
|
|
4534
|
+
} catch {
|
|
4535
|
+
}
|
|
4342
4536
|
}
|
|
4343
4537
|
}
|
|
4538
|
+
} finally {
|
|
4539
|
+
clearInterval(timeoutInterval);
|
|
4540
|
+
reader.releaseLock();
|
|
4541
|
+
}
|
|
4542
|
+
if (timeoutController.signal.aborted) {
|
|
4543
|
+
throw new Error(
|
|
4544
|
+
`Stream timeout: No response from Codex API for ${STREAM_TIMEOUT_MS / 1e3}s`
|
|
4545
|
+
);
|
|
4344
4546
|
}
|
|
4345
|
-
yield { type: "done", stopReason: response.stopReason };
|
|
4346
4547
|
}
|
|
4347
4548
|
/**
|
|
4348
|
-
* Stream a chat response with tool use
|
|
4349
|
-
*
|
|
4350
|
-
*
|
|
4549
|
+
* Stream a chat response with tool use via Responses API.
|
|
4550
|
+
*
|
|
4551
|
+
* IMPORTANT: fnCallBuilders is keyed by output item ID (item.id), NOT by
|
|
4552
|
+
* call_id. The streaming events (function_call_arguments.delta/done) use
|
|
4553
|
+
* item_id which references the output item's id field, not call_id.
|
|
4351
4554
|
*/
|
|
4352
4555
|
async *streamWithTools(messages, options) {
|
|
4353
|
-
|
|
4556
|
+
const model = options?.model ?? this.config.model ?? DEFAULT_MODEL3;
|
|
4557
|
+
const { input, instructions } = this.convertToResponsesInput(messages, options?.system);
|
|
4558
|
+
const body = this.buildRequestBody(model, input, instructions, {
|
|
4559
|
+
tools: options.tools,
|
|
4560
|
+
maxTokens: options?.maxTokens
|
|
4561
|
+
});
|
|
4562
|
+
const response = await this.makeRequest(body);
|
|
4563
|
+
if (!response.body) {
|
|
4564
|
+
throw new ProviderError("No response body from Codex API", { provider: this.id });
|
|
4565
|
+
}
|
|
4566
|
+
const reader = response.body.getReader();
|
|
4567
|
+
const decoder = new TextDecoder();
|
|
4568
|
+
let buffer = "";
|
|
4569
|
+
const fnCallBuilders = /* @__PURE__ */ new Map();
|
|
4570
|
+
let lastActivityTime = Date.now();
|
|
4571
|
+
const timeoutController = new AbortController();
|
|
4572
|
+
const timeoutInterval = setInterval(() => {
|
|
4573
|
+
if (Date.now() - lastActivityTime > STREAM_TIMEOUT_MS) {
|
|
4574
|
+
clearInterval(timeoutInterval);
|
|
4575
|
+
timeoutController.abort();
|
|
4576
|
+
}
|
|
4577
|
+
}, 5e3);
|
|
4578
|
+
try {
|
|
4579
|
+
while (true) {
|
|
4580
|
+
if (timeoutController.signal.aborted) break;
|
|
4581
|
+
const { done, value } = await reader.read();
|
|
4582
|
+
if (done) break;
|
|
4583
|
+
lastActivityTime = Date.now();
|
|
4584
|
+
buffer += decoder.decode(value, { stream: true });
|
|
4585
|
+
const lines = buffer.split("\n");
|
|
4586
|
+
buffer = lines.pop() ?? "";
|
|
4587
|
+
for (const line of lines) {
|
|
4588
|
+
if (!line.startsWith("data: ")) continue;
|
|
4589
|
+
const data = line.slice(6).trim();
|
|
4590
|
+
if (!data || data === "[DONE]") continue;
|
|
4591
|
+
let event;
|
|
4592
|
+
try {
|
|
4593
|
+
event = JSON.parse(data);
|
|
4594
|
+
} catch {
|
|
4595
|
+
continue;
|
|
4596
|
+
}
|
|
4597
|
+
switch (event.type) {
|
|
4598
|
+
case "response.output_text.delta":
|
|
4599
|
+
yield { type: "text", text: event.delta ?? "" };
|
|
4600
|
+
break;
|
|
4601
|
+
case "response.output_item.added": {
|
|
4602
|
+
const item = event.item;
|
|
4603
|
+
if (item.type === "function_call") {
|
|
4604
|
+
const itemKey = item.id ?? item.call_id;
|
|
4605
|
+
fnCallBuilders.set(itemKey, {
|
|
4606
|
+
callId: item.call_id,
|
|
4607
|
+
name: item.name,
|
|
4608
|
+
arguments: ""
|
|
4609
|
+
});
|
|
4610
|
+
yield {
|
|
4611
|
+
type: "tool_use_start",
|
|
4612
|
+
toolCall: { id: item.call_id, name: item.name }
|
|
4613
|
+
};
|
|
4614
|
+
}
|
|
4615
|
+
break;
|
|
4616
|
+
}
|
|
4617
|
+
case "response.function_call_arguments.delta": {
|
|
4618
|
+
const builder = fnCallBuilders.get(event.item_id);
|
|
4619
|
+
if (builder) {
|
|
4620
|
+
builder.arguments += event.delta ?? "";
|
|
4621
|
+
}
|
|
4622
|
+
break;
|
|
4623
|
+
}
|
|
4624
|
+
case "response.function_call_arguments.done": {
|
|
4625
|
+
const builder = fnCallBuilders.get(event.item_id);
|
|
4626
|
+
if (builder) {
|
|
4627
|
+
yield {
|
|
4628
|
+
type: "tool_use_end",
|
|
4629
|
+
toolCall: {
|
|
4630
|
+
id: builder.callId,
|
|
4631
|
+
name: builder.name,
|
|
4632
|
+
input: parseArguments(event.arguments ?? builder.arguments)
|
|
4633
|
+
}
|
|
4634
|
+
};
|
|
4635
|
+
fnCallBuilders.delete(event.item_id);
|
|
4636
|
+
}
|
|
4637
|
+
break;
|
|
4638
|
+
}
|
|
4639
|
+
case "response.completed": {
|
|
4640
|
+
for (const [, builder] of fnCallBuilders) {
|
|
4641
|
+
yield {
|
|
4642
|
+
type: "tool_use_end",
|
|
4643
|
+
toolCall: {
|
|
4644
|
+
id: builder.callId,
|
|
4645
|
+
name: builder.name,
|
|
4646
|
+
input: parseArguments(builder.arguments)
|
|
4647
|
+
}
|
|
4648
|
+
};
|
|
4649
|
+
}
|
|
4650
|
+
fnCallBuilders.clear();
|
|
4651
|
+
const resp = event.response;
|
|
4652
|
+
const output = resp?.output ?? [];
|
|
4653
|
+
const hasToolCalls = output.some((i) => i.type === "function_call");
|
|
4654
|
+
yield {
|
|
4655
|
+
type: "done",
|
|
4656
|
+
stopReason: hasToolCalls ? "tool_use" : "end_turn"
|
|
4657
|
+
};
|
|
4658
|
+
break;
|
|
4659
|
+
}
|
|
4660
|
+
}
|
|
4661
|
+
}
|
|
4662
|
+
}
|
|
4663
|
+
} finally {
|
|
4664
|
+
clearInterval(timeoutInterval);
|
|
4665
|
+
reader.releaseLock();
|
|
4666
|
+
}
|
|
4667
|
+
if (timeoutController.signal.aborted) {
|
|
4668
|
+
throw new Error(
|
|
4669
|
+
`Stream timeout: No response from Codex API for ${STREAM_TIMEOUT_MS / 1e3}s`
|
|
4670
|
+
);
|
|
4671
|
+
}
|
|
4354
4672
|
}
|
|
4355
4673
|
};
|
|
4356
4674
|
}
|
|
@@ -4374,6 +4692,7 @@ var init_copilot2 = __esm({
|
|
|
4374
4692
|
// OpenAI models — chat/completions
|
|
4375
4693
|
"gpt-4.1": 1048576,
|
|
4376
4694
|
// OpenAI models — /responses API (Codex/GPT-5+)
|
|
4695
|
+
"gpt-5.4-codex": 4e5,
|
|
4377
4696
|
"gpt-5.3-codex": 4e5,
|
|
4378
4697
|
"gpt-5.2-codex": 4e5,
|
|
4379
4698
|
"gpt-5.1-codex-max": 4e5,
|
|
@@ -5035,6 +5354,7 @@ var init_pricing = __esm({
|
|
|
5035
5354
|
"claude-sonnet-4-20250514": { inputPerMillion: 3, outputPerMillion: 15, contextWindow: 2e5 },
|
|
5036
5355
|
"claude-opus-4-20250514": { inputPerMillion: 15, outputPerMillion: 75, contextWindow: 2e5 },
|
|
5037
5356
|
// OpenAI models
|
|
5357
|
+
"gpt-5.4-codex": { inputPerMillion: 2, outputPerMillion: 8, contextWindow: 4e5 },
|
|
5038
5358
|
"gpt-5.3-codex": { inputPerMillion: 2, outputPerMillion: 8, contextWindow: 4e5 },
|
|
5039
5359
|
"gpt-5.2-codex": { inputPerMillion: 2, outputPerMillion: 8, contextWindow: 4e5 },
|
|
5040
5360
|
"gpt-5.1-codex-max": { inputPerMillion: 3, outputPerMillion: 12, contextWindow: 4e5 },
|
|
@@ -5640,7 +5960,7 @@ function getDefaultModel(provider) {
|
|
|
5640
5960
|
case "anthropic":
|
|
5641
5961
|
return process.env["ANTHROPIC_MODEL"] ?? "claude-opus-4-6";
|
|
5642
5962
|
case "openai":
|
|
5643
|
-
return process.env["OPENAI_MODEL"] ?? "gpt-5.
|
|
5963
|
+
return process.env["OPENAI_MODEL"] ?? "gpt-5.4-codex";
|
|
5644
5964
|
case "gemini":
|
|
5645
5965
|
return process.env["GEMINI_MODEL"] ?? "gemini-3.1-pro-preview";
|
|
5646
5966
|
case "kimi":
|
|
@@ -5652,7 +5972,7 @@ function getDefaultModel(provider) {
|
|
|
5652
5972
|
case "ollama":
|
|
5653
5973
|
return process.env["OLLAMA_MODEL"] ?? "llama3.1";
|
|
5654
5974
|
case "codex":
|
|
5655
|
-
return process.env["CODEX_MODEL"] ?? "gpt-5.
|
|
5975
|
+
return process.env["CODEX_MODEL"] ?? "gpt-5.4-codex";
|
|
5656
5976
|
case "copilot":
|
|
5657
5977
|
return process.env["COPILOT_MODEL"] ?? "claude-sonnet-4.6";
|
|
5658
5978
|
case "groq":
|
|
@@ -5670,7 +5990,7 @@ function getDefaultModel(provider) {
|
|
|
5670
5990
|
case "qwen":
|
|
5671
5991
|
return process.env["QWEN_MODEL"] ?? "qwen-coder-plus";
|
|
5672
5992
|
default:
|
|
5673
|
-
return "gpt-5.
|
|
5993
|
+
return "gpt-5.4-codex";
|
|
5674
5994
|
}
|
|
5675
5995
|
}
|
|
5676
5996
|
function getDefaultProvider() {
|
|
@@ -25735,13 +26055,20 @@ var PROVIDER_DEFINITIONS = {
|
|
|
25735
26055
|
// Updated: March 2026 — from platform.openai.com/docs/models
|
|
25736
26056
|
models: [
|
|
25737
26057
|
{
|
|
25738
|
-
id: "gpt-5.
|
|
25739
|
-
name: "GPT-5.
|
|
25740
|
-
description: "Latest agentic coding model (
|
|
26058
|
+
id: "gpt-5.4-codex",
|
|
26059
|
+
name: "GPT-5.4 Codex",
|
|
26060
|
+
description: "Latest agentic coding model (Mar 2026)",
|
|
25741
26061
|
contextWindow: 4e5,
|
|
25742
26062
|
maxOutputTokens: 128e3,
|
|
25743
26063
|
recommended: true
|
|
25744
26064
|
},
|
|
26065
|
+
{
|
|
26066
|
+
id: "gpt-5.3-codex",
|
|
26067
|
+
name: "GPT-5.3 Codex",
|
|
26068
|
+
description: "Previous agentic coding model (Feb 2026)",
|
|
26069
|
+
contextWindow: 4e5,
|
|
26070
|
+
maxOutputTokens: 128e3
|
|
26071
|
+
},
|
|
25745
26072
|
{
|
|
25746
26073
|
id: "gpt-5.2-codex",
|
|
25747
26074
|
name: "GPT-5.2 Codex",
|
|
@@ -25871,13 +26198,20 @@ var PROVIDER_DEFINITIONS = {
|
|
|
25871
26198
|
},
|
|
25872
26199
|
// OpenAI models (Codex/GPT-5+ use /responses API, others use /chat/completions)
|
|
25873
26200
|
{
|
|
25874
|
-
id: "gpt-5.
|
|
25875
|
-
name: "GPT-5.
|
|
25876
|
-
description: "OpenAI's latest coding model via Copilot",
|
|
26201
|
+
id: "gpt-5.4-codex",
|
|
26202
|
+
name: "GPT-5.4 Codex",
|
|
26203
|
+
description: "OpenAI's latest coding model via Copilot (Mar 2026)",
|
|
25877
26204
|
contextWindow: 4e5,
|
|
25878
26205
|
maxOutputTokens: 128e3,
|
|
25879
26206
|
recommended: true
|
|
25880
26207
|
},
|
|
26208
|
+
{
|
|
26209
|
+
id: "gpt-5.3-codex",
|
|
26210
|
+
name: "GPT-5.3 Codex",
|
|
26211
|
+
description: "OpenAI's previous coding model via Copilot",
|
|
26212
|
+
contextWindow: 4e5,
|
|
26213
|
+
maxOutputTokens: 128e3
|
|
26214
|
+
},
|
|
25881
26215
|
{
|
|
25882
26216
|
id: "gpt-5.2-codex",
|
|
25883
26217
|
name: "GPT-5.2 Codex",
|
|
@@ -25949,13 +26283,20 @@ var PROVIDER_DEFINITIONS = {
|
|
|
25949
26283
|
},
|
|
25950
26284
|
models: [
|
|
25951
26285
|
{
|
|
25952
|
-
id: "gpt-5.
|
|
25953
|
-
name: "GPT-5.
|
|
25954
|
-
description: "Latest coding model via ChatGPT subscription (
|
|
26286
|
+
id: "gpt-5.4-codex",
|
|
26287
|
+
name: "GPT-5.4 Codex",
|
|
26288
|
+
description: "Latest coding model via ChatGPT subscription (Mar 2026)",
|
|
25955
26289
|
contextWindow: 2e5,
|
|
25956
26290
|
maxOutputTokens: 128e3,
|
|
25957
26291
|
recommended: true
|
|
25958
26292
|
},
|
|
26293
|
+
{
|
|
26294
|
+
id: "gpt-5.3-codex",
|
|
26295
|
+
name: "GPT-5.3 Codex",
|
|
26296
|
+
description: "Previous coding model via ChatGPT subscription",
|
|
26297
|
+
contextWindow: 2e5,
|
|
26298
|
+
maxOutputTokens: 128e3
|
|
26299
|
+
},
|
|
25959
26300
|
{
|
|
25960
26301
|
id: "gpt-5.2-codex",
|
|
25961
26302
|
name: "GPT-5.2 Codex",
|
|
@@ -26372,9 +26713,9 @@ var PROVIDER_DEFINITIONS = {
|
|
|
26372
26713
|
recommended: true
|
|
26373
26714
|
},
|
|
26374
26715
|
{
|
|
26375
|
-
id: "openai/gpt-5.
|
|
26376
|
-
name: "GPT-5.
|
|
26377
|
-
description: "OpenAI's coding model \u2014 via OpenRouter",
|
|
26716
|
+
id: "openai/gpt-5.4-codex",
|
|
26717
|
+
name: "GPT-5.4 Codex (via OR)",
|
|
26718
|
+
description: "OpenAI's latest coding model \u2014 via OpenRouter",
|
|
26378
26719
|
contextWindow: 4e5,
|
|
26379
26720
|
maxOutputTokens: 128e3
|
|
26380
26721
|
},
|