pmx-canvas 0.2.7 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/CHANGELOG.md +148 -1
  2. package/Readme.md +13 -11
  3. package/dist/canvas/index.js +2 -2
  4. package/dist/json-render/index.css +1 -1
  5. package/dist/types/cli/daemon.d.ts +74 -0
  6. package/dist/types/cli/watch.d.ts +2 -2
  7. package/dist/types/client/canvas/CanvasViewport.d.ts +1 -1
  8. package/dist/types/client/canvas/CommandPalette.d.ts +1 -1
  9. package/dist/types/client/canvas/Minimap.d.ts +1 -1
  10. package/dist/types/client/nodes/FileNode.d.ts +1 -1
  11. package/dist/types/client/nodes/ImageNode.d.ts +1 -1
  12. package/dist/types/client/nodes/InlineFormatBar.d.ts +1 -1
  13. package/dist/types/client/nodes/MarkdownNode.d.ts +1 -1
  14. package/dist/types/client/nodes/PromptNode.d.ts +1 -1
  15. package/dist/types/client/nodes/ResponseNode.d.ts +1 -1
  16. package/dist/types/mcp/canvas-access.d.ts +0 -7
  17. package/dist/types/server/ax-state-manager.d.ts +3 -2
  18. package/dist/types/server/bundled-skills.d.ts +3 -3
  19. package/dist/types/server/canvas-schema.d.ts +1 -1
  20. package/dist/types/server/canvas-state.d.ts +2 -0
  21. package/dist/types/server/ext-app-lookup.d.ts +1 -3
  22. package/dist/types/server/html-primitives.d.ts +1 -1
  23. package/dist/types/server/index.d.ts +4 -4
  24. package/dist/types/server/operations/composites.d.ts +29 -23
  25. package/dist/types/server/operations/http.d.ts +2 -1
  26. package/dist/types/server/operations/index.d.ts +1 -1
  27. package/dist/types/server/operations/ops/ax-read.d.ts +2 -0
  28. package/dist/types/server/operations/ops/canvas-wire.d.ts +2 -0
  29. package/dist/types/server/operations/ops/ext-app.d.ts +2 -0
  30. package/dist/types/shared/ax-intent.d.ts +1 -1
  31. package/docs/RELEASE.md +23 -8
  32. package/docs/ax-host-adapter-contract.md +7 -7
  33. package/docs/ax-state-contract.md +7 -6
  34. package/docs/bun-webview-integration.md +5 -5
  35. package/docs/cli.md +1 -1
  36. package/docs/http-api.md +35 -3
  37. package/docs/mcp.md +121 -100
  38. package/docs/node-types.md +45 -35
  39. package/docs/plans/plan-006-mcp-tool-consolidation.md +3 -1
  40. package/docs/plans/plan-008-registry-finish.md +2 -0
  41. package/docs/plans/plan-009-tech-debt-backlog.md +51 -0
  42. package/docs/screenshot.png +0 -0
  43. package/docs/tech-debt-assessment-2026-06.md +1 -1
  44. package/docs/tech-debt-assessment-2026-07.md +135 -0
  45. package/package.json +5 -2
  46. package/skills/data-analysis/SKILL.md +3 -3
  47. package/skills/frontend-design/SKILL.md +3 -2
  48. package/skills/json-render-mcp/SKILL.md +4 -3
  49. package/skills/playwright-cli/SKILL.md +1 -1
  50. package/skills/pmx-canvas/SKILL.md +12 -7
  51. package/skills/pmx-canvas/evals/evals.json +2 -2
  52. package/skills/pmx-canvas/references/ax-html-control-surface.md +3 -1
  53. package/skills/pmx-canvas/references/excalidraw-diagram-authoring.md +2 -2
  54. package/skills/pmx-canvas/references/full-reference.md +58 -56
  55. package/skills/pmx-canvas/references/html-primitives.md +3 -3
  56. package/skills/tufte-viz/SKILL.md +5 -4
  57. package/skills/web-artifacts-builder/SKILL.md +4 -4
  58. package/src/cli/agent.ts +1861 -1548
  59. package/src/cli/daemon.ts +460 -0
  60. package/src/cli/index.ts +63 -326
  61. package/src/cli/watch.ts +2 -10
  62. package/src/client/App.tsx +48 -46
  63. package/src/client/canvas/AttentionHistory.tsx +11 -1
  64. package/src/client/canvas/CanvasNode.tsx +41 -29
  65. package/src/client/canvas/CanvasViewport.tsx +101 -66
  66. package/src/client/canvas/CommandPalette.tsx +61 -27
  67. package/src/client/canvas/ContextMenu.tsx +13 -20
  68. package/src/client/canvas/ContextPinBar.tsx +1 -5
  69. package/src/client/canvas/ContextPinHud.tsx +1 -6
  70. package/src/client/canvas/DockedNode.tsx +4 -4
  71. package/src/client/canvas/EdgeLayer.tsx +37 -36
  72. package/src/client/canvas/ExpandedNodeOverlay.tsx +69 -45
  73. package/src/client/canvas/FocusFieldLayer.tsx +20 -22
  74. package/src/client/canvas/IntentLayer.tsx +31 -14
  75. package/src/client/canvas/Minimap.tsx +11 -16
  76. package/src/client/canvas/SelectionBar.tsx +4 -11
  77. package/src/client/canvas/ShortcutOverlay.tsx +3 -1
  78. package/src/client/canvas/SnapshotPanel.tsx +77 -95
  79. package/src/client/canvas/auto-fit.ts +15 -14
  80. package/src/client/canvas/snap-guides.ts +12 -12
  81. package/src/client/canvas/use-node-resize.ts +1 -5
  82. package/src/client/canvas/use-pan-zoom.ts +25 -26
  83. package/src/client/ext-app/bridge.ts +3 -12
  84. package/src/client/icons.tsx +63 -20
  85. package/src/client/nodes/ContextNode.tsx +14 -25
  86. package/src/client/nodes/ExtAppFrame.tsx +60 -39
  87. package/src/client/nodes/FileNode.tsx +74 -62
  88. package/src/client/nodes/GroupNode.tsx +4 -6
  89. package/src/client/nodes/HtmlNode.tsx +76 -46
  90. package/src/client/nodes/ImageNode.tsx +18 -27
  91. package/src/client/nodes/InlineFormatBar.tsx +4 -21
  92. package/src/client/nodes/InlineMarkdownEditor.tsx +0 -1
  93. package/src/client/nodes/LedgerNode.tsx +10 -4
  94. package/src/client/nodes/MarkdownNode.tsx +3 -10
  95. package/src/client/nodes/McpAppNode.tsx +26 -22
  96. package/src/client/nodes/MdFormatBar.tsx +10 -7
  97. package/src/client/nodes/PromptNode.tsx +23 -51
  98. package/src/client/nodes/ResponseNode.tsx +3 -13
  99. package/src/client/nodes/StatusNode.tsx +5 -9
  100. package/src/client/nodes/StatusSummary.tsx +2 -8
  101. package/src/client/nodes/WebpageNode.tsx +20 -14
  102. package/src/client/nodes/iframe-document-url.ts +25 -16
  103. package/src/client/nodes/image-warnings.ts +1 -7
  104. package/src/client/nodes/md-format.ts +20 -5
  105. package/src/client/state/attention-bridge.ts +4 -9
  106. package/src/client/state/attention-store.ts +1 -7
  107. package/src/client/state/canvas-store.ts +52 -36
  108. package/src/client/state/intent-bridge.ts +176 -112
  109. package/src/client/state/intent-store.ts +4 -1
  110. package/src/client/state/sse-bridge.ts +53 -70
  111. package/src/json-render/catalog.ts +12 -16
  112. package/src/json-render/charts/components.tsx +16 -20
  113. package/src/json-render/charts/extra-components.tsx +8 -16
  114. package/src/json-render/charts/extra-definitions.ts +1 -2
  115. package/src/json-render/charts/tufte-components.tsx +37 -20
  116. package/src/json-render/charts/tufte-definitions.ts +8 -2
  117. package/src/json-render/renderer/index.tsx +42 -22
  118. package/src/json-render/schema.ts +6 -3
  119. package/src/json-render/server.ts +33 -39
  120. package/src/mcp/canvas-access.ts +40 -121
  121. package/src/mcp/server.ts +206 -271
  122. package/src/server/agent-context.ts +63 -36
  123. package/src/server/ax-context.ts +7 -5
  124. package/src/server/ax-interaction.ts +176 -43
  125. package/src/server/ax-state-manager.ts +185 -41
  126. package/src/server/ax-state.ts +142 -47
  127. package/src/server/bundled-skills.ts +3 -3
  128. package/src/server/canvas-db.ts +213 -95
  129. package/src/server/canvas-operations.ts +177 -120
  130. package/src/server/canvas-provenance.ts +1 -4
  131. package/src/server/canvas-schema.ts +500 -102
  132. package/src/server/canvas-serialization.ts +27 -35
  133. package/src/server/canvas-state.ts +155 -58
  134. package/src/server/chart-template.ts +4 -5
  135. package/src/server/code-graph.ts +20 -7
  136. package/src/server/diagram-presets.ts +28 -29
  137. package/src/server/ext-app-lookup.ts +4 -20
  138. package/src/server/html-node-summary.ts +19 -10
  139. package/src/server/html-primitives.ts +326 -97
  140. package/src/server/html-surface.ts +6 -9
  141. package/src/server/image-source.ts +6 -4
  142. package/src/server/index.ts +321 -218
  143. package/src/server/intent-registry.ts +3 -6
  144. package/src/server/mcp-app-candidate.ts +5 -10
  145. package/src/server/mcp-app-host.ts +14 -38
  146. package/src/server/mcp-app-runtime.ts +12 -20
  147. package/src/server/mutation-history.ts +15 -5
  148. package/src/server/operations/composites.ts +38 -40
  149. package/src/server/operations/http.ts +5 -5
  150. package/src/server/operations/index.ts +7 -1
  151. package/src/server/operations/invoker.ts +4 -3
  152. package/src/server/operations/mcp.ts +29 -36
  153. package/src/server/operations/ops/annotation.ts +122 -10
  154. package/src/server/operations/ops/app.ts +102 -77
  155. package/src/server/operations/ops/ax-await.ts +17 -10
  156. package/src/server/operations/ops/ax-read.ts +347 -0
  157. package/src/server/operations/ops/ax-shared.ts +2 -7
  158. package/src/server/operations/ops/ax-state.ts +32 -14
  159. package/src/server/operations/ops/ax-timeline.ts +32 -19
  160. package/src/server/operations/ops/ax-work.ts +54 -37
  161. package/src/server/operations/ops/batch.ts +39 -14
  162. package/src/server/operations/ops/canvas-wire.ts +91 -0
  163. package/src/server/operations/ops/edges.ts +37 -25
  164. package/src/server/operations/ops/ext-app.ts +346 -0
  165. package/src/server/operations/ops/groups.ts +49 -20
  166. package/src/server/operations/ops/intent.ts +18 -12
  167. package/src/server/operations/ops/json-render.ts +239 -98
  168. package/src/server/operations/ops/nodes.ts +300 -111
  169. package/src/server/operations/ops/query.ts +62 -33
  170. package/src/server/operations/ops/snapshots.ts +35 -26
  171. package/src/server/operations/ops/validate.ts +2 -1
  172. package/src/server/operations/ops/viewport.ts +60 -16
  173. package/src/server/operations/ops/webview.ts +44 -18
  174. package/src/server/operations/registry.ts +2 -3
  175. package/src/server/operations/types.ts +7 -5
  176. package/src/server/operations/webview-runner.ts +1 -3
  177. package/src/server/placement.ts +8 -18
  178. package/src/server/server.ts +121 -1012
  179. package/src/server/spatial-analysis.ts +39 -25
  180. package/src/server/trace-manager.ts +3 -8
  181. package/src/server/web-artifacts.ts +23 -27
  182. package/src/server/webpage-node.ts +5 -13
  183. package/src/shared/auto-arrange.ts +12 -5
  184. package/src/shared/ax-intent.ts +1 -1
  185. package/src/shared/content-height-reporter.ts +8 -6
  186. package/src/shared/ext-app-tool-result.ts +2 -6
  187. package/src/shared/placement.ts +1 -4
  188. package/src/shared/semantic-attention.ts +39 -37
  189. package/src/shared/surface.ts +8 -4
@@ -1,10 +1,6 @@
1
1
  import { type CanvasLayout, type CanvasNodeState, type PmxCanvas } from '../server/index.js';
2
2
  import type { PmxAxSource } from '../server/ax-state.js';
3
3
  import { type OperationInvoker } from '../server/operations/index.js';
4
- type RefreshWebpageNodeResult = Awaited<ReturnType<PmxCanvas['refreshWebpageNode']>>;
5
- type AddHtmlNodeInput = Parameters<PmxCanvas['addHtmlNode']>[0];
6
- type AddHtmlPrimitiveInput = Parameters<PmxCanvas['addHtmlPrimitive']>[0];
7
- type AddHtmlPrimitiveResult = ReturnType<PmxCanvas['addHtmlPrimitive']>;
8
4
  type AxStateResult = ReturnType<PmxCanvas['getAxState']>;
9
5
  type AxContextResult = ReturnType<PmxCanvas['getAxContext']>;
10
6
  type SubmitAxInteractionInput = Parameters<PmxCanvas['submitAxInteraction']>[0];
@@ -33,9 +29,6 @@ export interface CanvasAccess {
33
29
  invoker(): OperationInvoker;
34
30
  getLayout(): Promise<CanvasLayout>;
35
31
  getNode(id: string): Promise<CanvasNodeState | undefined>;
36
- refreshWebpageNode(id: string, url?: string): Promise<RefreshWebpageNodeResult>;
37
- addHtmlNode(input: AddHtmlNodeInput): Promise<string>;
38
- addHtmlPrimitive(input: AddHtmlPrimitiveInput): Promise<AddHtmlPrimitiveResult>;
39
32
  getAxState(): Promise<AxStateResult>;
40
33
  getAxContext(options?: {
41
34
  consumer?: string;
@@ -7,8 +7,9 @@
7
7
  *
8
8
  * • Canvas-bound (`_axState`): focus, work items, approval gates, review
9
9
  * annotations, elicitations, mode requests, policy. Snapshotted, cleared by
10
- * `canvas_clear`, replaced by `restore`. Mutators record undo/redo history
11
- * (via the injected `recordMutation` / `suppressed` callbacks).
10
+ * `canvas_view` (`clear` action), replaced by `restore`. Mutators record
11
+ * undo/redo history (via the injected `recordMutation` / `suppressed`
12
+ * callbacks).
12
13
  * • Timeline (audit-only): agent events, evidence, steering. DB-direct, NOT in
13
14
  * `_axState`, NOT history-recorded, NOT snapshotted, retention-bounded.
14
15
  * • Host/session: a single host-capability row in its own table.
@@ -3,9 +3,9 @@
3
3
  *
4
4
  * Skill files ship inside the npm package under `skills/<name>/SKILL.md`
5
5
  * but until 0.1.2 they were not discoverable to the agent — an agent
6
- * calling `canvas_build_web_artifact` had no way to find the companion
7
- * `skills/web-artifacts-builder/SKILL.md` prompt that documents the
8
- * workflow, stack choices, and gotchas.
6
+ * calling `canvas_app` (`build-artifact` action) had no way to find the
7
+ * companion `skills/web-artifacts-builder/SKILL.md` prompt that documents
8
+ * the workflow, stack choices, and gotchas.
9
9
  *
10
10
  * This module locates the bundled `skills/` directory relative to the
11
11
  * package root (works for both repo-local development and global npm
@@ -34,7 +34,7 @@ export interface StructuredValidationResult {
34
34
  summary: Record<string, unknown>;
35
35
  }
36
36
  declare const CANONICAL_GRAPH_TYPES: readonly ["line", "bar", "pie", "area", "scatter", "radar", "stacked-bar", "composed", "sparkline", "dot-plot", "bullet", "slopegraph"];
37
- type CanvasGraphType = typeof CANONICAL_GRAPH_TYPES[number];
37
+ type CanvasGraphType = (typeof CANONICAL_GRAPH_TYPES)[number];
38
38
  export declare function describeCanvasSchema(): {
39
39
  ok: true;
40
40
  source: 'running-server';
@@ -174,6 +174,8 @@ declare class CanvasStateManager {
174
174
  private _stateFilePath;
175
175
  private _db;
176
176
  private _saveTimer;
177
+ /** Workspace root backing persistence and workspace-relative path resolution. */
178
+ get workspaceRoot(): string;
177
179
  /** Set the workspace root to enable auto-persistence. */
178
180
  setWorkspaceRoot(workspaceRoot: string): void;
179
181
  private get blobsDir();
@@ -9,9 +9,7 @@
9
9
  * Resolution order:
10
10
  * 1. The direct prefixed form (`ext-app-<toolCallId>` if not already
11
11
  * prefixed, otherwise `toolCallId` as-is).
12
- * 2. The legacy `ext-app-ext-app-…` form, for canvases persisted before
13
- * v0.1.4 and still on disk. Remove this fallback in v0.2.x.
14
- * 3. A scan of the layout for any `mcp-app` ext-app node carrying that
12
+ * 2. A scan of the layout for any `mcp-app` ext-app node carrying that
15
13
  * `toolCallId` in its data.
16
14
  */
17
15
  import type { CanvasNodeState } from './canvas-state.js';
@@ -1,5 +1,5 @@
1
1
  export declare const HTML_PRIMITIVE_KINDS: readonly ["choice-grid", "plan-timeline", "review-sheet", "pr-writeup", "system-map", "code-walkthrough", "design-sheet", "component-gallery", "interaction-prototype", "flowchart", "deck", "presentation", "illustration-set", "explainer", "status-report", "incident-report", "triage-board", "config-editor", "prompt-tuner"];
2
- export type HtmlPrimitiveKind = typeof HTML_PRIMITIVE_KINDS[number];
2
+ export type HtmlPrimitiveKind = (typeof HTML_PRIMITIVE_KINDS)[number];
3
3
  export interface HtmlPrimitiveDescriptor {
4
4
  kind: HtmlPrimitiveKind;
5
5
  title: string;
@@ -13,7 +13,7 @@ import { type SerializedCanvasNode } from './canvas-serialization.js';
13
13
  import type { HtmlPrimitiveKind } from './html-primitives.js';
14
14
  import { type WebArtifactBuildInput, type WebArtifactCanvasBuildResult } from './web-artifacts.js';
15
15
  import { type ExternalMcpTransportConfig } from './mcp-app-runtime.js';
16
- import { type DiagramPresetOpenInput } from './diagram-presets.js';
16
+ import type { DiagramPresetOpenInput } from './diagram-presets.js';
17
17
  import { type GraphNodeInput, type JsonRenderNodeInput, type JsonRenderSpec } from '../json-render/server.js';
18
18
  import type { CanvasAutomationWebViewOptions, CanvasAutomationWebViewStatus } from './server.js';
19
19
  /**
@@ -557,21 +557,21 @@ export type { CanvasNodeState, CanvasEdge, CanvasLayout, ViewportState } from '.
557
557
  export type { CanvasAutomationWebViewOptions, CanvasAutomationWebViewStatus, PrimaryWorkbenchCanvasPromptRequest, PrimaryWorkbenchIntent, } from './server.js';
558
558
  export { emitPrimaryWorkbenchEvent, consumePrimaryWorkbenchIntents, setPrimaryWorkbenchAutoOpenEnabled, setPrimaryWorkbenchCanvasPromptHandler, startCanvasServer, stopCanvasServer, getCanvasServerPort, openUrlInExternalBrowser, getCanvasAutomationWebViewStatus, startCanvasAutomationWebView, stopCanvasAutomationWebView, evaluateCanvasAutomationWebView, resizeCanvasAutomationWebView, screenshotCanvasAutomationWebView, } from './server.js';
559
559
  export { canvasState } from './canvas-state.js';
560
- export type { CanvasAnnotation, CanvasSnapshot, CanvasSnapshotGcResult, CanvasSnapshotListOptions } from './canvas-state.js';
560
+ export type { CanvasAnnotation, CanvasSnapshot, CanvasSnapshotGcResult, CanvasSnapshotListOptions, } from './canvas-state.js';
561
561
  export { findOpenCanvasPosition } from './placement.js';
562
562
  export { searchNodes, buildSpatialContext, detectClusters, findNeighborhoods } from './spatial-analysis.js';
563
563
  export type { SpatialCluster, SpatialContext, SpatialNeighbor, NodeSpatialInfo } from './spatial-analysis.js';
564
564
  export { mutationHistory, diffLayouts, formatDiff } from './mutation-history.js';
565
565
  export { recomputeCodeGraph, buildCodeGraphSummary, formatCodeGraph } from './code-graph.js';
566
566
  export { describeCanvasSchema, validateStructuredCanvasPayload } from './canvas-schema.js';
567
- export { buildHtmlPrimitive, getHtmlPrimitiveSemanticMetadata, isHtmlPrimitiveKind, listHtmlPrimitiveDescriptors } from './html-primitives.js';
567
+ export { buildHtmlPrimitive, getHtmlPrimitiveSemanticMetadata, isHtmlPrimitiveKind, listHtmlPrimitiveDescriptors, } from './html-primitives.js';
568
568
  export { buildWebArtifactOnCanvas, executeWebArtifactBuild, openWebArtifactInCanvas, resolveWebArtifactScriptPath, resolveWorkspacePath, } from './web-artifacts.js';
569
569
  export { buildGraphSpec, buildJsonRenderViewerHtml, createJsonRenderNodeData, GRAPH_NODE_SIZE, JSON_RENDER_NODE_SIZE, normalizeAndValidateJsonRenderSpec, } from '../json-render/server.js';
570
570
  export type { CodeGraphSummary, CodeGraphEdge } from './code-graph.js';
571
571
  export type { MutationEntry, MutationSummary, SnapshotDiffResult } from './mutation-history.js';
572
572
  export type { WebArtifactBuildInput, WebArtifactBuildOutput, WebArtifactCanvasBuildResult, WebArtifactCanvasOpenResult, } from './web-artifacts.js';
573
573
  export type { GraphNodeInput, JsonRenderNodeInput, JsonRenderSpec } from '../json-render/server.js';
574
- export type { HtmlPrimitiveKind, HtmlPrimitiveDescriptor, HtmlPrimitiveInput, HtmlPrimitiveBuildResult } from './html-primitives.js';
574
+ export type { HtmlPrimitiveKind, HtmlPrimitiveDescriptor, HtmlPrimitiveInput, HtmlPrimitiveBuildResult, } from './html-primitives.js';
575
575
  export { traceManager } from './trace-manager.js';
576
576
  export type { PmxAxApprovalGate, PmxAxApprovalStatus, PmxAxCommandDescriptor, PmxAxContext, PmxAxEvent, PmxAxElicitation, PmxAxElicitationStatus, PmxAxEventKind, PmxAxEvidence, PmxAxEvidenceKind, PmxAxFocusState, PmxAxHostCapability, PmxAxMode, PmxAxModeRequest, PmxAxModeRequestStatus, PmxAxPolicy, PmxAxReviewAnchorType, PmxAxReviewAnnotation, PmxAxReviewKind, PmxAxReviewRegion, PmxAxReviewSeverity, PmxAxReviewStatus, PmxAxSource, PmxAxState, PmxAxSteeringMessage, PmxAxTimelineSummary, PmxAxWorkItem, PmxAxWorkItemStatus, } from './ax-state.js';
577
577
  export type { AxTimelineQuery } from './canvas-db.js';
@@ -9,17 +9,22 @@
9
9
  * to the standalone `canvas_add_edge` — same op, same arg mapping, same result
10
10
  * shape — by construction. No handler logic lives here.
11
11
  *
12
- * Migration (docs/api-stability.md + plan-006): composites land ADDITIVELY in
13
- * v0.2 alongside the legacy single-purpose tools (the tool surface grows, then
14
- * shrinks when the legacy tools are removed in v0.3). Every action here maps to a
15
- * registry-backed operation (plan-005 slices 1–7 + plan-008 Wave 1).
12
+ * Migration (docs/api-stability.md + plan-006): composites landed ADDITIVELY in
13
+ * v0.2 alongside the legacy single-purpose tools. As of v0.3.0 the legacy tools
14
+ * those composites fold are REMOVED (registration-suppressed see
15
+ * `compositeFoldedOpNames` below); every action here maps to a registry-backed
16
+ * operation (plan-005 slices 1–7 + plan-008 Wave 1) that is now reachable ONLY
17
+ * through its composite (or `canvas_batch`).
16
18
  *
17
- * Still deferred (its legacy standalone tool keeps working; see plan-008): the
18
- * `canvas_snapshot` composite (the v0.3 name collision). The action enums are
19
- * forward-compatible: adding an action later is additive. (`canvas_webview`
20
- * shipped in plan-008 Wave 3 via runner injection; `canvas_app` shipped in Wave 4
21
- * open-mcp-app / diagram / build-artifact. Wave 5 folded the last three legacy
22
- * tools deprecate-only NO per-action input-injection mechanism was needed:
19
+ * Deferred to v0.4: the `canvas_snapshot` composite (still a name collision
20
+ * see below). Its 6 legacy snapshot standalones (`canvas_snapshot`,
21
+ * `canvas_list_snapshots`, `canvas_restore`, `canvas_delete_snapshot`,
22
+ * `canvas_gc_snapshots`, `canvas_diff`) stay registered in v0.3.0 but are marked
23
+ * deprecated (description-prefixed) per docs/api-stability.md's
24
+ * deprecate-one-minor-before-removal rule. (`canvas_webview` shipped in plan-008
25
+ * Wave 3 via runner injection; `canvas_app` shipped in Wave 4 — open-mcp-app /
26
+ * diagram / build-artifact. Wave 5 folded the last three legacy tools
27
+ * deprecate-only — NO per-action input-injection mechanism was needed:
23
28
  * `canvas_add_html_node` / `canvas_add_html_primitive` → `canvas_node` action
24
29
  * "add" (type:"html" [+ primitive]); `canvas_refresh_webpage_node` → `canvas_node`
25
30
  * action "update" (refresh:true). `canvas_screenshot` stays standalone — it
@@ -30,8 +35,8 @@
30
35
  * name is ALREADY a legacy standalone tool (the save-snapshot tool, op
31
36
  * `snapshot.save`), so it cannot be added additively without a name clash, and
32
37
  * repurposing `canvas_snapshot` to be action-discriminated now would break
33
- * existing callers. It lands in v0.3, in the same change that removes the legacy
34
- * single-purpose snapshot tools and frees the name.
38
+ * existing callers. It lands in v0.4, in the same change that removes the 6
39
+ * kept-but-deprecated legacy snapshot tools and frees the name.
35
40
  *
36
41
  * This module must never import server.ts or index.ts.
37
42
  */
@@ -45,7 +50,7 @@ import { type ZodRawShape } from 'zod';
45
50
  * - Two-discriminator (`canvas_ax_gate`, plan-007 Slice C): a `kind` × `action`
46
51
  * matrix folds 9 ops into one tool. Set `extraDiscriminatorShape` (the `kind`
47
52
  * enum), `memberOps` (the op names — used to derive the schema union + the
48
- * deprecation notes), `actionEnum` (the action discriminator values), and
53
+ * folded-op set), `actionEnum` (the action discriminator values), and
49
54
  * `resolveOp` (maps `{ kind, action }` → op name, or undefined for an invalid
50
55
  * combo → a loud error at dispatch). The flat `actions` map is left empty for
51
56
  * these; the matrix path uses `resolveOp` instead.
@@ -76,8 +81,9 @@ export interface CompositeToolDefinition {
76
81
  actionEnum?: readonly string[];
77
82
  /**
78
83
  * Two-discriminator extension: every member op name. Used to build the schema
79
- * union (all member-op fields, optional) and to derive a deprecation note per
80
- * member op (each mapped back to its (kind, action) by `describeOp`).
84
+ * union (all member-op fields, optional) and to populate the folded-op set
85
+ * (`compositeFoldedOpNames`) that suppresses each member op's standalone
86
+ * registration.
81
87
  */
82
88
  memberOps?: string[];
83
89
  /**
@@ -110,12 +116,12 @@ export interface CompositeToolDefinition {
110
116
  }
111
117
  export declare const compositeToolDefinitions: CompositeToolDefinition[];
112
118
  /**
113
- * Deprecation notes for the legacy single-purpose tools, DERIVED from the
114
- * composites: every operation a composite folds gets a `Deprecated: use
115
- * canvas_x with action "y".` prefix on its standalone tool description, steering
116
- * agents to the composite during the v0.2 overlap window (the legacy tools and
117
- * these notes are removed together in v0.3). Keyed by registry operation name.
118
- * Deriving it keeps the deprecation list in lockstep with the composites — a new
119
- * folded action automatically deprecates the tool it replaces.
119
+ * Operation names FOLDED by a composite, DERIVED from the composites: every op
120
+ * a composite folds had its standalone single-purpose tool REMOVED in v0.3.0
121
+ * (see docs/api-stability.md). This set is the do-not-register list
122
+ * `registerOperationTools` skips any op whose name is in it, since the op is
123
+ * only reachable through its composite (and through `canvas_batch`) now.
124
+ * Deriving it from the composites keeps suppression in lockstep with them — a
125
+ * newly folded action is automatically suppressed from standalone registration.
120
126
  */
121
- export declare function buildCompositeDeprecationNotes(definitions?: CompositeToolDefinition[]): Map<string, string>;
127
+ export declare function compositeFoldedOpNames(definitions?: CompositeToolDefinition[]): Set<string>;
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * Shared body reader: preserves the parsed JSON value as-is (object, array,
3
3
  * or primitive) — per-op `readInput` decides what to do with non-object
4
- * bodies; the shared reader never coerces.
4
+ * bodies; the shared reader never coerces. A non-empty body that fails to
5
+ * parse is a 400 (OperationError), never a silent empty input.
5
6
  */
6
7
  export declare function readJsonValue(req: Request): Promise<unknown>;
7
8
  export declare function dispatchOperationRoute(req: Request, url: URL): Promise<Response | null>;
@@ -1,7 +1,7 @@
1
1
  export { executeOperation, getOperation, listOperations, registerOperation, setOperationEventEmitter, } from './registry.js';
2
2
  export { dispatchOperationRoute } from './http.js';
3
3
  export { runCanvasBatchOperation, type BatchEnvelope } from './ops/batch.js';
4
- export { type OpenMcpAppCoreResult } from './ops/app.js';
4
+ export type { OpenMcpAppCoreResult } from './ops/app.js';
5
5
  export { LocalOperationInvoker, HttpOperationInvoker, type OperationInvoker } from './invoker.js';
6
6
  export { registerOperationTools, registerCompositeTools, type OperationToolHost } from './mcp.js';
7
7
  export { compositeToolDefinitions, type CompositeToolDefinition } from './composites.js';
@@ -0,0 +1,2 @@
1
+ import { type Operation } from '../types.js';
2
+ export declare const axReadOperations: Operation[];
@@ -0,0 +1,2 @@
1
+ import { type Operation } from '../types.js';
2
+ export declare const canvasWireOperations: Operation[];
@@ -0,0 +1,2 @@
1
+ import { type Operation } from '../types.js';
2
+ export declare const extAppOperations: Operation[];
@@ -5,7 +5,7 @@
5
5
  * agent is ABOUT to make (create / move / connect / remove / edit) so the canvas
6
6
  * can paint a faint placeholder before the real mutation lands. Like a
7
7
  * multiplayer cursor, it auto-expires, is count-capped, and never enters
8
- * `canvas_get_layout`, `state.json`, or snapshots.
8
+ * `canvas_query` (`layout` action), `state.json`, or snapshots.
9
9
  *
10
10
  * This module is import-shared by the server (IntentRegistry + the intent ops)
11
11
  * and the client (intent-store + IntentLayer); it must stay free of any
package/docs/RELEASE.md CHANGED
@@ -46,14 +46,18 @@ and exercises the CLI flows from
46
46
 
47
47
  ## Versioning
48
48
 
49
- Semantic versioning. While we are still in `0.1.x`:
50
-
51
- - Patch (`0.1.x → 0.1.x+1`): bug fixes, hardening, internal cleanups,
52
- additive CLI flags / MCP fields with backwards-compat fallbacks.
53
- - Minor (`0.1.x → 0.2.0`): documented breaking changes (e.g. dropping
54
- the `hostMode + path` fallback in `getCanvasNodeKind`, removing legacy
55
- `ext-app-ext-app-…` ID handling, JSON-shape changes that aren't
56
- additive).
49
+ Semantic versioning, in 0.x semver honestly (see `docs/api-stability.md`).
50
+ We are pre-1.0, so minor versions — not just major — are the breaking-change
51
+ boundary:
52
+
53
+ - Patch (`0.x.y → 0.x.y+1`): bug fixes, hardening, internal cleanups,
54
+ additive CLI flags / MCP fields with backwards-compat fallbacks. Never
55
+ breaks a public surface.
56
+ - Minor (`0.x → 0.x+1`): documented breaking changes are allowed only here
57
+ (e.g. the 0.2 → 0.3 removal of the 57 deprecated legacy single-purpose MCP
58
+ tools in favor of their composite replacements). Every breaking change in
59
+ a minor needs a `### Breaking` CHANGELOG entry naming the replacement —
60
+ see the release-blocker gate below.
57
61
  - Major (`0.x → 1.0`): production-stability commitment.
58
62
 
59
63
  CLAUDE.md rule #5 (CanvasStateManager / PmxCanvas SDK / HTTP / MCP
@@ -70,6 +74,11 @@ Each release section uses these subheadings:
70
74
  command/flag, SDK export, JSON shape).
71
75
  - **Changed** — behavior changes that aren't strictly bug fixes
72
76
  (response shape additions, schema cleanups, stricter validation).
77
+ - **Breaking** — removed or incompatibly changed public surface (see
78
+ `docs/api-stability.md`). Required at minor version boundaries whenever a
79
+ public surface breaks; must name the replacement.
80
+ - **Deprecated** — public surface marked for removal in a future minor,
81
+ per the `docs/api-stability.md` deprecation policy.
73
82
  - **Fixed** — bug fixes.
74
83
  - **Internal** — refactors, test additions, docs that don't affect
75
84
  the public surface.
@@ -78,6 +87,12 @@ Don't ship a release without a CHANGELOG entry. The GitHub release
78
87
  notes file (`/tmp/pmx-canvas-vX.Y.Z-release-notes.md` by convention)
79
88
  expands on the CHANGELOG with examples and migration notes.
80
89
 
90
+ **Release-blocker gate:** a breaking change without a `### Breaking`
91
+ CHANGELOG entry blocks the release (`docs/api-stability.md`). Check the
92
+ diff against the previous tag for removed/renamed public surface — MCP
93
+ tool names, HTTP routes, SDK exports, CLI flags — before tagging; if
94
+ anything broke and isn't named under `### Breaking`, add the entry first.
95
+
81
96
  ## Tag → publish
82
97
 
83
98
  The publish workflow ([`/.github/workflows/publish.yml`](../.github/workflows/publish.yml))
@@ -20,9 +20,9 @@ surface each one binds to.
20
20
  | Adapter method | PMX surface (owned) | Harness-owned part |
21
21
  | --- | --- | --- |
22
22
  | `pullContext()` | `GET /api/canvas/ax/context?consumer=<id>` · `canvas://ax-context` — full board **plus** a compact `delivery` lead block | When/where to inject it into the model's turn |
23
- | `deliverSteer()` | `GET /api/canvas/ax/delivery/pending?consumer=<id>` · `canvas_claim_ax_delivery` → act → `POST …/delivery/<id>/mark` · `canvas_mark_ax_delivery` | Calling the host's native send/wake |
23
+ | `deliverSteer()` | `GET /api/canvas/ax/delivery/pending?consumer=<id>` · `canvas_ax_delivery { action: "claim" }` → act → `POST …/delivery/<id>/mark` · `canvas_ax_delivery { action: "mark" }` | Calling the host's native send/wake |
24
24
  | `ingestActivity(event)` | `POST /api/canvas/ax/activity` · `canvas_ingest_activity` — board auto-reacts | Forwarding the host's tool/session hooks |
25
- | `awaitGate(id)` | `GET /api/canvas/ax/{approval\|elicitation\|mode}/<id>?waitMs=` · `canvas_await_*` | Optionally surfacing a native modal; the agent must await PMX |
25
+ | `awaitGate(id)` | `GET /api/canvas/ax/{approval\|elicitation\|mode}/<id>?waitMs=` · `canvas_ax_gate { kind: "approval"\|"elicitation"\|"mode", action: "await" }` | Optionally surfacing a native modal; the agent must await PMX |
26
26
  | `mirrorLog(event)` *(optional)* | `GET /api/canvas/ax/timeline` · `canvas://ax-timeline` | Writing AX events into the host's own chat/session log |
27
27
 
28
28
  ## Steering is gated, not pushed (#54)
@@ -37,8 +37,8 @@ message; it does **not** wake the agent. It reaches the next turn only when:
37
37
  2. **A human message fires the turn.** A sandbox button click cannot itself create a
38
38
  new agent turn (an app-platform constraint). Any human prompt triggers the injection.
39
39
  3. **The agent acts, then acks.** Injected `pendingSteering` / `pendingActivity` is
40
- *to-do*, not narration: act on it, then `canvas_mark_ax_delivery` the steering
41
- (or resolve the work item / gate). Until acked, steering re-injects every gated turn.
40
+ *to-do*, not narration: act on it, then `canvas_ax_delivery { action: "mark" }`
41
+ the steering (or resolve the work item / gate). Until acked, steering re-injects every gated turn.
42
42
 
43
43
  The `delivery` lead block (`GET /api/canvas/ax/context?consumer=<id>`) is the
44
44
  robustness hedge: it's compact and sits above the full dump, so an adapter can inject
@@ -69,9 +69,9 @@ turn"), never imply it interrupts the agent now.
69
69
  tool becomes a blocked work item + a review finding + evidence without the agent
70
70
  remembering to push it. Reactions are kind-driven and overridable per call.
71
71
  - **Blocking gates (gates that actually gate).** Before, an approval gate was inert
72
- data the agent had to poll. With `canvas_await_approval` (and the `?waitMs` HTTP
73
- long-poll), the agent requests a gate then *blocks* until the human resolves it in
74
- the browser — real human-in-the-loop control on any harness.
72
+ data the agent had to poll. With `canvas_ax_gate { kind: "approval", action: "await" }`
73
+ (and the `?waitMs` HTTP long-poll), the agent requests a gate then *blocks* until the
74
+ human resolves it in the browser — real human-in-the-loop control on any harness.
75
75
 
76
76
  ## What stays harness-owned
77
77
 
@@ -6,7 +6,7 @@ snapshot-vs-audit boundary; it is the documented module boundary for
6
6
  `AxStateManager` (`src/server/ax-state-manager.ts`), which `CanvasStateManager`
7
7
  holds and delegates to.
8
8
 
9
- | Partition | Members | Storage | Snapshotted | Cleared by `canvas_clear` | Cleared by `restore` |
9
+ | Partition | Members | Storage | Snapshotted | Cleared by `canvas_view { action: "clear" }` | Cleared by `restore` |
10
10
  |-----------|---------|---------|:-----------:|:-------------------------:|:--------------------:|
11
11
  | **Canvas-bound** | `focus`, `workItems`, `approvalGates`, `reviewAnnotations`, `elicitations`, `modeRequests`, `policy` | in-memory `_axState` + one JSON blob in the `ax_state` table | ✅ | ✅ | ✅ (replaced by the snapshot's AX) |
12
12
  | **Timeline (audit-only)** | `agent-event`, `evidence-item`, `steering-message` | `ax_events` / `ax_evidence` / `ax_steering` tables, 500-row retention, sequential ids | ❌ | ❌ | ❌ |
@@ -15,14 +15,15 @@ holds and delegates to.
15
15
  **Rules.** Canvas-bound state travels with the canvas (snapshot / restore / clear);
16
16
  timeline and host data are diagnostic and survive all three. Timeline rows are
17
17
  append-only, retention-bounded (`AX_TIMELINE_RETENTION = 500` per table), and
18
- read via `canvas_get_ax_timeline` / `canvas://ax-timeline`. The host-capability
19
- row is reported by adapters and read via `canvas_get_ax`.
18
+ read via `canvas_ax_timeline { action: "read" }` / `canvas://ax-timeline`. The
19
+ host-capability row is reported by adapters and read via `canvas_ax_state {
20
+ action: "get" }`.
20
21
 
21
22
  ## Read surfaces
22
23
 
23
- - **Canvas-bound:** `canvas_get_ax`, `canvas://ax`, `canvas://ax-context`, `canvas://ax-work`
24
- - **Timeline:** `canvas_get_ax_timeline`, `canvas://ax-timeline`, `canvas://ax-pending-steering`, `canvas://ax-delivery`
25
- - **Host:** `canvas_get_ax`
24
+ - **Canvas-bound:** `canvas_ax_state { action: "get" }`, `canvas://ax`, `canvas://ax-context`, `canvas://ax-work`
25
+ - **Timeline:** `canvas_ax_timeline { action: "read" }`, `canvas://ax-timeline`, `canvas://ax-pending-steering`, `canvas://ax-delivery`
26
+ - **Host:** `canvas_ax_state { action: "get" }`
26
27
 
27
28
  ## Node-deletion semantics (soft-orphan + audit)
28
29
 
@@ -144,11 +144,11 @@ These flags are intentionally automation-only. They do **not** imply a visible B
144
144
 
145
145
  Implemented in [`src/mcp/server.ts`](../src/mcp/server.ts):
146
146
 
147
- - `canvas_webview_status`
148
- - `canvas_webview_start`
149
- - `canvas_webview_stop`
150
- - `canvas_evaluate`
151
- - `canvas_resize`
147
+ - `canvas_webview { action: "status" }`
148
+ - `canvas_webview { action: "start" }`
149
+ - `canvas_webview { action: "stop" }`
150
+ - `canvas_webview { action: "evaluate" }`
151
+ - `canvas_webview { action: "resize" }`
152
152
  - `canvas_screenshot`
153
153
 
154
154
  Current MCP behavior:
package/docs/cli.md CHANGED
@@ -217,7 +217,7 @@ take a screenshot when the drawn form matters.
217
217
 
218
218
  Humans draw with the pen toolbar button and remove marks with the eraser button.
219
219
  If an agent already knows the annotation ID from context, it can remove it through
220
- MCP with `canvas_remove_annotation`.
220
+ MCP with `canvas_view { action: "remove-annotation", id }`.
221
221
 
222
222
  ## When to reach for the CLI
223
223
 
package/docs/http-api.md CHANGED
@@ -3,14 +3,18 @@
3
3
  REST endpoints for all canvas operations + an SSE event stream. Works from
4
4
  any language. Default base URL: `http://localhost:4313`.
5
5
 
6
+ A non-empty request body that is not valid JSON returns
7
+ `400 { "ok": false, "error": "Malformed JSON body." }` on every route; empty
8
+ bodies are treated as an empty request.
9
+
6
10
  ## Canvas state
7
11
 
8
12
  ```bash
9
13
  # Get canvas state
10
14
  curl http://localhost:4313/api/canvas/state
11
15
 
12
- # Search nodes
13
- curl "http://localhost:4313/api/canvas/search?q=auth"
16
+ # Search nodes (optional limit= caps the result count)
17
+ curl "http://localhost:4313/api/canvas/search?q=auth&limit=10"
14
18
 
15
19
  # Validate the current layout
16
20
  curl http://localhost:4313/api/canvas/validate
@@ -133,7 +137,7 @@ Host-agnostic agent-experience primitives across three state partitions.
133
137
  Canvas-bound state (work items, approval gates, review annotations) rides
134
138
  canvas snapshots; timeline state (events, evidence, steering) persists for
135
139
  diagnostics but is retention-bounded and not restored by snapshots; the host
136
- capability is reported by adapters and survives `canvas_clear`.
140
+ capability is reported by adapters and survives `canvas_view { action: "clear" }`.
137
141
 
138
142
  ```bash
139
143
  # Timeline — record a normalized agent-event
@@ -272,6 +276,34 @@ and `/ax/mode/:id/resolve` return 404 for unknown IDs; `/ax/activity` requires a
272
276
  valid `kind` + `title` (400 otherwise); the single-item gate GETs return 404 for
273
277
  unknown IDs and clamp `?waitMs` to ≤120000.
274
278
 
279
+ ## Ghost Cursor intents (ephemeral)
280
+
281
+ Intents are verb-routed over HTTP — there is **no `action` field**. That
282
+ discriminator exists only on the MCP `canvas_intent` composite; `POST` always
283
+ signals a *new* intent, so a body like `{"action":"clear"}` is rejected by the
284
+ registry's kind validation. Pick the operation by method + path:
285
+
286
+ ```bash
287
+ # Signal a ghost — returns { ok, intent } with the id. Auto-expires after ttlMs
288
+ # (default 8000, max 60000), so cleanup is optional.
289
+ curl -X POST http://localhost:4313/api/canvas/ax/intent \
290
+ -H "Content-Type: application/json" \
291
+ -d '{"kind":"create","position":{"x":400,"y":300},"nodeType":"markdown","label":"Add evidence","reason":"collecting run logs"}'
292
+
293
+ # Update the ghost (position/label/reason/confidence/ttlMs; vetoed:true dissolves
294
+ # it AND poisons the id so a later linked settle is rejected)
295
+ curl -X PATCH http://localhost:4313/api/canvas/ax/intent/<id> \
296
+ -H "Content-Type: application/json" \
297
+ -d '{"position":{"x":520,"y":300},"reason":"moved next to the trace"}'
298
+
299
+ # Clear the ghost: plain DELETE dissolves it; settle it into the real node it
300
+ # became via settledNodeId, or veto it. Fields are accepted as JSON body or
301
+ # query params (?vetoed=true/false is coerced; other values are rejected 400).
302
+ curl -X DELETE http://localhost:4313/api/canvas/ax/intent/<id>
303
+ curl -X DELETE "http://localhost:4313/api/canvas/ax/intent/<id>?settledNodeId=node-42"
304
+ curl -X DELETE "http://localhost:4313/api/canvas/ax/intent/<id>?vetoed=true"
305
+ ```
306
+
275
307
  ## Diagrams (Excalidraw preset)
276
308
 
277
309
  ```bash