• v1.4.1 0cf8a84f1f

    wqmll released this 2026-10-19 12:29:24 +08:00 | 0 commits to main since this release

    1.4.1 — 修「装完就用不了」的 cwd 变量崩溃

    症状

    装完插件、还没点「应用」,每一轮对话都失败:

    unknown prompt variable "{{cwd}}" in section "deployment:persona-suffix";
    registered variables: provider, model
    

    根因

    0.2.1 的 dsh-agent-loop 只注册两个提示词变量:

    ctx.systemPrompt.variable("provider", …)
    ctx.systemPrompt.variable("model",    …)
    

    cwd 在 0.1.x 还有,0.2 拿掉了。而宿主的 interpolate() 遇到未注册变量不是留空,
    是抛异常:

    if (!Object.hasOwn(variables, name)) {
      throw new Error(`unknown prompt variable "{{{name}}}" in ${kind} "${input.name}"; …`)
    }
    

    旧版 cordis.patch.yml 给 system-prompt 配了:

    personaSuffix: Your working directory is {{cwd}}.
    

    deployment:persona-suffix 是每轮都参与渲染的段,所以这个错在插件一装上就发生,
    与是否点过「应用」无关——这就是「基本上下载安装后都有」的原因。

    修法(三层,逐层收窄)

    层 文件 做法
    1. 配置 cordis.patch.yml personaSuffix 改为 ""。persona 不再发任何变量;工作目录由宿主 runtime context 提供,persona 里重复一遍没有收益。
    2. 注册 lib/cwd-variable.js(新) 插件在 systemPrompt 全局层补一个 cwd provider,取值优先级 agent.options.cwd → session.header.cwd → process.cwd()。这样用户自己 preset 里写的 {{cwd}} 也能解析。宿主已注册同名变量时抛 already registered,按成功处理。
    3. 兜底 lib/identity.js ① overlayOfficialIdentity 写出的 persona-suffix 改成纯文本;② 旧的 Working directory: {{cwd}}. 剥离后不再留变量;③ sanitizeAssemblySections 把任何残留的 {{cwd}} 降级成 { {cwd} }——变量注册失败时也不会炸整轮。

    关键取舍:不依赖「宿主会留空未注册变量」。三层里任何一层单独生效都不会崩,
    三层齐全时 {{cwd}} 正常解析出真实路径。

    验证

    npm run test:cwd(test/cwd-variable.mjs,21 项)直接加载宿主真实的
    dsh-system-prompt 渲染器,逐条断言:

    • 旧配置在 provider/model 两个变量下确实抛错,且报错文本与用户反馈一致(复现)
    • 当前 cordis.patch.yml 的活动配置里没有 {{cwd}}
    • overlayOfficialIdentity 的产物能渲染通过,输出里没有裸 {{cwd}}
    • registerCwdVariable 注册后 {{cwd}} 解析出真实路径(含三条 fallback)
    • 重复注册不算失败
    • 转义后的文本能被宿主渲染(证明兜底不是装饰)

    找不到宿主模块时,依赖渲染器的断言会显式 skip,不会假装通过——上一版正是
    这种「看起来没问题」的验证方式漏掉了这个 bug。



    镜像说明 / Mirror note:dsh-purge-plus 是社区分支(JeffKeeping/dsh-purge-plus),不在已删除的 YuJunZhiXue/dsh-purge 仓库内,原仓库没有发布过 GitHub Release。本 Release 由镜像仓库 wqmll/dsh-purge-plus 依据 tag 生成,说明文字取自该分支的 CHANGELOG.md。

    资产按上游 CI 相同方式生成:dsh-purge-plus-1.4.1.zip 是 git archive 源码包(可直接 dsh plugin install 离线安装);dsh-purge-plus-1.4.1.tgz 是内容等价的 npm 包。

    Downloads
  • v1.4.0 5511091cd3

    wqmll released this 2026-10-18 23:27:25 +08:00 | 0 commits to main since this release

    1.4.0 — 借 IG5 的做法加固四处工程细节

    参考 Minglink/dsh-infinite-gen-5 的 source/json_output.js、source/attachment_lease.js、审批门与 CAS 修订号模式,挑了四处与本插件能力相符的改进。不搬它的逆向引擎、worker 协议、Ghidra / x64dbg 适配层——那些属于另一类插件。

    新增:严格 JSON 输出边界(lib/json-safe.js)

    宿主会在渲染前校验工具结果。旧实现全程 JSON.stringify,三种情况会出事:

    情况 旧行为 现在
    BigInt(如 64 位地址被算成 BigInt) 抛异常 → 整个响应 500 投影阶段报 INVALID_PLUGIN_OUTPUT 并指出路径
    循环引用 抛异常 → 500 同上,带 at 定位
    字段值 undefined 静默丢字段 → 界面以为「没数据」 省略该字段但保留 null;不可靠值转成 {__invalid,__reason,__at}

    另外拒绝 -0、NaN、Infinity、Symbol、Function、稀疏数组、带额外属性的数组、accessor、非普通对象(class 实例)。深度上限 64、节点上限 20 万,超限报错而非截断。64 位地址保持字符串精度。

    sendJson 全部改走 stringifySafe —— 永不抛,坏字段变成可诊断标记,而不是把 200 变成 500。

    新增:文件身份与写入守卫(lib/host-lease.js)

    改宿主文件之前要先确认「我改的还是我看到的那个文件」。旧实现只比路径字符串,三种情况会被骗:

    1. 目标被换成符号链接 / 目录联接 → 看到的是另一份文件;
    2. 同一路径下文件被替换(重装、换版本)→ 路径对、内容不对;
    3. Windows 路径大小写不敏感,C:\App 与 c:\app 会被当成两份。

    现在写前取一份 identity(真实路径 + size + mtime + 前 64 KiB 的 SHA-256),写完再取一次,不一致报 changed / vanished。另附 writeFileAtomic(同目录临时文件 → rename),保留原文件 mode。

    新增:审批档位与审计流水(lib/tool-gate.js)

    purge_apply / purge_revert / purge_uninstall 三个破坏性工具以前没有任何「这次调用批没批」的记录。现在:

    • 审计永远记:$DSH_HOME/dsh-purge/audit.jsonl,每次调用一行(intent / allow / deny / result),失败也记;审计写不进去绝不影响工具结果。
    • 审批可选,approvalMode 两档:
      • audit(默认):只记流水,行为与旧版完全一致,不弹任何东西。
      • enforce:走宿主 approval.request,只有明确 allowed-once 才 next()。缺 agent、缺 approval 服务、抛错、未知结果、signal 已 abort —— 一律 deny 不派发,从不缓存上一次批准。

    默认不是 enforce 的原因:本插件的主入口是人自己点「应用」,人就是审批者,再弹一次只是多一次点击;而本机策略可能把 approval 一律自动拒绝,强制审批等于把插件锁死。需要更严时(模型自动跑长任务)再切 enforce。

    • 新工具 purge_audit:读回审计尾部,让模型在长任务里能自查「上一次 apply 到底批没批、结果如何」。
    • 设置页新增「审批与审计」区块:单选档位 + 展开查看流水(.dshp-audit,等宽、限高滚动)。

    新增:状态修订号(lib/revision.js)

    设置页每 5 秒轮询 /status,Web 与桌面可能同时开着。旧实现里 apply / revert 无条件执行,后点的会静默覆盖先点的结果。

    现在状态机有单调递增的 rev:/status 带出,动作时带回,不一致返回 409 + 当前 rev + 最后一次动作。客户端收到 409 提示「另一个窗口刚改过状态」并自动重新拉取,而不是留着旧数字让用户重复点击。不持久化——重启后归零是正确语义。

    修复:hooksDenyBypass 保存时抹掉同文件其他键

    saveHookDenySettings 原来只重建 hooksDenyBypass 一个键再覆盖整文件。approvalMode 住进同一个 hooks-deny.json 后,切一次 hooks 开关就会把审批档位悄悄重置。改成合并写入(实测:approvalMode 与 hooksDenyBypass 同时保留)。

    优化:请求体错误分类

    readJsonBody 原来只抛一句 Error,所有异常都压成 500,界面无法区分「内容太大」和「JSON 写错了」。现在按 BODY_TOO_LARGE(413)、BODY_INVALID_JSON(400)、BODY_NOT_OBJECT(400)分类,19 处 catch 统一走 bodyErrorStatus。

    新增接口

    路由 方法 作用
    /dsh-purge/approval-mode GET / POST 读/写审批档位,重启后生效
    /dsh-purge/audit GET 读审计尾部(?limit=1..200,默认 40)

    /dsh-purge/status 新增 rev、last_action、approval_mode 三个字段。

    测试

    71 项离线断言(json-safe 20 / host-lease 13 / revision 11 / tool-gate 27),不启动宿主、不碰真实工程。



    镜像说明 / Mirror note:dsh-purge-plus 是社区分支(JeffKeeping/dsh-purge-plus),不在已删除的 YuJunZhiXue/dsh-purge 仓库内,原仓库没有发布过 GitHub Release。本 Release 由镜像仓库 wqmll/dsh-purge-plus 依据 tag 生成,说明文字取自该分支的 CHANGELOG.md。

    资产按上游 CI 相同方式生成:dsh-purge-plus-1.4.0.zip 是 git archive 源码包(可直接 dsh plugin install 离线安装);dsh-purge-plus-1.4.0.tgz 是内容等价的 npm 包。

    Downloads