opencode-acp 1.14.8-dev.3 → 1.14.8-dev.4
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 +27 -0
- package/README.zh-CN.md +27 -0
- package/dist/index.js +13 -69
- package/dist/index.js.map +1 -1
- package/dist/lib/messages/inject/inject.d.ts.map +1 -1
- package/dist/lib/messages/inject/utils.d.ts +10 -7
- package/dist/lib/messages/inject/utils.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -492,6 +492,33 @@ For the complete list with root cause analysis, see the [bug tracker](https://gi
|
|
|
492
492
|
|
|
493
493
|
## Changelog
|
|
494
494
|
|
|
495
|
+
### v1.14.8-dev.4 — Dev Prerelease (1 PR since v1.14.8-dev.3)
|
|
496
|
+
|
|
497
|
+
Dev prerelease covering PR #252. Published to `dev` npm tag for early testing.
|
|
498
|
+
|
|
499
|
+
**PRs included**:
|
|
500
|
+
- **#252** — Two fixes: (1) **Issue #251**: `filterRecommendedRanges` suppressed ALL recommendations when compressible content was below 5% of modelContextLimit (50K at 1M context). Rewrote to always show all ranges with `dangerous: true` on the last segment. Simplified `RangeFilterOptions`. (2) **Nudge loop fix**: `lastNudgeShownTokens` was reset to `undefined` on `nothingToCompress`, causing `growthReference` to fall back to stale session-start baseline → nudges firing every turn. Removed the reset. Also fixed growth display to use `lastNudgeShownTokens ?? lastPerMessageNudgeTokens` matching the actual decision logic. Oracle + Explore review: both APPROVE.
|
|
501
|
+
|
|
502
|
+
**Install**: `opencode plugin opencode-acp@dev --global`
|
|
503
|
+
|
|
504
|
+
### v1.14.8 — Stable Release (10 PRs since v1.14.7)
|
|
505
|
+
|
|
506
|
+
Stable release promoted from dev prereleases v1.14.8-dev.1 through v1.14.8-dev.3. Includes all fixes tested in the dev channel.
|
|
507
|
+
|
|
508
|
+
**Highlights**:
|
|
509
|
+
|
|
510
|
+
- **System token visibility** (#241): `acp_status` and nudge breakdown now show system prompt token proportion (5-15% of context). Consolidated 4 duplicate estimation algorithms into 1 shared `estimateSystemPromptTokens()` using the real Anthropic tokenizer.
|
|
511
|
+
- **HOW_TO_COMPRESS_RULES re-added to nudge** (#245): v1.14.7 over-removed the compression rules from all nudge locations. In long sessions (8,000+ messages), the rules in the system prompt degrade due to "lost in the middle" effect — re-added to nudge for high-attention guidance when compression triggers.
|
|
512
|
+
- **Tool pair integrity** (#248): Compression ranges that split tool_use/tool_result pairs caused API rejections. New `adjustBoundariesForToolPairs` auto-extends boundaries by callID matching.
|
|
513
|
+
- **Pluggable message filters** (#239, #242): `keepLastOnly` dedup mechanism + 4 OMO builtin filters for cleaning up third-party (e.g. OMO) injected messages that accumulate duplicates.
|
|
514
|
+
- **Orphan message splicing** (#240): When `hideConsumedCompressCalls` leaves only structural parts (step-finish, reasoning), the message is now spliced out instead of surviving as a 500-2000 token orphan.
|
|
515
|
+
- **E2E hardening** (#238): Observation recording, T2 cadence regression scenario, consumed-call hiding scenario, auxiliary call filtering. 12 E2E scenarios (was 8).
|
|
516
|
+
- **acp_status consumed compress hiding** (#244): Hides consumed compress calls from the PROTECTED list.
|
|
517
|
+
|
|
518
|
+
**All PRs included**: #238 (E2E hardening), #232 (remove dead turnProtection/DCP migration), #234 (re-add /acp stats), #239 (pluggable message filter), #240 (orphan splice), #241 (system tokens), #242 (keepLastOnly + OMO filters), #244 (acp_status consumed compress), #245 (HOW_TO_COMPRESS_RULES re-add), #248 (tool pair integrity).
|
|
519
|
+
|
|
520
|
+
**Install**: `opencode plugin opencode-acp@stable --global`
|
|
521
|
+
|
|
495
522
|
### v1.14.8-dev.3 — Dev Prerelease (1 PR since v1.14.8-dev.2)
|
|
496
523
|
|
|
497
524
|
Dev prerelease covering PR #248. Published to `dev` npm tag for early testing.
|
package/README.zh-CN.md
CHANGED
|
@@ -446,6 +446,15 @@ ACP 是 DCP 的直接替代品。迁移步骤:
|
|
|
446
446
|
|
|
447
447
|
## 更新日志
|
|
448
448
|
|
|
449
|
+
### v1.14.8-dev.4 — Dev 预发布(v1.14.8-dev.3 以来 1 个 PR)
|
|
450
|
+
|
|
451
|
+
Dev 预发布,涵盖 PR #252。发布到 `dev` npm tag 供早期测试。
|
|
452
|
+
|
|
453
|
+
**包含的 PR**:
|
|
454
|
+
- **#252** — 两个修复:(1) **Issue #251**:`filterRecommendedRanges` 在可压缩内容低于 modelContextLimit 的 5%(1M 上下文为 50K)时抑制所有推荐。重写为始终显示所有范围,最后一段标记 `dangerous: true`。简化 `RangeFilterOptions`。(2) **Nudge 循环修复**:`nothingToCompress` 时 `lastNudgeShownTokens` 被重置为 `undefined`,导致 `growthReference` 回退到 session 开始时的 stale baseline → 每轮都触发 nudge。移除该重置。同时修复增长显示使用 `lastNudgeShownTokens ?? lastPerMessageNudgeTokens`,与实际决策逻辑一致。Oracle + Explore Review:均通过。
|
|
455
|
+
|
|
456
|
+
**安装**:`opencode plugin opencode-acp@dev --global`
|
|
457
|
+
|
|
449
458
|
### v1.14.8-dev.3 — Dev 预发布(v1.14.8-dev.2 以来 1 个 PR)
|
|
450
459
|
|
|
451
460
|
Dev 预发布,涵盖 PR #248。发布到 `dev` npm tag 供早期测试。
|
|
@@ -480,6 +489,24 @@ Dev 预发布,涵盖 PR #238–#242。发布到 `dev` npm tag 供早期测试
|
|
|
480
489
|
|
|
481
490
|
**安装**:`opencode plugin opencode-acp@dev --global`
|
|
482
491
|
|
|
492
|
+
### v1.14.8 — 正式版(v1.14.7 以来 10 个 PR)
|
|
493
|
+
|
|
494
|
+
正式版,从 dev 预发布 v1.14.8-dev.1 至 v1.14.8-dev.3 提升而来。包含所有在 dev 通道测试过的修复。
|
|
495
|
+
|
|
496
|
+
**亮点**:
|
|
497
|
+
|
|
498
|
+
- **系统 token 可见性**(#241):`acp_status` 和 nudge breakdown 现在显示系统 prompt token 占比(上下文的 5-15%)。将 4 处重复的估算算法整合为 1 个共享 `estimateSystemPromptTokens()`,使用真正的 Anthropic 分词器。
|
|
499
|
+
- **HOW_TO_COMPRESS_RULES 重新加入 nudge**(#245):v1.14.7 过度删除了所有 nudge 位置的压缩规则。在长 session(8000+ 消息)中,system prompt 中的规则因"lost in the middle"效应衰减——重新加入 nudge 以在压缩触发时提供高注意力区指导。
|
|
500
|
+
- **工具配对完整性**(#248):压缩范围拆分 tool_use/tool_result 配对会导致 API 拒绝。新增 `adjustBoundariesForToolPairs` 按 callID 匹配自动扩展边界。
|
|
501
|
+
- **可插拔消息过滤器**(#239, #242):`keepLastOnly` 去重机制 + 4 个 OMO 内置过滤器,用于清理第三方注入消息的重复累积。
|
|
502
|
+
- **孤儿消息清理**(#240):`hideConsumedCompressCalls` 仅剩结构化部分时(step-finish、reasoning),消息现在被 splice 掉,不再作为 500-2000 token 孤儿存活。
|
|
503
|
+
- **E2E 强化**(#238):观察记录、T2 节奏回归场景、consumed-call 隐藏场景、辅助调用过滤。E2E 场景 8→12 个。
|
|
504
|
+
- **acp_status 隐藏已消耗 compress**(#244):从 PROTECTED 列表中隐藏已消耗的 compress 调用。
|
|
505
|
+
|
|
506
|
+
**包含的所有 PR**:#238(E2E 强化)、#232(移除死代码 turnProtection/DCP 迁移)、#234(重新添加 /acp stats)、#239(可插拔消息过滤器)、#240(孤儿消息清理)、#241(系统 token)、#242(keepLastOnly + OMO 过滤器)、#244(acp_status 已消耗 compress)、#245(HOW_TO_COMPRESS_RULES 重新加入)、#248(工具配对完整性)。
|
|
507
|
+
|
|
508
|
+
**安装**:`opencode plugin opencode-acp@stable --global`
|
|
509
|
+
|
|
483
510
|
### v1.14.7 — 去重 HOW_TO_COMPRESS_RULES(PR #228)
|
|
484
511
|
|
|
485
512
|
**问题**:`HOW_TO_COMPRESS_RULES`(~1.2K tokens)每次 nudge 注入重复 3-4 次——系统提示词 1 次、nudge 模板 1-2 次、breakdown 块 1 次。非 maxLimit 场景下 suffix 消息单独就包含 2-3 份规则。每次 nudge 浪费 2.4-3.6K tokens。v1.14.6 让 debug 模式持久化 nudge 文本到聊天界面后,重复变得可见。
|
package/dist/index.js
CHANGED
|
@@ -7168,62 +7168,19 @@ function buildCompressibleRanges(messages, state, protectedTools = [], protected
|
|
|
7168
7168
|
};
|
|
7169
7169
|
}
|
|
7170
7170
|
function filterRecommendedRanges(compressible, _protectedRanges, options) {
|
|
7171
|
-
const {
|
|
7171
|
+
const { logger } = options;
|
|
7172
7172
|
const log = logger?.debug.bind(logger);
|
|
7173
|
-
if (!modelContextLimit || modelContextLimit <= 0) {
|
|
7174
|
-
log?.("filterRecommendedRanges: modelContextLimit unknown, passthrough", {
|
|
7175
|
-
inputCount: compressible.length
|
|
7176
|
-
});
|
|
7177
|
-
if (compressible.length === 0) return [];
|
|
7178
|
-
const passthrough = [...compressible];
|
|
7179
|
-
passthrough[passthrough.length - 1] = {
|
|
7180
|
-
...passthrough[passthrough.length - 1],
|
|
7181
|
-
dangerous: true
|
|
7182
|
-
};
|
|
7183
|
-
return passthrough;
|
|
7184
|
-
}
|
|
7185
7173
|
if (compressible.length === 0) {
|
|
7186
7174
|
log?.("filterRecommendedRanges: no compressible ranges, returning empty");
|
|
7187
7175
|
return [];
|
|
7188
7176
|
}
|
|
7189
|
-
const
|
|
7190
|
-
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
const lastSegmentIncluded = lastRange.tokens >= lastSegmentFloor;
|
|
7194
|
-
let effectiveCompressible = 0;
|
|
7195
|
-
const result = [];
|
|
7196
|
-
for (let i = 0; i < compressible.length; i++) {
|
|
7197
|
-
const r = compressible[i];
|
|
7198
|
-
if (i === lastIndex) {
|
|
7199
|
-
const excess = Math.max(0, r.tokens - lastSegmentFloor);
|
|
7200
|
-
effectiveCompressible += excess;
|
|
7201
|
-
if (lastSegmentIncluded) {
|
|
7202
|
-
result.push({ ...r, dangerous: true });
|
|
7203
|
-
}
|
|
7204
|
-
} else {
|
|
7205
|
-
effectiveCompressible += r.tokens;
|
|
7206
|
-
result.push(r);
|
|
7207
|
-
}
|
|
7208
|
-
}
|
|
7209
|
-
const suppressed = effectiveCompressible < growthThreshold;
|
|
7210
|
-
log?.("filterRecommendedRanges: decision", {
|
|
7177
|
+
const result = compressible.map(
|
|
7178
|
+
(r, i) => i === compressible.length - 1 ? { ...r, dangerous: true } : r
|
|
7179
|
+
);
|
|
7180
|
+
log?.("filterRecommendedRanges: passthrough (last segment marked dangerous)", {
|
|
7211
7181
|
inputRanges: compressible.length,
|
|
7212
|
-
|
|
7213
|
-
growthThreshold,
|
|
7214
|
-
lastSegmentFloor,
|
|
7215
|
-
lastSegment: {
|
|
7216
|
-
ref: `${lastRange.startRef}\u2013${lastRange.endRef}`,
|
|
7217
|
-
tokens: lastRange.tokens,
|
|
7218
|
-
included: lastSegmentIncluded
|
|
7219
|
-
},
|
|
7220
|
-
effectiveCompressible,
|
|
7221
|
-
suppressed,
|
|
7222
|
-
outputRanges: suppressed ? 0 : result.length
|
|
7182
|
+
outputRanges: result.length
|
|
7223
7183
|
});
|
|
7224
|
-
if (suppressed) {
|
|
7225
|
-
return [];
|
|
7226
|
-
}
|
|
7227
7184
|
return result;
|
|
7228
7185
|
}
|
|
7229
7186
|
function formatCompressibleRanges(ranges, protectedRanges) {
|
|
@@ -7537,37 +7494,24 @@ var injectCompressNudges = (state, config, logger, messages, prompts, compressio
|
|
|
7537
7494
|
const recommendedRanges = filterRecommendedRanges(
|
|
7538
7495
|
unprotectedCompressible,
|
|
7539
7496
|
contextRanges.protected,
|
|
7540
|
-
{
|
|
7497
|
+
{ logger }
|
|
7541
7498
|
);
|
|
7542
7499
|
const hasRecommendations = recommendedRanges.length > 0;
|
|
7543
|
-
if (config.debug && contextRanges.compressible.length > 0
|
|
7544
|
-
const growthThreshold = modelContextLimit * 0.05;
|
|
7545
|
-
const lastSegmentFloor = growthThreshold * 2;
|
|
7500
|
+
if (config.debug && contextRanges.compressible.length > 0) {
|
|
7546
7501
|
const compressible = contextRanges.compressible;
|
|
7547
|
-
const lastRange = compressible[compressible.length - 1];
|
|
7548
|
-
const lastIncluded = lastRange.tokens >= lastSegmentFloor;
|
|
7549
|
-
const nonLastTokens = compressible.slice(0, -1).reduce((s, r) => s + r.tokens, 0);
|
|
7550
|
-
const effective = nonLastTokens + Math.max(0, lastRange.tokens - lastSegmentFloor);
|
|
7551
|
-
const suppressed = effective < growthThreshold;
|
|
7552
7502
|
const fmt = (n) => n >= 1e3 ? `${(n / 1e3).toFixed(1)}K` : String(n);
|
|
7553
7503
|
const lines = [
|
|
7554
|
-
`[ACP Debug] Recommendation filter
|
|
7504
|
+
`[ACP Debug] Recommendation filter:`,
|
|
7555
7505
|
` Input: ${compressible.length} range(s), ${fmt(compressible.reduce((s, r) => s + r.tokens, 0))} tokens`,
|
|
7556
|
-
`
|
|
7557
|
-
` Last segment: ${lastRange.startRef}\u2013${lastRange.endRef} ${fmt(lastRange.tokens)} tokens \u2192 ${lastIncluded ? "included (dangerous)" : "excluded (< floor)"}`,
|
|
7558
|
-
` Effective compressible: ${fmt(effective)} \u2192 ${suppressed ? "SUPPRESSED (< growth threshold)" : `${recommendedRanges.length} range(s) recommended`}`
|
|
7506
|
+
` Output: ${recommendedRanges.length} range(s) (last segment marked dangerous)`
|
|
7559
7507
|
];
|
|
7560
7508
|
logger.debug(lines.join("\n"));
|
|
7561
7509
|
}
|
|
7562
|
-
const filterSuppressed = contextRanges.compressible.length > 0 && !hasRecommendations;
|
|
7563
7510
|
const allProtected = contextRanges.compressible.length === 0 && contextRanges.protected.length > 0;
|
|
7564
7511
|
const allInProtectedZone = protectedRefs.size > 0 && unprotectedCompressible.length === 0;
|
|
7565
|
-
const nothingToCompress =
|
|
7512
|
+
const nothingToCompress = allProtected || allInProtectedZone;
|
|
7566
7513
|
const shouldInjectNudge = nudgeAllowed && (!nothingToCompress || emergencyOverride);
|
|
7567
7514
|
let shouldInject = shouldInjectNudge;
|
|
7568
|
-
if (nudgeAllowed && nothingToCompress && !emergencyOverride) {
|
|
7569
|
-
state.nudges.lastNudgeShownTokens = void 0;
|
|
7570
|
-
}
|
|
7571
7515
|
if (shouldInjectNudge) {
|
|
7572
7516
|
applyAnchoredNudges(state, config, messages, prompts, compressionPriorities, currentTokens, modelContextLimit, suffixMessage);
|
|
7573
7517
|
}
|
|
@@ -7662,7 +7606,7 @@ ${rules}`;
|
|
|
7662
7606
|
if (suffixMessage && composition.total > 0) {
|
|
7663
7607
|
const fmt = (n) => n >= 1e3 ? `${(n / 1e3).toFixed(1)}K` : String(n);
|
|
7664
7608
|
const pct2 = (n) => n > 0 ? Math.max(1, Math.round(n / composition.total * 100)) : 0;
|
|
7665
|
-
const growth = currentTokens !== void 0 && state.nudges.lastPerMessageNudgeTokens !== void 0 ? currentTokens - state.nudges.lastPerMessageNudgeTokens : 0;
|
|
7609
|
+
const growth = currentTokens !== void 0 && (state.nudges.lastNudgeShownTokens ?? state.nudges.lastPerMessageNudgeTokens) !== void 0 ? currentTokens - (state.nudges.lastNudgeShownTokens ?? state.nudges.lastPerMessageNudgeTokens) : 0;
|
|
7666
7610
|
const growthStr = growth > 0 ? ` (+${fmt(growth)} since last nudge)` : "";
|
|
7667
7611
|
const plainTextTokens = composition.textTokens;
|
|
7668
7612
|
const efficiencyNote = effectiveTipsVariant !== "maxLimit" ? `
|
|
@@ -8892,7 +8836,7 @@ import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
|
8892
8836
|
import { join as join3 } from "path";
|
|
8893
8837
|
import { existsSync as existsSync3 } from "fs";
|
|
8894
8838
|
import { homedir as homedir3 } from "os";
|
|
8895
|
-
var LOG_VERSION = true ? "1.14.8-dev.
|
|
8839
|
+
var LOG_VERSION = true ? "1.14.8-dev.4" : "dev";
|
|
8896
8840
|
var Logger = class {
|
|
8897
8841
|
logDir;
|
|
8898
8842
|
enabled;
|