# 变更记录 本文件记录每个**打过标签**的版本。 格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/), 版本号遵循[语义化版本](https://semver.org/lang/zh-CN/)。 **怎么钉版本**(避免"装到一半上游改了"): ```sh dsh plugin --profile <你的profile名> add github:eighteentang/dsh-plugin-im-bridge#v1.2.7 ``` --- ## [1.2.7] - 2026-10-14 ### 修复 - **QQ 连接静默死亡:界面显示「已连接」,实际可能已经断了好几个小时** 用户早上在 QQ 里发消息,收到「灵魂不在线,机器人已下线」, 而插件这边 `/status` 报的却是 `connected: true` —— 查日志才发现它**已经 7.6 小时不知道自己死了**: QQ 侧最后一次有动静是昨晚 23:43,之后日志里关于 QQ 的记录**一条都没有** (连"断开"都没有),而「断开 / 重连 / 错误」在全历史日志里是 **0 条**。 两个原因叠加: | | 问题 | 后果 | |---|---|---| | ① | 电脑睡了一夜 → 对端把 TCP 关了,但**本机收不到 `close` 事件**(TCP 的经典行为)| `close` 处理器从不触发 → 重连从不发生 | | ② | 心跳是「**只发不验**」的:`if (readyState === OPEN) send(...)` | 僵尸连接上 `readyState` 永远是 OPEN、发送失败也不抛错 → 没有任何机制能发现它已死 | **修法(四件事,一起做才闭环)**: - **心跳 ACK 判活**:官方协议里 `op 1` 心跳会得到 `op 11` Heartbeat ACK ([官方文档](https://bot.q.qq.com/wiki/develop/api-v2/dev-prepare/interface-framework/event-emit.html) opcode 表:「当发送心跳成功之后,就会收到该消息」)。 原来代码里**没有 `case 11`** —— 心跳没人签收。 现在发一次计数 +1、收到 ACK -1,连续 3 次没 ACK 就判定连接已死、 **主动 `close()`** 走重连。判死容忍 2 次,避免网络抖动误杀。 - **断线时状态如实上报**:`close` 时把状态改成「连接中…」, 不再停在「已连接」骗人。新增 `onStatus` 回调直连设置页/面板。 - **补上 `op 6 Resume`**:官方原文「断开之后短时间内重连会**补发中间遗漏的事件**」。 原来没实现 —— 睡眠/断网那段时间别人发的消息会**永久丢失**。 现在重连带上 `session_id` 与 `seq`,让网关补发;Resume 失败退回 `op 2 Identify`。 - **`op 9` 鉴权失败不再卡死**:原来只把 `token` 置 null 就什么都不做, 那条连接就此永久停住。现在清掉会话状态、刷新 token、主动断开重连; 且每次重连都会重新 `ensureToken()`,**睡一夜 token 过期也能自愈**。 另外两处顺手修的: - 重连原来是固定 5 秒且**没有防抖** → 反复失败会叠出多条重连链; 现在单个定时器 + 指数退避(5s→10s→20s→30s 封顶),连上即归零。 - `case 10`(Hello)里发握手那份代码**没有 try/catch** → socket 不在 OPEN 状态时异常会从事件监听器里抛出去、**没人接**, 表现是"连接莫名停住、日志一片安静"。现在兜住并走断开路径。 ### 校验 新增 `verify-qq-reconnect.mjs`(48 项)—— 这是本仓库**第一个真跑代码**的校验器: import 真实的 `QqClient`、用假 WebSocket + 手动时钟,逐条检查它**真的做了什么** (心跳有 ACK 就不许断、没 ACK 就必须断、重连要退避、有会话要走 Resume…)。 > 为什么必须这样:这次的缺陷是**行为**问题,而此前所有校验器都只做**文本断言** —— > 能看到 `case 11` 写没写,看不出「连续 3 次心跳无 ACK 到底会不会断开重连」。 > 而那才是唯一重要的事。 --- ## [1.2.6] - 2026-10-13 **能收发图文 / 语音 / 文件了** —— 从"只能传文字"变成能传附件。 ### 新增 - **QQ:图文 / 语音 / 文件 收发**(2026-10-12 提交,本次补记) 落地位置在**桌面**:`<桌面>/im-bridge-media/<平台>/<日期>/<文件名>`。 不用 `os.homedir() + '\Desktop'` 硬拼 —— 桌面可能被 OneDrive 重定向。 · **文件名会清洗**:附件名是对方给的,`..\..\evil.png` 这种不能信 —— 剥掉路径分隔符、前导点、Windows 非法字符。 · **语音优先用 `voice_wav_url`** 而不是原始 silk:本机没有 ffmpeg,silk 转不了码。 · **上传上限 5 MB**,超过**明确报错**,不硬传一个会被截断的东西 (分片上传是下一版的事,不是这一版偷偷糊过去的事)。 · 下载失败**只记事件、不抛错**:宁可少一个附件,也不要因为一张图失败把整条消息丢掉。 - **微信:入站媒体下载 + 解密 + 落盘**(2026-10-13) 微信的图**不是下下来就能看** —— 得先从 CDN 取密文,再用消息带的 `aes_key` 做一次 AES-128-ECB 解密。这段逻辑跟 iLink 协议绑死,所以单独一个 `weixin-media.js`(`media.js` 保持平台无关)。 · `aes_key` 实测有**两种编码**(`base64(16 字节原始值)` 与 `base64(32 位 hex 字符串)`),两条都要处理 —— 只按第一种处理的话, 文件和视频会**解出乱码而不是报错**,更难查。 · **解密失败不静默**:留一份密文原件在旁、并报事件,否则事后无从查起。 · **语音取微信自带转写**,不下载音频(本机没有 ffmpeg)。 ### 修复 - **纯图片消息会被整条静默丢掉**(QQ 与微信都有,两边都修了) 原来的判据是"有没有文字":`item_list[0].text_item.text` 取到空串就丢掉整条消息。 于是"对方只发一张图、不打字"——**最常见的用法**——插件这边安静得像什么都没发生。 现在放行条件改成「**文字和媒体都空才丢**」,且去重挪到**下载之前** (平台会给同一 `msg_id` 重推,重复下载又慢又费流量)。 - **媒体处理的失败不再升级成"通道故障"** 微信那条改动里,媒体那一步的异常**就地兜住**:外层那个 catch 会把它当作 "轮询失败",于是整条通道进入退避重试 —— 一张图存不下(比如桌面不可写) 不该让用户从此收不到任何消息。 ### 配置 - **新增 `agentSessionId`:可覆盖 QQ 的专用会话 id**(默认留空 = 用内置值) 用途:模型会把历史里关于自身能力的错误结论继承下去,**改代码解决不了,得换上下文**。 ⚠ **不要靠改默认值来解决这件事** —— 默认值是所有用户共享的,改掉它等于让每个 装这个插件的人一起换会话、丢历史。要在自己的 **profile patch** 里设: ```yaml - id: im-bridge config: agentSessionId: "im-bridge-dedicated-v2" ``` 换 id 是**非破坏性**的:旧会话数据仍完整留在 `~/.dsh/sessions/.../im-bridge-dedicated-v1/`,只是不再被 resume;想回去把那一行删掉即可。 代价是新会话读不到旧对话历史。 > ⚠ 这个覆盖**必须重启客户端才生效**(插件代码不热加载)。 ### 校验 新增 `verify-weixin-media.mjs`(38 项)、`verify-files-whitelist.mjs`(4 项)并入例行清单。 全套 578 项通过。 --- ## [1.2.5] - 2026-10-11 ### 修复 - **插件列表里显示的是包名 `dsh-plugin-im-bridge`,不是「IM 桥接」;也没有图标** 根因:展示元数据写在了 `package.json` 的 `meta` 字段里,而**那个字段根本不被读**。 DSH 的真实机制(读 `dsh-app-boot/lib/index.js` 的 `readPluginMeta()` 确认)是: - 标题/描述来自 **`locale/en.json`**(它是 base)里的 `{"meta":{"title","description"}}` - 图标来自 `package.json` 顶层的 **`icon`** 字段(相对路径,svg/png/jpeg/webp,≤256 KiB) - locale 文件经 **Node ESM 解析器**读取 → **受 `exports` 门控**; 没放行会得到 `ERR_PACKAGE_PATH_NOT_EXPORTED`,而被当作"没有元数据"**静默略过** 本次改动:新增 `locale/en.json`、`locale/zh.json`、`icon.svg`; `exports` 加 `"./locale/*"`;顶层加 `"icon": "icon.svg"`; `files` 补 `locale` 与 `icon.svg`;**删掉那个无效的 `meta` 字段**。 > 这类失效是**静默的** —— 插件照常工作,只是没名字没图标,界面上不报任何错。 ### 文档 - **README 的安装说明改成"界面为主"**:原来只给命令行 (`dsh plugin --profile <名字> add …`),对不用命令行的用户是门槛。 现在首选**在 DSH 界面里装**:插件页 →「添加插件」→ 填 GitHub 地址。 三条查证过的依据(都是 DSH 界面/服务的**原文**,不是推测): - 安装页说明就是「输入插件的**包名、GitHub 仓库地址**或本地目录路径」 - `pluginManager.installBundle` 的 spec 接受 git 地址(`InstallSpecKind: 'git'`), 且 GitHub 仓库会先做连接检查再交给 pnpm - spec 从界面到宿主**原样传递**:`inspect(spec)` → `startInstall({ spec })` → `installBundle(spec, …)` 另外补了两条会踩的坑: - **不能只填 `dsh-plugin-im-bridge`** —— 这个插件**没发布到 npm**、只发 GitHub, 只填包名会被拒(而界面的引导却提示"填插件包名",很误导) - **界面装的插件暂不支持自动更新**(界面原文),升级要卸载重装 --- ## [1.2.4] - 2026-10-08 ### 修复 - **侧边栏图标被别的插件插到中间** 用户装了一个新插件后,它出现在了「奇怪的企鹅」和「微信绿泡泡」**之间**。 根因不是那个插件"插队",而是**顺序值撞了**: ``` plugins(0) schedules(10) qq-panel(20) dsh-context(20) weixin-panel(21) ↑ 我们 ↑ 对方同分 ↑ 我们 ``` 侧边栏是**一条全局列表**(按 `order` 升序),而 `sidebar.panellist` 的注册契约只有 `id` / `order` / `label` —— **没有分组或嵌套能力**。 所以别的插件只要取到相同或相邻的 `order`,就会插进我们自己的面板之间。 现在把五个平台的 `order` 从 `20..24` 挪到 `100..104`: | | 之前 | 现在 | |---|---|---| | 取值 | 20, 21, 22, 23, 24 | 100, 101, 102, 103, 104 | | 位置 | 紧跟 DSH 自带的 0/10 | 整体排到后面 | | 撞车概率 | 高(别人多取 20/30) | 低 | 三个考虑:**整体排到最后**(DSH 自带是 0/10,第三方面板跟在后面更合这个侧边栏的设计)、 **彼此连续**(自己的面板不该被自己拆开)、**避开常见档位**(降低再撞的概率)。 > ⚠ **这只是降低概率,不是保证** —— 另一个插件完全可以也取 100。 > 要彻底解决得让侧边栏支持分组,那是 DSH 侧的能力,不是插件能给的。 ### 校验 新增 4 条断言(`verify-panel-per-platform.mjs` 62 → 66 项), 守"order 无重复 / 连续 / 不退回拥挤区间"。全套 434 项通过。 --- ## [1.2.3] - 2026-10-08 **文档与元数据版本** —— 没有改任何代码行为。 ### 文档 - **README 补上多平台**:原来整篇只讲 QQ(标题写"把 QQ 机器人接成 DSH 的对话入口"、 第二章写"你需要一个 QQ 机器人"),而微信不用申请任何东西、扫码即可。 现在新增「零、现在支持哪些平台」能力表(每个平台能做什么、怎么连), 第二章拆成两条路(微信 / QQ),"试第一条消息"按平台分开给排查表。 - **`--profile` 的说法改准确**:`web` 是 DSH **随附的模板名**,不必然是用户的 profile 名。 照抄 `web` 会装到另一个 profile、当前客户端看不到(很难自查)。 详见 [dsh-app-boot 的说明](https://github.com/deepseek-ai)。 ### 元数据 - `description`:补上"各平台完成度不同"这个事实 —— 原文只写"把 QQ / 微信等 IM 接成…", 容易被读成"所有提到的平台都能用"。实际只有 QQ 与微信能收发并接 agent。 - `keywords`:补 `wechat`(微信已是完整可用平台,但 keywords 是只有 QQ 时写的); 去掉过宽的 `bridge`,换成更精确的 `im-bridge`。 --- ## [1.2.2] - 2026-10-08 修一个**让微信回复"看起来发出去了、实际收不到"**的根因, 外加设置页的一轮整理。**微信用户务必升到这个版本。** ### 修复 - **微信回复缺 `client_id` —— 只有第一条能显示,之后全部收不到** 官方协议规范里的实测结论: > 使用**同一个 `client_id`** 连续发送多条消息,API 层面**均返回 200**, > 但微信客户端**仅显示第一条**;对照组使用**不同 `client_id`**, > 三条消息均独立显示。 而我们原来**根本没传 `client_id`** —— 于是服务端把每条新回复都当成 "同一条消息的更新":**第一条渲染出来,之后的全都不显示**, 而日志里每次都是 `status=200` 还带着 `message_id`,看起来完全成功。 症状正是"第一次(测试回显)收到了,之后每一条都收不到"。 现在每次发送都生成新的 `client_id`。 - **QQ 重启后显示「未测试」而它明明能用** QQ 的 READY 事件只写了 `writeStatusSnapshot()`,而设置页读的是 `setRuntime()` 写的另一份 —— 于是 QQ 明明连着、能收发, 界面却一直停在初始值。现在补上了,并如实报"已连接 —— 机器人 X"。 ### 变更 - **设置页「连接 IM」重排**:每个平台一行(开关 / 完成度 / 连接状态 / 凭据状态), 凭据在那一行里展开填写 —— 和飞书/钉钉/企微**完全同一形态**。 原来 QQ 的凭据有**两处界面**:平台列表里那一行(键 `QQ_BOT_APPID/SECRET`, 走宿主凭据存储)+ 页面下方一个独立区块(键 `appId/appSecret`,走浏览器 localStorage)。 同一个 QQ 被拆到两处、键名还不一样、互不联动。现在只留行内那一处。 - **QQ 可以关掉了**:原来有三层硬编码把它做成"常开", 理由是"当前会话就跑在它上面"—— 而那句**不成立**(QQ 用的是自己的专用会话)。 现在唯一的守卫是通用的:**不能关掉最后一个开着的平台** (全关掉侧边栏就一个入口都不剩)。 - 页脚不再挂 QQ 专属的说明(那两条已在 QQ 那一行的开通指引里)。 ### 校验 新增 `verify-panel-per-platform.mjs`(62 项)等断言。全套 430 项通过。 --- ## [1.2.1] - 2026-10-08 修两个**只有真机才会暴露**的问题 —— 都会让"微信能收能发"看起来是坏的。 ### 修复 - **重启客户端后微信通道失效**(用户现象:"发了消息没反应") 启动路径只连 QQ,其它平台要手动去设置页点「测试连接」才会起来。 所以重启后的实际状态是: ``` 日志:qq-event READY ← QQ 连上了 日志:之后一行 weixin 都没有 ← 微信通道是死的,消息根本没进插件 ``` 现在新增 `autoResumePlatforms()`:启动时自动恢复**已启用且能双向收发**的平台。 微信的 `bot_token` 存在本机(`im-bridge-weixin.json`),所以恢复它不需要重新扫码。 ⚠ **其余平台有意不自动恢复**:企微/钉钉/飞书有凭据但**出站没实现**(发不回去), 自动连上只会让界面显示"已连接"却依然不能用 —— 那比不连更误导。 - **把"请求发出去了"当成"对方收到了"**(用户现象:"日志说成功了,我什么都没收到") 两处根因,都和当初 QQ 那个 `msg_id` 的坑同型: ① `post()` 只返回裸 `body`,调用方**完全不看** —— 于是"HTTP 200 + 体内错误码" 被当成成功。微信/iLink 这类接口的惯例就是 `200 + errcode/ret`。 现在 `post()` 返回 `{ status, ok, body, raw }`,并新增 `bodyLooksFailed()` 覆盖 `errcode`/`ret`/`code`/`errCode`;**失败时把返回体记进事件** (截断 300 字符)—— 否则"报成功但没收到"这类问题永远查不出原因。 ② 微信的 `reply` 闭包返回 Promise,但 `finishTurn` 拿它 `.then()` 时 **并没有真的等到 API 结果**,失败被静默吞掉。现在会把 Promise 原样传上去。 顺带:轮询 `getupdates` 也检查返回体 —— 原来只在抛异常时才记, "200 + 错误码"会被无声跳过,连退避重试都不触发。 ### 校验 新增断言 14 条(`verify-multi-platform-dispatch.mjs` 从 26 → 40 项), 新增第 7、8 两节守住"必须看返回体才算成功"和"启动要恢复平台"。 --- ## [1.2.0] - 2026-10-08 从"只有 QQ 一个平台"变成**多平台**:新增微信 / 企业微信 / 飞书 / 钉钉四个入口, 并把两个平台的收发真正接到 agent 上。 ### 新增 - **微信(个人号)接上 agent** —— 走官方 iLink / ClawBot 通道,HTTP 长轮询,**免公网、不需要 SDK**。 在微信里给 ClawBot 发消息,它会用这台电脑上的 agent 回答。 - **企业微信 / 飞书 / 钉钉的传输层**(见下面"已知限制"里的完成度说明)。 - **多平台入口 + 平台开关**:侧边栏与设置页按平台分列,每个平台可单独开关、单独配凭据。 - **「保存并测试连接」**:四个平台都能在设置页里点一下按钮验证凭据与连接。 - **平台图标**(企鹅 / 绿泡泡 / 飞书 / 钉钉 / 企微)。 - **可选重依赖组件**:飞书 / 钉钉的长连接需要它们的官方 SDK, 这两个组件做成**按需安装**(装到 `$DSH_HOME/im-bridge-deps`,不塞进插件目录), 设置页里能看体积、能装、能卸(卸载用"标记 + 重启清理")。 ### 修复 - **写死路径**:`workspace` 不再从随包发布的值来(见 1.1.0),并新增"路径不存在就回退"。 - **入站去重**:同一条消息被平台重推时只处理一次。 - **发送失败重试**。 - **客户端写凭据的静默失败**:写不进去时不再假装成功。 - **平台图标里的 `useLinkUp()` 改成无条件调用**(React hooks 规则: 条件调用会让 hooks 顺序在渲染之间改变)。 ### 变更(内部) - **回复分派不再写死 QQ**。原来 `finishTurn` 里是 `qq.reply(...)`, 于是"非 QQ 平台接 agent"无处落地。现在: - `pending` 里存 `platform` 与**消息级回复闭包** - 发送时优先用那个闭包,否则按平台查表(QQ 走原路径,行为不变) - **微信的回复必须带上那条消息的 `context_token`** —— 所以回复能力是 *消息级*的,不能用一个全局 `send` 替代 - **会话按平台派生**:`dedicatedSessionId(platform)`。 ⚠ **QQ 保持原 id `im-bridge-dedicated-v1`**(不丢历史); 其余平台用 `im-bridge-<平台>-v1`。 - **设置页如实标注每个平台的完成度**(见下),而不是让界面暗示"都能用"。 ### ⚠ 已知限制:四个平台的完成度**不一样** | 平台 | 收消息 | 发消息 | 接 agent | 说明 | |---|---|---|---|---| | **QQ** | ✅ | ✅ | ✅ | 完整可用 | | **微信** | ✅ | ✅ | ✅ | 完整可用(iLink / ClawBot) | | **企业微信** | ✅ | ❌ | ❌ | 长连接能收;**回复帧格式官方未公开**,所以发不回去 | | **飞书** | ✅ | ❌ | ❌ | 要装可选组件(`@larksuiteoapi/node-sdk`);出站未实现 | | **钉钉** | ✅ | ❌ | ❌ | 要装可选组件(`dingtalk-stream`);出站未实现(Stream 通道本身不能回复) | **"发不回去"就是接不了 agent** —— agent 的产出必须能送回去才有意义。 所以这一版只把 QQ 和微信接上,另外三个平台**界面和传输层先就位**, 等各自的出站实现好再接(分派层已经通用化,那时不用再改 `finishTurn`)。 **设置页会显示「可用」/「部分」标签**,鼠标悬停能看到具体到哪一步。 ### 顺带 - **`buildExperienceText(filePath)` 新增可选的 `budget` 参数**(这是 dev-tools 那边的改动, 不影响本插件)—— 让"索引预算取多少"变成可实测的,而不是靠估算。 --- ## [1.1.0] - 2026-10-07 第一个**打过标签**的版本。 > 之前 `package.json` 里写着 `1.0.0`,但那个号**从未被标记过** —— > 于是"1.0.0"同时指代了两个不同的代码状态。这个版本把它显式定下来, > 并把那一堆修复归到 `1.1.0`。 ### 修复 - **`workspace` 默认值泄漏给所有用户**(影响最大) 随包发布的 `cordis.patch.yml` 里写着 `workspace: 'D:\EasyDSH'` —— 开发机的路径。 别人从 GitHub 装完,这个值会进他们的配置,把 agent 的工作目录指向一个 **他们机器上不存在的路径**。现在: - 从随包 patch 里删掉了 `workspace`(留空 = 用 DSH 自己的工作目录) - 新增 `resolveWorkspace()`:候选路径**不存在就跳过**,并记一条 `workspace-missing-fallback` 日志。这同时修掉了"用户删了工作目录后静默失效" 这类极难排查的情况。 - **provider 写死白名单,对 API-key 登录的用户是错的** 原来写死 `['deepseek-account', 'deepseek-account-platform']`。 用 API key 登录的机器上 `deepseek-official` 才是唯一能用的路由, 白名单会把它丢掉、回退到一个不存在的 provider → 机器人整轮失败。 现在改成**按凭据探测推导**: | provider | 探测方式 | |---|---| | `deepseek-official` | `credentials.describe('DEEPSEEK_API_KEY')` | | `deepseek-account` | `credentials.readRecord('deepseek-account-platform/default')` | 两种键空间都要查:API key 存在 `refs` 里(`listRecords()` 看不到它), 账号凭据存在 `records` 里。 - **`/im-bridge/credential` 端点缺失**,设置页读不到宿主的凭据状态, 于是在"凭据其实已配好"的情况下显示空白输入框 —— **界面在骗人**。 现在设置页**以宿主为准**,并且区分三种状态(探测中 / 已配置 / 真未配置), 而不是把"问不到"当成"没有"。 - **客户端 `CONTROL_BASE` 与服务器路由不一致**(改名时只改了一边) 服务器改成了 `/im-bridge/*`,客户端还停在 `/qq-bridge` → **六个端点全部 404**(面板、消息列表、发消息、模型列表、模型切换、凭据状态), 而 35 项 bundle 校验全绿(它不比对两端路径)。 ### 变更 - **包名 `dsh-plugin-qq-bridge-v4` → `dsh-plugin-im-bridge`**, 目录 `qq-plugin-fresh/` → `im-bridge/`。 内核与平台无关(队列 / 会话 / 面板 / 权限),只有传输层是 QQ 专属的 —— 所以以后接飞书等平台只需新增一个传输层。 **平台专属的东西保持 QQ 命名**(这是有意的,不是漏改): 凭据键里存的仍是 `QQ_BOT_APPID` / `QQ_BOT_SECRET`; 面板标题「奇怪的企鹅」、`qqb-*` CSS 类名。 ⚠ 换包名的代价:要卸载重装 + **丢掉整个会话历史**(会话 id 跟着前缀走)。 正常改代码只需**重启客户端**,不需要换名。 - **设置页父菜单「连接 QQ」→「连接 IM」**。 以后加平台不该拆成多个父菜单 —— 父菜单没有说明文字, 并排两个入口说不清"现在生效的是哪个"。 - **凭据键 `qq-bridge/bot` → `im-bridge/bot`**,含**自动迁移**: 启动时若新键为空、旧键有值 → 复制过去 → 回读确认 → 再删旧键。 用户不用重填。 ### 新增 - **README.md** —— 从"创建 QQ 机器人"到"第一条消息"的完整流程, 含几个会**静默失败**的关键步骤说明。 - **LICENSE**(MIT)、**CHANGELOG.md**、**`.gitattributes`**(统一 LF)。 ### 已知问题 - 卸载后 `~/.dsh/profiles/<名字>/node_modules/` 下可能残留一个死链接(junction)。 不影响使用(加载清单已清干净),可手动删。 - 需要人工审批的权限预设会让 QQ 会话**卡死**(审批框在电脑上,手机看不到)。 插件默认用 `danger-full-access` 避开,但这意味着**能私聊机器人的人 就能在本机执行命令** —— 请自行确认机器人的可见范围。 --- ## [1.0.0] - 2026-09-26(未打标签) 首个版本:QQ 机器人 ↔ DSH 双向消息桥接。 - WebSocket 长连接、被动回复 + 主动推送回退 - 消息排队(避免 agent 把两条消息合并成一个回合) - 看门狗(会话卡死 45 秒后自动解封并放出排队消息) - 侧边栏面板「奇怪的企鹅」+ 设置页 > ⚠ 这个版本**没有 git 标签**,所以无法用 `#v1.0.0` 钉住它。 > 它的代码等于 commit `6ec4d18`。