pmx-canvas 0.2.7 → 0.3.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 (57) hide show
  1. package/CHANGELOG.md +110 -1
  2. package/Readme.md +13 -11
  3. package/dist/json-render/index.css +1 -1
  4. package/dist/types/mcp/canvas-access.d.ts +0 -7
  5. package/dist/types/server/ax-state-manager.d.ts +3 -2
  6. package/dist/types/server/bundled-skills.d.ts +3 -3
  7. package/dist/types/server/canvas-state.d.ts +2 -0
  8. package/dist/types/server/ext-app-lookup.d.ts +1 -3
  9. package/dist/types/server/operations/composites.d.ts +29 -23
  10. package/dist/types/server/operations/http.d.ts +2 -1
  11. package/dist/types/shared/ax-intent.d.ts +1 -1
  12. package/docs/RELEASE.md +23 -8
  13. package/docs/ax-host-adapter-contract.md +7 -7
  14. package/docs/ax-state-contract.md +7 -6
  15. package/docs/bun-webview-integration.md +5 -5
  16. package/docs/cli.md +1 -1
  17. package/docs/http-api.md +7 -3
  18. package/docs/mcp.md +121 -100
  19. package/docs/node-types.md +45 -35
  20. package/docs/plans/plan-006-mcp-tool-consolidation.md +3 -1
  21. package/docs/plans/plan-008-registry-finish.md +2 -0
  22. package/docs/plans/plan-009-tech-debt-backlog.md +51 -0
  23. package/docs/screenshot.png +0 -0
  24. package/docs/tech-debt-assessment-2026-06.md +1 -1
  25. package/docs/tech-debt-assessment-2026-07.md +135 -0
  26. package/package.json +1 -1
  27. package/skills/data-analysis/SKILL.md +3 -3
  28. package/skills/frontend-design/SKILL.md +3 -2
  29. package/skills/json-render-mcp/SKILL.md +4 -3
  30. package/skills/playwright-cli/SKILL.md +1 -1
  31. package/skills/pmx-canvas/SKILL.md +9 -6
  32. package/skills/pmx-canvas/evals/evals.json +2 -2
  33. package/skills/pmx-canvas/references/ax-html-control-surface.md +3 -1
  34. package/skills/pmx-canvas/references/excalidraw-diagram-authoring.md +2 -2
  35. package/skills/pmx-canvas/references/full-reference.md +48 -53
  36. package/skills/pmx-canvas/references/html-primitives.md +3 -3
  37. package/skills/tufte-viz/SKILL.md +5 -4
  38. package/skills/web-artifacts-builder/SKILL.md +4 -4
  39. package/src/mcp/canvas-access.ts +4 -99
  40. package/src/mcp/server.ts +74 -201
  41. package/src/server/ax-state-manager.ts +3 -2
  42. package/src/server/bundled-skills.ts +3 -3
  43. package/src/server/canvas-schema.ts +46 -29
  44. package/src/server/canvas-state.ts +5 -0
  45. package/src/server/code-graph.ts +1 -1
  46. package/src/server/ext-app-lookup.ts +1 -8
  47. package/src/server/index.ts +1 -1
  48. package/src/server/intent-registry.ts +1 -1
  49. package/src/server/operations/composites.ts +39 -39
  50. package/src/server/operations/http.ts +3 -2
  51. package/src/server/operations/mcp.ts +7 -6
  52. package/src/server/operations/ops/app.ts +4 -4
  53. package/src/server/operations/ops/nodes.ts +5 -5
  54. package/src/server/operations/ops/query.ts +19 -8
  55. package/src/server/operations/ops/snapshots.ts +6 -6
  56. package/src/server/server.ts +30 -2
  57. package/src/shared/ax-intent.ts +1 -1
@@ -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>;
@@ -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
package/docs/mcp.md CHANGED
@@ -1,17 +1,19 @@
1
1
  # MCP reference
2
2
 
3
- PMX Canvas ships an MCP stdio server with **84 tools** + **14 core resources**,
3
+ PMX Canvas ships an MCP stdio server with **27 tools** + **14 core resources**,
4
4
  plus per-skill resources at `canvas://skills/<name>`. The server emits
5
5
  `notifications/resources/updated` when canvas state changes — humans pin
6
6
  nodes in the browser, agents are notified immediately.
7
7
 
8
- > **Consolidation in progress (plan-006/008).** The 84 tools are 15 action-discriminated
9
- > **composites** (recommended see below) plus 69 legacy single-purpose tools.
10
- > The composites fold the legacy tools behind an `action` (and, for `canvas_ax_gate`,
11
- > a `kind`) enum; each action dispatches to the same operation, so behavior is
12
- > identical. Folded legacy tools are marked `Deprecated:` in their descriptions and
13
- > are removed in v0.3 per [`api-stability.md`](api-stability.md). **Prefer the
14
- > composites.**
8
+ > **Consolidation completed in v0.3.0 (plan-006/008).** The MCP surface shrank
9
+ > from 84 tools to 27: 15 action-discriminated **composites** (recommended
10
+ > see below) plus 12 standalone tools. The 57 legacy single-purpose tools that
11
+ > the composites replaced were removed in v0.3.0 per
12
+ > [`api-stability.md`](api-stability.md); their descriptions carried
13
+ > `Deprecated:` prefixes for one minor before removal. The 6 snapshot tools
14
+ > remain registered as deprecated standalones — they fold into a
15
+ > `canvas_snapshot` composite in v0.4 (see [Standalone tools](#standalone-tools)
16
+ > below). **Prefer the composites.**
15
17
 
16
18
  ## Connect
17
19
 
@@ -35,15 +37,15 @@ The canvas auto-starts on first tool call.
35
37
  Action-discriminated tools that consolidate the single-purpose tools. Each maps
36
38
  its `action` to the same operation the legacy tool used, so results are identical.
37
39
 
38
- | Composite | `action` values | Replaces |
40
+ | Composite | `action` values | Replaced (removed in v0.3.0) |
39
41
  |-----------|-----------------|----------|
40
42
  | `canvas_node` | `add` · `get` · `update` · `remove` | `canvas_add_node`, `canvas_get_node`, `canvas_update_node`, `canvas_remove_node`, `canvas_add_html_node` (`add` + `type:"html"`), `canvas_add_html_primitive` (`add` + `type:"html"`, `primitive:"<kind>"`), `canvas_refresh_webpage_node` (`update` + `refresh:true`) |
41
43
  | `canvas_render` | `describe-schema` · `validate` · `add-json-render` · `stream-json-render` · `add-graph` | `canvas_describe_schema`, `canvas_validate_spec`, `canvas_add_json_render_node`, `canvas_stream_json_render_node`, `canvas_add_graph_node` |
42
44
  | `canvas_edge` | `add` · `remove` | `canvas_add_edge`, `canvas_remove_edge` |
43
45
  | `canvas_group` | `create` · `add` · `ungroup` | `canvas_create_group`, `canvas_group_nodes`, `canvas_ungroup` |
44
46
  | `canvas_history` | `undo` · `redo` | `canvas_undo`, `canvas_redo` |
45
- | `canvas_view` | `arrange` · `focus` · `fit` · `clear` | `canvas_arrange`, `canvas_focus_node`, `canvas_fit_view`, `canvas_clear` |
46
- | `canvas_query` | `search` · `layout` | `canvas_search`, `canvas_get_layout` |
47
+ | `canvas_view` | `arrange` · `focus` · `fit` · `clear` · `remove-annotation` | `canvas_arrange`, `canvas_focus_node`, `canvas_fit_view`, `canvas_clear`, `canvas_remove_annotation` |
48
+ | `canvas_query` | `search` · `layout` · `validate` | `canvas_search`, `canvas_get_layout`, `canvas_validate` |
47
49
  | `canvas_webview` | `status` · `start` · `stop` · `resize` · `evaluate` | `canvas_webview_status`, `canvas_webview_start`, `canvas_webview_stop`, `canvas_resize`, `canvas_evaluate` |
48
50
  | `canvas_app` | `open-mcp-app` · `diagram` · `build-artifact` | `canvas_open_mcp_app`, `canvas_add_diagram`, `canvas_build_web_artifact` |
49
51
  | `canvas_ax_state` | `get` · `set-focus` · `set-policy` · `report-capability` | `canvas_get_ax`, `canvas_set_ax_focus`, `canvas_set_ax_policy`, `canvas_report_host_capability` |
@@ -64,7 +66,7 @@ veto it — before the mutation lands.
64
66
  - `clear` — abandon/dissolve it explicitly. Normal linked mutations settle automatically.
65
67
 
66
68
  Intents are **ephemeral presence**: never persisted, never snapshotted, never in
67
- `canvas_get_layout`, and auto-expiring. They ride their own SSE channel
69
+ `canvas_query { action: "layout" }`, and auto-expiring. They ride their own SSE channel
68
70
  (`ax-intent` / `ax-intent-clear`) and replay to reconnecting browsers while still
69
71
  live. Best practice — narrate your next move: `signal` → mutate with the returned
70
72
  `intent.id` as `intentId`. A vetoed or expired linked mutation is rejected, and a
@@ -87,86 +89,105 @@ tools). `canvas_screenshot` also stays standalone — it returns a binary image
87
89
  the composite/registry JSON wire shape does not model. (Wave 5 folded
88
90
  `canvas_refresh_webpage_node` → `canvas_node { action: "update", refresh: true }` after
89
91
  fixing `node.update`'s `formatResult` to surface a FAILED refresh as `isError` +
90
- `{ ok:false, error }` instead of masking it as a false `{ ok:true }`.) Snapshots
91
- fold as their registry slice lands.
92
+ `{ ok:false, error }` instead of masking it as a false `{ ok:true }`.) The 6
93
+ snapshot tools are the one deferred fold: a `canvas_snapshot` composite is
94
+ planned for v0.4 (the name collides with the legacy `canvas_snapshot` save
95
+ tool, and [`api-stability.md`](api-stability.md) requires deprecating a tool
96
+ for one minor before removing it) — they stay registered as deprecated
97
+ standalones until then.
92
98
 
93
- ## Tools (legacy single-purpose)
99
+ ## Standalone tools
100
+
101
+ 12 tools that intentionally stay outside the composites — either because
102
+ folding them would hurt (distinct callers, binary payloads, trust-boundary or
103
+ execution-intent semantics), or because they're mid-deprecation pending a
104
+ v0.4 composite. See [Migration reference](#migration-reference) below for the
105
+ 57 tools these composites replaced.
94
106
 
95
107
  | Tool | Description |
96
108
  |------|-------------|
97
- | `canvas_add_node` | Add a node (markdown, status, context, file, webpage, html, etc.) |
98
- | `canvas_add_html_node` | **Deprecated** `canvas_node { action: "add", type: "html" }`. Create an `html` node from a self-contained HTML/JS document (sandboxed iframe) |
99
- | `canvas_add_html_primitive` | **Deprecated** `canvas_node { action: "add", type: "html", primitive: "<kind>" }`. Create a reusable generated HTML communication primitive as a sandboxed `html` node |
100
- | `canvas_add_diagram` | Hand-drawn diagram via the hosted Excalidraw MCP App (preset alias for `canvas_open_mcp_app`) |
101
- | `canvas_open_mcp_app` | Open any [MCP Apps](https://modelcontextprotocol.io/docs/extensions/apps) server's `ui://` resource as an iframe node |
102
- | `canvas_describe_schema` | Describe the running server's create schemas, examples, json-render catalog, and HTML primitive catalog |
103
- | `canvas_validate_spec` | Validate a json-render spec, graph payload, or HTML primitive payload without creating a node |
104
- | `canvas_refresh_webpage_node` | **Deprecated** → `canvas_node { action: "update", refresh: true }`. Re-fetch and update a webpage node from its stored URL |
105
- | `canvas_add_json_render_node` | Create a native json-render node from a validated spec |
106
- | `canvas_stream_json_render_node` | Progressively build a json-render node from SpecStream JSON-Patch ops (live/streaming panels) |
107
- | `canvas_add_graph_node` | Create a native graph node (line, bar, pie, area, scatter, radar, stacked-bar, composed, sparkline, dot-plot, bullet, slopegraph) |
108
- | `canvas_build_web_artifact` | Build a bundled HTML artifact and open it on the canvas |
109
-
110
- `canvas_add_html_node` accepts optional `summary`, `agentSummary`, `embeddedNodeIds`, and
111
- `embeddedUrls`. PMX also derives a bounded text summary from visible HTML, so rich HTML nodes stay
112
- searchable and readable in pinned/spatial context.
113
- | `canvas_update_node` | Update content, position, size, collapsed state |
114
- | `canvas_remove_node` | Remove a node and its edges |
115
- | `canvas_get_layout` | Get full canvas state |
116
- | `canvas_get_node` | Get a single node by ID |
117
- | `canvas_remove_annotation` | Remove a human-drawn annotation by ID |
118
- | `canvas_add_edge` | Connect two nodes |
119
- | `canvas_remove_edge` | Remove a connection |
120
- | `canvas_arrange` | Auto-arrange (grid/column/flow) |
121
- | `canvas_validate` | Validate collisions, containment, and missing edge endpoints |
122
- | `canvas_focus_node` | Pan viewport to a node; use CLI `focus --no-pan` when you only need to select/raise |
123
- | `canvas_fit_view` | Fit the canvas viewport to all nodes or a selected subset |
124
- | `canvas_get_ax` | Read the PMX AX state (focus, work items, approvals, review annotations, host capability) plus pinned/focused context |
125
- | `canvas_set_ax_focus` | Set the host-agnostic AX focus node set; adapters can pass a source such as `codex` |
126
- | `canvas_record_ax_event` | Record a normalized timeline `agent-event` (prompt/assistant-message/tool-start/tool-result/failure/approval/steering) |
127
- | `canvas_send_steering` | Record a `steering-message`: a user instruction from the surface to the active agent session |
128
- | `canvas_get_ax_timeline` | Read the bounded AX timeline (events, evidence, steering) plus counts |
129
- | `canvas_add_work_item` | Add a canvas-bound `work-item` (visible task/plan/status tied to nodes) |
130
- | `canvas_update_work_item` | Update a work item's title/status/detail/nodeIds by ID |
131
- | `canvas_request_approval` | Request human approval via an `approval-gate` (pending) before a high-impact action |
132
- | `canvas_resolve_approval` | Resolve a pending approval gate (`approved`/`rejected`) |
133
- | `canvas_add_evidence` | Record an `evidence-item` on the timeline (logs/tool-result/screenshot/file/diff/test-output) |
134
- | `canvas_add_review_annotation` | Add a canvas-bound `review-annotation` (comment/finding) anchored to a node, file, or region |
135
- | `canvas_report_host_capability` | Report a host/session `host-capability` for diagnostics |
109
+ | `canvas_batch` | Run a batch of canvas operations with `$ref` support |
110
+ | `canvas_pin_nodes` | Pin nodes to include in agent context |
111
+ | `canvas_invoke_command` | Invoke a registry command (`pmx.plan`, `pmx.execute`, `pmx.promote-context`, `pmx.summarize`, `pmx.review`); records a `command` agent-event, unknown names rejected |
136
112
  | `canvas_ax_interaction` | Submit one capability-gated AX interaction envelope (`{ type, sourceNodeId, payload }`) that maps onto an AX operation; the server re-validates and clamps sandboxed surfaces to their own node |
137
- | `canvas_claim_ax_delivery` | Claim undelivered steering messages for an adapterless consumer (loop-safe — never returns steering the consumer originated) |
138
- | `canvas_mark_ax_delivery` | Mark a steering message delivered for a consumer |
139
- | `canvas_request_elicitation` | Request structured human input via a canvas-bound `elicitation` (pending) |
140
- | `canvas_respond_elicitation` | Respond to / resolve a pending elicitation |
141
- | `canvas_request_mode` | Request a workflow `mode-request` transition (plan/execute/autonomous) |
142
- | `canvas_resolve_mode` | Resolve a pending mode request |
143
113
  | `canvas_ingest_activity` | Ingest a harness-forwarded agent activity (tool/session event); the board auto-reacts with kind-driven, overridable defaults (failure → work item + review + evidence; `tool-result`+success → evidence). Makes AX bidirectional |
144
- | `canvas_await_approval` | Block until an approval gate resolves (human approves/rejects in the browser) or the timeout elapses (`timeoutMs` 0 = immediate read). Gates that actually gate |
145
- | `canvas_await_elicitation` | Block until an elicitation is answered or the timeout elapses |
146
- | `canvas_await_mode` | Block until a mode request resolves or the timeout elapses |
147
- | `canvas_invoke_command` | Invoke a registry command (`pmx.plan`, `pmx.execute`, `pmx.promote-context`, `pmx.summarize`, `pmx.review`); records a `command` agent-event, unknown names rejected |
148
- | `canvas_set_ax_policy` | Patch the canvas-bound tool/prompt policy (`tools.allowed\|excluded\|approvalRequired`, `prompt.systemAppend\|mode`); patches merge and are normalized |
149
- | `canvas_pin_nodes` | Pin nodes to include in agent context |
150
- | `canvas_clear` | Clear all nodes and edges |
151
- | `canvas_snapshot` | Save current canvas as a named snapshot |
152
- | `canvas_list_snapshots` | List saved snapshots, bounded to the newest 20 by default |
153
- | `canvas_gc_snapshots` | Delete old snapshots while keeping the newest N |
154
- | `canvas_restore` | Restore canvas from a saved snapshot |
155
- | `canvas_delete_snapshot` | Delete a saved snapshot |
156
- | `canvas_search` | Find nodes by title/content keywords |
157
- | `canvas_undo` | Undo the last canvas mutation |
158
- | `canvas_redo` | Redo the last undone mutation |
159
- | `canvas_diff` | Compare current canvas vs a saved snapshot |
160
- | `canvas_create_group` | Create a group containing specified nodes |
161
- | `canvas_group_nodes` | Add nodes to an existing group |
162
- | `canvas_ungroup` | Release all children from a group |
163
- | `canvas_batch` | Run a batch of canvas operations with `$ref` support |
164
- | `canvas_webview_status` | Get Bun.WebView automation status for the workbench |
165
- | `canvas_webview_start` | Start or replace the Bun.WebView automation session |
166
- | `canvas_webview_stop` | Stop the active Bun.WebView automation session |
167
- | `canvas_evaluate` | Evaluate JavaScript in the active workbench automation session |
168
- | `canvas_resize` | Resize the active workbench automation viewport |
169
114
  | `canvas_screenshot` | Capture a screenshot from the active workbench automation session |
115
+ | `canvas_snapshot` | **Deprecated** → folds into the `canvas_snapshot` composite in v0.4 (actions `save\|list\|restore\|delete\|gc\|diff`). Save current canvas as a named snapshot |
116
+ | `canvas_list_snapshots` | **Deprecated** → folds into the `canvas_snapshot` composite in v0.4 (actions `save\|list\|restore\|delete\|gc\|diff`). List saved snapshots, bounded to the newest 20 by default |
117
+ | `canvas_restore` | **Deprecated** → folds into the `canvas_snapshot` composite in v0.4 (actions `save\|list\|restore\|delete\|gc\|diff`). Restore canvas from a saved snapshot |
118
+ | `canvas_delete_snapshot` | **Deprecated** → folds into the `canvas_snapshot` composite in v0.4 (actions `save\|list\|restore\|delete\|gc\|diff`). Delete a saved snapshot |
119
+ | `canvas_gc_snapshots` | **Deprecated** → folds into the `canvas_snapshot` composite in v0.4 (actions `save\|list\|restore\|delete\|gc\|diff`). Delete old snapshots while keeping the newest N |
120
+ | `canvas_diff` | **Deprecated** → folds into the `canvas_snapshot` composite in v0.4 (actions `save\|list\|restore\|delete\|gc\|diff`). Compare current canvas vs a saved snapshot |
121
+
122
+ `canvas_node { action: "add", type: "html" }` accepts optional `summary`, `agentSummary`,
123
+ `embeddedNodeIds`, and `embeddedUrls`. PMX also derives a bounded text summary from visible
124
+ HTML, so rich HTML nodes stay searchable and readable in pinned/spatial context.
125
+
126
+ ## Migration reference
127
+
128
+ The 57 legacy single-purpose tools below were removed in v0.3.0. Each row is
129
+ the composite call that replaces it — kept as a lookup table for anyone
130
+ migrating an older integration.
131
+
132
+ | Removed tool | Composite replacement |
133
+ |------|-------------|
134
+ | `canvas_add_node` | `canvas_node { action: "add" }` |
135
+ | `canvas_get_node` | `canvas_node { action: "get" }` |
136
+ | `canvas_update_node` | `canvas_node { action: "update" }` |
137
+ | `canvas_remove_node` | `canvas_node { action: "remove" }` |
138
+ | `canvas_add_html_node` | `canvas_node { action: "add", type: "html" }` |
139
+ | `canvas_add_html_primitive` | `canvas_node { action: "add", type: "html", primitive: "<kind>" }` |
140
+ | `canvas_refresh_webpage_node` | `canvas_node { action: "update", refresh: true }` |
141
+ | `canvas_describe_schema` | `canvas_render { action: "describe-schema" }` |
142
+ | `canvas_validate_spec` | `canvas_render { action: "validate" }` |
143
+ | `canvas_add_json_render_node` | `canvas_render { action: "add-json-render" }` |
144
+ | `canvas_stream_json_render_node` | `canvas_render { action: "stream-json-render" }` |
145
+ | `canvas_add_graph_node` | `canvas_render { action: "add-graph" }` |
146
+ | `canvas_add_edge` | `canvas_edge { action: "add" }` |
147
+ | `canvas_remove_edge` | `canvas_edge { action: "remove" }` |
148
+ | `canvas_create_group` | `canvas_group { action: "create" }` |
149
+ | `canvas_group_nodes` | `canvas_group { action: "add" }` |
150
+ | `canvas_ungroup` | `canvas_group { action: "ungroup" }` |
151
+ | `canvas_undo` | `canvas_history { action: "undo" }` |
152
+ | `canvas_redo` | `canvas_history { action: "redo" }` |
153
+ | `canvas_arrange` | `canvas_view { action: "arrange" }` |
154
+ | `canvas_focus_node` | `canvas_view { action: "focus" }` |
155
+ | `canvas_fit_view` | `canvas_view { action: "fit" }` |
156
+ | `canvas_clear` | `canvas_view { action: "clear" }` |
157
+ | `canvas_remove_annotation` | `canvas_view { action: "remove-annotation" }` |
158
+ | `canvas_search` | `canvas_query { action: "search" }` |
159
+ | `canvas_get_layout` | `canvas_query { action: "layout" }` |
160
+ | `canvas_validate` | `canvas_query { action: "validate" }` |
161
+ | `canvas_open_mcp_app` | `canvas_app { action: "open-mcp-app" }` |
162
+ | `canvas_add_diagram` | `canvas_app { action: "diagram" }` |
163
+ | `canvas_build_web_artifact` | `canvas_app { action: "build-artifact" }` |
164
+ | `canvas_webview_status` | `canvas_webview { action: "status" }` |
165
+ | `canvas_webview_start` | `canvas_webview { action: "start" }` |
166
+ | `canvas_webview_stop` | `canvas_webview { action: "stop" }` |
167
+ | `canvas_resize` | `canvas_webview { action: "resize" }` |
168
+ | `canvas_evaluate` | `canvas_webview { action: "evaluate" }` |
169
+ | `canvas_get_ax` | `canvas_ax_state { action: "get" }` |
170
+ | `canvas_set_ax_focus` | `canvas_ax_state { action: "set-focus" }` |
171
+ | `canvas_set_ax_policy` | `canvas_ax_state { action: "set-policy" }` |
172
+ | `canvas_report_host_capability` | `canvas_ax_state { action: "report-capability" }` |
173
+ | `canvas_add_work_item` | `canvas_ax_work { action: "add" }` |
174
+ | `canvas_update_work_item` | `canvas_ax_work { action: "update" }` |
175
+ | `canvas_add_review_annotation` | `canvas_ax_work { action: "annotate" }` |
176
+ | `canvas_request_approval` | `canvas_ax_gate { kind: "approval", action: "request" }` |
177
+ | `canvas_resolve_approval` | `canvas_ax_gate { kind: "approval", action: "resolve" }` |
178
+ | `canvas_await_approval` | `canvas_ax_gate { kind: "approval", action: "await" }` |
179
+ | `canvas_request_elicitation` | `canvas_ax_gate { kind: "elicitation", action: "request" }` |
180
+ | `canvas_respond_elicitation` | `canvas_ax_gate { kind: "elicitation", action: "resolve" }` |
181
+ | `canvas_await_elicitation` | `canvas_ax_gate { kind: "elicitation", action: "await" }` |
182
+ | `canvas_request_mode` | `canvas_ax_gate { kind: "mode", action: "request" }` |
183
+ | `canvas_resolve_mode` | `canvas_ax_gate { kind: "mode", action: "resolve" }` |
184
+ | `canvas_await_mode` | `canvas_ax_gate { kind: "mode", action: "await" }` |
185
+ | `canvas_get_ax_timeline` | `canvas_ax_timeline { action: "read" }` |
186
+ | `canvas_record_ax_event` | `canvas_ax_timeline { action: "record-event" }` |
187
+ | `canvas_add_evidence` | `canvas_ax_timeline { action: "add-evidence" }` |
188
+ | `canvas_send_steering` | `canvas_ax_timeline { action: "send-steering" }` |
189
+ | `canvas_claim_ax_delivery` | `canvas_ax_delivery { action: "claim" }` |
190
+ | `canvas_mark_ax_delivery` | `canvas_ax_delivery { action: "mark" }` |
170
191
 
171
192
  ## Resources
172
193
 
@@ -255,27 +276,27 @@ raw stroke geometry or visual shape.
255
276
 
256
277
  Annotations are a browser-visible markup layer. Use the pen toolbar button to
257
278
  draw and the eraser toolbar button to remove an annotation again; agents can also
258
- remove a known annotation ID with `canvas_remove_annotation`.
279
+ remove a known annotation ID with `canvas_view { action: "remove-annotation", id }`.
259
280
 
260
281
  Use WebView automation when an agent needs to actually see annotations as drawn.
261
- For example, inspect `.annotation-layer path` with `canvas_evaluate` or capture a
262
- `canvas_screenshot` to distinguish an arrow from a line, circle, or highlight.
282
+ For example, inspect `.annotation-layer path` with `canvas_webview { action: "evaluate" }`
283
+ or capture a `canvas_screenshot` to distinguish an arrow from a line, circle, or highlight.
263
284
 
264
285
  ## Node-type routing
265
286
 
266
- MCP node creation uses dedicated tools for structured node families. Read
267
- `mcp.nodeTypeRouting` from `canvas_describe_schema` / `canvas://schema` when
268
- in doubt:
269
-
270
- - `json-render` → `canvas_add_json_render_node`
271
- - `graph` → `canvas_add_graph_node`
272
- - `html-primitive` → `canvas_node { action: "add", type: "html", primitive: "<kind>" }` (or the deprecated `canvas_add_html_primitive`)
273
- - `html` → `canvas_node { action: "add", type: "html" }` (or the deprecated `canvas_add_html_node`)
274
- - `web-artifact` → `canvas_build_web_artifact`
275
- - `mcp-app` → `canvas_open_mcp_app`
276
- - `group` → `canvas_create_group`
287
+ MCP node creation uses dedicated composite actions for structured node
288
+ families. Read `mcp.nodeTypeRouting` from `canvas_render { action:
289
+ "describe-schema" }` / `canvas://schema` when in doubt:
290
+
291
+ - `json-render` → `canvas_render { action: "add-json-render" }`
292
+ - `graph` → `canvas_render { action: "add-graph" }`
293
+ - `html-primitive` → `canvas_node { action: "add", type: "html", primitive: "<kind>" }`
294
+ - `html` → `canvas_node { action: "add", type: "html" }`
295
+ - `web-artifact` → `canvas_app { action: "build-artifact" }`
296
+ - `mcp-app` → `canvas_app { action: "open-mcp-app" }`
297
+ - `group` → `canvas_group { action: "create" }`
277
298
  - Basic nodes (`markdown`, `status`, `file`, `image`, `webpage`) →
278
- `canvas_add_node`
299
+ `canvas_node { action: "add" }`
279
300
 
280
301
  ## CLI/MCP alignment
281
302