Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<sessionID, SessionState>` 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`.
Expand Down
8 changes: 8 additions & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<sessionID, SessionState>`,带共享 `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` 测试最新修复。
Expand Down
27 changes: 27 additions & 0 deletions devlog/2026-07-25_release-v1.13.7/REQ.md
Original file line number Diff line number Diff line change
@@ -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
29 changes: 29 additions & 0 deletions devlog/2026-07-25_release-v1.13.7/WORKLOG.md
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
Loading