@nelsonlongxiang/dsh-open-a2a-net 0.5.14 → 0.6.24
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 +243 -121
- package/README_zh.md +144 -84
- package/assets/nexusDist/assets/index-034h1cNc.js +3 -0
- package/assets/nexusDist/assets/index-PG9EieGX.css +1 -0
- package/assets/nexusDist/index.html +26 -0
- package/assets/stageDist/assets/index-Bj7S2pA1.css +1 -0
- package/assets/stageDist/assets/index-C-28k_o5.js +974 -0
- package/assets/stageDist/assets/interiors-CQGBjffT.png +0 -0
- package/assets/stageDist/assets/office-tileset-CHQT-uYL.png +0 -0
- package/assets/stageDist/index.html +13 -0
- package/lib/a2a-client.js +190 -13
- package/lib/canvas-store.js +218 -0
- package/lib/card.js +25 -0
- package/lib/client/A2aControl.js +45 -137
- package/lib/client/locales.js +16 -40
- package/lib/client.js +164 -467
- package/lib/client.js.map +1 -1
- package/lib/group-store.js +6 -0
- package/lib/idempotency-store.js +291 -0
- package/lib/index.js +4217 -259
- package/lib/joined-store.js +11 -0
- package/lib/layout-store.js +155 -0
- package/lib/peer-store.js +98 -13
- package/lib/receipt-ladder.js +35 -0
- package/lib/receipt.js +102 -0
- package/lib/self-suppress.js +62 -0
- package/lib/session-transcript.js +150 -0
- package/lib/skill/SKILL.md +77 -0
- package/lib/skill/references/discovery.md +38 -0
- package/lib/skill/references/membership.md +50 -0
- package/lib/skill/references/pitfalls.md +42 -0
- package/lib/skill/references/routing.md +43 -0
- package/lib/skill-registration.js +52 -0
- package/lib/stage-mount.js +32 -0
- package/lib/task-ledger.js +540 -0
- package/lib/team-store.js +122 -0
- package/lib/teams-bridge.js +18 -0
- package/lib/transport-caps.js +31 -0
- package/lib/types/a2a-client.d.ts +72 -1
- package/lib/types/canvas-store.d.ts +102 -0
- package/lib/types/client/A2aControl.d.ts +18 -28
- package/lib/types/client/locales.d.ts +1 -1
- package/lib/types/group-store.d.ts +6 -0
- package/lib/types/idempotency-store.d.ts +164 -0
- package/lib/types/index.d.ts +210 -6
- package/lib/types/joined-store.d.ts +9 -0
- package/lib/types/layout-store.d.ts +63 -0
- package/lib/types/peer-store.d.ts +45 -2
- package/lib/types/receipt-ladder.d.ts +25 -0
- package/lib/types/receipt.d.ts +51 -0
- package/lib/types/self-suppress.d.ts +41 -0
- package/lib/types/session-transcript.d.ts +37 -0
- package/lib/types/skill-registration.d.ts +3 -0
- package/lib/types/stage-mount.d.ts +29 -0
- package/lib/types/task-ledger.d.ts +308 -0
- package/lib/types/team-store.d.ts +55 -0
- package/lib/types/teams-bridge.d.ts +112 -0
- package/lib/types/transport-caps.d.ts +29 -0
- package/lib/types/types.d.ts +43 -0
- package/nexus-stage/package-lock.json +2262 -0
- package/nexus-stage/package.json +21 -0
- package/nexus-stage/scripts/__pycache__/mockhost.cpython-312.pyc +0 -0
- package/nexus-stage/scripts/mockhost.py +253 -0
- package/nexus-stage/src/canvas-ops.ts +175 -0
- package/nexus-stage/src/canvas-wire.ts +156 -0
- package/nexus-stage/src/edges.ts +72 -0
- package/nexus-stage/src/fault.ts +56 -0
- package/nexus-stage/src/federation.ts +234 -0
- package/nexus-stage/src/layout-doc.ts +143 -0
- package/nexus-stage/src/layout-wire.ts +158 -0
- package/nexus-stage/src/main.ts +361 -0
- package/nexus-stage/src/planning-menus.ts +352 -0
- package/nexus-stage/src/planning-nodelist.ts +294 -0
- package/nexus-stage/src/planning-types.ts +111 -0
- package/nexus-stage/src/planning-view.ts +1313 -0
- package/nexus-stage/src/planning.css +280 -0
- package/nexus-stage/src/seat.ts +35 -0
- package/nexus-stage/src/stage-name.ts +13 -0
- package/nexus-stage/src/tokens.ts +32 -0
- package/nexus-stage/src/viewport.ts +83 -0
- package/nexus-stage/src/workbench.css +267 -0
- package/nexus-stage/src/workbench.ts +1530 -0
- package/nexus-stage/src/world.ts +509 -0
- package/nexus-stage/tsconfig.json +25 -0
- package/nexus-stage/vite.config.ts +16 -0
- package/package.json +83 -22
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport-level wire caps shared by both directions of the direct-route
|
|
3
|
+
* surface. B5 enforcement (network ruling, 2026-08-27): an oversized payload
|
|
4
|
+
* is REJECTED, never truncated — rejection is structured on the inbound half
|
|
5
|
+
* (HTTP 413 + a JSON wire error code) and fail-fast on the outbound half
|
|
6
|
+
* (no bytes leave the process, no doomed upload burns the 15s HTTP budget).
|
|
7
|
+
* @module @nelsonlongxiang/dsh-open-a2a-net/transport-caps
|
|
8
|
+
*/
|
|
9
|
+
/** Hard ceiling on one `/a2a/direct` request body, in UTF-8 bytes (512 KiB). */
|
|
10
|
+
export const MAX_ROUTE_BODY_BYTES = 512 * 1024;
|
|
11
|
+
/**
|
|
12
|
+
* Hard ceiling on one control-route layout save (`POST
|
|
13
|
+
* /__dsh_a2a/canvas-layout`), in UTF-8 bytes (64 KiB). Sized from the
|
|
14
|
+
* legitimate document envelope: LayoutStore clamps to 256 nodes (keys
|
|
15
|
+
* ≤ 128 chars) + 96 frames (names ≤ 40 chars), whose worst case is
|
|
16
|
+
* ≈50 KiB — the historical shared 10 KiB control cap rejected real
|
|
17
|
+
* full-fleet documents (256 nodes ≈ 23 KiB) outright, so the layout
|
|
18
|
+
* route overrides with this cap while every other control route keeps
|
|
19
|
+
* the 10 KiB default.
|
|
20
|
+
*/
|
|
21
|
+
export const MAX_LAYOUT_BODY_BYTES = 64 * 1024;
|
|
22
|
+
/**
|
|
23
|
+
* Wire error code for a body above {@link MAX_ROUTE_BODY_BYTES}. First member
|
|
24
|
+
* of the transport rejection family (the enum registry lives in
|
|
25
|
+
* docs/protocol/delivery-origin-auth.md until it earns its own module).
|
|
26
|
+
*/
|
|
27
|
+
export const WIRE_ERROR_PAYLOAD_TOO_LARGE = -32001;
|
|
28
|
+
/** Whether a body of this byte length may cross the wire. */
|
|
29
|
+
export function withinRouteBodyCap(byteLength) {
|
|
30
|
+
return byteLength <= MAX_ROUTE_BODY_BYTES;
|
|
31
|
+
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* tests drive them without a network.
|
|
6
6
|
* @module @nelsonlongxiang/dsh-open-a2a-net/a2a-client
|
|
7
7
|
*/
|
|
8
|
+
import { type CardRejection } from './card.ts';
|
|
8
9
|
import type { A2aPeerCard, A2aRouteResult } from './types.ts';
|
|
9
10
|
/** One-shot delayed callback returning its disposer. */
|
|
10
11
|
export type A2aSchedule = (callback: () => void, delayMs: number) => () => void;
|
|
@@ -25,11 +26,43 @@ export interface A2aClientOptions {
|
|
|
25
26
|
readonly apiKey: string;
|
|
26
27
|
/** This node's caller label stamped on outbound routes. */
|
|
27
28
|
readonly sessionId: string;
|
|
29
|
+
/**
|
|
30
|
+
* Zone handle stamped as `X-A2A-Caller` on card GETs (gh#7 G1): peers
|
|
31
|
+
* serve the FULL card only to named callers — anonymous fetchers get the
|
|
32
|
+
* minimal discovery card without sessionTeams/teamMemberships/lanIp.
|
|
33
|
+
* Empty omits the header (anonymous posture).
|
|
34
|
+
*/
|
|
35
|
+
readonly cardCaller: string;
|
|
36
|
+
/**
|
|
37
|
+
* gh#9+gh#10 OriginClaim signer (docs/protocol/delivery-origin-auth.md):
|
|
38
|
+
* produces the four envelope headers with the NODE'S CARD KEY. Present ⇒
|
|
39
|
+
* every card GET and direct POST carries a verifiable origin claim;
|
|
40
|
+
* absent ⇒ the caller stays at the named-only posture (transition).
|
|
41
|
+
*/
|
|
42
|
+
readonly originClaim?: () => Record<string, string>;
|
|
28
43
|
/** Delayed-callback seam (HTTP timeouts). */
|
|
29
44
|
readonly schedule: A2aSchedule;
|
|
30
45
|
/** HTTP seam. */
|
|
31
46
|
readonly fetch: A2aFetch;
|
|
32
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* One card fetch with its failure stage kept: a peer that cannot be reached
|
|
50
|
+
* (transport or HTTP error) is a different diagnosis from one that answers
|
|
51
|
+
* with a card verification rejects — probes report the difference, plain
|
|
52
|
+
* discovery collapses both to `undefined`.
|
|
53
|
+
*/
|
|
54
|
+
export type CardFetchOutcome = {
|
|
55
|
+
readonly ok: true;
|
|
56
|
+
readonly card: A2aPeerCard;
|
|
57
|
+
} | {
|
|
58
|
+
readonly ok: false;
|
|
59
|
+
readonly stage: 'unreachable';
|
|
60
|
+
readonly detail: string;
|
|
61
|
+
} | {
|
|
62
|
+
readonly ok: false;
|
|
63
|
+
readonly stage: 'rejected';
|
|
64
|
+
readonly reason: CardRejection;
|
|
65
|
+
};
|
|
33
66
|
/**
|
|
34
67
|
* The A2A peer client. One instance serves the whole fiber's outbound
|
|
35
68
|
* card fetches and direct routes; it owns no connection state.
|
|
@@ -49,6 +82,14 @@ export declare class A2aClient {
|
|
|
49
82
|
* @returns the verified card, or `undefined` when unreachable, malformed, or rejected.
|
|
50
83
|
*/
|
|
51
84
|
fetchCard(baseUrl: string): Promise<A2aPeerCard | undefined>;
|
|
85
|
+
/**
|
|
86
|
+
* The diagnosing half of {@link fetchCard}: the same fetch and verification
|
|
87
|
+
* with the failure stage preserved, so a probe can tell a down node from a
|
|
88
|
+
* distrusted card.
|
|
89
|
+
* @param baseUrl - the peer's base URL.
|
|
90
|
+
* @returns the verified card, or the failure's stage and detail.
|
|
91
|
+
*/
|
|
92
|
+
fetchCardDetail(baseUrl: string): Promise<CardFetchOutcome>;
|
|
52
93
|
/**
|
|
53
94
|
* Extract the reply text from a route result member: the wire carries a
|
|
54
95
|
* plain string, a `{text}` object, or anything else JSON-encoded.
|
|
@@ -69,5 +110,35 @@ export declare class A2aClient {
|
|
|
69
110
|
* @param callerSession - caller label overriding the node's label (the calling session's node label).
|
|
70
111
|
* @returns the canonical route result (success or explicit failure).
|
|
71
112
|
*/
|
|
72
|
-
routeDirect(baseUrl: string, team: string, message: string, contextId?: string, signal?: AbortSignal, callerSession?: string, asyncMode?: boolean, taskIdFromCaller?: string): Promise<A2aRouteResult>;
|
|
113
|
+
routeDirect(baseUrl: string, team: string, message: string, contextId?: string, signal?: AbortSignal, callerSession?: string, asyncMode?: boolean, taskIdFromCaller?: string, callbackAddress?: string): Promise<A2aRouteResult>;
|
|
114
|
+
/**
|
|
115
|
+
* Query a peer's `/a2a/query` outcome surface (W7 slice 2): read-only
|
|
116
|
+
* lookup of one claimed task's settled outcome. The fingerprint is the
|
|
117
|
+
* authorization — the caller recomputes it from the exact submit fields
|
|
118
|
+
* with the shared `peerPayloadFingerprint`. Every transport-level failure
|
|
119
|
+
* (network miss, HTTP error, unparseable or malformed body, empty fields)
|
|
120
|
+
* answers `undefined`: a query that cannot complete carries no increment
|
|
121
|
+
* of information, never a verdict.
|
|
122
|
+
*/
|
|
123
|
+
queryOutcome(baseUrl: string, taskId: string, fingerprint: string, signal?: AbortSignal): Promise<A2aQueryAnswer | undefined>;
|
|
73
124
|
}
|
|
125
|
+
/** The typed `/a2a/query` answer, 1:1 with the wire body's four states. */
|
|
126
|
+
export type A2aQueryAnswer = {
|
|
127
|
+
readonly found: false;
|
|
128
|
+
readonly reason: 'unknown-task' | 'payload-mismatch';
|
|
129
|
+
} | {
|
|
130
|
+
readonly found: true;
|
|
131
|
+
readonly status: 'pending';
|
|
132
|
+
} | {
|
|
133
|
+
readonly found: true;
|
|
134
|
+
readonly status: 'completed';
|
|
135
|
+
readonly reply: string;
|
|
136
|
+
readonly settledAt: string;
|
|
137
|
+
readonly truncated?: boolean;
|
|
138
|
+
} | {
|
|
139
|
+
readonly found: true;
|
|
140
|
+
readonly status: 'failed';
|
|
141
|
+
readonly error: string;
|
|
142
|
+
readonly settledAt: string;
|
|
143
|
+
readonly truncated?: boolean;
|
|
144
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persisted canvas teams: user-composed multi-member routing groups under
|
|
3
|
+
* the a2a home (`a2a/canvas.json`). A canvas team is a named set of member
|
|
4
|
+
* references in one of two shapes: a local session id (a joined session
|
|
5
|
+
* node on this host) or a remote team handle (`<zone>/<id8>`, a session
|
|
6
|
+
* node published by a peer — one member may sit in many teams). Routing to
|
|
7
|
+
* `<team>/canvas/<name>` resolves the first live local member, wakes the
|
|
8
|
+
* first cold local one, then falls through to the remote members in order
|
|
9
|
+
* (the directory resolves their handles; remote nodes never wake from
|
|
10
|
+
* here). Membership order is the routing priority (member list order),
|
|
11
|
+
* preserved verbatim on disk. Plain JSON, one writer (the control routes),
|
|
12
|
+
* read fresh by the state route.
|
|
13
|
+
* @module @nelsonlongxiang/dsh-open-a2a-net/canvas-store
|
|
14
|
+
*/
|
|
15
|
+
/** On-disk shape: ordered team entries, each an ordered member id list. */
|
|
16
|
+
export interface CanvasSnapshot {
|
|
17
|
+
readonly teams: ReadonlyArray<{
|
|
18
|
+
readonly name: string;
|
|
19
|
+
readonly members: readonly string[];
|
|
20
|
+
}>;
|
|
21
|
+
}
|
|
22
|
+
/** Upper bounds: a canvas taxonomy, not a database. */
|
|
23
|
+
export declare const CANVAS_TEAM_CAP = 64;
|
|
24
|
+
export declare const CANVAS_MEMBER_CAP = 32;
|
|
25
|
+
/** Longest accepted team name (same budget as group names). */
|
|
26
|
+
export declare const CANVAS_NAME_MAX = 40;
|
|
27
|
+
/**
|
|
28
|
+
* Whether one member entry references a remote node rather than a local
|
|
29
|
+
* session id. Remote refs are zone-qualified team handles (`<zone>/<id8>`),
|
|
30
|
+
* so the '/'-test is deterministic: local session ids (`session-<uuid>`,
|
|
31
|
+
* `import-<uuid>`) never contain one, and canvas team names — the only other
|
|
32
|
+
* strings in a member slot — are barred from '/' at creation.
|
|
33
|
+
*/
|
|
34
|
+
export declare function isRemoteMemberRef(id: string): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Shape-gate for a remote member reference at the control-route seam: at
|
|
37
|
+
* least one '/'-delimited segment on each side, no whitespace. The registry
|
|
38
|
+
* cross-check lives in the UI (the picker offers discovered rows only);
|
|
39
|
+
* this gate keeps malformed strings out of the persisted file.
|
|
40
|
+
*/
|
|
41
|
+
export declare function validRemoteMemberRef(id: string): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Bounded canvas-team store with last-write-wins persistence. Pure of any
|
|
44
|
+
* registry knowledge: callers validate join state and resolve members.
|
|
45
|
+
*/
|
|
46
|
+
export declare class CanvasStore {
|
|
47
|
+
/** Team entries in creation order; members in add order (routing priority). */
|
|
48
|
+
private teams;
|
|
49
|
+
private readonly path;
|
|
50
|
+
/**
|
|
51
|
+
* @param path - the snapshot file; an empty path keeps the store in memory.
|
|
52
|
+
*/
|
|
53
|
+
constructor(path: string);
|
|
54
|
+
/** Load a persisted snapshot, if present and well-formed. */
|
|
55
|
+
private restore;
|
|
56
|
+
/** Persist the current snapshot (no-op without a path). */
|
|
57
|
+
private persist;
|
|
58
|
+
/** Every team name, in creation order. */
|
|
59
|
+
list(): readonly string[];
|
|
60
|
+
/** Whether the named team exists. */
|
|
61
|
+
hasTeam(name: string): boolean;
|
|
62
|
+
/** The named team's member ids in routing-priority order (a copy). */
|
|
63
|
+
membersOf(name: string): readonly string[];
|
|
64
|
+
/**
|
|
65
|
+
* The named team's remote member refs (zone-qualified team handles) in
|
|
66
|
+
* routing-priority order — the failover tier after the local live/cold
|
|
67
|
+
* tiers miss. Shape test only: whether the handle currently resolves is
|
|
68
|
+
* the directory's question at route time, not the store's.
|
|
69
|
+
*/
|
|
70
|
+
remoteMembersOf(name: string): readonly string[];
|
|
71
|
+
/** Every team name the id is a member of, in team creation order. */
|
|
72
|
+
teamsOf(id: string): readonly string[];
|
|
73
|
+
/**
|
|
74
|
+
* Create a named team; whitespace trims, duplicates are idempotent, and
|
|
75
|
+
* the cap rejects a team beyond {@link CANVAS_TEAM_CAP}.
|
|
76
|
+
* @param name - the raw user-supplied name.
|
|
77
|
+
* @returns the stored name, or undefined when rejected.
|
|
78
|
+
*/
|
|
79
|
+
create(name: string): string | undefined;
|
|
80
|
+
/**
|
|
81
|
+
* Delete a team (with its memberships).
|
|
82
|
+
* @param name - the team name.
|
|
83
|
+
*/
|
|
84
|
+
remove(name: string): boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Add one member (idempotent; duplicates keep the original priority slot).
|
|
87
|
+
* The team must exist — callers create it first so the cap applies once.
|
|
88
|
+
* @param name - the existing team name.
|
|
89
|
+
* @param id - the member session id.
|
|
90
|
+
*/
|
|
91
|
+
addMember(name: string, id: string): boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Remove one member from one team (a no-op when absent).
|
|
94
|
+
*/
|
|
95
|
+
removeMember(name: string, id: string): boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Drop one id from every team — the leave and archive paths: a session
|
|
98
|
+
* leaving the network keeps no canvas membership anywhere.
|
|
99
|
+
* @param id - the departing session id.
|
|
100
|
+
*/
|
|
101
|
+
dropMember(id: string): void;
|
|
102
|
+
}
|
|
@@ -1,29 +1,8 @@
|
|
|
1
1
|
import type { PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
2
|
-
/** One session as the host's state route reports it. */
|
|
2
|
+
/** One session as the host's state route reports it (jump-target lookup only). */
|
|
3
3
|
export interface A2aSessionRow {
|
|
4
4
|
readonly id: string;
|
|
5
|
-
readonly label: string;
|
|
6
5
|
readonly team: string;
|
|
7
|
-
readonly name?: string;
|
|
8
|
-
readonly description?: string;
|
|
9
|
-
readonly workspace?: string;
|
|
10
|
-
readonly joined: boolean;
|
|
11
|
-
/** `false` on cold rows: the join intent is persisted but the session's agent is not loaded. */
|
|
12
|
-
readonly live?: boolean;
|
|
13
|
-
/** The user-named group this session is assigned to, if any. */
|
|
14
|
-
readonly group?: string;
|
|
15
|
-
}
|
|
16
|
-
/** One peer-side team row as the state route reports it (origin is its natural group). */
|
|
17
|
-
export interface A2aRemoteRow {
|
|
18
|
-
readonly team: string;
|
|
19
|
-
readonly name: string;
|
|
20
|
-
readonly origin?: string;
|
|
21
|
-
readonly workspace?: string;
|
|
22
|
-
}
|
|
23
|
-
/** One tracked peer as the state route reports it. */
|
|
24
|
-
export interface A2aPeerRow {
|
|
25
|
-
readonly url: string;
|
|
26
|
-
readonly score?: number;
|
|
27
6
|
}
|
|
28
7
|
/** One routing outcome as the state route reports it. */
|
|
29
8
|
export interface A2aActivityRow {
|
|
@@ -39,16 +18,27 @@ export interface A2aInFlightRow {
|
|
|
39
18
|
readonly peer: string;
|
|
40
19
|
readonly startedAt: number;
|
|
41
20
|
}
|
|
42
|
-
/**
|
|
21
|
+
/** One outbound task owed a receipt, as the state route reports it. */
|
|
22
|
+
export interface A2aTaskRow {
|
|
23
|
+
readonly taskId: string;
|
|
24
|
+
readonly team: string;
|
|
25
|
+
readonly peer: string;
|
|
26
|
+
readonly startedAt: number;
|
|
27
|
+
readonly status: string;
|
|
28
|
+
/** C-ruling: false rows are conversational noise (delivered, no formal receipt, past the stale hour). */
|
|
29
|
+
readonly settleable?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/** The state the feed renders. */
|
|
43
32
|
export interface A2aState {
|
|
33
|
+
/** Session rows exist only to resolve an activity row's jump target. */
|
|
44
34
|
readonly sessions: readonly A2aSessionRow[];
|
|
45
|
-
readonly groups: readonly string[];
|
|
46
|
-
readonly remote: readonly A2aRemoteRow[];
|
|
47
|
-
readonly peers: readonly A2aPeerRow[];
|
|
48
35
|
readonly activity: readonly A2aActivityRow[];
|
|
49
36
|
readonly inFlight: readonly A2aInFlightRow[];
|
|
37
|
+
readonly tasks: readonly A2aTaskRow[];
|
|
50
38
|
/** The host plugin package version, when the state route reports it. */
|
|
51
39
|
readonly version?: string;
|
|
40
|
+
/** C face: stale conversational tail count (delivered without formal receipt, past the stale hour). */
|
|
41
|
+
readonly tasksStaleCount?: number;
|
|
52
42
|
}
|
|
53
43
|
/** Wake face the registration injects: opens one session through the standard sessions flow. */
|
|
54
44
|
export interface A2aControlInjected {
|
|
@@ -57,8 +47,8 @@ export interface A2aControlInjected {
|
|
|
57
47
|
/** Full component props: the footer-action owner share, the locale seat, and the wake face. */
|
|
58
48
|
export type A2aControlProps = PropsRuntime<'sidebar.footer.action'> & PropsLocale<'a2aNet'> & A2aControlInjected;
|
|
59
49
|
/**
|
|
60
|
-
* Render the network toggle and its
|
|
50
|
+
* Render the network toggle and its activity feed.
|
|
61
51
|
* @param props - composed slot props.
|
|
62
52
|
* @returns the control element tree.
|
|
63
53
|
*/
|
|
64
|
-
export declare function A2aControl({ wide, t,
|
|
54
|
+
export declare function A2aControl({ wide, t, openSession }: A2aControlProps): import("react").JSX.Element;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Locale dictionaries for the A2A network sidebar control (namespace `a2aNet`). */
|
|
2
2
|
/** Dictionary keys owned by the `a2aNet` namespace. */
|
|
3
|
-
export type A2aNetKey = 'a2a.label' | 'a2a.title' | 'a2a.
|
|
3
|
+
export type A2aNetKey = 'a2a.label' | 'a2a.title' | 'a2a.activity' | 'a2a.activityEmpty' | 'a2a.inFlight' | 'a2a.inFlightStale' | 'a2a.tasks' | 'a2a.tasksNote' | 'a2a.tasksStale' | 'a2a.jump' | 'a2a.stageTitle' | 'a2a.stageWorkbench';
|
|
4
4
|
/** Simplified Chinese dictionary. */
|
|
5
5
|
export declare const zh: Record<A2aNetKey, string>;
|
|
6
6
|
/** English dictionary. */
|
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
* session listing, stored under the a2a home so they survive restarts and
|
|
4
4
|
* follow the node (not the browser). Plain JSON, one writer (the control
|
|
5
5
|
* routes), read fresh by the state route.
|
|
6
|
+
*
|
|
7
|
+
* @deprecated 0.6.6 — the unified team model supersedes manual grouping:
|
|
8
|
+
* canvas teams are the human-assigned grouping face and remote rows already
|
|
9
|
+
* cluster by publishing host (`origin`). The store stays readable so old
|
|
10
|
+
* panels keep working, but it is not developed further; use canvas teams
|
|
11
|
+
* for anything new.
|
|
6
12
|
* @module @nelsonlongxiang/dsh-open-a2a-net/group-store
|
|
7
13
|
*/
|
|
8
14
|
/** On-disk shape; assignments map a session id to its group name. */
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side idempotency key uniqueness (work-order P3 / B3 ruling).
|
|
3
|
+
*
|
|
4
|
+
* Caller-born task ids are single-use by contract; until now the server only
|
|
5
|
+
* deduped its own ledger rows. This store closes the execution-side gap: the
|
|
6
|
+
* FIRST claim of a key inside the TTL window executes, a same-key replay of
|
|
7
|
+
* the identical payload answers `replay` (the prior attempt owns whatever
|
|
8
|
+
* outcome it produced — never re-steer, never double-bill), and a same-key
|
|
9
|
+
* DIFFERENT payload is a hard `conflict`. All three verdicts are plain data;
|
|
10
|
+
* the endpoint maps them to 200/409(-32003)/409(-32002).
|
|
11
|
+
*
|
|
12
|
+
* W7 slice 2: the claim row now also RETAINS the prior attempt's settled
|
|
13
|
+
* outcome (when a settlement hook recorded one), and {@link query} answers
|
|
14
|
+
* the read-only `/a2a/query` surface — the retrieval half of the S1
|
|
15
|
+
* recovery row. The fingerprint that guards a claim is the same value that
|
|
16
|
+
* authorizes a query: presenting {@link peerPayloadFingerprint} of the
|
|
17
|
+
* original submit means holding the original payload.
|
|
18
|
+
*
|
|
19
|
+
* Persistence at `<dsh-home>/a2a/idempotency.json`; TTL prunes lazily on
|
|
20
|
+
* every entry point (no timers — this host restarts too often), capacity
|
|
21
|
+
* evicts oldest-inserted first, and every failure degrades to memory-only.
|
|
22
|
+
* Never throws: orchestrator settlement paths fire here repeatedly under
|
|
23
|
+
* conditions nobody can predict.
|
|
24
|
+
* @module @nelsonlongxiang/dsh-open-a2a-net/idempotency-store
|
|
25
|
+
*/
|
|
26
|
+
/** How long one claimed key blocks re-execution. */
|
|
27
|
+
export declare const IDEMPOTENCY_TTL_MS: number;
|
|
28
|
+
/** Capacity before oldest-inserted eviction (a window, not an archive). */
|
|
29
|
+
export declare const IDEMPOTENCY_CAP = 256;
|
|
30
|
+
/** Wire code: same key came back with a DIFFERENT payload inside the window. */
|
|
31
|
+
export declare const WIRE_ERROR_IDEMPOTENCY_CONFLICT = -32002;
|
|
32
|
+
/** Wire code: exact replay — refused so the prior run stays authoritative. */
|
|
33
|
+
export declare const WIRE_ERROR_REPLAY_REJECTED = -32003;
|
|
34
|
+
/** Cap on one stored outcome text; truncation is flagged, never silent. */
|
|
35
|
+
export declare const OUTCOME_TEXT_CAP = 65536;
|
|
36
|
+
/** The three claim verdicts; stable vocabulary per the consumer contract. */
|
|
37
|
+
export type ClaimVerdict = 'fresh' | 'replay' | 'conflict';
|
|
38
|
+
/**
|
|
39
|
+
* The payload fingerprint of one A2A submit — the shared implementation the
|
|
40
|
+
* gate claims with, the query endpoint re-verifies with, and the caller-side
|
|
41
|
+
* bridge face recomputes from the exact submit fields. ONE implementation by
|
|
42
|
+
* contract (W7 slice 2): any drift across those three roles would turn
|
|
43
|
+
* honest outcome queries into phantom payload-mismatch verdicts. The field
|
|
44
|
+
* order below IS the wire format; do not reorder.
|
|
45
|
+
*/
|
|
46
|
+
export declare function peerPayloadFingerprint(input: {
|
|
47
|
+
readonly caller: string;
|
|
48
|
+
readonly message: string;
|
|
49
|
+
readonly noWait: boolean;
|
|
50
|
+
readonly team: string;
|
|
51
|
+
}): string;
|
|
52
|
+
/** The settled outcome of the execution that earned a claim (W7 slice 2). */
|
|
53
|
+
export interface TaskOutcome {
|
|
54
|
+
readonly status: 'completed' | 'failed';
|
|
55
|
+
/** The settled round text (completed rows). */
|
|
56
|
+
readonly reply?: string;
|
|
57
|
+
/** The failure prose (failed rows). */
|
|
58
|
+
readonly error?: string;
|
|
59
|
+
/** True when the stored text was cut to {@link OUTCOME_TEXT_CAP}. */
|
|
60
|
+
readonly truncated?: boolean;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The read-only answer for one outcome lookup. `pending` honestly means
|
|
64
|
+
* "in flight OR the outcome was never registered" — the two are
|
|
65
|
+
* indistinguishable from outside, and the answer pretends otherwise for
|
|
66
|
+
* neither (old snapshots restored without outcomes, hooks that never fire
|
|
67
|
+
* for a delivery mode, all land here).
|
|
68
|
+
*/
|
|
69
|
+
export type StoredQueryVerdict = {
|
|
70
|
+
readonly found: false;
|
|
71
|
+
readonly reason: 'unknown-task' | 'payload-mismatch';
|
|
72
|
+
} | {
|
|
73
|
+
readonly found: true;
|
|
74
|
+
readonly status: 'pending';
|
|
75
|
+
} | {
|
|
76
|
+
readonly found: true;
|
|
77
|
+
readonly status: 'completed';
|
|
78
|
+
readonly reply: string;
|
|
79
|
+
/** Settle time (epoch ms); the endpoint maps it to wire ISO. */
|
|
80
|
+
readonly settledAt: number;
|
|
81
|
+
readonly truncated?: boolean;
|
|
82
|
+
} | {
|
|
83
|
+
readonly found: true;
|
|
84
|
+
readonly status: 'failed';
|
|
85
|
+
readonly error: string;
|
|
86
|
+
/** Settle time (epoch ms); the endpoint maps it to wire ISO. */
|
|
87
|
+
readonly settledAt: number;
|
|
88
|
+
readonly truncated?: boolean;
|
|
89
|
+
};
|
|
90
|
+
export interface IdempotencyOptions {
|
|
91
|
+
/** TTL override (tests inject short windows); defaults to {@link IDEMPOTENCY_TTL_MS}. */
|
|
92
|
+
ttlMs?: number;
|
|
93
|
+
/** Capacity override; defaults to {@link IDEMPOTENCY_CAP}. */
|
|
94
|
+
cap?: number;
|
|
95
|
+
/** Clock injection for deterministic expiry tests. */
|
|
96
|
+
now?: () => number;
|
|
97
|
+
}
|
|
98
|
+
/** The read-only idempotency-window aggregate (state route / a2a_status;
|
|
99
|
+
* 0.5.36 observability slice). Counters are cumulative since genesis. */
|
|
100
|
+
export interface IdempotencyStats {
|
|
101
|
+
readonly window: number;
|
|
102
|
+
readonly cap: number;
|
|
103
|
+
readonly pending: number;
|
|
104
|
+
readonly settled: number;
|
|
105
|
+
readonly claimsFresh: number;
|
|
106
|
+
readonly replays: number;
|
|
107
|
+
readonly conflicts: number;
|
|
108
|
+
}
|
|
109
|
+
export declare class IdempotencyStore {
|
|
110
|
+
private readonly path;
|
|
111
|
+
private readonly rows;
|
|
112
|
+
private readonly ttlMs;
|
|
113
|
+
private readonly cap;
|
|
114
|
+
private readonly now;
|
|
115
|
+
private claimsFresh;
|
|
116
|
+
private replays;
|
|
117
|
+
private conflicts;
|
|
118
|
+
/**
|
|
119
|
+
* @param path - persistence file; empty string keeps the store memory-only.
|
|
120
|
+
* @param options - TTL/capacity/clock overrides for tests.
|
|
121
|
+
*/
|
|
122
|
+
constructor(path: string, options?: IdempotencyOptions);
|
|
123
|
+
/**
|
|
124
|
+
* Claim or inspect one key against a payload fingerprint.
|
|
125
|
+
* @returns `'fresh'` when the key may execute now (and is recorded),
|
|
126
|
+
* `'replay'` when the identical fingerprint was already claimed inside the
|
|
127
|
+
* window, `'conflict'` when the key exists with a different fingerprint.
|
|
128
|
+
*/
|
|
129
|
+
claim(taskId: string, fingerprint: string): ClaimVerdict;
|
|
130
|
+
/**
|
|
131
|
+
* Read-only aggregate for the observability surfaces (state route /
|
|
132
|
+
* a2a_status, 0.5.36): window occupancy, outcome split, and the
|
|
133
|
+
* cumulative claim-verdict counters. Never throws; the only write it may
|
|
134
|
+
* perform is the shared lazy TTL prune.
|
|
135
|
+
*/
|
|
136
|
+
stats(): IdempotencyStats;
|
|
137
|
+
/**
|
|
138
|
+
* Attach the settled outcome to an existing claim (W7 slice 2). First
|
|
139
|
+
* write wins: the sync / detached-bridge / receipt hooks are mutually
|
|
140
|
+
* exclusive by wait semantics today, so a second write would only ever
|
|
141
|
+
* mean a future hook bug — ignore it instead of flip-flopping the record.
|
|
142
|
+
* Unknown or already-settled ids answer `false`. Never throws.
|
|
143
|
+
* @returns whether the outcome was recorded.
|
|
144
|
+
*/
|
|
145
|
+
recordOutcome(taskId: string, outcome: TaskOutcome, settledAt?: number): boolean;
|
|
146
|
+
/**
|
|
147
|
+
* Read-only outcome lookup for the `/a2a/query` surface (W7 slice 2).
|
|
148
|
+
* The caller must present the task id AND the original payload's
|
|
149
|
+
* fingerprint — the fingerprint match is the authorization, and a
|
|
150
|
+
* mismatch is a plain negative answer, never the frozen -32002 conflict
|
|
151
|
+
* vocabulary (queries do not produce conflicts). Empty or unknown ids
|
|
152
|
+
* answer unknown-task. Never claims, never throws; the only write it may
|
|
153
|
+
* perform is the shared lazy TTL prune.
|
|
154
|
+
*/
|
|
155
|
+
query(taskId: string, fingerprint: string): StoredQueryVerdict;
|
|
156
|
+
/** Whether one key currently sits inside the window (diagnostics). */
|
|
157
|
+
has(taskId: string): boolean;
|
|
158
|
+
/** Drop expired keys; runs on every entry point, never timer-driven. */
|
|
159
|
+
private prune;
|
|
160
|
+
/** Capacity guard: Map iteration order = insertion order, so first key dies. */
|
|
161
|
+
private evictOldest;
|
|
162
|
+
private restore;
|
|
163
|
+
private persist;
|
|
164
|
+
}
|