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.
- package/CHANGELOG.md +28 -0
- package/CHANGELOG.zh-CN.md +28 -0
- package/README.md +151 -54
- package/README.zh-CN.md +113 -53
- package/cordis.patch.yml +10 -6
- package/lib/briefing.d.ts +39 -0
- package/lib/briefing.d.ts.map +1 -0
- package/lib/briefing.js +108 -0
- package/lib/briefing.js.map +1 -0
- package/lib/config.d.ts +69 -22
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +76 -6
- package/lib/config.js.map +1 -1
- package/lib/contexts.d.ts +9 -4
- package/lib/contexts.d.ts.map +1 -1
- package/lib/contexts.js +7 -13
- package/lib/contexts.js.map +1 -1
- package/lib/index.d.ts +1 -26
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +420 -408
- package/lib/index.js.map +1 -1
- package/lib/management.d.ts +5 -0
- package/lib/management.d.ts.map +1 -0
- package/lib/management.js +74 -0
- package/lib/management.js.map +1 -0
- package/lib/projection.d.ts +67 -22
- package/lib/projection.d.ts.map +1 -1
- package/lib/projection.js +30 -11
- package/lib/projection.js.map +1 -1
- package/lib/protocol/card.d.ts +25 -7
- package/lib/protocol/card.d.ts.map +1 -1
- package/lib/protocol/card.js +35 -14
- package/lib/protocol/card.js.map +1 -1
- package/lib/protocol/index.d.ts +10 -10
- package/lib/protocol/index.d.ts.map +1 -1
- package/lib/protocol/index.js +8 -8
- package/lib/protocol/index.js.map +1 -1
- package/lib/protocol/jsonrpc.d.ts +54 -6
- package/lib/protocol/jsonrpc.d.ts.map +1 -1
- package/lib/protocol/jsonrpc.js +70 -8
- package/lib/protocol/jsonrpc.js.map +1 -1
- package/lib/protocol/parse.d.ts +79 -0
- package/lib/protocol/parse.d.ts.map +1 -0
- package/lib/protocol/parse.js +249 -0
- package/lib/protocol/parse.js.map +1 -0
- package/lib/protocol/sse.d.ts +6 -3
- package/lib/protocol/sse.d.ts.map +1 -1
- package/lib/protocol/sse.js +4 -2
- package/lib/protocol/sse.js.map +1 -1
- package/lib/protocol/wire.d.ts +165 -54
- package/lib/protocol/wire.d.ts.map +1 -1
- package/lib/protocol/wire.js +54 -9
- package/lib/protocol/wire.js.map +1 -1
- package/lib/router.d.ts +55 -1
- package/lib/router.d.ts.map +1 -1
- package/lib/router.js +419 -137
- package/lib/router.js.map +1 -1
- package/lib/security.d.ts +37 -3
- package/lib/security.d.ts.map +1 -1
- package/lib/security.js +48 -4
- package/lib/security.js.map +1 -1
- package/lib/state.d.ts +37 -0
- package/lib/state.d.ts.map +1 -0
- package/lib/state.js +146 -0
- package/lib/state.js.map +1 -0
- package/lib/tasks.d.ts +4 -0
- package/lib/tasks.d.ts.map +1 -1
- package/lib/tasks.js +23 -5
- package/lib/tasks.js.map +1 -1
- package/lib/types.d.ts +12 -3
- package/lib/types.d.ts.map +1 -1
- package/lib/types.js +1 -1
- package/lib/types.js.map +1 -1
- package/lib/version.d.ts +20 -0
- package/lib/version.d.ts.map +1 -0
- package/lib/version.js +20 -0
- package/lib/version.js.map +1 -0
- package/lib/workspace-read.d.ts +9 -0
- package/lib/workspace-read.d.ts.map +1 -0
- package/lib/workspace-read.js +152 -0
- package/lib/workspace-read.js.map +1 -0
- package/package.json +39 -30
- package/lib/protocol/normalize.d.ts +0 -72
- package/lib/protocol/normalize.d.ts.map +0 -1
- package/lib/protocol/normalize.js +0 -278
- package/lib/protocol/normalize.js.map +0 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
[English](CHANGELOG.md) · [简体中文](CHANGELOG.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
## 0.3.0
|
|
6
|
+
|
|
7
|
+
Requires DeepSeek Harness `>=0.2.1-rc.2 <0.3.0` and speaks A2A 1.0 JSON-RPC.
|
|
8
|
+
|
|
9
|
+
- Restrict visitors to chat and `read_file` within the owner's selected data directory. Block path escapes, links, private plugin state and other tools; separate public prompt context from the owner's runtime context.
|
|
10
|
+
- Move owner RPC and monitoring to authenticated local IPC. Public HTTP accepts visitor credentials only. An empty `trustedPeers` list denies all visitors.
|
|
11
|
+
- Bound concurrent tasks, daily admission, execution time, model calls, outputs and stream buffers. Prevent concurrent tasks in one context from mixing answers; clean up disconnected SSE subscriptions.
|
|
12
|
+
- Persist task results and idle conversation history across eviction and restart within the retention window. Fail interrupted work without replaying it.
|
|
13
|
+
- Add the optional Portal-configured automatic briefing loop: default-on hourly updates, a persistent switch, restart-safe timing, and direct publication to `.hop/handoff.md`.
|
|
14
|
+
- Declare required runtime packages and verify imports without development dependencies. Preserve the guard against malformed Unicode from upstream streaming decoders.
|
|
15
|
+
|
|
16
|
+
Validation: 217 tests in 15 files, TypeScript checking, build and declared-runtime package import check. Models are deterministic test adapters; real-model and cross-network deployment verification remain separate.
|
|
17
|
+
|
|
18
|
+
### Upgrade
|
|
19
|
+
|
|
20
|
+
Stop the agent, update dsh if needed, then install into the existing profile:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
dsh plugin --profile YOUR_PROFILE add dsh-a2a-server@0.3.0
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Replace `YOUR_PROFILE` with the saved profile name and restart the agent. Upgrade the companion [Talk2MyAgent Portal](https://github.com/huangjuhua-aigc/Talk2MyAgent) for the private management endpoint and automatic briefing settings. Do not recreate an existing profile with `--from-default-profile` during an upgrade.
|
|
27
|
+
|
|
28
|
+
Old HTTP owner/monitoring integrations must migrate to local IPC. Review visitor admission and the selected data directory before exposing the service. [Migration details](https://github.com/huangjuhua-aigc/dsh-a2a/blob/main/docs/public-access-hardening.en.md).
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# 更新日志
|
|
2
|
+
|
|
3
|
+
[English](CHANGELOG.md) · [简体中文](CHANGELOG.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
## 0.3.0
|
|
6
|
+
|
|
7
|
+
要求 DeepSeek Harness `>=0.2.1-rc.2 <0.3.0`,使用 A2A 1.0 JSON-RPC。
|
|
8
|
+
|
|
9
|
+
- 访客可以聊天,并通过 `read_file` 读取主人指定的资料目录。拦截路径越界、链接、插件私有状态及其他工具;公共模型提示与主人运行时上下文分离。
|
|
10
|
+
- 主人 RPC 和监控迁到需要认证的本机 IPC;公网 HTTP 仅接受访客凭据。空白名单 `trustedPeers: []` 拒绝全部访客。
|
|
11
|
+
- 限制并发、每日任务数、执行时限、模型调用、输出和流缓冲;同一 context 的并发请求不会串答,SSE 断开后释放订阅。
|
|
12
|
+
- 在保留期内持久化任务结果与空闲对话,支持驱逐和重启后的恢复。重启中断的任务标记失败,不自动重放。
|
|
13
|
+
- 增加由 Portal 配置的自动简报循环:默认每小时更新、一个持久化开关、重启去重,完成后直接写入 `.hop/handoff.md`。
|
|
14
|
+
- 补齐运行依赖声明,验证脱离开发依赖后可导入。保留上游流式解码异常的 Unicode 防护。
|
|
15
|
+
|
|
16
|
+
验证:15 个文件共 217 项测试、类型检查、构建和仅使用声明运行依赖的包导入检查。测试使用确定性模型;真实模型与跨网络部署需要单独验收。
|
|
17
|
+
|
|
18
|
+
### 升级
|
|
19
|
+
|
|
20
|
+
停止 agent,必要时升级 dsh,然后更新原有 profile 中的插件:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
dsh plugin --profile YOUR_PROFILE add dsh-a2a-server@0.3.0
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
将 `YOUR_PROFILE` 替换为已保存的 profile 名称,安装后重新启动。配套 [Talk2MyAgent Portal](https://github.com/huangjuhua-aigc/Talk2MyAgent) 也需要更新,以使用私有管理入口和自动简报开关。升级已有 profile 时不要再次使用 `--from-default-profile`。
|
|
27
|
+
|
|
28
|
+
原来的 HTTP 主人/监控调用必须迁移至本机 IPC。部署前检查访客许可与资料目录,详见[迁移指南](https://github.com/huangjuhua-aigc/dsh-a2a/blob/main/docs/public-access-hardening.md)。
|
package/README.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 version"></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-
|
|
19
|
-
<img src="https://img.shields.io/badge/DSH-0.1
|
|
20
|
-
<img src="https://img.shields.io/badge/tests-
|
|
18
|
+
<img src="https://img.shields.io/badge/A2A-v1.0%20JSON--RPC-4D6BFE?style=flat" alt="A2A v1.0 JSON-RPC binding">
|
|
19
|
+
<img src="https://img.shields.io/badge/DSH-0.2.1--rc.2-4493F8?style=flat" alt="Built against DSH 0.2.1-rc.2">
|
|
20
|
+
<img src="https://img.shields.io/badge/tests-217-2EA44F?style=flat" alt="217 tests">
|
|
21
21
|
</p>
|
|
22
22
|
|
|
23
23
|
`dsh-a2a-server` makes a [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)
|
|
24
24
|
agent reachable over the [A2A (Agent2Agent)](https://a2a-protocol.org) protocol. It
|
|
25
|
-
serves an Agent Card at a well-known URI and implements the **
|
|
25
|
+
serves an Agent Card at a well-known URI and implements the **v1.0 JSON-RPC
|
|
26
26
|
binding**, so any compliant peer that knows the deployment's URL can discover the
|
|
27
27
|
agent and submit tasks to it.
|
|
28
28
|
|
|
@@ -30,14 +30,21 @@ The plugin is **inbound only**: it never connects to another agent. There is no
|
|
|
30
30
|
client, no peer directory, and no A2A subagent provider. It is a transport
|
|
31
31
|
adapter over `ctx.agents`, not a capability seam.
|
|
32
32
|
|
|
33
|
+
For a local configuration UI and cross-network access, use the companion
|
|
34
|
+
[Talk2MyAgent setup guide](https://github.com/huangjuhua-aigc/Talk2MyAgent/blob/main/README.en.md).
|
|
35
|
+
See the [deployment and migration guide](https://github.com/huangjuhua-aigc/dsh-a2a/blob/main/docs/public-access-hardening.en.md)
|
|
36
|
+
for visitor permissions, automatic briefings, private owner access and Tailscale.
|
|
37
|
+
|
|
33
38
|
## Install
|
|
34
39
|
|
|
35
40
|
```sh
|
|
36
|
-
dsh plugin --profile web add dsh-a2a-server
|
|
41
|
+
dsh plugin --profile web add dsh-a2a-server@0.3.0
|
|
37
42
|
```
|
|
38
43
|
|
|
39
44
|
`dsh plugin` forwards to pnpm inside the profile directory and appends this
|
|
40
45
|
bundle to `dsh.profile.bundles`, because the package declares `dsh.bundle`.
|
|
46
|
+
Since DSH v0.2.1-alpha.1, `--profile` must come before any pnpm arguments —
|
|
47
|
+
the forms above already do.
|
|
41
48
|
|
|
42
49
|
To work from a checkout instead, point it at the directory:
|
|
43
50
|
|
|
@@ -50,13 +57,28 @@ dsh plugin --profile web add ./path/to/dsh-a2a-server
|
|
|
50
57
|
| `ctx.agents` | `dsh-base` |
|
|
51
58
|
| `ctx.credentials` | `dsh-base` |
|
|
52
59
|
| `ctx.webServer` | **`dsh-web-app`** |
|
|
53
|
-
| `ctx.
|
|
60
|
+
| `ctx.tools` and `ctx.systemPrompt` | required visitor tool and prompt isolation services |
|
|
61
|
+
| `ctx.sessionProjections` (optional) | composition; exposes task lifecycle projections |
|
|
54
62
|
|
|
55
|
-
The plugin stays PENDING until
|
|
63
|
+
The plugin stays PENDING until all required services exist. `ctx.webServer`
|
|
56
64
|
ships in `dsh-web-app` rather than `dsh-base`, so a `headless` profile needs
|
|
57
65
|
`@deepseek-ai/dsh-host-webserver` mounted before this bundle serves anything.
|
|
58
66
|
`dsh --profile <name> --dump-config` prints the composed rows.
|
|
59
67
|
|
|
68
|
+
### Upgrading from 0.2.x
|
|
69
|
+
|
|
70
|
+
Version 0.3.0 requires DSH `>=0.2.1-rc.2 <0.3.0`. Stop the agent, install
|
|
71
|
+
`dsh-a2a-server@0.3.0` into its existing profile, then restart it. Owner RPC and
|
|
72
|
+
monitoring now use authenticated local IPC; update the companion Portal as well.
|
|
73
|
+
Visitors retain chat and scoped workspace reads, while other tools are denied.
|
|
74
|
+
An empty `trustedPeers: []` denies all visitors. Task results persist across
|
|
75
|
+
restart within the configured retention window.
|
|
76
|
+
|
|
77
|
+
The current [Talk2MyAgent Portal](https://github.com/huangjuhua-aigc/Talk2MyAgent)
|
|
78
|
+
configures default-on hourly briefings with one persistent switch. The plugin
|
|
79
|
+
alone does not enable automatic briefing without its `briefing` configuration.
|
|
80
|
+
See the [0.3.0 release notes](https://github.com/huangjuhua-aigc/dsh-a2a/blob/main/CHANGELOG.md).
|
|
81
|
+
|
|
60
82
|
## Quick start
|
|
61
83
|
|
|
62
84
|
The bundled example composition runs a real model and a listening server.
|
|
@@ -93,9 +115,9 @@ curl -s http://127.0.0.1:9922/.well-known/agent-card.json
|
|
|
93
115
|
curl -s http://127.0.0.1:9922/a2a \
|
|
94
116
|
-H "authorization: Bearer demo123" \
|
|
95
117
|
-H 'content-type: application/json' \
|
|
96
|
-
-d '{"jsonrpc":"2.0","id":1,"method":"
|
|
97
|
-
"message":{"
|
|
98
|
-
"parts":[{"
|
|
118
|
+
-H 'a2a-version: 1.0' -d '{"jsonrpc":"2.0","id":1,"method":"SendMessage","params":{
|
|
119
|
+
"message":{"messageId":"m1","role":"ROLE_USER",
|
|
120
|
+
"parts":[{"text":"hello"}]}}}'
|
|
99
121
|
```
|
|
100
122
|
|
|
101
123
|
| Variable | Default | Meaning |
|
|
@@ -103,10 +125,10 @@ curl -s http://127.0.0.1:9922/a2a \
|
|
|
103
125
|
| `A2A_PEERS` | `alice` | `name[:token]` list; a token is required for each |
|
|
104
126
|
| `A2A_PORT` | `9900` | Listening port |
|
|
105
127
|
| `A2A_SEND_MODE` | `block` | `block` or `immediate` |
|
|
106
|
-
| `A2A_WORKSPACE_ROOT` | temp dir |
|
|
128
|
+
| `A2A_WORKSPACE_ROOT` | temp dir | Authorized visitor data root and parent of per-peer working directories |
|
|
107
129
|
| `DEEPSEEK_MODEL` | `deepseek-v4-flash` | Model id requested from the adapter |
|
|
108
130
|
|
|
109
|
-
A one-shot probe runs
|
|
131
|
+
A one-shot probe runs 67 checks against a live server and exits non-zero on any
|
|
110
132
|
mismatch:
|
|
111
133
|
|
|
112
134
|
```sh
|
|
@@ -121,22 +143,44 @@ node example/probe.mjs http://127.0.0.1:9922 demo123
|
|
|
121
143
|
| `/.well-known/agent-card.json` | GET | Public by default |
|
|
122
144
|
| `/.well-known/agent.json` | GET | Public by default |
|
|
123
145
|
| `{basePath}` (default `/a2a`) | POST | Bearer required |
|
|
146
|
+
| `{basePath}/admin/contexts[/<id>]` | GET | Local IPC only + admin token; never registered on public HTTP |
|
|
124
147
|
|
|
125
|
-
| JSON-RPC method |
|
|
148
|
+
| JSON-RPC method | Status |
|
|
149
|
+
| --- | --- |
|
|
150
|
+
| `SendMessage` | Waiting negotiated per request |
|
|
151
|
+
| `SendStreamingMessage` | SSE |
|
|
152
|
+
| `GetTask` | Idempotent; answers after settlement |
|
|
153
|
+
| `ListTasks` | Cursor-paginated, filterable, live and retained tasks |
|
|
154
|
+
| `CancelTask` | Cancels the running turn |
|
|
155
|
+
| `SubscribeToTask` | Live stream; `-32004` on a terminal task |
|
|
156
|
+
| `GetExtendedAgentCard` | Extra skills for an authenticated peer; `-32007` when unconfigured |
|
|
157
|
+
| `*TaskPushNotificationConfig` | `-32003` |
|
|
158
|
+
|
|
159
|
+
**Only A2A v1.0 is served.** The v0.3 method names, `kind` discriminators,
|
|
160
|
+
lowercase enums, and nested `file` parts are gone rather than aliased: every
|
|
161
|
+
reply this server writes is v1.0 JSON, which a v0.3 client could not read, so
|
|
162
|
+
answering a v0.3 request would fail further from its cause than refusing it. A
|
|
163
|
+
retired method answers `-32601` naming its replacement, and an explicit
|
|
164
|
+
`A2A-Version: 0.3` answers `-32009` (`VersionNotSupportedError`). A request with
|
|
165
|
+
no `A2A-Version` header is served as 1.0 — the only version this interface
|
|
166
|
+
declares on its card.
|
|
167
|
+
|
|
168
|
+
What that means concretely, against v0.3:
|
|
169
|
+
|
|
170
|
+
| Concern | v0.3 | v1.0 as served here |
|
|
126
171
|
| --- | --- | --- |
|
|
127
|
-
|
|
|
128
|
-
| `
|
|
129
|
-
|
|
|
130
|
-
|
|
|
131
|
-
|
|
|
132
|
-
|
|
|
133
|
-
|
|
|
134
|
-
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
dialect the request used.
|
|
172
|
+
| Task reference | `params.taskId` | `params.id` |
|
|
173
|
+
| `SendMessage` result | bare `Task` | `{ "task": … }` |
|
|
174
|
+
| Task state | `"working"` | `"TASK_STATE_WORKING"` |
|
|
175
|
+
| Message role | `"user"` | `"ROLE_USER"` |
|
|
176
|
+
| Part | `{"kind":"text","text":…}` | `{"text":…}`; file parts flatten to `url`/`raw`/`filename`/`mediaType` |
|
|
177
|
+
| Stream frame | `{"kind":"status-update",…,"final":true}` | `{"statusUpdate":{…}}`; the stream's close is the terminal signal |
|
|
178
|
+
| Stream opening | a non-final status update | the `Task` object itself |
|
|
179
|
+
| Wait control | `configuration.blocking` | `configuration.returnImmediately` (inverted; blocking is the default) |
|
|
180
|
+
| Card endpoint | `url` + `preferredTransport` | `supportedInterfaces[]`, each carrying its own `protocolVersion` |
|
|
181
|
+
| Card auth | `securitySchemes` + `security` | schemes wrapped in `httpAuthSecurityScheme`; `securityRequirements` |
|
|
182
|
+
| Extended card | `supportsAuthenticatedExtendedCard` | `capabilities.extendedAgentCard` |
|
|
183
|
+
| A2A error detail | none | `error.data[]` carrying a `google.rpc.ErrorInfo` with `reason` and `domain` |
|
|
140
184
|
|
|
141
185
|
Inbound messages may carry text, file, and data parts. File and data parts are
|
|
142
186
|
rendered into the model's context as bracketed references. Replies are text.
|
|
@@ -153,19 +197,23 @@ another peer answers exactly as an absent one.
|
|
|
153
197
|
config:
|
|
154
198
|
basePath: /a2a
|
|
155
199
|
publicUrl: https://agents.example.com/a2a # advertised on the card
|
|
156
|
-
protocolVersion: 0.3.0
|
|
157
200
|
provider: deepseek-official
|
|
158
201
|
model: deepseek-v4-flash
|
|
159
202
|
|
|
160
203
|
card:
|
|
161
204
|
name: dsh-harness
|
|
162
|
-
description:
|
|
205
|
+
description: Answers questions using code and text in the authorized directory.
|
|
163
206
|
public: true
|
|
164
207
|
skills:
|
|
165
208
|
- id: general
|
|
166
209
|
name: general
|
|
167
|
-
description:
|
|
210
|
+
description: Chat and scoped file reading.
|
|
168
211
|
tags: [coding, research]
|
|
212
|
+
extendedSkills: # authenticated peers only
|
|
213
|
+
- id: internal
|
|
214
|
+
name: internal diagnostics
|
|
215
|
+
description: Explains project documentation from the authorized directory.
|
|
216
|
+
tags: [internal]
|
|
169
217
|
provider:
|
|
170
218
|
organization: Example Inc.
|
|
171
219
|
url: https://example.com
|
|
@@ -196,21 +244,36 @@ another peer answers exactly as an absent one.
|
|
|
196
244
|
| --- | --- | --- |
|
|
197
245
|
| `basePath` | `/a2a` | JSON-RPC route |
|
|
198
246
|
| `publicUrl` | derived from `Host` | Routable URL published on the card |
|
|
199
|
-
| `protocolVersion` | `0.3.0` | Version advertised on the card |
|
|
200
247
|
| `provider` · `model` | — | Model route for every agent this server creates |
|
|
201
248
|
| `card.public` | `true` | Serve the card without a credential |
|
|
202
249
|
| `card.skills` | `[]` | Declared skills; falls back to one `general` entry |
|
|
250
|
+
| `card.extendedSkills` | `[]` | Skills revealed only through `GetExtendedAgentCard` |
|
|
203
251
|
| `peers` | `{}` | Identity → credential **reference name** |
|
|
204
|
-
| `trustedPeers` | all authenticated |
|
|
252
|
+
| `trustedPeers` | all authenticated | Access allow-list for conversation and scoped file reads; `[]` denies everyone |
|
|
205
253
|
| `rateLimitPerMinute` | `60` | Sliding window per identity |
|
|
206
254
|
| `maxContextTurns` | `5` | Messages accepted per context before `rejected` |
|
|
207
|
-
| `sendMode` | `block` | Default when the client
|
|
208
|
-
| `blockTimeoutMs` | `60000` |
|
|
255
|
+
| `sendMode` | `block` | Default when the client sends no `returnImmediately` |
|
|
256
|
+
| `blockTimeoutMs` | `60000` | Maximum blocking wait; returns the current task on timeout so the client can poll |
|
|
209
257
|
| `contextIdleTtlMs` | `1800000` | Idle time before a context's agent is released |
|
|
210
258
|
| `maxResidentContexts` | `64` | Ceiling on resident contexts |
|
|
259
|
+
| `maxConcurrentTasks` / `maxConcurrentTasksPerPeer` | `8` / `2` | Atomic task admission; excess requests receive 429 |
|
|
260
|
+
| `maxTasksPerPeerPerDay` | `200` | Persisted task allowance per identity per UTC day |
|
|
261
|
+
| `taskTimeoutMs` | `120000` | Cancels execution when its deadline expires |
|
|
262
|
+
| `maxModelCallsPerTask` / `maxOutputTokens` | `8` / `4096` | Model request and per-request output limits |
|
|
263
|
+
| `maxInputChars` / `maxOutputChars` | `32000` / `64000` | Input/output text limits |
|
|
264
|
+
| `streamHeartbeatMs` / `maxStreamsPerTask` | `15000` / `4` | Heartbeat interval and subscription cap |
|
|
265
|
+
| `maxStreamBufferBytes` | `262144` | Disconnect a stream whose buffered frame exceeds the cap |
|
|
266
|
+
| `storage.directory` | `~/.dsh-a2a/<deployment-key>` | Private checkpoints; one process per directory |
|
|
267
|
+
| `storage.retentionMs` / `storage.maxContexts` | `86400000` / `256` | Retention after last activity, and maximum stored contexts |
|
|
268
|
+
| `storage.maxContextBytes` | `4000000` | Maximum checkpoint bytes per context |
|
|
211
269
|
| `isolation.workspaceMode` | `per-peer` | `per-peer` or `shared` |
|
|
212
|
-
| `isolation.workspaceRoot` | — | Required; parent directory or shared cwd |
|
|
213
|
-
| `isolation.peerWorkspaces` | `{}` | Per-identity
|
|
270
|
+
| `isolation.workspaceRoot` | — | Required; authorized shared read-only data root and parent directory or shared cwd |
|
|
271
|
+
| `isolation.peerWorkspaces` | `{}` | Per-identity cwd and authorized read-root override |
|
|
272
|
+
| `preamble.files` | `[".hop/handoff.md"]` | Host briefing files (**agent-level**: resolved against the workspace root and shared by every visitor; absolute paths pass through), merged into each context's first message; absent files are skipped |
|
|
273
|
+
| `briefing.prompt` | — | Optional single-update instruction executed as the bounded local owner at startup and hourly; configured by Portal, requires owner administration |
|
|
274
|
+
| `briefing.settingsFile` | — | Absolute path to a JSON `{ "enabled": boolean }` preference; absent file means enabled, observed every 5 seconds. Disabling preserves the current briefing and lets active work finish; restart timing is persisted |
|
|
275
|
+
| `admin.tokenEnv` | — | Credential reference for local owner and monitoring requests; rejected by public RPC |
|
|
276
|
+
| `admin.socketPath` | deployment-specific | Windows named pipe or private Unix socket for owner RPC and monitoring |
|
|
214
277
|
| `push.enabled` | `false` | Reserved; see [Boundaries](#boundaries) |
|
|
215
278
|
|
|
216
279
|
Configuration is refused at load when `isolation.workspaceRoot` is absent, a peer
|
|
@@ -229,7 +292,8 @@ peers:
|
|
|
229
292
|
|
|
230
293
|
```yaml
|
|
231
294
|
# ~/.dsh/.credentials.yaml
|
|
232
|
-
|
|
295
|
+
refs:
|
|
296
|
+
A2A_PEER_ALICE: <32-byte-hex-from-openssl-rand>
|
|
233
297
|
```
|
|
234
298
|
|
|
235
299
|
`ctx.credentials` resolves the reference per request across four layers — the
|
|
@@ -245,11 +309,29 @@ identities.
|
|
|
245
309
|
| --- | --- | --- |
|
|
246
310
|
| Model context | One peer's conversation cannot enter another's model request | Separate `contextId` → separate Session → separate log |
|
|
247
311
|
| Protocol access | A peer cannot read, continue, or cancel another's context or task | Ownership by authenticated identity |
|
|
248
|
-
| Tooling |
|
|
312
|
+
| Tooling | Only the scoped `read_file` is available; no writes, shell, network or cross-session tools | Inherited-tool masking, scoped reader, and execution guard |
|
|
313
|
+
| Prompt context | Public agents have a dedicated system prompt and suppress optional host runtime context | Scoped complete prompt and context suppression |
|
|
249
314
|
|
|
250
315
|
`per-peer` (the default) derives each identity's `cwd` from `workspaceRoot`.
|
|
251
|
-
`shared` places every peer in one directory
|
|
252
|
-
|
|
316
|
+
`shared` places every peer in one directory. These are working-directory choices,
|
|
317
|
+
not filesystem sandboxes. `read_file` reads from the owner-selected
|
|
318
|
+
`workspaceRoot`; a `peerWorkspaces` override restricts that peer to the overridden
|
|
319
|
+
directory. Relative tool paths resolve from this **data root**, not the per-peer
|
|
320
|
+
cwd. These files are available to admitted visitors, so choose the data root
|
|
321
|
+
accordingly. The A2A checkpoint directory is always excluded.
|
|
322
|
+
|
|
323
|
+
`read_file({"path":"."})` lists the root's entries. A file path reads UTF-8 text;
|
|
324
|
+
`offset` (1-based) and `limit` page lines or directory entries. Reads are bounded
|
|
325
|
+
to 2 MiB files, 32,000 returned characters and 500 lines/entries per call; directory
|
|
326
|
+
scans cover at most 1,000 entries. Binary/office-document extraction is not yet
|
|
327
|
+
implemented. Canonical path checks refuse escapes; symlinks, junctions, hard
|
|
328
|
+
links and Windows alternate data streams are rejected. No shell is invoked.
|
|
329
|
+
`preamble.files` continues to provide an optional opening briefing.
|
|
330
|
+
|
|
331
|
+
Owner RPC uses the same `/a2a` path on **local IPC**, authenticated with the admin
|
|
332
|
+
token. It retains the standard caretaker preset. The public HTTP listener accepts
|
|
333
|
+
only visitor credentials and never mounts the monitoring API. See the
|
|
334
|
+
[migration and deployment notes](https://github.com/huangjuhua-aigc/dsh-a2a/blob/main/docs/public-access-hardening.en.md).
|
|
253
335
|
|
|
254
336
|
## Boundaries
|
|
255
337
|
|
|
@@ -257,17 +339,23 @@ single repository; under it, files written by one peer are readable by another.
|
|
|
257
339
|
- JSONRPC binding only. gRPC and HTTP+JSON are not served, and the card says so.
|
|
258
340
|
- Push notifications are not implemented. `push.enabled` selects which error the
|
|
259
341
|
push methods return; the card advertises `pushNotifications: false`.
|
|
260
|
-
- No
|
|
261
|
-
signatures.
|
|
342
|
+
- No `stateTransitionHistory`, protocol extensions, or card signatures.
|
|
262
343
|
- Streaming emits committed assistant messages. Per-chunk streaming is not
|
|
263
344
|
implemented.
|
|
264
|
-
-
|
|
265
|
-
|
|
266
|
-
|
|
345
|
+
- Execution deadlines cancel the agent. Admission remains occupied until the
|
|
346
|
+
underlying adapter finishes cancellation; an adapter ignoring AbortSignal
|
|
347
|
+
needs process-level supervision. These limits are not a currency budget.
|
|
348
|
+
- Only rooted `read_file` is enabled for visitors. Other tools require separate
|
|
349
|
+
capability work; changing the sandbox mode does not grant those tools. This
|
|
350
|
+
application-level file reader is not an OS sandbox against a malicious local
|
|
351
|
+
administrator mutating the filesystem.
|
|
267
352
|
- A token ceiling settles a task as `completed`; the real turn ending travels in
|
|
268
353
|
`Task.metadata.dsh.stopReason`, which A2A's state enum cannot express.
|
|
269
|
-
- Task
|
|
270
|
-
|
|
354
|
+
- Task results and idle conversation history survive eviction/restart within
|
|
355
|
+
the retention window. Interrupted tasks become failed and are never replayed.
|
|
356
|
+
Cold restoration creates a new harness session behind the stable contextId.
|
|
357
|
+
- One task may run per context; concurrent submissions receive 409. Message IDs
|
|
358
|
+
are not deduplication keys: proxies must not blindly retry submissions.
|
|
271
359
|
- `ctx.webServer` provides no TLS. Any non-loopback exposure belongs behind a
|
|
272
360
|
reverse proxy.
|
|
273
361
|
- A configuration change restarts the plugin and cancels in-flight tasks.
|
|
@@ -277,8 +365,8 @@ single repository; under it, files written by one peer are readable by another.
|
|
|
277
365
|
```
|
|
278
366
|
src/
|
|
279
367
|
├── protocol/ dependency-free library: no Cordis, no HTTP, no harness
|
|
280
|
-
│ ├── wire.ts the A2A vocabulary
|
|
281
|
-
│ ├──
|
|
368
|
+
│ ├── wire.ts the A2A v1.0 vocabulary: one shape, no translation
|
|
369
|
+
│ ├── parse.ts inbound parsing and the method table
|
|
282
370
|
│ ├── jsonrpc.ts framing and the A2A error codes
|
|
283
371
|
│ ├── card.ts Agent Card construction
|
|
284
372
|
│ └── sse.ts SSE frame encoding
|
|
@@ -288,6 +376,10 @@ src/
|
|
|
288
376
|
├── tasks.ts task slots and the three-stage turn correlation
|
|
289
377
|
├── projection.ts the a2aTask fold over the session log
|
|
290
378
|
├── security.ts authentication, rate limiting, defanging, redaction
|
|
379
|
+
├── workspace-read.ts bounded read_file within the authorized data root
|
|
380
|
+
├── management.ts private owner RPC and monitoring over local IPC
|
|
381
|
+
├── state.ts durable task and conversation checkpoints
|
|
382
|
+
├── briefing.ts hourly briefing updates and persistent scheduling
|
|
291
383
|
├── config.ts schema plus the cross-field checks that fail at load
|
|
292
384
|
└── types.ts declaration merges into SessionEventMap / MessageSourceMap
|
|
293
385
|
```
|
|
@@ -303,17 +395,19 @@ carries the committed output, so the fold serves the answer without reaching
|
|
|
303
395
|
back into message history.
|
|
304
396
|
|
|
305
397
|
**Residency is explicit.** HTTP has no connection lifetime, so each `contextId`
|
|
306
|
-
maps to an Activation evicted when idle
|
|
398
|
+
maps to an Activation evicted when idle. Private checkpoints retain task results
|
|
399
|
+
and conversation history; cold restoration creates a new harness session.
|
|
307
400
|
|
|
308
401
|
## Development
|
|
309
402
|
|
|
310
403
|
```sh
|
|
311
404
|
pnpm install
|
|
312
405
|
pnpm typecheck
|
|
313
|
-
pnpm test #
|
|
406
|
+
pnpm test # protocol, lifecycle, security and workspace-read regressions
|
|
314
407
|
pnpm serve # a listening server
|
|
315
|
-
pnpm probe #
|
|
408
|
+
pnpm probe # 67 checks against a running server
|
|
316
409
|
pnpm build # emit lib/
|
|
410
|
+
pnpm test:package # verify imports with only declared runtime packages
|
|
317
411
|
```
|
|
318
412
|
|
|
319
413
|
The end-to-end suites boot a real Cordis composition with a real agent loop and
|
|
@@ -327,13 +421,16 @@ Built against [deepseek-ai/deepseek-harness](https://github.com/deepseek-ai/deep
|
|
|
327
421
|
The official project supplies the agent runtime, the plugin system, and the
|
|
328
422
|
capability seams this plugin consumes. This project supplies:
|
|
329
423
|
|
|
330
|
-
- The A2A
|
|
331
|
-
- Agent Card construction and
|
|
424
|
+
- The A2A v1.0 JSON-RPC binding, served inbound
|
|
425
|
+
- Agent Card construction and inbound request parsing
|
|
332
426
|
- Mapping between A2A tasks and harness turns
|
|
333
427
|
- Per-peer authentication, isolation, and workspace policy
|
|
334
428
|
|
|
335
429
|
The harness is pre-release and does not promise compatibility across renames or
|
|
336
|
-
repackaging, so
|
|
430
|
+
repackaging, so compatibility is declared through `engines.dsh`
|
|
431
|
+
(`>=0.2.1-rc.2 <0.3.0`) plus range-bounded `@deepseek-ai/dsh-*` peer
|
|
432
|
+
dependencies: an install preflight refuses any combination the plugin was not
|
|
433
|
+
built against, and the range admits every 0.2.x patch that follows.
|
|
337
434
|
|
|
338
435
|
## Community
|
|
339
436
|
|