状态:已实现(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 方案修订(与用户讨论后确定,先定方案再实现):三点语义在此定稿——
- preset 选项完全对齐应用内:下拉只列 roster 的真实预设(id/name/描述,显示
name),无”默认”占位项;每个 server 实例总绑定一个具体 preset,初始值为部署默认(agentPresets.defaultId,web profile =standard)。- preset 确定 → 技能确定(一切皆插件):技能宣告纯自动,从该 preset 的技能目录派生——
agentPresets.standingKeyFor(preset)取得该 preset standing mount 的 scope key,再ctx.skills.list({ scope })取模型可调(invocation.modelInvocable)条目,填入 AgentCard;创建者不再手写宣告文字。远端用metadata.skill指定技能,任务进该 preset 会话执行,模型经会话内的tool-skill装载并运行该技能(闭环)。- 需求 6 的”创建者输入,默认取 preset 名”按以上 2 修订:技能来源是 preset 组合,不是自由文本;原先的实现(“宣告即声明”手写列表 +
defaultSkillFor兜底)已删除。
2026-09-07 GUI 打磨与鉴权填入方案(与用户讨论,待实施):对标 ryubyte/dsh-a2a 的成熟 GUI 并超越之,两项方案在此定稿——
- GUI 全面打磨(§9 重写):Tab 分区信息架构、完整
--dsw-alias-*样式系统(独立dashboard.css.ts模块,含状态点/hover-focus/@container响应式/空态/徽章)、出站”导入→预览→连接”两阶段流程、每区块空态引导。功能面(多实例 CRUD/preset/派生技能)本就超越 ryubyte,本轮让观感与交互同级且更强。- 鉴权填入优化(无模式切换,§10.1 修订):GUI 表单就是一个”Bearer Token(可选)”输入框(空=匿名),不设”变量名/直接填”模式切换。填写保存时插件自动生成变量名(
A2A_INBOUND_<id>/A2A_OUTBOUND_<id>),经 harnessctx.credentials.set(credentialRef(name), value)写入用户层.env/凭据存储(0o700 管理);实例记录只存该变量名。运行时统一ctx.credentials.resolve(name)分层读取(进程环境 → 凭据存储 →.env),因此既支持 GUI 直填,也兼容外部 export 同名变量;token 明文绝不进 a2a 域/AgentCard,GUI 不回显 token 值(优于 ryubyte 的明文落a2a.json)。
历史:v0.2 文档(单实例、工具白名单派生技能)已废弃,由本稿取代;旧版配置不迁移,直接删除。
ptc agent preset”一句话:把 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 | 单页 | 多实例增删改/启停/预设/宣告/鉴权全覆盖 |
┌─ 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 绑定 + 鉴权 + (出站时)远端引用。入站与出站共享同一定义框架,只是方向与承载不同。
每个入站 server 实例:
id: 稳定标识(路径安全 [a-z0-9-])name / description / version: 身份(AgentCard 头部)endpointPath: 独立端点,默认 /a2a/<id>preset: 必填的 agent preset id(如 standard/ptc);创建时未选 = 部署默认(agentPresets.defaultId)。该实例的入站任务会话全部按此 preset 组装authTokenEnv: 可选,环境变量名(每实例独立鉴权;也可经 GUI 直接填 token,由插件写入凭据层,§10.1)skills: 派生视图(§5),不落库——由 preset 实时派生,创建者不输入enabled: 开关GUI 预设下拉只列 roster 真实预设(id/name/描述),默认选中部署默认,无”默认”占位项(与应用内 select 完全一致;见 §9)。
webServer 上每实例注册:
GET /.well-known/agent-card.json(默认实例)或 /a2a/<id>/agent-card.json(命名实例)POST /a2a/<id>(JSON-RPC / SSE)AgentCard 的 supportedInterfaces[].url 指向实例自己端点,protocolBinding: "JSONRPC", protocolVersion: "1.0"。
实例创建时把 preset 传入会话池:入站任务 SendMessage → 该实例的会话按 agentPresets.resolve(preset) + mount(agentCtx, preset) 组装(机制同 ContextSessionPool,预设来自实例配置)。不同入站 server 各自拥有不同的工作模式(如一个 standard、一个 ptc)。
技能的宣告与执行同源:宣告列表由该 preset 的 standing scope 技能目录派生(§5);远端任务带 metadata.skill 进入后,由该 preset 会话执行,模型经会话内的 tool-skill(目录/装载工具)装载并运行该技能——宣告集合 ⊂ 会话可用技能,闭环一致。
GUI 可:创建(选 preset/设鉴权 env)、启用/停用、删除、编辑。删除会 dispose 该实例的全部 effect 与环境。技能列表随 preset 派生,创建/编辑时无需手工输入。
每个出站 server 连接实例:
id: 稳定标识(a2a-out-<uuid> 或用户命名)name: 展示名(工具命名空间)agentCardUrl: 远端 AgentCard 地址bearerTokenEnv: 可选,环境变量名(或经 GUI 直接填 token,由插件写入凭据层,§10.1)preset: 必填,本端在与该远端交互时按该 preset 组装(调用远端时本地维护的会话);创建时未选 = 部署默认enabled / timeoutMs出站 preset 语义(已确认):需求 4 按用户确认的 (b) 解读——出站 server 的 preset 决定”与这个远端对话时本 DSH 维护的本地会话组装”。当前版本仅持久化该元数据,尚未接线本地 hand-off 会话;出站不宣告 AgentCard。
每连接每技能注册为模型工具 a2a__<name>__<skill>(规范名,冲突哈希)。
preset 确定 → 技能确定;一切皆插件。 harness 中技能是真实机制:ctx.skills(@deepseek-ai/dsh-skill)是按 scope 分层的技能注册表,插件(提供者,如 preset 内的 skill-filesystem)往所在 scope 的层注册技能条目;tool-skill 是技能的目录/装载工具(模型经它看到、装载技能)。技能条目含 name(kebab-case 技能 id)、description(必填)、whenToUse?、invocation.modelInvocable。
每个入站实例的技能列表 = 该 preset 的真实可用技能:
agentPresets.standingKeyFor(preset) → 该 preset standing mount 的 scope key(不启动 agent、不占会话;dsh-agent-presets 为这种”无 agent 读取”场景专门提供);ctx.skills.list({ scope }) → 该 preset agent 可见的完整技能目录(preset 层贡献 + 部署全局层——全局技能该 agent 确实能调,宣告”这台 server 实际能做的”正是这份清单);invocation.modelInvocable === false(远端调用是模型面),映射为 AgentSkill[]:id = name、name = name、description = description(可拼 whenToUse)。agentPresets/skills 服务或 standing mount 不可用时(最小组合/单测),宣告内置 chat 技能(“Conversational assistance over a DSH agent session”)以保持可启动、可端到端验证;metadata.skill → preset 会话执行 → 模型经 tool-skill 装载运行(§3.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[]。
| 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 |
实现 |
"TASK_STATE_*"(SCREAMING_SNAKE_CASE)"ROLE_USER" / "ROLE_AGENT"application/json;流式 text/event-streamA2A-Version 头:客户端发送版本,服务端校验,不支持则 VersionNotSupportedError(-32007)A2A-Extensions 头:透传(本插件不消费)-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。
a2a 域表(JSON,版本 1——加表不改记录格式):
inbound-servers: InboundServerRecord { id, name, description, version, endpointPath, preset?, authTokenEnv?, skills: AgentSkill[], enabled }outbound-servers: OutboundServerRecord { id, name, agentCardUrl, bearerTokenEnv?, preset?, enabled, timeoutMs }tasks / contexts / agents / identity: 保留;任务记录加 serverId 来源字段TaskRecord 增加可选 serverId,标识来自哪个入站实例;jobs/pushConfigs(P1 push)预留。
ctx.a2a facade 扩展:server 实例 CRUD(enable/disable/remove/update)、preset 选择、技能宣告编辑、出站连接 CRUD、任务按实例查询/取消/a2a 命令面同步扩展(备用路线)/a2a/api)扩展:多实例快照 + 控制浏览器端注册 A2A 连接 设置页(React,src/client/)。对标 ryubyte/dsh-a2a 的成熟面板并超越之(功能面本就更广:多入站/多出站 CRUD、preset 选择、派生技能、鉴权 env;本轮让观感与交互同级):
src/client/dashboard.css.ts(独立样式模块,idempotent <style> 注入,类名 a2a- 前缀防碰撞);--dsw-alias-* 设计 token:label-primary/secondary/tertiary、bg-layer-2、border-l2/l3、state-success/warn/error、button-primary、interactive-bg-hover、brand-primary;另用 --ds-font-family-code(mono);@container(inline-size)按设置抽屉宽度响应式(非浏览器视口);style 属性与内联样式字符串。discover 仅读卡片不改状态);add)或「取消」。A2A_INBOUND_<id> / A2A_OUTBOUND_<id>),调用 ctx.credentials.set(ref, value) 写入用户层 .env/凭据存储;实例记录只存该变量名;ctx.credentials.unset(ref) 并移除记录变量名(恢复匿名);所有写操作经 loopback-only /a2a/api。
A2A_INBOUND_<id> / A2A_OUTBOUND_<id>),调用 harness ctx.credentials.set(credentialRef(name), value) 写入用户层 .env/凭据存储(目录 0o700 管理);实例记录只存该变量名;ctx.credentials.resolve(name) 分层读取(每操作重解析):进程环境(外部 export 同名变量)→ 凭据存储 → .env 回退;故外部部署仍可用旧式”预置环境变量 + 记录变量名”方式,与 GUI 直填并存;a2a.json):token 明文只存在于凭据服务管理的层,绝不写入 a2a 域表/插件配置/AgentCard;记录与卡片始终只有变量名;GUI 不反向回显 token 值。/a2a/api 仅回环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
server.enabled/client.agents 等配置不再被读取。TASK_STATE_*)ctx.skills.list,过滤 modelInvocable),创建者不手写 —— 已确认(2026-09-06,B’ 方案).research/A2A(官方 1.0.1) —— 已确认(方法名/枚举/结构已按此对齐)ctx.credentials.set 写入 .env,记录仅存变量名、token 不落域明文) —— 方案已定(2026-09-07,待实施)