跳到主要内容

dsh-a2a

已验证

@hanphone/dsh-a2a · v0.3.5 · MIT · 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.

安装

dsh plugin add @hanphone/dsh-a2a

用 dsh --profile default --dump-config 确认 layer 已生效 —— 参见安装指南。

源码

标签

作者

说明文档

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 组装的会话中执行(标准 agentPresets resolve+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-task waterfall,策略插件可否决或审计。
  • 入站连接监控 — 面板展示每个实例的对端连接,可关闭某个对端。
  • 入站会话层(按 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

快速开始

  1. 安装 — dsh plugin --profile web add @hanphone/dsh-a2a。
  2. 重启 GUI — 浏览器端插件表在 host 启动时扫描,装完请重启一次(pnpm dsh web 或对应 profile 启动命令)。
  3. 打开 设置 → 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