pmx-canvas 0.6.5 → 0.7.1

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 (181) hide show
  1. package/.github/extensions/pmx-canvas/extension.mjs +139 -14
  2. package/CHANGELOG.md +70 -0
  3. package/Readme.md +32 -6
  4. package/dist/canvas/global.css +510 -31
  5. package/dist/canvas/index.js +92 -77
  6. package/dist/types/cli/agent.d.ts +3 -0
  7. package/dist/types/cli/commands/backup.d.ts +1 -0
  8. package/dist/types/cli/commands/boards.d.ts +1 -0
  9. package/dist/types/cli/commands/export.d.ts +1 -0
  10. package/dist/types/cli/daemon.d.ts +5 -5
  11. package/dist/types/cli/shared.d.ts +3 -1
  12. package/dist/types/client/canvas/BoardCopyDialog.d.ts +5 -0
  13. package/dist/types/client/canvas/BoardFolderDialog.d.ts +7 -0
  14. package/dist/types/client/canvas/BoardLinkDialog.d.ts +3 -0
  15. package/dist/types/client/canvas/BoardSwitcher.d.ts +8 -0
  16. package/dist/types/client/canvas/ExportDialog.d.ts +7 -0
  17. package/dist/types/client/canvas/HomeView.d.ts +9 -0
  18. package/dist/types/client/nodes/AttachmentNode.d.ts +6 -0
  19. package/dist/types/client/nodes/BoardNode.d.ts +5 -0
  20. package/dist/types/client/state/boards-store.d.ts +79 -0
  21. package/dist/types/client/state/canvas-store.d.ts +2 -0
  22. package/dist/types/client/state/intent-bridge.d.ts +1 -0
  23. package/dist/types/client/state/session-store.d.ts +7 -0
  24. package/dist/types/client/types.d.ts +2 -2
  25. package/dist/types/json-render/catalog.d.ts +1 -1
  26. package/dist/types/json-render/charts/definitions.d.ts +1 -1
  27. package/dist/types/json-render/server.d.ts +6 -0
  28. package/dist/types/mcp/canvas-access.d.ts +10 -3
  29. package/dist/types/mcp/context-uri-template.d.ts +6 -0
  30. package/dist/types/server/agent-presence.d.ts +1 -1
  31. package/dist/types/server/attribution.d.ts +11 -0
  32. package/dist/types/server/ax-context.d.ts +3 -1
  33. package/dist/types/server/ax-interaction.d.ts +2 -2
  34. package/dist/types/server/ax-state.d.ts +13 -3
  35. package/dist/types/server/backup.d.ts +36 -0
  36. package/dist/types/server/board-export.d.ts +45 -0
  37. package/dist/types/server/canvas-db.d.ts +63 -9
  38. package/dist/types/server/canvas-operations.d.ts +23 -0
  39. package/dist/types/server/canvas-provenance.d.ts +1 -1
  40. package/dist/types/server/canvas-state.d.ts +140 -3
  41. package/dist/types/server/context-brief.d.ts +111 -0
  42. package/dist/types/server/context-reads.d.ts +55 -0
  43. package/dist/types/server/document-import.d.ts +66 -0
  44. package/dist/types/server/index.d.ts +103 -4
  45. package/dist/types/server/mutation-history.d.ts +1 -0
  46. package/dist/types/server/operations/invoker.d.ts +2 -0
  47. package/dist/types/server/operations/mcp.d.ts +7 -1
  48. package/dist/types/server/operations/ops/ax-shared.d.ts +2 -2
  49. package/dist/types/server/operations/ops/backup.d.ts +2 -0
  50. package/dist/types/server/operations/ops/boards.d.ts +10 -0
  51. package/dist/types/server/operations/ops/export.d.ts +2 -0
  52. package/dist/types/server/operations/ops/imports.d.ts +2 -0
  53. package/dist/types/server/operations/ops/nodes.d.ts +1 -1
  54. package/dist/types/server/operations/ops/query.d.ts +2 -0
  55. package/dist/types/server/operations/registry.d.ts +9 -1
  56. package/dist/types/server/operations/types.d.ts +1 -1
  57. package/dist/types/server/server.d.ts +1 -0
  58. package/dist/types/server/trace-manager.d.ts +7 -0
  59. package/dist/types/server/webpage-node.d.ts +22 -1
  60. package/dist/types/server/workbench-auth.d.ts +3 -0
  61. package/dist/types/shared/boards.d.ts +6 -0
  62. package/docs/RELEASE.md +5 -0
  63. package/docs/cli.md +73 -0
  64. package/docs/http-api.md +198 -2
  65. package/docs/mcp.md +71 -6
  66. package/docs/node-types.md +26 -0
  67. package/docs/plans/plan-010-attributed-ask-answers.md +39 -0
  68. package/docs/plans/plan-011-context-read-instrumentation.md +54 -0
  69. package/docs/plans/plan-012-many-boards-stage-1.md +98 -0
  70. package/docs/plans/plan-013-static-board-export.md +47 -0
  71. package/docs/plans/plan-014-connected-board-memory.md +140 -0
  72. package/docs/product-vision-2026-09.md +118 -18
  73. package/docs/sdk.md +50 -0
  74. package/package.json +1 -1
  75. package/skills/pmx-canvas/SKILL.md +68 -11
  76. package/skills/pmx-canvas/evals/evals.json +130 -0
  77. package/skills/pmx-canvas/references/full-reference.md +43 -7
  78. package/skills/pmx-canvas/references/github-copilot-app-adapter.md +3 -0
  79. package/skills/pmx-canvas/references/installing-pmx-canvas.md +4 -4
  80. package/skills/pmx-canvas/references/tours-and-recording.md +69 -0
  81. package/skills/pmx-canvas-orchestration/SKILL.md +5 -0
  82. package/skills/pmx-canvas-testing/SKILL.md +11 -2
  83. package/src/cli/agent.ts +4 -0
  84. package/src/cli/commands/apps.ts +1 -1
  85. package/src/cli/commands/ax.ts +13 -0
  86. package/src/cli/commands/backup.ts +66 -0
  87. package/src/cli/commands/boards.ts +71 -0
  88. package/src/cli/commands/export.ts +37 -0
  89. package/src/cli/commands/nodes.ts +4 -1
  90. package/src/cli/commands/pins.ts +6 -2
  91. package/src/cli/commands/pump.ts +3 -0
  92. package/src/cli/commands/query.ts +33 -7
  93. package/src/cli/commands/view.ts +7 -5
  94. package/src/cli/daemon.ts +37 -7
  95. package/src/cli/index.ts +19 -9
  96. package/src/cli/shared.ts +11 -2
  97. package/src/client/App.tsx +21 -4
  98. package/src/client/canvas/AttentionToast.tsx +5 -3
  99. package/src/client/canvas/BoardCopyDialog.tsx +153 -0
  100. package/src/client/canvas/BoardFolderDialog.tsx +120 -0
  101. package/src/client/canvas/BoardLinkDialog.tsx +93 -0
  102. package/src/client/canvas/BoardSwitcher.tsx +149 -0
  103. package/src/client/canvas/CanvasNode.tsx +0 -1
  104. package/src/client/canvas/CanvasViewport.tsx +3 -0
  105. package/src/client/canvas/CommandPalette.tsx +107 -4
  106. package/src/client/canvas/ContextMenu.tsx +4 -1
  107. package/src/client/canvas/ExpandedNodeOverlay.tsx +3 -0
  108. package/src/client/canvas/ExportDialog.tsx +199 -0
  109. package/src/client/canvas/HomeView.tsx +277 -0
  110. package/src/client/canvas/Presentation.tsx +39 -9
  111. package/src/client/canvas/SelectionBar.tsx +0 -2
  112. package/src/client/canvas/SessionPanel.tsx +28 -1
  113. package/src/client/canvas/ToolRail.tsx +38 -3
  114. package/src/client/canvas/TopBar.tsx +34 -18
  115. package/src/client/canvas/import-files.ts +19 -1
  116. package/src/client/canvas/kind-colors.ts +1 -0
  117. package/src/client/icons.tsx +2 -0
  118. package/src/client/nodes/AttachmentNode.tsx +145 -0
  119. package/src/client/nodes/BoardNode.tsx +35 -0
  120. package/src/client/nodes/FileNode.tsx +6 -0
  121. package/src/client/nodes/MarkdownNode.tsx +47 -19
  122. package/src/client/nodes/ax-node-actions.ts +2 -5
  123. package/src/client/nodes/use-ax-surface-bridge.ts +1 -1
  124. package/src/client/state/attention-bridge.ts +3 -1
  125. package/src/client/state/boards-store.ts +167 -0
  126. package/src/client/state/canvas-store.ts +22 -0
  127. package/src/client/state/intent-bridge.ts +30 -2
  128. package/src/client/state/node-factory.ts +1 -0
  129. package/src/client/state/session-store.ts +5 -0
  130. package/src/client/state/sse-bridge.ts +52 -6
  131. package/src/client/theme/global.css +510 -31
  132. package/src/client/types.ts +2 -0
  133. package/src/json-render/server.ts +16 -9
  134. package/src/mcp/canvas-access.ts +59 -22
  135. package/src/mcp/context-uri-template.ts +28 -0
  136. package/src/mcp/server.ts +122 -15
  137. package/src/server/agent-context.ts +116 -29
  138. package/src/server/attribution.ts +29 -0
  139. package/src/server/ax-context.ts +4 -2
  140. package/src/server/ax-interaction.ts +1 -0
  141. package/src/server/ax-state-manager.ts +72 -3
  142. package/src/server/ax-state.ts +41 -0
  143. package/src/server/backup.ts +178 -0
  144. package/src/server/board-export.ts +686 -0
  145. package/src/server/canvas-db.ts +599 -107
  146. package/src/server/canvas-operations.ts +68 -9
  147. package/src/server/canvas-provenance.ts +1 -0
  148. package/src/server/canvas-schema.ts +12 -0
  149. package/src/server/canvas-state.ts +774 -45
  150. package/src/server/context-brief.ts +372 -0
  151. package/src/server/context-reads.ts +238 -0
  152. package/src/server/demo-state.json +659 -2
  153. package/src/server/document-import.ts +223 -0
  154. package/src/server/index.ts +231 -5
  155. package/src/server/mutation-history.ts +10 -13
  156. package/src/server/operations/composites.ts +26 -4
  157. package/src/server/operations/http.ts +9 -3
  158. package/src/server/operations/index.ts +8 -0
  159. package/src/server/operations/invoker.ts +14 -3
  160. package/src/server/operations/mcp.ts +50 -11
  161. package/src/server/operations/ops/app.ts +5 -0
  162. package/src/server/operations/ops/ax-read.ts +10 -6
  163. package/src/server/operations/ops/ax-timeline.ts +75 -0
  164. package/src/server/operations/ops/backup.ts +134 -0
  165. package/src/server/operations/ops/batch.ts +8 -1
  166. package/src/server/operations/ops/boards.ts +351 -0
  167. package/src/server/operations/ops/export.ts +74 -0
  168. package/src/server/operations/ops/ext-app.ts +4 -0
  169. package/src/server/operations/ops/imports.ts +236 -0
  170. package/src/server/operations/ops/nodes.ts +57 -5
  171. package/src/server/operations/ops/query.ts +206 -9
  172. package/src/server/operations/registry.ts +123 -31
  173. package/src/server/operations/types.ts +1 -1
  174. package/src/server/server.ts +146 -2
  175. package/src/server/spatial-analysis.ts +9 -17
  176. package/src/server/trace-manager.ts +19 -0
  177. package/src/server/web-artifacts.ts +2 -1
  178. package/src/server/webpage-node.ts +173 -13
  179. package/src/server/workbench-auth.ts +11 -0
  180. package/src/shared/boards.ts +18 -0
  181. package/src/shared/node-sizes.ts +1 -0
@@ -16,14 +16,14 @@ export declare const AX_SOURCES: readonly ["agent", "amp", "api", "browser", "cl
16
16
  export declare const AX_SOURCE_SHAPE: z.ZodOptional<z.ZodEnum<{
17
17
  amp: "amp";
18
18
  agent: "agent";
19
+ system: "system";
20
+ sdk: "sdk";
19
21
  api: "api";
20
22
  browser: "browser";
21
23
  cli: "cli";
22
24
  codex: "codex";
23
25
  copilot: "copilot";
24
26
  mcp: "mcp";
25
- sdk: "sdk";
26
- system: "system";
27
27
  }>>;
28
28
  /** Zod schema for the optional `agentId` field shared by AX MCP tool shapes. */
29
29
  export declare const AX_AGENT_ID_SHAPE: z.ZodOptional<z.ZodString>;
@@ -0,0 +1,2 @@
1
+ import { type Operation } from '../types.js';
2
+ export declare const backupOperations: Operation[];
@@ -0,0 +1,10 @@
1
+ import { type CanvasLayout } from '../../canvas-state.js';
2
+ import { type Operation } from '../types.js';
3
+ export declare function boardsPayload(): Record<string, unknown>;
4
+ /** Read a board without opening it. Omitted board preserves the active-board contract. */
5
+ export declare function readTargetBoard(boardId?: unknown, includeBlobs?: boolean): {
6
+ boardId: string | null;
7
+ layout: CanvasLayout;
8
+ pinnedNodeIds: Set<string>;
9
+ };
10
+ export declare const boardOperations: Operation[];
@@ -0,0 +1,2 @@
1
+ import { type Operation } from '../types.js';
2
+ export declare const exportOperations: Operation[];
@@ -0,0 +1,2 @@
1
+ import { type Operation } from '../types.js';
2
+ export declare const importOperations: Operation[];
@@ -1,7 +1,7 @@
1
1
  import { type CanvasLayout, type CanvasNodeState } from '../../canvas-state.js';
2
2
  import { type CanvasSizeAdjustment } from '../../canvas-operations.js';
3
3
  import { type Operation } from '../types.js';
4
- export declare const NODE_TYPES: readonly ["markdown", "status", "context", "ledger", "trace", "file", "diff", "mermaid", "image", "mcp-app", "webpage", "html", "group"];
4
+ export declare const NODE_TYPES: readonly ["markdown", "board", "status", "context", "ledger", "trace", "file", "diff", "mermaid", "image", "mcp-app", "webpage", "html", "group"];
5
5
  /** Per-type default node frame size (formerly copy-pasted ladders). */
6
6
  export declare function defaultNodeSize(type: string): {
7
7
  width: number;
@@ -1,2 +1,4 @@
1
1
  import { type Operation } from '../types.js';
2
+ export declare const DEFAULT_CONTEXT_BRIEF_BUDGET = 16000;
3
+ export declare const MAX_CONTEXT_BRIEF_BUDGET = 100000;
2
4
  export declare const queryOperations: Operation[];
@@ -9,7 +9,7 @@ export declare function setOperationEventEmitter(emitter: OperationEventEmitter
9
9
  * mcpapp.open, whose canvas node is created as a side-effect of `ext-app-open` —
10
10
  * use this to reject loudly instead of silently no-op'ing in a suppressed run. */
11
11
  export declare function isEmitSuppressed(): boolean;
12
- /** Run `fn` with all operation SSE emits suppressed; restores depth on finally. */
12
+ /** Suppress this call chain's per-entry frames; the batch emits its final frame afterward. */
13
13
  export declare function runWithSuppressedEmits<T>(fn: () => Promise<T>): Promise<T>;
14
14
  export interface ExecuteOperationMeta {
15
15
  /**
@@ -29,6 +29,14 @@ export interface ExecuteOperationMeta {
29
29
  * to agents only, and batch inner writes are always agent-originated.
30
30
  */
31
31
  fromWorkbench?: boolean;
32
+ /** Authorship only: true when the per-boot workbench token matched. */
33
+ humanAuthor?: boolean;
34
+ /**
35
+ * An MCP server attached to this daemon, or a host adapter, fetching on an
36
+ * agent's behalf. It records the read the agent actually made itself
37
+ * (POST /api/canvas/ax/context-reads), so this fetch is not recorded.
38
+ */
39
+ proxiedRead?: boolean;
32
40
  }
33
41
  export declare function executeOperation(name: string, rawInput: unknown, meta?: ExecuteOperationMeta): Promise<unknown>;
34
42
  export {};
@@ -45,7 +45,7 @@ export interface OperationHttpRoute {
45
45
  }
46
46
  /** Host capabilities available to MCP result formatters. */
47
47
  export interface OperationMcpToolHost {
48
- getPinnedNodeIds(): Promise<string[]>;
48
+ getPinnedNodeIds(board?: string): Promise<string[]>;
49
49
  /**
50
50
  * Invoke another registered operation over the host's transport (local or
51
51
  * HTTP) — structural subset of OperationInvoker to avoid an import cycle.
@@ -23,6 +23,7 @@
23
23
  * - POST /api/canvas/graph -> create a native graph node
24
24
  * - GET /api/canvas/json-render/view?nodeId=... -> local json-render viewer
25
25
  * - GET /api/canvas/file-bytes?nodeId=... -> raw bytes for a file node
26
+ * - GET /api/canvas/exports/<file> -> a written static board export (sandboxed)
26
27
  * - POST /api/canvas/web-artifact -> build bundled HTML artifact + optional canvas node
27
28
  * - GET /api/workbench/events -> SSE event stream
28
29
  * - GET /api/workbench/poll -> proxy-safe polling transport (same events, JSON)
@@ -37,6 +37,13 @@ declare class TraceManager {
37
37
  durationMs?: number;
38
38
  failed?: boolean;
39
39
  }): void;
40
+ /**
41
+ * The chain's last node is gone (another board was opened, or it was
42
+ * deleted): start a new chain instead of wiring edges to a missing node.
43
+ */
44
+ private dropStaleChain;
45
+ /** Forget the trace chain without touching nodes. */
46
+ resetChain(): void;
40
47
  clearTrace(): void;
41
48
  getTraceNodeCount(): number;
42
49
  private getOrigin;
@@ -17,9 +17,30 @@ export interface WebpageSnapshot {
17
17
  }
18
18
  export declare function normalizeWebpageUrl(rawUrl: string): string;
19
19
  export declare function summarizeWebpageContent(data: Record<string, unknown>, maxLength?: number): string;
20
- export declare function fetchWebpageSnapshot(inputUrl: string): Promise<WebpageSnapshot>;
20
+ export type AddressScope = 'public' | 'private' | 'link-local';
21
+ /**
22
+ * Where an IP literal points. `private` covers loopback, RFC 1918, CGNAT and
23
+ * IPv6 ULA; `link-local` is kept apart because it holds cloud metadata
24
+ * (169.254.169.254) and is refused on every hop.
25
+ */
26
+ export declare function classifyAddress(address: string): AddressScope;
27
+ type ResolveHost = (hostname: string) => Promise<Array<{
28
+ address: string;
29
+ family: number;
30
+ }>>;
31
+ /**
32
+ * Refuses link-local on every hop, and a redirect that moves a public fetch
33
+ * onto a loopback or private address. A URL the caller points at a local dev
34
+ * server on purpose still loads, and may redirect within the local network.
35
+ */
36
+ export declare function checkWebpageHop(originScope: AddressScope, hopScope: AddressScope, hopUrl: string): void;
37
+ export declare function fetchWebpageSnapshot(inputUrl: string, options?: {
38
+ resolveHost?: ResolveHost;
39
+ timeoutMs?: number;
40
+ }): Promise<WebpageSnapshot>;
21
41
  export declare function getWebpageFetchErrorDetails(error: unknown): {
22
42
  message: string;
23
43
  statusCode: number | null;
24
44
  contentType: string | null;
25
45
  };
46
+ export {};
@@ -0,0 +1,3 @@
1
+ /** Per-process capability used only to distinguish workbench clicks from HTTP agents. */
2
+ export declare const workbenchToken: string;
3
+ export declare function isWorkbenchToken(value: string | null): boolean;
@@ -0,0 +1,6 @@
1
+ /** The Home category migrated 0.6 snapshots land on, so they do not bury the boards in use. */
2
+ export declare const MIGRATED_SNAPSHOT_CATEGORY = "From old snapshots";
3
+ /** Slash-separated folder paths shared by the UI, HTTP/MCP and SDK. */
4
+ export declare function normalizeBoardCategory(value: string | null): string | null;
5
+ /** Includes implicit parents, so a board in A/B can also be moved into A. */
6
+ export declare function categoryAncestors(path: string): string[];
package/docs/RELEASE.md CHANGED
@@ -40,6 +40,11 @@ bun run pack:dry-run # confirms the tarball shape
40
40
  do not call `bun x playwright test` directly; it fails before test
41
41
  discovery with a `.esm.preflight` loader error (ERR-20260508-001).
42
42
 
43
+ The Playwright run includes the `pane-600` project
44
+ (`tests/e2e/reference-pane.pw.ts`): the reference surface, Chromium at
45
+ 600 px. Every node type must paint there or the release does not ship.
46
+ Embedded WebKit panes (the Copilot app) stay best-effort.
47
+
43
48
  `bun run test:e2e-cli` starts a local server in a fresh temp workspace
44
49
  and exercises the CLI flows from
45
50
  [`docs/evals/e2e-cli-coverage.md`](evals/e2e-cli-coverage.md).
package/docs/cli.md CHANGED
@@ -8,6 +8,19 @@ wins over `--port`, and both win over the environment variables). An invalid
8
8
  value for either flag is a hard error — the CLI never falls back silently to
9
9
  the default port.
10
10
 
11
+ Read commands accept `--board <board-id>` without switching the active board:
12
+ `layout` (including `--summary`), `status`, `spatial`, `pin --list`, and
13
+ `node get`. Writes and presence still target the board open in the workbench;
14
+ unknown board IDs fail rather than falling back to it.
15
+
16
+ An in-flight asynchronous write stops if the board changes; subsequent calls use
17
+ the newly open board. Batches stop rather than continuing on another board, and
18
+ already-completed entries remain applied.
19
+
20
+ On SIGINT or SIGTERM, the server saves before exiting. If saving fails, it reports
21
+ the error and stays running so the in-memory edits can be recovered. Fix the
22
+ storage failure and retry shutdown; a forced process kill can still lose edits.
23
+
11
24
  ```bash
12
25
  pmx-canvas node list --port 4750 # target a non-default daemon
13
26
  pmx-canvas --server-url http://127.0.0.1:4750 status # same, by URL
@@ -262,6 +275,53 @@ pmx-canvas fit <id-a> <id-b> # Fit to just these nodes
262
275
  `--width` / `--height`. After creating output a human should see, focus a single
263
276
  node or fit the new ids — auto-placement is board-relative, not camera-relative.
264
277
 
278
+ ## Boards
279
+
280
+ ```bash
281
+ pmx-canvas board list # most recently opened first; activeBoardId null = Home
282
+ pmx-canvas board create "Q4 planning" # created, not opened
283
+ pmx-canvas board rename <board-id> "New name"
284
+ pmx-canvas board create "Q4 planning" --category Planning
285
+ pmx-canvas board category <board-id> "Planning/Quarterly" # nested folders; --clear unfiles it
286
+ pmx-canvas board from <source-id> "Follow-up" --preview
287
+ pmx-canvas board from <source-id> "Follow-up" --nodes id1,id2 --readme --structure
288
+ ```
289
+
290
+ Commands write to the board open in the workbench. Opening, switching and
291
+ deleting boards happens in the workbench (the human's call). `board from`
292
+ excludes `prompt`, `response`, `trace`, and `mcp-app` cards; selected reusable
293
+ cards get fresh IDs. `--structure` includes group frames; edges between copied
294
+ cards are retained automatically. `--readme` carries the designated README. The new board remains inactive and
295
+ does not inherit pins, AX state, or mutation history.
296
+
297
+ ## Backup and restore
298
+
299
+ Boards save automatically. Backups are separate recovery copies of the whole library,
300
+ configured through the CLI or HTTP API rather than the Home screen. Scheduled backups
301
+ run while the server is running; scheduling is off until configured.
302
+
303
+ ```bash
304
+ pmx-canvas backup # every board, now; into .pmx-canvas/backups
305
+ pmx-canvas backup --to ~/Backups/pmx --keep 14
306
+ pmx-canvas backup schedule --every 24h --keep 14 --to ~/Backups/pmx # run by the server
307
+ pmx-canvas backup schedule --off
308
+ pmx-canvas backup status
309
+ pmx-canvas restore ~/Backups/pmx/canvas-20260927T090000000Z.db # keeps canvas.db.before-restore
310
+ ```
311
+
312
+ ## Export
313
+
314
+ ```bash
315
+ pmx-canvas export # the open board → .pmx-canvas/exports/<board>-<time>.html
316
+ pmx-canvas export --board <board-id> --out ~/Desktop/okrs.html
317
+ pmx-canvas export --include-files # include local file contents (left out by default)
318
+ pmx-canvas export --include-derived-text # include imported document text (left out by default)
319
+ ```
320
+
321
+ The two private-content flags are independent opt-ins. `--include-files` does not include
322
+ source-derived document text, and `--include-derived-text` does not include local file contents.
323
+ Unknown export flags are rejected instead of being ignored.
324
+
265
325
  ## Snapshots
266
326
 
267
327
  ```bash
@@ -275,6 +335,18 @@ pmx-canvas diff <snapshot-id> # Compare the canvas against a snapshot
275
335
 
276
336
  ## AX context
277
337
 
338
+ The cross-board brief has its own compact command. Its budget is measured in
339
+ UTF-16 code units (default 16000, maximum 100000), not model tokens. `--consumer`
340
+ selects a durable cursor scoped to the open board; `--since` performs an
341
+ explicit pull without changing that cursor. With neither flag, every call starts
342
+ from the beginning. Use a stable, unique consumer ID per agent/adapter; changing
343
+ or sharing it loses or conflates delivery progress.
344
+
345
+ ```bash
346
+ pmx-canvas context --consumer codex --budget 16000
347
+ pmx-canvas context --since 42 --budget 8000
348
+ ```
349
+
278
350
  AX commands expose the host-agnostic context contract used by adapters. Pins
279
351
  remain the explicit human-curated context set; AX focus is the current
280
352
  attention target and persists with canvas state and snapshots.
@@ -298,6 +370,7 @@ pmx-canvas ax event add --kind tool-start --summary "ran tests"
298
370
  pmx-canvas ax steer "focus on the failing test first"
299
371
  pmx-canvas ax evidence add --kind test-output --title "unit pass"
300
372
  pmx-canvas ax timeline --limit 50
373
+ pmx-canvas ax reads --limit 50 # context read log: which context each agent read, pinned delivery
301
374
 
302
375
  # Work items (canvas-bound)
303
376
  pmx-canvas ax work add --title "Wire up auth" --status in-progress node-1
package/docs/http-api.md CHANGED
@@ -43,14 +43,173 @@ targets, duration, easing, padding, and pullback. Open `/workbench?present=1` to
43
43
  chrome; camera movement is local to that viewer. Recording writes local files via
44
44
  the CLI, not an HTTP job endpoint.
45
45
 
46
+ ## Boards
47
+
48
+ A workspace holds many named boards; one is open at a time. Agent writes go to
49
+ the open board. While no board is open (Home), the first write opens the most
50
+ recently opened board, or creates one when the workspace has none;
51
+ `canvas.clear` on Home opens nothing. Only the trusted workbench opens or
52
+ deletes a board — HTTP calls require both `x-pmx-workbench: 1` and the
53
+ per-boot `x-pmx-workbench-token` embedded in the served `/workbench` HTML;
54
+ the marker alone is not authority. Public HTTP integrations must not open or
55
+ delete boards. In-process SDK embedding hosts retain authority to do so.
56
+ Every change emits a
57
+ `boards-changed` SSE frame with `{ activeBoardId, boards }`.
58
+
59
+ `category` is a folder path, for example `Engineering/Canvas/Decisions`.
60
+ Home renders nested, collapsible folders and searches board names and paths.
61
+ Paths support up to eight levels, each with 1–60 characters; surrounding spaces
62
+ are trimmed. Empty segments and `.`/`..` are rejected. `null` or `""` unfiles a
63
+ board. Folders are derived from boards, so empty folders do not persist. Moving
64
+ a board changes its path, not its ID or content.
65
+
66
+ ```bash
67
+ # List boards (most recently opened first) and which is open (null = Home)
68
+ curl http://localhost:4313/api/canvas/boards
69
+
70
+ # Read one board
71
+ curl http://localhost:4313/api/canvas/boards/<board-id>
72
+
73
+ # Create a board (optionally in a category) — it is NOT opened
74
+ curl -X POST http://localhost:4313/api/canvas/boards \
75
+ -H "Content-Type: application/json" -d '{"name":"Q4 planning","category":"Planning"}'
76
+
77
+ # Preview/create an inactive copy from reusable cards (preview false creates)
78
+ curl -X POST http://localhost:4313/api/canvas/boards/from \
79
+ -H "Content-Type: application/json" \
80
+ -d '{"sourceBoardId":"board-source","name":"Follow-up","nodeIds":["node-1"],"includeReadme":true,"includeStructure":true,"preview":true}'
81
+
82
+ # Rename and/or file under a category on Home ("category": "" removes it)
83
+ curl -X PATCH http://localhost:4313/api/canvas/boards/<board-id> \
84
+ -H "Content-Type: application/json" -d '{"name":"Q4 planning (final)","category":"Planning"}'
85
+
86
+ # Designate a Markdown card on this board as its README (null clears it)
87
+ curl -X PATCH http://localhost:4313/api/canvas/boards/<board-id> \
88
+ -H "Content-Type: application/json" -d '{"readmeNodeId":"<markdown-node-id>"}'
89
+
90
+ # Open a board, or Home with {"id":null} — workbench only
91
+ curl -X POST http://localhost:4313/api/canvas/boards/open \
92
+ -H "Content-Type: application/json" -H "x-pmx-workbench: 1" \
93
+ -H "x-pmx-workbench-token: <token-from-served-workbench>" -d '{"id":"<board-id>"}'
94
+
95
+ # Delete a board and its snapshots — workbench only; deleting the open board returns to Home
96
+ curl -X DELETE http://localhost:4313/api/canvas/boards/<board-id> \
97
+ -H "x-pmx-workbench: 1" -H "x-pmx-workbench-token: <token-from-served-workbench>"
98
+ ```
99
+
100
+ ## Backup and restore
101
+
102
+ A backup is one SQLite file holding every board, its snapshots and blobs,
103
+ written with `VACUUM INTO` (safe while the server runs) to
104
+ `canvas-<timestamp>.db` in the backup folder — by default `backups/` beside the
105
+ database (`.pmx-canvas/backups`). Keeping the newest `keep` (default 14)
106
+ removes older backups in that folder. The schedule is stored in the database
107
+ and runs inside the server. Not exposed over MCP.
108
+
109
+ ```bash
110
+ # Folder, schedule, last backup and the backups on disk
111
+ curl http://localhost:4313/api/canvas/backup
112
+
113
+ # Back up now (optional folder and keep)
114
+ curl -X POST http://localhost:4313/api/canvas/backup \
115
+ -H "Content-Type: application/json" -d '{"to":"/Users/me/Backups/pmx","keep":14}'
116
+
117
+ # Back up every 24h ("30m", "7d"; at least once a minute; "off" stops it)
118
+ curl -X POST http://localhost:4313/api/canvas/backup/schedule \
119
+ -H "Content-Type: application/json" -d '{"every":"24h","keep":14,"to":"/Users/me/Backups/pmx"}'
120
+
121
+ # Replace every board with a backup; the current file is kept as canvas.db.before-restore.
122
+ # Refuses files that are not PMX Canvas databases, and the live database itself.
123
+ curl -X POST http://localhost:4313/api/canvas/backup/restore \
124
+ -H "Content-Type: application/json" -d '{"file":"/Users/me/Backups/pmx/canvas-20260927T090000000Z.db"}'
125
+ ```
126
+
127
+ ## Static board export
128
+
129
+ One self-contained, read-only HTML file of one board (pan, zoom, expand) that
130
+ opens with no server or install. Cards render as escaped text; html cards,
131
+ charts, diagrams and built apps run in sandboxed iframes, with viewer bundles
132
+ stored once per file. Live MCP apps become placeholders; local file contents
133
+ are left out unless `includeFiles` is true. Files are written to `exports/`
134
+ beside the database. Built-app files are only embedded when they are regular,
135
+ bounded-size files inside the board's owned `.pmx-canvas/artifacts/` directory.
136
+ The preview manifest lists card links, remote images, and statically visible
137
+ network destinations in embedded HTML. Imported source-linked Markdown is also
138
+ excluded unless `includeDerivedText=true`, independently of `includeFiles`;
139
+ editing it does not remove that classification. Original attachment bytes are
140
+ never embedded in v1. Embedded scripts can make additional
141
+ network requests that cannot be inventoried in advance. Not exposed over MCP —
142
+ sharing is the human's act.
143
+
144
+ ```bash
145
+ # What the file would contain (defaults to the open board; ?board=<id> for another)
146
+ curl "http://localhost:4313/api/canvas/export/preview?includeFiles=false&includeDerivedText=false"
147
+
148
+ # Write it → { path, url, bytes, manifest }
149
+ curl -X POST http://localhost:4313/api/canvas/export \
150
+ -H "Content-Type: application/json" \
151
+ -d '{"board":"<board-id>","includeFiles":false,"includeDerivedText":false}'
152
+
153
+ # Serve a written export (opaque-origin sandbox; ?download=1 as an attachment)
154
+ curl "http://localhost:4313/api/canvas/exports/<file>.html?download=1" -o board.html
155
+ ```
156
+
157
+ ## Agent-assisted document imports
158
+
159
+ PDF and Office uploads are raw bytes, not JSON. Upload requires the trusted
160
+ workbench token, an active captured board, a non-empty body of at most 20 MiB,
161
+ and creates both an original file card and an `attached` import. The original
162
+ remains board-owned until board deletion; snapshot restore does not remove it,
163
+ and whole-database backups include it.
164
+
165
+ ```bash
166
+ curl -X POST \
167
+ "http://localhost:4313/api/canvas/attachments?boardId=<board-id>&name=report.pdf&mime=application/pdf&x=40&y=40" \
168
+ -H "x-pmx-workbench-token: <token>" --data-binary @report.pdf
169
+
170
+ # Human-only: explicit consent asks the connected agent; captured board must be open
171
+ curl -X POST http://localhost:4313/api/canvas/imports/<import-id>/request \
172
+ -H "Content-Type: application/json" -H "x-pmx-workbench: 1" \
173
+ -H "x-pmx-workbench-token: <token>" \
174
+ -d '{"consent":true}'
175
+
176
+ # Agent reads metadata and a controlled byte path; inline=true adds base64 only <=2 MiB
177
+ curl "http://localhost:4313/api/canvas/imports/<import-id>?inline=true"
178
+ curl http://localhost:4313/api/canvas/attachments/<attachment-id>/bytes -o source.bin
179
+
180
+ # Agent submits a reviewable draft (allowed even if another board is active)
181
+ curl -X POST http://localhost:4313/api/canvas/imports/<import-id>/submit \
182
+ -H "Content-Type: application/json" \
183
+ -d '{"sections":[{"title":"Summary","markdown":"...","reference":"page 1"}],"warnings":[],"agentDescription":"Extracted with the connected host PDF reader"}'
184
+
185
+ # Human-only review/commit; the original captured board must be open
186
+ curl -X POST http://localhost:4313/api/canvas/imports/<import-id>/commit \
187
+ -H "x-pmx-workbench: 1" -H "x-pmx-workbench-token: <token>"
188
+ ```
189
+
190
+ `GET /api/canvas/imports?boardId=...` lists jobs. An agent that cannot access or
191
+ interpret the source posts `{ "reason": "..." }` to
192
+ `/api/canvas/imports/:id/unavailable`; a human may later request a new attempt.
193
+ Humans can cancel requested or drafted jobs at `/cancel`. There is no bundled
194
+ converter, OCR, automatic agent launch, or guarantee for charts, scans, and
195
+ host-specific formats. A remote agent may need the human/host to transfer the
196
+ bytes before it can submit a draft.
197
+
46
198
  ## Canvas state
47
199
 
200
+ Read endpoints accept an optional `board=<board-id>` query parameter without
201
+ opening that board or changing where writes go. This is supported by
202
+ `/api/canvas/state`, `/api/canvas/node/:id`, `/api/canvas/summary`,
203
+ `/api/canvas/spatial-context`, and `/api/canvas/pinned-context`. Unknown boards
204
+ return 404. Omit `board` to read the active board. Live AX session/delivery reads
205
+ and fixed MCP resources continue to describe the open board.
206
+
48
207
  ```bash
49
208
  # Get canvas state
50
209
  curl http://localhost:4313/api/canvas/state
51
210
 
52
- # Search nodes (optional limit= caps the result count)
53
- curl "http://localhost:4313/api/canvas/search?q=auth&limit=10"
211
+ # Search the library (omit scope for the active board)
212
+ curl "http://localhost:4313/api/canvas/search?q=auth&limit=10&scope=library"
54
213
 
55
214
  # Validate the current layout
56
215
  curl http://localhost:4313/api/canvas/validate
@@ -108,6 +267,11 @@ A node creation request must resolve a `type` — pass it in the body (`{ "type"
108
267
  ... }`) or as a `?type=` query param. An empty / type-less body returns `400`
109
268
  rather than silently creating a markdown node.
110
269
 
270
+ An optional `boardId` on node creation is a captured-board precondition, not a
271
+ switch command: if it no longer matches the active board, the request returns
272
+ `409` without creating a node or opening a board from Home. The file-drop UI
273
+ uses this to prevent delayed uploads from landing on a different board.
274
+
111
275
  Mermaid nodes accept `data: { "fit": "contain" | "none" }` on both
112
276
  `POST /api/canvas/node` and `PATCH /api/canvas/node/:id`. The default, `contain`,
113
277
  shrinks overflowing diagrams without changing aspect ratio; `none` keeps 100%
@@ -223,14 +387,30 @@ curl -X POST http://localhost:4313/api/canvas/context-pins \
223
387
 
224
388
  # Get pinned context
225
389
  curl http://localhost:4313/api/canvas/pinned-context
390
+
391
+ # Cross-board brief. budget is a UTF-16 character ceiling (default 16000, max 100000).
392
+ # Without since, the board/consumer cursor advances through only fully delivered revisions.
393
+ curl 'http://localhost:4313/api/canvas/context?consumer=codex&budget=16000'
226
394
  ```
227
395
 
396
+ `consumer` is optional. Supply a stable unique value only for a durable,
397
+ board-scoped cursor; without `consumer` or `since`, each request starts from the
398
+ beginning. An explicit `since` never advances a cursor. The brief prioritizes
399
+ pins, then relevant current-board changes and asks, then compact linked or
400
+ same-folder board context. Every item carries source board/card IDs. If card text
401
+ is clipped, use those IDs with the board/node read route rather than guessing
402
+ from the excerpt. Reading inactive boards does not open them.
403
+
228
404
  ## AX context and focus
229
405
 
230
406
  AX context is the host-agnostic agent-experience layer. It combines existing
231
407
  context pins with a persisted focus node set that adapters can inject into
232
408
  their native prompt/context hooks.
233
409
 
410
+ The context response includes `boardId` (`null` on Home), captured with its content
411
+ and pins. Adapters should send that ID when recording a proxied context read, rather
412
+ than looking up whichever board is active after delivery.
413
+
234
414
  ```bash
235
415
  # Get persisted AX state
236
416
  curl http://localhost:4313/api/canvas/ax
@@ -276,6 +456,22 @@ curl -X POST http://localhost:4313/api/canvas/ax/evidence \
276
456
  # Timeline — read the bounded timeline (default limit 50, max 200)
277
457
  curl "http://localhost:4313/api/canvas/ax/timeline?limit=50"
278
458
 
459
+ # Context reads — which canvas context each agent read, and whether its pinned
460
+ # nodes were in what came back (newest first, default 50, max 500; the per-consumer
461
+ # summary covers the whole retained log of 5,000 reads). Agent reads of pinned
462
+ # context, AX context, AX state, summary, spatial context and layout are recorded
463
+ # automatically; the workbench's own reads are not.
464
+ curl "http://localhost:4313/api/canvas/ax/context-reads?limit=50"
465
+
466
+ # Context reads — a proxy (an MCP server attached to this daemon, a host adapter)
467
+ # records the read its agent actually made. Its own fetches send
468
+ # `x-pmx-proxied-read: 1` so they are not recorded twice.
469
+ # Pass boardId captured at read time (null for Home); omitting it uses the
470
+ # currently open board. MCP bytes measure the final formatted text response.
471
+ curl -X POST http://localhost:4313/api/canvas/ax/context-reads \
472
+ -H "Content-Type: application/json" \
473
+ -d '{"channel":"adapter","resource":"copilot:prompt-context","consumer":"copilot","pinnedNodeIds":["node-1"],"deliveredNodeIds":["node-1"],"bytes":2048}'
474
+
279
475
  # Canvas-bound — add / update a work item
280
476
  curl -X POST http://localhost:4313/api/canvas/ax/work \
281
477
  -H "Content-Type: application/json" \