@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
@@ -1,175 +1,175 @@
1
- /**
2
- * Canvas action algebra for PR C (design.md §3.3/§3.4): the five user-facing
3
- * write actions as optimistic model mutations that each return a TEAM-SCOPED
4
- * undo. Scoped undos are the feedback loop's core - a whole-state revert
5
- * would clobber other queued actions' optimistic state; a per-team undo only
6
- * ever touches the team whose write failed, so queued actions on other teams
7
- * survive, and undo state (captured at apply time) stays consistent with the
8
- * per-team serial settle order.
9
- *
10
- * Pure: no DOM, no three, no fetch. The wire (canvas-wire.ts) owns transport;
11
- * the view owns calling applyAction before emitting and undo() on failure.
12
- * @module nexus-stage/canvas-ops
13
- */
14
-
15
- import { nodeRect, deriveInitialFrame, type WorldModel } from './world'
16
-
17
- /** One pre-computed host op (remove = remove-member, add = add-member). */
18
- export type RosterOp = { op: 'remove' | 'add'; id: string }
19
-
20
- /** The five user-facing write actions (one per design.md §3.3 row), plus the two network join/leave actions the panel-slim migration moved into the planning view. */
21
- export type CanvasAction =
22
- | { type: 'create-team'; name: string; ids: readonly string[]; /** true when the model had no such team before this action (a serial failure then leaves a ghost EMPTY team on the host — the wire compensates with a remove). */ created?: boolean }
23
- | { type: 'add-member'; team: string; ids: readonly string[]; /** gh#41: set when the target frame is a REMOTE peer's team — the wire routes the op through the host fleet relay. */ fleetPeer?: string }
24
- | { type: 'remove-member'; team: string; ids: readonly string[]; /** gh#41: see add-member. */ fleetPeer?: string }
25
- | { type: 'remove-team'; name: string }
26
- /** `ops` comes from reorderOps() at emit time; the wire stays model-free. */
27
- | { type: 'reorder'; team: string; ops: ReadonlyArray<RosterOp> }
28
- /** Network-level join/leave (the session joins the A2A mesh) — distinct from canvas-team membership. */
29
- | { type: 'join-network'; id: string }
30
- | { type: 'leave-network'; id: string }
31
-
32
- /** Reverts exactly what one applyAction did (team-scoped). */
33
- export type Undo = () => void
34
-
35
- /** The team whose writes this action touches (queue key for the wire). */
36
- export function actionTeam(a: CanvasAction): string {
37
- // Network join/leave are session-scoped, not team-scoped: one shared
38
- // queue key keeps them serial against each other.
39
- if (a.type === 'join-network' || a.type === 'leave-network') return '$network'
40
- return a.type === 'create-team' || a.type === 'remove-team' ? a.name : a.team
41
- }
42
-
43
- /**
44
- * Apply one action's optimistic mutation and return its undo. The undo is
45
- * expressed as the INVERSE DELTA against the roster at undo time, never as
46
- * a wholesale restore of a captured prefix - a later queued action on the
47
- * same team may have applied optimistically before this action's write
48
- * fails, and rolling back must remove only this action's effect.
49
- */
50
- export function applyAction(model: WorldModel, a: CanvasAction): Undo {
51
- if (a.type === 'create-team') {
52
- const rects = a.ids
53
- .map(id => model.getNode(id))
54
- .filter(n => n !== undefined)
55
- .map(n => nodeRect(n))
56
- // Ids not on the canvas yet (a create emitted from the node list for an
57
- // unteamed session) resolve no rect: skip the optimistic frame so the
58
- // next poll derives it from the member's real seat instead of pinning a
59
- // 260x160 placeholder at the origin.
60
- if (rects.length > 0) model.setFrame(a.name, deriveInitialFrame(rects))
61
- model.setTeamMembers(a.name, a.ids)
62
- return () => {
63
- // Only the created ids (a later add to this new team must survive).
64
- model.setTeamMembers(a.name, model.teamMemberIds(a.name).filter(id => !a.ids.includes(id)))
65
- model.removeFrame(a.name)
66
- }
67
- }
68
- if (a.type === 'add-member') {
69
- const pre = model.teamMemberIds(a.team)
70
- const fresh = a.ids.filter(id => !pre.includes(id))
71
- model.setTeamMembers(a.team, [...pre, ...fresh])
72
- return () => {
73
- model.setTeamMembers(a.team, model.teamMemberIds(a.team).filter(id => !fresh.includes(id)))
74
- }
75
- }
76
- if (a.type === 'remove-member') {
77
- const pre = model.teamMemberIds(a.team)
78
- model.setTeamMembers(a.team, pre.filter(id => !a.ids.includes(id)))
79
- return () => {
80
- // Re-append the removed ids that nothing else has re-added since.
81
- const current = model.teamMemberIds(a.team)
82
- model.setTeamMembers(a.team, [...current, ...a.ids.filter(id => !current.includes(id))])
83
- }
84
- }
85
- if (a.type === 'remove-team') {
86
- const preIds = model.teamMemberIds(a.name)
87
- const frame = model.getFrame(a.name)
88
- const preRect = frame !== undefined ? { ...frame } : undefined
89
- model.setTeamMembers(a.name, [])
90
- model.removeFrame(a.name)
91
- return () => {
92
- if (preRect !== undefined) model.setFrame(a.name, preRect)
93
- model.setTeamMembers(a.name, preIds)
94
- }
95
- }
96
- // Network join/leave: no optimistic canvas mutation — the next inventory
97
- // poll absorbs the host's truth (the node appears/disappears on the
98
- // reconcile after the host accepts), so the undo is a no-op.
99
- if (a.type === 'join-network' || a.type === 'leave-network') {
100
- return () => {}
101
- }
102
- // reorder: apply the pre-computed ops to the current roster (remove
103
- // anywhere, append). The undo WALKS the current roster back to the
104
- // captured pre-order (same append-host discipline), so members a later
105
- // queued action added stay put.
106
- const pre = model.teamMemberIds(a.team)
107
- let roster = [...pre]
108
- for (const op of a.ops) {
109
- if (op.op === 'remove') roster = roster.filter(x => x !== op.id)
110
- else roster.push(op.id)
111
- }
112
- model.setTeamMembers(a.team, roster)
113
- return () => {
114
- let back = model.teamMemberIds(a.team)
115
- for (let i = 0; i < pre.length; i++) {
116
- const id = pre[i]!
117
- if (back[i] === id) continue
118
- if (!back.includes(id)) continue
119
- back = back.filter(x => x !== id)
120
- back.push(id)
121
- }
122
- model.setTeamMembers(a.team, back)
123
- }
124
- }
125
-
126
- /**
127
- * The minimal remove+add walk that turns `current` into `desired` on a
128
- * host whose add-member always APPENDS: walk desired positions in order;
129
- * whenever the working copy disagrees, remove+re-add the desired member
130
- * (host-side remove is positional, add appends, so the working copy mirrors
131
- * the host roster exactly). Bounded at 2*|desired| writes and converges;
132
- * an already-equal roster yields zero ops; desired ids absent from current
133
- * are skipped (adding members is never reorder's business).
134
- */
135
- export function reorderOps(
136
- current: ReadonlyArray<string>,
137
- desired: ReadonlyArray<string>,
138
- ): Array<{ op: 'remove' | 'add'; id: string }> {
139
- const working = [...current]
140
- const ops: Array<{ op: 'remove' | 'add'; id: string }> = []
141
- for (let i = 0; i < desired.length; i++) {
142
- const id = desired[i]!
143
- if (working[i] === id) continue
144
- if (!working.includes(id)) continue // not a member: not reorder's business
145
- ops.push({ op: 'remove', id })
146
- ops.push({ op: 'add', id })
147
- working.splice(working.indexOf(id), 1)
148
- working.push(id)
149
- }
150
- return ops
151
- }
152
-
153
- /** That team's members ordered by card-center y (ties: current priority). */
154
- export function yOrderedMembers(model: WorldModel, team: string): string[] {
155
- return model
156
- .teamMemberIds(team)
157
- .map(id => ({ id, y: model.getNode(id)?.y ?? 0 }))
158
- .sort((a, b) => a.y - b.y)
159
- .map(r => r.id)
160
- }
161
-
162
- /** Innermost (smallest-area) frame containing the world point, if any. */
163
- export function innermostFrameAt(
164
- frames: ReadonlyMap<string, { x: number; y: number; w: number; h: number }>,
165
- p: { x: number; y: number },
166
- ): string | undefined {
167
- let best: string | undefined
168
- let bestArea = Number.POSITIVE_INFINITY
169
- for (const [name, r] of frames) {
170
- if (p.x < r.x || p.x > r.x + r.w || p.y < r.y || p.y > r.y + r.h) continue
171
- const area = r.w * r.h
172
- if (area < bestArea) { best = name; bestArea = area }
173
- }
174
- return best
175
- }
1
+ /**
2
+ * Canvas action algebra for PR C (design.md §3.3/§3.4): the five user-facing
3
+ * write actions as optimistic model mutations that each return a TEAM-SCOPED
4
+ * undo. Scoped undos are the feedback loop's core - a whole-state revert
5
+ * would clobber other queued actions' optimistic state; a per-team undo only
6
+ * ever touches the team whose write failed, so queued actions on other teams
7
+ * survive, and undo state (captured at apply time) stays consistent with the
8
+ * per-team serial settle order.
9
+ *
10
+ * Pure: no DOM, no three, no fetch. The wire (canvas-wire.ts) owns transport;
11
+ * the view owns calling applyAction before emitting and undo() on failure.
12
+ * @module nexus-stage/canvas-ops
13
+ */
14
+
15
+ import { nodeRect, deriveInitialFrame, type WorldModel } from './world'
16
+
17
+ /** One pre-computed host op (remove = remove-member, add = add-member). */
18
+ export type RosterOp = { op: 'remove' | 'add'; id: string }
19
+
20
+ /** The five user-facing write actions (one per design.md §3.3 row), plus the two network join/leave actions the panel-slim migration moved into the planning view. */
21
+ export type CanvasAction =
22
+ | { type: 'create-team'; name: string; ids: readonly string[]; /** true when the model had no such team before this action (a serial failure then leaves a ghost EMPTY team on the host — the wire compensates with a remove). */ created?: boolean }
23
+ | { type: 'add-member'; team: string; ids: readonly string[]; /** gh#41: set when the target frame is a REMOTE peer's team — the wire routes the op through the host fleet relay. */ fleetPeer?: string }
24
+ | { type: 'remove-member'; team: string; ids: readonly string[]; /** gh#41: see add-member. */ fleetPeer?: string }
25
+ | { type: 'remove-team'; name: string }
26
+ /** `ops` comes from reorderOps() at emit time; the wire stays model-free. */
27
+ | { type: 'reorder'; team: string; ops: ReadonlyArray<RosterOp> }
28
+ /** Network-level join/leave (the session joins the A2A mesh) — distinct from canvas-team membership. */
29
+ | { type: 'join-network'; id: string }
30
+ | { type: 'leave-network'; id: string }
31
+
32
+ /** Reverts exactly what one applyAction did (team-scoped). */
33
+ export type Undo = () => void
34
+
35
+ /** The team whose writes this action touches (queue key for the wire). */
36
+ export function actionTeam(a: CanvasAction): string {
37
+ // Network join/leave are session-scoped, not team-scoped: one shared
38
+ // queue key keeps them serial against each other.
39
+ if (a.type === 'join-network' || a.type === 'leave-network') return '$network'
40
+ return a.type === 'create-team' || a.type === 'remove-team' ? a.name : a.team
41
+ }
42
+
43
+ /**
44
+ * Apply one action's optimistic mutation and return its undo. The undo is
45
+ * expressed as the INVERSE DELTA against the roster at undo time, never as
46
+ * a wholesale restore of a captured prefix - a later queued action on the
47
+ * same team may have applied optimistically before this action's write
48
+ * fails, and rolling back must remove only this action's effect.
49
+ */
50
+ export function applyAction(model: WorldModel, a: CanvasAction): Undo {
51
+ if (a.type === 'create-team') {
52
+ const rects = a.ids
53
+ .map(id => model.getNode(id))
54
+ .filter(n => n !== undefined)
55
+ .map(n => nodeRect(n))
56
+ // Ids not on the canvas yet (a create emitted from the node list for an
57
+ // unteamed session) resolve no rect: skip the optimistic frame so the
58
+ // next poll derives it from the member's real seat instead of pinning a
59
+ // 260x160 placeholder at the origin.
60
+ if (rects.length > 0) model.setFrame(a.name, deriveInitialFrame(rects))
61
+ model.setTeamMembers(a.name, a.ids)
62
+ return () => {
63
+ // Only the created ids (a later add to this new team must survive).
64
+ model.setTeamMembers(a.name, model.teamMemberIds(a.name).filter(id => !a.ids.includes(id)))
65
+ model.removeFrame(a.name)
66
+ }
67
+ }
68
+ if (a.type === 'add-member') {
69
+ const pre = model.teamMemberIds(a.team)
70
+ const fresh = a.ids.filter(id => !pre.includes(id))
71
+ model.setTeamMembers(a.team, [...pre, ...fresh])
72
+ return () => {
73
+ model.setTeamMembers(a.team, model.teamMemberIds(a.team).filter(id => !fresh.includes(id)))
74
+ }
75
+ }
76
+ if (a.type === 'remove-member') {
77
+ const pre = model.teamMemberIds(a.team)
78
+ model.setTeamMembers(a.team, pre.filter(id => !a.ids.includes(id)))
79
+ return () => {
80
+ // Re-append the removed ids that nothing else has re-added since.
81
+ const current = model.teamMemberIds(a.team)
82
+ model.setTeamMembers(a.team, [...current, ...a.ids.filter(id => !current.includes(id))])
83
+ }
84
+ }
85
+ if (a.type === 'remove-team') {
86
+ const preIds = model.teamMemberIds(a.name)
87
+ const frame = model.getFrame(a.name)
88
+ const preRect = frame !== undefined ? { ...frame } : undefined
89
+ model.setTeamMembers(a.name, [])
90
+ model.removeFrame(a.name)
91
+ return () => {
92
+ if (preRect !== undefined) model.setFrame(a.name, preRect)
93
+ model.setTeamMembers(a.name, preIds)
94
+ }
95
+ }
96
+ // Network join/leave: no optimistic canvas mutation — the next inventory
97
+ // poll absorbs the host's truth (the node appears/disappears on the
98
+ // reconcile after the host accepts), so the undo is a no-op.
99
+ if (a.type === 'join-network' || a.type === 'leave-network') {
100
+ return () => {}
101
+ }
102
+ // reorder: apply the pre-computed ops to the current roster (remove
103
+ // anywhere, append). The undo WALKS the current roster back to the
104
+ // captured pre-order (same append-host discipline), so members a later
105
+ // queued action added stay put.
106
+ const pre = model.teamMemberIds(a.team)
107
+ let roster = [...pre]
108
+ for (const op of a.ops) {
109
+ if (op.op === 'remove') roster = roster.filter(x => x !== op.id)
110
+ else roster.push(op.id)
111
+ }
112
+ model.setTeamMembers(a.team, roster)
113
+ return () => {
114
+ let back = model.teamMemberIds(a.team)
115
+ for (let i = 0; i < pre.length; i++) {
116
+ const id = pre[i]!
117
+ if (back[i] === id) continue
118
+ if (!back.includes(id)) continue
119
+ back = back.filter(x => x !== id)
120
+ back.push(id)
121
+ }
122
+ model.setTeamMembers(a.team, back)
123
+ }
124
+ }
125
+
126
+ /**
127
+ * The minimal remove+add walk that turns `current` into `desired` on a
128
+ * host whose add-member always APPENDS: walk desired positions in order;
129
+ * whenever the working copy disagrees, remove+re-add the desired member
130
+ * (host-side remove is positional, add appends, so the working copy mirrors
131
+ * the host roster exactly). Bounded at 2*|desired| writes and converges;
132
+ * an already-equal roster yields zero ops; desired ids absent from current
133
+ * are skipped (adding members is never reorder's business).
134
+ */
135
+ export function reorderOps(
136
+ current: ReadonlyArray<string>,
137
+ desired: ReadonlyArray<string>,
138
+ ): Array<{ op: 'remove' | 'add'; id: string }> {
139
+ const working = [...current]
140
+ const ops: Array<{ op: 'remove' | 'add'; id: string }> = []
141
+ for (let i = 0; i < desired.length; i++) {
142
+ const id = desired[i]!
143
+ if (working[i] === id) continue
144
+ if (!working.includes(id)) continue // not a member: not reorder's business
145
+ ops.push({ op: 'remove', id })
146
+ ops.push({ op: 'add', id })
147
+ working.splice(working.indexOf(id), 1)
148
+ working.push(id)
149
+ }
150
+ return ops
151
+ }
152
+
153
+ /** That team's members ordered by card-center y (ties: current priority). */
154
+ export function yOrderedMembers(model: WorldModel, team: string): string[] {
155
+ return model
156
+ .teamMemberIds(team)
157
+ .map(id => ({ id, y: model.getNode(id)?.y ?? 0 }))
158
+ .sort((a, b) => a.y - b.y)
159
+ .map(r => r.id)
160
+ }
161
+
162
+ /** Innermost (smallest-area) frame containing the world point, if any. */
163
+ export function innermostFrameAt(
164
+ frames: ReadonlyMap<string, { x: number; y: number; w: number; h: number }>,
165
+ p: { x: number; y: number },
166
+ ): string | undefined {
167
+ let best: string | undefined
168
+ let bestArea = Number.POSITIVE_INFINITY
169
+ for (const [name, r] of frames) {
170
+ if (p.x < r.x || p.x > r.x + r.w || p.y < r.y || p.y > r.y + r.h) continue
171
+ const area = r.w * r.h
172
+ if (area < bestArea) { best = name; bestArea = area }
173
+ }
174
+ return best
175
+ }