Skip to content

[bug] DSH 桌面端 0.1.188:宿主原生压缩落地摧毁折叠基底 → 同一区间永久不可锚定、compress 连片失败并熔断(compaction-basic auto:false 仍无效) #2432

Closed

Description

@weirdseed

DSH 桌面端 0.1.188:宿主原生压缩落地摧毁折叠基底 → 同一区间永久不可锚定、compress 连片失败并熔断

环境

  • macOS(Apple Silicon)/ DSH 桌面端(/Applications/DeepSeek Harness.app,非 npm 全局 CLI)
  • billion-context 0.1.188(ACTIVE SURFACE: pack=default | host=billion-context 0.1.188 (62598940))
  • 工作目录 /Users/lzc/Downloads/jsb;项目缓存 ~/.dsh/storages/session_projcache/sessions/session-4f278126-4844-47a3-b022-effee487481a.json
  • 上游 deepseek-official / deepseek-flash(非回环 ⇒ 走代理 env 路径)
  • profile ~/.dsh/profiles/desktop/package.json 的 dsh.profile.bundles 含 billion-context(0.1.188)

与既有 issue 的关系(先说清,避免被判重复)


摘要(三条缺陷,按严重度排序)

# 缺陷 影响
1 宿主原生压缩实际落地,把已折叠块的内容替换成原生摘要,ref 映射随之失效;bili 侧仍按旧 ref 广播区间 之后任何 compress 都报 cannot be anchored,且不可通过重试恢复;acp_status 广播的"Nudge 可用区间"是陈旧 ref 算出来的,照它去压必定失败
2 compress FAILED 文案("stop / do not retry / do not poll")与 acp_status 的 Nudge("Compressible ranges (…oldest first)")互相矛盾,且熔断计数器在模型不可见的情况下递增 模型两头堵:照 Nudge 压 → 失败;失败文案叫它别再调 → 于是彻底放弃压缩,上下文只能靠宿主原生压缩硬撑,进一步加剧缺陷 1
3 profile 层 dsh.bundle.patch.yml 已经写了 compaction-basic: { auto: false },仍然没能阻止宿主原生压缩落地 用户以为已按官方方式关闭原生压缩,实际没有;allowDshCompaction 这条兜底也没生效(与 #2360 缺陷 3 同源,但在 DSH 桌面端仍未修复)

缺陷 1(主要):基底被摧毁后区间永久不可锚定

1.1 bili 自己的诊断标签

[cause: substrate-destruction — host-native compaction or bulk client-side history rewrite
 landed outside bili's knowledge (#1729/#2193); structural, report it]

1.2 最硬的证据:acp_status 自述折叠块内容已被宿主原生压缩替换

acp_status 输出里直接有这一段:

PRE-COMPACTION ARCHIVE — 2 block(s): content was replaced by the client's native compaction summary,
so it is no longer in the session history and decompress is unavailable.
  b51 — content replaced by client native compaction summary
  b56 — content replaced by client native compaction summary

即:bili 自己知道 b51/b56 的内容已被宿主原生压缩摘要顶掉、decompress 已不可用;但同一份 acp_status 紧接着仍然广播这些块覆盖区间作为"可压缩区间"。

1.3 随之而来的连片失败

同一会话内,compress 连续失败 15 次以上,全部是同一族错误:

Requested range(s) cannot be anchored (e.g. m01778..m01923) — the refs exist in this session's
ref map, but the messages they point to are no longer in the visible context and no active block
covers them: the message content changed (or the message was filtered out of the view) and now
carries a new ref, leaving your old refs dangling.
[diagnostics: session highest ref=m03761, unknown ranges in request=3/5, session history=63 compression(s), 63 block(s)]

紧接着同一个响应里还出现自相矛盾的第二个诊断(这条尤其可疑:m00001 是 acp_status 自己在前一条消息里刚广播过的区间起点):

range m00001..m00600: startId="m00001" does not exist in this session (typo or wrong session) — run acp_status for current refs.
range m00601..m01200: startId="m00601" does not exist in this session (typo or wrong session) — run acp_status for current refs.
range m01201..m01768: startId="m01201" does not exist in this session (typo or wrong session) — run acp_status for current refs.

且失败区间随尝试而漂移:早先失败的是 m01778..m01923,后来变成整片 m00001..m01768。这与 #2362 描述的"失败区间随每次尝试变宽"一致。

1.4 附带记录到的 unannounced-rewrite

COMPRESSION CONFLICTS — 1 event(s) in this session (1 active · 0 historical; active = within 7 days):
[2026-10-08 15:33:23Z] unannounced-rewrite — 359/1611 incoming message(s) carry pre-turn refs of 2196 known

本例没有 fork / 没有会话限额 / 没有模型切换。这条记录与 #2426 描述的"fork 后误报"不是一回事;在本例里它更像是缺陷 1 的症状(宿主重写了历史),而不是独立 bug。不过既然 bili 把它写成"冲突",模型会照着"两个压缩器在打架"去做无用功 —— 建议在这种"由自身 substrate-destruction 导致的 ref 失效"场景下,不要把它呈现为"另一个压缩器在跟你抢",而是直接说明基底已失效。

1.5 熔断计数器不可见地递增

失败文案说 3 consecutive:

[COMPRESS CIRCUIT BREAKER: 3 consecutive compress failures in this session — every attempt has
failed and further attempts will keep failing. STOP calling compress now: do not try other ranges,
do not re-issue any previous range, and do not poll acp_status. Continue your actual task without
compressing — compression happens again only when there is genuinely new content to fold.]

但同一次尝试之后我只多发了一次 compress,熔断器就变成 4 consecutive。也就是说计数器把"被拒绝/未真正尝试"的调用也算进去了,而模型看不到当前计数,无法判断还剩几次。


缺陷 2:两条指令互相矛盾

同一时刻系统里同时存在:

  • compress 失败:"STOP calling compress now: do not try other ranges, do not re-issue any previous range, and do not poll acp_status."
  • acp_status 的 Nudge:"Nudge: idle — max compressible 13051 < threshold 50000" + Compressible ranges (3, oldest first): m02339–m03456 … m03760–m03761 [PROTECTED: compress — not compressible]

而且被标成 PROTECTED: compress — not compressible 的区间,与 Nudge 标题"Compressible ranges"写在同一张表里 —— 模型很容易把这两行当成"可压"。实测照它压就是失败。

期望:熔断后 Nudge 应当停止广播可压区间(或明确标注"该区间已因基底失效不可压"),而不是让模型在"压"与"别压"之间反复横跳。这与 #2360 缺陷 2 是同一类问题,在 0.1.188 仍未修。


缺陷 3:profile 层关不掉宿主原生压缩

本机 bundle patch(~/.dsh/profiles/desktop/node_modules/billion-context/dsh.bundle.patch.yml)原文:

# billion-context dsh bundle layer (#949).
- insert:
    - id: bili-native
      name: billion-context
- id: compaction-basic
  config:
    auto: false

compaction-basic.auto 已经是 false,但宿主原生压缩仍然落地(见 1.2 的 PRE-COMPACTION ARCHIVE)。

dist/index.js 里 allowDshCompaction 出现 536 次,同时存在 dshCompactionRefusal / dshCompactionRefused 两个状态字段 —— 说明拒绝闸门确实被触发过,却没能阻止 checkpoint 落地。这与 #2360 缺陷 1/3 是同一族:闸门判据之外的路径把压缩放进来了。

备注:错误文案引用的 CONFIGURATION.md → "Detecting other compression plugins (#1206)" 在本机 ~/.dsh/profiles/desktop/node_modules/billion-context/ 下找不到该文件(包内只有 LICENSE / README.md / README.zh-CN.md / dist / dsh.bundle.patch.yml / hermes-plugin / paper)。建议把该节内容也放进 README 或包内,否则用户按错误提示去自查时无从下手。


复现步骤

  1. DSH 桌面端,profile 装 billion-context 0.1.188,dsh.bundle.patch.yml 保持默认(compaction-basic: auto: false)。
  2. 开一个持续较长的会话(本例 63 次折叠、最高 ref m03761、约 1600+ 条消息),做正常的 compress,允许宿主在上下文压力下触发一次原生压缩。
  3. 观察 acp_status:出现 PRE-COMPACTION ARCHIVE — content replaced by the client's native compaction summary。
  4. 此后任意一次 compress(无论用哪段区间)→ Requested range(s) cannot be anchored。
  5. 重试 3~4 次 → 熔断器触发,且 Nudge 仍在广播陈旧区间。

期望行为

  1. 基底被宿主压缩破坏后,acp_status 应停止广播基于旧 ref 的可用区间,并显式标注"折叠基底已失效,需重开会话"。
  2. 熔断后 compress 与 Nudge 的指令必须一致(要么都不提可压区间,要么给出可恢复路径)。
  3. compaction-basic: auto:false + allowDshCompaction 必须真的挡住宿主原生压缩落地;挡不住时至少要在 acp_status 里明确说明"开关未生效"。
  4. 熔断计数器当前值应当对模型可见(例如在 acp_status 里显示 consecutiveFailures: N / limit)。
  5. 建议:在 substrate-destruction 场景下不要把记录呈现为"另一个压缩器在抢",直接说明基底已失效,避免模型去做无用的"去掉另一个插件"排查。

影响

本会话里 compress 已完全不可用(最终熔断)。模型只能改用"靠宿主原生压缩硬撑",正是这个循环把基底摧毁得更彻底。对于宣称"month-long single sessions (billions of tokens)"的场景,这个组合是阻断性的。

Activity

  1. weirdseed commented on Oct 8, 2026

    @weirdseed
    Author

    补充证据(同一会话,提交本 issue 之后):Nudge 广播的 ref 与 compress 校验的 ref 不是同一代

    又试了一次 compress,这次报的是更根本的原因 —— 三条请求区间全部解析失败:

    None of the 3 requested range(s) resolved — every ref is unknown to this session.
    Refs are per-session snapshots, assigned once when a message is first rendered; no compress
    reassigns them, so unknown refs cannot come from an earlier compress in this session.
    They come from a different generation: a previous session instance (switching model or upstream
    mid-conversation starts a fresh session whose refs restart at m00001), the generation before a
    native-compaction rebase (which also resets refs to m00001), or a typo.
    [diagnostics: session highest ref=m03761, unknown ranges in request=3/3, session history=63 compression(s), 63 block(s)]
    [cause: stale-ref — the refs belong to another session generation (or are typos)]
    
    range m01979..m02019: startId="m01979" does not exist in this session (typo or wrong session)
    range m02021..m02043: startId="m02021" does not exist in this session (typo or wrong session)
    range m02048..m02049: startId="m02048" does not exist in this session (typo or wrong session)
    

    关键点:这 3 段区间是 acp_status 在紧邻的上一条消息里、以 Compressible ranges (3, oldest first) 的名义主动广播给我的。 也就是说同一个插件里两个子系统对「当前是哪一代会话」的判断不一致:

    • acp_status(Nudge / 区间计算) 用的仍是 rebase 之前的 ref 映射 —— 于是它把 m01979..m02019 这类在新一代里根本不存在的 ref 当成可用区间推给模型;
    • compress(校验) 用的是当前代的 ref 映射 —— 因此每次都判 stale-ref。

    这与本 issue 缺陷 1 中 PRE-COMPACTION ARCHIVE — content replaced by the client's native compaction summary 是同一根因的两种表现:宿主原生压缩触发了一次 rebase(ref 重置回 m00001),acp_status 没有跟着切换代次,而 compress 切换了。

    这条解释了为什么模型会连挂 15+ 次:它严格按 Nudge 广播的区间去压,而那些区间在新一代里必然无法锚定。失败文案还反过来叫它「do not re-issue any previous range, and do not poll acp_status」,于是它既无法从失败中恢复,也无法通过重新查询拿到正确区间 —— 只能盲试,直到熔断。

    熔断计数仍在无提示地递增

    三次尝试后计数依次为 3 → 4 → 5 consecutive compress failures,模型全程看不到当前计数,也无法判断还剩几次额度。

    建议的最小修复(按性价比排序)

    1. Nudge 必须从 compress 校验所用的同一份代次快照计算区间。两者不一致时,宁可完全不广播区间,也不要广播无法锚定的区间 —— 现在的行为是把模型引向必然失败。
    2. compress 判定为 stale-ref / substrate-destruction 时,acp_status 应停止广播可压区间,并明确输出类似「折叠基底已失效(代次已变),压缩不可用;请重开会话」。
    3. 熔断计数器对模型可见(例如 consecutiveFailures: 5 / limit)。
    4. 失败文案与 Nudge 的指令必须一致:要么都禁止压缩,要么都给出可执行的恢复路径。
    5. 不要在这种场景呈现为「另一个压缩器在跟你抢」(本会话出现的 COMPRESSION CONFLICTS … unannounced-rewrite — 359/1611 incoming message(s) carry pre-turn refs of 2196 known),模型会去做无用的「排查并关掉另一个插件」,而实际上冲突源就是宿主自己的原生压缩。
  2. ranxianglei commented on Oct 8, 2026

    @ranxianglei
    Owner

    分拣:三条缺陷里,1 条大概率是"旧版本落地的存量伤害",2 条是 master 上仍开放的真实缺陷

    先给结论,再给证据链。

    缺陷 3(auto:false 没拦住)——最可能是时间线误读,需要两份证据定案

    关键时间线:#2360(把 responses 线纳入 dsh compaction 拒绝闸门)的分支是 2026-10-08_dsh-responses-gate,今天 09:04 才合入,v0.1.188(今天 12:58)是第一个包含它的发布。而你的会话有 63 个折叠块、~1600+ 消息、最高 ref m03761——这是一个数周跨度的会话。

    也就是说:如果 checkpoint 是在 0.1.187 及更早版本上落地的(那时 responses 线的 summarize 调用在协议检查处静默放行,#2360 修的就是这个),0.1.188 的闸门根本没机会见到那次调用。你今天 15:33Z 的 unannounced-rewrite 事件时间戳与"升级后第一次重放被检测到"完全兼容——压缩历史在落地之后的每个请求里都会重放,新版本 bili 第一眼看到它就记事件。

    auto:false 这层是 dsh 侧语义:packages/compaction/compaction-basic/src/index.ts:130 只跳过 pressure/overflow 的自动注册,手动 /compact 不受它管(bili 侧闸门注释也明说三类触发共用一个信封、拒绝是无条件的)。

    请提供两份证据定案(任一即可):

    1. bili 日志(~/.local/state/billion-context/ 下)按时间 grep:dsh native compaction call identified(被拒会有这条 warn)+ 版本启动行(bili 自更新时间线)——如果落地时刻 bili 版本 < 0.1.188 且无 REFUSED 记录,即坐实"存量伤害";
    2. session projcache JSON(~/.dsh/storages/session_projcache/sessions/session-4f278126-….json)或 bili 侧 session 状态——可以精确定位 checkpoint 落地事件的时间。

    缺陷 1 的"两个子系统代次不一致"——机制存疑,效果为真

    src/acp-status.ts:28-32:ranges 每次调用从活状态重算,且 #847 保证"只广播 submit gate 接受的区间"——同一份状态,同代。你引用的证据里,广播区间是 m02339–m03456,失败区间是 m01979..m02049——两者数字对不上,更像模型引用的是自己可见历史里的旧代 acp 标签(rebase 后整个旧标签空间全死),而不是"status 广播了死区间"。这个需要上面的 session 文件才能最终裁定。

    但效果是真的:rebase 后模型上下文里满是死 ref,唯一正确的恢复路径就是"acp_status 拿活区间→压缩成功→熔断自动解除",而熔断文案明确禁止 poll acp_status(见下)——模型被关进了没有出口的房间。

    缺陷 2(熔断 vs Nudge 矛盾)——master 上仍开放,已核实

    修复方向(按此开工作项)

    1. Nudge/acp_status 熔断感知(小):武装时停播(或明确标注"已因连续失败停用")可压区间,并显示 consecutiveFailures: N / 3(含 10min 衰减说明)。
    2. 熔断恢复指令按因施策(小):src/stream.ts 的暂停段落写死"do not poll acp_status",对 model-loop 噪音是对的,对 substrate-destruction 是错的——唯一出路就是查一次 acp_status 用活区间重锚(成功即解除熔断)。cause 已有(compressFailureCause),让暂停段落 cause-aware。
    3. 检测式兜底泛化到 dsh 线(中):fix: rebase fold substrate on Codex local auto-compaction (#2372) #2373 已给 codex 线做了"摘要模板+覆盖缺口→立即原地 rebase"(master ede3244),dsh 的 checkpoint 有稳定文本标记(<compacted-summary> / CHECKPOINT_PREAMBLE),同样的模式可以让"漏网的落地"只花一个 turn,而不是进入死亡螺旋——无论它是手动 /compact、allowDshCompaction 显式放行,还是未来的闸门失配。
    4. CONFIGURATION.md 补一行:auto:false 只关自动触发,手动 /compact 仍在(会被 bili 拒,拒得对)。

    你的复现路径(缺陷 1.4/1.5 的 receipt 全文)对定案很有价值——如果能补上前述日志/会话文件,1 和 3 可以直接开工。

  3. ranxianglei commented on Oct 8, 2026

    @ranxianglei
    Owner

    🤖 Powered by ework · qwen3.8-27b

    [bot] 🏷 收到分拣结论。独立核查完毕,先交证据,再报开工计划。

    一、对分拣结论的独立核实(全部代码级验证过)

    • 缺陷 2 在 master 仍开放 ✓ 与你的核实一致:compressFailStreak 只存在于 src/stream.ts,kernel/src/nudge-text.ts 的 Compressible ranges 表与 src/acp-status.ts 的 ranges 段均无熔断感知;[PROTECTED: compress] 行混在同一标题下(kernel/src/nudge-text.ts formatRanges)确认是误导源。计数把每个失败调用计入是 dsh内部问题 出现这个错误后,重启dsh可以发消息 但陷入无限压缩失败 #2146 设计(any spec),不是 bug。
    • marker 漂移排除:拉了 @deepseek-ai/dsh-compaction-basic 0.2.0-rc.2 与 0.2.1-alpha.1 两个 tarball,lib/ 逐字节相同,COMPACTION_INSTRUCTION 未变、仍以被跟踪的前缀开头。已知版本区间内不存在指令文本漂移——闸门失配若发生,不在 marker 这一层。
    • fix: rebase fold substrate on Codex local auto-compaction (#2372) #2373 模式已在 master(ede3244),dsh 泛化有现成原语:markNativeCompactionBoundary + reconcileNativeCompactionBoundary 背靠背 rebase。

    二、新增一条旁路证据(与时间线假设互补,不互斥)

    你的"存量伤害"时间线推断我认为是本例最可能的定案;但代码里存在第二条与版本无关的旁路,0.1.188 上同样成立:

    src/agent/dsh-native.ts:865 的 takeoverGate(#1117):无法归因到活动 agent turn 的模型请求直接发往 provider,不经 bili。compaction 摘要调用走 ctx.llm.stream()(直连 LLM service,不经过 agent/request 扩展点,dsh-compaction-basic README 明示),以下入口没有 ALS 归因边界,其摘要调用会静默直连 DeepSeek,服务端闸门根本见不到:

    1. 手动 /compact —— 命令执行路径从不建立 ALS 边界([Bug] 插件模式下 /acp 显示的是「最近活跃会话」的状态面板,而非当前会话(/acp-cache 同) #1677 已证实命令路径 currentInitiator() 恒空);
    2. idle-session compaction(无 open turn);
    3. 任何宿主把 LLM 调用派发到 turn 上下文之外的路径。

    这与 auto:false 语义正好咬合:patch 只关 pressure/overflow 自动触发,手动 /compact 本来就不受它管,而这条调用又恰好绕过服务端闸门——两层防线同时不覆盖同一入口。日志签名(GUI 宿主 stderr 被吞,但 persistClientEvent 落盘到 bili.log):bili-native-dsh: model request sent DIRECT (uncompressed) — takeover gate refused <key>。

    另附已知项:#1772 —— 桌面 profile 若 bundle 了 @deepseek-ai/dsh-web-app,其 agent preset 内的 compaction-basic 实例 patch 够不着(auto:false 落在错误的行上),bili 启动时会写一条 profile bundles include @deepseek-ai/dsh-web-app … warn。

    三、请 @weirdseed 补的证据(在你原两份之上加一条签名)

    bili.log(~/.local/state/billion-context/bili.log)按时间 grep 三个签名,任一命中即可定位漏网通道:

    1. dsh native compaction call identified(服务端拒绝,0.1.188+ 才有);
    2. takeover gate refused(客户端直连旁路,任何版本);
    3. profile bundles include @deepseek-ai/dsh-web-app(preset 层实例)。
      外加你原有的:版本启动行(bili 自更新时间线)+ session projcache JSON 里 checkpoint(<compacted-summary>)落地时刻。

    四、开工计划(按你的四个方向,全部 host 侧,kernel 不动)

    1. Nudge/acp_status 熔断感知:acp_status 武装时停播可压区间列表、改播明确标注 + consecutiveFailures: N / 3(含 10min 衰减说明);per-turn nudge 注入用与 会话卡死自愈机制:卡死模式检测与代理侧主动补救 #2155 nudgeSuppressed 同款的 willInjectNudge 门控武装时静默——纯 host 侧,不需要动 kernel(ranges 表虽由 kernel 渲染,但注入与否和 acp_status 组装都在 host)。
    2. 暂停段落 cause-aware:noteCompressLoopFailure 按 compressFailureCause 分叉——substrate-destruction 时给出唯一正确出路(查一次 acp_status、按活区间重锚,成功即解除熔断),其余 cause 保持 2026-10-07 owner 定稿原文逐字不变。行为变更会在 PR 里显式披露旧文案→新文案。
    3. 检测式兜底泛化到 dsh 线:prepare-openai(dsh 桌面走 deepseek-official OpenAI 兼容线)复刻 fix: rebase fold substrate on Codex local auto-compaction (#2372) #2373 签名——active blocks + 入站消息携带 dsh checkpoint 标记(<compacted-summary> / CHECKPOINT_PREAMBLE,常量版本化,同 Subagent first token stalls 1-2min: withSessionLock covers full forward + slow flash upstream first byte #970 纪律)+ foldCoverage 缺口 ≥8 且过半 → 立即 mark+reconcile rebase + recordConflict("native-compaction")。单信号各自保持现有路径(用户粘贴模板文本无覆盖缺口 → 不误伤)。
    4. CONFIGURATION.md 一行:auto:false 只关自动触发,手动 /compact 仍在(会被 bili 拒,拒得对)。
      附带小修(独立标注 commit):npm 包 files 字段根本没带 CONFIGURATION.md(LICENSE/README/dist/dsh.bundle.patch.yml/hermes-plugin/paper),而冲突提示指针引用 CONFIGURATION.md → "Detecting other compression plugins (#1206)"——装好的副本里该文件不存在,用户按提示自查无从下手。加入 files。

    五、记入本 issue 清单的一个 facet(实现等拍板)

    第二节的 takeoverGate 直连旁路属于同一失败域("宿主压缩落在 bili 知识之外")的预防层 facet:你的第 3 条是事后兜底(落地后一个 turn 内 rebase),而根治选项是在拦截门对"携带 dsh compaction 指令的请求"无论归因与否都路由进 bili(让服务端闸门统一裁决,尊重 allowDshCompaction 显式放行)。这触碰共享拦截热文件(native-intercept,全 lane 共用)且需要 body 嗅探,风险面与收益比需要权衡——本 PR 不做,留作清单项等你定策略(兜底优先 or 预防优先)。

    现在开工 1–4,完成后提 PR(Closes #2432 的主缺陷 1/2 及展示层,缺陷 3 定案以 @weirdseed 的日志为准)。

  4. ranxianglei commented on Oct 8, 2026

    @ranxianglei
    Owner

    🤖 Powered by ework · qwen3.8-27b

    按楼层 2 分拣的修复方向 1/2/3 + CONFIGURATION.md 一行实现完毕,全部 host 侧、不碰 kernel:PR #2436(4 commits,基于 master 2422394)。

    交付内容

    1. 熔断 cause-aware + 表面可见(50a50f7):receipt 按 compressFailureCause 分叉——substrate-destruction 收到新段落(跑一次 acp_status、只压它当前报告的活区间、一次成功即解除熔断);其余 cause 逐字保留 dsh内部问题 出现这个错误后,重启dsh可以发消息 但陷入无限压缩失败 #2146 文案(测试钉住)。新增 compressBreakerDetail/Armed:acp_status 武装时停播 Compressible ranges、改显 consecutiveFailures: N / 3 + 解除条件;四条 prepare lane + handle.ts 的 nudge 门全部加武装抑制(含 emergency)——不再邀请模型去压必败区间。
    2. dsh 原生压缩单 turn rebase(fe912eb):复用 fix: rebase fold substrate on Codex local auto-compaction (#2372) #2373 的「签名 + 覆盖缺口 → 立即 rebase」模式到 dsh 线。双信号 = checkpoint framing(<compacted-summary> / preamble 前缀,hex 版本化常量 + drift-pin 测试)+ fold 覆盖缺口 ≥8 且过半;命中即 recordConflict("native-compaction") + mark/reconcileNativeCompactionBoundary(先于 processTurn,避免毒锚),并跳过第二个 unannounced-rewrite 事件——基底摧毁不再被呈现成「另一个压缩器在打架」。无双信号一律走原路径(三个负例测试钉住)。
    3. CONFIGURATION.md en+zh(1a474cd):auto:false 只关 pressure/overflow 自动触发;手动 /compact 与 idle-session 仍会触发,路由过的会被闸门拒,直连落地的由本检测器下一回放 rebase。
    4. 顺手小修(b54bf24,独立标注 commit):npm 包 files 补 CONFIGURATION.md(+zh)——CONFLICT_DOCS_POINTER 指向的文件在旧安装包里根本不存在(已核实 tarball)。

    结构化八问

    1. 是什么问题:dsh 宿主原生压缩绕开 bili 落地后摧毁 ACP 折叠基底,此后所有 compress 永久失败,熔断与 nudge/acp_status 互相矛盾地把模型关进死循环。
    2. 什么原因导致的:三层叠加——(a) dsh summarizer 直调 ctx.llm.stream(),手动 /compact 与 idle 路径无 ALS 归属,插件 takeover gate(dsh-native.ts:865,「BUG」native-intercept 进程级 globalThis.fetch 补丁按 URL 形状接管所有模型流量,无关调用者(如 MemOS)被卷入宿主压缩流程 #1117)放行 DIRECT,服务端闸门结构性看不到;(b) dsh 线缺少「识别 checkpoint 回放并 rebase」的路径(codex 线有 fix: rebase fold substrate on Codex local auto-compaction (#2372) #2373,dsh 没有),半活块永远 active、锚点自毁;(c) 熔断武装后 nudge/acp_status 继续广播必败区间且 receipt 禁止查 acp_status,恢复出口被关死。
    3. 影响范围与严重程度:dsh 桌面端 profile 用户;触发 = 任意一次不经 bili 的原生压缩落地;程度 = 该会话永久不可用(compress 必败 + 熔断),需新开会话。
    4. 怎么修复的:检测即 rebase(单 turn 换基底)+ 熔断状态成为一等公民(cause-aware receipt、表面可见、武装时停播邀请源)+ 文档澄清 auto:false 边界。takeoverGate 预防性收口等 owner 策略拍板(列 checklist)。
    5. 是否有回归问题:审过——非 substrate receipt 逐字不变(测试钉住);未武装时 acp_status/nudge 输出不变;无双信号改写走原路径;applyCompactionArchive no-op 路径不变;self-heal/codex-#2373 既有行为未动(对应套件全绿)。无 fail-fast→吞错、无阈值漂移(MIN_MISSING=8 与 codex 线一致)、ARMED 段仅武装时出现。
    6. 是否破坏缓存:否。不触碰 prompt-cache 键/重试/游标/去重;rebase 复用 codex 线同款 resetSessionCompression 路径。
    7. 是否改了设计方向:否,沿用 fix: rebase fold substrate on Codex local auto-compaction (#2372) #2373 模式扩展 + metadata 单写者纪律。
    8. 是否引入了新的配置:无。

    行为变更披露(详见 PR 正文):① substrate-destruction 的 receipt 从「禁止 poll acp_status」变为「指向唯一恢复出口」(其余 cause 逐字不变);② acp_status 武装时 Compressible ranges 段替换为 ARMED 段;③ per-turn nudge(含 emergency)在熔断武装时停播;④ dsh 会话的双信号改写事件从 unannounced-rewrite 变 native-compaction 并立即 rebase ACP 状态。

    待办(不在本 PR)

    • 缺陷 3 定案证据仍等你:bili.log 三条签名(dsh native compaction call identified / takeover gate refused / profile bundles include @deepseek-ai/dsh-web-app)或 session projcache JSON。若坐实「0.1.187 及更早落地的存量伤害」,本 PR 的检测器正好兜住这类历史落地的下一次回放。
    • takeoverGate 预防性收口(让 compaction 调用也强制经 bili):涉及共享热文件 + body sniffing,按分拣「检测式兜底优先」列为后续工作项,等策略拍板。

    本地定向验证:两个新套件 11/11 + 邻域 8 套件 61/61 绿,typecheck/build 干净;全量回归交给 CI。

  5. ranxianglei commented on Oct 8, 2026

    @ranxianglei
    Owner

    🤖 Powered by ework · qwen3.8-27b

    收到补充证据。先给核验结论,再说这条如何落进 PR #2436(已加一个 follow-up commit:cfaa32556)。

    「两个子系统不同代」的核验——效果为真,机制要修正一格

    acp_status 和 compress 读的是同一份 ref 映射(都从 session.state.messageRefs 取),任何一个都不缓存旧代快照。你观察到的「广播的 ref 校验时不存在」不是两个子系统各持一代,而是两次调用之间基底被改了:每次未宣告改写落地,下一请求会走 applyCompactionArchive(src/session.ts)把 byRaw/byRef prune 到存活 raw id——广播时刻还有效的 ref,等 compress 跑起来时已从映射里消失(kind 从 consumed 变 unknown)。你自己的诊断数据支持这个版本而不是「rebase 换代」:session highest ref=m03761 一直没动——若本会话真发生过 rebase(ref 重置回 m00001),最高 ref 会是小数字;且 63 blocks / 63 compression(s) 跨尝试恒定、失败区间随尝试漂移,都是「持续 prune」的形状。结论不变:acp_status 广播了必然锚不上的区间,这是缺陷——修复方向与你建议 1/2 完全一致,已实现。

    你的五条建议 → PR #2436 现状

    1. 「宁可完全不广播」:✅ 新 commit 起,最后一次失败判定为 substrate-destruction 或 stale-ref 时,acp_status 从第 1 次失败就停播 Compressible ranges(不再等第 3 次熔断武装),并打印显式段落:FOLD SUBSTRATE INVALID — … compression is unavailable here. Recovery: start a fresh conversation(stale-ref 单独一段 FOLD BASE GENERATION MISMATCH)。其他 cause(如 covered-by-block)不过度抑制,照常广播;10 分钟衰减自动解除。
    2. 显式输出基底失效:✅ 同上两段(cause 直接内嵌在段首)。
    3. 计数器可见:✅ 上一 commit 已做——武装时 acp_status 输出 COMPRESS CIRCUIT BREAKER: ARMED — consecutiveFailures: N / 3 + 解除条件。
    4. 文案与指令一致:✅ receipt 按 cause 分叉:substrate-destruction 收到唯一恢复步骤(查一次 acp_status、只压它当前报告的活区间、成功即解除);loop-noise 类保留 owner 逐字文案。停播与文案现在指向同一方向,不再有矛盾指令。
    5. 不呈现为「另一个压缩器在抢」:✅ 冲突台账里只要没有确认的外来第三方插件事件,footer 不再命令「找出并关掉另一个插件」,改为点名宿主自己的原生压缩落地 + bili 检测/rebase 路径(fix: rebase fold substrate on Codex local auto-compaction (#2372) #2373/[bug] DSH 桌面端 0.1.188:宿主原生压缩落地摧毁折叠基底 → 同一区间永久不可锚定、compress 连片失败并熔断(compaction-basic auto:false 仍无效) #2432)或重开会话;检测器命中时也不再追加第二个 unannounced-rewrite 事件。

    边界说明

    • 你手上这个会话的基底已经摧毁,不可恢复——修复改变的是之后的行为(下一次落地单 turn rebase、失败后表面不再引向必败区间、冲突呈现不再误导排查方向)。该会话请直接新开。
    • 本地定向验证:新增/扩展测试全绿(breaker-awareness 7、dsh-local-compaction 7、issue2146、dsh-compaction-guard、codex-local-compaction、acp-status×2、cache-friendly-plugin、self-heal、thirdparty-scan 共 101 pass),typecheck/build 干净;CI 会对新 commit 重跑。
    • 仍待你那边定案的只有缺陷 3 的证据(bili.log 三条签名:dsh native compaction call identified / takeover gate refused / profile bundles include @deepseek-ai/dsh-web-app)——它决定「存量伤害 vs 现行绕过」的最终归因,不影响本 PR 的修复面。
  6. ranxianglei commented on Oct 8, 2026

    @ranxianglei
    Owner

    🤖 Powered by ework · qwen3.8-27b

    状态更新(无新变更,例行确认):PR #2436 @ cfaa32556(5 commits)——最新 commit 的 4 条 e2e lane(codex-persona / native-opencode / dsh-plugin-lane / real codex)已全部绿,主 CI 套件还在跑,红了会定向排查并回帖。线程内无新增输入;待办仍是两项:①缺陷 3 定案证据(bili.log 三条签名:dsh native compaction call identified / takeover gate refused / profile bundles include @deepseek-ai/dsh-web-app);②owner 对 takeoverGate 预防性收口的策略拍板。合并由人执行:#2436

  7. added 2 commits that reference this issue on Oct 9, 2026
  8. XR1988 commented on Oct 9, 2026

    @XR1988

    补上缺陷 3 的定案证据。本机 DSH 桌面端插件模式,bili 0.1.188(62598940),macOS Apple Silicon,lane dsh。

    环境

    • GET /__bili/status → version 0.1.188 / diskVersion 0.1.188 / stale false / autoRestartOnUpdate true
    • 代理由 DSH spawn(BILLION_CONTEXT_PROXY 未设置,走 bili-native-dsh)
    • 日志文件:/Users/lizhi/.local/state/billion-context/bili.log(9.8 MB)+ bili.log.old(10.5 MB)
    • 覆盖区间:2026-10-08T13:13:56Z → 2026-10-09T04:04Z

    三条签名

    签名 命中
    takeover gate refused 2
    dsh native compaction call identified 0
    profile bundles include @deepseek-ai/dsh-web-app 0

    两条 takeover gate refused 全文(都在 bili.log.old):

    2026-10-08T13:14:45.439Z [warn] [v=0.1.188] [dsh-client] bili-native-dsh: model request sent DIRECT (uncompressed) — takeover gate refused http://oneapi.ai-chat.host:8602/v1/messages: no active initiator attribution (agentless/background lane, third-party in-process caller, or stale attribution after host resume?) — refusals so far: 1
    2026-10-08T16:31:09.474Z [warn] [v=0.1.188] [dsh-client] bili-native-dsh: model request sent DIRECT (uncompressed) — takeover gate refused http://oneapi.ai-chat.host:8602/v1/messages: no active initiator attribution (agentless/background lane, third-party in-process caller, or stale attribution after host resume?) — refusals so far: 1
    

    两条都以 refusals so far: 1 结尾 —— 各自是所在进程的首次拒绝,两次之间进程重启过(本机 2026-10-09T03:46Z 重启过一次代理)。目标上游是第三方线路 http://oneapi.ai-chat.host:8602/v1/messages,不是 api.anthropic.com。

    同窗口的佐证行

    2026-10-08T16:25:51.183Z [error] [sess=session-4e2d3bf6-91b1-4939-95a6-173524260810] [v=0.1.188] [fold-reconcile] compression substrate appears destroyed: 1265 covered id(s) missing with NO anchor match for 3 consecutive passes, 1 min so far — consistent with a host-native compaction landing outside bili's knowledge (dsh native compaction, #1729/#2193) or a bulk client-side history rewrite; bili folds can no longer cover the resent history (#1921)
    2026-10-09T02:23:12.787Z [error] [sess=session-19e0aa42-57e7-4a02-96c2-3dfe1cccd3cb] [v=0.1.188] [fold-reconcile] compression substrate appears destroyed: 354 covered id(s) missing with NO anchor match for 3 consecutive passes, 1 min so far — ...
    2026-10-08T16:26:22.155Z [info] [sess=session-19e0aa42-57e7-4a02-96c2-3dfe1cccd3cb] native compaction boundary: archived 0 pre-compaction block(s); pruned ref maps to 342 live raw id(s)
    2026-10-08T16:37:40.405Z [info] [sess=session-64d2b9de-3a4f-4f16-9696-875b350d394f] native compaction boundary: archived 0 pre-compaction block(s); pruned ref maps to 167 live raw id(s)
    

    计数:compression substrate appears destroyed 4 条(session-4e2d3bf6-… 1265 / session-19e0aa42-… 354 / 另两条),native compaction boundary 2 条(342 / 167 live raw id(s))。

    我的读法(解读,不是结论)

    dsh native compaction call identified 一条都没有,但基底在同一个窗口里被摧毁了 4 次。两条放在一起看:落地路径不是 bili 能识别并接管的那条。takeover gate refused 那两次的请求被判为 no active initiator attribution 后直连不压缩——也就是说存在一条宿主能发、bili 不接管的线路,而这正是「现行绕过」的形状,不只是存量伤害。

    这只是相关性。哪条代码路径真的落地了原生压缩,需要你那边按路径确认。如果确认是这条,那 takeoverGate 的预防性收口就不只是防存量,而是防现行。

    附:这条链的入口症状

    同一台机器上,host-stamped native caller … conflicts with outbound witness — honoring the native caller, refusing the witness (#2024) 在 bili.log.old 25 条、bili.log 34 条,时间跨 2026-10-08T13:25:41Z → 2026-10-09T03:44:54Z,涉及 session-19e0aa42-…、session-048e0457-…。4 个会话因此进入永久 cannot be anchored 并熔断。详情我另开在 #2165。

  9. 1 remaining item

  10. added a commit that references this issue on Oct 10, 2026
  11. added a commit that references this issue on Oct 11, 2026
  12. added 3 commits that reference this issue on Oct 11, 2026
  13. added a commit that references this issue on Oct 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions