# dsh-connection-card-host 能力报告 > 版本:1.0.0 报告 · 包名 `@noob-stupid/dsh-connection-card-host`(1.0.2) > 所有能力均经真机验证。**验证方式与证据分开标注** —— 凡"我自己代码写的日志" > 都算弱证据,只有框架事件/对端日志/磁盘文件才算强证据(见第 11 节)。 > > ⚠️ **时效说明**:本报告写于 1.0.0。第 7 节与第 10 节的"工具全局注册 ≈400 tokens 常驻" > 自 **1.0.1** 起已改变(改为按会话 scope 注册,没连接的会话不再背这笔成本); > 其余条目的能力与限制未变。规划中的项目见 README 的「路线图」一节。 --- ## 0. 一句话概括 在**两个 DSH 会话之间建立有状态连接**,让它们能: - **互相看见**对方在做什么(拉取式,**0 上下文成本**,不打扰) - **共享一份说好的前提**(公约盒,拉取式,0 成本) - **按需互相传话**(显式发送,三档紧急度,**由发起方判断急不急**) - 在连接上**挂载卡片插件**,并可**在面板内安装**新卡片 --- ## 1. 连接管理 | 能力 | 状态 | |:---|:---| | 拖拽建立连接 | ✅ **开关语义**:已连则断开,未连则连接;落点提示(即将连接=蓝调,将断开=红调) | | 面板建立连接 | ✅ 两个下拉选会话;点中间 `↔` 可加第三个 → **三会话两两相连** | | 持久化 + 重启恢复 | ✅ `$DSH_HOME/connection-cards/connections.json`,带 `.bak` 轮转 | | 断开 | ✅ 面板按钮,或拖拽到已连会话 | --- ## 2. 消息转发权限(三档,逐档实测) ### ⚠️ 先澄清一件最容易误解的事 > **这个开关控制的是「允许发哪类消息」,不是「对方能不能干活」。** 对方的干活能力**与权限完全无关** —— 它是完整会话,本来就有工具。 实测依据:派给对端会话的任务走 `debug/deliver` 直投通道,**根本不检查权限**, 在 `read` 档下它照样完成并交付了修复。 ### 三档的实测行为 | 档位 | 颜色 | `say` | `ask` / `reply` | |:---|:---|:---|:---| | **只读** `read` | ⚪ 灰 | ❌ 拒绝 | ❌ 拒绝 | | **可建议** `suggest` | 🔵 蓝 | ✅ 通过 | ❌ 拒绝 | | **可写入** `write` | 🟠 橙 | ✅ 通过 | ✅ 通过 | **两个方向独立设置** → 可不对称。线上两个圆点各显示一端颜色,所以**一根线可以一端橙一端灰**。 **拒绝原因自证**:频率限制会说"还需 196ms",权限不足会说"需要写权限(当前 suggest)"。 ### 只读**不影响感知** | 能力 | 只读时 | |:---|:---| | 工作状态查询 | ✅ 可用 | | 公约盒 | ✅ 可用 | | 面板看对方在做什么 | ✅ 可用 | | 发消息 | ❌ | 原因是前三个都是**拉取式**的(对端主动查询),不需要权限。 --- ## 3. 自动镜像:**已关闭**(默认,且是有意为之) ### 历史与结论 中继曾经自动转发会话内容,但**它没有任何信息判断"这条内容是不是说给对端听的"**, 于是只能全发。三次事故: 1. **用户给 A 下的指令被镜像给 B** → B 当成自己的任务去做 2. **助手汇报被镜像** → 对端当成用户指令去响应 3. **助手对用户说的旁白被镜像** → 对端界面显示「收到执行请求」并**真的跑了一轮** **对端的量化**:30 条 / 12,765 字的镜像占它会话"用户侧字符"的 **77.8%** (它自己真人的输入才 3,653 字),而其中**没有一条**产生了有用动作。 **所以 `relayAssistant` 与 `relayUser` 都默认 false** —— 默认不自动转发任何东西。 面板上若真有转发开启(非默认),会显示常驻警告条(依据 `relayConfig` 实际值,不依据权限档位)。 --- ## 4. 真传话(显式发送) ### 这是唯一的跨会话投递路径 ⚠️ 早先 `messages/send` **只写进连接的消息日志**(给面板看),**不投递** —— "传话"其实从来没有真的到达过对端。现在它会**真的投到对端会话**。 ### 三档紧急度(**由发起方按情况判断**) | 档位 | 底层机制 | 对方会怎样 | |:---|:---|:---| | **`quiet`** | `inject` | 放进上下文**但不唤醒** —— 它下次干活时看到,**不被打断** | | **`normal`** | `followup` | **排队** —— 处理完手头的事就看到(默认) | | **`urgent`** | `steer` | **插话** —— 插进它**正在跑的那一轮**,当场读到 | **降级**:`urgent` 但对端空闲 → 自动退成排队(下一轮立刻开始,效果等同即时),**不报错**。 ### 判断原则(写在工具描述里,由模型执行) > **打断是有代价的**(对方要中断当前思路)。大部分消息不急 —— > 默认用 normal;只有确实需要它**立刻**改变行为时才 urgent >(阻塞问题、叫停、发现它正在做错的事)。 旧实现是 `对端在跑 → steer`,等于**每条消息都是最高优先级** —— 已修正。 ### 消息性质标记 `form` | form | summary | 含义 | |:---|:---|:---| | `mirror` | 对方进展(自动同步) | **信息,不是任务**,不必动手 | | `handoff` | 对方派活(需要处理) | **需要处理** | 实测:对端收到 `handoff` 后,自己的助手说这是 "a **real message** via the new explicit `connection_send` tool" —— **它认得出性质**(而之前那 30 条镜像它全误判了)。 ### ⚠️ 这条路径不可能带上"镜像"那类内容 | | 镜像(已关) | 显式传话 | |:---|:---|:---| | 内容来源 | **读取会话里发生的事** | **调用时传进来的 `text`** | | 触发 | 会话有活动就触发 | 有人显式发起才触发 | | 能否读到会话内容 | 是(它就是干这个的) | **否 —— 代码里没有任何途径** | --- ## 5. 协作感知 A 层:工作状态(拉取式,**0 上下文成本**) 自动采集(不要求模型额外产出任何东西): | 事件 | 提取出 | |:---|:---| | `tool/call` name=`todo_write` | **计划与进度**(最佳来源) | | `tool/call` name=`edit`/`write`/`read` | **在动哪些文件** | | 工具名序列 | 在做哪类活 | | `turn/start` / `step/start` | 推进到第几轮第几步 | **通道**:工具 `connection_peer_work`(会话自己查,对端视角)/面板「对方在做什么」(可折叠)。 **实测**:对端查询返回「正在执行命令(2 秒前)/ 最近动过的文件 / 第 69 轮第 39 步」—— **它不需要我告诉它,就知道我在干什么。** --- ## 6. 协作感知 B 层:公约盒(拉取式,**0 上下文成本**) 连接级共享约定:接口、字段名、坐标方向、单位、命名规范、文件分工边界。 **判断标准**:对方不知道就会做错的东西。 | 工具 | 作用 | |:---|:---| | `connection_conventions` | 查(可按关键词) | | `connection_declare` | 声明一条(可指定取代旧约定) | 面板上也能直接增删,归属显示为「你」(人是第三方,不是 A 也不是 B)。 **为什么是"拉"不是"推"**:推送会把对方上下文慢慢填满,而大部分时候它用不上。 **实测**:对端查询返回约定,归属判定正确。 --- ## 7. 全体工具清单 | 工具 | 作用 | 成本 | |:---|:---|:---| | `connection_send` | **给对端发消息**(真的投递),紧急度自己判断 | 一次往返 | | `connection_peer_work` | 看所有相连会话在做什么 | 一次往返 | | `connection_conventions` | 查共享约定 | 一次往返 | | `connection_declare` | 声明/取代约定 | 一次往返 | 四个工具的 schema **常驻上下文 ≈ 400 tokens**(唯一固定成本)。 --- ## 8. 卡片系统 | 能力 | 状态 | |:---|:---| | 模板发现 | ✅ 内置(随插件发布)+ 已安装(`$DSH_HOME/connection-cards/cards/`) | | 装载 / 卸载 / 重载 / 重启重放 | ✅ | | 崩溃隔离 | ✅ 卡片 import/apply 抛异常不拖垮宿主 | | 面板渲染 | ✅ 宿主侧跑 `renderPanel`/`mountPanel`,取回 HTML | | **可见范围** | ✅ 两端通用 / 仅 A 端 / 仅 B 端;装载时可选,装载后可改 | | 卡片协议 | 卡片**绝不 import `@deepseek-ai/*`**,所有交互走 CardAPI | **实测**:对端交付的 `hello-card` 被装载后渲染出 `
…
`。 --- ## 9. 面板内安装 来源:本地目录 / 本地 tgz / npm 包名 / HTTP tgz。 **装到 `$DSH_HOME/connection-cards/cards//`,完全不碰 profile** —— 不跑 pnpm、不改 `dsh.profile.bundles`。 **理由**:不会被 DSH 的更新破坏,也不会破坏 DSH。 **校验**(先校验来源,再动目标目录):拒绝无 `package.json` / 非法 JSON / **无 `dshCard` 字段** / **入口文件不存在**。**校验失败绝不删旧目录**。 **实测**:坏包被拒 ✅、入口缺失被拒 ✅、合法包装上并成功渲染 ✅、卸载后模板同步消失 ✅。 --- ## 10. 上下文成本总账 | 层 | 成本 | |:---|:---| | 工作状态(A 层) | **0** —— 拉取式 | | 公约盒(B 层) | **0** —— 拉取式 | | 三个工具的 schema | **≈400 tokens 常驻** | | **显式传话** | **每条都花** —— 且按紧急度可能唤醒对方 | | 自动镜像 | **0(已关闭)** | **可优化项**:工具目前**全局注册**,每个会话(哪怕没有连接)都背着这 400 tokens。 可改成只对参与连接的会话可见。**未做。** --- ## 11. 验证方法的强弱(这一节是踩出来的) | 强度 | 证据 | 例子 | |:---|:---|:---| | **弱** | **我自己代码写的日志** | 审计日志"投递成功(followup)" —— 只能证明"代码**打算**排队" | | **强** | **框架自己落的事件** | 接收端日志 `agent/inbox/spliced` 的 `target` 字段 | | **强** | **对端会话日志** | 消息真的落成它的 `user/message`,带正确的 `source` | | **强** | **共享文件系统** | 会话日志、审计日志、调试日志 | **用自己的自我报告验证自己的逻辑是循环论证。** 实例:我曾用审计日志 "验证"了排队路径,而对端用框架事件(`target=next-turn` + 随后真的起了新回合) 才真正证明了它。 **磁盘级判别口径**(对端从接收侧定出来的): ``` agent/inbox/spliced 的 target 字段 target = "next-step" → 插话(插进正在跑的那一轮) target = "next-turn" → 排队(下一轮才看到) 交叉印证:next-step 那条不起新回合;next-turn 那条后面会跟新的 turn/start ``` --- ## 12. 安全边界 | 边界 | 状态 | |:---|:---| | 跨会话通道**只放行真人发言进入观察层** | ✅ 宿主通知(任务完成、模型切换、运行时上下文)在入口即挡下 | | **转发层只发助手输出** | ✅ `relayUser: false`(真人发言不跨会话) | | **自动转发整体关闭** | ✅ `relayAssistant: false` | | 被挡内容**可查询 + 带内容预览** | ✅ `awareness/diagnostics`,含 `lastDropped` | | 防回环(投递痕迹识别 + 跳数上限) | ✅ 保留为安全网 | | 频控**分来源分桶** | ✅ 手动发送与自动写入互不干扰,拒绝时带 `retryAfterMs` | | 安装隔离 / 卡片沙箱 | ✅ 不碰 profile;卡片崩溃隔离 | **实测挡下比例**:某会话 `user/message` 里**近三分之一**是宿主生产方 (user 85 / model-selection 23 / tool-jobs 5 / goal 4 / …)。 **为什么必须挡**:宿主通知**常带面向特定属主的祈使句**。实例:后台任务完成通知里有 `Read its output with job_output` —— 那是喊给**任务属主**的,转发出去等于让对端 执行一条**没发给它的指令**。 --- ## 13. 未实现 / 已知限制 | 项 | 状态 | |:---|:---| | **面板发送框** | ❌ 未做 —— 想手打一条只能走 RPC 或让助手调工具 | | **回执给发起方** | ⚠️ 只有 `delivered`,不知道对方后续(开始了吗/做完了吗) | | **"完成"信号** | ❌ 没有 —— 而**新鲜度不是完成信号**(实测:28 秒无更新其实是在跑长命令) | | `awareness/work` 身份自描述 | ⚠️ 返回 `{a,b}`,消费方要自己再推一遍谁是谁(**已因此读错过端**) | | `requestRemote` | ❌ **接口已就位、实现待补** —— 宿主无 `ctx.remote`,目前永远返回 `not_available` | | `repairPreset` | ❌ **接口已就位、实现待补** | | 卡片面板浏览器侧执行 | ❌ 未实现 | | 工具按会话 scope | ❌ 未做(见第 10 节) | --- ## 14. 当前实机状态 ``` 一条连接 eabdc88d(aToB=write bToA=write) session-bd5ac1b1(对端) ↔ session-a4a3d1a8(我) 卡片:hello-card 已装载 卡片模板:goal-relay-card / monitor-card / self-heal-card(内置)+ hello-card(已安装) 自动镜像:关闭 ```