Leeminjing / dsh-messages-sanitizer

Listed

DeepSeek Harness plugin: auto-repairs invalid tool_calls/tool message pairing in the messages array to stop 400 INVALID_REQUEST session crashes.

mainToolSession View source

Installation

npx -y @deepseek-ai/dsh plugin --profile web add github:Leeminjing/dsh-messages-sanitizer

This installation command is an unverified starting point generated from the GitHub repository address.

README

Maintainer-authored documentation snapshot.

View on GitHub ↗
Commit 9f8578eSynced Aug 18, 2026

dsh-messages-sanitizer

English

🔧 你在 DeepSeek Harness 里创建 / 加载插件,对话崩了吗?本插件专治这个。

开发或加载本地插件时,一次工具调度崩溃(Cannot read properties of undefined (reading 'prepare'))就会在会话里留下孤儿 tool_calls,之后每一轮都被 400 INVALID_REQUEST 拒绝、重试无效、会话卡死。本插件自动把 messages 数组修回 合法,让对话继续,不再卡死。

💥 Before                                ✅ After (安装本插件后)
插件崩溃                                  插件崩溃
   ↓                                        ↓
留下孤儿 tool_calls                       messages 自动修复
   ↓                                        ↓
400 INVALID_REQUEST 死循环                对话继续
   ↓
对话卡死

DeepSeek Harness 消息数组自动矫正插件:防止一切因 messages 数组不合法导致的聊天崩溃

背景:你遇到的那个崩溃

OpenAI 兼容协议要求工具调用成对出现,且 tool 消息必须紧邻其 assistant tool_calls 消息(中间不能插入任何 user / assistant 消息):

assistant  { content: ..., tool_calls: [{ id: "call_A", ... }] }
tool       { tool_call_id: "call_A", ... }   ← 必须紧跟覆盖每个 id

当一次工具调度在「记录 assistant tool_calls / tool/call 之后、产出 tool 结果之前」 崩溃时(例如 ctx.tools[symbol].prepareCannot read properties of undefined), 会话日志会留下一个没有 tool 消息响应的孤儿 tool_calls。下一轮请求把历史拼成:

[..., assistant{tool_calls:[write]}, user{...}]        ← 非法

API 直接返回 400 INVALID_REQUEST,且重试时历史原封不动,反复被拒,会话卡死。 如果崩溃后还有多次失败重试,日志里还会留下多条重复的 user 消息横在孤儿 assistant 与注入点之间,让"补插 tool 消息"也无法满足紧邻约束。

插件如何修复(三层防御)

  1. 预防(agent/pre-step,主路径):追踪每个会话中「已声明但从未被 tool/result 响应」的调用;仅当其声明所在的 assistant 消息就是 surface 的最后一个节点时 (新崩溃的典型形态),在模型请求构建前把合成 error tool-result 消息插到该步消息 最前面。合成消息随 decision.messages 以 user/message 事件落盘, deriveMessages() 从根上恢复合法——循环构建的请求(deep-frozen、不可改写) 自然就合法了,从源头杜绝 400

  2. 治愈(agent/request-error):若 API 仍因 tool_calls 配对/紧邻违规返回 400 (例如旧版本已污染的会话、或孤儿 assistant 后面已横着过期消息),用 surface 替换完成修复,然后强制重试一次(重试基于修复后的日志重建请求,一次成功):

    • 把悬空 assistant 消息改写成不含 tool_calls 的版本(剥离无响应的调用);
    • 把孤儿 tool 消息(无前置 tool_calls 的 tool-result)中和成纯文本 user 消息;
    • 恢复被误剥但结果仍紧邻的 assistant(还原 tool_calls,保留历史工具上下文);
    • 折叠崩溃重试留下的重复 user 消息。 修复幂等:第二次遇到同一违规时无事可做,自然回退下游策略,不会无限重试。
  3. 兜底(llm/stream):对每个请求做纯数组矫正(配对 + 紧邻重排 + 孤儿/重复 丢弃 + 空 assistant 丢弃)。循环构建的请求是冻结的,只告警不改写; compaction、session-title 等自建 messages 的非冻结请求直接原地替换。

安装

dsh plugin --profile web add github:Leeminjing/dsh-messages-sanitizer

重启 harness 即生效(插件随 profile 层栈自动加载)。

配置

类型默认说明
enabledbooleantrue总开关

停用方式:删除 cordis.patch.yml 中的插入行,或改为:

- insert:
    - id: messages-sanitizer
      name: 'dsh-messages-sanitizer'
      disabled: true

验证

cd dsh-messages-sanitizer
node --test        # 40 个用例:纯函数矫正 + 会话追踪 + 请求失败修复 + 真实 cordis/Session 集成

测试覆盖(均用真实 @deepseek-ai/dsh-session 的 foldSurface / Session 校验):

  • 真实崩溃序列端到端:assistant/message{tool_calls}tool/call → 崩溃 → step/endturn/end error → 下一回合注入后 wire 合法;
  • 真实污染日志(孤儿 + 过期重复 user 消息)经修复后纯 user/assistant,无任何 tool 消息,且修复幂等;
  • 真实 Session 上执行 surface 替换(通过 Session 自带校验);
  • 请求失败修复只在 tool_calls 配对 400 时干预、修复后强制重试一次、且不会无限重试。

目录结构

dsh-messages-sanitizer/
├── package.json      # 声明 dsh.bundle(dsh plugin add 的安装入口)
├── cordis.patch.yml  # bundle 补丁层(挂载 messages-sanitizer)
├── LICENSE
├── README.md
├── README.en.md
├── lib/
│   ├── index.js      # 插件入口(name / inject / Config / apply)
│   ├── sanitize.js   # 纯函数消息数组矫正器(配对/紧邻/孤儿/重复/空消息)
│   └── repair.js     # 孤儿追踪 + 预步预防 + surface 替换治愈 + 请求失败强制重试
└── tests/
    ├── sanitize.test.mjs            # 纯函数矫正用例
    ├── repair.test.mjs              # 追踪器 + 预步修复 + 请求失败修复(假 ctx)
    ├── integration.test.mjs         # 真实崩溃序列端到端模拟
    ├── heal.test.mjs                # 治愈路径:正常回合不误判 / 孤儿中和 / 误剥恢复
    └── cordis-integration.test.mjs  # 真实 cordis + 真实 Session 集成

说明

  • 插件是零构建的纯 ESM,直接可被 cordis 加载器加载;运行期依赖 @deepseek-ai/dsh-llm(合成消息)、@deepseek-ai/dsh-session(surface 折叠)、 @deepseek-ai/cordis@deepseek-ai/schemastery(配置 schema),与 harness 运行时同源。
  • 已崩溃的旧会话在重启后继续聊天时,会被治愈路径自动修复(第一次请求失败时 自动剥离悬空调用并重试成功)。
  • 本目录下的 node_modules 是一个指向 harness 运行时 ~/.dsh/profiles/node_modules 的 junction,仅为本地 node --test 提供依赖解析;harness 运行期不依赖它。
  • 修改插件代码后无需重新构建;重启 harness(或让 cordis HMR 重载)即生效。

Project files and signals

Shown items are public repository signals detected in the directory snapshot.

TestsDetected

Repository information

Language
JavaScript
License
MIT
Latest release
v0.1.0
Last updated
Aug 15, 2026, 5:03 PM

Install deliberately

Review source code, permissions, lifecycle hooks, dependencies and network access. Test untrusted plugins in an isolated environment.