pmx-canvas 0.3.0 → 0.3.1

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 (156) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/canvas/index.js +2 -2
  3. package/dist/types/cli/daemon.d.ts +74 -0
  4. package/dist/types/cli/watch.d.ts +2 -2
  5. package/dist/types/client/canvas/CanvasViewport.d.ts +1 -1
  6. package/dist/types/client/canvas/CommandPalette.d.ts +1 -1
  7. package/dist/types/client/canvas/Minimap.d.ts +1 -1
  8. package/dist/types/client/nodes/FileNode.d.ts +1 -1
  9. package/dist/types/client/nodes/ImageNode.d.ts +1 -1
  10. package/dist/types/client/nodes/InlineFormatBar.d.ts +1 -1
  11. package/dist/types/client/nodes/MarkdownNode.d.ts +1 -1
  12. package/dist/types/client/nodes/PromptNode.d.ts +1 -1
  13. package/dist/types/client/nodes/ResponseNode.d.ts +1 -1
  14. package/dist/types/server/canvas-schema.d.ts +1 -1
  15. package/dist/types/server/html-primitives.d.ts +1 -1
  16. package/dist/types/server/index.d.ts +4 -4
  17. package/dist/types/server/operations/index.d.ts +1 -1
  18. package/dist/types/server/operations/ops/ax-read.d.ts +2 -0
  19. package/dist/types/server/operations/ops/canvas-wire.d.ts +2 -0
  20. package/dist/types/server/operations/ops/ext-app.d.ts +2 -0
  21. package/docs/http-api.md +28 -0
  22. package/docs/plans/plan-009-tech-debt-backlog.md +5 -5
  23. package/docs/tech-debt-assessment-2026-07.md +2 -2
  24. package/package.json +5 -2
  25. package/skills/pmx-canvas/SKILL.md +3 -1
  26. package/skills/pmx-canvas/references/full-reference.md +10 -3
  27. package/src/cli/agent.ts +1861 -1548
  28. package/src/cli/daemon.ts +460 -0
  29. package/src/cli/index.ts +63 -326
  30. package/src/cli/watch.ts +2 -10
  31. package/src/client/App.tsx +48 -46
  32. package/src/client/canvas/AttentionHistory.tsx +11 -1
  33. package/src/client/canvas/CanvasNode.tsx +41 -29
  34. package/src/client/canvas/CanvasViewport.tsx +101 -66
  35. package/src/client/canvas/CommandPalette.tsx +61 -27
  36. package/src/client/canvas/ContextMenu.tsx +13 -20
  37. package/src/client/canvas/ContextPinBar.tsx +1 -5
  38. package/src/client/canvas/ContextPinHud.tsx +1 -6
  39. package/src/client/canvas/DockedNode.tsx +4 -4
  40. package/src/client/canvas/EdgeLayer.tsx +37 -36
  41. package/src/client/canvas/ExpandedNodeOverlay.tsx +69 -45
  42. package/src/client/canvas/FocusFieldLayer.tsx +20 -22
  43. package/src/client/canvas/IntentLayer.tsx +31 -14
  44. package/src/client/canvas/Minimap.tsx +11 -16
  45. package/src/client/canvas/SelectionBar.tsx +4 -11
  46. package/src/client/canvas/ShortcutOverlay.tsx +3 -1
  47. package/src/client/canvas/SnapshotPanel.tsx +77 -95
  48. package/src/client/canvas/auto-fit.ts +15 -14
  49. package/src/client/canvas/snap-guides.ts +12 -12
  50. package/src/client/canvas/use-node-resize.ts +1 -5
  51. package/src/client/canvas/use-pan-zoom.ts +25 -26
  52. package/src/client/ext-app/bridge.ts +3 -12
  53. package/src/client/icons.tsx +63 -20
  54. package/src/client/nodes/ContextNode.tsx +14 -25
  55. package/src/client/nodes/ExtAppFrame.tsx +60 -39
  56. package/src/client/nodes/FileNode.tsx +74 -62
  57. package/src/client/nodes/GroupNode.tsx +4 -6
  58. package/src/client/nodes/HtmlNode.tsx +76 -46
  59. package/src/client/nodes/ImageNode.tsx +18 -27
  60. package/src/client/nodes/InlineFormatBar.tsx +4 -21
  61. package/src/client/nodes/InlineMarkdownEditor.tsx +0 -1
  62. package/src/client/nodes/LedgerNode.tsx +10 -4
  63. package/src/client/nodes/MarkdownNode.tsx +3 -10
  64. package/src/client/nodes/McpAppNode.tsx +26 -22
  65. package/src/client/nodes/MdFormatBar.tsx +10 -7
  66. package/src/client/nodes/PromptNode.tsx +23 -51
  67. package/src/client/nodes/ResponseNode.tsx +3 -13
  68. package/src/client/nodes/StatusNode.tsx +5 -9
  69. package/src/client/nodes/StatusSummary.tsx +2 -8
  70. package/src/client/nodes/WebpageNode.tsx +20 -14
  71. package/src/client/nodes/iframe-document-url.ts +25 -16
  72. package/src/client/nodes/image-warnings.ts +1 -7
  73. package/src/client/nodes/md-format.ts +20 -5
  74. package/src/client/state/attention-bridge.ts +4 -9
  75. package/src/client/state/attention-store.ts +1 -7
  76. package/src/client/state/canvas-store.ts +52 -36
  77. package/src/client/state/intent-bridge.ts +176 -112
  78. package/src/client/state/intent-store.ts +4 -1
  79. package/src/client/state/sse-bridge.ts +53 -70
  80. package/src/json-render/catalog.ts +12 -16
  81. package/src/json-render/charts/components.tsx +16 -20
  82. package/src/json-render/charts/extra-components.tsx +8 -16
  83. package/src/json-render/charts/extra-definitions.ts +1 -2
  84. package/src/json-render/charts/tufte-components.tsx +37 -20
  85. package/src/json-render/charts/tufte-definitions.ts +8 -2
  86. package/src/json-render/renderer/index.tsx +42 -22
  87. package/src/json-render/schema.ts +6 -3
  88. package/src/json-render/server.ts +33 -39
  89. package/src/mcp/canvas-access.ts +35 -21
  90. package/src/mcp/server.ts +132 -70
  91. package/src/server/agent-context.ts +63 -36
  92. package/src/server/ax-context.ts +7 -5
  93. package/src/server/ax-interaction.ts +176 -43
  94. package/src/server/ax-state-manager.ts +182 -39
  95. package/src/server/ax-state.ts +142 -47
  96. package/src/server/canvas-db.ts +213 -95
  97. package/src/server/canvas-operations.ts +177 -120
  98. package/src/server/canvas-provenance.ts +1 -4
  99. package/src/server/canvas-schema.ts +454 -73
  100. package/src/server/canvas-serialization.ts +27 -35
  101. package/src/server/canvas-state.ts +150 -58
  102. package/src/server/chart-template.ts +4 -5
  103. package/src/server/code-graph.ts +19 -6
  104. package/src/server/diagram-presets.ts +28 -29
  105. package/src/server/ext-app-lookup.ts +3 -12
  106. package/src/server/html-node-summary.ts +19 -10
  107. package/src/server/html-primitives.ts +326 -97
  108. package/src/server/html-surface.ts +6 -9
  109. package/src/server/image-source.ts +6 -4
  110. package/src/server/index.ts +320 -217
  111. package/src/server/intent-registry.ts +2 -5
  112. package/src/server/mcp-app-candidate.ts +5 -10
  113. package/src/server/mcp-app-host.ts +14 -38
  114. package/src/server/mcp-app-runtime.ts +12 -20
  115. package/src/server/mutation-history.ts +15 -5
  116. package/src/server/operations/composites.ts +1 -3
  117. package/src/server/operations/http.ts +2 -3
  118. package/src/server/operations/index.ts +7 -1
  119. package/src/server/operations/invoker.ts +4 -3
  120. package/src/server/operations/mcp.ts +22 -30
  121. package/src/server/operations/ops/annotation.ts +122 -10
  122. package/src/server/operations/ops/app.ts +98 -73
  123. package/src/server/operations/ops/ax-await.ts +17 -10
  124. package/src/server/operations/ops/ax-read.ts +347 -0
  125. package/src/server/operations/ops/ax-shared.ts +2 -7
  126. package/src/server/operations/ops/ax-state.ts +32 -14
  127. package/src/server/operations/ops/ax-timeline.ts +32 -19
  128. package/src/server/operations/ops/ax-work.ts +54 -37
  129. package/src/server/operations/ops/batch.ts +39 -14
  130. package/src/server/operations/ops/canvas-wire.ts +91 -0
  131. package/src/server/operations/ops/edges.ts +37 -25
  132. package/src/server/operations/ops/ext-app.ts +346 -0
  133. package/src/server/operations/ops/groups.ts +49 -20
  134. package/src/server/operations/ops/intent.ts +18 -12
  135. package/src/server/operations/ops/json-render.ts +239 -98
  136. package/src/server/operations/ops/nodes.ts +298 -109
  137. package/src/server/operations/ops/query.ts +46 -28
  138. package/src/server/operations/ops/snapshots.ts +35 -26
  139. package/src/server/operations/ops/validate.ts +2 -1
  140. package/src/server/operations/ops/viewport.ts +60 -16
  141. package/src/server/operations/ops/webview.ts +44 -18
  142. package/src/server/operations/registry.ts +2 -3
  143. package/src/server/operations/types.ts +7 -5
  144. package/src/server/operations/webview-runner.ts +1 -3
  145. package/src/server/placement.ts +8 -18
  146. package/src/server/server.ts +108 -1027
  147. package/src/server/spatial-analysis.ts +39 -25
  148. package/src/server/trace-manager.ts +3 -8
  149. package/src/server/web-artifacts.ts +23 -27
  150. package/src/server/webpage-node.ts +5 -13
  151. package/src/shared/auto-arrange.ts +12 -5
  152. package/src/shared/content-height-reporter.ts +8 -6
  153. package/src/shared/ext-app-tool-result.ts +2 -6
  154. package/src/shared/placement.ts +1 -4
  155. package/src/shared/semantic-attention.ts +39 -37
  156. package/src/shared/surface.ts +8 -4
@@ -86,11 +86,29 @@ function caps(
86
86
  * a node can anchor AX state but only eligible types may EMIT interactions.
87
87
  */
88
88
  export const DEFAULT_NODE_AX_CAPABILITIES: Record<CanvasNodeType, NodeAxCapabilities> = {
89
- markdown: caps(true, ['ax.steer', 'ax.work.create', 'ax.evidence.add', 'ax.command.invoke', 'ax.event.record'], 'notify-agent'),
90
- context: caps(true, ['ax.focus.set', 'ax.steer', 'ax.evidence.add', 'ax.command.invoke', 'ax.event.record'], 'notify-agent'),
91
- status: caps(true, ['ax.work.create', 'ax.work.update', 'ax.approval.request', 'ax.mode.request', 'ax.event.record'], 'notify-agent'),
89
+ markdown: caps(
90
+ true,
91
+ ['ax.steer', 'ax.work.create', 'ax.evidence.add', 'ax.command.invoke', 'ax.event.record'],
92
+ 'notify-agent',
93
+ ),
94
+ context: caps(
95
+ true,
96
+ ['ax.focus.set', 'ax.steer', 'ax.evidence.add', 'ax.command.invoke', 'ax.event.record'],
97
+ 'notify-agent',
98
+ ),
99
+ status: caps(
100
+ true,
101
+ ['ax.work.create', 'ax.work.update', 'ax.approval.request', 'ax.mode.request', 'ax.event.record'],
102
+ 'notify-agent',
103
+ ),
92
104
  file: caps(true, ['ax.evidence.add', 'ax.review.add', 'ax.focus.set', 'ax.event.record']),
93
- 'json-render': caps(true, ['ax.work.create', 'ax.work.update', 'ax.evidence.add', 'ax.elicitation.request', 'ax.event.record']),
105
+ 'json-render': caps(true, [
106
+ 'ax.work.create',
107
+ 'ax.work.update',
108
+ 'ax.evidence.add',
109
+ 'ax.elicitation.request',
110
+ 'ax.event.record',
111
+ ]),
94
112
  graph: caps(true, ['ax.evidence.add', 'ax.focus.set', 'ax.event.record']),
95
113
  ledger: caps(true, ['ax.evidence.add', 'ax.event.record']),
96
114
  trace: caps(true, ['ax.evidence.add', 'ax.event.record']),
@@ -123,8 +141,12 @@ export const DEFAULT_NODE_AX_CAPABILITIES: Record<CanvasNodeType, NodeAxCapabili
123
141
  // itself, and request human input. Excludes higher-trust types (steer, approval,
124
142
  // review, command, mode) which stay native-control / adapter only.
125
143
  'mcp-app': caps(false, [
126
- 'ax.event.record', 'ax.evidence.add', 'ax.work.create', 'ax.work.update',
127
- 'ax.focus.set', 'ax.elicitation.request',
144
+ 'ax.event.record',
145
+ 'ax.evidence.add',
146
+ 'ax.work.create',
147
+ 'ax.work.update',
148
+ 'ax.focus.set',
149
+ 'ax.elicitation.request',
128
150
  ]),
129
151
  // Internal thread nodes — anchor only, no human-facing emission by default.
130
152
  prompt: caps(false, ['ax.event.record']),
@@ -141,11 +163,13 @@ export function normalizeNodeAxCapabilities(value: unknown): Partial<NodeAxCapab
141
163
  if (typeof v.enabled === 'boolean') out.enabled = v.enabled;
142
164
  if (Array.isArray(v.allowed)) {
143
165
  out.allowed = v.allowed.filter((a): a is AxInteractionType =>
144
- AX_INTERACTION_TYPES.includes(a as AxInteractionType));
166
+ AX_INTERACTION_TYPES.includes(a as AxInteractionType),
167
+ );
145
168
  }
146
169
  if (Array.isArray(v.requiresApproval)) {
147
170
  out.requiresApproval = v.requiresApproval.filter((a): a is AxInteractionType =>
148
- AX_INTERACTION_TYPES.includes(a as AxInteractionType));
171
+ AX_INTERACTION_TYPES.includes(a as AxInteractionType),
172
+ );
149
173
  }
150
174
  if (v.delivery === 'record-only' || v.delivery === 'notify-agent' || v.delivery === 'send-to-agent') {
151
175
  out.delivery = v.delivery;
@@ -172,7 +196,15 @@ export function resolveNodeAxCapabilities(node: CanvasNodeState): NodeAxCapabili
172
196
 
173
197
  // ── Envelope + payload validation ──────────────────────────────
174
198
 
175
- const EVENT_KINDS = ['prompt', 'assistant-message', 'tool-start', 'tool-result', 'failure', 'approval', 'steering'] as const;
199
+ const EVENT_KINDS = [
200
+ 'prompt',
201
+ 'assistant-message',
202
+ 'tool-start',
203
+ 'tool-result',
204
+ 'failure',
205
+ 'approval',
206
+ 'steering',
207
+ ] as const;
176
208
  const EVIDENCE_KINDS = ['logs', 'tool-result', 'screenshot', 'file', 'diff', 'test-output'] as const;
177
209
  const WORK_STATUSES = ['todo', 'in-progress', 'blocked', 'done', 'cancelled'] as const;
178
210
  const REVIEW_KINDS = ['comment', 'finding'] as const;
@@ -284,7 +316,13 @@ const PAYLOAD_SCHEMAS: Record<string, z.ZodType> = {
284
316
  export interface AxInteractionManager {
285
317
  getNode(id: string): CanvasNodeState | undefined;
286
318
  recordAxEvent(
287
- input: { kind: PmxAxEventKind; summary: string; detail?: string | null; nodeIds?: string[]; data?: Record<string, unknown> | null },
319
+ input: {
320
+ kind: PmxAxEventKind;
321
+ summary: string;
322
+ detail?: string | null;
323
+ nodeIds?: string[];
324
+ data?: Record<string, unknown> | null;
325
+ },
288
326
  options?: { source?: PmxAxSource },
289
327
  ): PmxAxEvent;
290
328
  recordSteeringMessage(message: string, options?: { source?: PmxAxSource }): PmxAxSteeringMessage;
@@ -298,7 +336,14 @@ export interface AxInteractionManager {
298
336
  options?: { source?: PmxAxSource },
299
337
  ): PmxAxWorkItem | null;
300
338
  addEvidence(
301
- input: { kind: PmxAxEvidenceKind; title: string; body?: string | null; ref?: string | null; nodeIds?: string[]; data?: Record<string, unknown> | null },
339
+ input: {
340
+ kind: PmxAxEvidenceKind;
341
+ title: string;
342
+ body?: string | null;
343
+ ref?: string | null;
344
+ nodeIds?: string[];
345
+ data?: Record<string, unknown> | null;
346
+ },
302
347
  options?: { source?: PmxAxSource },
303
348
  ): PmxAxEvidence;
304
349
  requestApproval(
@@ -332,7 +377,11 @@ export interface AxInteractionManager {
332
377
  input: { mode: PmxAxMode; reason?: string | null; nodeIds?: string[] },
333
378
  options?: { source?: PmxAxSource },
334
379
  ): PmxAxModeRequest;
335
- invokeCommand(name: string, args?: Record<string, unknown> | null, options?: { source?: PmxAxSource }): PmxAxEvent | null;
380
+ invokeCommand(
381
+ name: string,
382
+ args?: Record<string, unknown> | null,
383
+ options?: { source?: PmxAxSource },
384
+ ): PmxAxEvent | null;
336
385
  }
337
386
 
338
387
  export interface AxInteractionEvent {
@@ -353,13 +402,7 @@ function outcomeEvent(extra: Record<string, unknown>): AxInteractionEvent {
353
402
  return { event: 'ax-interaction', payload: extra };
354
403
  }
355
404
 
356
- function reject(
357
- type: string,
358
- sourceNodeId: string,
359
- status: number,
360
- code: string,
361
- error: string,
362
- ): AxInteractionResult {
405
+ function reject(type: string, sourceNodeId: string, status: number, code: string, error: string): AxInteractionResult {
363
406
  return {
364
407
  result: { ok: false, status, code, error },
365
408
  events: [outcomeEvent({ ok: false, type, sourceNodeId, code, error })],
@@ -375,10 +418,7 @@ function accept(
375
418
  ): AxInteractionResult {
376
419
  return {
377
420
  result: { ok: true, type, sourceNodeId, primitive },
378
- events: [
379
- outcomeEvent({ ok: true, type, sourceNodeId }),
380
- { event: stateEvent, payload: statePayload },
381
- ],
421
+ events: [outcomeEvent({ ok: true, type, sourceNodeId }), { event: stateEvent, payload: statePayload }],
382
422
  };
383
423
  }
384
424
 
@@ -396,10 +436,14 @@ export function applyAxInteraction(
396
436
  const parsed = InteractionEnvelopeSchema.safeParse(rawBody);
397
437
  if (!parsed.success) {
398
438
  const error = parsed.error.issues.map((i) => `${i.path.join('.') || 'envelope'}: ${i.message}`).join('; ');
399
- const type = typeof (rawBody as { type?: unknown })?.type === 'string' ? String((rawBody as { type?: unknown }).type) : 'unknown';
400
- const sourceNodeId = typeof (rawBody as { sourceNodeId?: unknown })?.sourceNodeId === 'string'
401
- ? String((rawBody as { sourceNodeId?: unknown }).sourceNodeId)
402
- : '';
439
+ const type =
440
+ typeof (rawBody as { type?: unknown })?.type === 'string'
441
+ ? String((rawBody as { type?: unknown }).type)
442
+ : 'unknown';
443
+ const sourceNodeId =
444
+ typeof (rawBody as { sourceNodeId?: unknown })?.sourceNodeId === 'string'
445
+ ? String((rawBody as { sourceNodeId?: unknown }).sourceNodeId)
446
+ : '';
403
447
  return reject(type, sourceNodeId, 400, 'invalid-envelope', error);
404
448
  }
405
449
  const interaction = parsed.data;
@@ -410,7 +454,13 @@ export function applyAxInteraction(
410
454
 
411
455
  const capabilities = resolveNodeAxCapabilities(node);
412
456
  if (!capabilities.enabled) {
413
- return reject(type, sourceNodeId, 403, 'ax-disabled', `AX interactions are not enabled for node "${sourceNodeId}".`);
457
+ return reject(
458
+ type,
459
+ sourceNodeId,
460
+ 403,
461
+ 'ax-disabled',
462
+ `AX interactions are not enabled for node "${sourceNodeId}".`,
463
+ );
414
464
  }
415
465
  if (!capabilities.allowed.includes(type)) {
416
466
  return reject(type, sourceNodeId, 403, 'not-allowed', `Node type "${node.type}" cannot emit "${type}".`);
@@ -418,7 +468,13 @@ export function applyAxInteraction(
418
468
  // Fail closed: approval-gated interaction types are rejected until approval
419
469
  // routing lands, rather than dispatched without the gate they require.
420
470
  if (capabilities.requiresApproval.includes(type)) {
421
- return reject(type, sourceNodeId, 403, 'requires-approval', `"${type}" requires approval routing, which is not yet available.`);
471
+ return reject(
472
+ type,
473
+ sourceNodeId,
474
+ 403,
475
+ 'requires-approval',
476
+ `"${type}" requires approval routing, which is not yet available.`,
477
+ );
422
478
  }
423
479
 
424
480
  const schema = PAYLOAD_SCHEMAS[type];
@@ -446,9 +502,21 @@ export function applyAxInteraction(
446
502
 
447
503
  switch (type) {
448
504
  case 'ax.event.record': {
449
- const p = payloadParsed.data as { kind: PmxAxEventKind; summary: string; detail?: string | null; nodeIds?: string[]; data?: Record<string, unknown> | null };
505
+ const p = payloadParsed.data as {
506
+ kind: PmxAxEventKind;
507
+ summary: string;
508
+ detail?: string | null;
509
+ nodeIds?: string[];
510
+ data?: Record<string, unknown> | null;
511
+ };
450
512
  const event = manager.recordAxEvent(
451
- { kind: p.kind, summary: p.summary, detail: p.detail ?? null, nodeIds: scopedNodeIds(p.nodeIds), data: p.data ?? null },
513
+ {
514
+ kind: p.kind,
515
+ summary: p.summary,
516
+ detail: p.detail ?? null,
517
+ nodeIds: scopedNodeIds(p.nodeIds),
518
+ data: p.data ?? null,
519
+ },
452
520
  opts,
453
521
  );
454
522
  return accept(type, sourceNodeId, event, 'ax-event-created', { event });
@@ -459,15 +527,31 @@ export function applyAxInteraction(
459
527
  return accept(type, sourceNodeId, steering, 'ax-event-created', { steering });
460
528
  }
461
529
  case 'ax.work.create': {
462
- const p = payloadParsed.data as { title: string; status?: PmxAxWorkItemStatus; detail?: string | null; nodeIds?: string[] };
530
+ const p = payloadParsed.data as {
531
+ title: string;
532
+ status?: PmxAxWorkItemStatus;
533
+ detail?: string | null;
534
+ nodeIds?: string[];
535
+ };
463
536
  const workItem = manager.addWorkItem(
464
- { title: p.title, ...(p.status ? { status: p.status } : {}), ...(p.detail !== undefined ? { detail: p.detail } : {}), nodeIds: scopedNodeIds(p.nodeIds) },
537
+ {
538
+ title: p.title,
539
+ ...(p.status ? { status: p.status } : {}),
540
+ ...(p.detail !== undefined ? { detail: p.detail } : {}),
541
+ nodeIds: scopedNodeIds(p.nodeIds),
542
+ },
465
543
  opts,
466
544
  );
467
545
  return accept(type, sourceNodeId, workItem, 'ax-state-changed', { workItem });
468
546
  }
469
547
  case 'ax.work.update': {
470
- const p = payloadParsed.data as { id: string; title?: string; status?: PmxAxWorkItemStatus; detail?: string | null; nodeIds?: string[] };
548
+ const p = payloadParsed.data as {
549
+ id: string;
550
+ title?: string;
551
+ status?: PmxAxWorkItemStatus;
552
+ detail?: string | null;
553
+ nodeIds?: string[];
554
+ };
471
555
  const { id, ...patch } = p;
472
556
  if (scoped && patch.nodeIds !== undefined) patch.nodeIds = [sourceNodeId];
473
557
  const workItem = manager.updateWorkItem(id, patch, opts);
@@ -475,29 +559,71 @@ export function applyAxInteraction(
475
559
  return accept(type, sourceNodeId, workItem, 'ax-state-changed', { workItem });
476
560
  }
477
561
  case 'ax.evidence.add': {
478
- const p = payloadParsed.data as { kind: PmxAxEvidenceKind; title: string; body?: string | null; ref?: string | null; nodeIds?: string[]; data?: Record<string, unknown> | null };
562
+ const p = payloadParsed.data as {
563
+ kind: PmxAxEvidenceKind;
564
+ title: string;
565
+ body?: string | null;
566
+ ref?: string | null;
567
+ nodeIds?: string[];
568
+ data?: Record<string, unknown> | null;
569
+ };
479
570
  const evidence = manager.addEvidence(
480
- { kind: p.kind, title: p.title, body: p.body ?? null, ref: p.ref ?? null, nodeIds: scopedNodeIds(p.nodeIds), data: p.data ?? null },
571
+ {
572
+ kind: p.kind,
573
+ title: p.title,
574
+ body: p.body ?? null,
575
+ ref: p.ref ?? null,
576
+ nodeIds: scopedNodeIds(p.nodeIds),
577
+ data: p.data ?? null,
578
+ },
481
579
  opts,
482
580
  );
483
581
  return accept(type, sourceNodeId, evidence, 'ax-event-created', { evidence });
484
582
  }
485
583
  case 'ax.approval.request': {
486
- const p = payloadParsed.data as { title: string; detail?: string | null; action?: string | null; nodeIds?: string[] };
584
+ const p = payloadParsed.data as {
585
+ title: string;
586
+ detail?: string | null;
587
+ action?: string | null;
588
+ nodeIds?: string[];
589
+ };
487
590
  const approvalGate = manager.requestApproval(
488
- { title: p.title, ...(p.detail !== undefined ? { detail: p.detail } : {}), ...(p.action !== undefined ? { action: p.action } : {}), nodeIds: scopedNodeIds(p.nodeIds) },
591
+ {
592
+ title: p.title,
593
+ ...(p.detail !== undefined ? { detail: p.detail } : {}),
594
+ ...(p.action !== undefined ? { action: p.action } : {}),
595
+ nodeIds: scopedNodeIds(p.nodeIds),
596
+ },
489
597
  opts,
490
598
  );
491
599
  return accept(type, sourceNodeId, approvalGate, 'ax-state-changed', { approvalGate });
492
600
  }
493
601
  case 'ax.approval.resolve': {
494
602
  const p = payloadParsed.data as { id: string; decision: 'approved' | 'rejected'; resolution?: string };
495
- const approvalGate = manager.resolveApproval(p.id, p.decision, { ...(p.resolution !== undefined ? { resolution: p.resolution } : {}), source });
496
- if (!approvalGate) return reject(type, sourceNodeId, 404, 'approval-not-found', `Approval "${p.id}" not found or already resolved.`);
603
+ const approvalGate = manager.resolveApproval(p.id, p.decision, {
604
+ ...(p.resolution !== undefined ? { resolution: p.resolution } : {}),
605
+ source,
606
+ });
607
+ if (!approvalGate)
608
+ return reject(
609
+ type,
610
+ sourceNodeId,
611
+ 404,
612
+ 'approval-not-found',
613
+ `Approval "${p.id}" not found or already resolved.`,
614
+ );
497
615
  return accept(type, sourceNodeId, approvalGate, 'ax-state-changed', { approvalGate });
498
616
  }
499
617
  case 'ax.review.add': {
500
- const p = payloadParsed.data as { body: string; kind?: PmxAxReviewKind; severity?: PmxAxReviewSeverity; anchorType?: PmxAxReviewAnchorType; nodeId?: string; file?: string; author?: string };
618
+ const p = payloadParsed.data as {
619
+ body: string;
620
+ kind?: PmxAxReviewKind;
621
+ severity?: PmxAxReviewSeverity;
622
+ anchorType?: PmxAxReviewAnchorType;
623
+ nodeId?: string;
624
+ file?: string;
625
+ author?: string;
626
+ };
501
627
  // Sandboxed surfaces may only review their own node; trusted surfaces may
502
628
  // anchor to a file/region or another node.
503
629
  // A node-interaction review carries a sourceNodeId, so it defaults to a node
@@ -510,13 +636,20 @@ export function applyAxInteraction(
510
636
  ...(p.kind ? { kind: p.kind } : {}),
511
637
  ...(p.severity ? { severity: p.severity } : {}),
512
638
  anchorType,
513
- nodeId: scoped ? sourceNodeId : (anchorType === 'node' ? (p.nodeId ?? sourceNodeId) : (p.nodeId ?? null)),
639
+ nodeId: scoped ? sourceNodeId : anchorType === 'node' ? (p.nodeId ?? sourceNodeId) : (p.nodeId ?? null),
514
640
  ...(!scoped && p.file !== undefined ? { file: p.file } : {}),
515
641
  ...(p.author !== undefined ? { author: p.author } : {}),
516
642
  },
517
643
  opts,
518
644
  );
519
- if (!reviewAnnotation) return reject(type, sourceNodeId, 400, 'invalid-review-anchor', 'Node-anchored review requires a nodeId that exists on the canvas.');
645
+ if (!reviewAnnotation)
646
+ return reject(
647
+ type,
648
+ sourceNodeId,
649
+ 400,
650
+ 'invalid-review-anchor',
651
+ 'Node-anchored review requires a nodeId that exists on the canvas.',
652
+ );
520
653
  return accept(type, sourceNodeId, reviewAnnotation, 'ax-state-changed', { reviewAnnotation });
521
654
  }
522
655
  case 'ax.focus.set': {