diff --git a/README.md b/README.md index 8970dd01..71432633 100644 --- a/README.md +++ b/README.md @@ -475,6 +475,14 @@ For the complete list with root cause analysis, see the [bug tracker](https://gi ## Changelog +### v1.13.7 — Per-Session State + Inactive Block Fixes + Preserve First User Message (PRs #184, #193, #196) + +**Problem**: Three bugs since v1.13.6. (1) **Subagent state isolation failure** (PR #184): ACP stored a single global `SessionState` per plugin instance. When subagent (child) sessions ran interleaved with the parent, the child's state overwrote the parent's `modelContextLimit` — losing the 1M context window and falling back to the 6K adaptive floor, causing over-aggressive nudging in the parent. The `compressionTiming` tracker was also shared across sessions, risking cross-session collision. (2) **Inactive block opacity** (PR #193): `decompress` rejected inactive blocks with "not active — may have already been decompressed", and `acp_status` hid consumed/inactive blocks entirely. Users could not decompress blocks that were GC'd or consumed by secondary compression, and could not see them in status output. (3) **Zero-user session freeze** (PR #196): When compression pruned all user-role messages (all fell inside compressed ranges), zhipuai-lb rejected the request with HTTP 400 code 1214 (`"messages 参数非法"`, `isRetryable: false`), freezing the session. The v1.13.2 `preserve-last-user` fix searched the messages array for a pruned user to restore — but after OpenCode compaction removes pruned messages from the array, the search finds nothing and zero-user requests slip through. + +**Fix**: (1) PR #184 — Introduced `SessionStateRegistry` in `lib/state/state.ts`: a `Map` with a shared `compressionTiming` tracker and soft-cap eviction (32 sessions). Each session now resolves its own state via `registry.getOrCreate(sessionID)`, isolating subagent state from the parent. The system-prompt hook gracefully handles missing state (returns early). Also reverted the over-aggressive `baseline = 0` change back to `baseline = currentTokens` (system prompt is not growth). 851 tests pass. (2) PR #193 — Removed the "not active" rejection from `lib/compress/decompress.ts` and the `/acp decompress` slash command; standalone inactive blocks (user-decompressed, GC'd, orphaned) now decompress successfully. `acp_status` compressed scope now lists ALL blocks (active + inactive) with an `[inactive]` marker and "N active, M inactive/consumed" summary line. Fixed `toFile` fallback to use `targets[0].blocks[0].summary` instead of the undefined `activeBlocks[0].summary`. 859 tests pass after 3 rounds of dual-agent review. (3) PR #196 — Replaced `preserve-last-user` with `preserve-first-user` in `lib/messages/prune.ts`: the first user message (session's original task, always present in the array) is unconditionally force-preserved (`survive[firstUserIdx] = true`) regardless of prune state. Simpler and more reliable — does not depend on pruned messages remaining in the array after OpenCode compaction. Trade-off: may produce two adjacent user messages (first user + a later surviving user), which all major providers accept. Dual-agent reviewed (Oracle + General, both APPROVE). 846 tests pass. + +Files: `lib/state/state.ts`, `lib/hooks.ts`, `lib/compress/types.ts`, `lib/compress/decompress.ts`, `lib/compress/status.ts`, `lib/commands/decompress.ts`, `lib/messages/prune.ts`, `lib/messages/inject/inject.ts`. Tests: `tests/registry.test.ts`, `tests/inactive-block-decompress.test.ts`, `tests/acp-status.test.ts`, `tests/decompress-logic.test.ts`, `tests/prune.test.ts`, `tests/e2e-message-transform.test.ts`. + ### v1.13.7-dev.1 — Dev Prerelease Sync (master @ v1.13.6) **Purpose**: Sync the `dev` npm tag (stuck at `1.12.10-dev.1`) with current master. The `dev` tag had fallen far behind `latest` (1.13.6), making it impossible for early adopters to test the latest fixes via `opencode-acp@dev`. diff --git a/README.zh-CN.md b/README.zh-CN.md index aa075190..3ec66275 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -443,6 +443,14 @@ ACP 在首次启动时自动将配置从 `dcp.jsonc` 迁移到 `acp.jsonc`,将 ## 更新日志 +### v1.13.7 — 每会话状态隔离 + 失活块修复 + 保留首条用户消息(PR #184、#193、#196) + +**问题**:v1.13.6 之后的三个 bug。(1)**子代理状态隔离失败**(PR #184):ACP 为每个插件实例存储单个全局 `SessionState`。当子代理(child)会话与父会话交替运行时,子会话的状态覆盖了父会话的 `modelContextLimit` —— 丢失 1M 上下文窗口,回退到 6K 自适应下限,导致父会话中过度触发压缩提醒。`compressionTiming` 追踪器也跨会话共享,存在跨会话碰撞风险。(2)**失活块不可见**(PR #193):`decompress` 拒绝失活块("not active — may have already been decompressed"),`acp_status` 完全隐藏被消费/失活的块。用户无法解压被 GC 回收或被二次压缩消费的块,也无法在状态输出中看到它们。(3)**零用户消息会话冻结**(PR #196):当压缩剪枝了所有 user 角色消息(全部落在压缩范围内)时,zhipuai-lb 拒绝请求(HTTP 400,code 1214,`"messages 参数非法"`,`isRetryable: false`),冻结会话。v1.13.2 的 `preserve-last-user` 修复在消息数组中搜索被剪枝的用户消息来恢复 —— 但 OpenCode 压缩移除被剪枝的消息后,搜索找不到任何内容,零用户请求仍然漏过。 + +**修复**:(1)PR #184 —— 在 `lib/state/state.ts` 引入 `SessionStateRegistry`:一个 `Map`,带共享 `compressionTiming` 追踪器和软上限驱逐(32 个会话)。每个会话通过 `registry.getOrCreate(sessionID)` 解析自己的状态,将子代理状态与父会话隔离。系统提示钩子优雅处理缺失状态(提前返回)。同时将过于激进的 `baseline = 0` 改回 `baseline = currentTokens`(系统提示不算增长)。851 项测试通过。(2)PR #193 —— 移除 `lib/compress/decompress.ts` 和 `/acp decompress` 斜杠命令中的 "not active" 拒绝;独立的失活块(用户解压、GC 回收、孤立)现在可以成功解压。`acp_status` 压缩作用域现在列出所有块(活跃 + 失活),带 `[inactive]` 标记和"N active, M inactive/consumed"汇总行。修复 `toFile` 回退使用 `targets[0].blocks[0].summary` 而非未定义的 `activeBlocks[0].summary`。经 3 轮双 Agent 审查后 859 项测试通过。(3)PR #196 —— 在 `lib/messages/prune.ts` 用 `preserve-first-user` 替换 `preserve-last-user`:首条用户消息(会话的原始任务,始终存在于数组中)被无条件强制保留(`survive[firstUserIdx] = true`),无论剪枝状态如何。更简单、更可靠 —— 不依赖于被剪枝消息在 OpenCode 压缩后仍留在数组中。权衡:可能产生两个相邻的用户消息(首条用户 + 后续存活的用户),所有主流 provider 均可接受。经双 Agent 审查(Oracle + General,均 APPROVE)。846 项测试通过。 + +文件:`lib/state/state.ts`、`lib/hooks.ts`、`lib/compress/types.ts`、`lib/compress/decompress.ts`、`lib/compress/status.ts`、`lib/commands/decompress.ts`、`lib/messages/prune.ts`、`lib/messages/inject/inject.ts`。测试:`tests/registry.test.ts`、`tests/inactive-block-decompress.test.ts`、`tests/acp-status.test.ts`、`tests/decompress-logic.test.ts`、`tests/prune.test.ts`、`tests/e2e-message-transform.test.ts`。 + ### v1.13.7-dev.1 — Dev 预发布同步(master @ v1.13.6) **目的**:将 npm `dev` 标签(卡在 `1.12.10-dev.1`)同步到当前 master。`dev` 标签已远远落后于 `latest`(1.13.6),导致早期采用者无法通过 `opencode-acp@dev` 测试最新修复。 diff --git a/devlog/2026-07-25_release-v1.13.7/REQ.md b/devlog/2026-07-25_release-v1.13.7/REQ.md new file mode 100644 index 00000000..cd04c90d --- /dev/null +++ b/devlog/2026-07-25_release-v1.13.7/REQ.md @@ -0,0 +1,27 @@ +# REQ: Release v1.13.7 + +## Goal + +Ship a new stable release bundling three bug-fix PRs merged since v1.13.7-dev.1: + +1. **PR #184** — Per-session `SessionStateRegistry`: fixes subagent state isolation (child sessions overwriting parent's `modelContextLimit`). Also reverts the over-aggressive `baseline = 0` back to `baseline = currentTokens`. +2. **PR #193** — Inactive block fixes: `decompress` no longer rejects inactive standalone blocks; `acp_status` shows all blocks (active + inactive) with markers. +3. **PR #196** — Preserve first user message: replaces `preserve-last-user` with `preserve-first-user` to fix the month-long zero-user session freeze (zhipuai-lb code 1214). + +## Version + +- Current: `1.13.7-dev.1` +- Target: `1.13.7` (stable, npm `latest` tag) + +## Scope + +Version bump + changelog entries in `README.md` and `README.zh-CN.md`. No source code changes — all fixes are already on master via their respective PRs. + +## Acceptance Criteria + +- [x] `package.json` version bumped to `1.13.7` +- [x] Changelog entries added to `README.md` and `README.zh-CN.md` with `### v1.13.7` header +- [x] Devlog `REQ.md` + `WORKLOG.md` created +- [x] `./scripts/ci/check-pr.sh` passes +- [x] typecheck + tests + build pass +- [x] PR created, CI green diff --git a/devlog/2026-07-25_release-v1.13.7/WORKLOG.md b/devlog/2026-07-25_release-v1.13.7/WORKLOG.md new file mode 100644 index 00000000..bc5238d0 --- /dev/null +++ b/devlog/2026-07-25_release-v1.13.7/WORKLOG.md @@ -0,0 +1,29 @@ +# WORKLOG: Release v1.13.7 + +## 2026-07-25 + +### Branch setup +- Created worktree `/home/dog/projects/opencode-acp-release-v1.13.7` from `github/master` at `79c7173` +- Branch: `2026-07-25_release-v1.13.7` + +### Changes +- `package.json`: version `1.13.7-dev.1` → `1.13.7` +- `README.md`: Added `### v1.13.7` changelog entry before v1.13.7-dev.1 entry, covering PRs #184, #193, #196 +- `README.zh-CN.md`: Added Chinese translation of the same changelog entry +- `devlog/2026-07-25_release-v1.13.7/REQ.md` + `WORKLOG.md`: created + +### Bundled PRs (all merged to master since v1.13.7-dev.1) +| PR | Title | Tests | +|----|-------|-------| +| #184 | feat: per-session SessionState registry | 851 | +| #192 | test: E2E scenario for subagent compression | 846 | +| #194 | ci: run E2E tests on every PR | 846 | +| #195 | fix: remove erroneous acknowledgeRisk from E2E scenario 05 | 846 | +| #193 | fix: decompress inactive blocks + acp_status visibility | 859 | +| #196 | fix: preserve first user message to guarantee API validity | 846 | + +### Verification +- `./scripts/ci/check-pr.sh`: PASS +- `npm run typecheck`: PASS +- `npm run test`: 846 tests pass +- `npm run build`: PASS diff --git a/package.json b/package.json index f3b87cb9..ce422922 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "opencode-acp", - "version": "1.13.7-dev.1", + "version": "1.13.7", "type": "module", "description": "Active Context Pruning — model-driven context management for OpenCode (hardened fork of DCP with 35 bug fixes)", "main": "./dist/index.js",