@nelsonlongxiang/dsh-open-a2a-net 0.6.28 → 0.6.30
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/README.md +87 -14
- package/README_zh.md +83 -7
- package/assets/nexusDist/assets/index-C_x3iAth.js +9 -0
- package/assets/nexusDist/assets/index-Dz8N80tI.css +1 -0
- package/assets/nexusDist/index.html +2 -2
- package/cli/a2a.mjs +0 -0
- package/cordis.patch.yml +21 -21
- package/lib/canvas-store.js +21 -1
- package/lib/client.js +64 -64
- package/lib/client.js.map +1 -1
- package/lib/faces/admin.js +755 -0
- package/lib/faces/canvas-notify.js +138 -0
- package/lib/faces/wire.js +669 -0
- package/lib/flood-guard.js +106 -0
- package/lib/index.js +200 -1392
- package/lib/recursion-guard.js +78 -0
- package/lib/runtime.js +1 -0
- package/lib/skill/SKILL.md +35 -9
- package/lib/skill/references/cli.md +24 -7
- package/lib/skill/references/collaboration.md +355 -0
- package/lib/skill/references/discovery.md +5 -2
- package/lib/skill/references/pitfalls.md +2 -1
- package/lib/skill/references/routing.md +18 -8
- package/lib/skill/references/setup.md +152 -0
- package/lib/task-ledger.js +40 -4
- package/lib/types/canvas-store.d.ts +10 -1
- package/lib/types/faces/admin.d.ts +133 -0
- package/lib/types/faces/canvas-notify.d.ts +74 -0
- package/lib/types/faces/wire.d.ts +171 -0
- package/lib/types/flood-guard.d.ts +29 -0
- package/lib/types/index.d.ts +11 -3
- package/lib/types/recursion-guard.d.ts +56 -0
- package/lib/types/runtime.d.ts +35 -0
- package/lib/types/task-ledger.d.ts +15 -1
- package/nexus-stage/package-lock.json +2262 -2262
- package/nexus-stage/src/canvas-ops.ts +175 -175
- package/nexus-stage/src/canvas-wire.ts +156 -156
- package/nexus-stage/src/edges.ts +72 -72
- package/nexus-stage/src/federation-overlay.ts +103 -103
- package/nexus-stage/src/federation.ts +240 -240
- package/nexus-stage/src/layout-doc.ts +143 -143
- package/nexus-stage/src/layout-wire.ts +158 -158
- package/nexus-stage/src/main.ts +5 -1
- package/nexus-stage/src/planning-card-text.ts +73 -0
- package/nexus-stage/src/planning-edges.ts +26 -0
- package/nexus-stage/src/planning-fit.ts +45 -0
- package/nexus-stage/src/planning-legend.ts +38 -0
- package/nexus-stage/src/planning-mirror.ts +31 -0
- package/nexus-stage/src/planning-overlap.ts +159 -0
- package/nexus-stage/src/planning-relayout.ts +121 -0
- package/nexus-stage/src/planning-search.ts +168 -0
- package/nexus-stage/src/planning-types.ts +1 -0
- package/nexus-stage/src/planning-undo.ts +68 -0
- package/nexus-stage/src/planning-view.ts +120 -121
- package/nexus-stage/src/planning.css +41 -8
- package/nexus-stage/src/seat.ts +35 -35
- package/nexus-stage/src/stage-name.ts +13 -13
- package/nexus-stage/src/tokens.ts +31 -31
- package/nexus-stage/src/viewport.ts +24 -1
- package/nexus-stage/src/wb-chat.ts +20 -6
- package/nexus-stage/src/wb-ledger.ts +20 -7
- package/nexus-stage/src/wb-names.ts +43 -0
- package/nexus-stage/src/workbench.css +8 -2
- package/nexus-stage/src/workbench.ts +55 -17
- package/nexus-stage/src/world.ts +13 -21
- package/package.json +216 -214
- package/assets/nexusDist/assets/index-1niOBYxp.js +0 -5
- package/assets/nexusDist/assets/index-uQf7NwjF.css +0 -1
- package/nexus-stage/scripts/__pycache__/mockhost.cpython-312.pyc +0 -0
package/README.md
CHANGED
|
@@ -35,7 +35,8 @@ shared tool runtime.
|
|
|
35
35
|
|
|
36
36
|
## What you get
|
|
37
37
|
|
|
38
|
-
- **Model tools** — `a2a_teams` lists the teams this node can see (own
|
|
38
|
+
- **Model tools** (ten; see the table below) — `a2a_teams` lists the teams this node can see (its own rows
|
|
39
|
+
plus peer teams inside the joined-team scope); `a2a_route`
|
|
39
40
|
sends one message to a team, reuses `context_id` to continue a remote conversation, and fails over across
|
|
40
41
|
candidates when a peer is unreachable. `a2a_tasks` reconciles the receipt contract across three tiers: every route that leaves a
|
|
41
42
|
task owed a reply (async delivery, a released wait) stays in the owed book as pending until its
|
|
@@ -65,15 +66,40 @@ shared tool runtime.
|
|
|
65
66
|
|
|
66
67
|
## Model tools at a glance
|
|
67
68
|
|
|
69
|
+
Ten tools. With `teamScopeInfo` on (the default), `a2a_teams`, `a2a_status` and `a2a_probe` answer from
|
|
70
|
+
this host's joined-team scope: `[this host]` rows plus peers serving a team this host's nodes declared.
|
|
71
|
+
`a2a_tasks` (this host's own dispatch ledger) stays unscoped.
|
|
72
|
+
|
|
68
73
|
| Tool | Answers | Notes |
|
|
69
74
|
| --- | --- | --- |
|
|
70
|
-
| `
|
|
75
|
+
| `a2a_whoami` | Who am I on this network? | The calling session's node id, joined/live state, routable handle, roster + canvas memberships, the owner's `teamJoinAllowlist`, the `teamScopeRouting` switch, and recent refused join/leave attempts. |
|
|
76
|
+
| `a2a_teams` | Which teams can I route to? | `[this host]` rows plus in-scope peer teams, with activity excerpts. |
|
|
71
77
|
| `a2a_route` | Send a message to a team. | `context_id` continues a conversation; `async: true` delivers without waiting; fails over across candidates. |
|
|
72
78
|
| `a2a_tasks` | Which async tasks still owe a receipt? | Three tiers: owed rows keep the follow-up `context_id`; past-TTL rows auto-dead-letter (still settleable); outcomes land in the bounded archive; persisted across restarts. |
|
|
73
|
-
| `a2a_probe` | Is the fleet healthy? | One verified-card fetch per peer (or one given url); `N reachable, M down`; misses classified `unreachable` vs `rejected`. |
|
|
74
|
-
| `a2a_team_join` | Declare one joined node a member of a team. | Allowlist-gated
|
|
79
|
+
| `a2a_probe` | Is the fleet healthy? | One verified-card fetch per in-scope peer (or one given url); `N reachable, M down`; misses classified `unreachable` vs `rejected`. |
|
|
80
|
+
| `a2a_team_join` | Declare one joined node a member of a team. | Allowlist-gated: only an **exact** name in `teamJoinAllowlist` authorizes a join (empty list denies every join). A wildcard/prefix entry is shown by `a2a_whoami` but authorizes nothing (`[PATTERN_NO_MINT]`); canvas teams are assigned in the workbench, never here (`[CANVAS_TEAM]`). Attempts land in the state face's `registry.teamAudit`. |
|
|
75
81
|
| `a2a_team_leave` | Retract a team membership. | Always allowed — leaving only shrinks exposure. |
|
|
76
|
-
| `
|
|
82
|
+
| `a2a_exposure` | How visible is this session? | Sets the session's own tier: `linked` (the default floor) or `fleet` (a voluntary opt-up); a group's visibility follows its strictest member. Requires a joined session. |
|
|
83
|
+
| `a2a_report` | Tell this host's supervision something while off the network. | The one ingress exempt from the join gate: one-way, lands in the state face's reports ring, grants no presence or routing. One report per 30s per session, ≤4000 chars, identical text 3× in 10 min is refused as a loop. |
|
|
84
|
+
| `a2a_status` | What is this node doing right now? | In-scope tracked peers with quality scores, in-flight routes, recent routing activity, and the idempotency window aggregate (occupancy + claim/replay/conflict counters). |
|
|
85
|
+
|
|
86
|
+
## a2a-net CLI (callers outside a session)
|
|
87
|
+
|
|
88
|
+
The package also ships a command, `a2a-net` (`bin` → `cli/a2a.mjs`, 0.6.27+), for scripts, terminals
|
|
89
|
+
and other agent harnesses that are not a DSH session. It creates its own Ed25519 identity
|
|
90
|
+
(`<name>/<16 hex>`), deposits a signed card on one home host for up to 4h (`publish`; an expired deposit
|
|
91
|
+
stays listed as an offline row until re-published), and receives into a mailbox on that host (64
|
|
92
|
+
entries kept) instead of being woken:
|
|
93
|
+
|
|
94
|
+
```sh
|
|
95
|
+
a2a-net init review-seat --home http://127.0.0.1:3080 # identity + home host
|
|
96
|
+
A2A_KEY=... a2a-net publish # deposit (key-gated host)
|
|
97
|
+
a2a-net route dsh/1a2b3c4d "status?" # async by default; --sync waits for the reply
|
|
98
|
+
a2a-net inbox --new # unread mail; --wait blocks, --hook feeds a Claude Code hook
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
It delivers only to teams on its home host (direct delivery never relays). Commands, flags, environment
|
|
102
|
+
variables and exit codes: [`src/skill/references/cli.md`](src/skill/references/cli.md).
|
|
77
103
|
|
|
78
104
|
## Canvas teams (arbitrary grouping, v0.5.25)
|
|
79
105
|
|
|
@@ -82,9 +108,14 @@ A canvas team is a user-composed, named, multi-member routing group: **one atomi
|
|
|
82
108
|
- Storage: `<dsh-home>/a2a/canvas.json` (ordered team entries; caps: 64 teams × 32 members)
|
|
83
109
|
- Control API: `POST /__dsh_a2a/canvas` with `action: create | remove | add-member | remove-member`
|
|
84
110
|
- Membership requires a joined session (live node or remembered intent) — no routing backdoor over unjoined sessions; leaving the network (leave/archive) drops every canvas membership
|
|
85
|
-
-
|
|
111
|
+
- A member may also be a **remote** zone-qualified team handle (`zone/a1b2c3d4`) the operator adds by hand — the link that admits that remote caller here and entitles it to the full card
|
|
112
|
+
- `a2a_teams` lists canvas teams as local rows with member/live counts; `/__dsh_a2a/state` serves per-member `joined`/`live` flags for local members and `{ id, team, remote: true }` for remote ones
|
|
113
|
+
|
|
114
|
+
## Native-teams bridge (node unification, P1) — deprecated
|
|
86
115
|
|
|
87
|
-
|
|
116
|
+
> **Deprecated.** `@nelsonlongxiang/dsh-native-teams` is deprecated. The bridge code still ships
|
|
117
|
+
> (`src/teams-bridge.ts`; `nativeTeamsInbound` defaults to `false`), kept for existing deployments;
|
|
118
|
+
> new deployments should not rely on it. The section below describes it as it stands.
|
|
88
119
|
|
|
89
120
|
Two half-bridges to `@nelsonlongxiang/dsh-native-teams` (structural contract mirror: `src/teams-bridge.ts`; the frozen contract lives in that package's `src/a2a-face.ts`):
|
|
90
121
|
|
|
@@ -112,7 +143,23 @@ Row config overlays in the profile's patch layers; every key has a schema defaul
|
|
|
112
143
|
| `wakeJoinedOnBoot` | `false` | Prewarm cold joined sessions' agents after mount (needs the session controller wake face; wake-on-route and the sidebar wake button stay available without it). The prewarm is deferred, foreground-yielding, and cancellable — it never blocks the boot window (see the two knobs below). |
|
|
113
144
|
| `wakePrewarmDelayMs` | `10000` | Idle delay between loader settlement and the first prewarm wake; `0` restores fire-at-settle. |
|
|
114
145
|
| `wakePrewarmQuietMs` | `5000` | Foreground quiet window: a wake/route demand (or any outbound route in flight) inside this window postpones the next prewarm step; `0` disables the yield. |
|
|
115
|
-
| `
|
|
146
|
+
| `wakeBootStaggerMs` | `3000` | Pause between serial boot-prewarm wakes (caps log-decode saturation after a restart; wake-on-route never waits on it). |
|
|
147
|
+
| `wakeReconcile` | `true` | Continuous desired-state reconciliation: each tick re-derives the cold joined intents due and wakes one per pass (post-boot drift, failed wakes, late gateways). Tuned by `wakeReconcileIntervalMs` (`60000`), `wakeReconcileBackoffBaseMs` (`5000`) and `wakeReconcileMaxBackoffMs` (`600000`). |
|
|
148
|
+
| `teamJoinAllowlist` | `[]` | Teams `a2a_team_join` may declare: **exact names only** (a `*`/prefix entry authorizes nothing). Empty denies every join. The owner can also edit it from the workbench owner panel (`/__dsh_a2a/owner-settings`). |
|
|
149
|
+
| `teamScopeRouting` | `true` | S3 admission: routing checks caller↔target shared team membership; a session with no declared team has no network. `false` restores the open outbound seam. |
|
|
150
|
+
| `teamScopeInfo` | `true` | S5 information-plane scope for `a2a_teams` / `a2a_status` / `a2a_probe` (see the tools section). `false` restores fleet-wide disclosure. |
|
|
151
|
+
| `peerAllowlist` | `[]` | Non-empty: a referral is admitted only when its host matches an entry (exact `host[:port]`, bare domain suffix, or full URL). Seeds are never filtered. |
|
|
152
|
+
| `referralLearning` | `true` | `false` freezes the tracked set to the configured seeds: referrals are neither learned nor offered. |
|
|
153
|
+
| `referralTeamResolution` | `false` | gh#8: team names resolve over seeds + delegates only; `true` restores the legacy whole-store basis (referral-learned peers included). |
|
|
154
|
+
| `exposureExcerpt` | `'full'` | How much of a session's activity excerpt the card publishes: `full`, `masked` (shape-only prefix) or `off`. Titles and handles stay. |
|
|
155
|
+
| `originClaimWindow` | `true` | OriginClaim transition window: deliveries without an envelope still walk the caller gate (counted as `legacyInbound`); `false` rejects them like failed signatures. |
|
|
156
|
+
| `originClaimUrl` | `''` | Override for the envelope's `callerUrl` (pocket-tunneled hosts); `''` derives it. |
|
|
157
|
+
| `gateAgentLessCalls` | `'audit'` | Host-side (agent-less) routing calls: audit them, or `refuse`. |
|
|
158
|
+
| `asyncNudgeDelayMs` | `120000` | Delay before a delivered-but-unconsumed async route re-wakes its target with a one-line nudge; `0` disables. |
|
|
159
|
+
| `taskStaleTtlMs` | `86400000` | Owed-task stale TTL (24h): past it a pending row auto-dead-letters (a late receipt can still settle it). |
|
|
160
|
+
| `dshHome` | `''` | DSH home directory override for the plugin's state (`<home>/a2a/…`); `''` resolves the default home. |
|
|
161
|
+
| `cardTtlMs` / `flushTimeoutMs` / `routeTimeoutMs` | see schema | Card lifetime (48h) and route timing budgets. |
|
|
162
|
+
| `cardCacheTtlMs` / `cardCacheNegativeTtlMs` / `remoteRowsTtlMs` / `stateColdRowsTtlMs` | `60000` / `30000` / `15000` / `5000` | Peer card cache (hit / miss), remote session rows refresh window, and cold-row refresh window of the state face. |
|
|
116
163
|
|
|
117
164
|
Example — one announcing node with a seed:
|
|
118
165
|
|
|
@@ -126,7 +173,7 @@ Example — one announcing node with a seed:
|
|
|
126
173
|
|
|
127
174
|
## Leave semantics (bounded staleness)
|
|
128
175
|
|
|
129
|
-
`leave` is a per-session, per-host gesture: it removes the join intent locally and unmounts the node. There is **no network-wide revocation broadcast** —
|
|
176
|
+
`leave` is a per-session, per-host gesture: it removes the join intent locally and unmounts the node. There is **no network-wide revocation broadcast** — peers keep listing the team until their cached copy of this node's card refreshes (`cardCacheTtlMs`, 60s by default — `sessionTeams` is an unsigned serve-fresh field, so the card's own 48h TTL does not hold it), and inbound deliveries during that window fail honestly (the host no longer resolves the team). Remote targets that already implemented the receipt contract are unaffected. A formal revocation-broadcast protocol is tracked in upstream note `2026-08-27-a2a-leave-no-revocation-broadcast.md` (proposed).
|
|
130
177
|
|
|
131
178
|
## Trust model
|
|
132
179
|
|
|
@@ -134,9 +181,28 @@ Cards are Ed25519-signed and expire; `peers` referrals, `sessionTeams` listings,
|
|
|
134
181
|
are unsigned and read fresh — the last two exist so operators can audit which build every fleet node actually runs
|
|
135
182
|
without visiting each host. A referral URL whose own fetch just proved it serves this node's card is remembered,
|
|
136
183
|
and re-offers of it within a suppression window are ignored instead of flickering in and out of the peer store.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
184
|
+
Every `/__dsh_a2a/*` control route — reads included — requires the configured `apiKey` (`X-API-Key`,
|
|
185
|
+
constant-time compare) when one is set. With an empty key only a loopback socket is trusted; a
|
|
186
|
+
`Sec-Fetch-Site` header can only refuse (a cross-site page is rejected even on loopback), never grant.
|
|
187
|
+
Set an `apiKey` before exposing the listener beyond loopback.
|
|
188
|
+
|
|
189
|
+
| Control route | Purpose |
|
|
190
|
+
| --- | --- |
|
|
191
|
+
| `GET /__dsh_a2a/state` | The state face the panel and workbench poll (sessions, canvas, peers, remote rows, activity, ledger). |
|
|
192
|
+
| `POST /__dsh_a2a/join` · `/leave` | Join or leave one session node. |
|
|
193
|
+
| `/__dsh_a2a/canvas` · `/canvas-layout` | Canvas teams (create/remove/add-member/remove-member) and the board layout. |
|
|
194
|
+
| `/__dsh_a2a/groups` | Manual session grouping — deprecated since 0.6.6 (canvas teams supersede it), kept for old panels. |
|
|
195
|
+
| `POST /__dsh_a2a/dispatch` | Workbench quick dispatch into a team. |
|
|
196
|
+
| `POST /__dsh_a2a/tasks/abandon` · `/tasks/cancel` | Settle an owed task from the caller side (cancel also steers a stop notice to a live local target); the row lands in `archived[]`. |
|
|
197
|
+
| `/__dsh_a2a/session-log` · `/remote-session-log` | Session log reads for the workbench. |
|
|
198
|
+
| `/__dsh_a2a/owner-settings` | The owner panel: read (GET) or write (POST) `teamJoinAllowlist`, `teamScopeInfo` and `announce` into the home patch layer (the first two apply immediately; `announce` on the next restart). |
|
|
199
|
+
| `/__dsh_a2a/fleet-canvas-op` | Relay a canvas operation to a peer's `/a2a/canvas-op`, stamped with this node's OriginClaim; the peer's link gate decides. |
|
|
200
|
+
| `/__dsh_a2a/cli-deposit` | Deposit, list, revoke and mailbox reads for `a2a-net` CLI identities. |
|
|
201
|
+
|
|
202
|
+
The state face's ledger: `tasks` (pending, each with `settleable`), `tasksStaleCount`, `tasksDead` (with
|
|
203
|
+
`direction` when known), and `archived` — the newest 64 settled rows, newest first, each with `outcome`,
|
|
204
|
+
`endedBy` (`receipt` | `echo` | `abandoned` | `cancelled`), `late: true` when a receipt arrived after the
|
|
205
|
+
caller ended it, and `direction` when known — plus `archivedCount` and `archivedSplit` (count per outcome).
|
|
140
206
|
|
|
141
207
|
## Stages (visualization surfaces)
|
|
142
208
|
|
|
@@ -157,6 +223,8 @@ itself before building.
|
|
|
157
223
|
|
|
158
224
|
## Collaboration SOP (network practice, v0.1)
|
|
159
225
|
|
|
226
|
+
> Current rules: the A2A collaboration norm ([`src/skill/references/collaboration.md`](src/skill/references/collaboration.md), in Chinese, shipped with the plugin to every node). The v0.1 SOP below is kept as history; where they conflict, the norm wins.
|
|
227
|
+
|
|
160
228
|
Distilled from the 0.1.x–0.5.x delivery cycles by the network's research node; adopted by the maintainer.
|
|
161
229
|
|
|
162
230
|
- **Roles** — one session, one office, no crossing workspace boundaries: a research node (protocol/design
|
|
@@ -212,8 +280,13 @@ Implement your own signed card, `GET /.well-known/agent-card.json`, and
|
|
|
212
280
|
`POST /a2a/direct`. The wire shapes these modules encode are the contract.
|
|
213
281
|
Anonymous card fetches get the minimal discovery face. The full card needs a
|
|
214
282
|
verified OriginClaim envelope (`X-A2A-Caller` / `-Url` / `-Ts` / `-Sig`,
|
|
215
|
-
signed with your own card key)
|
|
216
|
-
|
|
283
|
+
signed with your own card key) **and** a link to the serving host: your card
|
|
284
|
+
URL shares its origin, your handle is one of its joined session handles, its
|
|
285
|
+
operator added your exact handle as a canvas remote member, or both operators
|
|
286
|
+
seeded each other (its `peers` names your origin and your card's `peers` names
|
|
287
|
+
its origin). A verified but unlinked caller still gets the minimal card. See
|
|
288
|
+
`docs/protocol/delivery-origin-auth.md` and the rest of `docs/protocol/` for
|
|
289
|
+
the envelope details.
|
|
217
290
|
|
|
218
291
|
**Which artifact to build against.** npm direct install already includes the
|
|
219
292
|
four subpaths: releases from 0.6.25 (badge `latest`) carry them, so
|
package/README_zh.md
CHANGED
|
@@ -24,13 +24,43 @@ dsh plugin --profile web add @nelsonlongxiang/dsh-open-a2a-net
|
|
|
24
24
|
|
|
25
25
|
## 你能得到什么
|
|
26
26
|
|
|
27
|
-
-
|
|
28
|
-
- **侧栏控制** —— Web 侧栏底部的操作入口,把本宿主的会话列为可加入的网络节点:标题、近期活动摘录、每行所属团队,就地加入/退出,冷行(已持久化加入意图但会话未加载)可唤醒——打开会话即重挂节点。会话团队名为 `<team>/<id8>`;Web 会话取 id 前 8 位,导入会话(`import-<uuid>`)保留 `import-` 前缀加 uuid 前 8 位十六进制,避免导入 id
|
|
27
|
+
- **模型工具**(共十个,见下表)—— `a2a_teams` 列出本节点可见的团队(本机行,加上已加入团队作用域内的 peer 团队);`a2a_route` 向一个团队发送消息,用 `context_id` 继续远端会话,peer 不可达时在候选间故障切换。`a2a_tasks` 按三层对账回执契约:每个让任务欠回复的路由(异步投递、已释放的等待)都以 pending 留在欠账本里,直到其 `[A2A receipt] task <task_id>` 消息关联上——超过 stale TTL 的逾期行自动转死信(复活的目标仍可用迟到回执结清),每次结清都落入有界归档而不是被静默淘汰,每行保留续聊用的 `context_id`,跨重启持久化。`a2a_probe` 测量受跟踪舰队的可达性与往返延迟(每个 peer 一次验签取卡,可收窄到单个 url),供派单前健康检查,并标明每次失败的阶段——`unreachable`(传输/HTTP)或 `rejected`(卡片不可信)。
|
|
28
|
+
- **侧栏控制** —— Web 侧栏底部的操作入口,把本宿主的会话列为可加入的网络节点:标题、近期活动摘录、每行所属团队,就地加入/退出,冷行(已持久化加入意图但会话未加载)可唤醒——打开会话即重挂节点。会话团队名为 `<team>/<id8>`;Web 会话取 id 前 8 位,导入会话(`import-<uuid>`)保留 `import-` 前缀加 uuid 前 8 位十六进制,避免导入 id 塌缩成少数几个团队。面板的活动区还列出欠回执——已投递但尚未被其 `[A2A receipt]` 消息关联的异步任务——与在途等待并列,跨轮等待无需询问模型即可看见。
|
|
29
29
|
- **归档即离网** —— 归档会话(workspace 注册表状态)会剪除其加入意图并卸载其节点:唤醒前的启动结算、每次状态读取(会话中归档在一个面板轮询周期内消失)、以及永不唤醒已归档目标的路由时守卫。
|
|
30
30
|
- **诚实的等待** —— 目标始终不应答的同步路由,在 180s 回复等待期限后以已投递形态与回执契约释放调用方(目标按自己的节奏作答);`async: true` 完全跳过等待。网络面板对超过 120s 的在途行降亮度标记为过期等待而非暗示活跃进度,标题带包版本徽标。
|
|
31
31
|
- **A2A 工作台** —— `/__dsh_a2a_nexus/` 全页 DOM 工作台(0.6.0 取代 Three.js 3D 观测;0.6.2 合并:规划板即主舞台,目录/台账/活动/规划乘可折叠侧栏,响应式三档——窄屏侧栏变抽屉、详情变底部浮层;canvas 面掉线时面板自动回落主区):目录(本机现场/冷启动、canvas 队、peers、远端节点)、台账(待回执/死信+就地清账)、活动环、规划(衍生行动清单+催办/派单起草)、空间规划板(canvas 队组建/成员编排/网络加入退出,布局持久化,`?mode=plan` 直达)。
|
|
32
32
|
- **通告** —— `announce: true` 发布本节点卡片(团队、能力、引荐、已加入会话团队),peers 无需任何目录即可发现它。
|
|
33
33
|
|
|
34
|
+
## 模型工具一览
|
|
35
|
+
|
|
36
|
+
共十个工具。`teamScopeInfo` 开启时(默认),`a2a_teams`、`a2a_status`、`a2a_probe` 只在本宿主的已加入团队作用域内作答:`[this host]` 本机行,加上服务于本宿主节点所声明团队的 peers。`a2a_tasks`(本宿主自己的派单账本)不受作用域限制。
|
|
37
|
+
|
|
38
|
+
| 工具 | 回答什么 | 说明 |
|
|
39
|
+
| --- | --- | --- |
|
|
40
|
+
| `a2a_whoami` | 我在网络里是谁? | 调用会话的节点 id、加入/在线状态、可路由句柄、roster + canvas 成员关系、属主的 `teamJoinAllowlist`、`teamScopeRouting` 开关,以及最近被拒的加入/退出尝试。 |
|
|
41
|
+
| `a2a_teams` | 我能路由到哪些团队? | `[this host]` 本机行加上作用域内的 peer 团队,带活动摘录。 |
|
|
42
|
+
| `a2a_route` | 向一个团队发消息。 | `context_id` 续聊;`async: true` 投递不等待;在候选间故障切换。 |
|
|
43
|
+
| `a2a_tasks` | 哪些异步任务还欠回执? | 三层:欠账行保留续聊 `context_id`;过 TTL 的行自动转死信(仍可结清);结局落入有界归档;跨重启持久化。 |
|
|
44
|
+
| `a2a_probe` | 舰队健康吗? | 每个作用域内 peer 一次验签取卡(或指定一个 url);`N reachable, M down`;失败分为 `unreachable` 与 `rejected`。 |
|
|
45
|
+
| `a2a_team_join` | 把一个已加入节点声明为某团队成员。 | 白名单门禁:只有 `teamJoinAllowlist` 里的**精确名**才授权加入(空列表拒绝一切加入)。通配/前缀条目会在 `a2a_whoami` 里显示,但不授权任何加入(`[PATTERN_NO_MINT]`);画布队伍在工作台分配,从不经此工具(`[CANVAS_TEAM]`)。尝试记录进状态面的 `registry.teamAudit`。 |
|
|
46
|
+
| `a2a_team_leave` | 撤回一个团队成员关系。 | 总是允许——退出只会缩小暴露面。 |
|
|
47
|
+
| `a2a_exposure` | 本会话有多可见? | 设置会话自己的暴露档:`linked`(默认下限)或 `fleet`(自愿上调);组的可见性取最严成员。需会话已加入网络。 |
|
|
48
|
+
| `a2a_report` | 不在网时向本宿主监督面报告。 | 唯一不受加入门约束的入口:单向,落入状态面的 reports 环,不授予在场或路由权。每会话 30s 一次、≤4000 字,同文 10 分钟内 3 次按循环拒绝。 |
|
|
49
|
+
| `a2a_status` | 本节点此刻在做什么? | 作用域内受跟踪 peers 及质量分、在途路由、近期路由活动,以及幂等窗口汇总(占用 + claim/replay/conflict 计数)。 |
|
|
50
|
+
|
|
51
|
+
## a2a-net CLI(会话之外的调用方)
|
|
52
|
+
|
|
53
|
+
本包还附带一个命令 `a2a-net`(`bin` → `cli/a2a.mjs`,0.6.27+),供脚本、终端和其他 agent 工具链这类非 DSH 会话的调用方使用。它生成自己的 Ed25519 身份(`<name>/<16 位十六进制>`),把签名卡片寄存到一台 home 宿主上,最长 4 小时(`publish`;过期的寄存以离线行保留,重新 publish 即恢复),并在该宿主上用信箱收件(保留 64 条),而不是被唤醒:
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
a2a-net init review-seat --home http://127.0.0.1:3080 # 身份 + home 宿主
|
|
57
|
+
A2A_KEY=... a2a-net publish # 寄存(有密钥的宿主)
|
|
58
|
+
a2a-net route dsh/1a2b3c4d "status?" # 默认异步;--sync 等待回复
|
|
59
|
+
a2a-net inbox --new # 未读来信;--wait 阻塞等待,--hook 供 Claude Code hook 使用
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
它只能投递到 home 宿主上的团队(直连投递不转发)。命令、参数、环境变量与退出码见 [`src/skill/references/cli.md`](src/skill/references/cli.md)。
|
|
63
|
+
|
|
34
64
|
## 画布队伍(任意组队,v0.5.25)
|
|
35
65
|
|
|
36
66
|
画布队伍是用户自建的有名多成员路由组:**一个原子会话节点可同时属于多个队伍**,路由到 `<team>/canvas/<name>` 解析**第一个活成员**(成员顺序即路由优先级),无活成员则唤醒**第一个冷 joined 成员**(wake-on-route)。`canvas/` 路径段与节点别名(`<team>/<id8>`)结构性无冲突,两个命名空间互不干扰。
|
|
@@ -38,9 +68,12 @@ dsh plugin --profile web add @nelsonlongxiang/dsh-open-a2a-net
|
|
|
38
68
|
- 存储:`<dsh-home>/a2a/canvas.json`(有序队伍条目;上限:64 队 × 32 成员)
|
|
39
69
|
- 控制 API:`POST /__dsh_a2a/canvas`,`action: create | remove | add-member | remove-member`
|
|
40
70
|
- 成员必须是 joined 会话(活节点或已记意图)——未加入会话没有路由后门;退出网络(leave/archive)自动清空全部画布成员关系
|
|
41
|
-
-
|
|
71
|
+
- 成员也可以是操作者手工加入的**远端**带 zone 团队句柄(`zone/a1b2c3d4`)——这就是让该远端调用方在本机获准入、并有权拿完整卡片的链接
|
|
72
|
+
- `a2a_teams` 以本地行列出画布队伍(含成员数/活数);`/__dsh_a2a/state` 对本地成员提供 `joined`/`live` 标记,对远端成员提供 `{ id, team, remote: true }`
|
|
42
73
|
|
|
43
|
-
## Native-teams 桥(节点统一,P1
|
|
74
|
+
## Native-teams 桥(节点统一,P1)——已弃用
|
|
75
|
+
|
|
76
|
+
> **已弃用。** `@nelsonlongxiang/dsh-native-teams` 已弃用。桥接代码仍随包发布(`src/teams-bridge.ts`;`nativeTeamsInbound` 默认 `false`),仅为既有部署保留;新部署不应依赖它。下文按其现状描述。
|
|
44
77
|
|
|
45
78
|
通往 `@nelsonlongxiang/dsh-native-teams` 的两条半桥(结构契约镜像:`src/teams-bridge.ts`;冻结契约原文在该包 `src/a2a-face.ts`):
|
|
46
79
|
|
|
@@ -68,7 +101,23 @@ dsh plugin --profile web add @nelsonlongxiang/dsh-open-a2a-net
|
|
|
68
101
|
| `wakeJoinedOnBoot` | `false` | 挂载后预热冷加入会话的 agent(需要 session controller 唤醒面;无它时路由唤醒与侧栏唤醒按钮仍可用)。预热是延迟启动、前台让路、可取消的——不会阻塞启动窗口(见下方两个参数)。 |
|
|
69
102
|
| `wakePrewarmDelayMs` | `10000` | loader 树就绪到第一次预热唤醒之间的空闲延迟;`0` 恢复就绪即唤醒的旧行为。 |
|
|
70
103
|
| `wakePrewarmQuietMs` | `5000` | 前台静默窗口:窗口内有唤醒/路由需求(或有出站路由在途)则推迟下一步预热;`0` 关闭让路。 |
|
|
71
|
-
| `
|
|
104
|
+
| `wakeBootStaggerMs` | `3000` | 启动预热串行唤醒之间的停顿(压住重启后的日志解码饱和;路由唤醒不排此队)。 |
|
|
105
|
+
| `wakeReconcile` | `true` | 持续的期望态对账:每个 tick 重新推导到期的冷加入意图,每轮唤醒一个(覆盖启动后漂移、唤醒失败、晚挂载的网关)。由 `wakeReconcileIntervalMs`(`60000`)、`wakeReconcileBackoffBaseMs`(`5000`)、`wakeReconcileMaxBackoffMs`(`600000`)调节。 |
|
|
106
|
+
| `teamJoinAllowlist` | `[]` | `a2a_team_join` 可声明的团队:**只认精确名**(`*`/前缀条目不授权任何加入)。空列表拒绝一切加入。属主也可在工作台属主面板(`/__dsh_a2a/owner-settings`)编辑。 |
|
|
107
|
+
| `teamScopeRouting` | `true` | S3 准入:路由检查调用方与目标是否共享团队;未声明任何团队的会话没有网络。`false` 恢复开放出站缝。 |
|
|
108
|
+
| `teamScopeInfo` | `true` | S5 信息面作用域,作用于 `a2a_teams` / `a2a_status` / `a2a_probe`(见工具一览)。`false` 恢复全舰队披露。 |
|
|
109
|
+
| `peerAllowlist` | `[]` | 非空时,引荐只有在主机匹配某条目时才被接纳(精确 `host[:port]`、裸域名后缀或完整 URL)。种子从不过滤。 |
|
|
110
|
+
| `referralLearning` | `true` | `false` 把跟踪集冻结为配置的种子:既不学习也不提供引荐。 |
|
|
111
|
+
| `referralTeamResolution` | `false` | gh#8:团队名只在种子 + 委托上解析;`true` 恢复旧的全 store 基础(含引荐学到的 peers)。 |
|
|
112
|
+
| `exposureExcerpt` | `'full'` | 卡片发布会话活动摘录的程度:`full`、`masked`(仅形状前缀)或 `off`。标题与句柄始终保留。 |
|
|
113
|
+
| `originClaimWindow` | `true` | OriginClaim 过渡窗口:不带信封的投递仍走调用方门(计入 `legacyInbound`);`false` 则按验签失败拒绝。 |
|
|
114
|
+
| `originClaimUrl` | `''` | 信封 `callerUrl` 的覆盖值(pocket 隧道宿主用);`''` 自动推导。 |
|
|
115
|
+
| `gateAgentLessCalls` | `'audit'` | 宿主侧(无 agent)路由调用:审计,或 `refuse` 拒绝。 |
|
|
116
|
+
| `asyncNudgeDelayMs` | `120000` | 已投递但未被消费的异步路由,在此延迟后用一行催办重新唤醒目标;`0` 关闭。 |
|
|
117
|
+
| `taskStaleTtlMs` | `86400000` | 欠账任务的 stale TTL(24h):过期后 pending 行自动转死信(迟到回执仍可结清)。 |
|
|
118
|
+
| `dshHome` | `''` | 插件状态所用的 DSH home 目录覆盖(`<home>/a2a/…`);`''` 解析默认 home。 |
|
|
119
|
+
| `cardTtlMs` / `flushTimeoutMs` / `routeTimeoutMs` | 见 schema | 卡片生命周期(48h)与路由时序预算。 |
|
|
120
|
+
| `cardCacheTtlMs` / `cardCacheNegativeTtlMs` / `remoteRowsTtlMs` / `stateColdRowsTtlMs` | `60000` / `30000` / `15000` / `5000` | peer 卡片缓存(命中/未命中)、远端会话行刷新窗口、状态面冷行刷新窗口。 |
|
|
72
121
|
|
|
73
122
|
示例——一个带种子的通告节点:
|
|
74
123
|
|
|
@@ -80,12 +129,39 @@ dsh plugin --profile web add @nelsonlongxiang/dsh-open-a2a-net
|
|
|
80
129
|
peers: ['http://127.0.0.1:41243']
|
|
81
130
|
```
|
|
82
131
|
|
|
132
|
+
## 退出语义(有界的陈旧期)
|
|
133
|
+
|
|
134
|
+
`leave` 是按会话、按宿主的动作:在本地移除加入意图并卸载节点。**没有全网撤销广播**——peers 会继续列出该团队,直到它们缓存的本节点卡片刷新(`cardCacheTtlMs`,默认 60s;`sessionTeams` 是未签名的现读字段,不受卡片自身 48h TTL 约束),这段时间内的入站投递会诚实失败(宿主已不再解析该团队)。已实现回执契约的远端目标不受影响。正式的撤销广播协议记录在上游笔记 `2026-08-27-a2a-leave-no-revocation-broadcast.md`(提案)。
|
|
135
|
+
|
|
83
136
|
## 信任模型
|
|
84
137
|
|
|
85
|
-
卡片经 Ed25519 签名且会过期;卡片上的 `peers`
|
|
138
|
+
卡片经 Ed25519 签名且会过期;卡片上的 `peers` 引荐、`sessionTeams` 列表、`lanIp` 与 `version` 未签名、每次现读——后两项让运维无需逐台登录即可核对每个舰队节点实际运行的构建。若某引荐 URL 自身的取卡刚证明它提供的就是本节点的卡片,该 URL 会被记住,抑制窗口内再次被引荐时直接忽略,不会在 peer store 里反复闪现。
|
|
139
|
+
|
|
140
|
+
每个 `/__dsh_a2a/*` 控制路由(读也算)在配置了 `apiKey` 时都要求之(`X-API-Key`,常数时间比较)。密钥为空时只信任 loopback socket;`Sec-Fetch-Site` 头只能否决、不能授权(跨站页面即使在 loopback 上也会被拒)。把监听器暴露到 loopback 之外前请先设置 `apiKey`。
|
|
141
|
+
|
|
142
|
+
| 控制路由 | 用途 |
|
|
143
|
+
| --- | --- |
|
|
144
|
+
| `GET /__dsh_a2a/state` | 面板与工作台轮询的状态面(会话、画布、peers、远端行、活动、账本)。 |
|
|
145
|
+
| `POST /__dsh_a2a/join` · `/leave` | 加入或退出一个会话节点。 |
|
|
146
|
+
| `/__dsh_a2a/canvas` · `/canvas-layout` | 画布队伍(create/remove/add-member/remove-member)与规划板布局。 |
|
|
147
|
+
| `/__dsh_a2a/groups` | 手工会话分组——自 0.6.6 起弃用(被画布队伍取代),为旧面板保留。 |
|
|
148
|
+
| `POST /__dsh_a2a/dispatch` | 工作台快速派单到一个团队。 |
|
|
149
|
+
| `POST /__dsh_a2a/tasks/abandon` · `/tasks/cancel` | 从调用方一侧结清一个欠账任务(cancel 还会向在线的本地目标转达停止通知);该行落入 `archived[]`。 |
|
|
150
|
+
| `/__dsh_a2a/session-log` · `/remote-session-log` | 供工作台读取会话日志。 |
|
|
151
|
+
| `/__dsh_a2a/owner-settings` | 属主面板:读取(GET)或写入(POST)`teamJoinAllowlist`、`teamScopeInfo` 与 `announce` 到 home patch 层(前两项即时生效;`announce` 下次重启生效)。 |
|
|
152
|
+
| `/__dsh_a2a/fleet-canvas-op` | 把画布操作中继到某 peer 的 `/a2a/canvas-op`,盖上本节点的 OriginClaim;由对端的链接门裁决。 |
|
|
153
|
+
| `/__dsh_a2a/cli-deposit` | `a2a-net` CLI 身份的寄存、列表、撤销与信箱读取。 |
|
|
154
|
+
|
|
155
|
+
状态面的账本:`tasks`(pending,每行带 `settleable`)、`tasksStaleCount`、`tasksDead`(已知时带 `direction`),以及 `archived`——最新 64 条已结清行,最新在前,每行带 `outcome`、`endedBy`(`receipt` | `echo` | `abandoned` | `cancelled`)、回执在调用方结束之后才到时带 `late: true`、已知时带 `direction`——另有 `archivedCount` 与 `archivedSplit`(按结局计数)。
|
|
156
|
+
|
|
157
|
+
## 舞台(可视化面)
|
|
158
|
+
|
|
159
|
+
两个全页舞台与模型工具并列挂载:`/__dsh_a2a_nexus`(A2A 工作台——自 0.6.2 起 2D 空间规划板即舞台,目录/台账/活动/规划面板乘可折叠侧栏,合并为一个响应式外壳;canvas 面掉线时面板回落主区;Three.js 拓扑观测在 0.6.0 退役;`?mode=plan` 仍汇聚到规划板)与 `/__dsh_a2a_canvas`。不带斜杠的挂载路径会 301 重定向到带尾斜杠形式,两种写法都能渲染,外壳的相对资源总在服务树内解析。离线开发时,`nexus-stage/scripts/mockhost.py [port]` 用预置状态服务已构建的舞台;在其上打开 `/?fault=500` 可演练页内故障徽标。新检出的仓库上 `pnpm verify` 无需手工引导:其 `verify:nexus` 步骤会自行安装子包依赖再构建。
|
|
86
160
|
|
|
87
161
|
## 协作 SOP(网络实践,v0.1)
|
|
88
162
|
|
|
163
|
+
> 现行规则见《A2A 协作规范》([`src/skill/references/collaboration.md`](src/skill/references/collaboration.md),随插件分发到每个节点)。下面的 v0.1 保留作历史,冲突处以新规范为准。
|
|
164
|
+
|
|
89
165
|
由网络研究节点从 0.1.x–0.5.x 交付周期蒸馏;维护者采纳。
|
|
90
166
|
|
|
91
167
|
- **角色** —— 一个会话一个职能,不越工作区边界:研究节点(协议/设计评审、裁决标准、带 must-fix/recommend 标签的源码行引用)、维护者节点(版本裁决、发布、采纳回执)、测试节点(所有活体验证的唯一执行点;生产宿主绝不用于临时测试)、安装/运维节点(生产 profile 部署、单一 owner、等待维护窗口)。
|
|
@@ -123,7 +199,7 @@ import { withinRouteBodyCap } from '@nelsonlongxiang/dsh-open-a2a-net/transport-
|
|
|
123
199
|
import { A2aClient } from '@nelsonlongxiang/dsh-open-a2a-net/a2a-client'
|
|
124
200
|
```
|
|
125
201
|
|
|
126
|
-
外部节点需要自己实现三样东西:签名卡片、`GET /.well-known/agent-card.json` 和 `POST /a2a/direct`,这些模块编码的线上格式就是契约。匿名取卡只能拿到最小发现面。要拿完整卡片,必须带上通过验证的 OriginClaim
|
|
202
|
+
外部节点需要自己实现三样东西:签名卡片、`GET /.well-known/agent-card.json` 和 `POST /a2a/direct`,这些模块编码的线上格式就是契约。匿名取卡只能拿到最小发现面。要拿完整卡片,必须带上通过验证的 OriginClaim 信封(即用你自己的卡片密钥签名的 `X-A2A-Caller` / `-Url` / `-Ts` / `-Sig`),**并且**与提供卡片的宿主有链接:你的卡片 URL 与它同源、你的句柄是它的已加入会话句柄之一、它的操作者把你的精确句柄加成了画布远端成员,或双方操作者互相种了对方(它的 `peers` 列有你的源,你卡片的 `peers` 也列有它的源)。验签通过但没有链接的调用方仍只拿到最小卡片。细节见 `docs/protocol/delivery-origin-auth.md` 及 `docs/protocol/` 下其余文档。
|
|
127
203
|
|
|
128
204
|
**按哪个产物实现。** npm 直装已含四个子路径:自 0.6.25 起(徽章 `latest`)的正式版带齐这批导出,`npm install @nelsonlongxiang/dsh-open-a2a-net` 开箱即用——`examples/external-node/` 有一个可对内嵌 mock peer 跑通全部四个子路径的外部节点参考实现。子路径导出随提交 `9cb3852` 落地;更早的版本(含公开 0.5.15)早于这批导出,也早于上述准入规则和 OriginClaim 信封,从它们引入会报 `ERR_PACKAGE_PATH_NOT_EXPORTED`——历史上的做法(钉提交、`pnpm build`、`npm pack`、依赖 tarball)仅留作 0.6.25 之前部署的历史注:
|
|
129
205
|
|