@corbat-tech/coco 2.22.2 → 2.23.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
@@ -9617,7 +9617,15 @@ Responses are short and direct by default. Lead with the answer or action, not r
9617
9617
 
9618
9618
  ## File Changes
9619
9619
 
9620
- **Never output raw diff or unified diff format in your responses.** Use edit_file and write_file to make changes \u2014 Coco renders a visual diff automatically. Do not quote file contents back to the user after reading them unless explicitly asked.`;
9620
+ **Never output raw diff or unified diff format in your responses.** Use edit_file and write_file to make changes \u2014 Coco renders a visual diff automatically.
9621
+
9622
+ ## Output Discipline
9623
+
9624
+ **NEVER echo file contents in your responses.** When you read a file, extract only the specific information needed \u2014 do NOT reprint whole files, functions, or large excerpts. The terminal shows which files you read and their line counts \u2014 the user does not need to see the content again.
9625
+
9626
+ **During tool-calling iterations, keep text minimal.** A single short orienting line before tool calls is acceptable. Do NOT explain every step, narrate what you are about to do, or produce paragraphs between tool calls. Reserve explanatory text for your final response after all tools have completed.
9627
+
9628
+ **Code blocks in responses are expensive.** Only include a code block when the user explicitly asks to see code, or when the code IS the deliverable (e.g., a script to paste in a terminal). Never include a code block to "show your work" when you can write the file directly instead.`;
9621
9629
  SHELL_METACHARACTERS = /[;|&`$(){}<>!\n\\'"]/;
9622
9630
  SAFE_COMMAND_VALIDATORS = {
9623
9631
  git: (args) => {
@@ -47052,6 +47060,8 @@ var streamingIndicatorInterval = null;
47052
47060
  var streamingIndicatorFrame = 0;
47053
47061
  var STREAMING_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
47054
47062
  var getTerminalWidth2 = () => process.stdout.columns || 80;
47063
+ var MAX_STREAMING_CODE_LINES = 50;
47064
+ var STREAMING_CODE_HEAD_LINES = 40;
47055
47065
  function startStreamingIndicator() {
47056
47066
  if (streamingIndicatorActive) return;
47057
47067
  streamingIndicatorActive = true;
@@ -47375,11 +47385,17 @@ function renderSimpleCodeBlock(lang, lines, blockId) {
47375
47385
  const isDiff = lang === "diff" || !lang && looksLikeDiff(lines);
47376
47386
  const title = lang || "Code";
47377
47387
  console.log(chalk2.dim(`${title} \xB7 #${blockId}`));
47388
+ let displayLines = lines;
47389
+ let truncatedCount = 0;
47390
+ if (!isDiff && lines.length > MAX_STREAMING_CODE_LINES) {
47391
+ displayLines = lines.slice(0, STREAMING_CODE_HEAD_LINES);
47392
+ truncatedCount = lines.length - STREAMING_CODE_HEAD_LINES;
47393
+ }
47378
47394
  const bgDel = chalk2.bgRgb(80, 20, 20);
47379
47395
  const bgAdd = chalk2.bgRgb(20, 60, 20);
47380
47396
  let oldLineNo = 0;
47381
47397
  let newLineNo = 0;
47382
- for (const line of lines) {
47398
+ for (const line of displayLines) {
47383
47399
  if (isDiff) {
47384
47400
  const hunkMatch = line.match(/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
47385
47401
  if (hunkMatch) {
@@ -47407,6 +47423,9 @@ function renderSimpleCodeBlock(lang, lines, blockId) {
47407
47423
  }
47408
47424
  }
47409
47425
  }
47426
+ if (truncatedCount > 0) {
47427
+ console.log(chalk2.dim(` \u2026 ${truncatedCount} more lines \xB7 /copy ${blockId} for full content`));
47428
+ }
47410
47429
  console.log(chalk2.dim(` #${blockId} \xB7 /copy ${blockId}`));
47411
47430
  }
47412
47431
  function formatDiffLineNo(line, oldLineNo, newLineNo) {
@@ -50058,6 +50077,8 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
50058
50077
  const maxIterations = session.config.agent.maxToolIterations;
50059
50078
  const toolErrorCounts = /* @__PURE__ */ new Map();
50060
50079
  const MAX_CONSECUTIVE_TOOL_ERRORS = 3;
50080
+ const ITERATION_LIMIT_WARNING_RATIO = 0.75;
50081
+ const ITERATION_LIMIT_SUMMARY_PROMPT = `[System: You have now used all allowed iterations and tool calls are no longer available. Write your final response immediately: (1) briefly state what was completed, (2) describe what still needs to be done, (3) give specific next steps so the user can continue. Be concise and direct.]`;
50061
50082
  const INLINE_RESULT_MAX_CHARS = 8e3;
50062
50083
  const INLINE_RESULT_HEAD_CHARS = 6500;
50063
50084
  const INLINE_RESULT_TAIL_CHARS = 1e3;
@@ -50072,6 +50093,8 @@ ${tail}`;
50072
50093
  }
50073
50094
  while (iteration < maxIterations) {
50074
50095
  iteration++;
50096
+ const isLastIteration = iteration === maxIterations;
50097
+ const iterationTextChunks = [];
50075
50098
  if (options.signal?.aborted) {
50076
50099
  return abortReturn();
50077
50100
  }
@@ -50099,7 +50122,7 @@ ${tail}`;
50099
50122
  }
50100
50123
  responseContent += chunk.text;
50101
50124
  finalContent += chunk.text;
50102
- options.onStream?.(chunk);
50125
+ iterationTextChunks.push(chunk);
50103
50126
  }
50104
50127
  if (chunk.type === "tool_use_start" && chunk.toolCall) {
50105
50128
  flushLineBuffer();
@@ -50176,6 +50199,11 @@ ${tail}`;
50176
50199
  error: errorMsg
50177
50200
  };
50178
50201
  }
50202
+ if (collectedToolCalls.length === 0) {
50203
+ for (const bufferedChunk of iterationTextChunks) {
50204
+ options.onStream?.(bufferedChunk);
50205
+ }
50206
+ }
50179
50207
  const inputText = messages.map((m) => {
50180
50208
  if (typeof m.content === "string") return m.content;
50181
50209
  try {
@@ -50351,10 +50379,20 @@ ${tail}`;
50351
50379
  });
50352
50380
  const declineReason = declinedTools.get(toolCall.id);
50353
50381
  if (declineReason) {
50382
+ let skipContent;
50383
+ if (declineReason === "User declined") {
50384
+ skipContent = "The user explicitly declined this tool call. You MUST find a different approach \u2014 do not retry the same action or parameters.";
50385
+ } else if (declineReason.toLowerCase().includes("timeout") || declineReason.toLowerCase().includes("timed out")) {
50386
+ skipContent = `Tool execution timed out: ${declineReason}. Try a simpler or faster alternative, or break the task into smaller steps.`;
50387
+ } else if (declineReason.toLowerCase().includes("abort")) {
50388
+ skipContent = "Tool execution was cancelled.";
50389
+ } else {
50390
+ skipContent = `Tool execution was skipped: ${declineReason}`;
50391
+ }
50354
50392
  toolResults.push({
50355
50393
  type: "tool_result",
50356
50394
  tool_use_id: toolCall.id,
50357
- content: `Tool execution was declined: ${declineReason}`,
50395
+ content: skipContent,
50358
50396
  is_error: true
50359
50397
  });
50360
50398
  continue;
@@ -50408,6 +50446,28 @@ ${tail}`;
50408
50446
  }
50409
50447
  }
50410
50448
  }
50449
+ if (toolResults.length > 0) {
50450
+ const warningThreshold = Math.ceil(maxIterations * ITERATION_LIMIT_WARNING_RATIO);
50451
+ const lastIdx = toolResults.length - 1;
50452
+ const last = toolResults[lastIdx];
50453
+ if (isLastIteration && !stuckInErrorLoop) {
50454
+ toolResults[lastIdx] = {
50455
+ ...last,
50456
+ content: typeof last.content === "string" ? last.content + `
50457
+
50458
+ ${ITERATION_LIMIT_SUMMARY_PROMPT}` : ITERATION_LIMIT_SUMMARY_PROMPT
50459
+ };
50460
+ } else if (iteration === warningThreshold) {
50461
+ const remaining = maxIterations - iteration;
50462
+ const warning = `
50463
+
50464
+ [System: Iteration budget warning \u2014 ${iteration} of ${maxIterations} iterations used, ${remaining} remaining. Begin wrapping up your task. Prioritize the most critical remaining steps.]`;
50465
+ toolResults[lastIdx] = {
50466
+ ...last,
50467
+ content: typeof last.content === "string" ? last.content + warning : warning
50468
+ };
50469
+ }
50470
+ }
50411
50471
  const assistantContent = response.content ? [{ type: "text", text: response.content }, ...toolUses] : toolUses;
50412
50472
  addMessage(session, {
50413
50473
  role: "assistant",
@@ -50452,14 +50512,38 @@ ${tail}`;
50452
50512
  }
50453
50513
  break;
50454
50514
  }
50455
- }
50456
- if (iteration >= maxIterations) {
50457
- const notice = `
50515
+ if (isLastIteration && toolResults.length > 0) {
50516
+ let summaryThinkingEnded = false;
50517
+ options.onThinkingStart?.();
50518
+ try {
50519
+ const finalMessages = getConversationContext(session, toolRegistry);
50520
+ for await (const chunk of provider.streamWithTools(finalMessages, {
50521
+ tools: [],
50522
+ maxTokens: session.config.provider.maxTokens,
50523
+ signal: options.signal
50524
+ })) {
50525
+ if (options.signal?.aborted) break;
50526
+ if (chunk.type === "text" && chunk.text) {
50527
+ if (!summaryThinkingEnded) {
50528
+ options.onThinkingEnd?.();
50529
+ summaryThinkingEnded = true;
50530
+ }
50531
+ finalContent += chunk.text;
50532
+ options.onStream?.(chunk);
50533
+ }
50534
+ if (chunk.type === "done") break;
50535
+ }
50536
+ } catch {
50537
+ const notice = `
50458
50538
 
50459
- ---
50460
- _Reached the iteration limit (${maxIterations}). The task may be incomplete. You can say "continue" to resume._`;
50461
- finalContent += notice;
50462
- options.onStream?.({ type: "text", text: notice });
50539
+ I have reached the maximum iteration limit (${maxIterations}). The task may be incomplete. Type "continue" to give me more iterations, or describe what you'd like me to focus on next.`;
50540
+ finalContent += notice;
50541
+ options.onStream?.({ type: "text", text: notice });
50542
+ } finally {
50543
+ if (!summaryThinkingEnded) options.onThinkingEnd?.();
50544
+ }
50545
+ break;
50546
+ }
50463
50547
  }
50464
50548
  options.onStream?.({ type: "done" });
50465
50549
  return {