@corbat-tech/coco 2.15.0 → 2.16.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 +180 -24
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +50 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -4705,12 +4705,12 @@ var init_copilot2 = __esm({
|
|
|
4705
4705
|
init_openai();
|
|
4706
4706
|
init_copilot();
|
|
4707
4707
|
CONTEXT_WINDOWS4 = {
|
|
4708
|
-
// Claude models
|
|
4709
|
-
"claude-sonnet-4.6":
|
|
4710
|
-
"claude-opus-4.6":
|
|
4711
|
-
"claude-sonnet-4.5":
|
|
4712
|
-
"claude-opus-4.5":
|
|
4713
|
-
"claude-haiku-4.5":
|
|
4708
|
+
// Claude models — Copilot API caps these at 168 000 (not 200 000 like Anthropic direct)
|
|
4709
|
+
"claude-sonnet-4.6": 168e3,
|
|
4710
|
+
"claude-opus-4.6": 168e3,
|
|
4711
|
+
"claude-sonnet-4.5": 168e3,
|
|
4712
|
+
"claude-opus-4.5": 168e3,
|
|
4713
|
+
"claude-haiku-4.5": 168e3,
|
|
4714
4714
|
// OpenAI models — chat/completions
|
|
4715
4715
|
"gpt-4.1": 1048576,
|
|
4716
4716
|
// OpenAI models — /responses API (Codex/GPT-5+)
|
|
@@ -7716,7 +7716,11 @@ var init_manager = __esm({
|
|
|
7716
7716
|
"src/cli/repl/context/manager.ts"() {
|
|
7717
7717
|
DEFAULT_CONTEXT_CONFIG = {
|
|
7718
7718
|
maxTokens: 2e5,
|
|
7719
|
-
|
|
7719
|
+
// 75% leaves a comfortable headroom for the compaction summary call itself
|
|
7720
|
+
// and the active tool-call accumulation within the current turn.
|
|
7721
|
+
// Industry reference: OpenCode uses 75%, Claude Code effective ~83.5% but
|
|
7722
|
+
// with an additional 33K output reserve. 75% is the recommended safe value.
|
|
7723
|
+
compactionThreshold: 0.75,
|
|
7720
7724
|
reservedTokens: 4096
|
|
7721
7725
|
};
|
|
7722
7726
|
ContextManager = class {
|
|
@@ -7832,35 +7836,59 @@ var init_manager = __esm({
|
|
|
7832
7836
|
|
|
7833
7837
|
// src/cli/repl/context/compactor.ts
|
|
7834
7838
|
function buildCompactionPrompt(focusTopic) {
|
|
7835
|
-
let prompt = `
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7839
|
+
let prompt = `This is a coding agent session that needs to be compacted due to context length.
|
|
7840
|
+
Create a structured summary that preserves everything the agent needs to continue working.
|
|
7841
|
+
|
|
7842
|
+
## Required sections (use these exact headings):
|
|
7843
|
+
|
|
7844
|
+
### Original Request
|
|
7845
|
+
State the user's original task or question verbatim (or paraphrase if very long).
|
|
7846
|
+
|
|
7847
|
+
### Work Completed
|
|
7848
|
+
List every concrete action taken: files created/modified (with paths), commands run,
|
|
7849
|
+
bugs fixed, features implemented. Be specific \u2014 include file paths and function names.
|
|
7850
|
+
|
|
7851
|
+
### Key Decisions
|
|
7852
|
+
Document architectural decisions, approaches chosen, and the reasoning behind them.
|
|
7853
|
+
|
|
7854
|
+
### Current State
|
|
7855
|
+
Describe exactly where the work stands: what is done, what is in progress, what remains.
|
|
7856
|
+
|
|
7857
|
+
### Files Touched
|
|
7858
|
+
List all file paths that were read, modified, or created during this session.
|
|
7859
|
+
|
|
7860
|
+
### Errors & Resolutions
|
|
7861
|
+
Document any errors encountered and how they were resolved (or if still unresolved).
|
|
7862
|
+
|
|
7863
|
+
### Next Steps
|
|
7864
|
+
If the task is incomplete, list the immediate next actions the agent should take.`;
|
|
7841
7865
|
if (focusTopic) {
|
|
7842
7866
|
prompt += `
|
|
7843
7867
|
|
|
7844
|
-
**
|
|
7868
|
+
**PRIORITY**: Preserve ALL details related to "${focusTopic}" \u2014 include specific code snippets, exact file paths, and full context. Be concise about unrelated topics.`;
|
|
7845
7869
|
}
|
|
7846
7870
|
prompt += `
|
|
7847
7871
|
|
|
7848
|
-
Keep the summary under
|
|
7872
|
+
Keep the total summary under 600 words. Use bullet points within each section.
|
|
7849
7873
|
|
|
7850
|
-
|
|
7874
|
+
SESSION HISTORY TO SUMMARIZE:
|
|
7851
7875
|
`;
|
|
7852
7876
|
return prompt;
|
|
7853
7877
|
}
|
|
7854
7878
|
function createContextCompactor(config) {
|
|
7855
7879
|
return new ContextCompactor(config);
|
|
7856
7880
|
}
|
|
7857
|
-
var DEFAULT_COMPACTOR_CONFIG, ContextCompactor;
|
|
7881
|
+
var DEFAULT_COMPACTOR_CONFIG, PRESERVED_RESULT_SOFT_CAP, PRESERVED_RESULT_SOFT_HEAD, PRESERVED_RESULT_SOFT_TAIL, HOT_TAIL_TOOL_PAIRS, ContextCompactor;
|
|
7858
7882
|
var init_compactor = __esm({
|
|
7859
7883
|
"src/cli/repl/context/compactor.ts"() {
|
|
7860
7884
|
DEFAULT_COMPACTOR_CONFIG = {
|
|
7861
|
-
preserveLastN:
|
|
7885
|
+
preserveLastN: 8,
|
|
7862
7886
|
summaryMaxTokens: 1e3
|
|
7863
7887
|
};
|
|
7888
|
+
PRESERVED_RESULT_SOFT_CAP = 16e3;
|
|
7889
|
+
PRESERVED_RESULT_SOFT_HEAD = 13e3;
|
|
7890
|
+
PRESERVED_RESULT_SOFT_TAIL = 1500;
|
|
7891
|
+
HOT_TAIL_TOOL_PAIRS = 4;
|
|
7864
7892
|
ContextCompactor = class {
|
|
7865
7893
|
config;
|
|
7866
7894
|
constructor(config) {
|
|
@@ -7899,7 +7927,9 @@ var init_compactor = __esm({
|
|
|
7899
7927
|
}
|
|
7900
7928
|
}
|
|
7901
7929
|
const messagesToSummarize = conversationMessages.slice(0, preserveStart);
|
|
7902
|
-
const messagesToPreserve =
|
|
7930
|
+
const messagesToPreserve = this.trimPreservedToolResults(
|
|
7931
|
+
conversationMessages.slice(preserveStart)
|
|
7932
|
+
);
|
|
7903
7933
|
if (messagesToSummarize.length === 0) {
|
|
7904
7934
|
return {
|
|
7905
7935
|
messages,
|
|
@@ -7997,6 +8027,60 @@ ${summary}
|
|
|
7997
8027
|
return `[Summary generation failed: ${errorMessage}. Previous conversation had ${conversationText.length} characters.]`;
|
|
7998
8028
|
}
|
|
7999
8029
|
}
|
|
8030
|
+
/**
|
|
8031
|
+
* Hot-tail policy: apply a soft cap to tool results in the preserved window.
|
|
8032
|
+
*
|
|
8033
|
+
* The last HOT_TAIL_TOOL_PAIRS tool-result pairs are kept verbatim (hot tail).
|
|
8034
|
+
* Older pairs in the preserved window that contain results larger than
|
|
8035
|
+
* PRESERVED_RESULT_SOFT_CAP are trimmed to head+tail with a marker.
|
|
8036
|
+
*
|
|
8037
|
+
* This handles legacy results that were stored before the inline cap was in
|
|
8038
|
+
* place, ensuring that a single large stale tree/grep/web result cannot fill
|
|
8039
|
+
* the context even after compaction.
|
|
8040
|
+
*/
|
|
8041
|
+
trimPreservedToolResults(messages) {
|
|
8042
|
+
let hotTailStart = messages.length;
|
|
8043
|
+
let pairsFound = 0;
|
|
8044
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
8045
|
+
const msg = messages[i];
|
|
8046
|
+
if (!msg) continue;
|
|
8047
|
+
const isToolResultMsg = Array.isArray(msg.content) && msg.content.length > 0 && msg.content[0]?.type === "tool_result";
|
|
8048
|
+
if (isToolResultMsg) {
|
|
8049
|
+
pairsFound++;
|
|
8050
|
+
if (pairsFound >= HOT_TAIL_TOOL_PAIRS) {
|
|
8051
|
+
hotTailStart = i > 0 ? i - 1 : i;
|
|
8052
|
+
break;
|
|
8053
|
+
}
|
|
8054
|
+
}
|
|
8055
|
+
}
|
|
8056
|
+
return messages.map((msg, idx) => {
|
|
8057
|
+
if (idx >= hotTailStart) return msg;
|
|
8058
|
+
if (!Array.isArray(msg.content)) return msg;
|
|
8059
|
+
const hasOversized = msg.content.some((block) => {
|
|
8060
|
+
const b = block;
|
|
8061
|
+
return b.type === "tool_result" && typeof b.content === "string" && b.content.length > PRESERVED_RESULT_SOFT_CAP;
|
|
8062
|
+
});
|
|
8063
|
+
if (!hasOversized) return msg;
|
|
8064
|
+
const blocks = msg.content;
|
|
8065
|
+
const trimmedContent = blocks.map((block) => {
|
|
8066
|
+
if (block.type === "tool_result" && block.content.length > PRESERVED_RESULT_SOFT_CAP) {
|
|
8067
|
+
const full = block.content;
|
|
8068
|
+
const head = full.slice(0, PRESERVED_RESULT_SOFT_HEAD);
|
|
8069
|
+
const tail = full.slice(-PRESERVED_RESULT_SOFT_TAIL);
|
|
8070
|
+
const omitted = full.length - PRESERVED_RESULT_SOFT_HEAD - PRESERVED_RESULT_SOFT_TAIL;
|
|
8071
|
+
const trimmedResult = {
|
|
8072
|
+
...block,
|
|
8073
|
+
content: `${head}
|
|
8074
|
+
[... ${omitted.toLocaleString()} chars trimmed (compaction soft-cap) ...]
|
|
8075
|
+
${tail}`
|
|
8076
|
+
};
|
|
8077
|
+
return trimmedResult;
|
|
8078
|
+
}
|
|
8079
|
+
return block;
|
|
8080
|
+
});
|
|
8081
|
+
return { ...msg, content: trimmedContent };
|
|
8082
|
+
});
|
|
8083
|
+
}
|
|
8000
8084
|
/**
|
|
8001
8085
|
* Estimate token count for messages
|
|
8002
8086
|
*/
|
|
@@ -8526,7 +8610,7 @@ async function checkAndCompactContext(session, provider, signal, toolRegistry) {
|
|
|
8526
8610
|
return null;
|
|
8527
8611
|
}
|
|
8528
8612
|
const compactor = createContextCompactor({
|
|
8529
|
-
preserveLastN:
|
|
8613
|
+
preserveLastN: 8,
|
|
8530
8614
|
summaryMaxTokens: 1e3
|
|
8531
8615
|
});
|
|
8532
8616
|
const result = await compactor.compact(session.messages, provider, signal);
|
|
@@ -9401,7 +9485,7 @@ function humanizeError(message, toolName) {
|
|
|
9401
9485
|
)) {
|
|
9402
9486
|
return msg;
|
|
9403
9487
|
}
|
|
9404
|
-
if (/run git_init\b
|
|
9488
|
+
if (/run git_init\b/i.test(msg)) {
|
|
9405
9489
|
return msg;
|
|
9406
9490
|
}
|
|
9407
9491
|
if (/ECONNREFUSED/i.test(msg)) {
|
|
@@ -39183,10 +39267,37 @@ Examples:
|
|
|
39183
39267
|
}
|
|
39184
39268
|
}
|
|
39185
39269
|
});
|
|
39270
|
+
var TREE_IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
39271
|
+
"node_modules",
|
|
39272
|
+
"dist",
|
|
39273
|
+
"build",
|
|
39274
|
+
"out",
|
|
39275
|
+
".next",
|
|
39276
|
+
".nuxt",
|
|
39277
|
+
".cache",
|
|
39278
|
+
".turbo",
|
|
39279
|
+
".parcel-cache",
|
|
39280
|
+
"coverage",
|
|
39281
|
+
".nyc_output",
|
|
39282
|
+
"vendor",
|
|
39283
|
+
"__pycache__",
|
|
39284
|
+
".venv",
|
|
39285
|
+
"venv",
|
|
39286
|
+
"env",
|
|
39287
|
+
"target",
|
|
39288
|
+
".gradle",
|
|
39289
|
+
".mvn",
|
|
39290
|
+
"bin",
|
|
39291
|
+
"obj"
|
|
39292
|
+
]);
|
|
39293
|
+
var MAX_TREE_LINES = 500;
|
|
39186
39294
|
var treeTool = defineTool({
|
|
39187
39295
|
name: "tree",
|
|
39188
39296
|
description: `Display directory structure as a tree.
|
|
39189
39297
|
|
|
39298
|
+
Large dependency directories (node_modules, dist, .next, etc.) are excluded
|
|
39299
|
+
automatically. Output is capped at ${MAX_TREE_LINES} lines to keep context lean.
|
|
39300
|
+
|
|
39190
39301
|
Examples:
|
|
39191
39302
|
- Current dir: { }
|
|
39192
39303
|
- Specific dir: { "path": "src" }
|
|
@@ -39206,9 +39317,12 @@ Examples:
|
|
|
39206
39317
|
let totalFiles = 0;
|
|
39207
39318
|
let totalDirs = 0;
|
|
39208
39319
|
const lines = [path36__default.basename(absolutePath) + "/"];
|
|
39320
|
+
let truncated = false;
|
|
39209
39321
|
async function buildTree(dir, prefix, currentDepth) {
|
|
39210
39322
|
if (currentDepth > (depth ?? 4)) return;
|
|
39323
|
+
if (lines.length >= MAX_TREE_LINES) return;
|
|
39211
39324
|
let items = await fs34__default.readdir(dir, { withFileTypes: true });
|
|
39325
|
+
items = items.filter((item) => !TREE_IGNORED_DIRS.has(item.name));
|
|
39212
39326
|
if (!showHidden) {
|
|
39213
39327
|
items = items.filter((item) => !item.name.startsWith("."));
|
|
39214
39328
|
}
|
|
@@ -39221,6 +39335,10 @@ Examples:
|
|
|
39221
39335
|
return a.name.localeCompare(b.name);
|
|
39222
39336
|
});
|
|
39223
39337
|
for (let i = 0; i < items.length; i++) {
|
|
39338
|
+
if (lines.length >= MAX_TREE_LINES) {
|
|
39339
|
+
truncated = true;
|
|
39340
|
+
return;
|
|
39341
|
+
}
|
|
39224
39342
|
const item = items[i];
|
|
39225
39343
|
const isLast = i === items.length - 1;
|
|
39226
39344
|
const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
@@ -39236,10 +39354,17 @@ Examples:
|
|
|
39236
39354
|
}
|
|
39237
39355
|
}
|
|
39238
39356
|
await buildTree(absolutePath, "", 1);
|
|
39357
|
+
if (truncated) {
|
|
39358
|
+
lines.push(
|
|
39359
|
+
`
|
|
39360
|
+
[... output truncated at ${MAX_TREE_LINES} lines. Use a deeper path or lower depth to see more.]`
|
|
39361
|
+
);
|
|
39362
|
+
}
|
|
39239
39363
|
return {
|
|
39240
39364
|
tree: lines.join("\n"),
|
|
39241
39365
|
totalFiles,
|
|
39242
|
-
totalDirs
|
|
39366
|
+
totalDirs,
|
|
39367
|
+
truncated
|
|
39243
39368
|
};
|
|
39244
39369
|
} catch (error) {
|
|
39245
39370
|
if (isENOENT(error)) {
|
|
@@ -41790,7 +41915,7 @@ init_registry4();
|
|
|
41790
41915
|
init_errors();
|
|
41791
41916
|
init_version();
|
|
41792
41917
|
var DEFAULT_TIMEOUT_MS5 = 3e4;
|
|
41793
|
-
var DEFAULT_MAX_LENGTH =
|
|
41918
|
+
var DEFAULT_MAX_LENGTH = 8e3;
|
|
41794
41919
|
var MAX_DOWNLOAD_SIZE = 10 * 1024 * 1024;
|
|
41795
41920
|
var BLOCKED_SCHEMES = ["file:", "ftp:", "data:", "javascript:"];
|
|
41796
41921
|
var PRIVATE_IP_PATTERNS = [
|
|
@@ -48443,6 +48568,18 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
|
|
|
48443
48568
|
const maxIterations = session.config.agent.maxToolIterations;
|
|
48444
48569
|
const toolErrorCounts = /* @__PURE__ */ new Map();
|
|
48445
48570
|
const MAX_CONSECUTIVE_TOOL_ERRORS = 3;
|
|
48571
|
+
const INLINE_RESULT_MAX_CHARS = 8e3;
|
|
48572
|
+
const INLINE_RESULT_HEAD_CHARS = 6500;
|
|
48573
|
+
const INLINE_RESULT_TAIL_CHARS = 1e3;
|
|
48574
|
+
function truncateInlineResult(content, toolName) {
|
|
48575
|
+
if (content.length <= INLINE_RESULT_MAX_CHARS) return content;
|
|
48576
|
+
const head = content.slice(0, INLINE_RESULT_HEAD_CHARS);
|
|
48577
|
+
const tail = content.slice(-INLINE_RESULT_TAIL_CHARS);
|
|
48578
|
+
const omitted = content.length - INLINE_RESULT_HEAD_CHARS - INLINE_RESULT_TAIL_CHARS;
|
|
48579
|
+
return `${head}
|
|
48580
|
+
[... ${omitted.toLocaleString()} characters omitted \u2014 use read_file with offset/limit to retrieve more of '${toolName}' output ...]
|
|
48581
|
+
${tail}`;
|
|
48582
|
+
}
|
|
48446
48583
|
while (iteration < maxIterations) {
|
|
48447
48584
|
iteration++;
|
|
48448
48585
|
if (options.signal?.aborted) {
|
|
@@ -48695,7 +48832,7 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
|
|
|
48695
48832
|
toolResults.push({
|
|
48696
48833
|
type: "tool_result",
|
|
48697
48834
|
tool_use_id: toolCall.id,
|
|
48698
|
-
content: executedCall.result.output,
|
|
48835
|
+
content: truncateInlineResult(executedCall.result.output, toolCall.name),
|
|
48699
48836
|
is_error: !executedCall.result.success
|
|
48700
48837
|
});
|
|
48701
48838
|
} else {
|
|
@@ -50073,6 +50210,25 @@ async function startRepl(options = {}) {
|
|
|
50073
50210
|
continue;
|
|
50074
50211
|
}
|
|
50075
50212
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
50213
|
+
if (errorMsg.includes("prompt token count") && errorMsg.includes("exceeds the limit")) {
|
|
50214
|
+
renderError("Context window full \u2014 compacting conversation history...");
|
|
50215
|
+
try {
|
|
50216
|
+
const compactionResult = await checkAndCompactContext(
|
|
50217
|
+
session,
|
|
50218
|
+
provider,
|
|
50219
|
+
void 0,
|
|
50220
|
+
toolRegistry
|
|
50221
|
+
);
|
|
50222
|
+
if (compactionResult?.wasCompacted) {
|
|
50223
|
+
console.log(chalk2.green(" \u2713 Context compacted. Please retry your message."));
|
|
50224
|
+
} else {
|
|
50225
|
+
console.log(chalk2.yellow(" \u26A0 Could not compact context. Use /clear to start fresh."));
|
|
50226
|
+
}
|
|
50227
|
+
} catch {
|
|
50228
|
+
console.log(chalk2.yellow(" \u26A0 Context compaction failed. Use /clear to start fresh."));
|
|
50229
|
+
}
|
|
50230
|
+
continue;
|
|
50231
|
+
}
|
|
50076
50232
|
if (errorMsg.includes("context length") || errorMsg.includes("tokens to keep")) {
|
|
50077
50233
|
renderError(errorMsg);
|
|
50078
50234
|
console.log();
|