opencode-acp 1.12.0 → 1.12.1
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/README.md +8 -0
- package/README.zh-CN.md +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +305 -243
- package/dist/index.js.map +1 -1
- package/dist/lib/compress/index.d.ts +1 -0
- package/dist/lib/compress/index.d.ts.map +1 -1
- package/dist/lib/compress/keep-markers.d.ts.map +1 -1
- package/dist/lib/compress/message.d.ts.map +1 -1
- package/dist/lib/compress/range.d.ts.map +1 -1
- package/dist/lib/compress/recap.d.ts +4 -0
- package/dist/lib/compress/recap.d.ts.map +1 -0
- package/dist/lib/hooks.d.ts.map +1 -1
- package/dist/lib/messages/index.d.ts +1 -1
- package/dist/lib/messages/index.d.ts.map +1 -1
- package/dist/lib/messages/inject/inject.d.ts +1 -1
- package/dist/lib/messages/inject/inject.d.ts.map +1 -1
- package/dist/lib/messages/prune.d.ts +1 -0
- package/dist/lib/messages/prune.d.ts.map +1 -1
- package/dist/lib/ui/notification.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2677,20 +2677,20 @@ function matchesGlob(inputPath, pattern) {
|
|
|
2677
2677
|
regex += "$";
|
|
2678
2678
|
return new RegExp(regex).test(input);
|
|
2679
2679
|
}
|
|
2680
|
-
function getFilePathsFromParameters(
|
|
2680
|
+
function getFilePathsFromParameters(tool8, parameters) {
|
|
2681
2681
|
if (typeof parameters !== "object" || parameters === null) {
|
|
2682
2682
|
return [];
|
|
2683
2683
|
}
|
|
2684
2684
|
const paths = [];
|
|
2685
2685
|
const params = parameters;
|
|
2686
|
-
if (
|
|
2686
|
+
if (tool8 === "apply_patch" && typeof params.patchText === "string") {
|
|
2687
2687
|
const pathRegex = /\*\*\* (?:Add|Delete|Update) File: ([^\n\r]+)/g;
|
|
2688
2688
|
let match;
|
|
2689
2689
|
while ((match = pathRegex.exec(params.patchText)) !== null) {
|
|
2690
2690
|
paths.push(match[1].trim());
|
|
2691
2691
|
}
|
|
2692
2692
|
}
|
|
2693
|
-
if (
|
|
2693
|
+
if (tool8 === "multiedit") {
|
|
2694
2694
|
if (typeof params.filePath === "string") {
|
|
2695
2695
|
paths.push(params.filePath);
|
|
2696
2696
|
}
|
|
@@ -4578,13 +4578,13 @@ var deduplicate = (state, logger, config, messages) => {
|
|
|
4578
4578
|
logger.debug(`Marked ${newPruneIds.length} duplicate tool calls for pruning`);
|
|
4579
4579
|
}
|
|
4580
4580
|
};
|
|
4581
|
-
function createToolSignature(
|
|
4581
|
+
function createToolSignature(tool8, parameters) {
|
|
4582
4582
|
if (!parameters) {
|
|
4583
|
-
return
|
|
4583
|
+
return tool8;
|
|
4584
4584
|
}
|
|
4585
4585
|
const normalized = normalizeParameters(parameters);
|
|
4586
4586
|
const sorted = sortObjectKeys(normalized);
|
|
4587
|
-
return `${
|
|
4587
|
+
return `${tool8}::${JSON.stringify(sorted)}`;
|
|
4588
4588
|
}
|
|
4589
4589
|
function normalizeParameters(params) {
|
|
4590
4590
|
if (typeof params !== "object" || params === null) return params;
|
|
@@ -4659,9 +4659,9 @@ var purgeErrors = (state, logger, config, messages) => {
|
|
|
4659
4659
|
};
|
|
4660
4660
|
|
|
4661
4661
|
// lib/ui/utils.ts
|
|
4662
|
-
function extractParameterKey(
|
|
4662
|
+
function extractParameterKey(tool8, parameters) {
|
|
4663
4663
|
if (!parameters) return "";
|
|
4664
|
-
if (
|
|
4664
|
+
if (tool8 === "read" && parameters.filePath) {
|
|
4665
4665
|
const offset = parameters.offset;
|
|
4666
4666
|
const limit = parameters.limit;
|
|
4667
4667
|
if (offset !== void 0 && limit !== void 0) {
|
|
@@ -4675,10 +4675,10 @@ function extractParameterKey(tool7, parameters) {
|
|
|
4675
4675
|
}
|
|
4676
4676
|
return parameters.filePath;
|
|
4677
4677
|
}
|
|
4678
|
-
if ((
|
|
4678
|
+
if ((tool8 === "write" || tool8 === "edit" || tool8 === "multiedit") && parameters.filePath) {
|
|
4679
4679
|
return parameters.filePath;
|
|
4680
4680
|
}
|
|
4681
|
-
if (
|
|
4681
|
+
if (tool8 === "apply_patch" && typeof parameters.patchText === "string") {
|
|
4682
4682
|
const pathRegex = /\*\*\* (?:Add|Delete|Update) File: ([^\n\r]+)/g;
|
|
4683
4683
|
const paths = [];
|
|
4684
4684
|
let match;
|
|
@@ -4695,51 +4695,51 @@ function extractParameterKey(tool7, parameters) {
|
|
|
4695
4695
|
}
|
|
4696
4696
|
return "patch";
|
|
4697
4697
|
}
|
|
4698
|
-
if (
|
|
4698
|
+
if (tool8 === "list") {
|
|
4699
4699
|
return parameters.path || "(current directory)";
|
|
4700
4700
|
}
|
|
4701
|
-
if (
|
|
4701
|
+
if (tool8 === "glob") {
|
|
4702
4702
|
if (parameters.pattern) {
|
|
4703
4703
|
const pathInfo = parameters.path ? ` in ${parameters.path}` : "";
|
|
4704
4704
|
return `"${parameters.pattern}"${pathInfo}`;
|
|
4705
4705
|
}
|
|
4706
4706
|
return "(unknown pattern)";
|
|
4707
4707
|
}
|
|
4708
|
-
if (
|
|
4708
|
+
if (tool8 === "grep") {
|
|
4709
4709
|
if (parameters.pattern) {
|
|
4710
4710
|
const pathInfo = parameters.path ? ` in ${parameters.path}` : "";
|
|
4711
4711
|
return `"${parameters.pattern}"${pathInfo}`;
|
|
4712
4712
|
}
|
|
4713
4713
|
return "(unknown pattern)";
|
|
4714
4714
|
}
|
|
4715
|
-
if (
|
|
4715
|
+
if (tool8 === "bash") {
|
|
4716
4716
|
if (parameters.description) return parameters.description;
|
|
4717
4717
|
if (parameters.command) {
|
|
4718
4718
|
return parameters.command.length > 50 ? parameters.command.substring(0, 50) + "..." : parameters.command;
|
|
4719
4719
|
}
|
|
4720
4720
|
}
|
|
4721
|
-
if (
|
|
4721
|
+
if (tool8 === "webfetch" && parameters.url) {
|
|
4722
4722
|
return parameters.url;
|
|
4723
4723
|
}
|
|
4724
|
-
if (
|
|
4724
|
+
if (tool8 === "websearch" && parameters.query) {
|
|
4725
4725
|
return `"${parameters.query}"`;
|
|
4726
4726
|
}
|
|
4727
|
-
if (
|
|
4727
|
+
if (tool8 === "codesearch" && parameters.query) {
|
|
4728
4728
|
return `"${parameters.query}"`;
|
|
4729
4729
|
}
|
|
4730
|
-
if (
|
|
4730
|
+
if (tool8 === "todowrite") {
|
|
4731
4731
|
return `${parameters.todos?.length || 0} todos`;
|
|
4732
4732
|
}
|
|
4733
|
-
if (
|
|
4733
|
+
if (tool8 === "todoread") {
|
|
4734
4734
|
return "read todo list";
|
|
4735
4735
|
}
|
|
4736
|
-
if (
|
|
4736
|
+
if (tool8 === "task" && parameters.description) {
|
|
4737
4737
|
return parameters.description;
|
|
4738
4738
|
}
|
|
4739
|
-
if (
|
|
4739
|
+
if (tool8 === "skill" && parameters.name) {
|
|
4740
4740
|
return parameters.name;
|
|
4741
4741
|
}
|
|
4742
|
-
if (
|
|
4742
|
+
if (tool8 === "lsp") {
|
|
4743
4743
|
const op = parameters.operation || "lsp";
|
|
4744
4744
|
const path = parameters.filePath || "";
|
|
4745
4745
|
const line = parameters.line;
|
|
@@ -4752,7 +4752,7 @@ function extractParameterKey(tool7, parameters) {
|
|
|
4752
4752
|
}
|
|
4753
4753
|
return op;
|
|
4754
4754
|
}
|
|
4755
|
-
if (
|
|
4755
|
+
if (tool8 === "question") {
|
|
4756
4756
|
const questions = parameters.questions;
|
|
4757
4757
|
if (Array.isArray(questions) && questions.length > 0) {
|
|
4758
4758
|
const headers = questions.map((q) => q.header || "").filter(Boolean).slice(0, 3);
|
|
@@ -4896,7 +4896,22 @@ function formatPrunedItemsList(pruneToolIds, toolMetadata, workingDirectory) {
|
|
|
4896
4896
|
var TOAST_BODY_MAX_LINES = 12;
|
|
4897
4897
|
var TOAST_SUMMARY_MAX_CHARS = 600;
|
|
4898
4898
|
var NOTIFICATION_SUMMARY_MAX_CHARS = 1500;
|
|
4899
|
-
|
|
4899
|
+
function formatEntryRanges(entries, state) {
|
|
4900
|
+
const parts = [];
|
|
4901
|
+
for (const entry of entries) {
|
|
4902
|
+
const block = state.prune.messages.blocksById.get(entry.blockId);
|
|
4903
|
+
if (!block) continue;
|
|
4904
|
+
const startRef = block.startId;
|
|
4905
|
+
const endRef = block.endId;
|
|
4906
|
+
if (!startRef || !endRef) continue;
|
|
4907
|
+
if (startRef === endRef) {
|
|
4908
|
+
parts.push(`b${entry.blockId}: ${startRef}`);
|
|
4909
|
+
} else {
|
|
4910
|
+
parts.push(`b${entry.blockId}: ${startRef}\u2013${endRef}`);
|
|
4911
|
+
}
|
|
4912
|
+
}
|
|
4913
|
+
return parts.length > 0 ? parts.join(", ") : null;
|
|
4914
|
+
}
|
|
4900
4915
|
function truncateToastBody(body, maxLines = TOAST_BODY_MAX_LINES) {
|
|
4901
4916
|
const lines = body.split("\n");
|
|
4902
4917
|
if (lines.length <= maxLines) {
|
|
@@ -5014,6 +5029,7 @@ async function sendCompressNotification(client, logger, config, state, sessionId
|
|
|
5014
5029
|
contextTokensBefore,
|
|
5015
5030
|
contextTokensAfter
|
|
5016
5031
|
)}`;
|
|
5032
|
+
let displaySummary = summary;
|
|
5017
5033
|
if (config.pruneNotification === "minimal") {
|
|
5018
5034
|
message = `${notificationHeader} \u2014 ${compressionLabel}`;
|
|
5019
5035
|
} else {
|
|
@@ -5035,6 +5051,11 @@ async function sendCompressNotification(client, logger, config, state, sessionId
|
|
|
5035
5051
|
${progressBar}`;
|
|
5036
5052
|
message += `
|
|
5037
5053
|
\u25A3 ${compressionLabel} ${formatCompressionMetrics(compressedTokens, summaryTokens)}`;
|
|
5054
|
+
const rangeStr = formatEntryRanges(entries, state);
|
|
5055
|
+
if (rangeStr) {
|
|
5056
|
+
message += `
|
|
5057
|
+
\u2192 Range: ${rangeStr}`;
|
|
5058
|
+
}
|
|
5038
5059
|
message += `
|
|
5039
5060
|
\u2192 Topic: ${topic}`;
|
|
5040
5061
|
message += `
|
|
@@ -5045,25 +5066,17 @@ ${progressBar}`;
|
|
|
5045
5066
|
message += ` compressed`;
|
|
5046
5067
|
}
|
|
5047
5068
|
if (config.compress.showCompression) {
|
|
5048
|
-
|
|
5049
|
-
|
|
5069
|
+
if (config.pruneNotification === "detailed") {
|
|
5070
|
+
displaySummary = summary;
|
|
5071
|
+
} else {
|
|
5072
|
+
displaySummary = summary.length > NOTIFICATION_SUMMARY_MAX_CHARS ? truncateToastSummary(summary, NOTIFICATION_SUMMARY_MAX_CHARS) : summary;
|
|
5073
|
+
}
|
|
5050
5074
|
message += `
|
|
5051
5075
|
\u2192 Compression (~${summaryTokensStr}): ${displaySummary}`;
|
|
5052
5076
|
}
|
|
5053
5077
|
}
|
|
5054
5078
|
if (config.pruneNotificationType === "toast") {
|
|
5055
5079
|
let toastMessage = message;
|
|
5056
|
-
if (config.compress.showCompression) {
|
|
5057
|
-
const truncatedSummary = truncateToastSummary(summary);
|
|
5058
|
-
if (truncatedSummary !== summary) {
|
|
5059
|
-
toastMessage = toastMessage.replace(
|
|
5060
|
-
`
|
|
5061
|
-
\u2192 Compression (~${summaryTokensStr}): ${truncateToastSummary(summary, NOTIFICATION_SUMMARY_MAX_CHARS)}`,
|
|
5062
|
-
`
|
|
5063
|
-
\u2192 Compression (~${summaryTokensStr}): ${truncatedSummary}`
|
|
5064
|
-
);
|
|
5065
|
-
}
|
|
5066
|
-
}
|
|
5067
5080
|
toastMessage = config.pruneNotification === "minimal" ? toastMessage : truncateToastBody(toastMessage);
|
|
5068
5081
|
await client.tui.showToast({
|
|
5069
5082
|
body: {
|
|
@@ -5162,6 +5175,118 @@ async function finalizeSession(ctx, toolCtx, rawMessages, entries, batchTopic) {
|
|
|
5162
5175
|
);
|
|
5163
5176
|
}
|
|
5164
5177
|
|
|
5178
|
+
// lib/compress/keep-markers.ts
|
|
5179
|
+
var KEEP_REGEX = /\[\[KEEP:(m\d+)\]\]/g;
|
|
5180
|
+
var REF_REGEX = /\[\[REF:(m\d+)\|([^\]]+)\]\]/g;
|
|
5181
|
+
function resolveKeepMarkers(summary, messages, state, config) {
|
|
5182
|
+
const msgByRef = /* @__PURE__ */ new Map();
|
|
5183
|
+
for (const msg of messages) {
|
|
5184
|
+
const ref = state.messageIds.byRawId.get(msg.info.id);
|
|
5185
|
+
if (ref) msgByRef.set(ref, msg);
|
|
5186
|
+
}
|
|
5187
|
+
const maxChars = config.compress?.keepEmbedMaxChars ?? 2e3;
|
|
5188
|
+
let expandedCount = 0;
|
|
5189
|
+
let refCount = 0;
|
|
5190
|
+
const unresolvedRefs = [];
|
|
5191
|
+
const expanded = summary.replace(KEEP_REGEX, (match, ref) => {
|
|
5192
|
+
const normalized = normalizeRef(ref);
|
|
5193
|
+
const msg = normalized ? msgByRef.get(normalized) : void 0;
|
|
5194
|
+
if (!msg) {
|
|
5195
|
+
unresolvedRefs.push(ref);
|
|
5196
|
+
return match;
|
|
5197
|
+
}
|
|
5198
|
+
expandedCount++;
|
|
5199
|
+
return formatKeptMessage(msg, normalized, maxChars);
|
|
5200
|
+
}).replace(REF_REGEX, (_match, ref, desc) => {
|
|
5201
|
+
const normalized = normalizeRef(ref);
|
|
5202
|
+
const msg = normalized ? msgByRef.get(normalized) : void 0;
|
|
5203
|
+
if (!msg) {
|
|
5204
|
+
unresolvedRefs.push(ref);
|
|
5205
|
+
return _match;
|
|
5206
|
+
}
|
|
5207
|
+
refCount++;
|
|
5208
|
+
return `[\u2192 ${normalized}: ${desc.trim()}]`;
|
|
5209
|
+
});
|
|
5210
|
+
return { summary: expanded, expandedCount, refCount, unresolvedRefs };
|
|
5211
|
+
}
|
|
5212
|
+
function normalizeRef(ref) {
|
|
5213
|
+
const idx = parseMessageRef(ref);
|
|
5214
|
+
if (idx === null) return null;
|
|
5215
|
+
return formatMessageRef(idx);
|
|
5216
|
+
}
|
|
5217
|
+
function formatKeptMessage(msg, ref, maxChars) {
|
|
5218
|
+
const formatted = formatByType(msg);
|
|
5219
|
+
const truncated = truncate2(formatted, maxChars);
|
|
5220
|
+
return `
|
|
5221
|
+
--- [${ref}: ${labelForMessage(msg)}] ---
|
|
5222
|
+
${truncated}
|
|
5223
|
+
--- end ---
|
|
5224
|
+
`;
|
|
5225
|
+
}
|
|
5226
|
+
function formatByType(msg) {
|
|
5227
|
+
for (const part of msg.parts || []) {
|
|
5228
|
+
if (part.type === "text" && typeof part.text === "string") {
|
|
5229
|
+
return part.text;
|
|
5230
|
+
}
|
|
5231
|
+
if (part.type === "tool") {
|
|
5232
|
+
const tool8 = part.tool || "unknown";
|
|
5233
|
+
const state = part.state || {};
|
|
5234
|
+
const input = state.input || {};
|
|
5235
|
+
const output = state.output || "";
|
|
5236
|
+
switch (tool8) {
|
|
5237
|
+
case "bash":
|
|
5238
|
+
case "interactive_bash": {
|
|
5239
|
+
const cmd = typeof input === "string" ? input : input.command || JSON.stringify(input);
|
|
5240
|
+
return `$ ${cmd}
|
|
5241
|
+
${output}`;
|
|
5242
|
+
}
|
|
5243
|
+
case "read": {
|
|
5244
|
+
const fp = input.filePath || input.path || input.file || "";
|
|
5245
|
+
return output;
|
|
5246
|
+
}
|
|
5247
|
+
case "write":
|
|
5248
|
+
case "edit": {
|
|
5249
|
+
const fp = input.filePath || input.path || "";
|
|
5250
|
+
const content = input.content || input.newString || "";
|
|
5251
|
+
return `${fp}:
|
|
5252
|
+
${content}`;
|
|
5253
|
+
}
|
|
5254
|
+
case "reply": {
|
|
5255
|
+
return output || "[reply posted]";
|
|
5256
|
+
}
|
|
5257
|
+
case "grep":
|
|
5258
|
+
case "glob": {
|
|
5259
|
+
return output;
|
|
5260
|
+
}
|
|
5261
|
+
default: {
|
|
5262
|
+
if (output && typeof output === "string" && output.length > 0) {
|
|
5263
|
+
return output;
|
|
5264
|
+
}
|
|
5265
|
+
const compact = JSON.stringify({ tool: tool8, input }, null, 0);
|
|
5266
|
+
return compact.length > 500 ? compact.slice(0, 500) + "..." : compact;
|
|
5267
|
+
}
|
|
5268
|
+
}
|
|
5269
|
+
}
|
|
5270
|
+
}
|
|
5271
|
+
return "[empty message]";
|
|
5272
|
+
}
|
|
5273
|
+
function labelForMessage(msg) {
|
|
5274
|
+
for (const part of msg.parts || []) {
|
|
5275
|
+
if (part.type === "tool") {
|
|
5276
|
+
const tool8 = part.tool || "unknown";
|
|
5277
|
+
const input = part.state?.input || {};
|
|
5278
|
+
const fp = input.filePath || input.path || input.command || "";
|
|
5279
|
+
return fp ? `${tool8}: ${String(fp).slice(0, 60)}` : tool8;
|
|
5280
|
+
}
|
|
5281
|
+
}
|
|
5282
|
+
return msg.info.role === "user" ? "user" : "text";
|
|
5283
|
+
}
|
|
5284
|
+
function truncate2(text, maxChars) {
|
|
5285
|
+
if (text.length <= maxChars) return text;
|
|
5286
|
+
return text.slice(0, maxChars) + `
|
|
5287
|
+
... [truncated, ${text.length} chars total]`;
|
|
5288
|
+
}
|
|
5289
|
+
|
|
5165
5290
|
// lib/compress/message.ts
|
|
5166
5291
|
function buildSchema(maxSummaryLengthHard) {
|
|
5167
5292
|
return {
|
|
@@ -5263,7 +5388,14 @@ function createCompressMessageTool(ctx) {
|
|
|
5263
5388
|
const runId = allocateRunId(ctx.state);
|
|
5264
5389
|
for (const { plan, summaryWithTools } of preparedPlans) {
|
|
5265
5390
|
const blockId = allocateBlockId(ctx.state);
|
|
5266
|
-
const
|
|
5391
|
+
const keepResult = resolveKeepMarkers(
|
|
5392
|
+
summaryWithTools,
|
|
5393
|
+
rawMessages,
|
|
5394
|
+
ctx.state,
|
|
5395
|
+
ctx.config
|
|
5396
|
+
);
|
|
5397
|
+
const resolvedSummary = keepResult.summary;
|
|
5398
|
+
const storedSummary = wrapCompressedSummary(blockId, resolvedSummary);
|
|
5267
5399
|
const summaryTokens = countTokens2(storedSummary);
|
|
5268
5400
|
applyCompressionState(
|
|
5269
5401
|
ctx.state,
|
|
@@ -5288,7 +5420,7 @@ function createCompressMessageTool(ctx) {
|
|
|
5288
5420
|
notifications.push({
|
|
5289
5421
|
blockId,
|
|
5290
5422
|
runId,
|
|
5291
|
-
summary:
|
|
5423
|
+
summary: resolvedSummary,
|
|
5292
5424
|
summaryTokens
|
|
5293
5425
|
});
|
|
5294
5426
|
}
|
|
@@ -5300,113 +5432,6 @@ function createCompressMessageTool(ctx) {
|
|
|
5300
5432
|
|
|
5301
5433
|
// lib/compress/range.ts
|
|
5302
5434
|
import { tool as tool3 } from "@opencode-ai/plugin";
|
|
5303
|
-
|
|
5304
|
-
// lib/compress/keep-markers.ts
|
|
5305
|
-
var KEEP_REGEX = /\[\[KEEP:(m\d+)\]\]/g;
|
|
5306
|
-
var REF_REGEX = /\[\[REF:(m\d+)\|([^\]]+)\]\]/g;
|
|
5307
|
-
function resolveKeepMarkers(summary, messages, state, config) {
|
|
5308
|
-
const msgByRef = /* @__PURE__ */ new Map();
|
|
5309
|
-
for (const msg of messages) {
|
|
5310
|
-
const ref = state.messageIds.byRawId.get(msg.info.id);
|
|
5311
|
-
if (ref) msgByRef.set(ref, msg);
|
|
5312
|
-
}
|
|
5313
|
-
const maxChars = config.compress?.keepEmbedMaxChars ?? 2e3;
|
|
5314
|
-
let expandedCount = 0;
|
|
5315
|
-
let refCount = 0;
|
|
5316
|
-
const unresolvedRefs = [];
|
|
5317
|
-
const expanded = summary.replace(KEEP_REGEX, (match, ref) => {
|
|
5318
|
-
const msg = msgByRef.get(ref);
|
|
5319
|
-
if (!msg) {
|
|
5320
|
-
unresolvedRefs.push(ref);
|
|
5321
|
-
return match;
|
|
5322
|
-
}
|
|
5323
|
-
expandedCount++;
|
|
5324
|
-
return formatKeptMessage(msg, ref, maxChars);
|
|
5325
|
-
}).replace(REF_REGEX, (_match, ref, desc) => {
|
|
5326
|
-
const msg = msgByRef.get(ref);
|
|
5327
|
-
if (!msg) {
|
|
5328
|
-
unresolvedRefs.push(ref);
|
|
5329
|
-
return _match;
|
|
5330
|
-
}
|
|
5331
|
-
refCount++;
|
|
5332
|
-
return `[\u2192 ${ref}: ${desc.trim()}]`;
|
|
5333
|
-
});
|
|
5334
|
-
return { summary: expanded, expandedCount, refCount, unresolvedRefs };
|
|
5335
|
-
}
|
|
5336
|
-
function formatKeptMessage(msg, ref, maxChars) {
|
|
5337
|
-
const formatted = formatByType(msg);
|
|
5338
|
-
const truncated = truncate2(formatted, maxChars);
|
|
5339
|
-
return `
|
|
5340
|
-
--- [${ref}: ${labelForMessage(msg)}] ---
|
|
5341
|
-
${truncated}
|
|
5342
|
-
--- end ---
|
|
5343
|
-
`;
|
|
5344
|
-
}
|
|
5345
|
-
function formatByType(msg) {
|
|
5346
|
-
for (const part of msg.parts || []) {
|
|
5347
|
-
if (part.type === "text" && typeof part.text === "string") {
|
|
5348
|
-
return part.text;
|
|
5349
|
-
}
|
|
5350
|
-
if (part.type === "tool") {
|
|
5351
|
-
const tool7 = part.tool || "unknown";
|
|
5352
|
-
const state = part.state || {};
|
|
5353
|
-
const input = state.input || {};
|
|
5354
|
-
const output = state.output || "";
|
|
5355
|
-
switch (tool7) {
|
|
5356
|
-
case "bash":
|
|
5357
|
-
case "interactive_bash": {
|
|
5358
|
-
const cmd = typeof input === "string" ? input : input.command || JSON.stringify(input);
|
|
5359
|
-
return `$ ${cmd}
|
|
5360
|
-
${output}`;
|
|
5361
|
-
}
|
|
5362
|
-
case "read": {
|
|
5363
|
-
const fp = input.filePath || input.path || input.file || "";
|
|
5364
|
-
return output;
|
|
5365
|
-
}
|
|
5366
|
-
case "write":
|
|
5367
|
-
case "edit": {
|
|
5368
|
-
const fp = input.filePath || input.path || "";
|
|
5369
|
-
const content = input.content || input.newString || "";
|
|
5370
|
-
return `${fp}:
|
|
5371
|
-
${content}`;
|
|
5372
|
-
}
|
|
5373
|
-
case "reply": {
|
|
5374
|
-
return output || "[reply posted]";
|
|
5375
|
-
}
|
|
5376
|
-
case "grep":
|
|
5377
|
-
case "glob": {
|
|
5378
|
-
return output;
|
|
5379
|
-
}
|
|
5380
|
-
default: {
|
|
5381
|
-
if (output && typeof output === "string" && output.length > 0) {
|
|
5382
|
-
return output;
|
|
5383
|
-
}
|
|
5384
|
-
const compact = JSON.stringify({ tool: tool7, input }, null, 0);
|
|
5385
|
-
return compact.length > 500 ? compact.slice(0, 500) + "..." : compact;
|
|
5386
|
-
}
|
|
5387
|
-
}
|
|
5388
|
-
}
|
|
5389
|
-
}
|
|
5390
|
-
return "[empty message]";
|
|
5391
|
-
}
|
|
5392
|
-
function labelForMessage(msg) {
|
|
5393
|
-
for (const part of msg.parts || []) {
|
|
5394
|
-
if (part.type === "tool") {
|
|
5395
|
-
const tool7 = part.tool || "unknown";
|
|
5396
|
-
const input = part.state?.input || {};
|
|
5397
|
-
const fp = input.filePath || input.path || input.command || "";
|
|
5398
|
-
return fp ? `${tool7}: ${String(fp).slice(0, 60)}` : tool7;
|
|
5399
|
-
}
|
|
5400
|
-
}
|
|
5401
|
-
return msg.info.role === "user" ? "user" : "text";
|
|
5402
|
-
}
|
|
5403
|
-
function truncate2(text, maxChars) {
|
|
5404
|
-
if (text.length <= maxChars) return text;
|
|
5405
|
-
return text.slice(0, maxChars) + `
|
|
5406
|
-
... [truncated, ${text.length} chars total]`;
|
|
5407
|
-
}
|
|
5408
|
-
|
|
5409
|
-
// lib/compress/range.ts
|
|
5410
5435
|
function buildSchema2(maxSummaryLengthHard) {
|
|
5411
5436
|
return {
|
|
5412
5437
|
topic: tool3.schema.string().describe("Short label (3-5 words) for display - e.g., 'Auth System Exploration'"),
|
|
@@ -5934,6 +5959,40 @@ var filterCompressedRanges = (state, logger, config, messages) => {
|
|
|
5934
5959
|
messages.length = 0;
|
|
5935
5960
|
messages.push(...result);
|
|
5936
5961
|
};
|
|
5962
|
+
function stripStaleCompressCalls(messages) {
|
|
5963
|
+
const lastUserIdx = messages.findLastIndex(
|
|
5964
|
+
(m) => m.info.role === "user" && !isIgnoredUserMessage(m)
|
|
5965
|
+
);
|
|
5966
|
+
if (lastUserIdx < 0) return 0;
|
|
5967
|
+
let stripped = 0;
|
|
5968
|
+
const result = [];
|
|
5969
|
+
for (let i = 0; i < messages.length; i++) {
|
|
5970
|
+
const msg = messages[i];
|
|
5971
|
+
if (i >= lastUserIdx) {
|
|
5972
|
+
result.push(msg);
|
|
5973
|
+
continue;
|
|
5974
|
+
}
|
|
5975
|
+
const hasCompress = msg.parts.some(
|
|
5976
|
+
(p) => p.type === "tool" && p.tool === "compress"
|
|
5977
|
+
);
|
|
5978
|
+
if (!hasCompress) {
|
|
5979
|
+
result.push(msg);
|
|
5980
|
+
continue;
|
|
5981
|
+
}
|
|
5982
|
+
const remaining = msg.parts.filter(
|
|
5983
|
+
(p) => !(p.type === "tool" && p.tool === "compress")
|
|
5984
|
+
);
|
|
5985
|
+
stripped++;
|
|
5986
|
+
if (remaining.length > 0) {
|
|
5987
|
+
result.push({ ...msg, parts: remaining });
|
|
5988
|
+
}
|
|
5989
|
+
}
|
|
5990
|
+
if (stripped > 0) {
|
|
5991
|
+
messages.length = 0;
|
|
5992
|
+
messages.push(...result);
|
|
5993
|
+
}
|
|
5994
|
+
return stripped;
|
|
5995
|
+
}
|
|
5937
5996
|
|
|
5938
5997
|
// lib/messages/sync.ts
|
|
5939
5998
|
function sortBlocksByCreation(a, b) {
|
|
@@ -6082,8 +6141,8 @@ var resolveEffectiveCompressPermission = (basePermission, hostPermissions, agent
|
|
|
6082
6141
|
agentName ? hostPermissions.agents[agentName] : void 0
|
|
6083
6142
|
) ? "deny" : basePermission;
|
|
6084
6143
|
};
|
|
6085
|
-
var hasExplicitToolPermission = (permissionConfig,
|
|
6086
|
-
return permissionConfig ? Object.prototype.hasOwnProperty.call(permissionConfig,
|
|
6144
|
+
var hasExplicitToolPermission = (permissionConfig, tool8) => {
|
|
6145
|
+
return permissionConfig ? Object.prototype.hasOwnProperty.call(permissionConfig, tool8) : false;
|
|
6087
6146
|
};
|
|
6088
6147
|
|
|
6089
6148
|
// lib/compress-permission.ts
|
|
@@ -6660,7 +6719,7 @@ function estimateContextComposition(messages, state) {
|
|
|
6660
6719
|
perTool.sort((a, b) => b.tokens - a.tokens);
|
|
6661
6720
|
perCode.sort((a, b) => b.tokens - a.tokens);
|
|
6662
6721
|
perText.sort((a, b) => b.tokens - a.tokens);
|
|
6663
|
-
const toolTypeBreakdown = Array.from(toolTypeMap.entries()).map(([
|
|
6722
|
+
const toolTypeBreakdown = Array.from(toolTypeMap.entries()).map(([tool8, tokens]) => ({ tool: tool8, tokens })).sort((a, b) => b.tokens - a.tokens);
|
|
6664
6723
|
return {
|
|
6665
6724
|
toolTokens,
|
|
6666
6725
|
codeTokens,
|
|
@@ -6795,7 +6854,7 @@ function createSuffixMessage(messages) {
|
|
|
6795
6854
|
messages.push(synthetic);
|
|
6796
6855
|
return synthetic;
|
|
6797
6856
|
}
|
|
6798
|
-
var injectCompressNudges = (state, config, logger, messages, prompts, compressionPriorities, debugNotify) => {
|
|
6857
|
+
var injectCompressNudges = (state, config, logger, messages, prompts, compressionPriorities, debugNotify, preCompressTokens) => {
|
|
6799
6858
|
if (compressPermission(state, config) === "deny") {
|
|
6800
6859
|
return;
|
|
6801
6860
|
}
|
|
@@ -6818,13 +6877,30 @@ var injectCompressNudges = (state, config, logger, messages, prompts, compressio
|
|
|
6818
6877
|
const currentTurnStart = lastUserIdx >= 0 ? lastUserIdx + 1 : 0;
|
|
6819
6878
|
const currentTurnHasCompress = messages.slice(currentTurnStart).some((m) => m.info.role === "assistant" && messageHasCompress(m));
|
|
6820
6879
|
if (currentTurnHasCompress) {
|
|
6880
|
+
const wasNudgeTriggered = state.nudges.lastNudgeShownTokens !== void 0;
|
|
6821
6881
|
state.nudges.contextLimitAnchors.clear();
|
|
6822
6882
|
state.nudges.turnNudgeAnchors.clear();
|
|
6823
6883
|
state.nudges.iterationNudgeAnchors.clear();
|
|
6824
6884
|
state.nudges.lastNudgeShownTokens = void 0;
|
|
6825
6885
|
state.nudges.lastToolOutputNudgeTokens = void 0;
|
|
6826
|
-
if (!state.nudges.compressBaselineSet) {
|
|
6827
|
-
state.nudges.lastPerMessageNudgeTokens
|
|
6886
|
+
if (wasNudgeTriggered && !state.nudges.compressBaselineSet) {
|
|
6887
|
+
const baseline = state.nudges.lastPerMessageNudgeTokens;
|
|
6888
|
+
const postCompress = currentTokens;
|
|
6889
|
+
const preCompress = preCompressTokens;
|
|
6890
|
+
if (baseline !== void 0 && postCompress !== void 0 && preCompress !== void 0 && preCompress > postCompress) {
|
|
6891
|
+
const growth = preCompress - baseline;
|
|
6892
|
+
const compressed = preCompress - postCompress;
|
|
6893
|
+
if (growth > 0 && compressed > 0) {
|
|
6894
|
+
const ratio = Math.min(1, compressed / growth);
|
|
6895
|
+
const adjustment = Math.min(1, ratio * 2);
|
|
6896
|
+
const newBaseline = baseline + Math.round((postCompress - baseline) * adjustment);
|
|
6897
|
+
state.nudges.lastPerMessageNudgeTokens = newBaseline;
|
|
6898
|
+
} else {
|
|
6899
|
+
state.nudges.lastPerMessageNudgeTokens = postCompress;
|
|
6900
|
+
}
|
|
6901
|
+
} else {
|
|
6902
|
+
state.nudges.lastPerMessageNudgeTokens = postCompress;
|
|
6903
|
+
}
|
|
6828
6904
|
state.nudges.compressBaselineSet = true;
|
|
6829
6905
|
}
|
|
6830
6906
|
saveSessionState(state, logger).catch(() => {
|
|
@@ -6938,7 +7014,11 @@ Breakdown: ${fmt(composition.toolTokens)} tool (${pct2(composition.toolTokens)}%
|
|
|
6938
7014
|
breakdown += `
|
|
6939
7015
|
|
|
6940
7016
|
${formatCompressibleRanges(ranges)}`;
|
|
7017
|
+
breakdown += `
|
|
7018
|
+
\u{1F4A1} Compress all ranges in one call (pass multiple content entries: \`content: [{...}, {...}]\`).`;
|
|
6941
7019
|
}
|
|
7020
|
+
breakdown += `
|
|
7021
|
+
Use \`acp_status({scope:"uncompressed"})\` to re-fetch compressible ranges after compressing, or \`acp_status\` for compressed block details.`;
|
|
6942
7022
|
if (decision.tipsVariant !== "maxLimit") {
|
|
6943
7023
|
breakdown += `
|
|
6944
7024
|
|
|
@@ -6967,7 +7047,6 @@ ${HOW_TO_COMPRESS_RULES}`;
|
|
|
6967
7047
|
if (tipsText && suffixMessage) {
|
|
6968
7048
|
appendToLastTextPart(suffixMessage, tipsText);
|
|
6969
7049
|
}
|
|
6970
|
-
injectVisibleIdRange(state, config, messages, suffixMessage);
|
|
6971
7050
|
}
|
|
6972
7051
|
if (suffixMessage) {
|
|
6973
7052
|
if (hasContent(suffixMessage)) {
|
|
@@ -7003,86 +7082,6 @@ function injectContextUsage(target, config, currentTokens, modelContextLimit) {
|
|
|
7003
7082
|
}
|
|
7004
7083
|
target.parts.push(createSyntheticTextPart(target, usageTag));
|
|
7005
7084
|
}
|
|
7006
|
-
function refNumber(ref) {
|
|
7007
|
-
const n = parseInt(ref.slice(1), 10);
|
|
7008
|
-
return Number.isNaN(n) ? -1 : n;
|
|
7009
|
-
}
|
|
7010
|
-
function buildVisibleSegments(state, messages) {
|
|
7011
|
-
const refInfo = /* @__PURE__ */ new Map();
|
|
7012
|
-
for (const msg of messages) {
|
|
7013
|
-
const ref = state.messageIds.byRawId.get(msg.info.id);
|
|
7014
|
-
if (!ref) continue;
|
|
7015
|
-
let tokens = 0;
|
|
7016
|
-
let hasTool = false;
|
|
7017
|
-
for (const part of msg.parts || []) {
|
|
7018
|
-
if (part.type === "text" && typeof part.text === "string") {
|
|
7019
|
-
tokens += Math.round(part.text.length / 4);
|
|
7020
|
-
} else if (part.type !== "text" && part.type !== "reasoning") {
|
|
7021
|
-
tokens += Math.round(JSON.stringify(part).length / 4);
|
|
7022
|
-
hasTool = true;
|
|
7023
|
-
}
|
|
7024
|
-
}
|
|
7025
|
-
refInfo.set(ref, { tokens, hasTool });
|
|
7026
|
-
}
|
|
7027
|
-
if (refInfo.size === 0) return [];
|
|
7028
|
-
const refs = Array.from(refInfo.keys()).sort((a, b) => refNumber(a) - refNumber(b));
|
|
7029
|
-
const segments = [];
|
|
7030
|
-
let cur = null;
|
|
7031
|
-
let prevNum = -2;
|
|
7032
|
-
for (const ref of refs) {
|
|
7033
|
-
const num = refNumber(ref);
|
|
7034
|
-
const info = refInfo.get(ref);
|
|
7035
|
-
if (cur && num === prevNum + 1) {
|
|
7036
|
-
cur.endRef = ref;
|
|
7037
|
-
cur.count++;
|
|
7038
|
-
cur.tokens += info.tokens;
|
|
7039
|
-
if (info.hasTool) cur.hasTool = true;
|
|
7040
|
-
} else {
|
|
7041
|
-
if (cur) segments.push(cur);
|
|
7042
|
-
cur = { startRef: ref, endRef: ref, count: 1, tokens: info.tokens, hasTool: info.hasTool };
|
|
7043
|
-
}
|
|
7044
|
-
prevNum = num;
|
|
7045
|
-
}
|
|
7046
|
-
if (cur) segments.push(cur);
|
|
7047
|
-
return segments;
|
|
7048
|
-
}
|
|
7049
|
-
function formatSegment(seg) {
|
|
7050
|
-
return seg.startRef === seg.endRef ? seg.startRef : `${seg.startRef}\u2013${seg.endRef}`;
|
|
7051
|
-
}
|
|
7052
|
-
function formatVisibleGuidance(segments, maxSegs) {
|
|
7053
|
-
if (segments.length === 0) return "";
|
|
7054
|
-
const totalMsgs = segments.reduce((s, seg) => s + seg.count, 0);
|
|
7055
|
-
const totalSegs = segments.length;
|
|
7056
|
-
const fmt = (n) => n >= 1e3 ? `${(n / 1e3).toFixed(1)}K` : String(n);
|
|
7057
|
-
if (totalSegs <= maxSegs) {
|
|
7058
|
-
return `[Visible: ${segments.map(formatSegment).join(", ")} (${totalMsgs} msg${totalMsgs === 1 ? "" : "s"}, ${totalSegs} segment${totalSegs === 1 ? "" : "s"})]`;
|
|
7059
|
-
}
|
|
7060
|
-
const keepSet = new Set(
|
|
7061
|
-
[...segments].sort((a, b) => {
|
|
7062
|
-
if (a.hasTool !== b.hasTool) return a.hasTool ? -1 : 1;
|
|
7063
|
-
return b.tokens - a.tokens;
|
|
7064
|
-
}).slice(0, maxSegs)
|
|
7065
|
-
);
|
|
7066
|
-
const shown = segments.filter((s) => keepSet.has(s));
|
|
7067
|
-
const omitted = segments.filter((s) => !keepSet.has(s));
|
|
7068
|
-
const omittedTokens = omitted.reduce((sum, s) => sum + s.tokens, 0);
|
|
7069
|
-
const omittedMsgs = omitted.reduce((sum, s) => sum + s.count, 0);
|
|
7070
|
-
return `[Visible (top ${shown.length} of ${totalSegs} segments, ${totalMsgs} msgs): ${shown.map(formatSegment).join(", ")} | +${omitted.length} smaller segment${omitted.length === 1 ? "" : "s"} (~${fmt(omittedTokens)} tokens, ${omittedMsgs} msg${omittedMsgs === 1 ? "" : "s"}) omitted]`;
|
|
7071
|
-
}
|
|
7072
|
-
function injectVisibleIdRange(state, config, messages, target) {
|
|
7073
|
-
if (!target) return;
|
|
7074
|
-
const segments = buildVisibleSegments(state, messages);
|
|
7075
|
-
if (segments.length === 0) return;
|
|
7076
|
-
const maxSegs = config.compress?.maxVisibleSegments ?? 50;
|
|
7077
|
-
const rangeTag = "\n\n" + formatVisibleGuidance(segments, maxSegs);
|
|
7078
|
-
for (const part of target.parts) {
|
|
7079
|
-
if (part.type === "text") {
|
|
7080
|
-
appendToTextPart(part, rangeTag);
|
|
7081
|
-
return;
|
|
7082
|
-
}
|
|
7083
|
-
}
|
|
7084
|
-
target.parts.push(createSyntheticTextPart(target, rangeTag));
|
|
7085
|
-
}
|
|
7086
7085
|
var injectMessageIds = (state, config, messages, compressionPriorities) => {
|
|
7087
7086
|
if (compressPermission(state, config) === "deny") {
|
|
7088
7087
|
return;
|
|
@@ -7705,7 +7704,7 @@ function renderOverview(visibleMessages, summaryTokens, blocks, fetchFailed, raw
|
|
|
7705
7704
|
lines.push(
|
|
7706
7705
|
` ${formatTokens(total)} total | ${formatTokens(totalTool)} tool (${toolPct}%) | ${formatTokens(totalText)} text (${textPct}%) | ${formatTokens(summaryTokens)} summaries (${summaryPct}%)`
|
|
7707
7706
|
);
|
|
7708
|
-
const topTypes = Array.from(toolTypeMap.entries()).map(([
|
|
7707
|
+
const topTypes = Array.from(toolTypeMap.entries()).map(([tool8, tokens]) => ({ tool: tool8, tokens })).sort((a, b) => b.tokens - a.tokens).slice(0, 3);
|
|
7709
7708
|
if (topTypes.length > 0) {
|
|
7710
7709
|
lines.push(` Top tools: ${topTypes.map((t) => `${t.tool} (${pct(t.tokens, total)}%)`).join(", ")}`);
|
|
7711
7710
|
}
|
|
@@ -7897,8 +7896,67 @@ function createAcpStatusTool(ctx) {
|
|
|
7897
7896
|
});
|
|
7898
7897
|
}
|
|
7899
7898
|
|
|
7900
|
-
// lib/compress/
|
|
7899
|
+
// lib/compress/recap.ts
|
|
7901
7900
|
import { tool as tool6 } from "@opencode-ai/plugin";
|
|
7901
|
+
function formatRange(startId, endId) {
|
|
7902
|
+
const start = (startId || "").trim();
|
|
7903
|
+
const end = (endId || "").trim();
|
|
7904
|
+
if (!start || !end) return "\u2014";
|
|
7905
|
+
if (start === end) return start;
|
|
7906
|
+
return `${start}\u2013${end}`;
|
|
7907
|
+
}
|
|
7908
|
+
var RECAP_TOOL_DESCRIPTION = `Read-only retrieval of compression block summaries.
|
|
7909
|
+
|
|
7910
|
+
This tool is primarily system-managed: ACP automatically injects compression summaries into context via this tool's result format. You can also call it directly to re-fetch a specific block's summary without decompressing the full original content.
|
|
7911
|
+
|
|
7912
|
+
Args:
|
|
7913
|
+
- blockId: optional block number (e.g., 5). If omitted, lists all active blocks with brief info.`;
|
|
7914
|
+
function createAcpContextRecapTool(ctx) {
|
|
7915
|
+
return tool6({
|
|
7916
|
+
description: RECAP_TOOL_DESCRIPTION,
|
|
7917
|
+
args: {
|
|
7918
|
+
blockId: tool6.schema.number().optional().describe("Block number to retrieve (e.g., 5). If omitted, lists all active blocks.")
|
|
7919
|
+
},
|
|
7920
|
+
async execute(args) {
|
|
7921
|
+
const msgState = ctx.state.prune.messages;
|
|
7922
|
+
const activeIds = Array.from(msgState.activeBlockIds).sort((a, b) => a - b);
|
|
7923
|
+
if (activeIds.length === 0) {
|
|
7924
|
+
return "No active compression blocks.";
|
|
7925
|
+
}
|
|
7926
|
+
if (args.blockId !== void 0) {
|
|
7927
|
+
const block = msgState.blocksById.get(args.blockId);
|
|
7928
|
+
if (!block) {
|
|
7929
|
+
return `Block b${args.blockId} not found. Active blocks: ${activeIds.map((id) => `b${id}`).join(", ")}`;
|
|
7930
|
+
}
|
|
7931
|
+
if (!block.active) {
|
|
7932
|
+
return `Block b${args.blockId} is inactive (deactivated by GC or nested compression).`;
|
|
7933
|
+
}
|
|
7934
|
+
const range = formatRange(block.startId, block.endId);
|
|
7935
|
+
return `[Compressed conversation section]
|
|
7936
|
+
${block.summary}
|
|
7937
|
+
|
|
7938
|
+
[Block b${args.blockId} | ${range} | topic: "${block.topic || "(none)"}"]`;
|
|
7939
|
+
}
|
|
7940
|
+
const lines = [];
|
|
7941
|
+
lines.push(`Active compression blocks (${activeIds.length}):`);
|
|
7942
|
+
for (const id of activeIds) {
|
|
7943
|
+
const block = msgState.blocksById.get(id);
|
|
7944
|
+
if (!block || !block.active) continue;
|
|
7945
|
+
const range = formatRange(block.startId, block.endId);
|
|
7946
|
+
const summaryPreview = block.summary.slice(0, 200);
|
|
7947
|
+
lines.push(`
|
|
7948
|
+
b${id} | ${range} | "${block.topic || "(none)"}"`);
|
|
7949
|
+
lines.push(` ${summaryPreview}${block.summary.length > 200 ? "..." : ""}`);
|
|
7950
|
+
}
|
|
7951
|
+
lines.push(`
|
|
7952
|
+
Call with blockId to get the full summary: acp_context_recap({ blockId: N })`);
|
|
7953
|
+
return lines.join("\n");
|
|
7954
|
+
}
|
|
7955
|
+
});
|
|
7956
|
+
}
|
|
7957
|
+
|
|
7958
|
+
// lib/compress/prune-tool.ts
|
|
7959
|
+
import { tool as tool7 } from "@opencode-ai/plugin";
|
|
7902
7960
|
var PRUNE_TOOL_DESCRIPTION = `Remove old tool outputs by tool type \u2014 frees context without compression.
|
|
7903
7961
|
|
|
7904
7962
|
Unlike compress (which creates summaries), prune directly strips tool call outputs from context. Use for disposable tool outputs where the content is no longer needed: old todowrite states, edit success echoes, repeated status checks.
|
|
@@ -7908,11 +7966,11 @@ Args:
|
|
|
7908
7966
|
- keepLatest: how many recent calls to keep visible (default 3)`;
|
|
7909
7967
|
function createPruneTool(ctx) {
|
|
7910
7968
|
ctx.prompts.reload();
|
|
7911
|
-
return
|
|
7969
|
+
return tool7({
|
|
7912
7970
|
description: PRUNE_TOOL_DESCRIPTION,
|
|
7913
7971
|
args: {
|
|
7914
|
-
toolType:
|
|
7915
|
-
keepLatest:
|
|
7972
|
+
toolType: tool7.schema.string().describe('Tool name to prune (e.g., "todowrite", "bash", "edit")'),
|
|
7973
|
+
keepLatest: tool7.schema.number().optional().describe("How many recent calls to keep visible (default 3)")
|
|
7916
7974
|
},
|
|
7917
7975
|
async execute(args, toolCtx) {
|
|
7918
7976
|
const keepLatest = args.keepLatest ?? 3;
|
|
@@ -9199,7 +9257,7 @@ var COMPRESS_TRIGGER_PROMPT = [
|
|
|
9199
9257
|
"Follow the active compress mode, preserve all critical implementation details, and choose safe targets.",
|
|
9200
9258
|
"Return after compress with a brief explanation of what content was compressed."
|
|
9201
9259
|
].join("\n\n");
|
|
9202
|
-
function getTriggerPrompt(
|
|
9260
|
+
function getTriggerPrompt(tool8, state, config, userFocus) {
|
|
9203
9261
|
const base = COMPRESS_TRIGGER_PROMPT;
|
|
9204
9262
|
const compressedBlockGuidance = config.compress.mode === "message" ? "" : buildCompressedBlockGuidance(state, config.gc);
|
|
9205
9263
|
const sections = [base, compressedBlockGuidance];
|
|
@@ -9228,8 +9286,8 @@ async function handleManualToggleCommand(ctx, modeArg) {
|
|
|
9228
9286
|
);
|
|
9229
9287
|
logger.info("Manual mode toggled", { manualMode: state.manualMode });
|
|
9230
9288
|
}
|
|
9231
|
-
async function handleManualTriggerCommand(ctx,
|
|
9232
|
-
return getTriggerPrompt(
|
|
9289
|
+
async function handleManualTriggerCommand(ctx, tool8, userFocus) {
|
|
9290
|
+
return getTriggerPrompt(tool8, ctx.state, ctx.config, userFocus);
|
|
9233
9291
|
}
|
|
9234
9292
|
function applyPendingManualTrigger(state, messages, logger) {
|
|
9235
9293
|
const pending = state.pendingManualTrigger;
|
|
@@ -10075,7 +10133,9 @@ function createChatMessageTransformHandler(client, state, logger, config, prompt
|
|
|
10075
10133
|
saveSessionState(state, logger).catch(() => {
|
|
10076
10134
|
});
|
|
10077
10135
|
}
|
|
10136
|
+
const prePruneTokens = getCurrentTokenUsage(state, output.messages);
|
|
10078
10137
|
prune(state, logger, config, output.messages);
|
|
10138
|
+
stripStaleCompressCalls(output.messages);
|
|
10079
10139
|
assignMessageRefs(state, output.messages);
|
|
10080
10140
|
await injectExtendedSubAgentResults(
|
|
10081
10141
|
client,
|
|
@@ -10103,7 +10163,8 @@ ${text}`,
|
|
|
10103
10163
|
logger
|
|
10104
10164
|
).catch(() => {
|
|
10105
10165
|
});
|
|
10106
|
-
} : void 0
|
|
10166
|
+
} : void 0,
|
|
10167
|
+
prePruneTokens
|
|
10107
10168
|
);
|
|
10108
10169
|
injectMessageIds(state, config, output.messages, compressionPriorities);
|
|
10109
10170
|
applyPendingManualTrigger(state, output.messages, logger);
|
|
@@ -10509,7 +10570,8 @@ var server = (async (ctx) => {
|
|
|
10509
10570
|
decompress: createDecompressTool(compressToolContext),
|
|
10510
10571
|
prune: createPruneTool(compressToolContext),
|
|
10511
10572
|
search_context: createSearchContextTool(compressToolContext),
|
|
10512
|
-
acp_status: createAcpStatusTool(compressToolContext)
|
|
10573
|
+
acp_status: createAcpStatusTool(compressToolContext),
|
|
10574
|
+
acp_context_recap: createAcpContextRecapTool(compressToolContext)
|
|
10513
10575
|
}
|
|
10514
10576
|
},
|
|
10515
10577
|
config: async (opencodeConfig) => {
|