@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.
Files changed (69) hide show
  1. package/README.md +87 -14
  2. package/README_zh.md +83 -7
  3. package/assets/nexusDist/assets/index-C_x3iAth.js +9 -0
  4. package/assets/nexusDist/assets/index-Dz8N80tI.css +1 -0
  5. package/assets/nexusDist/index.html +2 -2
  6. package/cli/a2a.mjs +0 -0
  7. package/cordis.patch.yml +21 -21
  8. package/lib/canvas-store.js +21 -1
  9. package/lib/client.js +64 -64
  10. package/lib/client.js.map +1 -1
  11. package/lib/faces/admin.js +755 -0
  12. package/lib/faces/canvas-notify.js +138 -0
  13. package/lib/faces/wire.js +669 -0
  14. package/lib/flood-guard.js +106 -0
  15. package/lib/index.js +200 -1392
  16. package/lib/recursion-guard.js +78 -0
  17. package/lib/runtime.js +1 -0
  18. package/lib/skill/SKILL.md +35 -9
  19. package/lib/skill/references/cli.md +24 -7
  20. package/lib/skill/references/collaboration.md +355 -0
  21. package/lib/skill/references/discovery.md +5 -2
  22. package/lib/skill/references/pitfalls.md +2 -1
  23. package/lib/skill/references/routing.md +18 -8
  24. package/lib/skill/references/setup.md +152 -0
  25. package/lib/task-ledger.js +40 -4
  26. package/lib/types/canvas-store.d.ts +10 -1
  27. package/lib/types/faces/admin.d.ts +133 -0
  28. package/lib/types/faces/canvas-notify.d.ts +74 -0
  29. package/lib/types/faces/wire.d.ts +171 -0
  30. package/lib/types/flood-guard.d.ts +29 -0
  31. package/lib/types/index.d.ts +11 -3
  32. package/lib/types/recursion-guard.d.ts +56 -0
  33. package/lib/types/runtime.d.ts +35 -0
  34. package/lib/types/task-ledger.d.ts +15 -1
  35. package/nexus-stage/package-lock.json +2262 -2262
  36. package/nexus-stage/src/canvas-ops.ts +175 -175
  37. package/nexus-stage/src/canvas-wire.ts +156 -156
  38. package/nexus-stage/src/edges.ts +72 -72
  39. package/nexus-stage/src/federation-overlay.ts +103 -103
  40. package/nexus-stage/src/federation.ts +240 -240
  41. package/nexus-stage/src/layout-doc.ts +143 -143
  42. package/nexus-stage/src/layout-wire.ts +158 -158
  43. package/nexus-stage/src/main.ts +5 -1
  44. package/nexus-stage/src/planning-card-text.ts +73 -0
  45. package/nexus-stage/src/planning-edges.ts +26 -0
  46. package/nexus-stage/src/planning-fit.ts +45 -0
  47. package/nexus-stage/src/planning-legend.ts +38 -0
  48. package/nexus-stage/src/planning-mirror.ts +31 -0
  49. package/nexus-stage/src/planning-overlap.ts +159 -0
  50. package/nexus-stage/src/planning-relayout.ts +121 -0
  51. package/nexus-stage/src/planning-search.ts +168 -0
  52. package/nexus-stage/src/planning-types.ts +1 -0
  53. package/nexus-stage/src/planning-undo.ts +68 -0
  54. package/nexus-stage/src/planning-view.ts +120 -121
  55. package/nexus-stage/src/planning.css +41 -8
  56. package/nexus-stage/src/seat.ts +35 -35
  57. package/nexus-stage/src/stage-name.ts +13 -13
  58. package/nexus-stage/src/tokens.ts +31 -31
  59. package/nexus-stage/src/viewport.ts +24 -1
  60. package/nexus-stage/src/wb-chat.ts +20 -6
  61. package/nexus-stage/src/wb-ledger.ts +20 -7
  62. package/nexus-stage/src/wb-names.ts +43 -0
  63. package/nexus-stage/src/workbench.css +8 -2
  64. package/nexus-stage/src/workbench.ts +55 -17
  65. package/nexus-stage/src/world.ts +13 -21
  66. package/package.json +216 -214
  67. package/assets/nexusDist/assets/index-1niOBYxp.js +0 -5
  68. package/assets/nexusDist/assets/index-uQf7NwjF.css +0 -1
  69. package/nexus-stage/scripts/__pycache__/mockhost.cpython-312.pyc +0 -0
@@ -0,0 +1,171 @@
1
+ /**
2
+ * The /a2a/* wire faces (gh#77 batch B2; slice 2 adds log + query).
3
+ *
4
+ * Moved verbatim out of apply() in src/index.ts: the routes register when
5
+ * the web server settles, exactly as before. What used to be closure
6
+ * captures arrives through WireDeps and is destructured once below, so the
7
+ * route bodies read as they did in index.ts (the B1 admin.ts pattern). The
8
+ * one accessor deviation: the log face's `legacyInboundCount += 1` (a let in
9
+ * index.ts) goes through `d.bumpLegacyInbound()`, the same mutable-binding
10
+ * treatment admin.ts gives its six lets. direct stays inline for slice 3.
11
+ * @module src/faces/wire
12
+ */
13
+ import type { IncomingMessage, ServerResponse } from 'node:http';
14
+ import type { Context } from '@deepseek-ai/cordis';
15
+ import type { Agent } from '@deepseek-ai/dsh-agent';
16
+ import type WebServer from '@deepseek-ai/dsh-host-webserver';
17
+ import type { A2aPeerCard } from '../types.ts';
18
+ import { CanvasStore } from '../canvas-store.ts';
19
+ import type { CliDepositStore } from '../cli-deposit.ts';
20
+ import type { IdempotencyStore } from '../idempotency-store.ts';
21
+ import type { JoinedSessions } from '../joined-store.ts';
22
+ import { type TaskLedger } from '../task-ledger.ts';
23
+ import type { Config } from '../index.ts';
24
+ /** The cold-wake answer (mirrors index.ts's local ColdWake). */
25
+ export type ColdWake = {
26
+ readonly kind: 'flight';
27
+ readonly flight: Promise<Agent>;
28
+ } | {
29
+ readonly kind: 'no-face';
30
+ } | {
31
+ readonly kind: 'archived';
32
+ } | {
33
+ readonly kind: 'no-match';
34
+ };
35
+ /** routeIntoAgent's settled answer (mirrors index.ts's local union). */
36
+ export type RouteOutcome = {
37
+ ok: true;
38
+ reply: string;
39
+ status?: string;
40
+ bridge?: 'native-teams';
41
+ placeholder?: boolean;
42
+ } | {
43
+ ok: false;
44
+ error: string;
45
+ };
46
+ interface BridgeTeamsRegistryMirror {
47
+ listTeams(): Array<{
48
+ name: string;
49
+ description: string;
50
+ }>;
51
+ describeTarget(handle: string): Promise<{
52
+ plane: 'local' | 'a2a';
53
+ ambiguous?: boolean;
54
+ localLabel?: string;
55
+ }>;
56
+ startRound(args: {
57
+ team?: string;
58
+ message: string;
59
+ }, parent: {
60
+ id: string;
61
+ session: {
62
+ events?: readonly unknown[];
63
+ };
64
+ }, signal: AbortSignal): Promise<string>;
65
+ }
66
+ /** nativeTeamsPrepare's answer (mirrors index.ts's local union). */
67
+ export type NativePrepared = {
68
+ readonly ok: true;
69
+ readonly teams: BridgeTeamsRegistryMirror;
70
+ readonly parent: {
71
+ id: string;
72
+ session: {
73
+ events?: readonly unknown[];
74
+ };
75
+ };
76
+ } | {
77
+ readonly ok: false;
78
+ readonly reason: 'not-claimed';
79
+ } | {
80
+ readonly ok: false;
81
+ readonly reason: 'error';
82
+ readonly error: string;
83
+ };
84
+ /** nativeTeamsRound's settled answer (mirrors index.ts's local union). */
85
+ export type NativeRoundOutcome = {
86
+ readonly ok: true;
87
+ readonly reply: string;
88
+ readonly settled: boolean;
89
+ } | {
90
+ readonly ok: false;
91
+ readonly error: string;
92
+ readonly phase?: 'pre-dispatch';
93
+ readonly aborted?: boolean;
94
+ };
95
+ /** What verifyOriginClaim answers (see index.ts; reason widens to string here — the faces only interpolate it). */
96
+ export type OriginClaimResult = {
97
+ ok: true;
98
+ caller: string;
99
+ callerUrl: string;
100
+ card: A2aPeerCard;
101
+ } | {
102
+ ok: false;
103
+ reason: string;
104
+ };
105
+ /** The closure captures the wire faces read (grows with each extracted face). */
106
+ export interface WireDeps {
107
+ readonly ctx: Context;
108
+ readonly logger: ReturnType<Context['logger']>;
109
+ readonly config: Config;
110
+ readonly cliDeposits: CliDepositStore;
111
+ readonly canvasStore: CanvasStore;
112
+ readonly sessionNodes: Map<string, Agent>;
113
+ readonly joinedSessions: JoinedSessions;
114
+ readonly idempotencyStore: IdempotencyStore;
115
+ readonly verifyOriginClaim: (req: IncomingMessage) => Promise<OriginClaimResult>;
116
+ readonly callerLinkedToThisHost: (caller: string, callerUrl: string, callerCard: A2aPeerCard | undefined) => boolean;
117
+ readonly recordActivity: (dir: 'in' | 'out', team: string, peer: string, ok: boolean) => void;
118
+ readonly bumpLegacyInbound: () => void;
119
+ readonly secretEqual: (a: string, b: string) => boolean;
120
+ readonly a2aCallerRefusal: (caller: string, targetTeam?: string) => string | undefined;
121
+ readonly scanSessionLogList: () => Array<{
122
+ id: string;
123
+ mtimeMs: number;
124
+ sizeBytes: number;
125
+ }>;
126
+ readonly readSessionLogTail: (id: string, limit: number) => {
127
+ ok: true;
128
+ id: string;
129
+ messages: Array<{
130
+ role: string;
131
+ text: string;
132
+ time?: number;
133
+ }>;
134
+ } | {
135
+ ok: false;
136
+ error: string;
137
+ };
138
+ readonly taskLedger: TaskLedger;
139
+ readonly refuseIfFlooded: (caller: string, message: string, team: string, taskId: string, res: ServerResponse) => boolean;
140
+ readonly settleAndAnnounce: (message: string) => void;
141
+ readonly touchWakeDemand: () => void;
142
+ readonly session: string;
143
+ readonly resolveAgentForTeam: (team: string) => Agent | undefined;
144
+ readonly parseCanvasTeamName: (team: string) => string | undefined;
145
+ readonly canvasLiveAgent: (name: string | undefined, skipId?: string) => Agent | undefined;
146
+ readonly wakeColdTeam: (team: string, skipId?: string) => ColdWake;
147
+ readonly probeConsumption: (agent: Agent, taskId: string) => boolean;
148
+ readonly steerRelay: (agent: Agent, text: string, sender?: string) => void;
149
+ readonly fromClause: (from: string) => string;
150
+ readonly armReceiptAutosend: (target: Agent, taskId: string, callerTeam: string | undefined, deliveredText: string) => void;
151
+ readonly receiptExpectationOf: (receiptTarget: string) => 'receivable' | 'process-label';
152
+ readonly armAsyncNudge: (agent: Agent, team: string, taskId: string, delayMs: number) => void;
153
+ readonly liveAgent: () => Agent | undefined;
154
+ readonly callerIsAgent: (agent: Agent, caller: string) => boolean;
155
+ readonly selfRouteText: (team: string) => string;
156
+ readonly noWakeFaceText: (team: string) => string;
157
+ readonly archivedColdMatchText: (team: string) => string;
158
+ readonly noColdMatchText: (team: string) => string;
159
+ readonly nativeTeamsPrepare: (team: string) => Promise<NativePrepared>;
160
+ readonly nativeTeamsRound: (prepared: Extract<NativePrepared, {
161
+ ok: true;
162
+ }>, team: string, caller: string, message: string, taskId: string | undefined) => Promise<NativeRoundOutcome>;
163
+ readonly routeIntoAgent: (team: string, message: string, caller: string, taskId?: string) => Promise<RouteOutcome>;
164
+ }
165
+ /**
166
+ * Register the small wire faces on a settled web server.
167
+ * @param webServer - the settled host web server.
168
+ * @param d - the captures listed on {@link WireDeps}.
169
+ */
170
+ export declare function registerWireFaces(webServer: WebServer, d: WireDeps): void;
171
+ export {};
@@ -0,0 +1,29 @@
1
+ /** gh#91 transitional ingress brakes. One guard per apply(), never process-global.
2
+ * Run AFTER admission, OriginClaim and the fresh idempotency claim, BEFORE
3
+ * settlement/mailbox/steer. Replays keep 409 and spend no delivery quota.
4
+ * All refusals carry a stable reason, task id, failed query outcome and an
5
+ * activity event. This is not the independent file channel or deployment.
6
+ */
7
+ import type { ServerResponse } from 'node:http';
8
+ import type { IdempotencyStore } from './idempotency-store.ts';
9
+ export declare const WIRE_ERROR_RATE_LIMITED = -32008;
10
+ export declare const MAX_PARTS_PER_TRANSFER = 16;
11
+ export declare const DEFAULT_RATE_PER_MINUTE = 16;
12
+ type Reason = 'sender-rate-limited' | 'file-parts-exceeded' | 'file-part-invalid' | 'file-part-duplicate' | 'flood-state-full';
13
+ export declare class FloodGuard {
14
+ private readonly now;
15
+ private readonly senders;
16
+ private readonly transfers;
17
+ constructor(now?: () => number);
18
+ /** Check/admit one fresh task. No timer; maps and per-key sets are bounded.
19
+ * Active transfer entries NEVER evict to make space (which would reset a
20
+ * transfer's allowance); capacity exhaustion refuses visibly instead.
21
+ * Counts are boot-local: restart resets them, disclosed in the guide.
22
+ */
23
+ check(caller: string, message: string): Reason | undefined;
24
+ }
25
+ /** Host-scoped adapter; rejected fresh keys are FAILED, not stranded pending.
26
+ * HTTP 200 retains the existing client's structured peer-error path.
27
+ */
28
+ export declare function createFloodGuard(outcomes: Pick<IdempotencyStore, 'recordOutcome'>, recordActivity: (dir: 'in' | 'out', team: string, peer: string, ok: boolean) => void): (caller: string, message: string, team: string, taskId: string, res: ServerResponse) => boolean;
29
+ export {};
@@ -191,11 +191,19 @@ export interface Config {
191
191
  /** Backoff ceiling; corrupt-log rows park as needs-repair and never retry. */
192
192
  readonly wakeReconcileMaxBackoffMs: number;
193
193
  /**
194
- * S4: joinable-team patterns for a2a_team_join — exact names or
195
- * trailing-`*` prefixes. Empty list denies every join (default).
194
+ * S4 (gh#11 N7, operator directive: team creation is a human act): the
195
+ * joinable-team curation list for a2a_team_join. ONLY an EXACT entry
196
+ * authorizes a join; trailing-`*` pattern entries stay readable here
197
+ * (whoami shows them) but carry no mint rights — a pattern that
198
+ * pre-authorized every future name under it would let a model mint teams
199
+ * nobody created. Empty list denies every join (default).
196
200
  */
197
201
  readonly teamJoinAllowlist: string[];
198
- /** S3 (queued): team-scoped routing admission. Enforcement off by default. */
202
+ /**
203
+ * S3 (phase-2 default-ON, owner ruling 2026-09-01): route dispatch checks
204
+ * caller↔target shared team membership before addressing; set false to
205
+ * restore the open outbound seam. (Kept in sync with the schema comment.)
206
+ */
199
207
  readonly teamScopeRouting: boolean;
200
208
  /**
201
209
  * S5 (owner directive 2026-10-20): information-plane scoping — the
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Recursion guard (#60, first step: the hop budget) per
3
+ * docs/protocol/recursion-guard.md. State rides the payload: the origin
4
+ * mints the envelope with a full budget, every forwarded leg decrements,
5
+ * and a zero-budget arrival is refused with a STRUCTURED budget-exhausted
6
+ * error — silent swallowing is forbidden. The visited set (protocol §4.3)
7
+ * is the SECOND step and is deliberately absent here.
8
+ *
9
+ * Wire form: the X-A2A-Guard header carries a base64 JSON payload
10
+ * { visitToken, hopBudget, deadlineAt? }. An ABSENT header means a
11
+ * non-forwarding origin: the receiver grants the default one-shot budget
12
+ * and records guardMode 'default' (protocol §6 — old peers are
13
+ * constrained, not broken). Parse cost is O(1) for the budget path.
14
+ * @module @nelsonlongxiang/dsh-open-a2a-net/recursion-guard
15
+ */
16
+ export declare const DEFAULT_HOP_BUDGET = 4;
17
+ /** Wire code for a zero-budget arrival (cluster-C registry, after -32006). */
18
+ export declare const WIRE_ERROR_BUDGET_EXHAUSTED = -32007;
19
+ /** The guard payload as it travels on the wire. */
20
+ export interface RecursionGuard {
21
+ readonly visitToken: string;
22
+ readonly hopBudget: number;
23
+ readonly deadlineAt?: number;
24
+ }
25
+ export type GuardVerdict = {
26
+ readonly kind: 'ok';
27
+ readonly guard: RecursionGuard;
28
+ readonly mode: 'explicit' | 'default';
29
+ } | {
30
+ readonly kind: 'budget-exhausted';
31
+ };
32
+ /**
33
+ * Ingress check: parse the header, validate the budget, fail closed. A
34
+ * malformed payload falls back to the default budget (a guard that cannot
35
+ * be evaluated must not reject honest first-hop traffic; the budget bound
36
+ * still applies). hopBudget <= 0 on arrival ⇒ budget-exhausted.
37
+ */
38
+ export declare function checkGuardAtIngress(header: string | undefined): GuardVerdict;
39
+ /** Mint the outbound guard for a first-hop dispatch (full budget at origin). */
40
+ export declare function mintGuard(): RecursionGuard;
41
+ /** Serialize for the X-A2A-Guard header. */
42
+ export declare function guardHeader(guard: RecursionGuard): string;
43
+ /**
44
+ * The ingress refusal path (gh#60): budget-exhausted answers the structured
45
+ * error, records the receiptless ledger row, and reports true (the caller
46
+ * returns). The whole response lives here so the ingress seam stays ONE
47
+ * line (the index.ts ratchet pays-as-it-goes: no net line growth).
48
+ */
49
+ export declare function refuseIfBudgetExhausted(req: {
50
+ headers: Record<string, unknown>;
51
+ }, res: {
52
+ writeHead: (status: number, headers: Record<string, string | number>) => void;
53
+ end: (body: string) => void;
54
+ }, team: string, caller: string, taskId: string, ledger: {
55
+ trackReceiptless: (taskId: string, team: string, peer: string, contextId?: string, brief?: string) => void;
56
+ }): boolean;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Shared A2A runtime types (gh#77): shapes that used to be declared inside
3
+ * `apply()` and are now read by the face modules as well.
4
+ * @module src/runtime
5
+ */
6
+ import type { A2aPeerCard } from './types.ts';
7
+ /**
8
+ * Recent routing activity for the network panel: a bounded ring of the
9
+ * latest inbound and outbound route outcomes, newest last. The state route
10
+ * serves a copy; the panel derives unread badges from new entries.
11
+ */
12
+ export interface RouteActivityEntry {
13
+ readonly ts: number;
14
+ readonly dir: 'in' | 'out';
15
+ readonly team: string;
16
+ readonly peer: string;
17
+ readonly ok: boolean;
18
+ }
19
+ /**
20
+ * In-flight outbound routes: a route registers on dial and unregisters on
21
+ * settlement, so the panel can show collaboration in progress ("who is
22
+ * waiting on whom") — the state route serves a snapshot.
23
+ */
24
+ export interface InFlightRoute {
25
+ readonly id: number;
26
+ readonly team: string;
27
+ readonly peer: string;
28
+ readonly startedAt: number;
29
+ }
30
+ /** One peer-card cache entry: when it was fetched and what came back. */
31
+ export interface CardCacheEntry {
32
+ at: number;
33
+ card: A2aPeerCard | undefined;
34
+ missAt?: number;
35
+ }
@@ -53,7 +53,7 @@ export type TaskStatus = 'pending' | 'dead';
53
53
  /** F5 layer-3: why a dead row aged out (stamped by the sweep at flip time). */
54
54
  export type DeadReason = 'fire-forget' | 'unconsumed';
55
55
  /** gh#31: which archive path ended a row — a receipt, a content echo, or the caller. */
56
- export type ArchiveEnd = 'receipt' | 'echo' | 'abandoned' | 'cancelled';
56
+ export type ArchiveEnd = 'receipt' | 'echo' | 'abandoned' | 'cancelled' | 'evicted';
57
57
  /** One tracked outbound task still in the owed book (unsettled). */
58
58
  export interface TaskRecord {
59
59
  readonly taskId: string;
@@ -120,6 +120,12 @@ export interface ArchivedRecord {
120
120
  readonly endedBy?: ArchiveEnd;
121
121
  /** gh#31: which side of the wire the row served, copied from the owed row (absent on rows archived before the field). */
122
122
  readonly direction?: 'inbound' | 'outbound';
123
+ /**
124
+ * gh#68: copied from the owed row when it awaited no receipt, so a tidied
125
+ * fire-forget expiry is not read as a send we gave up on. Only `false` is
126
+ * ever stored; absent means a receipt was owed (or the row predates the field).
127
+ */
128
+ readonly receiptExpected?: false;
123
129
  }
124
130
  /**
125
131
  * How one archived row ended (gh#31). The recorded field wins; legacy rows
@@ -317,6 +323,14 @@ export declare class TaskLedger {
317
323
  resolveEcho(message: string): string[];
318
324
  /** Move one settled record to the archive head, trimming at the cap. */
319
325
  private archiveSettled;
326
+ /**
327
+ * gh#86 (chief order direct-4f596524): when the owed book exceeds
328
+ * TASK_CAP, the overflow rows go to the ARCHIVE as evicted - visible
329
+ * for audit, never silently dropped. The review seat's recommendation,
330
+ * chief-endorsed: the silent cap slice lost the oldest pending rows
331
+ * with no trace. L8 criterion: zero silently-dropped.
332
+ */
333
+ private trimOwed;
320
334
  /** Lazy dead-letter sweep: overdue pending rows flip status, at most once. */
321
335
  private sweep;
322
336
  /** Load a persisted snapshot on construction, if present. */