opencode-acp 1.11.0 → 1.11.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 CHANGED
@@ -422,6 +422,28 @@ For the complete list with root cause analysis, see the [bug tracker](https://gi
422
422
 
423
423
  ## Changelog
424
424
 
425
+ ### v1.11.2 — CI Enforcement & Auto-Publish (PR #104)
426
+
427
+ Added GitHub Actions CI to automate AGENTS.md compliance enforcement:
428
+
429
+ - **PR validation** (`pr-checks.yml`): every PR to master is checked for branch name convention (`YYYY-MM-DD_short-title`), devlog existence (`devlog/{branch}/REQ.md` + `WORKLOG.md`), and changelog updates on version bumps.
430
+ - **Auto-publish** (`release.yml`): pushing a `v*` tag triggers `npm ci` → `npm run check:package` → `npm test` → `npm publish` → GitHub Release, fully automated.
431
+ - Script: `scripts/ci/check-pr.sh` — reusable PR validation logic.
432
+
433
+ Requires `NPM_TOKEN` secret in GitHub repo settings.
434
+
435
+ ---
436
+
437
+ ### v1.11.1 — Compress Baseline Fix (PR #99)
438
+
439
+ **Problem**: When the model called `compress`, both `lastPerMessageNudgeTokens` and `lastToolOutputNudgeTokens` were set to `currentTokens` — the token count from the compress-calling assistant message, which reflects **pre-compression** context. After compressing 100K→50K, the baseline was stuck at 100K, so `growth = 50K - 100K = -50K` and nudges never fired again.
440
+
441
+ **Fix**: Both baselines are now set to `undefined` on compress detection. The next message-transform run re-establishes the baseline from the real post-compression API token count, without triggering a nudge (`computeShouldNudge` returns `shouldNudge: false` when baseline is `undefined`).
442
+
443
+ Files: `lib/messages/inject/inject.ts` (line 98-99). Tests: `tests/inject.test.ts` — 3 updated + 2 new (621 total, 0 fail).
444
+
445
+ ---
446
+
425
447
  ### v1.11.0 — Tool-Result Recap Injection, Context Breakdown & Fork Rebuild
426
448
 
427
449
  This release fixes two critical compression-injection bugs (#20 echo, #78 drift), adds a visible context breakdown to `acp_status`, and introduces a fork-rebuild mechanism.
package/README.zh-CN.md CHANGED
@@ -395,6 +395,28 @@ ACP 在首次启动时自动将配置从 `dcp.jsonc` 迁移到 `acp.jsonc`,将
395
395
 
396
396
  ## 更新日志
397
397
 
398
+ ### v1.11.2 — CI 自动校验 & 自动发布(PR #104)
399
+
400
+ 新增 GitHub Actions CI 自动执行 AGENTS.md 规范:
401
+
402
+ - **PR 校验**(`pr-checks.yml`):每个到 master 的 PR 自动检查分支名规范(`YYYY-MM-DD_short-title`)、devlog 是否存在(`devlog/{分支}/REQ.md` + `WORKLOG.md`)、版本号变更时 changelog 是否更新。
403
+ - **自动发布**(`release.yml`):push `v*` tag 后自动执行 `npm ci` → `npm run check:package` → `npm test` → `npm publish` → GitHub Release,全自动。
404
+ - 脚本:`scripts/ci/check-pr.sh` — 可复用的 PR 校验逻辑。
405
+
406
+ 需要在 GitHub Secrets 中配置 `NPM_TOKEN`。
407
+
408
+ ---
409
+
410
+ ### v1.11.1 — 压缩基线修复(PR #99)
411
+
412
+ **问题**:当模型调用 `compress` 时,`lastPerMessageNudgeTokens` 和 `lastToolOutputNudgeTokens` 都被设为 `currentTokens` —— 这是调用 compress 的 assistant 消息的 token 计数,反映的是**压缩前**的上下文。压缩 100K→50K 后,基线卡在 100K,导致 `growth = 50K - 100K = -50K`,nudge 永远不再触发。
413
+
414
+ **修复**:压缩时将两个基线都设为 `undefined`。下一次 message-transform 运行时从真实的压缩后 API token 计数重建基线,不会误触发 nudge(`computeShouldNudge` 在基线为 `undefined` 时返回 `shouldNudge: false`)。
415
+
416
+ 文件:`lib/messages/inject/inject.ts`(第 98-99 行)。测试:`tests/inject.test.ts` — 3 个更新 + 2 个新增(共 621 个测试,0 失败)。
417
+
418
+ ---
419
+
398
420
  ### v1.11.0 — 工具结果注入、上下文分解 & Fork 重建
399
421
 
400
422
  本次发布修复了两个关键压缩注入 bug(#20 复读、#78 漂移),为 `acp_status` 添加了可见上下文分解,并引入了 fork 重建机制。
package/dist/index.js CHANGED
@@ -2161,6 +2161,34 @@ function formatMessageIdTag(ref, attributes) {
2161
2161
  return `
2162
2162
  <${MESSAGE_ID_TAG_NAME}${serializedAttributes}>${ref}</${MESSAGE_ID_TAG_NAME}>`;
2163
2163
  }
2164
+ function formatTokenSize(tokens) {
2165
+ if (tokens < 1e3) return String(tokens);
2166
+ if (tokens < 1e4) return `${(tokens / 1e3).toFixed(1)}K`;
2167
+ return `${Math.round(tokens / 1e3)}K`;
2168
+ }
2169
+ function classifyMessageType(parts) {
2170
+ let hasTool = false;
2171
+ let hasText = false;
2172
+ let hasReasoning = false;
2173
+ const toolNames = [];
2174
+ for (const part of parts) {
2175
+ if (part.type === "tool") {
2176
+ hasTool = true;
2177
+ if (typeof part.tool === "string" && !toolNames.includes(part.tool)) {
2178
+ toolNames.push(part.tool);
2179
+ }
2180
+ } else if (part.type === "text") {
2181
+ hasText = true;
2182
+ } else if (part.type === "reasoning") {
2183
+ hasReasoning = true;
2184
+ }
2185
+ }
2186
+ if (hasTool) {
2187
+ return toolNames.length > 0 ? `tool:${toolNames.join(",")}` : "tool";
2188
+ }
2189
+ if (hasReasoning && !hasText) return "reasoning";
2190
+ return "text";
2191
+ }
2164
2192
  function assignMessageRefs(state, messages) {
2165
2193
  let assigned = 0;
2166
2194
  let skippedSubAgentPrompt = false;
@@ -6571,7 +6599,8 @@ var injectCompressNudges = (state, config, logger, messages, prompts, compressio
6571
6599
  state.nudges.contextLimitAnchors.clear();
6572
6600
  state.nudges.turnNudgeAnchors.clear();
6573
6601
  state.nudges.iterationNudgeAnchors.clear();
6574
- state.nudges.lastPerMessageNudgeTokens = currentTokens;
6602
+ state.nudges.lastPerMessageNudgeTokens = void 0;
6603
+ state.nudges.lastToolOutputNudgeTokens = void 0;
6575
6604
  saveSessionState(state, logger).catch(() => {
6576
6605
  });
6577
6606
  return;
@@ -6860,9 +6889,15 @@ var injectMessageIds = (state, config, messages, compressionPriorities) => {
6860
6889
  }
6861
6890
  const isBlockedMessage = isProtectedUserMessage(config, message);
6862
6891
  const priority = config.compress.mode === "message" && !isBlockedMessage ? compressionPriorities?.get(message.info.id)?.priority : void 0;
6892
+ const msgType = classifyMessageType(message.parts);
6893
+ const msgTokens = Math.round(countMessageCharacters(message) / 4);
6863
6894
  const tag = formatMessageIdTag(
6864
6895
  isBlockedMessage ? "BLOCKED" : messageRef,
6865
- priority ? { priority } : void 0
6896
+ {
6897
+ priority: priority ?? void 0,
6898
+ type: msgType,
6899
+ tokens: formatTokenSize(msgTokens)
6900
+ }
6866
6901
  );
6867
6902
  if (message.info.role === "user") {
6868
6903
  let injected = false;
@@ -7885,7 +7920,7 @@ You operate in a context-constrained environment. All compression serves the pri
7885
7920
 
7886
7921
  ACP TAGS
7887
7922
 
7888
- \`<acp-context>\` tags wrap ACP (Agent Context Pruning) system metadata \u2014 context management information injected each turn. This is system data, not user input. You may also see \`<dcp-message-id>\` and \`<dcp-system-reminder>\` tags \u2014 these are equivalent (DCP was the previous name for ACP). Treat them as boundary metadata only, not as tool-result content.
7923
+ Each message in the conversation is annotated with a <dcp-message-id> tag showing its reference ID, approximate token size, and content type. For example: <dcp-message-id tokens="2.1K" type="tool:bash">m00175</dcp-message-id>. Use these annotations to assess which messages are consuming the most context and prioritize compression accordingly. The token size is approximate \u2014 treat it as a relative guide, not an exact count. You may also see <dcp-system-reminder> tags \u2014 these are system directives. Treat all tags as boundary metadata, not as tool-result content.
7889
7924
 
7890
7925
  COMPRESSION SUMMARIES IN CONTEXT
7891
7926