开放智能体网络 NelsonLongxiang/dsh-open-a2a-net
DeepSeek Harnessv0.6.31为 DeepSeek Harness 开放 A2A 网络插件:签名智能体卡片、去中心化对等节点与区域发现、直连路由模型工具,以及网页侧边栏里可加入的会话节点
38.2/100
综合分
6
GitHub 星标
1,321
npm 周下载
—/5
用户评分
数据截至 2026/10/31 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add @nelsonlongxiang/dsh-open-a2a-netnpm 源 · @nelsonlongxiang/dsh-open-a2a-net 的发布者账号已校验与本仓库一致
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
dsh 原生插件 · browser
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 4 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查✓ 自动检查通过展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✓npm 包@nelsonlongxiang/dsh-open-a2a-net @ 0.6.31
✓Node 引擎未声明 engines.node
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/11/1 08:18:57
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
读写/删除本地文件写入剪贴板(不计风险)发起外部网络请求
查看扫描证据(共 105 处,展示前 90 条)
- 读取本地文件lib/index.js:15
const PLUGIN_VERSION = String(JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version); - 发起网络请求lib/index.js:291
const fetchImpl = (url, init) => globalThis.fetch(url, init); - 读取本地文件lib/index.js:2477
if (!existsSync(sessionsRoot)) - 读取本地文件lib/index.js:2479
for (const bucket of readdirSync(sessionsRoot, { withFileTypes: true })) { - 读取本地文件lib/index.js:2482
for (const child of readdirSync(join(sessionsRoot, bucket.name), { withFileTypes: true })) { - 读取本地文件lib/index.js:2486
const files = readdirSync(dir).filter(name => name.startsWith('session.') && (name.endsWith('.jsonl.zstd') || name.endsWith('.jsonl'))); - 读取本地文件lib/index.js:2491
const st = statSync(join(dir, name)); - 发起网络请求lib/index.js:2799
const listResponse = await globalThis.fetch(listUrl, { signal: AbortSignal.timeout(5000), headers: { accept: 'application/json' } }); - 发起网络请求lib/index.js:2827
const response = await globalThis.fetch(target, { signal: AbortSignal.timeout(5000), headers: { accept: 'application/json' } }); - 发起网络请求lib/index.js:3728
const card = await fetch(peer); - 发起网络请求lib/index.js:4091
const card = await fetch(candidate); - 发起网络请求lib/index.js:4499
const cards = await mapBounded(peers, CANDIDATE_CONCURRENCY, async (peer) => ({ peer, card: await fetch(peer) })); - 写入本地文件lib/index.js:5143
mkdirSync(dirname(path), { recursive: true }); - 写入本地文件lib/index.js:5144
writeFileSync(path, privateKey.export({ format: 'pem', type: 'pkcs8' }), { mode: 0o600 }); - 写入本地文件lib/index.js:5159
mkdirSync(dirname(path), { recursive: true }); - 写入本地文件lib/index.js:5160
writeFileSync(path, `${id}\n`, { mode: 0o600 }); - 发起网络请求lib/client.js:112
const response = await fetch("/__dsh_a2a/state", { cache: "no-store" }); - 发起网络请求lib/client.js:433
fetch("/__dsh_a2a/state", { cache: "no-store" }).then((response) => response.ok ? response.json() : void 0).then((info) => { - 发起网络请求lib/a2a-client.js:65
const response = await this.options.fetch(`${baseUrl}${path}`, { - 读取本地文件cli/a2a.mjs:119
if (!existsSync(IDENTITY)) return undefined - 读取本地文件cli/a2a.mjs:121
return JSON.parse(readFileSync(IDENTITY, 'utf8')) - 发起网络请求cli/a2a.mjs:184
const res = await fetch(`${home()}/__dsh_a2a/cli-deposit`, { - 发起网络请求cli/a2a.mjs:197
const res = await fetch(`${home()}/__dsh_a2a/state`, { headers: keyHeaders() }) - 读取本地文件cli/a2a.mjs:251
if (!existsSync(root)) continue - 读取本地文件cli/a2a.mjs:252
for (const entry of readdirSync(root, { withFileTypes: true })) { - 读取本地文件cli/a2a.mjs:255
if (!existsSync(md)) continue - 读取本地文件cli/a2a.mjs:257
const text = readFileSync(md, 'utf8') - 写入本地文件cli/a2a.mjs:319
mkdirSync(IDENTITY_DIR, { recursive: true }) - 写入本地文件cli/a2a.mjs:320
writeFileSync(INBOX_CURSOR, JSON.stringify({ handle, at, seen }), { mode: 0o600 }) - 写入本地文件cli/a2a.mjs:500
mkdirSync(IDENTITY_DIR, { recursive: true }) - 写入本地文件cli/a2a.mjs:501
writeFileSync(IDENTITY, JSON.stringify({ - 发起网络请求cli/a2a.mjs:520
const res = await fetch(`${home()}/a2a/direct`, { - 发起网络请求cli/a2a.mjs:596
const res = await fetch(`${home()}/a2a/direct`, { - 读取本地文件lib/group-store.js:34
if (this.path === '' || !existsSync(this.path)) - 读取本地文件lib/group-store.js:37
const snapshot = JSON.parse(readFileSync(this.path, 'utf8')); - 写入本地文件lib/group-store.js:59
mkdirSync(dirname(this.path), { recursive: true }); - 写入本地文件lib/group-store.js:61
writeFileSync(this.path, JSON.stringify(snapshot), { mode: 0o600 }); - 读取本地文件lib/canvas-store.js:86
if (this.path === '' || !existsSync(this.path)) - 读取本地文件lib/canvas-store.js:89
const snapshot = JSON.parse(readFileSync(this.path, 'utf8')); - 写入本地文件lib/canvas-store.js:126
mkdirSync(dirname(this.path), { recursive: true }); - 写入本地文件lib/canvas-store.js:128
writeFileSync(this.path, JSON.stringify(snapshot), { mode: 0o600 }); - 读取本地文件lib/layout-store.js:108
if (this.path === '' || !existsSync(this.path)) - 读取本地文件lib/layout-store.js:111
this.doc = clampDoc(JSON.parse(readFileSync(this.path, 'utf8'))); - 写入本地文件lib/layout-store.js:121
mkdirSync(dirname(this.path), { recursive: true }); - 写入本地文件lib/layout-store.js:122
writeFileSync(this.path, JSON.stringify(this.doc), { mode: 0o600 }); - 写入本地文件lib/layout-store.js:150
mkdirSync(dirname(this.path), { recursive: true }); - 写入本地文件lib/layout-store.js:151
writeFileSync(this.path, '', { mode: 0o600 }); - 读取本地文件lib/joined-store.js:83
if (this.path === '' || !existsSync(this.path)) - 读取本地文件lib/joined-store.js:86
const snapshot = JSON.parse(readFileSync(this.path, 'utf8')); - 写入本地文件lib/joined-store.js:101
mkdirSync(dirname(this.path), { recursive: true }); - 写入本地文件lib/joined-store.js:103
writeFileSync(this.path, JSON.stringify(snapshot), { mode: 0o600 }); - 读取本地文件lib/exposure-store.js:32
if (!existsSync(this.path)) - 读取本地文件lib/exposure-store.js:35
const snapshot = JSON.parse(readFileSync(this.path, 'utf8')); - 写入本地文件lib/exposure-store.js:71
mkdirSync(dirname(this.path), { recursive: true }); - 写入本地文件lib/exposure-store.js:76
writeFileSync(this.path, JSON.stringify(snapshot), { mode: 0o600 }); - 写入本地文件lib/cli-deposit.js:163
mkdirSync(join(this.path, '..'), { recursive: true }); - 写入本地文件lib/cli-deposit.js:164
writeFileSync(this.path, JSON.stringify(snapshot), 'utf8'); - 读取本地文件lib/cli-deposit.js:171
if (this.path === '' || !existsSync(this.path)) - 读取本地文件lib/cli-deposit.js:174
const snapshot = JSON.parse(readFileSync(this.path, 'utf8')); - 读取本地文件lib/team-store.js:43
if (this.path === '' || !existsSync(this.path)) - 读取本地文件lib/team-store.js:46
const snapshot = JSON.parse(readFileSync(this.path, 'utf8')); - 写入本地文件lib/team-store.js:70
mkdirSync(dirname(this.path), { recursive: true }); - 写入本地文件lib/team-store.js:71
writeFileSync(this.path, JSON.stringify({ memberships: this.entries })); - 读取本地文件lib/peer-store.js:262
if (this.path === '' || !existsSync(this.path)) - 读取本地文件lib/peer-store.js:265
const snapshot = JSON.parse(readFileSync(this.path, 'utf8')); - 写入本地文件lib/peer-store.js:342
await mkdir(dirname(this.path), { recursive: true }); - 写入本地文件lib/peer-store.js:347
await writeFile(this.path, JSON.stringify(snapshot), { mode: 0o600 }); - 读取本地文件lib/task-ledger.js:513
if (this.path === '' || !existsSync(this.path)) - 读取本地文件lib/task-ledger.js:516
const snapshot = JSON.parse(readFileSync(this.path, 'utf8')); - 写入本地文件lib/task-ledger.js:605
mkdirSync(dirname(this.path), { recursive: true }); - 写入本地文件lib/task-ledger.js:610
writeFileSync(this.path, JSON.stringify(snapshot), { mode: 0o600 }); - 读取本地文件lib/idempotency-store.js:236
if (this.path === '' || !existsSync(this.path)) - 读取本地文件lib/idempotency-store.js:239
const snapshot = JSON.parse(readFileSync(this.path, 'utf8')); - 写入本地文件lib/idempotency-store.js:280
mkdirSync(dirname(this.path), { recursive: true }); - 写入本地文件lib/idempotency-store.js:282
writeFileSync(this.path, JSON.stringify({ - 读取本地文件lib/skill-registration.js:13
if (!existsSync(path)) { - 读取本地文件lib/skill-registration.js:16
const raw = readFileSync(path, 'utf8'); - 发起网络请求lib/faces/admin.js:418
const reply = await globalThis.fetch(target, { - 读取本地文件lib/faces/admin.js:493
const current = existsSync(patchPath) ? readFileSync(patchPath, 'utf8') : ''; - 读取本地文件lib/faces/admin.js:493
const current = existsSync(patchPath) ? readFileSync(patchPath, 'utf8') : ''; - 写入本地文件lib/faces/admin.js:495
mkdirSync(home, { recursive: true }); - 写入本地文件lib/faces/admin.js:496
writeFileSync(patchPath, yaml, 'utf8'); - 读取本地文件lib/faces/admin.js:676
const file = readFileSync(join(rootDir, rel)); - 读取本地文件lib/faces/admin.js:721
const file = readFileSync(join(rootDirNexus, rel)); - 发起网络请求lib/faces/canvas-notify.js:42
void globalThis.fetch(url.replace(/ $/, '') + '/a2a/canvas-changed', { - 发起网络请求lib/client/A2aControl.js:18
const response = await fetch('/__dsh_a2a/state', { cache: 'no-store' }); - 发起网络请求lib/client/index.js:16
void fetch('/__dsh_a2a/state', { cache: 'no-store' }) - 发起网络请求nexus-stage/src/main.ts:30
fetch(path, { ...init, headers: { ...(init.headers as Record<string, string> | undefined ?? {}), ...(CONTROL_KEY !== '' ? { 'X-API-Key': CONTROL_KEY } - 发起网络请求nexus-stage/src/main.ts:55
void fetch('/__dsh_a2a/owner-settings', { headers: { 'X-API-Key': v } }).then(r => { - 写入剪贴板nexus-stage/src/main.ts:254
void navigator.clipboard?.writeText(text).catch(() => {})
扫描于 2026/11/3(1 天前) · 范围 入口闭包 + 全包扫描 · 来源 npm 发布包
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/dsh-client-store@deepseek-ai/dsh-client-ui-primitives@deepseek-ai/dsh-scope@deepseek-ai/dsh-system-prompt@deepseek-ai/dsh-util-code-language@deepseek-ai/dsh-util-workspace-path@deepseek-ai/cordis@deepseek-ai/cordis-plugin-loader@deepseek-ai/cordis-plugin-timer@deepseek-ai/dsh-agent@deepseek-ai/dsh-api-session-controller@deepseek-ai/dsh-client-locale给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
界面预览
2 张 · 点击查看大图功能亮点
- 签名智能体卡片
- 去中心化对等节点发现
- 直连路由模型工具
README
由 DeepSeek 最新模型翻译生成@nelsonlongxiang/dsh-open-a2a-net
DeepSeek Harness(DSH)的开放 A2A 网络插件:把一套 DSH 部署变成去中心化智能体网络中的一个节点——没有中心服务器,也没有注册中心。
每个节点在 /.well-known/agent-card.json 发布签名、带有效期的智能体名片,通过 种子 URL 和引荐发现对等节点(每张名片列出它认识的对等节点),通过区域委派 解析名称(GNUnet GNS 风格:名片可以把一个名称委派给另一个区域),并直接路由到对等节点的团队,在可达的那一半候选里做故障转移。每个主会话还可以额外暴露为会话节点,其他主机能从网页侧边栏发现并加入。
| 侧边栏网络面板 | 上下文消息展示 |
| 侧边栏网络面板 | 上下文消息展示 |
安装
dsh plugin --profile web add @nelsonlongxiang/dsh-open-a2a-net
重启 dsh web,然后打开侧边栏网络面板(页脚操作)。
(可以写成包名、本地路径或 Git URL;包在 prepare 里跑 pnpm build,所以源码安装会在安装时构建。
展开完整文档(README 剩余 1 节)
@nelsonlongxiang/dsh-open-a2a-net
DeepSeek Harness(DSH)的开放 A2A 网络插件:把一套 DSH 部署变成去中心化智能体网络中的一个节点——没有中心服务器,也没有注册中心。
每个节点在 /.well-known/agent-card.json 发布签名、带有效期的智能体名片,通过 种子 URL 和引荐发现对等节点(每张名片列出它认识的对等节点),通过区域委派 解析名称(GNUnet GNS 风格:名片可以把一个名称委派给另一个区域),并直接路由到对等节点的团队,在可达的那一半候选里做故障转移。每个主会话还可以额外暴露为会话节点,其他主机能从网页侧边栏发现并加入。
| 侧边栏网络面板 | 上下文消息展示 |
| 侧边栏网络面板 | 上下文消息展示 |
安装
dsh plugin --profile web add @nelsonlongxiang/dsh-open-a2a-net
重启 dsh web,然后打开侧边栏网络面板(页脚操作)。
(可以写成包名、本地路径或 Git URL;包在 prepare 里跑 pnpm build,所以源码安装会在安装时构建。注意:独立的 Git 安装在 DSH 配置档之外目前构建失败——官方 @deepseek-ai/ npm 包落后于本插件编译所依赖的 harness 源码,所以请从 registry 安装,或在 harness 提供当前 @deepseek-ai 对等依赖的配置档里安装。)插件与自带的 webserver 行并列组合:它在共享监听器上注册自己的名片、状态和控制路由,在共享工具运行时上注册自己的模型工具。
你能得到什么
- 模型工具(十个;见下表)——a2a_teams 列出本节点能看到的团队(自己的行加上已加入团队范围内的对等团队);a2a_route 把一条消息发给某个团队,复用 context_id 继续远程对话,对等节点不可达时在候选间做故障转移。a2a_tasks 在三个层级上核对回执契约:每条离开后还欠一条回复的路由(异步投递、释放的等待)都留在欠账簿里记为 pending,直到它的 [A2A receipt] task <task_id> 消息对上号——超期行过了 stale TTL 会自动死信(复活的目标仍可用迟到的回执结清),每次结清都落进有界归档,而不是被悄悄驱逐,每行保留后续的 context_id,跨重启持久化。a2a_probe 测量所跟踪节点的可达性和往返延迟(每个对等节点抓取一次已验证名片,可选收窄到单个 url),用于派发前的健康检查,并标出每次未命中的阶段——unreachable(传输/HTTP)还是 rejected(名片不受信任)。
- 侧边栏控制 — Web 侧边栏底部有一个操作入口,列出本机可作为网络节点加入的会话:每行显示标题、最近活动摘要和所属团队,可就地加入/退出;冷行(已持久化加入意图、但会话尚未加载)提供唤醒操作,打开会话即重新挂载节点。会话团队名为 <team>/<id8>;Web 会话取 id 前 8 个字符,导入会话(import-<uuid>)保留 import- 前缀加 uuid 前 8 个十六进制字符,避免导入 id 塌缩成少数几个团队。面板的活动区还会列出待回执——已投递、但尚未由其 [A2A receipt] 消息关联的异步任务——与进行中的等待并列展示,这样跨轮等待无需询问模型即可看到。
- 归档即退出网络 — 归档会话(工作区注册表状态)会清除其加入意图并卸载其节点:启动结算时先于任何唤醒执行,每次读取状态时执行(会话中途归档会在一次面板轮询内消失),并作为路由时守卫,绝不唤醒已归档目标。
- 诚实的等待 — 同步路由的目标始终不应答时,在 180s 回复等待截止点释放调用方,并给出已投递形态和回执契约(目标按自己的节奏应答);async: true 完全跳过等待。网络面板把超过 120s 的进行中行标记为陈旧等待并变暗,而不是暗示仍在推进;其标题带有包版本徽章。
- 宣告 — announce: true 发布本节点卡片(团队、能力、推荐、已加入的会话团队),对等方无需任何目录即可找到它。
模型工具一览
共十个工具。开启 teamScopeInfo(默认)时,a2a_teams、a2a_status 和 a2a_probe 按本机已加入团队范围作答:[this host] 行,加上服务本机节点所声明团队的对等方。a2a_tasks(本机自己的派发台账)不受范围限制。
| 工具 | 回答 | 说明 |
| a2a_whoami | 我在这个网络上是谁? | 调用会话的节点 id、加入/在线状态、可路由句柄、名册与画布成员关系、所有者的 teamJoinAllowlist、teamScopeRouting 开关,以及最近被拒绝的加入/退出尝试。 |
| a2a_teams | 我能路由到哪些团队? | [this host] 行加上范围内的对等团队,附活动摘要。 |
| a2a_route | 向团队发送消息。 | context_id 延续对话;async: true 投递后不等待;跨候选故障转移。 |
| a2a_tasks | 哪些异步任务仍欠回执? | 三档:欠回执行保留后续 context_id;超过 TTL 的行自动死信(仍可结算);结果落入有界归档;重启后仍持久化。 |
| a2a_probe | 集群是否健康? | 对每个范围内对等方(或给定 url)做一次已验证卡片抓取;N reachable, M down;未命中分类为 unreachable 与 rejected。 |
| a2a_team_join | 把一个已加入的节点声明为团队成员。 | 受允许列表约束:只有 teamJoinAllowlist 中的精确名称才能授权加入(空列表拒绝所有加入)。通配符或前缀条目会由 a2a_whoami 显示,但不授权任何操作([PATTERN_NO_MINT]);canvas 团队在工作台中分配,绝不在此处分配([CANVAS_TEAM])。尝试记录会写入状态面的 registry.teamAudit。 |
| a2a_team_leave | 撤销团队成员身份。 | 始终允许——退出只会缩小暴露面。 |
| a2a_exposure | 此会话的可见程度如何? | 设置会话自身的层级:linked(默认下限)或 fleet(自愿上调);组的可见性遵循其最严格的成员。需要已加入的会话。 |
| a2a_report | 在离线时向此主机的监督机制报告。 | 唯一豁免加入门禁的入口:单向,写入状态面的 reports 环形缓冲区,不授予存在性或路由。每会话每 30s 一份报告,≤4000 字符,10 分钟内相同文本出现 3 次会因循环被拒绝。 |
| a2a_status | 此节点当前在做什么? | 范围内被跟踪的对等节点及其质量分数、进行中的路由、近期路由活动,以及幂等窗口聚合(占用率 + 认领/重放/冲突计数器)。 |
a2a-net 命令行(会话外的调用方)
包还附带一个命令 a2a-net(bin → cli/a2a.mjs,0.6.27+),供脚本、终端和其他非 DSH 会话的智能体框架使用。它创建自己的 Ed25519 身份(<name>/<16 hex>),在一个 home 主机上存放签名卡片最多 4h(publish;过期的存放会作为离线行继续列出,直到重新发布),并接收该主机上的邮箱(保留 64 条),而不是被唤醒:
a2a-net init review-seat --home http://127.0.0.1:3080 # identity + home host
A2A_KEY=... a2a-net publish # deposit (key-gated host)
a2a-net route dsh/1a2b3c4d "status?" # async by default; --sync waits for the reply
a2a-net inbox --new # unread mail; --wait blocks, --hook feeds a Claude Code hook
它只投递到其 home 主机上的团队(直接投递从不中继)。命令、标志、环境变量和退出码:src/skill/references/cli.md。
Canvas 团队(任意分组,v0.5.25)
canvas 团队是用户组合的、命名的、多成员路由组:一个原子会话节点可以属于多个团队,路由到 <team>/canvas/<name> 会解析第一个活跃成员(成员顺序即路由优先级)或唤醒第一个冷加入成员(路由唤醒)。canvas/ 路径段永远不会与节点别名(<team>/<id8>)冲突,因此两个命名空间可以互不干扰地共存。
- 存储:<dsh-home>/a2a/canvas.json(有序团队条目;上限:64 个团队 × 32 个成员)
- 控制 API:POST /__dsh_a2a/canvas,带 action: create | remove | add-member | remove-member
- 加入成员资格要求会话已加入(活跃节点或记住的意图)——未加入的会话没有路由后门;退出网络(leave/archive)会丢弃全部画布成员资格
- 成员也可以是远程的、带 zone 限定的团队句柄(zone/a1b2c3d4),由操作者手动添加——这条链接允许该远程调用方进入此处,并赋予其完整卡片权限
- a2a_teams 把画布团队列为本地行,带成员数与活跃数;/__dsh_a2a/state 为本地成员提供每个成员的 joined/live 标志,为远程成员提供 { id, team, remote: true }
原生团队桥接(节点统一,P1)——已弃用
已弃用。 @nelsonlongxiang/dsh-native-teams 已弃用。桥接代码仍随包发布(src/teams-bridge.ts;nativeTeamsInbound 默认为 false),保留给现有部署;新部署不应依赖它。下文描述的是它当前的状态。
通往 @nelsonlongxiang/dsh-native-teams 的两个半桥(结构契约镜像:src/teams-bridge.ts;冻结的契约位于该包的 src/a2a-face.ts):
- 出站传输面——本插件挂载 nativeTeamsA2a 服务:resolve 通过 submit 所用的同一次目录遍历来应答(避免两套匹配器产生漂移),submit 走直连路由调度器(每个候选都有异步门控,镜像 a2a_route;对端的幂等 409 重放对每次投递都是终态(异步 → accepted,同步 → 抛出带 -32003 字面量的错误),绝不作为故障转移的重复投递;已接受的提交记入欠账台账;当提交会话是已加入节点时,其节点团队会作为回执 callback 随链路传递,于是对端的回执能唤醒发起该轮的会话——路由唤醒覆盖冷会话;没有已加入的父级时,不携带 callback,回执回退到调用方标签),cancel 通过链路向所属团队发送协作式 [A2A cancel] 停止通知(对端不跟踪入站任务 id,所以走台账路由永远只会答 unknown),并清除欠账行。它不暴露任何新东西——只有对端网络已发布的团队才能解析。
- 入站分发(配置项 nativeTeamsInbound,默认 false)—— 同级注册表判定某个路由到的团队名属于无歧义的本地声明时,就通过该团队自己的权威接缝(describeTarget/startRound)发起一轮路由回合,父级是本节点当前活跃的发起者,A2A 信封随回合消息一起传递。注册表里存在记录本身绝不等于暴露:要由运维方主动开启。只作用于调度器层 —— 入站调用方寻址的是团队,永远不是团队成员(成员保持可见但不可寻址)。回合有边界,和 steer 路径一样:回复等待截止时间为 180s(nativeRoundWaitMs),超时后返回诚实的「已投递、未结算」形态,同时回合继续运行;调用方的中止(如果存在)通过自己的信号取消回合。wait: false 在完成前置检查(声明、接缝、发起者 —— 幽灵分发永远不会返回成功)后以分离方式触发回合;本切片中 native-teams 回合不发出 A2A 回执,因此其结果带有 bridge 标记,永远不会被记为待回执(回执回流到 callbackTarget 属于 P2 切片)。
完整映射与范围说明见 docs/native-teams-bridge.md。
配置
行配置叠加在配置档的补丁层上;每个键都有 schema 默认值。
| 键 | 默认值 | 用途 |
| announce | false | 发布本节点的智能体卡片,供对等方发现。 |
| peers | [] | 种子基础 URL;被引用的对等方从其卡片中学习。 |
| delegates | [] | 区域委托:{ name, url, publicKey },发布在卡片上。 |
| team | 'dsh' | 本节点为直接路由暴露的团队名。 |
| session | '' | 调用方标签;'' 按 home 派生稳定的 dsh-host-<8hex>。 |
| agentName | 'DeepSeek Harness A2A node' | 卡片上面向人的名称。 |
| apiKey | '' | 对等请求中发送的 X-API-Key;非空时也用于保护控制路由。 |
| sessionNodes | true | 把主会话暴露为可加入的网络节点。 |
| nativeTeamsInbound | false | 把入站直接路由(以及出站 A2A 工具的本地候选)通过其路由接缝分发给 native-teams 注册表团队;需要组合同级插件。 |
| nativeRoundWaitMs | 180000 | 单个 native-teams 回合的回复等待预算(桥的截止时间,对应 steer 路径的 180s)。回合超过该时间仍在运行,就返回诚实的「已投递、未结算」形态并继续运行。 |
| wakeJoinedOnBoot | false | 挂载后预热冷的已加入会话的智能体(需要会话控制器的唤醒面;没有它,按路由唤醒和侧边栏唤醒按钮仍然可用)。预热是延迟的、让出前台的、可取消的 —— 它绝不阻塞启动窗口(见下面两个旋钮)。 |
| wakePrewarmDelayMs | 10000 | 加载器结算与首次预热唤醒之间的空闲延迟;0 恢复为结算即触发。 |
| wakePrewarmQuietMs | 5000 | 前台静默窗口:窗口内出现唤醒/路由需求(或任何出站路由正在处理中),就推迟下一步预热;0 关闭让位。 |
| wakeBootStaggerMs | 3000 | 串行启动预热的唤醒之间暂停多久(限制重启后日志解码饱和;路由触发的唤醒从不等待它)。 |
| wakeReconcile | true | 持续做期望状态对账:每轮重新推导到期的冷加入意图,每轮唤醒一个(覆盖启动后漂移、唤醒失败、迟到的网关)。由 wakeReconcileIntervalMs(60000)、wakeReconcileBackoffBaseMs(5000)和 wakeReconcileMaxBackoffMs(600000)调节。 |
| teamJoinAllowlist | [] | Teams 的 a2a_team_join 可以声明哪些团队:只认精确名称( 或前缀条目不授权任何东西)。留空则拒绝所有加入。所有者也可以在工作台所有者面板(/__dsh_a2a/owner-settings)里修改。 |
| teamScopeRouting | true | S3 准入:路由时检查调用方与目标是否共享团队归属;没有声明团队的会话没有网络。设为 false 恢复开放的出站接缝。 |
| teamScopeInfo | true | S5 信息面范围,作用于 a2a_teams / a2a_status / a2a_probe(见工具一节)。设为 false 恢复全舰队范围的披露。 |
| peerAllowlist | [] | 非空时:只有主机匹配某个条目(精确 host[:port]、裸域名后缀或完整 URL)的引荐才被准入。种子永不被过滤。 |
| referralLearning | true | 设为 false 把跟踪集合冻结在配置的种子上:引荐既不学习也不提供。 |
| referralTeamResolution | false | gh#8:团队名只在种子 + 委托方范围内解析;设为 true 恢复旧版全存储基准(包含引荐学到的对等方)。 |
| exposureExcerpt | 'full' | 卡片发布会话活动摘录的多少:full、masked(仅形状前缀)或 off。标题和句柄保留。 |
| originClaimWindow | true | OriginClaim 过渡窗口:没有信封的投递仍走调用方门禁(计入 legacyInbound);设为 false 则像签名失败一样拒绝。 |
| originClaimUrl | '' | 覆盖信封里的 callerUrl(口袋隧道主机);'' 表示自动推导。 |
| gateAgentLessCalls | 'audit' | 主机侧(无智能体)路由调用:审计它们,或 refuse。 |
| asyncNudgeDelayMs | 120000 | 异步路由已投递但未被消费时,延迟多久再用一行提醒重新唤醒目标;0 关闭。 |
| taskStaleTtlMs | 86400000 | 欠付任务的过期 TTL(24 小时):超过后待处理行自动进入死信(迟到的回执仍可结算它)。 |
| dshHome | '' | 覆盖插件状态的 DSH 主目录(<home>/a2a/…);'' 解析默认主目录。 |
| cardTtlMs / flushTimeoutMs / routeTimeoutMs | 见 schema | 卡片生命周期(48 小时)和路由时间预算。 |
| cardCacheTtlMs / cardCacheNegativeTtlMs / remoteRowsTtlMs / stateColdRowsTtlMs | 60000 / 30000 / 15000 / 5000 | Peer 卡片缓存(命中 / 未命中)、远程会话行刷新窗口,以及 state face 的冷行刷新窗口。 |
示例 —— 一个带 seed 的宣告节点:
- id: a2a
name: '@nelsonlongxiang/dsh-open-a2a-net'
config:
announce: true
peers: ['http://127.0.0.1:41243']
离开语义(有界陈旧)
leave 是按会话、按主机生效的操作:它在本地移除加入意图,并卸载节点。没有全网范围的撤销广播 —— peer 会继续列出团队,直到它们缓存的该节点卡片副本刷新(cardCacheTtlMs,默认 60s —— sessionTeams 是未签名的 serve-fresh 字段,所以卡片自身的 48h TTL 管不住它),这段时间内的入站投递会如实失败(主机不再解析该团队)。已经实现回执契约的远程目标不受影响。正式的撤销广播协议记录在上游笔记 2026-08-27-a2a-leave-no-revocation-broadcast.md 中(提案)。
信任模型
卡片由 Ed25519 签名并会过期;卡片上的 peers 推荐、sessionTeams 列表、lanIp 和 version 均未签名,读取时取最新值 —— 后两项的存在,是为了让运维人员不必逐台登录主机,就能审计每个 fleet 节点实际运行的构建版本。如果某个推荐 URL 在自身抓取时刚刚证明它提供的是本节点的卡片,它会被记住;在抑制窗口内重复推荐该 URL 会被忽略,而不是在 peer 存储里忽隐忽现。每条 /__dsh_a2a/ 控制路由 —— 包括读操作 —— 在设置了 apiKey 时都要求提供该密钥(X-API-Key,常量时间比较)。密钥为空时只信任 loopback socket;Sec-Fetch-Site 头只能拒绝(跨站页面即使在 loopback 上也会被拒),永远不能授权。把监听器暴露到 loopback 之外之前,先设置 apiKey。
| 控制路由 | 用途 |
| GET /__dsh_a2a/state | 面板和工作台轮询的 state face(会话、画布、peer、远程行、活动、账本)。 |
| POST /__dsh_a2a/join · /leave | 加入或离开某个会话节点。 |
| /__dsh_a2a/canvas · /canvas-layout | 画布团队(创建/移除/添加成员/移除成员)和看板布局。 |
| /__dsh_a2a/groups | 手动会话分组 —— 自 0.6.6 起废弃(画布团队取代了它),保留给旧面板。 |
| POST /__dsh_a2a/dispatch | 工作台快速派发到团队。 |
| POST /__dsh_a2a/tasks/abandon · /tasks/cancel | 从调用方一侧了结欠下的任务(cancel 还会向活跃的本地目标发送停止通知);该行落入 archived[]。 |
| /__dsh_a2a/session-log · /remote-session-log | 工作台的会话日志读取。 |
| /__dsh_a2a/owner-settings | 所有者面板:把 teamJoinAllowlist、teamScopeInfo 和 announce 读(GET)或写(POST)进 home 补丁层(前两项立即生效;announce 下次重启生效)。 |
| /__dsh_a2a/fleet-canvas-op | 把画布操作转发到对等节点的 /a2a/canvas-op,盖上本节点的 OriginClaim;由对等节点的链路门决定是否放行。 |
| /__dsh_a2a/cli-deposit | 为 a2a-net 命令行身份提供存入、列出、撤销和邮箱读取。 |
状态面的账本:tasks(待处理,每项带 settleable)、tasksStaleCount、tasksDead(已知时带 direction),以及 archived —— 最新 64 条已结算记录,最新的在前,每条带 outcome、endedBy(receipt | echo | abandoned | cancelled)、late: true(回执在调用方结束之后才到达时置位),已知时还带 direction —— 外加 archivedCount 和 archivedSplit(按 outcome 计数)。
舞台(可视化界面)
模型工具旁边挂着两个整页舞台:/__dsh_a2a_nexus(A2A 工作台 —— 从 0.6.2 起,2D 空间规划板就是舞台本身,目录/账本/活动/计划面板收进一条可折叠侧栏,合并在一个响应式外壳里;画布面不可用时,面板回落到主栈;Three.js 拓扑查看器已在 0.6.0 退役;?mode=plan 仍会收敛到规划板)和 /__dsh_a2a_canvas。只写挂载路径会重定向(301)到带尾斜杠的形式,所以两种写法都能渲染,外壳的相对资源也总能在被服务的目录树内解析。离线开发时,nexus-stage/scripts/mockhost.py [port] 用预置状态服务构建好的舞台;在它上面打开 /?fault=500 可以试出页面内的故障徽标。全新检出时 pnpm verify 无需手动引导:它的 verify:nexus 步骤会先自行安装子包依赖,再开始构建。
协作 SOP(网络实践,v0.1)
现行规则:A2A 协作规范(src/skill/references/collaboration.md,中文,随插件下发到每个节点)。下面的 v0.1 SOP 作为历史保留;两者冲突时以规范为准。
由网络的研究节点从 0.1.x–0.5.x 交付周期中提炼;维护者采纳。
- 角色 —— 一个会话一个岗位,不跨工作区边界:研究节点(协议/设计评审、判定标准、源码行引用,标注 must-fix/recommend)、维护节点(版本裁定、发布、采纳回执)、测试节点(所有实机验证的唯一执行点;生产主机绝不用于临时测试)、安装/运维节点(生产配置档部署,单一负责人,等待维护窗口)。
- 派发纪律 —— 测试任务交给测试节点,附版本 + 提交 + 可判定标准 + 回执目标;评审任务交给研究节点,附源码指针,不要转述。
- 回执契约 — 耗时超过几分钟的任务用 async: true(wait:false)路由;回执读作 [A2A receipt] task <task_id> <outcome summary>;task id 由调用方生成、随请求携带、由对端回显、经转向头传输 —— 四路一致后回执才能关联。
- 升级链 — 测试节点通过 → 维护者回执 → 运维节点申请窗口 → 生产升级 → 维护者浏览器端最终检查 → 全网通知。
- 复用红线 — 共享的纯逻辑抽成库;共享的运行时能力抽成 Service;插件之间绝不按值互相 import;生产主机不承担验证职责(不搞「谁开发谁自测」)。
验证
测试按深度分层(契约见 docs/testing-layers.md):每层有自己的目录和脚本,迭代时可以只针对某一层。
pnpm install # auto-install-peers pulls the DSH peer train from npm
pnpm verify # typecheck + build + ALL vitest layers below + layout gate + nexus-stage suite
pnpm test:unit # L0 — pure logic, no fiber, no sockets (stores, codecs, jsdom components)
pnpm test:wire # L1 — ONE real plugin instance over real HTTP against faked peers
pnpm test:mesh # L2 — >=2 REAL nodes (own homes/keys/peers.json) over real HTTP: the
cross-machine equivalent, incl. fault injection (clock-skew cards,
tampered cards, partition/restart, alias collapse, zone delegation,
referral freeze) and receipt replay — see the fault matrix in docs/testing-layers.md
pnpm test:live # L4 — REAL remote machines, env-gated (DSH_A2A_LIVE_MESH=...); skipped
by default, never in CI — runbook in docs/testing-layers.md
pnpm build # host lib (including lib/a2a-client.js), client types, and browser bundle (lib/client.js)
非 DSH 互操作(协议模块)
不是 DeepSeek Harness 主机的节点,照样能说这套协议。四个零依赖协议模块以子路径导出(gh#13)—— 直接 import 即可,不必拉入 DSH peer train:
import { signCard, verifyCard } from '@nelsonlongxiang/dsh-open-a2a-net/card'
import { formatReceipt, parseReceipt } from '@nelsonlongxiang/dsh-open-a2a-net/receipt'
import { withinRouteBodyCap } from '@nelsonlongxiang/dsh-open-a2a-net/transport-caps'
import { A2aClient } from '@nelsonlongxiang/dsh-open-a2a-net/a2a-client'
自己实现签名卡、GET /.well-known/agent-card.json 和 POST /a2a/direct。这些模块编码的线上格式就是契约。匿名拉取卡片只能拿到最小发现面。要拿完整卡片,需要已验证的 OriginClaim 信封(X-A2A-Caller / -Url / -Ts / -Sig,用你自己的卡片密钥签名)并且与服务主机存在关联:你的卡片 URL 与其同源,你的 handle 是其已加入会话 handle 之一,其操作员把你的确切 handle 加成了 canvas 远程成员,或者双方操作员互相把对方加进了种子(它的 peers 里写着你的 origin,你的卡片 peers 里写着它的 origin)。已验证但未关联的调用方仍然只能拿到最小卡片。信封细节见 docs/protocol/delivery-origin-auth.md 和 docs/protocol/ 里的其余文档。
该基于哪个产物构建。 npm 直接安装已经包含四个子路径:0.6.25 起的发布版(badge latest)就带这些子路径,所以 npm install @nelsonlongxiang/dsh-open-a2a-net 开箱即用——可运行的 external-node 示例见 examples/external-node/,它针对内嵌的 mock peer 跑通全部四个子路径。子路径导出落在提交 9cb3852;更早的版本(包括公开的 0.5.15)还没有上面的准入规则和 OriginClaim 信封,从这些版本导入子路径会报 ERR_PACKAGE_PATH_NOT_EXPORTED——下面这套历史做法(锁定提交、pnpm build、npm pack、依赖 tarball)只适用于 0.6.25 之前的部署:
HISTORICAL (0.5.15 and earlier): build the package from a pinned commit
git clone https://github.com/NelsonLongxiang/dsh-open-a2a-net && cd dsh-open-a2a-net
git checkout <commit> # pin one; any commit at or after 9cb3852 exports the subpaths
pnpm install --frozen-lockfile # pnpm version from package.json "packageManager"
pnpm build
npm pack # → nelsonlongxiang-dsh-open-a2a-net-<version>.tgz
in your project:
npm install /path/to/nelsonlongxiang-dsh-open-a2a-net-<version>.tgz
安装这个包(npm 或历史 tarball)不会拉取任何 @deepseek-ai/ 包:DSH peers 是可选的,四个子路径只互相导入,外加 node:crypto。
License
MIT预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
NelsonLongxiang/dsh-open-a2a-net
为 DeepSeek Harness 开放 A2A 网络插件:签名智能体卡片、去中心化对等节点与区域发现、直连路由模型工具,以及网页侧边栏里可加入的会话节点
相关插件 · 浏览器
智能体插件框架deepseek-ai/deepseek-harness
DeepSeek Harnessdsh是由 DeepSeek AI 开发的开源 agent harness智能体框架。
★ 270,422已汉化DSH
71.9
多设备 Agent 看板YaoApp/yao
✨ 所有 Agent 和工作区集中管理,覆盖你的每一台设备。任务看板追踪进度,桌面端、移动端、浏览器、API…
★ 8,147已汉化DSH
71.2
可扩展侧边栏工作台omdsh-dev/DSH-better-sidebar
DSH 的双工作台:原生右侧栏 + 自绘底部面板,并把 ctx.betterSidebar 服务开放给所有插件
★ 4,761已汉化DSH
71.1
浏览器自动化桥Tencent/BrowserSkill
你的 Agent。你的浏览器。连接,就能协作。
★ 9,967已汉化DSH
71
AI 小说创作工作台EthanYoQ/AI-Novel-Writer
AI 小说作家 / AI Novel Writer
★ 1,591已汉化DSH
70.7
智能体浏览器引擎platonai/Browser4
DeepSeek Harness 集成
★ 1,175已汉化DSH
70.4
这是你的仓库?认领条目