dsh-a2a
Đã xác minh@hanphone/dsh-a2a · v0.3.5 · MIT · Giao diện web
Agent2Agent (A2A) v1.0.1 dual-end plugin for DeepSeek Harness: multiple inbound servers (per-instance agent presets; skills derived from each preset's skill directory), multiple outbound connections, GUI-managed instance lifecycle.
Cài đặt
dsh plugin add @hanphone/dsh-a2a Xác nhận layer đã áp bằng dsh --profile default --dump-config — xem hướng dẫn cài plugin.
Mã nguồn
Thẻ
Tác giả
Readme
dsh-a2a
Agent2Agent(A2A)v1.0.1 双端插件,用于 DeepSeek Harness — English · 中文
@hanphone/dsh-a2a 是一个独立开源的 A2A 插件,把 DeepSeek Harness profile 变成多面手 A2A 一等公民:能同时对外发布多个入站 A2A server,每个绑定一个自己的 agent preset、拥有独立端点 / AgentCard / 派生技能 / 鉴权;也能同时接入多个出站 A2A server,每个独立 preset,远端技能映射为模型工具。所有 server 实例的创建、启停、编辑、删除全部在 GUI 完成——无需改任何配置文件。
架构与设计决策:docs/architecture.md。 跟随 DeepSeek Harness 版本更新:docs/harness-compatibility.md。
功能
- A2A v1.0.1 协议面,对齐官方规范 — JSON-RPC 上的
SendMessage、SendStreamingMessage、GetTask、ListTasks、CancelTask、GetExtendedAgentCard、SubscribeToTask;SSE 流式带补发帧;官方TASK_STATE_*/ROLE_*枚举与 AgentCard 结构(含supportedInterfaces、capabilities、完整错误码表)。 - 多入站 server — 每实例一个"人格":独立端点(
/a2a/<id>)、AgentCard 路由、鉴权 env、派生技能。 - 每实例 agent preset — 每个入站 server 绑定一个具体 agent preset(如
ptc、standard、minimal、…);选择器只列 roster 真实预设(与应用内选择器一致)并默认选中部署默认。入站任务在按该 preset 组装的会话中执行(标准agentPresetsresolve+mount 路径)。 - 技能宣告从 preset 派生 — AgentCard 技能 = 所绑 preset 技能目录中模型可调条目(
agentPresets.standingKeyFor+ctx.skills.list),纯自动派生——"preset 确定、技能确定,一切皆插件"。无需手写技能表单;skills 服务缺失时兜底内置chat。远端用metadata.skill调技能,由该 preset 会话经其tool-skill装载执行。 - 多出站 server — 每个连接独立远端 URL、鉴权 env、超时与可选 preset;启用实例把远端技能映射为
a2a__<name>__<skill>模型工具。 - 持久化任务存储 — 任务存于
a2a存储域(默认 json 后端,可按部署切 SQLite);服务端生成 id 跨重启存活,每个任务记录来源入站实例。 - 执行器 —
session(每个contextId一个 DSH 会话)与subagent(委托ctx.subagents,工具调用过程流式回传)。 - 受治理入站 — 每个入站任务经过
a2a/inbound-taskwaterfall,策略插件可否决或审计。 - 入站连接监控 — 面板展示每个实例的对端连接,可关闭某个对端。
- 入站会话层(按 contextId) — 每条 A2A 对话(
contextId)对应一个活跃 DSH 会话(a2a-<contextId>);GUI 按 contextId 聚合会话(状态、任务数、流式、首次/最近活动),可取消该会话的全部活跃任务或关闭其活跃会话——关闭是内存级释放,同一 contextId 再来任务时自动重开新会话。会话数据全在内存(与对端注册表一致);没有 agent 循环时表格仍从任务记录降级展示。 - 直接填 Bearer Token — GUI 的 Bearer Token 输入框把每个实例的 token 经 harness 凭据服务写入托管
.env/凭据库(0o700);记录只保留自动生成的变量名,token 明文绝不进 a2a 域或 AgentCard。运行时读取分层(凭据 → 进程环境),外部 export 同名变量仍兼容。 - 最小插件配置 — 实例经 GUI 创建并存于域中;插件
Config只承载宿主级默认值(baseUrl、subagentProvider、defaultTimeoutMs)。
安装
从 npm
dsh plugin --profile web add @hanphone/dsh-a2a
任意 profile 名均可(web、自定义 profile、headless 等):
dsh plugin --profile <name> add @hanphone/dsh-a2a
本地构建安装
cd dsh-a2a
pnpm build
npm pack
dsh plugin --profile <name> add <path-to>/hanphone-dsh-a2a-<version>.tgz
快速开始
- 安装 —
dsh plugin --profile web add @hanphone/dsh-a2a。 - 重启 GUI — 浏览器端插件表在 host 启动时扫描,装完请重启一次(
pnpm dsh web或对应 profile 启动命令)。 - 打开 设置 → A2A 连接 — 创建第一个入站 server(选 preset——技能自动派生——可选鉴权 env)。创建即时启用并发布自己的端点与 AgentCard。
每个入站 server 监听在 profile 的 webServer 上:
# 所建实例的 AgentCard(确切 id 见 GUI)
curl http://127.0.0.1:3080/a2a/<id>/agent-card.json
向实例发任务(其宣告的 chat 技能):
curl -X POST http://127.0.0.1:3080/a2a/<id> \
-H "content-type: application/json" \
-d '{"jsonrpc":"2.0","id":"1","method":"SendMessage","params":{"message":{"role":"user","parts":[{"text":"hello"}],"metadata":{"skill":"chat"}}}}'
GUI 面板
浏览器端在设置中注册 A2A 连接 页,分三个 Tab。无需改文件即可:
- 入站 Servers — 创建入站 server(名称/描述/版本、agent preset 选择器——只列真实 roster 预设并预选部署默认、Bearer Token 输入框),启停、编辑(含清除鉴权)、删除;每张卡片显示端点、preset 徽章、鉴权状态、preset 派生技能 chips 与实时 AgentCard URL。
- 出站 Servers — 两阶段添加出站连接:输入远端 AgentCard URL(± Bearer Token)→「导入」预览远端卡片(名称/版本/技能/端点)→「连接」确认;启停、刷新、编辑(名称/preset/超时/token)、删除。卡片显示连接状态点、工具数与错误。
- 连接与任务 — 入站对端表(谁在调用、任务数、流式、关闭控制)、按 contextId 的会话表(状态点、任务/活跃数、流式、首次/最近活动、取消活跃/关闭按钮)与任务列表(按来源查看、取消)。
所有面板流量走 profile webServer 上的仅回环 /a2a/api 路由——远程对端永远无法驱动它。
配置
GUI 覆盖日常实例管理。插件 Config 只有宿主级默认值,如需覆盖可经 profile 用户 patch 层($DSH_HOME/profiles/<name>/cordis.patch.yml)设置:
- id: a2a
config:
baseUrl: http://127.0.0.1:<port> # 省略则从 webServer 地址派生
subagentProvider: in-process
defaultTimeoutMs: 60000 # 出站连接默认超时
实例不经 patch 配置——它们在 GUI 中创建并持久化于 a2a 域(inbound_servers / outbound_servers 表)。
所需宿主服务
base 类 profile 全部挂载:webServer(@deepseek-ai/dsh-host-webserver)、存储栈(@deepseek-ai/dsh-storage + @deepseek-ai/dsh-storage-domain)、工具注册表(@deepseek-ai/dsh-tools)、agent 循环(@deepseek-ai/dsh-agent + @deepseek-ai/dsh-agent-loop)、agent presets(@deepseek-ai/dsh-agent-presets;preset 选择器与 preset 化会话组装需要它)。subagent 执行器还需要 @deepseek-ai/dsh-subagent。
存储后端
任务与实例存储位于 a2a 存储域。base 组合默认走 json 后端;要切 SQLite,在同一 patch 层路由域并加后端:
- id: storage-domain
config:
backend: json
routes:
a2a: sqlite
- insert:
- id: storage-sqlite
name: '@deepseek-ai/dsh-storage-sqlite'
config:
path: /absolute/path/to/a2a.sqlite
CLI
/a2a 聊天命令与面板对应(GUI 的文字备用路线):
a2a status | presets | peers |
inbound list|create|remove|enable|disable |
outbound list|create|remove|enable|disable|refresh |
tasks | task get|cancel <id> |
sessions | session cancel|close <contextId> | help
工作原理
- 入站 —
InboundServerManager拥有每个实例:每实例 = preset 化会话池 +A2AServer+ 路由。实例持久化于inbound_servers表,各自服务独立端点与 AgentCard。任务流经a2a/inbound-task→ 执行器 → 任务存储,SSE 帧推送给订阅者。每实例的会话注册表观察 SSE 生命周期钩子;facade 把这些活跃观测与共享任务存储折叠成按contextId的会话视图。 - 出站 —
OutboundServerManager拥有每个连接:每实例一个带独立 agent 存储的OutboundAgentRegistry,实例持久化于outbound_servers表。A2AClient发现 AgentCard,每个技能注册为一个工具。 - 面板 — 浏览器端(React,
settings.section)经仅回环/a2a/api路由读写;host 端喂给它入站/出站 server 视图与 preset 名单(/a2a/api/presets)。
完整设计见 docs/architecture.md。
目录结构
src/
api.ts # 回环面板 API (/a2a/api、/a2a/api/presets)
index.ts # Cordis 插件入口 (apply)
protocol.ts # A2A v1.0.1 协议常量与类型
jsonrpc.ts # JSON-RPC 帧
servers/ # 多实例管理器
inbound-manager.ts # 入站 server 实例(CRUD、路由、生命周期)
outbound-manager.ts # 出站连接实例(CRUD、工具)
server/ # 单实例内部件:store、card、a2a-server、
# routes、executors、inbound-registry、session-registry
outbound/ # 出站内部件:A2AClient、registry、tools
client/ # 浏览器半区:设置面板 (React)
service.ts # ctx.a2a 服务 facade
commands.ts # /a2a 聊天命令
tests/
unit/ # protocol、framing、card、store、registry、server、
# client、api、inbound-registry、identity
composition/ # 在真实 Cordis Context 上以 stub 宿主服务跑 apply()
cordis.patch.yml # bundle patch(挂载插件;实例由 GUI 管理)
开发
插件通过项目引用对 harness 源码图做类型检查;需要已构建宿主聚合的 harness checkout。
pnpm typecheck # host (tsc -b) + client (tsc -p tsconfig.client.json)
pnpm test # vitest run(单元 + 组合套件)
pnpm build # tsc + tsdown → lib/index.js(host)+ lib/client.js(浏览器)
致谢
本项目受 ryubyte/dsh-a2a 启发并与其并行开发——那是 DeepSeek Harness 上更早的 A2A 插件。其设计——双端范围、AgentCard 宣告、设置面板模式——为本文实现指明了方向。我们的协议层、任务存储与执行器接缝均为独立实现;GUI 管理模型则直接承袭了 ryubyte 的连接面板。
许可证
MIT