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.
- package/CHANGELOG.md +461 -0
- package/Readme.md +14 -2
- package/dist/canvas/index.js +82 -41
- package/dist/json-render/index.js +89 -334
- package/dist/types/client/nodes/ExtAppFrame.d.ts +2 -0
- package/dist/types/mcp/canvas-access.d.ts +12 -159
- package/dist/types/server/ax-context.d.ts +1 -1
- package/dist/types/server/ax-state-manager.d.ts +256 -0
- package/dist/types/server/ax-state.d.ts +29 -1
- package/dist/types/server/ax-wait.d.ts +23 -0
- package/dist/types/server/canvas-operations.d.ts +1 -12
- package/dist/types/server/canvas-state.d.ts +46 -14
- package/dist/types/server/html-surface.d.ts +7 -0
- package/dist/types/server/index.d.ts +66 -26
- package/dist/types/server/operations/composites.d.ts +121 -0
- package/dist/types/server/operations/http.d.ts +7 -0
- package/dist/types/server/operations/index.d.ts +8 -0
- package/dist/types/server/operations/invoker.d.ts +13 -0
- package/dist/types/server/operations/mcp.d.ts +15 -0
- package/dist/types/server/operations/ops/annotation.d.ts +2 -0
- package/dist/types/server/operations/ops/app.d.ts +33 -0
- package/dist/types/server/operations/ops/ax-await.d.ts +2 -0
- package/dist/types/server/operations/ops/ax-shared.d.ts +31 -0
- package/dist/types/server/operations/ops/ax-state.d.ts +2 -0
- package/dist/types/server/operations/ops/ax-timeline.d.ts +2 -0
- package/dist/types/server/operations/ops/ax-work.d.ts +2 -0
- package/dist/types/server/operations/ops/batch.d.ts +19 -0
- package/dist/types/server/operations/ops/edges.d.ts +2 -0
- package/dist/types/server/operations/ops/groups.d.ts +2 -0
- package/dist/types/server/operations/ops/json-render.d.ts +31 -0
- package/dist/types/server/operations/ops/nodes.d.ts +62 -0
- package/dist/types/server/operations/ops/query.d.ts +2 -0
- package/dist/types/server/operations/ops/snapshots.d.ts +2 -0
- package/dist/types/server/operations/ops/validate.d.ts +2 -0
- package/dist/types/server/operations/ops/viewport.d.ts +2 -0
- package/dist/types/server/operations/ops/webview.d.ts +2 -0
- package/dist/types/server/operations/registry.d.ts +15 -0
- package/dist/types/server/operations/types.d.ts +116 -0
- package/dist/types/server/operations/webview-runner.d.ts +69 -0
- package/docs/RELEASE.md +5 -0
- package/docs/adr-001-bun-only-runtime.md +46 -0
- package/docs/api-stability.md +57 -0
- package/docs/ax-host-adapter-contract.md +65 -0
- package/docs/ax-state-contract.md +72 -0
- package/docs/http-api.md +34 -2
- package/docs/mcp.md +64 -11
- package/docs/plans/plan-005-operation-registry.md +84 -0
- package/docs/plans/plan-006-mcp-tool-consolidation.md +109 -0
- package/docs/plans/plan-007-ax-domain.md +99 -0
- package/docs/plans/plan-008-registry-finish.md +91 -0
- package/docs/screenshot.png +0 -0
- package/docs/tech-debt-assessment-2026-06.md +90 -0
- package/package.json +3 -3
- package/skills/pmx-canvas/SKILL.md +233 -185
- package/skills/pmx-canvas/evals/evals.json +3 -3
- package/skills/pmx-canvas/references/codex-app-adapter.md +24 -11
- package/skills/pmx-canvas/references/github-copilot-app-adapter.md +31 -1
- package/src/cli/agent.ts +52 -31
- package/src/client/nodes/ExtAppFrame.tsx +73 -5
- package/src/client/nodes/HtmlNode.tsx +12 -3
- package/src/client/nodes/McpAppNode.tsx +12 -3
- package/src/json-render/renderer/index.tsx +3 -0
- package/src/mcp/canvas-access.ts +43 -774
- package/src/mcp/server.ts +190 -2001
- package/src/server/ax-context.ts +7 -1
- package/src/server/ax-state-manager.ts +808 -0
- package/src/server/ax-state.ts +89 -2
- package/src/server/ax-wait.ts +56 -0
- package/src/server/canvas-operations.ts +2 -328
- package/src/server/canvas-schema.ts +2 -2
- package/src/server/canvas-state.ts +140 -382
- package/src/server/html-surface.ts +49 -11
- package/src/server/index.ts +136 -192
- package/src/server/operations/composites.ts +355 -0
- package/src/server/operations/http.ts +103 -0
- package/src/server/operations/index.ts +65 -0
- package/src/server/operations/invoker.ts +87 -0
- package/src/server/operations/mcp.ts +221 -0
- package/src/server/operations/ops/annotation.ts +60 -0
- package/src/server/operations/ops/app.ts +447 -0
- package/src/server/operations/ops/ax-await.ts +216 -0
- package/src/server/operations/ops/ax-shared.ts +38 -0
- package/src/server/operations/ops/ax-state.ts +249 -0
- package/src/server/operations/ops/ax-timeline.ts +381 -0
- package/src/server/operations/ops/ax-work.ts +635 -0
- package/src/server/operations/ops/batch.ts +365 -0
- package/src/server/operations/ops/edges.ts +166 -0
- package/src/server/operations/ops/groups.ts +176 -0
- package/src/server/operations/ops/json-render.ts +691 -0
- package/src/server/operations/ops/nodes.ts +1047 -0
- package/src/server/operations/ops/query.ts +281 -0
- package/src/server/operations/ops/snapshots.ts +366 -0
- package/src/server/operations/ops/validate.ts +37 -0
- package/src/server/operations/ops/viewport.ts +219 -0
- package/src/server/operations/ops/webview.ts +339 -0
- package/src/server/operations/registry.ts +79 -0
- package/src/server/operations/types.ts +150 -0
- package/src/server/operations/webview-runner.ts +77 -0
- package/src/server/server.ts +253 -2170
- package/src/server/web-artifacts.ts +6 -2
|
@@ -276,7 +276,7 @@ The CLI targets `http://localhost:4313` by default. Override with `PMX_CANVAS_UR
|
|
|
276
276
|
| `graph` | Native chart panel | Line, bar, pie, area, scatter, radar, stacked-bar, composed, plus Tufte primitives (sparkline, dot-plot, bullet, slopegraph) rendered inside the canvas |
|
|
277
277
|
| `html` | Sandboxed HTML+JS document | Self-contained HTML with optional inline `<script>` and CDN imports rendered in a sandbox-restricted iframe; canvas theme tokens are auto-injected |
|
|
278
278
|
| `group` | Spatial container/frame | Visually group related nodes together |
|
|
279
|
-
| `prompt` | Prompt thread root | Canvas-native prompt entry points for agent conversations. **Internal type — surfaces in `canvas://layout` for thread rendering but is not created via the public `
|
|
279
|
+
| `prompt` | Prompt thread root | Canvas-native prompt entry points for agent conversations. **Internal type — surfaces in `canvas://layout` for thread rendering but is not created via the public `canvas_node { action: "add" }` API. Don't try to add one directly.** |
|
|
280
280
|
| `response` | Prompt reply / streamed answer | Agent responses linked to prompt threads. **Same internal-only restriction as `prompt`.** |
|
|
281
281
|
|
|
282
282
|
### Edge Types
|
|
@@ -328,56 +328,100 @@ Use color consistently to convey meaning:
|
|
|
328
328
|
|
|
329
329
|
## MCP Tools Reference
|
|
330
330
|
|
|
331
|
+
PMX Canvas exposes **~21 composable tools**: 12 action-discriminated **composites** (the
|
|
332
|
+
recommended surface) plus a set of first-class standalones. The composites fold the older
|
|
333
|
+
single-purpose tools behind an `action` (and, for `canvas_ax_gate`, a `kind`) discriminator —
|
|
334
|
+
**field names are unchanged**; only the tool name + the `action`/`kind` selector differ.
|
|
335
|
+
|
|
336
|
+
> **Legacy single-purpose tools are Deprecated.** The old names (`canvas_add_node`,
|
|
337
|
+
> `canvas_update_node`, `canvas_request_approval`, `canvas_add_work_item`, …) still work but are
|
|
338
|
+
> marked `Deprecated:` and are **removed in v0.3**. Prefer the composites. The authoritative
|
|
339
|
+
> legacy→composite mapping table lives in [`docs/mcp.md`](../../docs/mcp.md) — this skill does not
|
|
340
|
+
> re-enumerate the deprecated names.
|
|
341
|
+
|
|
342
|
+
### The 12 composites
|
|
343
|
+
|
|
344
|
+
| Composite | `action` values | What it does |
|
|
345
|
+
|-----------|-----------------|--------------|
|
|
346
|
+
| `canvas_node` | `add` · `get` · `update` · `remove` | Create / read / mutate / delete a node |
|
|
347
|
+
| `canvas_render` | `describe-schema` · `validate` · `add-json-render` · `stream-json-render` · `add-graph` | Schema introspection, dry-run validation, and native json-render / graph node creation |
|
|
348
|
+
| `canvas_edge` | `add` · `remove` | Connect / disconnect nodes |
|
|
349
|
+
| `canvas_group` | `create` · `add` · `ungroup` | Manage spatial group containers |
|
|
350
|
+
| `canvas_history` | `undo` · `redo` | Time travel through the mutation ring buffer |
|
|
351
|
+
| `canvas_view` | `arrange` · `focus` · `fit` · `clear` | Auto-arrange, pan-to-node, fit viewport, clear the board |
|
|
352
|
+
| `canvas_query` | `search` · `layout` | Find nodes by keyword, or read full canvas state |
|
|
353
|
+
| `canvas_ax_state` | `get` · `set-focus` · `set-policy` · `report-capability` | Read AX state; set AX focus; patch tool/prompt policy; report host capability |
|
|
354
|
+
| `canvas_ax_work` | `add` · `update` · `annotate` | Canvas-bound work items + review annotations |
|
|
355
|
+
| `canvas_ax_gate` | `request` · `resolve` · `await` × `kind` `approval` \| `elicitation` \| `mode` | The human-decision gate machine (request → await → resolve) |
|
|
356
|
+
| `canvas_ax_timeline` | `read` · `record-event` · `add-evidence` · `send-steering` | The bounded AX diagnostics timeline |
|
|
357
|
+
| `canvas_ax_delivery` | `claim` · `mark` | Adapterless steering delivery (claim → act → mark) |
|
|
358
|
+
|
|
359
|
+
Call shape examples: `canvas_node { action: "add", type, title }`,
|
|
360
|
+
`canvas_view { action: "focus", id }`, `canvas_group { action: "create", childIds }`,
|
|
361
|
+
`canvas_render { action: "add-graph", graphType, data }`,
|
|
362
|
+
`canvas_query { action: "search", query }`,
|
|
363
|
+
`canvas_ax_work { action: "update", id, status }`.
|
|
364
|
+
|
|
365
|
+
`canvas_ax_gate` takes **two** discriminators, `{ kind, action }` — e.g.
|
|
366
|
+
`{ kind: "approval", action: "request", title }`,
|
|
367
|
+
`{ kind: "elicitation", action: "resolve", id, response }`,
|
|
368
|
+
`{ kind: "mode", action: "await", id, timeoutMs }`. (The approval machine-readable action
|
|
369
|
+
identifier is passed as `approvalAction`, since `action` is the lifecycle discriminator.)
|
|
370
|
+
|
|
371
|
+
### Standalones (first-class — not deprecated)
|
|
372
|
+
|
|
373
|
+
These stay separate by design (trust-boundary, firehose, execution-intent, or not-yet-consolidated
|
|
374
|
+
surfaces): `canvas_batch`, `canvas_pin_nodes`, `canvas_screenshot`, `canvas_build_web_artifact`,
|
|
375
|
+
`canvas_open_mcp_app`, `canvas_add_diagram`, `canvas_add_html_node`, `canvas_add_html_primitive`,
|
|
376
|
+
`canvas_refresh_webpage_node`, `canvas_remove_annotation`, `canvas_ax_interaction`,
|
|
377
|
+
`canvas_ingest_activity`, `canvas_invoke_command`, the WebView tools
|
|
378
|
+
(`canvas_webview_start` / `canvas_webview_status` / `canvas_webview_stop`, `canvas_resize`,
|
|
379
|
+
`canvas_evaluate`), and the snapshot tools (`canvas_snapshot`, `canvas_list_snapshots`,
|
|
380
|
+
`canvas_restore`, `canvas_delete_snapshot`, `canvas_gc_snapshots`, `canvas_diff` — a
|
|
381
|
+
`canvas_snapshot` composite is deferred to v0.3).
|
|
382
|
+
|
|
331
383
|
### Node Operations
|
|
332
384
|
|
|
333
|
-
MCP node-type routing:
|
|
385
|
+
MCP node-type routing — which tool creates which node category:
|
|
334
386
|
|
|
335
|
-
| Node category | MCP creation
|
|
387
|
+
| Node category | MCP creation call |
|
|
336
388
|
|---------------|-------------------|
|
|
337
|
-
| Basic nodes (`markdown`, `status`, `context`, `ledger`, `trace`, `file`, `image`, `webpage`) | `
|
|
338
|
-
| `json-render` | `
|
|
339
|
-
| `graph` | `
|
|
340
|
-
| `html-primitive` | `canvas_add_html_primitive` |
|
|
341
|
-
| `html` | `canvas_add_html_node` |
|
|
342
|
-
| `web-artifact` | `canvas_build_web_artifact` |
|
|
343
|
-
| `external-app` / tool-backed `mcp-app` | `canvas_open_mcp_app` |
|
|
344
|
-
| `group` | `
|
|
345
|
-
|
|
346
|
-
If a node type is rejected by `
|
|
347
|
-
`mcp.nodeTypeRouting`; do not keep
|
|
348
|
-
|
|
349
|
-
|
|
389
|
+
| Basic nodes (`markdown`, `status`, `context`, `ledger`, `trace`, `file`, `image`, `webpage`) | `canvas_node { action: "add" }` |
|
|
390
|
+
| `json-render` | `canvas_render { action: "add-json-render" }` |
|
|
391
|
+
| `graph` | `canvas_render { action: "add-graph" }` |
|
|
392
|
+
| `html-primitive` | `canvas_add_html_primitive` (standalone) |
|
|
393
|
+
| `html` | `canvas_add_html_node` (standalone) |
|
|
394
|
+
| `web-artifact` | `canvas_build_web_artifact` (standalone) |
|
|
395
|
+
| `external-app` / tool-backed `mcp-app` | `canvas_open_mcp_app` (standalone) |
|
|
396
|
+
| `group` | `canvas_group { action: "create" }` |
|
|
397
|
+
|
|
398
|
+
If a node type is rejected by `canvas_node { action: "add" }`, call
|
|
399
|
+
`canvas_render { action: "describe-schema" }` and read `mcp.nodeTypeRouting`; do not keep
|
|
400
|
+
retrying the generic add.
|
|
401
|
+
|
|
402
|
+
**`canvas_node { action: "add", … }`** — Add a node to the canvas
|
|
350
403
|
- `type` (required): basic node type only; structured/app/group nodes use the routing table above
|
|
351
404
|
- `title`: short, scannable title
|
|
352
|
-
- `content`: for most types
|
|
353
|
-
|
|
354
|
-
For `image` type, pass a file path, URL, or data URI.
|
|
405
|
+
- `content`: markdown text for most types. For `file`, pass the **file path** (e.g. `"src/auth/login.ts"`) —
|
|
406
|
+
the server auto-loads + watches it. For `image`, pass a file path, URL, or data URI.
|
|
355
407
|
- `path`: compatibility alias for image paths only; prefer `content` for new image calls
|
|
356
|
-
- `x`, `y`: position (
|
|
357
|
-
- `width`, `height`: dimensions (sensible defaults provided)
|
|
358
|
-
- `color`: semantic color
|
|
359
|
-
- `metadata`: arbitrary JSON
|
|
408
|
+
- `x`, `y`: position (prefer omitting for auto-layout); `width`, `height`: dimensions (sensible defaults); `color`: semantic color; `metadata`: arbitrary JSON
|
|
360
409
|
- Returns: `{ id: "<node-id>" }` — capture this ID for edges and groups
|
|
361
410
|
|
|
362
|
-
**`
|
|
363
|
-
|
|
364
|
-
-
|
|
365
|
-
- For `json-render`, pass `spec` to update the rendered spec in place
|
|
366
|
-
- For `graph`, pass graph fields
|
|
367
|
-
- Use to update status nodes as work progresses
|
|
411
|
+
**`canvas_node { action: "update", id, … }`** — Update an existing node in place (preferred over
|
|
412
|
+
delete+recreate; preserves edges, pins, position)
|
|
413
|
+
- `id` (required), plus any of: `title`, `content`, `x`, `y`, `width`, `height`, `collapsed`, `arrangeLocked`, `data`
|
|
414
|
+
- For `json-render`, pass `spec` to update the rendered spec in place
|
|
415
|
+
- For `graph`, pass graph fields (`graphType`, `data`, `xKey`, `yKey`, `color`, `chartHeight`) to rebuild the chart; `height`/`nodeHeight` set frame geometry, `chartHeight` the chart content
|
|
368
416
|
|
|
369
|
-
**`
|
|
370
|
-
- `id` (required): node to remove
|
|
371
|
-
- Clean up nodes that are no longer relevant
|
|
417
|
+
**`canvas_node { action: "remove", id }`** — Remove a node and all its connected edges. Clean up nodes that are no longer relevant.
|
|
372
418
|
|
|
373
|
-
**`
|
|
374
|
-
- `id` (required): annotation to remove
|
|
375
|
-
- Use when context gives you the annotation ID; use WebView first if you need to identify a mark by shape or location.
|
|
419
|
+
**`canvas_node { action: "get", id }`** — Get a single node's full data by `id`.
|
|
376
420
|
|
|
377
|
-
**`
|
|
378
|
-
|
|
421
|
+
**`canvas_remove_annotation`** (standalone) — Remove a human-drawn annotation by `id`. Use when
|
|
422
|
+
context gives you the annotation ID; use WebView first if you need to identify a mark by shape or location.
|
|
379
423
|
|
|
380
|
-
**`canvas_refresh_webpage_node`** — Re-fetch the URL stored on a `webpage` node
|
|
424
|
+
**`canvas_refresh_webpage_node`** (standalone) — Re-fetch the URL stored on a `webpage` node
|
|
381
425
|
- `id` (required): webpage node to refresh
|
|
382
426
|
- Optional `url`: replace the stored URL before refreshing (use when the human moved the page)
|
|
383
427
|
- Returns the refreshed node with updated `pageTitle` and cached extracted text
|
|
@@ -386,7 +430,7 @@ If a node type is rejected by `canvas_add_node`, call `canvas_describe_schema` a
|
|
|
386
430
|
|
|
387
431
|
```typescript
|
|
388
432
|
// Add the page once
|
|
389
|
-
|
|
433
|
+
canvas_node({ action: 'add', type: 'webpage', url: 'https://example.com/docs' })
|
|
390
434
|
// → returns { id: 'node-abc' }
|
|
391
435
|
|
|
392
436
|
// …later, after the human reopens the canvas…
|
|
@@ -394,7 +438,7 @@ If a node type is rejected by `canvas_add_node`, call `canvas_describe_schema` a
|
|
|
394
438
|
// → re-fetches the URL, updates pageTitle + extracted text, keeps the node ID and position
|
|
395
439
|
```
|
|
396
440
|
|
|
397
|
-
**`
|
|
441
|
+
**`canvas_render { action: "add-json-render", … }`** — Add a native json-render node
|
|
398
442
|
- Required: `spec`; `title` is optional and inferred from the root element when omitted
|
|
399
443
|
- Prefer a complete json-render object with `root`, `elements`, and optional `state`
|
|
400
444
|
- Legacy bare component specs like `{ type: "Badge", props: {...} }` are accepted and wrapped into a one-element document for compatibility
|
|
@@ -403,18 +447,16 @@ If a node type is rejected by `canvas_add_node`, call `canvas_describe_schema` a
|
|
|
403
447
|
`outline`. Legacy `props.label` and status variants (`success`, `info`, `warning`, `error`,
|
|
404
448
|
`danger`) are normalized for saved-spec compatibility.
|
|
405
449
|
|
|
406
|
-
**`
|
|
407
|
-
- Omit `nodeId` on the first call to create a
|
|
408
|
-
|
|
409
|
-
- `patches` are SpecStream JSON-Patch ops applied server-side (the canvas accumulates the spec)
|
|
410
|
-
`{ "op": "add", "path": "/elements/card", "value": { "
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
**`canvas_add_graph_node`** — Add a native graph/chart node
|
|
450
|
+
**`canvas_render { action: "stream-json-render", … }`** — Build a json-render node progressively (live)
|
|
451
|
+
- Omit `nodeId` on the first call to create a streaming node (returns its `id`); reuse that `nodeId`
|
|
452
|
+
on later calls to append `patches`; set `done: true` on the final call.
|
|
453
|
+
- `patches` are SpecStream JSON-Patch ops applied server-side (the canvas accumulates the spec), e.g.
|
|
454
|
+
`{ "op": "add", "path": "/elements/card", "value": { … } }`, `{ "op": "replace", "path": "/root", "value": "card" }`.
|
|
455
|
+
- Build incrementally: set `/root`, add container elements, then append child element ids/elements.
|
|
456
|
+
Each call re-renders; partial specs render what they can. Use for dashboards/reports that fill in
|
|
457
|
+
as you generate them rather than appearing all at once.
|
|
458
|
+
|
|
459
|
+
**`canvas_render { action: "add-graph", … }`** — Add a native graph/chart node
|
|
418
460
|
- Required: `graphType`, `data`
|
|
419
461
|
- Supports `line`, `bar`, `pie`, `area`, `scatter`, `radar`, `stacked-bar`, `composed`,
|
|
420
462
|
and the Tufte primitives `sparkline`, `dot-plot`, `bullet`, `slopegraph` (aliases accepted)
|
|
@@ -444,7 +486,7 @@ the `tufte-viz` skill (`skills/tufte-viz/SKILL.md`). Key rules:
|
|
|
444
486
|
- For more than ~4 overlapping series, build small multiples (several small graph nodes on a shared
|
|
445
487
|
scale, arranged in a grid/group) instead of one multi-color chart.
|
|
446
488
|
|
|
447
|
-
**`canvas_build_web_artifact`** — Build and optionally open a bundled web artifact
|
|
489
|
+
**`canvas_build_web_artifact`** (standalone) — Build and optionally open a bundled web artifact
|
|
448
490
|
- Required: `title`, `appTsx` (source string contents, not a file path)
|
|
449
491
|
- CLI `--app-file` reads a file before calling the same build path; MCP callers must pass the source contents
|
|
450
492
|
- Cold builds commonly take 45-60 seconds; use a long client timeout such as 300000 ms or more
|
|
@@ -454,88 +496,60 @@ ID extraction for mixed tool responses:
|
|
|
454
496
|
- Most add-style tools return a flat `id`; web artifacts return `id` plus `nodeId`; snapshots return `id` plus nested `snapshot.id`.
|
|
455
497
|
- Defensive extractor: `const getId = (r) => r.id ?? r.nodeId ?? r.snapshot?.id;`
|
|
456
498
|
|
|
457
|
-
**`canvas_open_mcp_app`** — Open a tool-backed external MCP app node
|
|
499
|
+
**`canvas_open_mcp_app`** (standalone) — Open a tool-backed external MCP app node
|
|
458
500
|
- Required: `toolName`, `transport`
|
|
459
501
|
- `transport` is either `{ type: "stdio", command, args?, cwd?, env? }` or `{ type: "http", url, headers? }`
|
|
460
502
|
- This is lower-level than `pmx-canvas external-app add --kind excalidraw`; use `canvas_add_diagram` for the built-in Excalidraw preset
|
|
461
503
|
|
|
462
|
-
**`canvas_pin_nodes`** — Set, add, or remove pinned context nodes
|
|
463
|
-
- Use `{ nodeIds: [...] }`; the field is `nodeIds`, not `ids`
|
|
504
|
+
**`canvas_pin_nodes`** (standalone) — Set, add, or remove pinned context nodes. Use `{ nodeIds: [...] }` — the field is `nodeIds`, not `ids`.
|
|
464
505
|
|
|
465
|
-
**`canvas_diff`** — Compare current canvas state with a saved snapshot
|
|
466
|
-
- Requires `{ snapshot: "<snapshot-id-or-name>" }`; there is no implicit previous-snapshot default
|
|
506
|
+
**`canvas_diff`** (standalone) — Compare current canvas state with a saved snapshot. Requires `{ snapshot: "<snapshot-id-or-name>" }`; there is no implicit previous-snapshot default.
|
|
467
507
|
|
|
468
|
-
**`
|
|
469
|
-
|
|
470
|
-
|
|
508
|
+
**`canvas_render { action: "describe-schema" }`** — Inspect the running server's create schemas and
|
|
509
|
+
canonical examples. Use before generating structured payloads when you need the authoritative current
|
|
510
|
+
shape; read `mcp.nodeTypeRouting` to choose the right creation call for each node category.
|
|
471
511
|
|
|
472
|
-
**`
|
|
473
|
-
|
|
474
|
-
- Use this when you want a dry run before creating a `json-render` or `graph` node
|
|
512
|
+
**`canvas_render { action: "validate", … }`** — Dry-run a json-render spec or graph payload without
|
|
513
|
+
creating a node. Returns the normalized json-render spec the server would accept.
|
|
475
514
|
|
|
476
|
-
**`
|
|
477
|
-
|
|
478
|
-
|
|
515
|
+
**`canvas_view { action: "fit", … }`** — Fit viewport to all nodes or selected nodes; optional
|
|
516
|
+
`width`, `height`, `padding`, `maxScale`, `nodeIds`. Use before screenshot/whole-board review so the
|
|
517
|
+
server viewport matches the intended camera.
|
|
479
518
|
|
|
480
|
-
**Batch graph creation**
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
- It accepts the same shape as `canvas_add_graph_node`: `graphType`, `data`, optional `title`,
|
|
484
|
-
`xKey`, `yKey`, `zKey`, `nameKey`, `valueKey`, `axisKey`, `metrics`, `series`, `barKey`,
|
|
485
|
-
`lineKey`, `aggregate`, `color`, `barColor`, `lineColor`, `height`, `x`, `y`, `width`, and
|
|
486
|
-
`nodeHeight`.
|
|
487
|
-
- In batch/MCP/HTTP payloads, `height` is chart content height and `nodeHeight` is the canvas frame height.
|
|
519
|
+
**Batch graph creation** — Use the `graph.add` op inside `canvas_batch` / `pmx-canvas batch` for a
|
|
520
|
+
graph node in a larger one-shot build. It takes the same shape as `canvas_render { action: "add-graph" }`.
|
|
521
|
+
In batch/MCP/HTTP payloads, `height` is chart content height and `nodeHeight` is the canvas frame height.
|
|
488
522
|
|
|
489
523
|
### Edge Operations
|
|
490
524
|
|
|
491
|
-
**`
|
|
525
|
+
**`canvas_edge { action: "add", … }`** — Connect two nodes
|
|
492
526
|
- `from`, `to` (required): source and target node IDs
|
|
493
527
|
- `fromSearch`, `toSearch`: optional search-based selectors when you do not have IDs. Each search
|
|
494
528
|
query must resolve to exactly one node or the edge creation fails with an ambiguity error.
|
|
495
529
|
- `type`: `flow`, `depends-on`, `relation`, or `references` (default: `relation`)
|
|
496
|
-
- `label`: descriptive relationship label
|
|
497
|
-
- `
|
|
498
|
-
- `animated`: boolean for visual emphasis
|
|
499
|
-
|
|
500
|
-
**`canvas_remove_edge`** — Remove a connection
|
|
501
|
-
- `id` (required): edge to remove
|
|
530
|
+
- `label`: descriptive relationship label; `style`: `solid`/`dashed`/`dotted`; `animated`: visual emphasis
|
|
531
|
+
- `canvas_edge { action: "remove", id }` removes a connection by edge `id`.
|
|
502
532
|
|
|
503
533
|
### Layout & Navigation
|
|
504
534
|
|
|
505
|
-
**`
|
|
506
|
-
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
-
|
|
511
|
-
|
|
512
|
-
- Call after adding multiple nodes
|
|
513
|
-
- For tiered/layered layouts (e.g., gateway → services → data stores), use `canvas_update_node`
|
|
514
|
-
with explicit `x`/`y` coordinates after auto-arrange to fine-tune the topology
|
|
515
|
-
|
|
516
|
-
**`canvas_focus_node`** — Pan viewport to center on a specific node
|
|
517
|
-
- `id` (required): node to focus
|
|
518
|
-
- Good for drawing the human's attention
|
|
519
|
-
- Avoid focusing every node in a batch. Focus only the final result or use CLI `focus --no-pan`
|
|
520
|
-
when the goal is selection/raising without camera movement.
|
|
535
|
+
- **`canvas_query { action: "layout" }`** — full canvas state (nodes, edges, viewport). Read before mutating.
|
|
536
|
+
- **`canvas_view { action: "arrange", layout }`** — auto-arrange all nodes; `layout` is `grid` (default,
|
|
537
|
+
dashboards/overviews), `column` (vertical lists), or `flow` (horizontal sequences / dependency chains).
|
|
538
|
+
Call once after a batch of adds. For tiered/layered layouts, fine-tune with explicit `x`/`y` via
|
|
539
|
+
`canvas_node { action: "update" }` after arranging.
|
|
540
|
+
- **`canvas_view { action: "focus", id }`** — pan the viewport to a node. Don't focus every node in a
|
|
541
|
+
batch — focus only the final result, or use CLI `focus --no-pan` to select/raise without moving the camera.
|
|
521
542
|
|
|
522
543
|
### Groups
|
|
523
544
|
|
|
524
|
-
**`
|
|
525
|
-
-
|
|
526
|
-
-
|
|
527
|
-
- `color`: group border/background color
|
|
528
|
-
- Auto-sizes to fit children
|
|
529
|
-
|
|
530
|
-
**`canvas_group_nodes`** — Add nodes to an existing group
|
|
531
|
-
- `groupId`, `childIds` (required)
|
|
532
|
-
|
|
533
|
-
**`canvas_ungroup`** — Release children from a group
|
|
534
|
-
- `groupId` (required): group to dissolve
|
|
545
|
+
- **`canvas_group { action: "create", … }`** — visual container; `title`, `childIds` (node IDs), `color`. Auto-sizes to fit children.
|
|
546
|
+
- **`canvas_group { action: "add", groupId, childIds }`** — add nodes to an existing group.
|
|
547
|
+
- **`canvas_group { action: "ungroup", groupId }`** — release all children from a group.
|
|
535
548
|
|
|
536
549
|
### Group Layout Guidance
|
|
537
550
|
|
|
538
|
-
Use groups as spacious semantic regions, not as tight containers.
|
|
551
|
+
Use groups as spacious semantic regions, not as tight containers. (Group calls below use
|
|
552
|
+
`canvas_group { action: "create" | "add" | "ungroup" }`.)
|
|
539
553
|
|
|
540
554
|
- Size the child nodes first, especially `graph`, `json-render`, `mcp-app`, image, and webpage
|
|
541
555
|
nodes whose rendered content may need more height than their visible title suggests.
|
|
@@ -548,7 +562,7 @@ Use groups as spacious semantic regions, not as tight containers.
|
|
|
548
562
|
tight groups make the canvas harder to read than no groups.
|
|
549
563
|
- Keep edges local to a group where possible. Long cross-board edges can look like they come from
|
|
550
564
|
nowhere; use a nearby bridge/context node or split the relationship into shorter labeled edges.
|
|
551
|
-
- After grouping, verify the result in `
|
|
565
|
+
- After grouping, verify the result in `canvas_query { action: "layout" }` or the browser: child nodes should be
|
|
552
566
|
fully inside the group with padding, visible nodes should not overlap, and group headers should
|
|
553
567
|
not cover content.
|
|
554
568
|
- If a group makes important content less visible, enlarge the group, split it into clearer
|
|
@@ -558,8 +572,8 @@ Use groups as spacious semantic regions, not as tight containers.
|
|
|
558
572
|
|
|
559
573
|
Use groups as named comparison areas, not just visual boxes.
|
|
560
574
|
|
|
561
|
-
- Create the comparison frame first with `
|
|
562
|
-
add charts, artifacts, and diagrams into that area deliberately.
|
|
575
|
+
- Create the comparison frame first with `canvas_group { action: "create" }` or
|
|
576
|
+
`pmx-canvas group create`, then add charts, artifacts, and diagrams into that area deliberately.
|
|
563
577
|
- Prefer graph nodes for fast capability demos and side-by-side comparisons. They are lightweight,
|
|
564
578
|
validate quickly, and are easier to regenerate.
|
|
565
579
|
- Prefer web artifacts when the board needs a richer narrative UI, custom interaction, or a more
|
|
@@ -588,42 +602,31 @@ Current product caveats for grouped comparison boards:
|
|
|
588
602
|
|
|
589
603
|
### Search & Discovery
|
|
590
604
|
|
|
591
|
-
**`
|
|
592
|
-
|
|
593
|
-
- Returns matches with relevance ranking and content snippets
|
|
594
|
-
- Use instead of parsing full layout when looking for specific nodes
|
|
605
|
+
**`canvas_query { action: "search", query }`** — Find nodes by title or content keywords. Returns
|
|
606
|
+
ranked matches with content snippets. Use instead of parsing the full layout to locate specific nodes.
|
|
595
607
|
|
|
596
608
|
### Context Pinning
|
|
597
609
|
|
|
598
|
-
**`canvas_pin_nodes`** — Manage pinned context
|
|
599
|
-
|
|
600
|
-
-
|
|
601
|
-
|
|
602
|
-
-
|
|
603
|
-
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
- Artifact and Excalidraw pins still matter as intent signals, but pair them with a markdown or
|
|
607
|
-
graph pin when you want the agent to understand what is inside the app, not just that it matters
|
|
610
|
+
**`canvas_pin_nodes`** (standalone) — Manage pinned context: `nodeIds` (required) plus `mode`
|
|
611
|
+
(`set` replaces all pins, `add`, `remove`).
|
|
612
|
+
- Pinned nodes are the primary human-to-agent communication channel — when a human pins in the
|
|
613
|
+
browser, they're saying "pay attention to these."
|
|
614
|
+
- Best default pin set: one intent-setting markdown node plus 1-3 concrete output nodes.
|
|
615
|
+
- Graph, file, and markdown pins carry richer usable context than `mcp-app` pins. Artifact and
|
|
616
|
+
Excalidraw pins still matter as intent signals, but pair them with a markdown or graph pin so the
|
|
617
|
+
agent understands what is inside the app, not just that it matters.
|
|
608
618
|
|
|
609
619
|
### History & Snapshots
|
|
610
620
|
|
|
611
|
-
**`
|
|
612
|
-
**`
|
|
613
|
-
**`
|
|
614
|
-
-
|
|
615
|
-
- Returns `{ ok, id, snapshot }`; the flat `id` is an alias for `snapshot.id`
|
|
616
|
-
**`canvas_restore`** — Restore canvas from a saved snapshot
|
|
617
|
-
- `id`: snapshot to restore
|
|
618
|
-
**`canvas_diff`** — Compare current canvas against a saved snapshot
|
|
619
|
-
- Shows added, removed, and modified nodes/edges
|
|
620
|
-
- Useful for reviewing what changed during a work session
|
|
621
|
+
- **`canvas_history { action: "undo" }`** / **`canvas_history { action: "redo" }`** — step the mutation ring buffer.
|
|
622
|
+
- **`canvas_snapshot`** (standalone) — save a named snapshot; `name` required. Returns `{ ok, id, snapshot }` (flat `id` aliases `snapshot.id`).
|
|
623
|
+
- **`canvas_restore`** (standalone) — restore from a snapshot `id`.
|
|
624
|
+
- **`canvas_diff`** (standalone) — compare current canvas against a saved snapshot (added/removed/modified nodes & edges).
|
|
621
625
|
|
|
622
626
|
### Canvas Management
|
|
623
627
|
|
|
624
|
-
**`
|
|
625
|
-
|
|
626
|
-
- This is irreversible without a prior snapshot
|
|
628
|
+
**`canvas_view { action: "clear" }`** — Remove all nodes and edges. **Always `canvas_snapshot` first** —
|
|
629
|
+
this is irreversible without a prior snapshot.
|
|
627
630
|
|
|
628
631
|
### Browser Automation (WebView)
|
|
629
632
|
|
|
@@ -670,7 +673,7 @@ Useful workbench selectors:
|
|
|
670
673
|
the eraser toolbar button; agents can remove a known annotation ID with
|
|
671
674
|
`canvas_remove_annotation`.
|
|
672
675
|
- Canvas chrome: `.hud-layer`, `.canvas-toolbar`, `.connection-dot`, `.canvas-bootstrap-card`
|
|
673
|
-
- Nodes do not expose stable `data-node-id` attributes. Use `
|
|
676
|
+
- Nodes do not expose stable `data-node-id` attributes. Use `canvas_query` (`layout` / `search`) or MCP resource data for exact node IDs.
|
|
674
677
|
|
|
675
678
|
Async script example:
|
|
676
679
|
|
|
@@ -748,7 +751,7 @@ server's `ui://` resource as an iframe node on the canvas
|
|
|
748
751
|
|
|
749
752
|
### HTML Nodes (Sandboxed iframe)
|
|
750
753
|
|
|
751
|
-
**`canvas_add_html_node`** — Add a normal self-contained HTML document rendered in a sandboxed iframe
|
|
754
|
+
**`canvas_add_html_node`** (standalone) — Add a normal self-contained HTML document rendered in a sandboxed iframe
|
|
752
755
|
- Required: `html` (full document or fragment; inline `<script>` and CDN `<script src="...">` are allowed). If `html` is a bare path to an existing local `.html`/`.htm` file, the server reads that file's contents; otherwise it is treated as raw HTML.
|
|
753
756
|
- Optional: `title`, `summary`, `agentSummary`, `presentation`, `slideTitles`, `embeddedNodeIds`, `embeddedUrls`, `x`, `y`, `width` (default 720), `height` (default 640), `strictSize`
|
|
754
757
|
- Iframe sandbox is `allow-scripts` only — no same-origin access, no top-navigation, no forms
|
|
@@ -758,12 +761,11 @@ server's `ui://` resource as an iframe node on the canvas
|
|
|
758
761
|
- Only presentation-marked HTML nodes expose a browser `Present` button. Use it when the HTML is a deck, briefing, or fullscreen review surface; the PMX shell owns the fullscreen overlay and exits via `Esc` or `Exit presentation`.
|
|
759
762
|
- PMX stores a semantic sidecar (`agentSummary`, `contentSummary`, embedded references) so HTML nodes remain understandable in search, pinned context, and spatial context
|
|
760
763
|
|
|
761
|
-
**`canvas_add_html_primitive`** — Generate a reusable HTML communication primitive as a sandboxed `html` node
|
|
762
|
-
- Required: `kind`; run `
|
|
764
|
+
**`canvas_add_html_primitive`** (standalone) — Generate a reusable HTML communication primitive as a sandboxed `html` node
|
|
765
|
+
- Required: `kind`; run `canvas_render { action: "describe-schema" }` and read `htmlPrimitives` for the current catalog
|
|
763
766
|
- Optional: `title`, `data`, `x`, `y`, `width`, `height`, `strictSize`
|
|
764
767
|
- Use when markdown would be too dense and a structured visual artifact is clearer: tradeoff grids, implementation plans, PR reviews, module maps, design sheets, explainers, reports, and lightweight human-editable boards/editors
|
|
765
768
|
- When the human asks for a PowerPoint-like output, pitch deck, briefing, or presentation, use `kind: "presentation"` unless a bespoke raw HTML deck is required. Include `slides` with short titles, one idea per slide, optional `metrics`, `note` fields for speaker notes, and optional `theme: "canvas" | "midnight" | "paper" | "aurora"` or a custom theme object.
|
|
766
|
-
- Read `htmlPrimitives` from `canvas_describe_schema` for the data shape and examples before constructing a payload
|
|
767
769
|
- For payload patterns, export loops, and the primitive catalog, read `references/html-primitives.md` before creating dense or editable artifacts
|
|
768
770
|
|
|
769
771
|
### Open as Site (standalone surfaces)
|
|
@@ -782,9 +784,9 @@ the host's embedded browser (e.g. Codex) opens `_blank` tabs in-place.
|
|
|
782
784
|
in-canvas iframe loads the **exact same URL**, so there is one render path and no
|
|
783
785
|
separate "preview" version — what you see in the canvas is what opens. The URL reflects
|
|
784
786
|
current node state and survives a refresh.
|
|
785
|
-
- Agents can read this URL from any node payload (`
|
|
786
|
-
as `surfaceUrl` — a reliable way to tell a human
|
|
787
|
-
the canvas.
|
|
787
|
+
- Agents can read this URL from any node payload (`canvas_node { action: "get" }` /
|
|
788
|
+
`canvas_query { action: "layout" }`) as `surfaceUrl` — a reliable way to tell a human
|
|
789
|
+
"open the artifact" without disturbing the canvas.
|
|
788
790
|
- Served HTML stays sandboxed (opaque origin via a `Content-Security-Policy: sandbox`
|
|
789
791
|
response header), so opening author code top-level cannot reach the canvas origin.
|
|
790
792
|
- ext-app `mcp-app` nodes open their UI, but interactive tool-calls only work inside the
|
|
@@ -798,7 +800,7 @@ When the output is more than markdown, pick the lightest tier that fits:
|
|
|
798
800
|
|
|
799
801
|
| Tier | Tool | Build cost | When to pick it |
|
|
800
802
|
|------|------|------------|-----------------|
|
|
801
|
-
| Declarative UI | `
|
|
803
|
+
| Declarative UI | `canvas_render` (`add-json-render` / `add-graph`) | None | Schema-driven dashboards, forms, charts; agent-friendly to read back via `canvas_node { action: "get" }` |
|
|
802
804
|
| Generated HTML primitive | `canvas_add_html_primitive` | None | Reusable communication artifacts such as choices, plans, reviews, maps, reports, presentations/decks, and lightweight editors |
|
|
803
805
|
| Sandboxed HTML+JS | `canvas_add_html_node` | None | Self-contained HTML with inline JS or CDN scripts; one-off visualizations or report views |
|
|
804
806
|
| Hosted MCP app | `canvas_open_mcp_app` / `canvas_add_diagram` | None | Interactive editors backed by an external MCP server (e.g. Excalidraw) |
|
|
@@ -808,8 +810,8 @@ When the output is more than markdown, pick the lightest tier that fits:
|
|
|
808
810
|
|
|
809
811
|
Use native `json-render` and `graph` nodes when the output should stay fully inside PMX Canvas:
|
|
810
812
|
|
|
811
|
-
1. Use `
|
|
812
|
-
2. Use `
|
|
813
|
+
1. Use `canvas_render { action: "add-json-render" }` for dashboards, forms, summaries, and interactive UI panels
|
|
814
|
+
2. Use `canvas_render { action: "add-graph" }` for charts and trend visualizations
|
|
813
815
|
3. Use the repo-local `json-render-*` skills when you need help authoring or refining the spec itself
|
|
814
816
|
4. Use `canvas_build_web_artifact` instead when the result needs a full custom React app rather than a schema-driven UI
|
|
815
817
|
|
|
@@ -843,6 +845,12 @@ Eligible nodes can emit one normalized, validated AX interaction that maps onto
|
|
|
843
845
|
AX operation — work item, evidence, approval, review, focus, steering, event,
|
|
844
846
|
elicitation, or mode request. One envelope, many transports:
|
|
845
847
|
|
|
848
|
+
This is the **agent-native nodes** model: existing canvas node types become
|
|
849
|
+
interactive agent controls when their AX capabilities allow it. Do not describe
|
|
850
|
+
this as a separate node type; it is a capability layer on top of markdown,
|
|
851
|
+
status, HTML, json-render, graph, web-artifact, MCP app, and other supported
|
|
852
|
+
nodes.
|
|
853
|
+
|
|
846
854
|
- **Endpoint:** `POST /api/canvas/ax/interaction` with
|
|
847
855
|
`{ type, sourceNodeId, payload }` (MCP: `canvas_ax_interaction`; CLI:
|
|
848
856
|
`pmx-canvas ax interaction`). Returns `{ ok, primitive }` or
|
|
@@ -857,39 +865,65 @@ elicitation, or mode request. One envelope, many transports:
|
|
|
857
865
|
before submitting: `html` / `html-primitive` nodes (when opted in) call
|
|
858
866
|
`window.PMX_AX.emit(type, payload)`; **json-render / graph** viewers forward a
|
|
859
867
|
spec action named after an AX type (e.g. `on.press → { action:
|
|
860
|
-
"ax.work.create", params }`, `sourceSurface: 'json-render'`);
|
|
861
|
-
**`mcp-app`** nodes
|
|
862
|
-
(`
|
|
863
|
-
|
|
868
|
+
"ax.work.create", params }`, `sourceSurface: 'json-render'`); web-artifact
|
|
869
|
+
**`mcp-app`** nodes use the same parent bridge; external MCP app frames
|
|
870
|
+
(`mode: "ext-app"`) can emit through an injected `window.PMX_AX.emit` with
|
|
871
|
+
Promise acknowledgements, but do not get the read-state bridge. The server
|
|
872
|
+
re-validates capabilities regardless of transport — bridges are convenience,
|
|
873
|
+
not a trust boundary.
|
|
864
874
|
- **Delivery (adapterless):** `canvas://ax-pending-steering` /
|
|
865
|
-
`
|
|
875
|
+
`canvas_ax_delivery { action: "claim" }` return two things, both loop-safe (a consumer never
|
|
866
876
|
receives items it originated):
|
|
867
877
|
- `pending` — undelivered **steering** (directives). Act, then acknowledge with
|
|
868
|
-
`
|
|
878
|
+
`canvas_ax_delivery { action: "mark" }`.
|
|
869
879
|
- `pendingActivity` — open canvas-bound items **awaiting the agent** (open work
|
|
870
880
|
items, pending approval gates / elicitations / mode requests), usually created
|
|
871
881
|
by the human in the browser. These are **state, not steering**: don't
|
|
872
|
-
`
|
|
873
|
-
(`
|
|
874
|
-
`
|
|
882
|
+
`canvas_ax_delivery { action: "mark" }` them — resolve each via its gate/work call
|
|
883
|
+
(`canvas_ax_gate { kind: "approval", action: "resolve" }` /
|
|
884
|
+
`canvas_ax_gate { kind: "elicitation", action: "resolve" }` /
|
|
885
|
+
`canvas_ax_gate { kind: "mode", action: "resolve" }` /
|
|
886
|
+
`canvas_ax_work { action: "update" }`).
|
|
875
887
|
- **Contract:** every AX mutation fires `ax-state-changed`, so MCP clients that
|
|
876
888
|
**subscribe** to resources are pushed `canvas://ax-work` / `canvas://ax-context`
|
|
877
|
-
live. Clients that **poll** instead should poll `
|
|
889
|
+
live. Clients that **poll** instead should poll `canvas_ax_delivery { action: "claim" }` —
|
|
878
890
|
`pendingActivity` is how non-steering browser changes reach them. Only steering
|
|
879
891
|
flows through the claim/ack queue.
|
|
892
|
+
- **Steering is gated, not pushed.** A surface button that emits `ax.steer`
|
|
893
|
+
enqueues a steer — it does NOT wake the agent. With a prompt-injecting host
|
|
894
|
+
adapter (e.g. Copilot), it reaches the next turn only when (1) the **pin/focus
|
|
895
|
+
gate is open** (something pinned or focused — so keep a steering board pinned, or
|
|
896
|
+
have its button also emit `ax.focus.set` on itself), (2) a **human message** fires
|
|
897
|
+
the turn, and (3) the agent **acts then acks** (`canvas_ax_delivery { action: "mark" }`),
|
|
898
|
+
or the steer re-injects every gated turn. `GET /api/canvas/ax/context?consumer=<id>` adds
|
|
899
|
+
a compact, loop-safe `delivery: { pendingSteering, pendingActivity }` lead block an
|
|
900
|
+
adapter can inject un-truncated, so steering survives the full-context char clip.
|
|
901
|
+
- **Activity ingestion (bidirectional board):** a host adapter forwards the agent's
|
|
902
|
+
tool/session events with `canvas_ingest_activity` (standalone; HTTP `POST /api/canvas/ax/activity`)
|
|
903
|
+
and the board auto-reacts — `failure`/`error` (or `outcome:"failure"`) → a blocked
|
|
904
|
+
work item + a review finding + `logs` evidence; `tool-result` + `outcome:"success"` →
|
|
905
|
+
`tool-result` evidence; everything else records a timeline event only. Override or
|
|
906
|
+
suppress per call via `reactions` (`{ workItem: false }`, `{ review: { severity } }`, …).
|
|
907
|
+
- **Blocking gates (gates that actually gate):** `canvas_ax_gate` is the request →
|
|
908
|
+
await → resolve machine. After `{ action: "request" }`, call
|
|
909
|
+
`canvas_ax_gate { kind, action: "await", id, timeoutMs }` (HTTP
|
|
910
|
+
`GET /api/canvas/ax/<kind>/<id>?waitMs=`) to BLOCK until the human resolves it in the
|
|
911
|
+
browser or the timeout elapses (`timeoutMs` 0 = immediate read; ≤120000). Use this to
|
|
912
|
+
pause real work on a human decision instead of polling.
|
|
880
913
|
- **Elicitation / mode:** request structured human input
|
|
881
|
-
(`
|
|
882
|
-
|
|
883
|
-
|
|
914
|
+
(`canvas_ax_gate { kind: "elicitation", action: "request" }` →
|
|
915
|
+
`canvas_ax_gate { kind: "elicitation", action: "resolve" }`) or a workflow
|
|
916
|
+
mode transition (`canvas_ax_gate { kind: "mode", action: "request" }` →
|
|
917
|
+
`canvas_ax_gate { kind: "mode", action: "resolve" }`); both are canvas-bound and snapshotted.
|
|
884
918
|
- **Commands:** invoke a registry command — `pmx.plan`, `pmx.execute`,
|
|
885
919
|
`pmx.promote-context`, `pmx.summarize`, `pmx.review` — via
|
|
886
|
-
`canvas_invoke_command` (HTTP `POST /api/canvas/ax/command`; CLI
|
|
920
|
+
`canvas_invoke_command` (standalone; HTTP `POST /api/canvas/ax/command`; CLI
|
|
887
921
|
`pmx-canvas ax command invoke`; envelope `ax.command.invoke`). Unknown names
|
|
888
922
|
are rejected; an invocation records an `agent-event` of kind `command`.
|
|
889
923
|
- **Policy:** a canvas-bound, snapshotted tool/prompt policy
|
|
890
924
|
(`tools.allowed|excluded|approvalRequired`, `prompt.systemAppend|mode`) read
|
|
891
|
-
into `canvas://ax-context`. Patch it with `
|
|
892
|
-
`GET|POST /api/canvas/ax/policy`; CLI `pmx-canvas ax policy get|set`); patches
|
|
925
|
+
into `canvas://ax-context`. Patch it with `canvas_ax_state { action: "set-policy" }`
|
|
926
|
+
(HTTP `GET|POST /api/canvas/ax/policy`; CLI `pmx-canvas ax policy get|set`); patches
|
|
893
927
|
merge and are normalized server-side.
|
|
894
928
|
|
|
895
929
|
Interactions request PMX-AX primitives only — never arbitrary shell, tool, MCP,
|
|
@@ -916,8 +950,10 @@ AX interactions are gated per node type. The lists below are each type's **ceili
|
|
|
916
950
|
| `webpage` | `ax.evidence.add`, `ax.review.add`, `ax.focus.set`, `ax.event.record` |
|
|
917
951
|
| `group` | `ax.focus.set`, `ax.work.create`, `ax.command.invoke`, `ax.event.record` |
|
|
918
952
|
|
|
919
|
-
**Opt-in** — set `
|
|
920
|
-
`canvas_add_html_node`
|
|
953
|
+
**Opt-in** — set `axCapabilities.enabled = true` (MCP: pass `axCapabilities` to
|
|
954
|
+
`canvas_add_html_node` / `canvas_node { action: "update" }`. HTTP: `axCapabilities` **and** the
|
|
955
|
+
`html` body are accepted **top-level on both `POST /api/canvas/node` and
|
|
956
|
+
`PATCH /api/canvas/node/<id>`**, or nested under `data` — both work, top-level wins):
|
|
921
957
|
|
|
922
958
|
| Node type | Allowed AX interaction types |
|
|
923
959
|
|-----------|------------------------------|
|
|
@@ -928,7 +964,9 @@ AX interactions are gated per node type. The lists below are each type's **ceili
|
|
|
928
964
|
only, no human-facing emit.
|
|
929
965
|
|
|
930
966
|
The 11 interaction types and what they create: `ax.work.create` / `ax.work.update`
|
|
931
|
-
(work-queue items
|
|
967
|
+
(work-queue items; status is exactly one of `todo`, `in-progress`, `blocked`, `done`,
|
|
968
|
+
`cancelled` — **hyphens, not underscores**; `POST`/`PATCH /api/canvas/ax/work` reject an
|
|
969
|
+
unknown token like `in_progress` with `400`), `ax.evidence.add`
|
|
932
970
|
(timeline evidence), `ax.review.add` (review annotation), `ax.focus.set` (agent focus
|
|
933
971
|
pointer), `ax.steer` (a steering message delivered to the agent), `ax.approval.request`
|
|
934
972
|
(approval gate), `ax.elicitation.request` (structured human input), `ax.mode.request`
|
|
@@ -944,11 +982,17 @@ state. The read side mirrors the write side:
|
|
|
944
982
|
- **Opt in** (html/mcp-app are off by default): create with
|
|
945
983
|
`canvas_add_html_node({ html, axCapabilities: { enabled: true, allowed: ["ax.work.create","ax.work.update"] } })`,
|
|
946
984
|
or flip an existing node on with
|
|
947
|
-
`
|
|
985
|
+
`canvas_node({ action: "update", id, axCapabilities: { enabled: true, allowed: [...] } })`.
|
|
948
986
|
json-render / graph nodes are enabled by default.
|
|
949
987
|
- **Emit (write):** in `html`, call `window.PMX_AX.emit("ax.work.create", { title })`;
|
|
950
988
|
in `json-render`, bind a control action named after the AX type
|
|
951
989
|
(`on: { press: { action: "ax.work.create", params: { title } } }`).
|
|
990
|
+
- **Confirm (#55):** for `html` / `html-primitive` and PMX_AX-enabled `mcp-app`
|
|
991
|
+
surfaces, `emit` returns a Promise that resolves with the result once the
|
|
992
|
+
canvas acks it, so a button can self-confirm: `const r = await
|
|
993
|
+
window.PMX_AX.emit(...); if (r.ok) showQueued();`. You can also
|
|
994
|
+
`window.PMX_AX.on('ack', cb)` or listen for the `pmx-ax-ack` event. (Falls back
|
|
995
|
+
to an `ax-ack-timeout` result after 10s, so `await` never hangs.)
|
|
952
996
|
- **Reflect (read):** the canvas seeds the surface with a compact AX snapshot at
|
|
953
997
|
load (the same shape as `GET /api/canvas/ax/surface-snapshot`) and live-updates it
|
|
954
998
|
as AX state changes. Works on all three authored surface types:
|
|
@@ -966,11 +1010,15 @@ state. The read side mirrors the write side:
|
|
|
966
1010
|
Minimal html work board (drop-in via `canvas_add_html_node`, `axCapabilities.enabled: true`):
|
|
967
1011
|
|
|
968
1012
|
```html
|
|
969
|
-
<button
|
|
1013
|
+
<button id="add">+ Task</button> <span id="ok"></span>
|
|
970
1014
|
<ul id="q"></ul>
|
|
971
1015
|
<script>
|
|
972
1016
|
function render(s){ document.getElementById('q').innerHTML =
|
|
973
1017
|
((s&&s.workItems)||[]).map(w => '<li>['+w.status+'] '+w.title+'</li>').join(''); }
|
|
1018
|
+
document.getElementById('add').onclick = async () => {
|
|
1019
|
+
const r = await window.PMX_AX.emit('ax.work.create',{title:'New task'});
|
|
1020
|
+
document.getElementById('ok').textContent = r && r.ok ? 'queued ✓' : 'failed'; // #55 self-confirm
|
|
1021
|
+
};
|
|
974
1022
|
render(window.PMX_AX && window.PMX_AX.state);
|
|
975
1023
|
window.addEventListener('pmx-ax-update', e => render(e.detail));
|
|
976
1024
|
</script>
|
|
@@ -1112,10 +1160,10 @@ Show system components and how they interact:
|
|
|
1112
1160
|
|
|
1113
1161
|
1. Create `markdown` nodes for each service/component (include port, tech stack in content)
|
|
1114
1162
|
2. Use `flow` edges for data flow, `depends-on` for dependencies — always label edges
|
|
1115
|
-
3. Group related services with `
|
|
1163
|
+
3. Group related services with `canvas_group { action: "create" }` (e.g., "Application Services", "Data Layer")
|
|
1116
1164
|
4. Use colors: green for healthy, yellow for degraded, red for down
|
|
1117
1165
|
5. Arrange with `grid` layout initially
|
|
1118
|
-
6. For tiered architectures, fine-tune with explicit `x`/`y` via `
|
|
1166
|
+
6. For tiered architectures, fine-tune with explicit `x`/`y` via `canvas_node { action: "update" }` to show
|
|
1119
1167
|
layers (e.g., gateway at top, services in middle, data stores at bottom)
|
|
1120
1168
|
7. Connect pipeline stages with `flow` edges where applicable
|
|
1121
1169
|
|
|
@@ -1127,7 +1175,7 @@ Track work items and their relationships:
|
|
|
1127
1175
|
2. Color-code: green=done, yellow=in-progress, red=blocked, gray=queued, blue=ready/available
|
|
1128
1176
|
3. Connect with `depends-on` edges — use `dashed` style for blocked dependencies, `solid` for
|
|
1129
1177
|
satisfied ones
|
|
1130
|
-
4. Update status nodes as work progresses using `
|
|
1178
|
+
4. Update status nodes as work progresses using `canvas_node { action: "update" }`
|
|
1131
1179
|
5. Arrange with `flow` layout to show the dependency chain left-to-right
|
|
1132
1180
|
6. Group related tasks if the plan has distinct phases
|
|
1133
1181
|
|
|
@@ -1140,7 +1188,7 @@ Understand a codebase by visualizing file relationships:
|
|
|
1140
1188
|
don't need to manually add import-based edges. You can still add manual edges for
|
|
1141
1189
|
conceptual relationships beyond imports (e.g., "middleware validates using jwt")
|
|
1142
1190
|
3. Read `canvas://code-graph` for dependency analysis: central files, isolated files
|
|
1143
|
-
4. Group related files with `
|
|
1191
|
+
4. Group related files with `canvas_group { action: "create" }` (e.g., "Auth Module", "API Routes")
|
|
1144
1192
|
5. Pin important files so the human sees them highlighted
|
|
1145
1193
|
6. Arrange with `grid` layout after adding files
|
|
1146
1194
|
|
|
@@ -1162,8 +1210,8 @@ Monitor ongoing processes:
|
|
|
1162
1210
|
1. Create `status` nodes for each metric/process
|
|
1163
1211
|
2. Use semantic colors: green=passing, yellow=running, red=failing, gray=queued
|
|
1164
1212
|
3. Connect sequential pipeline stages with `flow` edges (label: "then", "triggers")
|
|
1165
|
-
4. Update nodes in-place as state changes using `
|
|
1166
|
-
as that loses position and edges
|
|
1213
|
+
4. Update nodes in-place as state changes using `canvas_node { action: "update" }` — never delete
|
|
1214
|
+
and recreate, as that loses position and edges
|
|
1167
1215
|
5. Arrange with `grid` layout
|
|
1168
1216
|
6. The human sees real-time updates via SSE
|
|
1169
1217
|
|
|
@@ -1181,7 +1229,7 @@ Show two states side by side for the human to compare:
|
|
|
1181
1229
|
When the human wants to explore a different approach without losing current work:
|
|
1182
1230
|
|
|
1183
1231
|
1. **First**, save the current state: `canvas_snapshot` with a descriptive name
|
|
1184
|
-
2. **Then** clear: `
|
|
1232
|
+
2. **Then** clear: `canvas_view { action: "clear" }` (never clear without snapshotting first)
|
|
1185
1233
|
3. Set up the new workspace with initial nodes
|
|
1186
1234
|
4. Tell the human the snapshot name and that `canvas_restore` can bring everything back
|
|
1187
1235
|
|
|
@@ -1195,17 +1243,17 @@ When the human wants to explore a different approach without losing current work
|
|
|
1195
1243
|
3. **Label every edge.** Unlabeled edges lose meaning. "depends on", "calls", "blocks"
|
|
1196
1244
|
are all more useful than a bare arrow.
|
|
1197
1245
|
|
|
1198
|
-
4. **Auto-arrange after batch adds.** When adding multiple nodes, call
|
|
1199
|
-
once at the end, not after each node.
|
|
1246
|
+
4. **Auto-arrange after batch adds.** When adding multiple nodes, call
|
|
1247
|
+
`canvas_view { action: "arrange" }` once at the end, not after each node.
|
|
1200
1248
|
|
|
1201
|
-
5. **Update in place.** Use `
|
|
1202
|
-
Don't delete and recreate — that loses position and edges.
|
|
1249
|
+
5. **Update in place.** Use `canvas_node { action: "update" }` to change status, content, or
|
|
1250
|
+
color. Don't delete and recreate — that loses position and edges.
|
|
1203
1251
|
|
|
1204
1252
|
6. **Clean up.** Remove nodes that are no longer relevant. A cluttered canvas is worse
|
|
1205
1253
|
than no canvas.
|
|
1206
1254
|
|
|
1207
|
-
7. **Read before writing.** Check `canvas://layout` or `
|
|
1208
|
-
nodes to avoid duplicates and understand the current state.
|
|
1255
|
+
7. **Read before writing.** Check `canvas://layout` or `canvas_query { action: "layout" }` before
|
|
1256
|
+
adding nodes to avoid duplicates and understand the current state.
|
|
1209
1257
|
|
|
1210
1258
|
8. **Use pinning.** When you want the human to focus on specific nodes, pin them.
|
|
1211
1259
|
When the human pins nodes, read `canvas://pinned-context` to see what they care about.
|