@corbat-tech/coco 2.8.1 → 2.9.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.d.ts CHANGED
@@ -1032,6 +1032,7 @@ interface StreamChunk {
1032
1032
  type: "text" | "tool_use_start" | "tool_use_delta" | "tool_use_end" | "done";
1033
1033
  text?: string;
1034
1034
  toolCall?: Partial<ToolCall>;
1035
+ stopReason?: "end_turn" | "max_tokens" | "stop_sequence" | "tool_use";
1035
1036
  }
1036
1037
  /**
1037
1038
  * LLM Provider interface
package/dist/index.js CHANGED
@@ -25,6 +25,7 @@ import JSON5 from 'json5';
25
25
  import 'events';
26
26
  import 'minimatch';
27
27
  import { simpleGit } from 'simple-git';
28
+ import { diffWords } from 'diff';
28
29
  import hljs from 'highlight.js/lib/core';
29
30
  import bash from 'highlight.js/lib/languages/bash';
30
31
  import css from 'highlight.js/lib/languages/css';
@@ -11803,6 +11804,7 @@ var AnthropicProvider = class {
11803
11804
  };
11804
11805
  const timeoutInterval = setInterval(checkTimeout, 5e3);
11805
11806
  try {
11807
+ let streamStopReason;
11806
11808
  for await (const event of stream) {
11807
11809
  lastActivityTime = Date.now();
11808
11810
  if (event.type === "content_block_delta") {
@@ -11810,9 +11812,14 @@ var AnthropicProvider = class {
11810
11812
  if (delta.type === "text_delta" && delta.text) {
11811
11813
  yield { type: "text", text: delta.text };
11812
11814
  }
11815
+ } else if (event.type === "message_delta") {
11816
+ const delta = event.delta;
11817
+ if (delta.stop_reason) {
11818
+ streamStopReason = this.mapStopReason(delta.stop_reason);
11819
+ }
11813
11820
  }
11814
11821
  }
11815
- yield { type: "done" };
11822
+ yield { type: "done", stopReason: streamStopReason };
11816
11823
  } finally {
11817
11824
  clearInterval(timeoutInterval);
11818
11825
  }
@@ -11849,9 +11856,15 @@ var AnthropicProvider = class {
11849
11856
  };
11850
11857
  const timeoutInterval = setInterval(checkTimeout, 5e3);
11851
11858
  try {
11859
+ let streamStopReason;
11852
11860
  for await (const event of stream) {
11853
11861
  lastActivityTime = Date.now();
11854
- if (event.type === "content_block_start") {
11862
+ if (event.type === "message_delta") {
11863
+ const delta = event.delta;
11864
+ if (delta.stop_reason) {
11865
+ streamStopReason = this.mapStopReason(delta.stop_reason);
11866
+ }
11867
+ } else if (event.type === "content_block_start") {
11855
11868
  const contentBlock = event.content_block;
11856
11869
  if (contentBlock.type === "tool_use") {
11857
11870
  if (currentToolCall) {
@@ -11922,7 +11935,7 @@ var AnthropicProvider = class {
11922
11935
  }
11923
11936
  }
11924
11937
  }
11925
- yield { type: "done" };
11938
+ yield { type: "done", stopReason: streamStopReason };
11926
11939
  } finally {
11927
11940
  clearInterval(timeoutInterval);
11928
11941
  }
@@ -12418,13 +12431,18 @@ var OpenAIProvider = class {
12418
12431
  stream: true,
12419
12432
  ...supportsTemp && { temperature: options?.temperature ?? this.config.temperature ?? 0 }
12420
12433
  });
12434
+ let streamStopReason;
12421
12435
  for await (const chunk of stream) {
12422
12436
  const delta = chunk.choices[0]?.delta;
12423
12437
  if (delta?.content) {
12424
12438
  yield { type: "text", text: delta.content };
12425
12439
  }
12440
+ const finishReason = chunk.choices[0]?.finish_reason;
12441
+ if (finishReason) {
12442
+ streamStopReason = this.mapFinishReason(finishReason);
12443
+ }
12426
12444
  }
12427
- yield { type: "done" };
12445
+ yield { type: "done", stopReason: streamStopReason };
12428
12446
  } catch (error) {
12429
12447
  throw this.handleError(error);
12430
12448
  }
@@ -12489,6 +12507,7 @@ var OpenAIProvider = class {
12489
12507
  return input;
12490
12508
  };
12491
12509
  try {
12510
+ let streamStopReason;
12492
12511
  for await (const chunk of stream) {
12493
12512
  const delta = chunk.choices[0]?.delta;
12494
12513
  if (delta?.content || delta?.tool_calls) {
@@ -12535,6 +12554,9 @@ var OpenAIProvider = class {
12535
12554
  }
12536
12555
  }
12537
12556
  const finishReason = chunk.choices[0]?.finish_reason;
12557
+ if (finishReason) {
12558
+ streamStopReason = this.mapFinishReason(finishReason);
12559
+ }
12538
12560
  if (finishReason && toolCallBuilders.size > 0) {
12539
12561
  for (const [, builder] of toolCallBuilders) {
12540
12562
  yield {
@@ -12559,7 +12581,7 @@ var OpenAIProvider = class {
12559
12581
  }
12560
12582
  };
12561
12583
  }
12562
- yield { type: "done" };
12584
+ yield { type: "done", stopReason: streamStopReason };
12563
12585
  } finally {
12564
12586
  clearInterval(timeoutInterval);
12565
12587
  }
@@ -13322,7 +13344,7 @@ var CodexProvider = class {
13322
13344
  }
13323
13345
  }
13324
13346
  }
13325
- yield { type: "done" };
13347
+ yield { type: "done", stopReason: response.stopReason };
13326
13348
  }
13327
13349
  /**
13328
13350
  * Stream a chat response with tool use
@@ -13475,13 +13497,18 @@ var GeminiProvider = class {
13475
13497
  const { history, lastMessage } = this.convertMessages(messages);
13476
13498
  const chat = model.startChat({ history });
13477
13499
  const result = await chat.sendMessageStream(lastMessage);
13500
+ let streamStopReason;
13478
13501
  for await (const chunk of result.stream) {
13479
13502
  const text = chunk.text();
13480
13503
  if (text) {
13481
13504
  yield { type: "text", text };
13482
13505
  }
13506
+ const finishReason = chunk.candidates?.[0]?.finishReason;
13507
+ if (finishReason) {
13508
+ streamStopReason = this.mapFinishReason(finishReason);
13509
+ }
13483
13510
  }
13484
- yield { type: "done" };
13511
+ yield { type: "done", stopReason: streamStopReason };
13485
13512
  } catch (error) {
13486
13513
  throw this.handleError(error);
13487
13514
  }
@@ -13516,11 +13543,16 @@ var GeminiProvider = class {
13516
13543
  const chat = model.startChat({ history });
13517
13544
  const result = await chat.sendMessageStream(lastMessage);
13518
13545
  const emittedToolCalls = /* @__PURE__ */ new Set();
13546
+ let streamStopReason;
13519
13547
  for await (const chunk of result.stream) {
13520
13548
  const text = chunk.text();
13521
13549
  if (text) {
13522
13550
  yield { type: "text", text };
13523
13551
  }
13552
+ const finishReason = chunk.candidates?.[0]?.finishReason;
13553
+ if (finishReason) {
13554
+ streamStopReason = this.mapFinishReason(finishReason);
13555
+ }
13524
13556
  const candidate = chunk.candidates?.[0];
13525
13557
  if (candidate?.content?.parts) {
13526
13558
  for (const part of candidate.content.parts) {
@@ -13554,7 +13586,7 @@ var GeminiProvider = class {
13554
13586
  }
13555
13587
  }
13556
13588
  }
13557
- yield { type: "done" };
13589
+ yield { type: "done", stopReason: streamStopReason };
13558
13590
  } catch (error) {
13559
13591
  throw this.handleError(error);
13560
13592
  }
@@ -18884,6 +18916,10 @@ function highlightLine(line, lang) {
18884
18916
  }
18885
18917
 
18886
18918
  // src/cli/repl/output/diff-renderer.ts
18919
+ var bgDeleteLine = chalk3.bgRgb(80, 20, 20);
18920
+ var bgAddLine = chalk3.bgRgb(20, 60, 20);
18921
+ var bgDeleteWord = chalk3.bgRgb(160, 40, 40);
18922
+ var bgAddWord = chalk3.bgRgb(40, 120, 40);
18887
18923
  function parseDiff(raw) {
18888
18924
  const files = [];
18889
18925
  const lines = raw.split("\n");
@@ -19008,6 +19044,46 @@ function parseHunk(lines, start) {
19008
19044
  nextIndex: i
19009
19045
  };
19010
19046
  }
19047
+ function pairAdjacentLines(lines) {
19048
+ const pairs = [];
19049
+ let i = 0;
19050
+ while (i < lines.length) {
19051
+ const deleteStart = i;
19052
+ while (i < lines.length && lines[i].type === "delete") i++;
19053
+ const deleteEnd = i;
19054
+ const addStart = i;
19055
+ while (i < lines.length && lines[i].type === "add") i++;
19056
+ const addEnd = i;
19057
+ const deleteCount = deleteEnd - deleteStart;
19058
+ const addCount = addEnd - addStart;
19059
+ if (deleteCount > 0 && addCount > 0) {
19060
+ const pairCount = Math.min(deleteCount, addCount);
19061
+ for (let j = 0; j < pairCount; j++) {
19062
+ pairs.push({ deleteIdx: deleteStart + j, addIdx: addStart + j });
19063
+ }
19064
+ }
19065
+ if (i === deleteEnd && i === addEnd) {
19066
+ i++;
19067
+ }
19068
+ }
19069
+ return pairs;
19070
+ }
19071
+ function highlightWordChanges(deletedContent, addedContent) {
19072
+ const changes = diffWords(deletedContent, addedContent);
19073
+ let styledDelete = "";
19074
+ let styledAdd = "";
19075
+ for (const change of changes) {
19076
+ if (change.added) {
19077
+ styledAdd += bgAddWord(change.value);
19078
+ } else if (change.removed) {
19079
+ styledDelete += bgDeleteWord(change.value);
19080
+ } else {
19081
+ styledDelete += bgDeleteLine(change.value);
19082
+ styledAdd += bgAddLine(change.value);
19083
+ }
19084
+ }
19085
+ return { styledDelete, styledAdd };
19086
+ }
19011
19087
  var getTerminalWidth = () => process.stdout.columns || 80;
19012
19088
  function stripAnsi(str) {
19013
19089
  return str.replace(/\x1b\[[0-9;]*m/g, "");
@@ -19076,32 +19152,64 @@ function renderFileBlock(file, opts) {
19076
19152
  ) + hunkLabel
19077
19153
  );
19078
19154
  }
19079
- for (const line of hunk.lines) {
19155
+ const pairs = pairAdjacentLines(hunk.lines);
19156
+ const pairedDeleteIndices = new Set(pairs.map((p4) => p4.deleteIdx));
19157
+ const pairedAddIndices = new Set(pairs.map((p4) => p4.addIdx));
19158
+ const pairByAdd = new Map(pairs.map((p4) => [p4.addIdx, p4.deleteIdx]));
19159
+ const wordHighlights = /* @__PURE__ */ new Map();
19160
+ for (const pair of pairs) {
19161
+ const delLine = hunk.lines[pair.deleteIdx];
19162
+ const addLine = hunk.lines[pair.addIdx];
19163
+ wordHighlights.set(pair.deleteIdx, highlightWordChanges(delLine.content, addLine.content));
19164
+ }
19165
+ for (let li = 0; li < hunk.lines.length; li++) {
19166
+ const line = hunk.lines[li];
19080
19167
  const lineNo = formatLineNo(line, showLineNumbers);
19081
19168
  const prefix = line.type === "add" ? "+" : line.type === "delete" ? "-" : " ";
19082
- let content = line.content;
19083
- if (line.type !== "delete" && lang) {
19084
- content = highlightLine(content, lang);
19085
- }
19086
- const lineStr = `${lineNo}${prefix} ${content}`;
19087
- const plainLen = stripAnsi(lineStr).length;
19088
- const pad = Math.max(0, contentWidth - plainLen);
19089
19169
  if (line.type === "add") {
19170
+ const isPaired = pairedAddIndices.has(li);
19171
+ let content;
19172
+ if (isPaired) {
19173
+ const delIdx = pairByAdd.get(li);
19174
+ content = wordHighlights.get(delIdx).styledAdd;
19175
+ } else {
19176
+ content = line.content;
19177
+ }
19178
+ const innerText = `${lineNo}${prefix} ${content}`;
19179
+ const plainLen = stripAnsi(innerText).length + 1;
19180
+ const pad = Math.max(0, contentWidth - plainLen);
19090
19181
  console.log(
19091
- chalk3.magenta("\u2502") + chalk3.green(` ${lineStr}`) + " ".repeat(pad) + " " + chalk3.magenta("\u2502")
19182
+ chalk3.magenta("\u2502") + bgAddLine(` ${innerText}` + " ".repeat(pad + 2)) + chalk3.magenta("\u2502")
19092
19183
  );
19093
19184
  } else if (line.type === "delete") {
19185
+ const isPaired = pairedDeleteIndices.has(li);
19186
+ let content;
19187
+ if (isPaired) {
19188
+ content = wordHighlights.get(li).styledDelete;
19189
+ } else {
19190
+ content = line.content;
19191
+ }
19192
+ const innerText = `${lineNo}${prefix} ${content}`;
19193
+ const plainLen = stripAnsi(innerText).length + 1;
19194
+ const pad = Math.max(0, contentWidth - plainLen);
19094
19195
  console.log(
19095
- chalk3.magenta("\u2502") + chalk3.red(` ${lineStr}`) + " ".repeat(pad) + " " + chalk3.magenta("\u2502")
19196
+ chalk3.magenta("\u2502") + bgDeleteLine(` ${innerText}` + " ".repeat(pad + 2)) + chalk3.magenta("\u2502")
19096
19197
  );
19097
19198
  } else {
19199
+ let content = line.content;
19200
+ if (lang) {
19201
+ content = highlightLine(content, lang);
19202
+ }
19203
+ const lineStr = `${lineNo}${prefix} ${content}`;
19204
+ const plainLen = stripAnsi(lineStr).length;
19205
+ const pad = Math.max(0, contentWidth - plainLen);
19098
19206
  console.log(
19099
19207
  chalk3.magenta("\u2502") + chalk3.dim(` ${lineStr}`) + " ".repeat(pad) + " " + chalk3.magenta("\u2502")
19100
19208
  );
19101
19209
  }
19102
19210
  }
19103
19211
  }
19104
- console.log(chalk3.magenta("\u2570" + "\u2500".repeat(maxWidth - 2) + "\u256F"));
19212
+ console.log(chalk3.magenta("\u2570" + "\u2500".repeat(Math.max(0, maxWidth - 2)) + "\u256F"));
19105
19213
  }
19106
19214
  function formatLineNo(line, show) {
19107
19215
  if (!show) return "";