opencode-acp 1.14.1 → 1.14.3
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 +16 -0
- package/README.zh-CN.md +16 -0
- package/dist/index.js +151 -111
- package/dist/index.js.map +1 -1
- package/dist/lib/compress/message.d.ts.map +1 -1
- package/dist/lib/compress/pipeline.d.ts +9 -7
- package/dist/lib/compress/pipeline.d.ts.map +1 -1
- package/dist/lib/compress/protected-content.d.ts +3 -0
- package/dist/lib/compress/protected-content.d.ts.map +1 -1
- package/dist/lib/compress/range.d.ts.map +1 -1
- package/dist/lib/compress/status.d.ts.map +1 -1
- package/dist/lib/messages/inject/inject.d.ts.map +1 -1
- package/dist/lib/messages/inject/utils.d.ts +9 -5
- package/dist/lib/messages/inject/utils.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -528,6 +528,22 @@ For the complete list with root cause analysis, see the [bug tracker](https://gi
|
|
|
528
528
|
|
|
529
529
|
## Changelog
|
|
530
530
|
|
|
531
|
+
### v1.14.3 — Soften Protected Zone + Reduce Defaults (PR #212)
|
|
532
|
+
|
|
533
|
+
**Problem**: `checkProtectedRange` hard-rejected any compress call covering protected recent messages (last N messages + last N tokens). The model got an error and had to retry with a different range or use `dangerous: true`. Additionally, the default `preserveRecentMessages: 20` and `preserveRecentTokens: 20000` (≈40 messages) were too aggressive — protecting nearly half the conversation in autonomous sessions.
|
|
534
|
+
|
|
535
|
+
**Fix**: (1) Converted `checkProtectedRange` hard-reject to `filterProtectedRecentMessages` soft-filter — protected messages are filtered from the compress plan (same pattern as `filterLastUserMessage` and `filterProtectedToolMessages`), non-protected messages compress normally. Compress always succeeds unless ALL messages are protected. (2) Reduced `preserveRecentMessages` default 20 → 5 and `preserveRecentTokens` default 20000 → 5000. (3) `dangerous` parameter is now a no-op (no hard-reject to bypass; stays in schema for backward compat). 922 tests pass.
|
|
536
|
+
|
|
537
|
+
Files: `lib/config.ts`, `lib/compress/{protected-content,pipeline,range,message}.ts`, `lib/messages/inject/utils.ts`. Tests: `tests/soft-block.test.ts`. No persisted-state schema changes. Config defaults changed; existing configs with explicit values are unaffected.
|
|
538
|
+
|
|
539
|
+
### v1.14.2 — Split Protected Ranges + Soften Last-User-Message (PR #210)
|
|
540
|
+
|
|
541
|
+
**Problem**: In autonomous agentic sessions (1 user message + many assistant/tool messages), `buildCompressibleRanges` created one giant compressible group because grouping only breaks on user messages — and tool results are `assistant` role in OpenCode. The giant group's endRef fell in the protected zone → `excludeProtectedRanges` removed the entire range → zero recommendations → nudge suppressed → model could never compress. Additionally, `preserveLastUserMessage` hard-rejected any compress call covering the last user message, blocking deliberate compressions of surrounding tool output.
|
|
542
|
+
|
|
543
|
+
**Fix**: (1) `buildCompressibleRanges` now accepts `protectedZoneRefs` and splits groups at the protected-zone boundary — the unprotected head survives as a recommended range while the protected tail is excluded. (2) `preserveLastUserMessage` moved from hard-reject (`checkProtectedRange` throw) to soft-filter (`filterLastUserMessage` excludes from compress plan, following Bug 39's `filterProtectedToolMessages` pattern). The last user message survives in visible context; surrounding tool output compresses normally. (3) Added `allInProtectedZone` condition to `nothingToCompress` to cover the case where all messages are in the protected zone. (4) Fixed stale error messages and added empty-plan guard in both range and message modes. Dual-agent reviewed (both APPROVE with minor fixes; all WARNINGs addressed in follow-up commit). 922 tests pass.
|
|
544
|
+
|
|
545
|
+
Files: `lib/messages/inject/{utils,inject}.ts`, `lib/compress/{pipeline,protected-content,range,message,status}.ts`. Tests: `tests/{preserve-recent,soft-block,protected-tool-exclusion}.test.ts`. No persisted-state schema changes. `preserveLastUserMessage` config semantics changed from hard-reject to soft-filter (intentional fix; `lastSegmentSoftBlock: false` disables all protection including the new soft filter).
|
|
546
|
+
|
|
531
547
|
### v1.14.1 — Log Version Info + Growth Baseline Fix (PRs #205, #207)
|
|
532
548
|
|
|
533
549
|
**Problem**: Two issues since v1.14.0. (1) **Unidentifiable logs** (PR #205): ACP daily logs and per-request context logs carried no version marker, so when users shared debug logs it was impossible to tell which ACP version produced them — making regression triage across versions guesswork. (2) **Growth-baseline feedback loop in short sessions** (PR #207): When the growth threshold was met (`nudgeAllowed`) but every compressible range was filtered out (`nothingToCompress`, e.g. all ranges inside the protected zone in a short session or subagent), the code reset `state.nudges.lastPerMessageNudgeTokens = currentTokens`. This ate the accumulated growth every cycle, so the baseline chased the current context and the model never saw a nudge even though context had grown well past the threshold.
|
package/README.zh-CN.md
CHANGED
|
@@ -482,6 +482,22 @@ ACP 在首次启动时自动将配置从 `dcp.jsonc` 迁移到 `acp.jsonc`,将
|
|
|
482
482
|
|
|
483
483
|
## 更新日志
|
|
484
484
|
|
|
485
|
+
### v1.14.3 — 软化保护区 + 减小默认值(PR #212)
|
|
486
|
+
|
|
487
|
+
**问题**:`checkProtectedRange` 硬拒绝任何覆盖保护区最近消息的压缩调用(最后 N 条消息 + 最后 N tokens)。模型收到错误后必须换范围重试或使用 `dangerous: true`。此外,默认 `preserveRecentMessages: 20` 和 `preserveRecentTokens: 20000`(约 40 条消息)过于激进 —— 在自主会话中保护了近一半的对话。
|
|
488
|
+
|
|
489
|
+
**修复**:(1) 将 `checkProtectedRange` 硬拒绝转为 `filterProtectedRecentMessages` 软过滤 —— 保护区消息从压缩计划中过滤掉(与 `filterLastUserMessage` 和 `filterProtectedToolMessages` 同模式),非保护区消息正常压缩。除非所有消息都在保护区内,压缩总能成功。(2) 减小 `preserveRecentMessages` 默认值 20 → 5,`preserveRecentTokens` 默认值 20000 → 5000。(3) `dangerous` 参数现在无实际效果(没有硬拒绝了,不需要 bypass;保留在 schema 中向后兼容)。922 项测试通过。
|
|
490
|
+
|
|
491
|
+
文件:`lib/config.ts`、`lib/compress/{protected-content,pipeline,range,message}.ts`、`lib/messages/inject/utils.ts`。测试:`tests/soft-block.test.ts`。无持久化状态 schema 变更。配置默认值改变;已设置显式值的配置不受影响。
|
|
492
|
+
|
|
493
|
+
### v1.14.2 — 拆分保护区范围 + 软化最后用户消息保护(PR #210)
|
|
494
|
+
|
|
495
|
+
**问题**:在自主代理会话中(1 条用户消息 + 多条 assistant/tool 消息),`buildCompressibleRanges` 创建了一个巨型可压缩组,因为分组只在用户消息处断开 —— 而 OpenCode 中 tool 结果是 `assistant` 角色。巨型组的 endRef 落在保护区内 → `excludeProtectedRanges` 移除整个范围 → 零推荐 → nudge 被抑制 → 模型永远无法压缩。此外,`preserveLastUserMessage` 硬拒绝任何覆盖最后用户消息的压缩调用,阻塞了对周围 tool 输出的有意压缩。
|
|
496
|
+
|
|
497
|
+
**修复**:(1) `buildCompressibleRanges` 现在接受 `protectedZoneRefs` 参数,在保护区边界处拆分组 —— 非保护区头部保留为推荐范围,保护区尾部被排除。(2) `preserveLastUserMessage` 从硬拒绝(`checkProtectedRange` 抛错)改为软过滤(`filterLastUserMessage` 从压缩计划中排除,遵循 Bug 39 的 `filterProtectedToolMessages` 模式)。最后用户消息保留在可见上下文中;周围的 tool 输出正常压缩。(3) 在 `nothingToCompress` 中添加了 `allInProtectedZone` 条件,覆盖所有消息都在保护区内的情况。(4) 修复了过时的错误消息,在 range 和 message 模式中都添加了空计划守卫。双 agent 审查通过(均 APPROVE WITH MINOR FIXES;所有 WARNING 在后续 commit 中解决)。922 项测试通过。
|
|
498
|
+
|
|
499
|
+
文件:`lib/messages/inject/{utils,inject}.ts`、`lib/compress/{pipeline,protected-content,range,message,status}.ts`。测试:`tests/{preserve-recent,soft-block,protected-tool-exclusion}.test.ts`。无持久化状态 schema 变更。`preserveLastUserMessage` 配置语义从硬拒绝变为软过滤(有意的修复;`lastSegmentSoftBlock: false` 禁用所有保护包括新的软过滤)。
|
|
500
|
+
|
|
485
501
|
### v1.14.1 — 日志版本信息 + 增长基线修复(PR #205, #207)
|
|
486
502
|
|
|
487
503
|
**问题**:v1.14.0 之后的两个问题。(1) **日志无法识别版本**(PR #205):ACP daily 日志和每请求 context 日志没有任何版本标记,用户分享调试日志时无法判断是哪个 ACP 版本产生的 —— 跨版本回归排查只能靠猜。(2) **短会话中的增长基线反馈循环**(PR #207):当增长阈值已满足(`nudgeAllowed`)但所有可压缩范围都被过滤掉时(`nothingToCompress`,例如短会话或子代理中所有范围都在保护区内),旧代码会重置 `state.nudges.lastPerMessageNudgeTokens = currentTokens`。这会每轮吃掉累积增长,基线一路追逐当前上下文,即使上下文早已远超阈值,模型也永远等不到 nudge。
|
package/dist/index.js
CHANGED
|
@@ -1648,8 +1648,8 @@ var defaultConfig = {
|
|
|
1648
1648
|
maxVisibleSegments: 50,
|
|
1649
1649
|
keepEmbedMaxChars: 2e3,
|
|
1650
1650
|
lastSegmentSoftBlock: true,
|
|
1651
|
-
preserveRecentMessages:
|
|
1652
|
-
preserveRecentTokens:
|
|
1651
|
+
preserveRecentMessages: 5,
|
|
1652
|
+
preserveRecentTokens: 5e3,
|
|
1653
1653
|
preserveLastUserMessage: true
|
|
1654
1654
|
},
|
|
1655
1655
|
strategies: {
|
|
@@ -3062,6 +3062,91 @@ function filterProtectedToolMessages(selection, searchContext, protectedTools, p
|
|
|
3062
3062
|
toolIds: filteredToolIds
|
|
3063
3063
|
};
|
|
3064
3064
|
}
|
|
3065
|
+
function filterLastUserMessage(selection, searchContext, state, compress) {
|
|
3066
|
+
if (compress.lastSegmentSoftBlock === false) return selection;
|
|
3067
|
+
if (!(compress.preserveLastUserMessage ?? true)) return selection;
|
|
3068
|
+
let lastUserMessageId = null;
|
|
3069
|
+
for (let i = searchContext.rawMessages.length - 1; i >= 0; i--) {
|
|
3070
|
+
const msg = searchContext.rawMessages[i];
|
|
3071
|
+
const id = msg?.info?.id;
|
|
3072
|
+
if (!id || typeof id !== "string") continue;
|
|
3073
|
+
if (isSyntheticMessage(msg)) continue;
|
|
3074
|
+
if (isIgnoredUserMessage(msg)) continue;
|
|
3075
|
+
if (state.prune.messages.byMessageId.has(id)) continue;
|
|
3076
|
+
if (msg.info.role === "user") {
|
|
3077
|
+
lastUserMessageId = id;
|
|
3078
|
+
break;
|
|
3079
|
+
}
|
|
3080
|
+
}
|
|
3081
|
+
if (!lastUserMessageId || !selection.messageIds.includes(lastUserMessageId)) {
|
|
3082
|
+
return selection;
|
|
3083
|
+
}
|
|
3084
|
+
const filteredMessageIds = selection.messageIds.filter((id) => id !== lastUserMessageId);
|
|
3085
|
+
const filteredMessageTokenById = /* @__PURE__ */ new Map();
|
|
3086
|
+
for (const id of filteredMessageIds) {
|
|
3087
|
+
const tokens = selection.messageTokenById.get(id);
|
|
3088
|
+
if (tokens !== void 0) {
|
|
3089
|
+
filteredMessageTokenById.set(id, tokens);
|
|
3090
|
+
}
|
|
3091
|
+
}
|
|
3092
|
+
return {
|
|
3093
|
+
...selection,
|
|
3094
|
+
messageIds: filteredMessageIds,
|
|
3095
|
+
messageTokenById: filteredMessageTokenById
|
|
3096
|
+
};
|
|
3097
|
+
}
|
|
3098
|
+
function filterProtectedRecentMessages(selection, searchContext, state, compress) {
|
|
3099
|
+
if (compress.lastSegmentSoftBlock === false) return selection;
|
|
3100
|
+
const preserveN = compress.preserveRecentMessages ?? 5;
|
|
3101
|
+
const preserveTokens = compress.preserveRecentTokens ?? 5e3;
|
|
3102
|
+
if (preserveN <= 0 && preserveTokens <= 0) return selection;
|
|
3103
|
+
const protectedIds = /* @__PURE__ */ new Set();
|
|
3104
|
+
const visible = [];
|
|
3105
|
+
for (const msg of searchContext.rawMessages) {
|
|
3106
|
+
const id = msg?.info?.id;
|
|
3107
|
+
if (!id || typeof id !== "string") continue;
|
|
3108
|
+
if (isSyntheticMessage(msg)) continue;
|
|
3109
|
+
if (isIgnoredUserMessage(msg)) continue;
|
|
3110
|
+
if (state.prune.messages.byMessageId.has(id)) continue;
|
|
3111
|
+
let tokens = 0;
|
|
3112
|
+
for (const part of msg.parts || []) {
|
|
3113
|
+
if (part.type === "text" && typeof part.text === "string") {
|
|
3114
|
+
tokens += Math.round(part.text.length / 4);
|
|
3115
|
+
} else if (part.type !== "text" && part.type !== "reasoning") {
|
|
3116
|
+
tokens += Math.round(JSON.stringify(part).length / 4);
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
3119
|
+
visible.push({ id, tokens });
|
|
3120
|
+
}
|
|
3121
|
+
if (preserveN > 0) {
|
|
3122
|
+
for (const m of visible.slice(-preserveN)) {
|
|
3123
|
+
protectedIds.add(m.id);
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3126
|
+
if (preserveTokens > 0) {
|
|
3127
|
+
let tokenAccum = 0;
|
|
3128
|
+
for (let i = visible.length - 1; i >= 0 && tokenAccum < preserveTokens; i--) {
|
|
3129
|
+
protectedIds.add(visible[i].id);
|
|
3130
|
+
tokenAccum += visible[i].tokens;
|
|
3131
|
+
}
|
|
3132
|
+
}
|
|
3133
|
+
if (protectedIds.size === 0) return selection;
|
|
3134
|
+
const hasProtected = selection.messageIds.some((id) => protectedIds.has(id));
|
|
3135
|
+
if (!hasProtected) return selection;
|
|
3136
|
+
const filteredMessageIds = selection.messageIds.filter((id) => !protectedIds.has(id));
|
|
3137
|
+
const filteredMessageTokenById = /* @__PURE__ */ new Map();
|
|
3138
|
+
for (const id of filteredMessageIds) {
|
|
3139
|
+
const tokens = selection.messageTokenById.get(id);
|
|
3140
|
+
if (tokens !== void 0) {
|
|
3141
|
+
filteredMessageTokenById.set(id, tokens);
|
|
3142
|
+
}
|
|
3143
|
+
}
|
|
3144
|
+
return {
|
|
3145
|
+
...selection,
|
|
3146
|
+
messageIds: filteredMessageIds,
|
|
3147
|
+
messageTokenById: filteredMessageTokenById
|
|
3148
|
+
};
|
|
3149
|
+
}
|
|
3065
3150
|
|
|
3066
3151
|
// lib/compress/state.ts
|
|
3067
3152
|
var DEFAULT_PROMOTION_THRESHOLD = 5;
|
|
@@ -6396,75 +6481,6 @@ function checkPhantomBlock(state, plans) {
|
|
|
6396
6481
|
}
|
|
6397
6482
|
return null;
|
|
6398
6483
|
}
|
|
6399
|
-
function computeProtectedRawIds(rawMessages, state, compress) {
|
|
6400
|
-
const preserveN = compress.preserveRecentMessages ?? 20;
|
|
6401
|
-
const preserveTokens = compress.preserveRecentTokens ?? 2e4;
|
|
6402
|
-
const preserveLastUser = compress.preserveLastUserMessage ?? true;
|
|
6403
|
-
const result = /* @__PURE__ */ new Set();
|
|
6404
|
-
const visible = [];
|
|
6405
|
-
for (const msg of rawMessages) {
|
|
6406
|
-
const id = msg?.info?.id;
|
|
6407
|
-
if (!id || typeof id !== "string") continue;
|
|
6408
|
-
if (isSyntheticMessage(msg)) continue;
|
|
6409
|
-
if (isIgnoredUserMessage(msg)) continue;
|
|
6410
|
-
if (state.prune.messages.byMessageId.has(id)) continue;
|
|
6411
|
-
let tokens = 0;
|
|
6412
|
-
for (const part of msg.parts || []) {
|
|
6413
|
-
if (part.type === "text" && typeof part.text === "string") {
|
|
6414
|
-
tokens += Math.round(part.text.length / 4);
|
|
6415
|
-
} else if (part.type !== "text" && part.type !== "reasoning") {
|
|
6416
|
-
tokens += Math.round(JSON.stringify(part).length / 4);
|
|
6417
|
-
}
|
|
6418
|
-
}
|
|
6419
|
-
visible.push({ id, tokens, isUser: msg.info.role === "user" });
|
|
6420
|
-
}
|
|
6421
|
-
if (preserveN > 0) {
|
|
6422
|
-
for (const m of visible.slice(-preserveN)) {
|
|
6423
|
-
result.add(m.id);
|
|
6424
|
-
}
|
|
6425
|
-
}
|
|
6426
|
-
if (preserveTokens > 0) {
|
|
6427
|
-
let tokenAccum = 0;
|
|
6428
|
-
for (let i = visible.length - 1; i >= 0 && tokenAccum < preserveTokens; i--) {
|
|
6429
|
-
result.add(visible[i].id);
|
|
6430
|
-
tokenAccum += visible[i].tokens;
|
|
6431
|
-
}
|
|
6432
|
-
}
|
|
6433
|
-
if (preserveLastUser) {
|
|
6434
|
-
for (let i = visible.length - 1; i >= 0; i--) {
|
|
6435
|
-
if (visible[i].isUser) {
|
|
6436
|
-
result.add(visible[i].id);
|
|
6437
|
-
break;
|
|
6438
|
-
}
|
|
6439
|
-
}
|
|
6440
|
-
}
|
|
6441
|
-
return result;
|
|
6442
|
-
}
|
|
6443
|
-
function checkProtectedRange(ctx, allPlanMessageIds, rawMessages, dangerous) {
|
|
6444
|
-
if (ctx.config.compress.lastSegmentSoftBlock === false) return null;
|
|
6445
|
-
const protectedIds = computeProtectedRawIds(rawMessages, ctx.state, ctx.config.compress);
|
|
6446
|
-
if (protectedIds.size === 0) return null;
|
|
6447
|
-
const coveredProtected = [];
|
|
6448
|
-
for (const ids of allPlanMessageIds) {
|
|
6449
|
-
for (const id of ids) {
|
|
6450
|
-
if (protectedIds.has(id)) {
|
|
6451
|
-
coveredProtected.push(id);
|
|
6452
|
-
}
|
|
6453
|
-
}
|
|
6454
|
-
}
|
|
6455
|
-
if (coveredProtected.length === 0) return null;
|
|
6456
|
-
if (dangerous) return null;
|
|
6457
|
-
const sample = coveredProtected.slice(0, 3).join(", ");
|
|
6458
|
-
const nMsgs = ctx.config.compress.preserveRecentMessages ?? 20;
|
|
6459
|
-
const nToks = ctx.config.compress.preserveRecentTokens ?? 2e4;
|
|
6460
|
-
return new Error(
|
|
6461
|
-
`This range includes ${coveredProtected.length} protected recent message(s) (${sample}), which are likely still needed for the current task step.
|
|
6462
|
-
|
|
6463
|
-
Protected zone: last ${nMsgs} messages + last ${nToks >= 1e3 ? `${nToks / 1e3}K` : nToks} tokens + most recent user message.
|
|
6464
|
-
If you are certain this content is genuinely consumed and must be compressed, re-issue the call with \`dangerous: true\`.
|
|
6465
|
-
Otherwise, compress older ranges that do not include the tail of the conversation.`
|
|
6466
|
-
);
|
|
6467
|
-
}
|
|
6468
6484
|
|
|
6469
6485
|
// lib/compress/keep-markers.ts
|
|
6470
6486
|
var KEEP_REGEX = /\[\[KEEP:(m\d+)\]\]/g;
|
|
@@ -6635,14 +6651,33 @@ function createCompressMessageTool(factoryCtx) {
|
|
|
6635
6651
|
ctx.state,
|
|
6636
6652
|
ctx.config
|
|
6637
6653
|
);
|
|
6638
|
-
|
|
6639
|
-
|
|
6654
|
+
const filteredPlans = plans.map((plan) => ({
|
|
6655
|
+
...plan,
|
|
6656
|
+
selection: filterLastUserMessage(
|
|
6657
|
+
plan.selection,
|
|
6658
|
+
searchContext,
|
|
6659
|
+
ctx.state,
|
|
6660
|
+
ctx.config.compress
|
|
6661
|
+
)
|
|
6662
|
+
})).map((plan) => ({
|
|
6663
|
+
...plan,
|
|
6664
|
+
selection: filterProtectedRecentMessages(
|
|
6665
|
+
plan.selection,
|
|
6666
|
+
searchContext,
|
|
6667
|
+
ctx.state,
|
|
6668
|
+
ctx.config.compress
|
|
6669
|
+
)
|
|
6670
|
+
})).filter((plan) => plan.selection.messageIds.length > 0);
|
|
6671
|
+
if (filteredPlans.length === 0) {
|
|
6672
|
+
throw new Error(
|
|
6673
|
+
skippedCount > 0 ? formatIssues(skippedIssues, skippedCount) : "All selected messages were filtered out (protected tool outputs and/or the last user message). They must remain in visible context."
|
|
6674
|
+
);
|
|
6640
6675
|
}
|
|
6641
6676
|
const minCompressRange = ctx.config.compress.minCompressRange;
|
|
6642
6677
|
if (minCompressRange > 0) {
|
|
6643
6678
|
let totalChars = 0;
|
|
6644
6679
|
const counted = /* @__PURE__ */ new Set();
|
|
6645
|
-
for (const plan of
|
|
6680
|
+
for (const plan of filteredPlans) {
|
|
6646
6681
|
for (const messageId of plan.selection.messageIds) {
|
|
6647
6682
|
if (counted.has(messageId)) continue;
|
|
6648
6683
|
counted.add(messageId);
|
|
@@ -6658,17 +6693,9 @@ function createCompressMessageTool(factoryCtx) {
|
|
|
6658
6693
|
);
|
|
6659
6694
|
}
|
|
6660
6695
|
}
|
|
6661
|
-
const dangerous = args.dangerous === true;
|
|
6662
|
-
const lastSegmentError = checkProtectedRange(
|
|
6663
|
-
ctx,
|
|
6664
|
-
plans.map((p) => p.selection.messageIds),
|
|
6665
|
-
rawMessages,
|
|
6666
|
-
dangerous
|
|
6667
|
-
);
|
|
6668
|
-
if (lastSegmentError) throw lastSegmentError;
|
|
6669
6696
|
const notifications = [];
|
|
6670
6697
|
const preparedPlans = [];
|
|
6671
|
-
for (const plan of
|
|
6698
|
+
for (const plan of filteredPlans) {
|
|
6672
6699
|
const summaryWithPromptInfo = appendProtectedPromptInfo(
|
|
6673
6700
|
plan.entry.summary,
|
|
6674
6701
|
plan.selection,
|
|
@@ -6778,7 +6805,7 @@ function createCompressMessageTool(factoryCtx) {
|
|
|
6778
6805
|
ctx.state.qualityGateRetryPending = qualityGateRetryPendingBefore;
|
|
6779
6806
|
throw error;
|
|
6780
6807
|
}
|
|
6781
|
-
return formatResult(
|
|
6808
|
+
return formatResult(filteredPlans.length, skippedIssues, skippedCount);
|
|
6782
6809
|
}
|
|
6783
6810
|
});
|
|
6784
6811
|
}
|
|
@@ -6852,10 +6879,26 @@ function createCompressRangeTool(factoryCtx) {
|
|
|
6852
6879
|
ctx.config.compress.protectedTools,
|
|
6853
6880
|
ctx.config.protectedFilePatterns
|
|
6854
6881
|
)
|
|
6882
|
+
})).map((plan) => ({
|
|
6883
|
+
...plan,
|
|
6884
|
+
selection: filterLastUserMessage(
|
|
6885
|
+
plan.selection,
|
|
6886
|
+
searchContext,
|
|
6887
|
+
ctx.state,
|
|
6888
|
+
ctx.config.compress
|
|
6889
|
+
)
|
|
6890
|
+
})).map((plan) => ({
|
|
6891
|
+
...plan,
|
|
6892
|
+
selection: filterProtectedRecentMessages(
|
|
6893
|
+
plan.selection,
|
|
6894
|
+
searchContext,
|
|
6895
|
+
ctx.state,
|
|
6896
|
+
ctx.config.compress
|
|
6897
|
+
)
|
|
6855
6898
|
})).filter((plan) => plan.selection.messageIds.length > 0);
|
|
6856
6899
|
if (filteredPlans.length === 0) {
|
|
6857
6900
|
throw new Error(
|
|
6858
|
-
"All selected messages
|
|
6901
|
+
"All selected messages were filtered out (protected tool outputs and/or the last user message). They must remain in visible context."
|
|
6859
6902
|
);
|
|
6860
6903
|
}
|
|
6861
6904
|
const minCompressRange = ctx.config.compress.minCompressRange;
|
|
@@ -6878,14 +6921,6 @@ function createCompressRangeTool(factoryCtx) {
|
|
|
6878
6921
|
);
|
|
6879
6922
|
}
|
|
6880
6923
|
}
|
|
6881
|
-
const dangerous = args.dangerous === true;
|
|
6882
|
-
const lastSegmentError = checkProtectedRange(
|
|
6883
|
-
ctx,
|
|
6884
|
-
filteredPlans.map((p) => p.selection.messageIds),
|
|
6885
|
-
rawMessages,
|
|
6886
|
-
dangerous
|
|
6887
|
-
);
|
|
6888
|
-
if (lastSegmentError) throw lastSegmentError;
|
|
6889
6924
|
const notifications = [];
|
|
6890
6925
|
const preparedPlans = [];
|
|
6891
6926
|
let totalCompressedMessages = 0;
|
|
@@ -8122,7 +8157,7 @@ function refNum(ref) {
|
|
|
8122
8157
|
const n = parseInt(ref.slice(1), 10);
|
|
8123
8158
|
return Number.isNaN(n) ? -1 : n;
|
|
8124
8159
|
}
|
|
8125
|
-
function buildCompressibleRanges(messages, state, protectedTools = [], protectedFilePatterns = []) {
|
|
8160
|
+
function buildCompressibleRanges(messages, state, protectedTools = [], protectedFilePatterns = [], protectedZoneRefs) {
|
|
8126
8161
|
const msgInfo = [];
|
|
8127
8162
|
const protectedMsgInfo = [];
|
|
8128
8163
|
for (const msg of messages) {
|
|
@@ -8174,6 +8209,14 @@ function buildCompressibleRanges(messages, state, protectedTools = [], protected
|
|
|
8174
8209
|
let cur = null;
|
|
8175
8210
|
let prevRefNum = -2;
|
|
8176
8211
|
for (const info of msgInfo) {
|
|
8212
|
+
if (protectedZoneRefs?.has(info.ref)) {
|
|
8213
|
+
if (cur) {
|
|
8214
|
+
groups.push(cur);
|
|
8215
|
+
cur = null;
|
|
8216
|
+
}
|
|
8217
|
+
prevRefNum = info.refNum;
|
|
8218
|
+
continue;
|
|
8219
|
+
}
|
|
8177
8220
|
const hasGap = info.refNum > prevRefNum + 1;
|
|
8178
8221
|
if (cur && (info.isUser && cur.count >= 3 || hasGap)) {
|
|
8179
8222
|
groups.push(cur);
|
|
@@ -8378,9 +8421,8 @@ ${lines.join("\n")}`;
|
|
|
8378
8421
|
}
|
|
8379
8422
|
function computeProtectedRefs(messages, state, compress) {
|
|
8380
8423
|
if (compress.lastSegmentSoftBlock === false) return /* @__PURE__ */ new Set();
|
|
8381
|
-
const preserveN = compress.preserveRecentMessages ??
|
|
8382
|
-
const preserveTokens = compress.preserveRecentTokens ??
|
|
8383
|
-
const preserveLastUser = compress.preserveLastUserMessage ?? true;
|
|
8424
|
+
const preserveN = compress.preserveRecentMessages ?? 5;
|
|
8425
|
+
const preserveTokens = compress.preserveRecentTokens ?? 5e3;
|
|
8384
8426
|
const result = /* @__PURE__ */ new Set();
|
|
8385
8427
|
const visible = [];
|
|
8386
8428
|
for (const msg of messages) {
|
|
@@ -8411,14 +8453,6 @@ function computeProtectedRefs(messages, state, compress) {
|
|
|
8411
8453
|
tokenAccum += visible[i].tokens;
|
|
8412
8454
|
}
|
|
8413
8455
|
}
|
|
8414
|
-
if (preserveLastUser) {
|
|
8415
|
-
for (let i = visible.length - 1; i >= 0; i--) {
|
|
8416
|
-
if (visible[i].isUser) {
|
|
8417
|
-
result.add(visible[i].ref);
|
|
8418
|
-
break;
|
|
8419
|
-
}
|
|
8420
|
-
}
|
|
8421
|
-
}
|
|
8422
8456
|
return result;
|
|
8423
8457
|
}
|
|
8424
8458
|
function excludeProtectedRanges(ranges, protectedRefs) {
|
|
@@ -8600,13 +8634,14 @@ var injectCompressNudges = (state, config, logger, messages, prompts, compressio
|
|
|
8600
8634
|
config.compress.protectedTools,
|
|
8601
8635
|
config.protectedFilePatterns
|
|
8602
8636
|
);
|
|
8637
|
+
const protectedRefs = computeProtectedRefs(messages, state, config.compress);
|
|
8603
8638
|
const contextRanges = buildCompressibleRanges(
|
|
8604
8639
|
messages,
|
|
8605
8640
|
state,
|
|
8606
8641
|
config.compress.protectedTools,
|
|
8607
|
-
config.protectedFilePatterns
|
|
8642
|
+
config.protectedFilePatterns,
|
|
8643
|
+
protectedRefs
|
|
8608
8644
|
);
|
|
8609
|
-
const protectedRefs = computeProtectedRefs(messages, state, config.compress);
|
|
8610
8645
|
const unprotectedCompressible = excludeProtectedRanges(contextRanges.compressible, protectedRefs);
|
|
8611
8646
|
const recommendedRanges = filterRecommendedRanges(
|
|
8612
8647
|
unprotectedCompressible,
|
|
@@ -8635,7 +8670,8 @@ var injectCompressNudges = (state, config, logger, messages, prompts, compressio
|
|
|
8635
8670
|
}
|
|
8636
8671
|
const filterSuppressed = contextRanges.compressible.length > 0 && !hasRecommendations;
|
|
8637
8672
|
const allProtected = contextRanges.compressible.length === 0 && contextRanges.protected.length > 0;
|
|
8638
|
-
const
|
|
8673
|
+
const allInProtectedZone = protectedRefs.size > 0 && unprotectedCompressible.length === 0;
|
|
8674
|
+
const nothingToCompress = filterSuppressed || allProtected || allInProtectedZone;
|
|
8639
8675
|
let shouldInject = nudgeAllowed && (!nothingToCompress || emergencyOverride);
|
|
8640
8676
|
if (nudgeAllowed && nothingToCompress && !emergencyOverride) {
|
|
8641
8677
|
state.nudges.lastNudgeShownTokens = void 0;
|
|
@@ -9634,11 +9670,13 @@ function renderOverview(visibleMessages, summaryTokens, blocks, fetchFailed, raw
|
|
|
9634
9670
|
const entry = pruneMap.get(msgId);
|
|
9635
9671
|
return !entry || entry.activeBlockIds.length === 0;
|
|
9636
9672
|
});
|
|
9673
|
+
const protectedRefs = ctx.config?.compress ? computeProtectedRefs(visibleRaw, ctx.state, ctx.config.compress) : /* @__PURE__ */ new Set();
|
|
9637
9674
|
const contextRanges = buildCompressibleRanges(
|
|
9638
9675
|
visibleRaw,
|
|
9639
9676
|
ctx.state,
|
|
9640
9677
|
ctx.config?.compress?.protectedTools ?? [],
|
|
9641
|
-
ctx.config?.protectedFilePatterns ?? []
|
|
9678
|
+
ctx.config?.protectedFilePatterns ?? [],
|
|
9679
|
+
protectedRefs
|
|
9642
9680
|
);
|
|
9643
9681
|
if (contextRanges.compressible.length > 0 || contextRanges.protected.length > 0) {
|
|
9644
9682
|
lines.push("");
|
|
@@ -9661,11 +9699,13 @@ function renderUncompressedRanges(rawMessages, ctx) {
|
|
|
9661
9699
|
const entry = pruneMap.get(msgId);
|
|
9662
9700
|
return !entry || entry.activeBlockIds.length === 0;
|
|
9663
9701
|
});
|
|
9702
|
+
const protectedRefs = ctx.config?.compress ? computeProtectedRefs(visibleMessages, ctx.state, ctx.config.compress) : /* @__PURE__ */ new Set();
|
|
9664
9703
|
const contextRanges = buildCompressibleRanges(
|
|
9665
9704
|
visibleMessages,
|
|
9666
9705
|
ctx.state,
|
|
9667
9706
|
ctx.config?.compress?.protectedTools ?? [],
|
|
9668
|
-
ctx.config?.protectedFilePatterns ?? []
|
|
9707
|
+
ctx.config?.protectedFilePatterns ?? [],
|
|
9708
|
+
protectedRefs
|
|
9669
9709
|
);
|
|
9670
9710
|
const compressible = contextRanges.compressible;
|
|
9671
9711
|
const totalTokens = compressible.reduce((s, r) => s + r.tokens, 0);
|
|
@@ -10022,7 +10062,7 @@ import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
|
10022
10062
|
import { join as join3 } from "path";
|
|
10023
10063
|
import { existsSync as existsSync3 } from "fs";
|
|
10024
10064
|
import { homedir as homedir3 } from "os";
|
|
10025
|
-
var LOG_VERSION = true ? "1.14.
|
|
10065
|
+
var LOG_VERSION = true ? "1.14.3" : "dev";
|
|
10026
10066
|
var Logger = class {
|
|
10027
10067
|
logDir;
|
|
10028
10068
|
enabled;
|