@corbat-tech/coco 2.20.0 → 2.21.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 CHANGED
@@ -4487,7 +4487,7 @@ var init_codex = __esm({
4487
4487
  model,
4488
4488
  input,
4489
4489
  instructions: instructions ?? "You are a helpful coding assistant.",
4490
- max_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
4490
+ max_output_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
4491
4491
  temperature: options?.temperature ?? this.config.temperature ?? 0,
4492
4492
  store: false,
4493
4493
  stream: true
@@ -10461,8 +10461,9 @@ function isNonRetryableProviderError(error) {
10461
10461
  if (error instanceof ProviderError) {
10462
10462
  const code = error.statusCode;
10463
10463
  if (code === 401 || code === 403) return true;
10464
+ if (code === 400) return true;
10464
10465
  const msg = error.message.toLowerCase();
10465
- if (msg.includes("exceeded your current quota") || msg.includes("usage limit") || msg.includes("insufficient_quota") || msg.includes("billing") || msg.includes("payment") || msg.includes("invalid_api_key") || msg.includes("incorrect api key") || msg.includes("bad credentials") || msg.includes("not authorized")) {
10466
+ if (msg.includes("exceeded your current quota") || msg.includes("usage limit") || msg.includes("insufficient_quota") || msg.includes("billing") || msg.includes("payment") || msg.includes("invalid_api_key") || msg.includes("incorrect api key") || msg.includes("bad credentials") || msg.includes("not authorized") || msg.includes("unsupported parameter")) {
10466
10467
  return true;
10467
10468
  }
10468
10469
  }
@@ -10530,15 +10531,19 @@ function installProcessSafetyNet() {
10530
10531
  _safetyNetInstalled = true;
10531
10532
  const debug = Boolean(process.env["COCO_DEBUG"]);
10532
10533
  process.on("uncaughtException", (error) => {
10534
+ if (isAbortError(error)) return;
10533
10535
  const msg = error instanceof Error ? error.message : String(error);
10534
10536
  console.error(chalk2.red(`
10535
- \xD7 Unexpected error (uncaught): ${msg}`));
10537
+ \xD7 Unexpected error: ${msg}`));
10538
+ console.error(chalk2.dim(" The REPL continues \u2014 type your next message or /help"));
10536
10539
  if (debug) console.error(error.stack);
10537
10540
  });
10538
10541
  process.on("unhandledRejection", (reason) => {
10542
+ if (isAbortError(reason)) return;
10539
10543
  const msg = reason instanceof Error ? reason.message : String(reason);
10540
10544
  console.error(chalk2.red(`
10541
- \xD7 Unhandled rejection: ${msg}`));
10545
+ \xD7 Unhandled error: ${msg}`));
10546
+ console.error(chalk2.dim(" The REPL continues \u2014 type your next message or /help"));
10542
10547
  if (debug && reason instanceof Error) console.error(reason.stack);
10543
10548
  });
10544
10549
  }
@@ -11173,12 +11178,12 @@ function renderFileBlock(file, opts) {
11173
11178
  }
11174
11179
  }
11175
11180
  }
11176
- console.log();
11177
11181
  }
11178
11182
  function formatLineNo(line, show) {
11179
11183
  if (!show) return "";
11180
- const lineNo = line.type === "delete" ? line.oldLineNo : line.newLineNo;
11181
- return chalk2.dim(`${String(lineNo ?? "").padStart(5)} `);
11184
+ const oldStr = line.oldLineNo !== void 0 ? String(line.oldLineNo) : "";
11185
+ const newStr = line.newLineNo !== void 0 ? String(line.newLineNo) : "";
11186
+ return chalk2.dim(`${oldStr.padStart(4)} | ${newStr.padStart(4)} `);
11182
11187
  }
11183
11188
  function getChangedLines(diff) {
11184
11189
  const result = /* @__PURE__ */ new Map();
@@ -36816,7 +36821,22 @@ async function savePermissionPreference(key, value) {
36816
36821
  }
36817
36822
  async function shouldShowPermissionSuggestion() {
36818
36823
  const prefs = await loadPermissionPreferences();
36819
- return !prefs.recommendedAllowlistApplied && !prefs.recommendedAllowlistDismissed;
36824
+ if (prefs.recommendedAllowlistDismissed) {
36825
+ return false;
36826
+ }
36827
+ if (!prefs.recommendedAllowlistApplied) {
36828
+ return true;
36829
+ }
36830
+ try {
36831
+ const content = await fs34__default.readFile(CONFIG_PATHS.trustedTools, "utf-8");
36832
+ const settings = JSON.parse(content);
36833
+ if (!settings.globalTrusted || settings.globalTrusted.length === 0) {
36834
+ return true;
36835
+ }
36836
+ } catch {
36837
+ return true;
36838
+ }
36839
+ return false;
36820
36840
  }
36821
36841
  async function applyRecommendedPermissions() {
36822
36842
  for (const tool of [...RECOMMENDED_GLOBAL, ...RECOMMENDED_PROJECT]) {
@@ -47606,11 +47626,7 @@ ${icon} ${label}`);
47606
47626
  if (toolName === "edit_file") {
47607
47627
  console.log(`
47608
47628
  ${icon} ${chalk2.yellow.bold("EDIT")} ${chalk2.cyan(String(input.path || ""))}`);
47609
- const editPreview = renderEditPreview(
47610
- String(input.old_string || ""),
47611
- String(input.new_string || "")
47612
- );
47613
- if (editPreview) console.log(editPreview);
47629
+ printEditDiff(String(input.oldText || ""), String(input.newText || ""));
47614
47630
  return;
47615
47631
  }
47616
47632
  console.log(`
@@ -47682,20 +47698,21 @@ function wordLevelHighlight(deletedContent, addedContent) {
47682
47698
  }
47683
47699
  return { styledDelete, styledAdd };
47684
47700
  }
47685
- function renderEditPreview(oldStr, newStr) {
47686
- const termWidth = Math.max(getTerminalWidth2() - 14, 30);
47701
+ function printEditDiff(oldStr, newStr) {
47702
+ const termWidth = Math.max(getTerminalWidth2() - 15, 30);
47687
47703
  const MAX_SHOWN = 30;
47688
47704
  if (!oldStr.trim()) {
47689
47705
  const lines = newStr.split("\n").filter((l) => l.trim().length > 0).slice(0, 6);
47690
- if (lines.length === 0) return "";
47706
+ if (lines.length === 0) return;
47691
47707
  const truncate4 = (s) => s.length > termWidth - 2 ? s.slice(0, termWidth - 3) + "\u2026" : s;
47692
- return lines.map((l) => {
47708
+ for (const l of lines) {
47693
47709
  const text13 = `+ ${truncate4(l)}`;
47694
47710
  const pad = Math.max(0, termWidth - stripAnsi(text13).length + 2);
47695
- return " " + diffBgAdd(text13 + " ".repeat(pad));
47696
- }).join("\n");
47711
+ console.log(" " + diffBgAdd(text13 + " ".repeat(pad)));
47712
+ }
47713
+ return;
47697
47714
  }
47698
- if (!newStr.trim() && !oldStr.trim()) return "";
47715
+ if (!newStr.trim() && !oldStr.trim()) return;
47699
47716
  const changes = diffLines(oldStr, newStr);
47700
47717
  const diffLineList = [];
47701
47718
  let oldNo = 1;
@@ -47717,7 +47734,7 @@ function renderEditPreview(oldStr, newStr) {
47717
47734
  }
47718
47735
  }
47719
47736
  }
47720
- if (diffLineList.length === 0) return "";
47737
+ if (diffLineList.length === 0) return;
47721
47738
  const pairs = pairAdjacentDiffLines(diffLineList);
47722
47739
  const pairedDeletes = new Set(pairs.map((p45) => p45.deleteIdx));
47723
47740
  const pairedAdds = new Set(pairs.map((p45) => p45.addIdx));
@@ -47739,7 +47756,6 @@ function renderEditPreview(oldStr, newStr) {
47739
47756
  if (n >= 0 && n < diffLineList.length) visibleIndices.add(n);
47740
47757
  }
47741
47758
  }
47742
- const result = [];
47743
47759
  let shown = 0;
47744
47760
  let prevIdx = -1;
47745
47761
  const truncate3 = (s) => s.length > termWidth ? s.slice(0, termWidth - 1) + "\u2026" : s;
@@ -47751,11 +47767,11 @@ function renderEditPreview(oldStr, newStr) {
47751
47767
  for (let i = 0; i < diffLineList.length; i++) {
47752
47768
  if (!visibleIndices.has(i)) continue;
47753
47769
  if (shown >= MAX_SHOWN) {
47754
- result.push(chalk2.dim(` \u2026 +${diffLineList.length - i} more lines`));
47770
+ console.log(chalk2.dim(` \u2026 +${diffLineList.length - i} more lines`));
47755
47771
  break;
47756
47772
  }
47757
47773
  if (prevIdx >= 0 && i > prevIdx + 1) {
47758
- result.push(chalk2.dim(" \u22EE"));
47774
+ console.log(chalk2.dim(" \u22EE"));
47759
47775
  }
47760
47776
  prevIdx = i;
47761
47777
  shown++;
@@ -47765,20 +47781,19 @@ function renderEditPreview(oldStr, newStr) {
47765
47781
  const visLen = stripAnsi(rawContent).length;
47766
47782
  const pad = Math.max(0, termWidth - visLen);
47767
47783
  const line = diffBgAdd(` + ${rawContent}${" ".repeat(pad)}`);
47768
- result.push(" " + gutter(void 0, dl.newLineNo) + line);
47784
+ console.log(" " + gutter(void 0, dl.newLineNo) + line);
47769
47785
  } else if (dl.type === "delete") {
47770
47786
  const rawContent = pairedDeletes.has(i) ? wordHighlights.get(i)?.styledDelete ?? truncate3(dl.content) : truncate3(dl.content);
47771
47787
  const visLen = stripAnsi(rawContent).length;
47772
47788
  const pad = Math.max(0, termWidth - visLen);
47773
47789
  const line = diffBgDel(` - ${rawContent}${" ".repeat(pad)}`);
47774
- result.push(" " + gutter(dl.oldLineNo, void 0) + line);
47790
+ console.log(" " + gutter(dl.oldLineNo, void 0) + line);
47775
47791
  } else {
47776
- result.push(
47792
+ console.log(
47777
47793
  " " + gutter(dl.oldLineNo, dl.newLineNo) + chalk2.dim(` ${truncate3(dl.content)}`)
47778
47794
  );
47779
47795
  }
47780
47796
  }
47781
- return result.join("\n");
47782
47797
  }
47783
47798
  function renderToolEnd(result) {
47784
47799
  const status = result.result.success ? chalk2.green("\u2713") : chalk2.red("\u2717");
@@ -49800,7 +49815,21 @@ var ParallelToolExecutor = class {
49800
49815
  if (isAbortError(error, signal)) {
49801
49816
  return null;
49802
49817
  }
49803
- throw error;
49818
+ const errMsg = error instanceof Error ? error.message : String(error);
49819
+ const duration2 = performance.now() - startTime;
49820
+ const executedCall2 = {
49821
+ id: toolCall.id,
49822
+ name: toolCall.name,
49823
+ input: toolCall.input,
49824
+ result: {
49825
+ success: false,
49826
+ output: `Unexpected error in ${toolCall.name}: ${errMsg}`,
49827
+ error: errMsg
49828
+ },
49829
+ duration: duration2
49830
+ };
49831
+ onToolEnd?.(executedCall2);
49832
+ return executedCall2;
49804
49833
  }
49805
49834
  if (!result.success && result.error && onPathAccessDenied) {
49806
49835
  const dirPath = extractDeniedPath(result.error);
@@ -49811,7 +49840,13 @@ var ParallelToolExecutor = class {
49811
49840
  } catch {
49812
49841
  }
49813
49842
  if (authorized) {
49814
- result = await registry.execute(toolCall.name, toolCall.input, { signal });
49843
+ try {
49844
+ result = await registry.execute(toolCall.name, toolCall.input, { signal });
49845
+ } catch (retryError) {
49846
+ if (isAbortError(retryError, signal)) return null;
49847
+ const msg = retryError instanceof Error ? retryError.message : String(retryError);
49848
+ result = { success: false, error: `Retry failed: ${msg}`, duration: 0 };
49849
+ }
49815
49850
  }
49816
49851
  }
49817
49852
  }
@@ -50046,6 +50081,9 @@ ${tail}`;
50046
50081
  if (isAbortError(streamError, options.signal)) {
50047
50082
  return abortReturn();
50048
50083
  }
50084
+ if (isNonRetryableProviderError(streamError)) {
50085
+ throw streamError;
50086
+ }
50049
50087
  const errorMsg = streamError instanceof Error ? streamError.message : String(streamError);
50050
50088
  addMessage(session, {
50051
50089
  role: "assistant",
@@ -50110,7 +50148,18 @@ ${tail}`;
50110
50148
  const needsConfirmation = !options.skipConfirmation && !session.trustedTools.has(trustPattern) && requiresConfirmation(toolCall.name, toolCall.input);
50111
50149
  if (needsConfirmation) {
50112
50150
  options.onBeforeConfirmation?.();
50113
- const confirmResult = await confirmToolExecution(toolCall);
50151
+ let confirmResult;
50152
+ try {
50153
+ confirmResult = await confirmToolExecution(toolCall);
50154
+ } catch (confirmError) {
50155
+ options.onAfterConfirmation?.();
50156
+ declinedTools.set(
50157
+ toolCall.id,
50158
+ `Confirmation failed: ${confirmError instanceof Error ? confirmError.message : String(confirmError)}`
50159
+ );
50160
+ options.onToolSkipped?.(toolCall, "Confirmation error");
50161
+ continue;
50162
+ }
50114
50163
  options.onAfterConfirmation?.();
50115
50164
  if (typeof confirmResult === "object" && confirmResult.type === "edit") {
50116
50165
  const editedToolCall = {
@@ -51164,6 +51213,11 @@ async function startRepl(options = {}) {
51164
51213
  agentMessage = commandResult.forkPrompt;
51165
51214
  } else if (hasPendingImage()) {
51166
51215
  const images = consumePendingImages();
51216
+ const imagePrompts = images.map((img) => img.prompt).join("\n");
51217
+ const userText = input?.trim() || "";
51218
+ const combinedText = userText ? `${userText}
51219
+
51220
+ ${imagePrompts}`.trim() : imagePrompts;
51167
51221
  agentMessage = [
51168
51222
  ...images.map(
51169
51223
  (img) => ({
@@ -51173,7 +51227,7 @@ async function startRepl(options = {}) {
51173
51227
  ),
51174
51228
  {
51175
51229
  type: "text",
51176
- text: images.map((img) => img.prompt).join("\n")
51230
+ text: combinedText
51177
51231
  }
51178
51232
  ];
51179
51233
  } else {
@@ -51182,6 +51236,11 @@ async function startRepl(options = {}) {
51182
51236
  }
51183
51237
  if (agentMessage === null && hasPendingImage()) {
51184
51238
  const images = consumePendingImages();
51239
+ const imagePrompts = images.map((img) => img.prompt).join("\n");
51240
+ const userText = input?.trim() || "";
51241
+ const combinedText = userText ? `${userText}
51242
+
51243
+ ${imagePrompts}`.trim() : imagePrompts;
51185
51244
  agentMessage = [
51186
51245
  ...images.map(
51187
51246
  (img) => ({
@@ -51191,7 +51250,7 @@ async function startRepl(options = {}) {
51191
51250
  ),
51192
51251
  {
51193
51252
  type: "text",
51194
- text: images.map((img) => img.prompt).join("\n")
51253
+ text: combinedText
51195
51254
  }
51196
51255
  ];
51197
51256
  }
@@ -51524,6 +51583,31 @@ async function startRepl(options = {}) {
51524
51583
  console.log();
51525
51584
  continue;
51526
51585
  }
51586
+ if (result.error) {
51587
+ session.messages.length = preCallMessageLength;
51588
+ if (originalUserMessage !== null && consecutiveErrors < MAX_CONSECUTIVE_ERRORS && !isNonRetryableProviderError(new Error(result.error))) {
51589
+ consecutiveErrors++;
51590
+ const humanized = humanizeProviderError(new Error(result.error));
51591
+ renderError(humanized);
51592
+ console.log(
51593
+ chalk2.dim(
51594
+ ` \u21BB Retrying automatically (attempt ${consecutiveErrors}/${MAX_CONSECUTIVE_ERRORS})\u2026`
51595
+ )
51596
+ );
51597
+ const recoveryPrefix = `[System: The previous attempt failed with: "${humanized}". Please try a different approach, tool, or method to complete the task. Do NOT repeat the exact same action that caused the error.]
51598
+
51599
+ `;
51600
+ pendingQueuedMessages = [recoveryPrefix + originalUserMessage];
51601
+ } else {
51602
+ renderError(result.error);
51603
+ console.log(
51604
+ chalk2.dim(" Recovery failed or error is non-retryable. Returning to prompt.")
51605
+ );
51606
+ consecutiveErrors = 0;
51607
+ }
51608
+ console.log();
51609
+ continue;
51610
+ }
51527
51611
  console.log();
51528
51612
  if (pendingExplanations.length > 0) {
51529
51613
  const settled = await Promise.race([
@@ -51700,6 +51784,8 @@ async function startRepl(options = {}) {
51700
51784
  console.log(chalk2.dim(" Recovery failed after multiple attempts. Returning to prompt."));
51701
51785
  continue;
51702
51786
  }
51787
+ session.messages.length = preCallMessageLength;
51788
+ consecutiveErrors = 0;
51703
51789
  renderError(errorMsg);
51704
51790
  } finally {
51705
51791
  clearSpinner();
@@ -52122,6 +52208,7 @@ registerSwarmCommand(program);
52122
52208
  program.command("setup").description("Configure AI provider and API key").action(async () => {
52123
52209
  const result = await runOnboardingV2();
52124
52210
  if (result) {
52211
+ await saveConfiguration(result);
52125
52212
  console.log("\n\u2705 Configuration saved! Run `coco` to start coding.");
52126
52213
  } else {
52127
52214
  console.log("\n\u274C Setup cancelled.");
@@ -52135,6 +52222,7 @@ program.command("chat", { isDefault: true }).description("Start interactive chat
52135
52222
  console.log("\n\u274C Setup cancelled.");
52136
52223
  return;
52137
52224
  }
52225
+ await saveConfiguration(result);
52138
52226
  }
52139
52227
  const providerType = options.provider ?? await getLastUsedProvider();
52140
52228
  if (options.print !== void 0) {