pmx-canvas 0.1.28 → 0.1.30

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 (67) hide show
  1. package/CHANGELOG.md +193 -0
  2. package/Readme.md +20 -10
  3. package/dist/canvas/global.css +13 -0
  4. package/dist/canvas/index.js +80 -163
  5. package/dist/canvas/surface-theme.css +142 -0
  6. package/dist/json-render/index.js +103 -103
  7. package/dist/types/client/nodes/HtmlNode.d.ts +0 -7
  8. package/dist/types/client/nodes/ax-node-actions.d.ts +18 -0
  9. package/dist/types/client/nodes/surface-url.d.ts +22 -0
  10. package/dist/types/client/state/attention-bridge.d.ts +3 -0
  11. package/dist/types/client/state/intent-bridge.d.ts +17 -0
  12. package/dist/types/json-render/renderer/index.d.ts +2 -0
  13. package/dist/types/json-render/schema.d.ts +2 -0
  14. package/dist/types/json-render/server.d.ts +2 -0
  15. package/dist/types/mcp/canvas-access.d.ts +47 -0
  16. package/dist/types/server/ax-interaction.d.ts +210 -0
  17. package/dist/types/server/ax-state.d.ts +67 -1
  18. package/dist/types/server/canvas-db.d.ts +4 -0
  19. package/dist/types/server/canvas-serialization.d.ts +2 -0
  20. package/dist/types/server/canvas-state.d.ts +47 -2
  21. package/dist/types/server/html-surface.d.ts +40 -0
  22. package/dist/types/server/index.d.ts +56 -2
  23. package/dist/types/server/mutation-history.d.ts +1 -1
  24. package/dist/types/server/placement.d.ts +1 -1
  25. package/dist/types/shared/surface.d.ts +19 -0
  26. package/docs/cli.md +30 -0
  27. package/docs/http-api.md +55 -0
  28. package/docs/mcp.md +40 -2
  29. package/docs/node-types.md +26 -0
  30. package/docs/plans/plan-004-pmx-ax-primitives.md +623 -394
  31. package/docs/sdk.md +20 -0
  32. package/package.json +2 -2
  33. package/skills/pmx-canvas/SKILL.md +107 -9
  34. package/src/cli/agent.ts +190 -0
  35. package/src/client/canvas/CanvasNode.tsx +8 -4
  36. package/src/client/canvas/ExpandedNodeOverlay.tsx +12 -0
  37. package/src/client/nodes/ContextNode.tsx +17 -0
  38. package/src/client/nodes/ExtAppFrame.tsx +40 -3
  39. package/src/client/nodes/FileNode.tsx +26 -0
  40. package/src/client/nodes/HtmlNode.tsx +60 -188
  41. package/src/client/nodes/LedgerNode.tsx +39 -5
  42. package/src/client/nodes/McpAppNode.tsx +47 -2
  43. package/src/client/nodes/StatusNode.tsx +20 -0
  44. package/src/client/nodes/ax-node-actions.ts +39 -0
  45. package/src/client/nodes/surface-url.ts +48 -0
  46. package/src/client/state/attention-bridge.ts +5 -0
  47. package/src/client/state/intent-bridge.ts +33 -0
  48. package/src/client/theme/global.css +13 -0
  49. package/src/client/theme/surface-theme.css +142 -0
  50. package/src/json-render/renderer/index.tsx +31 -0
  51. package/src/json-render/schema.ts +4 -0
  52. package/src/json-render/server.ts +31 -1
  53. package/src/mcp/canvas-access.ts +212 -1
  54. package/src/mcp/server.ts +238 -5
  55. package/src/server/ax-context.ts +3 -0
  56. package/src/server/ax-interaction.ts +549 -0
  57. package/src/server/ax-state.ts +188 -2
  58. package/src/server/canvas-db.ts +20 -0
  59. package/src/server/canvas-operations.ts +11 -0
  60. package/src/server/canvas-serialization.ts +9 -0
  61. package/src/server/canvas-state.ts +177 -16
  62. package/src/server/html-surface.ts +170 -0
  63. package/src/server/index.ts +105 -1
  64. package/src/server/mutation-history.ts +5 -0
  65. package/src/server/placement.ts +5 -1
  66. package/src/server/server.ts +305 -0
  67. package/src/shared/surface.ts +38 -0
package/CHANGELOG.md CHANGED
@@ -3,6 +3,197 @@
3
3
  All notable changes to `pmx-canvas` are documented here. This project follows
4
4
  [Semantic Versioning](https://semver.org/).
5
5
 
6
+ ## [0.1.30] - 2026-06-07
7
+
8
+ ### Added
9
+
10
+ - **PMX-AX node interactions (plan-004 Phase 1).** A host-agnostic, capability-gated
11
+ interaction layer over the existing AX primitives. Eligible nodes emit one
12
+ normalized `PmxAxInteraction` envelope (`{ type, sourceNodeId, payload, ... }`)
13
+ that is validated against a per-node-type capability registry (with optional
14
+ per-node `data.axCapabilities` opt-in/narrowing, clamped to a server ceiling)
15
+ and mapped onto the matching AX operation — work item, evidence, approval,
16
+ review, focus, steering, or event. New `POST /api/canvas/ax/interaction`
17
+ endpoint plus full SDK (`PmxCanvas.submitAxInteraction`), CanvasAccess, MCP
18
+ (`canvas_ax_interaction`), and CLI (`pmx-canvas ax interaction`) coverage.
19
+ Interactions are sandbox/transport-agnostic (the same envelope will back native
20
+ nodes, json-render actions, the HTML bridge, MCP apps, and host adapters in
21
+ later phases); `html`/`html-primitive`, `mcp-app`, and internal `prompt`/`response`
22
+ nodes are disabled by default. Accepted and rejected interactions emit SSE
23
+ (`ax-interaction`) plus the underlying AX state event. PMX core still imports no
24
+ host SDK (guarded by `ax-parity.test.ts`).
25
+
26
+ - **PMX-AX sandboxed HTML bridge (plan-004 Phase 3).** Opted-in `html` /
27
+ `html-primitive` nodes can emit AX interactions from inside the sandbox via
28
+ `window.PMX_AX.emit(type, payload)`. The server injects the bridge into the
29
+ surface document only when the node's AX capabilities are enabled; the bridge
30
+ posts a nonce-tagged message to the parent canvas, which validates the nonce +
31
+ node id and submits through the capability-gated endpoint (the server
32
+ re-validates, so the bridge is convenience, not a trust boundary). The iframe
33
+ sandbox stays `allow-scripts` only. (json-render action→AX mapping remains a
34
+ documented follow-up — the viewer needs a parent channel, same shape as this
35
+ bridge.)
36
+
37
+ - **PMX-AX delivery semantics (plan-004 Phase 4).** Steering messages can be
38
+ claimed and acknowledged by adapterless consumers. New
39
+ `GET /api/canvas/ax/delivery/pending?consumer=` (loop-safe — excludes steering
40
+ the consumer itself originated) and `POST /api/canvas/ax/delivery/:id/mark`,
41
+ plus SDK/CanvasAccess (`getPendingSteering`, `markSteeringDelivered`), MCP tools
42
+ (`canvas_claim_ax_delivery`, `canvas_mark_ax_delivery`), MCP resources
43
+ (`canvas://ax-pending-steering`, `canvas://ax-delivery`), an MCP prompt
44
+ template (`pmx-current-context`) so MCP-aware clients can inject PMX context
45
+ without a host adapter, and CLI (`pmx-canvas ax delivery list|mark`).
46
+
47
+ - **PMX-AX elicitation + mode-request primitives (plan-004 Phase 5).** Two new
48
+ canvas-bound, snapshotted AX primitives: `elicitation` (request structured
49
+ human input → respond) and `mode-request` (request a plan/execute/autonomous
50
+ transition → resolve). Full HTTP / SDK / CanvasAccess / MCP
51
+ (`canvas_request_elicitation`, `canvas_respond_elicitation`,
52
+ `canvas_request_mode`, `canvas_resolve_mode`) / CLI coverage, and both are
53
+ executable via the interaction envelope (`ax.elicitation.request`,
54
+ `ax.mode.request`). Command registry and tool/prompt policy primitives are
55
+ intentionally deferred pending the plan's open product questions (which
56
+ commands are first-class; how much prompt/system-message mutation PMX should
57
+ allow by default); the capability registry already reserves `mcp-app` (disabled
58
+ by default), so the MCP-app interaction bridge (Phase 6) is gated and
59
+ forward-compatible until that trust boundary is designed.
60
+
61
+ - **PMX-AX follow-ups — command registry, tool/prompt policy, json-render +
62
+ MCP-app bridges.** The four documented deferrals now ship. (1) A **command
63
+ registry** (`pmx.plan`, `pmx.execute`, `pmx.promote-context`, `pmx.summarize`,
64
+ `pmx.review`) with a registry-gated `invokeCommand` (records an `agent-event`
65
+ of kind `command`); unknown names are rejected (`400`). Executable via the
66
+ envelope (`ax.command.invoke`) and exposed over HTTP
67
+ (`GET|POST /api/canvas/ax/command`), SDK, CanvasAccess, MCP
68
+ (`canvas_invoke_command`), and CLI (`pmx-canvas ax command list|invoke`).
69
+ (2) A canvas-bound, snapshotted **tool/prompt policy** singleton
70
+ (`tools.allowed|excluded|approvalRequired`, `prompt.systemAppend|mode`) read
71
+ into `canvas://ax-context`; set via `GET|POST /api/canvas/ax/policy`,
72
+ `canvas_set_ax_policy`, and `pmx-canvas ax policy get|set` (patches merge and
73
+ are normalized server-side). (3) The **json-render viewer → AX channel**: a
74
+ spec action named after an AX type (e.g. `on.press → { action:
75
+ "ax.work.create" }`) is forwarded by the viewer bundle to the parent canvas,
76
+ which validates (iframe source + per-viewer nonce + node id) and submits
77
+ through the capability-gated endpoint (`sourceSurface: 'json-render'`). The
78
+ bridge nonce/node-id globals are injected into the viewer HTML only when the
79
+ embedding node requests them. (4) The **MCP-app interaction bridge (Phase 6)**:
80
+ opted-in ext-app `mcp-app` nodes get `window.PMX_AX.emit(...)` injected into
81
+ the app HTML (same nonce-tagged shape as the HTML bridge,
82
+ `sourceSurface: 'mcp-app'`), disabled by default and node-scoped. All emit
83
+ surfaces remain convenience-only — the server re-validates every interaction
84
+ against the node's effective capabilities, so it stays the single trust
85
+ boundary.
86
+
87
+ - **PMX-AX native node controls (plan-004 Phase 2).** Inline AX controls on
88
+ native nodes that submit interactions through the browser: status nodes get a
89
+ "Track as work" button (→ `ax.work.create`), file nodes a "mark as evidence"
90
+ control (→ `ax.evidence.add`), and context nodes a "Set focus" button
91
+ (→ `ax.focus.set`). A client helper (`submitAxInteractionFromClient`) posts to
92
+ the interaction endpoint and surfaces the outcome as a transient toast.
93
+ (json-render action → AX mapping is deferred to the bridge-transport work: the
94
+ json-render viewer consumes actions internally and needs a viewer→parent
95
+ channel, the same shape as the HTML and MCP-app bridges.)
96
+
97
+ - **Open as site — standalone node surfaces.** Every renderable surface node can
98
+ now be opened full-page in its own browser tab via an ↗ "Open as site" button
99
+ (node title bar and expanded overlay), covering `html` / `html-primitive`,
100
+ bundled `web-artifact`, `json-render` / `graph`, `webpage`, and hosted ext-app
101
+ `mcp-app` nodes. A new stable route, `GET /api/canvas/surface/:nodeId`, serves
102
+ (or redirects to) the surface, and the in-canvas `html` iframe now loads that
103
+ **same URL** — one render path, no separate "preview" document, and the URL
104
+ reflects current node state and survives a refresh. Node payloads expose the
105
+ URL as `surfaceUrl` (`canvas_get_node` / `canvas_get_layout`) so agents can
106
+ point a human at "the artifact" without disturbing the canvas. Served HTML
107
+ keeps its opaque-origin posture (`Content-Security-Policy: sandbox`), so author
108
+ code opened top-level cannot reach the canvas origin. The html surface theme is
109
+ served from a new same-origin stylesheet (`/canvas/surface-theme.css`) and
110
+ live-switches via the existing theme bridge. ext-app surfaces render their UI
111
+ but, opened standalone, cannot run interactive tool-calls (no host bridge in a
112
+ bare tab); `webpage` / URL-backed `mcp-app` nodes redirect to their site.
113
+
114
+ ### Fixed
115
+
116
+ - **json-render specs preserve `on` event bindings through validation.** The
117
+ json-render element schema dropped the `on` field during validation, so
118
+ spec-authored action bindings (`on.press`, `on.change`, …) were silently
119
+ stripped before reaching the viewer and never fired. `on` is now retained,
120
+ which both makes general json-render interactivity work and is what lets the
121
+ json-render → AX channel above dispatch its `ax.*` actions. `on` is optional —
122
+ normalization defaults it to an empty bindings object, so specs whose elements
123
+ have no event bindings still validate.
124
+
125
+ - **Canvas iframes are promoted to their own compositing layer**
126
+ (`transform: translateZ(0)` on `.html-node-frame` / `.mcp-app-frame`) to
127
+ mitigate an intermittent blank-iframe glitch — an HTML/app node iframe in
128
+ the zoom/pan-transformed canvas (near the chrome's heavy backdrop-filter
129
+ blur and behind group frames) could occasionally paint blank until a
130
+ resize/zoom forced a repaint. Investigation ruled out a content/load issue
131
+ (the frame document served valid HTML) and a DOM-reorder/reload on grouping
132
+ (reproduced grouping single and multiple HTML nodes with the iframe keeping
133
+ its src and content); the remaining cause is a browser compositor paint
134
+ invalidation, which the dedicated GPU layer addresses.
135
+
136
+ ### Changed
137
+
138
+ - **Group frames are roomier.** Auto-fit group frames now leave more margin
139
+ around their children (`GROUP_PAD` 40 → 56) so the group header and its
140
+ node-count badge stay visible instead of sitting under the top-left child.
141
+ - **Skill guidance: open the canvas first, always.** The bundled
142
+ `pmx-canvas` skill now tells agents to open/focus the workbench themselves
143
+ before mutating nodes on **every** host (Codex in-app Browser, Copilot
144
+ panel, or any plain browser) rather than assuming the host opened it — some
145
+ hosts don't. Added layout guidance too: space nodes generously (more when
146
+ edges connect them and inside groups, so the edge flow is visible) and size
147
+ group frames larger than their children.
148
+
149
+ ### Fixed
150
+
151
+ - **Group operations no longer auto-pack or repack children.** Grouping
152
+ existing nodes (`canvas_group_nodes` / `/api/canvas/group/add`) without
153
+ an explicit `childLayout` now preserves their positions (matching
154
+ `canvas_create_group` and the batch `group.add`), and moving or resizing
155
+ a grouped child re-fits the group frame **without** repacking siblings or
156
+ discarding the child's requested coordinates. Compaction is opt-in via an
157
+ explicit layout. (0.1.29 report Bug #32.)
158
+ - **HTTP/CLI node-create responses now include the `nodeId` alias.** The
159
+ 0.1.29 `id`/`nodeId` alias only reached the MCP responses; the
160
+ `/api/canvas/node` (and graph/json-render) responses used by the CLI now
161
+ expose both keys too. (0.1.29 report Bug #31.)
162
+ - **`canvas_batch` now supports `node.remove`.** The operation is
163
+ implemented (and listed in the tool description) instead of being
164
+ rejected as unsupported. (0.1.29 report Bug #33.)
165
+
166
+ ## [0.1.29] - 2026-06-06
167
+
168
+ ### Changed
169
+
170
+ - **Bullet graph nodes accept the conventional `actual` measure key** in
171
+ addition to `value`, so `data: [{ label, actual, target }]` renders
172
+ without an explicit `valueKey` instead of failing the data-key check.
173
+ - **Node-create MCP responses expose both `id` and a `nodeId` alias.**
174
+ All node-create tools (including SpecStream) now return both keys,
175
+ matching the existing external-app / web-artifact responses, so agents
176
+ using either key (or a cached schema) work.
177
+ - **Friendlier AX CLI errors for bare subcommands.** Running `pmx-canvas
178
+ ax event` (or `evidence`/`host`/`work`/`approval`/`review`) without the
179
+ action now suggests the full command (e.g. `ax event add`) or lists the
180
+ available actions instead of a generic unknown-command error.
181
+
182
+ ### Fixed
183
+
184
+ - **`pmx-canvas --mcp` no longer crashes with `EADDRINUSE`** when a daemon
185
+ already holds the target port for a *different* workspace. The MCP/SDK
186
+ auto-start now falls back to a free port (a same-workspace daemon is
187
+ still attached to as before) and prints a stderr note explaining how to
188
+ share one canvas (`PMX_CANVAS_URL` / `PMX_CANVAS_PORT` or run the daemon
189
+ from this workspace). An explicit SDK `start()` port is still honored
190
+ exactly unless `allowPortFallback` is passed.
191
+ - **Ledger nodes render body `content` as a log** — one line per entry,
192
+ no stray "Content" field label, and a literal `\n` (as the shell
193
+ passes through `--content "a\nb"`) is treated as a line break instead
194
+ of being shown verbatim. Structured key/value fields still render as
195
+ rows, now with a gap so the label never runs into the value.
196
+
6
197
  ## [0.1.28] - 2026-06-06
7
198
 
8
199
  ### Changed
@@ -1384,6 +1575,8 @@ otherwise have to discover by trial and error.
1384
1575
  - Regression coverage for snapshot flat-`id` aliases on both MCP and
1385
1576
  HTTP surfaces, plus async / top-level-`await` WebView script bodies.
1386
1577
 
1578
+ [0.1.30]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.30
1579
+ [0.1.29]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.29
1387
1580
  [0.1.28]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.28
1388
1581
  [0.1.27]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.27
1389
1582
  [0.1.26]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.26
package/Readme.md CHANGED
@@ -37,7 +37,9 @@ neighborhoods) and uses your layout to ground its next action.
37
37
 
38
38
  Files, web pages, screenshots, structured panels, charts, hand-drawn
39
39
  diagrams, embedded MCP Apps, and bundled web artifacts all live on the same
40
- surface. The reach of the canvas is the union of its
40
+ surface. Any rich surface an HTML node or a web artifact — can be **opened as
41
+ a site**, full-page in its own browser tab with one click; the canvas and the
42
+ tab render the same document. The reach of the canvas is the union of its
41
43
  [built-in node types](docs/node-types.md) and **whatever your agent's harness
42
44
  already has access to** — MCP servers, CLIs, file reads, web fetch, anything
43
45
  on its toolbelt.
@@ -65,14 +67,22 @@ canvas into a shared workspace between you and the agent:
65
67
  - **Steering messages & agent-event timeline** — send instructions to the
66
68
  active session, and read a normalized, bounded timeline of prompts, tool
67
69
  runs, evidence (logs/diffs/screenshots/test-output), and failures.
70
+ - **Node interactions** — eligible nodes emit one capability-gated, validated
71
+ interaction envelope from native controls, the sandboxed HTML / MCP-app
72
+ bridges (`window.PMX_AX.emit`), or json-render spec actions. The server is the
73
+ single trust boundary and clamps sandboxed surfaces to their own node.
74
+ - **Elicitations, mode requests, commands & policy** — request structured human
75
+ input, propose a plan/execute/autonomous mode transition, invoke registry
76
+ commands (`pmx.plan`, `pmx.review`, …), and read a tool/prompt policy.
68
77
  - **Host capability** — adapters report what the host can do, for diagnostics.
69
78
 
70
- Canvas-bound state (focus, work items, approvals, review annotations) rides
71
- canvas snapshots and restore; the timeline persists for continuity but is
72
- retention-bounded and never restored by a snapshot. Every primitive is reachable
73
- from MCP, the HTTP API, the SDK, and `pmx-canvas ax …`. The core never depends
74
- on any host SDK, so adapters (e.g. the GitHub Copilot app) map onto the same
75
- neutral surfaces without making PMX Canvas vendor-specific.
79
+ Canvas-bound state (focus, work items, approvals, review annotations,
80
+ elicitations, mode requests, policy) rides canvas snapshots and restore; the
81
+ timeline persists for continuity but is retention-bounded and never restored by a
82
+ snapshot. Every primitive is reachable from MCP, the HTTP API, the SDK, and
83
+ `pmx-canvas ax …`. The core never depends on any host SDK, so adapters (e.g. the
84
+ GitHub Copilot app) map onto the same neutral surfaces without making PMX Canvas
85
+ vendor-specific.
76
86
 
77
87
  ### 05 / Save
78
88
 
@@ -85,8 +95,8 @@ the DB so SQLite WAL data is checkpointed into the file.
85
95
 
86
96
  ### 06 / Any agent
87
97
 
88
- Harness-agnostic. Drive the canvas from [MCP](docs/mcp.md) (56 tools,
89
- 12 resources, change notifications), the [CLI](docs/cli.md), the
98
+ Harness-agnostic. Drive the canvas from [MCP](docs/mcp.md) (65 tools,
99
+ 14 resources, change notifications), the [CLI](docs/cli.md), the
90
100
  [HTTP API](docs/http-api.md), or the [Bun SDK](docs/sdk.md). Works with
91
101
  Claude Code, GitHub Copilot CLI, Codex, Cursor, Windsurf, or any agent
92
102
  that can spawn an MCP stdio server, call a CLI, or hit an HTTP endpoint.
@@ -235,7 +245,7 @@ the agent can read `canvas://skills` and pull in companion skills
235
245
  the three-tier visual matrix (json-render → html → web-artifact)
236
246
  - **[CLI reference](docs/cli.md)** — full command surface, daemon mode,
237
247
  watch streams, WebView automation
238
- - **[MCP reference](docs/mcp.md)** — 56 tools, 12 resources, change
248
+ - **[MCP reference](docs/mcp.md)** — 65 tools, 14 resources, change
239
249
  notifications, node-type routing
240
250
  - **[HTTP API](docs/http-api.md)** — REST endpoints, SSE, batch operations
241
251
  - **[Bun SDK](docs/sdk.md)** — `createCanvas()` for TypeScript on Bun
@@ -1473,6 +1473,19 @@ html.is-node-resizing .ext-app-preview-catcher {
1473
1473
  border-radius: 0 0 calc(var(--radius) - 1px) calc(var(--radius) - 1px);
1474
1474
  }
1475
1475
 
1476
+ /* Promote canvas iframes to their own GPU compositing layer. An iframe embedded
1477
+ in the zoom/pan-transformed canvas — especially near the heavy backdrop-filter
1478
+ blur used across the chrome and behind group frames — can intermittently paint
1479
+ blank until a resize/zoom forces a repaint (reported for grouped HTML nodes;
1480
+ the same class of compositor glitch as the earlier Excalidraw flicker). A no-op
1481
+ 3D transform forces a stable layer so the iframe keeps painting through
1482
+ surrounding layout/stacking changes. */
1483
+ .mcp-app-frame,
1484
+ .html-node-frame {
1485
+ transform: translateZ(0);
1486
+ backface-visibility: hidden;
1487
+ }
1488
+
1476
1489
  /* ── Prompt nodes ──────────────────────────────────────────── */
1477
1490
  .canvas-node:has(.prompt-node-inner) {
1478
1491
  border-color: var(--c-accent-30);