DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

dingchenhui0618-arch /

dingchenhui0618-arch/dsh-wechat-draft-gate

Verified

Outbound approval gate for WeChat replies in DeepSeek Harness: every reply lands in a local draft box; only a panel confirmation or /send releases it.

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@93d96cdf

dsh-wechat-draft-gate

CI license

给 DeepSeek Harness 的微信出站闸门:Agent 写好的回复先落进本机「待发草稿箱」,保留引用原文与语音转写来源/置信度标注,允许人工改稿;只有面板点「确认发送」或在会话里发 /send <id> 才会离开本机。没人确认的草稿到点自动作废,并写清原因。

微信是对外真人渠道。一次误发无法撤回,所以这个插件只做一件事:把「生成回复」和「真正发出」拆成两个阶段,中间必须有人点一下。

灵感来源与原版

这个项目受 panqing0313/dsh-plugin-weixin 启发:它把个人微信接成 DSH 的远程入口,覆盖收发、图片文件、语音转写、/new /list /use 多会话,是一套完整、成熟的通道。

本项目独立实现,不是 fork,也没有复制它的代码。两者定位不同:

维度 原版 dsh-plugin-weixin 本项目 dsh-wechat-draft-gate
定位 个人微信 ↔ DSH 的双向通道 只做出站审批的闸门
回复路径 按原版 README:微信里说一句 → Agent 执行 → 把结果回给你 回复先入草稿箱;没有自动直发路径
草稿/确认 原版有 /draft(重要日子提醒的问候草稿,36 小时过期),属于特定功能 所有出站回复统一进闸门,人工确认后才发;默认 30 分钟未确认作废
语音标注 入站语音转文字后作为用户原话交给 Agent 草稿额外保存转写来源、模型、置信度;低于阈值在面板标黄
引用原文 原版支持引用类消息(README 提到 refermsg 监听) 草稿固定保存被回复的原话:类型、发送者、时间
发送实现 接微信 iLink Bot API 不接微信;确认后 POST 给一个你配置的桥接端点,没配就是演练模式
范围 收发、媒体、ASR、多账号、记忆、群聊等 故意只做闸门,保持小而可审计

原版的完整能力值得直接使用;如果你要的是「所有回复先审后发」这一道闸,这个项目只补这一块。

说明:上表中关于原版的描述来自其公开 README(2026-11 读取)。原版仍在演进,以其仓库当前文档为准。

两级审批

微信消息 ──> DSH 会话 ──> Agent 回复
                              │
                              ▼
                    ┌──────────────────┐
                    │  待发草稿箱       │  第 1 级:自动落草稿
                    │  引用原文         │  可编辑、可作废
                    │  语音来源/置信度  │  默认 30 分钟过期
                    └────────┬─────────┘
                             │ 第 2 级:人工确认
                 ┌───────────┴───────────┐
                 ▼                       ▼
          面板「确认发送」          /send <id>
                 └───────────┬───────────┘
                             ▼
                   POST 桥接端点 ──> 微信

没有第三条发送路径。插件不会因为「回复写完了」「超时了」「重试了」而自行发出任何消息。

安装

dsh plugin --profile <你的 profile> add dsh-wechat-draft-gate

或从 GitHub 装:

dsh plugin --profile <你的 profile> add github:dingchenhui0618-arch/dsh-wechat-draft-gate

装好后重启 DSH,对话视图里出现「待发」Tab。

快速验证(不接微信)

插件默认是演练模式:确认动作会被完整记录,但不会发出任何网络请求。先跑一遍状态机:

node examples/bridge-stub.mjs          # 另开一个终端;不接微信,只收 POST 并写文件

在 cordis.patch.yml 里把 transportUrl 指向它:

- insert:
    - id: dsh-wechat-draft-gate
      name: dsh-wechat-draft-gate
      config:
        draftTtlMinutes: 30
        lowConfidenceThreshold: 0.75
        transportUrl: http://127.0.0.1:47832/wechat/send
        # 不要把 token 写进仓库;用环境变量:
        transportTokenEnv: WECHAT_GATE_TOKEN

然后从本机模拟一条待发草稿:

curl -X POST http://127.0.0.1:3080/dsh-wechat-draft-gate/stage ^
  -H "content-type: application/json" ^
  -d "{\"chatId\":\"wxid_alice\",\"chatName\":\"Alice\",\"text\":\"明天下午三点见。\",\"quotedText\":\"我们明天见吧?\",\"quotedSender\":\"Alice\",\"quotedKind\":\"voice\",\"voiceSource\":\"local-whisper\",\"voiceModel\":\"small\",\"voiceConfidence\":0.86}"

打开「待发」Tab:能看到引用原文、语音来源与置信度、可编辑的正文。点「确认发送」后,bridge-stub 会打印并保存完整的出站载荷。

面板

待发面板:引用原文、语音来源/置信度、可编辑正文与确认按钮

  • 待确认:接收人、剩余时间、引用原文、语音来源/模型/置信度、可编辑正文、逐条错误提示
  • 低置信度:低于阈值时标黄「请核对原文」,但不阻止发送——决定权在人
  • 处理记录:已发送 / 已作废 / 已过期,附确认方式、时间、失败次数与作废原因
  • 演练模式横幅:没配 transportUrl 时常驻提示「确认只记录,不会真的发出」

命令

命令 作用
/send <id> 确认发送一条草稿;id 在「待发」面板可见
/drafts 列出待确认草稿(不发送)

两个命令都不消耗模型对话。

Agent 工具

wechat_stage_draft:让 Agent 把一条回复放进草稿箱,参数包括 chatId、text、quotedText、voiceSource、voiceConfidence 等。工具描述明确告诉模型:这只是入草稿,不是发送。工具返回草稿 id 与过期时间。

桥接契约

入草稿(本机 → 插件)

POST /dsh-wechat-draft-gate/stage

{
  "chatId": "wxid_alice",
  "chatName": "Alice",
  "text": "要发出的回复",
  "quotedText": "对方原话",
  "quotedSender": "Alice",
  "quotedKind": "text | voice | image | file | unknown",
  "quotedAt": 1699999000000,
  "quotedMessageId": "msg-1",
  "voiceSource": "local-whisper",
  "voiceModel": "small",
  "voiceConfidence": 0.86,
  "voiceTranscript": "对方原话的转写"
}

返回 201 {ok:true, draft}。其余接口:POST /edit(id、text)、POST /confirm(id、可选 text 原子改稿后发送)、POST /discard(id、reason)、GET /state。

发送(插件 → 你的桥)

确认后,插件向 transportUrl 发一次 POST:

{
  "version": 1,
  "kind": "wechat.outbound",
  "draftId": "wg_...",
  "idempotencyKey": "draft:wg_...",
  "chat": { "id": "wxid_alice", "name": "Alice" },
  "text": "最终确认的正文",
  "quoted": { "text": "...", "sender": "Alice", "at": 1699999000000, "kind": "voice", "messageId": "msg-1" },
  "voice": { "source": "local-whisper", "sourceLabel": "本地 Whisper", "model": "small", "confidence": 0.86, "threshold": 0.75, "lowConfidence": false, "transcript": "...", "note": "" },
  "origin": "bridge",
  "stagedAt": 1699999000000,
  "expiresAt": 1699999900000
}

请求头带 idempotency-key: draft:<id>(桥应按它去重)、x-dsh-draft-gate: 1,以及配置了 token 时的 authorization: Bearer <token>。桥返回任意 2xx 即视为送达;返回 {"messageId":"..."} 会把回执记进草稿。非 2xx 或超时 → 草稿保持待确认、记录失败次数,可重试。

examples/bridge-stub.mjs 是一份 20 行左右的参考实现,只落盘,不接微信。

配置

字段 默认 说明
draftTtlMinutes 30 草稿未确认的存活分钟数
draftTtlMs 空 直接给毫秒;设置后覆盖 draftTtlMinutes(测试用)
lowConfidenceThreshold 0.75 低于此值的语音置信度标黄
sweepIntervalMs 15000 过期扫描间隔;每次读取状态时也会先扫描
historyLimit 200 面板最多返回多少条处理记录
maxDrafts 500 草稿箱上限,超出时从最旧的已处理草稿开始清理
transportUrl '' 桥接端点;留空 = 演练模式
transportToken '' 直接给 token(不推荐入库)
transportTokenEnv '' 从环境变量读 token(推荐)
transportTimeoutMs 10000 发送超时
stateDir $DSH_HOME/wechat-draft-gate 草稿与审计文件目录

过期与审计

草稿箱是 drafts.json(临时文件 + rename 原子替换)。每次状态变更另追加一行 JSONL 到 ledger.jsonl:

{"at":...,"action":"staged","id":"wg_...","chatId":"wxid_alice","status":"pending"}
{"at":...,"action":"expired","id":"wg_...","chatId":"wxid_alice","status":"expired","expiresAt":...}

action 取值:staged / edited / sent / send-failed / voided / expired。过期记录的 status 与 expiresAt 都在,原因同时写在草稿的 voidReason.detail(例如「超过 30 分钟未确认,自动作废」)。

drafts.json 损坏时会被改名为 drafts.json.corrupt-<时间戳> 保留现场,草稿箱从空开始,不阻塞插件加载。

语音置信度的诚实边界

插件不计算置信度。 它只接收桥接端给出的数字并做标注:

  • voiceConfidence 接受 0-1,也接受 1-100(会自动除以 100);
  • 非数字或越界 → 置信度记为 null,面板显示「未提供置信度」,不会猜一个值;
  • 低于阈值只标黄提示,不拦截发送;
  • 来源名未知时保留原始字符串,不硬套成「本地 Whisper」。

转写文本与引用原文分别保存,面板会同时展示(相同时不重复)。

它不做什么

  • 不登录微信。没有扫码、没有 iLink/协议调用、没有账号凭据。
  • 不做入站。收消息、媒体下载、ASR、会话映射都属于桥接端;本插件只接收已经整理好的回复。
  • 不做自动放行。没有「白名单联系人自动发」这种开关——那会把唯一的闸门绕过去。
  • 不绕过 DSH 权限层。它只是出站链路的一环,不改变工具审批、文件权限或沙箱策略。

安全边界(端口暴露、token 存放、状态目录权限)见 SECURITY.md。

测试

npm test        # node --test,零依赖
npm run build   # 从 src/ 生成 lib/(CI 会检查 lib/ 与 src/ 一致)

测试覆盖:草稿状态机、过期与重载过期、确认/作废/重试、语音置信度归一化、落盘与损坏恢复、审计行、HTTP 路由、/send /drafts、Agent 工具、真实本地桥接 POST、超时、环境变量 token、卸载清理。

兼容性

宿主契约来自实测的 DSH 0.2.x:webServer.register({kind:'prefix'})、ctx.tools.register、ctx.commands.register、conversation.view 槽位。宿主升级后如果 Tab 不出现,先看 DSH 控制台里的插件加载错误。

许可

MIT。代码为独立实现;原版项目仅作为方向参考,其代码未复制进本仓库。

—/ 5

No ratings yet

Verified DSH bundle

Commit 93d96cdfdbd4

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout