@hanphone/dsh-a2a 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +101 -106
  2. package/README.zh.md +50 -74
  3. package/cordis.patch.yml +4 -7
  4. package/lib/client.js +217 -73
  5. package/lib/index.js +1258 -867
  6. package/lib/types/api.d.ts +40 -23
  7. package/lib/types/api.d.ts.map +1 -1
  8. package/lib/types/api.js +58 -23
  9. package/lib/types/api.js.map +1 -1
  10. package/lib/types/client/index.d.ts +50 -19
  11. package/lib/types/client/index.d.ts.map +1 -1
  12. package/lib/types/commands.d.ts +4 -2
  13. package/lib/types/commands.d.ts.map +1 -1
  14. package/lib/types/commands.js +59 -46
  15. package/lib/types/commands.js.map +1 -1
  16. package/lib/types/events.d.ts +32 -9
  17. package/lib/types/events.d.ts.map +1 -1
  18. package/lib/types/index.d.ts +21 -32
  19. package/lib/types/index.d.ts.map +1 -1
  20. package/lib/types/index.js +173 -272
  21. package/lib/types/index.js.map +1 -1
  22. package/lib/types/outbound/calls.d.ts.map +1 -1
  23. package/lib/types/outbound/calls.js +8 -6
  24. package/lib/types/outbound/calls.js.map +1 -1
  25. package/lib/types/protocol.d.ts +174 -109
  26. package/lib/types/protocol.d.ts.map +1 -1
  27. package/lib/types/protocol.js +58 -34
  28. package/lib/types/protocol.js.map +1 -1
  29. package/lib/types/server/a2a-server.d.ts +4 -0
  30. package/lib/types/server/a2a-server.d.ts.map +1 -1
  31. package/lib/types/server/a2a-server.js +6 -2
  32. package/lib/types/server/a2a-server.js.map +1 -1
  33. package/lib/types/server/card.d.ts +6 -30
  34. package/lib/types/server/card.d.ts.map +1 -1
  35. package/lib/types/server/card.js +13 -49
  36. package/lib/types/server/card.js.map +1 -1
  37. package/lib/types/server/exec/agent-runtime.d.ts +3 -0
  38. package/lib/types/server/exec/agent-runtime.d.ts.map +1 -1
  39. package/lib/types/server/exec/agent-runtime.js +2 -1
  40. package/lib/types/server/exec/agent-runtime.js.map +1 -1
  41. package/lib/types/server/routes.d.ts +5 -1
  42. package/lib/types/server/routes.d.ts.map +1 -1
  43. package/lib/types/server/routes.js +9 -5
  44. package/lib/types/server/routes.js.map +1 -1
  45. package/lib/types/server/store.d.ts +35 -1
  46. package/lib/types/server/store.d.ts.map +1 -1
  47. package/lib/types/server/store.js +10 -0
  48. package/lib/types/server/store.js.map +1 -1
  49. package/lib/types/servers/inbound-manager.d.ts +131 -0
  50. package/lib/types/servers/inbound-manager.d.ts.map +1 -0
  51. package/lib/types/servers/inbound-manager.js +312 -0
  52. package/lib/types/servers/inbound-manager.js.map +1 -0
  53. package/lib/types/servers/outbound-manager.d.ts +129 -0
  54. package/lib/types/servers/outbound-manager.d.ts.map +1 -0
  55. package/lib/types/servers/outbound-manager.js +238 -0
  56. package/lib/types/servers/outbound-manager.js.map +1 -0
  57. package/lib/types/service.d.ts +106 -40
  58. package/lib/types/service.d.ts.map +1 -1
  59. package/lib/types/service.js +36 -27
  60. package/lib/types/service.js.map +1 -1
  61. package/package.json +2 -2
  62. package/src/api.ts +67 -53
  63. package/src/client/index.ts +309 -148
  64. package/src/commands.ts +59 -43
  65. package/src/events.ts +14 -7
  66. package/src/index.ts +199 -310
  67. package/src/outbound/calls.ts +8 -6
  68. package/src/protocol.ts +204 -95
  69. package/src/server/a2a-server.ts +9 -2
  70. package/src/server/card.ts +13 -62
  71. package/src/server/exec/agent-runtime.ts +5 -2
  72. package/src/server/routes.ts +6 -4
  73. package/src/server/store.ts +45 -4
  74. package/src/servers/inbound-manager.ts +385 -0
  75. package/src/servers/outbound-manager.ts +289 -0
  76. package/src/service.ts +125 -40
  77. package/lib/tsconfig.client.tsbuildinfo +0 -1
  78. package/lib/tsconfig.tsbuildinfo +0 -1
package/README.md CHANGED
@@ -1,42 +1,55 @@
1
1
  # dsh-a2a
2
2
 
3
- Agent2Agent (A2A) Protocol v1.0 dual-end plugin for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) — **English** · [中文](README.zh.md)
3
+ Agent2Agent (A2A) Protocol v1.0.1 dual-end plugin for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) — **English** · [中文](README.zh.md)
4
4
 
5
5
  `@hanphone/dsh-a2a` is an independent, open-source A2A plugin that turns a
6
- DeepSeek Harness profile into a first-class agent-to-agent citizen: an inbound
7
- server with a durable governed task store and a live-derived AgentCard, an
8
- outbound client whose remote skills appear as model tools, and a GUI
9
- dashboard that manages both halves without touching any file.
6
+ DeepSeek Harness profile into a multi-faced agent-to-agent citizen: it can
7
+ simultaneously serve **multiple inbound A2A servers**, each bound to its own
8
+ agent preset with its own endpoint, AgentCard, declared skills and auth, and
9
+ connect to **multiple outbound A2A servers**, each with its own preset, whose
10
+ remote skills appear as model tools. Every server instance is created,
11
+ started, stopped, edited and removed entirely from the GUI — no config-file
12
+ editing.
10
13
 
11
14
  Architecture and design decisions: [docs/architecture.md](docs/architecture.md).
12
15
 
13
16
  ## Features
14
17
 
15
- - **A2A v1.0 protocol surface** — `SendMessage`, `SendStreamingMessage`,
16
- `GetTask`, `ListTasks`, `CancelTask`, `GetExtendedAgentCard`,
17
- `SubscribeToTask` over JSON-RPC; SSE streaming with catch-up frames.
18
- - **Dynamic AgentCard** skills derived from the live `ctx.tools` registry
19
- (explicit id list, loud failure on missing referents) plus a built-in
20
- `chat` skill; a fresh install answers end-to-end immediately.
18
+ - **A2A v1.0.1 protocol surface, aligned with the official spec** —
19
+ `SendMessage`, `SendStreamingMessage`, `GetTask`, `ListTasks`, `CancelTask`,
20
+ `GetExtendedAgentCard`, `SubscribeToTask` over JSON-RPC; SSE streaming with
21
+ catch-up frames; official `TASK_STATE_*` / `ROLE_*` enums and AgentCard
22
+ structure (including `supportedInterfaces`, `capabilities`, full error-code
23
+ table).
24
+ - **Multiple inbound servers** — one per persona. Each instance gets its own
25
+ endpoint (`/a2a/<id>`), AgentCard route, authentication env, and skill
26
+ declarations.
27
+ - **Per-instance agent preset** — every inbound server binds an agent preset
28
+ (e.g. `ptc`, `standard`, `minimal`, …); inbound tasks execute in sessions
29
+ composed from that preset through the standard `agentPresets` resolve+mount
30
+ path.
31
+ - **Creator-declared skill declarations** — the AgentCard's skills are the
32
+ text the creator entered (id/name/description per skill); when left empty at
33
+ creation, the default is the bound preset's display name (built-in `chat`
34
+ otherwise). The v0.2 tool white-list derivation is removed.
35
+ - **Multiple outbound servers** — each connection has its own remote URL,
36
+ auth env, timeout and optional preset; enabled instances map remote skills
37
+ to `a2a__<name>__<skill>` model tools.
21
38
  - **Durable task store** — tasks live in the `a2a` storage domain (JSON
22
39
  backend by default, SQLite per deployment choice); server-generated ids
23
- survive restarts.
40
+ survive restarts, and each task records the inbound server it arrived
41
+ through.
24
42
  - **Executors** — `session` (one DSH session per `contextId`) and `subagent`
25
43
  (delegates to `ctx.subagents`, streams tool-call artifacts back).
26
44
  - **Governed inbound** — every inbound task passes through the
27
45
  `a2a/inbound-task` waterfall so policy plugins can veto or audit.
28
46
  - **Inbound connection monitoring** — the dashboard shows which remote peers
29
- are talking to this DSH (source, first/last seen, task counts, active
30
- streams) and can close a peer.
31
- - **Runtime service identity** edit the AgentCard's name/description/version
32
- from the dashboard; the card rebuilds immediately and the identity persists
33
- across restarts.
34
- - **First-run onboarding** — a fresh install shows a pre-filled identity form
35
- ("service identity") so publishing the service is one click.
36
- - **Auth by environment variable** — inbound bearer token is referenced by
37
- env-var name (`authTokenEnv`), never stored in config as plaintext.
38
- - **Install-and-use** — both halves are enabled by default after
39
- `dsh plugin add`; no manual patch required to start.
47
+ are talking to each instance and can close a peer.
48
+ - **Auth by environment variable** — each instance's bearer token is
49
+ referenced by env-var name (`authTokenEnv`), never stored as plaintext.
50
+ - **Minimal plugin config** instances are created through the GUI and live
51
+ in the domain; the plugin `Config` only carries host-level defaults
52
+ (`baseUrl`, `subagentProvider`, `defaultTimeoutMs`).
40
53
 
41
54
  ## Installation
42
55
 
@@ -58,7 +71,7 @@ dsh plugin --profile <name> add @hanphone/dsh-a2a
58
71
  cd dsh-a2a
59
72
  pnpm build
60
73
  npm pack
61
- dsh plugin --profile <name> add <path-to>/hanphone-dsh-a2a-0.2.0.tgz
74
+ dsh plugin --profile <name> add <path-to>/hanphone-dsh-a2a-<version>.tgz
62
75
  ```
63
76
 
64
77
  ## Quick start
@@ -66,21 +79,21 @@ dsh plugin --profile <name> add <path-to>/hanphone-dsh-a2a-0.2.0.tgz
66
79
  1. **Install** — `dsh plugin --profile web add @hanphone/dsh-a2a`.
67
80
  2. **Restart the GUI** — the browser half is scanned at host startup, so
68
81
  restart once after installing (`pnpm dsh web` or your profile launcher).
69
- 3. **Open Settings → A2A 连接** — the dashboard shows the inbound server
70
- status, the service identity (with first-run onboarding pre-filled), the
71
- outbound agent list, inbound peer connections, and the task list.
82
+ 3. **Open Settings → A2A 连接** — create your first inbound server (pick a
83
+ preset, declare skills, optionally set an auth env). It is enabled
84
+ immediately and publishes its own endpoint and AgentCard.
72
85
 
73
- The inbound server listens on the profile's webServer (default
74
- `http://127.0.0.1:3080`):
86
+ Each inbound server listens on the profile's webServer:
75
87
 
76
88
  ```sh
77
- curl http://127.0.0.1:3080/.well-known/agent-card.json
89
+ # the created instance's AgentCard (see the GUI for the exact id)
90
+ curl http://127.0.0.1:3080/a2a/<id>/agent-card.json
78
91
  ```
79
92
 
80
- Send a task (the built-in `chat` skill):
93
+ Send a task to an instance (its declared `chat` skill):
81
94
 
82
95
  ```sh
83
- curl -X POST http://127.0.0.1:3080/a2a \
96
+ curl -X POST http://127.0.0.1:3080/a2a/<id> \
84
97
  -H "content-type: application/json" \
85
98
  -d '{"jsonrpc":"2.0","id":"1","method":"SendMessage","params":{"message":{"role":"user","parts":[{"text":"hello"}],"metadata":{"skill":"chat"}}}}'
86
99
  ```
@@ -90,59 +103,53 @@ curl -X POST http://127.0.0.1:3080/a2a \
90
103
  The browser half registers an **A2A 连接** page under Settings. From it you
91
104
  can, without touching any file:
92
105
 
93
- - toggle the inbound server;
94
- - view and edit the service identity (name/description/version) the
95
- AgentCard rebuilds immediately and the change persists;
96
- - list, add, enable/disable, refresh, and remove outbound agents;
97
- - see which inbound peers are connected (source, activity, tasks) and close
98
- one;
99
- - view and cancel inbound tasks.
106
+ - **入站 Servers** — create inbound servers (name/description/version, agent
107
+ preset picker, auth env, skill-declaration textarea), start/stop, edit, and
108
+ remove them; each row shows its endpoint, preset, declared skills and live
109
+ AgentCard URL.
110
+ - **出站 Servers** add outbound connections (name, remote AgentCard URL,
111
+ preset picker, bearer env, timeout), start/stop, refresh, and remove them;
112
+ each row shows connection state and tool registration counts.
113
+ - **任务** — view and cancel inbound tasks (each task carries its source
114
+ server).
115
+ - **入站连接** — see which remote peers are talking to the instances and
116
+ close one.
100
117
 
101
118
  All dashboard traffic goes through the **loopback-only** `/a2a/api` route —
102
119
  remote peers can never drive it.
103
120
 
104
121
  ## Configuration
105
122
 
106
- The dashboard covers day-to-day operations. Values the dashboard does not
107
- edit (baseUrl, `authTokenEnv`, skills, executors, toolPrefix) are configured
108
- through the profile's user patch layer (`$DSH_HOME/profiles/<name>/cordis.patch.yml`)
109
- — the reserve path:
123
+ The GUI covers instance management day-to-day. The plugin `Config` only has
124
+ host-level defaults, set through the profile's user patch layer
125
+ (`$DSH_HOME/profiles/<name>/cordis.patch.yml`) if you want to override them:
110
126
 
111
127
  ```yaml
112
128
  - id: a2a
113
129
  config:
114
- server:
115
- enabled: true
116
- name: My DSH Agent
117
- description: A DeepSeek Harness agent exposed over A2A v1.0
118
- version: 0.1.0
119
- baseUrl: http://127.0.0.1:<port> # omit to derive from the webServer address
120
- endpointPath: /a2a
121
- authTokenEnv: A2A_INBOUND_TOKEN # optional; an env var NAME, never the token
122
- skills:
123
- ids: [] # explicit tool ids to expose; chat is built-in
124
- exclude: []
125
- executors:
126
- chat: session # or subagent (needs the subagent seam)
127
- subagentProvider: in-process
128
- client:
129
- toolPrefix: a2a
130
- agents: [] # or declare agents declaratively
130
+ baseUrl: http://127.0.0.1:<port> # omit to derive from the webServer address
131
+ subagentProvider: in-process
132
+ defaultTimeoutMs: 60000 # default outbound connection timeout
131
133
  ```
132
134
 
135
+ Instances are **not** configured via patches — they are created in the GUI and
136
+ persisted in the `a2a` domain (`inbound_servers` / `outbound_servers` tables).
137
+
133
138
  ### Required host services
134
139
 
135
140
  Base-backed profiles mount them all: `webServer` (`@deepseek-ai/dsh-host-webserver`),
136
141
  the storage stack (`@deepseek-ai/dsh-storage` + `@deepseek-ai/dsh-storage-domain`),
137
- the tools registry (`@deepseek-ai/dsh-tools`), and an agent loop
138
- (`@deepseek-ai/dsh-agent` + `@deepseek-ai/dsh-agent-loop`; the subagent
139
- executor additionally needs `@deepseek-ai/dsh-subagent`).
142
+ the tools registry (`@deepseek-ai/dsh-tools`), an agent loop
143
+ (`@deepseek-ai/dsh-agent` + `@deepseek-ai/dsh-agent-loop`), and agent presets
144
+ (`@deepseek-ai/dsh-agent-presets`; the preset pickers and preset-bound session
145
+ composition need it). The subagent executor additionally needs
146
+ `@deepseek-ai/dsh-subagent`.
140
147
 
141
148
  ### Storage backend
142
149
 
143
- The task store lives in the `a2a` storage domain. The base composition routes
144
- storage through the `json` backend; to use SQLite, route the domain and add
145
- the backend in the same patch layer:
150
+ The task and instance stores live in the `a2a` storage domain. The base
151
+ composition routes storage through the `json` backend; to use SQLite, route
152
+ the domain and add the backend in the same patch layer:
146
153
 
147
154
  ```yaml
148
155
  - id: storage-domain
@@ -157,46 +164,31 @@ the backend in the same patch layer:
157
164
  path: /absolute/path/to/a2a.sqlite
158
165
  ```
159
166
 
160
- ### Outbound agents (file-declared, optional — the GUI manages the same list)
161
-
162
- ```yaml
163
- - id: a2a
164
- config:
165
- client:
166
- toolPrefix: a2a
167
- agents:
168
- - name: my-remote-agent
169
- agentCardUrl: https://remote.example/.well-known/agent-card.json
170
- bearerTokenEnv: A2A_REMOTE_TOKEN # optional; env var NAME
171
- enabled: true
172
- timeoutMs: 60000
173
- ```
174
-
175
- Each enabled remote agent's skills become model tools named
176
- `a2a__<name>__<skill>` (normalized, collision-hashed). The registry persists
177
- across restarts.
178
-
179
167
  ## CLI
180
168
 
181
- A `/a2a` chat command mirrors the dashboard:
169
+ A `/a2a` chat command mirrors the dashboard (a text backup to the GUI):
182
170
 
183
171
  ```
184
- a2a status | enable | disable | card | agents |
185
- agent add|remove|enable|disable|refresh |
172
+ a2a status | presets | peers |
173
+ inbound list|create|remove|enable|disable |
174
+ outbound list|create|remove|enable|disable|refresh |
186
175
  tasks | task get|cancel <id> | help
187
176
  ```
188
177
 
189
178
  ## How it works
190
179
 
191
- - **Inbound** — `POST /a2a` (JSON-RPC) and `GET /.well-known/agent-card.json`;
192
- the AgentCard derives from the live tool registry and carries the persisted
193
- service identity. Tasks flow through `a2a/inbound-task` executor task
194
- store, with SSE frames streamed to subscribers.
195
- - **Outbound** — a persisted `agents` table in the `a2a` domain; `A2AClient`
196
- discovers an AgentCard, and each skill registers as a tool.
180
+ - **Inbound** — an `InboundServerManager` owns every instance: one preset-bound
181
+ session pool + `A2AServer` + routes per instance. Each instance persists in
182
+ the `inbound_servers` table and serves its own endpoint + AgentCard. Tasks
183
+ flow through `a2a/inbound-task` → executor → task store, with SSE frames
184
+ streamed to subscribers.
185
+ - **Outbound** an `OutboundServerManager` owns every connection: one
186
+ `OutboundAgentRegistry` with an isolated agent store per instance,
187
+ persisted in the `outbound_servers` table. `A2AClient` discovers an
188
+ AgentCard, and each skill registers as a tool.
197
189
  - **Dashboard** — the browser half (React, `settings.section`) reads/writes
198
- the loopback-only `/a2a/api` route; the host half feeds it snapshots of the
199
- server, tasks, agents, inbound peers, and the service identity.
190
+ the loopback-only `/a2a/api` route; the host half feeds it snapshots of
191
+ inbound/outbound server views and the preset roster (`/a2a/api/presets`).
200
192
 
201
193
  See [docs/architecture.md](docs/architecture.md) for the full design.
202
194
 
@@ -204,13 +196,16 @@ See [docs/architecture.md](docs/architecture.md) for the full design.
204
196
 
205
197
  ```
206
198
  src/
207
- api.ts # loopback dashboard API (/a2a/api)
199
+ api.ts # loopback dashboard API (/a2a/api, /a2a/api/presets)
208
200
  index.ts # Cordis plugin entry (apply)
209
- protocol.ts # A2A v1.0 protocol constants + types
201
+ protocol.ts # A2A v1.0.1 protocol constants + types
210
202
  jsonrpc.ts # JSON-RPC framing
211
- server/ # inbound half: store, card, a2a-server, routes,
212
- # executors, inbound-registry, identity
213
- outbound/ # outbound half: A2AClient, registry, tools
203
+ servers/ # multi-instance managers
204
+ inbound-manager.ts # inbound server instances (CRUD, routes, lifecycle)
205
+ outbound-manager.ts # outbound connection instances (CRUD, tools)
206
+ server/ # single-instance internals: store, card, a2a-server,
207
+ # routes, executors, inbound-registry
208
+ outbound/ # outbound internals: A2AClient, registry, tools
214
209
  client/ # browser half: settings dashboard (React)
215
210
  service.ts # ctx.a2a service facade
216
211
  commands.ts # /a2a chat command
@@ -218,7 +213,7 @@ tests/
218
213
  unit/ # protocol, framing, card, store, registry, server,
219
214
  # client, api, inbound-registry, identity
220
215
  composition/ # apply() on a real Cordis Context with stub host services
221
- cordis.patch.yml # bundle patch (mounts the plugin, enabled by default)
216
+ cordis.patch.yml # bundle patch (mounts the plugin; instances are GUI-managed)
222
217
  ```
223
218
 
224
219
  ## Development
@@ -237,10 +232,10 @@ pnpm build # tsc + tsdown → lib/index.js (host) + lib/client.js (browser
237
232
  This plugin was inspired by and developed alongside
238
233
  [ryubyte/dsh-a2a](https://github.com/ryubyte/dsh-a2a), an earlier A2A plugin
239
234
  for DeepSeek Harness. That project's design — dual-end scope, AgentCard
240
- derivation from the tool registry, and the settings-dashboard pattern — set
241
- the direction this implementation follows. Our protocol layer, task store,
242
- and executor seam are independent implementations; the GUI management model
243
- owes a direct debt to ryubyte's connection dashboard.
235
+ advertisement, and the settings-dashboard pattern — set the direction this
236
+ implementation follows. Our protocol layer, task store, and executor seam are
237
+ independent implementations; the GUI management model owes a direct debt to
238
+ ryubyte's connection dashboard.
244
239
 
245
240
  ## License
246
241
 
package/README.zh.md CHANGED
@@ -1,23 +1,24 @@
1
1
  # dsh-a2a
2
2
 
3
- Agent2Agent(A2A)v1.0 双端插件,用于 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) — [English](README.md) · **中文**
3
+ Agent2Agent(A2A)v1.0.1 双端插件,用于 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) — [English](README.md) · **中文**
4
4
 
5
- `@hanphone/dsh-a2a` 是一个独立开源的 A2A 插件,把 DeepSeek Harness profile 变成 A2A 一等公民:带持久化、受治理任务存储与实时派生 AgentCard 的入站服务端;远程技能映射为模型工具的出站客户端;以及无需改任何文件即可管理两端的 GUI 面板。
5
+ `@hanphone/dsh-a2a` 是一个独立开源的 A2A 插件,把 DeepSeek Harness profile 变成多面手 A2A 一等公民:能**同时对外发布多个入站 A2A server**,每个绑定一个自己的 agent preset、拥有独立端点 / AgentCard / 技能宣告 / 鉴权;也能**同时接入多个出站 A2A server**,每个独立 preset,远端技能映射为模型工具。所有 server 实例的创建、启停、编辑、删除全部在 GUI 完成——无需改任何配置文件。
6
6
 
7
7
  架构与设计决策:[docs/architecture.md](docs/architecture.md)。
8
8
 
9
9
  ## 功能
10
10
 
11
- - **A2A v1.0 协议面** — JSON-RPC 上的 `SendMessage`、`SendStreamingMessage`、`GetTask`、`ListTasks`、`CancelTask`、`GetExtendedAgentCard`、`SubscribeToTask`;SSE 流式带补发帧。
12
- - **动态 AgentCard** — 技能从实时 `ctx.tools` 注册表派生(显式 id 清单,缺失引用大声失败),外加内置 `chat` 技能;全新安装即可端到端验证。
13
- - **持久化任务存储**任务存于 `a2a` 存储域(默认 json 后端,可按部署切 SQLite);服务端生成 id,跨重启存活。
14
- - **执行器**`session`(每个 `contextId` 一个 DSH 会话)与 `subagent`(委托 `ctx.subagents`,把工具调用过程流式回传)。
15
- - **受治理入站**每个入站任务都经过 `a2a/inbound-task` waterfall,策略插件可否决或审计。
16
- - **入站连接监控**面板展示谁在调用本 DSH(来源、首/末次、任务数、活跃流),可关闭某个对端。
17
- - **运行时服务身份**在面板中编辑 AgentCard name/description/version;卡片立即重建,身份跨重启持久化。
18
- - **引导式首次配置**全新安装显示预填身份表单("服务身份"),一步发布服务。
19
- - **环境变量鉴权**入站 Bearer token 只以环境变量名(`authTokenEnv`)引用,不以明文落配置。
20
- - **装完即用**`dsh plugin add` 后两端默认启用,无需手动 patch。
11
+ - **A2A v1.0.1 协议面,对齐官方规范** — JSON-RPC 上的 `SendMessage`、`SendStreamingMessage`、`GetTask`、`ListTasks`、`CancelTask`、`GetExtendedAgentCard`、`SubscribeToTask`;SSE 流式带补发帧;官方 `TASK_STATE_*` / `ROLE_*` 枚举与 AgentCard 结构(含 `supportedInterfaces`、`capabilities`、完整错误码表)。
12
+ - **多入站 server** — 每实例一个"人格":独立端点(`/a2a/<id>`)、AgentCard 路由、鉴权 env、技能宣告。
13
+ - **每实例 agent preset** 每个入站 server 绑定一个 agent preset(如 `ptc`、`standard`、`minimal`、…);入站任务在按该 preset 组装的会话中执行(标准 `agentPresets` resolve+mount 路径)。
14
+ - **创建者输入技能宣告**AgentCard 技能即创建者输入的文字(每个技能 id/name/description);创建留空时默认取所绑 preset 展示名(否则内置 `chat`)。v0.2 工具白名单派生机制已删除。
15
+ - **多出站 server** 每个连接独立远端 URL、鉴权 env、超时与可选 preset;启用实例把远端技能映射为 `a2a__<name>__<skill>` 模型工具。
16
+ - **持久化任务存储**任务存于 `a2a` 存储域(默认 json 后端,可按部署切 SQLite);服务端生成 id 跨重启存活,每个任务记录来源入站实例。
17
+ - **执行器**`session`(每个 `contextId` 一个 DSH 会话)与 `subagent`(委托 `ctx.subagents`,工具调用过程流式回传)。
18
+ - **受治理入站**每个入站任务经过 `a2a/inbound-task` waterfall,策略插件可否决或审计。
19
+ - **入站连接监控**面板展示每个实例的对端连接,可关闭某个对端。
20
+ - **环境变量鉴权**每个实例的 Bearer token 只以环境变量名(`authTokenEnv`)引用,不落明文。
21
+ - **最小插件配置** — 实例经 GUI 创建并存于域中;插件 `Config` 只承载宿主级默认值(`baseUrl`、`subagentProvider`、`defaultTimeoutMs`)。
21
22
 
22
23
  ## 安装
23
24
 
@@ -39,25 +40,26 @@ dsh plugin --profile <name> add @hanphone/dsh-a2a
39
40
  cd dsh-a2a
40
41
  pnpm build
41
42
  npm pack
42
- dsh plugin --profile <name> add <path-to>/hanphone-dsh-a2a-0.2.0.tgz
43
+ dsh plugin --profile <name> add <path-to>/hanphone-dsh-a2a-<version>.tgz
43
44
  ```
44
45
 
45
46
  ## 快速开始
46
47
 
47
48
  1. **安装** — `dsh plugin --profile web add @hanphone/dsh-a2a`。
48
49
  2. **重启 GUI** — 浏览器端插件表在 host 启动时扫描,装完请重启一次(`pnpm dsh web` 或对应 profile 启动命令)。
49
- 3. **打开 设置 → A2A 连接** — 面板展示入站服务端状态、服务身份(全新安装预填引导表单)、出站 agent 列表、入站对端连接、任务列表。
50
+ 3. **打开 设置 → A2A 连接** — 创建第一个入站 server(选 preset、填技能宣告、可选鉴权 env)。创建即时启用并发布自己的端点与 AgentCard。
50
51
 
51
- 入站服务端监听在 profile 的 webServer 上(默认 `http://127.0.0.1:3080`):
52
+ 每个入站 server 监听在 profile 的 webServer 上:
52
53
 
53
54
  ```sh
54
- curl http://127.0.0.1:3080/.well-known/agent-card.json
55
+ # 所建实例的 AgentCard(确切 id 见 GUI)
56
+ curl http://127.0.0.1:3080/a2a/<id>/agent-card.json
55
57
  ```
56
58
 
57
- 发一个任务(内置 `chat` 技能):
59
+ 向实例发任务(其宣告的 `chat` 技能):
58
60
 
59
61
  ```sh
60
- curl -X POST http://127.0.0.1:3080/a2a \
62
+ curl -X POST http://127.0.0.1:3080/a2a/<id> \
61
63
  -H "content-type: application/json" \
62
64
  -d '{"jsonrpc":"2.0","id":"1","method":"SendMessage","params":{"message":{"role":"user","parts":[{"text":"hello"}],"metadata":{"skill":"chat"}}}}'
63
65
  ```
@@ -66,47 +68,34 @@ curl -X POST http://127.0.0.1:3080/a2a \
66
68
 
67
69
  浏览器端在设置中注册 **A2A 连接** 页。无需改文件即可:
68
70
 
69
- - 开关入站服务端;
70
- - 查看与编辑服务身份(name/description/version)——AgentCard 立即重建,变更持久化;
71
- - 列出、添加、启用/停用、刷新、删除出站 agent;
72
- - 查看入站对端连接(来源、活动、任务)并关闭某个对端;
73
- - 查看与取消入站任务。
71
+ - **入站 Servers** — 创建入站 server(名称/描述/版本、agent preset 选择器、鉴权 env、技能宣告表单),启停、编辑、删除;每行显示端点、preset、宣告技能与实时 AgentCard URL。
72
+ - **出站 Servers** — 添加出站连接(名称、远端 AgentCard URL、preset 选择器、Bearer env、超时),启停、刷新、删除;每行显示连接状态与工具注册数。
73
+ - **任务** — 查看与取消入站任务(每个任务携带来源 server)。
74
+ - **入站连接** — 查看哪些远程对端在调用各实例,可关闭某个对端。
74
75
 
75
- 所有面板流量都走 profile webServer 上的**仅回环** `/a2a/api` 路由——远程对端永远无法驱动它。
76
+ 所有面板流量走 profile webServer 上的**仅回环** `/a2a/api` 路由——远程对端永远无法驱动它。
76
77
 
77
78
  ## 配置
78
79
 
79
- 面板覆盖日常操作。面板不编辑的项(baseUrl、`authTokenEnv`、skills、executors、toolPrefix)通过 profile 用户 patch 层(`$DSH_HOME/profiles/<name>/cordis.patch.yml`)配置——保留方式:
80
+ GUI 覆盖日常实例管理。插件 `Config` 只有宿主级默认值,如需覆盖可经 profile 用户 patch 层(`$DSH_HOME/profiles/<name>/cordis.patch.yml`)设置:
80
81
 
81
82
  ```yaml
82
83
  - id: a2a
83
84
  config:
84
- server:
85
- enabled: true
86
- name: My DSH Agent
87
- description: A DeepSeek Harness agent exposed over A2A v1.0
88
- version: 0.1.0
89
- baseUrl: http://127.0.0.1:<port> # 省略则从 webServer 地址派生
90
- endpointPath: /a2a
91
- authTokenEnv: A2A_INBOUND_TOKEN # 可选;环境变量名,绝不写 token 明文
92
- skills:
93
- ids: [] # 暴露的显式工具 id;chat 为内置
94
- exclude: []
95
- executors:
96
- chat: session # 或 subagent(需要 subagent 接缝)
97
- subagentProvider: in-process
98
- client:
99
- toolPrefix: a2a
100
- agents: [] # 或声明式列出 agent
85
+ baseUrl: http://127.0.0.1:<port> # 省略则从 webServer 地址派生
86
+ subagentProvider: in-process
87
+ defaultTimeoutMs: 60000 # 出站连接默认超时
101
88
  ```
102
89
 
90
+ 实例**不**经 patch 配置——它们在 GUI 中创建并持久化于 `a2a` 域(`inbound_servers` / `outbound_servers` 表)。
91
+
103
92
  ### 所需宿主服务
104
93
 
105
- 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`;subagent 执行器还需要 `@deepseek-ai/dsh-subagent`)。
94
+ 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`。
106
95
 
107
96
  ### 存储后端
108
97
 
109
- 任务存储位于 `a2a` 存储域。base 组合默认走 `json` 后端;要切 SQLite,在同一 patch 层路由域并加后端:
98
+ 任务与实例存储位于 `a2a` 存储域。base 组合默认走 `json` 后端;要切 SQLite,在同一 patch 层路由域并加后端:
110
99
 
111
100
  ```yaml
112
101
  - id: storage-domain
@@ -121,38 +110,22 @@ base 类 profile 全部挂载:`webServer`(`@deepseek-ai/dsh-host-webserver`
121
110
  path: /absolute/path/to/a2a.sqlite
122
111
  ```
123
112
 
124
- ### 出站 agent(文件声明,可选——GUI 管理同一列表)
125
-
126
- ```yaml
127
- - id: a2a
128
- config:
129
- client:
130
- toolPrefix: a2a
131
- agents:
132
- - name: my-remote-agent
133
- agentCardUrl: https://remote.example/.well-known/agent-card.json
134
- bearerTokenEnv: A2A_REMOTE_TOKEN # 可选;环境变量名
135
- enabled: true
136
- timeoutMs: 60000
137
- ```
138
-
139
- 每个启用远程 agent 的技能会注册为 `a2a__<name>__<skill>` 模型工具(规范化、冲突哈希)。注册表跨重启持久化。
140
-
141
113
  ## CLI
142
114
 
143
- `/a2a` 聊天命令与面板对应:
115
+ `/a2a` 聊天命令与面板对应(GUI 的文字备用路线):
144
116
 
145
117
  ```
146
- a2a status | enable | disable | card | agents |
147
- agent add|remove|enable|disable|refresh |
118
+ a2a status | presets | peers |
119
+ inbound list|create|remove|enable|disable |
120
+ outbound list|create|remove|enable|disable|refresh |
148
121
  tasks | task get|cancel <id> | help
149
122
  ```
150
123
 
151
124
  ## 工作原理
152
125
 
153
- - **入站** — `POST /a2a`(JSON-RPC)与 `GET /.well-known/agent-card.json`;AgentCard 从实时工具注册表派生并携带持久化服务身份。任务流经 `a2a/inbound-task` → 执行器 → 任务存储,SSE 帧推送给订阅者。
154
- - **出站** — `a2a` 域中的持久化 `agents` 表;`A2AClient` 发现 AgentCard,每个技能注册为一个工具。
155
- - **面板** — 浏览器端(React,`settings.section`)经仅回环 `/a2a/api` 路由读写;host 端喂给它 server/tasks/agents/入站对端/服务身份的快照。
126
+ - **入站** — `InboundServerManager` 拥有每个实例:每实例 = preset 化会话池 + `A2AServer` + 路由。实例持久化于 `inbound_servers` 表,各自服务独立端点与 AgentCard。任务流经 `a2a/inbound-task` → 执行器 → 任务存储,SSE 帧推送给订阅者。
127
+ - **出站** — `OutboundServerManager` 拥有每个连接:每实例一个带独立 agent 存储的 `OutboundAgentRegistry`,实例持久化于 `outbound_servers` 表。`A2AClient` 发现 AgentCard,每个技能注册为一个工具。
128
+ - **面板** — 浏览器端(React,`settings.section`)经仅回环 `/a2a/api` 路由读写;host 端喂给它入站/出站 server 视图与 preset 名单(`/a2a/api/presets`)。
156
129
 
157
130
  完整设计见 [docs/architecture.md](docs/architecture.md)。
158
131
 
@@ -160,13 +133,16 @@ a2a status | enable | disable | card | agents |
160
133
 
161
134
  ```
162
135
  src/
163
- api.ts # 回环面板 API (/a2a/api)
136
+ api.ts # 回环面板 API (/a2a/api、/a2a/api/presets)
164
137
  index.ts # Cordis 插件入口 (apply)
165
- protocol.ts # A2A v1.0 协议常量与类型
138
+ protocol.ts # A2A v1.0.1 协议常量与类型
166
139
  jsonrpc.ts # JSON-RPC 帧
167
- server/ # 入站半区:store、card、a2a-server、routes、
168
- # executors、inbound-registry、identity
169
- outbound/ # 出站半区:A2AClient、registry、tools
140
+ servers/ # 多实例管理器
141
+ inbound-manager.ts # 入站 server 实例(CRUD、路由、生命周期)
142
+ outbound-manager.ts # 出站连接实例(CRUD、工具)
143
+ server/ # 单实例内部件:store、card、a2a-server、
144
+ # routes、executors、inbound-registry
145
+ outbound/ # 出站内部件:A2AClient、registry、tools
170
146
  client/ # 浏览器半区:设置面板 (React)
171
147
  service.ts # ctx.a2a 服务 facade
172
148
  commands.ts # /a2a 聊天命令
@@ -174,7 +150,7 @@ tests/
174
150
  unit/ # protocol、framing、card、store、registry、server、
175
151
  # client、api、inbound-registry、identity
176
152
  composition/ # 在真实 Cordis Context 上以 stub 宿主服务跑 apply()
177
- cordis.patch.yml # bundle patch(挂载插件,默认启用)
153
+ cordis.patch.yml # bundle patch(挂载插件;实例由 GUI 管理)
178
154
  ```
179
155
 
180
156
  ## 开发
@@ -189,7 +165,7 @@ pnpm build # tsc + tsdown → lib/index.js(host)+ lib/client.js(浏
189
165
 
190
166
  ## 致谢
191
167
 
192
- 本项目受 [ryubyte/dsh-a2a](https://github.com/ryubyte/dsh-a2a) 启发并与其并行开发——那是 DeepSeek Harness 上更早的 A2A 插件。其设计——双端范围、从工具注册表派生 AgentCard、设置面板模式——为本文实现指明了方向。我们的协议层、任务存储与执行器接缝均为独立实现;GUI 管理模型则直接承袭了 ryubyte 的连接面板。
168
+ 本项目受 [ryubyte/dsh-a2a](https://github.com/ryubyte/dsh-a2a) 启发并与其并行开发——那是 DeepSeek Harness 上更早的 A2A 插件。其设计——双端范围、AgentCard 宣告、设置面板模式——为本文实现指明了方向。我们的协议层、任务存储与执行器接缝均为独立实现;GUI 管理模型则直接承袭了 ryubyte 的连接面板。
193
169
 
194
170
  ## 许可证
195
171
 
package/cordis.patch.yml CHANGED
@@ -1,11 +1,8 @@
1
1
  # @hanphone/dsh-a2a bundle patch: mount the plugin as one row, enabled by
2
- # default (install-and-use; turn either half off through the GUI dashboard or
3
- # an overlay patch on this row).
2
+ # default. v1.0 instances are NOT patch-configured inbound/outbound server
3
+ # instances are created, preset-bound, and enabled/disabled entirely from the
4
+ # GUI (persisted in the a2a domain); this row only mounts the plugin core.
4
5
  - insert:
5
6
  - id: a2a
6
7
  name: '@hanphone/dsh-a2a'
7
- config:
8
- server:
9
- enabled: true
10
- client:
11
- agents: []
8
+ config: {}