opencode-acp 1.14.8 → 1.14.9-dev.2
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 +47 -0
- package/README.zh-CN.md +47 -0
- package/dist/index.js +85 -101
- package/dist/index.js.map +1 -1
- package/dist/lib/config-validation.d.ts.map +1 -1
- package/dist/lib/messages/filter/builtin/omo-mode-injection.d.ts.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/dist/lib/prompts/system.d.ts +1 -1
- package/dist/lib/prompts/system.d.ts.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -492,6 +492,53 @@ For the complete list with root cause analysis, see the [bug tracker](https://gi
|
|
|
492
492
|
|
|
493
493
|
## Changelog
|
|
494
494
|
|
|
495
|
+
### v1.14.9 — Stable Release (3 PRs since v1.14.8)
|
|
496
|
+
|
|
497
|
+
Stable release with nudge loop fix, holistic T2/T3 compression prompts, and multi-entry compress format documentation.
|
|
498
|
+
|
|
499
|
+
**PRs included**:
|
|
500
|
+
- **#252** — Two fixes: (1) **Issue #251**: `filterRecommendedRanges` suppressed ALL recommendations when compressible content was below 5% of modelContextLimit. Rewrote to always show all ranges. (2) **Nudge loop fix**: `lastNudgeShownTokens` reset to `undefined` on `nothingToCompress` caused nudges firing every turn.
|
|
501
|
+
- **#257** — Bump `context-compress-algorithms` 1.2.1 → 1.3.0. Holistic TIER2/TIER3 prompts — summarize by theme instead of per-block, fixing length overflow with 70+ T1 blocks (issue #256).
|
|
502
|
+
- **#259** — Document multi-entry compress format in system prompt and T2/T3 nudge. The compress tool already supported `content` arrays (PR #156), but only single-entry was shown for block-based compression.
|
|
503
|
+
|
|
504
|
+
**Install**: `opencode plugin opencode-acp@stable --global`
|
|
505
|
+
|
|
506
|
+
### v1.14.9-dev.2 — Dev Prerelease (1 PR since v1.14.9-dev.1)
|
|
507
|
+
|
|
508
|
+
Dev prerelease covering PR #263. Published to `dev` npm tag for early testing.
|
|
509
|
+
|
|
510
|
+
**PRs included**:
|
|
511
|
+
- **#263** — Fix: `omo-mode-injection` filter was dropping entire user messages when OMO mode injections (`<ultrawork-mode>`, `[search-mode]`, etc.) were prepended to user content. Mode injections are prepended via `UserPromptSubmit.additionalContext`, not standalone — the old filter (v1.0.0) matched the injection and returned `drop`, clearing the entire message including the user's actual request. Rewrote to v1.1.0: strips injection blocks and preserves user content via `modify`. Also fixes config validation (messageFilters.filters dynamic key skip) and adds messageFilters to dcp.schema.json.
|
|
512
|
+
|
|
513
|
+
**Install**: `opencode plugin opencode-acp@dev --global`
|
|
514
|
+
|
|
515
|
+
### v1.14.9-dev.1 — Dev Prerelease (1 PR since v1.14.8-dev.5)
|
|
516
|
+
|
|
517
|
+
Dev prerelease covering PR #259. Published to `dev` npm tag for early testing.
|
|
518
|
+
|
|
519
|
+
**PRs included**:
|
|
520
|
+
- **#259** — Document multi-entry compress format in system prompt and T2/T3 nudge. The compress tool already supported `content` arrays (PR #156), but only single-entry was shown for block-based compression. Now both formats are documented neutrally.
|
|
521
|
+
|
|
522
|
+
**Install**: `opencode plugin opencode-acp@dev --global`
|
|
523
|
+
|
|
524
|
+
### v1.14.8-dev.5 — Dev Prerelease (1 PR since v1.14.8-dev.4)
|
|
525
|
+
|
|
526
|
+
Dev prerelease covering PR #257. Published to `dev` npm tag for early testing.
|
|
527
|
+
|
|
528
|
+
**PRs included**:
|
|
529
|
+
- **#257** — Bump `context-compress-algorithms` from 1.2.1 to 1.3.0. cc-alg 1.3.0 ships holistic TIER2/TIER3 compression prompts — summarize by theme instead of per-block. The old per-block format (Source header + 3-5 bullets + 50-150 tokens per block) caused length overflow when compressing 70+ T1 blocks (~30K chars exceeding `maxSummaryLengthHard`). No source code changes needed (prompts consumed from dependency).
|
|
530
|
+
|
|
531
|
+
**Install**: `opencode plugin opencode-acp@dev --global`
|
|
532
|
+
|
|
533
|
+
### v1.14.8-dev.4 — Dev Prerelease (1 PR since v1.14.8-dev.3)
|
|
534
|
+
|
|
535
|
+
Dev prerelease covering PR #252. Published to `dev` npm tag for early testing.
|
|
536
|
+
|
|
537
|
+
**PRs included**:
|
|
538
|
+
- **#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.
|
|
539
|
+
|
|
540
|
+
**Install**: `opencode plugin opencode-acp@dev --global`
|
|
541
|
+
|
|
495
542
|
### v1.14.8 — Stable Release (10 PRs since v1.14.7)
|
|
496
543
|
|
|
497
544
|
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.
|
package/README.zh-CN.md
CHANGED
|
@@ -446,6 +446,53 @@ ACP 是 DCP 的直接替代品。迁移步骤:
|
|
|
446
446
|
|
|
447
447
|
## 更新日志
|
|
448
448
|
|
|
449
|
+
### v1.14.9 — 正式版(v1.14.8 以来 3 个 PR)
|
|
450
|
+
|
|
451
|
+
正式版发布,包含 nudge 循环修复、整体式 T2/T3 压缩提示词、多条目 compress 格式文档。
|
|
452
|
+
|
|
453
|
+
**包含的 PR**:
|
|
454
|
+
- **#252** — 两个修复:(1) **Issue #251**:`filterRecommendedRanges` 在可压缩内容低于 modelContextLimit 的 5% 时抑制所有推荐。重写为始终显示所有范围。(2) **Nudge 循环修复**:`lastNudgeShownTokens` 在 `nothingToCompress` 时被重置为 `undefined`,导致每轮都触发 nudge。
|
|
455
|
+
- **#257** — 升级 `context-compress-algorithms` 1.2.1 → 1.3.0。整体式 TIER2/TIER3 提示词 — 按主题综述而非逐块处理,修复 70+ T1 块时的长度溢出问题(issue #256)。
|
|
456
|
+
- **#259** — 在系统提示词和 T2/T3 nudge 中文档化多条目 compress 格式。compress 工具已支持 `content` 数组(PR #156),但块压缩只展示了单条目格式。
|
|
457
|
+
|
|
458
|
+
**安装**:`opencode plugin opencode-acp@stable --global`
|
|
459
|
+
|
|
460
|
+
### v1.14.9-dev.2 — Dev 预发布(v1.14.9-dev.1 以来 1 个 PR)
|
|
461
|
+
|
|
462
|
+
Dev 预发布,覆盖 PR #263。已发布到 `dev` npm 标签供早期测试。
|
|
463
|
+
|
|
464
|
+
**包含的 PR**:
|
|
465
|
+
- **#263** — 修复:`omo-mode-injection` 过滤器在 OMO 模式注入(`<ultrawork-mode>`、`[search-mode]` 等)被前置到用户消息时,会丢弃整个用户消息。模式注入通过 `UserPromptSubmit.additionalContext` 前置,不是独立消息——旧过滤器(v1.0.0)匹配到注入就返回 `drop`,清空了整个消息包括用户的实际请求。重写为 v1.1.0:剥离注入块、通过 `modify` 保留用户内容。同时修复配置校验(messageFilters.filters 动态键跳过)并补充 dcp.schema.json。
|
|
466
|
+
|
|
467
|
+
**安装**:`opencode plugin opencode-acp@dev --global`
|
|
468
|
+
|
|
469
|
+
### v1.14.9-dev.1 — Dev 预发布(v1.14.8-dev.5 以来 1 个 PR)
|
|
470
|
+
|
|
471
|
+
Dev 预发布,涵盖 PR #259。发布到 `dev` npm tag 供早期测试。
|
|
472
|
+
|
|
473
|
+
**包含的 PR**:
|
|
474
|
+
- **#259** — 在系统提示词和 T2/T3 nudge 中文档化多条目 compress 格式。compress 工具已支持 `content` 数组(PR #156),但块压缩只展示了单条目格式。现在两种格式都被中立地文档化。
|
|
475
|
+
|
|
476
|
+
**安装**:`opencode plugin opencode-acp@dev --global`
|
|
477
|
+
|
|
478
|
+
### v1.14.8-dev.5 — Dev 预发布(v1.14.8-dev.4 以来 1 个 PR)
|
|
479
|
+
|
|
480
|
+
Dev 预发布,涵盖 PR #257。发布到 `dev` npm tag 供早期测试。
|
|
481
|
+
|
|
482
|
+
**包含的 PR**:
|
|
483
|
+
- **#257** — 升级 `context-compress-algorithms` 从 1.2.1 到 1.3.0。cc-alg 1.3.0 提供整体式 TIER2/TIER3 压缩提示——按主题综合而非逐块处理。旧的逐块格式(Source 头 + 每块 3-5 条 + 每块 50-150 tokens)在压缩 70+ 个 T1 块时导致长度溢出(~30K 字符超过 `maxSummaryLengthHard`)。无需源码修改(提示从依赖中获取)。
|
|
484
|
+
|
|
485
|
+
**安装**: `opencode plugin opencode-acp@dev --global`
|
|
486
|
+
|
|
487
|
+
### v1.14.8-dev.4 — Dev 预发布(v1.14.8-dev.3 以来 1 个 PR)
|
|
488
|
+
|
|
489
|
+
Dev 预发布,涵盖 PR #252。发布到 `dev` npm tag 供早期测试。
|
|
490
|
+
|
|
491
|
+
**包含的 PR**:
|
|
492
|
+
- **#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:均通过。
|
|
493
|
+
|
|
494
|
+
**安装**:`opencode plugin opencode-acp@dev --global`
|
|
495
|
+
|
|
449
496
|
### v1.14.8-dev.3 — Dev 预发布(v1.14.8-dev.2 以来 1 个 PR)
|
|
450
497
|
|
|
451
498
|
Dev 预发布,涵盖 PR #248。发布到 `dev` npm tag 供早期测试。
|
package/dist/index.js
CHANGED
|
@@ -929,7 +929,7 @@ function getConfigKeyPaths(obj, prefix = "") {
|
|
|
929
929
|
for (const key of Object.keys(obj)) {
|
|
930
930
|
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
931
931
|
keys.push(fullKey);
|
|
932
|
-
if (fullKey === "compress.modelMaxLimits" || fullKey === "compress.modelMinLimits") {
|
|
932
|
+
if (fullKey === "compress.modelMaxLimits" || fullKey === "compress.modelMinLimits" || fullKey === "messageFilters.filters") {
|
|
933
933
|
continue;
|
|
934
934
|
}
|
|
935
935
|
if (obj[key] && typeof obj[key] === "object" && !Array.isArray(obj[key])) {
|
|
@@ -5500,7 +5500,7 @@ function evaluatePreCommitQuality(rawMessages, messageIds, messageTokenById, sum
|
|
|
5500
5500
|
}
|
|
5501
5501
|
}
|
|
5502
5502
|
|
|
5503
|
-
// node_modules/context-compress-algorithms/dist/chunk-
|
|
5503
|
+
// node_modules/context-compress-algorithms/dist/chunk-YCHW53QG.js
|
|
5504
5504
|
var COMPRESS_PHILOSOPHY = `Compression Philosophy:
|
|
5505
5505
|
- All compression serves the primary task, but be frugal.
|
|
5506
5506
|
- Context capacity is precious. Save context by compressing consumed outputs, not by avoiding tools.
|
|
@@ -5547,7 +5547,7 @@ PRIORITY \u2014 when the summary must be compact, preserve in this order:
|
|
|
5547
5547
|
Write dense, scannable bullets \u2014 not narrative prose. If the range spans distinct concerns (request \u2192 findings \u2192 decision), group bullets under short thematic headers so a reader can scan to the part they need. Every line must earn its place. Do not mimic the style of existing summaries in context; follow these rules.`;
|
|
5548
5548
|
var TIER2_DISTILL_RULES = `TIER 2 COMPRESSION \u2014 DISTILLATION
|
|
5549
5549
|
|
|
5550
|
-
You are compressing historical summaries (not raw conversation). These summaries have already captured the details. Your job is to DISTILL them:
|
|
5550
|
+
You are compressing historical summaries (not raw conversation). These summaries have already captured the details. Your job is to DISTILL them: write a holistic summary of what matters for future work, discarding the process.
|
|
5551
5551
|
|
|
5552
5552
|
KEEP \u2014 these are the only things that survive distillation:
|
|
5553
5553
|
- Decisions and their rationale ("chose X over Y because Z" \u2014 the "because" is load-bearing).
|
|
@@ -5555,9 +5555,9 @@ KEEP \u2014 these are the only things that survive distillation:
|
|
|
5555
5555
|
- Key lessons: what failed and why ("tried X, failed because Y"). These prevent repeating mistakes.
|
|
5556
5556
|
- Critical constraints discovered ("must support Node 22", "AGENTS.md forbids as any").
|
|
5557
5557
|
- Design decisions with architectural impact ("chose compress-as-anchor over synthetic messages because prefix cache").
|
|
5558
|
-
- Whether content is OBSOLETE or SUPERSEDED \u2014 mark with one line: "[SUPERSEDED by PR #NNN]" or "[OBSOLETE: deleted in vX.Y.Z]".
|
|
5559
|
-
- Function/class/type names and module paths that are the SUBJECT of the work \u2014 e.g., "fixed filterCompressedRanges in prune.ts"
|
|
5560
|
-
- Exploration findings: if a block was exploratory with no decision, keep the CONCLUSION in one line
|
|
5558
|
+
- Whether content is OBSOLETE or SUPERSEDED \u2014 mark with one line: "[SUPERSEDED by PR #NNN]" or "[OBSOLETE: deleted in vX.Y.Z]".
|
|
5559
|
+
- Function/class/type names and module paths that are the SUBJECT of the work \u2014 e.g., "fixed filterCompressedRanges in prune.ts". Not exact line numbers or full signatures \u2014 just enough to LOCATE the code.
|
|
5560
|
+
- Exploration findings: if a block was exploratory with no decision, keep the CONCLUSION in one line.
|
|
5561
5561
|
|
|
5562
5562
|
DROP \u2014 these were useful during the work but are no longer needed:
|
|
5563
5563
|
- Exact line numbers, diffs, verbose function signatures, full code listings.
|
|
@@ -5566,20 +5566,17 @@ DROP \u2014 these were useful during the work but are no longer needed:
|
|
|
5566
5566
|
- Verbose logs, command output, intermediate debugging steps.
|
|
5567
5567
|
|
|
5568
5568
|
FORMAT:
|
|
5569
|
-
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
-
|
|
5573
|
-
- Dense, scannable \u2014 no narrative prose.
|
|
5574
|
-
-
|
|
5575
|
-
- Cross-block synthesis: if multiple source blocks cover the same topic (same PR, same feature, same bug), MERGE them into a single group of bullets. Do not repeat the same fact from different blocks \u2014 keep it once under the most relevant source header.
|
|
5576
|
-
|
|
5577
|
-
SIZE TARGET: 50-150 tokens per source block (excluding the header). If you can't fit it in 150 tokens, you're keeping too much process. If a block has nothing worth keeping (pure noise), output just the header followed by "[no actionable content]".`;
|
|
5569
|
+
- Write a HOLISTIC summary grouped by THEME, not by source block.
|
|
5570
|
+
- NO per-block headers. NO "Source: bN" lines. This is a review of what happened, not a catalog of blocks.
|
|
5571
|
+
- Group related work together: all releases in one section, all bug fixes in another, all architecture decisions in a third.
|
|
5572
|
+
- Start with the most important outcomes (shipped releases, merged PRs, critical bugs).
|
|
5573
|
+
- Dense, scannable bullets \u2014 no narrative prose.
|
|
5574
|
+
- Most blocks will collapse into 1-2 bullets within a theme group. Many will have nothing worth keeping \u2014 omit them entirely.`;
|
|
5578
5575
|
var TIER3_CONDENSE_RULES = `TIER 3 COMPRESSION \u2014 ULTRA-CONDENSATION
|
|
5579
5576
|
|
|
5580
5577
|
You are compressing distilled summaries (Tier 2) into ultra-condensed facts (Tier 3). The distilled summaries already contain only decisions and outcomes. Your job is to reduce them to bare factual references.
|
|
5581
5578
|
|
|
5582
|
-
PRIORITY \u2014
|
|
5579
|
+
PRIORITY \u2014 keep in this order:
|
|
5583
5580
|
1. Shipped outcomes (versions released, PRs merged) \u2014 these are permanent record.
|
|
5584
5581
|
2. Open work (PRs/issues still pending) \u2014 these may need follow-up.
|
|
5585
5582
|
3. Key decisions with architectural impact ("chose X over Y because Z").
|
|
@@ -5587,12 +5584,11 @@ PRIORITY \u2014 when a source block has more facts than the size target allows,
|
|
|
5587
5584
|
Drop everything else. Tier 3 is a lookup index, not a knowledge base.
|
|
5588
5585
|
|
|
5589
5586
|
FORMAT:
|
|
5590
|
-
-
|
|
5591
|
-
|
|
5592
|
-
-
|
|
5587
|
+
- Write a HOLISTIC list of bare facts, grouped by theme \u2014 not by source block.
|
|
5588
|
+
- NO per-block headers. NO "Source: bN" lines.
|
|
5589
|
+
- Each fact is a single line: "[PR/Issue/Version] \u2014 [outcome in \u22648 words]"
|
|
5590
|
+
- Merge related facts aggressively. If multiple blocks concern the same release/feature/bug, output ONE line covering all of them.
|
|
5593
5591
|
- No explanations, no rationale, no process \u2014 just the fact.
|
|
5594
|
-
- Format: "[PR/Issue/Version] \u2014 [outcome in \u22648 words]"
|
|
5595
|
-
- Merge related facts from different source blocks if they concern the same topic.
|
|
5596
5592
|
|
|
5597
5593
|
EXAMPLES:
|
|
5598
5594
|
- "v1.13.0 shipped \u2014 quality gate + GC fix (7 PRs)"
|
|
@@ -5605,9 +5601,7 @@ DROP:
|
|
|
5605
5601
|
- Multi-sentence context. If a fact needs >1 sentence, it's too detailed for Tier 3.
|
|
5606
5602
|
- Lessons learned ("tried X, failed because Y") \u2014 drop UNLESS the failure is likely to recur and the block is <30 days old.
|
|
5607
5603
|
- Design rationale details \u2014 keep the decision, drop the "because" unless it's a critical constraint.
|
|
5608
|
-
- Anything marked [OBSOLETE] or [SUPERSEDED] \u2014 drop entirely, note "[N blocks obsolete]"
|
|
5609
|
-
|
|
5610
|
-
SIZE TARGET: 30-60 tokens per source block (including header). For a batch of N source blocks, total output \u2248 N \xD7 40 tokens. If a source block has only one trivial fact, output just the header + one line.`;
|
|
5604
|
+
- Anything marked [OBSOLETE] or [SUPERSEDED] \u2014 drop entirely, note "[N blocks obsolete]" at the end.`;
|
|
5611
5605
|
|
|
5612
5606
|
// lib/compress/quality-gate/rejection.ts
|
|
5613
5607
|
function formatMetric(result, name) {
|
|
@@ -7168,62 +7162,19 @@ function buildCompressibleRanges(messages, state, protectedTools = [], protected
|
|
|
7168
7162
|
};
|
|
7169
7163
|
}
|
|
7170
7164
|
function filterRecommendedRanges(compressible, _protectedRanges, options) {
|
|
7171
|
-
const {
|
|
7165
|
+
const { logger } = options;
|
|
7172
7166
|
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
7167
|
if (compressible.length === 0) {
|
|
7186
7168
|
log?.("filterRecommendedRanges: no compressible ranges, returning empty");
|
|
7187
7169
|
return [];
|
|
7188
7170
|
}
|
|
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", {
|
|
7171
|
+
const result = compressible.map(
|
|
7172
|
+
(r, i) => i === compressible.length - 1 ? { ...r, dangerous: true } : r
|
|
7173
|
+
);
|
|
7174
|
+
log?.("filterRecommendedRanges: passthrough (last segment marked dangerous)", {
|
|
7211
7175
|
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
|
|
7176
|
+
outputRanges: result.length
|
|
7223
7177
|
});
|
|
7224
|
-
if (suppressed) {
|
|
7225
|
-
return [];
|
|
7226
|
-
}
|
|
7227
7178
|
return result;
|
|
7228
7179
|
}
|
|
7229
7180
|
function formatCompressibleRanges(ranges, protectedRanges) {
|
|
@@ -7537,37 +7488,24 @@ var injectCompressNudges = (state, config, logger, messages, prompts, compressio
|
|
|
7537
7488
|
const recommendedRanges = filterRecommendedRanges(
|
|
7538
7489
|
unprotectedCompressible,
|
|
7539
7490
|
contextRanges.protected,
|
|
7540
|
-
{
|
|
7491
|
+
{ logger }
|
|
7541
7492
|
);
|
|
7542
7493
|
const hasRecommendations = recommendedRanges.length > 0;
|
|
7543
|
-
if (config.debug && contextRanges.compressible.length > 0
|
|
7544
|
-
const growthThreshold = modelContextLimit * 0.05;
|
|
7545
|
-
const lastSegmentFloor = growthThreshold * 2;
|
|
7494
|
+
if (config.debug && contextRanges.compressible.length > 0) {
|
|
7546
7495
|
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
7496
|
const fmt = (n) => n >= 1e3 ? `${(n / 1e3).toFixed(1)}K` : String(n);
|
|
7553
7497
|
const lines = [
|
|
7554
|
-
`[ACP Debug] Recommendation filter
|
|
7498
|
+
`[ACP Debug] Recommendation filter:`,
|
|
7555
7499
|
` 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`}`
|
|
7500
|
+
` Output: ${recommendedRanges.length} range(s) (last segment marked dangerous)`
|
|
7559
7501
|
];
|
|
7560
7502
|
logger.debug(lines.join("\n"));
|
|
7561
7503
|
}
|
|
7562
|
-
const filterSuppressed = contextRanges.compressible.length > 0 && !hasRecommendations;
|
|
7563
7504
|
const allProtected = contextRanges.compressible.length === 0 && contextRanges.protected.length > 0;
|
|
7564
7505
|
const allInProtectedZone = protectedRefs.size > 0 && unprotectedCompressible.length === 0;
|
|
7565
|
-
const nothingToCompress =
|
|
7506
|
+
const nothingToCompress = allProtected || allInProtectedZone;
|
|
7566
7507
|
const shouldInjectNudge = nudgeAllowed && (!nothingToCompress || emergencyOverride);
|
|
7567
7508
|
let shouldInject = shouldInjectNudge;
|
|
7568
|
-
if (nudgeAllowed && nothingToCompress && !emergencyOverride) {
|
|
7569
|
-
state.nudges.lastNudgeShownTokens = void 0;
|
|
7570
|
-
}
|
|
7571
7509
|
if (shouldInjectNudge) {
|
|
7572
7510
|
applyAnchoredNudges(state, config, messages, prompts, compressionPriorities, currentTokens, modelContextLimit, suffixMessage);
|
|
7573
7511
|
}
|
|
@@ -7644,6 +7582,7 @@ Target blocks (oldest first):
|
|
|
7644
7582
|
${blockList}${extraCount}
|
|
7645
7583
|
|
|
7646
7584
|
Compress range: \`content: [{ startId: "b${firstBlock.blockId}", endId: "b${lastBlock.blockId}", summary: "..." }]\`
|
|
7585
|
+
Multiple entries create separate blocks: \`content: [{ startId: "b${firstBlock.blockId}", endId: "b...", summary: "..." }, { startId: "b...", endId: "b${lastBlock.blockId}", summary: "..." }]\`
|
|
7647
7586
|
|
|
7648
7587
|
${rules}`;
|
|
7649
7588
|
appendToLastTextPart(suffixMessage, tierText);
|
|
@@ -7662,7 +7601,7 @@ ${rules}`;
|
|
|
7662
7601
|
if (suffixMessage && composition.total > 0) {
|
|
7663
7602
|
const fmt = (n) => n >= 1e3 ? `${(n / 1e3).toFixed(1)}K` : String(n);
|
|
7664
7603
|
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;
|
|
7604
|
+
const growth = currentTokens !== void 0 && (state.nudges.lastNudgeShownTokens ?? state.nudges.lastPerMessageNudgeTokens) !== void 0 ? currentTokens - (state.nudges.lastNudgeShownTokens ?? state.nudges.lastPerMessageNudgeTokens) : 0;
|
|
7666
7605
|
const growthStr = growth > 0 ? ` (+${fmt(growth)} since last nudge)` : "";
|
|
7667
7606
|
const plainTextTokens = composition.textTokens;
|
|
7668
7607
|
const efficiencyNote = effectiveTipsVariant !== "maxLimit" ? `
|
|
@@ -8892,7 +8831,7 @@ import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
|
8892
8831
|
import { join as join3 } from "path";
|
|
8893
8832
|
import { existsSync as existsSync3 } from "fs";
|
|
8894
8833
|
import { homedir as homedir3 } from "os";
|
|
8895
|
-
var LOG_VERSION = true ? "1.14.
|
|
8834
|
+
var LOG_VERSION = true ? "1.14.9-dev.2" : "dev";
|
|
8896
8835
|
var Logger = class {
|
|
8897
8836
|
logDir;
|
|
8898
8837
|
enabled;
|
|
@@ -9161,7 +9100,7 @@ Summaries accumulate as the session grows. When tier-1 summaries pile up, the sy
|
|
|
9161
9100
|
- Tier 2: Distillation of old tier-1 block summaries. Uses TIER 2 DISTILLATION rules (decisions/outcomes only, drop paths/code/process).
|
|
9162
9101
|
- Tier 3: Ultra-condensation of tier-2 summaries. Uses TIER 3 CONDENSATION rules (bare facts, 1-3 lines per block).
|
|
9163
9102
|
|
|
9164
|
-
To compress blocks: use block IDs as boundaries: \`compress({ content: [{ startId: "b3", endId: "b15", summary: "..." }] })\`. This deactivates the consumed blocks and creates a new higher-tier block. The system prompt at the trigger tells you which rules to follow.
|
|
9103
|
+
To compress blocks: use block IDs as boundaries: \`compress({ content: [{ startId: "b3", endId: "b15", summary: "..." }] })\`. Multiple entries create separate blocks: \`compress({ content: [{ startId: "b3", endId: "b10", summary: "..." }, { startId: "b11", endId: "b20", summary: "..." }] })\`. This deactivates the consumed blocks and creates a new higher-tier block per entry. The system prompt at the trigger tells you which rules to follow.
|
|
9165
9104
|
|
|
9166
9105
|
If you are unsure which \`mNNNNN\` refs are still compressible, or which blocks have already consumed which ranges, call \`acp_status\` first. It returns the visible context breakdown and the compressed block list.
|
|
9167
9106
|
|
|
@@ -10195,17 +10134,62 @@ var OMO_TASK_FILTER = {
|
|
|
10195
10134
|
};
|
|
10196
10135
|
|
|
10197
10136
|
// lib/messages/filter/builtin/omo-mode-injection.ts
|
|
10198
|
-
var
|
|
10137
|
+
var XML_MODE_TAGS = [
|
|
10138
|
+
{ open: "<hyperplan-ultrawork-mode>", close: "</hyperplan-ultrawork-mode>" },
|
|
10139
|
+
{ open: "<ultrawork-mode>", close: "</ultrawork-mode>" }
|
|
10140
|
+
];
|
|
10141
|
+
var BRACKET_MODE_PATTERNS = ["[search-mode]", "[analyze-mode]", "[ultrawork-mode]"];
|
|
10142
|
+
function stripLeadingModeInjections(text) {
|
|
10143
|
+
let current = text;
|
|
10144
|
+
let strippedAny = false;
|
|
10145
|
+
for (let iter = 0; iter < 5; iter++) {
|
|
10146
|
+
current = current.trimStart();
|
|
10147
|
+
let matched = false;
|
|
10148
|
+
for (const { open, close } of XML_MODE_TAGS) {
|
|
10149
|
+
if (current.startsWith(open)) {
|
|
10150
|
+
const closeIdx = current.indexOf(close);
|
|
10151
|
+
if (closeIdx !== -1) {
|
|
10152
|
+
current = current.slice(closeIdx + close.length);
|
|
10153
|
+
} else {
|
|
10154
|
+
current = current.slice(open.length);
|
|
10155
|
+
}
|
|
10156
|
+
matched = true;
|
|
10157
|
+
break;
|
|
10158
|
+
}
|
|
10159
|
+
}
|
|
10160
|
+
if (!matched) {
|
|
10161
|
+
for (const pattern of BRACKET_MODE_PATTERNS) {
|
|
10162
|
+
if (current.startsWith(pattern)) {
|
|
10163
|
+
current = current.slice(pattern.length);
|
|
10164
|
+
matched = true;
|
|
10165
|
+
break;
|
|
10166
|
+
}
|
|
10167
|
+
}
|
|
10168
|
+
}
|
|
10169
|
+
if (matched) {
|
|
10170
|
+
strippedAny = true;
|
|
10171
|
+
} else {
|
|
10172
|
+
break;
|
|
10173
|
+
}
|
|
10174
|
+
}
|
|
10175
|
+
return strippedAny ? current.trim() : null;
|
|
10176
|
+
}
|
|
10199
10177
|
var OMO_MODE_FILTER = {
|
|
10200
10178
|
name: "omo-mode-injection",
|
|
10201
|
-
version: "1.
|
|
10202
|
-
description: "Strip OMO mode injection blocks (
|
|
10179
|
+
version: "1.1.0",
|
|
10180
|
+
description: "Strip OMO mode injection blocks (<ultrawork-mode>...</ultrawork-mode>, [search-mode], etc.) from user messages, preserving user content",
|
|
10203
10181
|
filter(ctx) {
|
|
10204
10182
|
if (ctx.role !== "user") return { action: "keep" };
|
|
10205
|
-
const
|
|
10206
|
-
|
|
10207
|
-
if (
|
|
10208
|
-
|
|
10183
|
+
const remaining = stripLeadingModeInjections(ctx.text);
|
|
10184
|
+
if (remaining === null) return { action: "keep" };
|
|
10185
|
+
if (remaining.length === 0) {
|
|
10186
|
+
return { action: "drop", reason: "OMO mode injection (no user content after stripping)" };
|
|
10187
|
+
}
|
|
10188
|
+
return {
|
|
10189
|
+
action: "modify",
|
|
10190
|
+
text: remaining,
|
|
10191
|
+
reason: "Stripped OMO mode injection block(s), preserved user content"
|
|
10192
|
+
};
|
|
10209
10193
|
}
|
|
10210
10194
|
};
|
|
10211
10195
|
|