# 交付物 · IM 插件跨进程桥(2026-09-25 · 第 2 棒) > **工作区**:`E:/ProgramData/AIProject/aliyun-dsh-server`|**基线**:`D:/github/dsh_shenxian` > **上游**:群组插件对接单(只读)+ 本工作区 `交付物/IM插件接入完备性审计-20260925.md`(本棒即其整改) > **用户口径**:「按照你的方案处理,完善 IM 功能让群组会话的插件可以开发」 --- ## §0 一句话 上一棒查出:插件跑**实例内**、IM 宿主在**平台进程** ⇒ 缺跨进程那一层,插件「登记不上、够不到出向」。 本棒把**那座桥**建起来 ⇒ **「插件单向发起」的接入现在通了**(登记 / 读 / 出向); **「平台要回头叫插件」的还没通**(发言规则 / 事件回调)—— 这是唯一剩余缺口,已如实登记。 ## §1 交付(3 新 3 改) | 件 | 内容 | |---|---| | **`src/im/sdk/registry.ts`(新)** | `ImPluginBridgeRegistry`:按实例隔离(每实例一份宿主)· `room_type` 全局唯一(跨实例同名具名拒)· 形状闸**先于**占用(失败不留残登记)· `authorizeOutbound`(bot 必须是该房成员)· `unregisterInstance` / `snapshot` | | **`sdk/im-plugin-host/index.mjs`(新)** | 插件侧 SDK(零依赖单文件):`ready()` 登记 / `frame()` 广播 / `say()` bot 发言 / `rooms()` / `messages()` / **`subscribe()` 轮询增量订阅** / `sanitizeManifest()` 剔函数 | | **`test/im-plugin-bridge.test.mjs`(新)** | 14 例 | | `src/web/routes/im.ts`(+230) | 5 个端点:`plugins/register` · `plugins/unregister` · `plugins/out/frame` · `plugins/out/message` · `plugins/bridge`(观测) | | `src/im/sdk/index.ts` | 门面导出 | | `package.json` | `test` / `verify` 各登记 1 处(diff **仅 2 行**) | ## §2 判据读数 - `npm run build` **rc=0** - `node --test test/im-plugin-bridge.test.mjs` = **14 / 14 过 / 0 败** - `npm test` = **618 / 608 过 / 5 败 / 5 跳过**(基线 604/594 ⇒ +14 全为本棒;5 败**仍是**投放线 `tar EBUSY`,未增一败) - `npm run check:layering` **rc=0 ✅ 无新增违规** ## §3 插件作者现在能做什么 ```js import { createImPluginHost } from '@dshs/im-plugin-host' const host = await createImPluginHost({ pluginId: '@dsh-local/im-plugin-demo', manifest: { /* 声明面 */ } }) await host.ready() await host.frame({ roomId, channel: 'doc.cursor', data: { at: 42 } }) // 高频广播(不落库) await host.say({ roomId, botId: 'bot_demo', payload: { kind: 'text' } }) // 落库发言 host.subscribe(roomId, (msg) => { /* 增量消息 */ }, { intervalMs: 1500 }) // 订阅 ``` ✅ 能做:纯数据型 · 纯界面型 · **机器人助手型** · **聊天增强型(卡片/公告/投票)** · **实时协同型(帧广播部分)** ❌ 还不能:**回合制 / 限速型**(发言规则走同步回调)—— 降级三条见 §5。 ## §4 两条关键自决(可推翻) 1. **登记只收「声明面」**:`check` / `onEvent` / `render` / `onAction` 是**函数**,跨不了进程 ⇒ 只传声明,函数侧回调留在实例内;SDK 的 `sanitizeManifest` 自动剔除(测试断言"序列化后不含 `function`")。 2. **出向发言多一道闸**:同进程时 bot 必然由房主加进成员表;跨进程必须自己查 ⇒ `authorizeOutbound`,且**未注入成员判定 ⇒ fail-closed 拒**(⛔ 不"判不了就放行")。 3. **不做"平台反向打进实例"**(要实例可寻址 + 心跳,脆)⇒ 改用**插件拨出**,符合既有"拨出式"纪律(⛔ 插件不开端口)。 ## §5 反向通道:🔴 **已定必须补**(2026-09-26 用户拍板 —— 本条覆盖下表原「本期不支持」的口径) > 🔴 **用户裁定原话**:「**都需要实现完整,才能让插件接入,否则两边都要返工**」 > ⇒ 下表两行的"怎么办(降级)"**已作废**:`speakRules.check` 与 `events.onEvent` **两条都要补齐**。 > 做法 = 实例侧 SDK **长轮询拉取**(保持"拨出式",⛔ 平台不打进实例、⛔ 不需实例可寻址)。 > 规格 ⇒ 平台工作区 `接续包_IM反向通道实现_20260926.md`。⛔ 原会话"不必急着补"的判断被用户推翻。 | 项 | 原「本期不支持」原因(**已作废**) | 现状 | |---|---|---| | **发言规则 `speakRules.check`** | 是"平台**写之前同步问插件**"的回调 ⇒ 需反向通道 | 🔴 **已定要补**(同步语义 + deadline;超时缺省放行、契约可声明 `onTimeout:'deny'`) | | `events.onEvent` 回调 | 同上 | 🔴 **已定要补**(异步语义,⛔ 不阻塞消息流) | | 面板 `render` / `onAction` | **不需跨进程**(面板在实例内渲染,函数本来就在自己进程里) | 要写数据/发言 ⇒ 用出向 | | 出向的**审计归属** | 本口不校验"该插件是否真在该实例启用"(与 `/api/im/data/*` 现状同源) | 补法 = 把 `pluginId` 绑进装配凭据 ⇒ **投放线**面 | ## §6 未做 / 边界 ⛔ 未部署 · 未 commit / push · 未动 47/106 · **未动云安全组**(D8 仍需你在控制台操作)· 未改 A–E 五单结论。 🔴 **下一棒已登记**(2026-09-26):**反向通道实现** = `接续包_IM反向通道实现_20260926.md`(用户已拍板必须补)。 ⚠️ 云安全组操作(D8)仍为用户主动搁置项(`交付物/端口决策与设备中继可行性-20260926.md`),⛔ 不当待办。