pmx-canvas 0.1.35 → 0.2.0

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