You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
顺带发现的小问题(记在此处,不另立 issue,不阻塞本 PR):kernel 0.0.84 的 not-found 错误文案是「…list current rules first (omit the text argument)」,而参数实际叫 rule——纯措辞出入,我们原样透传,修正归属 acp-kernel。
[bot] 🏷 Short answer: no — CCR v2 covers an orthogonal layer, so the kernel change is still required. But pairing the two upgrades the end-state beyond what I originally described.
Why CCR v2 alone can't do it. The blocker for deleted rules sits in the protection layer, upstream of everything CCR touches: buildCompressibleRanges (kernel dist/index.js:3983/3998) consults isMessageProtected, and acp_rule is in ALWAYS_PROTECTED_TOOLS (dist/index.js:1179) — those pairs are pushed into protectedMsgs and never enter any fold range. CCR v2's storeCoveredOriginals only archives messages that a fold actually covers; a message that can't be included in a range is never covered, so there is simply nothing for acp_retrieve to hand back. CCR improves the fate of folded content; it cannot make something foldable. Gate vs. recovery — the missing piece is the gate.
How they compose (and why it's better than my original plan stated):
Kernel change = the gate. Conditional protection (record pair protected ⟺ id still live per the message-stream walk; delete/clear/list/failed pairs unprotected) makes dead rule pairs eligible for folds. Mode-agnostic, cache-neutral until a fold happens.
CCR v2 = the upgraded outcome. Once those pairs are foldable, CCR v2 (default-on in proxy mode) automatically persists their originals at fold time → a deleted rule becomes retrievable by ref after folding, not just absorbed into a lossy summary. My earlier wording "死 pair 随周围内容一起被吸收进摘要" upgrades to "absorbed AND archived — exact text one acp_retrieve("mNNNNN") away, zero resident cost."
No sequencing coupling. Either merge order works: kernel-first without CCR v2 yet = dead pairs fold into plain summaries (the original ask, satisfied); CCR v2-first alone = no behavior change for these pairs (they're still gate-excluded). Both landed = best of both.
Cache discipline identical. All byte mutation stays at fold time; CCR v2's own invariant ("substitution only at fold time, retrieval/range-restore injections ride request-only channels") is the same prefix-cache-safe pattern the owner set as the condition.
Plugin mode: CCR stays inert there (existing arming/disarm gate — agents own their folds and acp_retrieve has no channel), so plugin-mode dead pairs fold into plain summaries, exactly like any other ordinary tool result in that mode. Consistent, nothing extra to build.
So: proceeding with the acp-kernel PR as announced (it's the prerequisite either way); CCR v2 (#1207) complements it rather than replacing it — no scope changes on my side, and I'd suggest no cross-linking beyond mentioning this composition in both PR descriptions.
[bot] 🏷 Checked the full #1179 thread (all floors, incl. the v2 plan and the #1207 completion report). Conclusion unchanged, and the close read actually tightens it in three ways:
1. Zero overlap — literally different repos. Your own plan (floor 4) states CCR v2 needs no kernel changes: it's host-side wiring in bili around the already-shipped-but-unwired kernel storeCoveredOriginals(). My work is the exact opposite side — kernel-side protection logic, zero bili files. The two efforts can't collide; whichever lands first just gets rebased past by the other.
2. The gate-vs-recovery split is visible right in your API signature.storeCoveredOriginals(store, messages, state, newBlockIds, countTokens) archives what a completed fold covered — i.e. members of newBlockIds' effectiveMessageIds. A message that buildCompressibleRanges never admitted into any range (which is exactly what ALWAYS_PROTECTED_TOOLS does to every acp_rule pair today) appears in no block, gets no store entry, and is unreachable by acp_retrieve, range-decompress, or ID-returning search_context. Recovery machinery, however deep, starts below the gate.
3. Your L0/L1/L2 ladder describes the end-state of deleted rules perfectly — once the gate opens. Dead rule pair folded → L0: listed in the covering block's startRef/endRef span + summary; L1: head preview on search_context hits ("📦 removed rule · 24 tok"); L2: exact original text via acp_retrieve("mNNNNN") or range restore. That's your "let the model decide" philosophy applied to rule history for free — nothing rule-specific to build in CCR. And it degrades gracefully per mode: proxy mode gets the full ladder (CCR default-on), plugin mode folds them to plain summaries like any ordinary tool result (CCR inert there by the existing arming gate).
So: CCR v2 doesn't cover the need standalone, but post-#1207 the kernel change delivers exactly the shape you'd want rather than mere lossy absorption. No scope impact on either side; I'll cross-reference #1179 in the kernel PR description for discoverability.
Starting the acp-kernel implementation now (clone → conditional-protection design v2: stream-derived liveness walk over Recorded/Removed/Cleared result lines, fail-closed on malformed Recorded , everything else unprotected — as detailed in my earlier comment). Progress links follow here.
Activity
ranxianglei commentedon Sep 23, 2026
收到,开始处理。先确认两点:
removeRule/clearRules可用,本工作基于当前 master。delete: "ruleN"(按 id 精确删除)和clear: true(清空全部,标题里点名的两个内核能力都接上);两者彼此互斥、且与rule互斥。删除成功返回Removed ruleN: <text>;id 不存在时透传内核的引导信息(提示先 list)。tool 描述同步补充删除语义,让模型能发现这个能力。实现位置:
executeRule(src/rules-feature.ts)是 streaming loop 与 plugin tool API 两条路径共用的执行器,改一处即覆盖两种压缩模式;工具定义在src/compress-tool.ts单一定义、四处 wire shape 共用,schema/description 改一处即全生效。测试完成后贴 PR 链接。ranxianglei commentedon Sep 23, 2026
[bot] 🏷 完成,PR 已开:**https://github.com/ranxianglei/billion-context/pull/1178**(分支
2026-09-23_rule-delete)实现(方案 A)
acp_ruleschema 增加两个互斥可选参数:delete: "ruleN"(按 id 精确删除)、clear: true(清空全部);工具描述同步补了说明句,模型才能发现这个能力(anthropic/openai/responses/google 四种 wire shape 共用同一份定义)。executeRule(src/rules-feature.ts,proxy 模式与 plugin 模式共用的唯一执行器)接线 kernel 0.0.84 的removeRule/clearRules:Removed ruleN: <text>;清空返回Cleared N rule(s).;无规则时清空诚实报No rules to clear.,不假装成功;delete/ 非 trueclear被忽略,与既有非字符串rule的约定一致。行为变更披露(old → new)
rule= 记录);wire 上唯一可见变化是工具描述文本变长和 schema properties 从 1 个键变 3 个键,且都只在compress.rules: true时可见。验证
E2E_CHECK=1零 token 预检 ✅compress.rules门控之后,三条 native-tool wire 均有 HTTP 级 wire 测试覆盖。如需要可用 Actions manual dispatch 触发 CI e2e。顺带发现的小问题(记在此处,不另立 issue,不阻塞本 PR):kernel 0.0.84 的 not-found 错误文案是「…list current rules first (omit the text argument)」,而参数实际叫
rule——纯措辞出入,我们原样透传,修正归属 acp-kernel。合并留给你:#1178
ranxianglei commentedon Sep 23, 2026
需要核实,删除会导致缓存大面积破坏吗
ranxianglei commentedon Sep 23, 2026
[bot] 🏷 已核实,结论:删除不会破坏缓存——请求字节零变化。证据链如下(均可在代码里对到行):
1. 规则在 wire 上的真实载体不是系统提示词。 kernel 确实导出了
formatRulesForPrompt(每轮往系统提示词注入# Persistent rules…块,见 dist/index.js:6517),但本 proxy 完全没有引用它(src/ 全量 grep 零命中;config.ts 也明确写了该特性刻意不添加任何系统提示词)。模型每轮实际看到的规则内容,是历史中按原始时序位置的acp_rule调用+结果消息对(src/loop/core.ts:664-682把执行后的 pair 写回 coreMessages),且这对消息被 kernel 按工具名硬保护、永不被折叠(ALWAYS_PROTECTED_TOOLS = ["compress", "acp_rule"],dist/index.js:1179/1203)。2. 删除改了什么。
removeRule/clearRules只动state.rules记账数组——它只服务于 list 输出、重复检测、id 分配和条数上限,从不被渲染进请求体。所以删除前后发出的请求逐字节相同 → 前缀缓存完整保留,不存在任何失效点。同理:新增规则是纯尾部追加(前缀不动),list 是只读。3. 反面对比。 如果按系统提示词注入方案实现(kernel usage prompt 暗示的方向),那么每次增删都会改写头部字节 → 立即全量缓存失效。正是「历史消息对」这个载体让新增(尾部追加)和删除(仅元数据)都成为缓存零成本操作。
4. 一个诚实的代价披露(设计取舍,非缺陷)。 因为旧的调用/结果对仍然留在历史里(保护判定只看工具名,与记录是否存在无关),删除后那条规则的原文在上下文中依然可见。删除生效于:list 不再列出它、重复检测与上限不再计它、且新增的
Removed ruleN轨迹让模型明确知道它已作废(相当于把原来手写的「作废声明」变成了结构化操作)。若要连旧文本一起从历史抹掉,就必须移除那个位于历史中段的消息对——其后所有字节整体前移,缓存会从最早被删的 pair 位置起一直失效到上下文末尾,那才是真正的大面积破坏。这是「彻底清除 vs 一次性大缓存失效」的产品决策,我没有擅自改;如需要可以另开讨论(比如加一个显式的 purge 参数,默认不擦除)。kuviki commentedon Sep 23, 2026
能否实现允许被删除的 rule 被折叠?
ranxianglei commentedon Sep 23, 2026
[bot] 🏷 可以实现,但改动归属在 acp-kernel,不在本仓库——核实过保护机制后结论如下:
为什么本仓库做不到干净的版本。 折叠时的保留判定在 kernel 的
isMessageProtected(dist/index.js:1198):ALWAYS_PROTECTED_TOOLS.includes(toolName)对acp_rule无条件先行返回 true,任何宿主可配的钩子(config.isToolProtected)都排在其后、只能加保护、不能撤。proxy 侧唯一能动手的办法是删除时改写历史消息对里的 toolName——但那会立刻改动中段字节,正是上一条回复里说的「从最早被删 pair 起到末尾全量缓存失效」。所以 proxy-only 方案与「缓存零成本」互斥,排除。推荐设计(kernel 侧,条件化保护)。 把
acp_rule的保护从「按名永保」改为「记录存活才保」:Recorded ruleN: …解析出 id(这是两种模式共用的唯一执行器executeRule产出的规范格式,proxy 模式src/loop/core.ts:664-682与 plugin 模式src/plugin.ts:797走同一条路),配对 call 复用现有 toolCallId 配对逻辑;缓存语义(与 @Dog 关心的对齐)。 删除时刻字节仍零变化(缓存不破坏);死 pair 在下一次自然折叠时被当作普通旧内容吸收进摘要——不产生超出折叠本身的额外成本(折叠本来就会从折叠点起重写历史)。若上下文压力始终不触发折叠,死 pair 会滞留,但每条 ≤300 字符×2 条消息、总量受 50 条上限约束,量级很小。
实施顺序(跨仓,按发布纪律)。 ① acp-kernel 提 PR 实现条件化保护 → ② 发版 0.0.85 并确认 npm 可见 → ③ 本仓库单独一个 bump PR(只动 pin,预计零逻辑改动)+ 文档补一句「被删规则的历史调用对自 0.0.85 起可被折叠」。
方案就是上面这一条,没有更优替代;你确认后我先把 acp-kernel 的 PR 提了。
kuviki commentedon Sep 23, 2026
delete/clear 的工具调用也是受保护的吗?若是,它们也改为条件化保护吗?
ranxianglei commentedon Sep 23, 2026
[bot] 🏷 是,目前全部受保护——保护判定纯按工具名(
ALWAYS_PROTECTED_TOOLS,dist/index.js:1179),不区分操作:record、list、delete、clear、甚至失败调用(参数超限/重复被拒)的 pair 都被折叠规划器同等排除(buildCompressibleRanges→isMessageProtectedWithPairing,dist/index.js:3983/3998)。一个名字罩住所有操作。条件化方案里,我的建议不是「一刀切条件化」,而是分两类:
Recorded ruleN:):条件化保护——id ∈ state.rules 时受保护,id 失效后变得可折叠。这是唯一真正的常驻内容载体。Removed ruleN轨迹也随之失去意义,两者一起消失 ≈ 净归零,模型不会残留「有条规则但已被作废」的半截知识;preserveRecentMessages: 5/preserveRecentTokens: 5e3,dist/index.js:369-370)本来就让刚发生的操作轨迹多活几轮;折叠时摘要器也会把它们当普通内容提一笔。最坏情况是留下一条孤立的Removed rule3:短行,自明且无害。Recorded开头但 id 畸形 → 回退为继续保护(防格式漂移把活规则折掉)。其余已知结果格式(Removed/Cleared/各类错误串)由共用的executeRule产出、集合封闭,确定性归类为可折叠。顺带澄清为什么 compress 留在 ALWAYS 列表而 acp_rule 不必: compress 调用是压缩块引用的唯一载体,折掉它整个 ref/decompress 体系就断了;acp_rule pair 没有任何外部引用结构,折叠它不损失任何结构性信息。
实现位置不变:以上全部落在同一个 acp-kernel PR(保护判定改为「按 pair 解析 + 记录存活性」),billion-context 仍是纯 pin bump。确认的话我就从 kernel 侧开工。
ranxianglei commentedon Sep 24, 2026
内核你可以搞,只要是不破坏缓存就可以搞
ranxianglei commentedon Sep 24, 2026
[bot] 🏷 收到,开工。方案按上一条的两类保护实现,补充一个关键改进:存活性判定改为从消息流自身推导(按序扫描:
Recorded ruleN:入集合、Removed ruleN:出集合、Cleared N清空集合),而不是查 state.rules——因为 plugin 模式下折叠发生在 agent 自带的 kernel 副本里,它的 state 没有 rules 表,查表会把活规则误判为可折;从消息流推导在两种模式下结论一致,且无需任何跨进程同步。缓存语义不变:删除时刻字节零变化,死 pair 只在下次自然折叠时随周围内容一起被吸收。另外这个 kernel 改动单独发版是行为无感的(no-op safe):宿主不产生 Removed/Cleared 事件时,所有 Recorded pair 照旧永保,与现状逐字节一致。
进度会在这里回贴:① acp-kernel PR ② 0.0.85 发布确认 ③ 本仓库 pin bump PR。
BsoBird commentedon Sep 24, 2026
Can we achieve this by pairing it with the newly built CCR mechanism?Once CCR V2 lands, does it cover this need?
BsoBird commentedon Sep 24, 2026
u can check this:#1179
ranxianglei commentedon Sep 24, 2026
[bot] 🏷 Short answer: no — CCR v2 covers an orthogonal layer, so the kernel change is still required. But pairing the two upgrades the end-state beyond what I originally described.
Why CCR v2 alone can't do it. The blocker for deleted rules sits in the protection layer, upstream of everything CCR touches:
buildCompressibleRanges(kernel dist/index.js:3983/3998) consultsisMessageProtected, andacp_ruleis inALWAYS_PROTECTED_TOOLS(dist/index.js:1179) — those pairs are pushed intoprotectedMsgsand never enter any fold range. CCR v2'sstoreCoveredOriginalsonly archives messages that a fold actually covers; a message that can't be included in a range is never covered, so there is simply nothing foracp_retrieveto hand back. CCR improves the fate of folded content; it cannot make something foldable. Gate vs. recovery — the missing piece is the gate.How they compose (and why it's better than my original plan stated):
acp_retrieve("mNNNNN")away, zero resident cost."acp_retrievehas no channel), so plugin-mode dead pairs fold into plain summaries, exactly like any other ordinary tool result in that mode. Consistent, nothing extra to build.So: proceeding with the acp-kernel PR as announced (it's the prerequisite either way); CCR v2 (#1207) complements it rather than replacing it — no scope changes on my side, and I'd suggest no cross-linking beyond mentioning this composition in both PR descriptions.
中文一句话:CCR v2 解决的是「折叠后内容去哪」(可检索归档),而删除规则卡的是「能不能进折叠范围」(内核保护判定)——前者替代不了后者,两者叠加后效果是:被删规则在下次自然折叠时既腾出上下文、又可通过 acp_retrieve 按 ref 取回原文,缓存语义不变(字节变化只发生在折叠时刻)。内核 PR 照原计划推进。
ranxianglei commentedon Sep 24, 2026
[bot] 🏷 Checked the full #1179 thread (all floors, incl. the v2 plan and the #1207 completion report). Conclusion unchanged, and the close read actually tightens it in three ways:
1. Zero overlap — literally different repos. Your own plan (floor 4) states CCR v2 needs no kernel changes: it's host-side wiring in bili around the already-shipped-but-unwired kernel
storeCoveredOriginals(). My work is the exact opposite side — kernel-side protection logic, zero bili files. The two efforts can't collide; whichever lands first just gets rebased past by the other.2. The gate-vs-recovery split is visible right in your API signature.
storeCoveredOriginals(store, messages, state, newBlockIds, countTokens)archives what a completed fold covered — i.e. members ofnewBlockIds'effectiveMessageIds. A message thatbuildCompressibleRangesnever admitted into any range (which is exactly whatALWAYS_PROTECTED_TOOLSdoes to everyacp_rulepair today) appears in no block, gets no store entry, and is unreachable byacp_retrieve, range-decompress, or ID-returningsearch_context. Recovery machinery, however deep, starts below the gate.3. Your L0/L1/L2 ladder describes the end-state of deleted rules perfectly — once the gate opens. Dead rule pair folded → L0: listed in the covering block's
startRef/endRefspan + summary; L1: head preview onsearch_contexthits ("📦 removed rule · 24 tok"); L2: exact original text viaacp_retrieve("mNNNNN")or range restore. That's your "let the model decide" philosophy applied to rule history for free — nothing rule-specific to build in CCR. And it degrades gracefully per mode: proxy mode gets the full ladder (CCR default-on), plugin mode folds them to plain summaries like any ordinary tool result (CCR inert there by the existing arming gate).So: CCR v2 doesn't cover the need standalone, but post-#1207 the kernel change delivers exactly the shape you'd want rather than mere lossy absorption. No scope impact on either side; I'll cross-reference #1179 in the kernel PR description for discoverability.
Starting the acp-kernel implementation now (clone → conditional-protection design v2: stream-derived liveness walk over
Recorded/Removed/Clearedresult lines, fail-closed on malformedRecorded, everything else unprotected — as detailed in my earlier comment). Progress links follow here.ranxianglei commentedon Sep 24, 2026
[bot] 🏷 进度 ①:acp-kernel 侧已完成并开 PR。
feat: conditionally protect acp_rule pairs by stream-derived rule liveness,CI 已绿(test 22/24 + pr-validation)实现要点(与上一条口径一致):
acp_rule移出ALWAYS_PROTECTED_TOOLS,新增collectRulePairProtection(messages)——按序扫描 acp_rule tool-result:Recorded ruleN:入活集、Removed ruleN:出集、Cleared N rule(s).清空;record pair 仅当 id 存活时受保护(内核契约:id 永不复用,终态集合即逐事件存活性)。其余 acp_rule 交互(删除/清空/列表/失败)= 普通可折叠内容。畸形Recorded前缀 fail-closed。五个保护消费点全部接线(fold gate、BLOCKED refs、filterProtectedToolMessages、CCR skip-store、absorb),并从 index 导出。行为变更披露(owner 已批准的方向):旧 = 所有 acp_rule pair 永久硬保护;新 = record pair 随规则存活性保护,删除后下次自然折叠可被吸收。no-op safe:历史中没有任何 Removed/Cleared 事件时行为与现状完全一致——单独发版零影响,等宿主真正开始删规则才生效。缓存安全(owner 条件):删除时刻字节零变化,只有自然折叠吸收死 pair 时才改请求字节(折叠本身就要重写历史);叠加 CCR v2(#1179)后死 pair 原文还可
acp_retrieve取回。测试:新增
tests/rule-pair-protection.test.ts11 个用例(liveness walk 全分支 + 经buildCompressibleRanges的 gate 集成 + no-op safety),更新 1 条过期断言。本地 pre-flight:typecheck 干净 · 全套 1040/1040 · build 干净。下一步:② PR #378 合并 → 走 kernel 发布流程出 0.0.86(我会准备好 release 分支/commit,合并动作按规矩由人执行)→
npm view acp-kernel version确认上线;③ 本仓库再开一个纯 pin bump PR(0.0.84 → 0.0.86,跨过的 0.0.85 是 media-payload 保留修复 #365,会在 PR 里一并披露)+ 文档注记。届时回贴链接。Merge pull request #1178 from ranxianglei/2026-09-23_rule-delete
feat: acp_rule gains delete/clear backed by kernel removeRule/clearRu…