Repository navigation
feat: CCR v2 — covered-ref spans, range-level decompress, ID-returning search_context; CCR default-on (#1179) - #1207
Conversation
📦 Built Package ArtifactBranch: Option A — Install from npm PR tag (recommended)npm install -g billion-context@pr-1207Each push to this PR publishes a new version under the Option B — Download artifact
tar xzf billion-context-pr1207.tgz
npm install -g packageInstall instructions are refreshed on each push. |
[bot] 🏷 Reviewed PR #1207 against current master ( ① Original issue / direction. Matches the agreed design in #1179 (floors 4/7/12): no kernel changes needed (0.0.84 ships ② Regression review (pre-existing behaviors on touched paths):
③ Code review. Fold-time storing inside
④ Test validity. Real teeth throughout: exact rawIds per stored ref, uncovered-ref negative assertions, all five failure-mode error strings, ephemeral-channel isolation (block stays active, whole-block cache untouched, stats separate), and wire-level e2e (streaming SSE + non-stream JSON) asserting fold-on-wire, head-anchor survival, span boundary exclusion, tool_use/tool_result pair integrity, and per-request ⑤ Diff cleanliness. 16 files, +696/−44, every file on-purpose; the four modified existing tests are justified adaptations, not churn. Clean. Behavior change (flagged, old → new): Evidence: CI green on head — real codex e2e (zero tokens), test ubuntu+windows × node 22/24, version-guard, build-artifact. Local The two nits are cosmetic/robustness — fix in passing or follow-up, either way doesn't block. CCR v2(折叠内容可检索 + 区间级 decompress + search_context 返回消息 ref)+ CCR 默认开启已按 #1179 商定方案实现,回归面(processTurn 剥离完整性、启用门、持久化兼容、整块行为不变)逐项核验通过,测试有牙、diff 干净、CI 全绿——可以合并(两个非阻塞小瑕疵见上)。 |
… default-on comment (#1207)
9dcdd27 to
78546d6
Compare
Both nits addressed in
Pre-flight on the new head: typecheck clean, targeted CCR suites 41/41, full suite 2505 pass / 0 fail (skips = env-gated e2e), build OK. |
[bot] 🏷 Coordination note from the PR #1273 review (CCR plugin-lane enablement — same subsystem, merge-order hazard): #1273 threads the global CCR view into the plugin manifest at the This PR flips the resolved default to ON ( Suggested sequencing: merge #1273 first (self-consistent while unset=off), then when landing this PR add one hunk making the manifest's ccr view honor the same default-on rule (unset → advertise). Alternatively land this PR first and have #1273's threading pick up the resolved view. Either way, whichever merge comes second must revisit the manifest call site — and a test asserting manifest advertisement matches the arming gate under default (unset) config would pin this class of drift permanently. (No code changes made here; flagging only. Full analysis in the #1273 thread.) |
[bot] 🏷 Correction to my coordination note above: PR #1273 has since landed follow-up commit Consequence for this PR: when it lands, zero-config plugin-lane sessions simply stay disarmed from CCR (base view unset → gate off AND manifest silent, together) — consistent and fail-closed, no silent-loss window in any merge order. The "manifest must honor default-on" hunk I suggested is therefore not needed; #1273 can merge first without further changes, and this PR needs no manifest-side adjustment. (Optional, still worth considering: a test pinning advertisement-vs-gating under unset config, now that both read the same source — cheap insurance against future drift.) |
|
Independent review round landed at 074d98f (reviewer run + my verification; all findings addressed): F0 — default-on was dead at the server gate (reviewer's exploration was right, the verdict table missed it). F1 — range restore now falls back to the fold-time content store. Covered originals may have left the client's re-sent view (native-compaction archive adoption, client trimming, restart with a compacted client) — exactly the scenario F3 — plugin-mode error names the real constraint ("proxy-mode only — plugin mode owns its folds natively") instead of the unsatisfiable "enable compress.ccr.enabled". F4 — span temp files: F5 — client retries dedupe on the deterministic injection id: no duplicate full-text injection, no F2 — filed as #1282 (store grows unbounded ≈ full transcript per session; cap/LRU/tier-aware eviction needs an explicit product decision — byte-cap + LRU + Gates on 074d98f: typecheck ✓ · tests 2508 pass / 0 fail (9 e2e-gated skips) · build ✓. |
[bot] 🏷 Verified the fix round at 074d98f (rebased onto current master Honest correction first: my earlier review missed F0. I verified the arming condition ( Fix-by-fix verification:
One new finding from probing (non-blocking): the store fallback's mutual-exclusion assumption (view XOR store per ref) holds in all standard server flows — my probes confirm it end-to-end — but it is assumed, not enforced: if a covered ref ever remained visible in the exec-time view (protect-zone edges, future rebuild changes, direct core consumers), an arrival-stored ref would restore its placeholder text under a "full content follows" ack. Filed as #1283 with repro evidence and a ~10-line suggested fix (per-ref store-first selection); low priority, doesn't block this PR. Conclusion: fix round is complete and correct, gates green everywhere, behavior-change disclosure stands as originally written (now actually effective through the server path). Ready to merge when you are. 修复轮(074d98f)已逐项核验:F0 的 server 门默认开启接线、F1 区间恢复回退内容库(含 head-anchor 边界的真实服务器探针)、F3/F4/F5 及两个 nit 均正确落地,本地与 CI 门禁全绿——可以合并;另发现一个非阻塞的防御性缺口已立 #1283。 |
|
[bot] 🏷 Reviewer commit Behavior note (unreleased CCR v2 surface only): for CCR folds where all covered refs are in the content store (the normal post-fold state), the ephemeral range-restore span is now always store-sourced: per-part headers read Verification: typecheck clean, full suite 2509 pass / 0 fail, build OK, regression test red on pre-fix code / green on fixed. |
…g search_context (#1179)
… default-on comment (#1207)
… falls back to the content store - server.ts: resolvedCcrCfg now re-applies the #1179 default-on (resolveCompress returns the raw merge; applyCompressSettings owns the unset-means-enabled default this arming path bypassed) — with no ccr key at any level the session previously NEVER armed: 'on by default in proxy mode' was dead at its only enforcement point (reviewer-caught wiring gap, confirmed against the real server path). - decompress-shared.ts: range restore falls back to the fold-time content store when covered originals left the client's re-sent view (native-compaction archive adoption, client-side trimming, restart with a compacted client) — keyed by the coverage's own ref numbers; honest failure text when neither view nor store has them. - decompress-shared.ts: plugin-mode error names the real constraint (proxy-mode only) instead of unsatisfiable 'enable compress.ccr.enabled'. - decompress-shared.ts: client retries dedupe on the deterministic injection id — no duplicate full-text message, no rangeRestores inflation; temp files 0600 with the span in the filename. - tests: store-fallback + retry-dedupe + no-config default-on e2e (real server path); codex-official tool list gains acp_retrieve (intended default-on ripple in proxy mode). Follow-up: unbounded store growth tracked in #1282.
…1283) resolveDecompressRange consulted the fold-time content store only when zero refs were found in the exec-time view, assuming view/store mutual exclusion per ref that nothing enforces: a covered ref left visible in the view contributed its view text, which for arrival-stored refs (oversized tool results — the primary CCR case) is the 📦 placeholder, not the original, while the ack still claimed a full restore; a partially visible span silently dropped the missing refs. Per-ref selection supersedes the all-or-nothing fallback: each picked ref prefers retrieveByRef(store) when present (store entries are immutable originals — append-only, first write wins, refs never reissued), else falls back to the view text (pre-CCR folds have no entries). Span order stays deterministic (coverage num order). Regression test: an arrival-stored ref whose view copy is the placeholder inside a restored span yields the stored full original (verified to fail against the pre-fix code).
eee5607 to
936a702
Compare
|
Rebased onto current master (after #1189 / #1273 / #1270 landed) — head What the rebase surfaced
Gates on the rebased head
Ready to merge once CI mirrors the local gates. Merge order note: this is the last of the CCR batch (#1270 → #1189 → #1273 → #1207). |
[bot] 🏷 Verified the rebased head 936a702 end-to-end. Gates mirror yours exactly: Rebase integrity — head sits directly on current master tip Doc union — checked all four files line by line: the PR's default-on wording and master's plugin-lane scope (#1271/#1273) now coexist consistently (proxy mode default-on; plugin lanes opt-in via explicit global #1283 fix (417c95d) — implemented exactly as filed: per-ref store-first selection iterating coverage in num order (deterministic span), view text only as fallback, honest failure text preserved for the neither-source case. Regression test has teeth — asserts the stored full original IS present AND the Preflight pin (936a702) — four boundary-pinning e2e configs in Arming site post-rebase — F0's fix survives intact ( Gates on 936a702 (ran locally after Ready to merge once you click — nothing left open on this branch from my side; #1283 is fixed in-branch, #1282 resolved by design decision and documented. 已核验 rebase 后的 head(936a702):直接落在当前 master 顶(#1273 之后),rebase diff 逐文件审计无意外改动,文档并集与 #1282 定案措辞正确,#1283 修复按建议实现且回归测试有牙,preflight 测试钉 CCR-off 的根因与隔离方式成立,本地门禁(2535/2526/0/9)与 CI 9/9 全绿——可以合并。 |
…owner decision) Unset compress.ccr no longer arms anything: the server stamp keeps the raw three-level merge (resolvedCcrCfg = compressCfg.ccr), applyCompressSettings loses the default-on else-branch, and resolveRequestConfig returns base unchanged when nothing is configured. Flip CCR on only via an explicit compress.ccr.enabled=true at some level, after local verification. Tests, READMEs (en/zh) and CONFIGURATION (en/zh) updated to the opt-in contract; preflight e2e pins dropped — the default-off boundary is now a tripwire against accidental default flips.
|
Owner decision shipped (head Unset
Gates: typecheck ✓ / 2526 tests 0 fail (9 e2e-gated skip) / build ✓ |
…t outlive lost text In plugin mode, acp_retrieve committed its success (ack text + retrieveHits) before the full text had any guaranteed delivery path: the ack rode the tool response immediately while the full text sat in the volatile in-memory session.pendingRetrievals queue, appended only to a LATER upstream request. A restart, an upstream failure after drain, a CCR disarm, or a long run of non-qualifying requests could each leave the ack on record while the text was silently lost or surfaced detached from context. Give queued retrievals a lifecycle (queued -> attached -> delivered | dropped): - executeRetrieve queues with a durable undelivered ledger in session.metadata (round-trips to disk; no on-disk schema change). - Plugin-lane prepares SNAPSHOT (not splice) the queue onto the outgoing body; forward() settles it at the final response: committed on 2xx, dropped-and- logged on network failure / non-2xx / transform failure. - Disarm terminates pending deliveries observably (no silent truncate, no stale flush on re-arm); reload reconciles acked-but-never-delivered refs; a TTL (BILI_CCR_RETRIEVAL_TTL_MS, default 10min) expires loudly. - Every loss logs refs+count+reason, bumps stats.retrieveDropped, and queues a corrective note flushed as an ephemeral trailing user message next round. - New additive counter stats.retrieveDelivered counts confirmed deliveries. retrieveHits semantics are unchanged. Range-restore riders (ranxianglei#1207) share the carrier but are ccr:false: removed at the delivery outcome but exempt from the CCR ledger/TTL/counters/note, so their original ride-along semantics are preserved. Proxy-mode drain is unchanged (atomic in-flow) -- four-wire happy path and pair integrity intact. Tests cover all four windows plus two-riders isolation and the proxy regression.
… v2 lockstep)
executeRetrieve now calls kernel applyRetrieve({store, ref, exportDir})
and returns the result as the tool result directly — retrieval is a plain
tool call whose output carries the payload. The ranxianglei#1343 queued-delivery
lifecycle for CCR is retired: queueRetrieval deleted, delivery counts at
tool time, carrier/ledger/note machinery survives only for legacy state
and the ranxianglei#1207 range-decompress rider (which now rides as user).
Originals >= 4000 tok export to stateDir()/retrieve/<ref>.txt with a
pointer tool result; write failure degrades to a bounded head preview.
compress-settings spreads kernel DEFAULT_CCR_CONFIG first so new kernel
ccr keys (retrieveInlineTokens) survive the deepEqual contract.
Requires acp-kernel 0.0.99 (unreleased): package.json still pins 0.0.98
and CI is expected red until the kernel lands on npm — the bump commit +
re-run turns it green. Tests reworked to the atomic-delivery contract;
3295/3295 green against the local kernel overlay.
Closes #1179
What ships
CCR v2 core (commit 1):
storeCoveredOriginals, first-write-wins, reasoning skipped), soacp_retrieve("mNNNNN")works for folded content too. Block→ref linkage is exposed inacp_statusas aBLOCK SPANSsection (b1=m00001–m00007).decompress: optionalstartId/endIdmessage refs restore just a span of a block instead of the whole block. Ephemeral injection on the same request-only channel as retrieves (never enters fold space); pair integrity preserved on OpenAI-family wires. Wired on all four wires (anthropic/openai/responses/google) including non-stream JSON responses.search_context: hits carry the covered ref span (b1 (T1) "topic" [m00044–m00097 · N msgs]) alongside block metadata, so the model can retrieve exact messages instead of decompressing whole blocks.rangeRestorescounted separately from whole-block decompresses (session stats + persistence, additive field, old session files load unchanged).CCR default-on (commit 2, per owner decision in-thread): unset
ccr.enablednow resolves totrue; explicitfalseat any level still wins. See behavior-change section below.Docs updated: README + CONFIGURATION (en/zh).
ccr.enabledunset at every level → CCR fully off everywhere (no placeholders, noacp_retrievetool, no.content-store.json).enabled: true, effective exactly where the existing v1 arming gate allows it — proxy mode on native-tools wires. Plugin-mode agents own their folds and marker/text wires have no retrieve channel, so they stay inert via the same gate (no new arming logic)..content-store.jsonwhen oversized tool results (≥ 4000 tokens by default) arrive or folds land, and models may see retrieve / range-restore injections. Opt-out:ccr: { enabled: false }at global/provider/model level.decompresstool schema gains optionalstartId/endIdparams (all four wire shapes + plugin manifest);acp_statusgains theBLOCK SPANSsection and arange-restored Nsuffix on the STORE line.Pre-flight evidence (rebased head, current master)
npm run typecheckclean;npm run buildOK.tests/ccr-v2.test.ts(11 tests: unit for spans/range-decompress/search-refs/stats + streaming SSE e2e + non-stream JSON e2e, incl. per-request anthropiccache_controlbreakpoint stability assertions covering range-restore injections, mirroring v1's retrieve-injection coverage).e2e-codex(ACP_TEST_E2E-gated) not runnable in this sandbox (needs a local upstream); no changes to its covered paths beyond the additive tool params.Note discovered while testing
The kernel never hides the conversation's first user message, even when it falls inside an active fold (
rebuildMessageshead-anchor rule, pre-existing v1-era behavior). Documented in the e2e assertions; no kernel change needed.