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
package/src/server/ax-state.ts
CHANGED
|
@@ -12,10 +12,16 @@ export interface PmxAxFocusState {
|
|
|
12
12
|
|
|
13
13
|
// ── New enums ──────────────────────────────────────────────────────
|
|
14
14
|
export type PmxAxEventKind =
|
|
15
|
-
| 'prompt'
|
|
16
|
-
| '
|
|
17
|
-
|
|
18
|
-
| '
|
|
15
|
+
| 'prompt'
|
|
16
|
+
| 'assistant-message'
|
|
17
|
+
| 'tool-start'
|
|
18
|
+
| 'tool-result'
|
|
19
|
+
| 'failure'
|
|
20
|
+
| 'approval'
|
|
21
|
+
| 'steering'
|
|
22
|
+
| 'command'
|
|
23
|
+
| 'note';
|
|
24
|
+
export type PmxAxEvidenceKind = 'logs' | 'tool-result' | 'screenshot' | 'file' | 'diff' | 'test-output';
|
|
19
25
|
export type PmxAxWorkItemStatus = 'todo' | 'in-progress' | 'blocked' | 'done' | 'cancelled';
|
|
20
26
|
export type PmxAxApprovalStatus = 'pending' | 'approved' | 'rejected';
|
|
21
27
|
export type PmxAxReviewKind = 'comment' | 'finding';
|
|
@@ -226,17 +232,38 @@ export function buildPendingAxActivity(state: PmxAxState, consumer?: string): Pe
|
|
|
226
232
|
}
|
|
227
233
|
for (const g of state.approvalGates) {
|
|
228
234
|
if (g.status === 'pending' && notMine(g.source)) {
|
|
229
|
-
out.push({
|
|
235
|
+
out.push({
|
|
236
|
+
kind: 'approval-gate',
|
|
237
|
+
id: g.id,
|
|
238
|
+
title: g.title,
|
|
239
|
+
status: g.status,
|
|
240
|
+
nodeIds: g.nodeIds,
|
|
241
|
+
source: g.source,
|
|
242
|
+
});
|
|
230
243
|
}
|
|
231
244
|
}
|
|
232
245
|
for (const e of state.elicitations) {
|
|
233
246
|
if (e.status === 'pending' && notMine(e.source)) {
|
|
234
|
-
out.push({
|
|
247
|
+
out.push({
|
|
248
|
+
kind: 'elicitation',
|
|
249
|
+
id: e.id,
|
|
250
|
+
title: e.prompt,
|
|
251
|
+
status: e.status,
|
|
252
|
+
nodeIds: e.nodeIds,
|
|
253
|
+
source: e.source,
|
|
254
|
+
});
|
|
235
255
|
}
|
|
236
256
|
}
|
|
237
257
|
for (const m of state.modeRequests) {
|
|
238
258
|
if (m.status === 'pending' && notMine(m.source)) {
|
|
239
|
-
out.push({
|
|
259
|
+
out.push({
|
|
260
|
+
kind: 'mode-request',
|
|
261
|
+
id: m.id,
|
|
262
|
+
title: m.reason ? `${m.mode}: ${m.reason}` : `mode: ${m.mode}`,
|
|
263
|
+
status: m.status,
|
|
264
|
+
nodeIds: m.nodeIds,
|
|
265
|
+
source: m.source,
|
|
266
|
+
});
|
|
240
267
|
}
|
|
241
268
|
}
|
|
242
269
|
return out;
|
|
@@ -249,9 +276,7 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
249
276
|
}
|
|
250
277
|
|
|
251
278
|
function normalizeSource(value: unknown): PmxAxSource | null {
|
|
252
|
-
return typeof value === 'string' && AX_SOURCES.has(value as PmxAxSource)
|
|
253
|
-
? value as PmxAxSource
|
|
254
|
-
: null;
|
|
279
|
+
return typeof value === 'string' && AX_SOURCES.has(value as PmxAxSource) ? (value as PmxAxSource) : null;
|
|
255
280
|
}
|
|
256
281
|
|
|
257
282
|
function normalizeTimestamp(value: unknown): string | null {
|
|
@@ -271,27 +296,50 @@ function normalizeNodeIds(value: unknown, validNodeIds?: Set<string>): string[]
|
|
|
271
296
|
return ids;
|
|
272
297
|
}
|
|
273
298
|
|
|
274
|
-
const AX_EVENT_KINDS = new Set<PmxAxEventKind>([
|
|
299
|
+
const AX_EVENT_KINDS = new Set<PmxAxEventKind>([
|
|
300
|
+
'prompt',
|
|
301
|
+
'assistant-message',
|
|
302
|
+
'tool-start',
|
|
303
|
+
'tool-result',
|
|
304
|
+
'failure',
|
|
305
|
+
'approval',
|
|
306
|
+
'steering',
|
|
307
|
+
'command',
|
|
308
|
+
'note',
|
|
309
|
+
]);
|
|
275
310
|
|
|
276
311
|
// ── Activity ingestion (harness-forwarded tool/session events) ─────
|
|
277
312
|
// A normalized activity the agent's harness forwards; the board auto-reacts.
|
|
278
313
|
// The precise activity kind is preserved on the recorded event's `data.activityKind`;
|
|
279
314
|
// only the (coarser) timeline event kind is constrained to PmxAxEventKind.
|
|
280
315
|
export type PmxAxActivityKind =
|
|
281
|
-
| 'tool-start'
|
|
282
|
-
| '
|
|
316
|
+
| 'tool-start'
|
|
317
|
+
| 'tool-result'
|
|
318
|
+
| 'failure'
|
|
319
|
+
| 'error'
|
|
320
|
+
| 'session-start'
|
|
321
|
+
| 'session-end'
|
|
322
|
+
| 'command'
|
|
323
|
+
| 'note';
|
|
283
324
|
const AX_ACTIVITY_KINDS = new Set<PmxAxActivityKind>([
|
|
284
|
-
'tool-start',
|
|
325
|
+
'tool-start',
|
|
326
|
+
'tool-result',
|
|
327
|
+
'failure',
|
|
328
|
+
'error',
|
|
329
|
+
'session-start',
|
|
330
|
+
'session-end',
|
|
331
|
+
'command',
|
|
332
|
+
'note',
|
|
285
333
|
]);
|
|
286
334
|
const ACTIVITY_TO_EVENT_KIND: Record<PmxAxActivityKind, PmxAxEventKind> = {
|
|
287
335
|
'tool-start': 'tool-start',
|
|
288
336
|
'tool-result': 'tool-result',
|
|
289
|
-
|
|
290
|
-
|
|
337
|
+
failure: 'failure',
|
|
338
|
+
error: 'failure',
|
|
291
339
|
'session-start': 'assistant-message',
|
|
292
340
|
'session-end': 'assistant-message',
|
|
293
|
-
|
|
294
|
-
|
|
341
|
+
command: 'command',
|
|
342
|
+
note: 'assistant-message',
|
|
295
343
|
};
|
|
296
344
|
export function isAxActivityKind(value: unknown): value is PmxAxActivityKind {
|
|
297
345
|
return typeof value === 'string' && AX_ACTIVITY_KINDS.has(value as PmxAxActivityKind);
|
|
@@ -312,13 +360,16 @@ export interface PmxAxCommandDescriptor {
|
|
|
312
360
|
export const AX_COMMAND_REGISTRY: Record<string, PmxAxCommandDescriptor> = {
|
|
313
361
|
'pmx.plan': { name: 'pmx.plan', description: 'Enter planning: outline the approach before executing.' },
|
|
314
362
|
'pmx.execute': { name: 'pmx.execute', description: 'Proceed from plan to execution.' },
|
|
315
|
-
'pmx.promote-context': {
|
|
363
|
+
'pmx.promote-context': {
|
|
364
|
+
name: 'pmx.promote-context',
|
|
365
|
+
description: 'Promote focused/pinned nodes into durable agent context.',
|
|
366
|
+
},
|
|
316
367
|
'pmx.summarize': { name: 'pmx.summarize', description: 'Summarize the current canvas/work state.' },
|
|
317
368
|
'pmx.review': { name: 'pmx.review', description: 'Start a review pass over the focused nodes.' },
|
|
318
369
|
};
|
|
319
370
|
|
|
320
371
|
export function isAxCommand(name: unknown): name is string {
|
|
321
|
-
return typeof name === 'string' && Object.
|
|
372
|
+
return typeof name === 'string' && Object.hasOwn(AX_COMMAND_REGISTRY, name);
|
|
322
373
|
}
|
|
323
374
|
|
|
324
375
|
export function listAxCommands(): PmxAxCommandDescriptor[] {
|
|
@@ -361,7 +412,14 @@ export function normalizeAxPolicy(input: unknown): PmxAxPolicy {
|
|
|
361
412
|
},
|
|
362
413
|
};
|
|
363
414
|
}
|
|
364
|
-
const AX_EVIDENCE_KINDS = new Set<PmxAxEvidenceKind>([
|
|
415
|
+
const AX_EVIDENCE_KINDS = new Set<PmxAxEvidenceKind>([
|
|
416
|
+
'logs',
|
|
417
|
+
'tool-result',
|
|
418
|
+
'screenshot',
|
|
419
|
+
'file',
|
|
420
|
+
'diff',
|
|
421
|
+
'test-output',
|
|
422
|
+
]);
|
|
365
423
|
const AX_WORK_STATUSES = new Set<PmxAxWorkItemStatus>(['todo', 'in-progress', 'blocked', 'done', 'cancelled']);
|
|
366
424
|
const AX_APPROVAL_STATUSES = new Set<PmxAxApprovalStatus>(['pending', 'approved', 'rejected']);
|
|
367
425
|
const AX_REVIEW_KINDS = new Set<PmxAxReviewKind>(['comment', 'finding']);
|
|
@@ -425,7 +483,9 @@ export function normalizeAxElicitation(input: unknown, validNodeIds?: Set<string
|
|
|
425
483
|
id: input.id,
|
|
426
484
|
prompt: typeof input.prompt === 'string' ? input.prompt : '(input requested)',
|
|
427
485
|
fields: Array.isArray(input.fields) ? input.fields.filter((f): f is string => typeof f === 'string') : [],
|
|
428
|
-
status: AX_ELICITATION_STATUSES.has(input.status as PmxAxElicitationStatus)
|
|
486
|
+
status: AX_ELICITATION_STATUSES.has(input.status as PmxAxElicitationStatus)
|
|
487
|
+
? (input.status as PmxAxElicitationStatus)
|
|
488
|
+
: 'pending',
|
|
429
489
|
response: isRecord(input.response) ? input.response : null,
|
|
430
490
|
nodeIds: normalizeNodeIds(input.nodeIds, validNodeIds),
|
|
431
491
|
createdAt: normalizeTimestamp(input.createdAt) ?? nowIso(),
|
|
@@ -475,9 +535,11 @@ export function normalizeAxModeRequest(input: unknown, validNodeIds?: Set<string
|
|
|
475
535
|
if (!isRecord(input) || typeof input.id !== 'string') return null;
|
|
476
536
|
return {
|
|
477
537
|
id: input.id,
|
|
478
|
-
mode: AX_MODES.has(input.mode as PmxAxMode) ? input.mode as PmxAxMode : 'execute',
|
|
538
|
+
mode: AX_MODES.has(input.mode as PmxAxMode) ? (input.mode as PmxAxMode) : 'execute',
|
|
479
539
|
reason: optionalString(input.reason),
|
|
480
|
-
status: AX_MODE_REQUEST_STATUSES.has(input.status as PmxAxModeRequestStatus)
|
|
540
|
+
status: AX_MODE_REQUEST_STATUSES.has(input.status as PmxAxModeRequestStatus)
|
|
541
|
+
? (input.status as PmxAxModeRequestStatus)
|
|
542
|
+
: 'pending',
|
|
481
543
|
nodeIds: normalizeNodeIds(input.nodeIds, validNodeIds),
|
|
482
544
|
createdAt: normalizeTimestamp(input.createdAt) ?? nowIso(),
|
|
483
545
|
resolvedAt: normalizeTimestamp(input.resolvedAt),
|
|
@@ -535,9 +597,10 @@ export function createEmptyAxHostCapability(): PmxAxHostCapability {
|
|
|
535
597
|
export function normalizeAxFocusState(input: unknown, validNodeIds?: Set<string>): PmxAxFocusState {
|
|
536
598
|
if (!isRecord(input)) return createEmptyAxFocusState();
|
|
537
599
|
const nodeIds = normalizeNodeIds(input.nodeIds, validNodeIds);
|
|
538
|
-
const primaryNodeId =
|
|
539
|
-
|
|
540
|
-
|
|
600
|
+
const primaryNodeId =
|
|
601
|
+
typeof input.primaryNodeId === 'string' && nodeIds.includes(input.primaryNodeId)
|
|
602
|
+
? input.primaryNodeId
|
|
603
|
+
: (nodeIds[0] ?? null);
|
|
541
604
|
return {
|
|
542
605
|
nodeIds,
|
|
543
606
|
primaryNodeId,
|
|
@@ -553,7 +616,7 @@ export function normalizeAxWorkItem(input: unknown, validNodeIds?: Set<string>):
|
|
|
553
616
|
return {
|
|
554
617
|
id: input.id,
|
|
555
618
|
title: typeof input.title === 'string' ? input.title : '(untitled)',
|
|
556
|
-
status: AX_WORK_STATUSES.has(input.status as PmxAxWorkItemStatus) ? input.status as PmxAxWorkItemStatus : 'todo',
|
|
619
|
+
status: AX_WORK_STATUSES.has(input.status as PmxAxWorkItemStatus) ? (input.status as PmxAxWorkItemStatus) : 'todo',
|
|
557
620
|
detail: optionalString(input.detail),
|
|
558
621
|
nodeIds: normalizeNodeIds(input.nodeIds, validNodeIds),
|
|
559
622
|
createdAt,
|
|
@@ -569,7 +632,9 @@ export function normalizeAxApprovalGate(input: unknown, validNodeIds?: Set<strin
|
|
|
569
632
|
title: typeof input.title === 'string' ? input.title : '(approval)',
|
|
570
633
|
detail: optionalString(input.detail),
|
|
571
634
|
action: optionalString(input.action),
|
|
572
|
-
status: AX_APPROVAL_STATUSES.has(input.status as PmxAxApprovalStatus)
|
|
635
|
+
status: AX_APPROVAL_STATUSES.has(input.status as PmxAxApprovalStatus)
|
|
636
|
+
? (input.status as PmxAxApprovalStatus)
|
|
637
|
+
: 'pending',
|
|
573
638
|
nodeIds: normalizeNodeIds(input.nodeIds, validNodeIds),
|
|
574
639
|
createdAt: normalizeTimestamp(input.createdAt) ?? nowIso(),
|
|
575
640
|
resolvedAt: normalizeTimestamp(input.resolvedAt),
|
|
@@ -581,7 +646,9 @@ export function normalizeAxApprovalGate(input: unknown, validNodeIds?: Set<strin
|
|
|
581
646
|
export function normalizeAxReviewAnnotation(input: unknown, validNodeIds?: Set<string>): PmxAxReviewAnnotation | null {
|
|
582
647
|
if (!isRecord(input) || typeof input.id !== 'string') return null;
|
|
583
648
|
const createdAt = normalizeTimestamp(input.createdAt) ?? nowIso();
|
|
584
|
-
const anchorType = AX_REVIEW_ANCHORS.has(input.anchorType as PmxAxReviewAnchorType)
|
|
649
|
+
const anchorType = AX_REVIEW_ANCHORS.has(input.anchorType as PmxAxReviewAnchorType)
|
|
650
|
+
? (input.anchorType as PmxAxReviewAnchorType)
|
|
651
|
+
: 'node';
|
|
585
652
|
// node anchor pruned to a valid node; if invalid, drop the whole annotation (mirrors focus pruning)
|
|
586
653
|
let nodeId: string | null = null;
|
|
587
654
|
if (anchorType === 'node') {
|
|
@@ -589,17 +656,21 @@ export function normalizeAxReviewAnnotation(input: unknown, validNodeIds?: Set<s
|
|
|
589
656
|
if (validNodeIds && !validNodeIds.has(input.nodeId)) return null;
|
|
590
657
|
nodeId = input.nodeId;
|
|
591
658
|
}
|
|
592
|
-
const region = isRecord(input.region)
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
659
|
+
const region = isRecord(input.region)
|
|
660
|
+
? {
|
|
661
|
+
...(typeof input.region.line === 'number' ? { line: input.region.line } : {}),
|
|
662
|
+
...(typeof input.region.endLine === 'number' ? { endLine: input.region.endLine } : {}),
|
|
663
|
+
...(typeof input.region.label === 'string' ? { label: input.region.label } : {}),
|
|
664
|
+
}
|
|
665
|
+
: null;
|
|
597
666
|
return {
|
|
598
667
|
id: input.id,
|
|
599
|
-
kind: AX_REVIEW_KINDS.has(input.kind as PmxAxReviewKind) ? input.kind as PmxAxReviewKind : 'comment',
|
|
668
|
+
kind: AX_REVIEW_KINDS.has(input.kind as PmxAxReviewKind) ? (input.kind as PmxAxReviewKind) : 'comment',
|
|
600
669
|
body: typeof input.body === 'string' ? input.body : '',
|
|
601
|
-
severity: AX_REVIEW_SEVERITIES.has(input.severity as PmxAxReviewSeverity)
|
|
602
|
-
|
|
670
|
+
severity: AX_REVIEW_SEVERITIES.has(input.severity as PmxAxReviewSeverity)
|
|
671
|
+
? (input.severity as PmxAxReviewSeverity)
|
|
672
|
+
: 'info',
|
|
673
|
+
status: AX_REVIEW_STATUSES.has(input.status as PmxAxReviewStatus) ? (input.status as PmxAxReviewStatus) : 'open',
|
|
603
674
|
anchorType,
|
|
604
675
|
nodeId,
|
|
605
676
|
file: anchorType === 'file' ? optionalString(input.file) : null,
|
|
@@ -632,7 +703,7 @@ export function normalizeAxHostCapability(input: unknown): PmxAxHostCapability |
|
|
|
632
703
|
// ── Timeline normalizers (DB row parse; node ids kept as recorded) ──
|
|
633
704
|
export function normalizeAxEvent(input: unknown): PmxAxEvent | null {
|
|
634
705
|
if (!isRecord(input) || typeof input.id !== 'string') return null;
|
|
635
|
-
const kind = AX_EVENT_KINDS.has(input.kind as PmxAxEventKind) ? input.kind as PmxAxEventKind : null;
|
|
706
|
+
const kind = AX_EVENT_KINDS.has(input.kind as PmxAxEventKind) ? (input.kind as PmxAxEventKind) : null;
|
|
636
707
|
if (!kind) return null;
|
|
637
708
|
return {
|
|
638
709
|
id: input.id,
|
|
@@ -649,7 +720,7 @@ export function normalizeAxEvent(input: unknown): PmxAxEvent | null {
|
|
|
649
720
|
|
|
650
721
|
export function normalizeAxEvidence(input: unknown): PmxAxEvidence | null {
|
|
651
722
|
if (!isRecord(input) || typeof input.id !== 'string') return null;
|
|
652
|
-
const kind = AX_EVIDENCE_KINDS.has(input.kind as PmxAxEvidenceKind) ? input.kind as PmxAxEvidenceKind : null;
|
|
723
|
+
const kind = AX_EVIDENCE_KINDS.has(input.kind as PmxAxEvidenceKind) ? (input.kind as PmxAxEvidenceKind) : null;
|
|
653
724
|
if (!kind) return null;
|
|
654
725
|
return {
|
|
655
726
|
id: input.id,
|
|
@@ -750,7 +821,13 @@ export function createAxReviewAnnotation(
|
|
|
750
821
|
}
|
|
751
822
|
|
|
752
823
|
export function createAxEvent(
|
|
753
|
-
input: {
|
|
824
|
+
input: {
|
|
825
|
+
kind: PmxAxEventKind;
|
|
826
|
+
summary: string;
|
|
827
|
+
detail?: string | null;
|
|
828
|
+
nodeIds?: string[];
|
|
829
|
+
data?: Record<string, unknown> | null;
|
|
830
|
+
},
|
|
754
831
|
source: PmxAxSource | null,
|
|
755
832
|
): Omit<PmxAxEvent, 'seq'> {
|
|
756
833
|
return {
|
|
@@ -766,7 +843,14 @@ export function createAxEvent(
|
|
|
766
843
|
}
|
|
767
844
|
|
|
768
845
|
export function createAxEvidence(
|
|
769
|
-
input: {
|
|
846
|
+
input: {
|
|
847
|
+
kind: PmxAxEvidenceKind;
|
|
848
|
+
title: string;
|
|
849
|
+
body?: string | null;
|
|
850
|
+
ref?: string | null;
|
|
851
|
+
nodeIds?: string[];
|
|
852
|
+
data?: Record<string, unknown> | null;
|
|
853
|
+
},
|
|
770
854
|
source: PmxAxSource | null,
|
|
771
855
|
): Omit<PmxAxEvidence, 'seq'> {
|
|
772
856
|
return {
|
|
@@ -782,7 +866,10 @@ export function createAxEvidence(
|
|
|
782
866
|
};
|
|
783
867
|
}
|
|
784
868
|
|
|
785
|
-
export function createAxSteeringMessage(
|
|
869
|
+
export function createAxSteeringMessage(
|
|
870
|
+
message: string,
|
|
871
|
+
source: PmxAxSource | null,
|
|
872
|
+
): Omit<PmxAxSteeringMessage, 'seq'> {
|
|
786
873
|
return {
|
|
787
874
|
id: axId('steer'),
|
|
788
875
|
message,
|
|
@@ -801,16 +888,24 @@ export function normalizeAxState(input: unknown, validNodeIds?: Set<string>): Pm
|
|
|
801
888
|
? input.workItems.map((w) => normalizeAxWorkItem(w, validNodeIds)).filter((w): w is PmxAxWorkItem => w !== null)
|
|
802
889
|
: [],
|
|
803
890
|
approvalGates: Array.isArray(input.approvalGates)
|
|
804
|
-
? input.approvalGates
|
|
891
|
+
? input.approvalGates
|
|
892
|
+
.map((g) => normalizeAxApprovalGate(g, validNodeIds))
|
|
893
|
+
.filter((g): g is PmxAxApprovalGate => g !== null)
|
|
805
894
|
: [],
|
|
806
895
|
reviewAnnotations: Array.isArray(input.reviewAnnotations)
|
|
807
|
-
? input.reviewAnnotations
|
|
896
|
+
? input.reviewAnnotations
|
|
897
|
+
.map((r) => normalizeAxReviewAnnotation(r, validNodeIds))
|
|
898
|
+
.filter((r): r is PmxAxReviewAnnotation => r !== null)
|
|
808
899
|
: [],
|
|
809
900
|
elicitations: Array.isArray(input.elicitations)
|
|
810
|
-
? input.elicitations
|
|
901
|
+
? input.elicitations
|
|
902
|
+
.map((e) => normalizeAxElicitation(e, validNodeIds))
|
|
903
|
+
.filter((e): e is PmxAxElicitation => e !== null)
|
|
811
904
|
: [],
|
|
812
905
|
modeRequests: Array.isArray(input.modeRequests)
|
|
813
|
-
? input.modeRequests
|
|
906
|
+
? input.modeRequests
|
|
907
|
+
.map((m) => normalizeAxModeRequest(m, validNodeIds))
|
|
908
|
+
.filter((m): m is PmxAxModeRequest => m !== null)
|
|
814
909
|
: [],
|
|
815
910
|
policy: normalizeAxPolicy(input.policy),
|
|
816
911
|
};
|