@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/index.js
CHANGED
|
@@ -189,7 +189,7 @@ function getDefaultModel(provider) {
|
|
|
189
189
|
case "anthropic":
|
|
190
190
|
return process.env["ANTHROPIC_MODEL"] ?? "claude-opus-4-6";
|
|
191
191
|
case "openai":
|
|
192
|
-
return process.env["OPENAI_MODEL"] ?? "gpt-5.
|
|
192
|
+
return process.env["OPENAI_MODEL"] ?? "gpt-5.4-codex";
|
|
193
193
|
case "gemini":
|
|
194
194
|
return process.env["GEMINI_MODEL"] ?? "gemini-3.1-pro-preview";
|
|
195
195
|
case "kimi":
|
|
@@ -201,7 +201,7 @@ function getDefaultModel(provider) {
|
|
|
201
201
|
case "ollama":
|
|
202
202
|
return process.env["OLLAMA_MODEL"] ?? "llama3.1";
|
|
203
203
|
case "codex":
|
|
204
|
-
return process.env["CODEX_MODEL"] ?? "gpt-5.
|
|
204
|
+
return process.env["CODEX_MODEL"] ?? "gpt-5.4-codex";
|
|
205
205
|
case "copilot":
|
|
206
206
|
return process.env["COPILOT_MODEL"] ?? "claude-sonnet-4.6";
|
|
207
207
|
case "groq":
|
|
@@ -219,7 +219,7 @@ function getDefaultModel(provider) {
|
|
|
219
219
|
case "qwen":
|
|
220
220
|
return process.env["QWEN_MODEL"] ?? "qwen-coder-plus";
|
|
221
221
|
default:
|
|
222
|
-
return "gpt-5.
|
|
222
|
+
return "gpt-5.4-codex";
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
225
|
function getDefaultProvider() {
|
|
@@ -12192,7 +12192,7 @@ function createKimiCodeProvider(config) {
|
|
|
12192
12192
|
}
|
|
12193
12193
|
return provider;
|
|
12194
12194
|
}
|
|
12195
|
-
var DEFAULT_MODEL2 = "gpt-5.
|
|
12195
|
+
var DEFAULT_MODEL2 = "gpt-5.4-codex";
|
|
12196
12196
|
var CONTEXT_WINDOWS2 = {
|
|
12197
12197
|
// OpenAI models
|
|
12198
12198
|
"gpt-4o": 128e3,
|
|
@@ -12215,6 +12215,7 @@ var CONTEXT_WINDOWS2 = {
|
|
|
12215
12215
|
"gpt-5.2-instant": 4e5,
|
|
12216
12216
|
"gpt-5.2-pro": 4e5,
|
|
12217
12217
|
"gpt-5.3-codex": 4e5,
|
|
12218
|
+
"gpt-5.4-codex": 4e5,
|
|
12218
12219
|
// Kimi/Moonshot models
|
|
12219
12220
|
"kimi-k2.5": 262144,
|
|
12220
12221
|
"kimi-k2-0324": 131072,
|
|
@@ -12271,7 +12272,7 @@ var CONTEXT_WINDOWS2 = {
|
|
|
12271
12272
|
"microsoft/Phi-4": 16384,
|
|
12272
12273
|
// OpenRouter model IDs
|
|
12273
12274
|
"anthropic/claude-opus-4-6": 2e5,
|
|
12274
|
-
"openai/gpt-5.
|
|
12275
|
+
"openai/gpt-5.4-codex": 4e5,
|
|
12275
12276
|
"google/gemini-3-flash-preview": 1e6,
|
|
12276
12277
|
"meta-llama/llama-3.3-70b-instruct": 128e3
|
|
12277
12278
|
};
|
|
@@ -12526,7 +12527,7 @@ var OpenAIProvider = class {
|
|
|
12526
12527
|
once: true
|
|
12527
12528
|
});
|
|
12528
12529
|
const providerName = this.name;
|
|
12529
|
-
const
|
|
12530
|
+
const parseArguments2 = (builder) => {
|
|
12530
12531
|
let input = {};
|
|
12531
12532
|
try {
|
|
12532
12533
|
input = builder.arguments ? JSON.parse(builder.arguments) : {};
|
|
@@ -12607,7 +12608,7 @@ var OpenAIProvider = class {
|
|
|
12607
12608
|
toolCall: {
|
|
12608
12609
|
id: builder.id,
|
|
12609
12610
|
name: builder.name,
|
|
12610
|
-
input:
|
|
12611
|
+
input: parseArguments2(builder)
|
|
12611
12612
|
}
|
|
12612
12613
|
};
|
|
12613
12614
|
}
|
|
@@ -12620,7 +12621,7 @@ var OpenAIProvider = class {
|
|
|
12620
12621
|
toolCall: {
|
|
12621
12622
|
id: builder.id,
|
|
12622
12623
|
name: builder.name,
|
|
12623
|
-
input:
|
|
12624
|
+
input: parseArguments2(builder)
|
|
12624
12625
|
}
|
|
12625
12626
|
};
|
|
12626
12627
|
}
|
|
@@ -13611,8 +13612,9 @@ async function getCachedADCToken() {
|
|
|
13611
13612
|
|
|
13612
13613
|
// src/providers/codex.ts
|
|
13613
13614
|
var CODEX_API_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses";
|
|
13614
|
-
var DEFAULT_MODEL3 = "gpt-5.
|
|
13615
|
+
var DEFAULT_MODEL3 = "gpt-5.4-codex";
|
|
13615
13616
|
var CONTEXT_WINDOWS3 = {
|
|
13617
|
+
"gpt-5.4-codex": 2e5,
|
|
13616
13618
|
"gpt-5.3-codex": 2e5,
|
|
13617
13619
|
"gpt-5.2-codex": 2e5,
|
|
13618
13620
|
"gpt-5-codex": 2e5,
|
|
@@ -13621,6 +13623,7 @@ var CONTEXT_WINDOWS3 = {
|
|
|
13621
13623
|
"gpt-5.2": 2e5,
|
|
13622
13624
|
"gpt-5.1": 2e5
|
|
13623
13625
|
};
|
|
13626
|
+
var STREAM_TIMEOUT_MS = 12e4;
|
|
13624
13627
|
function parseJwtClaims(token) {
|
|
13625
13628
|
const parts = token.split(".");
|
|
13626
13629
|
if (parts.length !== 3 || !parts[1]) return void 0;
|
|
@@ -13636,12 +13639,28 @@ function extractAccountId(accessToken) {
|
|
|
13636
13639
|
const auth = claims["https://api.openai.com/auth"];
|
|
13637
13640
|
return claims["chatgpt_account_id"] || auth?.["chatgpt_account_id"] || claims["organizations"]?.[0]?.id;
|
|
13638
13641
|
}
|
|
13642
|
+
function parseArguments(args) {
|
|
13643
|
+
try {
|
|
13644
|
+
return args ? JSON.parse(args) : {};
|
|
13645
|
+
} catch {
|
|
13646
|
+
try {
|
|
13647
|
+
if (args) {
|
|
13648
|
+
const repaired = jsonrepair(args);
|
|
13649
|
+
return JSON.parse(repaired);
|
|
13650
|
+
}
|
|
13651
|
+
} catch {
|
|
13652
|
+
console.error(`[Codex] Cannot parse tool arguments: ${args.slice(0, 200)}`);
|
|
13653
|
+
}
|
|
13654
|
+
return {};
|
|
13655
|
+
}
|
|
13656
|
+
}
|
|
13639
13657
|
var CodexProvider = class {
|
|
13640
13658
|
id = "codex";
|
|
13641
13659
|
name = "OpenAI Codex (ChatGPT Plus/Pro)";
|
|
13642
13660
|
config = {};
|
|
13643
13661
|
accessToken = null;
|
|
13644
13662
|
accountId;
|
|
13663
|
+
retryConfig = DEFAULT_RETRY_CONFIG;
|
|
13645
13664
|
/**
|
|
13646
13665
|
* Initialize the provider with OAuth tokens
|
|
13647
13666
|
*/
|
|
@@ -13726,166 +13745,466 @@ var CodexProvider = class {
|
|
|
13726
13745
|
/**
|
|
13727
13746
|
* Extract text content from a message
|
|
13728
13747
|
*/
|
|
13729
|
-
|
|
13730
|
-
if (typeof
|
|
13731
|
-
|
|
13732
|
-
|
|
13733
|
-
if (Array.isArray(msg.content)) {
|
|
13734
|
-
return msg.content.map((part) => {
|
|
13735
|
-
if (part.type === "text") return part.text;
|
|
13736
|
-
if (part.type === "tool_result") return `Tool result: ${JSON.stringify(part.content)}`;
|
|
13737
|
-
return "";
|
|
13738
|
-
}).join("\n");
|
|
13748
|
+
contentToString(content) {
|
|
13749
|
+
if (typeof content === "string") return content;
|
|
13750
|
+
if (Array.isArray(content)) {
|
|
13751
|
+
return content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
|
|
13739
13752
|
}
|
|
13740
13753
|
return "";
|
|
13741
13754
|
}
|
|
13742
13755
|
/**
|
|
13743
|
-
* Convert messages to
|
|
13744
|
-
* Codex uses a different format than Chat Completions:
|
|
13745
|
-
* {
|
|
13746
|
-
* "input": [
|
|
13747
|
-
* { "type": "message", "role": "developer|user", "content": [{ "type": "input_text", "text": "..." }] },
|
|
13748
|
-
* { "type": "message", "role": "assistant", "content": [{ "type": "output_text", "text": "..." }] }
|
|
13749
|
-
* ]
|
|
13750
|
-
* }
|
|
13756
|
+
* Convert messages to Responses API input format.
|
|
13751
13757
|
*
|
|
13752
|
-
*
|
|
13758
|
+
* Handles:
|
|
13759
|
+
* - system messages → extracted as instructions
|
|
13760
|
+
* - user text messages → { role: "user", content: "..." }
|
|
13761
|
+
* - user tool_result messages → function_call_output items
|
|
13762
|
+
* - assistant text → { role: "assistant", content: "..." }
|
|
13763
|
+
* - assistant tool_use → function_call items
|
|
13753
13764
|
*/
|
|
13754
|
-
|
|
13755
|
-
|
|
13756
|
-
|
|
13757
|
-
|
|
13758
|
-
|
|
13759
|
-
|
|
13760
|
-
|
|
13761
|
-
|
|
13762
|
-
|
|
13763
|
-
|
|
13764
|
-
|
|
13765
|
+
convertToResponsesInput(messages, systemPrompt) {
|
|
13766
|
+
const input = [];
|
|
13767
|
+
let instructions = systemPrompt ?? null;
|
|
13768
|
+
for (const msg of messages) {
|
|
13769
|
+
if (msg.role === "system") {
|
|
13770
|
+
instructions = (instructions ? instructions + "\n\n" : "") + this.contentToString(msg.content);
|
|
13771
|
+
} else if (msg.role === "user") {
|
|
13772
|
+
if (Array.isArray(msg.content) && msg.content.some((b) => b.type === "tool_result")) {
|
|
13773
|
+
for (const block of msg.content) {
|
|
13774
|
+
if (block.type === "tool_result") {
|
|
13775
|
+
const tr = block;
|
|
13776
|
+
input.push({
|
|
13777
|
+
type: "function_call_output",
|
|
13778
|
+
call_id: tr.tool_use_id,
|
|
13779
|
+
output: tr.content
|
|
13780
|
+
});
|
|
13781
|
+
}
|
|
13782
|
+
}
|
|
13783
|
+
} else {
|
|
13784
|
+
input.push({
|
|
13785
|
+
role: "user",
|
|
13786
|
+
content: this.contentToString(msg.content)
|
|
13787
|
+
});
|
|
13788
|
+
}
|
|
13789
|
+
} else if (msg.role === "assistant") {
|
|
13790
|
+
if (typeof msg.content === "string") {
|
|
13791
|
+
input.push({ role: "assistant", content: msg.content });
|
|
13792
|
+
} else if (Array.isArray(msg.content)) {
|
|
13793
|
+
const textParts = [];
|
|
13794
|
+
for (const block of msg.content) {
|
|
13795
|
+
if (block.type === "text") {
|
|
13796
|
+
textParts.push(block.text);
|
|
13797
|
+
} else if (block.type === "tool_use") {
|
|
13798
|
+
if (textParts.length > 0) {
|
|
13799
|
+
input.push({ role: "assistant", content: textParts.join("") });
|
|
13800
|
+
textParts.length = 0;
|
|
13801
|
+
}
|
|
13802
|
+
const tu = block;
|
|
13803
|
+
input.push({
|
|
13804
|
+
type: "function_call",
|
|
13805
|
+
call_id: tu.id,
|
|
13806
|
+
name: tu.name,
|
|
13807
|
+
arguments: JSON.stringify(tu.input)
|
|
13808
|
+
});
|
|
13809
|
+
}
|
|
13810
|
+
}
|
|
13811
|
+
if (textParts.length > 0) {
|
|
13812
|
+
input.push({ role: "assistant", content: textParts.join("") });
|
|
13813
|
+
}
|
|
13814
|
+
}
|
|
13815
|
+
}
|
|
13816
|
+
}
|
|
13817
|
+
return { input, instructions };
|
|
13765
13818
|
}
|
|
13766
13819
|
/**
|
|
13767
|
-
*
|
|
13820
|
+
* Convert tool definitions to Responses API function tool format
|
|
13768
13821
|
*/
|
|
13769
|
-
|
|
13770
|
-
|
|
13771
|
-
|
|
13772
|
-
|
|
13773
|
-
|
|
13822
|
+
convertTools(tools) {
|
|
13823
|
+
return tools.map((tool) => ({
|
|
13824
|
+
type: "function",
|
|
13825
|
+
name: tool.name,
|
|
13826
|
+
description: tool.description ?? void 0,
|
|
13827
|
+
parameters: tool.input_schema ?? null,
|
|
13828
|
+
strict: false
|
|
13829
|
+
}));
|
|
13830
|
+
}
|
|
13831
|
+
/**
|
|
13832
|
+
* Build the request body for the Codex Responses API
|
|
13833
|
+
*/
|
|
13834
|
+
buildRequestBody(model, input, instructions, options) {
|
|
13774
13835
|
const body = {
|
|
13775
13836
|
model,
|
|
13776
|
-
|
|
13777
|
-
|
|
13778
|
-
|
|
13837
|
+
input,
|
|
13838
|
+
instructions: instructions ?? "You are a helpful coding assistant.",
|
|
13839
|
+
max_output_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
|
|
13840
|
+
temperature: options?.temperature ?? this.config.temperature ?? 0,
|
|
13779
13841
|
store: false,
|
|
13780
13842
|
stream: true
|
|
13781
13843
|
// Codex API requires streaming
|
|
13782
13844
|
};
|
|
13783
|
-
|
|
13845
|
+
if (options?.tools && options.tools.length > 0) {
|
|
13846
|
+
body.tools = this.convertTools(options.tools);
|
|
13847
|
+
}
|
|
13848
|
+
return body;
|
|
13849
|
+
}
|
|
13850
|
+
/**
|
|
13851
|
+
* Read SSE stream and call handler for each parsed event.
|
|
13852
|
+
* Returns when stream ends.
|
|
13853
|
+
*/
|
|
13854
|
+
async readSSEStream(response, onEvent) {
|
|
13784
13855
|
if (!response.body) {
|
|
13785
|
-
throw new ProviderError("No response body from Codex API", {
|
|
13786
|
-
provider: this.id
|
|
13787
|
-
});
|
|
13856
|
+
throw new ProviderError("No response body from Codex API", { provider: this.id });
|
|
13788
13857
|
}
|
|
13789
13858
|
const reader = response.body.getReader();
|
|
13790
13859
|
const decoder = new TextDecoder();
|
|
13791
13860
|
let buffer = "";
|
|
13792
|
-
let
|
|
13793
|
-
|
|
13794
|
-
|
|
13795
|
-
|
|
13796
|
-
|
|
13861
|
+
let lastActivityTime = Date.now();
|
|
13862
|
+
const timeoutController = new AbortController();
|
|
13863
|
+
const timeoutInterval = setInterval(() => {
|
|
13864
|
+
if (Date.now() - lastActivityTime > STREAM_TIMEOUT_MS) {
|
|
13865
|
+
clearInterval(timeoutInterval);
|
|
13866
|
+
timeoutController.abort();
|
|
13867
|
+
}
|
|
13868
|
+
}, 5e3);
|
|
13797
13869
|
try {
|
|
13798
13870
|
while (true) {
|
|
13871
|
+
if (timeoutController.signal.aborted) break;
|
|
13799
13872
|
const { done, value } = await reader.read();
|
|
13800
13873
|
if (done) break;
|
|
13874
|
+
lastActivityTime = Date.now();
|
|
13801
13875
|
buffer += decoder.decode(value, { stream: true });
|
|
13802
13876
|
const lines = buffer.split("\n");
|
|
13803
13877
|
buffer = lines.pop() ?? "";
|
|
13804
13878
|
for (const line of lines) {
|
|
13805
|
-
if (line.startsWith("data: "))
|
|
13806
|
-
|
|
13807
|
-
|
|
13808
|
-
|
|
13809
|
-
|
|
13810
|
-
|
|
13811
|
-
responseId = parsed.id;
|
|
13812
|
-
}
|
|
13813
|
-
if (parsed.type === "response.output_text.delta" && parsed.delta) {
|
|
13814
|
-
content += parsed.delta;
|
|
13815
|
-
} else if (parsed.type === "response.completed" && parsed.response) {
|
|
13816
|
-
if (parsed.response.usage) {
|
|
13817
|
-
inputTokens = parsed.response.usage.input_tokens ?? 0;
|
|
13818
|
-
outputTokens = parsed.response.usage.output_tokens ?? 0;
|
|
13819
|
-
}
|
|
13820
|
-
status = parsed.response.status ?? "completed";
|
|
13821
|
-
} else if (parsed.type === "response.output_text.done" && parsed.text) {
|
|
13822
|
-
content = parsed.text;
|
|
13823
|
-
}
|
|
13824
|
-
} catch {
|
|
13825
|
-
}
|
|
13879
|
+
if (!line.startsWith("data: ")) continue;
|
|
13880
|
+
const data = line.slice(6).trim();
|
|
13881
|
+
if (!data || data === "[DONE]") continue;
|
|
13882
|
+
try {
|
|
13883
|
+
onEvent(JSON.parse(data));
|
|
13884
|
+
} catch {
|
|
13826
13885
|
}
|
|
13827
13886
|
}
|
|
13828
13887
|
}
|
|
13829
13888
|
} finally {
|
|
13889
|
+
clearInterval(timeoutInterval);
|
|
13830
13890
|
reader.releaseLock();
|
|
13831
13891
|
}
|
|
13832
|
-
if (
|
|
13833
|
-
throw new
|
|
13834
|
-
|
|
13835
|
-
|
|
13892
|
+
if (timeoutController.signal.aborted) {
|
|
13893
|
+
throw new Error(
|
|
13894
|
+
`Stream timeout: No response from Codex API for ${STREAM_TIMEOUT_MS / 1e3}s`
|
|
13895
|
+
);
|
|
13836
13896
|
}
|
|
13837
|
-
const stopReason = status === "completed" ? "end_turn" : status === "incomplete" ? "max_tokens" : "end_turn";
|
|
13838
|
-
return {
|
|
13839
|
-
id: responseId,
|
|
13840
|
-
content,
|
|
13841
|
-
stopReason,
|
|
13842
|
-
model,
|
|
13843
|
-
usage: {
|
|
13844
|
-
inputTokens,
|
|
13845
|
-
outputTokens
|
|
13846
|
-
}
|
|
13847
|
-
};
|
|
13848
13897
|
}
|
|
13849
13898
|
/**
|
|
13850
|
-
* Send a chat message
|
|
13851
|
-
|
|
13852
|
-
|
|
13899
|
+
* Send a chat message using Codex Responses API format
|
|
13900
|
+
*/
|
|
13901
|
+
async chat(messages, options) {
|
|
13902
|
+
return withRetry(async () => {
|
|
13903
|
+
const model = options?.model ?? this.config.model ?? DEFAULT_MODEL3;
|
|
13904
|
+
const { input, instructions } = this.convertToResponsesInput(messages, options?.system);
|
|
13905
|
+
const body = this.buildRequestBody(model, input, instructions, {
|
|
13906
|
+
maxTokens: options?.maxTokens,
|
|
13907
|
+
temperature: options?.temperature
|
|
13908
|
+
});
|
|
13909
|
+
const response = await this.makeRequest(body);
|
|
13910
|
+
let content = "";
|
|
13911
|
+
let responseId = `codex-${Date.now()}`;
|
|
13912
|
+
let inputTokens = 0;
|
|
13913
|
+
let outputTokens = 0;
|
|
13914
|
+
let status = "completed";
|
|
13915
|
+
await this.readSSEStream(response, (event) => {
|
|
13916
|
+
if (event.id) responseId = event.id;
|
|
13917
|
+
if (event.type === "response.output_text.delta" && event.delta) {
|
|
13918
|
+
content += event.delta;
|
|
13919
|
+
} else if (event.type === "response.output_text.done" && event.text) {
|
|
13920
|
+
content = event.text;
|
|
13921
|
+
} else if (event.type === "response.completed" && event.response) {
|
|
13922
|
+
const resp = event.response;
|
|
13923
|
+
const usage = resp.usage;
|
|
13924
|
+
if (usage) {
|
|
13925
|
+
inputTokens = usage.input_tokens ?? 0;
|
|
13926
|
+
outputTokens = usage.output_tokens ?? 0;
|
|
13927
|
+
}
|
|
13928
|
+
status = resp.status ?? "completed";
|
|
13929
|
+
}
|
|
13930
|
+
});
|
|
13931
|
+
const stopReason = status === "completed" ? "end_turn" : status === "incomplete" ? "max_tokens" : "end_turn";
|
|
13932
|
+
return {
|
|
13933
|
+
id: responseId,
|
|
13934
|
+
content,
|
|
13935
|
+
stopReason,
|
|
13936
|
+
model,
|
|
13937
|
+
usage: { inputTokens, outputTokens }
|
|
13938
|
+
};
|
|
13939
|
+
}, this.retryConfig);
|
|
13940
|
+
}
|
|
13941
|
+
/**
|
|
13942
|
+
* Send a chat message with tool use via Responses API
|
|
13853
13943
|
*/
|
|
13854
13944
|
async chatWithTools(messages, options) {
|
|
13855
|
-
|
|
13856
|
-
|
|
13857
|
-
|
|
13858
|
-
|
|
13859
|
-
|
|
13860
|
-
|
|
13945
|
+
return withRetry(async () => {
|
|
13946
|
+
const model = options?.model ?? this.config.model ?? DEFAULT_MODEL3;
|
|
13947
|
+
const { input, instructions } = this.convertToResponsesInput(messages, options?.system);
|
|
13948
|
+
const body = this.buildRequestBody(model, input, instructions, {
|
|
13949
|
+
tools: options.tools,
|
|
13950
|
+
maxTokens: options?.maxTokens
|
|
13951
|
+
});
|
|
13952
|
+
const response = await this.makeRequest(body);
|
|
13953
|
+
let content = "";
|
|
13954
|
+
let responseId = `codex-${Date.now()}`;
|
|
13955
|
+
let inputTokens = 0;
|
|
13956
|
+
let outputTokens = 0;
|
|
13957
|
+
const toolCalls = [];
|
|
13958
|
+
const fnCallBuilders = /* @__PURE__ */ new Map();
|
|
13959
|
+
await this.readSSEStream(response, (event) => {
|
|
13960
|
+
if (event.id) responseId = event.id;
|
|
13961
|
+
switch (event.type) {
|
|
13962
|
+
case "response.output_text.delta":
|
|
13963
|
+
content += event.delta ?? "";
|
|
13964
|
+
break;
|
|
13965
|
+
case "response.output_text.done":
|
|
13966
|
+
content = event.text ?? content;
|
|
13967
|
+
break;
|
|
13968
|
+
case "response.output_item.added": {
|
|
13969
|
+
const item = event.item;
|
|
13970
|
+
if (item.type === "function_call") {
|
|
13971
|
+
const itemKey = item.id ?? item.call_id;
|
|
13972
|
+
fnCallBuilders.set(itemKey, {
|
|
13973
|
+
callId: item.call_id,
|
|
13974
|
+
name: item.name,
|
|
13975
|
+
arguments: ""
|
|
13976
|
+
});
|
|
13977
|
+
}
|
|
13978
|
+
break;
|
|
13979
|
+
}
|
|
13980
|
+
case "response.function_call_arguments.delta": {
|
|
13981
|
+
const builder = fnCallBuilders.get(event.item_id);
|
|
13982
|
+
if (builder) builder.arguments += event.delta ?? "";
|
|
13983
|
+
break;
|
|
13984
|
+
}
|
|
13985
|
+
case "response.function_call_arguments.done": {
|
|
13986
|
+
const builder = fnCallBuilders.get(event.item_id);
|
|
13987
|
+
if (builder) {
|
|
13988
|
+
toolCalls.push({
|
|
13989
|
+
id: builder.callId,
|
|
13990
|
+
name: builder.name,
|
|
13991
|
+
input: parseArguments(event.arguments)
|
|
13992
|
+
});
|
|
13993
|
+
fnCallBuilders.delete(event.item_id);
|
|
13994
|
+
}
|
|
13995
|
+
break;
|
|
13996
|
+
}
|
|
13997
|
+
case "response.completed": {
|
|
13998
|
+
const resp = event.response;
|
|
13999
|
+
const usage = resp.usage;
|
|
14000
|
+
if (usage) {
|
|
14001
|
+
inputTokens = usage.input_tokens ?? 0;
|
|
14002
|
+
outputTokens = usage.output_tokens ?? 0;
|
|
14003
|
+
}
|
|
14004
|
+
for (const [, builder] of fnCallBuilders) {
|
|
14005
|
+
toolCalls.push({
|
|
14006
|
+
id: builder.callId,
|
|
14007
|
+
name: builder.name,
|
|
14008
|
+
input: parseArguments(builder.arguments)
|
|
14009
|
+
});
|
|
14010
|
+
}
|
|
14011
|
+
fnCallBuilders.clear();
|
|
14012
|
+
break;
|
|
14013
|
+
}
|
|
14014
|
+
}
|
|
14015
|
+
});
|
|
14016
|
+
return {
|
|
14017
|
+
id: responseId,
|
|
14018
|
+
content,
|
|
14019
|
+
stopReason: toolCalls.length > 0 ? "tool_use" : "end_turn",
|
|
14020
|
+
model,
|
|
14021
|
+
usage: { inputTokens, outputTokens },
|
|
14022
|
+
toolCalls
|
|
14023
|
+
};
|
|
14024
|
+
}, this.retryConfig);
|
|
13861
14025
|
}
|
|
13862
14026
|
/**
|
|
13863
|
-
* Stream a chat response
|
|
13864
|
-
* Note: True streaming with Codex Responses API is complex.
|
|
13865
|
-
* For now, we make a non-streaming call and simulate streaming by emitting chunks.
|
|
14027
|
+
* Stream a chat response (no tools)
|
|
13866
14028
|
*/
|
|
13867
14029
|
async *stream(messages, options) {
|
|
13868
|
-
const
|
|
13869
|
-
|
|
13870
|
-
|
|
13871
|
-
|
|
13872
|
-
|
|
13873
|
-
|
|
13874
|
-
|
|
13875
|
-
|
|
13876
|
-
|
|
14030
|
+
const model = options?.model ?? this.config.model ?? DEFAULT_MODEL3;
|
|
14031
|
+
const { input, instructions } = this.convertToResponsesInput(messages, options?.system);
|
|
14032
|
+
const body = this.buildRequestBody(model, input, instructions, {
|
|
14033
|
+
maxTokens: options?.maxTokens
|
|
14034
|
+
});
|
|
14035
|
+
const response = await this.makeRequest(body);
|
|
14036
|
+
if (!response.body) {
|
|
14037
|
+
throw new ProviderError("No response body from Codex API", { provider: this.id });
|
|
14038
|
+
}
|
|
14039
|
+
const reader = response.body.getReader();
|
|
14040
|
+
const decoder = new TextDecoder();
|
|
14041
|
+
let buffer = "";
|
|
14042
|
+
let lastActivityTime = Date.now();
|
|
14043
|
+
const timeoutController = new AbortController();
|
|
14044
|
+
const timeoutInterval = setInterval(() => {
|
|
14045
|
+
if (Date.now() - lastActivityTime > STREAM_TIMEOUT_MS) {
|
|
14046
|
+
clearInterval(timeoutInterval);
|
|
14047
|
+
timeoutController.abort();
|
|
14048
|
+
}
|
|
14049
|
+
}, 5e3);
|
|
14050
|
+
try {
|
|
14051
|
+
while (true) {
|
|
14052
|
+
if (timeoutController.signal.aborted) break;
|
|
14053
|
+
const { done, value } = await reader.read();
|
|
14054
|
+
if (done) break;
|
|
14055
|
+
lastActivityTime = Date.now();
|
|
14056
|
+
buffer += decoder.decode(value, { stream: true });
|
|
14057
|
+
const lines = buffer.split("\n");
|
|
14058
|
+
buffer = lines.pop() ?? "";
|
|
14059
|
+
for (const line of lines) {
|
|
14060
|
+
if (!line.startsWith("data: ")) continue;
|
|
14061
|
+
const data = line.slice(6).trim();
|
|
14062
|
+
if (!data || data === "[DONE]") continue;
|
|
14063
|
+
try {
|
|
14064
|
+
const event = JSON.parse(data);
|
|
14065
|
+
if (event.type === "response.output_text.delta" && event.delta) {
|
|
14066
|
+
yield { type: "text", text: event.delta };
|
|
14067
|
+
} else if (event.type === "response.completed") {
|
|
14068
|
+
yield { type: "done", stopReason: "end_turn" };
|
|
14069
|
+
}
|
|
14070
|
+
} catch {
|
|
14071
|
+
}
|
|
13877
14072
|
}
|
|
13878
14073
|
}
|
|
14074
|
+
} finally {
|
|
14075
|
+
clearInterval(timeoutInterval);
|
|
14076
|
+
reader.releaseLock();
|
|
14077
|
+
}
|
|
14078
|
+
if (timeoutController.signal.aborted) {
|
|
14079
|
+
throw new Error(
|
|
14080
|
+
`Stream timeout: No response from Codex API for ${STREAM_TIMEOUT_MS / 1e3}s`
|
|
14081
|
+
);
|
|
13879
14082
|
}
|
|
13880
|
-
yield { type: "done", stopReason: response.stopReason };
|
|
13881
14083
|
}
|
|
13882
14084
|
/**
|
|
13883
|
-
* Stream a chat response with tool use
|
|
13884
|
-
*
|
|
13885
|
-
*
|
|
14085
|
+
* Stream a chat response with tool use via Responses API.
|
|
14086
|
+
*
|
|
14087
|
+
* IMPORTANT: fnCallBuilders is keyed by output item ID (item.id), NOT by
|
|
14088
|
+
* call_id. The streaming events (function_call_arguments.delta/done) use
|
|
14089
|
+
* item_id which references the output item's id field, not call_id.
|
|
13886
14090
|
*/
|
|
13887
14091
|
async *streamWithTools(messages, options) {
|
|
13888
|
-
|
|
14092
|
+
const model = options?.model ?? this.config.model ?? DEFAULT_MODEL3;
|
|
14093
|
+
const { input, instructions } = this.convertToResponsesInput(messages, options?.system);
|
|
14094
|
+
const body = this.buildRequestBody(model, input, instructions, {
|
|
14095
|
+
tools: options.tools,
|
|
14096
|
+
maxTokens: options?.maxTokens
|
|
14097
|
+
});
|
|
14098
|
+
const response = await this.makeRequest(body);
|
|
14099
|
+
if (!response.body) {
|
|
14100
|
+
throw new ProviderError("No response body from Codex API", { provider: this.id });
|
|
14101
|
+
}
|
|
14102
|
+
const reader = response.body.getReader();
|
|
14103
|
+
const decoder = new TextDecoder();
|
|
14104
|
+
let buffer = "";
|
|
14105
|
+
const fnCallBuilders = /* @__PURE__ */ new Map();
|
|
14106
|
+
let lastActivityTime = Date.now();
|
|
14107
|
+
const timeoutController = new AbortController();
|
|
14108
|
+
const timeoutInterval = setInterval(() => {
|
|
14109
|
+
if (Date.now() - lastActivityTime > STREAM_TIMEOUT_MS) {
|
|
14110
|
+
clearInterval(timeoutInterval);
|
|
14111
|
+
timeoutController.abort();
|
|
14112
|
+
}
|
|
14113
|
+
}, 5e3);
|
|
14114
|
+
try {
|
|
14115
|
+
while (true) {
|
|
14116
|
+
if (timeoutController.signal.aborted) break;
|
|
14117
|
+
const { done, value } = await reader.read();
|
|
14118
|
+
if (done) break;
|
|
14119
|
+
lastActivityTime = Date.now();
|
|
14120
|
+
buffer += decoder.decode(value, { stream: true });
|
|
14121
|
+
const lines = buffer.split("\n");
|
|
14122
|
+
buffer = lines.pop() ?? "";
|
|
14123
|
+
for (const line of lines) {
|
|
14124
|
+
if (!line.startsWith("data: ")) continue;
|
|
14125
|
+
const data = line.slice(6).trim();
|
|
14126
|
+
if (!data || data === "[DONE]") continue;
|
|
14127
|
+
let event;
|
|
14128
|
+
try {
|
|
14129
|
+
event = JSON.parse(data);
|
|
14130
|
+
} catch {
|
|
14131
|
+
continue;
|
|
14132
|
+
}
|
|
14133
|
+
switch (event.type) {
|
|
14134
|
+
case "response.output_text.delta":
|
|
14135
|
+
yield { type: "text", text: event.delta ?? "" };
|
|
14136
|
+
break;
|
|
14137
|
+
case "response.output_item.added": {
|
|
14138
|
+
const item = event.item;
|
|
14139
|
+
if (item.type === "function_call") {
|
|
14140
|
+
const itemKey = item.id ?? item.call_id;
|
|
14141
|
+
fnCallBuilders.set(itemKey, {
|
|
14142
|
+
callId: item.call_id,
|
|
14143
|
+
name: item.name,
|
|
14144
|
+
arguments: ""
|
|
14145
|
+
});
|
|
14146
|
+
yield {
|
|
14147
|
+
type: "tool_use_start",
|
|
14148
|
+
toolCall: { id: item.call_id, name: item.name }
|
|
14149
|
+
};
|
|
14150
|
+
}
|
|
14151
|
+
break;
|
|
14152
|
+
}
|
|
14153
|
+
case "response.function_call_arguments.delta": {
|
|
14154
|
+
const builder = fnCallBuilders.get(event.item_id);
|
|
14155
|
+
if (builder) {
|
|
14156
|
+
builder.arguments += event.delta ?? "";
|
|
14157
|
+
}
|
|
14158
|
+
break;
|
|
14159
|
+
}
|
|
14160
|
+
case "response.function_call_arguments.done": {
|
|
14161
|
+
const builder = fnCallBuilders.get(event.item_id);
|
|
14162
|
+
if (builder) {
|
|
14163
|
+
yield {
|
|
14164
|
+
type: "tool_use_end",
|
|
14165
|
+
toolCall: {
|
|
14166
|
+
id: builder.callId,
|
|
14167
|
+
name: builder.name,
|
|
14168
|
+
input: parseArguments(event.arguments ?? builder.arguments)
|
|
14169
|
+
}
|
|
14170
|
+
};
|
|
14171
|
+
fnCallBuilders.delete(event.item_id);
|
|
14172
|
+
}
|
|
14173
|
+
break;
|
|
14174
|
+
}
|
|
14175
|
+
case "response.completed": {
|
|
14176
|
+
for (const [, builder] of fnCallBuilders) {
|
|
14177
|
+
yield {
|
|
14178
|
+
type: "tool_use_end",
|
|
14179
|
+
toolCall: {
|
|
14180
|
+
id: builder.callId,
|
|
14181
|
+
name: builder.name,
|
|
14182
|
+
input: parseArguments(builder.arguments)
|
|
14183
|
+
}
|
|
14184
|
+
};
|
|
14185
|
+
}
|
|
14186
|
+
fnCallBuilders.clear();
|
|
14187
|
+
const resp = event.response;
|
|
14188
|
+
const output = resp?.output ?? [];
|
|
14189
|
+
const hasToolCalls = output.some((i) => i.type === "function_call");
|
|
14190
|
+
yield {
|
|
14191
|
+
type: "done",
|
|
14192
|
+
stopReason: hasToolCalls ? "tool_use" : "end_turn"
|
|
14193
|
+
};
|
|
14194
|
+
break;
|
|
14195
|
+
}
|
|
14196
|
+
}
|
|
14197
|
+
}
|
|
14198
|
+
}
|
|
14199
|
+
} finally {
|
|
14200
|
+
clearInterval(timeoutInterval);
|
|
14201
|
+
reader.releaseLock();
|
|
14202
|
+
}
|
|
14203
|
+
if (timeoutController.signal.aborted) {
|
|
14204
|
+
throw new Error(
|
|
14205
|
+
`Stream timeout: No response from Codex API for ${STREAM_TIMEOUT_MS / 1e3}s`
|
|
14206
|
+
);
|
|
14207
|
+
}
|
|
13889
14208
|
}
|
|
13890
14209
|
};
|
|
13891
14210
|
var CONTEXT_WINDOWS4 = {
|
|
@@ -13898,6 +14217,7 @@ var CONTEXT_WINDOWS4 = {
|
|
|
13898
14217
|
// OpenAI models — chat/completions
|
|
13899
14218
|
"gpt-4.1": 1048576,
|
|
13900
14219
|
// OpenAI models — /responses API (Codex/GPT-5+)
|
|
14220
|
+
"gpt-5.4-codex": 4e5,
|
|
13901
14221
|
"gpt-5.3-codex": 4e5,
|
|
13902
14222
|
"gpt-5.2-codex": 4e5,
|
|
13903
14223
|
"gpt-5.1-codex-max": 4e5,
|