@corbat-tech/coco 2.12.0 → 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/index.js CHANGED
@@ -12527,7 +12527,7 @@ var OpenAIProvider = class {
12527
12527
  once: true
12528
12528
  });
12529
12529
  const providerName = this.name;
12530
- const parseArguments = (builder) => {
12530
+ const parseArguments2 = (builder) => {
12531
12531
  let input = {};
12532
12532
  try {
12533
12533
  input = builder.arguments ? JSON.parse(builder.arguments) : {};
@@ -12608,7 +12608,7 @@ var OpenAIProvider = class {
12608
12608
  toolCall: {
12609
12609
  id: builder.id,
12610
12610
  name: builder.name,
12611
- input: parseArguments(builder)
12611
+ input: parseArguments2(builder)
12612
12612
  }
12613
12613
  };
12614
12614
  }
@@ -12621,7 +12621,7 @@ var OpenAIProvider = class {
12621
12621
  toolCall: {
12622
12622
  id: builder.id,
12623
12623
  name: builder.name,
12624
- input: parseArguments(builder)
12624
+ input: parseArguments2(builder)
12625
12625
  }
12626
12626
  };
12627
12627
  }
@@ -13623,6 +13623,7 @@ var CONTEXT_WINDOWS3 = {
13623
13623
  "gpt-5.2": 2e5,
13624
13624
  "gpt-5.1": 2e5
13625
13625
  };
13626
+ var STREAM_TIMEOUT_MS = 12e4;
13626
13627
  function parseJwtClaims(token) {
13627
13628
  const parts = token.split(".");
13628
13629
  if (parts.length !== 3 || !parts[1]) return void 0;
@@ -13638,12 +13639,28 @@ function extractAccountId(accessToken) {
13638
13639
  const auth = claims["https://api.openai.com/auth"];
13639
13640
  return claims["chatgpt_account_id"] || auth?.["chatgpt_account_id"] || claims["organizations"]?.[0]?.id;
13640
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
+ }
13641
13657
  var CodexProvider = class {
13642
13658
  id = "codex";
13643
13659
  name = "OpenAI Codex (ChatGPT Plus/Pro)";
13644
13660
  config = {};
13645
13661
  accessToken = null;
13646
13662
  accountId;
13663
+ retryConfig = DEFAULT_RETRY_CONFIG;
13647
13664
  /**
13648
13665
  * Initialize the provider with OAuth tokens
13649
13666
  */
@@ -13728,166 +13745,466 @@ var CodexProvider = class {
13728
13745
  /**
13729
13746
  * Extract text content from a message
13730
13747
  */
13731
- extractTextContent(msg) {
13732
- if (typeof msg.content === "string") {
13733
- return msg.content;
13734
- }
13735
- if (Array.isArray(msg.content)) {
13736
- return msg.content.map((part) => {
13737
- if (part.type === "text") return part.text;
13738
- if (part.type === "tool_result") return `Tool result: ${JSON.stringify(part.content)}`;
13739
- return "";
13740
- }).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");
13741
13752
  }
13742
13753
  return "";
13743
13754
  }
13744
13755
  /**
13745
- * Convert messages to Codex Responses API format
13746
- * Codex uses a different format than Chat Completions:
13747
- * {
13748
- * "input": [
13749
- * { "type": "message", "role": "developer|user", "content": [{ "type": "input_text", "text": "..." }] },
13750
- * { "type": "message", "role": "assistant", "content": [{ "type": "output_text", "text": "..." }] }
13751
- * ]
13752
- * }
13756
+ * Convert messages to Responses API input format.
13753
13757
  *
13754
- * IMPORTANT: User/developer messages use "input_text", assistant messages use "output_text"
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
13755
13764
  */
13756
- convertMessagesToResponsesFormat(messages) {
13757
- return messages.map((msg) => {
13758
- const text = this.extractTextContent(msg);
13759
- const role = msg.role === "system" ? "developer" : msg.role;
13760
- const contentType = msg.role === "assistant" ? "output_text" : "input_text";
13761
- return {
13762
- type: "message",
13763
- role,
13764
- content: [{ type: contentType, text }]
13765
- };
13766
- });
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 };
13767
13818
  }
13768
13819
  /**
13769
- * Send a chat message using Codex Responses API format
13820
+ * Convert tool definitions to Responses API function tool format
13770
13821
  */
13771
- async chat(messages, options) {
13772
- const model = options?.model ?? this.config.model ?? DEFAULT_MODEL3;
13773
- const systemMsg = messages.find((m) => m.role === "system");
13774
- const instructions = systemMsg ? this.extractTextContent(systemMsg) : "You are a helpful coding assistant.";
13775
- const inputMessages = messages.filter((m) => m.role !== "system").map((msg) => this.convertMessagesToResponsesFormat([msg])[0]);
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) {
13776
13835
  const body = {
13777
13836
  model,
13778
- instructions,
13779
- input: inputMessages,
13780
- tools: [],
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,
13781
13841
  store: false,
13782
13842
  stream: true
13783
13843
  // Codex API requires streaming
13784
13844
  };
13785
- const response = await this.makeRequest(body);
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) {
13786
13855
  if (!response.body) {
13787
- throw new ProviderError("No response body from Codex API", {
13788
- provider: this.id
13789
- });
13856
+ throw new ProviderError("No response body from Codex API", { provider: this.id });
13790
13857
  }
13791
13858
  const reader = response.body.getReader();
13792
13859
  const decoder = new TextDecoder();
13793
13860
  let buffer = "";
13794
- let content = "";
13795
- let responseId = `codex-${Date.now()}`;
13796
- let inputTokens = 0;
13797
- let outputTokens = 0;
13798
- let status = "completed";
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);
13799
13869
  try {
13800
13870
  while (true) {
13871
+ if (timeoutController.signal.aborted) break;
13801
13872
  const { done, value } = await reader.read();
13802
13873
  if (done) break;
13874
+ lastActivityTime = Date.now();
13803
13875
  buffer += decoder.decode(value, { stream: true });
13804
13876
  const lines = buffer.split("\n");
13805
13877
  buffer = lines.pop() ?? "";
13806
13878
  for (const line of lines) {
13807
- if (line.startsWith("data: ")) {
13808
- const data = line.slice(6).trim();
13809
- if (!data || data === "[DONE]") continue;
13810
- try {
13811
- const parsed = JSON.parse(data);
13812
- if (parsed.id) {
13813
- responseId = parsed.id;
13814
- }
13815
- if (parsed.type === "response.output_text.delta" && parsed.delta) {
13816
- content += parsed.delta;
13817
- } else if (parsed.type === "response.completed" && parsed.response) {
13818
- if (parsed.response.usage) {
13819
- inputTokens = parsed.response.usage.input_tokens ?? 0;
13820
- outputTokens = parsed.response.usage.output_tokens ?? 0;
13821
- }
13822
- status = parsed.response.status ?? "completed";
13823
- } else if (parsed.type === "response.output_text.done" && parsed.text) {
13824
- content = parsed.text;
13825
- }
13826
- } catch {
13827
- }
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 {
13828
13885
  }
13829
13886
  }
13830
13887
  }
13831
13888
  } finally {
13889
+ clearInterval(timeoutInterval);
13832
13890
  reader.releaseLock();
13833
13891
  }
13834
- if (!content) {
13835
- throw new ProviderError("No response content from Codex API", {
13836
- provider: this.id
13837
- });
13892
+ if (timeoutController.signal.aborted) {
13893
+ throw new Error(
13894
+ `Stream timeout: No response from Codex API for ${STREAM_TIMEOUT_MS / 1e3}s`
13895
+ );
13838
13896
  }
13839
- const stopReason = status === "completed" ? "end_turn" : status === "incomplete" ? "max_tokens" : "end_turn";
13840
- return {
13841
- id: responseId,
13842
- content,
13843
- stopReason,
13844
- model,
13845
- usage: {
13846
- inputTokens,
13847
- outputTokens
13848
- }
13849
- };
13850
13897
  }
13851
13898
  /**
13852
- * Send a chat message with tool use
13853
- * Note: Codex Responses API tool support is complex; for now we delegate to chat()
13854
- * and return empty toolCalls. Full tool support can be added later.
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
13855
13943
  */
13856
13944
  async chatWithTools(messages, options) {
13857
- const response = await this.chat(messages, options);
13858
- return {
13859
- ...response,
13860
- toolCalls: []
13861
- // Tools not yet supported in Codex provider
13862
- };
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);
13863
14025
  }
13864
14026
  /**
13865
- * Stream a chat response
13866
- * Note: True streaming with Codex Responses API is complex.
13867
- * For now, we make a non-streaming call and simulate streaming by emitting chunks.
14027
+ * Stream a chat response (no tools)
13868
14028
  */
13869
14029
  async *stream(messages, options) {
13870
- const response = await this.chat(messages, options);
13871
- if (response.content) {
13872
- const content = response.content;
13873
- const chunkSize = 20;
13874
- for (let i = 0; i < content.length; i += chunkSize) {
13875
- const chunk = content.slice(i, i + chunkSize);
13876
- yield { type: "text", text: chunk };
13877
- if (i + chunkSize < content.length) {
13878
- await new Promise((resolve3) => setTimeout(resolve3, 5));
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
+ }
13879
14072
  }
13880
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
+ );
13881
14082
  }
13882
- yield { type: "done", stopReason: response.stopReason };
13883
14083
  }
13884
14084
  /**
13885
- * Stream a chat response with tool use
13886
- * Note: Tools and true streaming with Codex Responses API are not yet implemented.
13887
- * For now, we delegate to stream() which uses non-streaming under the hood.
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.
13888
14090
  */
13889
14091
  async *streamWithTools(messages, options) {
13890
- yield* this.stream(messages, options);
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
+ }
13891
14208
  }
13892
14209
  };
13893
14210
  var CONTEXT_WINDOWS4 = {