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.
- package/CHANGELOG.md +38 -0
- package/dist/canvas/index.js +2 -2
- package/dist/types/cli/daemon.d.ts +74 -0
- package/dist/types/cli/watch.d.ts +2 -2
- package/dist/types/client/canvas/CanvasViewport.d.ts +1 -1
- package/dist/types/client/canvas/CommandPalette.d.ts +1 -1
- package/dist/types/client/canvas/Minimap.d.ts +1 -1
- package/dist/types/client/nodes/FileNode.d.ts +1 -1
- package/dist/types/client/nodes/ImageNode.d.ts +1 -1
- package/dist/types/client/nodes/InlineFormatBar.d.ts +1 -1
- package/dist/types/client/nodes/MarkdownNode.d.ts +1 -1
- package/dist/types/client/nodes/PromptNode.d.ts +1 -1
- package/dist/types/client/nodes/ResponseNode.d.ts +1 -1
- package/dist/types/server/canvas-schema.d.ts +1 -1
- package/dist/types/server/html-primitives.d.ts +1 -1
- package/dist/types/server/index.d.ts +4 -4
- package/dist/types/server/operations/index.d.ts +1 -1
- package/dist/types/server/operations/ops/ax-read.d.ts +2 -0
- package/dist/types/server/operations/ops/canvas-wire.d.ts +2 -0
- package/dist/types/server/operations/ops/ext-app.d.ts +2 -0
- package/docs/http-api.md +28 -0
- package/docs/plans/plan-009-tech-debt-backlog.md +5 -5
- package/docs/tech-debt-assessment-2026-07.md +2 -2
- package/package.json +5 -2
- package/skills/pmx-canvas/SKILL.md +3 -1
- package/skills/pmx-canvas/references/full-reference.md +10 -3
- package/src/cli/agent.ts +1861 -1548
- package/src/cli/daemon.ts +460 -0
- package/src/cli/index.ts +63 -326
- package/src/cli/watch.ts +2 -10
- package/src/client/App.tsx +48 -46
- package/src/client/canvas/AttentionHistory.tsx +11 -1
- package/src/client/canvas/CanvasNode.tsx +41 -29
- package/src/client/canvas/CanvasViewport.tsx +101 -66
- package/src/client/canvas/CommandPalette.tsx +61 -27
- package/src/client/canvas/ContextMenu.tsx +13 -20
- package/src/client/canvas/ContextPinBar.tsx +1 -5
- package/src/client/canvas/ContextPinHud.tsx +1 -6
- package/src/client/canvas/DockedNode.tsx +4 -4
- package/src/client/canvas/EdgeLayer.tsx +37 -36
- package/src/client/canvas/ExpandedNodeOverlay.tsx +69 -45
- package/src/client/canvas/FocusFieldLayer.tsx +20 -22
- package/src/client/canvas/IntentLayer.tsx +31 -14
- package/src/client/canvas/Minimap.tsx +11 -16
- package/src/client/canvas/SelectionBar.tsx +4 -11
- package/src/client/canvas/ShortcutOverlay.tsx +3 -1
- package/src/client/canvas/SnapshotPanel.tsx +77 -95
- package/src/client/canvas/auto-fit.ts +15 -14
- package/src/client/canvas/snap-guides.ts +12 -12
- package/src/client/canvas/use-node-resize.ts +1 -5
- package/src/client/canvas/use-pan-zoom.ts +25 -26
- package/src/client/ext-app/bridge.ts +3 -12
- package/src/client/icons.tsx +63 -20
- package/src/client/nodes/ContextNode.tsx +14 -25
- package/src/client/nodes/ExtAppFrame.tsx +60 -39
- package/src/client/nodes/FileNode.tsx +74 -62
- package/src/client/nodes/GroupNode.tsx +4 -6
- package/src/client/nodes/HtmlNode.tsx +76 -46
- package/src/client/nodes/ImageNode.tsx +18 -27
- package/src/client/nodes/InlineFormatBar.tsx +4 -21
- package/src/client/nodes/InlineMarkdownEditor.tsx +0 -1
- package/src/client/nodes/LedgerNode.tsx +10 -4
- package/src/client/nodes/MarkdownNode.tsx +3 -10
- package/src/client/nodes/McpAppNode.tsx +26 -22
- package/src/client/nodes/MdFormatBar.tsx +10 -7
- package/src/client/nodes/PromptNode.tsx +23 -51
- package/src/client/nodes/ResponseNode.tsx +3 -13
- package/src/client/nodes/StatusNode.tsx +5 -9
- package/src/client/nodes/StatusSummary.tsx +2 -8
- package/src/client/nodes/WebpageNode.tsx +20 -14
- package/src/client/nodes/iframe-document-url.ts +25 -16
- package/src/client/nodes/image-warnings.ts +1 -7
- package/src/client/nodes/md-format.ts +20 -5
- package/src/client/state/attention-bridge.ts +4 -9
- package/src/client/state/attention-store.ts +1 -7
- package/src/client/state/canvas-store.ts +52 -36
- package/src/client/state/intent-bridge.ts +176 -112
- package/src/client/state/intent-store.ts +4 -1
- package/src/client/state/sse-bridge.ts +53 -70
- package/src/json-render/catalog.ts +12 -16
- package/src/json-render/charts/components.tsx +16 -20
- package/src/json-render/charts/extra-components.tsx +8 -16
- package/src/json-render/charts/extra-definitions.ts +1 -2
- package/src/json-render/charts/tufte-components.tsx +37 -20
- package/src/json-render/charts/tufte-definitions.ts +8 -2
- package/src/json-render/renderer/index.tsx +42 -22
- package/src/json-render/schema.ts +6 -3
- package/src/json-render/server.ts +33 -39
- package/src/mcp/canvas-access.ts +35 -21
- package/src/mcp/server.ts +132 -70
- package/src/server/agent-context.ts +63 -36
- package/src/server/ax-context.ts +7 -5
- package/src/server/ax-interaction.ts +176 -43
- package/src/server/ax-state-manager.ts +182 -39
- package/src/server/ax-state.ts +142 -47
- package/src/server/canvas-db.ts +213 -95
- package/src/server/canvas-operations.ts +177 -120
- package/src/server/canvas-provenance.ts +1 -4
- package/src/server/canvas-schema.ts +454 -73
- package/src/server/canvas-serialization.ts +27 -35
- package/src/server/canvas-state.ts +150 -58
- package/src/server/chart-template.ts +4 -5
- package/src/server/code-graph.ts +19 -6
- package/src/server/diagram-presets.ts +28 -29
- package/src/server/ext-app-lookup.ts +3 -12
- package/src/server/html-node-summary.ts +19 -10
- package/src/server/html-primitives.ts +326 -97
- package/src/server/html-surface.ts +6 -9
- package/src/server/image-source.ts +6 -4
- package/src/server/index.ts +320 -217
- package/src/server/intent-registry.ts +2 -5
- package/src/server/mcp-app-candidate.ts +5 -10
- package/src/server/mcp-app-host.ts +14 -38
- package/src/server/mcp-app-runtime.ts +12 -20
- package/src/server/mutation-history.ts +15 -5
- package/src/server/operations/composites.ts +1 -3
- package/src/server/operations/http.ts +2 -3
- package/src/server/operations/index.ts +7 -1
- package/src/server/operations/invoker.ts +4 -3
- package/src/server/operations/mcp.ts +22 -30
- package/src/server/operations/ops/annotation.ts +122 -10
- package/src/server/operations/ops/app.ts +98 -73
- package/src/server/operations/ops/ax-await.ts +17 -10
- package/src/server/operations/ops/ax-read.ts +347 -0
- package/src/server/operations/ops/ax-shared.ts +2 -7
- package/src/server/operations/ops/ax-state.ts +32 -14
- package/src/server/operations/ops/ax-timeline.ts +32 -19
- package/src/server/operations/ops/ax-work.ts +54 -37
- package/src/server/operations/ops/batch.ts +39 -14
- package/src/server/operations/ops/canvas-wire.ts +91 -0
- package/src/server/operations/ops/edges.ts +37 -25
- package/src/server/operations/ops/ext-app.ts +346 -0
- package/src/server/operations/ops/groups.ts +49 -20
- package/src/server/operations/ops/intent.ts +18 -12
- package/src/server/operations/ops/json-render.ts +239 -98
- package/src/server/operations/ops/nodes.ts +298 -109
- package/src/server/operations/ops/query.ts +46 -28
- package/src/server/operations/ops/snapshots.ts +35 -26
- package/src/server/operations/ops/validate.ts +2 -1
- package/src/server/operations/ops/viewport.ts +60 -16
- package/src/server/operations/ops/webview.ts +44 -18
- package/src/server/operations/registry.ts +2 -3
- package/src/server/operations/types.ts +7 -5
- package/src/server/operations/webview-runner.ts +1 -3
- package/src/server/placement.ts +8 -18
- package/src/server/server.ts +108 -1027
- package/src/server/spatial-analysis.ts +39 -25
- package/src/server/trace-manager.ts +3 -8
- package/src/server/web-artifacts.ts +23 -27
- package/src/server/webpage-node.ts +5 -13
- package/src/shared/auto-arrange.ts +12 -5
- package/src/shared/content-height-reporter.ts +8 -6
- package/src/shared/ext-app-tool-result.ts +2 -6
- package/src/shared/placement.ts +1 -4
- package/src/shared/semantic-attention.ts +39 -37
- 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(
|
|
90
|
-
|
|
91
|
-
|
|
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, [
|
|
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',
|
|
127
|
-
'ax.
|
|
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 = [
|
|
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: {
|
|
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: {
|
|
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(
|
|
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 =
|
|
400
|
-
|
|
401
|
-
|
|
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(
|
|
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(
|
|
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 {
|
|
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
|
-
{
|
|
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 {
|
|
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
|
-
{
|
|
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 {
|
|
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 {
|
|
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
|
-
{
|
|
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 {
|
|
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
|
-
{
|
|
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, {
|
|
496
|
-
|
|
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 {
|
|
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 :
|
|
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)
|
|
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': {
|