npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

dsh-interconnect

v0.13.0

Published

Cross-instance message/event handoff plugins for DeepSeek Harness (DSH): interconnect service + model-facing tools + companion skill

Readme

dsh-interconnect

跨实例消息互通与事件通知插件,用于 DeepSeek Harness (DSH)。 让一个 DSH 实例能向同一个实例、另一台机器、或另一台机器上的别的 DSH 实例发送消息、探测活性,并在实例之间双向推送事件。

项目状态(2026-09-16):本仓库是该插件的树外(out-of-tree)主源。并入 DSH monorepo 的计划(PR #3243)已按「保持树外」的评审意见关闭;插件继续经 npm 分发(dsh-interconnect),并由团队内部的插件集合仓以 git submodule 引用本仓库。

包含三个插件

interconnect —— host 服务(ctx.interconnect):

  • 全走持久 WebSocket 链接:跨实例、跨机器投递消息、枚举 live session、探测活性(send/reply/ping/list 经 /interconnect/link 的 msg/query 帧)
  • /interconnect/link WebSocket 端点:双向实时事件推流,含心跳与指数退避重连;也承载 send/reply 消息(msg/msg-result 帧)。0.9 起没有 HTTP 端点,也没有 HTTP 回退
  • 事件 fan-out 只在 WebSocket 链路上;入站事件以 interconnect/event 发出
  • 共享密钥鉴权(DSH_INTERCONNECT_TOKEN,bearer,fail-closed,timing-safe 比较)

tool-interconnect —— 模型可见工具:

  • interconnect_peers:列出本实例已配置的对端及其拨号链路状态(up/down),用于在不知道 peer id 时先看有哪些可用目标,也是区分「没配这个 peer」与「配了但链路不通」的唯一入口
  • interconnect_send:向对端实例的指定 session 投递消息;默认唤醒没有 live agent 的持久 session(resume: false 关闭),可选 delivery 选投递模式
  • interconnect_list:列出对端实例的 live session(id + 标题 + 状态),用于在不预先知道 session id 时寻址
  • interconnect_ping:探测对端实例活性与身份
  • interconnect_reply:向记录过的发送方回传消息,只需文本;回信 session 是本 agent 自己的,无需再次寻址

skill-interconnect —— 配套 skill:

  • 向模型注册 dsh-interconnect skill,说明 peers/list/ping/send/reply 的完整用法、 投递模式、resume 唤醒语义与失败处理。
  • 明确告知模型:interconnect_send 会自动注入发送方的 instanceId 和 sessionId, 接收方凭记录的 sender 即可用 interconnect_reply 回信,不需要手工传地址。
  • 依赖 interconnect 服务,只有传输层存在时才注册进 ctx.skills。

用法

寻址(0.9 起用 instanceId,全走持久链接)

从 0.9 起,传输只走 WebSocket 持久链接,不再有 HTTP 端点,寻址参数从 baseUrl 改为 instanceId:

  • interconnect_send(instanceId="peer", sessionId=..., text=...)
  • interconnect_ping(instanceId="peer")
  • interconnect_list(instanceId="peer")
  • interconnect_peers()(不接受参数;返回本实例配置的全部对端及其链路状态)
  • interconnect_reply(text=...)(只需文本;回信 session 是本 agent 自己的,目标从记录的 sender 解析)

instanceId 是 interconnect 行 peers 映射里的键;真正用来拨号的 origin 由该映射的值给出(例如隧道端点 http://127.0.0.1:13080),instanceId 本身从不出现在线上,也不参与路由——origin 才是唯一的拨号依据。

对端没配在这里时,send/reply 返回 peer-not-configured,ping/list 则在返回 reachable: false 的同时带上 configured: false——这一支重试永远不会成功,只能去补 peers 映射。配了但链路不通是另一支:链路断或两端 token 不一致时返回 unreachable (configured: true),重连成功后就可能通(无 HTTP 回退)。

interconnect_peers 就是这两支的判据来源,输出每行一个对端:

peer-a  http://127.0.0.1:13080  [up]
peer-b  http://127.0.0.1:13081  [down]

up/down 是拨号链路当前是否可用;down 不代表对端没开——重连按指数退避自己跑,改完 网络或对端重启后无需重启本实例,链路会自己回来。

增删或改点 peers 同样不需要重启:profile patch 层的改动会重建本插件的 fiber,构造器按新映射 重新建链。这条已实测(双向对照):patch 层加入 peers 后出现拨号、删掉后拨号消失;改 instanceId 后新链接自报的就是新名字。生效的前提是这一层被真正应用——由 dsh 启动、组合里带 hmr 行的实例会自己应用(见「安装」一节),其它启动方式只能重启。构造器只在激活时读一次该 映射,所以改了文件但没被应用时,行为仍是旧的。

interconnect_list 返回对端当前 live 的 session,每一行的 sessionId 在调用时刻都是合法的投递目标:

session-264d37b0-…  重构 interconnect 插件  [idle]
session-b07326da-…                          [running]

title 与 status 是尽力而为的:标题来自可选的 title projection 服务,对端没装该服务、或 该 session 还没有标题时,整个键不出现(而不是空字符串),所以「无标题」与「该对端不提供 标题」可以区分。projection 抛错只会让那一行降级成只有 id,不会让整个列表失败。

只列 live session 是有意的:send 能到达的正好是这些。对端存在但没有运行 agent 的 session 不会出现在列表里,也收不到消息。

答案有双重上限:行数(MAX_LISTED_SESSIONS,100 行)与字节预算(MAX_LIST_ROWS_BYTES,等于链路 帧上限减去 4 KiB 的 query-result 信封)。整帧必须待在链路的帧上限内,ws 对超限帧会直接关闭 链路,所以 live session 极多、或标题很长时,对端只回答能装下的前若干行,而不是把传输打断。 对端不上报截断标志,所以 interconnect_list 只在收到的行数正好等于该上限时追加一行提示 (100 of possibly more live sessions)——满页可能被截断,缺失的目标仍可能 live。 ping 与 list 的答复还会按调用方问的 kind 校验形状,形状不符按「无答复」处理(对应 unreachable)。

回复(reply)

send 的线负载带一个 sender 身份(无地址:instanceId + sessionId),收到消息的 instance 会按「本地 session id → 该 sender」记下这份身份。之后那个 session 回信只需要文本: 回信 session 就是执行 interconnect_reply 的 agent 自己的 session,工具据此查出记录的 sender, 不用再次给出本地 session id、对端 instanceId 或远程 session id——回信走的是本机到那个 instance 的持久链接。

# 源实例 A 指定目标 B 的 session,并带上自己的身份(无 baseUrl)
interconnect_send(instanceId="b", sessionId=B-sess, text="…", sender={instanceId:A, sessionId:A-sess})

# B 回传:只给文本,回信 session 即执行该工具的 agent 自己的 session
interconnect_reply(text="reply")
  • sender 是自报的,只用于 reply 归因与寻址,不是路由或鉴权依据——连接本身仍由 共享密钥在每个 origin 上独立鉴权。
  • 回复的消息也带 sender(本机恒带,不再需要配置 origin),所以对话可双向多轮延续。
  • 只有当入站 send 带了 sender 时 reply 才有目标;对端版本没带、或本 session 从未收过 互联消息时,reply 返回 delivered: false, reason: "no-sender-known"。
  • sender 不进模型上下文(和 source 一样只落到持久化日志与 UI 归因)——这条消息的 内容字面就是 wire 上传来的 text,模型看到的仍是普通 user 文本,只是不带任何结构化的 发送方标记。

消息信道:全走 WS(msg / query 帧)

从 0.9 起不再有任何 HTTP 端点——send/reply/ping/list 全部在持久 WebSocket 链路 (/interconnect/link)上完成。peers 映射在激活时自动 link() 每个对端(心跳 + 指数退避 重连沿用既有实现),寻址按 instanceId 查对应链接。

| 帧 | 方向 | 作用 | |---|---|---| | hello | 双方 | 拨号方自报 instance id | | event | 双方 | 生命周期事件推流 | | msg | 请求方 → 接收方 | 携带 kind(send/reply)、sessionId、text、sender/delivery/resume、reqId | | msg-result | 接收方 → 请求方 | 与 reqId 对应的 SendResult | | query | 请求方 → 接收方 | ping / list / event 发现与事件查询 | | query-result | 接收方 → 请求方 | 与 reqId 对应的查询结果 |

  • 出站:interconnect_send/interconnect_reply/ping/list 都发对应帧并等待匹配 reqId 的结果(受 requestTimeoutMs 约束)。到未配置或未联通的对端直接返回 unreachable—— 没有 HTTP 回退,这是 0.9 的破坏性变化。
  • 入站:msg 帧携带 send,走 deliver 逻辑(sender 记录、subagent 封栏等),结果经同一 socket 回 msg-result;query 帧回 query-result。
  • 心跳与指数退避重连沿用既有实现。

投递失败的原因

delivered: false 单独一个布尔值无法据以行动,因为不同失败需要不同应对,所以 SendResult.reason 会指明是哪一种:

| reason | 含义 | 应对 | |---|---|---| | session-not-live | 对端答复了,但那个 session 没有 live agent,且唤醒被关闭(resume: false)或对端无法唤醒 | 不要改投其它 session;报告失败,或移除 resume: false 后重试 | | unreachable | 配了该对端,但没拿到可用答复(链路断,或鉴权被拒) | 目标 session 可能完好,链路重连后重试可能成功 | | peer-not-configured | 本实例的 peers 里没有这个 instanceId,链路根本不存在 | 重试永远不会成功;补 peers 映射(patch 层改完即生效,见「寻址」一节),或改用 interconnect_peers 列出的 id | | resume-refused | 请求了唤醒,但对端不允许(allowResume: false) | 再请求也没用;不要改投其它 session,报告失败 | | resume-failed | 允许唤醒且尝试了,但没得到 live agent(无此持久化 session,或被别的 owner 持有) | 不要改投其它 session;报告失败 | | session-owned-by-subagent | 该 session 属于 subagent 路由,投递权在它的父 agent | 通过父 agent 触达,别直接投 | | message-too-large | 编好的帧超过链路帧上限(1 MiB),什么都没写、链路也没断 | 缩短文本,重发原文没用 | | no-sender-known | reply 指向的本地 session 从未记录过发送方(它没收到过带 sender 的消息,或对端版本过旧没带 sender) | 先用 interconnect_send 主动建立联系 |

reason 恰好在 delivered 为 false 时出现。

后两种加上 peer-not-configured 是本机产生的原因,不会从线缆上来:msgResultSchema 只接受 接收方能回答的原因,所以这三条只可能出自本实例自己的服务(对端无法借它们伪造一个本地判定)。

只有真的「尝试唤醒但失败」才是 resume-failed。没装 api-proxy 的部署里,agent lookup 退化成一次 registry 查询、根本没有唤醒能力,这时报 session-not-live——否则会让调用方去重试 一个永远不可能成功的操作。

subagent 会话不可直投

interconnect_send 不会往 subagent 拥有的 session 里投递,interconnect_list 也不会把它们 列出来。那类 session 的投递权属于它的父 agent,从这里 splice 进 inbox 会和父 agent 抢。判定 逻辑镜像 Host 的 hasApiSessionSubagentOwner(@deepseek-ai/dsh-api-session-controller): Host 在 0.6 之后把这个谓词从 @deepseek-ai/dsh-api-remotes 移走,且没有公开导出——桌面端和 npx @deepseek-ai/dsh web 运行时里没有任何可 import 的 Host 绑定,所以这里逐字复制一份 (isSessionOwnedBySubagent,见 src/interconnect/index.ts)。这是安全规则,Host 改动该规则 时必须同步此副本;tests 覆盖 origin=subagent 与 parent-owned 两条封栏分支。

已实测:起一个真实 subagent 后,interconnect_list 不包含它;直接 send 到它的 id 返回 session-owned-by-subagent,消息没有进入 inbox。

唤醒离线 session(resume,默认开)

投递目标是一个「已持久化但没有运行 agent」的 session 时默认会唤醒它:不带 resume 时 interconnect_send 与 interconnect_reply 都按 resume: true 处理;传 resume: false 才保持原样并返回 session-not-live。

默认开启是 2026-10-23 的决定。 实测确认:消息投递到 session 后会触发一次完整的 agent 回合——wakeDriver() → kick() → turn() → llm.stream(),即一次计费的模型调用,且 assembly 里带着该 session 的完整工具集。默认关闭时,最常见的结果不是省下这一次调用,而是 投递失败后发送方改投另一个正在运行的 session:原会话永远收不到,别的会话收到一段不属于它的 内容。目标通常是用户关心的那个会话,只是它此刻没在跑;所以默认唤醒它,把「不唤醒」留给明确 这样要求的调用方。

两侧的控制权不变:

  • 发送方可逐条用 resume: false 关闭唤醒
  • 接收方用 Config.allowResume(默认 true)一票否决——因为花钱和跑工具的是它那台机器; 拒绝时在跑 lookup 之前就短路,回 resume-refused

唤醒不是调本插件的 ctx.agents.resume(),而是走 Host 已配置的 agent lookup (typert.lookups.get('agent'))。这一点是关键:resume() 返回的 handle 由调用方 context 拥有,实测确认插件 fiber 被 dispose 时会把 resume 出来的 agent 和 session 一起 拆掉(同一调用改用根 ctx 则两者都存活)。交给 Host 的 resolver 之后 owner 是 api-proxy, 而且它会按 session 日志里记录的 preset 重建工具集——不是空壳。

没有 Host lookup 的部署(headless、无 api-proxy 的 profile)会降级为 session-not-live, 不会报错。

唤醒只把消息放进 inbox,是否真的开始处理取决于 delivery:

# 默认:唤醒并让对方实际处理(会起一个计费回合)
interconnect_send(instanceId="peer", sessionId, text, delivery="followup")

# 唤醒但不起回合:只写入上下文,等对方下次被唤醒时一起读
interconnect_send(instanceId="peer", sessionId, text, delivery="inject")

# 不唤醒:目标保持原样,返回 session-not-live
interconnect_send(instanceId="peer", sessionId, text, resume=false)

已实测:唤醒(默认开启)+ delivery="inject" 之后目标从非 live 变 live (interconnect_list 计数 +1),且该 session 日志里只多一条 agent/inbox/spliced、 后面没有 turn/start。

已知限制:磁盘格式过旧的 session 无法唤醒,返回 resume-failed。这不是本插件的限制—— Host 自己的 resume 路径对同一个 session 报 SessionFormatUnsupportedError,同样失败。

投递模式

delivery 的三个取值各自对应一个 Agent 方法,即 (inbox target, wakeup) 组合:

| 模式 | inbox target | 唤醒 | 行为 | |---|---|---|---| | followup | next-turn | 是 | 排队成独立一轮,等接收方当前那轮结束 | | steer | next-step | 是 | 插进运行中那轮的最近 step 边界,不等整轮结束;接收方 idle 时起新一轮 | | inject | next-step | 否 | 只写入上下文,不唤醒 idle 的 agent,可能一直不被读到 |

紧急程度属于单条消息而非整条链路,所以发送方可以按消息覆盖接收方的默认模式;不带 该字段时沿用接收方 Config.delivery 的配置。SendResult.delivery 回报实际生效的 模式,发送方据此判断覆盖是否被采纳。

配置

interconnect 行的 config(全部可选,下表为默认值):

| 字段 | 默认 | 说明 | |---|---|---| | instanceId | 'dsh' | 本实例自报的 id,出现在 ping/send/list 的回包里。也作为「对端如何寻址到我」的身份 | | requestTimeoutMs | 10000 | 出站请求超时,上限 60000 | | peers | {} | 对端路由映射:{ [对端 instanceId]: 我拨向它的 origin },例如隧道端点 http://127.0.0.1:13080。激活时对每个对端自动建持久 WS 链;send/reply/ping/list 都按 instanceId 走对应链接。改这一行不需要重启:profile patch 层的改动会重建 fiber 并按新映射建链 | | delivery | 'followup' | 入站消息未带 delivery 时的默认投递模式 | | allowResume | true | 是否允许发送方用 resume 唤醒本机的离线 session |

- id: interconnect
  config:
    instanceId: my-box
    peers:
      peer-a: http://127.0.0.1:13080   # 我拨向对端 peer-a 的 origin
      peer-b: http://127.0.0.1:13081
    delivery: followup
    allowResume: false   # 拒绝一切唤醒请求

鉴权用的共享密钥不在这里,而是取自 credentials 的 DSH_INTERCONNECT_TOKEN(fail-closed: 没有 token 时端点返回 403)。

安装

本包已发布到 npm:dsh-interconnect。 本仓库是一个 DSH profile bundle(根 package.json 声明 dsh.bundle.patch 指向 根 cordis.patch.yml,后者 insert 三个插件行)。

# 从 npm
dsh plugin --profile <name> add dsh-interconnect

# 或从本地路径(已实测)
dsh plugin --profile <name> add file:/path/to/dsh-interconnect

registry 上的 tarball 自带 lib/*.js 与 lib/types/**/*.d.ts,安装时不跑构建。

dsh plugin add 会把仓库识别为 bundle 并追加进 profile 的 dsh.profile.bundles。这次改动 落在组合层:@deepseek-ai/dsh-base 插入的 hmr 行监听 profile 的 package.json, manifest 一变就重读 dsh.profile.bundles 并重算 patch 层,所以新增的行会在几秒内插进 运行中的树并激活,不需要重启。已实测:安装前 GET /interconnect/link 返回 404,安装后 6 秒内同一请求返回 403(插件 fail-closed 的应答)。

插件代码的更新属于另一层,必须重启该实例:hmr 的模块监听根在 base 里是空的 (root: [])且跳过 node_modules,已经导入的模块不会重载。升级已装插件、或在 file: 安装下重建 lib/,都落在这一类。没有 profileContext 的启动方式(hmr 行被禁用)也只能靠 重启。

file: 安装还有一个文件粒度的差异,来自 pnpm 用硬链接装本地目录依赖(实测所装副本的 link 数:lib/** 为 2,其余为 1):lib/** 是硬链接,仓库里重建后 profile 里那份跟着变, 重启即可生效;assets/、dsh.plugin.json、package.json、cordis.patch.yml 是安装时刻 的副本,改仓库不会传播。skill 正文来自 assets/,所以「只重建 + 重启」会得到新代码配旧 skill 正文,profile 的 package.json 里还可能写着很久以前的版本号。要整体刷新就用重装:

dsh plugin --profile <name> remove dsh-interconnect
dsh plugin --profile <name> add file:/path/to/dsh-interconnect

两端实例用相同的 DSH_INTERCONNECT_TOKEN 作为共享密钥,任选一种来源:继承的进程环境变量、 $DSH_HOME/.env、或 $DSH_HOME/.credentials.yaml 的 refs 段(该 provider 由 @deepseek-ai/dsh-base 挂载;优先级为 继承的环境 > credentials 文档 > 调用目录的 .env > $DSH_HOME/.env):

version: 1
refs:
  DSH_INTERCONNECT_TOKEN: <两端相同的共享密钥>

两台机器最小可用

下面用 A、B 两台机器说明最短路径:A 要主动发消息给 B,B 也要能回信。五步里前四步两端都要做, 漏掉哪一步就会停在对应的失败原因上。

1. 两端各装一次

dsh plugin --profile <name> add dsh-interconnect

由 dsh 启动的实例几秒内自己热加载(见上一节),不用重启。

2. 两端设同一个共享密钥

按上一节任选一种来源,两侧的值必须逐字节相同。任一端没配 token 时端点 fail-closed,直接 403, 另一侧只会看到链路 down。

3. 打通网络:把 B 的端口映射到 A 的本机端口

链接路由 /interconnect/link 挂在 B 的宿主 webserver 上,也就是 B 那个 DSH 实例监听的端口 (dsh web 就是 GUI 的端口)。同一台机器上就是回环地址;跨机器时先建立隧道:

# 在 A 上执行:B 的 3080 变成 A 本机的 13080;-N 不执行远程命令
ssh -N -L 13080:127.0.0.1:3080 <B 的用户@主机>

只用 A→B 单向发消息时,这一条就够了。要让 B 也能主动发给 A,在 B 上反向再建一条(把 A 的端口 映射成 B 本机的 13081)。

4. 两端各配一遍 peer

改 profile 的 patch 层(不是插件包内的文件;interconnect 行由 bundle 插入,这里按键名覆盖它的 config):

# A: $DSH_HOME/profiles/<name>/cordis.patch.yml
- id: interconnect
  config:
    instanceId: a
    peers:
      b: http://127.0.0.1:13080   # key = B 自报的 instanceId;value = A 拨向 B 的 origin
# B: $DSH_HOME/profiles/<name>/cordis.patch.yml
- id: interconnect
  config:
    instanceId: b
    peers:
      a: http://127.0.0.1:13081   # 没有这一条,B 能收消息但回信会得到 peer-not-configured

改完不用重启:patch 层的改动会重建 fiber 并按新映射建链。key 建议与对端自报的 instanceId 一致,否则 B 拨入的那条链路在 A 侧认不出是同一个 peer,事件会重复一份(服务会 warn 一次)。

5. 按顺序验证

  1. interconnect_peers —— 应看到 b 且链路 up。
  2. interconnect_ping(instanceId="b") —— 应答里是 B 的实例 id。
  3. interconnect_list(instanceId="b") —— 列出 B 上 live 的 session。
  4. interconnect_send(instanceId="b", sessionId=..., text=...) —— 投递成功。
  5. B 侧 interconnect_reply(text=...) 回信,A 侧收到续文。

停在哪一步就回查哪一步:unreachable(configured: true)回查第 2、3 步(token、隧道); configured: false / peer-not-configured 回查第 4 步(映射里没有这个 key);链路一直 down 时, 对端日志里对应的是 401/403,回查第 2 步(两侧 token 是否都有值且逐字节相同)。

开发

依赖 公开的 DeepSeek Harness monorepo 作为 sibling checkout:package.json 的 devDependencies 用 link:../dsh/... 指向它, peer 依赖由该 checkout 提供,构建与测试都跑在这份源码上。

ln -s /path/to/deepseek-harness ../dsh
pnpm install --config.auto-install-peers=false   # peer @deepseek-ai/dsh-* 由 sibling checkout 提供
pnpm run check    # typecheck + test + build
pnpm run build    # esbuild → lib/

架构说明

  • 三个插件都挂在 host composition:interconnect 是跨 session、跨机器的进程级 服务(有 HTTP/WS 端点),必须 host 级;tool-interconnect 和 skill-interconnect 也放 host,因为 interconnect 未做 TypeRT @Remote/Gateway 绑定,放进 agent preset 的 isolate realm 会导致工具/技能行无法 inject 到该服务。
  • 服务只 inject agents 与 credentials;宿主 webserver 是可选的。有 webserver 时入站 升级路由注册在它上面(webServer 经等待式 inject fiber 接入,与组合顺序无关);没有 webserver 的 profile(例如 headless)里服务仍会拨号已配置对端并经这些出站链接投递,即 仅出站模式。dsh.plugin.json 的 inject 与之一致,只有 agents、credentials。
  • ws 是运行依赖,由宿主的 node_modules 提供(构建时 external)。

验证

  • 179/179 单测通过(服务 + 工具 + skill,含 wire 校验、畸形帧、心跳/池清理、结果帧按 kind 绑定与投影、list 的行数与字节双上限及满页提示、peer 状态与「未配置」分支、本机原因不得从线缆上来的负例等回归用例); 类型检查、构建均干净。
  • 已在两台机器之间实测双向互通:消息投递、WebSocket 事件推流、以及 agent 经 interconnect_send 工具反向回发,均验证通过。
  • CI(GitHub Actions):clone 公开 DSH 仓库作为 sibling,跑 pnpm run check。
  • 已发布版本:从 registry 下载的 tarball 与本地构建 shasum 一致;干净消费端 解析 .、./tool-interconnect、./skill-interconnect 入口的类型均通过,负例(把 string 赋给 number)如期报 TS2322。
  • 投递消息以 source: { kind: 'plugin', plugin: 'dsh-interconnect' } 落库,而不是 { kind: 'user' }。负例:把该 source 改回 kind: 'user',对应断言转红。 注意 source 不进模型上下文——只有 role 和 content 会,而 role 是 user。 所以这个字段的价值在持久化日志与 UI 归因,接收方的模型本身分辨不出消息来自插件。 同理,reply 的记录与寻址依赖 sender,它同样不进模型上下文。
  • reply 双向多轮延续在同一测试进程内用真实 WS 双向回环验证(A→B→A→B),不是 mockout。
  • 每个行为改动都配负例对照(删掉实现使对应断言转红),而不只是「测试通过」。 例如:去掉 peers 激活时的自动 link(),3 条「自动建链投递 / ping/list / reply 回环」 测试转红;去掉 requestTimeoutMs 的定时会违反「超时即 unreachable」的契约。
  • WS + instanceId 传输在真实 WS 链路上验证:接收方只暴露 upgrade 路由、不暴露任何 HTTP, 一条 send 仍投递成功——排除了「其实走了 HTTP 回退」的误判(0.9 起根本没有 HTTP)。

许可

MIT,Copyright (c) 2026 Chinesezjc。