Files
dsh_ai1net_server/docs/交接单/接续包_IM接入面收尾与插件对接答复_20260926.md
T
admin c1b5e4d966 chore(工作区): 全量入库 + 补齐 .gitignore(以工作区为准)
- 变更规模:新增 514 / 修改 62 / 重命名 155 / 删除 4(归档重组与文档轮次)
- .gitignore 修:`归档/**/db-cwd归一-备份-*/` —— 原规则写绝对层级(归档/db-cwd归一-…),
  目录搬进 归档/配置与备份/ 后**静默失效**,43 MB 的 DB 备份又变成未跟踪
- .gitignore 补:嵌套 git 内部数据(归档/内嵌git-20261008/、归档/skills-git-旧线-20261007/dotgit-原样移出/)
- .gitignore 补:运行态与部署副本(.workbuddy/collab/、.workbuddy/tools/、.workbuddy/.load-pending、.workbuddy/tmp-*)
- .gitignore 补:备份件(*.bak-*)
- 未跟踪文件从 2190 降到 890(其余为 归档/ 归档件与 .workbuddy/memory/ 知识文件,按口径入库)
2026-10-10 23:13:22 +08:00

124 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 接续包 · IM 线 · 2026-09-26 00:32
## 接续点 · IM 线(插件接入面收尾 + 文档 + 对接答复) · 2026-09-26 00:32
- **来源会话**: 本会话(阿里云 dsh 平台开发会话) | **结束原因**: 上下文 32.8 万强制收口
- **原目标**(用户本轮原话,不翻译、不缩写):
1. 「**创建接续会话把IM的改造优化处理完成**」
2. 「**然后更新IM接入文档**」
3. 「**并回复插件的对接文档**」
- **基线**: HEAD=`e6207aa`(`D:/github/dsh_shenxian`,**未 commit**) | 远端 master = 未核(本会话未 push) | 域锁 = **无**(已释放)
- **产物**(新会话必须逐一确认存在):
- 代码面(`D:/github/dsh_shenxian`):`src/im/sdk/types.ts`(+206)· `src/im/sdk/host.ts`(+362)· **`src/im/sdk/binding.ts`(新)** · **`src/im/sdk/registry.ts`(新)** · `src/im/sdk/index.ts` · `src/im/hub.ts`(+36)· `src/im/ws.ts`(+84/−1)· `src/web/routes/im.ts`(+368)· **`sdk/im-plugin-host/index.mjs`(新)** + `package.json`
- 测试(新):`test/im-plugin-bridge.test.mjs`(14 例)· `test/im-backpressure.test.mjs`(8 例)
- 文档库(`D:/github/dsh_shenxian/dsh-server-docs`):`01-规范/09-IM插件SDK与扩展点契约.md`(§2 加第 8 项 · §3.5 接线器 · §6 修事务口径 · §8 判据四档 · §9 出向端口 · **§10 跨进程接入** · **§11 投递可靠性与容量**)· `01-规范/08-插件开发与对接规范.md`(修路由指针 + SDK 获取 + 09 指针 + §11 变更记录)
- 评估/交付件(本工作区 `交付物/`):`IM插件接入面-20260925.md` · `IM插件接入完备性审计-20260925.md` · `用户组网群聊可行性评估-20260925.md` · `端口与容量瓶颈评估-20260925.md` · `端口决策与设备中继可行性-20260926.md` · `扇出背压修复-20260926.md`
- **校验命令**: `cd /d/github/dsh_shenxian && npm run build && node --test test/im-plugin-bridge.test.mjs test/im-backpressure.test.mjs 2>&1 | tail -5`
→ **期望输出**: 两个文件合计 **22 例、`fail 0`**(bridge 14 + backpressure 8)
- **未完成**(用户要的、还没做的 —— 优先于 AI 自己加的收尾):
> ## ✅ 本棒(「IM接入面收尾-20260926」· 2026-09-26 00:38–01:0x)收口状态
>
> **已完成**
> - **1-① 端到端真跑 ✅**(真起平台 + 真 HTTP + 插件侧 SDK 走 5 端点):
> 脚本 `tmp/im-bridge-e2e-20260926.mjs`,**23 例 / fail 0**。⇒ 跑法 `cd /d/github/dsh_shenxian && node <脚本绝对路径>`。
> - **1-② 部署到 47 ✅**:推 `lib/im/**` + `lib/web/routes/im.js` + `sdk/**`(⛔ 未带别的线的未部署改动)。
> 47 侧备份 `/opt/dsh/backups/im-bridge-predeploy-20260926.tgz`;重启 `dshs` = active;
> `/api/im/plugins/bridge` 与 `/api/im/stats` 均 **401(= 路由已加载,非 404)**。
> ⚠️ **落地文件属主**:tar 会保留本地 uid ⇒ 47 上新文件曾变 `197108`,**已 chown -R root:root 修回**。
> (⛔ 下棒再推时给 tar 加 `--owner=root --group=root`,或推完立刻 chown。)
> ⚠️ 47 上另有**先前就存在**的非 root 文件(`lib/web/routes/business-plugins.js`、`admin.js`、
> `essential-plugins.js` 等)——**非本棒落地、未动**,仅登记。
> ⛔ **106 未部署**(IM 宿主在 Manager 进程,Worker 不带 IM 路由)⇒ 按需为「不需要」。
> - **3 更新 IM 接入文档 ✅(部分)**:09 新增 **§12 插件作者快速上手**(五步 + 可跑片段 +
> 能做/不能做 + 四个必知坑 + 反向通道降级三条)。⛔ **未动任何已落章节**(§2–§11 一字未改)。
> ❌ **仍缺**:可跑的最小**示例工程**(`poc/business-plugins-im/` 三例仍是旧契约)。
> - **4 插件对接答复件 ✅**:本工作区 `交付物/插件对接答复件-20260926.md`(⛔ 未改 `dsh-plugin-partment` 一字节)。
>
> **🔴 本棒抓出并修掉一个真缺陷(新增,不在原接续包内)**
> - **症状**:插件走完登记后 `frame()` / `say()` **恒 `plugin-unavailable`**。
> - **根因**:`routes/im.ts` 的 `bridgeAuth` 把**请求头里的包名**(`@dsh-local/im-plugin-demo`)
> 直接当内部 id 去查注册表,而注册表按 `pluginIdOf(包名)`(`im_plugin_demo`)存 ⇒ **恒查不到**。
> ⇒ **出向面在生产上不可达**(第 8 项等于白做)。
> - **为什么单测没抓到**:`test/im-plugin-bridge.test.mjs` **刻意不碰真实网络**(mock fetch +
> 直接把归一化 id 递给注册表)⇒ **从不经过这一层翻译**。只有真 HTTP 才暴露。
> - **修法**:`bridgeAuth` 现同时交 `packageName`(供"头与清单自洽"判)与 `pluginId`
> (供注册表查);两处 `outboundPortFor` 改用 `auth.pluginId`。**改动仅限 `routes/im.ts`**
> (内核四档不变:`store.ts` 零改动 / `hub.ts` 纯插入 / `ws.ts` 只动投递点 / `routes/im.ts` 纯插入 ——
> 本次属"修 bug"那类,故允许动既有逻辑,须有回归证据)。
> - ⚠️ **回归证据未入库**:现为 `tmp/` 脚本(接续包原口径即"临时脚本落 tmp/")。
> 若要**永久回归**⇒ 下棒需把它落成 `test/im-plugin-bridge-e2e.test.mjs` + 在 `package.json`
> 的 `test`/`verify` 各登记 1 处(A=B 各 1 行,⛔ 用 Edit 精确替换)。
>
> **⛔ 仍未完成(下棒依据)**
> - **1-③ 生产标定背压阈值 ❌ 未做(缺真实读数)**:`/api/im/stats` 现为 **401(admin 专属)**,
> 无 admin 会话读不到 `peakPendingBytes`;且当前无真实流量 ⇒ 读数恒 0,**改了也是拍脑袋**。
> ⇒ **阈值保持 4 MiB 不变**(保守起点),**改参数入口已就绪**(`IM_SEND_BUFFER_LIMIT_BYTES`,可注入)。
> 标定法(拿到 admin 会话或真实流量后照做):连读 `GET /api/im/stats` 的 `peakPendingBytes` 一段时间,
> 取其分位值作为阈值下限,改 drop-in 环境变量 → `systemctl restart dshs`(⛔ **不改代码**)。
> - **2 反向通道** —— 🔴 **2026-09-26 用户已拍板:必须补、且要做完整**
> (原话「**都需要实现完整,才能让插件接入,否则两边都要返工**」)。
> 本棒据此**改了 09 §10.4 / §10.5 / §12.5 的口径**(从"本期不支持"→"已定必须补")+
> 同步 `交付物/IM插件跨进程桥-20260925.md §5/§6` 与 `交付物/插件对接答复件-20260926.md`,
> 并产出**施工规格** `接续包_IM反向通道实现_20260926.md`。
> ⚠️ **实现交执行棒**(本棒受会话预算限制,⛔ 未动任何代码);⛔ 原文"不许拍成已定"的禁令**至此解除**
> —— 因为**是用户本人拍的**,不是新会话替原会话拍的。
> - **§1 对接单未消的 4 条**(纯文档):**#2 表声明入口(P1,需先定口径)** · #4 移动端 ·
> #5 错误码/自测清单/变更记录 · #8 内部编号 107/108。
> ⚠️ **接续包原记「P1×2 已消」不准**:实测 **#1 已消、#2 未消**(09 §4 仍有 `tables: [{` 且全篇无 `dsh.data.schema`)。
1. 🔴 **IM 改造的收尾三件**(= 用户「处理完成」的落地口径,按序):
① **端到端真跑验收** —— 桥的 5 个端点(`/api/im/plugins/{register,unregister,out/frame,out/message,bridge}`)**只过了单测,从未真跑**;需在本机起平台+用 `sdk/im-plugin-host` 走一遍真 HTTP。
② **部署到 47(+按需 106)** —— 本机代码面完成,**生产未部署**(R8:开发环境服务器,动手前一句话说明即可)。
③ **生产标定背压阈值** —— 现为保守起点 4 MiB;观测面 `GET /api/im/stats` 的 `peakPendingBytes` 拿到真实读数后**改参数**(⛔ 不改代码)。
2. 🔴 **「反向通道」是否补 —— ⛔ 属待用户拍板,**不许新会话替原会话拍成「已定」**:
- 现状:**「插件单向发起」已通**(登记 / 读 / 出向);**「平台回头叫插件」未通**(`speakRules.check` 发言规则、`events.onEvent` 事件回调)。
- 原会话判断(**可推翻**):「**不必急着补**」—— 已有降级三条(规则参数写房间 `config` / 出向主动纠偏 / `host.subscribe()` 轮询替代事件回调,后者已实测够用)。
- 若要补 ⇒ 需要**平台 → 实例的反向调用面**(含鉴权、超时、实例可寻址性),是新工作量。
3. **更新 IM 接入文档**(用户第 2 件事):`09` 已更新到 §11,但**缺一份「插件作者快速上手」** —— 现在用法散在 §10.3,且**没有可跑的最小示例工程**(`poc/business-plugins-im/` 三个示例是**旧契约**,未含第 8 项出向与跨进程桥)。
4. **回复插件对接文档**(用户第 3 件事):即 `E:/ProgramData/AIProject/dsh-plugin-partment/对接单_平台侧-IM板块待优化与Macro改造可行性_20260925.md`。⚠️ **那是别的工作区 ⇒ ⛔ 只能在本工作区产出「平台侧答复件」,由用户转交,⛔ 不得直接改那边一个字节**。答复要覆盖:§1 九条(P1×2 + P2×3 **已消**,P2×2 + P3×2 待定)+ §2 Macro 改造可行性(第 8 项出向端口**已具备**,§2-3-1 高频广播**已解决**:`frame` 档不落库)+ §4 两个待拍板项的**平台侧回答**。
- **下一步**(第 1 个动作 = 具体命令):
1. **先跑校验命令**(见上)—— 不符即停、只报告。
2. 读 `交付物/IM插件跨进程桥-20260925.md` 与 `交付物/扇出背压修复-20260926.md`(本会话两份最新落地件,含未完成项与设计取舍)。
3. **端到端真跑**(未完成 1-①):本机起平台(`DSH_IM_WS` 默认开)→ 写一个临时脚本用 `sdk/im-plugin-host` 的 `createImPluginHost()` 走 `register → frame → say` → 断言 `ok:true`(**临时脚本一律落 `tmp/`,⛔ 不入库**)。
4. 部署(未完成 1-②)→ 标定(1-③)→ 文档(3)→ 答复件(4)。
5. ✅ **未完成 2(反向通道)—— 用户已表态**(2026-09-26:必须补、做完整)⇒
**下棒依据改为 `接续包_IM反向通道实现_20260926.md`**(⛔ 不再是"先不碰")。
- **关键决定**(已定项 + 为什么 —— 防新会话推翻重来):
① 🔴 **插件跑在「用户实例内」、IM 宿主在「平台进程」⇒ 跨进程** —— 依据:用户自配模型是起实例时写进实例的(`credential_vault` → spawn 写 `settings.yaml`),平台侧拿不到 ⇒ 插件必须在实例内。
② **桥 = 插件「拨出」而非平台「打进」** —— 符合既有"拨出式"纪律(`09 §2-6`:⛔ 插件不开监听端口),且断连可退化为普通聊天。
③ **登记只收「声明面」** —— `check`/`onEvent`/`render`/`onAction` 是函数、跨不了进程;函数侧回调留在实例内。
④ **出向发言多一道闸** —— bot 必须是该房成员(同进程时必然成立,跨进程必须自己查);未注入成员判定 ⇒ **fail-closed 拒**。
⑤ 🔴 **背压:慢客户端不虚减 `delivered`** —— `write()` 返回 false = 水位满(**数据已排队、会送达**)⇒ 若记成丢帧会污染观测。**若用户要"慢客户端单独计 skipped"⇒ 是语义变更、需重新拍板**。
⑥ **判据分两类**(已写进 `09 §8`):**新增能力 ⇒ 纯插入**;**修 bug ⇒ 可改既有逻辑**,但须「回归用例 + 新行为可观测 + 阈值可注入」。内核四档:`store.ts` 零改动 / `hub.ts` 纯插入 / `ws.ts` 允许改但**被删行只限投递点** / `routes/im.ts` 纯插入。
⑦ **端口 D8 = 用户拍板「先不开,后续有需要再开」**(09-26)⇒ ⛔ 不当待办、⛔ 不催;群聊全走 443 中继,功能完整。
⑧ **设备当中继**:路径已存在(桌面端节点接入已实测),但**中继方需"别人能连到它"**=用户侧 NAT 门槛;**推荐替代 = 设备主动拉**(⛔ 不需任何端口)。⛔ 未排期。
- **回滚点**:`D:/github/dsh_shenxian` HEAD=`e6207aa`(**本会话全部改动未 commit** ⇒ `git checkout -- <file>` 即可回到干净基线);`dsh-server-docs` 同理;部署前须另做 47 侧备份(`/opt/dsh/backups/`)。
- **⛔ 不要重做**:
- **扩展点第 8 项(出向端口)+ 面板动作 + 分片工具**(types/host 已落,22 例测试覆盖)
- **跨进程桥平台侧**(`registry.ts` + 5 端点 + `bridgeAuth`)
- **插件侧 SDK**(`sdk/im-plugin-host/index.mjs`)
- **背压修复**(`ws.ts` 1 行改写 + `hub.ts` 计数 + 8 例)
- **两处规范文档**(09 已到 §11;08 已修指针)—— 只**补**「快速上手」,⛔ 不重写已落章节
---
## §A 本轮(原会话)做了什么 —— 一句话
把「IM 基础」从**插件接不进来**改造成**双向可接入**:① 补出向出口(扩展点 8)② 补跨进程桥(登记 / 出向,插件侧 SDK 齐备)③ 修扇出背压隐患;并把两处规范同步到 §11。
## §B 判据读数(原会话实测,供新会话对照基线)
- `npm run build` **rc=0**
- `npm test` = **626 / 616 过 / 5 败 / 5 跳过** —— ⚠️ **5 败是既有环境问题**(投放线 `test/shared-layer-sync.test.mjs` 的 `spawnSync tar EBUSY`),**与本线无关**,⛔ 不要去修它(属投放线 lane)
- 本线专项:`test/im-plugin-bridge.test.mjs` **14/14** · `test/im-backpressure.test.mjs` **8/8** · `test/im-sdk.test.mjs` **78/75 过 / 3 跳过**(跳过 = `spawnSync git EBUSY`,已改具名跳过)
- `npm run check:layering` **rc=0 ✅ 无新增违规**
- 内核改动形态:`src/im/store.ts` **零改动**;`ws.ts` 被删改的既有行**只有 1 行**(`socket.write(textFrame(text))`,正是背压修复点)
## §C 工具纪律(本线踩过的)
- ⚠️ **测试跑的是 `lib/` 编译产物 ⇒ 改完必 `npm run build`**(原会话踩过:`hub.ts` 新、`ws.ts` 旧混跑 ⇒ 症状像"注入无效",浪费两轮)。定位法 = 打印**两个独立观测点**(注入值 ✅ 但计数为 0 ⇒ 走的是旧代码)。
- ⚠️ 本机 `spawnSync git|tar EBUSY` 会让测试**假失败** ⇒ 取证类用例改 `t.skip` 具名跳过。
- 🔴 **改 `package.json` 一律用 Edit 精确替换**,⛔ 不用「读-改-写整个 JSON」(会重排格式)。原会话实测侥幸未炸,但那是运气。
- 🔴 (「接续包 md5」口径门禁)开工前重算 md5 与 prompt 里的一致才开工;不一致 ⇒ 停手报告。