dsh-purge
YuJunZhiXue/dsh-purge在设置页编辑 prompt-inject.md、管理 AGENTS.md 或 CLAUDE.md 规则集、检查插件更新,并对本机包做可回滚的应用或还原。
DSH 上下文智能压缩 + 智能思考:每轮注入用量、模型自主决定压缩并自动续跑;可选让模型按任务难度自主调整思考强度档位
DSH(DeepSeek Harness)宿主侧插件 dsh-context-pilot(面板名「上下文智能压缩」,曾用名「上下文领航」): 让长会话的上下文压力可见、可控,并把「何时压缩」的决策权交给模型——用工具调用触发压缩, 压缩在你的下一个步骤开始前落地,本轮不中断、不注入任何消息、不伪造任何用户发言。 另含可选的智能思考:把当前模型的思考强度档位告知 Agent,由它按任务难度自主换档(同样是工具)。
# 从 GitHub 仓库安装(本项目**不分发 npm 包**,这是推荐路径;pnpm 规格)
dsh plugin --profile web add github:Shuffle-1992/dsh-context-pilot分发方式(2026-10-10 决定):不发布 npm。安装一律走上面的 GitHub 仓库规格;
dsh plugin --profile web add dsh-context-pilot 这条 npm 形式在本项目不可用(无 npm 包)。安装后重启 DSH 生效(bundle 资格由本包 package.json 的 dsh.bundle.patch 声明, patch 文件:plugin/cordis.patch.yml)。
本地开发挂载(本机已实测的路径)
profile package.json 两处要改(以本机 desktop profile 为例):
{
"dependencies": { "dsh-context-pilot": "link:F:/My Code/dsh-context-pilot/plugin" },
"dsh": { "profile": { "bundles": [ /* … 其它 bundle …, */ "dsh-context-pilot" ] } }
}再手工 Junction(可免 pnpm install):node_modules/dsh-context-pilot → /plugin。
⚠️ 插件package.json必须有 `dsh.bundle` 段,否则 plugin-manager 报not-bundle并静默跳过
(重启几次都不生效、host 无日志)——详见 docs/setup.md §2 坑 P1。包名统一为dsh-context-pilot(2026-10-10 起去掉@local/前缀);旧挂载名@local/dsh-context-pilot
仍可用 plugin/cordis.patch.local.yml 兼容,但新装请用正式名。路径 · 作用 package.json(根) · 可分发包清单(name/version/license/repository/dsh.bundle.patch/meta)——市场按仓库根识别 plugin/ · 插件本体(entry.mjs / client.js / 域模块 / icon.svg / 两个 patch 文件)——也是本机 link: 挂载目标 test/ · 四套断言(contract / static / report / boot) docs/ · 设计与交接文档(含 docs/setup.md 部署矩阵、docs/hub-publishing.md 上架清单)
交接说明:本文是入口,读完 §1–§3 即可上手;深入内容在 docs/。最后更新 2026-10-15。当前版本 `v0.1.8` · 逐版本历史与实证记录见 docs/milestones.md。
版本 · 更新 · 为什么值得知道 v0.1.70 · R77 遗留清单收尾(C8 先量化 / E4 不改) · C8:slim 快照「只裁载荷、不减测量」——不改,先加 snapBuilds/measureCalls/stateOfCalls 三计数并随快照进 diag(含 slim 档),拿真实量级后再决定(判据写进文档)。E4:effort 取证字段迁移会改报告字段形状、收益仅命名 ⇒ 不改,注释声明属域。R72 遗留清单至此全部有结论。949 断言 v0.1.69 · R76 候选链合并 / 非法配置留痕 / 关键词单源 · S3:core 两条官方包候选链(各 ~50 行逐字同构)合并为参数化 loadFromOfficial(保留字面量 await import 以免 R48 白名单扫描失效——合并时踩到并修复)。C12 保守变体:非法配置值被拒时留痕 state.cfgRejected(不改 set 行为)。E9:换档关键词改为数组单一来源派生正则。947 断言 v0.1.68 · R75 界线单源 / 意图失败重挂 / 解包统一 · E1:m3 抽出 effectiveCriticalOf(含 5–20pp 动态余量)并导出,pre-step/idle 共用、宿主注入给 m2 教学 ⇒ 教学线 = 实际触发线(原先单步增量大时卡上教的线偏高、「N 轮触线」偏晚)。E3:rearmIntent 执行失败后重挂意图(上限 1 次,计数独立于条目——首版存在条目上被行为测试抓住)。S4:client 信封解包收敛为模块级一份。943 断言 v0.1.67 · R74 收尾批次(2 项) · 5 份 writeJsonAtomic 的非原子降级补留痕(rename EBUSY 时半截 JSON 风险不可观测)、删除死配置字段 hudLastAct(host 只写内存、从不写 settings)。937 断言。R72 清单里低风险 P2 已清空,余 8 条需产品判断(见 milestones R74 文末) v0.1.66 · R73 P2 批次(10 项) · 换档让位补 acceptFrom(原判据漏「接受后 apply 前用户手改」⇒ 覆盖用户选择)、setTier carry 不算申报、宿主 verifyBySid 有界、用户模板超预算留痕(不截断)、报告损坏先备份再重建、门禁加 120s 超时且取最后一处匹配、TTL 登记路径也扫、淘汰跳过有未生效 want 的槽、wire 注释残字段、diag() 虚假注释更正。935 断言 v0.1.65 · R72 真 GLM-5.3 跨模型审查(7 条 P1) · 先修派发通道模型替换 bug(只读单一 q.model ⇒ 改按授予集合判定,真 GLM-5.3 跑通)。落地:lastUserText 读错 surface 形状(换档提醒两触发从未生效)、guardOfficial 收口其余 retainTokens=0 入口、中步回执顺序补正(R71 不完整)、{tool} 被解析成压缩工具(教错工具)、action 打错字静默真压、面板指向不存在的刷新按钮、预渲染决策卡恒空、dataset prop 无效、落盘浮点/NaN 距离/空串余量、删宿主死委托(行预算 318→314)。923 断言 v0.1.64 · R71 双份审查交叉验证落地(13 项) · 报告热路径 stat 指纹门控(不再每次 MB 级 JSON.parse)、中步回执补 noFactory 守卫、失败回执用真实 trigger、面板两次 RPC 合一且带 sid、两处无界 Map 有界化、偶数样本真中位、删死导出/死解构/6 个死样式类、面板删除已退役「压缩标记通道」说明。907 断言 v0.1.63 · 修趋势外推不准(R70) · 三处真缺陷:①只看单轮增量(一轮贴图就能算成「1 轮触线」);②压缩后基线不重置 ⇒ 增量变负、趋势行消失;③两处轮数取整不一致(同一张卡出现 18 与 37)。现改为最近 4 轮均值并标出样本数、骤降即重置基线(该轮不外推,明说「刚刚压缩过」)、两处统一 Math.round(≥1)。897 断言 v0.1.62 · 「最近切换」时间加日期(R69) · chip tooltip 原先只显示 时:分:秒,而 lastSwitch 是落盘数据(跨天/跨重启仍在)⇒ 看不出是哪天。改为 YYYY-MM-DD HH:MM:SS(自写补零,不依赖 locale,避免不同环境分隔符漂移)。891 断言 v0.1.61 · 修强制压缩线显示 `0.7000000000000001`(R68) · 上限是浮点减法的产物(0.75 + 0.05 − 0.1 = 0.7000000000000001),R67 联动钳制直接 String() 写进输入框。加 roundRatio(4 位小数,0.01% 精度)统一归一 ⇒ 显示 0.7;保存路径同源,不会把浮点长串落盘。889 断言 v0.1.60 · 上限随余量实时重算 + 强制压缩线联动钳制(R67) · 用户:"设置 0.1 应该自动钳回 0.7"。旧实现 engineCap 只在挂载拉一次 ⇒ 改余量不重算。现按 引擎阈值 = engineCap + 拉取时余量、上限 capNow = 引擎阈值 − **当前草稿余量** 实时算;capNow 一降就把强制压缩线当场钳到新上限(草稿级,保存即落盘;不自动写盘)。输入/保存/计算器/提示四处统一改用 capNow。888 断言 v0.1.59 · 说清「强制压缩线」与「阈值余量」谁生效(R66) · 两者不冲突:上限 = 引擎阈值 − 余量(如 80% − 5pp = 75%),生效强制线 = min(你的值, 上限);当前两者都是 75% 所以看不出差别。旧提示把余量写死成 5pp且不显示最终值 ⇒ 现按活值动态生成,两条线都写明「生效强制线 = X%」。886 断言 v0.1.58 · 撤掉「本轮注入」整段,改为模板行「查看/复制内置文案」(R65) · 用户:"太乱了,下面的本轮注入可以不要"。删除 injBlock/injSec/injAll/refreshInj 及其按钮与 R59/R63/R64 关联逻辑;三个模板行各加「查看内置文案」+「复制内置文案」(复制不需先展开、有可见反馈)。内置文案数据仍统一走 inj.builtin* ?? cfg.builtins.*。886 断言 v0.1.57 · 「本轮注入」六行恒显(R64) · 用户实测「只剩 2 个?」⇒ 另四段是按时机注入(一次性说明/思考强度教学=会话首轮一次;换档提示=建议换档时;回执=压缩实际执行后),普通一轮本就为空,而原实现「空即整行不渲染」⇒ 看不出是没注入还是坏了。改为六行恒显,空行标「本轮未注入(原因)」。896 断言 v0.1.56 · 去掉内置文案与本轮注入的重复展示(R63) · 用户实测「2 处重复」⇒ 模板留空时注入内容就是内置文案本身,两处逐字相同。新增判重 tplIsDupOfInj:留空且已有本轮注入 ⇒ 不显示「查看内置文案」按钮,改为一句说明指向下方同名段;模板非空、或尚无注入记录时仍可查看(那时它是有用的参照)。895 断言 v0.1.55 · 三条实测反馈同源修复(R62) · ①「本轮注入」板块原先与带边框卡片平级(看起来在卡片外)⇒ 放进 .dcp-grid 末尾,落在「强制压缩冷却」之下、同一张卡片内;②「2 个按钮没反应」⇒ 刷新改为回一句状态、展开按钮在无注入时置灰并说明原因;③「查看内置文案是空的」⇒ 内置原文改由 host 随时可读(ensure() 期按当前配置预渲染 + m2 留档 effort 原文 + HUD config 下发 builtins + client 回落)。893 断言 v0.1.54 · 六段原文改「整段一个」开关 + 刷新按钮说清用途(R61) · 用户反馈「查看按钮重复了」⇒ 6 个重复的「查看」合并为板块标题行的「展开全部原文 / 收起全部原文」,每行只留「复制」;「上面的没有刷新按钮」⇒ 刷新本来就在标题行,标签改为「刷新注入数据」并说明它同时刷新内置文案(同一份 inj 载荷)。888 断言 v0.1.53 · 补「思考强度教学」模板 + 六段原文改按钮展开(R60) · ① 六段注入原文原先无条件整块铺开(一屏上千行撑爆面板)⇒ 改为每段 [标题][查看][复制],` 仅在展开时渲染;② 新增配置字段 **tplEffBrief**(贯通 schema/M3_DEFAULTS/mergeConfig/校验/compact-tool 新增的通用套壳 renderTemplated/m2 套模板+下发内置原文/client FIELDS 与映射,共 7 处)。**888 断言** **v0.1.52** · **「本轮活值/生效配置」并进板块标题行(R59)** · 原先这两行落在「本轮注入(只读)」板块**最底部**,要读完六段原文才看到活值(与"先看结论"相反)。现并进**板块标题行**(板块名 → 最近一轮 → 刷新 → 本轮活值 → 生效配置),标题行改为可换行 flex;底部重复行删除。**877 断言** **v0.1.51** · **内置文案按钮的「暂无」改成可操作提示(R58)** · 判据:client 半已生效(输入框变大/按钮在/空模板为空白/注入区有数据)⇒ 空的是 **host 半未重载**(内置原文由 host 的 compact-tool+m2 落档,PANEL-HOWTO.md §0:host 改动需 **toggle 或重启**)。提示改为明确写出「请先 toggle 插件(关→开)加载新版 host」。**873 断言** **v0.1.50** · **空模板显示成 0 的根因修复(R57b)** · oUi 一律 Number(base),而 Number(") === 0 ⇒ 空模板被渲染成 (用户实测「里面默认是写个 0,应该为空白才对」)。**同源第二个 bug**:parseInput 对 pl 落到数值分支 ⇒ 清空模板被判「输入不合法」⇒ **「留空 = 内置文案」根本走不通**。两处都改为文本类字段当字符串( oUi 原样返回、parseInput 直通)。**872 断言** **v0.1.49** · **卡片 key 覆盖 profile 旧别名(R56f)** · plugins.bundle.config 是 **keyed** 槽位,宿主按 renderSlot(..., { entryKey: pkg.name }) 派发,而 pkg.name 是**profile 的 bundle 名**(本机仍是旧别名 @local/dsh-context-pilot)——只注册规范名时两边不等 ⇒ 卡片区域**静默空白**(无 error/warn,槽位探针却显示 occupant active)。改为**两个 key 都注册**(规范名 + @local/ 别名),并把 client-render 的断言从"key 等于包名"改为"覆盖两个名字"。 **v0.1.48** · **照 PANEL-HOWTO.md 自查 + 补 NS_EXACT(R56e)** · 逐条对照 zcode 的实测文档:① 清单 ✓ ② 信封红线(零静态 import、require 只有 react)✓ ③ key=包名 ✓ ⑤ status 路由不适用 ✓;**补上 §1④ 的 NS_EXACT**(原先只有模糊匹配,文档警告会绑错 ns ⇒ 永远只读)⇒ 精确优先 + 模糊兜底。static 加 5 条(含 NS_EXACT === cordis.patch.yml 的 - id: 常量副本一致性)。**863 断言** | **v0.1.47** | **配置卡失败必须可见(R56d)** | CardBoundary 原先失败
eturn null ⇒ 页面**一片空白零线索**(只有控制台 warn)⇒ 三轮排查都卡在这里。现渲染自解释诊断(含**错误消息**)。探针补盲:真跑 useEffect + 校验 Hook 顺序 + 捕获 console 告警,client-render 13 → **24 断言**;三场景 × 两视图全绿 ⇒ 组件本身健康。另用证据排除 key 问题(两个 junction 的 ame 都是 dsh-context-pilot,occupant ctive:true)。**859 断言** | **v0.1.46** · **dsh.client 形状对齐 + 清单核对** · 补 immediately: true(对齐 dsh-dispatch;**不是**本次原因——zcode 无它照样渲染);static 加 2 条守卫。逐条核对用户转来的排查清单:槽位 vailable:true、key = 包名、occupant **active:true** ⇒ 清单三条假设全部不成立;真因是清单之外的第四种形态——**注册成功但渲染期抛异常被边界吞掉**(R56b 自递归)。**847 断言** **v0.1.45** · **配置卡整卡消失的真根因(R56b)** · scope.getSnapshot 的兜底写 settingsScope.getSnapshot(),而 settingsScope = scope 指回自己 ⇒ 命名空间未就绪时**自我递归** ⇒ 渲染期 RangeError ⇒ 被 CardBoundary 吞掉 ⇒ **配置卡从来就没渲染过**(R56 修的 pct1 只是被它挡住的第二个 bug)。兜底改为独立常量 UNAVAILABLE_SCOPE。**新增第 5 套件 est/client-render.mjs**(React 桩真渲染冒烟,三运行时 × 两 view,13 断言,变异 CAUGHT 5 条)⇒ **845 断言** **v0.1.44** · **配置卡整卡消失的根因修复(R56)** · pct1 定义在 hintFor **内部**、却在 summary 分支外面被调用 ⇒ 渲染期 ReferenceError ⇒ 被 CardBoundary 吞掉 ⇒ **整张配置卡静默消失**(用户实测「这个界面没有看到」)。已提到组件作用域;contract 加 3 条守卫(缩进锚定 + 单一声明 + 边界留痕),变异 CAUGHT。**这是本项目第三次同型事故**(R54 F1/F3、§6.17)⇒ 记录已知缺口:需要 AST 级未定义标识符扫描或 React 真渲染冒烟。**830 断言** **v0.1.43** · **注入内容可视化 + 参数可配 + 文案模板(R55)** · 面板新增「本轮注入(只读)」区:用量行 / 决策卡 / 一次性说明 / 压缩回执**逐段原文** + 本轮活值 + 生效配置快照,可复制、可刷新;配置区新增 **阈值余量**、**三档保留比例**(活读,改完下一轮即生效,无需重启)、**两个文案模板**(tplBrief/tplCard,留空用内置,**缺 {tool} 自动回退内置并留痕**)。**827 断言**,变异 6/6 CAUGHT **v0.1.42** · **千问独立审计跑通:4 条真缺陷修复 + 测试盲区** · ①lastUserText 未解构 ⇒ 每次调用 ReferenceError(被空 catch 吞 = 静默降级);②清理块引用未导出的 pending ⇒ 卸载第一行就抛、disposer 全不跑(泄漏);③client alive 作用域外引用 ⇒ autoClamp 路径未捕获 rejection;④回执只写全局镜像、读取端只读 per-sid ⇒「未执行」读不回。另修 boot.mjs 桩:卸载器返回后被丢弃 ⇒ 卸载路径从未被执行。**782 断言**,变异 4/4 CAUGHT **v0.1.41** · **自审:落盘原子性 + 在飞标记按会话** · ①5 处 .data 直写 ⇒ 半截 JSON 被并发读者(含插件自己下次启动)读到 ⇒ 报告整份丢失(R13 同形;test/report.mjs 曾真实报 Unterminated string in JSON,当时只加重试绕开)⇒ 全部改 writeJsonAtomic(tmp+rename);②ownCompact 全局单槽 ⇒ 多会话压缩漏记/重复记录 ⇒ 改按 sid 分槽。附 docs/reviews/r51-audit-self.md 九条假设逐条结论。**780 断言**,变异 2/2 CAUGHT **v0.1.40** · **独立审计发现 #1 修复:official() 闭包 TDZ("压了却报未执行")** · 审计方(qoder/Qwen3.8-Flash)发现 compactWithOwnRange 的 official() 兜底闭包引用了**在它之后声明**的 retainSource/useAnchor/tierInfo:当 compact-range.mjs 加载失败(awaitRange→null)时**立刻**调用 official() ⇒ 抛 TDZ;而 compactIfNeeded 在 await 里**已经真的压了** ⇒ 回执写 not-executed(**假阴性**)+ 记录双丢 = 静默降级。修法:把不依赖 api 的三项前置、tierInfo 先给安全默认 ⇒ 正常路径逐字不变。新增行为级 + 结构级守卫,**变异 2/2 CAUGHT** **v0.1.39** · **修掉 R50 自己引入的回归风险(账本加载失败会连带压缩整域失效)** · R50 首版把事实账本与 m3 放进**同一个 Promise.all** ⇒ 账本加载失败 ⇒ m3Ctl 恒 null ⇒ **工具回 ok、实际永不压缩、模型无感**(历史上踩过的静默失效形态,也违背「最坏=旧行为」的承诺)。现 catch **只挂账本那个 import**(失败 ⇒ 退化为 factor=1/无样本/无锁),m3 自身失败仍 reject 留痕;宿主代码行净增 0(仍 ≤320)。新增守卫断言 + 变异验证 **v0.1.38** · **解耦(会话事实账本)+ 稳定回落样本落盘 + 简化 + 强制性不变量** · ① **解耦**:折算因子 / 稳定回落样本 / 压缩锁 / 当前模型路由 四类事实从 m3(800+ 行)搬进叶子模块 session-facts.mjs(宿主注入工厂、模块间零互引、m3 只剩薄委托);② **落盘**:样本写 .data/settle-drop.json ⇒ 跨重启/跨 toggle 仍在,本会话无样本时给**跨会话中位数**并在回执标「**参考其他会话**」(绝不冒充本会话实测);③ **简化**:pre-step/idle 两段近乎相同的「让路」代码收敛为一个判据函数;④ **强制性不变量**(带守卫断言):缺依赖必须退化(factor=1/无样本/无锁且不抛错)、让路必须在消费意图之前、持久化必须被消费;⑤ 顺手修测试竞态(读活插件正在写的 JSON ⇒ 有界重读)。**变异 6/6 CAUGHT** **v0.1.37** · **用 0.2.1-rc.2 的新能力做正向优化(锁避让 + 按模型的官方阈值)** · ① **压缩锁避让**:官方 assertCompactionInactive 规定"未匹配的 compaction/start 即持久锁",官方 /compact 与插件共用 ⇒ 同时发起必有一方吃 busy。现只用**已文档化的会话事件**记账(start 记锁 / end、session/end-seed 清锁),pre-step 与 idle 都让路,**且在消费意图之前**(意图保留 ⇒ 可重试);锁带 **10 分钟 TTL**(陈旧锁不得让插件永久不压,最坏=旧行为)。② **按当前模型的官方阈值**:官方 resolveTargetPolicy 支持 modelPolicies[] 按 provider+model 精确覆盖 thresholdRatio;原先只读默认值 ⇒ 配了策略时会与"当前模型"的阈值错位。③ 顺手修**既有隐患 R49.1**:pre-step 的 catch 引用 try 内 const sid ⇒ 错误分支自身抛错(lastPreStepError 全丢);真机从未触发。**变异 6/6 CAUGHT** **v0.1.36** · **DSH 0.2.1 升级兼容(只做兼容,不加功能)** · ① **两个清单都加 engines.dsh: ">=0.2.0-rc.2 =0.2.0 50%)时追加「(provider 待刷新,下次请求后会回落)」,正常轮次不附 v0.1.24 · 手动切档后不再写「保持 X」(措辞失真修正) · 真机:用户 06:25:57 手动把档位从 max 切到 high,插件 06:26:01 已注入「用户刚把档位从 最高(max) 调到 高(high)」(检测正常 ✓),但模型 06:26:20 写成「档位决策:保持 高(high)」——照抄了卡片模板里的"保持" ⇒ 用户以为它没感知到切换。现切档那一轮后缀显式补「这是用户改的,不是"保持":请写「用户刚调到 X」」,教学同步加一句。另:手动纠正的效果已复测确认(t108–t112 连续 5 轮都只有 1 条决策行) v0.1.23 · 持续重复 ⇒ 上报面板(含可复制纠正文案);备注上限 4→1 · 备注 4 条/轮已被证明压不住模型(实测照写 18–22 条/轮),压缩清先例也无效 ⇒ 不再跟模型较劲,改为把异常上报给用户:同轮决策行 ≥6 条时,面板 acts 行出现「20:xx · 决策行重复 12 次/轮 · 建议手动纠正;可直接复制:请停止在每个步骤重复写决策行…」——后半段可直接粘贴给 Agent(无需改客户端/刷新页面);同轮只上报一次。备注上限降到 1(留提示位、省 token) v0.1.22 · 「动作背书」的决策行不算重复(修 R31 的误报) · 用户追问「被纠正后就不会注入了吧」时复查判据,发现:R27 允许「中途真的改了决定再补一行」,而 R31 按"同轮 ≥2 条 = 重复"⇒ 合法补行被误判。现统计前先看该条消息里有没有指向压缩/换档工具的 tool-call(工具名从 api 取,不硬编码);有 ⇒ 不计入。其它 tool-call(如 pwsh)不豁免。完整语义:守规矩 ⇒ 永不注入;停手 ⇒ 同轮也不再注入;下轮重置;复发才再注入 v0.1.21 · 反重复备注改为「检测驱动」:监测到重复才注入 · 用户要求「只有当这种被强化后反复写决策的时候,监测到才注入」。R30 的固定节拍会打扰守规矩的会话。现改为读会话事件检测:assistant/message 自带 data.turn/step,只数正文 `text` 块(reasoning 里提到"压缩决策:"不算);同轮 ≥2 条才认定重复并投一条,只写 1 条完全不打扰;投后再写满 3 条才再提醒,每轮上限 4 条,新轮重置。state.m2.decisionRepeat / repeatNudges 进报告可核对 v0.1.20 · 反重复备注:决策行每步重复是"模型模式",用结构性信号压 · 会话日志取证(97 轮/3032 步):插件注入 108 次(≈1.1 次/轮 ⇒ 卡只在轮首一次,不是每步),而模型写的决策行 t86 2 条 → t92 25 条 → t95 27 条 ⇒ 自我强化,纯文案压不住。现于重复发生处投极短备注:本轮发过卡 + step≥2 + 每 3 步一次(回执优先),文本为「本轮决策行只需写一次,后续步骤不必重复」。⚠️ plugin/*.mjs 只需 toggle(无需重启 DSH);重启也解决不了这个问题 v0.1.19 · 手动压缩不误报「引擎抢先」+ 压缩后投影偏高会标注 · 真机手动 /compact 成功后:① 记录被写成「手动压缩(/compact) · 引擎抢先…」——用户主动压的,没有"抢先"(preempted 没判 kind,已修);② 表盘 13.1% 而当前 surface 只剩 6,185(真实约 2.4%)——投影公式里旧 provider 报的"非 surface 开销"(125K)压缩后没同步缩小,要等 provider 实报才回落。方向偏高=保守,故只标注:surface 缩水 >50% 时用量行附「provider 待刷新」。另附真机四口径前后对比(消息 221,980 → 4,062) v0.1.18 · 非插件压缩的失败原因可见(`/compact` 失败不再只说"失败") · 真机:同一会话连试 3 次 /compact 全失败(DSH 码 summary),我们记到了 3 条但只有「失败」两个字——原因只存内存、且这条路径不触发报告快照。现把 DSH 失败码翻成中文短语写进面板行(失败(摘要无法生成))与记录(error + reason),并在记录后立刻落一次报告快照;认不出码则截断原文照抄(不编造)。⚠️ 我们自己的压缩用同一个官方摘要后端 ⇒ summary 失败也可能打到我们,但会明确回报「未执行 + 原因」 v0.1.17 · 决策行节奏:每轮开头一次 + 中途变更再一次 · 长任务里同一轮的每个步骤都出现 档位决策:… + 压缩决策:不压|占用 ~57%|理由…(模型把每步都当成"阶段变化")。现两处文案写明一轮只写一次,中途真的改了决定(决定压缩 / 升或降档)再补一行,其余步骤不要重复。⚠️ 我第一版把卡改成稀疏(首次/涨5pp/隔20轮),与用户口径不符已撤销——卡照旧每轮一张(它是每轮决策的提醒位),改的只是决策行作用域 v0.1.16 · 压缩回执显式消歧:档位名里的百分比 ≠ 占用 · 回执原先虽已报「压缩后占用 ≈51%(510k)」,但同句仍有「浅压24% 档」「保留 ~240k」两个含 24 的数字 ⇒ 模型仍可能挑错那个数(真机先例:写出「回到 ~24%」)。现明写「档位名里的百分比是保留预算比例、不是占用」,「保留 ~240k」改为「保留预算 ~240k」,并预告「provider 下次实报后可能略降」(该数是压缩那一刻的 provider 锚定投影,真机 51% → 实报后 40%);每轮用量行的 used 始终与表盘同口径 v0.1.15 · 报告新增「占用账本」+ 口径公式单源 · 每个会话记 contextLedger:表盘口径(provider 实报 / 采样 surface / 计算投影 / 来源标注)+ 启发式口径(日志总量 / surface / 三行分项 system·tools·messages)+ 两口径之差 ⇒ 「表盘 408K 而三行 272K,差的 136K 是什么」直接从 m1-report.json 读(真机样例:748806+247859−487049=509616,三行 2161+200000+58256=260417,差 249,199)。公式实现唯一一份在 core.mjs(命名导出给测试 + 注入 M1/M3),m3 里出现第二份即被守卫断言抓红 v0.1.14 · R24.1:自算 DSH 投影公式(修我上一版的真缺陷) · v0.1.13 直接读 projectedTokens,但它只存在于 DSH 发给客户端的 view,插件读投影 state 拿到 undefined ⇒ 退回过期的 provider 实报值(真机压缩后仍是 748,806/74.9%,而表盘已是 51%→40%,比改之前更糟)。现照抄 DSH 源码自算:projected = max(0, pressureTokens + surfaceTokens − sampledSurfaceTokens),真机配对验证 509,616(51%) ✓ v0.1.13 · 占用口径对齐 DSH 表盘 · 真机反馈「压缩时 DSH 显示 75%、压完 40%,插件却说 ~24%」——查下来 DSH 有三套口径(表盘 = provider 锚定的投影;弹窗三行 = 启发式组成,含被取代的提示且不含图片/文件路由定价;measure().totalTokens = 又一码事)。现将 used 对齐表盘并从 v0.1.14 起自算投影、标明来源,回执直接念「压缩后占用 ≈41%(408k)」 v0.1.12 · 锚点来源中文描述 + 思考强度「低(low)」 · 修两处显示:① 压缩回执把内部 token recent-user/text 直接给模型 ⇒ 改为「语义锚点(从最近第 1 条你的消息起)」/「(按文本「…」定位)」;② 思考强度的用量行后缀、一次性教学、错误文案、HUD tooltip 全是裸英文 id ⇒ 统一为 低(low) / 高(high) / 最高(max),且入参兼容回填(照抄显示名也能生效)。⚠️ 本次含 client.js 变更 ⇒ 除 toggle 外需刷新页面 v0.1.11 · 档位说法全中文(英文 id 清零) · 审计出 3 条运行期泄漏:工具返回值 {"tier":"heavy"}、回执与 HUD 行的异常兜底会退回英文 id;另有 1 个隐患——canonicalTier 不认卡片上的「浅压24%」⇒ 模型照卡片回填会静默退回标准16%。现对外一律中文标签、内部与持久化仍用 id(兼容不变),并新增「模型可见文本扫描」断言(含"范围域未加载"的兜底路径) v0.1.10 · R21.2 真机测试发现并修掉的两处 · ① 不传 `tier` 会重置档位(卡片/文档都写"沿用",代码却写回 标准16% 并落盘)⇒ 改成真沿用;② 锚点未命中时回执不再丢降级痕迹("无需压缩"与"官方兜底"两条 return 路径补齐 retainSource/anchorFailReason)。两处都是真机跑出来的,各带行为级回归断言与变异验证 v0.1.9 · R21.1 紧急修复:工具静默不注册 · v0.1.8 的 keepFrom 是 type:'object' 却没写 additionalProperties ⇒ defineTool 拒收 ⇒ 整个 `compact_context` 不注册(模型调不了,教学还在喊它;强制线自动压缩不受影响)。已修 + 新增「工具 spec 结构化自检」断言(桩绕不过校验层) v0.1.8 · 语义锚点保留 `keepFrom` · 压缩不再只能"按比例留":可以让模型指位置——keepFrom:{recentUser:1}(从最近第 1 条你的消息起)或 {text:'开头十来字'},插件据此算预算,并保证锚点之后一律原样保留。典型收益:600k 里新需求只占 6% 时,从"留 240k(白留旧内容)"变成"留 ~44k" v0.1.7 · 决策卡「对称化」+ 越线反向建议 · 卡片原先只给"多留的好处" ⇒ 等于在劝模型选浅压24%。现两个方向都给数字(丢多少 / 压后占用 / 距线轮数),越线时明确建议选深压8% 或 标准16%(越线选浅压会"压了等于没压") v0.1.6 · 档位说法改中文 · 三档统一为 深压8% / 标准16% / 浅压24%(百分比由常量推导,不写死);英文 id 退为内部/兼容值,tier 中英都能传,老会话与落盘档位零迁移
更早的更新(R18 单档模型零注入、R17 改档轮首知情、R16 档位与教学去重、R5 保留范围自选…)见 docs/milestones.md。
插件只做三件事:把读数讲清(窗口 / 已用 / 占比 + 达标时的决策卡)、把选项给足(三档 + 语义锚点)、 把执行做对(按选定预算自选保留范围,边界交引擎校验)。"何时压、留多少"由模型决定——它才看得到任务形态。
每轮任务前 → 读上下文用量(tokenMeter.measure)→ 注入「窗口 / 已用 / 占比」到 prompt
| 档位(R16):深压8% / 标准16%(默认)/ 浅压24%;锚点(R21):keepFrom(更精确的"指位置")
→ 占用达「智能压缩线」时附决策卡(当前档位 + 两个方向的数字 + 趋势)
→ 模型【先在正文说一句理由,再】调用 compact_context(可选 tier / keepFrom)
→ 下一步的 pre-step 执行压缩:按锚点或档位算预算 → 自选范围 → compactRegion
→ 之后所有步骤跑在压缩后的上下文上(本轮不中断、不注入消息、不伪造发言)
→ 占用达「强制压缩线」时插件在 pre-step 无条件发起压缩(同一条预算/范围路径)
可选(智能思考开关):读当前模型档位 → 注入给模型 → 模型【先说理由,再】调 set_reasoning_effort总原则——失败极性统一:凡「信息不足 / 能力不可用」一律保守:宁可多留、多花 token、少做动作;
且绝不静默——每一跳都写进报告字段与「压缩自检」回执(anchorProbe/rangeProbe/rangeSource/effortGate…)。
环节 · 主路径 · 一级兜底 · 二级兜底 · 最后兜底 保留预算(R21+R16) · keepFrom:{text:'…'}(前缀→包含定位) · keepFrom:{recentUser:N}(最近第 N 条你的消息) · tier(本次传入 / 会话已登记) · 标准16%(从未登记);每跳记 tried/reason 保留范围(R5) · selectRange 按预算从尾部回退(+keepFromSeq 护栏:绝不越过锚点) · 末端边界不合法 ⇒ prevEnd 逐节点回退(≤64 次) · 仍不合法 ⇒ 官方 compactIfNeeded('context-overflow') · 判定「没什么可压」且未越线 ⇒ 什么都不压(source:none) 压完仍越线(R16) · 自算压完即收工 · 仍 ≥ 强制线 ⇒ 追加官方 overflow 收口(rangeSource=own+official) · — · 记录 officialWhy(如实标注是官方手段) 档位/路由读数(R1/R17) · 投影 pending(用户刚改的模型/档位) · requestHeader(上次请求的已生效值) · 两者都没有 ⇒ 取模型默认档并标 adapterDefault · 读不到 ⇒ 不注入(不编造档位),记 effortDiag.error 换档请求(R1/R3) · 合法 ⇒ 写期望档,下一步生效 · 不在可选集 ⇒ 工具拒绝并回可选集 · 他人已显式指定 ⇒ 让位(不覆盖别人的选择) · 内部异常 ⇒ 吞成结构化结果(绝不抛错、不中断本轮) 工具存在性(R18) · 全局注册 + 按 agent 可见 · 单档模型 ⇒ restrict 对该会话隐藏 · restrict 不可用 / 拿不到回滚句柄 ⇒ 不隐藏(宁可多花 ~417 字符/请求) · 开关关闭 ⇒ 撤回注册(模型清单里没有它) 可切性判据/收敛(R18) · 每步算 canSwitch,按路由变化收敛 · 读不到路由/可选集 ⇒ 不抑制(保守:继续注入 + 保留工具) · 路由键未变 ⇒ 走快路径跳过重算(零异步开销) · 异常 ⇒ 保持现状 + 留痕(effortReconcileError) 模块加载(R10/M3) · 域模块懒加载 + 空值降级 · 范围模块未就绪 ⇒ 退官方路径 · M3 未就绪 ⇒ 本轮跳过(m3-module-missing 留痕) · defineTool 未就绪 ⇒ 工具不注册,下一步重试 档位/锚点持久化(R16.16/R21) · 与档位一起落盘 compact-tier.json · 写失败 ⇒ 内存仍生效 + 日志 · 热换/重启 ⇒ 落盘恢复(tierState=restored) · 文件损坏 ⇒ 按未登记处理(标准16%) 开关关闭(R18.2) · 「智能思考」关 ⇒ 不注入、不注册 · 已注册过 ⇒ 撤回(成功支) · 撤回不可用(无 disposer)⇒ 留痕 + per-agent 限制兜底 · 「智能压缩」关 ⇒ 回到原生 DSH(不注入、不压缩、不注册) 跨热换残留(R18) · 限制的 disposer 存进程级注册表 · 激活时清扫(released≥1) · 开关翻转时再清扫 · 拿不到回滚句柄 ⇒ 当初就不隐藏(失败极性)
改进 · 内容 · 为什么重要 压缩改「工具触发」(R4) · 模型调用 compact_context ⇒ 登记意图 ⇒ 下一步 pre-step 执行 ⇒ 本轮无缝继续 · 彻底删掉「伪造用户消息拉起会话」那套(maybeResumeAfterMarker / sessionController.prompt / agent.followup 在代码里零出现)。压缩与「叫醒」解耦:工具调用本身就保证有下一步,不需要叫醒 保留范围「自己算」(R5+R16) · 保留预算由会话档位决定(R16 三档,见下一行)⇒ 调公开方法 compactRegion;边界不合法则逐节点回退;失败或压完仍越线才沿用官方 overflow 兜底 · 引擎 context-overflow 写死 retainTokens = 0:真机实测在只有 53% 占用时把 surface 从 1117 节点 / 367,794 token 砍到 4 节点 / 8,603 token(影子化 97.7%)。现在近端细节留得住,且整段没超预算时什么都不会压 语义锚点保留(R21) · keepFrom:{recentUser:1\ · 2\ · 3}(从最近第 N 条你的消息起)或 {text:'开头十来字'}(按文本定位)。模型指位置、插件算预算;预算 = 锚点→末尾 token + 余量,且 keepFromSeq 护栏保证范围绝不越过锚点 · 三档只能按比例留,而常见形态是「旧需求占地方、新需求只在最后几个百分点」⇒ 白留旧内容(浪费 + 降智源)。降级链 text → recentUser → 三档 → 标准16%,每跳都留痕 | 压缩档位(R16) | compact_context 可选参数 tier:**