pmx-canvas 0.6.5 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/.github/extensions/pmx-canvas/extension.mjs +67 -3
  2. package/CHANGELOG.md +54 -0
  3. package/Readme.md +29 -5
  4. package/dist/canvas/global.css +440 -24
  5. package/dist/canvas/index.js +90 -75
  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/client/canvas/BoardCopyDialog.d.ts +5 -0
  11. package/dist/types/client/canvas/BoardFolderDialog.d.ts +7 -0
  12. package/dist/types/client/canvas/BoardLinkDialog.d.ts +3 -0
  13. package/dist/types/client/canvas/BoardSwitcher.d.ts +8 -0
  14. package/dist/types/client/canvas/ExportDialog.d.ts +7 -0
  15. package/dist/types/client/canvas/HomeView.d.ts +9 -0
  16. package/dist/types/client/nodes/AttachmentNode.d.ts +6 -0
  17. package/dist/types/client/nodes/BoardNode.d.ts +5 -0
  18. package/dist/types/client/state/boards-store.d.ts +79 -0
  19. package/dist/types/client/state/canvas-store.d.ts +2 -0
  20. package/dist/types/client/state/intent-bridge.d.ts +1 -0
  21. package/dist/types/client/state/session-store.d.ts +7 -0
  22. package/dist/types/client/types.d.ts +2 -2
  23. package/dist/types/json-render/catalog.d.ts +1 -1
  24. package/dist/types/json-render/charts/definitions.d.ts +1 -1
  25. package/dist/types/json-render/server.d.ts +6 -0
  26. package/dist/types/mcp/canvas-access.d.ts +10 -3
  27. package/dist/types/mcp/context-uri-template.d.ts +6 -0
  28. package/dist/types/server/agent-presence.d.ts +1 -1
  29. package/dist/types/server/attribution.d.ts +11 -0
  30. package/dist/types/server/ax-context.d.ts +3 -1
  31. package/dist/types/server/ax-interaction.d.ts +2 -2
  32. package/dist/types/server/ax-state.d.ts +13 -3
  33. package/dist/types/server/backup.d.ts +36 -0
  34. package/dist/types/server/board-export.d.ts +45 -0
  35. package/dist/types/server/canvas-db.d.ts +63 -9
  36. package/dist/types/server/canvas-operations.d.ts +23 -0
  37. package/dist/types/server/canvas-provenance.d.ts +1 -1
  38. package/dist/types/server/canvas-state.d.ts +140 -3
  39. package/dist/types/server/context-brief.d.ts +111 -0
  40. package/dist/types/server/context-reads.d.ts +55 -0
  41. package/dist/types/server/document-import.d.ts +66 -0
  42. package/dist/types/server/index.d.ts +103 -4
  43. package/dist/types/server/mutation-history.d.ts +1 -0
  44. package/dist/types/server/operations/invoker.d.ts +2 -0
  45. package/dist/types/server/operations/mcp.d.ts +7 -1
  46. package/dist/types/server/operations/ops/ax-shared.d.ts +2 -2
  47. package/dist/types/server/operations/ops/backup.d.ts +2 -0
  48. package/dist/types/server/operations/ops/boards.d.ts +10 -0
  49. package/dist/types/server/operations/ops/export.d.ts +2 -0
  50. package/dist/types/server/operations/ops/imports.d.ts +2 -0
  51. package/dist/types/server/operations/ops/nodes.d.ts +1 -1
  52. package/dist/types/server/operations/ops/query.d.ts +2 -0
  53. package/dist/types/server/operations/registry.d.ts +9 -1
  54. package/dist/types/server/operations/types.d.ts +1 -1
  55. package/dist/types/server/server.d.ts +1 -0
  56. package/dist/types/server/trace-manager.d.ts +7 -0
  57. package/dist/types/server/webpage-node.d.ts +22 -1
  58. package/dist/types/server/workbench-auth.d.ts +3 -0
  59. package/dist/types/shared/boards.d.ts +6 -0
  60. package/docs/RELEASE.md +5 -0
  61. package/docs/cli.md +68 -0
  62. package/docs/http-api.md +198 -2
  63. package/docs/mcp.md +71 -6
  64. package/docs/node-types.md +26 -0
  65. package/docs/plans/plan-010-attributed-ask-answers.md +39 -0
  66. package/docs/plans/plan-011-context-read-instrumentation.md +54 -0
  67. package/docs/plans/plan-012-many-boards-stage-1.md +98 -0
  68. package/docs/plans/plan-013-static-board-export.md +47 -0
  69. package/docs/plans/plan-014-connected-board-memory.md +140 -0
  70. package/docs/product-vision-2026-09.md +118 -18
  71. package/docs/sdk.md +50 -0
  72. package/package.json +1 -1
  73. package/skills/pmx-canvas/SKILL.md +53 -4
  74. package/skills/pmx-canvas/references/full-reference.md +43 -7
  75. package/skills/pmx-canvas/references/github-copilot-app-adapter.md +3 -0
  76. package/skills/pmx-canvas/references/installing-pmx-canvas.md +4 -4
  77. package/skills/pmx-canvas-orchestration/SKILL.md +5 -0
  78. package/skills/pmx-canvas-testing/SKILL.md +4 -0
  79. package/src/cli/agent.ts +4 -0
  80. package/src/cli/commands/ax.ts +13 -0
  81. package/src/cli/commands/backup.ts +66 -0
  82. package/src/cli/commands/boards.ts +71 -0
  83. package/src/cli/commands/export.ts +30 -0
  84. package/src/cli/commands/nodes.ts +4 -1
  85. package/src/cli/commands/pins.ts +6 -2
  86. package/src/cli/commands/pump.ts +3 -0
  87. package/src/cli/commands/query.ts +33 -7
  88. package/src/cli/commands/view.ts +7 -5
  89. package/src/cli/index.ts +19 -9
  90. package/src/client/App.tsx +21 -3
  91. package/src/client/canvas/BoardCopyDialog.tsx +153 -0
  92. package/src/client/canvas/BoardFolderDialog.tsx +120 -0
  93. package/src/client/canvas/BoardLinkDialog.tsx +93 -0
  94. package/src/client/canvas/BoardSwitcher.tsx +149 -0
  95. package/src/client/canvas/CanvasViewport.tsx +3 -0
  96. package/src/client/canvas/CommandPalette.tsx +107 -4
  97. package/src/client/canvas/ContextMenu.tsx +4 -0
  98. package/src/client/canvas/ExpandedNodeOverlay.tsx +3 -0
  99. package/src/client/canvas/ExportDialog.tsx +197 -0
  100. package/src/client/canvas/HomeView.tsx +277 -0
  101. package/src/client/canvas/SessionPanel.tsx +28 -1
  102. package/src/client/canvas/ToolRail.tsx +38 -3
  103. package/src/client/canvas/TopBar.tsx +34 -18
  104. package/src/client/canvas/import-files.ts +19 -1
  105. package/src/client/canvas/kind-colors.ts +1 -0
  106. package/src/client/icons.tsx +2 -0
  107. package/src/client/nodes/AttachmentNode.tsx +148 -0
  108. package/src/client/nodes/BoardNode.tsx +35 -0
  109. package/src/client/nodes/FileNode.tsx +6 -0
  110. package/src/client/nodes/MarkdownNode.tsx +47 -19
  111. package/src/client/nodes/ax-node-actions.ts +2 -5
  112. package/src/client/nodes/use-ax-surface-bridge.ts +1 -1
  113. package/src/client/state/boards-store.ts +167 -0
  114. package/src/client/state/canvas-store.ts +22 -0
  115. package/src/client/state/intent-bridge.ts +30 -2
  116. package/src/client/state/node-factory.ts +1 -0
  117. package/src/client/state/session-store.ts +5 -0
  118. package/src/client/state/sse-bridge.ts +49 -5
  119. package/src/client/theme/global.css +440 -24
  120. package/src/client/types.ts +2 -0
  121. package/src/json-render/server.ts +16 -9
  122. package/src/mcp/canvas-access.ts +59 -22
  123. package/src/mcp/context-uri-template.ts +28 -0
  124. package/src/mcp/server.ts +122 -15
  125. package/src/server/agent-context.ts +116 -29
  126. package/src/server/attribution.ts +29 -0
  127. package/src/server/ax-context.ts +4 -2
  128. package/src/server/ax-interaction.ts +1 -0
  129. package/src/server/ax-state-manager.ts +72 -3
  130. package/src/server/ax-state.ts +41 -0
  131. package/src/server/backup.ts +178 -0
  132. package/src/server/board-export.ts +686 -0
  133. package/src/server/canvas-db.ts +599 -107
  134. package/src/server/canvas-operations.ts +68 -9
  135. package/src/server/canvas-provenance.ts +1 -0
  136. package/src/server/canvas-schema.ts +12 -0
  137. package/src/server/canvas-state.ts +774 -45
  138. package/src/server/context-brief.ts +372 -0
  139. package/src/server/context-reads.ts +238 -0
  140. package/src/server/demo-state.json +659 -2
  141. package/src/server/document-import.ts +223 -0
  142. package/src/server/index.ts +231 -5
  143. package/src/server/mutation-history.ts +10 -13
  144. package/src/server/operations/composites.ts +26 -4
  145. package/src/server/operations/http.ts +9 -3
  146. package/src/server/operations/index.ts +8 -0
  147. package/src/server/operations/invoker.ts +14 -3
  148. package/src/server/operations/mcp.ts +50 -11
  149. package/src/server/operations/ops/app.ts +5 -0
  150. package/src/server/operations/ops/ax-read.ts +10 -6
  151. package/src/server/operations/ops/ax-timeline.ts +75 -0
  152. package/src/server/operations/ops/backup.ts +134 -0
  153. package/src/server/operations/ops/batch.ts +8 -1
  154. package/src/server/operations/ops/boards.ts +351 -0
  155. package/src/server/operations/ops/export.ts +74 -0
  156. package/src/server/operations/ops/ext-app.ts +4 -0
  157. package/src/server/operations/ops/imports.ts +220 -0
  158. package/src/server/operations/ops/nodes.ts +57 -5
  159. package/src/server/operations/ops/query.ts +206 -9
  160. package/src/server/operations/registry.ts +123 -31
  161. package/src/server/operations/types.ts +1 -1
  162. package/src/server/server.ts +146 -2
  163. package/src/server/spatial-analysis.ts +9 -17
  164. package/src/server/trace-manager.ts +19 -0
  165. package/src/server/web-artifacts.ts +2 -1
  166. package/src/server/webpage-node.ts +173 -13
  167. package/src/server/workbench-auth.ts +11 -0
  168. package/src/shared/boards.ts +18 -0
  169. package/src/shared/node-sizes.ts +1 -0
@@ -12,8 +12,11 @@
12
12
  * the one-shot boot migration into SQLite was retired.
13
13
  */
14
14
  import { type Tour } from '../shared/tour.js';
15
- import { type PersistedCanvasState, type CanvasTheme, type AxTimelineQuery } from './canvas-db.js';
15
+ import { type CanvasBoard, type PersistedCanvasState, type CanvasTheme, type AxTimelineQuery } from './canvas-db.js';
16
+ import { type DocumentImport, type DocumentImportStatus, type ImportSection, type Attachment } from './document-import.js';
16
17
  import { type PmxAxActivityKind, type PmxAxElicitation, type PmxAxModeRequest, type PmxAxMode, type PmxAxCommandDescriptor, type PmxAxPolicy, type PmxAxFocusState, type PmxAxSource, type PmxAxState, type PmxAxWorkItem, type PmxAxWorkItemStatus, type PmxAxApprovalGate, type PmxAxReviewAnnotation, type PmxAxReviewKind, type PmxAxReviewSeverity, type PmxAxReviewStatus, type PmxAxReviewAnchorType, type PmxAxReviewRegion, type PmxAxEvent, type PmxAxEventKind, type PmxAxEvidence, type PmxAxEvidenceKind, type PmxAxSteeringMessage, type PmxAxHostCapability, type PmxAxTimelineSummary } from './ax-state.js';
18
+ import { type ActorAttribution } from './attribution.js';
19
+ import { type ContextRead, type ContextReadConsumerSummary, type ContextReadInput } from './context-reads.js';
17
20
  export declare const PMX_CANVAS_DIR = ".pmx-canvas";
18
21
  export interface PersistedBlobRef {
19
22
  __pmxCanvasBlob: 'v1';
@@ -54,7 +57,7 @@ export interface CanvasSnapshotGcResult {
54
57
  }
55
58
  export interface CanvasNodeState {
56
59
  id: string;
57
- type: 'markdown' | 'mcp-app' | 'webpage' | 'json-render' | 'graph' | 'prompt' | 'response' | 'status' | 'context' | 'ledger' | 'trace' | 'file' | 'diff' | 'mermaid' | 'image' | 'html' | 'group';
60
+ type: 'markdown' | 'mcp-app' | 'webpage' | 'json-render' | 'graph' | 'board' | 'prompt' | 'response' | 'status' | 'context' | 'ledger' | 'trace' | 'file' | 'diff' | 'mermaid' | 'image' | 'html' | 'group';
58
61
  position: {
59
62
  x: number;
60
63
  y: number;
@@ -67,6 +70,27 @@ export interface CanvasNodeState {
67
70
  collapsed: boolean;
68
71
  pinned: boolean;
69
72
  data: Record<string, unknown>;
73
+ /** Server-owned provenance. Legacy rows normalize to unknown. */
74
+ createdBy?: ActorAttribution;
75
+ lastEditedBy?: ActorAttribution;
76
+ /** Board-monotonic revision of this node's latest semantic content. */
77
+ contentRevision?: number;
78
+ }
79
+ export interface NodeDeletionTombstone {
80
+ nodeId: string;
81
+ revision: number;
82
+ deletedBy: ActorAttribution;
83
+ }
84
+ export interface BoardContentRevision {
85
+ revision: number;
86
+ /** Cursors below this value are expired and must perform a full reset. */
87
+ retentionFloor: number;
88
+ }
89
+ export interface BoardContentDelta extends BoardContentRevision {
90
+ since: number;
91
+ reset: boolean;
92
+ nodes: CanvasNodeState[];
93
+ deleted: NodeDeletionTombstone[];
70
94
  }
71
95
  export interface ViewportState {
72
96
  x: number;
@@ -123,6 +147,8 @@ export interface CanvasNodeUpdate {
123
147
  collapsed?: boolean;
124
148
  }
125
149
  export type CanvasChangeType = 'pins' | 'nodes' | 'ax' | 'ax-timeline';
150
+ /** Name for a board nobody named yet: when it was made, to the minute. */
151
+ export declare function defaultBoardName(): string;
126
152
  export interface MutationRecordInfo {
127
153
  operationType: 'addNode' | 'updateNode' | 'removeNode' | 'addEdge' | 'updateEdge' | 'removeEdge' | 'addAnnotation' | 'removeAnnotation' | 'clear' | 'restoreSnapshot' | 'setPins' | 'setAxFocus' | 'addWorkItem' | 'updateWorkItem' | 'requestApproval' | 'resolveApproval' | 'addReviewAnnotation' | 'updateReviewAnnotation' | 'requestElicitation' | 'respondElicitation' | 'requestMode' | 'resolveModeRequest' | 'setPolicy' | 'arrange' | 'batch' | 'groupNodes' | 'releaseGroupChildren' | 'viewport';
128
154
  description: string;
@@ -146,6 +172,9 @@ declare class CanvasStateManager {
146
172
  private _theme;
147
173
  private _contextPinnedNodeIds;
148
174
  private _workspaceRoot;
175
+ private _contentRevision;
176
+ private _revisionFloor;
177
+ private _deletionTombstones;
149
178
  private readonly ax;
150
179
  private _changeListeners;
151
180
  /**
@@ -178,12 +207,22 @@ declare class CanvasStateManager {
178
207
  private applyResolvedGroupBounds;
179
208
  private getGroupSnapshot;
180
209
  private normalizeNode;
210
+ private nextContentRevision;
211
+ private revisionState;
212
+ /**
213
+ * Stable delta cursor contract: revisions strictly increase per active board;
214
+ * geometry-only writes do not advance them. `reset` means `since` predates
215
+ * retained tombstones, so callers must replace their view with `nodes`.
216
+ */
217
+ readContentDelta(since?: number): BoardContentDelta;
218
+ getContentRevision(): BoardContentRevision;
181
219
  private nodeForRead;
182
220
  private reflowAllGroups;
183
221
  private translateGroupChildren;
184
222
  private recomputeParentGroupBounds;
185
223
  private compactGroupChildren;
186
224
  private _db;
225
+ private _dbPath;
187
226
  private _saveTimer;
188
227
  /** Workspace root backing persistence and workspace-relative path resolution. */
189
228
  get workspaceRoot(): string;
@@ -201,8 +240,95 @@ declare class CanvasStateManager {
201
240
  private externalizePersistedStateBlobs;
202
241
  getWorkspaceRoot(): string;
203
242
  private emptyPersistedState;
204
- /** Load canvas state from SQLite. Call once on server startup. */
243
+ /**
244
+ * Load the board that was open last. Call once on server startup. With no
245
+ * such board the workspace starts on Home (no board open).
246
+ */
205
247
  loadFromDisk(options?: LoadFromDiskOptions): boolean;
248
+ /** The SQLite file holding every board, or null without a workspace database. */
249
+ get databasePath(): string | null;
250
+ readWorkspaceMeta(key: string): string | null;
251
+ writeWorkspaceMeta(key: string, value: string | null): void;
252
+ /** A consistent copy of the whole library, safe while the server runs. */
253
+ backupDatabaseTo(path: string): void;
254
+ /**
255
+ * Replace the library with `file`: the current file is kept beside it as
256
+ * `<db>.before-restore`, then the restored file opens (migrating an older
257
+ * layout) on the board it had open last.
258
+ */
259
+ replaceDatabase(file: string): void;
260
+ private _activeBoardId;
261
+ private _boardGeneration;
262
+ /** Invalidates asynchronous work whenever the loaded board state is replaced. */
263
+ get boardGeneration(): number;
264
+ /** The open board, or null on Home. */
265
+ get activeBoardId(): string | null;
266
+ getActiveBoard(): CanvasBoard | null;
267
+ /**
268
+ * One board's content without opening it: the open board from memory, any
269
+ * other straight from SQLite. Null when there is no such board.
270
+ */
271
+ readBoard(id: string, includeBlobs?: boolean): {
272
+ board: CanvasBoard;
273
+ state: PersistedCanvasState;
274
+ layout: CanvasLayout;
275
+ } | null;
276
+ /** Every board in the workspace, most recently opened first. */
277
+ listBoards(): CanvasBoard[];
278
+ storeAttachment(input: {
279
+ boardId: string;
280
+ name: string;
281
+ mime: string;
282
+ bytes: Uint8Array;
283
+ }): Attachment | null;
284
+ getAttachment(id: string): Attachment | null;
285
+ readAttachmentBytes(id: string): Uint8Array | null;
286
+ createDocumentImport(attachmentId: string, position: {
287
+ x: number;
288
+ y: number;
289
+ }): DocumentImport | null;
290
+ getDocumentImport(id: string): DocumentImport | null;
291
+ listDocumentImports(boardId?: string): DocumentImport[];
292
+ updateDocumentImport(id: string, status: DocumentImportStatus, fields?: {
293
+ sections?: ImportSection[];
294
+ warnings?: string[];
295
+ agentDescription?: string;
296
+ reason?: string;
297
+ committedNodeIds?: string[];
298
+ }): DocumentImport | null;
299
+ commitDocumentImport(id: string, nodeIds: string[]): DocumentImport | null;
300
+ getContextBriefCursor(boardId: string, consumer: string): number | null;
301
+ advanceContextBriefCursor(boardId: string, consumer: string, revision: number): void;
302
+ /** Creates a board without opening it. */
303
+ createBoard(name: string, category?: string | null): CanvasBoard | null;
304
+ updateBoard(id: string, patch: {
305
+ name?: string;
306
+ category?: string | null;
307
+ }): boolean;
308
+ setBoardReadme(id: string, nodeId: string | null): boolean;
309
+ /** Bounded inactive-board creation; never replaces the state the human has open. */
310
+ createBoardFromBoard(input: {
311
+ sourceBoardId: string;
312
+ name: string;
313
+ category?: string | null;
314
+ nodeIds?: string[];
315
+ includeReadme?: boolean;
316
+ includeStructure?: boolean;
317
+ }): CanvasBoard | null;
318
+ /** Deletes a board and its snapshots; deleting the open board returns to Home first. */
319
+ deleteBoard(id: string): boolean;
320
+ /**
321
+ * Save the open board, then open `id` (or Home for null). The canvas state is
322
+ * replaced wholesale; callers own the runtime side (undo history, app
323
+ * sessions, watchers, SSE) — see `openCanvasBoard` in canvas-operations.
324
+ */
325
+ switchBoard(id: string | null): boolean;
326
+ /**
327
+ * Content written while no board is open (Home) lands on a new board, which
328
+ * opens: nothing the human was looking at is displaced.
329
+ */
330
+ private ensureActiveBoard;
331
+ private hasBoardContent;
206
332
  /** Debounced save — coalesces rapid mutations into a single write. */
207
333
  private scheduleSave;
208
334
  flushToDisk(): void;
@@ -246,6 +372,11 @@ declare class CanvasStateManager {
246
372
  deleteSnapshot(id: string): boolean;
247
373
  /** Remove all snapshots from the DB. Used by test teardown. */
248
374
  clearAllSnapshots(): void;
375
+ private restampRestoredContent;
376
+ private semanticData;
377
+ private nodeContentChanged;
378
+ private setSemanticNode;
379
+ private recordDeletion;
249
380
  get viewport(): ViewportState;
250
381
  addNode(node: CanvasNodeState): void;
251
382
  addJsonRenderNode(node: CanvasNodeState): void;
@@ -476,6 +607,12 @@ declare class CanvasStateManager {
476
607
  }): PmxAxSteeringMessage[];
477
608
  getPendingSteeringCount(consumer?: string): number;
478
609
  getAxTimelineSummary(): PmxAxTimelineSummary;
610
+ /** Records one agent context read (diagnostics; never notifies, so a read cannot trigger reads). */
611
+ recordContextRead(input: ContextReadInput, boardId?: string | null): ContextRead | null;
612
+ getContextReads(limit?: number): {
613
+ reads: ContextRead[];
614
+ summary: ContextReadConsumerSummary[];
615
+ };
479
616
  getAxTimeline(q?: AxTimelineQuery): {
480
617
  events: PmxAxEvent[];
481
618
  evidence: PmxAxEvidence[];
@@ -0,0 +1,111 @@
1
+ import type { CanvasNodeState, NodeDeletionTombstone } from './canvas-state.js';
2
+ export type ContextBriefReason = 'pinned' | 'changed' | 'human' | 'ask' | 'steer' | 'linked' | 'category';
3
+ export interface ContextBriefSourceEntry {
4
+ sourceBoardId: string;
5
+ nodeId: string;
6
+ reason: 'human' | 'ask' | 'steer';
7
+ title: string;
8
+ text: string;
9
+ provenance?: {
10
+ kind: 'imported';
11
+ source: string;
12
+ };
13
+ }
14
+ export interface ContextBriefLibraryBoard {
15
+ boardId: string;
16
+ name: string;
17
+ category: string | null;
18
+ readme?: {
19
+ nodeId: string;
20
+ title: string;
21
+ summary: string;
22
+ provenance?: {
23
+ kind: 'imported';
24
+ source: string;
25
+ };
26
+ };
27
+ pinnedTitles: Array<{
28
+ nodeId: string;
29
+ title: string;
30
+ }>;
31
+ /** IDs of link cards on the active board which explicitly target this board. */
32
+ linkIds: string[];
33
+ }
34
+ export interface ContextBriefInput {
35
+ activeBoard: {
36
+ boardId: string;
37
+ name: string;
38
+ category: string | null;
39
+ };
40
+ nodes: readonly CanvasNodeState[];
41
+ pinnedNodeIds: readonly string[];
42
+ contentRevision: number;
43
+ retentionFloor: number;
44
+ tombstones: readonly NodeDeletionTombstone[];
45
+ /** null means a first read. Other values must be non-negative safe integers. */
46
+ since: number | null;
47
+ libraryBoards: readonly ContextBriefLibraryBoard[];
48
+ entries?: readonly ContextBriefSourceEntry[];
49
+ /** Maximum JavaScript string length (UTF-16 code units), including the JSON envelope. */
50
+ budget: number;
51
+ }
52
+ export interface CompiledContextEntry {
53
+ sourceBoardId: string;
54
+ nodeId: string;
55
+ reason: ContextBriefReason;
56
+ title: string;
57
+ text: string;
58
+ /** A linked board's pin title is discovery metadata, not delivery of that pinned card. */
59
+ titleOnly?: true;
60
+ /** The body was shortened to fit; the same revision remains eligible on the next pull. */
61
+ truncated?: true;
62
+ provenance?: {
63
+ kind: 'imported';
64
+ source: string;
65
+ trust: 'source-material-not-instructions';
66
+ };
67
+ }
68
+ export interface ContextBriefDocument {
69
+ version: 1;
70
+ budgetUnit: 'utf16-code-units';
71
+ board: {
72
+ boardId: string;
73
+ name: string;
74
+ category: string | null;
75
+ };
76
+ cursor: {
77
+ requested: number | null;
78
+ next: number | null;
79
+ current: number;
80
+ retentionFloor: number;
81
+ reset: 'first-read' | 'retention-expired' | null;
82
+ valid: boolean;
83
+ };
84
+ entries: CompiledContextEntry[];
85
+ deletions: Array<{
86
+ sourceBoardId: string;
87
+ nodeId: string;
88
+ revision: number;
89
+ }>;
90
+ delivery: {
91
+ truncated: boolean;
92
+ omittedEntries: number;
93
+ omittedDeletions: number;
94
+ };
95
+ }
96
+ export interface ContextBriefResult {
97
+ /** Empty only when the budget cannot hold even the bounded JSON envelope. */
98
+ serialized: string;
99
+ document: ContextBriefDocument | null;
100
+ deliveredEntryIds: string[];
101
+ deliveredDeletionIds: string[];
102
+ nextCursor: number | null;
103
+ reset: ContextBriefDocument['cursor']['reset'];
104
+ invalidCursor: boolean;
105
+ truncated: boolean;
106
+ }
107
+ /**
108
+ * Pure, deterministic compiler. It reads only the supplied snapshot and never
109
+ * treats library visibility as authorization to read or mutate another board.
110
+ */
111
+ export declare function compileContextBrief(input: ContextBriefInput): ContextBriefResult;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Context read instrumentation (vision Part 1 item 4, the delivery half):
3
+ * one row per agent read of canvas context — which surface, by whom, and
4
+ * which pinned nodes were in what came back. Diagnostics only, like the AX
5
+ * timeline: never snapshotted, never cleared with the canvas, bounded, and
6
+ * recording never notifies (a read must not trigger more reads).
7
+ */
8
+ import type { Database } from 'bun:sqlite';
9
+ export declare const CONTEXT_READ_RETENTION = 5000;
10
+ export declare const CONTEXT_READ_CHANNELS: readonly ["operation", "mcp-resource", "mcp-prompt", "adapter"];
11
+ export type ContextReadChannel = (typeof CONTEXT_READ_CHANNELS)[number];
12
+ /** Registry reads that hand an agent canvas context; recorded in executeOperation. */
13
+ export declare const CONTEXT_READ_OPS: Set<string>;
14
+ export interface ContextRead {
15
+ seq: number;
16
+ id: string;
17
+ at: string;
18
+ channel: ContextReadChannel;
19
+ resource: string;
20
+ source: string;
21
+ consumer: string | null;
22
+ agentId: string | null;
23
+ pinnedNodeIds: string[];
24
+ deliveredNodeIds: string[];
25
+ bytes: number;
26
+ /** The board open when the read happened (null on Home). */
27
+ boardId: string | null;
28
+ }
29
+ export type ContextReadInput = Omit<ContextRead, 'seq' | 'id' | 'at' | 'boardId'> & {
30
+ /** Explicit read target, captured before asynchronous formatting/proxy work. */
31
+ boardId?: string | null;
32
+ };
33
+ export interface ContextReadConsumerSummary {
34
+ consumer: string;
35
+ reads: number;
36
+ readsWithPins: number;
37
+ readsDeliveringAllPins: number;
38
+ lastReadAt: string;
39
+ resources: Record<string, number>;
40
+ }
41
+ /**
42
+ * Pinned nodes whose serialized node (an object carrying `"id": "<id>"`) is in
43
+ * what the reader received. A bare id list, a title, or a clipped-off node does
44
+ * not count — the agent got the pin's name, not its content.
45
+ */
46
+ export declare function deliveredPinnedIds(pinnedNodeIds: string[], payloadText: string): string[];
47
+ export declare function contextReadFromPayload(base: Omit<ContextReadInput, 'deliveredNodeIds' | 'bytes'>, payload: unknown): ContextReadInput;
48
+ export declare const CONTEXT_READS_SCHEMA_SQL = "\n CREATE TABLE IF NOT EXISTS context_reads (\n seq INTEGER PRIMARY KEY AUTOINCREMENT,\n id TEXT NOT NULL UNIQUE,\n at TEXT NOT NULL,\n channel TEXT NOT NULL,\n resource TEXT NOT NULL,\n source TEXT NOT NULL,\n consumer TEXT,\n agent_id TEXT,\n pinned_node_ids TEXT NOT NULL DEFAULT '[]',\n delivered_node_ids TEXT NOT NULL DEFAULT '[]',\n bytes INTEGER NOT NULL DEFAULT 0,\n board_id TEXT\n );\n";
49
+ export declare function appendContextReadToDB(db: Database, input: ContextReadInput, boardId: string | null): ContextRead;
50
+ /** Newest first. The summary covers every retained row, not just the returned page. */
51
+ export declare function loadContextReadsFromDB(db: Database, limit?: number): {
52
+ reads: ContextRead[];
53
+ summary: ContextReadConsumerSummary[];
54
+ };
55
+ export declare function summarizeContextReads(reads: ContextRead[]): ContextReadConsumerSummary[];
@@ -0,0 +1,66 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ export declare const MAX_ATTACHMENT_BYTES: number;
3
+ export declare const MAX_INLINE_ATTACHMENT_BYTES: number;
4
+ export declare const MAX_ATTACHMENT_NAME_LENGTH = 255;
5
+ export declare const MAX_IMPORT_SECTIONS = 32;
6
+ export declare const MAX_IMPORT_WARNINGS = 20;
7
+ export declare const MAX_IMPORT_TEXT = 200000;
8
+ export declare const MAX_IMPORT_TITLE_LENGTH = 500;
9
+ export declare const MAX_IMPORT_REFERENCE_LENGTH = 2000;
10
+ export declare const MAX_IMPORT_WARNING_LENGTH = 2000;
11
+ export declare const MAX_IMPORT_AGENT_DESCRIPTION_LENGTH = 4000;
12
+ export declare const MAX_IMPORT_REASON_LENGTH = 4000;
13
+ export interface Attachment {
14
+ id: string;
15
+ boardId: string;
16
+ sha256: string;
17
+ name: string;
18
+ mime: string;
19
+ size: number;
20
+ createdAt: string;
21
+ }
22
+ export interface ImportSection {
23
+ title: string;
24
+ markdown: string;
25
+ reference?: string;
26
+ }
27
+ export type DocumentImportStatus = 'attached' | 'requested' | 'drafted' | 'unavailable' | 'cancelled' | 'committed';
28
+ export interface DocumentImport {
29
+ id: string;
30
+ boardId: string;
31
+ attachmentId: string;
32
+ status: DocumentImportStatus;
33
+ position: {
34
+ x: number;
35
+ y: number;
36
+ };
37
+ sections: ImportSection[];
38
+ warnings: string[];
39
+ agentDescription: string | null;
40
+ reason: string | null;
41
+ committedNodeIds: string[];
42
+ createdAt: string;
43
+ updatedAt: string;
44
+ }
45
+ export declare function storeAttachment(db: Database, input: {
46
+ boardId: string;
47
+ name: string;
48
+ mime: string;
49
+ bytes: Uint8Array;
50
+ }): Attachment;
51
+ export declare function getAttachment(db: Database, id: string): Attachment | null;
52
+ export declare function readAttachmentBytes(db: Database, id: string): Uint8Array | null;
53
+ export declare function copyBoardAttachments(db: Database, sourceBoardId: string, targetBoardId: string, referencedIds: Set<string>): Map<string, string>;
54
+ export declare function createImport(db: Database, attachmentId: string, position: {
55
+ x: number;
56
+ y: number;
57
+ }): DocumentImport;
58
+ export declare function getImport(db: Database, id: string): DocumentImport | null;
59
+ export declare function listImports(db: Database, boardId?: string): DocumentImport[];
60
+ export declare function updateImport(db: Database, id: string, status: DocumentImportStatus, fields?: {
61
+ sections?: ImportSection[];
62
+ warnings?: string[];
63
+ agentDescription?: string;
64
+ reason?: string;
65
+ committedNodeIds?: string[];
66
+ }): DocumentImport;
@@ -7,7 +7,10 @@ import type { CanvasAnnotation, CanvasNodeState, CanvasEdge, CanvasLayout } from
7
7
  import { type AxInteractionInput, type AxInteractionPublicResult } from './ax-interaction.js';
8
8
  import type { PmxAxIntent } from '../shared/ax-intent.js';
9
9
  import type { PmxAxActivityKind, PmxAxApprovalGate, PmxAxCommandDescriptor, PmxAxContext, PmxAxElicitation, PmxAxEvent, PmxAxEvidence, PmxAxEvidenceKind, PmxAxFocusState, PmxAxHostCapability, PmxAxMode, PmxAxModeRequest, PmxAxPolicy, PmxAxReviewAnchorType, PmxAxReviewAnnotation, PmxAxReviewKind, PmxAxReviewRegion, PmxAxReviewSeverity, PmxAxReviewStatus, PmxAxSource, PmxAxState, PmxAxSteeringMessage, PmxAxWorkItem, PmxAxWorkItemStatus } from './ax-state.js';
10
- import type { AxTimelineQuery } from './canvas-db.js';
10
+ import type { AxTimelineQuery, CanvasBoard } from './canvas-db.js';
11
+ import type { Attachment, DocumentImport, ImportSection } from './document-import.js';
12
+ import type { BackupFile, BackupStatus } from './backup.js';
13
+ import type { ExportManifest } from './board-export.js';
11
14
  import { searchNodes } from './spatial-analysis.js';
12
15
  import { diffLayouts } from './mutation-history.js';
13
16
  import { fitCanvasView, gcCanvasSnapshots, listCanvasSnapshots } from './canvas-operations.js';
@@ -155,7 +158,9 @@ export declare class PmxCanvas extends EventEmitter {
155
158
  getAxState(): PmxAxState;
156
159
  getAxContext(options?: {
157
160
  consumer?: string;
158
- }): PmxAxContext;
161
+ }): PmxAxContext & {
162
+ boardId: string | null;
163
+ };
159
164
  setAxFocus(nodeIds: string[], options?: {
160
165
  source?: PmxAxSource;
161
166
  }): PmxAxFocusState;
@@ -201,6 +206,93 @@ export declare class PmxCanvas extends EventEmitter {
201
206
  source?: PmxAxSource;
202
207
  }): AxInteractionPublicResult;
203
208
  getAxTimeline(query?: AxTimelineQuery): ReturnType<typeof canvasState.getAxTimeline>;
209
+ listBoards(): {
210
+ activeBoardId: string | null;
211
+ boards: CanvasBoard[];
212
+ };
213
+ listImports(boardId?: string): DocumentImport[];
214
+ readImport(id: string): {
215
+ import: DocumentImport;
216
+ attachment: Attachment;
217
+ downloadPath: string;
218
+ bytes?: Uint8Array;
219
+ } | null;
220
+ submitImport(id: string, sections: ImportSection[], agentDescription: string, warnings?: string[]): Promise<DocumentImport>;
221
+ markImportUnavailable(id: string, reason: string): Promise<DocumentImport>;
222
+ attachDocument(input: {
223
+ boardId: string;
224
+ name: string;
225
+ mime?: string;
226
+ bytes: Uint8Array;
227
+ x?: number;
228
+ y?: number;
229
+ }): {
230
+ attachment: Attachment;
231
+ import: DocumentImport;
232
+ nodeId: string;
233
+ };
234
+ requestImport(id: string, consent: true, trustedHumanToken: string): Promise<DocumentImport>;
235
+ cancelImport(id: string, trustedHumanToken: string): Promise<DocumentImport>;
236
+ commitImport(id: string, trustedHumanToken: string): Promise<{
237
+ import: DocumentImport;
238
+ nodeIds: string[];
239
+ }>;
240
+ createBoard(name: string, category?: string): Promise<CanvasBoard>;
241
+ /** Rename a board and/or file it under a category (`category: null` removes it). */
242
+ updateBoard(id: string, patch: {
243
+ name?: string;
244
+ category?: string | null;
245
+ }): Promise<void>;
246
+ setBoardReadme(id: string, readmeNodeId: string | null): Promise<void>;
247
+ createBoardFrom(input: {
248
+ sourceBoardId: string;
249
+ name: string;
250
+ category?: string;
251
+ nodeIds?: string[];
252
+ includeReadme?: boolean;
253
+ includeStructure?: boolean;
254
+ preview?: boolean;
255
+ }): Promise<Record<string, unknown>>;
256
+ searchLibrary(query: string, limit?: number): Promise<Record<string, unknown>>;
257
+ /** Budgeted cross-board brief. Budget is measured in UTF-16 characters. */
258
+ getContextBrief(options?: {
259
+ consumer?: string;
260
+ since?: number;
261
+ budget?: number;
262
+ }): Promise<Record<string, unknown>>;
263
+ /** Open a board, or Home with null. */
264
+ openBoard(id: string | null): Promise<void>;
265
+ deleteBoard(id: string): Promise<void>;
266
+ /** Write a self-contained HTML file of a board (default: the open board). */
267
+ exportBoard(options?: {
268
+ board?: string;
269
+ includeFiles?: boolean;
270
+ includeDerivedText?: boolean;
271
+ }): Promise<{
272
+ path: string;
273
+ url: string;
274
+ bytes: number;
275
+ manifest: ExportManifest;
276
+ }>;
277
+ getBackupStatus(): Promise<BackupStatus>;
278
+ /** Back up every board now; `to` and `keep` default to the configured folder and count. */
279
+ backup(options?: {
280
+ to?: string;
281
+ keep?: number;
282
+ }): Promise<{
283
+ backup: BackupFile;
284
+ removed: string[];
285
+ }>;
286
+ /** Back up on a schedule run by the server; `every: 'off'` stops it. */
287
+ setBackupSchedule(options: {
288
+ every: string;
289
+ to?: string;
290
+ keep?: number;
291
+ }): Promise<BackupStatus>;
292
+ /** Replace every board with a backup file; the current file is kept as `canvas.db.before-restore`. */
293
+ restoreBackup(file: string): Promise<void>;
294
+ /** The context read log: which canvas context each agent read and which pinned nodes reached it. */
295
+ getContextReads(limit?: number): ReturnType<typeof canvasState.getContextReads>;
204
296
  listWorkItems(): PmxAxWorkItem[];
205
297
  addWorkItem(input: {
206
298
  title: string;
@@ -390,8 +482,12 @@ export declare class PmxCanvas extends EventEmitter {
390
482
  maxScale?: number;
391
483
  nodeIds?: string[];
392
484
  }): ReturnType<typeof fitCanvasView>;
393
- getLayout(): CanvasLayout;
394
- getNode(id: string): SdkCanvasNode | undefined;
485
+ getLayout(options?: {
486
+ board?: string;
487
+ }): CanvasLayout;
488
+ getNode(id: string, options?: {
489
+ board?: string;
490
+ }): SdkCanvasNode | undefined;
395
491
  search(query: string): ReturnType<typeof searchNodes>;
396
492
  getSpatialContext(): import("./spatial-analysis.js").SpatialContext;
397
493
  undo(): Promise<{
@@ -592,6 +688,9 @@ export type { CanvasNodeState, CanvasEdge, CanvasLayout, ViewportState } from '.
592
688
  export type { CanvasAutomationWebViewOptions, CanvasAutomationWebViewStatus, PrimaryWorkbenchCanvasPromptRequest, PrimaryWorkbenchIntent, } from './server.js';
593
689
  export { emitPrimaryWorkbenchEvent, consumePrimaryWorkbenchIntents, setPrimaryWorkbenchAutoOpenEnabled, setPrimaryWorkbenchCanvasPromptHandler, startCanvasServer, stopCanvasServer, getCanvasServerPort, openUrlInExternalBrowser, getCanvasAutomationWebViewStatus, startCanvasAutomationWebView, stopCanvasAutomationWebView, evaluateCanvasAutomationWebView, resizeCanvasAutomationWebView, screenshotCanvasAutomationWebView, } from './server.js';
594
690
  export { canvasState } from './canvas-state.js';
691
+ export type { CanvasBoard } from './canvas-db.js';
692
+ export type { BackupFile, BackupStatus } from './backup.js';
693
+ export type { ExportManifest } from './board-export.js';
595
694
  export type { CanvasAnnotation, CanvasSnapshot, CanvasSnapshotGcResult, CanvasSnapshotListOptions, } from './canvas-state.js';
596
695
  export { findOpenCanvasPosition } from './placement.js';
597
696
  export { searchNodes, buildSpatialContext, detectClusters, findNeighborhoods } from './spatial-analysis.js';
@@ -114,5 +114,6 @@ export declare function diffLayouts(snapshotName: string, snapshotLayout: {
114
114
  */
115
115
  export declare function formatDiff(diff: SnapshotDiffResult): string;
116
116
  export declare const mutationHistory: MutationHistory;
117
+ /** Direct callers share the same async-local attribution as node mutations. */
117
118
  export declare function setMutationActor(actor: MutationActor | null, writer?: string | null): void;
118
119
  export {};
@@ -10,6 +10,7 @@ export declare function agentSourceLabel(fallback: string): string;
10
10
  /** Runs operations in-process against the shared canvasState singleton. */
11
11
  export declare class LocalOperationInvoker implements OperationInvoker {
12
12
  private readonly source;
13
+ private readonly proxiedRead;
13
14
  /** `source` labels this caller's agent presence ('mcp', 'sdk', …). */
14
15
  constructor(source?: string);
15
16
  invoke(name: string, input: Record<string, unknown>): Promise<unknown>;
@@ -18,6 +19,7 @@ export declare class LocalOperationInvoker implements OperationInvoker {
18
19
  export declare class HttpOperationInvoker implements OperationInvoker {
19
20
  private readonly baseUrl;
20
21
  private readonly source;
22
+ private readonly proxiedRead;
21
23
  /** `source` labels this caller's agent presence on the server ('cli', 'mcp', …). */
22
24
  constructor(baseUrl: string, source?: string);
23
25
  invoke(name: string, rawInput: Record<string, unknown>): Promise<unknown>;
@@ -1,9 +1,15 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import type { OperationInvoker } from './invoker.js';
2
+ import { type OperationInvoker } from './invoker.js';
3
3
  import { type OperationMcpToolHost } from './types.js';
4
+ import { type ContextReadInput } from '../context-reads.js';
4
5
  import { type CompositeToolDefinition } from './composites.js';
5
6
  export interface OperationToolHost extends OperationMcpToolHost {
6
7
  invoker(): OperationInvoker;
8
+ prepareContextRead(board?: string): Promise<{
9
+ boardId: string | null;
10
+ pinnedNodeIds: string[];
11
+ }>;
12
+ recordContextRead(read: ContextReadInput): Promise<void>;
7
13
  }
8
14
  export declare function registerOperationTools(server: McpServer, getHost: () => Promise<OperationToolHost>): void;
9
15
  /**
@@ -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[];