opencode-acp 1.14.8-dev.1 → 1.14.8-dev.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 +19 -0
- package/README.zh-CN.md +19 -0
- package/dist/index.js +134 -51
- package/dist/index.js.map +1 -1
- package/dist/lib/compress/hide-consumed.d.ts +0 -18
- package/dist/lib/compress/hide-consumed.d.ts.map +1 -1
- package/dist/lib/compress/search.d.ts.map +1 -1
- package/dist/lib/compress/status.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -492,6 +492,25 @@ 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.3 — Dev Prerelease (1 PR since v1.14.8-dev.2)
|
|
496
|
+
|
|
497
|
+
Dev prerelease covering PR #248. Published to `dev` npm tag for early testing.
|
|
498
|
+
|
|
499
|
+
**PRs included**:
|
|
500
|
+
- **#248** — Fix: auto-extend compression ranges to prevent splitting tool_use/tool_result pairs. When the model selects a compression boundary that falls between a tool call and its result, the orphaned tool_result references a non-existent tool_use_id → API rejection. New `adjustBoundariesForToolPairs` in `compress/search.ts` scans forward/backward (up to 20 messages) to include the matching pair. Excludes `compress` tool (force-protected) and only extends message boundaries (never block boundaries) to avoid tier misclassification. 11 tests.
|
|
501
|
+
|
|
502
|
+
**Install**: `opencode plugin opencode-acp@dev --global`
|
|
503
|
+
|
|
504
|
+
### v1.14.8-dev.2 — Dev Prerelease (2 PRs since v1.14.8-dev.1)
|
|
505
|
+
|
|
506
|
+
Dev prerelease covering PRs #244–#245. Published to `dev` npm tag for early testing.
|
|
507
|
+
|
|
508
|
+
**PRs included**:
|
|
509
|
+
- **#245** — Fix: re-add HOW_TO_COMPRESS_RULES to nudge injection for high-attention summary guidance. v1.14.7 over-removed the rules from all 4 nudge locations (breakdown + 3 templates), leaving them only in system prompt. In long sessions, system prompt rules degrade due to "lost in the middle" effect — model needs rules at high attention (end of context) when nudge fires.
|
|
510
|
+
- **#244** — Fix: acp_status hides consumed compress calls from PROTECTED list.
|
|
511
|
+
|
|
512
|
+
**Install**: `opencode plugin opencode-acp@dev --global`
|
|
513
|
+
|
|
495
514
|
### v1.14.8-dev.1 — Dev Prerelease (7 PRs since v1.14.7)
|
|
496
515
|
|
|
497
516
|
Dev prerelease covering PRs #238–#242. Published to `dev` npm tag for early testing.
|
package/README.zh-CN.md
CHANGED
|
@@ -446,6 +446,25 @@ ACP 是 DCP 的直接替代品。迁移步骤:
|
|
|
446
446
|
|
|
447
447
|
## 更新日志
|
|
448
448
|
|
|
449
|
+
### v1.14.8-dev.3 — Dev 预发布(v1.14.8-dev.2 以来 1 个 PR)
|
|
450
|
+
|
|
451
|
+
Dev 预发布,涵盖 PR #248。发布到 `dev` npm tag 供早期测试。
|
|
452
|
+
|
|
453
|
+
**包含的 PR**:
|
|
454
|
+
- **#248** — 修复:自动扩展压缩范围以防止拆分 tool_use/tool_result 配对。当模型选择的压缩边界落在 tool 调用与其结果之间时,孤立的 tool_result 引用不存在的 tool_use_id → API 拒绝。新增 `adjustBoundariesForToolPairs`(`compress/search.ts`)向前/向后扫描(最多 20 条消息)以包含匹配的配对。排除 `compress` 工具(强制保护)并仅扩展消息边界(从不扩展 block 边界)以避免层级错误分类。11 个测试。
|
|
455
|
+
|
|
456
|
+
**安装**:`opencode plugin opencode-acp@dev --global`
|
|
457
|
+
|
|
458
|
+
### v1.14.8-dev.2 — Dev 预发布(v1.14.8-dev.1 以来 2 个 PR)
|
|
459
|
+
|
|
460
|
+
Dev 预发布,涵盖 PR #244–#245。发布到 `dev` npm tag 供早期测试。
|
|
461
|
+
|
|
462
|
+
**包含的 PR**:
|
|
463
|
+
- **#245** — 修复:重新将 HOW_TO_COMPRESS_RULES 加入 nudge 注入。v1.14.7 过度删除了所有 4 处 nudge 位置的规则(breakdown + 3 个模板),只保留在 system prompt。在长 session 中,system prompt 的规则因"lost in the middle"效应衰减——模型在 nudge 触发时需要规则在高注意力区(上下文末尾)。
|
|
464
|
+
- **#244** — 修复:acp_status 隐藏 PROTECTED 列表中已消耗的 compress 调用。
|
|
465
|
+
|
|
466
|
+
**安装**:`opencode plugin opencode-acp@dev --global`
|
|
467
|
+
|
|
449
468
|
### v1.14.8-dev.1 — Dev 预发布(v1.14.7 以来 7 个 PR)
|
|
450
469
|
|
|
451
470
|
Dev 预发布,涵盖 PR #238–#242。发布到 `dev` npm tag 供早期测试。
|
package/dist/index.js
CHANGED
|
@@ -3040,8 +3040,79 @@ ${hint}` : body);
|
|
|
3040
3040
|
if (startReference.rawIndex > endReference.rawIndex) {
|
|
3041
3041
|
[startReference, endReference] = [endReference, startReference];
|
|
3042
3042
|
}
|
|
3043
|
+
const adjusted = adjustBoundariesForToolPairs(
|
|
3044
|
+
context,
|
|
3045
|
+
startReference.rawIndex,
|
|
3046
|
+
endReference.rawIndex
|
|
3047
|
+
);
|
|
3048
|
+
if (adjusted.startIndex < startReference.rawIndex && startReference.kind === "message") {
|
|
3049
|
+
const msg = context.rawMessages[adjusted.startIndex];
|
|
3050
|
+
if (msg) {
|
|
3051
|
+
startReference = {
|
|
3052
|
+
kind: "message",
|
|
3053
|
+
rawIndex: adjusted.startIndex,
|
|
3054
|
+
messageId: msg.info.id
|
|
3055
|
+
};
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
if (adjusted.endIndex > endReference.rawIndex && endReference.kind === "message") {
|
|
3059
|
+
const msg = context.rawMessages[adjusted.endIndex];
|
|
3060
|
+
if (msg) {
|
|
3061
|
+
endReference = {
|
|
3062
|
+
kind: "message",
|
|
3063
|
+
rawIndex: adjusted.endIndex,
|
|
3064
|
+
messageId: msg.info.id
|
|
3065
|
+
};
|
|
3066
|
+
}
|
|
3067
|
+
}
|
|
3043
3068
|
return { startReference, endReference };
|
|
3044
3069
|
}
|
|
3070
|
+
function adjustBoundariesForToolPairs(context, startIdx, endIdx) {
|
|
3071
|
+
const messages = context.rawMessages;
|
|
3072
|
+
const callIdsInRange = /* @__PURE__ */ new Set();
|
|
3073
|
+
for (let i = startIdx; i <= endIdx; i++) {
|
|
3074
|
+
const msg = messages[i];
|
|
3075
|
+
if (!msg) continue;
|
|
3076
|
+
const parts = Array.isArray(msg.parts) ? msg.parts : [];
|
|
3077
|
+
for (const part of parts) {
|
|
3078
|
+
if (part.type !== "tool" || !part.callID) continue;
|
|
3079
|
+
if (part.tool === "compress") continue;
|
|
3080
|
+
callIdsInRange.add(part.callID);
|
|
3081
|
+
}
|
|
3082
|
+
}
|
|
3083
|
+
if (callIdsInRange.size === 0) {
|
|
3084
|
+
return { startIndex: startIdx, endIndex: endIdx };
|
|
3085
|
+
}
|
|
3086
|
+
let newEndIdx = endIdx;
|
|
3087
|
+
for (let i = endIdx + 1; i < messages.length && i <= endIdx + 20; i++) {
|
|
3088
|
+
const msg = messages[i];
|
|
3089
|
+
if (!msg) break;
|
|
3090
|
+
const parts = Array.isArray(msg.parts) ? msg.parts : [];
|
|
3091
|
+
const hasMatch = parts.some(
|
|
3092
|
+
(part) => part.type === "tool" && part.callID && callIdsInRange.has(part.callID)
|
|
3093
|
+
);
|
|
3094
|
+
if (hasMatch) {
|
|
3095
|
+
newEndIdx = i;
|
|
3096
|
+
} else if (newEndIdx > endIdx) {
|
|
3097
|
+
break;
|
|
3098
|
+
}
|
|
3099
|
+
}
|
|
3100
|
+
let newStartIdx = startIdx;
|
|
3101
|
+
for (let i = startIdx - 1; i >= 0 && i >= startIdx - 20; i--) {
|
|
3102
|
+
const msg = messages[i];
|
|
3103
|
+
if (!msg) break;
|
|
3104
|
+
const parts = Array.isArray(msg.parts) ? msg.parts : [];
|
|
3105
|
+
const hasMatch = parts.some(
|
|
3106
|
+
(part) => part.type === "tool" && part.callID && callIdsInRange.has(part.callID)
|
|
3107
|
+
);
|
|
3108
|
+
if (hasMatch) {
|
|
3109
|
+
newStartIdx = i;
|
|
3110
|
+
} else if (newStartIdx < startIdx) {
|
|
3111
|
+
break;
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
3114
|
+
return { startIndex: newStartIdx, endIndex: newEndIdx };
|
|
3115
|
+
}
|
|
3045
3116
|
function buildBoundaryRecoveryHint(context, state) {
|
|
3046
3117
|
const visibleRefs = [];
|
|
3047
3118
|
for (const [messageRef, messageId] of state.messageIds.byRef) {
|
|
@@ -7609,6 +7680,8 @@ Breakdown: ${sysPart}${fmt(composition.toolTokens)} tool (${pct2(composition.too
|
|
|
7609
7680
|
if (recommendedRanges.length > 0) {
|
|
7610
7681
|
breakdown += `
|
|
7611
7682
|
|
|
7683
|
+
${HOW_TO_COMPRESS_RULES}
|
|
7684
|
+
|
|
7612
7685
|
${formatCompressibleRanges(recommendedRanges, contextRanges.protected)}`;
|
|
7613
7686
|
breakdown += `
|
|
7614
7687
|
\u{1F4A1} Compress all ranges in one call (pass multiple content entries: \`content: [{...}, {...}]\`).`;
|
|
@@ -7618,7 +7691,7 @@ Use \`acp_status({scope:"uncompressed"})\` to re-fetch compressible ranges after
|
|
|
7618
7691
|
appendToLastTextPart(suffixMessage, breakdown);
|
|
7619
7692
|
}
|
|
7620
7693
|
if (effectiveTipsVariant === "maxLimit") {
|
|
7621
|
-
tipsText =
|
|
7694
|
+
tipsText = "\n\n\u26A0\uFE0F Context limit reached \u2014 compress now. Prioritize consumed tool outputs.\n\n" + HOW_TO_COMPRESS_RULES + '\n\n{ "topic": "...", "content": [{ "startId": "<ID>", "endId": "<ID>", "summary": "..." }] }\n\nOnly use IDs from visible messages above. Compress older work first.';
|
|
7622
7695
|
}
|
|
7623
7696
|
state.nudges.lastNudgeShownTokens = currentTokens;
|
|
7624
7697
|
{
|
|
@@ -8258,6 +8331,64 @@ function createDecompressTool(factoryCtx) {
|
|
|
8258
8331
|
|
|
8259
8332
|
// lib/compress/status.ts
|
|
8260
8333
|
import { tool as tool4 } from "@opencode-ai/plugin";
|
|
8334
|
+
|
|
8335
|
+
// lib/compress/parts.ts
|
|
8336
|
+
var STRUCTURAL_PART_TYPES = /* @__PURE__ */ new Set(["step-start", "step-finish", "reasoning"]);
|
|
8337
|
+
function hasMeaningfulContent(parts) {
|
|
8338
|
+
return parts.some((p) => !STRUCTURAL_PART_TYPES.has(p.type));
|
|
8339
|
+
}
|
|
8340
|
+
|
|
8341
|
+
// lib/compress/hide-consumed.ts
|
|
8342
|
+
var KEEP_LAST_ORPHANED = 2;
|
|
8343
|
+
function hideConsumedCompressCalls(state, messages) {
|
|
8344
|
+
const activeCallIds = /* @__PURE__ */ new Set();
|
|
8345
|
+
const allBlockCallIds = /* @__PURE__ */ new Set();
|
|
8346
|
+
for (const block of state.prune.messages.blocksById.values()) {
|
|
8347
|
+
if (block.compressCallId) {
|
|
8348
|
+
allBlockCallIds.add(block.compressCallId);
|
|
8349
|
+
if (block.active && !block.deactivatedByUser && !block.deactivatedByUserDeep) {
|
|
8350
|
+
activeCallIds.add(block.compressCallId);
|
|
8351
|
+
}
|
|
8352
|
+
}
|
|
8353
|
+
}
|
|
8354
|
+
const lastOrphanedCallIds = [];
|
|
8355
|
+
for (let i = messages.length - 1; i >= 0 && lastOrphanedCallIds.length < KEEP_LAST_ORPHANED; i--) {
|
|
8356
|
+
const parts = Array.isArray(messages[i]?.parts) ? messages[i].parts : [];
|
|
8357
|
+
for (let j = parts.length - 1; j >= 0 && lastOrphanedCallIds.length < KEEP_LAST_ORPHANED; j--) {
|
|
8358
|
+
const p = parts[j];
|
|
8359
|
+
if (p.type === "tool" && p.tool === "compress" && p.callID && !allBlockCallIds.has(p.callID)) {
|
|
8360
|
+
lastOrphanedCallIds.push(p.callID);
|
|
8361
|
+
}
|
|
8362
|
+
}
|
|
8363
|
+
}
|
|
8364
|
+
const keepCallIds = /* @__PURE__ */ new Set([...activeCallIds, ...lastOrphanedCallIds]);
|
|
8365
|
+
let hidden = 0;
|
|
8366
|
+
for (let i = 0; i < messages.length; i++) {
|
|
8367
|
+
const msg = messages[i];
|
|
8368
|
+
const parts = Array.isArray(msg.parts) ? msg.parts : [];
|
|
8369
|
+
let changed = false;
|
|
8370
|
+
const remaining = parts.filter((p) => {
|
|
8371
|
+
if (p.type === "tool" && p.tool === "compress") {
|
|
8372
|
+
if (p.callID && keepCallIds.has(p.callID)) return true;
|
|
8373
|
+
hidden++;
|
|
8374
|
+
changed = true;
|
|
8375
|
+
return false;
|
|
8376
|
+
}
|
|
8377
|
+
return true;
|
|
8378
|
+
});
|
|
8379
|
+
if (changed) {
|
|
8380
|
+
if (hasMeaningfulContent(remaining)) {
|
|
8381
|
+
messages[i] = { ...msg, parts: remaining };
|
|
8382
|
+
} else {
|
|
8383
|
+
messages.splice(i, 1);
|
|
8384
|
+
i--;
|
|
8385
|
+
}
|
|
8386
|
+
}
|
|
8387
|
+
}
|
|
8388
|
+
return hidden;
|
|
8389
|
+
}
|
|
8390
|
+
|
|
8391
|
+
// lib/compress/status.ts
|
|
8261
8392
|
var ACP_STATUS_TOOL_DESCRIPTION = `Show context status \u2014 overview includes compressible ranges by default.
|
|
8262
8393
|
|
|
8263
8394
|
No args: Overview with totals, compressed blocks, and compressible ranges.
|
|
@@ -8648,6 +8779,7 @@ function createAcpStatusTool(factoryCtx) {
|
|
|
8648
8779
|
if (scope === "uncompressed") return "(unable to fetch messages)";
|
|
8649
8780
|
rawMessages = [];
|
|
8650
8781
|
}
|
|
8782
|
+
hideConsumedCompressCalls(ctx.state, rawMessages);
|
|
8651
8783
|
return buildStatusReport(
|
|
8652
8784
|
{ state: ctx.state, config: ctx.config },
|
|
8653
8785
|
rawMessages,
|
|
@@ -8714,55 +8846,6 @@ Call with blockId to get the full summary: acp_context_recap({ blockId: N })`);
|
|
|
8714
8846
|
});
|
|
8715
8847
|
}
|
|
8716
8848
|
|
|
8717
|
-
// lib/compress/parts.ts
|
|
8718
|
-
var STRUCTURAL_PART_TYPES = /* @__PURE__ */ new Set(["step-start", "step-finish", "reasoning"]);
|
|
8719
|
-
function hasMeaningfulContent(parts) {
|
|
8720
|
-
return parts.some((p) => !STRUCTURAL_PART_TYPES.has(p.type));
|
|
8721
|
-
}
|
|
8722
|
-
|
|
8723
|
-
// lib/compress/hide-consumed.ts
|
|
8724
|
-
function hideConsumedCompressCalls(state, messages) {
|
|
8725
|
-
if (state.prune.messages.blocksById.size === 0) {
|
|
8726
|
-
return 0;
|
|
8727
|
-
}
|
|
8728
|
-
const consumedMessageIds = /* @__PURE__ */ new Set();
|
|
8729
|
-
for (const block of state.prune.messages.blocksById.values()) {
|
|
8730
|
-
if (block.active) continue;
|
|
8731
|
-
if (block.deactivatedByUser) continue;
|
|
8732
|
-
if (block.deactivatedByUserDeep) continue;
|
|
8733
|
-
if (block.deactivatedByBlockId === void 0) continue;
|
|
8734
|
-
if (!block.compressMessageId) continue;
|
|
8735
|
-
consumedMessageIds.add(block.compressMessageId);
|
|
8736
|
-
}
|
|
8737
|
-
if (consumedMessageIds.size === 0) {
|
|
8738
|
-
return 0;
|
|
8739
|
-
}
|
|
8740
|
-
let hidden = 0;
|
|
8741
|
-
for (let i = 0; i < messages.length; i++) {
|
|
8742
|
-
const msg = messages[i];
|
|
8743
|
-
if (!consumedMessageIds.has(msg.info.id)) continue;
|
|
8744
|
-
const parts = Array.isArray(msg.parts) ? msg.parts : [];
|
|
8745
|
-
let changed = false;
|
|
8746
|
-
const remaining = parts.filter((p) => {
|
|
8747
|
-
if (p.type === "tool" && p.tool === "compress") {
|
|
8748
|
-
hidden++;
|
|
8749
|
-
changed = true;
|
|
8750
|
-
return false;
|
|
8751
|
-
}
|
|
8752
|
-
return true;
|
|
8753
|
-
});
|
|
8754
|
-
if (changed) {
|
|
8755
|
-
if (hasMeaningfulContent(remaining)) {
|
|
8756
|
-
messages[i] = { ...msg, parts: remaining };
|
|
8757
|
-
} else {
|
|
8758
|
-
messages.splice(i, 1);
|
|
8759
|
-
i--;
|
|
8760
|
-
}
|
|
8761
|
-
}
|
|
8762
|
-
}
|
|
8763
|
-
return hidden;
|
|
8764
|
-
}
|
|
8765
|
-
|
|
8766
8849
|
// lib/compress/hide-failed.ts
|
|
8767
8850
|
function hideFailedCompressCalls(messages) {
|
|
8768
8851
|
let mostRecentFailedId;
|
|
@@ -8809,7 +8892,7 @@ import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
|
8809
8892
|
import { join as join3 } from "path";
|
|
8810
8893
|
import { existsSync as existsSync3 } from "fs";
|
|
8811
8894
|
import { homedir as homedir3 } from "os";
|
|
8812
|
-
var LOG_VERSION = true ? "1.14.8-dev.
|
|
8895
|
+
var LOG_VERSION = true ? "1.14.8-dev.3" : "dev";
|
|
8813
8896
|
var Logger = class {
|
|
8814
8897
|
logDir;
|
|
8815
8898
|
enabled;
|