pmx-canvas 0.1.36 → 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.
Files changed (85) hide show
  1. package/CHANGELOG.md +409 -0
  2. package/Readme.md +2 -2
  3. package/dist/json-render/index.js +89 -334
  4. package/dist/types/mcp/canvas-access.d.ts +5 -171
  5. package/dist/types/server/ax-state-manager.d.ts +256 -0
  6. package/dist/types/server/ax-state.d.ts +1 -1
  7. package/dist/types/server/canvas-operations.d.ts +1 -12
  8. package/dist/types/server/canvas-state.d.ts +3 -23
  9. package/dist/types/server/index.d.ts +6 -24
  10. package/dist/types/server/operations/composites.d.ts +121 -0
  11. package/dist/types/server/operations/http.d.ts +7 -0
  12. package/dist/types/server/operations/index.d.ts +8 -0
  13. package/dist/types/server/operations/invoker.d.ts +13 -0
  14. package/dist/types/server/operations/mcp.d.ts +15 -0
  15. package/dist/types/server/operations/ops/annotation.d.ts +2 -0
  16. package/dist/types/server/operations/ops/app.d.ts +33 -0
  17. package/dist/types/server/operations/ops/ax-await.d.ts +2 -0
  18. package/dist/types/server/operations/ops/ax-shared.d.ts +31 -0
  19. package/dist/types/server/operations/ops/ax-state.d.ts +2 -0
  20. package/dist/types/server/operations/ops/ax-timeline.d.ts +2 -0
  21. package/dist/types/server/operations/ops/ax-work.d.ts +2 -0
  22. package/dist/types/server/operations/ops/batch.d.ts +19 -0
  23. package/dist/types/server/operations/ops/edges.d.ts +2 -0
  24. package/dist/types/server/operations/ops/groups.d.ts +2 -0
  25. package/dist/types/server/operations/ops/json-render.d.ts +31 -0
  26. package/dist/types/server/operations/ops/nodes.d.ts +62 -0
  27. package/dist/types/server/operations/ops/query.d.ts +2 -0
  28. package/dist/types/server/operations/ops/snapshots.d.ts +2 -0
  29. package/dist/types/server/operations/ops/validate.d.ts +2 -0
  30. package/dist/types/server/operations/ops/viewport.d.ts +2 -0
  31. package/dist/types/server/operations/ops/webview.d.ts +2 -0
  32. package/dist/types/server/operations/registry.d.ts +15 -0
  33. package/dist/types/server/operations/types.d.ts +116 -0
  34. package/dist/types/server/operations/webview-runner.d.ts +69 -0
  35. package/docs/RELEASE.md +5 -0
  36. package/docs/adr-001-bun-only-runtime.md +46 -0
  37. package/docs/api-stability.md +57 -0
  38. package/docs/ax-state-contract.md +72 -0
  39. package/docs/mcp.md +60 -11
  40. package/docs/plans/plan-005-operation-registry.md +84 -0
  41. package/docs/plans/plan-006-mcp-tool-consolidation.md +109 -0
  42. package/docs/plans/plan-007-ax-domain.md +99 -0
  43. package/docs/plans/plan-008-registry-finish.md +91 -0
  44. package/docs/tech-debt-assessment-2026-06.md +90 -0
  45. package/package.json +3 -3
  46. package/skills/pmx-canvas/SKILL.md +192 -186
  47. package/skills/pmx-canvas/evals/evals.json +3 -3
  48. package/skills/pmx-canvas/references/codex-app-adapter.md +13 -14
  49. package/skills/pmx-canvas/references/github-copilot-app-adapter.md +4 -5
  50. package/src/cli/agent.ts +52 -31
  51. package/src/mcp/canvas-access.ts +30 -830
  52. package/src/mcp/server.ts +162 -2014
  53. package/src/server/ax-state-manager.ts +808 -0
  54. package/src/server/ax-state.ts +2 -2
  55. package/src/server/canvas-operations.ts +2 -328
  56. package/src/server/canvas-schema.ts +2 -2
  57. package/src/server/canvas-state.ts +95 -465
  58. package/src/server/index.ts +54 -190
  59. package/src/server/operations/composites.ts +355 -0
  60. package/src/server/operations/http.ts +103 -0
  61. package/src/server/operations/index.ts +65 -0
  62. package/src/server/operations/invoker.ts +87 -0
  63. package/src/server/operations/mcp.ts +221 -0
  64. package/src/server/operations/ops/annotation.ts +60 -0
  65. package/src/server/operations/ops/app.ts +447 -0
  66. package/src/server/operations/ops/ax-await.ts +216 -0
  67. package/src/server/operations/ops/ax-shared.ts +38 -0
  68. package/src/server/operations/ops/ax-state.ts +249 -0
  69. package/src/server/operations/ops/ax-timeline.ts +381 -0
  70. package/src/server/operations/ops/ax-work.ts +635 -0
  71. package/src/server/operations/ops/batch.ts +365 -0
  72. package/src/server/operations/ops/edges.ts +166 -0
  73. package/src/server/operations/ops/groups.ts +176 -0
  74. package/src/server/operations/ops/json-render.ts +691 -0
  75. package/src/server/operations/ops/nodes.ts +1047 -0
  76. package/src/server/operations/ops/query.ts +281 -0
  77. package/src/server/operations/ops/snapshots.ts +366 -0
  78. package/src/server/operations/ops/validate.ts +37 -0
  79. package/src/server/operations/ops/viewport.ts +219 -0
  80. package/src/server/operations/ops/webview.ts +339 -0
  81. package/src/server/operations/registry.ts +79 -0
  82. package/src/server/operations/types.ts +150 -0
  83. package/src/server/operations/webview-runner.ts +77 -0
  84. package/src/server/server.ts +158 -2255
  85. package/src/server/web-artifacts.ts +6 -2
@@ -13,7 +13,7 @@ export interface PmxAxFocusState {
13
13
  // ── New enums ──────────────────────────────────────────────────────
14
14
  export type PmxAxEventKind =
15
15
  | 'prompt' | 'assistant-message' | 'tool-start' | 'tool-result'
16
- | 'failure' | 'approval' | 'steering' | 'command';
16
+ | 'failure' | 'approval' | 'steering' | 'command' | 'note';
17
17
  export type PmxAxEvidenceKind =
18
18
  | 'logs' | 'tool-result' | 'screenshot' | 'file' | 'diff' | 'test-output';
19
19
  export type PmxAxWorkItemStatus = 'todo' | 'in-progress' | 'blocked' | 'done' | 'cancelled';
@@ -263,7 +263,7 @@ function normalizeNodeIds(value: unknown, validNodeIds?: Set<string>): string[]
263
263
  return ids;
264
264
  }
265
265
 
266
- const AX_EVENT_KINDS = new Set<PmxAxEventKind>(['prompt', 'assistant-message', 'tool-start', 'tool-result', 'failure', 'approval', 'steering', 'command']);
266
+ const AX_EVENT_KINDS = new Set<PmxAxEventKind>(['prompt', 'assistant-message', 'tool-start', 'tool-result', 'failure', 'approval', 'steering', 'command', 'note']);
267
267
 
268
268
  // ── Activity ingestion (harness-forwarded tool/session events) ─────
269
269
  // A normalized activity the agent's harness forwards; the board auto-reacts.
@@ -19,7 +19,7 @@ import {
19
19
  import { mutationHistory } from './mutation-history.js';
20
20
  import { computeGroupBounds, findOpenCanvasPosition } from './placement.js';
21
21
  import { searchNodes } from './spatial-analysis.js';
22
- import { getCanvasNodeTitle, serializeCanvasNodeCompact, type SerializedCanvasNode } from './canvas-serialization.js';
22
+ import { getCanvasNodeTitle } from './canvas-serialization.js';
23
23
  import { computeAutoArrange } from '../shared/auto-arrange.js';
24
24
  import {
25
25
  applyJsonRenderStreamPatches,
@@ -52,7 +52,7 @@ export function setCanvasLayoutUpdateEmitter(emitter: (() => void) | null): void
52
52
  canvasLayoutUpdateEmitter = emitter;
53
53
  }
54
54
 
55
- function emitCanvasLayoutUpdate(): void {
55
+ export function emitCanvasLayoutUpdate(): void {
56
56
  canvasLayoutUpdateEmitter?.();
57
57
  }
58
58
 
@@ -125,12 +125,6 @@ interface CanvasCreateGroupInput {
125
125
  childLayout?: CanvasArrangeMode;
126
126
  }
127
127
 
128
- export interface CanvasBatchOperation {
129
- op: string;
130
- assign?: string;
131
- args?: Record<string, unknown>;
132
- }
133
-
134
128
  interface CanvasNodeLookupInput {
135
129
  id?: string;
136
130
  search?: string;
@@ -1659,323 +1653,3 @@ export function fitCanvasView(options: CanvasFitViewOptions = {}): CanvasFitView
1659
1653
  canvasState.setViewport(viewport);
1660
1654
  return { ok: true, viewport: canvasState.viewport, nodeCount: targetNodes.length, bounds };
1661
1655
  }
1662
-
1663
- function isPlainRecord(value: unknown): value is Record<string, unknown> {
1664
- return !!value && typeof value === 'object' && !Array.isArray(value);
1665
- }
1666
-
1667
- function resolveBatchRefs(value: unknown, refs: Record<string, unknown>): unknown {
1668
- if (typeof value === 'string' && value.startsWith('$')) {
1669
- const path = value.slice(1).split('.');
1670
- let current: unknown = refs[path[0] ?? ''];
1671
- if (path.length === 1 && isPlainRecord(current) && typeof current.id === 'string') return current.id;
1672
- for (const segment of path.slice(1)) {
1673
- if (!isPlainRecord(current) && !Array.isArray(current)) return undefined;
1674
- current = (current as Record<string, unknown>)[segment];
1675
- }
1676
- return current;
1677
- }
1678
- if (Array.isArray(value)) return value.map((item) => resolveBatchRefs(item, refs));
1679
- if (isPlainRecord(value)) {
1680
- const resolved: Record<string, unknown> = {};
1681
- for (const [key, child] of Object.entries(value)) {
1682
- resolved[key] = resolveBatchRefs(child, refs);
1683
- }
1684
- return resolved;
1685
- }
1686
- return value;
1687
- }
1688
-
1689
- function serializeCreatedNode(node: CanvasNodeState): SerializedCanvasNode {
1690
- return serializeCanvasNodeCompact(node);
1691
- }
1692
-
1693
- export async function executeCanvasBatch(
1694
- operations: CanvasBatchOperation[],
1695
- ): Promise<{
1696
- ok: boolean;
1697
- results: Array<Record<string, unknown>>;
1698
- refs: Record<string, unknown>;
1699
- failedIndex?: number;
1700
- error?: string;
1701
- }> {
1702
- const refs: Record<string, unknown> = {};
1703
- const results: Array<Record<string, unknown>> = [];
1704
-
1705
- for (let index = 0; index < operations.length; index++) {
1706
- const operation = operations[index];
1707
- const args = isPlainRecord(operation.args) ? resolveBatchRefs(operation.args, refs) : {};
1708
- if (!isPlainRecord(args)) {
1709
- return {
1710
- ok: false,
1711
- failedIndex: index,
1712
- error: `Operation ${index} has invalid args.`,
1713
- results,
1714
- refs,
1715
- };
1716
- }
1717
-
1718
- try {
1719
- let result: Record<string, unknown>;
1720
- switch (operation.op) {
1721
- case 'node.add': {
1722
- const type = typeof args.type === 'string' ? args.type : 'markdown';
1723
- if (type === 'html-primitive') {
1724
- throw new Error('Batch html-primitive creation is not supported yet. Use node.add with type "html" and generated html, or create the primitive through MCP/HTTP/CLI first.');
1725
- }
1726
- if (type === 'webpage') {
1727
- const content = typeof args.url === 'string' && args.url.trim().length > 0
1728
- ? args.url
1729
- : typeof args.content === 'string'
1730
- ? args.content
1731
- : undefined;
1732
- const created = addCanvasNode({
1733
- type: 'webpage',
1734
- ...(typeof args.title === 'string' ? { title: args.title } : {}),
1735
- ...(content ? { content } : {}),
1736
- ...(isPlainRecord(args.data) ? { data: args.data } : {}),
1737
- ...(typeof args.x === 'number' ? { x: args.x } : {}),
1738
- ...(typeof args.y === 'number' ? { y: args.y } : {}),
1739
- ...(typeof args.width === 'number' ? { width: args.width } : {}),
1740
- ...(typeof args.height === 'number' ? { height: args.height } : {}),
1741
- ...(args.strictSize === true ? { strictSize: true } : {}),
1742
- defaultWidth: 520,
1743
- defaultHeight: 420,
1744
- });
1745
- const fetch = await refreshCanvasWebpageNode(created.id);
1746
- const refreshed = canvasState.getNode(created.id) ?? created.node;
1747
- result = {
1748
- ok: true,
1749
- ...serializeCreatedNode(refreshed),
1750
- fetch: fetch.ok
1751
- ? { ok: true }
1752
- : { ok: false, error: fetch.error ?? 'Failed to fetch webpage content.' },
1753
- ...(fetch.ok ? {} : { error: fetch.error }),
1754
- };
1755
- } else {
1756
- const data = isPlainRecord(args.data) ? args.data : {};
1757
- const htmlData = type === 'html'
1758
- ? {
1759
- ...data,
1760
- ...(typeof args.html === 'string' ? { html: args.html } : {}),
1761
- ...(typeof args.summary === 'string' ? { summary: args.summary } : {}),
1762
- ...(typeof args.agentSummary === 'string' ? { agentSummary: args.agentSummary } : {}),
1763
- ...(typeof args.description === 'string' ? { description: args.description } : {}),
1764
- ...(Array.isArray(args.embeddedNodeIds) ? { embeddedNodeIds: args.embeddedNodeIds } : {}),
1765
- ...(Array.isArray(args.embeddedUrls) ? { embeddedUrls: args.embeddedUrls } : {}),
1766
- }
1767
- : data;
1768
- const created = addCanvasNode({
1769
- type: type as CanvasNodeState['type'],
1770
- ...(typeof args.title === 'string' ? { title: args.title } : {}),
1771
- ...(typeof args.content === 'string' ? { content: args.content } : {}),
1772
- ...(Object.keys(htmlData).length > 0 ? { data: htmlData } : {}),
1773
- ...(typeof args.x === 'number' ? { x: args.x } : {}),
1774
- ...(typeof args.y === 'number' ? { y: args.y } : {}),
1775
- ...(typeof args.width === 'number' ? { width: args.width } : {}),
1776
- ...(typeof args.height === 'number' ? { height: args.height } : {}),
1777
- ...(args.strictSize === true ? { strictSize: true } : {}),
1778
- defaultWidth: type === 'html'
1779
- ? 720
1780
- : type === 'markdown'
1781
- ? MARKDOWN_NODE_DEFAULT_SIZE.width
1782
- : type === 'mcp-app'
1783
- ? MCP_APP_NODE_DEFAULT_SIZE.width
1784
- : type === 'image'
1785
- ? IMAGE_NODE_DEFAULT_SIZE.width
1786
- : type === 'ledger'
1787
- ? LEDGER_NODE_DEFAULT_SIZE.width
1788
- : 360,
1789
- defaultHeight: type === 'html'
1790
- ? 640
1791
- : type === 'markdown'
1792
- ? MARKDOWN_NODE_DEFAULT_SIZE.height
1793
- : type === 'mcp-app'
1794
- ? MCP_APP_NODE_DEFAULT_SIZE.height
1795
- : type === 'image'
1796
- ? IMAGE_NODE_DEFAULT_SIZE.height
1797
- : type === 'ledger'
1798
- ? LEDGER_NODE_DEFAULT_SIZE.height
1799
- : 200,
1800
- fileMode: 'auto',
1801
- });
1802
- result = { ok: true, ...serializeCreatedNode(created.node) };
1803
- }
1804
- break;
1805
- }
1806
- case 'node.update': {
1807
- const id = typeof args.id === 'string' ? args.id : '';
1808
- const node = canvasState.getNode(id);
1809
- if (!node) throw new Error(`Node "${id}" not found.`);
1810
- const patch: Partial<CanvasNodeState> = {};
1811
- if (typeof args.x === 'number' || typeof args.y === 'number') {
1812
- patch.position = {
1813
- x: typeof args.x === 'number' ? args.x : node.position.x,
1814
- y: typeof args.y === 'number' ? args.y : node.position.y,
1815
- };
1816
- }
1817
- if (typeof args.width === 'number' || typeof args.height === 'number') {
1818
- patch.size = {
1819
- width: typeof args.width === 'number' ? args.width : node.size.width,
1820
- height: typeof args.height === 'number' ? args.height : node.size.height,
1821
- };
1822
- }
1823
- if (typeof args.collapsed === 'boolean') patch.collapsed = args.collapsed;
1824
- if (typeof args.pinned === 'boolean') patch.pinned = args.pinned;
1825
- if (args.dockPosition === null || args.dockPosition === 'left' || args.dockPosition === 'right') {
1826
- patch.dockPosition = args.dockPosition;
1827
- }
1828
- if (typeof args.title === 'string' || typeof args.content === 'string' || typeof args.arrangeLocked === 'boolean' || typeof args.strictSize === 'boolean' || isPlainRecord(args.data)) {
1829
- patch.data = {
1830
- ...node.data,
1831
- ...(typeof args.title === 'string' ? { title: args.title } : {}),
1832
- ...(typeof args.content === 'string' ? { content: args.content } : {}),
1833
- ...(typeof args.arrangeLocked === 'boolean' ? { arrangeLocked: args.arrangeLocked } : {}),
1834
- ...(typeof args.strictSize === 'boolean' ? { strictSize: args.strictSize } : {}),
1835
- ...(isPlainRecord(args.data) ? args.data : {}),
1836
- };
1837
- }
1838
- canvasState.updateNode(id, patch);
1839
- const updated = canvasState.getNode(id);
1840
- result = { ok: true, ...(updated ? serializeCreatedNode(updated) : { id }) };
1841
- break;
1842
- }
1843
- case 'node.remove': {
1844
- const id = typeof args.id === 'string' ? args.id : '';
1845
- const { removed } = removeCanvasNode(id);
1846
- if (!removed) throw new Error(`Node "${id}" not found.`);
1847
- result = { ok: true, id, removed: true };
1848
- break;
1849
- }
1850
- case 'graph.add': {
1851
- const created = createCanvasGraphNode({
1852
- graphType: String(args.graphType ?? 'line'),
1853
- data: Array.isArray(args.data) ? args.data.filter((item): item is Record<string, unknown> => isPlainRecord(item)) : [],
1854
- ...(typeof args.title === 'string' ? { title: args.title } : {}),
1855
- ...(typeof args.xKey === 'string' ? { xKey: args.xKey } : {}),
1856
- ...(typeof args.yKey === 'string' ? { yKey: args.yKey } : {}),
1857
- ...(typeof args.zKey === 'string' ? { zKey: args.zKey } : {}),
1858
- ...(typeof args.nameKey === 'string' ? { nameKey: args.nameKey } : {}),
1859
- ...(typeof args.valueKey === 'string' ? { valueKey: args.valueKey } : {}),
1860
- ...(typeof args.axisKey === 'string' ? { axisKey: args.axisKey } : {}),
1861
- ...(Array.isArray(args.metrics) ? { metrics: args.metrics.filter((m): m is string => typeof m === 'string') } : {}),
1862
- ...(Array.isArray(args.series) ? { series: args.series.filter((s): s is string => typeof s === 'string') } : {}),
1863
- ...(typeof args.barKey === 'string' ? { barKey: args.barKey } : {}),
1864
- ...(typeof args.lineKey === 'string' ? { lineKey: args.lineKey } : {}),
1865
- ...(args.aggregate === 'sum' || args.aggregate === 'count' || args.aggregate === 'avg'
1866
- ? { aggregate: args.aggregate }
1867
- : {}),
1868
- ...(typeof args.color === 'string' ? { color: args.color } : {}),
1869
- ...(typeof args.barColor === 'string' ? { barColor: args.barColor } : {}),
1870
- ...(typeof args.lineColor === 'string' ? { lineColor: args.lineColor } : {}),
1871
- ...(typeof args.height === 'number' ? { height: args.height } : {}),
1872
- ...(typeof args.x === 'number' ? { x: args.x } : {}),
1873
- ...(typeof args.y === 'number' ? { y: args.y } : {}),
1874
- ...(typeof args.width === 'number' ? { width: args.width } : {}),
1875
- ...(typeof args.nodeHeight === 'number' ? { heightPx: args.nodeHeight } : {}),
1876
- ...(args.strictSize === true ? { strictSize: true } : {}),
1877
- });
1878
- result = {
1879
- ok: true,
1880
- ...serializeCreatedNode(created.node),
1881
- url: created.url,
1882
- spec: created.spec,
1883
- };
1884
- break;
1885
- }
1886
- case 'edge.add': {
1887
- const added = addCanvasEdge({
1888
- ...(typeof args.from === 'string' ? { from: args.from } : {}),
1889
- ...(typeof args.to === 'string' ? { to: args.to } : {}),
1890
- ...(typeof args.fromSearch === 'string' ? { fromSearch: args.fromSearch } : {}),
1891
- ...(typeof args.toSearch === 'string' ? { toSearch: args.toSearch } : {}),
1892
- type: String(args.type) as CanvasEdge['type'],
1893
- ...(typeof args.label === 'string' ? { label: args.label } : {}),
1894
- ...(typeof args.style === 'string' ? { style: args.style as CanvasEdge['style'] } : {}),
1895
- ...(typeof args.animated === 'boolean' ? { animated: args.animated } : {}),
1896
- });
1897
- result = { ok: true, ...added };
1898
- break;
1899
- }
1900
- case 'group.create': {
1901
- const created = createCanvasGroup({
1902
- ...(typeof args.title === 'string' ? { title: args.title } : {}),
1903
- ...(Array.isArray(args.childIds) ? { childIds: args.childIds.filter((id): id is string => typeof id === 'string') } : {}),
1904
- ...(typeof args.x === 'number' ? { x: args.x } : {}),
1905
- ...(typeof args.y === 'number' ? { y: args.y } : {}),
1906
- ...(typeof args.width === 'number' ? { width: args.width } : {}),
1907
- ...(typeof args.height === 'number' ? { height: args.height } : {}),
1908
- ...(typeof args.color === 'string' ? { color: args.color } : {}),
1909
- ...(args.childLayout === 'grid' || args.childLayout === 'column' || args.childLayout === 'flow'
1910
- ? { childLayout: args.childLayout }
1911
- : {}),
1912
- });
1913
- result = { ok: true, ...serializeCreatedNode(created.node) };
1914
- break;
1915
- }
1916
- case 'group.add': {
1917
- const groupId = typeof args.groupId === 'string' ? args.groupId : '';
1918
- const childIds = Array.isArray(args.childIds) ? args.childIds.filter((id): id is string => typeof id === 'string') : [];
1919
- const ok = canvasState.groupNodes(groupId, childIds, {
1920
- preservePositions: args.childLayout === undefined,
1921
- ...(args.childLayout === 'grid' || args.childLayout === 'column' || args.childLayout === 'flow'
1922
- ? { layout: args.childLayout }
1923
- : {}),
1924
- });
1925
- if (!ok) throw new Error('Group not found or no valid children.');
1926
- const group = canvasState.getNode(groupId);
1927
- result = { ok: true, ...(group ? serializeCreatedNode(group) : { id: groupId }) };
1928
- break;
1929
- }
1930
- case 'group.remove': {
1931
- const groupId = typeof args.groupId === 'string' ? args.groupId : '';
1932
- const ok = canvasState.ungroupNodes(groupId);
1933
- if (!ok) throw new Error('Group not found or empty.');
1934
- result = { ok: true, groupId };
1935
- break;
1936
- }
1937
- case 'pin.set':
1938
- case 'pin.add':
1939
- case 'pin.remove': {
1940
- const ids = Array.isArray(args.nodeIds) ? args.nodeIds.filter((id): id is string => typeof id === 'string') : [];
1941
- result = {
1942
- ok: true,
1943
- ...setCanvasContextPins(ids, operation.op === 'pin.set' ? 'set' : operation.op === 'pin.add' ? 'add' : 'remove'),
1944
- };
1945
- break;
1946
- }
1947
- case 'snapshot.save': {
1948
- const snapshot = saveCanvasSnapshot(typeof args.name === 'string' ? args.name : '');
1949
- if (!snapshot) throw new Error('Failed to save snapshot.');
1950
- result = { ok: true, snapshot };
1951
- break;
1952
- }
1953
- case 'arrange': {
1954
- const layout =
1955
- args.layout === 'column' || args.layout === 'flow' || args.layout === 'grid'
1956
- ? args.layout
1957
- : 'grid';
1958
- result = { ok: true, ...arrangeCanvasNodes(layout) };
1959
- break;
1960
- }
1961
- default:
1962
- throw new Error(`Unsupported batch operation "${operation.op}".`);
1963
- }
1964
-
1965
- results.push(result);
1966
- if (typeof operation.assign === 'string' && operation.assign.trim().length > 0) {
1967
- refs[operation.assign] = result;
1968
- }
1969
- } catch (error) {
1970
- return {
1971
- ok: false,
1972
- failedIndex: index,
1973
- error: error instanceof Error ? error.message : String(error),
1974
- results,
1975
- refs,
1976
- };
1977
- }
1978
- }
1979
-
1980
- return { ok: true, results, refs };
1981
- }
@@ -244,7 +244,7 @@ const CANVAS_CREATE_TYPES: CanvasCreateTypeSchema[] = [
244
244
  kind: 'node',
245
245
  description: 'Sandboxed iframe node rendered from inline HTML.',
246
246
  endpoint: '/api/canvas/node',
247
- mcpTool: 'canvas_add_html_node',
247
+ mcpTool: 'canvas_node (action:"add", type:"html")',
248
248
  fields: [
249
249
  { name: 'html', type: 'string', required: false, description: 'HTML document or fragment rendered in the sandboxed iframe.', aliases: ['content', 'stdin'] },
250
250
  { name: 'summary', type: 'string', required: false, description: 'Explicit agent-readable summary. If omitted, PMX derives one from visible HTML text.' },
@@ -281,7 +281,7 @@ const CANVAS_CREATE_TYPES: CanvasCreateTypeSchema[] = [
281
281
  kind: 'virtual-node',
282
282
  description: 'Reusable sandboxed HTML communication primitive rendered as an html node.',
283
283
  endpoint: '/api/canvas/node',
284
- mcpTool: 'canvas_add_html_primitive',
284
+ mcpTool: 'canvas_node (action:"add", type:"html", primitive:"<kind>")',
285
285
  fields: [
286
286
  { name: 'kind', type: 'HtmlPrimitiveKind', required: true, description: 'Primitive kind. See top-level htmlPrimitives for the supported catalog.' },
287
287
  { name: 'data', type: 'record<string, unknown>', required: false, description: 'Primitive-specific JSON object payload.' },