@corbat-tech/coco 2.23.0 → 2.23.1

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 CHANGED
@@ -1792,6 +1792,7 @@ var init_openai = __esm({
1792
1792
  requestParams
1793
1793
  );
1794
1794
  const toolCallBuilders = /* @__PURE__ */ new Map();
1795
+ let lastToolCallKey = null;
1795
1796
  const streamTimeout = this.config.timeout ?? 12e4;
1796
1797
  let lastActivityTime = Date.now();
1797
1798
  const timeoutController = new AbortController();
@@ -1841,9 +1842,9 @@ var init_openai = __esm({
1841
1842
  }
1842
1843
  if (delta?.tool_calls) {
1843
1844
  for (const toolCallDelta of delta.tool_calls) {
1844
- const index = toolCallDelta.index ?? toolCallBuilders.size;
1845
- if (!toolCallBuilders.has(index)) {
1846
- toolCallBuilders.set(index, {
1845
+ const key = typeof toolCallDelta.index === "number" ? `index:${toolCallDelta.index}` : typeof toolCallDelta.id === "string" && toolCallDelta.id.length > 0 ? `id:${toolCallDelta.id}` : toolCallBuilders.size === 1 ? Array.from(toolCallBuilders.keys())[0] ?? `fallback:${toolCallBuilders.size}` : lastToolCallKey ?? `fallback:${toolCallBuilders.size}`;
1846
+ if (!toolCallBuilders.has(key)) {
1847
+ toolCallBuilders.set(key, {
1847
1848
  id: toolCallDelta.id ?? "",
1848
1849
  name: toolCallDelta.function?.name ?? "",
1849
1850
  arguments: ""
@@ -1856,7 +1857,8 @@ var init_openai = __esm({
1856
1857
  }
1857
1858
  };
1858
1859
  }
1859
- const builder = toolCallBuilders.get(index);
1860
+ const builder = toolCallBuilders.get(key);
1861
+ lastToolCallKey = key;
1860
1862
  if (toolCallDelta.id) {
1861
1863
  builder.id = toolCallDelta.id;
1862
1864
  }
@@ -2444,6 +2446,7 @@ var init_openai = __esm({
2444
2446
  requestParams
2445
2447
  );
2446
2448
  const fnCallBuilders = /* @__PURE__ */ new Map();
2449
+ const outputIndexToBuilderKey = /* @__PURE__ */ new Map();
2447
2450
  const streamTimeout = this.config.timeout ?? 12e4;
2448
2451
  let lastActivityTime = Date.now();
2449
2452
  const timeoutController = new AbortController();
@@ -2473,8 +2476,11 @@ var init_openai = __esm({
2473
2476
  fnCallBuilders.set(itemKey, {
2474
2477
  callId: fc.call_id,
2475
2478
  name: fc.name,
2476
- arguments: ""
2479
+ arguments: fc.arguments ?? ""
2477
2480
  });
2481
+ if (typeof event.output_index === "number") {
2482
+ outputIndexToBuilderKey.set(event.output_index, itemKey);
2483
+ }
2478
2484
  yield {
2479
2485
  type: "tool_use_start",
2480
2486
  toolCall: { id: fc.call_id, name: fc.name }
@@ -2483,7 +2489,9 @@ var init_openai = __esm({
2483
2489
  break;
2484
2490
  case "response.function_call_arguments.delta":
2485
2491
  {
2486
- const builder = fnCallBuilders.get(event.item_id);
2492
+ const builderKey = (event.item_id && fnCallBuilders.has(event.item_id) ? event.item_id : null) ?? (typeof event.output_index === "number" ? outputIndexToBuilderKey.get(event.output_index) ?? null : null) ?? (fnCallBuilders.size === 1 ? Array.from(fnCallBuilders.keys())[0] : null);
2493
+ if (!builderKey) break;
2494
+ const builder = fnCallBuilders.get(builderKey);
2487
2495
  if (builder) {
2488
2496
  builder.arguments += event.delta;
2489
2497
  }
@@ -2491,17 +2499,22 @@ var init_openai = __esm({
2491
2499
  break;
2492
2500
  case "response.function_call_arguments.done":
2493
2501
  {
2494
- const builder = fnCallBuilders.get(event.item_id);
2502
+ const builderKey = (event.item_id && fnCallBuilders.has(event.item_id) ? event.item_id : null) ?? (typeof event.output_index === "number" ? outputIndexToBuilderKey.get(event.output_index) ?? null : null) ?? (fnCallBuilders.size === 1 ? Array.from(fnCallBuilders.keys())[0] : null);
2503
+ if (!builderKey) break;
2504
+ const builder = fnCallBuilders.get(builderKey);
2495
2505
  if (builder) {
2496
2506
  yield {
2497
2507
  type: "tool_use_end",
2498
2508
  toolCall: {
2499
2509
  id: builder.callId,
2500
2510
  name: builder.name,
2501
- input: this.parseResponsesArguments(event.arguments)
2511
+ input: this.parseResponsesArguments(event.arguments ?? builder.arguments)
2502
2512
  }
2503
2513
  };
2504
- fnCallBuilders.delete(event.item_id);
2514
+ fnCallBuilders.delete(builderKey);
2515
+ for (const [idx, key] of outputIndexToBuilderKey.entries()) {
2516
+ if (key === builderKey) outputIndexToBuilderKey.delete(idx);
2517
+ }
2505
2518
  }
2506
2519
  }
2507
2520
  break;
@@ -4640,6 +4653,7 @@ var init_codex = __esm({
4640
4653
  let outputTokens = 0;
4641
4654
  const toolCalls = [];
4642
4655
  const fnCallBuilders = /* @__PURE__ */ new Map();
4656
+ const outputIndexToBuilderKey = /* @__PURE__ */ new Map();
4643
4657
  await this.readSSEStream(response, (event) => {
4644
4658
  if (event.id) responseId = event.id;
4645
4659
  switch (event.type) {
@@ -4656,25 +4670,35 @@ var init_codex = __esm({
4656
4670
  fnCallBuilders.set(itemKey, {
4657
4671
  callId: item.call_id,
4658
4672
  name: item.name,
4659
- arguments: ""
4673
+ arguments: item.arguments ?? ""
4660
4674
  });
4675
+ if (typeof event.output_index === "number") {
4676
+ outputIndexToBuilderKey.set(event.output_index, itemKey);
4677
+ }
4661
4678
  }
4662
4679
  break;
4663
4680
  }
4664
4681
  case "response.function_call_arguments.delta": {
4665
- const builder = fnCallBuilders.get(event.item_id);
4682
+ const builderKey = (event.item_id && fnCallBuilders.has(event.item_id) ? event.item_id : null) ?? (typeof event.output_index === "number" ? outputIndexToBuilderKey.get(event.output_index) ?? null : null) ?? (fnCallBuilders.size === 1 ? Array.from(fnCallBuilders.keys())[0] : null);
4683
+ if (!builderKey) break;
4684
+ const builder = fnCallBuilders.get(builderKey);
4666
4685
  if (builder) builder.arguments += event.delta ?? "";
4667
4686
  break;
4668
4687
  }
4669
4688
  case "response.function_call_arguments.done": {
4670
- const builder = fnCallBuilders.get(event.item_id);
4689
+ const builderKey = (event.item_id && fnCallBuilders.has(event.item_id) ? event.item_id : null) ?? (typeof event.output_index === "number" ? outputIndexToBuilderKey.get(event.output_index) ?? null : null) ?? (fnCallBuilders.size === 1 ? Array.from(fnCallBuilders.keys())[0] : null);
4690
+ if (!builderKey) break;
4691
+ const builder = fnCallBuilders.get(builderKey);
4671
4692
  if (builder) {
4672
4693
  toolCalls.push({
4673
4694
  id: builder.callId,
4674
4695
  name: builder.name,
4675
- input: parseArguments(event.arguments)
4696
+ input: parseArguments(event.arguments ?? builder.arguments)
4676
4697
  });
4677
- fnCallBuilders.delete(event.item_id);
4698
+ fnCallBuilders.delete(builderKey);
4699
+ for (const [idx, key] of outputIndexToBuilderKey.entries()) {
4700
+ if (key === builderKey) outputIndexToBuilderKey.delete(idx);
4701
+ }
4678
4702
  }
4679
4703
  break;
4680
4704
  }
@@ -4787,6 +4811,7 @@ var init_codex = __esm({
4787
4811
  const decoder = new TextDecoder();
4788
4812
  let buffer = "";
4789
4813
  const fnCallBuilders = /* @__PURE__ */ new Map();
4814
+ const outputIndexToBuilderKey = /* @__PURE__ */ new Map();
4790
4815
  let lastActivityTime = Date.now();
4791
4816
  const timeoutController = new AbortController();
4792
4817
  const timeoutInterval = setInterval(() => {
@@ -4825,8 +4850,11 @@ var init_codex = __esm({
4825
4850
  fnCallBuilders.set(itemKey, {
4826
4851
  callId: item.call_id,
4827
4852
  name: item.name,
4828
- arguments: ""
4853
+ arguments: item.arguments ?? ""
4829
4854
  });
4855
+ if (typeof event.output_index === "number") {
4856
+ outputIndexToBuilderKey.set(event.output_index, itemKey);
4857
+ }
4830
4858
  yield {
4831
4859
  type: "tool_use_start",
4832
4860
  toolCall: { id: item.call_id, name: item.name }
@@ -4835,14 +4863,18 @@ var init_codex = __esm({
4835
4863
  break;
4836
4864
  }
4837
4865
  case "response.function_call_arguments.delta": {
4838
- const builder = fnCallBuilders.get(event.item_id);
4866
+ const builderKey = (event.item_id && fnCallBuilders.has(event.item_id) ? event.item_id : null) ?? (typeof event.output_index === "number" ? outputIndexToBuilderKey.get(event.output_index) ?? null : null) ?? (fnCallBuilders.size === 1 ? Array.from(fnCallBuilders.keys())[0] : null);
4867
+ if (!builderKey) break;
4868
+ const builder = fnCallBuilders.get(builderKey);
4839
4869
  if (builder) {
4840
4870
  builder.arguments += event.delta ?? "";
4841
4871
  }
4842
4872
  break;
4843
4873
  }
4844
4874
  case "response.function_call_arguments.done": {
4845
- const builder = fnCallBuilders.get(event.item_id);
4875
+ const builderKey = (event.item_id && fnCallBuilders.has(event.item_id) ? event.item_id : null) ?? (typeof event.output_index === "number" ? outputIndexToBuilderKey.get(event.output_index) ?? null : null) ?? (fnCallBuilders.size === 1 ? Array.from(fnCallBuilders.keys())[0] : null);
4876
+ if (!builderKey) break;
4877
+ const builder = fnCallBuilders.get(builderKey);
4846
4878
  if (builder) {
4847
4879
  yield {
4848
4880
  type: "tool_use_end",
@@ -4852,7 +4884,10 @@ var init_codex = __esm({
4852
4884
  input: parseArguments(event.arguments ?? builder.arguments)
4853
4885
  }
4854
4886
  };
4855
- fnCallBuilders.delete(event.item_id);
4887
+ fnCallBuilders.delete(builderKey);
4888
+ for (const [idx, key] of outputIndexToBuilderKey.entries()) {
4889
+ if (key === builderKey) outputIndexToBuilderKey.delete(idx);
4890
+ }
4856
4891
  }
4857
4892
  break;
4858
4893
  }
@@ -5243,8 +5278,8 @@ var init_gemini = __esm({
5243
5278
  const { history, lastMessage } = this.convertMessages(messages);
5244
5279
  const chat = model.startChat({ history });
5245
5280
  const result = await chat.sendMessageStream(lastMessage);
5246
- const emittedToolCalls = /* @__PURE__ */ new Set();
5247
5281
  let streamStopReason;
5282
+ let streamToolCallCounter = 0;
5248
5283
  for await (const chunk of result.stream) {
5249
5284
  const text13 = chunk.text();
5250
5285
  if (text13) {
@@ -5259,30 +5294,23 @@ var init_gemini = __esm({
5259
5294
  for (const part of candidate.content.parts) {
5260
5295
  if ("functionCall" in part && part.functionCall) {
5261
5296
  const funcCall = part.functionCall;
5262
- const sortedArgs = funcCall.args ? Object.keys(funcCall.args).sort().map(
5263
- (k) => `${k}:${JSON.stringify(funcCall.args[k])}`
5264
- ).join(",") : "";
5265
- const callKey = `${funcCall.name}-${sortedArgs}`;
5266
- if (!emittedToolCalls.has(callKey)) {
5267
- emittedToolCalls.add(callKey);
5268
- const toolCall = {
5269
- id: funcCall.name,
5270
- // Gemini uses name as ID
5271
- name: funcCall.name,
5272
- input: funcCall.args ?? {}
5273
- };
5274
- yield {
5275
- type: "tool_use_start",
5276
- toolCall: {
5277
- id: toolCall.id,
5278
- name: toolCall.name
5279
- }
5280
- };
5281
- yield {
5282
- type: "tool_use_end",
5283
- toolCall
5284
- };
5285
- }
5297
+ streamToolCallCounter++;
5298
+ const toolCall = {
5299
+ id: `gemini_call_${streamToolCallCounter}`,
5300
+ name: funcCall.name,
5301
+ input: funcCall.args ?? {}
5302
+ };
5303
+ yield {
5304
+ type: "tool_use_start",
5305
+ toolCall: {
5306
+ id: toolCall.id,
5307
+ name: toolCall.name
5308
+ }
5309
+ };
5310
+ yield {
5311
+ type: "tool_use_end",
5312
+ toolCall
5313
+ };
5286
5314
  }
5287
5315
  }
5288
5316
  }
@@ -5357,13 +5385,13 @@ var init_gemini = __esm({
5357
5385
  * Convert messages to Gemini format
5358
5386
  */
5359
5387
  convertMessages(messages) {
5388
+ const toolNameByUseId = this.buildToolUseNameMap(messages);
5389
+ const conversation = messages.filter((m) => m.role !== "system");
5360
5390
  const history = [];
5361
5391
  let lastUserMessage = "";
5362
- for (const msg of messages) {
5363
- if (msg.role === "system") {
5364
- continue;
5365
- }
5366
- const parts = this.convertContent(msg.content);
5392
+ for (let i = 0; i < conversation.length; i++) {
5393
+ const msg = conversation[i];
5394
+ const isLastMessage = i === conversation.length - 1;
5367
5395
  if (msg.role === "user") {
5368
5396
  if (Array.isArray(msg.content) && msg.content[0]?.type === "tool_result") {
5369
5397
  const functionResponses = [];
@@ -5372,23 +5400,49 @@ var init_gemini = __esm({
5372
5400
  const toolResult = block;
5373
5401
  functionResponses.push({
5374
5402
  functionResponse: {
5375
- name: toolResult.tool_use_id,
5376
- // Gemini uses name, we store it in tool_use_id
5403
+ // Gemini expects the function name in functionResponse.name.
5404
+ // Recover it from prior assistant tool_use blocks when possible.
5405
+ name: toolNameByUseId.get(toolResult.tool_use_id) ?? toolResult.tool_use_id,
5377
5406
  response: { result: toolResult.content }
5378
5407
  }
5379
5408
  });
5380
5409
  }
5381
5410
  }
5382
- history.push({ role: "user", parts: functionResponses });
5411
+ if (isLastMessage) {
5412
+ lastUserMessage = functionResponses;
5413
+ } else {
5414
+ history.push({ role: "user", parts: functionResponses });
5415
+ }
5383
5416
  } else {
5384
- lastUserMessage = parts;
5417
+ const parts = this.convertContent(msg.content);
5418
+ if (isLastMessage) {
5419
+ lastUserMessage = parts;
5420
+ } else {
5421
+ history.push({ role: "user", parts });
5422
+ }
5385
5423
  }
5386
5424
  } else if (msg.role === "assistant") {
5425
+ const parts = this.convertContent(msg.content);
5387
5426
  history.push({ role: "model", parts });
5388
5427
  }
5389
5428
  }
5390
5429
  return { history, lastMessage: lastUserMessage };
5391
5430
  }
5431
+ /**
5432
+ * Build a map from tool_use IDs to function names from assistant history.
5433
+ */
5434
+ buildToolUseNameMap(messages) {
5435
+ const map = /* @__PURE__ */ new Map();
5436
+ for (const msg of messages) {
5437
+ if (msg.role !== "assistant" || !Array.isArray(msg.content)) continue;
5438
+ for (const block of msg.content) {
5439
+ if (block.type === "tool_use") {
5440
+ map.set(block.id, block.name);
5441
+ }
5442
+ }
5443
+ }
5444
+ return map;
5445
+ }
5392
5446
  /**
5393
5447
  * Convert content to Gemini parts
5394
5448
  */
@@ -5465,14 +5519,15 @@ var init_gemini = __esm({
5465
5519
  let textContent = "";
5466
5520
  const toolCalls = [];
5467
5521
  if (candidate?.content?.parts) {
5522
+ let toolIndex = 0;
5468
5523
  for (const part of candidate.content.parts) {
5469
5524
  if ("text" in part && part.text) {
5470
5525
  textContent += part.text;
5471
5526
  }
5472
5527
  if ("functionCall" in part && part.functionCall) {
5528
+ toolIndex++;
5473
5529
  toolCalls.push({
5474
- id: part.functionCall.name,
5475
- // Use name as ID for Gemini
5530
+ id: `gemini_call_${toolIndex}`,
5476
5531
  name: part.functionCall.name,
5477
5532
  input: part.functionCall.args ?? {}
5478
5533
  });
@@ -31546,6 +31601,15 @@ async function selectModelInteractively(models, currentModelId) {
31546
31601
  renderMenu();
31547
31602
  });
31548
31603
  }
31604
+ async function persistModelPreference(provider, model) {
31605
+ try {
31606
+ await saveProviderPreference(provider, model);
31607
+ } catch (error) {
31608
+ const reason = error instanceof Error ? error.message : String(error);
31609
+ console.log(chalk2.yellow(`\u26A0 Could not persist model preference: ${reason}`));
31610
+ console.log(chalk2.dim(" Model changed for this session only.\n"));
31611
+ }
31612
+ }
31549
31613
  var modelCommand = {
31550
31614
  name: "model",
31551
31615
  aliases: ["m"],
@@ -31594,7 +31658,7 @@ var modelCommand = {
31594
31658
  return false;
31595
31659
  }
31596
31660
  session.config.provider.model = selectedModel;
31597
- await saveProviderPreference(currentProvider, selectedModel);
31661
+ await persistModelPreference(currentProvider, selectedModel);
31598
31662
  const modelInfo2 = providerDef.models.find((m) => m.id === selectedModel);
31599
31663
  console.log(chalk2.green(`\u2713 Switched to ${modelInfo2?.name ?? selectedModel}
31600
31664
  `));
@@ -31616,7 +31680,7 @@ var modelCommand = {
31616
31680
  if (!foundInProvider) {
31617
31681
  console.log(chalk2.yellow(`Model "${newModel}" not in known list, setting anyway...`));
31618
31682
  session.config.provider.model = newModel;
31619
- await saveProviderPreference(currentProvider, newModel);
31683
+ await persistModelPreference(currentProvider, newModel);
31620
31684
  console.log(chalk2.green(`\u2713 Model set to: ${newModel}
31621
31685
  `));
31622
31686
  return false;
@@ -31631,7 +31695,7 @@ var modelCommand = {
31631
31695
  return false;
31632
31696
  }
31633
31697
  session.config.provider.model = newModel;
31634
- await saveProviderPreference(currentProvider, newModel);
31698
+ await persistModelPreference(currentProvider, newModel);
31635
31699
  const modelInfo = providerDef.models.find((m) => m.id === newModel);
31636
31700
  console.log(chalk2.green(`\u2713 Switched to ${modelInfo?.name ?? newModel}
31637
31701
  `));