Skip to content

feat: CCR v2 — covered-ref spans, range-level decompress, ID-returning search_context; CCR default-on (#1179) - #1207

Merged
ranxianglei merged 7 commits into
masterfrom
wt-1179-ework-daemon
Sep 25, 2026
Merged

ranxianglei merged 7 commits into
masterfrom
wt-1179-ework-daemon

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

Closes #1179

What ships

CCR v2 core (commit 1):

  • Covered-ref spans: when a compress fold lands, the covered originals are persisted into the content store (storeCoveredOriginals, first-write-wins, reasoning skipped), so acp_retrieve("mNNNNN") works for folded content too. Block→ref linkage is exposed in acp_status as a BLOCK SPANS section (b1=m00001–m00007).
  • Range-level decompress: optional startId/endId message 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.
  • ID-returning 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.
  • New stat: rangeRestores counted 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.enabled now resolves to true; explicit false at any level still wins. See behavior-change section below.

Docs updated: README + CONFIGURATION (en/zh).

⚠️ Behavior change (old → new)

  • Old: ccr.enabled unset at every level → CCR fully off everywhere (no placeholders, no acp_retrieve tool, no .content-store.json).
  • New: unset → 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).
  • Consequence: proxy-mode sessions will now write .content-store.json when 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.
  • Additive output changes: decompress tool schema gains optional startId/endId params (all four wire shapes + plugin manifest); acp_status gains the BLOCK SPANS section and a range-restored N suffix on the STORE line.

Pre-flight evidence (rebased head, current master)

  • npm run typecheck clean; npm run build OK.
  • Full suite: 2389 pass / 3 fail / 2 skipped. The 3 failures are claude-native live-process e2e tests that fail identically on clean origin/master in this sandbox (A/B verified via worktree) — pre-existing environment issues, not regressions from this PR.
  • New coverage: 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 anthropic cache_control breakpoint 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 (rebuildMessages head-anchor rule, pre-existing v1-era behavior). Documented in the e2e assertions; no kernel change needed.

@github-actions

github-actions Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

📦 Built Package Artifact

Branch: wt-1179-ework-daemon (b3e3762)

Option A — Install from npm PR tag (recommended)

npm install -g billion-context@pr-1207

Each push to this PR publishes a new version under the pr-1207 npm tag.

Option B — Download artifact

  1. Download the artifact from the Actions run
  2. Extract the tarball and install:
tar xzf billion-context-pr1207.tgz
npm install -g package

Install instructions are refreshed on each push.

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 Reviewed PR #1207 against current master (aae4de1 == base, head rebased clean, CI fully green). Conclusion: mergeable — implementation matches the plan agreed in #1179, the regression surface is covered, two non-blocking nits below.

① Original issue / direction. Matches the agreed design in #1179 (floors 4/7/12): no kernel changes needed (0.0.84 ships storeCoveredOriginals), fold-time storing rides the single applyRanges choke point, range restore rides the ephemeral retrieve channel, explicit-false opt-out preserved. CCR default-on follows the owner decision in-thread (floors 8/10/12). Direction is sound.

② Regression review (pre-existing behaviors on touched paths):

  • processTurn ccr-strip completeness — the highest-risk area, because config.ccr is now always present with enabled:true, so any unguarded site would silently arm kernel placeholder substitution without host arming. Verified all 12 sites strip when disarmed: src/server.ts:2440/2612/2816/3045/4768 (loopConfig pattern), :2903/:3267 (inline), :3348 (compactConfig), src/preflight.ts:848, src/plugin.ts:673, src/acp-status.ts:55. No gap.
  • Arming gate — unchanged by this PR; default-on is effective exactly where the v1 gate arms (proxy mode, native-tools wires). Plugin mode and marker/text wires stay inert via !pluginMode / storeChannelOk. Claim verified.
  • Persistence — rangeRestores?: number is additive; buildSession loads ?? 0; old session files load byte-compatible. ✓
  • Whole-block decompress — path untouched; explicitly pinned by a new test (file-mode ack, no injections queued, rangeRestores stays 0).
  • resolveRequestConfig fast path — previously "nothing configured → return base object identity"; now allocates a fresh config per request for fully-unset sessions (first call flips ccr). Functionally inert (values are deterministic kernel defaults + enabled:true); one small object per request — negligible. The re-feed idempotency case is pinned by the updated cascade test.
  • Tool schemas — additive optional startId/endId on decompress across all four wire shapes + plugin manifest; nothing else may change (pinned by deep-equal minus the two params).
  • Docs — en/zh kept in sync (CONFIGURATION ×2, README ×2, parallel edits). ✓

③ Code review. Fold-time storing inside applyRanges is the right layer — it covers anthropic/openai/responses/google streaming, non-stream JSON, and preflight overflow through one choke point; gated on ccrEnabled(session) so disarmed sessions never touch the store. Range resolver fails loudly on every malformed input (params not paired, bN boundaries, reversed range, uncovered span, CCR off, zero matches) and queues nothing on failure — good. First-write-wins ordering (arrival entries survive fold-time storing) is correct and tested. Two nits:

  1. BLOCK SPANS renders even when CCR is disarmed — the listed refs would be unretrievable there (retrieve tool absent, range decompress refuses). Cosmetic confusion risk; consider gating the section on ccrEnabled.
  2. In applyCompressSettings the unset-branch comment mentions an "advanced kernelConfig passthrough", but no user-facing kernelConfig passthrough exists (opts.kernelConfig is always defaultConfig(...), src/config.ts:648); the hardcoded enabled: true would override a programmatic base.ccr = {enabled:false} if one were ever constructed. Unreachable today — consider enabled: b?.enabled ?? true or fixing the comment.

④ 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 cache_control anchor stability with no breakpoint landing on injected content — mirroring v1's retrieve-injection discipline. Updated existing tests adapt honestly: the #389 assertion is scoped to range-entry line shape (justified — BLOCK SPANS legitimately names covered refs), the cascade fast-path test pins both the flip and idempotency, and the #841 schema-purity test proves the decompress delta is exactly the two optional params. One acceptable gap: range-restore e2e exists only on the anthropic wire; openai/responses/google rest on the shared-resolver unit tests plus the pre-existing (v1-proven) pendingRetrievals drain per wire. Fine for merge.

⑤ 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): ccr.enabled unset at every level: off everywhere → on, effective where the v1 arming gate allows (proxy mode, native-tools wires). Consequences: proxy-mode sessions will now write .content-store.json (oversized tool results ≥ 4000 tok by default, folds) and models may see retrieve / range-restore injections. Opt-out: ccr: { enabled: false } at any level — still wins. Additive outputs: decompress gains optional startId/endId; acp_status gains BLOCK SPANS + range-restored N suffix. PR body discloses all of this correctly.

Evidence: CI green on head — real codex e2e (zero tokens), test ubuntu+windows × node 22/24, version-guard, build-artifact. Local npm run typecheck clean on the checked-out head. Full local suite skipped deliberately (CI runs it on four matrix legs; the 3 sandbox-only claude-native failures noted in the PR body don't reproduce under CI).

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 全绿——可以合并(两个非阻塞小瑕疵见上)。

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

Both nits addressed in 78546d66 (branch rebased onto 8e5eb582 / v0.1.147 / kernel 0.0.86 first):

  1. BLOCK SPANS gating — done as suggested: the section now renders only when CCR is armed (ccrEnabled(session)), so a disarmed session no longer lists refs it cannot retrieve. Pinned by a new test (acp_status hides BLOCK SPANS when CCR is disarmed). Search-hit spans are deliberately left ungated — they're coverage metadata (which messages a block folded), still useful when CCR is off, and the range-decompress path already answers disabled calls with an explicit refusal rather than silent confusion.

  2. compress-settings unset branch — took the comment-fix half of the suggestion rather than b?.enabled ?? true: the kernel's defaultConfig() already ships ccr.enabled: false, so base.ccr is present-and-false in the normal default-on path — honoring it would flip every unconfigured session back off (verified: three existing default-on tests fail under that change). The hard-coded true is the product-level override of the kernel shipping default; the comment now says exactly that instead of referencing a nonexistent passthrough.

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.

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[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 /__bili/plugin/manifest call site (src/server.ts): { ...DEFAULT_CCR_CONFIG, ...config.ccr, ...opts.compress.ccr } — i.e. the manifest advertises acp_retrieve only when CCR is enabled in the raw/unresolved config view (conservative per #1192). Its arming gate, by contrast, uses the resolved per-request config (resolvedCcrCfg?.enabled === true).

This PR flips the resolved default to ON (resolveCompress: unset → enabled: true). If both land, a zero-config plugin-lane session becomes ARMED (placeholders emitted on the anthropic/openai wires) while the manifest still advertises nothing (raw view floors at DEFAULT_CCR_CONFIG.enabled: false) → hosts never register acp_retrieve → placeholders unretrievable = exactly the silent-loss trap #1192/#1097 designed out.

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.)

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 Correction to my coordination note above: PR #1273 has since landed follow-up commit eb3a95c, which fixes the hazard by design — the plugin-lane arming gate now requires opts.compress.ccr?.enabled === true (the base config), i.e. the exact same source its manifest reads. Arm and advertise share one config field and can never diverge.

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.)

@ranxianglei

Copy link
Copy Markdown
Owner Author

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). resolvedCcrCfg came from the raw resolveCompress merge, but the #1179 unset-means-enabled default lives in applyCompressSettings — so with no ccr key at any level, the session never armed and the headline "on by default in proxy mode" did nothing through the real server path (kernelConfig got the default via resolveRequestConfig; only this stamp was raw). I reproduced it end-to-end before fixing; there's now a no-config e2e that fails without the fix. codex-official's tool list gains acp_retrieve — intended ripple, expectation updated.

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 storeCoveredOriginals exists for; the span path previously died with "no longer in this request's view". Keyed by the coverage's own ref numbers (ids never reused); honest failure text when neither view nor store has them. Regression test simulates the trimmed client view.

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: 0600 (folded conversation content is not world-readable) + span in the filename (same-millisecond clobber); whole-block site got the same hygiene.

F5 — client retries dedupe on the deterministic injection id: no duplicate full-text injection, no rangeRestores inflation (retry re-acks identically).

F2 — filed as #1282 (store grows unbounded ≈ full transcript per session; cap/LRU/tier-aware eviction needs an explicit product decision — byte-cap + LRU + acp_status surface suggested there).

Gates on 074d98f: typecheck ✓ · tests 2508 pass / 0 fail (9 e2e-gated skips) · build ✓.

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 Verified the fix round at 074d98f (rebased onto current master 8e5eb582 — fresh). Local gates all match your report: npm run typecheck clean, npm run build OK, full suite 2508 pass / 0 fail / 9 e2e-gated skips. CI on 074d98f: 9/9 green (real codex e2e, 4-leg test matrix ubuntu/windows × node 22/24, version-guard, build-artifact, pi lane, codex overlay). #1282 confirmed filed for F2 with the cap/LRU design options.

Honest correction first: my earlier review missed F0. I verified the arming condition (resolvedCcrCfg?.enabled === true at the stamp site) but did not trace where resolvedCcrCfg was sourced — it came from the raw resolveCompress merge, bypassing applyCompressSettings where the unset-means-enabled default lives. So "on by default" was indeed dead at its only enforcement point, and my "verified" verdict on that path was wrong. Good catch by the reviewer; the no-config e2e that fails without the fix is exactly the right pin.

Fix-by-fix verification:

  • F0 — resolvedCcrCfg = compressCfg.ccr ?? { enabled: true }. Walked every case: unset at all levels → armed ✓; explicit false at any level → raw merge carries it → not armed ✓; partial block without enabled (e.g. only minToolTokens) → not armed, consistent with applyCompressSettings' s.ccr.enabled === true semantics (feature off, same as v1) ✓; custom toolName preserved through the stamp ✓. The bare { enabled: true } fallback stamp is safe: the stamp's only consumers are .enabled (arming) and .toolName (retrieveToolName, which falls back to the same default the kernel config resolves). Correct and minimal.
  • F1 — beyond reading the diff, I ran my own probe through the real server path on the hardest case: fold m00001–m00007, then decompress(b1, m00001–m00004) — a span that includes the head-anchored first user message (the one ref the kernel keeps visible on the wire). Result: ack "restored 4 item(s)", injected span contained exactly details 0–3 from the content store, details 4–6 excluded. Mechanism note for the record: the head anchor exists in the outbound payload (rebuild runs at send time) but NOT in the exec-time view (tool execution precedes rebuild), so parts.length === 0 and the store fallback fires correctly. Also verified the nits commit: BLOCK SPANS now gated on CCR arming (new test pins disarmed→absent) and the misleading comment fixed.
  • F3 / F4 / F5 — confirmed in diff: plugin-mode error names the proxy-mode constraint; temp files 0600 + span-in-filename on both the span and whole-block sites; retry dedupe wraps both the injection push AND the rangeRestores increment on the deterministic id.
  • codex-official tool list gaining acp_retrieve — intended ripple confirmed (proxy mode, default-on).

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。

@ranxianglei

Copy link
Copy Markdown
Owner Author

[bot] 🏷 Reviewer commit eee56079 (issue #1283): resolveDecompressRange now does per-ref source selection — each picked ref prefers retrieveByRef(store) over its exec-time view text, replacing the all-or-nothing parts.length === 0 store fallback that assumed unenforced view/store mutual exclusion per ref.

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 [kind • stored]/[tool • stored] instead of [role]/[role • tool], and restored text carries no leading <acp> tag (kernel stores tag-stripped originals). Ack format, item counts, retry dedupe, and the >10K file-mode threshold are unchanged. Pre-CCR folds (no store entries) are byte-identical to before via the view path.

Verification: typecheck clean, full suite 2509 pass / 0 fail, build OK, regression test red on pre-fix code / green on fixed.

ework-agent and others added 6 commits September 25, 2026 10:57
… 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).
…ema legitimately shifts the recent-window boundary (#1207 rebase onto #1189/#1273/#1270)
@ranxianglei

Copy link
Copy Markdown
Owner Author

Rebased onto current master (after #1189 / #1273 / #1270 landed) — head 936a702, force-pushed.

What the rebase surfaced

  1. Doc conflicts (4 files) — resolved by union: master's plugin-lane scope wording from [Feature] CCR enablement switch on every host lane: acp_retrieve tool + config surface, one aligned schema across three repos #1271/feat: enable CCR (acp_retrieve) on host/plugin lanes (#1271) #1273 (the branch's own "proxy mode only" phrasing predated the plugin-lane enablement) + this PR's v2 additions. While in there, replaced the stale "Known limitation: no eviction policy yet (tracked as a follow-up)" sentence in both languages with the CCR content store grows unbounded — fold-time storing makes the envelope ≈ full transcript per session #1282 design decision: no cap and no eviction, ever — the envelope shares the session's lifecycle and the footprint is visible in acp_status.
  2. Two real test failures, root-caused not masked: preflight-compress e2e (fresh-session + model-switch) asserted exact recent-window boundaries on a 10,600-token window. With this PR's CCR default-on, the injected acp_retrieve tool schema is (correctly) part of the preflight token budget, shifting the boundary by one ~1.1k-token message — MARKER_9 folded instead of retained. Verified the mechanism: pinning ccr: { enabled: false } in those tests restores the original boundaries, so this is honest budget accounting, not data loss. Fix: pinned CCR off in the four preflight-compress e2e configs with an explanatory comment — those tests pin preflight mechanics (payload-size / model-switch triggers, recent-window retention) and CCR interplay with the cascade is separately covered by e2e-compress-cascade.test.ts (updated in this PR for default-on).

Gates on the rebased head

  • npm run typecheck — clean
  • npm test — 2535 tests, 2526 pass, 0 fail (9 e2e gated skips)
  • npm run build — success, dist-import-annotations OK

Ready to merge once CI mirrors the local gates. Merge order note: this is the last of the CCR batch (#1270 → #1189 → #1273 → #1207).

@ranxianglei

Copy link
Copy Markdown
Owner Author

🤖 Powered by ework · qwen3.8-27b

[bot] 🏷 Verified the rebased head 936a702 end-to-end. Gates mirror yours exactly:

Rebase integrity — head sits directly on current master tip a37e0182 (#1273 merge; git merge-base --is-ancestor confirms master ⊂ head), so your merge-order note holds: #1270 / #1189 / #1273 are all in master, this closes the CCR batch. Diff audit old-head→new-head: every file is accounted for — the PR's own delta is exactly doc union (4 files) + the #1283 fix (src/decompress-shared.ts, tests/ccr-v2.test.ts) + the preflight pin (tests/preflight-compress.test.ts); the rest (package.json kernel pin, plugin.ts/server.ts/store.ts plugin-lane arming, session-gc.ts companions, ccr-plugin.test.ts) belongs verbatim to the three merged PRs. Zero unexpected churn in the v2 core files (stream.ts, compress-settings.ts, compress-tool.ts, acp-status.ts, persist.ts).

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 true on anthropic+openai wires; responses/google/marker/text disarmed) and en/zh stay in sync. Stale "no eviction policy yet (tracked as follow-up)" sentence is gone in both languages, replaced by the #1282 decision "no cap and no eviction, ever — shares the session's lifecycle, footprint visible in acp_status". Good cleanup.

#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 acp-stored placeholder is NOT, and per the commit message it fails against the pre-fix code. This also subsumes the partial-view edge I'd listed as hypothetical.

Preflight pin (936a702) — four boundary-pinning e2e configs in tests/preflight-compress.test.ts pinned ccr: { enabled: false } with an accurate comment. Your A/B evidence (pin restores original boundaries ⇒ MARKER_9 shift is honest budget accounting from the acp_retrieve schema entering the preflight budget, not data loss) is the right way to root-cause it, and the separation is sound: those tests pin preflight mechanics, CCR×cascade interplay stays covered by e2e-compress-cascade.test.ts. Pinning all four (not just the two failing) is correct — the other two assert the same exact-budget arithmetic and would have been next to drift.

Arming site post-rebase — F0's fix survives intact (src/server.ts:1395, compressCfg.ccr ?? { enabled: true }) and composes cleanly with master's new pluginCcrOk gate (src/server.ts:1824-1825): proxy mode arms by default; plugin mode additionally requires explicit base-level enabled: true plus an anthropic/openai wire — which is precisely what the updated docs now claim.

Gates on 936a702 (ran locally after npm ci): typecheck clean · build OK (dist-import-annotations OK) · suite 2535 / 2526 pass / 0 fail / 9 e2e skips — matches your numbers exactly. CI: 9/9 green (real codex e2e, 4-leg matrix, version-guard, build-artifact, pi lane, codex overlay).

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.
@ranxianglei

Copy link
Copy Markdown
Owner Author

Owner decision shipped (head b3e3762): CCR is now opt-in on every lane — the proxy-mode default-on from d49e172 is removed.

Unset compress.ccr arms nothing anywhere: proxy mode stays disarmed until some level sets compress.ccr.enabled: true (after local verification, per the owner's rule), and plugin lanes keep requiring the explicit global true (#1271/#1273 gate untouched).

  • server.ts arming stamp keeps the raw three-level merge (resolvedCcrCfg = compressCfg.ccr, no forced {enabled:true})
  • applyCompressSettings loses the default-on else-branch; resolveRequestConfig returns base unchanged when nothing is configured (fast path restored)
  • 5 test files updated to the opt-in contract: default = byte-transparent/unarmed (range restore now refuses with "requires CCR" by default), explicit enable arms; preflight e2e pins dropped — the default-off boundary is now a tripwire against accidental default flips
  • READMEs (en/zh) + CONFIGURATION (en/zh) state the opt-in contract

Gates: typecheck ✓ / 2526 tests 0 fail (9 e2e-gated skip) / build ✓

@ranxianglei
ranxianglei merged commit c64c136 into master Sep 25, 2026
9 checks passed
@github-actions github-actions Bot mentioned this pull request Sep 25, 2026
antclt pushed a commit to antclt/billion-context that referenced this pull request Sep 26, 2026
…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.
AEmbers pushed a commit to AEmbers/billion-context that referenced this pull request Sep 30, 2026
… 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.
@ranxianglei
ranxianglei deleted the wt-1179-ework-daemon branch October 15, 2026 05:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] CCR v2: message-ID coverage in block summaries, range-level decompress, ID-returning search_context

1 participant