Skip to content

Releases: cfanzx/dsh-compact-relay

0.5.5:已收藏星标常亮

Choose a tag to compare

@cfanzx cfanzx released this 29 Oct 10:58

dsh-compact-relay 0.5.5

0.5.4 的 debug 版:修用户反馈「收藏后星标自动消失」。只改客户端样式注入,收藏数据 / 后端 / 推荐逻辑零改动。测试 396 → 398 PASS。

根因

星标挂在宿主助手消息的 hover 动作行里。宿主 CSS 对 [data-actions-reveal="hover"] 的行默认 opacity:0,:hover/:focus-within 才恢复——收藏后星标仍在 DOM、aria-pressed=true,但整行透明,鼠标一移开就看不见。不是收藏丢失、也不是 0.5.4 回归。

修法

注入 CSS :has():只把「含已收藏星标的 hover 动作行」强制可见;非 hover / 非键盘焦点时,同行其它按钮与同 slot 的其它插件按钮保持隐藏且 pointer-events:none → 常亮的只有已收藏星标本身,hover/focus 时完整动作行照常出现。选择器只用稳定锚点([data-actions-reveal="hover"]、slot 名),不依赖宿主哈希类名;触屏不生效;不支持 :has() 的旧浏览器自动回退。

行为

  • 已收藏消息:鼠标移开 → 金色星标常亮;hover → 复制/赞/踩/分支/用量等完整出现
  • 取消收藏:立即恢复宿主原 hover 隐藏行为
  • 未收藏消息:行为完全不变

npm / GitHub main f325748 + tag v0.5.5。

0.5.4:0.5.3 debug(建议候选/无正文标题/压缩后建议清理)

Choose a tag to compare

@cfanzx cfanzx released this 29 Oct 10:38

dsh-compact-relay 0.5.4

0.5.3 的 debug 版:只动收藏/推荐两处数据面与预览卡文案,压缩主流程零改动。测试 391 → 396 PASS。

修复

  • D1 建议候选过滤工具角色:「建议先收藏」只保留用户/助手消息,工具结果的报错堆栈不再占名额(suggestPinCandidates 的 limit 先于角色过滤,工具占满时用同一函数对用户/助手事件重跑,不新写识别);已收藏列表不过滤,工具输出仍可手动收藏。
  • D2 无正文标题兜底:预览收藏条目里「(无文本)」显示为「第 条」,可按序号找回;摘要的收藏目录口径不变。
  • D3 压缩后清理推荐建议:收到 compaction/summary/compaction/end 立即清空本会话建议、取消待跑去抖、用世代号作废在跑打分——被压走的消息不再残留在 /favorite suggest 与星标建议上;压缩后新回合照常产生建议。

其他

  • 版本 0.5.4;npm / GitHub main c634852 + tag v0.5.4;Mac 已同步(Win 随本版一起同步)。

0.5.3:收藏(pin 退役)+ 推荐收藏 + 预览卡补收藏

Choose a tag to compare

@cfanzx cfanzx released this 29 Oct 10:30

dsh-compact-relay 0.5.3

从 0.5.2(e4bd0b2)拉出的分支 fix/0.5.3。主题:收藏(pin 退役)+ 推荐收藏,外加真机验证带出的 P2 语义修正。

收藏:把「标记」和「落档」彻底分成两步

  • 标记:点星 / /favorite mark [序号] / /favorite last,只写项目级 JSONL 一行 {op, sessionId, seq};不依赖活 agent(归档/关闭的会话也能按 seq 标)。
  • 落档:只在压缩时发生——摘要追加「收藏(本段)」目录 + ~/.dsh/dcr-favorites/snapshots/<链根id>.md 快照;压缩后补标会当场写快照兜底。
  • 压缩预览卡补收藏区:确认卡显示「本段 N 条 · 已收藏 M 条 · 建议补 K 条」,列出已收藏条目([用户]/[助手]+标题)与「建议先收藏」候选,每条可一键收藏(含用户发言);只提示,绝不自动收藏。
  • pin 退役:压缩不再按 pin 裁剪,/pin 仅保留只读兼容。

推荐收藏

  • 每条助手回复异步打分(flash/关思考/temp0,0-100,8s 超时,失败不亮);三档阈值 60/75/85(默认标准 75)+ 必亮/屏蔽词。
  • 星标金色光晕 + 「推荐」标签,只点亮不自动收藏;设置页可调开关/档位/词表,并支持「试算」。

P2 语义修正

  • force-archive 的 already 判断先于 confirmStop(已归档不带确认 → 200 already);已归档+确认时补跑 stopActivity 防漏杀。
  • 错误码分档:未知会话 404 / 宿主缺接缝 503 / 内部失败 500。
  • force-archive/recover 响应改 archivedCount + newlyArchived/newlyRecovered(不再回全量归档集合)。

其他

  • 测试 375 → 391 PASS;契约帧新增 favorites / favoriteSuggest / 预览 favoritePreview。
  • 已验证:npm dsh-compact-relay@0.5.3,Mac 真机(压缩快照链路 / 推荐点亮 / 预览卡收藏 / UI 一致性)。

dsh-compact-relay 0.5.2 — 缓存对齐 + 强制归档 + 契约快照

Choose a tag to compare

@cfanzx cfanzx released this 28 Oct 07:30

0.5.2(2026-10-28)

缓存对齐:摘要/交接单调用改为沿用主请求的 reasoningEffort,不再固定 off;宿主 thinking 开关不再切断前缀缓存——隔离 A/B 命中 74.0% → 96.9%,生产真实压缩 2–5% → 99.7%。输出上限 8,192 → 16,384 兜住思考输出。

契约快照 + record 构造收口:新增 test/contract-golden.mjs(readState 帧 / 父子 record 键序 / 标题格式)与 lib/core/relay-record.js(父子写点只传语义参数,字节级不变)。

强制归档旧会话:接力条菜单新增「强制归档旧会话(会停止后台任务)」,二次确认 + 置顶确认(confirmUnpin);服务端 action:'force-archive' 必须 confirmStop:true,是唯一 stopActivity:true 调用点。默认行为不变:交接的自动归档仍不强制、不停任务。

修复:交接预览九段草稿框被 flex 压成 0 高(flexShrink:0)。

契约新增(全部 additive):readState.parentPinned、readState.capabilities.forceArchive;action:'force-archive'。

测试 340 → 375 PASS。完整变更见 CHANGELOG。

v0.4.1 — 原生压缩线显示口径统一

Choose a tag to compare

@cfanzx cfanzx released this 25 Oct 08:01

修复:原生压缩线显示口径不一致(设置页仍写 80%,指示灯写实际触发 67.8%)

  • 现象:0.4.0 把指示灯/悬停卡改为「实际触发线」(DSH 按 min(窗口×thresholdRatio, 窗口−输出预留−headroomTokens) 触发),但设置页的「作用 / 调整后 / 原生阈值」三处和「指示灯怎么看」教学卡仍按 thresholdRatio 显示 80% —— 同一插件两处数字互相矛盾,而且设置页那句「80%:DSH 会自己压历史」本身是错的(实际 67.8% 就压)。
  • 修复:客户端抽出纯函数 nativeLineInfo()(实际线优先;给出配置阈值、实际触发点与差异原因);设置页三处 + 教学卡刻度/文案 + 指示灯悬停卡统一为「实际触发 X%(配置阈值 Y%)」,并说明「模型输出预留 + DSH 自身余量」使实际触发更早;交接提醒线上限也改用实际触发线(原按配置 80% 算,允许把交接线设在实际触发点之后、永远走不到)。
  • 不改:宿主计算、触发条件、压缩/交接行为;纯显示文案与校验上限。
  • 验证:Mac 真机(0.4.1 客户端):设置页与悬停卡同显 67.8%(678.5K / 1.00M,deepseek-flash),配置阈值 80% 作为注释保留;交接线上限随实际线收紧。

显示层补强(dev-god 独立审查后,同属 0.4.1)

  • 关闭态空数字:autoEnabled=false 时原生线信息原本连「实际会触发多少」一起丢,设置页会出现「原生阈值 %」、确认弹层出现「容量到 %」。现在关闭态显示「配置 80%(已关闭;开启后实际触发 67.8%)」,恢复确认也用实际值。
  • 差异归因:why 只在「预算分支确实更小」时才归因为输出预留/余量;modelPolicies 路由覆盖改说「该模型单独配了阈值」;实际触发晚于配置阈值时不再套用「理论上限」说法(原实现会自相矛盾)。
  • 交接线遗留值:设置页对「已保存交接线 > 实际触发线」给出显式提示(不静默改写);交接线输入上限与保存口径统一(按实际触发线向下取整)。服务端保存校验仍以配置阈值做上限(防呆不阻断),本版仅在 UI 收紧并提示如何调整。
  • 文档口径:README badge / README.en 同步到 0.4.1 与实际触发线说明;ROADMAP 标注已失效参考仓库。
  • 测试:test/native-line.mjs 新增 nativeLineInfo 行为回归(正常/关闭/算不出/路由覆盖/实际晚于配置五组输入,从客户端源码切出函数真跑)。

v0.4.0 — 接力质量(判断机制 / 诊断包 / 保留契约 / 账本)

Choose a tag to compare

@cfanzx cfanzx released this 24 Oct 17:22

小版本:接力质量——判断机制加强(cue v2 + 注入门控 + 计量)、脱敏诊断包、pin 保留契约、压缩/交接轻账本、交接预览两段可编辑、接力链元数据、原生自动压缩真实触发线;并修复 BUG-013(压缩/交接后旧 compressHint 从未失效,0.3.1 起存在)。

修复:压缩/交接后「压缩判断记录」从未失效(BUG-013,0.3.1 起存在)

  • 问题(真机发现,2026-10-24):压缩/交接后旧的 compressHint 本该自动失效(环上断点消失、不再抑制 cue),实际一直判定「有效」。P0-1 上线后真机诊断包里 counters.cueInjected=0 的直接原因就是它——旧 hint 的 ratio(26.4%)高于当前 ratio(18.5%),ratio - hint.ratio < 10% 的 hint 抑制规则把 cue 全部挡掉;指示灯环上也残留旧断点(26%)。
  • 根因:lib/actions/state.js 调 lastCheckpointSurfaceSeq(session) 时漏传第二个参数 isCheckpointSource;lib/dsh/session-view.js 里对 undefined 调用判定函数抛 TypeError,又被事件循环里的 try/catch 吞成「不是 checkpoint」→ 永远返回 -1 → checkpointSeq = undefined → compressHintIsActive(hint, undefined) 恒为 true。(对照:lib/dsh/select-range.js 传参正确,所以选段/不二压一直正常。)
  • 修复:① state.js 显式传 deps.engine?.isCompactCheckpointSource;② session-view.js 的 lastCheckpointSurfaceIdx / lastCheckpointSurfaceSeq 给 isCheckpointSource 加默认值 engine.isCompactCheckpointSource(dsh→dsh 同层、显式传参优先;纯 node 单测不注册 DSH 包解析钩子时安全降级为「无 checkpoint」,不抛);③ 回归测试加进 test/state-passive.mjs(旧 hint + 更新的 checkpoint → compressHint: null;hint 比 checkpoint 新 → 保留)。
  • 影响面:只影响「判断记录新鲜度」的显示与 cue 抑制;不改压缩/交接行为、不改拒绝条件、不动 client。此 bug 自 0.3.1 起存在,随 0.4.0 修复(不回溯 0.3.6)。

P2-3 原生自动压缩「真实触发线」(按路由容量算,不再只显示窗口比例)

  • 问题:指示灯一直把 compaction-basic 的 thresholdRatio(默认 80%)当触发线显示,但 DSH 实际按 token 数触发:thresholdTokens = floor(min(W × rate, W − 输出预留 − headroom)),触发点 = thresholdTokens / W。当路由请求给输出预留较多或 headroomTokens 被改过时,真实触发点会明显低于 80%(作者在 Windows 真实会话里问过「原生压缩线为什么降到了 50」)。
  • 修复:新增 lib/core/native-line.js(纯函数,公式与 dsh-compaction-basic 源码一致);lib/dsh/native-compaction.js 增读 config.headroomTokens(默认 65536)与嵌套 modelPolicies(按 provider+model 精确覆盖 thresholdRatio/headroomTokens,支持列表与映射两种 YAML 写法;BUG-012 保持:嵌套值不会变成全局线);lib/dsh/pressure.js 新增 routeBudgetOf():输出预留 = session.requestHeader().config.maxTokens ?? 适配器 defaultMaxTokens ?? 0,窗口复用 llm.resolveModelInfo 缓存;readState 的 native 新增 ratio/thresholdTokens/contextWindow/reservedCompletionTokens/headroomTokens/overridden/route/unavailable(thresholdRatio 仍下发生效比例,老客户端可回落;算不出时 ratio=null + unavailable=原因)。
  • 客户端:环上原生刻度/越线闪烁改用真实线;悬停标题显示 原生自动压缩线 X%(thresholdTokens / 窗口,provider/model,按路由覆盖);首次引导文案同步。
  • 测试:test/native-line.mjs(公式 80% vs 67.8%、边界/无预算、解析与 BUG-012 回归、路由覆盖匹配、readState 下发与老字段兼容);并入 npm test。
  • 不改任何触发/拒绝条件,只改「显示与数据」;版本仍 0.3.6,未发布。

P2-1 交接预览「两段可编辑」(只允许 约束 / 下一步)

  • 预览接口新增 editable: { titles: ['约束','下一步'], maxChars: 4000, note, sections } 与 ordinal(第 N 次交接);客户端交接预览卡只给这两段 textarea(data-cr-handoff-edit),其余段只读并显示「程序生成段不可改」。
  • 确认时客户端只提交「用户真的改过且非空」的段;宿主在压缩/写盘前校验:非法字段(程序生成段 / 拼错段名 / 非对象)→ code='edits'(HTTP 400,消息点名不能改的段);单段 >4000 字符拒绝(不静默截断);空值/未改回落重新生成的原文。应用方式:生成九段后覆盖这两段再渲染(composeHandoffText 仍按九段固定顺序),程序段永远来自程序/模型。
  • 九段结构不变;必须用户点「确认交接」才写盘(预览/校验过程不写盘)。action 路由 body 上限从 8KB 放宽到 48KB(容纳两段编辑)。
  • 测试:test/handoff-p2.mjs(白名单/空值/超长/非法字段/九段顺序/客户端入口静态守卫)+ test/handoff.mjs 集成(编辑后交接单落盘内容生效、程序段被拒且零副作用、空编辑回落)。

P2-2 接力链元数据补全

  • relay 记录 canonical 字段补齐:parentId / childId / ordinal / at(ms)/ atIso(本地 ISO);0.3 旧字段 parentSessionId / index 继续写入,读侧兼容旧记录并补齐 canonical 字段。
  • 链式序号:首次交接 ordinal=1;从子会话继续交接 ordinal=2 且 parentId 指向真正父会话;下一次交接标题序号改用 ordinal 推导。
  • 交接单头新增 - 交接序号:第 N 次交接(预览草稿里也能看到);接力链视图留 0.5,不做。
  • 测试:test/handoff-p2.mjs(canonical/旧记录兼容)+ test/handoff.mjs(首次 ordinal=1 + 文档头、链式第 2 次 ordinal=2 / parent 正确)。

P1-3 压缩/交接可审计流水(轻账本)+ 设置页累计数字

  • 新增轻账本 ~/.dsh/compact-relay-ledger.jsonl(JSONL,一行一条,schema dcr-ledger/v1):每次压缩/交接成功后追加一条——kind、本地时间、耗时 ms、压前/压后 tokens + ratio、腾出 tokens、pin 命中(inRange/total)、交接单落盘路径(仅路径)与 childId;交接内部那次压缩不重复记账(一次 /handoff 一条)。
  • 隐私:allowlist 构造,只记数值/枚举/时间/路径,不含消息原文、摘要、交接单正文、sessionId、cwd/用户名;测试用敏感 fixture 断言产物中不存在。
  • 有界与降级:只保留最近 200 条(超限整文件重写为最近 200 条,tmp+rename 原子替换);文件损坏按行跳过;任何读写失败静默返回 {ok:false},绝不影响压缩/交接本体(测试覆盖:账本 record 抛错时交接仍成功;账本路径不可写时动作照常、文件损坏仍可读可写)。
  • 设置页:GET /api/compact-relay-settings(含保存/恢复默认响应)新增 ledger 累计字段(count/compactCount/handoffCount/freedTokens/avgDurationMs/lastAt),设置页「关于与危险操作」显示一行 累计:已压缩 X 次 · 交接 Y 次 · 腾出约 Z tokens · 平均耗时 W;无记录时显示引导文案。数字与账本文件同源(测试断言相等),不做大盘。
  • 新增 test/ledger.mjs(字段完整 / allowlist + 敏感 fixture / JSONL 容错 / 上限裁剪 / 不可写降级 / 设置数字一致 / 客户端累计行静态守卫)+ test/handoff.mjs 压缩与交接成功后的账本落盘断言 + test/plugin-load.mjs 设置端点累计断言;并入 npm test。
  • 不改压缩引擎/拒绝条件/阈值;版本仍 0.3.6,未发布。

P1-1 压缩预览「建议锁定」提示(只提示、不自动锁)

  • 预览接口新增 preview.pinSuggest = { count, items: [{seq, role, reason, snippet}] }:宿主侧纯本地启发式(lib/core/pin-suggest.js)对「本次将被压掉的历史」识别三类信号——明确要求别改(用户消息里「不要改 / 别动 / 保持原样 / 必须保留」等)、报错堆栈(异常类名 / 堆栈帧 / 堆栈 / 报错等)、关键决策(决定 / 拍板 / 采用 / 改用 / 结论等);最多 5 条、每条一句摘要 ≤60 字符、不贴全文;短句、系统消息、运行时合成的 user/message(checkpoint 等)、普通工具输出不进入候选。
  • 客户端预览卡只渲染「建议锁定 N 条(先 /pin 再压)」+ 每条理由/摘要;绝不自动锁定(预览不写任何会话事件,测试断言 session.log 不变)。/bc 预览 message 里也带一句建议。
  • 收敛实现:消息角色/摘要抽取挪到 lib/core/pin-anchors.js,/pin list、建议锁定、交接单锚点共用一份(删除 actions/pin.js 内私有 previewOf)。

P1-2 pin 作为「保留契约」(声明与展示,不承诺第三方引擎)

  • 交接单九段结构不变:锚点写进程序段「当前进度」——- 锁定锚点(保留契约,接手者请原样保留这些消息) + 每条 seq N · 角色 · 「一句摘要(≤60 字符)」(最多列 10 条,其余提示 /pin list 看全)+ 一行「跨引擎压缩器是否遵守取决于其实现」。
  • 压缩预览新增 preview.pinHits = { inRange, total }:inRange = 本次候选范围里被锁定保护而跳过的条数,total = 会话仍活着的锁定总数;客户端预览卡显示「pin 命中 N 条」并区分范围外锁定。
  • 不回归:锁定消息仍在压缩范围之前截断(clipRangeByPins 既有逻辑),压缩后原样保留在 surface。
  • 测试:新增 test/pin-contract.mjs(候选识别含反例、建议条数/上限、交接单锚点、pin 命中数、预览只读不自动锁)+ test/handoff.mjs A1b 预览断言补锚点;并入 npm test。
  • 边界:只做「声明与展示」,不承诺其他压缩器遵守 pin;不改压缩引擎/拒绝条件/阈值;版本仍 0.3.6,未发布。

P0-1 判断机制加强(未发布,代码已落地,待 3 次真实任务验收)

  • 背景(BUG-010 后续):Windows 173 个真实会话里 cue 注入 18 次、mark_compress_point 调用 0 次;Mac 仅在「脚本写完 + 真机验证通过」这种明确节点自发调用过。归因:旧 cue 的「不确定就不用调」让模型倾向保守跳过、工具描述强调拒绝风险、每轮注入变成背景噪声。
  • cue 文案 v2(lib/core/compress-cue.js):把抽象「自然节点」换成 4 个可自检的完成信号——功能已实现并验证 / 文档已交付 / 问题已定位并修复 / 准备交接;明确「不确定也建议调一次让工具判断:被拒绝不会改动历史、不影响会话」;保留「不处于阶段收尾就不用管」边界,删除「不确定就不用调」。
  • 注入门控(lib/adapters/prompt.js + lib/core/policy.js 常量 CUE_MIN_RATIO=0.10 / CUE_MAX_RATIO=0.40 / CUE_MIN_TURNS=3):只有全满足才注入——① 容量落在 [max(10%, 动态最小线), min(40%, 压缩线))(到压缩线不注入,避免诱导注定被拒的调用);② 距上次注入 ≥3 轮(按 text 回调调用次数计,进程内 Map、不新增持久状态、不落盘);③ 一轮内最多一次。异常/无 agent/无缓存行为不变(返回空串)。
  • 工具描述 v2(lib/adapters/tool.js):明确「用户和模型都可以调用」;「被拒不需要致歉、不会改动历史、也不影响会话,不需要重试」;补充典型调用时机(阶段完成 / 准备交接前 / 长任务中途确认可回退点);把「不需要每个小步骤都调用」弱化为句尾的「小步骤不用调用」。
  • 计量接入(lib/dsh/diag-counters.js、lib/actions/compress-hint.js、lib/adapters/prompt.js):新增 cueInjected / markCalls / markAccepted / markRejected 四个计数(被拒也算调用)。只记次数与结果、不记 cue 原文/任务内容(隐私口径与 P0-2 一致);P0-2 诊断包的 counters 与 Markdown 直接带出这四个数 → 真实使用后看数字即可判断「标准 B:3 次真实任务 ≥2 次模型自发调用」,不用翻会话日志。
  • 测试:test/compress-cue.mjs 重写覆盖 U1(文案 v2:4 信号 + 被拒无副作用 + 不含旧措辞)、U2(区间 0.08 不注入 / 0.12 注入 / 0.40、0.45 不注入 + 第 4 轮再注入)、U3(无 agent 不注入 / 同轮不重复 / 异常降级)、U6(5% 桶 / 动态最小线 / hint 抑制 / state-hub 原有断言随 v2 同步);test/smoke.mjs 增 U4(markCompressHint 成功/被拒计数 + 不变式);test/plugin-load.mjs 增 U5(工具描述关键词)+ cue 门控下的 20% 注入断言;test/diagnostics.mjs 同步 counters 白名单/渲染。
  • 范围红线(未违反):不改压缩/交接行为、不改拒绝条件(最小线 5% / 压缩线 40% 不动)、不自动动作、不动指示灯客户端、零新依赖零网络、版本号仍 0.3.6。
  • 未完成(发布前):需 3 次真实长任务验证标准 B;soak 记录模板已补「判断机制读数方法」。

P0-2 诊断包最小版(已完成)

  • 新增「诊断信息」入口(设置 → 压缩接力 → 帮助):【生成并预览】在本机生成一份 Markdown + JSON 诊断信息,弹层里先预览,再【复制 Markdown】/【复制 JSON】;【打开问题报告页】打开预填版本/平台短字段的 GitHub issue 页。不做任何自动上传、遥测或后台外发,也不加网络能力。
  • 新增只读端点 GET /api/compact-relay-diagnostics?sessionId=:沿用宿主 connection.requestRejection 鉴权(401/403);只支持 GET(非 GET 405);无活动会话时 state: null;返回 {ok, schema, version, generatedAt, markdown, json, issueUrl},响应不含正文/堆栈(失败只回短文案)。
  • 新增 lib/dsh/diag-counters.js:进程内计数/耗时/错误码(重启清零)。压缩/交接成功失败计数、最近 20 个耗时样本的 p50/p95、按 (code, phase) 聚合的最近 20 条错误码(只收 code/phase/at/count,不收 message),锁定动作计数。
  • 新增 lib/dsh/diagnostics.js:dcr-diagnostics/v1 产物构造。allowlist 逐字段取值(禁止整对象透传),产物只含数值/枚举/布尔/错误码;JSON ≤ 64KB、errors ≤ 20 条且单条 ≤ 200 字符;额外正则脱敏(GitHub token / sk- / Bearer / cookie / 邮箱 / 绝对路径 / 内网 IP / session id 等命中替换为 [REDACTED],只记数量不保留原文);issue URL 只预填短字段、长度 < 7500。
  • 埋点(不改行为与文案):actions/compact-run.js 与 actions/handoff.js 记成功/失败/耗时/错误码;actions/pin.js 只在成功锁定时计数(解锁不计)。
  • 新增 lib/actions/diagnostics.js:诊断组装的动作层入口(仓库 A9 架构守卫要求 adapters 不直接 import dsh/,故组装落在 actions,HTTP 适配层只做协议转换)。
  • 文档/模板:README(中英)新增「诊断信息与隐私 / Diagnostics and privacy」节;.github/ISSUE_TEMPLATE/bug_report.yml 文案对齐「生成 → 预览 → 复制」;设置页按钮只用「生成 / 预览 / 复制」,不出现「发送 / 上传 / 上报」。
  • 测试:新增 test/diagnostics.mjs(T1–T9:形状白名单、敏感 fixture 脱敏、禁止字段、零网络(fetch 抛错 + 源码扫描)、URL < 7500、64KB/20 条/200 字符上限、p50/p95、计数器、文案),并入 npm test;test/plugin-load.mjs 增加只读端点用例(鉴权 / 405 / 白名单产物 / 无会话 state=null);test/e2e/settings-ui.mjs 增加诊断信息真机检查(帮助区入口按钮、点【生成并预览】后的弹层内容含 dcr-diagnostics/v1 与「脱敏」、不含 sessionId/绝对路径、点【复制 Markdown】必须有成功或失败反馈)。
  • 版本号仍保持 0.3.6(0.4.0 未发布、不 bump);package.json 无新增依赖,capabilities 未新增 network。

0.3.6(2026-10-24,原生压缩线解析作用域修复)

  • 问题(BUG-012,外部用户报告,Windows / DSH 0.2.1-rc.2,真实会话 session-1f90f5ee,2026-10-23):指示灯「原生自动压缩线」(紫色刻度)从默认 80% 误显示成 50%。用户配置的 compaction-basic 块里只有 config.modelPolicies(mac-unc / qwen35-unc-64k)写了 thresholdRatio: 0.5,并没有改全局线。
  • 根因:lib/dsh/native-compaction.js 的 parseCompactionOverride 只按「下一个顶层 - 条目」截断,不区分 YAML 缩进层级 —— config.modelPolicies[*].thresholdRatio 被当成整个插件的全局线。
  • 修复:按缩进定位 compaction-basic 条目 → 它的 config: 直接子键,只认这一层的 auto / thresholdRatio;更深嵌套层(modelPolicies[*]、多层 map/list)一律忽略。applyAutoOverride 写入口径同步(只写 config 直接子键,不写进 modelPolicies)。
  • 边界:没有 config: / config 为空 → found=true 但无覆盖,读侧回落默认值(80%);多个 compaction-basic 条目取第一个;CRLF 文件解析与 apply 后都保持 CRLF;# 注释与引号值兼容现有写法。
  • 范围:本次只修解析作用域;「按当前模型 route 算真实触发线(deepseek-flash 67.8% / mac-unc 75%,而非窗口比例 80%)」留 0.4。
  • 测试:新增 test/native-compaction.mjs(① config 直属阈值生效 ② 嵌套 modelPolicies 被忽略并回落 0.8 ③ 嵌套+直属取直属 ④ 多层嵌套 ⑤ 无 config / 无条目 ⑥ CRLF ⑦ apply 后回读且不破坏 modelPolicies ⑧ 引号/注释),并入 npm test;修复前该测试在嵌套用例上失败(把 0.6 误读成全局线)。
  • 测试基线:smoke 53/53、handoff 35/35、plugin-load ...
Read more

v0.3.6 — 原生压缩线解析作用域修复

Choose a tag to compare

@cfanzx cfanzx released this 24 Oct 06:35

补丁版:修复「原生自动压缩线」被嵌套配置误报(BUG-012)。

问题

指示灯上的「原生自动压缩线」从默认 80% 误显示成 50%。真实案例(Windows / DSH 0.2.1-rc.2):用户 profile 的 cordis.patch.yml 里,compaction-basic 块只有 config.modelPolicies(mac-unc / qwen35-unc-64k)写了 thresholdRatio: 0.5,并没有改全局线。

根因

lib/dsh/native-compaction.js 的 parseCompactionOverride 只按「下一个顶层 - 条目」截断,不区分 YAML 缩进层级 —— config.modelPolicies[*].thresholdRatio 被当成了整个插件的全局线。

修复

  • 按缩进定位:compaction-basic 条目 → 它的 config: 直接子键,只认这一层的 auto / thresholdRatio;更深嵌套层(modelPolicies[*]、多层 map/list)一律忽略。
  • applyAutoOverride 写入口径同步:只写 config: 直接子键,不写进 modelPolicies;无 config: 时补块;CRLF 保持不变。
  • 新增 test/native-compaction.mjs(8 组场景:直属生效 / 嵌套忽略 / 嵌套+直属 / 多层嵌套 / 无 config / 无条目 / CRLF / apply 回读 + 引号注释),并入 npm test;修复前嵌套用例会失败(把 0.6 误读成全局线)。

边界

  • 本次只修解析作用域。「按当前模型 route 算真实触发线(deepseek-flash 67.8% / mac-unc 75%,而非窗口比例 80%)」留 0.4。
  • config: {} 行内 flow 写法不专门支持(与旧实现一致;实际 profile 是块式 YAML)。

测试基线

bundle --check PASS(148857 字符,本补丁不改 client)、smoke 53/53、handoff 35/35、plugin-load 14/14、contract 17/17、compact-log PASS、compress-cue PASS、state-passive PASS、native-compaction PASS、markdown-lite PASS。

升级

dsh plugin --profile desktop add dsh-compact-relay@0.3.6

升级后重启 DSH 生效。0.3.4 / 0.3.5 的发布物保持原样不动。

v0.3.5 — 压缩按钮误禁修复

Choose a tag to compare

@cfanzx cfanzx released this 24 Oct 04:43

补丁版:修复外部用户报告的「压缩按钮灰置且提示暂无可压缩内容」。

问题

会话用量已经超过压缩线、插件也建议压缩,但指示灯菜单里的「压缩旧会话」呈灰色不可点,提示「暂无可压缩内容(会话太短,或近期会话需保留原文)」。典型场景:DSH 桌面端刚重启,会话已经加载、但还没有活的 agent(还没发过消息)。

根因

readState 允许在「没有活 agent」时退化成用已加载的 live session 测压力(所以百分比和建议是正常的),但 compactRange 只在有活 agent 时计算,返回 null;客户端把 null 统一显示成「暂无可压缩内容」并禁用按钮——把「会话未激活」误当成「没有可压缩内容」。0.3.3 / 0.3.4 均存在。

修复

  • state(additive,不破坏 HTTP 契约):target(含 passivized live session)非 null 即计算 compactRange(纯显示用;预览/真正压缩仍要求活 agent);新增字段 compactRangeReason,仅当范围存在且无活 agent 时为 'no_agent'。
  • 客户端:reason === 'no_agent' 时按钮仍禁用(预览需要活 agent,点了也执行不了),但文案改为「会话未激活:发一条消息唤醒 agent 后可压 N 条 · 约 X tokens」,悬停 title 可读;rangeInfo === null 与正常可压的既有文案不变。
  • 测试:新增 test/state-passive.mjs(无活 agent 的 live session 仍算出范围 + reason='no_agent';有活 agent reason=null;空会话/无会话不误报;既有 state 字段不丢),并入 npm test。
  • 测试基线:bundle --check PASS(148857 字符)、smoke 53/53、handoff 35/35、plugin-load 14/14、contract 17/17、compact-log PASS、compress-cue PASS、state-passive PASS、markdown-lite PASS。

升级

dsh plugin --profile desktop add dsh-compact-relay@0.3.5

升级后重启 DSH 生效。0.3.4 的发布物(npm 0.3.4 / tag v0.3.4 / 对应 Release)保持原样不动。

dsh-compact-relay 0.3.4 — 判断机制修复 + 小 bug

Choose a tag to compare

@cfanzx cfanzx released this 24 Oct 04:34

dsh-compact-relay 0.3.4 — Release notes

中文(主)

更新内容(0.3.4,判断机制修复 + 小 bug)

判断机制修复(BUG-010:「适合压缩的节点」几乎不触发)

  • 问题与证据:扫描 427 个真实会话(36,813 次工具调用)——0.3.1 上线后 mark_stage 工具调用 0 次,compress-hint 记录全库仅 1 条(开发期测试);而工具 schema 在会话日志中出现 43 次,说明工具确实进了模型上下文,只是模型从不调用。
  • 根因:旧工具描述写「系统会告诉你当前容量」,但系统从未告诉模型任何容量 → 「超过最小线 / 还没到压缩线」两条条件无法判断,描述末尾的「不要凑数调用」进一步抑制调用。
  • 修复:容量进入可压缩区间(最小压缩线~压缩线)时,插件通过动态 system-prompt context 主动告诉模型当前容量和区间,模型据此判断「刚完成一个自然节点」时是否记录压缩节点。为控制开销:容量按 5% 桶取整、刚记过节点且容量差 <10 个百分点时不重复提醒、异常/缺服务静默跳过。
  • 工具:mark_stage 改名 mark_compress_point,描述改为「它自己会做什么」:完成明确任务/阶段且认为可安全压缩旧历史时调用;工具返回当前容量并复核区间,太低/已到压缩线会被拒绝且不改历史;不确定时可以调用让工具判断。
  • 清理:删除 0.3.1 遗留的死代码 core/stage.js + actions/stage.js(无任何引用)。

小 bug 修复

  • BUG-005:交接单详情纯文本 → 新增轻量 Markdown 渲染(标题 / 段落 / 有序无序列表 / 粗体 / 行内代码 / 代码块 / 分隔线)。零依赖、不生成 HTML、无 XSS 面;512KB / 白名单 / 只读约束不变。
  • BUG-006:交接单列表只显示最近 20 份 → 列表返回总数,底部显示「共 N 份(显示最近 20)」并提供「加载更多」(上限 50),无更多时隐藏按钮。

内部整理(0.3.3 之后的架构工作)

  • 把 7 处重复实现抽取为共享模块(core/object.js、core/tokens.js、dsh/compaction-pair.js、dsh/model-task.js、actions/support.js、actions/compact-run.js、client/15-shared.js),行为不变,node --check / 构建产物 / 测试全部一致;client bundle 由片段重新构建。

测试

npm test 全绿:smoke 53/53、handoff 35/35、plugin-load 14/14、contract 17/17、compact-log PASS、compress-cue 3 组、markdown-lite 3 组;隔离 DSH + 干净无头 Chrome 的 e2e smoke / settings-ui 通过。

升级提示

  • 升级后重启 DSH 才会加载新代码;file: 挂载的开发环境升级后需要 pnpm install(本次新增了 lib 文件)。
  • 设置栏保存仍然即时生效,不需要重启。

安装

dsh plugin --profile desktop add dsh-compact-relay

npm:https://www.npmjs.com/package/dsh-compact-relay | 仓库:https://github.com/cfanzx/dsh-compact-relay

English

What changed (0.3.4 — compression-point judgment fix + small bug fixes)

Compression-point judgment (BUG-010: the "suitable point to compress" rarely appeared)

  • Evidence: across 427 real sessions (36,813 tool calls), the old mark_stage tool was called 0 times after 0.3.1, and only 1 compress-hint record existed in the whole store (a development test). The tool schema appeared 43 times in session logs — the model could see the tool, it just never called it.
  • Root cause: the old description promised "the system will tell you the current occupancy", but nothing ever told the model. The two conditions (above the minimum line / below the compression line) were therefore not actionable, and the "don't call just to fill a quota" wording further suppressed calls.
  • Fix: when occupancy enters the compressible band (between the minimum compression line and the compression line), the plugin now proactively tells the model the current occupancy and band through a dynamic system-prompt context, so the model can decide whether to record a compression node after finishing a natural milestone. To keep the overhead low, occupancy is bucketed to 5%, the cue is suppressed for 10 percentage points after a node is recorded, and any error or missing service is silently skipped.
  • Tool: mark_stage is renamed to mark_compress_point and its description now states what the tool does: call it after finishing a clear task/phase when the older history can safely be compressed; the tool returns the current occupancy and re-checks the band, rejects when too low or already at the compression line, and never changes history. When unsure, the model can call it and let the tool decide.
  • Cleanup: removed dead code left from 0.3.1 (core/stage.js, actions/stage.js), which nothing referenced.

Small bug fixes

  • BUG-005: handoff-document panel was plain text → a lightweight Markdown renderer (headings / paragraphs / ordered and unordered lists / bold / inline code / code blocks / horizontal rules). Zero dependencies, no HTML generation, no XSS surface; the 512 KB / whitelist / read-only constraints are unchanged.
  • BUG-006: handoff list only showed the latest 20 → the list endpoint returns a total; the panel shows "N total (showing latest 20)" and offers "load more" (up to 50), hiding the button when there is nothing more.

Internal cleanup (architecture work after 0.3.3)

  • Seven duplicated implementations were extracted into shared modules (core/object.js, core/tokens.js, dsh/compaction-pair.js, dsh/model-task.js, actions/support.js, actions/compact-run.js, client/15-shared.js) with no behavior change; syntax checks, the built client bundle and the test suite are all consistent, and the client bundle was rebuilt from its fragments.

Tests

npm test is fully green: smoke 53/53, handoff 35/35, plugin-load 14/14, contract 17/17, compact-log PASS, compress-cue 3 groups, markdown-lite 3 groups; the isolated-DSH e2e smoke and settings-ui suites also pass on a clean headless Chrome.

Upgrade notes

  • Restart DSH after upgrading to load the new code; development setups mounted via file: also need pnpm install (new lib files were added).
  • Settings saved from the Settings page still take effect immediately without a restart.

Install

dsh plugin --profile desktop add dsh-compact-relay

npm: https://www.npmjs.com/package/dsh-compact-relay · Repository: https://github.com/cfanzx/dsh-compact-relay

dsh-compact-relay 0.3.3 — Windows 适配

Choose a tag to compare

@cfanzx cfanzx released this 20 Oct 14:53

中文(主)

更新内容(0.3.3,跨平台补丁版,运行时行为无变化)

  • Windows 适配:完整测试套件(smoke 53 / handoff 35 / plugin-load 13 / contract 17 / compact-log)已在 Windows 11 + Node 24.22 实测全部通过。
  • 测试跨平台修复:handoff 的「目录不可写」用例改用「普通文件当父目录」制造 ENOTDIR(Windows 没有 /dev/null 语义);contract 的交接目录断言按 path.resolve 比较(Windows 上 /tmp 会被解析为 C:\tmp)。
  • 构建脚本:scripts/build-client.mjs 忽略隐藏文件与 macOS 资源分叉(._*),拷贝/打包后的源码在 Windows 上也能通过 --check。
  • 文档:README(中英)与 DEVELOPMENT 注明测试需要 Node 22.15+(node:module.registerHooks);TUTORIAL 常见问题补充「headless / QQ 桥接链路不加载指示灯与命令」;ROADMAP 明确不做桥接链路接管。

安装

dsh plugin --profile desktop add dsh-compact-relay

English

What changed (0.3.3, cross-platform patch; no runtime behavior changes)

  • Windows support: the full test suite (smoke 53 / handoff 35 / plugin-load 13 / contract 17 / compact-log) passes on Windows 11 with Node 24.22.
  • Cross-platform test fixes: the "directory not writable" handoff case now uses a regular file as the parent directory to force ENOTDIR (Windows has no /dev/null semantics); the contract handoff-dir assertion compares path.resolve(...) (Windows resolves /tmp to C:\tmp).
  • Build script: scripts/build-client.mjs ignores hidden files and macOS AppleDouble files (._*), so copied/packed sources pass --check on Windows as well.
  • Docs: README (zh/en) and DEVELOPMENT note that the test suite requires Node 22.15+ (node:module.registerHooks); TUTORIAL FAQ now explains that headless / QQ bridge links do not load the ring or commands; ROADMAP states bridge links are out of scope.

Install

dsh plugin --profile desktop add dsh-compact-relay