opencode-acp 1.4.2 → 1.5.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.js +93 -50
- package/dist/index.js.map +1 -1
- package/dist/lib/gc/merge.d.ts.map +1 -1
- package/dist/lib/prompts/context-limit-nudge.d.ts +1 -1
- package/dist/lib/prompts/context-limit-nudge.d.ts.map +1 -1
- package/dist/lib/prompts/extensions/nudge.d.ts.map +1 -1
- package/dist/lib/prompts/system.d.ts +1 -1
- package/dist/lib/prompts/system.d.ts.map +1 -1
- package/dist/lib/prompts/turn-nudge.d.ts +1 -1
- package/dist/lib/prompts/turn-nudge.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1485,7 +1485,7 @@ var defaultConfig = {
|
|
|
1485
1485
|
maxOldGenSummaryLength: 3e3,
|
|
1486
1486
|
majorGcThresholdPercent: "100%",
|
|
1487
1487
|
batchCleanup: {
|
|
1488
|
-
lowThreshold: "
|
|
1488
|
+
lowThreshold: "55%",
|
|
1489
1489
|
highThreshold: "75%",
|
|
1490
1490
|
forceThreshold: "90%"
|
|
1491
1491
|
}
|
|
@@ -5218,11 +5218,18 @@ function buildCompressedBlockGuidance(state, gcConfig, context) {
|
|
|
5218
5218
|
const activeBlockIds = Array.from(state.prune.messages.activeBlockIds).filter((id) => Number.isInteger(id) && id > 0).sort((a, b) => a - b);
|
|
5219
5219
|
const refs = activeBlockIds.map((id) => `b${id}`);
|
|
5220
5220
|
const blockCount = refs.length;
|
|
5221
|
-
|
|
5221
|
+
let blockList;
|
|
5222
|
+
if (blockCount <= 20) {
|
|
5223
|
+
blockList = blockCount > 0 ? refs.join(", ") : "none";
|
|
5224
|
+
} else {
|
|
5225
|
+
const recent = refs.slice(-20).join(", ");
|
|
5226
|
+
blockList = `${recent} (+${blockCount - 20} older, use decompress to access by ID)`;
|
|
5227
|
+
}
|
|
5222
5228
|
const lines = [
|
|
5223
5229
|
"Compressed block context:",
|
|
5224
5230
|
`- Active compressed blocks: ${blockCount} (${blockList})`,
|
|
5225
|
-
"- If your selected compression range includes any listed block, include each required placeholder exactly once in the summary using `(bN)`."
|
|
5231
|
+
"- If your selected compression range includes any listed block, include each required placeholder exactly once in the summary using `(bN)`.",
|
|
5232
|
+
"- \u{1F4A1} When you've finished using tool outputs, compress them \u2014 you can decompress later if needed. Lean context improves accuracy."
|
|
5226
5233
|
];
|
|
5227
5234
|
const usageRatio = context?.currentTokens && context?.modelContextLimit ? context.currentTokens / context.modelContextLimit : 0;
|
|
5228
5235
|
if (gcConfig && usageRatio > 0.5) {
|
|
@@ -5575,14 +5582,14 @@ function buildContextUsageGuidance(config, currentTokens, modelContextLimit) {
|
|
|
5575
5582
|
const formatK = (n) => n >= 1e3 ? `${(n / 1e3).toFixed(1)}K` : String(n);
|
|
5576
5583
|
const minPct = resolveThresholdPercent(config.compress.minContextLimit, modelContextLimit) ?? 45;
|
|
5577
5584
|
const maxPct = resolveThresholdPercent(config.compress.maxContextLimit, modelContextLimit) ?? 55;
|
|
5578
|
-
const base = `Context usage: ${formatK(currentTokens)} / ${formatK(modelContextLimit)} tokens (${percentage}%)
|
|
5585
|
+
const base = `Context usage: ${formatK(currentTokens)} / ${formatK(modelContextLimit)} tokens (${percentage}%).`;
|
|
5579
5586
|
let guidance;
|
|
5580
5587
|
if (pct < minPct) {
|
|
5581
|
-
guidance = "
|
|
5588
|
+
guidance = " \u{1F4A1} Be frugal with context \u2014 compress tool outputs you've finished using into summaries. You can decompress later; nothing is permanently lost. Lean context means better accuracy. Extract and keep what matters: user intent, key decisions, file paths, and important findings \u2014 even if buried in large messages. Compress everything else, including verbose parts of any message.";
|
|
5582
5589
|
} else if (pct < maxPct) {
|
|
5583
|
-
guidance = " Context is
|
|
5590
|
+
guidance = " \u26A0\uFE0F Context is growing \u2014 compress completed sections and high-token waste now. Preserve key details.";
|
|
5584
5591
|
} else {
|
|
5585
|
-
guidance = " Context is high \u2014 compress aggressively but selectively. Preserve only what is essential.";
|
|
5592
|
+
guidance = " \u{1F525} Context is high \u2014 compress aggressively but selectively. Preserve only what is essential.";
|
|
5586
5593
|
}
|
|
5587
5594
|
return `
|
|
5588
5595
|
|
|
@@ -6628,15 +6635,15 @@ COMPRESSION PHILOSOPHY
|
|
|
6628
6635
|
|
|
6629
6636
|
Compression replaces raw conversation content with dense summaries. When used correctly, it keeps your context sharp and focused. When used carelessly, it destroys information you need.
|
|
6630
6637
|
|
|
6631
|
-
The key principle: compress
|
|
6638
|
+
The key principle: compress proactively to keep context lean, but selectively. Large tool outputs (shell, diffs, logs) can be compressed into summaries at any time \u2014 you can decompress later if needed. Extract and keep what matters: user intent, key decisions, file paths, and important findings \u2014 even if buried in large messages. Compress everything else, including verbose parts of user messages, large code dumps, and long discussions.
|
|
6632
6639
|
|
|
6633
6640
|
Target the largest UNCOMPRESSED content first. Savings scale with original size \u2014 compressing a 5000-token tool output frees far more than re-shrinking an already-summarized 300-token block.
|
|
6634
6641
|
|
|
6635
6642
|
CONTEXT PRESSURE LEVELS
|
|
6636
6643
|
|
|
6637
|
-
-
|
|
6638
|
-
-
|
|
6639
|
-
-
|
|
6644
|
+
- Normal: Be frugal \u2014 compress tool outputs you've finished using into summaries. You can decompress later. Extract and keep what matters from any message; compress verbose parts \u2014 including large logs in user messages or generated code.
|
|
6645
|
+
- Elevated: Context is growing. Compress completed sections and high-token waste more urgently.
|
|
6646
|
+
- Critical: Compress aggressively now. Every compression should free meaningful tokens. Preserve only what is essential for the current task.
|
|
6640
6647
|
|
|
6641
6648
|
WHAT TO COMPRESS FIRST (high value, low risk)
|
|
6642
6649
|
|
|
@@ -6798,9 +6805,9 @@ General cleanup should be done periodically between other normal compression too
|
|
|
6798
6805
|
// lib/prompts/context-limit-nudge.ts
|
|
6799
6806
|
var CONTEXT_LIMIT_NUDGE = `
|
|
6800
6807
|
<system-reminder>
|
|
6801
|
-
\u26A0\uFE0F
|
|
6808
|
+
\u26A0\uFE0F Context limit reached \u2014 time to compress the largest ranges you no longer need. Prioritize completed tool outputs and resolved work. You can decompress specific blocks later if you need details. Keeping context lean helps you stay accurate.
|
|
6802
6809
|
|
|
6803
|
-
If mid-atomic-operation, finish that step first, then compress
|
|
6810
|
+
If mid-atomic-operation, finish that step first, then compress.
|
|
6804
6811
|
|
|
6805
6812
|
HOW TO CALL COMPRESS:
|
|
6806
6813
|
{
|
|
@@ -6815,7 +6822,7 @@ HOW TO CALL COMPRESS:
|
|
|
6815
6822
|
}
|
|
6816
6823
|
|
|
6817
6824
|
\u26A0\uFE0F ID RULES \u2014 MOST COMMON CAUSE OF ERRORS:
|
|
6818
|
-
- ONLY use IDs you can see in
|
|
6825
|
+
- ONLY use IDs you can see in tags in the messages ABOVE.
|
|
6819
6826
|
- Do NOT copy IDs from this example. Do NOT invent IDs.
|
|
6820
6827
|
- Do NOT use IDs from compressed block summaries \u2014 they are stale.
|
|
6821
6828
|
- startId must appear BEFORE endId in the conversation.
|
|
@@ -6831,14 +6838,14 @@ SUMMARY RULES:
|
|
|
6831
6838
|
// lib/prompts/turn-nudge.ts
|
|
6832
6839
|
var TURN_NUDGE = `
|
|
6833
6840
|
<system-reminder>
|
|
6834
|
-
Context is getting full.
|
|
6841
|
+
Context is getting full. If you've finished reading tool outputs or exploration results, compress them \u2014 you can decompress later if needed. This keeps your focus on the current task and improves accuracy.
|
|
6835
6842
|
|
|
6836
6843
|
{
|
|
6837
6844
|
"topic": "Short Label",
|
|
6838
6845
|
"content": [{ "startId": "<visible message ID>", "endId": "<visible message ID>", "summary": "..." }]
|
|
6839
6846
|
}
|
|
6840
6847
|
|
|
6841
|
-
\u26A0\uFE0F ONLY use IDs from
|
|
6848
|
+
\u26A0\uFE0F ONLY use IDs from tags visible above. Do NOT invent or copy example IDs.
|
|
6842
6849
|
</system-reminder>
|
|
6843
6850
|
`;
|
|
6844
6851
|
|
|
@@ -8212,10 +8219,12 @@ function parseGcThreshold(limit, modelContextLimit) {
|
|
|
8212
8219
|
|
|
8213
8220
|
// lib/gc/merge.ts
|
|
8214
8221
|
var DEFAULT_BATCH_CLEANUP = {
|
|
8215
|
-
lowThreshold: "
|
|
8222
|
+
lowThreshold: "55%",
|
|
8216
8223
|
highThreshold: "75%",
|
|
8217
8224
|
forceThreshold: "90%"
|
|
8218
8225
|
};
|
|
8226
|
+
var ESCALATE_MIN_MARKED = 3;
|
|
8227
|
+
var ESCALATE_MIN_RATIO = 0.4;
|
|
8219
8228
|
function resolveBatchCleanup(gc) {
|
|
8220
8229
|
return gc.batchCleanup ?? DEFAULT_BATCH_CLEANUP;
|
|
8221
8230
|
}
|
|
@@ -8239,11 +8248,15 @@ function collectActiveOldGenBlocks(state, maxOldGenSummaryLength) {
|
|
|
8239
8248
|
return blocks;
|
|
8240
8249
|
}
|
|
8241
8250
|
function collectActiveMarkedBlocks(state) {
|
|
8242
|
-
const
|
|
8251
|
+
const messagesState = state.prune.messages;
|
|
8252
|
+
const ids = Array.from(messagesState.markedForCleanup).sort((a, b) => a - b);
|
|
8243
8253
|
const blocks = [];
|
|
8244
8254
|
for (const id of ids) {
|
|
8245
|
-
const block =
|
|
8246
|
-
if (!block || !block.active)
|
|
8255
|
+
const block = messagesState.blocksById.get(id);
|
|
8256
|
+
if (!block || !block.active) {
|
|
8257
|
+
messagesState.markedForCleanup.delete(id);
|
|
8258
|
+
continue;
|
|
8259
|
+
}
|
|
8247
8260
|
blocks.push(block);
|
|
8248
8261
|
}
|
|
8249
8262
|
return blocks;
|
|
@@ -8368,21 +8381,53 @@ function mergeMarkedBlocks(state, markedIds, maxMergedLength) {
|
|
|
8368
8381
|
const savedTokens = Math.max(0, sourceTokens - newSummaryTokens);
|
|
8369
8382
|
return { mergedCount: sourceBlocks.length, savedTokens };
|
|
8370
8383
|
}
|
|
8371
|
-
function
|
|
8372
|
-
|
|
8373
|
-
if (blocks.length < 1) return void 0;
|
|
8374
|
-
const refs = blocks.map((b) => formatBlockRef(b.blockId)).join(", ");
|
|
8375
|
-
const sourceTokens = blocks.reduce(
|
|
8384
|
+
function estimateTokens(blocks) {
|
|
8385
|
+
return blocks.reduce(
|
|
8376
8386
|
(sum, block) => sum + (block.summaryTokens || Math.round(block.summary.length / 4)),
|
|
8377
8387
|
0
|
|
8378
8388
|
);
|
|
8379
|
-
|
|
8380
|
-
|
|
8389
|
+
}
|
|
8390
|
+
function buildNudgeText(state, maxMergedLength) {
|
|
8391
|
+
const marked = collectActiveMarkedBlocks(state);
|
|
8392
|
+
const oldGen = collectActiveOldGenBlocks(state, maxMergedLength);
|
|
8393
|
+
if (oldGen.length === 0) return void 0;
|
|
8394
|
+
const oldGenIds = new Set(oldGen.map((b) => b.blockId));
|
|
8395
|
+
const markedOldGen = marked.filter((b) => oldGenIds.has(b.blockId));
|
|
8396
|
+
const markedOldGenCount = markedOldGen.length;
|
|
8397
|
+
const oldGenCount = oldGen.length;
|
|
8398
|
+
const ratio = markedOldGenCount / oldGenCount;
|
|
8399
|
+
const ratioPct = Math.round(ratio * 100);
|
|
8400
|
+
const escalateMinPct = Math.round(ESCALATE_MIN_RATIO * 100);
|
|
8401
|
+
if (markedOldGenCount >= ESCALATE_MIN_MARKED && ratio >= ESCALATE_MIN_RATIO) {
|
|
8402
|
+
const refs = marked.map((b) => formatBlockRef(b.blockId)).join(", ");
|
|
8403
|
+
const firstRef = formatBlockRef(marked[0].blockId);
|
|
8404
|
+
const lastRef = formatBlockRef(marked[marked.length - 1].blockId);
|
|
8405
|
+
const estimatedSavings = Math.max(0, estimateTokens(marked) - Math.round(maxMergedLength / 4));
|
|
8406
|
+
return [
|
|
8407
|
+
`\u{1F525} ${markedOldGenCount}/${oldGenCount} old-gen blocks marked (${ratioPct}%) \u2014 ready for batch cleanup.`,
|
|
8408
|
+
`Compressing ${refs} (range ${firstRef}\u2013${lastRef}) would free ~${estimatedSavings} tokens in one cache break.`,
|
|
8409
|
+
`Call compress with this range now to consolidate them.`
|
|
8410
|
+
].join(" ");
|
|
8411
|
+
}
|
|
8412
|
+
if (marked.length >= 1) {
|
|
8413
|
+
const refs = marked.map((b) => formatBlockRef(b.blockId)).join(", ");
|
|
8414
|
+
const estimatedSavings = Math.max(0, estimateTokens(marked) - Math.round(maxMergedLength / 4));
|
|
8415
|
+
return [
|
|
8416
|
+
`\u26A0\uFE0F ${marked.length} block(s) marked for batch cleanup (${refs}).`,
|
|
8417
|
+
`Merge-compressing them would free ~${estimatedSavings} tokens.`,
|
|
8418
|
+
marked.length >= 2 ? "They will auto-merge when context pressure reaches the high threshold." : "A single marked block won't auto-merge on its own \u2014 use compress to consolidate it, or unmark_block if no longer needed.",
|
|
8419
|
+
`Mark more old-gen blocks (need \u2265${ESCALATE_MIN_MARKED} at \u2265${escalateMinPct}%) to trigger batch cleanup sooner.`,
|
|
8420
|
+
"To act now, use compress with a range covering these blocks."
|
|
8421
|
+
].join(" ");
|
|
8422
|
+
}
|
|
8423
|
+
const shown = oldGen.slice(0, 5);
|
|
8424
|
+
const oldGenRefs = shown.map((b) => formatBlockRef(b.blockId)).join(", ");
|
|
8425
|
+
const more = oldGenCount > 5 ? ` (+${oldGenCount - 5} more)` : "";
|
|
8381
8426
|
return [
|
|
8382
|
-
`\
|
|
8383
|
-
`
|
|
8384
|
-
|
|
8385
|
-
|
|
8427
|
+
`\u{1F4CB} Context pressure rising \u2014 ${oldGenCount} old-gen compressed block(s) occupy ~${estimateTokens(oldGen)} tokens (${oldGenRefs}${more}).`,
|
|
8428
|
+
`Review which blocks contain information you no longer need, and use mark_block to flag them.`,
|
|
8429
|
+
`Once enough are marked (\u2265${ESCALATE_MIN_MARKED} at \u2265${escalateMinPct}% of old-gen), they'll be batch-merged in one cache break to preserve cache hit rate.`,
|
|
8430
|
+
`Do NOT mark blocks you may still need.`
|
|
8386
8431
|
].join(" ");
|
|
8387
8432
|
}
|
|
8388
8433
|
function runBatchCleanup(state, config, logger, messages) {
|
|
@@ -8427,26 +8472,24 @@ function runBatchCleanup(state, config, logger, messages) {
|
|
|
8427
8472
|
}
|
|
8428
8473
|
if (currentTokens >= highTokens) {
|
|
8429
8474
|
const marked = collectActiveMarkedBlocks(state);
|
|
8430
|
-
if (marked.length
|
|
8431
|
-
|
|
8432
|
-
|
|
8433
|
-
|
|
8434
|
-
|
|
8435
|
-
|
|
8436
|
-
|
|
8475
|
+
if (marked.length >= 2) {
|
|
8476
|
+
const ids = marked.map((b) => b.blockId);
|
|
8477
|
+
const result = mergeMarkedBlocks(state, ids, maxMergedLength);
|
|
8478
|
+
if (result.mergedCount > 0) {
|
|
8479
|
+
logger.info("Batch cleanup tier 2 (high): merged marked blocks", {
|
|
8480
|
+
mergedCount: result.mergedCount,
|
|
8481
|
+
savedTokens: result.savedTokens,
|
|
8482
|
+
currentTokens,
|
|
8483
|
+
highThreshold: batchCleanup.highThreshold
|
|
8484
|
+
});
|
|
8485
|
+
return {
|
|
8486
|
+
tier: 2,
|
|
8487
|
+
action: "merge",
|
|
8488
|
+
mergedCount: result.mergedCount,
|
|
8489
|
+
savedTokens: result.savedTokens
|
|
8490
|
+
};
|
|
8491
|
+
}
|
|
8437
8492
|
}
|
|
8438
|
-
logger.info("Batch cleanup tier 2 (high): merged marked blocks", {
|
|
8439
|
-
mergedCount: result.mergedCount,
|
|
8440
|
-
savedTokens: result.savedTokens,
|
|
8441
|
-
currentTokens,
|
|
8442
|
-
highThreshold: batchCleanup.highThreshold
|
|
8443
|
-
});
|
|
8444
|
-
return {
|
|
8445
|
-
tier: 2,
|
|
8446
|
-
action: "merge",
|
|
8447
|
-
mergedCount: result.mergedCount,
|
|
8448
|
-
savedTokens: result.savedTokens
|
|
8449
|
-
};
|
|
8450
8493
|
}
|
|
8451
8494
|
if (currentTokens >= lowTokens) {
|
|
8452
8495
|
const nudgeText = buildNudgeText(state, maxMergedLength);
|