diff --git a/devlog/2026-08-15_viable-ranges/REQ.md b/devlog/2026-08-15_viable-ranges/REQ.md new file mode 100644 index 0000000..02978bf --- /dev/null +++ b/devlog/2026-08-15_viable-ranges/REQ.md @@ -0,0 +1,31 @@ +# REQ - Filter status compressible ranges via billion-context-kit + +- Task ID: `2026-08-15_viable-ranges` +- Home Repo: `billion-context-opencode` +- Created: 2026-08-15 +- Status: Done +- Priority: P2 +- Owner: 5258MF +- References: `packages/core/src/status-tool.ts`; PR #19 + +## 1. Background & Problem Statement + +- **Context**: The sibling adapters (billion-context-omp, billion-context-pi) hit a production failure mode: the nudge/status recommendation lists can contain fragmented ranges (e.g. a 16-token one-message ack). A model that batches the whole list into one compress call gets atomically rejected by acp-kernel — `minSummaryLength` (50 chars) cannot be satisfied by a tiny range, and the kernel validates the batch as a whole. omp issue evidence: 14-range list containing a 16-token range, every batch attempt failed. +- **Current behavior (symptom)**: `packages/core/src/status-tool.ts` lists `nudge.compressibleRanges` unfiltered, so sub-viability fragments are recommended to the model/user. +- **Expected behavior**: every surface that recommends ranges (this PR: the status tool) applies the shared viability floor from `billion-context-kit` (`viableRanges`, `VIABLE_RANGE_MIN_TOKENS = 200`). + +## 2. Requirements + +- R1: `status-tool.ts` compressible list is filtered by `viableRanges` before rendering. +- R2: No kernel bump — opencode stays on acp-kernel 0.0.19; the kit's `viableRanges` has no kernel dependency, so pulling the kit does not change kernel semantics. +- R3: The kit dependency is an exact pin (build-time, bundled inline); transitional git pin `git+https#v0.1.1` now swapped to npm `0.1.1` after publication. + +## 3. Non-Goals + +- Adopting the kit panel (`buildStatusPanel`) — larger surface, follow-up when the repo stabilizes. +- Filtering the nudge injection itself (omp/pi do this in their adapters; opencode's nudge path is host-side and out of scope here). + +## 4. Acceptance Criteria + +- AC1: `npm run typecheck --workspaces` and `npm test` (54/54) pass. +- AC2: Status output contains no compressible range below 200 tokens. diff --git a/devlog/2026-08-15_viable-ranges/WORKLOG.md b/devlog/2026-08-15_viable-ranges/WORKLOG.md new file mode 100644 index 0000000..292335f --- /dev/null +++ b/devlog/2026-08-15_viable-ranges/WORKLOG.md @@ -0,0 +1,27 @@ +# WORKLOG - Filter status compressible ranges via billion-context-kit + +- Task ID: `2026-08-15_viable-ranges` +- Home Repo: `billion-context-opencode` +- Status: Done +- Updated: 2026-08-15 + +## 1. Summary + +- **What was done**: Wired `billion-context-kit`'s `viableRanges` (>=200-token floor) into the status tool's compressible-range list, and pinned the kit dependency to the published npm version (`0.1.1`, was the transitional `git+https#v0.1.1`). + +## 2. Changes + +- `packages/core/package.json`: devDependency `billion-context-kit@0.1.1` (exact pin, bundled inline at build time). +- `packages/core/src/status-tool.ts`: `compressibleRanges` passed through `viableRanges()` before rendering. +- `package-lock.json` refreshed via `npm install --package-lock-only -w packages/core`. + +## 3. Verification + +- `npm run typecheck --workspaces`: clean. +- `npm test`: 54/54 pass. +- CI: build matrix (22/24) green; pr-validation initially failed on the missing devlog entry (this entry) — added, retriggered. + +## 4. Notes + +- acp-kernel intentionally stays at 0.0.19: `viableRanges` is a pure function with no kernel dependency, so no kernel semantics change rides along. +- The 200-token floor matches the sibling adapters (omp PR #20/#15, pi PR #148) — all three now share the same viability rule from one source. diff --git a/package-lock.json b/package-lock.json index b8d6a67..2aaeba1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -953,6 +953,28 @@ "dev": true, "license": "MIT" }, + "node_modules/billion-context-kit": { + "version": "0.1.1", + "resolved": "git+ssh://git@github.com/ranxianglei/billion-context-kit.git#6e1577002fbb8752f9530d785927e6bcceb28ac6", + "dev": true, + "license": "MIT", + "dependencies": { + "acp-kernel": "0.0.23" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/billion-context-kit/node_modules/acp-kernel": { + "version": "0.0.23", + "resolved": "https://registry.npmjs.org/acp-kernel/-/acp-kernel-0.0.23.tgz", + "integrity": "sha512-bG28aYfBda8z34fsaoNc4QBix4gRtQDQznR6G4XBaz9z1xgxdmdYtazQQMBjxnfEL9yCrDb9CTpkl7IXWiuviA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, "node_modules/billion-context-opencode": { "resolved": "packages/billion-context-opencode", "link": true @@ -2098,6 +2120,7 @@ }, "devDependencies": { "@types/node": "^22.10.0", + "billion-context-kit": "0.1.1", "typescript": "^5.7.0" } }, diff --git a/packages/core/package.json b/packages/core/package.json index 61f6e34..9f5604b 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -14,6 +14,7 @@ }, "devDependencies": { "@types/node": "^22.10.0", + "billion-context-kit": "0.1.1", "typescript": "^5.7.0" } } diff --git a/packages/core/src/status-tool.ts b/packages/core/src/status-tool.ts index 58fa29d..1ede90a 100644 --- a/packages/core/src/status-tool.ts +++ b/packages/core/src/status-tool.ts @@ -1,5 +1,6 @@ import { z } from "zod" import { buildStatusReport, defaultCountTokens, formatRanges } from "acp-kernel" +import { viableRanges } from "billion-context-kit" import type { AcpRuntime } from "./runtime.js" import type { ToolDef } from "./compress-tool.js" import { estimateTokens, collectCoveredMessageIds } from "./tokens.js" @@ -53,7 +54,10 @@ async function handleStatus(args: Record, runtime: AcpRuntime, if (args.scope) return base const nudge = turn.nudge - const ranges = nudge?.compressibleRanges ?? [] + // Kit filter: fragmented ranges (<200 tokens) cannot carry a meaningful + // summary and would fail the kernel's atomic batch validation — same guard + // as the omp/pi adapters (billion-context-kit viableRanges). + const ranges = viableRanges(nudge?.compressibleRanges ?? []) const protectedRanges = nudge?.protectedRanges ?? [] const extra: string[] = []