Skip to content

dsh-connection-card-host

Verified

@noob-stupid/dsh-connection-card-host · v1.0.71 · MIT · Web UI

会话连接卡片宿主:在DSH会话间建立有状态连接,挂载连接级卡片插件

Install

dsh plugin add @noob-stupid/dsh-connection-card-host

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Tags

Readme

dsh-connection-card-host

English | 中文

把「连接」做成 DSH 里的一等对象:会话是节点,连接是容器,卡片是连接级插件 —— 一个「连接级插件宿主」。

为什么是这个插件:别的插件把能力写死在插件里;这个插件让能力变成「连接上可安装的卡片」—— 装、卸、隔离都是连接粒度的。

在一个 DSH 里同时开着好几个会话(一个查资料、一个写代码、一个跑实验)是常态。 缺的不是"会话之间能互相看见"这个功能,而是 DSH 的会话之间可编程的关系层: 没有一个可以挂东西的"关系对象",权限边界、共享前提、可装载的能力就都无处安放。

连接是平台对象:会话 A 与会话 B 之间是一条连接,连接上有事件总线、权限边界、公约盒与卡片宿主;卡片挂在连接上,受连接权限约束,连接经适配层调用 DSH

在这个关系层之上,连接的两端互相看得见、说得上话、共用得上工具 —— 而互不打扰。

实际操作(录屏)

从输入框左侧锚点拖出连线

交互结构(架构图)

拖拽建连的结构:锚点 → 会话行 → 竖轨

左:真实操作 · 右:同一动作的交互结构示意,标出了「开关语义」


目录


它能做什么

互相看得见 查得到对方正在改哪个文件、计划进行到第几步、最近用了什么工具 —— 自动采集,对方不需要专门告诉你
说得上话 给对端发消息,紧急度自己判断:只告知(不打断)/排队/插话/抢占式中断(第四档,默认关闭)
共用得上工具 连接上可以挂卡片:卡片能给会话提供工具,甚至带几十 MB 的真依赖
共用前提 「公约盒」存放双方说好的事:接口、单位、命名、分工边界
互不打扰 感知是拉取式的 —— 对方不查就零成本;不相关的连接不会吵到你

同一个关系层的两个例子:A 问 B 现在在干什么;或者 B 交给 A 一个只存在于这条连接上的工具。


三十秒上手

  1. 建连接:按住输入框左侧的圆点(或会话行上的「…」),拖到左侧会话列表里的某一行。

起手式与开关语义

  • 输入框左侧的圆点 → 拖到某一行
  • 会话行上的「…」→ 拖到另一行
  • 落点即开关:未连的行 = 连接;已连的行 = 断开(悬停时会提示)
  • 也可以从侧栏「连接」面板里选两个会话

从会话行拖 → 连上之后(录屏)

从会话行拖出连线,松手后出现竖轨与权限彩点
  1. 完事。两端各自收到一条静默通知(说清了连上了谁、能做什么),不打断任何人。
  2. 想看得更细:点侧栏「连接」,或者让会话自己调 connection_peer_work。

连接建立后,会话行右侧会出现竖轨,两端各一个彩色圆点 —— 那是该方向的权限:

连接面板:每个连接的权限可以两个方向分别设置,卡片、感知、约定都在同一条连接下

连接会持久化:下次 DSH 启动时自动恢复。


三个层次:感知 / 约定 / 传话

这三层是分开的,因为它们的代价差别很大:

层 机制 进对方上下文吗 迫使对方行动吗 成本
A 工作状态 拉取(对端主动查) 只在它查的时候 ❌ 不会 0
B 公约盒 拉取(对端主动查) 只在它查的时候 ❌ 不会 0
C 传话 推送(进对方收件箱) 无条件 ✅ 必然 每条都花

关键事实:在 DSH 里投递一条消息 = 迫使对方跑一轮(agent loop 没有"看到但不理"这个状态)。 所以「说话」和「感知」必须分开做 —— 想要对方知道,用 A/B;想要对方做事,才用 C。

A. 工作状态(自动,0 成本)

自动从运行事件里采集,不要求模型额外产出任何东西:

【session-1a2b3c4d】
状态:正在执行命令(2 秒前)
最近动过的文件:<workspace>/src/example.js
进度:第 12 轮 / 第 4 步

B. 公约盒(显式,0 成本)

双方说好的标准:接口签名、单位、坐标系、命名、谁的活归谁。 面板上可增删;会话用 connection_conventions / connection_declare 读写。

C. 传话(四档紧急度,由发起方判断)

档位 底层 对方会怎样
quiet inject 放进上下文但不唤醒 —— 它下次干活时看到,不被打断
normal followup 排队 —— 处理完手头的事就看到
urgent steer 插话 —— 插进它正在跑的那一轮,当场读到
preempt steer + 可选 cancel 抢占 —— 打断它正在跑的这一轮(第四档,默认关闭;不满足条件时自动退化为 urgent,消息照样送到)

urgent 而对端空闲时自动降级为排队(下一轮立刻开始,效果等同即时),不会失败。

preempt 的四个条件 —— 必须全部满足,否则退化为 urgent 投递,永不失败:

条件 取值
连接权限 需要写权限(只读连接不能停别人的活)
频率上限 每个连接 5 分钟最多 1 次
对端正执行工具 绝不打断 —— 跑一半的工具被 cancel 会留下悬空调用
对端空闲或状态未知 不打断,只投递

preempt 是破坏性的:被打断的那一轮,已经做的工作白费。 取消一轮时必须带 keepInbox —— 它默认清空收件箱,会把用户自己排队的输入和别的会话发来的消息一起丢掉。


连接上的卡片

卡片 = 挂在连接上、且能分端可见的插件。

DSH 插件 卡片
装在哪 整个 DSH(profile) 一条连接
谁能调 所有会话 只这条连接上的会话
可见性 全局 可分端:两端 / 仅 A / 仅 B
生命周期 随 DSH 起停 随连接上的装载卸载

会话用一个常驻桥接工具,按需发现并调用卡片提供的工具,且受可见范围约束

卡片能给会话提供工具

卡片里 api.registerTool(name, fn) 注册的工具,连接上的会话可以用 一个常驻桥接工具按需发现并调用:

connection_card_tool                                  ← 唯一常驻的(1 个 schema)
  ├─ 不传 tool → 列出本连接上「对你在的这一端可见」的卡片与工具
  └─ 传 tool   → 调用它

为什么是一个桥接、而不是每个工具各占一个 schema:后者会让每个会话都为 每个卡片工具付常驻成本,而卡片是随连接动态装载的。桥接只占 1 个, 而且天然能在桥接层强制可见范围。

卡片里可以塞真依赖

卡片可以自带依赖(几十 MB 也可以),装在 $DSH_HOME/connection-cards/cards/, 不碰 DSH profile。

面板内安装与更新

卡片选择器:内置卡片一键加;也可以给包名、仓库 tgz 地址或本地目录来安装

卡片有三种来源 —— 与连接面板里卡片选择器的三种来源一致:

来源 给什么 实现
包名(注册表) monitor-card / @scope/monitor-card 从 registry 拉 tarball(一次 HTTPS GET)
仓库 tgz 地址 https://example.com/card.tgz 下载后解压
本地目录 D:\my-cards\monitor-card 直接拷贝
  • 装:包名 / 仓库 tgz 地址 / 本地目录 → 装进我们自己的目录,不跑 pnpm、不改 profile; 装完立刻可用,不需要重启 DSH
  • 更新:已安装的卡片带「检查更新」入口,三态分列
    「检查更新」→「↑ 更新到 x.y.z」/「已是最新」/「无法检查」
    
    "无法检查"绝不显示成"已是最新" —— 那是谎报。
  • 卸:已安装的卡片带卸载入口,点击前说清会移除什么(包括它在各条连接上的卡片实例)并确认一次。 内置卡随插件分发,不给卸载。卸载结果如实回报:清了什么、什么还被占用没清掉。

声明:第三方 / 社区卡片可以直接下载安装,装完即用

可以在内部直接下载安装外部 DSH 会话相关的插件卡片,并立即使用。 这不是"官方卡片市场",也不是一个需要审核上架的中心 —— 它是开放的分发: 任何按卡片协议写出来的包,都能从上面三种来源装进你自己的 DSH。

装完即可用 —— 卡片装到一条连接上之后:

  • 连接上的会话立刻能用它提供的工具(经 connection_card_tool 桥接调用)—— 不需要重启 DSH,不需要改 DSH 配置:不跑 pnpm、不写 dsh.profile.bundles
  • 卡片装在自己的目录 $DSH_HOME/connection-cards/cards/<id>/,与 DSH profile 完全隔离

第三方 / 社区卡片与 DSH 官方项目没有隶属关系;本插件不提供审核、背书, 也不存在"官方目录"这种东西。


卡片能做什么 / 不能做什么

卡片就是普通 DSH 插件,所以不是每个插件都适合当卡片。一个候选能不能挂到连接上、 挂上去之后有多少能用,由下面这张规格表决定。候选列表会提前标出 适配 / 能力 / 局部 / 全局 / 未判定,但只标注、不阻断 —— 是否挂载始终由你决定, 判定本身是启发式的。

挂载:三扇门

门 要求 不过会怎样
① 作用域 注册到连接级位置(conversation.* / message.* / input.*)或纯能力(无客户端半边) 注册到 App 级位置(侧栏 / 布局 / 设置页 / 主题 / 标题栏 / 工作区)⇒ 不建议当卡片:全局 UI 塞进连接级面板既装不下、也会和 App 布局打架
② 模块 宿主入口的 import 闭包内依赖必须全部可解析 拒绝挂载,并分类说清:致命(缺宿主能力,改设计才行)/ 可选(缺第三方依赖,补上就能过)
③ 能力 inject ⊆ {tools, effect, llm, prompt} 拒绝挂载 —— 它要的是本插件没有的宿主服务。拒绝是正确行为,答案不是放宽能力面

② 的扫描面:只看入口真正会加载到的文件 —— tests/ bin/ client/build.mjs *.d.ts 里的 import 不算运行时依赖。

不跑 pnpm、不执行构建:宿主不装依赖、也不构建任何包。所以作者没提交构建产物 (只有源码)的包挂不上 —— 拒绝原因会写明这一点。

客户端 UI 能到哪一层

能挂  ≠  能取源码  ≠  能注册槽位  ≠  能渲染
情况 结果
只用 slots / effect 能渲染 ✓
依赖客户端 hook(useScene / useEnabled / locale / configForms …) 挂得上、槽位也注册了,但渲染失败 —— 显示原因、不白屏 ✓
客户端制品上限 32 MB(自包含 bundle 到几 MB 是正常的:内联字体/素材)
组件的调用方式 createElement(component, {}) ⇒ 不给 props:需要槽位 props 的组件会报错,被错误边界接住

支持 / 不支持

支持 不支持
宿主工具 api.registerTool(name, fn) → 会话经 connection_card_tool 调用 把工具注册进 DSH 全局 Loader
面板 可选的面板 HTML(宿主侧渲染后取回) 面板里执行任意浏览器侧代码
客户端 UI 只用 slots / effect 的组件 需要客户端 hook、或需要槽位 props 的组件
连接级事件 on(event, handler) / emit(event, data) 跨连接的事件
消息 send(kind, text) / read()(以某一端身份) 会话内容的自动镜像(见配置项)
依赖 卡片自带的任意依赖 在你机器上跑 pnpm 或构建步骤
分发 registry / 仓库 tgz / 本地目录 审核上架的中心或市场
隔离 分端可见、版本门控、import / apply 崩溃隔离 卡片抛异常拖垮宿主

明确写出的边界

边界 含义
分端可见 可见范围分端:两端 / 仅 A 端 / 仅 B 端。A 端可见 ≠ B 端可见;不可见的一端硬调会被拒绝,并说明原因
版本门控 DSH 版本不匹配会被明确拒绝并说明原因,而不是装上再崩。卡片另有一条 CardAPI 版本护栏(要更高版本的卡片会被拒绝装载并说明原因)
崩溃隔离 卡片 import / apply 抛异常不会拖垮宿主
不重写 DSH 不碰 DSH 的通信、权限、插件系统;卡片不注册到 DSH 全局 Loader

架构与成本

三层,边界很硬 —— 每层只跟下一层说话:

┌─────────────────────────────────────────────────────────────┐
│  卡片层(连接级插件)                                        │
│  只依赖 CardAPI,绝不 import @deepseek-ai/*                  │
│  → DSH 升级不影响卡片;我们改 CardAPI 才影响(有版本护栏)    │
├─────────────────────────────────────────────────────────────┤
│  连接层(本插件)                                            │
│  连接 / 权限 / 感知 / 公约盒 / 卡片宿主 / 消息投递            │
│  → DSH 升级时,只需要改这一层                                 │
├─────────────────────────────────────────────────────────────┤
│  DSH 适配层(DSHAdapter + 白名单 + 审计)                    │
│  所有 DSH 交互的唯一出口                                      │
└─────────────────────────────────────────────────────────────┘

权限是分方向的

一根线两端各一个圆点,颜色是那个方向的权限 —— 两个方向互不影响, 可以做成「甲能发、乙只能看」:

连线颜色表示该方向的权限:灰=只读,蓝=可建议,橙=可写入;两端可不对称

该方向的权限 允许什么
只读 感知与公约盒(两层都是拉取式,与权限无关)
可建议 不修改对端工作的传话
可写入 能改变对端行为的传话,包括 preempt

降低权限立即生效;提高权限需要被授权的一方确认。拒绝会说明原因。

成本

项 成本 说明
感知(A + B 两层) 0 上下文 拉取式;对端不查就不产生任何成本
卡片工具 1 个常驻 schema 而不是每个卡片工具各占一个
不相关的连接 0 打扰 搭线不唤醒;不相关的会话照常干活
常驻工具 schema 5 个 connection_*,对没有连接的会话隐藏 工具面按会话 scope 过滤:一条连接都没有的会话不背这份 schema

有至少一条连接的会话,会带上这 5 个 connection_* 工具的 schema。


安装

npm(推荐,可锁版本):

dsh plugin --profile web add @noob-stupid/dsh-connection-card-host

固定版本:用 Releases 的 tgz 附件:

dsh plugin --profile web add https://github.com/Noob-stupid/dsh-connection-card-host/releases/download/<tag>/noob-stupid-dsh-connection-card-host-<version>.tgz

# 同一份 tgz 先下载到本地再装,效果相同
dsh plugin --profile web add ./noob-stupid-dsh-connection-card-host-<version>.tgz

GitHub 直装(装默认分支最新提交,不是固定版本):

dsh plugin --profile web add github:Noob-stupid/dsh-connection-card-host

# 同上,GitHub 简写(可省 github:)—— 带斜杠就走 GitHub 仓库
dsh plugin --profile web add Noob-stupid/dsh-connection-card-host

前置条件

  • pnpm 在 PATH 上 —— dsh plugin 会调它。
  • 兼容性:peerDependencies 声明 @deepseek-ai/dsh >=0.2.0-rc.1 <0.3.0 (另有 @deepseek-ai/cordis 与两个 @deepseek-ai/dsh-client-*)—— DSH 会在安装时按版本门控,不匹配会明确拒绝并说明原因(而不是装上再崩)。
  • 这四个 peer 都标了 peerDependenciesMeta.optional:这样按包名安装只增加 1 个包, 不会把 @deepseek-ai/* 依赖树拖进你的 profile。本包运行时一个 @deepseek-ai/* 都不 import(浏览器端那两个由 DSH 的 __ModuleLoader__ 注入),标 optional 不改门控 —— DSH 的 evaluatePluginCompatibility 只读 peerDependencies。 细节见 docs/compatibility.md。
  • lib/ 已随仓库提交并在 npm 包里带上:装完即可用,不需要构建步骤, 本包也没有需要授权的构建脚本。

怎么用

建连接 —— 三十秒上手写了两个拖拽起手式和侧栏面板。落点即开关; 连接会在重启后恢复。

设权限 —— 在连接面板里按方向分别设置。降低立即生效;提高需要对方确认。

让会话干活 —— 连接建立后,会话拿到 connection_* 这一组工具:

工具 用途
connection_peer_work 读对端工作状态(A 层)
connection_conventions 读公约盒(B 层)
connection_declare 写公约盒(B 层)
connection_send 发消息,带紧急度(C 层)
connection_card_tool 列出并调用本连接上卡片提供的工具

感知类工具(connection_peer_work、connection_conventions)是只读的,改不了对端。


配置项

本插件没有配置文件,也不接收任何设置 schema。可配置的东西都在界面上配, 其余的都在同一个目录里:

位置 存什么
连接面板(侧栏) 连接、分方向权限、公约盒、卡片挂载
卡片选择器(连接面板内) 卡片的安装与更新
$DSH_HOME/connection-cards/ 全部持久状态:connections.json、已安装的卡片、防篡改的 audit.log

有两个行为刻意默认关闭,需要一次明确动作才打开:

行为 默认 怎么打开
preempt(抢占式中断) 关 需要该连接上的写权限(见 C 层)
第三方卡片适配 关 创建 $DSH_HOME/connection-cards/adapter.enabled;删掉该文件即关闭

会话内容的自动镜像永久关闭 —— 除非会话主动调 connection_send,否则不在会话之间转发 任何东西。感知(A/B 两层)是拉取式的替代方案。


卡片开发

一张卡片就是一个带 dshCard 清单的 npm 包:

{
  "name": "my-card",
  "version": "1.0.0",
  "main": "index.js",
  "dshCard": { "id": "my-card", "name": "我的卡片", "entry": "index.js", "api": 1 }
}
// index.js —— ⚠️ 绝不 import 任何 @deepseek-ai/*,所有交互走 api
export function apply(api) {
  api.log(`已装载(scope=${api.scope})`)

  // 给连接上的会话提供工具
  api.registerTool('greet', async (args) => {
    return `你好,${args?.name ?? '世界'}`
  })
}

// 面板 HTML(可选)
export function renderPanel(api) {
  return `<div>可见范围:${api.scope}</div>`
}
api 成员 说明
registerTool(name, fn) 注册工具 → 会话经 connection_card_tool 调用
send(kind, text) / read() 以某一端身份收发连接消息
on(event, handler) / emit(event, data) 连接级事件
scope 本实例作用在哪一端(both / a / b)
log(...) 写宿主日志

dshCard.api 是卡片声明自己需要的 CardAPI 版本(缺省 1):

  • 加东西不升版本 —— 老卡片照常跑
  • 删或改语义才升版本 —— 宿主会拒绝装载要更高版本的卡片,并说明原因

这是我们自己的兼容性护栏:卡片不依赖 DSH 内部包,所以 DSH 升级不影响卡片; 但我们改 CardAPI 会影响卡片,而 DSH 的版本门控管不到这一层。

细节见 docs/card-protocol.md。


卸载

移除插件

dsh plugin --profile web remove @noob-stupid/dsh-connection-card-host

移除它的状态 —— 插件的所有状态都在一个目录里,所以删掉它也就删掉了连接、已安装的卡片和审计日志:

rm -rf "$DSH_HOME/connection-cards"

如果你打算重装并保留连接,就别删这个目录。


FAQ

没有连接能用卡片吗? 不能。卡片挂在连接上,只有这条连接上的会话能调它提供的工具。

挂卡片会动我的 DSH profile 吗? 不会。卡片装在 $DSH_HOME/connection-cards/cards/<id>/ 下;不跑 pnpm、不写 dsh.profile.bundles、不重启。

为什么某个常用插件挂不上? 大概率是卡片能做什么 / 不能做什么里的 ② 或 ③ 门没过。 拒绝信息会写明是哪扇门、为什么。最常见的原因是包只提交了源码、没提交构建产物 —— 宿主不构建任何东西。

卡片挂上了但界面上什么都没有。 看客户端 UI 那张表:需要客户端 hook 的卡片槽位注册成功但渲染失败。 面板会显示失败原因,不会白屏。

连接会额外花我的上下文吗? 感知(A/B 两层)是拉取式的,会话不问就不花。一条连接都没有的会话不背任何 connection_* 工具 schema;有连接的会话带这 5 个;卡片工具合计只占 1 个桥接 schema。

对端会话离线时还能用吗? 消息照样送到,但对端界面上显示为普通消息而不是连接卡片,内容开头会标明这一点。 东西不会丢。

第三方卡片有人审核吗? 没有。没有市场、没有审核、没有背书 —— 只从你信得过的来源装。


文档

文档 内容
docs/capabilities.md 能力报告:逐项结果、上下文成本总账、已知限制
docs/card-protocol.md 卡片协议:清单、CardAPI、安装校验、分发来源
docs/compatibility.md 兼容性:DSH 版本门控机制、两条防线
docs/adapter-api.md DSH 适配层:稳定接口与白名单

维护

本仓库是稳定门面,只在发版时按晋级流程同步。开发发生在预览线仓库 dsh-connection-card-host-preview。

改这个插件时
  • lib/ 随仓库提交,CI 会断言源码与构建产物对齐(scripts/ci/check-src-lib-parity.mjs), 所以改了 src/ 必须同时更新重新构建出来的 lib/。
  • 宿主侧改动要重启 DSH 或重载插件才生效;客户端侧改动还需要刷新页面。
  • CI 有四道硬门槛:语法检查、单元/契约测试 + 产物对齐、插件 patch 清单、 被跟踪文件的本机路径扫描。
  • npm test 不需要任何依赖、不需要网络。

MIT · 与 DSH 官方无隶属关系