dsh-qqbot-community
为 DeepSeek Harness 提供 QQ 官方机器人的接入能力
- Stars
- 1
- Language
- TypeScript
- Created
- Aug 15, 2026
- Updated
- Sep 19, 2026
Introduction
QQ 官方机器人适配器 (dsh-qqbot-community)
为 DeepSeek Harness 提供 QQ 官方机器人的接入能力。本项目由 openclaw-qqbot 插件功能迁移而来。
整体功能未严格测试,部分功能可能不稳定,欢迎反馈问题,或者直接提交 PR。
安装
npm install dsh-qqbot-community
该包以 npm 形式分发,供宿主消费。如需从源码构建/调试,参见下文「接入指南」。
致谢与来源
本插件的核心接入、网关与会话管理逻辑源自 openclaw-qqbot 项目,遵循原项目的 MIT 许可证。在此向原作者与贡献者致谢。
功能特性
基础
- 消息收发:频道
@机器人、群聊@机器人和单聊消息。 - 会话管理:按频道、群或单聊用户创建或恢复独立的 Agent 会话(cwd 对齐持久化 header,工作区分组,归档自动恢复)。
- Agent preset 挂载:每个 QQ 会话在 setup 阶段自动
agentPresets.mount()加入预设(默认standard,用agentPreset配置可换成code/minimal/cordis或任意用户自定义 id,QQ 里还可用/new <id>按会话即时切换 —— 见斜杠命令表),恢复持久会话时沿用 header 中记录的 preset —— 因此 QQ 里的 agent 与 Web UI 拥有同一套工具(bash / 文件 / 代码运行等)和提示词,仅追加qq_send_media/qq_api等 QQ 专属工具。 - 沙箱支持:开启后使用 QQ 官方沙箱 OpenAPI 接入点。
- 网关可靠性:WebSocket 心跳、断线重连退避、会话 RESUME(session_id + seq 持久化,重启不重放事件)。
- @mention 清洗:
<@!openid>→@昵称,机器人自身提及剔除(群/频道消息友好)。 - 引用消息上下文:记录每条消息到持久化引用索引(LRU + JSONL,
~/.dsh/storages/qq-refindex.jsonl);用户回复某条消息时自动解析(ref index 优先,msg_elements兜底)并以[引用消息开始]…[引用消息结束]前缀注入模型上下文。 - 图片理解:入站图片经 DSH attachment 服务持久化为
ImageBlock(模型直接可见);服务不可用或格式不支持(如 bmp)时回退下载到<cwd>/.qq-media/并注入路径。 - 语音转写(STT):优先使用 QQ 自带 ASR(
asr_refer_text);配置stt(OpenAI 兼容接口)时下载语音转写;均不可用时注入占位描述。 - 附件落盘:视频/文件等非图片附件下载到
<cwd>/.qq-media/,路径注入文本,供 agent 文件工具读取。 - 富媒体发送:AI 可通过
qq_send_media工具发送图片/语音/视频/文件(URL / data URL / 本地路径);>10MB 本地文件自动走分片上传(upload_prepare → presigned PUT 并行 → part_finish → complete)。 - 流式回复(C2C):
session/event的assistant/chunk→ QQstream_messages替换模式(全量帧、msg_seq 固定、index 递增、节流);前缀不一致自动合并;失败降级静态消息。 - 出站合并与分段:同一轮多条文本回复按窗口合并(默认 900ms/6s 上限);超长回复按段落边界分段(默认 4000 字/段);剥离
<think>/<system-reminder>等内部标签。 - 被动回复限额:每条入站消息被动回复(带 msg_id)默认上限 4 次、群 5 分钟/C2C 30 分钟窗口,超限自动降级主动消息( RouteStore 持久化,重启后提醒类主动推送仍可路由)。
- typing 指示:C2C 处理期间 60s 输入中状态自动续发(50s 间隔)。
- 访问控制:
allowFrom(C2C openid)/groupAllowFrom(群 openid)白名单,'*'通配,留空放行。 - 审批桥(inline keyboard):为每个 QQ 会话注册 agent 作用域
approval/requestanswerer —— 审批请求以三按钮消息送达 QQ(✅ 允许一次 / ⭐ 始终允许 / ❌ 拒绝),按钮回调即决策;"始终允许"按 会话×工具 持久化(qq-always-allow.json)。 - ask_user_question 转发(
questions,默认开启):agent 调用ask_user_question时,问题转发到 QQ 对话(否则只出现在 Web UI,QQ 侧会一直"无响应")。默认以纯文本呈现编号选项,直接回复编号(如1,3)、选项文字或自由文本(多问题按行回答);questionButtons: true可为 单问题+单选+选项≤5 附加内联键盘按钮(需开通消息按钮权限,沙箱可能不显示,发送失败自动回退纯文本);无效回答有引导提示且不进入 agent;超时(questionTimeoutMs,默认 300s)/turn 取消/会话结束自动收尾。经 agent 作用域tools/execute拦截实现,不影响 Web UI 的其它会话;questions: false关闭。 - QQ API 代理工具:
qq_api工具代理任意 QQ 开放平台 REST 调用(频道/群管理、公告、日程等),自动注入鉴权。 - 斜杠命令:
/help/ping(/bot-ping)/me/new [preset]/presets/sessions/switch <n|#nN|main>/model [<provider>[/<model>]]/compact/stop/status/approve ask|never|status/always clear(在投递给 agent 之前拦截;完整列表见下节)。 - 定时提醒:复用 DSH schedule 子系统(web profile 自带
schedule_create等工具);提醒到期触发同会话 follow-up,回复经出站管线(含主动降级)送达 QQ。 - HTTP 推送 API(
httpApi,默认关闭):在 dsh web 的 HTTP 端口上暴露认证端点,外部系统可把文本直接推送到指定 QQ 通道(不经模型);record: true可同时向会话注入一条不唤醒模型的记录,让 agent 知晓推送内容。详见下文「HTTP 推送 API」。
明确不迁移(平台强绑定或 DSH 已覆盖)
Webhook transport、热升级(/bot-upgrade/update-checker)、/bot-logs//bot-version//bot-clear-storage(DSH Web UI 承担)、pairing 配对流、credential-backup、claw_cfg 私有协议、群自主模式(需 QQ 特批 GROUP_MESSAGE_CREATE)、群历史缓冲注入(DSH 会话日志已持久化完整上下文)。
斜杠命令
在投递给 agent 之前由本插件直接拦截并响应(slashCommands: false 时整组关闭)。所有命令都以行首 / 触发,参数以空白分隔。
| 命令 | 参数 | 说明 |
|---|---|---|
/help | — | 在 QQ 内列出全部可用斜杠命令。 |
/ping(别名 /bot-ping) | — | 返回 QQ→插件总延迟,并拆分网络传输耗时与插件处理耗时;时间戳不可用时回退为 ✅ pong!。 |
/me | — | 返回当前会话发送者的 openid(可选附带昵称),便于排查白名单。 |
/new | 可选 preset id | 强制关闭当前 thread 上的流式回复(stream_messages DONE 帧)并取消进行中的 agent,再为同一会话目标分配下一个 thread id(#n1、#n2 …);旧 session 保留,仍可在侧边栏切换。携带 preset id 时(如 /new code),新会话改用该 preset 组装(先对照 host 的 preset 列表校验,未知/损坏的 id 直接报错并列出可选项,不推进 thread);不带参数则使用 agentPreset 配置值。覆盖按新 session id 持久化(qq-threads.json),重启后恢复同一会话仍用同一 preset。 |
/presets | — | 列出 host 当前提供的全部 agent preset(id、名称、损坏原因),供 /new <id> 选择。 |
/sessions(别名 /threads) | — 或 all | 列出本会话目标最近 3 天的持久化会话:线程编号(#0 主会话 / #n1…)、会话标题(有 session/title 记录时显示标题,否则回退首条用户消息摘录,截断 40 字)、创建时间、事件数、preset,并标记当前线程(接管模式下显示当前接管的会话 id)。数据来自 host 的 sessionPersistence.list(),按会话 id 前缀过滤本目标(C2C 用户 / 群 / 频道)。/sessions all 额外列出可接管的非 QQ 会话(web UI / 其它入口创建,最近 3 天、最多 20 个,带 [序号] 与标题),供 /switch pick 选择。两者均受 switchAllowFrom 权限控制。 |
/switch(别名 /sw) | <n> | <#nN> | main | pick <序号> | id <会话id> | 把当前线程指针切换到指定历史会话(如 /switch 2、/switch n2;main / 0 回主会话 #0),下一条消息即在所选会话的历史上下文中继续;或接管一个非 QQ 会话(/switch pick 3 引用 /sessions all 的序号,列表 5 分钟内有效;/switch id <完整会话id> 直选)——接管后本目标的 QQ 消息进入该会话,沿用其 cwd 与 preset,Web 侧同时使用会共享上下文。切换/接管前自动取消离开会话上正在生成的回复并关闭其 C2C 流;线程切换只能指向已存在的线程,接管目标必须是已持久化的非 QQ 会话。指针持久化于 qq-threads.json(v4),重启不丢;/new 与任何线程切换自动解除接管。受 switchAllowFrom 权限控制。 |
/model | [<provider>[/<model>]] 或 reset | 切换当前线程的 AI 模型(见下节)。无参数时显示当前覆盖、host 全部 provider 及其模型清单、已配置的别名与用法;reset 清除覆盖回到 plugin-config 默认。切换已存在会话时自动开启新 thread(沿用 preset)。 |
/compact | — | 立即压缩当前会话历史(需 host 提供 compaction 服务)。 |
/stop | — | 关闭当前 C2C 流并中止正在生成的回复。 |
/status | — | 查看 Session ID、会话类型与可用的持久化信息。 |
/approve | ask | never | status(缺省 status) | ask/never 切换当前会话的审批策略(仅当 approval: true 且当前环境提供了 approval 服务时生效);status 列出本会话已"始终允许"的工具名。 |
/always | clear | 清空本会话的"始终允许"清单;其它子命令视为未识别并回落到 agent。 |
未识别的命令(例如
/foo)会被原样转发给 agent,不会被插件吞掉。
实现位于 src/slash-commands.ts,仅依赖注入式的 SlashDeps;测试 / 单元化时可直接传入 fake 依赖调用,无需启动 WebSocket。
按会话切换 agent preset:/new <preset> 与 /presets
agentPreset 配置是全局默认,调整它需要改 patch 并重启。这两个命令让你直接在 QQ 里为新会话即时指定 preset,无需任何配置变更。
典型对话:
你:/presets
机器人:可用 agent preset:
- standard(标准)
- code(编码)
- cordis(Cordis 插件开发)
- my-agent(我的模式)
用 /new <id> 以指定 preset 开启新会话
你:/new code
机器人:✅ 已开启新会话(#n3,preset=code)。下次发送的消息将进入 `qq:v2:c2c:ABC123#n3`。旧的对话仍保留,可手动在侧边栏切换。
你:/new ← 不带参数:同样开新会话,但使用 agentPreset 配置的默认 preset
你:/new foo ← 未知 id:报错并列出全部可用 id,thread 不推进,当前会话不受影响
按会话切换模型:/model
会话的 provider/model 在创建时固定(持久化在会话 header 中),因此切换通过"新会话"完成:当前线程已有活跃 agent 时,/model 会像 /new 一样取消旧 agent 并开启新 thread(preset 覆盖沿用),再把 model 覆盖记录到新会话 id 上;没有活跃会话时覆盖直接落在当前 id,下一条消息即用新模型。覆盖持久化在 qq-threads.json,重启不丢。
目录发现是动态的:/model(无参数)通过 host 的 llm 服务(ctx.llm.listProviders() / listModels())列出当前部署实际配置的全部 provider 与模型——插件不做任何路由白名单,settings.yaml 里加了新 provider 即自动出现在清单中。host 未加载 llm 服务时优雅降级为只显示默认值与用法。
别名:modelAliases 配置字段(可选)把短名映射到 "provider" 或 "provider/model",使命令在多 provider 部署下不必敲全名。别名只是输入糖,解析后的参数与手写全名走完全相同的路径。
典型对话:
你:/model
机器人:当前线程无 model 覆盖,使用 plugin-config 默认:`deepseek-official/deepseek-v4-flash`
- deepseek-official:deepseek-v4-flash、deepseek-v4-pro
- zai-coding-cn:glm-5-turbo、glm-5.1、glm-5.2、glm-5.3
别名:zai→zai-coding-cn/glm-5.2,fast→openai/gpt-4o-mini
用法:/model <provider>[/<model>] 或别名;/model reset 恢复默认
你:/model zai
机器人:✅ model 已切到 `zai-coding-cn/glm-5.2`(已自动开启新会话 #n2,preset 沿用),下一条消息即用新模型
你:/model zai-coding-cn/glm-5v-turbo ← 全名直写,任意 provider/model 组合
你:/model reset ← 清除覆盖,回到 plugin-config 默认
会话列表与切换:/sessions 与 /switch
/new 只会前进到新线程,旧会话此前只能在 Web 侧边栏手动找回。这对命令把找回能力放回 QQ 内:/sessions 通过 host 的 sessionPersistence.list() 快照枚举当前会话目标(同一 C2C 用户 / 群 / 频道)下最近 3 天创建的持久化会话;/switch 把线程指针指回其中任意一个,下一条消息即在那个会话的历史上下文中继续(agent 按 header 恢复,preset/model 沿用该会话创建时的记录)。
切换安全:/switch 与 /new 走同一套收尾——取消离开线程上仍在生成的 agent、强制关闭其 C2C 流(避免 QQ 40034021 流占用),指针写入 qq-threads.json 原子落盘,重启不丢。只能切换到已存在的线程(编号不超过当前最高值),切换到尚未有持久化记录的未来线程会被拒绝。
切换权限(switchAllowFrom):会话 id 内嵌 openid,且群聊里一个目标的所有成员共享线程——任意成员切换会改变整个群的对话上下文。因此两个命令都受 switchAllowFrom 配置控制(按发送者 openid 匹配,群聊即成员 openid):
- 留空 / 未配置 /
['*']:所有人可用(默认,与allowFrom语义一致); ['disabled']:全部拒绝;['openid1', 'openid2']:仅列出的发送者可用(openid 可用/me查询)。
典型对话:
你:/sessions
机器人:📋 本会话目标最近 3 天的会话(共 3 个):
- #n2 ← 当前 “帮我重构登录模块” · 创建于 2025/9/19 10:32:00 · 48 条事件 · preset=standard
- #n1 “写一个爬虫脚本” · 创建于 2025/9/18 21:04:11 · 12 条事件 · preset=code
- #0(主会话)“你好,帮我看看这段代码” · 创建于 2025/9/17 09:15:40 · 36 条事件
用 /switch <编号> 切换(如 /switch 2、/switch n2、/switch main 回主会话);/sessions all 查看可接管的非 QQ 会话
你:/switch 1
机器人:✅ 已切换到 #n1。下次发送的消息将进入 `qq:v2:c2c:ABC123#n1`,继续该会话的历史上下文。
你:/switch 9
机器人:⚠️ 会话 #n9 不存在(当前最高为 #n2)。用 /new 开启新会话,或 /sessions 查看列表。
接管 web 创建的会话:/sessions all 与 /switch pick|id
/sessions 默认只看本 QQ 目标自己的线程;/sessions all 则通过 sessionPersistence.list() 列出非 QQ(qq: 前缀之外)的持久化会话——即 dsh web 等入口创建的会话——最近 3 天、最多 20 个,每行带 [序号]、创建时间、事件数、preset、cwd。/switch pick <序号>(列表 5 分钟内有效)或 /switch id <完整会话id> 把当前目标**接管(pin)**到该会话:下一条 QQ 消息进入其历史上下文,agent 按其 header 恢复(cwd、preset、模型沿用该会话的记录);Web 侧若同时使用该会话则共享上下文。接管指针持久化于 qq-threads.json(存储格式升到 v4,v3 及更早自动迁移),/new 或任意 /switch <线程编号|main> 自动解除接管回到线程模式。
边界:其它 QQ 会话目标(别的用户/群)的会话不可接管——其 id 内嵌他人 openid;接管目标必须已持久化。两个命令与线程切换一样受 switchAllowFrom 控制,且群聊中接管影响全体成员,建议显式配置授权名单。
典型对话:
你:/sessions all
机器人:🗂 最近 3 天的其它会话(非 QQ,共 2 个):
- [1] “QQ 机器人插件会话列表设计” · 创建于 2025/9/19 09:41:23 · 128 条事件 · preset=code · cwd=/Users/xxxx/workdir
- [2] “调试 WebSocket 重连” · 创建于 2025/9/18 18:02:10 · 12 条事件
用 /switch pick <序号> 接管所选会话(列表 5 分钟内有效);/switch id <完整会话id> 亦可直选
你:/switch pick 1
机器人:✅ 已接管会话 `9f3a…`。下次发送的消息将进入该会话的历史上下文(沿用其 cwd 与 preset)。
其它 QQ 会话方(如 Web UI)若同时使用该会话会共享上下文;/switch main 或 /switch <编号> 可回到本目标线程。
你:/switch main ← 解除接管,回到本目标线程模式
校验:host 提供清单时,未知 provider 直接拒绝并列出可选项;未知模型予以接受但附加警告——模型 catalog 是建议性的(未列出 id 在部分适配器上原样透传),是否严格校验取决于 provider 路由自身的实现。
接入指南
本包以 DSH bundle 形式分发 —— package.json 声明 dsh.bundle.patch,仓库根的 cordis.patch.yml 是该 bundle 的默认配置层。用户通过 dsh plugin add 一行安装,DSH 自动把它加入 ~/.dsh/profiles/<name>/package.json 的 dsh.profile.bundles 列表,并在下次启动时作为独立一层叠加。
1. 安装到 profile
# 从 npm 安装(正式用户)
dsh plugin --profile web add dsh-qqbot-community
# 从 GitHub / 本地 checkout 安装(开发/调试)
dsh plugin --profile web add github:DLive/dsh-qqbot-community
dsh plugin --profile web add /path/to/dsh-qqbot-community
# 安装后核对:bundle 已被识别为独立一层
dsh --profile web --dump-config | grep -A2 "dsh-qqbot-community"
2. 在 profile 的 cordis.patch.yml 覆盖默认配置
DSH 的层顺序是:每个 bundle 的 patch → profile 的 cordis.patch.yml → home 级 cordis.patch.yml → --patch 覆盖层。后者覆盖前者,所以用户在自己 profile 的 patch 文件里补 QQ 凭证即可,不需要改 bundle。
编辑 ~/.dsh/profiles/web/cordis.patch.yml,在已有内容末尾追加(不要覆盖文件里已有的其它 patch):
- id: qqbot-community
name: dsh-qqbot-community
config:
id: '你的 AppID' # 必须加引号(避免 YAML 数字解析)
secret: '你的 AppSecret'
sandbox: true
provider: 'DeepSeek' # 新建会话默认提供商
model: 'DeepSeek-V4-Flash' # 新建会话默认模型
agentPreset: 'standard' # 新建会话默认 preset id:standard / code / minimal / cordis / 自定义;缺省值 standard。QQ 里可用 /new <id> 按会话覆盖(见斜杠命令表)
cwd: '/Users/xxxx/workdir' # QQ 会话 agent 工作区目录(须真实存在)
# 以下均可省略,以下为默认值
allowFrom: ['*'] # C2C 白名单;填 openid 数组限定用户
groupAllowFrom: ['*'] # 群白名单
markdown: true # msg_type 2;未开通 markdown 权限时设为 false
typing: true # C2C 输入中指示
streaming: true # C2C 流式回复
streamThrottleMs: 1200 # 流式帧节流
deliverWindowMs: 900 # 轮内回复合并窗口
deliverMaxWaitMs: 6000 # 合并最大等待
textChunkLimit: 4000 # 单条静态回复上限
replyPassiveLimit: 4 # 每条消息被动回复上限
mediaDownload: true # 非图片附件落盘 <cwd>/.qq-media/
approval: true # QQ 内联键盘审批
approvalTimeoutMs: 300000 # 审批等待超时
slashCommands: true # /help /ping /bot-ping /me /new /model /sessions /switch /compact /stop /status 等
# switchAllowFrom: ['*'] # /sessions 与 /switch 的发送者 openid 白名单;'*' 通配、留空放行、'disabled' 全拒。
# # 群聊中按成员 openid 匹配——切换影响全体成员,建议群场景显式配置。
# modelAliases: # 可选:/model 命令的短名别名(短名 → "provider" 或 "provider/model")
# zai: 'zai-coding-cn/glm-5.2'
# fast: 'openai/gpt-4o-mini'
# stt: # 可选:语音转写(OpenAI 兼容)
# baseUrl: 'https://api.openai.com/v1'
# apiKey: 'sk-...'
# model: 'whisper-1'
如果你只想临时调试本仓库代码、不走 npm,也可以在不修改 profile 的前提下用
--patch直接喂这份 patch 给 dsh:cd /path/to/dsh-qqbot-community pnpm install && pnpm run build dsh web --patch ./cordis.patch.yml这条路径绕开
dsh plugin的依赖管理,仅适合本地开发。🛠️ 开发模式:clone → build → 以本地
lib/index.js绝对路径挂载到 profile patch 的完整步骤与常见问题,见 DEVELOPMENT.md。
3. 启动
dsh web
启动后自动:获取 token → 建立 WS 网关(RESUME 恢复)→ 收到消息按会话创建/恢复 agent → 回复经出站管线送回 QQ。
QQ 内审批卡片依赖 host 组合
@deepseek-ai/dsh-user-approval服务。标准 Web profile 通常已包含;自定义/minimal profile 若未组合该服务,插件会继续运行,但/approve策略切换与审批卡片不可用。
HTTP 推送 API
在 cordis.patch.yml 中启用 httpApi 后,本插件会在 dsh web 的 HTTP 服务上挂载两个 Bearer 认证端点,供外部系统(CI、监控、脚本)直接向 QQ 通道推送文本:
- id: qqbot-community
name: ... # 同上
config:
... # 同上
httpApi:
enable: true
token: '请生成一个足够长的随机串' # 必填,≥ 8 字符,缺失时插件加载直接报错
# path: '/external/qq' # 可选,默认 /external/qq;多机器人实例各用不同前缀
POST /external/qq/send
{
"channel": "c2c:A2C71F...", // 寻址方式一:简写 c2c:<openid> / group:<openid> / channel:<id>
// 也接受完整会话 id(qq:v2:c2c:XXX#n1,忽略线程后缀)
// "target": { "kind": "c2c", "userId": "A2C71F..." }, // 寻址方式二:对象形式
"text": "要推送的文本", // 必填;超过 textChunkLimit 自动分段
"msgId": "ROBOT1.0_...", // 可选:以该入站消息为被动回复锚点(省略则发主动消息)
"record": true // 可选:向该通道当前会话注入一条不唤醒模型的记录
}
响应:
// 200
{ "ok": true, "messageIds": ["..."], "chunks": 1, "recorded": false }
// 401 未认证 / 400 参数错误 / 502 QQ 发送失败(附 messageIds 已成功部分)
要点:
- 直接推送语义:文本经与 agent 回复相同的
QQApi.sendText路径送达,不创建对话轮次、不触发模型;record: true时才向会话注入[HTTP 推送记录]上下文(agent.inject,不唤醒 driver),用户下次提问时模型可据此回答。目标是从未出现过的新通道时,record会按插件配置为其创建新会话。 - 主动消息频控:不带
msgId的推送是主动消息,受 QQ 平台主动消息额度限制(C2C 每月限额、群更严)。高频通知场景建议借用msgId(如用户刚与机器人交互后的消息 id)走被动额度。 - 认证是强制的:dsh web 的请求防线(Host fence)不是认证层,本机任何进程都能访问该端口;
token必填且所有请求须携带Authorization: Bearer <token>。若 webserver 绑定0.0.0.0,这是唯一防线。
GET /external/qq/channels
列出所有路由过的通道(供调用方发现 channel 寻址值):
{ "ok": true, "channels": [
{ "kind": "c2c", "id": "A2C71F...", "target": { "kind": "c2c", "userId": "A2C71F..." },
"currentSessionId": "qq:v2:c2c:A2C71F...#n5", "lastActiveAt": 1786792729138 }
] }
验证
curl -X POST http://127.0.0.1:3080/external/qq/send \
-H 'Authorization: Bearer <token>' -H 'Content-Type: application/json' \
-d '{"channel":"c2c:A2C71F...","text":"hello from CI","record":true}'
开发时可运行 node scripts/smoke-http-api.mjs(stub QQApi 的本地端到端冒烟,16 项断言)。
注意事项
- 权限:默认 intents 同时订阅频道 @、群 @、单聊与按钮交互(INTERACTION)。审批按钮需要开通「消息按钮」能力。
- markdown:
markdown: true需在 QQ 开放平台申请 markdown 模板权限,否则发送失败。 - 审批链路:QQ answerer 仅在会话审批策略为
ask时收到请求(never直接拒绝);/approve never关闭审批后所有 ask 确定性拒绝 —— 与 DSH 审批语义一致。 - 流式与 markdown:
stream_messages帧固定content_type: markdown,与markdown配置独立(QQ 流式接口本身就是 markdown 渲染)。 - 工作目录:
cwd必须是已存在的绝对路径(workspaceRegistry.create会fs.realpath校验);已恢复会话保留原 cwd。 - 运行时产物:
~/.dsh/storages/qq-{routes,gateway-session,always-allow,threads}.json、qq-refindex.jsonl、<cwd>/.qq-media/;删除后自动重建(qq-threads.json同时承载 thread 计数与/new <preset>的会话级覆盖,删除后所有目标回到 thread 0 与配置默认 preset)。
故障排查
- 收不到消息:
debug: true查看网关 op 帧;确认 intents 与 QQ 平台消息权限审核状态。 - 回复没到:查看被动限额日志 —— 超限自动转主动消息,QQ 对主动消息有频控。
- 图片模型看不到:确认 DSH 挂载 attachment 服务(web profile 默认有);不支持格式自动回退为路径注入。
- 审批按钮无响应:确认开通按钮权限;
/approve status查看始终允许清单;超时默认 5 分钟自动拒绝。 Error: duplicate loader entry id: qqbot-community:合并后的 entry 树里出现了两个同 id 的 row。常见原因有两个:(1) 用户cordis.patch.yml里写了- insert: [{ id: qqbot-community, ... }](应该改成- id: qqbot-community, config: {...}替换形式);(2) 旧file://形式的 row 没清掉。执行dsh --profile web --dump-config | grep "id: qqbot-community"应该只看到一行;多于一行就重复了。- QQ 里只有基础对话、bash 等工具全无:说明 QQ 会话没有挂上 agent preset —— DSH 会打印
agent "qq:..." was published without joining an agent preset。本插件默认会调agentPresets.mount('standard'),但要求 host 上有dsh-agent-presetsrow(web / cli profile 自带,自定义 profile 需手动加载)。要换 preset 时在 patch 里设置agentPreset: 'code'(或自定义 id),或在 QQ 里/new code即时开一个用该 preset 的新会话(/presets可列出全部可选项);已有会话恢复时沿用 header 里记录的 preset,重启dsh或/new会用新值。 /new <id>报"未知的 preset":id 必须精确匹配/presets列出的 id(大小写敏感)。自定义 preset 放在${DSH_HOME:-~/.dsh}/.agent-presets/<id>/cordis.yml,新建后无需重启即可被/presets发现(名单每次实时读取)。
更新日志
见 CHANGELOG.md。