pmx-canvas 0.1.35 → 0.2.0
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 +461 -0
- package/Readme.md +14 -2
- package/dist/canvas/index.js +82 -41
- package/dist/json-render/index.js +89 -334
- package/dist/types/client/nodes/ExtAppFrame.d.ts +2 -0
- package/dist/types/mcp/canvas-access.d.ts +12 -159
- package/dist/types/server/ax-context.d.ts +1 -1
- package/dist/types/server/ax-state-manager.d.ts +256 -0
- package/dist/types/server/ax-state.d.ts +29 -1
- package/dist/types/server/ax-wait.d.ts +23 -0
- package/dist/types/server/canvas-operations.d.ts +1 -12
- package/dist/types/server/canvas-state.d.ts +46 -14
- package/dist/types/server/html-surface.d.ts +7 -0
- package/dist/types/server/index.d.ts +66 -26
- package/dist/types/server/operations/composites.d.ts +121 -0
- package/dist/types/server/operations/http.d.ts +7 -0
- package/dist/types/server/operations/index.d.ts +8 -0
- package/dist/types/server/operations/invoker.d.ts +13 -0
- package/dist/types/server/operations/mcp.d.ts +15 -0
- package/dist/types/server/operations/ops/annotation.d.ts +2 -0
- package/dist/types/server/operations/ops/app.d.ts +33 -0
- package/dist/types/server/operations/ops/ax-await.d.ts +2 -0
- package/dist/types/server/operations/ops/ax-shared.d.ts +31 -0
- package/dist/types/server/operations/ops/ax-state.d.ts +2 -0
- package/dist/types/server/operations/ops/ax-timeline.d.ts +2 -0
- package/dist/types/server/operations/ops/ax-work.d.ts +2 -0
- package/dist/types/server/operations/ops/batch.d.ts +19 -0
- package/dist/types/server/operations/ops/edges.d.ts +2 -0
- package/dist/types/server/operations/ops/groups.d.ts +2 -0
- package/dist/types/server/operations/ops/json-render.d.ts +31 -0
- package/dist/types/server/operations/ops/nodes.d.ts +62 -0
- package/dist/types/server/operations/ops/query.d.ts +2 -0
- package/dist/types/server/operations/ops/snapshots.d.ts +2 -0
- package/dist/types/server/operations/ops/validate.d.ts +2 -0
- package/dist/types/server/operations/ops/viewport.d.ts +2 -0
- package/dist/types/server/operations/ops/webview.d.ts +2 -0
- package/dist/types/server/operations/registry.d.ts +15 -0
- package/dist/types/server/operations/types.d.ts +116 -0
- package/dist/types/server/operations/webview-runner.d.ts +69 -0
- package/docs/RELEASE.md +5 -0
- package/docs/adr-001-bun-only-runtime.md +46 -0
- package/docs/api-stability.md +57 -0
- package/docs/ax-host-adapter-contract.md +65 -0
- package/docs/ax-state-contract.md +72 -0
- package/docs/http-api.md +34 -2
- package/docs/mcp.md +64 -11
- package/docs/plans/plan-005-operation-registry.md +84 -0
- package/docs/plans/plan-006-mcp-tool-consolidation.md +109 -0
- package/docs/plans/plan-007-ax-domain.md +99 -0
- package/docs/plans/plan-008-registry-finish.md +91 -0
- package/docs/screenshot.png +0 -0
- package/docs/tech-debt-assessment-2026-06.md +90 -0
- package/package.json +3 -3
- package/skills/pmx-canvas/SKILL.md +233 -185
- package/skills/pmx-canvas/evals/evals.json +3 -3
- package/skills/pmx-canvas/references/codex-app-adapter.md +24 -11
- package/skills/pmx-canvas/references/github-copilot-app-adapter.md +31 -1
- package/src/cli/agent.ts +52 -31
- package/src/client/nodes/ExtAppFrame.tsx +73 -5
- package/src/client/nodes/HtmlNode.tsx +12 -3
- package/src/client/nodes/McpAppNode.tsx +12 -3
- package/src/json-render/renderer/index.tsx +3 -0
- package/src/mcp/canvas-access.ts +43 -774
- package/src/mcp/server.ts +190 -2001
- package/src/server/ax-context.ts +7 -1
- package/src/server/ax-state-manager.ts +808 -0
- package/src/server/ax-state.ts +89 -2
- package/src/server/ax-wait.ts +56 -0
- package/src/server/canvas-operations.ts +2 -328
- package/src/server/canvas-schema.ts +2 -2
- package/src/server/canvas-state.ts +140 -382
- package/src/server/html-surface.ts +49 -11
- package/src/server/index.ts +136 -192
- package/src/server/operations/composites.ts +355 -0
- package/src/server/operations/http.ts +103 -0
- package/src/server/operations/index.ts +65 -0
- package/src/server/operations/invoker.ts +87 -0
- package/src/server/operations/mcp.ts +221 -0
- package/src/server/operations/ops/annotation.ts +60 -0
- package/src/server/operations/ops/app.ts +447 -0
- package/src/server/operations/ops/ax-await.ts +216 -0
- package/src/server/operations/ops/ax-shared.ts +38 -0
- package/src/server/operations/ops/ax-state.ts +249 -0
- package/src/server/operations/ops/ax-timeline.ts +381 -0
- package/src/server/operations/ops/ax-work.ts +635 -0
- package/src/server/operations/ops/batch.ts +365 -0
- package/src/server/operations/ops/edges.ts +166 -0
- package/src/server/operations/ops/groups.ts +176 -0
- package/src/server/operations/ops/json-render.ts +691 -0
- package/src/server/operations/ops/nodes.ts +1047 -0
- package/src/server/operations/ops/query.ts +281 -0
- package/src/server/operations/ops/snapshots.ts +366 -0
- package/src/server/operations/ops/validate.ts +37 -0
- package/src/server/operations/ops/viewport.ts +219 -0
- package/src/server/operations/ops/webview.ts +339 -0
- package/src/server/operations/registry.ts +79 -0
- package/src/server/operations/types.ts +150 -0
- package/src/server/operations/webview-runner.ts +77 -0
- package/src/server/server.ts +253 -2170
- package/src/server/web-artifacts.ts +6 -2
|
@@ -29,17 +29,6 @@ import {
|
|
|
29
29
|
isDbPopulated,
|
|
30
30
|
checkpointCanvasDb,
|
|
31
31
|
finalizeCanvasDbForClose,
|
|
32
|
-
appendAxEventToDB,
|
|
33
|
-
appendAxEvidenceToDB,
|
|
34
|
-
appendAxSteeringToDB,
|
|
35
|
-
markAxSteeringDeliveredInDB,
|
|
36
|
-
loadAxEventsFromDB,
|
|
37
|
-
loadAxEvidenceFromDB,
|
|
38
|
-
loadAxSteeringFromDB,
|
|
39
|
-
loadPendingAxSteeringFromDB,
|
|
40
|
-
loadAxTimelineSummaryFromDB,
|
|
41
|
-
upsertAxHostCapabilityToDB,
|
|
42
|
-
loadAxHostCapabilityFromDB,
|
|
43
32
|
type PersistedCanvasState,
|
|
44
33
|
type CanvasTheme,
|
|
45
34
|
type AxTimelineQuery,
|
|
@@ -55,21 +44,7 @@ import {
|
|
|
55
44
|
} from './placement.js';
|
|
56
45
|
import {
|
|
57
46
|
createEmptyAxState,
|
|
58
|
-
|
|
59
|
-
normalizeAxState,
|
|
60
|
-
normalizeAxHostCapability,
|
|
61
|
-
createAxWorkItem,
|
|
62
|
-
createAxApprovalGate,
|
|
63
|
-
createAxReviewAnnotation,
|
|
64
|
-
createAxEvent,
|
|
65
|
-
createAxEvidence,
|
|
66
|
-
createAxSteeringMessage,
|
|
67
|
-
createAxElicitation,
|
|
68
|
-
createAxModeRequest,
|
|
69
|
-
isAxCommand,
|
|
70
|
-
listAxCommands,
|
|
71
|
-
AX_COMMAND_REGISTRY,
|
|
72
|
-
normalizeAxPolicy,
|
|
47
|
+
type PmxAxActivityKind,
|
|
73
48
|
type PmxAxElicitation,
|
|
74
49
|
type PmxAxModeRequest,
|
|
75
50
|
type PmxAxMode,
|
|
@@ -95,6 +70,7 @@ import {
|
|
|
95
70
|
type PmxAxHostCapability,
|
|
96
71
|
type PmxAxTimelineSummary,
|
|
97
72
|
} from './ax-state.js';
|
|
73
|
+
import { AxStateManager } from './ax-state-manager.js';
|
|
98
74
|
|
|
99
75
|
function logCanvasStateWarning(action: string, error: unknown, details?: Record<string, unknown>): void {
|
|
100
76
|
console.warn(`[canvas-state] ${action}`, { error, ...(details ?? {}) });
|
|
@@ -327,20 +303,43 @@ class CanvasStateManager {
|
|
|
327
303
|
private _viewport: ViewportState = { x: 0, y: 0, scale: 1 };
|
|
328
304
|
private _theme: CanvasTheme = 'dark';
|
|
329
305
|
private _contextPinnedNodeIds = new Set<string>();
|
|
330
|
-
private _axState: PmxAxState = createEmptyAxState();
|
|
331
|
-
private _axHostCapability: PmxAxHostCapability | null = null;
|
|
332
306
|
private _workspaceRoot = process.cwd();
|
|
333
307
|
|
|
308
|
+
// ── AX state (canvas-bound + timeline + host partitions) ──────────
|
|
309
|
+
// Extracted into a dedicated manager (plan-007 Slice A). CanvasStateManager
|
|
310
|
+
// holds it and delegates its public AX methods so the SDK/HTTP/MCP surface is
|
|
311
|
+
// byte-stable; the manager receives the host hooks it needs as injected deps.
|
|
312
|
+
private readonly ax = new AxStateManager({
|
|
313
|
+
getNodeIds: () => this.currentNodeIdSet(),
|
|
314
|
+
getDb: () => this._db,
|
|
315
|
+
scheduleSave: () => this.scheduleSave(),
|
|
316
|
+
notifyChange: (type) => this.notifyChange(type),
|
|
317
|
+
recordMutation: (info) => this.recordMutation(info),
|
|
318
|
+
suppressed: (fn) => this.suppressed(fn),
|
|
319
|
+
});
|
|
320
|
+
|
|
334
321
|
// ── Change listeners (for MCP resource notifications) ──────
|
|
335
322
|
private _changeListeners: ((type: CanvasChangeType) => void)[] = [];
|
|
336
323
|
|
|
337
|
-
/**
|
|
338
|
-
|
|
324
|
+
/**
|
|
325
|
+
* Register a listener for state changes. Used by MCP server to emit resource
|
|
326
|
+
* notifications and by the blocking-wait endpoints to await an AX transition.
|
|
327
|
+
* Returns a disposer that unregisters the listener (callers that don't need it
|
|
328
|
+
* — e.g. the long-lived MCP subscription — may ignore the return value).
|
|
329
|
+
*/
|
|
330
|
+
onChange(cb: (type: CanvasChangeType) => void): () => void {
|
|
339
331
|
this._changeListeners.push(cb);
|
|
332
|
+
return () => {
|
|
333
|
+
const i = this._changeListeners.indexOf(cb);
|
|
334
|
+
if (i >= 0) this._changeListeners.splice(i, 1);
|
|
335
|
+
};
|
|
340
336
|
}
|
|
341
337
|
|
|
342
338
|
private notifyChange(type: CanvasChangeType): void {
|
|
343
|
-
|
|
339
|
+
// Iterate a snapshot: a listener (e.g. a blocking-wait via onChange) may dispose
|
|
340
|
+
// itself synchronously here, and splicing the live array mid-iteration would skip
|
|
341
|
+
// the next listener for this notification.
|
|
342
|
+
for (const cb of [...this._changeListeners]) {
|
|
344
343
|
try {
|
|
345
344
|
cb(type);
|
|
346
345
|
} catch (error) {
|
|
@@ -382,14 +381,6 @@ class CanvasStateManager {
|
|
|
382
381
|
return new Set(this.nodes.keys());
|
|
383
382
|
}
|
|
384
383
|
|
|
385
|
-
private normalizeAxForCurrentNodes(state: unknown): PmxAxState {
|
|
386
|
-
return normalizeAxState(state, this.currentNodeIdSet());
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
private applyAxState(state: PmxAxState): void {
|
|
390
|
-
this._axState = this.normalizeAxForCurrentNodes(state);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
384
|
private applyResolvedGroupBounds(
|
|
394
385
|
group: CanvasNodeState,
|
|
395
386
|
groupId: string,
|
|
@@ -885,13 +876,7 @@ class CanvasStateManager {
|
|
|
885
876
|
/** Load canvas state from SQLite (or legacy JSON fallback). Call once on server startup. */
|
|
886
877
|
loadFromDisk(options: LoadFromDiskOptions = {}): boolean {
|
|
887
878
|
// Host capability lives in its own table (not snapshotted / not in PmxAxState).
|
|
888
|
-
|
|
889
|
-
try {
|
|
890
|
-
this._axHostCapability = loadAxHostCapabilityFromDB(this._db);
|
|
891
|
-
} catch (error) {
|
|
892
|
-
logCanvasStateWarning('load host capability failed', error, {});
|
|
893
|
-
}
|
|
894
|
-
}
|
|
879
|
+
this.ax.loadHostCapabilityFromDb();
|
|
895
880
|
// Try SQLite first (only if DB has been populated)
|
|
896
881
|
if (this._db && isDbPopulated(this._db)) {
|
|
897
882
|
try {
|
|
@@ -1015,7 +1000,7 @@ class CanvasStateManager {
|
|
|
1015
1000
|
this.edges.clear();
|
|
1016
1001
|
this.annotations.clear();
|
|
1017
1002
|
this._contextPinnedNodeIds.clear();
|
|
1018
|
-
this.
|
|
1003
|
+
this.ax.resetCanvasBound();
|
|
1019
1004
|
|
|
1020
1005
|
this._viewport = {
|
|
1021
1006
|
x: state.viewport?.x ?? 0,
|
|
@@ -1046,7 +1031,7 @@ class CanvasStateManager {
|
|
|
1046
1031
|
if (this.nodes.has(pinId)) this._contextPinnedNodeIds.add(pinId);
|
|
1047
1032
|
}
|
|
1048
1033
|
}
|
|
1049
|
-
this.
|
|
1034
|
+
this.ax.applyPersistedAx(state.ax);
|
|
1050
1035
|
}
|
|
1051
1036
|
|
|
1052
1037
|
private readResolvedSnapshot(idOrName: string): {
|
|
@@ -1442,11 +1427,41 @@ class CanvasStateManager {
|
|
|
1442
1427
|
this.nodes.delete(id);
|
|
1443
1428
|
this.removeEdgesForNode(id);
|
|
1444
1429
|
this._contextPinnedNodeIds.delete(id);
|
|
1445
|
-
|
|
1430
|
+
// Re-normalize canvas-bound AX against the surviving node set. This strips the
|
|
1431
|
+
// dangling node ref from work items / approval gates / elicitations / mode
|
|
1432
|
+
// requests (re-anchored) and drops node-anchored review annotations (removed).
|
|
1433
|
+
// Previously SILENT — now audited (plan-007 Slice A): if the deleted node
|
|
1434
|
+
// orphaned anything, record one `note` timeline event so the human and a
|
|
1435
|
+
// resuming agent can see the work that changed instead of it changing quietly.
|
|
1436
|
+
const orphaned = this.ax.revalidateAfterNodeRemoval(id);
|
|
1446
1437
|
this.scheduleSave();
|
|
1447
1438
|
this.notifyChange('nodes');
|
|
1448
1439
|
this.notifyChange('pins');
|
|
1449
1440
|
this.notifyChange('ax');
|
|
1441
|
+
// Only record the audit note on a real (user-initiated) deletion. Undo/redo
|
|
1442
|
+
// replay removeNode inside `suppressed()` (_suppressRecordingDepth > 0); the
|
|
1443
|
+
// original deletion already recorded the note, so replaying must NOT append a
|
|
1444
|
+
// duplicate (the timeline is append-only). `revalidateAfterNodeRemoval` above
|
|
1445
|
+
// still runs unconditionally — only the timeline note is gated.
|
|
1446
|
+
const affected = orphaned.reanchoredIds.length > 0 || orphaned.removedReviewIds.length > 0 || orphaned.reanchoredFocus;
|
|
1447
|
+
if (existing && this._suppressRecordingDepth === 0 && affected) {
|
|
1448
|
+
const title = (existing.data.title as string) ?? id;
|
|
1449
|
+
const focusNote = orphaned.reanchoredFocus ? ' (focus anchor cleared)' : '';
|
|
1450
|
+
this.recordAxEvent(
|
|
1451
|
+
{
|
|
1452
|
+
kind: 'note',
|
|
1453
|
+
summary: `Node "${title}" deleted — re-anchored ${orphaned.reanchoredIds.length} AX item(s), removed ${orphaned.removedReviewIds.length} node-anchored review annotation(s).${focusNote}`,
|
|
1454
|
+
data: {
|
|
1455
|
+
systemEvent: 'ax-node-orphan',
|
|
1456
|
+
removedNodeId: id,
|
|
1457
|
+
reanchoredIds: orphaned.reanchoredIds,
|
|
1458
|
+
removedReviewIds: orphaned.removedReviewIds,
|
|
1459
|
+
reanchoredFocus: orphaned.reanchoredFocus,
|
|
1460
|
+
},
|
|
1461
|
+
},
|
|
1462
|
+
{ source: 'system' },
|
|
1463
|
+
);
|
|
1464
|
+
}
|
|
1450
1465
|
if (cloned) {
|
|
1451
1466
|
this.recordMutation({
|
|
1452
1467
|
operationType: 'removeNode',
|
|
@@ -1455,7 +1470,7 @@ class CanvasStateManager {
|
|
|
1455
1470
|
inverse: this.suppressed(() => {
|
|
1456
1471
|
this.addNode(structuredClone(cloned));
|
|
1457
1472
|
for (const edge of connectedEdges) this.addEdge(structuredClone(edge));
|
|
1458
|
-
this.
|
|
1473
|
+
this.ax.applyPersistedAx(oldAxState);
|
|
1459
1474
|
this.scheduleSave();
|
|
1460
1475
|
this.notifyChange('ax');
|
|
1461
1476
|
}),
|
|
@@ -1714,73 +1729,35 @@ class CanvasStateManager {
|
|
|
1714
1729
|
return new Set(this._contextPinnedNodeIds);
|
|
1715
1730
|
}
|
|
1716
1731
|
|
|
1732
|
+
// ── AX state delegation (canvas-bound + timeline + host) ──────────
|
|
1733
|
+
// All AX state lives in `this.ax` (AxStateManager); these are byte-stable
|
|
1734
|
+
// delegations so SDK/HTTP/MCP keep calling canvasState.<method>(...) unchanged.
|
|
1717
1735
|
getAxState(): PmxAxState {
|
|
1718
|
-
return
|
|
1736
|
+
return this.ax.getAxState();
|
|
1719
1737
|
}
|
|
1720
1738
|
|
|
1721
1739
|
getAxFocus(): PmxAxFocusState {
|
|
1722
|
-
return this.
|
|
1740
|
+
return this.ax.getAxFocus();
|
|
1723
1741
|
}
|
|
1724
1742
|
|
|
1725
1743
|
setAxFocus(nodeIds: string[], options: { source?: PmxAxSource; recordHistory?: boolean } = {}): PmxAxFocusState {
|
|
1726
|
-
|
|
1727
|
-
const nextAxState: PmxAxState = {
|
|
1728
|
-
...oldAxState,
|
|
1729
|
-
focus: {
|
|
1730
|
-
nodeIds,
|
|
1731
|
-
primaryNodeId: nodeIds[0] ?? null,
|
|
1732
|
-
updatedAt: new Date().toISOString(),
|
|
1733
|
-
source: options.source ?? 'api',
|
|
1734
|
-
},
|
|
1735
|
-
};
|
|
1736
|
-
this.applyAxState(nextAxState);
|
|
1737
|
-
const appliedAxState = this.getAxState();
|
|
1738
|
-
this.scheduleSave();
|
|
1739
|
-
this.notifyChange('ax');
|
|
1740
|
-
if (options.recordHistory === false) return appliedAxState.focus;
|
|
1741
|
-
this.recordMutation({
|
|
1742
|
-
operationType: 'setAxFocus',
|
|
1743
|
-
description: `Set AX focus (${appliedAxState.focus.nodeIds.length} nodes)`,
|
|
1744
|
-
forward: this.suppressed(() => {
|
|
1745
|
-
this.applyAxState(appliedAxState);
|
|
1746
|
-
this.scheduleSave();
|
|
1747
|
-
this.notifyChange('ax');
|
|
1748
|
-
}),
|
|
1749
|
-
inverse: this.suppressed(() => {
|
|
1750
|
-
this.applyAxState(oldAxState);
|
|
1751
|
-
this.scheduleSave();
|
|
1752
|
-
this.notifyChange('ax');
|
|
1753
|
-
}),
|
|
1754
|
-
});
|
|
1755
|
-
return appliedAxState.focus;
|
|
1744
|
+
return this.ax.setAxFocus(nodeIds, options);
|
|
1756
1745
|
}
|
|
1757
1746
|
|
|
1758
1747
|
clearAxFocus(): PmxAxFocusState {
|
|
1759
|
-
return this.
|
|
1748
|
+
return this.ax.clearAxFocus();
|
|
1760
1749
|
}
|
|
1761
1750
|
|
|
1762
1751
|
// ── Work items (canvas-bound; snapshotted via getAxState blob) ────
|
|
1763
1752
|
getWorkItems(): PmxAxWorkItem[] {
|
|
1764
|
-
return this.
|
|
1753
|
+
return this.ax.getWorkItems();
|
|
1765
1754
|
}
|
|
1766
1755
|
|
|
1767
1756
|
addWorkItem(
|
|
1768
1757
|
input: { title: string; status?: PmxAxWorkItemStatus; detail?: string | null; nodeIds?: string[] },
|
|
1769
1758
|
options: { source?: PmxAxSource } = {},
|
|
1770
1759
|
): PmxAxWorkItem {
|
|
1771
|
-
|
|
1772
|
-
const item = createAxWorkItem(input, options.source ?? 'api', this.currentNodeIdSet());
|
|
1773
|
-
this.applyAxState({ ...oldAxState, workItems: [...oldAxState.workItems, item] });
|
|
1774
|
-
const applied = this.getAxState();
|
|
1775
|
-
this.scheduleSave();
|
|
1776
|
-
this.notifyChange('ax');
|
|
1777
|
-
this.recordMutation({
|
|
1778
|
-
operationType: 'addWorkItem',
|
|
1779
|
-
description: `Added work item "${item.title}"`,
|
|
1780
|
-
forward: this.suppressed(() => { this.applyAxState(applied); this.scheduleSave(); this.notifyChange('ax'); }),
|
|
1781
|
-
inverse: this.suppressed(() => { this.applyAxState(oldAxState); this.scheduleSave(); this.notifyChange('ax'); }),
|
|
1782
|
-
});
|
|
1783
|
-
return applied.workItems.find((w) => w.id === item.id) ?? item;
|
|
1760
|
+
return this.ax.addWorkItem(input, options);
|
|
1784
1761
|
}
|
|
1785
1762
|
|
|
1786
1763
|
updateWorkItem(
|
|
@@ -1788,53 +1765,19 @@ class CanvasStateManager {
|
|
|
1788
1765
|
patch: { title?: string; status?: PmxAxWorkItemStatus; detail?: string | null; nodeIds?: string[] },
|
|
1789
1766
|
options: { source?: PmxAxSource } = {},
|
|
1790
1767
|
): PmxAxWorkItem | null {
|
|
1791
|
-
|
|
1792
|
-
const existing = oldAxState.workItems.find((w) => w.id === id);
|
|
1793
|
-
if (!existing) return null;
|
|
1794
|
-
const merged: PmxAxWorkItem = {
|
|
1795
|
-
...existing,
|
|
1796
|
-
...(patch.title !== undefined ? { title: patch.title } : {}),
|
|
1797
|
-
...(patch.status !== undefined ? { status: patch.status } : {}),
|
|
1798
|
-
...(patch.detail !== undefined ? { detail: patch.detail } : {}),
|
|
1799
|
-
...(patch.nodeIds !== undefined ? { nodeIds: patch.nodeIds.filter((n) => this.nodes.has(n)) } : {}),
|
|
1800
|
-
updatedAt: new Date().toISOString(),
|
|
1801
|
-
source: options.source ?? existing.source,
|
|
1802
|
-
};
|
|
1803
|
-
this.applyAxState({ ...oldAxState, workItems: replaceById(oldAxState.workItems, merged) });
|
|
1804
|
-
const applied = this.getAxState();
|
|
1805
|
-
this.scheduleSave();
|
|
1806
|
-
this.notifyChange('ax');
|
|
1807
|
-
this.recordMutation({
|
|
1808
|
-
operationType: 'updateWorkItem',
|
|
1809
|
-
description: `Updated work item ${id}`,
|
|
1810
|
-
forward: this.suppressed(() => { this.applyAxState(applied); this.scheduleSave(); this.notifyChange('ax'); }),
|
|
1811
|
-
inverse: this.suppressed(() => { this.applyAxState(oldAxState); this.scheduleSave(); this.notifyChange('ax'); }),
|
|
1812
|
-
});
|
|
1813
|
-
return applied.workItems.find((w) => w.id === id) ?? null;
|
|
1768
|
+
return this.ax.updateWorkItem(id, patch, options);
|
|
1814
1769
|
}
|
|
1815
1770
|
|
|
1816
1771
|
// ── Approval gates (canvas-bound) ─────────────────────────────────
|
|
1817
1772
|
getApprovalGates(): PmxAxApprovalGate[] {
|
|
1818
|
-
return this.
|
|
1773
|
+
return this.ax.getApprovalGates();
|
|
1819
1774
|
}
|
|
1820
1775
|
|
|
1821
1776
|
requestApproval(
|
|
1822
1777
|
input: { title: string; detail?: string | null; action?: string | null; nodeIds?: string[] },
|
|
1823
1778
|
options: { source?: PmxAxSource } = {},
|
|
1824
1779
|
): PmxAxApprovalGate {
|
|
1825
|
-
|
|
1826
|
-
const gate = createAxApprovalGate(input, options.source ?? 'api', this.currentNodeIdSet());
|
|
1827
|
-
this.applyAxState({ ...oldAxState, approvalGates: [...oldAxState.approvalGates, gate] });
|
|
1828
|
-
const applied = this.getAxState();
|
|
1829
|
-
this.scheduleSave();
|
|
1830
|
-
this.notifyChange('ax');
|
|
1831
|
-
this.recordMutation({
|
|
1832
|
-
operationType: 'requestApproval',
|
|
1833
|
-
description: `Requested approval "${gate.title}"`,
|
|
1834
|
-
forward: this.suppressed(() => { this.applyAxState(applied); this.scheduleSave(); this.notifyChange('ax'); }),
|
|
1835
|
-
inverse: this.suppressed(() => { this.applyAxState(oldAxState); this.scheduleSave(); this.notifyChange('ax'); }),
|
|
1836
|
-
});
|
|
1837
|
-
return applied.approvalGates.find((g) => g.id === gate.id) ?? gate;
|
|
1780
|
+
return this.ax.requestApproval(input, options);
|
|
1838
1781
|
}
|
|
1839
1782
|
|
|
1840
1783
|
resolveApproval(
|
|
@@ -1842,32 +1785,12 @@ class CanvasStateManager {
|
|
|
1842
1785
|
decision: 'approved' | 'rejected',
|
|
1843
1786
|
options: { resolution?: string; source?: PmxAxSource } = {},
|
|
1844
1787
|
): PmxAxApprovalGate | null {
|
|
1845
|
-
|
|
1846
|
-
const gate = oldAxState.approvalGates.find((g) => g.id === id);
|
|
1847
|
-
if (!gate || gate.status !== 'pending') return null;
|
|
1848
|
-
const resolved: PmxAxApprovalGate = {
|
|
1849
|
-
...gate,
|
|
1850
|
-
status: decision,
|
|
1851
|
-
resolvedAt: new Date().toISOString(),
|
|
1852
|
-
resolution: options.resolution ?? null,
|
|
1853
|
-
source: options.source ?? gate.source,
|
|
1854
|
-
};
|
|
1855
|
-
this.applyAxState({ ...oldAxState, approvalGates: replaceById(oldAxState.approvalGates, resolved) });
|
|
1856
|
-
const applied = this.getAxState();
|
|
1857
|
-
this.scheduleSave();
|
|
1858
|
-
this.notifyChange('ax');
|
|
1859
|
-
this.recordMutation({
|
|
1860
|
-
operationType: 'resolveApproval',
|
|
1861
|
-
description: `Resolved approval ${id} -> ${decision}`,
|
|
1862
|
-
forward: this.suppressed(() => { this.applyAxState(applied); this.scheduleSave(); this.notifyChange('ax'); }),
|
|
1863
|
-
inverse: this.suppressed(() => { this.applyAxState(oldAxState); this.scheduleSave(); this.notifyChange('ax'); }),
|
|
1864
|
-
});
|
|
1865
|
-
return applied.approvalGates.find((g) => g.id === id) ?? null;
|
|
1788
|
+
return this.ax.resolveApproval(id, decision, options);
|
|
1866
1789
|
}
|
|
1867
1790
|
|
|
1868
1791
|
// ── Review annotations (canvas-bound) ─────────────────────────────
|
|
1869
1792
|
getReviewAnnotations(): PmxAxReviewAnnotation[] {
|
|
1870
|
-
return this.
|
|
1793
|
+
return this.ax.getReviewAnnotations();
|
|
1871
1794
|
}
|
|
1872
1795
|
|
|
1873
1796
|
addReviewAnnotation(
|
|
@@ -1883,33 +1806,7 @@ class CanvasStateManager {
|
|
|
1883
1806
|
},
|
|
1884
1807
|
options: { source?: PmxAxSource } = {},
|
|
1885
1808
|
): PmxAxReviewAnnotation | null {
|
|
1886
|
-
|
|
1887
|
-
// missing or unknown would otherwise be silently dropped by
|
|
1888
|
-
// normalizeAxForCurrentNodes after apply, yet still returned as a phantom
|
|
1889
|
-
// success object — false success / silent data loss. Reject instead so the
|
|
1890
|
-
// HTTP/MCP layers surface ok:false / 4xx.
|
|
1891
|
-
// Context-aware default: only fall back to a node anchor when a usable nodeId
|
|
1892
|
-
// is present; otherwise treat it as an unanchored (body-only) note so a
|
|
1893
|
-
// `{ body }`-only annotation succeeds (anchorType is documented optional).
|
|
1894
|
-
const anchorType = input.anchorType ?? (typeof input.nodeId === 'string' && input.nodeId ? 'node' : 'file');
|
|
1895
|
-
// An EXPLICIT node anchor still requires a real nodeId — reject a phantom
|
|
1896
|
-
// node-anchored review rather than silently dropping it post-apply.
|
|
1897
|
-
if (anchorType === 'node' && (typeof input.nodeId !== 'string' || !this.currentNodeIdSet().has(input.nodeId))) {
|
|
1898
|
-
return null;
|
|
1899
|
-
}
|
|
1900
|
-
const oldAxState = this.getAxState();
|
|
1901
|
-
const annotation = createAxReviewAnnotation(input, options.source ?? 'api');
|
|
1902
|
-
this.applyAxState({ ...oldAxState, reviewAnnotations: [...oldAxState.reviewAnnotations, annotation] });
|
|
1903
|
-
const applied = this.getAxState();
|
|
1904
|
-
this.scheduleSave();
|
|
1905
|
-
this.notifyChange('ax');
|
|
1906
|
-
this.recordMutation({
|
|
1907
|
-
operationType: 'addReviewAnnotation',
|
|
1908
|
-
description: `Added review ${annotation.kind} (${annotation.severity})`,
|
|
1909
|
-
forward: this.suppressed(() => { this.applyAxState(applied); this.scheduleSave(); this.notifyChange('ax'); }),
|
|
1910
|
-
inverse: this.suppressed(() => { this.applyAxState(oldAxState); this.scheduleSave(); this.notifyChange('ax'); }),
|
|
1911
|
-
});
|
|
1912
|
-
return applied.reviewAnnotations.find((r) => r.id === annotation.id) ?? annotation;
|
|
1809
|
+
return this.ax.addReviewAnnotation(input, options);
|
|
1913
1810
|
}
|
|
1914
1811
|
|
|
1915
1812
|
updateReviewAnnotation(
|
|
@@ -1917,57 +1814,23 @@ class CanvasStateManager {
|
|
|
1917
1814
|
patch: { body?: string; status?: PmxAxReviewStatus; severity?: PmxAxReviewSeverity; kind?: PmxAxReviewKind },
|
|
1918
1815
|
options: { source?: PmxAxSource } = {},
|
|
1919
1816
|
): PmxAxReviewAnnotation | null {
|
|
1920
|
-
|
|
1921
|
-
const existing = oldAxState.reviewAnnotations.find((r) => r.id === id);
|
|
1922
|
-
if (!existing) return null;
|
|
1923
|
-
const merged: PmxAxReviewAnnotation = {
|
|
1924
|
-
...existing,
|
|
1925
|
-
...(patch.body !== undefined ? { body: patch.body } : {}),
|
|
1926
|
-
...(patch.status !== undefined ? { status: patch.status } : {}),
|
|
1927
|
-
...(patch.severity !== undefined ? { severity: patch.severity } : {}),
|
|
1928
|
-
...(patch.kind !== undefined ? { kind: patch.kind } : {}),
|
|
1929
|
-
updatedAt: new Date().toISOString(),
|
|
1930
|
-
source: options.source ?? existing.source,
|
|
1931
|
-
};
|
|
1932
|
-
this.applyAxState({ ...oldAxState, reviewAnnotations: replaceById(oldAxState.reviewAnnotations, merged) });
|
|
1933
|
-
const applied = this.getAxState();
|
|
1934
|
-
this.scheduleSave();
|
|
1935
|
-
this.notifyChange('ax');
|
|
1936
|
-
this.recordMutation({
|
|
1937
|
-
operationType: 'updateReviewAnnotation',
|
|
1938
|
-
description: `Updated review ${id}`,
|
|
1939
|
-
forward: this.suppressed(() => { this.applyAxState(applied); this.scheduleSave(); this.notifyChange('ax'); }),
|
|
1940
|
-
inverse: this.suppressed(() => { this.applyAxState(oldAxState); this.scheduleSave(); this.notifyChange('ax'); }),
|
|
1941
|
-
});
|
|
1942
|
-
return applied.reviewAnnotations.find((r) => r.id === id) ?? null;
|
|
1817
|
+
return this.ax.updateReviewAnnotation(id, patch, options);
|
|
1943
1818
|
}
|
|
1944
1819
|
|
|
1945
1820
|
// ── Host capability (own table; reported by adapters) ─────────────
|
|
1946
1821
|
getHostCapability(): PmxAxHostCapability | null {
|
|
1947
|
-
return this.
|
|
1822
|
+
return this.ax.getHostCapability();
|
|
1948
1823
|
}
|
|
1949
1824
|
|
|
1950
1825
|
getElicitations(): PmxAxElicitation[] {
|
|
1951
|
-
return this.
|
|
1826
|
+
return this.ax.getElicitations();
|
|
1952
1827
|
}
|
|
1953
1828
|
|
|
1954
1829
|
requestElicitation(
|
|
1955
1830
|
input: { prompt: string; fields?: string[]; nodeIds?: string[] },
|
|
1956
1831
|
options: { source?: PmxAxSource } = {},
|
|
1957
1832
|
): PmxAxElicitation {
|
|
1958
|
-
|
|
1959
|
-
const elicitation = createAxElicitation(input, options.source ?? 'api', this.currentNodeIdSet());
|
|
1960
|
-
this.applyAxState({ ...oldAxState, elicitations: [...oldAxState.elicitations, elicitation] });
|
|
1961
|
-
const applied = this.getAxState();
|
|
1962
|
-
this.scheduleSave();
|
|
1963
|
-
this.notifyChange('ax');
|
|
1964
|
-
this.recordMutation({
|
|
1965
|
-
operationType: 'requestElicitation',
|
|
1966
|
-
description: `Requested elicitation "${elicitation.prompt}"`,
|
|
1967
|
-
forward: this.suppressed(() => { this.applyAxState(applied); this.scheduleSave(); this.notifyChange('ax'); }),
|
|
1968
|
-
inverse: this.suppressed(() => { this.applyAxState(oldAxState); this.scheduleSave(); this.notifyChange('ax'); }),
|
|
1969
|
-
});
|
|
1970
|
-
return applied.elicitations.find((e) => e.id === elicitation.id) ?? elicitation;
|
|
1833
|
+
return this.ax.requestElicitation(input, options);
|
|
1971
1834
|
}
|
|
1972
1835
|
|
|
1973
1836
|
respondElicitation(
|
|
@@ -1975,50 +1838,18 @@ class CanvasStateManager {
|
|
|
1975
1838
|
response: Record<string, unknown>,
|
|
1976
1839
|
options: { source?: PmxAxSource } = {},
|
|
1977
1840
|
): PmxAxElicitation | null {
|
|
1978
|
-
|
|
1979
|
-
const existing = oldAxState.elicitations.find((e) => e.id === id);
|
|
1980
|
-
if (!existing || existing.status !== 'pending') return null;
|
|
1981
|
-
const merged: PmxAxElicitation = {
|
|
1982
|
-
...existing,
|
|
1983
|
-
status: 'answered',
|
|
1984
|
-
response,
|
|
1985
|
-
resolvedAt: new Date().toISOString(),
|
|
1986
|
-
source: options.source ?? existing.source,
|
|
1987
|
-
};
|
|
1988
|
-
this.applyAxState({ ...oldAxState, elicitations: replaceById(oldAxState.elicitations, merged) });
|
|
1989
|
-
const applied = this.getAxState();
|
|
1990
|
-
this.scheduleSave();
|
|
1991
|
-
this.notifyChange('ax');
|
|
1992
|
-
this.recordMutation({
|
|
1993
|
-
operationType: 'respondElicitation',
|
|
1994
|
-
description: `Answered elicitation ${id}`,
|
|
1995
|
-
forward: this.suppressed(() => { this.applyAxState(applied); this.scheduleSave(); this.notifyChange('ax'); }),
|
|
1996
|
-
inverse: this.suppressed(() => { this.applyAxState(oldAxState); this.scheduleSave(); this.notifyChange('ax'); }),
|
|
1997
|
-
});
|
|
1998
|
-
return applied.elicitations.find((e) => e.id === id) ?? null;
|
|
1841
|
+
return this.ax.respondElicitation(id, response, options);
|
|
1999
1842
|
}
|
|
2000
1843
|
|
|
2001
1844
|
getModeRequests(): PmxAxModeRequest[] {
|
|
2002
|
-
return this.
|
|
1845
|
+
return this.ax.getModeRequests();
|
|
2003
1846
|
}
|
|
2004
1847
|
|
|
2005
1848
|
requestMode(
|
|
2006
1849
|
input: { mode: PmxAxMode; reason?: string | null; nodeIds?: string[] },
|
|
2007
1850
|
options: { source?: PmxAxSource } = {},
|
|
2008
1851
|
): PmxAxModeRequest {
|
|
2009
|
-
|
|
2010
|
-
const request = createAxModeRequest(input, options.source ?? 'api', this.currentNodeIdSet());
|
|
2011
|
-
this.applyAxState({ ...oldAxState, modeRequests: [...oldAxState.modeRequests, request] });
|
|
2012
|
-
const applied = this.getAxState();
|
|
2013
|
-
this.scheduleSave();
|
|
2014
|
-
this.notifyChange('ax');
|
|
2015
|
-
this.recordMutation({
|
|
2016
|
-
operationType: 'requestMode',
|
|
2017
|
-
description: `Requested mode "${request.mode}"`,
|
|
2018
|
-
forward: this.suppressed(() => { this.applyAxState(applied); this.scheduleSave(); this.notifyChange('ax'); }),
|
|
2019
|
-
inverse: this.suppressed(() => { this.applyAxState(oldAxState); this.scheduleSave(); this.notifyChange('ax'); }),
|
|
2020
|
-
});
|
|
2021
|
-
return applied.modeRequests.find((m) => m.id === request.id) ?? request;
|
|
1852
|
+
return this.ax.requestMode(input, options);
|
|
2022
1853
|
}
|
|
2023
1854
|
|
|
2024
1855
|
resolveModeRequest(
|
|
@@ -2026,85 +1857,45 @@ class CanvasStateManager {
|
|
|
2026
1857
|
decision: 'approved' | 'rejected',
|
|
2027
1858
|
options: { resolution?: string; source?: PmxAxSource } = {},
|
|
2028
1859
|
): PmxAxModeRequest | null {
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
this.
|
|
2043
|
-
this.recordMutation({
|
|
2044
|
-
operationType: 'resolveModeRequest',
|
|
2045
|
-
description: `Resolved mode request ${id} -> ${decision}`,
|
|
2046
|
-
forward: this.suppressed(() => { this.applyAxState(applied); this.scheduleSave(); this.notifyChange('ax'); }),
|
|
2047
|
-
inverse: this.suppressed(() => { this.applyAxState(oldAxState); this.scheduleSave(); this.notifyChange('ax'); }),
|
|
2048
|
-
});
|
|
2049
|
-
return applied.modeRequests.find((m) => m.id === id) ?? null;
|
|
1860
|
+
return this.ax.resolveModeRequest(id, decision, options);
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
// ── Single-item AX readers (canvas-bound; for the blocking-wait endpoints) ──
|
|
1864
|
+
getApproval(id: string): PmxAxApprovalGate | null {
|
|
1865
|
+
return this.ax.getApproval(id);
|
|
1866
|
+
}
|
|
1867
|
+
|
|
1868
|
+
getElicitation(id: string): PmxAxElicitation | null {
|
|
1869
|
+
return this.ax.getElicitation(id);
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
getModeRequest(id: string): PmxAxModeRequest | null {
|
|
1873
|
+
return this.ax.getModeRequest(id);
|
|
2050
1874
|
}
|
|
2051
1875
|
|
|
2052
1876
|
getCommandRegistry(): PmxAxCommandDescriptor[] {
|
|
2053
|
-
return
|
|
1877
|
+
return this.ax.getCommandRegistry();
|
|
2054
1878
|
}
|
|
2055
1879
|
|
|
2056
1880
|
/** Invoke a registry-gated PMX command intent — records a timeline event (no execution). */
|
|
2057
1881
|
invokeCommand(name: string, args: Record<string, unknown> | null = null, options: { source?: PmxAxSource } = {}): PmxAxEvent | null {
|
|
2058
|
-
|
|
2059
|
-
return this.recordAxEvent(
|
|
2060
|
-
{ kind: 'command', summary: name, detail: AX_COMMAND_REGISTRY[name].description, data: { command: name, ...(args ? { args } : {}) } },
|
|
2061
|
-
options,
|
|
2062
|
-
);
|
|
1882
|
+
return this.ax.invokeCommand(name, args, options);
|
|
2063
1883
|
}
|
|
2064
1884
|
|
|
2065
1885
|
getPolicy(): PmxAxPolicy {
|
|
2066
|
-
return this.
|
|
1886
|
+
return this.ax.getPolicy();
|
|
2067
1887
|
}
|
|
2068
1888
|
|
|
2069
1889
|
/** Merge a declarative tool/prompt policy patch (canvas-bound, snapshotted). */
|
|
2070
1890
|
setPolicy(
|
|
2071
1891
|
patch: { tools?: Partial<PmxAxPolicy['tools']>; prompt?: Partial<PmxAxPolicy['prompt']> },
|
|
2072
|
-
|
|
1892
|
+
options: { source?: PmxAxSource } = {},
|
|
2073
1893
|
): PmxAxPolicy {
|
|
2074
|
-
|
|
2075
|
-
const merged = normalizeAxPolicy({
|
|
2076
|
-
tools: { ...oldAxState.policy.tools, ...(patch.tools ?? {}) },
|
|
2077
|
-
prompt: { ...oldAxState.policy.prompt, ...(patch.prompt ?? {}) },
|
|
2078
|
-
});
|
|
2079
|
-
this.applyAxState({ ...oldAxState, policy: merged });
|
|
2080
|
-
const applied = this.getAxState();
|
|
2081
|
-
this.scheduleSave();
|
|
2082
|
-
this.notifyChange('ax');
|
|
2083
|
-
this.recordMutation({
|
|
2084
|
-
operationType: 'setPolicy',
|
|
2085
|
-
description: 'Updated AX policy',
|
|
2086
|
-
forward: this.suppressed(() => { this.applyAxState(applied); this.scheduleSave(); this.notifyChange('ax'); }),
|
|
2087
|
-
inverse: this.suppressed(() => { this.applyAxState(oldAxState); this.scheduleSave(); this.notifyChange('ax'); }),
|
|
2088
|
-
});
|
|
2089
|
-
return applied.policy;
|
|
1894
|
+
return this.ax.setPolicy(patch, options);
|
|
2090
1895
|
}
|
|
2091
1896
|
|
|
2092
|
-
setHostCapability(input: unknown,
|
|
2093
|
-
|
|
2094
|
-
isRecord(input)
|
|
2095
|
-
? { ...input, reportedAt: new Date().toISOString() }
|
|
2096
|
-
: { reportedAt: new Date().toISOString() },
|
|
2097
|
-
) ?? createEmptyAxHostCapability();
|
|
2098
|
-
this._axHostCapability = cap;
|
|
2099
|
-
if (this._db) {
|
|
2100
|
-
try {
|
|
2101
|
-
upsertAxHostCapabilityToDB(this._db, cap);
|
|
2102
|
-
} catch (error) {
|
|
2103
|
-
logCanvasStateWarning('save host capability failed', error, {});
|
|
2104
|
-
}
|
|
2105
|
-
}
|
|
2106
|
-
this.notifyChange('ax');
|
|
2107
|
-
return cap;
|
|
1897
|
+
setHostCapability(input: unknown, options: { source?: PmxAxSource } = {}): PmxAxHostCapability {
|
|
1898
|
+
return this.ax.setHostCapability(input, options);
|
|
2108
1899
|
}
|
|
2109
1900
|
|
|
2110
1901
|
// ── Timeline (DB-direct; NOT in _axState; NOT history-recorded) ───
|
|
@@ -2112,99 +1903,66 @@ class CanvasStateManager {
|
|
|
2112
1903
|
input: { kind: PmxAxEventKind; summary: string; detail?: string | null; nodeIds?: string[]; data?: Record<string, unknown> | null },
|
|
2113
1904
|
options: { source?: PmxAxSource } = {},
|
|
2114
1905
|
): PmxAxEvent {
|
|
2115
|
-
|
|
2116
|
-
if (this._db) {
|
|
2117
|
-
try {
|
|
2118
|
-
const ev = appendAxEventToDB(this._db, draft);
|
|
2119
|
-
this.notifyChange('ax-timeline');
|
|
2120
|
-
return ev;
|
|
2121
|
-
} catch (error) {
|
|
2122
|
-
logCanvasStateWarning('record ax event failed', error, { id: draft.id });
|
|
2123
|
-
}
|
|
2124
|
-
}
|
|
2125
|
-
this.notifyChange('ax-timeline');
|
|
2126
|
-
return { ...draft, seq: 0 };
|
|
1906
|
+
return this.ax.recordAxEvent(input, options);
|
|
2127
1907
|
}
|
|
2128
1908
|
|
|
2129
1909
|
addEvidence(
|
|
2130
1910
|
input: { kind: PmxAxEvidenceKind; title: string; body?: string | null; ref?: string | null; nodeIds?: string[]; data?: Record<string, unknown> | null },
|
|
2131
1911
|
options: { source?: PmxAxSource } = {},
|
|
2132
1912
|
): PmxAxEvidence {
|
|
2133
|
-
|
|
2134
|
-
if (this._db) {
|
|
2135
|
-
try {
|
|
2136
|
-
const ev = appendAxEvidenceToDB(this._db, draft);
|
|
2137
|
-
this.notifyChange('ax-timeline');
|
|
2138
|
-
return ev;
|
|
2139
|
-
} catch (error) {
|
|
2140
|
-
logCanvasStateWarning('add evidence failed', error, { id: draft.id });
|
|
2141
|
-
}
|
|
2142
|
-
}
|
|
2143
|
-
this.notifyChange('ax-timeline');
|
|
2144
|
-
return { ...draft, seq: 0 };
|
|
1913
|
+
return this.ax.addEvidence(input, options);
|
|
2145
1914
|
}
|
|
2146
1915
|
|
|
2147
1916
|
recordSteeringMessage(message: string, options: { source?: PmxAxSource } = {}): PmxAxSteeringMessage {
|
|
2148
|
-
|
|
2149
|
-
if (this._db) {
|
|
2150
|
-
try {
|
|
2151
|
-
const s = appendAxSteeringToDB(this._db, draft);
|
|
2152
|
-
this.notifyChange('ax-timeline');
|
|
2153
|
-
return s;
|
|
2154
|
-
} catch (error) {
|
|
2155
|
-
logCanvasStateWarning('record steering failed', error, { id: draft.id });
|
|
2156
|
-
}
|
|
2157
|
-
}
|
|
2158
|
-
this.notifyChange('ax-timeline');
|
|
2159
|
-
return { ...draft, seq: 0 };
|
|
1917
|
+
return this.ax.recordSteeringMessage(message, options);
|
|
2160
1918
|
}
|
|
2161
1919
|
|
|
2162
1920
|
markSteeringDelivered(id: string): boolean {
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
1921
|
+
return this.ax.markSteeringDelivered(id);
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
ingestActivity(
|
|
1925
|
+
input: {
|
|
1926
|
+
kind: PmxAxActivityKind;
|
|
1927
|
+
title: string;
|
|
1928
|
+
summary?: string | null;
|
|
1929
|
+
outcome?: 'success' | 'failure';
|
|
1930
|
+
ref?: string | null;
|
|
1931
|
+
nodeIds?: string[];
|
|
1932
|
+
data?: Record<string, unknown> | null;
|
|
1933
|
+
reactions?: {
|
|
1934
|
+
workItem?: false | { status?: PmxAxWorkItemStatus; detail?: string | null };
|
|
1935
|
+
evidence?: false | { kind?: PmxAxEvidenceKind; body?: string | null };
|
|
1936
|
+
review?: false | { severity?: PmxAxReviewSeverity; kind?: PmxAxReviewKind; anchorType?: PmxAxReviewAnchorType; nodeId?: string | null };
|
|
1937
|
+
};
|
|
1938
|
+
},
|
|
1939
|
+
options: { source?: PmxAxSource } = {},
|
|
1940
|
+
): { event: PmxAxEvent; workItem: PmxAxWorkItem | null; evidence: PmxAxEvidence | null; review: PmxAxReviewAnnotation | null } {
|
|
1941
|
+
return this.ax.ingestActivity(input, options);
|
|
2172
1942
|
}
|
|
2173
1943
|
|
|
2174
1944
|
getAxEvents(q: AxTimelineQuery = {}): PmxAxEvent[] {
|
|
2175
|
-
return this.
|
|
1945
|
+
return this.ax.getAxEvents(q);
|
|
2176
1946
|
}
|
|
2177
1947
|
|
|
2178
1948
|
getAxEvidence(q: AxTimelineQuery = {}): PmxAxEvidence[] {
|
|
2179
|
-
return this.
|
|
1949
|
+
return this.ax.getAxEvidence(q);
|
|
2180
1950
|
}
|
|
2181
1951
|
|
|
2182
1952
|
getAxSteering(q: AxTimelineQuery & { onlyPending?: boolean } = {}): PmxAxSteeringMessage[] {
|
|
2183
|
-
return this.
|
|
1953
|
+
return this.ax.getAxSteering(q);
|
|
2184
1954
|
}
|
|
2185
1955
|
|
|
2186
|
-
/**
|
|
2187
|
-
* Undelivered steering for a consumer (Phase 4 delivery). Excludes messages
|
|
2188
|
-
* whose source equals the consumer to prevent delivery loops (e.g. Copilot
|
|
2189
|
-
* should not be handed back steering it originated).
|
|
2190
|
-
*/
|
|
2191
1956
|
getPendingSteering(options: { consumer?: string; limit?: number } = {}): PmxAxSteeringMessage[] {
|
|
2192
|
-
return this.
|
|
1957
|
+
return this.ax.getPendingSteering(options);
|
|
2193
1958
|
}
|
|
2194
1959
|
|
|
2195
1960
|
getAxTimelineSummary(): PmxAxTimelineSummary {
|
|
2196
|
-
return this.
|
|
2197
|
-
? loadAxTimelineSummaryFromDB(this._db)
|
|
2198
|
-
: { recentEvents: [], recentEvidence: [], pendingSteering: [], counts: { events: 0, evidence: 0, steering: 0 } };
|
|
1961
|
+
return this.ax.getAxTimelineSummary();
|
|
2199
1962
|
}
|
|
2200
1963
|
|
|
2201
1964
|
getAxTimeline(q: AxTimelineQuery = {}): { events: PmxAxEvent[]; evidence: PmxAxEvidence[]; steering: PmxAxSteeringMessage[]; summary: PmxAxTimelineSummary } {
|
|
2202
|
-
return
|
|
2203
|
-
events: this.getAxEvents(q),
|
|
2204
|
-
evidence: this.getAxEvidence(q),
|
|
2205
|
-
steering: this.getAxSteering(q),
|
|
2206
|
-
summary: this.getAxTimelineSummary(),
|
|
2207
|
-
};
|
|
1965
|
+
return this.ax.getAxTimeline(q);
|
|
2208
1966
|
}
|
|
2209
1967
|
|
|
2210
1968
|
setContextPins(nodeIds: string[]): void {
|
|
@@ -2341,7 +2099,7 @@ class CanvasStateManager {
|
|
|
2341
2099
|
// Clears canvas-bound AX state (focus, work items, approvals, review annotations).
|
|
2342
2100
|
// Timeline tables (ax_events/ax_evidence/ax_steering) and host capability are
|
|
2343
2101
|
// deliberately retained per the AX state-partition policy.
|
|
2344
|
-
this.
|
|
2102
|
+
this.ax.resetCanvasBound();
|
|
2345
2103
|
this._viewport = { x: 0, y: 0, scale: 1 };
|
|
2346
2104
|
this.scheduleSave();
|
|
2347
2105
|
this.notifyChange('nodes');
|
|
@@ -2356,7 +2114,7 @@ class CanvasStateManager {
|
|
|
2356
2114
|
for (const e of oldEdges) this.addEdge(structuredClone(e));
|
|
2357
2115
|
for (const annotation of oldAnnotations) this.addAnnotation(structuredClone(annotation));
|
|
2358
2116
|
this.setContextPins(oldPins);
|
|
2359
|
-
this.
|
|
2117
|
+
this.ax.applyPersistedAx(oldAxState);
|
|
2360
2118
|
this.setViewport(oldViewport);
|
|
2361
2119
|
this.notifyChange('ax');
|
|
2362
2120
|
}),
|