pmx-canvas 0.1.26 → 0.1.28

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 (64) hide show
  1. package/.github/extensions/pmx-canvas/extension.mjs +191 -0
  2. package/CHANGELOG.md +110 -0
  3. package/Readme.md +74 -27
  4. package/dist/canvas/index.js +82 -82
  5. package/dist/json-render/index.css +1 -1
  6. package/dist/json-render/index.js +944 -164
  7. package/dist/types/json-render/catalog.d.ts +195 -20
  8. package/dist/types/json-render/charts/components.d.ts +17 -0
  9. package/dist/types/json-render/charts/definitions.d.ts +13 -1
  10. package/dist/types/json-render/charts/tufte-components.d.ts +65 -0
  11. package/dist/types/json-render/charts/tufte-definitions.d.ts +164 -0
  12. package/dist/types/json-render/directives.d.ts +33 -0
  13. package/dist/types/json-render/renderer/index.d.ts +1 -0
  14. package/dist/types/json-render/server.d.ts +32 -1
  15. package/dist/types/mcp/canvas-access.d.ts +62 -0
  16. package/dist/types/server/ax-state.d.ts +170 -0
  17. package/dist/types/server/canvas-db.d.ts +17 -1
  18. package/dist/types/server/canvas-operations.d.ts +53 -0
  19. package/dist/types/server/canvas-schema.d.ts +5 -1
  20. package/dist/types/server/canvas-state.d.ts +95 -4
  21. package/dist/types/server/index.d.ts +120 -3
  22. package/dist/types/server/mutation-history.d.ts +1 -1
  23. package/docs/cli.md +42 -0
  24. package/docs/http-api.md +64 -0
  25. package/docs/mcp.md +23 -5
  26. package/docs/node-types.md +1 -1
  27. package/docs/screenshots/codex-app.png +0 -0
  28. package/docs/screenshots/github-copilot-app.png +0 -0
  29. package/docs/sdk.md +23 -5
  30. package/package.json +10 -7
  31. package/skills/control-session-orchestrator/SKILL.md +359 -0
  32. package/skills/control-session-orchestrator/evals/evals.json +75 -0
  33. package/skills/data-analysis/SKILL.md +6 -0
  34. package/skills/pmx-canvas/SKILL.md +50 -4
  35. package/skills/pmx-canvas/references/github-copilot-app-adapter.md +6 -0
  36. package/skills/tufte-viz/SKILL.md +157 -0
  37. package/skills/tufte-viz/references/analytical-design.md +217 -0
  38. package/skills/tufte-viz/references/tufte-principles.md +147 -0
  39. package/src/cli/agent.ts +302 -3
  40. package/src/cli/index.ts +2 -1
  41. package/src/client/nodes/ExtAppFrame.tsx +48 -1
  42. package/src/client/nodes/McpAppNode.tsx +6 -2
  43. package/src/json-render/catalog.ts +22 -1
  44. package/src/json-render/charts/components.tsx +127 -15
  45. package/src/json-render/charts/definitions.ts +19 -2
  46. package/src/json-render/charts/extra-components.tsx +5 -4
  47. package/src/json-render/charts/tufte-components.tsx +395 -0
  48. package/src/json-render/charts/tufte-definitions.ts +128 -0
  49. package/src/json-render/directives.ts +64 -0
  50. package/src/json-render/renderer/index.css +107 -1
  51. package/src/json-render/renderer/index.tsx +33 -0
  52. package/src/json-render/server.ts +275 -5
  53. package/src/mcp/canvas-access.ts +264 -1
  54. package/src/mcp/server.ts +498 -9
  55. package/src/server/ax-context.ts +8 -3
  56. package/src/server/ax-state.ts +447 -0
  57. package/src/server/canvas-db.ts +184 -1
  58. package/src/server/canvas-operations.ts +123 -2
  59. package/src/server/canvas-schema.ts +27 -3
  60. package/src/server/canvas-state.ts +349 -2
  61. package/src/server/index.ts +259 -7
  62. package/src/server/mutation-history.ts +6 -0
  63. package/src/server/server.ts +442 -5
  64. package/src/server/web-artifacts.ts +31 -5
@@ -1,6 +1,8 @@
1
1
  import { EventEmitter } from 'node:events';
2
+ import { canvasState } from './canvas-state.js';
2
3
  import type { CanvasAnnotation, CanvasNodeState, CanvasEdge, CanvasLayout } from './canvas-state.js';
3
- import type { PmxAxContext, PmxAxFocusState, PmxAxSource, PmxAxState } from './ax-state.js';
4
+ import type { PmxAxApprovalGate, PmxAxContext, PmxAxEvent, PmxAxEvidence, PmxAxEvidenceKind, PmxAxFocusState, PmxAxHostCapability, PmxAxReviewAnchorType, PmxAxReviewAnnotation, PmxAxReviewKind, PmxAxReviewRegion, PmxAxReviewSeverity, PmxAxReviewStatus, PmxAxSource, PmxAxState, PmxAxSteeringMessage, PmxAxWorkItem, PmxAxWorkItemStatus } from './ax-state.js';
5
+ import type { AxTimelineQuery } from './canvas-db.js';
4
6
  import { searchNodes } from './spatial-analysis.js';
5
7
  import { diffLayouts } from './mutation-history.js';
6
8
  import { fitCanvasView, gcCanvasSnapshots, listCanvasSnapshots } from './canvas-operations.js';
@@ -21,6 +23,11 @@ export declare class PmxCanvas extends EventEmitter {
21
23
  automationWebView?: boolean | CanvasAutomationWebViewOptions;
22
24
  }): Promise<void>;
23
25
  stop(): void;
26
+ /**
27
+ * Add a node to the canvas and return the created node (including its `id`,
28
+ * resolved geometry, and data). Destructure `const { id } = canvas.addNode(...)`
29
+ * or keep the whole node — both work. (Previously returned a bare id string.)
30
+ */
24
31
  addNode(input: {
25
32
  type: CanvasNodeState['type'];
26
33
  title?: string;
@@ -40,7 +47,7 @@ export declare class PmxCanvas extends EventEmitter {
40
47
  width?: number;
41
48
  height?: number;
42
49
  strictSize?: boolean;
43
- }): string;
50
+ }): CanvasNodeState;
44
51
  addWebpageNode(input: {
45
52
  title?: string;
46
53
  url: string;
@@ -114,6 +121,85 @@ export declare class PmxCanvas extends EventEmitter {
114
121
  setAxFocus(nodeIds: string[], options?: {
115
122
  source?: PmxAxSource;
116
123
  }): PmxAxFocusState;
124
+ recordAxEvent(input: {
125
+ kind: PmxAxEvent['kind'];
126
+ summary: string;
127
+ detail?: string | null;
128
+ nodeIds?: string[];
129
+ data?: Record<string, unknown> | null;
130
+ }, options?: {
131
+ source?: PmxAxSource;
132
+ }): PmxAxEvent;
133
+ sendSteering(message: string, options?: {
134
+ source?: PmxAxSource;
135
+ }): PmxAxSteeringMessage;
136
+ markSteeringDelivered(id: string): boolean;
137
+ getAxTimeline(query?: AxTimelineQuery): ReturnType<typeof canvasState.getAxTimeline>;
138
+ listWorkItems(): PmxAxWorkItem[];
139
+ addWorkItem(input: {
140
+ title: string;
141
+ status?: PmxAxWorkItemStatus;
142
+ detail?: string | null;
143
+ nodeIds?: string[];
144
+ }, options?: {
145
+ source?: PmxAxSource;
146
+ }): PmxAxWorkItem;
147
+ updateWorkItem(id: string, patch: {
148
+ title?: string;
149
+ status?: PmxAxWorkItemStatus;
150
+ detail?: string | null;
151
+ nodeIds?: string[];
152
+ }, options?: {
153
+ source?: PmxAxSource;
154
+ }): PmxAxWorkItem | null;
155
+ listApprovalGates(): PmxAxApprovalGate[];
156
+ requestApproval(input: {
157
+ title: string;
158
+ detail?: string | null;
159
+ action?: string | null;
160
+ nodeIds?: string[];
161
+ }, options?: {
162
+ source?: PmxAxSource;
163
+ }): PmxAxApprovalGate;
164
+ resolveApproval(id: string, decision: 'approved' | 'rejected', options?: {
165
+ resolution?: string;
166
+ source?: PmxAxSource;
167
+ }): PmxAxApprovalGate | null;
168
+ addEvidence(input: {
169
+ kind: PmxAxEvidenceKind;
170
+ title: string;
171
+ body?: string | null;
172
+ ref?: string | null;
173
+ nodeIds?: string[];
174
+ data?: Record<string, unknown> | null;
175
+ }, options?: {
176
+ source?: PmxAxSource;
177
+ }): PmxAxEvidence;
178
+ listReviewAnnotations(): PmxAxReviewAnnotation[];
179
+ addReviewAnnotation(input: {
180
+ body: string;
181
+ kind?: PmxAxReviewKind;
182
+ severity?: PmxAxReviewSeverity;
183
+ anchorType?: PmxAxReviewAnchorType;
184
+ nodeId?: string | null;
185
+ file?: string | null;
186
+ region?: PmxAxReviewRegion | null;
187
+ author?: string | null;
188
+ }, options?: {
189
+ source?: PmxAxSource;
190
+ }): PmxAxReviewAnnotation | null;
191
+ updateReviewAnnotation(id: string, patch: {
192
+ body?: string;
193
+ status?: PmxAxReviewStatus;
194
+ severity?: PmxAxReviewSeverity;
195
+ kind?: PmxAxReviewKind;
196
+ }, options?: {
197
+ source?: PmxAxSource;
198
+ }): PmxAxReviewAnnotation | null;
199
+ getHostCapability(): PmxAxHostCapability | null;
200
+ reportHostCapability(input: unknown, options?: {
201
+ source?: PmxAxSource;
202
+ }): PmxAxHostCapability;
117
203
  fitView(options?: {
118
204
  width?: number;
119
205
  height?: number;
@@ -188,9 +274,13 @@ export declare class PmxCanvas extends EventEmitter {
188
274
  jsonRender: {
189
275
  rootShape: Record<string, string>;
190
276
  components: import("../json-render/catalog.js").JsonRenderComponentDescriptor[];
277
+ directives: Array<{
278
+ name: string;
279
+ usage: string;
280
+ }>;
191
281
  };
192
282
  graph: {
193
- graphTypes: ("line" | "bar" | "pie" | "area" | "scatter" | "radar" | "composed" | "stacked-bar")[];
283
+ graphTypes: ("line" | "bar" | "pie" | "area" | "scatter" | "radar" | "composed" | "sparkline" | "bullet" | "slopegraph" | "stacked-bar" | "dot-plot")[];
194
284
  };
195
285
  htmlPrimitives: import("./html-primitives.js").HtmlPrimitiveDescriptor[];
196
286
  mcp: {
@@ -251,6 +341,31 @@ export declare class PmxCanvas extends EventEmitter {
251
341
  url: string;
252
342
  spec: JsonRenderSpec;
253
343
  };
344
+ /**
345
+ * Progressively build a json-render node from SpecStream patches. Omit nodeId
346
+ * to create a new streaming node; pass the same nodeId on later calls to
347
+ * append more patches. The server accumulates the spec and the browser
348
+ * reloads the viewer as the specVersion bumps.
349
+ */
350
+ streamJsonRenderNode(input: {
351
+ nodeId?: string;
352
+ title?: string;
353
+ patches?: unknown[];
354
+ done?: boolean;
355
+ x?: number;
356
+ y?: number;
357
+ width?: number;
358
+ height?: number;
359
+ strictSize?: boolean;
360
+ }): {
361
+ id: string;
362
+ url: string;
363
+ applied: number;
364
+ skipped: number;
365
+ specVersion: number;
366
+ elementCount: number;
367
+ streamStatus: 'open' | 'closed';
368
+ };
254
369
  addHtmlNode(input: {
255
370
  html: string;
256
371
  title?: string;
@@ -318,3 +433,5 @@ export type { WebArtifactBuildInput, WebArtifactBuildOutput, WebArtifactCanvasBu
318
433
  export type { GraphNodeInput, JsonRenderNodeInput, JsonRenderSpec } from '../json-render/server.js';
319
434
  export type { HtmlPrimitiveKind, HtmlPrimitiveDescriptor, HtmlPrimitiveInput, HtmlPrimitiveBuildResult } from './html-primitives.js';
320
435
  export { traceManager } from './trace-manager.js';
436
+ export type { PmxAxApprovalGate, PmxAxApprovalStatus, PmxAxContext, PmxAxEvent, PmxAxEventKind, PmxAxEvidence, PmxAxEvidenceKind, PmxAxFocusState, PmxAxHostCapability, PmxAxReviewAnchorType, PmxAxReviewAnnotation, PmxAxReviewKind, PmxAxReviewRegion, PmxAxReviewSeverity, PmxAxReviewStatus, PmxAxSource, PmxAxState, PmxAxSteeringMessage, PmxAxTimelineSummary, PmxAxWorkItem, PmxAxWorkItemStatus, } from './ax-state.js';
437
+ export type { AxTimelineQuery } from './canvas-db.js';
@@ -12,7 +12,7 @@
12
12
  * - _replaying flag prevents undo/redo from recording new entries
13
13
  */
14
14
  import type { CanvasNodeState, CanvasEdge } from './canvas-state.js';
15
- export type MutationOp = 'addNode' | 'updateNode' | 'removeNode' | 'addEdge' | 'removeEdge' | 'addAnnotation' | 'removeAnnotation' | 'clear' | 'arrange' | 'restoreSnapshot' | 'setPins' | 'setAxFocus' | 'batch' | 'viewport' | 'groupNodes' | 'ungroupNodes';
15
+ export type MutationOp = 'addNode' | 'updateNode' | 'removeNode' | 'addEdge' | 'removeEdge' | 'addAnnotation' | 'removeAnnotation' | 'clear' | 'arrange' | 'restoreSnapshot' | 'setPins' | 'setAxFocus' | 'addWorkItem' | 'updateWorkItem' | 'requestApproval' | 'resolveApproval' | 'addReviewAnnotation' | 'updateReviewAnnotation' | 'batch' | 'viewport' | 'groupNodes' | 'ungroupNodes';
16
16
  export interface MutationEntry {
17
17
  id: string;
18
18
  timestamp: string;
package/docs/cli.md CHANGED
@@ -125,6 +125,48 @@ pmx-canvas ax focus node-1 node-2 # Set AX focus
125
125
  pmx-canvas ax focus --clear # Clear AX focus
126
126
  ```
127
127
 
128
+ ### AX primitives
129
+
130
+ Host-agnostic agent-experience primitives. Timeline commands persist for
131
+ diagnostics (retention-bounded, not snapshotted); work items, approval gates,
132
+ and review annotations are canvas-bound and ride snapshots/restore.
133
+
134
+ ```bash
135
+ # Timeline
136
+ pmx-canvas ax event add --kind tool-start --summary "ran tests"
137
+ pmx-canvas ax steer "focus on the failing test first"
138
+ pmx-canvas ax evidence add --kind test-output --title "unit pass"
139
+ pmx-canvas ax timeline --limit 50
140
+
141
+ # Work items (canvas-bound)
142
+ pmx-canvas ax work add --title "Wire up auth" --status in-progress node-1
143
+ pmx-canvas ax work update <id> --status done
144
+ pmx-canvas ax work list
145
+
146
+ # Approval gates (canvas-bound; pending → approved/rejected)
147
+ pmx-canvas ax approval request --title "Deploy to prod" --action deploy.prod
148
+ pmx-canvas ax approval resolve <id> --decision approved
149
+ pmx-canvas ax approval list
150
+
151
+ # Review annotations (canvas-bound)
152
+ pmx-canvas ax review add --body "off-by-one" --kind finding --severity error --file src/x.ts
153
+ pmx-canvas ax review list
154
+
155
+ # Host capability (own partition; survives clear)
156
+ pmx-canvas ax host report --host copilot --canvas --session-messaging
157
+ pmx-canvas ax host status
158
+ ```
159
+
160
+ ## Copilot adapter
161
+
162
+ Install the bundled GitHub Copilot extension adapter into a repo. The adapter
163
+ maps onto the same neutral AX surfaces (it never makes the core GitHub-specific).
164
+
165
+ ```bash
166
+ pmx-canvas copilot install-extension --dry-run # Preview target, writes nothing
167
+ pmx-canvas copilot install-extension --yes # Install/overwrite into .github/extensions/pmx-canvas/
168
+ ```
169
+
128
170
  ## WebView automation
129
171
 
130
172
  Drive a headless Bun.WebView (Chromium or WebKit) pointed at the workbench:
package/docs/http-api.md CHANGED
@@ -117,6 +117,70 @@ curl -X PATCH http://localhost:4313/api/canvas/ax \
117
117
  -d '{"focus":{"nodeIds":["node-1"],"source":"api"}}'
118
118
  ```
119
119
 
120
+ ## AX primitives (timeline, work, host)
121
+
122
+ Host-agnostic agent-experience primitives across three state partitions.
123
+ Canvas-bound state (work items, approval gates, review annotations) rides
124
+ canvas snapshots; timeline state (events, evidence, steering) persists for
125
+ diagnostics but is retention-bounded and not restored by snapshots; the host
126
+ capability is reported by adapters and survives `canvas_clear`.
127
+
128
+ ```bash
129
+ # Timeline — record a normalized agent-event
130
+ curl -X POST http://localhost:4313/api/canvas/ax/event \
131
+ -H "Content-Type: application/json" \
132
+ -d '{"kind":"tool-start","summary":"ran tests","source":"api"}'
133
+
134
+ # Timeline — send a steering message to the active agent session
135
+ curl -X POST http://localhost:4313/api/canvas/ax/steer \
136
+ -H "Content-Type: application/json" \
137
+ -d '{"message":"focus on the failing test first","source":"api"}'
138
+
139
+ # Timeline — record an evidence item (logs/tool-result/screenshot/file/diff/test-output)
140
+ curl -X POST http://localhost:4313/api/canvas/ax/evidence \
141
+ -H "Content-Type: application/json" \
142
+ -d '{"kind":"test-output","title":"unit pass","source":"api"}'
143
+
144
+ # Timeline — read the bounded timeline (default limit 50, max 200)
145
+ curl "http://localhost:4313/api/canvas/ax/timeline?limit=50"
146
+
147
+ # Canvas-bound — add / update a work item
148
+ curl -X POST http://localhost:4313/api/canvas/ax/work \
149
+ -H "Content-Type: application/json" \
150
+ -d '{"title":"Wire up auth","status":"in-progress","nodeIds":["node-1"],"source":"api"}'
151
+ curl -X PATCH http://localhost:4313/api/canvas/ax/work/<id> \
152
+ -H "Content-Type: application/json" \
153
+ -d '{"status":"done"}'
154
+ curl http://localhost:4313/api/canvas/ax/work
155
+
156
+ # Canvas-bound — request / resolve an approval gate (pending → approved/rejected)
157
+ curl -X POST http://localhost:4313/api/canvas/ax/approval \
158
+ -H "Content-Type: application/json" \
159
+ -d '{"title":"Deploy to prod","action":"deploy.prod","source":"api"}'
160
+ curl -X POST http://localhost:4313/api/canvas/ax/approval/<id>/resolve \
161
+ -H "Content-Type: application/json" \
162
+ -d '{"decision":"approved","source":"api"}'
163
+ curl http://localhost:4313/api/canvas/ax/approval
164
+
165
+ # Canvas-bound — add a review annotation (comment/finding) anchored to node/file/region
166
+ curl -X POST http://localhost:4313/api/canvas/ax/review \
167
+ -H "Content-Type: application/json" \
168
+ -d '{"body":"off-by-one","kind":"finding","severity":"error","anchorType":"file","file":"src/x.ts","source":"api"}'
169
+ curl http://localhost:4313/api/canvas/ax/review
170
+
171
+ # Host/session — report and read host capability
172
+ curl -X PUT http://localhost:4313/api/canvas/ax/host-capability \
173
+ -H "Content-Type: application/json" \
174
+ -d '{"host":"copilot","canvas":true,"sessionMessaging":true,"source":"api"}'
175
+ curl http://localhost:4313/api/canvas/ax/host-capability
176
+ ```
177
+
178
+ Validation: `/ax/event` requires a valid `kind` + `summary` (400 otherwise);
179
+ `/ax/evidence` requires `kind` + `title`; `/ax/steer`, `/ax/work`,
180
+ `/ax/approval`, `/ax/review` require their primary field; `PATCH /ax/work/:id`
181
+ and `PATCH /ax/review/:id` return 404 for unknown IDs; approval resolve returns
182
+ 404 if the gate is missing or already resolved.
183
+
120
184
  ## Diagrams (Excalidraw preset)
121
185
 
122
186
  ```bash
package/docs/mcp.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # MCP reference
2
2
 
3
- PMX Canvas ships an MCP stdio server with **45 tools** + **9 core resources**,
3
+ PMX Canvas ships an MCP stdio server with **56 tools** + **12 core resources**,
4
4
  plus per-skill resources at `canvas://skills/<name>`. The server emits
5
5
  `notifications/resources/updated` when canvas state changes — humans pin
6
6
  nodes in the browser, agents are notified immediately.
@@ -35,7 +35,8 @@ The canvas auto-starts on first tool call.
35
35
  | `canvas_validate_spec` | Validate a json-render spec, graph payload, or HTML primitive payload without creating a node |
36
36
  | `canvas_refresh_webpage_node` | Re-fetch and update a webpage node from its stored URL |
37
37
  | `canvas_add_json_render_node` | Create a native json-render node from a validated spec |
38
- | `canvas_add_graph_node` | Create a native graph node (line, bar, pie, area, scatter, radar, stacked-bar, composed) |
38
+ | `canvas_stream_json_render_node` | Progressively build a json-render node from SpecStream JSON-Patch ops (live/streaming panels) |
39
+ | `canvas_add_graph_node` | Create a native graph node (line, bar, pie, area, scatter, radar, stacked-bar, composed, sparkline, dot-plot, bullet, slopegraph) |
39
40
  | `canvas_build_web_artifact` | Build a bundled HTML artifact and open it on the canvas |
40
41
 
41
42
  `canvas_add_html_node` accepts optional `summary`, `agentSummary`, `embeddedNodeIds`, and
@@ -51,8 +52,19 @@ searchable and readable in pinned/spatial context.
51
52
  | `canvas_arrange` | Auto-arrange (grid/column/flow) |
52
53
  | `canvas_validate` | Validate collisions, containment, and missing edge endpoints |
53
54
  | `canvas_focus_node` | Pan viewport to a node; use CLI `focus --no-pan` when you only need to select/raise |
54
- | `canvas_get_ax` | Read the PMX AX state plus pinned/focused context |
55
+ | `canvas_fit_view` | Fit the canvas viewport to all nodes or a selected subset |
56
+ | `canvas_get_ax` | Read the PMX AX state (focus, work items, approvals, review annotations, host capability) plus pinned/focused context |
55
57
  | `canvas_set_ax_focus` | Set the host-agnostic AX focus node set; adapters can pass a source such as `codex` |
58
+ | `canvas_record_ax_event` | Record a normalized timeline `agent-event` (prompt/assistant-message/tool-start/tool-result/failure/approval/steering) |
59
+ | `canvas_send_steering` | Record a `steering-message`: a user instruction from the surface to the active agent session |
60
+ | `canvas_get_ax_timeline` | Read the bounded AX timeline (events, evidence, steering) plus counts |
61
+ | `canvas_add_work_item` | Add a canvas-bound `work-item` (visible task/plan/status tied to nodes) |
62
+ | `canvas_update_work_item` | Update a work item's title/status/detail/nodeIds by ID |
63
+ | `canvas_request_approval` | Request human approval via an `approval-gate` (pending) before a high-impact action |
64
+ | `canvas_resolve_approval` | Resolve a pending approval gate (`approved`/`rejected`) |
65
+ | `canvas_add_evidence` | Record an `evidence-item` on the timeline (logs/tool-result/screenshot/file/diff/test-output) |
66
+ | `canvas_add_review_annotation` | Add a canvas-bound `review-annotation` (comment/finding) anchored to a node, file, or region |
67
+ | `canvas_report_host_capability` | Report a host/session `host-capability` for diagnostics |
56
68
  | `canvas_pin_nodes` | Pin nodes to include in agent context |
57
69
  | `canvas_clear` | Clear all nodes and edges |
58
70
  | `canvas_snapshot` | Save current canvas as a named snapshot |
@@ -82,8 +94,10 @@ Individual bundled skills are also readable at `canvas://skills/<name>`.
82
94
  | Resource | Description |
83
95
  |----------|-------------|
84
96
  | `canvas://pinned-context` | Content of pinned nodes + nearby unpinned neighbors |
85
- | `canvas://ax` | PMX AX state, currently including persisted focus |
86
- | `canvas://ax-context` | Agent-readable pinned and focused AX context |
97
+ | `canvas://ax` | PMX AX state: focus, work items, approval gates, review annotations |
98
+ | `canvas://ax-context` | Agent-readable pinned and focused AX context, plus timeline summary and host capability |
99
+ | `canvas://ax-work` | Canvas-bound AX work: work items, approval gates, review annotations |
100
+ | `canvas://ax-timeline` | Bounded AX timeline: recent agent-events, evidence, and steering messages |
87
101
  | `canvas://schema` | Running-server create schemas and json-render catalog metadata |
88
102
  | `canvas://layout` | Full canvas state (all nodes, edges, viewport) |
89
103
  | `canvas://summary` | Compact overview: counts, pinned titles, viewport |
@@ -99,6 +113,10 @@ changes:
99
113
 
100
114
  - Pin changes notify `canvas://pinned-context`, `canvas://ax`, and `canvas://ax-context`
101
115
  - AX focus changes notify `canvas://ax` and `canvas://ax-context`
116
+ - Canvas-bound AX mutations (work items, approval gates, review annotations,
117
+ host capability) notify `canvas://ax`, `canvas://ax-work`, and `canvas://ax-context`
118
+ - AX timeline mutations (agent-events, evidence, steering) notify
119
+ `canvas://ax-timeline` and `canvas://ax-context`
102
120
  - All mutations notify `canvas://layout`, `canvas://summary`,
103
121
  `canvas://spatial-context`, `canvas://history`, and `canvas://code-graph`
104
122
 
@@ -19,7 +19,7 @@ see [MCP tools](mcp.md), [HTTP API](http-api.md), and [SDK](sdk.md).
19
19
  | `webpage` | Persisted webpage snapshot with stored URL, extracted text, refresh |
20
20
  | `mcp-app` | Tool-backed hosted MCP App iframes (Excalidraw, etc.) |
21
21
  | `json-render` | Structured UI from JSON specs (cards, tables, forms) |
22
- | `graph` | Charts (line, bar, pie, area, scatter, radar, stacked-bar, composed) |
22
+ | `graph` | Charts (line, bar, pie, area, scatter, radar, stacked-bar, composed, plus Tufte primitives: sparkline, dot-plot, bullet, slopegraph) |
23
23
  | `html` | Self-contained HTML/JS in a sandboxed iframe |
24
24
  | `web-artifact` | Bundled React/Tailwind artifact (full single-file app) |
25
25
  | `group` | Spatial container/frame around other nodes |
Binary file
package/docs/sdk.md CHANGED
@@ -15,16 +15,16 @@ import { createCanvas } from 'pmx-canvas';
15
15
  const canvas = createCanvas({ port: 4313 });
16
16
  await canvas.start({ open: true });
17
17
 
18
- // Add nodes
18
+ // Add nodes — addNode returns the created node (with `.id`, geometry, and data)
19
19
  const n1 = canvas.addNode({ type: 'markdown', title: 'Plan', content: '# Step 1\nDo the thing.' });
20
20
  const n2 = canvas.addNode({ type: 'status', title: 'Build', content: 'passing' });
21
21
  const n3 = canvas.addNode({ type: 'file', content: 'src/index.ts' });
22
22
 
23
- // Connect them
24
- canvas.addEdge({ from: n1, to: n2, type: 'flow' });
23
+ // Connect them (edges and groups reference node ids)
24
+ canvas.addEdge({ from: n1.id, to: n2.id, type: 'flow' });
25
25
 
26
26
  // Group related nodes
27
- canvas.createGroup({ title: 'Build Pipeline', childIds: [n1, n2] });
27
+ canvas.createGroup({ title: 'Build Pipeline', childIds: [n1.id, n3.id] });
28
28
 
29
29
  // Self-contained HTML in a sandboxed iframe
30
30
  canvas.addHtmlNode({
@@ -85,9 +85,27 @@ console.log(canvas.validate());
85
85
  console.log(canvas.getLayout());
86
86
 
87
87
  // AX context for host adapters
88
- canvas.setAxFocus({ nodeIds: [n1], source: 'sdk' });
88
+ canvas.setAxFocus([n1], { source: 'sdk' });
89
89
  console.log(canvas.getAxState());
90
90
  console.log(canvas.getAxContext());
91
+
92
+ // AX primitives — host-agnostic agent-experience layer
93
+ // Timeline (persisted for diagnostics, retention-bounded, not snapshotted)
94
+ canvas.recordAxEvent({ kind: 'tool-start', summary: 'ran tests' }, { source: 'sdk' });
95
+ canvas.addEvidence({ kind: 'test-output', title: 'unit pass' }, { source: 'sdk' });
96
+ canvas.sendSteering('focus on the failing test first', { source: 'sdk' });
97
+ console.log(canvas.getAxTimeline({ limit: 50 }));
98
+
99
+ // Canvas-bound (rides snapshots + restore, cleared by canvas.clear())
100
+ const work = canvas.addWorkItem({ title: 'Wire up auth', status: 'in-progress', nodeIds: [n1] }, { source: 'sdk' });
101
+ canvas.updateWorkItem(work.id, { status: 'done' });
102
+ const gate = canvas.requestApproval({ title: 'Deploy to prod', action: 'deploy.prod' }, { source: 'sdk' });
103
+ canvas.resolveApproval(gate.id, 'approved', { source: 'sdk' });
104
+ canvas.addReviewAnnotation({ body: 'off-by-one', kind: 'finding', severity: 'error', anchorType: 'file', file: 'src/x.ts' }, { source: 'sdk' });
105
+
106
+ // Host/session capability (own table, survives clear)
107
+ canvas.reportHostCapability({ host: 'copilot', canvas: true, sessionMessaging: true }, { source: 'sdk' });
108
+ console.log(canvas.getHostCapability());
91
109
  ```
92
110
 
93
111
  ## WebView automation
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmx-canvas",
3
- "version": "0.1.26",
3
+ "version": "0.1.28",
4
4
  "description": "Spatial canvas workbench for coding agents — infinite 2D canvas with agent-native CLI, MCP integration, nodes, edges, file watching, and snapshots",
5
5
  "type": "module",
6
6
  "main": "./src/server/index.ts",
@@ -56,18 +56,21 @@
56
56
  },
57
57
  "dependencies": {
58
58
  "@joplin/turndown-plugin-gfm": "^1.0.64",
59
- "@json-render/core": "^0.14.1",
60
- "@json-render/mcp": "^0.14.1",
61
- "@json-render/react": "^0.14.1",
62
- "@json-render/shadcn": "^0.14.1",
59
+ "@json-render/core": "0.19.0",
60
+ "@json-render/devtools": "0.19.0",
61
+ "@json-render/devtools-react": "0.19.0",
62
+ "@json-render/directives": "0.19.0",
63
+ "@json-render/mcp": "0.19.0",
64
+ "@json-render/react": "0.19.0",
65
+ "@json-render/shadcn": "0.19.0",
63
66
  "@modelcontextprotocol/ext-apps": "^1.3.1",
64
67
  "@modelcontextprotocol/sdk": "^1.0.0",
65
68
  "@preact/signals": "^2.0.0",
66
69
  "@types/turndown": "^5.0.6",
67
70
  "marked": "^15.0.0",
68
71
  "preact": "^10.25.0",
69
- "react": "^19.2.0",
70
- "react-dom": "^19.2.0",
72
+ "react": "^19.2.3",
73
+ "react-dom": "^19.2.3",
71
74
  "recharts": "^3.2.1",
72
75
  "turndown": "^7.2.4",
73
76
  "zod": "^4.3.6"