pmx-canvas 0.1.36 → 0.2.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 (91) hide show
  1. package/CHANGELOG.md +447 -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 +267 -0
  6. package/dist/types/server/ax-state.d.ts +3 -1
  7. package/dist/types/server/canvas-db.d.ts +13 -0
  8. package/dist/types/server/canvas-operations.d.ts +1 -12
  9. package/dist/types/server/canvas-state.d.ts +8 -23
  10. package/dist/types/server/index.d.ts +6 -24
  11. package/dist/types/server/operations/composites.d.ts +121 -0
  12. package/dist/types/server/operations/http.d.ts +7 -0
  13. package/dist/types/server/operations/index.d.ts +8 -0
  14. package/dist/types/server/operations/invoker.d.ts +13 -0
  15. package/dist/types/server/operations/mcp.d.ts +15 -0
  16. package/dist/types/server/operations/ops/annotation.d.ts +2 -0
  17. package/dist/types/server/operations/ops/app.d.ts +33 -0
  18. package/dist/types/server/operations/ops/ax-await.d.ts +2 -0
  19. package/dist/types/server/operations/ops/ax-shared.d.ts +31 -0
  20. package/dist/types/server/operations/ops/ax-state.d.ts +2 -0
  21. package/dist/types/server/operations/ops/ax-timeline.d.ts +2 -0
  22. package/dist/types/server/operations/ops/ax-work.d.ts +2 -0
  23. package/dist/types/server/operations/ops/batch.d.ts +19 -0
  24. package/dist/types/server/operations/ops/edges.d.ts +2 -0
  25. package/dist/types/server/operations/ops/groups.d.ts +2 -0
  26. package/dist/types/server/operations/ops/json-render.d.ts +31 -0
  27. package/dist/types/server/operations/ops/nodes.d.ts +62 -0
  28. package/dist/types/server/operations/ops/query.d.ts +2 -0
  29. package/dist/types/server/operations/ops/snapshots.d.ts +2 -0
  30. package/dist/types/server/operations/ops/validate.d.ts +2 -0
  31. package/dist/types/server/operations/ops/viewport.d.ts +2 -0
  32. package/dist/types/server/operations/ops/webview.d.ts +2 -0
  33. package/dist/types/server/operations/registry.d.ts +15 -0
  34. package/dist/types/server/operations/types.d.ts +116 -0
  35. package/dist/types/server/operations/webview-runner.d.ts +69 -0
  36. package/docs/RELEASE.md +5 -0
  37. package/docs/adr-001-bun-only-runtime.md +46 -0
  38. package/docs/api-stability.md +57 -0
  39. package/docs/ax-host-adapter-contract.md +19 -1
  40. package/docs/ax-state-contract.md +72 -0
  41. package/docs/http-api.md +4 -0
  42. package/docs/mcp.md +61 -12
  43. package/docs/plans/plan-005-operation-registry.md +84 -0
  44. package/docs/plans/plan-006-mcp-tool-consolidation.md +109 -0
  45. package/docs/plans/plan-007-ax-domain.md +99 -0
  46. package/docs/plans/plan-008-registry-finish.md +91 -0
  47. package/docs/tech-debt-assessment-2026-06.md +90 -0
  48. package/package.json +3 -3
  49. package/skills/pmx-canvas/SKILL.md +221 -193
  50. package/skills/pmx-canvas/evals/evals.json +3 -3
  51. package/skills/pmx-canvas/references/ax-html-control-surface.md +93 -0
  52. package/skills/pmx-canvas/references/codex-app-adapter.md +13 -14
  53. package/skills/pmx-canvas/references/github-copilot-app-adapter.md +26 -11
  54. package/src/cli/agent.ts +52 -31
  55. package/src/mcp/canvas-access.ts +30 -830
  56. package/src/mcp/server.ts +162 -2014
  57. package/src/server/ax-context.ts +8 -1
  58. package/src/server/ax-state-manager.ts +826 -0
  59. package/src/server/ax-state.ts +10 -2
  60. package/src/server/canvas-db.ts +35 -0
  61. package/src/server/canvas-operations.ts +2 -328
  62. package/src/server/canvas-schema.ts +2 -2
  63. package/src/server/canvas-state.ts +103 -465
  64. package/src/server/index.ts +54 -190
  65. package/src/server/operations/composites.ts +355 -0
  66. package/src/server/operations/http.ts +103 -0
  67. package/src/server/operations/index.ts +65 -0
  68. package/src/server/operations/invoker.ts +87 -0
  69. package/src/server/operations/mcp.ts +221 -0
  70. package/src/server/operations/ops/annotation.ts +60 -0
  71. package/src/server/operations/ops/app.ts +447 -0
  72. package/src/server/operations/ops/ax-await.ts +216 -0
  73. package/src/server/operations/ops/ax-shared.ts +38 -0
  74. package/src/server/operations/ops/ax-state.ts +249 -0
  75. package/src/server/operations/ops/ax-timeline.ts +381 -0
  76. package/src/server/operations/ops/ax-work.ts +635 -0
  77. package/src/server/operations/ops/batch.ts +365 -0
  78. package/src/server/operations/ops/edges.ts +166 -0
  79. package/src/server/operations/ops/groups.ts +176 -0
  80. package/src/server/operations/ops/json-render.ts +691 -0
  81. package/src/server/operations/ops/nodes.ts +1047 -0
  82. package/src/server/operations/ops/query.ts +281 -0
  83. package/src/server/operations/ops/snapshots.ts +366 -0
  84. package/src/server/operations/ops/validate.ts +37 -0
  85. package/src/server/operations/ops/viewport.ts +219 -0
  86. package/src/server/operations/ops/webview.ts +339 -0
  87. package/src/server/operations/registry.ts +79 -0
  88. package/src/server/operations/types.ts +150 -0
  89. package/src/server/operations/webview-runner.ts +77 -0
  90. package/src/server/server.ts +158 -2255
  91. package/src/server/web-artifacts.ts +6 -2
package/CHANGELOG.md CHANGED
@@ -5,6 +5,451 @@ All notable changes to `pmx-canvas` are documented here. This project follows
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [0.2.1] - 2026-06-17
9
+
10
+ ### Fixed
11
+
12
+ - **Compact AX context surfaces the NEWEST steering first (report #57).** The
13
+ `delivery.pendingSteering` lead block on `GET /api/canvas/ax/context` /
14
+ `canvas://ax-context` returned the OLDEST 10 undelivered steers, so on a long-lived
15
+ board a fresh steer was buried behind old unacked ones and never appeared in the
16
+ compact block. It now returns the **newest** undelivered steering first (capped at
17
+ 10), so a fresh steer is always visible. The FIFO claim/ack delivery queue
18
+ (`/api/canvas/ax/delivery/pending`, `canvas_ax_delivery { action: "claim" }`) is
19
+ unchanged — still oldest-first for ordered processing.
20
+
21
+ ### Added
22
+
23
+ - **AX context delivery backlog counts (report #57).** `delivery` now includes
24
+ `totalPending` (undelivered steering for the consumer, loop-safe) and `omittedPending`
25
+ (`= totalPending − pendingSteering.length`), so an agent can tell the compact block
26
+ omitted a backlog and drain the full FIFO queue when `omittedPending > 0`. Additive,
27
+ non-breaking.
28
+ - **Blessed AX HTML Control Surface recipe (report #60).** New
29
+ `skills/pmx-canvas/references/ax-html-control-surface.md` (linked from `SKILL.md`): a
30
+ copy-paste-safe template (awaited `emit` + live ack display + `pmx-ax-update`
31
+ reflection) plus the three footguns that make a hand-rolled AX node look inert — the
32
+ sandboxed opaque-origin iframe throws on `localStorage`/`sessionStorage`/cookies,
33
+ `emit` must be awaited, and `ax.steer` is recorded (queued), not delivered.
34
+
35
+ ### Docs
36
+
37
+ - **Canvas-origin steering does not wake the active agent by itself (report #59).** The
38
+ host-adapter contract, `SKILL.md`, and the GitHub Copilot adapter reference now state
39
+ plainly that a browser-origin `ax.steer` (and its `ok:true` emit ack) is *queued*, not
40
+ pushed into the live session; the wake is host-adapter-owned (drain
41
+ `canvas_ax_delivery { action: "claim" }` → native send → `mark`), and a steering button
42
+ must be labeled "queued for the agent's next turn." Adapters should read steering from
43
+ the compact `delivery.pendingSteering` block, not `timeline.pendingSteering`.
44
+
45
+ ## [0.2.0] - 2026-06-16
46
+
47
+ ### Breaking
48
+
49
+ Per [`docs/api-stability.md`](docs/api-stability.md), v0.2.0 is the first minor
50
+ allowed to change public-surface behavior, and every such change is collected here.
51
+ All are **cross-surface unifications** — a surface that silently diverged from the
52
+ others is brought into line, never a new restriction invented for this release. The
53
+ detailed per-slice rationale is under **Changed**. MCP tool *names* + input *schemas*,
54
+ HTTP routes + documented success shapes, and CLI flags are all unchanged (frozen by
55
+ `tests/unit/mcp-tool-freeze.test.ts`).
56
+
57
+ - **SDK `PmxCanvas.removeNode(id)` now throws `Node "<id>" not found.` on an unknown
58
+ id** instead of silently succeeding — matching the HTTP `DELETE` (already 404) and
59
+ local MCP `canvas_remove_node`. Idempotent-delete callers must guard or catch.
60
+ - **Local (in-process) MCP error surfacing now matches HTTP for missing targets:**
61
+ `canvas_remove_node`, `canvas_remove_edge`, `canvas_create_group` (missing child ids),
62
+ `canvas_focus_node`, and `canvas_ungroup` now return an MCP error result (were silent
63
+ success / a different message).
64
+ - **MCP `*_resolve` / `*_respond` / `update_work_item` / review `update` /
65
+ `invoke_command` now return an `isError` result** (not a success-shaped
66
+ `{ ok:false, <item>:null }`) when the target is missing, already resolved, or an
67
+ unknown command. Success shapes are unchanged.
68
+ - **AX blocking-gate await over a *remote* MCP transport** surfaces a missing gate as an
69
+ `isError` rather than `{ <gate>:null }` (the in-process path is unchanged). The HTTP
70
+ single-gate read keeps its 404 status but its missing-gate body changed to
71
+ `{ ok:false, <gate>:null, pending:false }`, and it no longer aborts the long-poll on
72
+ client disconnect (it runs to its ≤120 s timeout).
73
+ - **`canvas_batch` entries now inherit each operation's validation** (they dispatch
74
+ through the same registry op as the standalone tools): a `node.add` with no `type`
75
+ fails the batch (was a silent markdown node), an empty-name `snapshot.save` fails the
76
+ batch (was `ok:true`), and `node.add type:"html-primitive"` now succeeds (was a
77
+ batch-only rejection). A batched `node.update` now applies the full HTTP superset
78
+ (webpage `refresh`, top-level `html`, group `children`). The
79
+ `{ ok, results, refs, failedIndex?, error? }` envelope itself is unchanged.
80
+
81
+ ### Added
82
+
83
+ - **AX state isolated into `AxStateManager` (plan-007 Slice A).** The canvas-bound
84
+ AX state, its mutators/readers, and the timeline-direct ops moved out of the
85
+ 2,498-line `CanvasStateManager` into `src/server/ax-state-manager.ts`;
86
+ `CanvasStateManager` delegates, so the public surface (SDK/HTTP/MCP) is
87
+ unchanged. The three-partition snapshot-vs-audit contract is now documented
88
+ authoritatively in `docs/ax-state-contract.md`.
89
+
90
+ - **Composite MCP tools (plan-006 MCP tool consolidation, wave 1).** Seven
91
+ action-discriminated tools fold the single-purpose tools behind a clear
92
+ `action` enum: `canvas_node` (add/get/update/remove), `canvas_render`
93
+ (describe-schema/validate/add-json-render/stream-json-render/add-graph),
94
+ `canvas_edge` (add/remove), `canvas_group` (create/add/ungroup),
95
+ `canvas_history` (undo/redo), `canvas_view` (arrange/focus/fit/clear), and
96
+ `canvas_query` (search/layout). Each action dispatches to the same registered
97
+ operation as its standalone tool — reusing that op's input mapping and result
98
+ formatting — so behavior is byte-identical (proven by
99
+ `tests/unit/mcp-composites.test.ts`). Additive: the legacy single-purpose
100
+ tools keep working and are removed in v0.3 per `docs/api-stability.md`. The
101
+ public MCP surface grows from 69 to 76 tools (deliberate; see
102
+ `tests/unit/mcp-tool-freeze.test.ts`). Composites/actions whose targets are
103
+ not yet registry-backed (`canvas_snapshot`, and the `refresh` /
104
+ `add-primitive` / `remove-annotation` / board-validation actions) land in later
105
+ waves as the AX / side-channel registry slices complete.
106
+
107
+ - **AX composite MCP tools (plan-006 MCP tool consolidation, wave 2 / plan-007
108
+ Slice C).** Five action-discriminated tools fold the 22 legacy AX tools behind
109
+ clear enums: `canvas_ax_state` (get/set-focus/set-policy/report-capability),
110
+ `canvas_ax_work` (add/update/annotate), `canvas_ax_timeline`
111
+ (read/record-event/add-evidence/send-steering), `canvas_ax_delivery`
112
+ (claim/mark), and `canvas_ax_gate` — which folds **9 gate tools into 1** via two
113
+ discriminators `{ kind: approval|elicitation|mode, action: request|resolve|await }`
114
+ so the request/resolve/await lifecycle finally reads as one tool. Each action
115
+ dispatches to the same registered AX operation as its standalone tool — reusing
116
+ that op's input mapping and result formatting — so behavior is byte-identical
117
+ (proven by `tests/unit/mcp-composites.test.ts`). `canvas_ax_interaction`,
118
+ `canvas_ingest_activity`, and `canvas_invoke_command` deliberately stay
119
+ standalone (trust-boundary / firehose / execution-intent tools). Additive: the
120
+ legacy AX tools keep working and are removed in v0.3 per `docs/api-stability.md`.
121
+ The public MCP surface grows from 76 to 81 tools (deliberate; see
122
+ `tests/unit/mcp-tool-freeze.test.ts`).
123
+
124
+ - **`canvas_webview` composite + the 5 webview ops migrated to the operation
125
+ registry via runner injection (plan-008 Wave 3).** The five browser-automation
126
+ tools (`canvas_webview_status`, `canvas_webview_start`, `canvas_webview_stop`,
127
+ `canvas_resize`, `canvas_evaluate`) are now defined once in
128
+ `src/server/operations/ops/webview.ts` and shared by HTTP, MCP, and the SDK.
129
+ The Bun.WebView machinery lives in `server.ts` (which `operations/` must not
130
+ import), so the runner is **injected** — `src/server/operations/webview-runner.ts`
131
+ declares a `WebviewRunner` interface + `setWebviewRunner`/`getWebviewRunner`,
132
+ and `server.ts` wires the real automation functions at module load, exactly
133
+ mirroring the `setOperationEventEmitter` SSE-emitter injection. The new
134
+ `canvas_webview` composite folds the five tools behind a `status` / `start` /
135
+ `stop` / `resize` / `evaluate` action enum; each action dispatches to the same
136
+ op, so behavior is byte-identical (proven by `tests/unit/mcp-composites.test.ts`).
137
+ Wire shapes, MCP result shapes, and the `canvas_evaluate` arbitrary-eval trust
138
+ posture are unchanged. `canvas_screenshot` is deliberately **not** folded — it
139
+ returns a binary image payload the registry JSON wire shape does not model — and
140
+ stays a standalone hand-written tool (its `POST /api/workbench/webview/screenshot`
141
+ route also stays hand-written). The public MCP surface grows from 81 to 82 tools
142
+ (deliberate; see `tests/unit/mcp-tool-freeze.test.ts`).
143
+
144
+ - **`canvas_app` composite + the 3 external / built-content tools migrated to the
145
+ operation registry (plan-008 Wave 4).** `canvas_open_mcp_app`,
146
+ `canvas_add_diagram`, and `canvas_build_web_artifact` are now defined once in
147
+ `src/server/operations/ops/app.ts` (ops `mcpapp.open`, `diagram.open`,
148
+ `webartifact.build`) and shared by HTTP, MCP, and the SDK. They were previously
149
+ deferred as "poor fits" (stateful external session + custom SSE; long-running
150
+ build), but they migrate cleanly: `executeOperation` is async (the long-running
151
+ build fits — the "long-running" caveat is about MCP-client timeouts, not registry
152
+ fit), and their runtimes are server-independent **domain modules**
153
+ (`mcp-app-runtime.ts`, `diagram-presets.ts`, `web-artifacts.ts`), not `server.ts`
154
+ — so the op handlers call them directly, no runner injection. To keep
155
+ `web-artifacts.ts` server-independent its one `emitPrimaryWorkbenchEvent` call
156
+ was switched to the already-injected `emitCanvasLayoutUpdate` (canvas-operations'
157
+ `setCanvasLayoutUpdateEmitter`, wired by `server.ts`). The three ops are
158
+ `mutates:false`: `mcpapp.open` / `diagram.open` emit `ext-app-open` +
159
+ `ext-app-result` via `ctx.emit` (byte-identical to the legacy events, fired
160
+ once); `webartifact.build`'s node creation emits its own `canvas-layout-update`
161
+ from inside `web-artifacts.ts`. The new `canvas_app` composite folds the three
162
+ behind an `open-mcp-app` / `diagram` / `build-artifact` action enum; each action
163
+ dispatches to the same op, so behavior is byte-identical (proven by
164
+ `tests/unit/mcp-composites.test.ts`). Wire shapes and MCP result shapes are
165
+ unchanged. The public MCP surface grows from 82 to 83 tools (deliberate; see
166
+ `tests/unit/mcp-tool-freeze.test.ts`).
167
+
168
+ ### Changed
169
+
170
+ - **Board validation and annotation removal migrated to the operation registry
171
+ (plan-008 Wave 1).** `canvas_validate` (board validation, a pure read) and
172
+ `canvas_remove_annotation` (DELETE-by-id) are now defined once in
173
+ `src/server/operations/ops/validate.ts` and
174
+ `src/server/operations/ops/annotation.ts`, shared by HTTP, MCP, and the CLI
175
+ (legacy hand-written handlers + routes + MCP tool blocks + the orphaned
176
+ `CanvasAccess.validate` / `CanvasAccess.removeAnnotation` methods deleted; the
177
+ SDK `PmxCanvas.validate()` / `removeAnnotation()` stay public). Tool names,
178
+ HTTP routes (`GET /api/canvas/validate`, `DELETE /api/canvas/annotation/:id`),
179
+ the 404 denial body, MCP result shapes, and the `canvas-layout-update` SSE
180
+ frame are unchanged. Two new composite actions land additively:
181
+ `canvas_query` gains `validate` (→ `validate.get`) and `canvas_view` gains
182
+ `remove-annotation` (→ `annotation.remove`).
183
+
184
+ - **Batch migrated to the `canvas.batch` registry meta-op (plan-008 Wave 2 —
185
+ the last and highest-risk registry slice).** `canvas_batch` now dispatches
186
+ each entry through the shared `executeOperation` path (defined once in
187
+ `src/server/operations/ops/batch.ts`) instead of a hand-written ~290-line
188
+ switch in `canvas-operations.ts`, which is deleted along with its
189
+ `resolveBatchRefs` / `serializeCreatedNode` helpers, the
190
+ `handleCanvasBatch` HTTP handler + route, and the standalone `canvas_batch`
191
+ MCP tool block. `executeOperation` gained an internal, depth-counted
192
+ emit-suppression (`runWithSuppressedEmits`) so the meta-op runs every entry
193
+ with per-entry SSE frames suppressed and then emits exactly ONE final
194
+ `canvas-layout-update` — preserving the single-final-frame behavior. The
195
+ `canvas_batch` tool name, the `{ operations:[...] }` and bare-`[...]` body
196
+ shapes, per-entry result shapes, and the `{ ok, results, refs, failedIndex?,
197
+ error? }` envelope are unchanged; per-entry ops record their own undo/redo
198
+ history as before. The SDK `PmxCanvas.runBatch` stays public and delegates to
199
+ the meta-op.
200
+
201
+ - **Deleting a node no longer silently re-anchors AX work (plan-007 Slice A).**
202
+ Node deletion already re-normalized canvas-bound AX state (work items / approval
203
+ gates / elicitations / mode requests keep the item but lose the dangling node
204
+ ref; node-anchored review annotations are dropped) — but did so silently. It now
205
+ records exactly one auditable timeline event (`kind:'note'`, `source:'system'`,
206
+ `data.systemEvent:'ax-node-orphan'`) naming what was re-anchored/removed, so the
207
+ human and a resuming agent can see the change. The note is append-only (not
208
+ rolled back on undo, not duplicated on redo). See `docs/ax-state-contract.md`.
209
+
210
+ - **AX state ops migrated to the operation registry (plan-007 Slice B, wave 1).**
211
+ `canvas_get_ax`, `canvas_set_ax_focus`, `canvas_set_ax_policy`, and
212
+ `canvas_report_host_capability` are now defined once in
213
+ `src/server/operations/ops/ax-state.ts` and shared by HTTP, MCP, and the SDK
214
+ (legacy hand-written handlers + tools + CanvasAccess methods deleted). Tool
215
+ names, MCP result shapes, SSE frames (`ax-state-changed`), and `source`
216
+ defaulting are unchanged. One deliberate HTTP expansion: `GET /api/canvas/ax`
217
+ now returns `{ ok, state, host, context }` (previously `{ ok, state }`) — the
218
+ registry serves one wire body that the remote-MCP invoker also consumes, so the
219
+ `canvas_get_ax` aggregate is preserved across local and remote MCP. Additive;
220
+ existing clients that read `state` are unaffected.
221
+
222
+ - **AX work / review / gate ops migrated to the operation registry (plan-007
223
+ Slice B, wave 2).** `canvas_add_work_item`, `canvas_update_work_item`,
224
+ `canvas_add_review_annotation`, `canvas_request_approval`,
225
+ `canvas_resolve_approval`, `canvas_request_elicitation`,
226
+ `canvas_respond_elicitation`, `canvas_request_mode`, `canvas_resolve_mode`
227
+ (plus the HTTP-only review-annotation update) are now defined once in
228
+ `src/server/operations/ops/ax-work.ts` (legacy handlers + tools + CanvasAccess
229
+ methods deleted). Tool names, success wire shapes, denial bodies (and their
230
+ 404/400 status codes), SSE frames, and `source` defaulting are unchanged.
231
+ Cross-surface unification (same class as plan-005 slices 1–4): the
232
+ `update`/`resolve`/`respond` MCP tools previously returned a success-shaped
233
+ `{ ok:false, <item>:null }` on a missing/already-resolved target; they now
234
+ surface the HTTP 404 as an MCP `isError` result with the same message —
235
+ success shapes are unchanged.
236
+
237
+ - **AX timeline / delivery / command ops migrated to the operation registry
238
+ (plan-007 Slice B, wave 3).** `canvas_record_ax_event`, `canvas_add_evidence`,
239
+ `canvas_send_steering`, `canvas_get_ax_timeline`, `canvas_claim_ax_delivery`,
240
+ `canvas_mark_ax_delivery`, and `canvas_invoke_command` are now defined once in
241
+ `src/server/operations/ops/ax-timeline.ts` (legacy handlers + tools +
242
+ CanvasAccess methods deleted). Tool names, success wire shapes (`{ ok, event }`,
243
+ `{ ok, evidence }`, `{ ok, steering }`, the timeline aggregate, `{ ok, delivered }`),
244
+ the timeline SSE frame (`ax-event-created`, distinct from the wave-1/2
245
+ `ax-state-changed`), the `delivery.mark` `{ steeringDelivered }` frame (emitted
246
+ only when a message is actually marked), the loop-safe consumer scoping of
247
+ `canvas_claim_ax_delivery` (steering + `pendingActivity` both filtered), the
248
+ command allowlist (unknown name → 400 `Unknown command "…"`), and `source`
249
+ defaulting are unchanged. Two deliberate unifications: (1) `GET
250
+ /api/canvas/ax/delivery/pending` now returns `{ ok, pending, pendingActivity }`
251
+ (previously `{ ok, pending }`) so the one wire body matches the
252
+ `canvas_claim_ax_delivery` aggregate across local and remote MCP — additive,
253
+ existing clients reading `pending` are unaffected; (2) `canvas_invoke_command`
254
+ on an unknown name previously returned a success-shaped `{ ok:false, event:null }`
255
+ over MCP and now surfaces the HTTP 400 as an MCP `isError` (same class as the
256
+ wave-2 resolve/respond unification). The legacy `GET /api/canvas/ax/command`
257
+ registry list, `canvas_ingest_activity`, and `canvas_ax_interaction` remain on
258
+ the standalone surface (deferred to later waves).
259
+
260
+ - **AX long-poll gate reads migrated to the operation registry (plan-007 Slice
261
+ B, wave 4).** `canvas_await_approval`, `canvas_await_elicitation`, and
262
+ `canvas_await_mode` (the report primitive D "gates that actually gate" reads,
263
+ HTTP `GET /api/canvas/ax/{approval,elicitation,mode}/:id`) are now defined once
264
+ in `src/server/operations/ops/ax-await.ts` (legacy MCP tools, HTTP single-read
265
+ handlers, and the orphaned CanvasAccess `await*` methods deleted; the public
266
+ SDK `PmxCanvas.await*` methods stay). The async handler performs the wait via
267
+ `waitForAxResolution` (`status !== 'pending'` resolution predicate for all
268
+ three, matching the legacy HTTP + SDK). `timeoutMs` is normalized to one field
269
+ the handler reads: HTTP parses `?waitMs` (absent/non-positive ⇒ 0 = immediate
270
+ read), MCP defaults an omitted `timeoutMs` to 30000; both clamp to
271
+ [0, 120000]. Tool names, the `{ <gate>, pending }` success shapes, and the
272
+ HTTP 404 status on a missing gate are unchanged. Two documented behavior
273
+ changes, both the plan's accepted long-poll tradeoff: (1) the HTTP handler no
274
+ longer aborts the wait on client disconnect — the registry handler has no
275
+ access to the `Request`, so the wait runs to its (≤120s, subscription-based,
276
+ cheap) timeout instead of honoring `req.signal`; resolution detection,
277
+ timeout, and the `{ value, pending }` result are otherwise identical. (2) The
278
+ HTTP missing-gate body changed from `{ ok:false, error:"<gate> not found." }`
279
+ to `{ ok:false, <gate>:null, pending:false }` (status stays 404) so one wire
280
+ body serves both surfaces; the in-process (Local) MCP path still round-trips
281
+ the success-shaped `{ <gate>:null }` JSON exactly as before. (Over a remote
282
+ MCP transport — untested for these reads — an await-on-missing now surfaces as
283
+ an `isError` rather than the legacy `{ <gate>:null }`, since the generic
284
+ HTTP invoker throws on 404.)
285
+
286
+ - **Removing a missing node now errors on every surface (plan-005 slice 1).**
287
+ Node CRUD + layout reads (`node.add` / `node.get` / `node.update` /
288
+ `node.remove` / `layout.get`) are defined once in a new operation registry
289
+ (`src/server/operations/`) shared by HTTP, MCP, CLI, and the SDK. As part of
290
+ the unification, `canvas_remove_node` over local (in-process) MCP access and
291
+ `PmxCanvas.removeNode` no longer silently succeed on an unknown node id —
292
+ they now report the same `Node "<id>" not found.` error the HTTP DELETE
293
+ already returned (404). The SDK's `updateNode` also gained the HTTP superset
294
+ patch semantics (webpage `titleSource`/`url`, html top-level `html` /
295
+ `axCapabilities`, group `children`), erasing long-standing cross-surface
296
+ drift. Wire shapes and MCP tool names are unchanged.
297
+
298
+ - **Edges, arrange/focus/fit/clear, and groups migrated to the operation
299
+ registry (plan-005 slice 2).** Same unification class as slice 1:
300
+ `canvas_remove_edge` over local MCP access now errors on a missing edge
301
+ (remote already did); `canvas_create_group` over local access now rejects
302
+ missing child ids (HTTP already did); `canvas_focus_node` on a missing node
303
+ now returns a proper MCP error result; `canvas_ungroup` failure text is now
304
+ the HTTP message `Group not found or empty.`. Group and focus SSE frames now
305
+ carry the standard sessionId/timestamp envelope like every other mutation.
306
+ HTTP paths, wire shapes, and MCP tool names are unchanged.
307
+
308
+ - **Pins, search, history, undo/redo, and snapshots migrated to the operation
309
+ registry (plan-005 slice 3).** Same unification class as earlier slices:
310
+ pin updates are now computed server-side authoritatively; undo/redo over
311
+ local MCP now use the HTTP emit set; restore/delete/diff on a missing
312
+ snapshot now return the same error on every surface; percent-encoded
313
+ snapshot ids now work on DELETE. POST `/api/canvas/context-pins` honors an
314
+ optional `mode` field (`set`/`add`/`remove`, default `set`). Plain-text
315
+ legacy snapshot error bodies became the standard `{ ok:false, error }`
316
+ envelope. HTTP paths, wire shapes, and MCP tool names are unchanged.
317
+
318
+ - **json-render, graph, streaming, schema, and spec-validation migrated to the
319
+ operation registry (plan-005 slice 4 / migration item 6).** `jsonrender.add`,
320
+ `jsonrender.stream`, `graph.add`, `schema.describe`, and `spec.validate` are
321
+ now defined once and shared by HTTP, MCP, CLI, and the SDK. The frame-height
322
+ alias triangle (`heightPx` from the SDK, `nodeHeight` over HTTP/MCP, `height`
323
+ for chart content, plus `size.height`) is absorbed into one schema with a
324
+ legacy-exact resolution order (`nodeHeight ?? heightPx ?? size.height`). The
325
+ CLI `node add --type json-render`/`--type graph` and `graph add` commands now
326
+ route through the registry invoker instead of hand-written `fetch` paths, and
327
+ the SDK's `streamJsonRenderNode` shares the create-or-append core with the op
328
+ handler. `spec.validate`'s graph branch now honors the full graph payload
329
+ surface (previously the HTTP handler silently dropped fields the MCP tool and
330
+ graph-create route honored). The spec-validation error body keeps its exact
331
+ `{ ok, error, type }` shape. HTTP paths, wire shapes, and MCP tool names are
332
+ unchanged.
333
+
334
+ - **Smaller consolidation behavior changes (surfaced by the v0.2.0 release review,
335
+ documented here for completeness).** Beyond the slice entries above, the unification
336
+ also changed a few smaller behaviors: (1) `PmxCanvas.addNode({ type:"html" })` and
337
+ `canvas_add_node { type:"html" }` *without* an explicit size now default to **720×640**
338
+ (was 360×200 on those two surfaces — their size ladder lacked an html case; this
339
+ converges to the documented html default and the dedicated `canvas_add_html_node`).
340
+ (2) The HTTP `POST /api/canvas/mcp-app/open` and `/api/canvas/diagram` success bodies no
341
+ longer echo `serverName` / `toolName` (both are caller-supplied inputs and remain on the
342
+ `ext-app-open` / `ext-app-result` SSE frames; the MCP results were never affected).
343
+ (3) `webview start`'s `dataStoreDir` is now workspace-sandboxed on the HTTP surface too
344
+ (out-of-workspace → 400), matching the MCP tool. (4) The batch unsupported-operation
345
+ error text is now `Unsupported canvas_batch operation "<op>".`. None of these change a
346
+ frozen tool name, input schema, or documented success response shape.
347
+
348
+ ### Deprecated
349
+
350
+ - **`canvas_validate` and `canvas_remove_annotation` superseded by composite
351
+ actions (plan-008 Wave 1).** Both tools now carry a `Deprecated: use canvas_x
352
+ with action "y".` prefix on their tool description (derived automatically from
353
+ the composite definitions): `canvas_validate` → `canvas_query` action
354
+ `validate`; `canvas_remove_annotation` → `canvas_view` action
355
+ `remove-annotation`. They keep working through v0.2 and are removed in v0.3 per
356
+ `docs/api-stability.md`.
357
+
358
+ - **The 5 webview MCP tools superseded by the `canvas_webview` composite
359
+ (plan-008 Wave 3).** `canvas_webview_status`, `canvas_webview_start`,
360
+ `canvas_webview_stop`, `canvas_resize`, and `canvas_evaluate` now carry a
361
+ `Deprecated: use canvas_webview with action "y".` prefix on their tool
362
+ description (derived automatically from the composite definition):
363
+ `canvas_webview_status` → action `status`; `canvas_webview_start` → `start`;
364
+ `canvas_webview_stop` → `stop`; `canvas_resize` → `resize`; `canvas_evaluate`
365
+ → `evaluate`. They keep working through v0.2 (now registry-served) and are
366
+ removed in v0.3 per `docs/api-stability.md`. `canvas_screenshot` is NOT
367
+ deprecated — it stays standalone (binary payload).
368
+
369
+ - **The 3 external / built-content MCP tools superseded by the `canvas_app`
370
+ composite (plan-008 Wave 4).** `canvas_open_mcp_app`, `canvas_add_diagram`, and
371
+ `canvas_build_web_artifact` now carry a `Deprecated: use canvas_app with action
372
+ "y".` prefix on their tool description (derived automatically from the composite
373
+ definition): `canvas_open_mcp_app` → action `open-mcp-app`; `canvas_add_diagram`
374
+ → `diagram`; `canvas_build_web_artifact` → `build-artifact`. They keep working
375
+ through v0.2 (now registry-served) and are removed in v0.3 per
376
+ `docs/api-stability.md`.
377
+
378
+ - **The 3 HTML/webpage MCP tools superseded by `canvas_node` (plan-008 Wave 5 — the
379
+ final fold).** `canvas_add_html_node` → `canvas_node` with `action: "add"` and
380
+ `type: "html"`; `canvas_add_html_primitive` → `canvas_node` with `action: "add"`,
381
+ `type: "html"`, `primitive: "<kind>"` (and `data`); `canvas_refresh_webpage_node`
382
+ → `canvas_node` with `action: "update"` and `refresh: true`. All three now carry a
383
+ `Deprecated: use canvas_node …` prefix on their tool description. **No new action,
384
+ composite mechanism, op, or SDK change was needed** — the earlier plan-008 verdict
385
+ that a fold required a per-action input-injection mechanism was wrong:
386
+ `node.add` already routes `type:"html"` + `primitive|kind` → `createHtmlPrimitiveNode`
387
+ and merges the top-level html fields (`summary`/`agentSummary`/`description`/
388
+ `presentation`/`slideTitles`/`embeddedNodeIds`/`embeddedUrls`) into the node data,
389
+ and `node.update` already routes `refresh:true` → `refreshCanvasWebpageNode`. Two
390
+ small correctness fixes made the fold sound: (a) `axCapabilities` is now **advertised**
391
+ in `node.add`'s schema (it previously only passed through `z.looseObject`, so
392
+ schema-guided agents migrating off `canvas_add_html_node` would silently drop the
393
+ AX-bridge config); (b) `node.update`'s `formatResult` now surfaces a FAILED webpage
394
+ refresh as `isError` + `{ ok:false, error }` (it previously masked it as a false
395
+ `{ ok:true }` over the MCP-default local invoker — a live bug), matching the HTTP
396
+ 400 and the legacy tool. Parity (incl. the refresh failure path) is proven by
397
+ `tests/unit/mcp-composites.test.ts`. All three keep working through v0.2 and are
398
+ removed in v0.3 per `docs/api-stability.md`.
399
+
400
+ - **Single-purpose MCP tools superseded by wave-1 composites.** The standalone
401
+ tools folded by the composites above (`canvas_add_node`, `canvas_get_node`,
402
+ `canvas_update_node`, `canvas_remove_node`, `canvas_add_edge`,
403
+ `canvas_remove_edge`, `canvas_arrange`, `canvas_focus_node`, `canvas_fit_view`,
404
+ `canvas_clear`, `canvas_create_group`, `canvas_group_nodes`, `canvas_ungroup`,
405
+ `canvas_undo`, `canvas_redo`, `canvas_search`, `canvas_get_layout`,
406
+ `canvas_add_json_render_node`, `canvas_stream_json_render_node`,
407
+ `canvas_add_graph_node`, `canvas_describe_schema`, `canvas_validate_spec`) now
408
+ carry a `Deprecated: use canvas_x with action "y".` prefix on their tool
409
+ description (derived automatically from the composite definitions). They keep
410
+ working through v0.2 and are removed in v0.3 per `docs/api-stability.md`.
411
+
412
+ - **Legacy AX MCP tools superseded by the AX composites (wave 2).** The 22
413
+ standalone AX tools folded by the composites above now carry a `Deprecated: use
414
+ canvas_x …` prefix on their tool description (derived automatically from the
415
+ composite definitions): `canvas_get_ax`, `canvas_set_ax_focus`,
416
+ `canvas_set_ax_policy`, `canvas_report_host_capability` → `canvas_ax_state`;
417
+ `canvas_add_work_item`, `canvas_update_work_item`, `canvas_add_review_annotation`
418
+ → `canvas_ax_work`; `canvas_request_approval`, `canvas_resolve_approval`,
419
+ `canvas_await_approval`, `canvas_request_elicitation`,
420
+ `canvas_respond_elicitation`, `canvas_await_elicitation`, `canvas_request_mode`,
421
+ `canvas_resolve_mode`, `canvas_await_mode` → `canvas_ax_gate` (with kind/action);
422
+ `canvas_get_ax_timeline`, `canvas_record_ax_event`, `canvas_add_evidence`,
423
+ `canvas_send_steering` → `canvas_ax_timeline`; `canvas_claim_ax_delivery`,
424
+ `canvas_mark_ax_delivery` → `canvas_ax_delivery`. They keep working through v0.2
425
+ and are removed in v0.3 per `docs/api-stability.md`.
426
+
427
+ ### Fixed
428
+
429
+ - **Webview `evaluate` / `resize` runtime failures return the legacy `400` JSON, not a
430
+ `500` HTML overlay (v0.2.0 release-review fix).** During the registry migration these
431
+ ops let the automation runner's plain `Error` (e.g. "no active session") escape
432
+ `dispatchOperationRoute` (which maps only `OperationError`); with no `Bun.serve`
433
+ request-level boundary, Bun rendered its dev error overlay — HTTP `500 text/html`
434
+ disclosing the absolute server source path. The handlers now convert runner failures to
435
+ the legacy `400 { ok:false, error, webview }` contract, the dispatcher returns a clean
436
+ JSON `500` for any other unexpected throw, and a `Bun.serve` `error()` boundary
437
+ guarantees no route can ever render the overlay. Covered by a new no-session regression
438
+ test.
439
+ - **In-place mcp-app / diagram update preserves the node's existing title (v0.2.0
440
+ release-review fix).** Re-opening an existing `mcp-app` node in place *without* a
441
+ `title` no longer resets the node title to the external tool's name — the
442
+ `ext-app-open` title fallback restores the legacy `targetNode.data.title` step.
443
+
444
+ - **json-render and graph viewer iframes crashed at mount (blank iframes).**
445
+ Bun 1.3.14 ignores the `NODE_ENV` define for JSX dev/prod selection when a
446
+ tsconfig is in scope, so the viewer bundle shipped the dev JSX transform
447
+ (`jsxDEV`) against production React, which exports it as `undefined`. Every
448
+ json-render/graph iframe failed with `TypeError: t is not a function`.
449
+ `scripts/build-json-render.sh` now passes `--production` to `bun build`,
450
+ forcing the production JSX transform. Caught by the restored Playwright e2e
451
+ gate (8 tests had been failing silently on main).
452
+
8
453
  ## [0.1.36] - 2026-06-09
9
454
 
10
455
  ### Added
@@ -1826,6 +2271,8 @@ otherwise have to discover by trial and error.
1826
2271
  - Regression coverage for snapshot flat-`id` aliases on both MCP and
1827
2272
  HTTP surfaces, plus async / top-level-`await` WebView script bodies.
1828
2273
 
2274
+ [0.2.1]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.2.1
2275
+ [0.2.0]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.2.0
1829
2276
  [0.1.36]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.36
1830
2277
  [0.1.35]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.35
1831
2278
  [0.1.34]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.34
package/Readme.md CHANGED
@@ -105,7 +105,7 @@ the DB so SQLite WAL data is checkpointed into the file.
105
105
 
106
106
  ### 06 / Any agent
107
107
 
108
- Harness-agnostic. Drive the canvas from [MCP](docs/mcp.md) (69 tools,
108
+ Harness-agnostic. Drive the canvas from [MCP](docs/mcp.md) (83 tools,
109
109
  14 resources, change notifications), the [CLI](docs/cli.md), the
110
110
  [HTTP API](docs/http-api.md), or the [Bun SDK](docs/sdk.md). Works with
111
111
  Claude Code, GitHub Copilot CLI, Codex, Cursor, Windsurf, or any agent
@@ -255,7 +255,7 @@ the agent can read `canvas://skills` and pull in companion skills
255
255
  the three-tier visual matrix (json-render → html → web-artifact)
256
256
  - **[CLI reference](docs/cli.md)** — full command surface, daemon mode,
257
257
  watch streams, WebView automation
258
- - **[MCP reference](docs/mcp.md)** — 69 tools, 14 resources, change
258
+ - **[MCP reference](docs/mcp.md)** — 83 tools, 14 resources, change
259
259
  notifications, node-type routing
260
260
  - **[HTTP API](docs/http-api.md)** — REST endpoints, SSE, batch operations
261
261
  - **[AX host-adapter contract](docs/ax-host-adapter-contract.md)** — how native