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
@@ -388,6 +388,16 @@ async function startPanelServer(instanceId, ctx, pmx) {
388
388
  return;
389
389
  }
390
390
  await copilotSession?.send({ prompt: body.prompt });
391
+ // Explicit user instruction from the panel → mirror onto the AX
392
+ // timeline as a steering message (fire-and-forget).
393
+ if (entry.pmx?.baseUrl) {
394
+ void fetchJson(entry.pmx.baseUrl, "/api/canvas/ax/steer", {
395
+ method: "POST",
396
+ headers: { "Content-Type": "application/json" },
397
+ body: JSON.stringify({ message: body.prompt, source: "copilot" }),
398
+ timeoutMs: 2_000,
399
+ }).catch(() => {});
400
+ }
391
401
  jsonResponse(res, 200, { ok: true });
392
402
  return;
393
403
  }
@@ -434,6 +444,39 @@ async function setAxFocus(baseUrl, workspaceRoot, input = {}, nodeIds = []) {
434
444
  });
435
445
  }
436
446
 
447
+ // POST a host-agnostic AX record to the canvas with the copilot source label.
448
+ // Resolves the server first so the action returns an actionable error when the
449
+ // canvas is unavailable, then maps the neutral primitive over plain HTTP.
450
+ async function postAxRecord(ctx, path, payload) {
451
+ const resolved = await resolvePmxServer(ctx, { autoStart: false });
452
+ if (!resolved.ok || !resolved.baseUrl) {
453
+ return { ok: false, error: resolved.error ?? "PMX Canvas server is unavailable." };
454
+ }
455
+ try {
456
+ return await fetchJson(resolved.baseUrl, path, {
457
+ method: "POST",
458
+ headers: { "Content-Type": "application/json" },
459
+ body: JSON.stringify({ ...payload, source: "copilot" }),
460
+ timeoutMs: 2_000,
461
+ });
462
+ } catch (error) {
463
+ return { ok: false, error: error instanceof Error ? error.message : String(error) };
464
+ }
465
+ }
466
+
467
+ // Fire-and-forget mirror of a copilot-originated action onto the AX timeline.
468
+ // Never throws — adapter UX must not depend on the canvas being reachable.
469
+ function recordCopilotSteering(ctx, message) {
470
+ void postAxRecord(ctx, "/api/canvas/ax/steer", { message }).catch(() => {});
471
+ }
472
+
473
+ async function getAxTimeline(ctx, limit) {
474
+ const resolved = await resolvePmxServer(ctx, { autoStart: false });
475
+ if (!resolved.ok || !resolved.baseUrl) return { ok: false, error: resolved.error };
476
+ const query = typeof limit === "number" && limit > 0 ? `?limit=${limit}` : "";
477
+ return await fetchJson(resolved.baseUrl, `/api/canvas/ax/timeline${query}`, { timeoutMs: 2_000 });
478
+ }
479
+
437
480
  function hasUsefulAxContext(context) {
438
481
  return Boolean(context?.pinned?.count > 0 || context?.focus?.nodeIds?.length > 0);
439
482
  }
@@ -513,9 +556,157 @@ const pmxCanvas = createCanvas({
513
556
  const prompt = typeof ctx.input?.prompt === "string" ? ctx.input.prompt.trim() : "";
514
557
  if (!prompt) throw new CanvasError("prompt_required", "prompt is required");
515
558
  await copilotSession?.send({ prompt });
559
+ // Mirror the explicit user instruction onto the AX timeline as a
560
+ // steering message (fire-and-forget). This is an explicit action
561
+ // flow, never a sync from the prompt hook.
562
+ recordCopilotSteering(ctx, prompt);
516
563
  return { ok: true };
517
564
  },
518
565
  },
566
+ {
567
+ name: "add_work_item",
568
+ description: "Add a canvas-bound PMX AX work item (visible task/plan/status) from Copilot.",
569
+ inputSchema: {
570
+ type: "object",
571
+ properties: {
572
+ title: { type: "string" },
573
+ status: { type: "string", enum: ["todo", "in-progress", "blocked", "done", "cancelled"] },
574
+ detail: { type: "string" },
575
+ nodeIds: { type: "array", items: { type: "string" } },
576
+ },
577
+ required: ["title"],
578
+ additionalProperties: false,
579
+ },
580
+ handler: async (ctx) => await postAxRecord(ctx, "/api/canvas/ax/work", {
581
+ title: ctx.input?.title,
582
+ ...(ctx.input?.status ? { status: ctx.input.status } : {}),
583
+ ...(ctx.input?.detail ? { detail: ctx.input.detail } : {}),
584
+ ...(Array.isArray(ctx.input?.nodeIds) ? { nodeIds: ctx.input.nodeIds } : {}),
585
+ }),
586
+ },
587
+ {
588
+ name: "request_approval",
589
+ description: "Request human approval before a high-impact action via a PMX AX approval gate.",
590
+ inputSchema: {
591
+ type: "object",
592
+ properties: {
593
+ title: { type: "string" },
594
+ detail: { type: "string" },
595
+ action: { type: "string" },
596
+ nodeIds: { type: "array", items: { type: "string" } },
597
+ },
598
+ required: ["title"],
599
+ additionalProperties: false,
600
+ },
601
+ handler: async (ctx) => await postAxRecord(ctx, "/api/canvas/ax/approval", {
602
+ title: ctx.input?.title,
603
+ ...(ctx.input?.detail ? { detail: ctx.input.detail } : {}),
604
+ ...(ctx.input?.action ? { action: ctx.input.action } : {}),
605
+ ...(Array.isArray(ctx.input?.nodeIds) ? { nodeIds: ctx.input.nodeIds } : {}),
606
+ }),
607
+ },
608
+ {
609
+ name: "resolve_approval",
610
+ description: "Resolve a pending PMX AX approval gate (approved or rejected).",
611
+ inputSchema: {
612
+ type: "object",
613
+ properties: {
614
+ id: { type: "string" },
615
+ decision: { type: "string", enum: ["approved", "rejected"] },
616
+ resolution: { type: "string" },
617
+ },
618
+ required: ["id", "decision"],
619
+ additionalProperties: false,
620
+ },
621
+ handler: async (ctx) => await postAxRecord(
622
+ ctx,
623
+ `/api/canvas/ax/approval/${encodeURIComponent(String(ctx.input?.id ?? ""))}/resolve`,
624
+ {
625
+ decision: ctx.input?.decision,
626
+ ...(ctx.input?.resolution ? { resolution: ctx.input.resolution } : {}),
627
+ },
628
+ ),
629
+ },
630
+ {
631
+ name: "add_review_annotation",
632
+ description: "Add a canvas-bound PMX AX review annotation (comment/finding) anchored to a node, file, or region.",
633
+ inputSchema: {
634
+ type: "object",
635
+ properties: {
636
+ body: { type: "string" },
637
+ kind: { type: "string", enum: ["comment", "finding"] },
638
+ severity: { type: "string", enum: ["info", "warning", "error"] },
639
+ anchorType: { type: "string", enum: ["node", "file", "region"] },
640
+ nodeId: { type: "string" },
641
+ file: { type: "string" },
642
+ },
643
+ required: ["body"],
644
+ additionalProperties: false,
645
+ },
646
+ handler: async (ctx) => await postAxRecord(ctx, "/api/canvas/ax/review", {
647
+ body: ctx.input?.body,
648
+ ...(ctx.input?.kind ? { kind: ctx.input.kind } : {}),
649
+ ...(ctx.input?.severity ? { severity: ctx.input.severity } : {}),
650
+ ...(ctx.input?.anchorType ? { anchorType: ctx.input.anchorType } : {}),
651
+ ...(ctx.input?.nodeId ? { nodeId: ctx.input.nodeId } : {}),
652
+ ...(ctx.input?.file ? { file: ctx.input.file } : {}),
653
+ }),
654
+ },
655
+ {
656
+ name: "get_timeline",
657
+ description: "Read the bounded PMX AX timeline (events, evidence, steering) for diagnostics and continuity.",
658
+ inputSchema: {
659
+ type: "object",
660
+ properties: {
661
+ limit: { type: "integer", minimum: 1, maximum: 200 },
662
+ },
663
+ additionalProperties: false,
664
+ },
665
+ handler: async (ctx) => await getAxTimeline(ctx, ctx.input?.limit),
666
+ },
667
+ {
668
+ name: "report_capability",
669
+ description: "Report this Copilot host/session capability to the canvas for AX diagnostics.",
670
+ inputSchema: {
671
+ type: "object",
672
+ properties: {
673
+ canvas: { type: "boolean" },
674
+ hooks: { type: "boolean" },
675
+ tools: { type: "boolean" },
676
+ sessionMessaging: { type: "boolean" },
677
+ permissions: { type: "boolean" },
678
+ files: { type: "boolean" },
679
+ uiPrompts: { type: "boolean" },
680
+ },
681
+ additionalProperties: false,
682
+ },
683
+ handler: async (ctx) => {
684
+ const resolved = await resolvePmxServer(ctx, { autoStart: false });
685
+ if (!resolved.ok || !resolved.baseUrl) {
686
+ return { ok: false, error: resolved.error ?? "PMX Canvas server is unavailable." };
687
+ }
688
+ try {
689
+ return await fetchJson(resolved.baseUrl, "/api/canvas/ax/host-capability", {
690
+ method: "PUT",
691
+ headers: { "Content-Type": "application/json" },
692
+ body: JSON.stringify({
693
+ host: "copilot",
694
+ canvas: ctx.input?.canvas === true,
695
+ hooks: ctx.input?.hooks === true,
696
+ tools: ctx.input?.tools === true,
697
+ sessionMessaging: ctx.input?.sessionMessaging === true,
698
+ permissions: ctx.input?.permissions === true,
699
+ files: ctx.input?.files === true,
700
+ uiPrompts: ctx.input?.uiPrompts === true,
701
+ source: "copilot",
702
+ }),
703
+ timeoutMs: 2_000,
704
+ });
705
+ } catch (error) {
706
+ return { ok: false, error: error instanceof Error ? error.message : String(error) };
707
+ }
708
+ },
709
+ },
519
710
  ],
520
711
  open: async (ctx) => {
521
712
  let pmx;
package/CHANGELOG.md CHANGED
@@ -3,6 +3,114 @@
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.28] - 2026-06-06
7
+
8
+ ### Changed
9
+
10
+ - **BREAKING (SDK): `PmxCanvas.addNode()` now returns the created node**
11
+ (a `CanvasNodeState` with `id`, geometry, and data) instead of a bare
12
+ id string. Use `const { id } = canvas.addNode(...)` or keep the whole
13
+ node. The MCP / HTTP / CLI surfaces and the internal `CanvasAccess`
14
+ contract are unchanged (still id-based).
15
+
16
+ ### Fixed
17
+
18
+ - Tufte graph nodes (DotPlot/Bullet/Slopegraph) no longer flicker: the
19
+ chart-height ResizeObserver no longer feeds the document's own scroll
20
+ overflow back into its height.
21
+ - Expanded Tufte graph views now fill the modal instead of staying
22
+ tile-sized with whitespace.
23
+ - Graph node iframes no longer show a doubled scrollbar; a chart that
24
+ fits shows none, a dense chart shows exactly one.
25
+ - Excalidraw / ext-app nodes paint on first mount instead of rendering
26
+ black until a manual expand/collapse (a post-layout host-context
27
+ nudge is delivered once the iframe has settled).
28
+ - `image` and `ledger` nodes get roomier default sizes (480×360 /
29
+ 420×280) instead of a cramped 360-wide frame.
30
+ - `pmx-canvas web-artifact build` streams a stderr heartbeat during the
31
+ long install+bundle so agents don't see it as hung.
32
+ - json-render rejects an unknown `$`-directive (e.g. `$path`) with a
33
+ clear error pointing at `$state`, instead of rendering
34
+ `"[object Object]"`.
35
+ - Web-artifact script-path overrides are contained to the workspace with
36
+ a symlink-aware (realpath) check, fixing a false rejection on macOS.
37
+ - **Security:** the `/api/canvas/image/<id>` route now refuses to serve
38
+ files outside the workspace root (returns 403), closing a path-
39
+ traversal read (e.g. an image node pointed at `../../etc/passwd`).
40
+
41
+ ## [0.1.27] - 2026-06-06
42
+
43
+ Big release: the full host-agnostic agent-experience (AX) primitive
44
+ contract and a json-render 0.19 upgrade with Tufte charts, directives,
45
+ and live SpecStream rendering. The MCP canvas now exposes 56 tools (was
46
+ 45) and four AX resources. Plus the native GitHub Copilot and Codex
47
+ canvas adapters are featured in the README.
48
+
49
+ ### Added
50
+
51
+ - **Full AX primitive contract (plan-004 Phases 2-5).** Eleven new MCP
52
+ tools, each with complete parity across CanvasStateManager, SQLite
53
+ persistence, the Bun SDK, HTTP, the MCP server, the RemoteCanvasAccess
54
+ proxy, and the CLI, organized into three lifecycle partitions:
55
+ - **Canvas-bound** (participate in snapshots + restore, cleared by
56
+ `canvas_clear`, read via `canvas_get_ax` / `canvas://ax-work`):
57
+ `canvas_add_work_item`, `canvas_update_work_item`,
58
+ `canvas_request_approval`, `canvas_resolve_approval` (state machine
59
+ `pending → approved/rejected`, double-resolve rejected),
60
+ `canvas_add_review_annotation`.
61
+ - **Timeline** (persist in dedicated DB tables bounded by 500-row
62
+ retention, NOT restored by snapshots, NOT cleared by
63
+ `canvas_clear`, read via `canvas_get_ax_timeline` /
64
+ `canvas://ax-timeline`): `canvas_add_evidence`,
65
+ `canvas_record_ax_event`, `canvas_send_steering`.
66
+ - **Host/session** (survives `canvas_clear`):
67
+ `canvas_report_host_capability`.
68
+ New resources: `canvas://ax-work` and `canvas://ax-timeline` (joining
69
+ `canvas://ax` and `canvas://ax-context`). CLI gains `pmx-canvas ax`
70
+ subcommands.
71
+ - **`canvas_stream_json_render_node` (SpecStream).** Progressively build
72
+ a json-render node by streaming JSON-Patch operations; the server
73
+ accumulates the spec (it is the source of truth) and the live node
74
+ re-renders as patches arrive. Omit `nodeId` to create, pass it back to
75
+ append, set `done:true` to finish.
76
+ - **Tufte chart types.** json-render graph nodes gain `Sparkline`,
77
+ `DotPlot`, `BulletChart`, and `Slopegraph`, wired through the full
78
+ definitions → catalog → component-registry pipeline alongside the
79
+ existing chart types.
80
+ - **json-render 0.19.0 upgrade + directives + devtools.** All
81
+ `@json-render/*` packages move to 0.19.0. Specs can use the standard
82
+ stateless directives (`$format`, `$math`, `$concat`, `$count`,
83
+ `$truncate`, `$pluralize`, `$join`) for server-side derivations; an
84
+ opt-in devtools panel (double-gated behind an env flag + `?devtools=1`)
85
+ is available for debugging.
86
+ - **Native GitHub Copilot and Codex canvas adapters featured in the
87
+ README**, with the `.github/extensions/pmx-canvas` Copilot extension
88
+ and the Codex/Copilot app-adapter skill references.
89
+
90
+ ### Fixed
91
+
92
+ - **SpecStream rejects prototype-pollution patch paths.** Streamed
93
+ JSON-Patch paths whose JSON-Pointer contains a `__proto__`,
94
+ `constructor`, or `prototype` segment are now skipped (and counted)
95
+ rather than applied, closing a server-side prototype-pollution vector
96
+ in the new streaming path.
97
+ - **`canvas_add_review_annotation` no longer reports false success for
98
+ invalid node anchors.** A node-anchored review annotation whose
99
+ `nodeId` is missing or not on the canvas was silently dropped by
100
+ normalization yet returned as a populated success object. It now
101
+ rejects up front (`ok:false` / HTTP 400 / MCP error) across every
102
+ layer, so agents can't lose review findings to a typo'd node id.
103
+
104
+ ### Internal
105
+
106
+ - Regression coverage added for: SpecStream patch application (happy
107
+ path, malformed-item skipping, and the prototype-pollution guard with
108
+ an explicit `Object.prototype` cleanliness assertion), the Tufte
109
+ Sparkline/Slopegraph spec builders, and node-anchor validation for
110
+ review annotations (missing / unknown / file / valid). Docs updated:
111
+ `docs/mcp.md` adds the `canvas_fit_view` row, and
112
+ `skills/pmx-canvas/SKILL.md` lists the four `canvas://ax*` resources.
113
+
6
114
  ## [0.1.26] - 2026-06-03
7
115
 
8
116
  Small follow-up to 0.1.25. `canvas_add_node` can now create populated
@@ -1276,6 +1384,8 @@ otherwise have to discover by trial and error.
1276
1384
  - Regression coverage for snapshot flat-`id` aliases on both MCP and
1277
1385
  HTTP surfaces, plus async / top-level-`await` WebView script bodies.
1278
1386
 
1387
+ [0.1.28]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.28
1388
+ [0.1.27]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.27
1279
1389
  [0.1.26]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.26
1280
1390
  [0.1.25]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.25
1281
1391
  [0.1.24]: https://github.com/pskoett/pmx-canvas/releases/tag/v0.1.24
package/Readme.md CHANGED
@@ -3,9 +3,10 @@
3
3
  **A moldable canvas for agent-assisted thinking.** An infinite 2D surface
4
4
  where files, plans, status, charts, fetched web pages, annotations, and
5
5
  hand-drawn diagrams live side by side. Every node carries its own renderer; agents
6
- (and you) build new views in the middle of a session, not as a separate
7
- tooling project. Pin what matters and the agent reads your spatial
8
- curation as structured context.
6
+ (and you) build new views in the middle of a session even streaming a
7
+ structured panel into place as they generate it not as a separate tooling
8
+ project. Pin what matters and the agent reads your spatial curation as
9
+ structured context.
9
10
 
10
11
  <p align="center">
11
12
  <img src="docs/screenshots/welcome-dark.png" alt="Empty canvas — dark theme" width="49%" />
@@ -50,15 +51,28 @@ agents as compact spatial context: target, bounds, and nearby canvas content.
50
51
 
51
52
  ### 04 / Control your context
52
53
 
53
- Pinning is an explicit, low-noise control over what the agent sees next. No
54
- prompt engineering, no copy-paste — pin a node in the browser and the MCP
55
- server fires a `notifications/resources/updated` event the agent's harness
56
- picks up immediately.
57
-
58
- AX context adds a host-agnostic focus layer on top of pins. Core PMX exposes
59
- `/api/canvas/ax/context`, `canvas://ax-context`, SDK methods, and
60
- `pmx-canvas ax context|focus`; adapters can map that same primitive to their
61
- native hook systems without making PMX Canvas GitHub-specific.
54
+ Steer the agent and see its work, without prompt engineering or copy-paste.
55
+ Pin a node in the browser and the MCP server fires a
56
+ `notifications/resources/updated` event the agent's harness picks up
57
+ immediately an explicit, low-noise control over what the agent sees next.
58
+
59
+ On top of pins, a host-agnostic **AX (agent-experience) layer** turns the
60
+ canvas into a shared workspace between you and the agent:
61
+
62
+ - **Focus** promote nodes into the agent's active context without moving the viewport.
63
+ - **Work items & approval gates** — track visible tasks tied to nodes, and gate
64
+ high-impact actions behind a human `pending → approved/rejected` decision.
65
+ - **Steering messages & agent-event timeline** — send instructions to the
66
+ active session, and read a normalized, bounded timeline of prompts, tool
67
+ runs, evidence (logs/diffs/screenshots/test-output), and failures.
68
+ - **Host capability** — adapters report what the host can do, for diagnostics.
69
+
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.
62
76
 
63
77
  ### 05 / Save
64
78
 
@@ -71,27 +85,47 @@ the DB so SQLite WAL data is checkpointed into the file.
71
85
 
72
86
  ### 06 / Any agent
73
87
 
74
- Harness-agnostic. Drive the canvas from [MCP](docs/mcp.md) (45 tools,
75
- 9 resources, change notifications), the [CLI](docs/cli.md), the
88
+ Harness-agnostic. Drive the canvas from [MCP](docs/mcp.md) (56 tools,
89
+ 12 resources, change notifications), the [CLI](docs/cli.md), the
76
90
  [HTTP API](docs/http-api.md), or the [Bun SDK](docs/sdk.md). Works with
77
91
  Claude Code, GitHub Copilot CLI, Codex, Cursor, Windsurf, or any agent
78
92
  that can spawn an MCP stdio server, call a CLI, or hit an HTTP endpoint.
79
93
 
80
- The repo also ships a GitHub Copilot app adapter at
81
- `.github/extensions/pmx-canvas/`. It opens the live PMX workbench in a native
82
- Copilot canvas panel, injects AX pinned/focused context on prompt submission,
83
- and exposes adapter actions for status, AX focus, context refresh, and explicit
84
- session steering.
94
+ ### 07 / Native app adapters
85
95
 
86
- In the Codex app, PMX Canvas is MCP-first plus the Codex in-app Browser: agents
87
- read `canvas://ax-context` / `canvas_get_ax`, humans use the live `/workbench`
88
- view, and Codex-originated focus can be labeled with `source: "codex"` through
89
- `canvas_set_ax_focus`. The CLI remains a fallback for scripts and manual
90
- debugging, not the native Codex adapter path.
96
+ PMX Canvas doesn't just run in a browser tab it embeds **natively inside the
97
+ agent apps you already use**, as a thin adapter layer over the same neutral AX
98
+ surfaces (the core never imports a host SDK).
99
+
100
+ <p align="center">
101
+ <img src="docs/screenshots/github-copilot-app.png" alt="PMX Canvas as a native canvas panel in the GitHub Copilot app" width="100%" />
102
+ </p>
103
+ <p align="center"><sub>PMX Canvas as a native canvas panel in the GitHub Copilot app.</sub></p>
104
+
105
+ <p align="center">
106
+ <img src="docs/screenshots/codex-app.png" alt="PMX Canvas workbench in the Codex in-app Browser" width="100%" />
107
+ </p>
108
+ <p align="center"><sub>The live PMX workbench in the Codex in-app Browser.</sub></p>
109
+
110
+ - **GitHub Copilot app** — a committed project canvas extension
111
+ (`.github/extensions/pmx-canvas/`) opens the live PMX workbench in a native
112
+ Copilot panel, injects pinned/focused AX context on prompt submission, and
113
+ exposes actions for focus, session steering, work items, approval gates,
114
+ review annotations, the AX timeline, and host-capability reporting. Install it
115
+ into any repo with `pmx-canvas copilot install-extension` (`--dry-run` to
116
+ preview).
117
+ - **Codex app** — native through the Codex in-app Browser (opened to
118
+ `/workbench`) plus the PMX MCP server: agents read `canvas://ax-context` /
119
+ `canvas_get_ax` and label Codex-originated focus with `source: "codex"`. No
120
+ extension API needed — Codex's two native surfaces (MCP + in-app Browser) are
121
+ exactly what the canvas requires.
122
+
123
+ The contract is host-agnostic, so a new host plugs in the same way: map its
124
+ hooks, canvas, and session APIs onto PMX's AX primitives — no core changes.
91
125
 
92
126
  ## Prerequisites
93
127
 
94
- - [Bun](https://bun.sh) >= 1.3.12
128
+ - [Bun](https://bun.sh) >= 1.3.14
95
129
 
96
130
  The published SDK entrypoint is Bun-first. Node.js consumers should use the
97
131
  CLI, MCP server, or HTTP API.
@@ -155,7 +189,20 @@ When loaded by the Copilot app, it opens the PMX workbench natively, starts a
155
189
  matching local PMX server when needed, and injects `AX` pinned/focused context
156
190
  as hidden per-turn context. The adapter is thin: PMX state still lives in
157
191
  `.pmx-canvas/canvas.db`, and the same HTTP, MCP, CLI, and SDK surfaces remain
158
- available to non-GitHub agents.
192
+ available to non-GitHub agents. See
193
+ [`github-copilot-app-adapter.md`](skills/pmx-canvas/references/github-copilot-app-adapter.md)
194
+ for the full setup and live-test checklist.
195
+
196
+ ### Use inside the Codex app
197
+
198
+ Codex needs no extension — its two native surfaces are exactly what the canvas
199
+ requires. Add the PMX MCP server to the Codex workspace config (same snippet as
200
+ [Connect your agent](#connect-your-agent-mcp)), then open the returned
201
+ `/workbench` URL in the **Codex in-app Browser** so you can see mutations live.
202
+ Agents read pinned/focused context from `canvas://ax-context` / `canvas_get_ax`
203
+ and label Codex-originated focus with `source: "codex"`. See
204
+ [`codex-app-adapter.md`](skills/pmx-canvas/references/codex-app-adapter.md) for
205
+ the full workflow and live-test checklist.
159
206
 
160
207
  ### Install the agent skill (recommended)
161
208
 
@@ -188,7 +235,7 @@ the agent can read `canvas://skills` and pull in companion skills
188
235
  the three-tier visual matrix (json-render → html → web-artifact)
189
236
  - **[CLI reference](docs/cli.md)** — full command surface, daemon mode,
190
237
  watch streams, WebView automation
191
- - **[MCP reference](docs/mcp.md)** — 45 tools, 9 resources, change
238
+ - **[MCP reference](docs/mcp.md)** — 56 tools, 12 resources, change
192
239
  notifications, node-type routing
193
240
  - **[HTTP API](docs/http-api.md)** — REST endpoints, SSE, batch operations
194
241
  - **[Bun SDK](docs/sdk.md)** — `createCanvas()` for TypeScript on Bun