dsh-a2a

dsh-a2a 设计文档 v1.0(多实例重构稿)

状态:已实现(2026-09-06 随 v1.0 交付)。v1.0 按用户新业务需求重构:从”单入站 server + 单出站 client”升级为”多入站 server + 多出站 server”,每个 server 实例绑定一个 DeepSeek Harness agent preset,全部配置与开关 GUI 可操作,协议面对齐官方 A2A v1.0.1(以 a2aproject/A2A 规范仓库为权威)。

2026-09-06 方案修订(与用户讨论后确定,先定方案再实现):三点语义在此定稿——

  1. preset 选项完全对齐应用内:下拉只列 roster 的真实预设(id/name/描述,显示 name),无”默认”占位项;每个 server 实例总绑定一个具体 preset,初始值为部署默认(agentPresets.defaultId,web profile = standard)。
  2. preset 确定 → 技能确定(一切皆插件):技能宣告纯自动,从该 preset 的技能目录派生——agentPresets.standingKeyFor(preset) 取得该 preset standing mount 的 scope key,再 ctx.skills.list({ scope }) 取模型可调(invocation.modelInvocable)条目,填入 AgentCard;创建者不再手写宣告文字。远端用 metadata.skill 指定技能,任务进该 preset 会话执行,模型经会话内的 tool-skill 装载并运行该技能(闭环)。
  3. 需求 6 的”创建者输入,默认取 preset 名”按以上 2 修订:技能来源是 preset 组合,不是自由文本;原先的实现(“宣告即声明”手写列表 + defaultSkillFor 兜底)已删除。

2026-09-07 GUI 打磨与鉴权填入方案(与用户讨论,待实施):对标 ryubyte/dsh-a2a 的成熟 GUI 并超越之,两项方案在此定稿——

  1. GUI 全面打磨(§9 重写):Tab 分区信息架构、完整 --dsw-alias-* 样式系统(独立 dashboard.css.ts 模块,含状态点/hover-focus/@container 响应式/空态/徽章)、出站”导入→预览→连接”两阶段流程、每区块空态引导。功能面(多实例 CRUD/preset/派生技能)本就超越 ryubyte,本轮让观感与交互同级且更强。
  2. 鉴权填入优化(无模式切换,§10.1 修订):GUI 表单就是一个”Bearer Token(可选)”输入框(空=匿名),不设”变量名/直接填”模式切换。填写保存时插件自动生成变量名(A2A_INBOUND_<id> / A2A_OUTBOUND_<id>),经 harness ctx.credentials.set(credentialRef(name), value) 写入用户层 .env/凭据存储(0o700 管理);实例记录只存该变量名。运行时统一 ctx.credentials.resolve(name) 分层读取(进程环境 → 凭据存储 → .env),因此既支持 GUI 直填,也兼容外部 export 同名变量;token 明文绝不进 a2a 域/AgentCard,GUI 不回显 token 值(优于 ryubyte 的明文落 a2a.json)。

历史:v0.2 文档(单实例、工具白名单派生技能)已废弃,由本稿取代;旧版配置不迁移,直接删除。


0. 需求(用户原话,逐条编号)

  1. 完全实现 a2a 协议的功能
  2. 支持多个入站 server 和出站 server
  3. 每个入站 server 可以选择不同的 DeepSeek Harness 的 agent preset,如”当前运行在 ptc agent preset”
  4. 不同的出站 server 也可以接入不同的 agent preset
  5. 完全支持 GUI 操作
  6. agent 中的技能宣告文字由创建者输入,默认为 preset 名

1. 定位与目标

一句话:把 DSH 变成 A2A 的多面手——可同时对外发布多个不同”人格”的入站 A2A server,每个由不同 agent preset 支撑;可同时接入多个出站 A2A server,每个按自己的 preset 组装本端会话;一切能力宣告与开关都在 GUI 里完成;协议面与官方 A2A v1.0.1 对齐。

与 v0.2 的定位差异:

维度 v0.2 v1.0
协议 自称 v1.0,枚举/结构有出入 官方 v1.0.1 对齐(枚举 TASK_STATE_*/ROLE_*、AgentCard 结构、方法全集含 push config)
入站 单 /a2a 端点 多 server 实例,各自端点/卡片/鉴权/preset
出站 单 registry 多 agent 多出站 server 连接,每连接独立 preset
技能宣告 从 ctx.tools 白名单派生 创建者输入(默认 preset 名),协议字段完整
预设 无 每实例绑定一个 agent preset
GUI 单页 多实例增删改/启停/预设/宣告/鉴权全覆盖

2. 架构总览

┌─ GUI(浏览器, settings.section "A2A 连接")──────────────────────────────┐
│  入站 servers 列表(创建/删除/启停/预设/宣告文字/鉴权 env)               │
│  出站 servers 列表(创建/删除/启停/预设/远端 URL/超时)                  │
│  任务视图(按实例过滤/取消)· 入站对端监控 · 状态                        │
└───────────────────────────────┬────────────────────────────────────────┘
                                │ loopback-only /a2a/api
┌─ Host 半区 ───────────────────▼────────────────────────────────────────┐
│  InboundServerManager                                                   │
│    per server:                                                         │
│      endpointPath (独立, 如 /a2a/<id>)                                  │
│      AgentCard(身份 + 技能宣告 + 鉴权 + supportedInterfaces)           │
│      preset 绑定 → 入站任务会话按该 preset 组装                          │
│      A2AServer 实例(JSON-RPC v1.0.1 + SSE + 任务落库)                  │
│  OutboundServerManager                                                  │
│    per server:                                                         │
│      A2AClient(远端 URL/鉴权/超时)                                     │
│      preset 绑定 → 调用远端时本端会话按该 preset 组装                    │
│      远端 skills → 模型工具 a2a__<id>__<skill>                          │
│  TaskStore(a2a 域, 任务带 serverId 来源)                              │
│  ctx.a2a facade(命令 + GUI 共用)                                      │
└─────────────────────────────────────────────────────────────────────────┘

核心抽象:“server 实例” = 端点 + 身份/技能宣告 + preset 绑定 + 鉴权 + (出站时)远端引用。入站与出站共享同一定义框架,只是方向与承载不同。


3. 多入站 server

3.1 实例定义

每个入站 server 实例:

GUI 预设下拉只列 roster 真实预设(id/name/描述),默认选中部署默认,无”默认”占位项(与应用内 select 完全一致;见 §9)。

3.2 路由

webServer 上每实例注册:

AgentCard 的 supportedInterfaces[].url 指向实例自己端点,protocolBinding: "JSONRPC", protocolVersion: "1.0"。

3.3 入站任务 → preset 会话 → 技能执行

实例创建时把 preset 传入会话池:入站任务 SendMessage → 该实例的会话按 agentPresets.resolve(preset) + mount(agentCtx, preset) 组装(机制同 ContextSessionPool,预设来自实例配置)。不同入站 server 各自拥有不同的工作模式(如一个 standard、一个 ptc)。

技能的宣告与执行同源:宣告列表由该 preset 的 standing scope 技能目录派生(§5);远端任务带 metadata.skill 进入后,由该 preset 会话执行,模型经会话内的 tool-skill(目录/装载工具)装载并运行该技能——宣告集合 ⊂ 会话可用技能,闭环一致。

3.4 实例生命周期

GUI 可:创建(选 preset/设鉴权 env)、启用/停用、删除、编辑。删除会 dispose 该实例的全部 effect 与环境。技能列表随 preset 派生,创建/编辑时无需手工输入。


4. 多出站 server

4.1 实例定义

每个出站 server 连接实例:

出站 preset 语义(已确认):需求 4 按用户确认的 (b) 解读——出站 server 的 preset 决定”与这个远端对话时本 DSH 维护的本地会话组装”。当前版本仅持久化该元数据,尚未接线本地 hand-off 会话;出站不宣告 AgentCard。

4.2 出站技能 → 工具

每连接每技能注册为模型工具 a2a__<name>__<skill>(规范名,冲突哈希)。


5. 技能宣告(从 preset 技能目录派生)

5.1 语义(2026-09-06 与用户讨论后定稿)

preset 确定 → 技能确定;一切皆插件。 harness 中技能是真实机制:ctx.skills(@deepseek-ai/dsh-skill)是按 scope 分层的技能注册表,插件(提供者,如 preset 内的 skill-filesystem)往所在 scope 的层注册技能条目;tool-skill 是技能的目录/装载工具(模型经它看到、装载技能)。技能条目含 name(kebab-case 技能 id)、description(必填)、whenToUse?、invocation.modelInvocable。

5.2 派生机制(纯自动,取代手写宣告)

每个入站实例的技能列表 = 该 preset 的真实可用技能:

  1. agentPresets.standingKeyFor(preset) → 该 preset standing mount 的 scope key(不启动 agent、不占会话;dsh-agent-presets 为这种”无 agent 读取”场景专门提供);
  2. ctx.skills.list({ scope }) → 该 preset agent 可见的完整技能目录(preset 层贡献 + 部署全局层——全局技能该 agent 确实能调,宣告”这台 server 实际能做的”正是这份清单);
  3. 过滤 invocation.modelInvocable === false(远端调用是模型面),映射为 AgentSkill[]:id = name、name = name、description = description(可拼 whenToUse)。

5.3 协议字段

AgentSkill(AgentSkill):id, name(必填), description?, tags?, examples?, inputModes?, outputModes?。AgentCard(AgentCard):name, description?, supportedInterfaces[], provider?, version, documentationUrl?, capabilities(streaming/pushNotifications/extendedAgentCard/stateTransitionHistory), securitySchemes?, securityRequirements?, defaultInputModes, defaultOutputModes, skills[]。


6. 完全实现 A2A v1.0.1 协议

6.1 方法全集(官方 §5.3 Method Mapping Reference)

JSON-RPC Method 状态
SendMessage 实现(returnImmediately/historyLength;acceptedOutputModes 仅文本)
SendStreamingMessage 实现(SSE)
GetTask 实现(historyLength 从持久化输入/输出重建 history)
ListTasks 实现(contextId/status/pageSize/pageToken/statusTimestampAfter/includeArtifacts 过滤分页)
CancelTask 实现
SubscribeToTask 实现(SSE;终态任务返回 UnsupportedOperationError)
CreateTaskPushNotificationConfig P1(方法面声明,返回 PushNotificationNotSupportedError)
GetTaskPushNotificationConfig P1(同上)
ListTaskPushNotificationConfigs P1(同上)
DeleteTaskPushNotificationConfig P1(同上)
GetExtendedAgentCard 实现

6.2 JSON 序列化(ADR-001 ProtoJSON)

6.3 传输

6.4 错误码(§3.3.2 + §9.5)

-32700 ParseError, -32600 InvalidRequest, -32601 MethodNotFound, -32602 InvalidParams, -32603 InternalError, -32001 TaskNotFound, -32002 TaskNotCancelable, -32003 PushNotificationNotSupported, -32004 UnsupportedOperation, -32005 ContentTypeNotSupported, -32006 StreamingNotSupported, -32007 VersionNotSupported, -32008 InvalidAgentResponse。


7. 数据模型与存储

7.1 server 实例持久化

a2a 域表(JSON,版本 1——加表不改记录格式):

7.2 任务存储

TaskRecord 增加可选 serverId,标识来自哪个入站实例;jobs/pushConfigs(P1 push)预留。


8. 服务与命令


9. GUI(settings.section “A2A 连接”)

浏览器端注册 A2A 连接 设置页(React,src/client/)。对标 ryubyte/dsh-a2a 的成熟面板并超越之(功能面本就更广:多入站/多出站 CRUD、preset 选择、派生技能、鉴权 env;本轮让观感与交互同级):

9.1 信息架构(Tab 分区)

9.2 样式系统(独立模块)

9.3 出站两阶段添加(超越 ryubyte 的同类流程)

  1. 输入远端 AgentCard URL(+ 可选 Bearer Token)→「导入」;
  2. 预览卡片:名称/版本/状态点/描述/技能列表/端点(hook discover 仅读卡片不改状态);
  3. 「连接」确认建立(add)或「取消」。

9.4 鉴权填入(§10.1)

所有写操作经 loopback-only /a2a/api。


10. 安全性

10.1 token 存储与读取(直接填,层级兼容)

10.2 其余


11. 目录结构(重构后)

src/
  index.ts                 # Cordis 插件入口(组装两个 Manager)
  protocol.ts              # A2A v1.0.1 协议常量 + 类型(全量)
  jsonrpc.ts               # JSON-RPC 2.0 帧
  servers/
    inbound-manager.ts     # 入站 server 实例集合(路由/生命周期)
    outbound-manager.ts    # 出站 server 实例集合(连接/工具)
    instance.ts            # 单实例装配(端点+卡片+preset+鉴权)
  server/                  # 单实例内部件
    a2a-server.ts          # JSON-RPC/SSE 服务(参数化 endpoint/card)
    card.ts                # AgentCard 构建(宣告驱动)
    executor/ …            # session/subagent 执行器(preset 化)
    store.ts               # 任务/实例持久化
    identity.ts            # 实例身份
  outbound/                # 出站单连接内部件
  client/                  # 浏览器面板(多实例 UI)
  api.ts                   # loopback GUI API(多实例)
  service.ts               # ctx.a2a facade
  commands.ts

12. 迁移与兼容


13. 测试


14. P1 / 明确不做


15. 评审确认点(已确认)

  1. 出站 preset 语义 = b) 与远端对话时本端会话按该 preset 组装 —— 已确认
  2. 预设下拉 = 只列 roster 真实预设,默认选中部署默认,无”默认”占位;每实例总绑定具体 preset —— 已确认(2026-09-06)
  3. 技能宣告 = preset 技能目录纯自动派生(standingKeyFor + ctx.skills.list,过滤 modelInvocable),创建者不手写 —— 已确认(2026-09-06,B’ 方案)
  4. 旧版本完全删除,不迁移 —— 已确认
  5. 协议权威 = .research/A2A(官方 1.0.1) —— 已确认(方法名/枚举/结构已按此对齐)
  6. GUI 打磨方案(§9:Tab 分区、样式系统、两阶段连接、状态点、空态/引导;目标超越 ryubyte/dsh-a2a) —— 方案已定(2026-09-07,待实施)
  7. 鉴权填入优化(§10.1:直接填 Bearer Token 经 ctx.credentials.set 写入 .env,记录仅存变量名、token 不落域明文) —— 方案已定(2026-09-07,待实施)