dsh-a2a-server 0.1.1 → 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 (86) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/CHANGELOG.zh-CN.md +28 -0
  3. package/README.md +151 -54
  4. package/README.zh-CN.md +113 -53
  5. package/cordis.patch.yml +10 -6
  6. package/lib/briefing.d.ts +39 -0
  7. package/lib/briefing.d.ts.map +1 -0
  8. package/lib/briefing.js +108 -0
  9. package/lib/briefing.js.map +1 -0
  10. package/lib/config.d.ts +69 -22
  11. package/lib/config.d.ts.map +1 -1
  12. package/lib/config.js +76 -6
  13. package/lib/config.js.map +1 -1
  14. package/lib/contexts.d.ts +9 -4
  15. package/lib/contexts.d.ts.map +1 -1
  16. package/lib/contexts.js +7 -13
  17. package/lib/contexts.js.map +1 -1
  18. package/lib/index.d.ts +1 -26
  19. package/lib/index.d.ts.map +1 -1
  20. package/lib/index.js +420 -408
  21. package/lib/index.js.map +1 -1
  22. package/lib/management.d.ts +5 -0
  23. package/lib/management.d.ts.map +1 -0
  24. package/lib/management.js +74 -0
  25. package/lib/management.js.map +1 -0
  26. package/lib/projection.d.ts +67 -22
  27. package/lib/projection.d.ts.map +1 -1
  28. package/lib/projection.js +30 -11
  29. package/lib/projection.js.map +1 -1
  30. package/lib/protocol/card.d.ts +25 -7
  31. package/lib/protocol/card.d.ts.map +1 -1
  32. package/lib/protocol/card.js +35 -14
  33. package/lib/protocol/card.js.map +1 -1
  34. package/lib/protocol/index.d.ts +10 -10
  35. package/lib/protocol/index.d.ts.map +1 -1
  36. package/lib/protocol/index.js +8 -8
  37. package/lib/protocol/index.js.map +1 -1
  38. package/lib/protocol/jsonrpc.d.ts +54 -6
  39. package/lib/protocol/jsonrpc.d.ts.map +1 -1
  40. package/lib/protocol/jsonrpc.js +70 -8
  41. package/lib/protocol/jsonrpc.js.map +1 -1
  42. package/lib/protocol/parse.d.ts +79 -0
  43. package/lib/protocol/parse.d.ts.map +1 -0
  44. package/lib/protocol/parse.js +249 -0
  45. package/lib/protocol/parse.js.map +1 -0
  46. package/lib/protocol/sse.d.ts +6 -3
  47. package/lib/protocol/sse.d.ts.map +1 -1
  48. package/lib/protocol/sse.js +4 -2
  49. package/lib/protocol/sse.js.map +1 -1
  50. package/lib/protocol/wire.d.ts +165 -54
  51. package/lib/protocol/wire.d.ts.map +1 -1
  52. package/lib/protocol/wire.js +54 -9
  53. package/lib/protocol/wire.js.map +1 -1
  54. package/lib/router.d.ts +55 -1
  55. package/lib/router.d.ts.map +1 -1
  56. package/lib/router.js +419 -137
  57. package/lib/router.js.map +1 -1
  58. package/lib/security.d.ts +37 -3
  59. package/lib/security.d.ts.map +1 -1
  60. package/lib/security.js +48 -4
  61. package/lib/security.js.map +1 -1
  62. package/lib/state.d.ts +37 -0
  63. package/lib/state.d.ts.map +1 -0
  64. package/lib/state.js +146 -0
  65. package/lib/state.js.map +1 -0
  66. package/lib/tasks.d.ts +4 -0
  67. package/lib/tasks.d.ts.map +1 -1
  68. package/lib/tasks.js +23 -5
  69. package/lib/tasks.js.map +1 -1
  70. package/lib/types.d.ts +12 -3
  71. package/lib/types.d.ts.map +1 -1
  72. package/lib/types.js +1 -1
  73. package/lib/types.js.map +1 -1
  74. package/lib/version.d.ts +20 -0
  75. package/lib/version.d.ts.map +1 -0
  76. package/lib/version.js +20 -0
  77. package/lib/version.js.map +1 -0
  78. package/lib/workspace-read.d.ts +9 -0
  79. package/lib/workspace-read.d.ts.map +1 -0
  80. package/lib/workspace-read.js +152 -0
  81. package/lib/workspace-read.js.map +1 -0
  82. package/package.json +39 -30
  83. package/lib/protocol/normalize.d.ts +0 -72
  84. package/lib/protocol/normalize.d.ts.map +0 -1
  85. package/lib/protocol/normalize.js +0 -278
  86. package/lib/protocol/normalize.js.map +0 -1
package/README.zh-CN.md CHANGED
@@ -15,14 +15,14 @@
15
15
  <p align="center">
16
16
  <a href="https://www.npmjs.com/package/dsh-a2a-server"><img src="https://img.shields.io/npm/v/dsh-a2a-server?style=flat&label=npm&color=CB3837" alt="npm 版本"></a>
17
17
  <a href="https://github.com/huangjuhua-aigc/dsh-a2a/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-2EA44F?style=flat" alt="MIT License"></a>
18
- <img src="https://img.shields.io/badge/A2A-v0.3.0%20JSON--RPC-4D6BFE?style=flat" alt="A2A v0.3.0 JSON-RPC 绑定">
19
- <img src="https://img.shields.io/badge/DSH-0.1.0--rc.6-4493F8?style=flat" alt="基于 DSH 0.1.0-rc.6 构建">
20
- <img src="https://img.shields.io/badge/tests-129-2EA44F?style=flat" alt="129 项测试">
18
+ <img src="https://img.shields.io/badge/A2A-v1.0%20JSON--RPC-4D6BFE?style=flat" alt="A2A v1.0 JSON-RPC 绑定">
19
+ <img src="https://img.shields.io/badge/DSH-0.2.1--rc.2-4493F8?style=flat" alt="基于 DSH 0.2.1-rc.2 构建">
20
+ <img src="https://img.shields.io/badge/tests-217-2EA44F?style=flat" alt="217 项测试">
21
21
  </p>
22
22
 
23
23
  `dsh-a2a-server` 让 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)
24
24
  上的 agent 可以通过 [A2A(Agent2Agent)](https://a2a-protocol.org)协议被访问。它在
25
- well-known 路径发布 Agent Card,并实现 **v0.3.0 JSON-RPC 绑定**——任何知道本部署
25
+ well-known 路径发布 Agent Card,并实现 **v1.0 JSON-RPC 绑定**——任何知道本部署
26
26
  URL 的合规 peer 都能发现这个 agent 并向它提交任务。
27
27
 
28
28
  本插件**只做入站**:从不主动连接其他 agent,没有 client、没有 peer 目录、没有 A2A
@@ -30,12 +30,16 @@ subagent provider。它是 `ctx.agents` 之上的传输适配层,不是能力
30
30
 
31
31
  ## 安装
32
32
 
33
+ 需要本机配置界面或跨网络访问时,先看配套 [Talk2MyAgent 使用说明](https://github.com/huangjuhua-aigc/Talk2MyAgent)。
34
+ 访客读取权限、自动简报、主人 IPC 与 Tailscale 部署详见[部署与迁移指南](https://github.com/huangjuhua-aigc/dsh-a2a/blob/main/docs/public-access-hardening.md)。
35
+
33
36
  ```sh
34
- dsh plugin --profile web add dsh-a2a-server
37
+ dsh plugin --profile web add dsh-a2a-server@0.3.0
35
38
  ```
36
39
 
37
40
  `dsh plugin` 会在 profile 目录里转发给 pnpm,并把这个 bundle 追加进
38
- `dsh.profile.bundles`——因为包中声明了 `dsh.bundle`。
41
+ `dsh.profile.bundles`——因为包中声明了 `dsh.bundle`。自 DSH v0.2.1-alpha.1 起,
42
+ `--profile` 必须放在所有 pnpm 参数之前——上面的写法已经是正确的。
39
43
 
40
44
  若想直接使用本地检出,把路径指过去即可:
41
45
 
@@ -48,12 +52,25 @@ dsh plugin --profile web add ./path/to/dsh-a2a-server
48
52
  | `ctx.agents` | `dsh-base` |
49
53
  | `ctx.credentials` | `dsh-base` |
50
54
  | `ctx.webServer` | **`dsh-web-app`** |
51
- | `ctx.sessionProjections`(可选) | 组合层;启用后 `tasks/get` 在任务结算后仍可应答 |
55
+ | `ctx.tools` 和 `ctx.systemPrompt` | 必需的访客工具及提示词隔离服务 |
56
+ | `ctx.sessionProjections`(可选) | 组合层;提供任务生命周期投影 |
52
57
 
53
- 三个必需服务齐备之前,插件保持 PENDING。`ctx.webServer` 由 `dsh-web-app` 提供而不在
58
+ 必需服务齐备之前,插件保持 PENDING。`ctx.webServer` 由 `dsh-web-app` 提供而不在
54
59
  `dsh-base` 中,因此 `headless` profile 需要先挂载 `@deepseek-ai/dsh-host-webserver`。
55
60
  `dsh --profile <name> --dump-config` 可以打印组合出来的配置行。
56
61
 
62
+ ### 从 0.2.x 升级
63
+
64
+ 0.3.0 适配 DSH `>=0.2.1-rc.2 <0.3.0`。先停止 agent,将
65
+ `dsh-a2a-server@0.3.0` 安装到原有 profile,再重新启动;配套 Portal 也需更新。
66
+ 主人 RPC 和监控已改为需要认证的本机 IPC,公网 HTTP 不接受主人凭据。
67
+ 访客可聊天并读取授权目录,其他工具关闭;空白名单 `trustedPeers: []` 拒绝所有访客。
68
+ 任务结果与空闲会话在保留期内可跨驱逐和重启恢复。
69
+
70
+ 新版 [Talk2MyAgent Portal](https://github.com/huangjuhua-aigc/Talk2MyAgent) 默认开启每小时简报更新,
71
+ 用一个开关控制,完成后直接写入 `.hop/handoff.md`。单独安装协议插件而未配置 `briefing`
72
+ 不会自动执行简报任务。详见 [0.3.0 发布说明](https://github.com/huangjuhua-aigc/dsh-a2a/blob/main/CHANGELOG.zh-CN.md)。
73
+
57
74
  ## 快速开始
58
75
 
59
76
  仓库自带的示例组合会运行真实模型并启动一个监听服务。
@@ -88,9 +105,10 @@ curl -s http://127.0.0.1:9922/.well-known/agent-card.json
88
105
  curl -s http://127.0.0.1:9922/a2a \
89
106
  -H "authorization: Bearer demo123" \
90
107
  -H 'content-type: application/json' \
91
- -d '{"jsonrpc":"2.0","id":1,"method":"message/send","params":{
92
- "message":{"kind":"message","messageId":"m1","role":"user",
93
- "parts":[{"kind":"text","text":"hello"}]}}}'
108
+ -H 'a2a-version: 1.0' \
109
+ -d '{"jsonrpc":"2.0","id":1,"method":"SendMessage","params":{
110
+ "message":{"messageId":"m1","role":"ROLE_USER",
111
+ "parts":[{"text":"hello"}]}}}'
94
112
  ```
95
113
 
96
114
  | 变量 | 默认值 | 含义 |
@@ -98,10 +116,10 @@ curl -s http://127.0.0.1:9922/a2a \
98
116
  | `A2A_PEERS` | `alice` | `名字[:token]` 列表;每个 peer 都必须有 token |
99
117
  | `A2A_PORT` | `9900` | 监听端口 |
100
118
  | `A2A_SEND_MODE` | `block` | `block` 或 `immediate` |
101
- | `A2A_WORKSPACE_ROOT` | 临时目录 | per-peer 工作目录的父目录 |
119
+ | `A2A_WORKSPACE_ROOT` | 临时目录 | 访客授权资料目录,同时作为 per-peer 工作目录的父目录 |
102
120
  | `DEEPSEEK_MODEL` | `deepseek-v4-flash` | 向适配器请求的模型 id |
103
121
 
104
- 一次性探针会对运行中的服务执行 48 项检查,任何一项不符即以非零码退出:
122
+ 一次性探针会对运行中的服务执行 67 项检查,任何一项不符即以非零码退出:
105
123
 
106
124
  ```sh
107
125
  pnpm probe # 默认 :9922 / demo123
@@ -115,21 +133,42 @@ node example/probe.mjs http://127.0.0.1:9922 demo123
115
133
  | `/.well-known/agent-card.json` | GET | 默认公开 |
116
134
  | `/.well-known/agent.json` | GET | 默认公开 |
117
135
  | `{basePath}`(默认 `/a2a`) | POST | 必须携带 Bearer |
136
+ | `{basePath}/admin/contexts[/<id>]` | GET | 仅本机 IPC + admin token,公网 HTTP 不挂载 |
118
137
 
119
- | JSON-RPC 方法 | v1.0 别名 | 状态 |
138
+ | JSON-RPC 方法 | 状态 |
139
+ | --- | --- |
140
+ | `SendMessage` | 是否等待逐请求协商 |
141
+ | `SendStreamingMessage` | SSE |
142
+ | `GetTask` | 幂等;结算后仍可应答 |
143
+ | `ListTasks` | 游标分页、可过滤;包括保留期内的持久化任务 |
144
+ | `CancelTask` | 取消正在执行的 turn |
145
+ | `SubscribeToTask` | 续订活任务;终态任务返回 `-32004` |
146
+ | `GetExtendedAgentCard` | 已认证 peer 可见的额外 skill;未配置时 `-32007` |
147
+ | `*TaskPushNotificationConfig` | `-32003` |
148
+
149
+ **只提供 A2A v1.0。** v0.3 的方法名、`kind` 判别字段、小写枚举、嵌套 `file`
150
+ part 是被**删除**而不是做了别名:本服务写出的每一个回包都是 v1.0 JSON,v0.3
151
+ 客户端本就读不懂,所以“应答一个 v0.3 请求”只会让故障离成因更远。已退役的方法回
152
+ `-32601` 并直接告知新名字;显式的 `A2A-Version: 0.3` 回 `-32009`
153
+ (`VersionNotSupportedError`)。不带 `A2A-Version` 头的请求按 1.0 处理——这是本接口
154
+ 在 Card 上声明的唯一版本。
155
+
156
+ 具体到线上,与 v0.3 的差异:
157
+
158
+ | 关注点 | v0.3 | 本服务的 v1.0 |
120
159
  | --- | --- | --- |
121
- | `message/send` | `SendMessage` | 是否阻塞逐请求协商 |
122
- | `message/stream` | `SendStreamingMessage` | SSE |
123
- | `tasks/get` | `GetTask` | 幂等;结算后仍可应答 |
124
- | `tasks/cancel` | `CancelTask` | 取消正在执行的 turn |
125
- | `tasks/resubscribe` | `SubscribeToTask` | 续订活任务,或给一帧终态 |
126
- | `tasks/pushNotificationConfig/*` | `*TaskPushNotificationConfig` | `-32003` |
127
- | `tasks/list` | `ListTasks` | `-32601` |
128
- | `agent/getAuthenticatedExtendedCard` | `GetExtendedAgentCard` | `-32601` |
129
-
130
- 两种方言都接受。v0.3(`message/send`、`"working"`、带 `kind` 的 part)是主线;v1.0
131
- 拼写(`SendMessage`、`TASK_STATE_WORKING`、成员存在式 part)在入站时被归一化,并按请求
132
- 所用的方言渲染回去。
160
+ | 任务引用 | `params.taskId` | `params.id` |
161
+ | `SendMessage` 结果 | 裸 `Task` | `{ "task": … }` |
162
+ | 任务状态 | `"working"` | `"TASK_STATE_WORKING"` |
163
+ | 消息角色 | `"user"` | `"ROLE_USER"` |
164
+ | Part | `{"kind":"text","text":…}` | `{"text":…}`;文件 part 展平为 `url`/`raw`/`filename`/`mediaType` |
165
+ | 流帧 | `{"kind":"status-update",…,"final":true}` | `{"statusUpdate":{…}}`;流关闭即终态信号 |
166
+ | 流首帧 | 一帧非终态 status update | `Task` 对象本身 |
167
+ | 等待控制 | `configuration.blocking` | `configuration.returnImmediately`(语义反转,默认等待) |
168
+ | Card 端点 | `url` + `preferredTransport` | `supportedInterfaces[]`,每条自带 `protocolVersion` |
169
+ | Card 鉴权 | `securitySchemes` + `security` | scheme 包在 `httpAuthSecurityScheme` 里;改用 `securityRequirements` |
170
+ | 扩展 Card | `supportsAuthenticatedExtendedCard` | `capabilities.extendedAgentCard` |
171
+ | A2A 错误细节 | 无 | `error.data[]` 携带 `google.rpc.ErrorInfo`,含 `reason` 与 `domain` |
133
172
 
134
173
  入站消息可携带 text、file、data 三类 part。file 与 data 会以方括号引用的形式进入模型
135
174
  上下文。回复为纯文本。
@@ -145,18 +184,17 @@ node example/probe.mjs http://127.0.0.1:9922 demo123
145
184
  config:
146
185
  basePath: /a2a
147
186
  publicUrl: https://agents.example.com/a2a # 写入 Card 的对外地址
148
- protocolVersion: 0.3.0
149
187
  provider: deepseek-official
150
188
  model: deepseek-v4-flash
151
189
 
152
190
  card:
153
191
  name: dsh-harness
154
- description: 读代码、执行命令、给出结论。
192
+ description: 读取授权目录中的代码和资料,回答访客问题。
155
193
  public: true
156
194
  skills:
157
195
  - id: general
158
196
  name: general
159
- description: 通用任务执行。
197
+ description: 对话与授权目录内的文件读取。
160
198
  tags: [coding, research]
161
199
  provider:
162
200
  organization: Example Inc.
@@ -188,21 +226,26 @@ node example/probe.mjs http://127.0.0.1:9922 demo123
188
226
  | --- | --- | --- |
189
227
  | `basePath` | `/a2a` | JSON-RPC 路由 |
190
228
  | `publicUrl` | 由 `Host` 推导 | 写入 Card 的可路由地址 |
191
- | `protocolVersion` | `0.3.0` | Card 上声明的协议版本 |
192
229
  | `provider` · `model` | — | 本服务创建的每个 agent 使用的模型路由 |
193
230
  | `card.public` | `true` | 无需凭据即可获取 Card |
194
231
  | `card.skills` | `[]` | 声明的 skills;为空时回退到一条 `general` |
232
+ | `card.extendedSkills` | `[]` | 只对已认证 peer 经 `GetExtendedAgentCard` 公开的 skills |
195
233
  | `peers` | `{}` | 身份 → 凭据**引用名** |
196
- | `trustedPeers` | 全部已认证身份 | 允许执行任务的身份白名单 |
234
+ | `trustedPeers` | 全部已认证身份 | 对话与受限文件读取白名单;`[]` 拒绝所有访客 |
197
235
  | `rateLimitPerMinute` | `60` | 按身份的滑动窗口 |
198
236
  | `maxContextTurns` | `5` | 单个 context 接受的消息数上限,超出后 `rejected` |
199
237
  | `sendMode` | `block` | 客户端未表态时的默认行为 |
200
- | `blockTimeoutMs` | `60000` | 超过后拒绝继续阻塞 |
238
+ | `blockTimeoutMs` | `60000` | 最长阻塞等待;到期返回任务当前状态,客户端可继续查询 |
201
239
  | `contextIdleTtlMs` | `1800000` | context 的 agent 被释放前的空闲时长 |
202
240
  | `maxResidentContexts` | `64` | 常驻 context 数量上限 |
203
241
  | `isolation.workspaceMode` | `per-peer` | `per-peer` 或 `shared` |
204
- | `isolation.workspaceRoot` | — | 必填;父目录或共享 cwd |
205
- | `isolation.peerWorkspaces` | `{}` | 按身份覆盖工作目录 |
242
+ | `isolation.workspaceRoot` | — | 必填;授权资料目录,同时作为访客 cwd 的父目录或共享 cwd |
243
+ | `isolation.peerWorkspaces` | `{}` | 按身份覆盖工作目录和授权读取范围 |
244
+ | `preamble.files` | `[".hop/handoff.md"]` | 主人背景简报文件(**agent 级**:相对工作区根目录解析,全体访客共用一份;绝对路径直通),合并进每个 context 的首条访客消息;文件不存在则跳过 |
245
+ | `briefing.prompt` | — | 可选的单次简报更新指令,由 Portal 配置,通过有执行预算的主人 agent 每小时运行 |
246
+ | `briefing.settingsFile` | — | 绝对路径;读取 `{ "enabled": boolean }` 设置,缺省开启,每 5 秒检查 |
247
+ | `admin.tokenEnv` | — | 本机主人 RPC 和监控的凭据引用,公网 RPC 拒绝此凭据 |
248
+ | `admin.socketPath` | 按部署生成 | Windows 命名管道或私有 Unix socket |
206
249
  | `push.enabled` | `false` | 保留字段,见[边界](#边界) |
207
250
 
208
251
  以下情况在加载期即被拒绝:缺少 `isolation.workspaceRoot`;peer 名不匹配
@@ -220,7 +263,8 @@ peers:
220
263
 
221
264
  ```yaml
222
265
  # ~/.dsh/.credentials.yaml
223
- A2A_PEER_ALICE: <32-byte-hex-from-openssl-rand>
266
+ refs:
267
+ A2A_PEER_ALICE: <32-byte-hex-from-openssl-rand>
224
268
  ```
225
269
 
226
270
  `ctx.credentials` 在每次请求时跨四层解析该引用——进程环境、托管文档、`<cwd>/.env`、
@@ -234,11 +278,18 @@ A2A_PEER_ALICE: <32-byte-hex-from-openssl-rand>
234
278
  | --- | --- | --- |
235
279
  | 模型上下文 | 一个 peer 的对话不会进入另一个 peer 的模型请求 | 不同 `contextId` → 不同 Session → 不同 log |
236
280
  | 协议访问 | peer 无法读取、续接或取消他人的 context 与任务 | 按认证身份判定归属 |
237
- | 工具层 | peer 的 agent 无法借工具读取他人的 session | `workspaceMode: per-peer` |
281
+ | 工具层 | 仅提供受控 `read_file`,不开放写入、命令、联网或跨会话工具 | 屏蔽继承工具、专用读取工具、执行校验 |
282
+ | 提示词 | 访客不继承可选的主人个性化提示与运行时上下文 | 独立的公共系统提示词 |
283
+
284
+ `per-peer`(默认)依据 `workspaceRoot` 为每个身份派生独立 `cwd`;`shared` 使用同一个目录。
285
+ 这只是工作目录策略,不是文件系统沙箱。`read_file` 的读取范围默认是用户选择的
286
+ `workspaceRoot`,有 `peerWorkspaces` 覆盖时限制为该访客的专属目录。工具相对路径从
287
+ 这个资料目录解析;插件私有状态目录始终排除。
238
288
 
239
- `per-peer`(默认)依据 `workspaceRoot` 为每个身份派生独立 `cwd`。`shared` 则把所有 peer
240
- 放进同一个目录,适用于协作维护同一个仓库的场景;此模式下一个 peer 写入的文件对其他 peer
241
- 可读。
289
+ `read_file({"path":"."})` 列出资料目录;文件支持 UTF-8 文本,按 `offset`/`limit`
290
+ 分页。单文件上限 2 MiB,单次最多 32000 字符、500 行/项,目录扫描最多 1000 项。
291
+ 拒绝越界、符号链接/junction、硬链接和 Windows ADS。主人维护能力使用独立本机 IPC,
292
+ 保留可信管理工具,不能从公网入口调用。资源配额的完整配置表见英文 README。
242
293
 
243
294
  ## 边界
244
295
 
@@ -246,14 +297,16 @@ A2A_PEER_ALICE: <32-byte-hex-from-openssl-rand>
246
297
  - 只提供 JSONRPC 绑定。不提供 gRPC 与 HTTP+JSON,Card 上如实声明。
247
298
  - 未实现推送通知。`push.enabled` 仅决定推送方法返回哪个错误码,Card 上声明
248
299
  `pushNotifications: false`。
249
- - 不支持扩展 Agent Card、`stateTransitionHistory`、协议扩展与 Card 签名。
300
+ - 不支持 `stateTransitionHistory`、协议扩展与 Card 签名。
250
301
  - 流式只推送已提交的 assistant 消息,未实现逐 chunk 推送。
251
- - 无孤儿任务看门狗:卡在非终态的任务会一直保持该状态。
252
- - 未实现跨会话工具拒绝;隔离依赖 `workspaceMode`。
302
+ - 有并发准入、任务期限及模型调用预算;超时后等待底层适配器完成取消才释放执行名额。
303
+ 不响应取消的适配器仍需进程级监管,这些额度不是按货币计费的预算。
304
+ - 访客仅能使用有目录边界的 `read_file`;改变沙箱模式不会自动开放其他工具。
305
+ 该工具边界不替代针对恶意本机管理员的操作系统隔离。
253
306
  - 触及 token 上限的任务结算为 `completed`,真实的 turn 结束原因放在
254
307
  `Task.metadata.dsh.stopReason`——A2A 的状态枚举无法表达它。
255
- - 任务状态可以跨结算存活,但无法跨进程重启:未组合 session 持久化,重启后 projection
256
- 没有日志可供冷折叠。
308
+ - 任务结果与空闲会话在保留期内可跨进程重启恢复;中断的任务标记失败,不重放。
309
+ - 每个 context 同时只允许一个任务,并发提交返回 409;messageId 不是提交去重键。
257
310
  - `ctx.webServer` 不提供 TLS。任何非回环地址的暴露都应置于反向代理之后。
258
311
  - 修改配置会重启插件并取消进行中的任务。
259
312
 
@@ -262,8 +315,8 @@ A2A_PEER_ALICE: <32-byte-hex-from-openssl-rand>
262
315
  ```
263
316
  src/
264
317
  ├── protocol/ 零依赖库:不碰 Cordis、不碰 HTTP、不碰 harness
265
- │ ├── wire.ts A2A 词汇表,统一归一到 v0.3 拼写
266
- │ ├── normalize.ts v0.3 <-> v1.0 方言双向翻译
318
+ │ ├── wire.ts A2A v1.0 词汇表:只有一种形状,不需要翻译
319
+ │ ├── parse.ts 入站解析与方法表
267
320
  │ ├── jsonrpc.ts 信封框架与 A2A 错误码
268
321
  │ ├── card.ts Agent Card 构造
269
322
  │ └── sse.ts SSE 帧编码
@@ -273,6 +326,10 @@ src/
273
326
  ├── tasks.ts 任务槽位与三段式 turn 关联
274
327
  ├── projection.ts 在 session log 上折叠出的 a2aTask 读模型
275
328
  ├── security.ts 认证、限流、注入去势、外发脱敏
329
+ ├── workspace-read.ts 授权目录内受限的 read_file
330
+ ├── management.ts 本机 IPC 主人 RPC 与监控
331
+ ├── state.ts 持久化任务及对话 checkpoint
332
+ ├── briefing.ts 每小时简报更新与持久化调度
276
333
  ├── config.ts schema,以及加载期就会拒绝的跨字段校验
277
334
  └── types.ts 向 SessionEventMap / MessageSourceMap 的声明合并
278
335
  ```
@@ -286,17 +343,18 @@ src/
286
343
  历史。
287
344
 
288
345
  **驻留是显式管理的。** HTTP 没有连接生命周期,因此每个 `contextId` 对应一个 Activation,
289
- 空闲后被驱逐,持久化的 Session 留在原地。
346
+ 空闲后被驱逐。私有 checkpoint 保留任务结果与对话历史,冷恢复时创建新的 harness session。
290
347
 
291
348
  ## 开发
292
349
 
293
350
  ```sh
294
351
  pnpm install
295
352
  pnpm typecheck
296
- pnpm test # 9 个文件共 129 项测试
353
+ pnpm test # 15 个文件共 217 项测试
297
354
  pnpm serve # 启动监听服务
298
- pnpm probe # 对运行中的服务执行 48 项检查
355
+ pnpm probe # 对运行中的服务执行 67 项检查
299
356
  pnpm build # 产出 lib/
357
+ pnpm test:package # 仅声明运行依赖时验证构建产物导入
300
358
  ```
301
359
 
302
360
  端到端测试会启动真实的 Cordis 组合与真实的 agent loop,并通过 HTTP 驱动它;模型使用
@@ -308,13 +366,15 @@ pnpm build # 产出 lib/
308
366
 
309
367
  官方项目提供 agent 运行时、插件系统,以及本插件所消费的各个能力接缝。本项目提供:
310
368
 
311
- - 入站的 A2A v0.3.0 JSON-RPC 绑定
312
- - Agent Card 构造与方言归一化
369
+ - 入站的 A2A v1.0 JSON-RPC 绑定
370
+ - Agent Card 构造与入站请求解析
313
371
  - A2A task 与 harness turn 之间的映射
314
372
  - 按 peer 的认证、隔离与工作目录策略
315
373
 
316
- harness 处于 pre-release 阶段,不承诺跨重命名或重新打包的兼容性,因此 peer 依赖精确锁定
317
- 在 `0.1.0-rc.6`。
374
+ harness 处于 pre-release 阶段,不承诺跨重命名或重新打包的兼容性,因此兼容性通过
375
+ `engines.dsh`(`>=0.2.1-rc.2 <0.3.0`)加上按 range 声明的 `@deepseek-ai/dsh-*`
376
+ peer 依赖来表达:安装预检会拒绝任何插件未曾适配过的组合,而这个 range 也放行后续
377
+ 所有的 0.2.x 补丁版本。
318
378
 
319
379
  ## 社区交流
320
380
 
package/cordis.patch.yml CHANGED
@@ -7,12 +7,15 @@
7
7
  # REQUIRES ctx.webServer, which ships in `dsh-web-app`, NOT in `dsh-base`. On a
8
8
  # profile without it this row loads but stays PENDING (Cordis holds a plugin
9
9
  # whose injected services are absent), so nothing serves and nothing errors.
10
+ #
11
+ # NOTE: keep this file plain YAML — the installer's package validation parses
12
+ # it with a strict loader that rejects `!!js` expressions, even though the
13
+ # runtime overlay loader accepts them.
10
14
  - insert:
11
15
  - id: a2a-server
12
16
  name: dsh-a2a-server
13
17
  config:
14
18
  basePath: /a2a
15
- protocolVersion: '0.3.0'
16
19
  card:
17
20
  name: dsh-harness
18
21
  description: A DeepSeek Harness agent reachable over A2A.
@@ -20,11 +23,12 @@
20
23
  skills: []
21
24
  peers: {}
22
25
  isolation:
23
- # Default posture is isolation: each peer gets its own working
24
- # directory, which also makes the cwd-equality authority in
25
- # cross-session tooling separate peers for free. A deployment whose
26
- # peers collaborate on one repository must say so explicitly.
26
+ # Each peer gets its own working directory. This is not a filesystem
27
+ # sandbox: read_file separately restricts reads to the selected root,
28
+ # or to a peerWorkspaces override. Other visitor tools are disabled.
29
+ # This placeholder is overridden by any real deployment layer (the
30
+ # console's overlay sets it per agent); supply an absolute path.
27
31
  workspaceMode: per-peer
28
- workspaceRoot: !!js `${process.env.DSH_HOME ?? process.env.HOME}/a2a-workspaces`
32
+ workspaceRoot: /srv/dsh/a2a
29
33
  push:
30
34
  enabled: false
@@ -0,0 +1,39 @@
1
+ export declare const BRIEFING_INTERVAL_MS: number;
2
+ interface LegacyBriefingScheduler {
3
+ catalog(): Promise<Array<{
4
+ id: string;
5
+ sessionId: string;
6
+ title: string;
7
+ prompt: string;
8
+ }>>;
9
+ delete(request: {
10
+ id: string;
11
+ sessionId: string;
12
+ }): Promise<unknown>;
13
+ }
14
+ /** Retire only the old Portal-generated job for this exact project. */
15
+ export declare function retireLegacyBriefing(scheduler: LegacyBriefingScheduler | undefined, root: string): Promise<void>;
16
+ export declare class AutomaticBriefing {
17
+ private readonly options;
18
+ private timer;
19
+ private closed;
20
+ private running;
21
+ private enabled;
22
+ private nextRunAt;
23
+ private readonly stateFile;
24
+ constructor(options: {
25
+ settingsFile: string;
26
+ stateDirectory: string;
27
+ run: () => Promise<void>;
28
+ warn: (message: string) => void;
29
+ now?: () => number;
30
+ });
31
+ private save;
32
+ private readEnabled;
33
+ /** Polling also observes an off/on switch without restarting the agent. */
34
+ tick(): Promise<void>;
35
+ start(): void;
36
+ stop(): void;
37
+ }
38
+ export {};
39
+ //# sourceMappingURL=briefing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"briefing.d.ts","sourceRoot":"","sources":["../src/briefing.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,oBAAoB,QAAiB,CAAA;AAGlD,UAAU,uBAAuB;IAC/B,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAA;IAC3F,MAAM,CAAC,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CACrE;AAED,uEAAuE;AACvE,wBAAsB,oBAAoB,CAAC,SAAS,EAAE,uBAAuB,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAUtH;AAED,qBAAa,iBAAiB;IAQhB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAPpC,OAAO,CAAC,KAAK,CAA4C;IACzD,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,OAAO,CAAO;IACtB,OAAO,CAAC,SAAS,CAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;gBAEL,OAAO,EAAE;QACpC,YAAY,EAAE,MAAM,CAAA;QACpB,cAAc,EAAE,MAAM,CAAA;QACtB,GAAG,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;QACxB,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;QAC/B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;KACnB;IAWD,OAAO,CAAC,IAAI;IAQZ,OAAO,CAAC,WAAW;IAWnB,2EAA2E;IACrE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB3B,KAAK,IAAI,IAAI;IAOb,IAAI,IAAI,IAAI;CAIb"}
@@ -0,0 +1,108 @@
1
+ /** One automatic briefing loop owned by the online agent, independent of Portal. */
2
+ import { mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ export const BRIEFING_INTERVAL_MS = 60 * 60 * 1000;
5
+ const CHECK_INTERVAL_MS = 5000;
6
+ /** Retire only the old Portal-generated job for this exact project. */
7
+ export async function retireLegacyBriefing(scheduler, root) {
8
+ if (!scheduler?.catalog || !scheduler.delete)
9
+ return;
10
+ const prefix = `【简报维护任务】受信任的项目范围只有一个:\n ${root}\n`;
11
+ for (const task of await scheduler.catalog()) {
12
+ if (task.title === '简报维护' && task.prompt.startsWith(prefix)
13
+ && (task.prompt.includes('4. 把简报写入当前工作目录的 handoff.draft.md')
14
+ || task.prompt.includes('4. 完整生成并检查上述内容后,直接覆盖写入当前工作目录的 handoff.md。'))) {
15
+ await scheduler.delete({ id: task.id, sessionId: task.sessionId });
16
+ }
17
+ }
18
+ }
19
+ export class AutomaticBriefing {
20
+ options;
21
+ timer;
22
+ closed = false;
23
+ running = false;
24
+ enabled = true;
25
+ nextRunAt = 0;
26
+ stateFile;
27
+ constructor(options) {
28
+ this.options = options;
29
+ mkdirSync(options.stateDirectory, { recursive: true, mode: 0o700 });
30
+ this.stateFile = join(options.stateDirectory, 'briefing-state.json');
31
+ try {
32
+ const state = JSON.parse(readFileSync(this.stateFile, 'utf8'));
33
+ if (typeof state.enabled !== 'boolean' || !Number.isSafeInteger(state.nextRunAt) || state.nextRunAt < 0)
34
+ throw new Error('invalid briefing state');
35
+ this.enabled = state.enabled;
36
+ this.nextRunAt = state.nextRunAt;
37
+ }
38
+ catch (error) {
39
+ if (error.code !== 'ENOENT')
40
+ throw error;
41
+ }
42
+ }
43
+ save() {
44
+ const temporary = `${this.stateFile}.tmp`;
45
+ try {
46
+ writeFileSync(temporary, JSON.stringify({ enabled: this.enabled, nextRunAt: this.nextRunAt }), { mode: 0o600 });
47
+ renameSync(temporary, this.stateFile);
48
+ }
49
+ finally {
50
+ rmSync(temporary, { force: true });
51
+ }
52
+ }
53
+ readEnabled() {
54
+ try {
55
+ const value = JSON.parse(readFileSync(this.options.settingsFile, 'utf8'));
56
+ if (typeof value.enabled !== 'boolean')
57
+ throw new Error('invalid automatic briefing setting');
58
+ return value.enabled;
59
+ }
60
+ catch (error) {
61
+ if (error.code === 'ENOENT')
62
+ return true;
63
+ throw error;
64
+ }
65
+ }
66
+ /** Polling also observes an off/on switch without restarting the agent. */
67
+ async tick() {
68
+ if (this.closed)
69
+ return;
70
+ try {
71
+ const enabled = this.readEnabled();
72
+ const now = this.options.now?.() ?? Date.now();
73
+ if (enabled !== this.enabled) {
74
+ this.enabled = enabled;
75
+ if (enabled)
76
+ this.nextRunAt = now;
77
+ this.save();
78
+ }
79
+ if (!enabled || this.running || now < this.nextRunAt)
80
+ return;
81
+ // Persist before paid work: restarting must not immediately repeat a run.
82
+ this.nextRunAt = now + BRIEFING_INTERVAL_MS;
83
+ this.save();
84
+ this.running = true;
85
+ try {
86
+ await this.options.run();
87
+ }
88
+ finally {
89
+ this.running = false;
90
+ }
91
+ }
92
+ catch (error) {
93
+ this.options.warn(`a2a: automatic briefing failed: ${String(error)}`);
94
+ }
95
+ }
96
+ start() {
97
+ if (this.closed || this.timer)
98
+ return;
99
+ this.timer = setInterval(() => { void this.tick(); }, CHECK_INTERVAL_MS);
100
+ this.timer.unref();
101
+ void this.tick();
102
+ }
103
+ stop() {
104
+ this.closed = true;
105
+ clearInterval(this.timer);
106
+ }
107
+ }
108
+ //# sourceMappingURL=briefing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"briefing.js","sourceRoot":"","sources":["../src/briefing.ts"],"names":[],"mappings":"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"}
package/lib/config.d.ts CHANGED
@@ -15,40 +15,39 @@ export interface PeerConfig {
15
15
  /** Credential reference name, NOT the token itself. */
16
16
  tokenEnv: string;
17
17
  }
18
- /**
19
- * How peers are kept from reaching each other.
20
- *
21
- * DEFERRED — a tool deny-list (`session_search`, `session_event_search`,
22
- * `session_trace`, `session_event_trace`, `session_event_read`, `list_agents`)
23
- * plus a named `unsafeAllowCrossSessionSearch` escape hatch for single-peer
24
- * deployments. Both were designed as defense in depth behind `workspaceMode`,
25
- * and neither is declared here until it is enforced: a security knob that
26
- * silently does nothing is worse than no knob at all.
27
- */
18
+ /** Working directories and the data roots exposed through the scoped reader. */
28
19
  export interface IsolationConfig {
29
20
  /**
30
- * `per-peer` gives each identity its own cwd, which also makes the existing
31
- * cwd-equality authority in cross-session tooling isolate them for free.
32
- * `shared` is the collaborative posture and must be chosen deliberately.
21
+ * `per-peer` gives each identity its own cwd. Neither mode is a filesystem
22
+ * security boundary. read_file enforces its own canonical data-root boundary.
33
23
  */
34
24
  workspaceMode: 'per-peer' | 'shared';
35
- /** Parent directory in `per-peer` mode; the common cwd in `shared` mode. */
25
+ /** Shared read-only data root; also the cwd parent in `per-peer` mode. */
36
26
  workspaceRoot: string;
37
- /** Per-peer cwd overrides, e.g. pointing one trusted peer at a real repo. */
27
+ /** Overrides both a peer's cwd and its authorized read root. */
38
28
  peerWorkspaces: Record<string, string>;
39
29
  }
30
+ /** One declared skill, as it appears on the Agent Card. */
31
+ export interface SkillConfig {
32
+ id: string;
33
+ name: string;
34
+ description: string;
35
+ tags: string[];
36
+ }
40
37
  /** Agent Card content and exposure. */
41
38
  export interface CardConfig {
42
39
  name: string;
43
40
  description: string;
44
41
  /** Serve the card without a credential, as discovery expects. */
45
42
  public: boolean;
46
- skills: {
47
- id: string;
48
- name: string;
49
- description: string;
50
- tags: string[];
51
- }[];
43
+ skills: SkillConfig[];
44
+ /**
45
+ * Skills revealed only through `GetExtendedAgentCard`, after a peer has
46
+ * authenticated. Declaring any turns on `capabilities.extendedAgentCard`;
47
+ * leaving this empty means the deployment has no extended card, and the
48
+ * method answers with the spec's own ExtendedAgentCardNotConfiguredError.
49
+ */
50
+ extendedSkills: SkillConfig[];
52
51
  provider?: {
53
52
  organization: string;
54
53
  url: string;
@@ -73,11 +72,32 @@ export interface PushConfig {
73
72
  */
74
73
  enabled: boolean;
75
74
  }
75
+ /**
76
+ * The host's background briefing, injected when a visitor's context opens.
77
+ *
78
+ * The files are read once per Activation (best effort: an absent file is the
79
+ * normal "nothing to hand over" case) and merged into the context's FIRST
80
+ * visitor message rather than sent as their own turn — one model call is
81
+ * spared, and the turn counters stay about the visitor's messages only.
82
+ */
83
+ export interface PreambleConfig {
84
+ /** Publication-safe paths relative to the workspace root; absolute paths pass through. */
85
+ files: string[];
86
+ }
87
+ /** Local-only monitoring surface credentials. */
88
+ export interface AdminConfig {
89
+ /**
90
+ * Credential REFERENCE name holding the admin token, NOT the token itself.
91
+ * Empty or absent means the monitoring surface stays unmounted.
92
+ */
93
+ tokenEnv: string;
94
+ /** Local named pipe / Unix socket. Never served on the public HTTP carrier. */
95
+ socketPath?: string;
96
+ }
76
97
  /** The whole plugin configuration. */
77
98
  export interface A2AServerConfig {
78
99
  basePath: string;
79
100
  publicUrl?: string;
80
- protocolVersion: string;
81
101
  /**
82
102
  * Provider route for every agent this server creates. Optional so another
83
103
  * `agent/request` listener may supply the target instead; a runnable
@@ -95,7 +115,34 @@ export interface A2AServerConfig {
95
115
  blockTimeoutMs: number;
96
116
  contextIdleTtlMs: number;
97
117
  maxResidentContexts: number;
118
+ maxConcurrentTasks: number;
119
+ maxConcurrentTasksPerPeer: number;
120
+ maxTasksPerPeerPerDay: number;
121
+ taskTimeoutMs: number;
122
+ maxModelCallsPerTask: number;
123
+ maxOutputTokens: number;
124
+ maxInputChars: number;
125
+ maxOutputChars: number;
126
+ streamHeartbeatMs: number;
127
+ maxStreamsPerTask: number;
128
+ maxStreamBufferBytes: number;
129
+ storage: {
130
+ directory?: string;
131
+ retentionMs: number;
132
+ maxContexts: number;
133
+ maxContextBytes: number;
134
+ };
98
135
  isolation: IsolationConfig;
136
+ preamble: PreambleConfig;
137
+ /** Optional Portal-managed automatic updates; omission leaves the transport inert. */
138
+ briefing?: {
139
+ settingsFile: string;
140
+ prompt: string;
141
+ };
142
+ /**
143
+ * Enables owner RPC and monitoring on private local IPC, never public HTTP.
144
+ */
145
+ admin?: AdminConfig;
99
146
  push: PushConfig;
100
147
  }
101
148
  export declare const Config: Schema<A2AServerConfig>;