pmx-canvas 0.8.0 → 0.9.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 (341) hide show
  1. package/.github/extensions/pmx-canvas/runtime.json +1 -1
  2. package/.github/extensions/pmx-canvas/tools.json +601 -22
  3. package/CHANGELOG.md +66 -0
  4. package/Readme.md +131 -532
  5. package/dist/canvas/global.css +2759 -510
  6. package/dist/canvas/index.js +168 -107
  7. package/dist/canvas/mcp-app.html +2936 -632
  8. package/dist/json-render/index.css +81 -1
  9. package/dist/json-render/index.js +20 -20
  10. package/dist/types/client/canvas/BarHint.d.ts +12 -2
  11. package/dist/types/client/canvas/BoardLinkDialog.d.ts +3 -1
  12. package/dist/types/client/canvas/BoardPinButton.d.ts +10 -0
  13. package/dist/types/client/canvas/BoardSwitcher.d.ts +1 -1
  14. package/dist/types/client/canvas/CanvasViewport.d.ts +1 -1
  15. package/dist/types/client/canvas/CommandBar.d.ts +3 -4
  16. package/dist/types/client/canvas/ConnectionBanner.d.ts +2 -1
  17. package/dist/types/client/canvas/ContextChip.d.ts +3 -0
  18. package/dist/types/client/canvas/CrossBoardReferences.d.ts +5 -0
  19. package/dist/types/client/canvas/EdgeLayer.d.ts +14 -3
  20. package/dist/types/client/canvas/EmptyState.d.ts +4 -4
  21. package/dist/types/client/canvas/HomeGraphView.d.ts +2 -0
  22. package/dist/types/client/canvas/HomeMapView.d.ts +1 -0
  23. package/dist/types/client/canvas/HomeReviewView.d.ts +6 -0
  24. package/dist/types/client/canvas/HomeView.d.ts +9 -7
  25. package/dist/types/client/canvas/KnowledgeDialog.d.ts +4 -0
  26. package/dist/types/client/canvas/LifecycleChrome.d.ts +5 -0
  27. package/dist/types/client/canvas/LifecycleFields.d.ts +7 -0
  28. package/dist/types/client/canvas/LifecyclePanel.d.ts +1 -0
  29. package/dist/types/client/canvas/MemoryInspector.d.ts +7 -0
  30. package/dist/types/client/canvas/NearPinLayer.d.ts +14 -0
  31. package/dist/types/client/canvas/NodeContextMark.d.ts +40 -7
  32. package/dist/types/client/canvas/RelationPresetsDialog.d.ts +4 -0
  33. package/dist/types/client/canvas/RelationsPanel.d.ts +4 -0
  34. package/dist/types/client/canvas/SessionPanel.d.ts +2 -0
  35. package/dist/types/client/canvas/canvas-area.d.ts +1 -1
  36. package/dist/types/client/canvas/graph-geometry.d.ts +27 -0
  37. package/dist/types/client/canvas/graph-model.d.ts +37 -0
  38. package/dist/types/client/canvas/memory-layout.d.ts +23 -0
  39. package/dist/types/client/canvas/use-chrome-height.d.ts +2 -0
  40. package/dist/types/client/canvas/use-focus-trap.d.ts +1 -0
  41. package/dist/types/client/canvas/use-memory-graph.d.ts +8 -0
  42. package/dist/types/client/canvas/use-pan-zoom.d.ts +4 -1
  43. package/dist/types/client/icons.d.ts +0 -10
  44. package/dist/types/client/nodes/AxStepControls.d.ts +1 -1
  45. package/dist/types/client/nodes/HtmlNode.d.ts +1 -0
  46. package/dist/types/client/nodes/InlineMarkdownEditor.d.ts +4 -4
  47. package/dist/types/client/nodes/MarkdownNode.d.ts +5 -1
  48. package/dist/types/client/nodes/McpAppNode.d.ts +4 -4
  49. package/dist/types/client/nodes/viewer-runtime-state.d.ts +1 -1
  50. package/dist/types/client/state/boards-store.d.ts +81 -9
  51. package/dist/types/client/state/canvas-store.d.ts +14 -1
  52. package/dist/types/client/state/context-chip-store.d.ts +22 -0
  53. package/dist/types/client/state/context-status-store.d.ts +35 -0
  54. package/dist/types/client/state/intent-bridge.d.ts +11 -8
  55. package/dist/types/client/state/knowledge-store.d.ts +22 -0
  56. package/dist/types/client/state/near-pin-store.d.ts +44 -1
  57. package/dist/types/client/state/presence-store.d.ts +4 -4
  58. package/dist/types/client/state/presentation.d.ts +28 -0
  59. package/dist/types/client/state/relation-store.d.ts +10 -0
  60. package/dist/types/client/state/session-store.d.ts +41 -2
  61. package/dist/types/client/state/workspace-settings.d.ts +7 -0
  62. package/dist/types/client/types.d.ts +13 -1
  63. package/dist/types/client/utils/keyboard.d.ts +2 -0
  64. package/dist/types/client/utils/time-ago.d.ts +4 -0
  65. package/dist/types/mcp/context-uri-template.d.ts +3 -1
  66. package/dist/types/server/agent-presence.d.ts +10 -3
  67. package/dist/types/server/ax-context.d.ts +1 -1
  68. package/dist/types/server/canvas-db.d.ts +30 -7
  69. package/dist/types/server/canvas-operations.d.ts +24 -2
  70. package/dist/types/server/canvas-provenance.d.ts +22 -2
  71. package/dist/types/server/canvas-serialization.d.ts +2 -0
  72. package/dist/types/server/canvas-state.d.ts +93 -15
  73. package/dist/types/server/canvas-validation.d.ts +1 -1
  74. package/dist/types/server/context-brief.d.ts +94 -6
  75. package/dist/types/server/context-reads.d.ts +33 -7
  76. package/dist/types/server/edit-summary.d.ts +20 -0
  77. package/dist/types/server/index.d.ts +71 -10
  78. package/dist/types/server/knowledge-lifecycle.d.ts +221 -0
  79. package/dist/types/server/lifecycle-status.d.ts +10 -0
  80. package/dist/types/server/memory-graph.d.ts +9 -0
  81. package/dist/types/server/mutation-history.d.ts +2 -0
  82. package/dist/types/server/operations/ops/ax-shared.d.ts +1 -1
  83. package/dist/types/server/operations/ops/boards.d.ts +2 -1
  84. package/dist/types/server/operations/ops/lifecycle.d.ts +2 -0
  85. package/dist/types/server/operations/ops/memory-graph.d.ts +2 -0
  86. package/dist/types/server/operations/ops/nodes.d.ts +5 -0
  87. package/dist/types/server/operations/ops/query.d.ts +23 -0
  88. package/dist/types/server/operations/registry.d.ts +1 -0
  89. package/dist/types/server/placement.d.ts +7 -0
  90. package/dist/types/server/scope-fence.d.ts +2 -1
  91. package/dist/types/server/spatial-analysis.d.ts +6 -3
  92. package/dist/types/server/tour-control.d.ts +32 -0
  93. package/dist/types/server/wiki-links.d.ts +14 -0
  94. package/dist/types/server/workbench-auth.d.ts +2 -0
  95. package/dist/types/shared/agent-context.d.ts +2 -0
  96. package/dist/types/shared/agent-presence.d.ts +15 -12
  97. package/dist/types/shared/auto-arrange.d.ts +1 -0
  98. package/dist/types/shared/boards.d.ts +4 -4
  99. package/dist/types/shared/knowledge-lifecycle.d.ts +41 -0
  100. package/dist/types/shared/knowledge-presets.d.ts +6 -0
  101. package/dist/types/shared/lifecycle-status.d.ts +24 -0
  102. package/dist/types/shared/memory-graph.d.ts +53 -0
  103. package/dist/types/shared/relations.d.ts +68 -0
  104. package/dist/types/shared/semantic-attention.d.ts +2 -0
  105. package/dist/types/shared/tour.d.ts +38 -0
  106. package/dist/types/shared/workspace-settings.d.ts +11 -0
  107. package/docs/RELEASE.md +21 -3
  108. package/docs/adr-001-bun-only-runtime.md +7 -1
  109. package/docs/adr-002-shared-product-separate-runtimes.md +93 -0
  110. package/docs/api-reference.md +109 -0
  111. package/docs/ax-host-adapter-contract.md +1 -1
  112. package/docs/ax-state-contract.md +5 -4
  113. package/docs/cli.md +153 -15
  114. package/docs/demos/pmx-canvas-demo.mp4 +0 -0
  115. package/docs/design/AgentContext.dc.html +3 -3
  116. package/docs/design/BoardPins.dc.html +37 -22
  117. package/docs/design/Categories.dc.html +208 -0
  118. package/docs/design/CategoryEditor.dc.html +33 -0
  119. package/docs/design/Chrome.dc.html +55 -9
  120. package/docs/design/ContextChip.dc.html +224 -0
  121. package/docs/design/ContextChip600.dc.html +136 -0
  122. package/docs/design/EmptyState.dc.html +45 -0
  123. package/docs/design/Flows.dc.html +23 -1
  124. package/docs/design/HeaderMarks.dc.html +183 -0
  125. package/docs/design/Home.dc.html +19 -1
  126. package/docs/design/HomeGraph.dc.html +86 -202
  127. package/docs/design/HomeMap.dc.html +23 -1
  128. package/docs/design/HomeTeam.dc.html +32 -14
  129. package/docs/design/Lifecycle.dc.html +22 -0
  130. package/docs/design/Lifecycle600.dc.html +18 -0
  131. package/docs/design/LifecycleVariants.dc.html +157 -0
  132. package/docs/design/LinkAuthorship.dc.html +139 -0
  133. package/docs/design/LinksChanged.dc.html +203 -0
  134. package/docs/design/LinksOptions.dc.html +137 -0
  135. package/docs/design/LinksOptions600.dc.html +134 -0
  136. package/docs/design/NearPin.dc.html +9 -10
  137. package/docs/design/Pane600.dc.html +4 -3
  138. package/docs/design/Relations.dc.html +3 -2
  139. package/docs/design/Restore.dc.html +152 -0
  140. package/docs/design/SeeChange.dc.html +267 -0
  141. package/docs/design/Uses.dc.html +12 -6
  142. package/docs/design/canvas.json +146 -19
  143. package/docs/design.md +454 -18
  144. package/docs/environment.md +0 -1
  145. package/docs/evals/curation-effect.md +128 -0
  146. package/docs/http-api.md +465 -38
  147. package/docs/mcp.md +150 -19
  148. package/docs/node-types.md +58 -3
  149. package/docs/plans/design-gaps-2026-10-08.md +53 -0
  150. package/docs/plans/plan-009-tech-debt-backlog.md +379 -0
  151. package/docs/plans/plan-016-context-made-visible-wave-1.md +150 -5
  152. package/docs/plans/plan-017-knowledge-categories-and-lifecycle.md +175 -0
  153. package/docs/plans/plan-018-connected-memory-wave-2.md +594 -0
  154. package/docs/plans/plan-019-post-0.9-roadmap.md +198 -0
  155. package/docs/product-context-vision-2026-09.md +17 -1
  156. package/docs/product-vision-2026-09.md +1082 -43
  157. package/docs/product-vision-review-2026-09.md +13 -1
  158. package/docs/screenshots/workbench-live-dark.png +0 -0
  159. package/docs/screenshots/workbench-live-light.png +0 -0
  160. package/docs/sdk.md +62 -2
  161. package/package.json +1 -1
  162. package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.json +1 -1
  163. package/plugins/pmx-canvas/plugin.json +1 -1
  164. package/plugins/pmx-canvas/runtime.json +1 -1
  165. package/plugins/pmx-canvas/tools.json +601 -22
  166. package/skills/pmx-canvas/SKILL.md +234 -43
  167. package/skills/pmx-canvas/evals/evals.json +62 -0
  168. package/skills/pmx-canvas/references/ax-html-control-surface.md +6 -0
  169. package/skills/pmx-canvas/references/full-reference.md +120 -21
  170. package/skills/pmx-canvas/references/github-copilot-app-adapter.md +2 -2
  171. package/skills/pmx-canvas/references/importing-context.md +132 -0
  172. package/skills/pmx-canvas/references/installing-pmx-canvas.md +6 -6
  173. package/skills/pmx-canvas/references/tours-and-recording.md +16 -1
  174. package/skills/pmx-canvas-testing/SKILL.md +20 -0
  175. package/src/cli/agent.ts +2 -0
  176. package/src/cli/commands/ax.ts +12 -0
  177. package/src/cli/commands/boards.ts +87 -11
  178. package/src/cli/commands/edges.ts +28 -0
  179. package/src/cli/commands/groups.ts +7 -0
  180. package/src/cli/commands/nodes.ts +4 -2
  181. package/src/cli/commands/query.ts +34 -2
  182. package/src/cli/commands/record.ts +199 -48
  183. package/src/cli/index.ts +7 -1
  184. package/src/client/App.tsx +177 -70
  185. package/src/client/canvas/AttentionHistory.tsx +0 -2
  186. package/src/client/canvas/BarHint.tsx +40 -2
  187. package/src/client/canvas/BoardCopyDialog.tsx +1 -1
  188. package/src/client/canvas/BoardFolderDialog.tsx +7 -7
  189. package/src/client/canvas/BoardLinkDialog.tsx +138 -33
  190. package/src/client/canvas/BoardPinButton.tsx +33 -0
  191. package/src/client/canvas/BoardSwitcher.tsx +26 -40
  192. package/src/client/canvas/CanvasNode.tsx +360 -237
  193. package/src/client/canvas/CanvasViewport.tsx +45 -7
  194. package/src/client/canvas/CommandBar.tsx +29 -23
  195. package/src/client/canvas/CommandPalette.tsx +5 -12
  196. package/src/client/canvas/ConnectionBanner.tsx +3 -3
  197. package/src/client/canvas/ContextChip.tsx +296 -0
  198. package/src/client/canvas/ContextMenu.tsx +63 -5
  199. package/src/client/canvas/CrossBoardReferences.tsx +60 -0
  200. package/src/client/canvas/EdgeLayer.tsx +339 -76
  201. package/src/client/canvas/EmptyState.tsx +33 -18
  202. package/src/client/canvas/ExpandedNodeOverlay.tsx +72 -40
  203. package/src/client/canvas/ExternalWriters.tsx +57 -42
  204. package/src/client/canvas/HomeGraphView.tsx +942 -0
  205. package/src/client/canvas/HomeMapView.tsx +641 -0
  206. package/src/client/canvas/HomeReviewView.tsx +167 -0
  207. package/src/client/canvas/HomeView.tsx +687 -160
  208. package/src/client/canvas/IntentLayer.tsx +19 -12
  209. package/src/client/canvas/KnowledgeDialog.tsx +488 -0
  210. package/src/client/canvas/LifecycleChrome.tsx +133 -0
  211. package/src/client/canvas/LifecycleFields.tsx +91 -0
  212. package/src/client/canvas/LifecyclePanel.tsx +533 -0
  213. package/src/client/canvas/MemoryInspector.tsx +104 -0
  214. package/src/client/canvas/Minimap.tsx +3 -3
  215. package/src/client/canvas/NearPinLayer.tsx +187 -0
  216. package/src/client/canvas/NodeContextMark.tsx +175 -97
  217. package/src/client/canvas/Presentation.tsx +23 -57
  218. package/src/client/canvas/RelationPresetsDialog.tsx +413 -0
  219. package/src/client/canvas/RelationsPanel.tsx +82 -0
  220. package/src/client/canvas/SelectionBar.tsx +3 -6
  221. package/src/client/canvas/SessionPanel.tsx +90 -50
  222. package/src/client/canvas/SessionReceipt.tsx +311 -39
  223. package/src/client/canvas/ShortcutOverlay.tsx +1 -1
  224. package/src/client/canvas/SnapshotPanel.tsx +27 -36
  225. package/src/client/canvas/ToolRail.tsx +367 -192
  226. package/src/client/canvas/TopBar.tsx +354 -124
  227. package/src/client/canvas/canvas-area.ts +10 -3
  228. package/src/client/canvas/graph-geometry.ts +195 -0
  229. package/src/client/canvas/graph-model.ts +254 -0
  230. package/src/client/canvas/memory-layout.ts +106 -0
  231. package/src/client/canvas/use-chrome-height.ts +20 -0
  232. package/src/client/canvas/use-focus-trap.ts +31 -5
  233. package/src/client/canvas/use-memory-graph.ts +58 -0
  234. package/src/client/canvas/use-node-drag.ts +2 -1
  235. package/src/client/canvas/use-pan-zoom.ts +64 -7
  236. package/src/client/icons.tsx +0 -63
  237. package/src/client/mcp-app/style.css +0 -6
  238. package/src/client/nodes/AxStepControls.tsx +74 -50
  239. package/src/client/nodes/BoardNode.tsx +5 -1
  240. package/src/client/nodes/ContextNode.tsx +18 -11
  241. package/src/client/nodes/ExtAppFrame.tsx +0 -1
  242. package/src/client/nodes/FileNode.tsx +46 -42
  243. package/src/client/nodes/HtmlNode.tsx +44 -2
  244. package/src/client/nodes/ImageNode.tsx +1 -1
  245. package/src/client/nodes/InlineFormatBar.tsx +6 -9
  246. package/src/client/nodes/InlineMarkdownEditor.tsx +56 -17
  247. package/src/client/nodes/MarkdownNode.tsx +377 -109
  248. package/src/client/nodes/McpAppNode.tsx +50 -20
  249. package/src/client/nodes/MdFormatBar.tsx +21 -18
  250. package/src/client/nodes/PromptNode.tsx +1 -1
  251. package/src/client/nodes/ResponseNode.tsx +1 -1
  252. package/src/client/nodes/viewer-runtime-state.ts +54 -22
  253. package/src/client/state/attention-bridge.ts +64 -15
  254. package/src/client/state/boards-store.ts +193 -39
  255. package/src/client/state/canvas-store.ts +99 -12
  256. package/src/client/state/context-chip-store.ts +70 -0
  257. package/src/client/state/context-status-store.ts +53 -0
  258. package/src/client/state/intent-bridge.ts +51 -30
  259. package/src/client/state/knowledge-store.ts +43 -0
  260. package/src/client/state/near-pin-store.ts +115 -6
  261. package/src/client/state/presence-store.ts +7 -10
  262. package/src/client/state/presentation.ts +91 -0
  263. package/src/client/state/relation-store.ts +81 -0
  264. package/src/client/state/session-store.ts +144 -18
  265. package/src/client/state/sse-bridge.ts +206 -29
  266. package/src/client/state/workspace-settings.ts +11 -0
  267. package/src/client/theme/global.css +2759 -510
  268. package/src/client/types.ts +12 -0
  269. package/src/client/utils/keyboard.ts +10 -0
  270. package/src/client/utils/time-ago.ts +14 -0
  271. package/src/json-render/renderer/index.css +6 -1
  272. package/src/json-render/renderer/index.tsx +31 -1
  273. package/src/mcp/canvas-app.ts +12 -2
  274. package/src/mcp/context-uri-template.ts +7 -4
  275. package/src/mcp/server.ts +63 -41
  276. package/src/mcp/workbench-app.ts +39 -6
  277. package/src/server/agent-presence.ts +128 -28
  278. package/src/server/ax-context.ts +27 -13
  279. package/src/server/board-export.ts +12 -1
  280. package/src/server/canvas-db.ts +331 -42
  281. package/src/server/canvas-operations.ts +196 -84
  282. package/src/server/canvas-provenance.ts +53 -22
  283. package/src/server/canvas-serialization.ts +2 -0
  284. package/src/server/canvas-state.ts +950 -122
  285. package/src/server/canvas-validation.ts +9 -2
  286. package/src/server/context-brief.ts +345 -38
  287. package/src/server/context-reads.ts +179 -30
  288. package/src/server/demo-state.json +355 -24
  289. package/src/server/edit-summary.ts +74 -0
  290. package/src/server/html-surface.ts +8 -1
  291. package/src/server/index.ts +203 -13
  292. package/src/server/knowledge-lifecycle.ts +304 -0
  293. package/src/server/lifecycle-status.ts +232 -0
  294. package/src/server/memory-graph.ts +166 -0
  295. package/src/server/mutation-history.ts +18 -6
  296. package/src/server/operations/composites.ts +32 -11
  297. package/src/server/operations/http.ts +7 -2
  298. package/src/server/operations/index.ts +4 -0
  299. package/src/server/operations/mcp.ts +32 -1
  300. package/src/server/operations/ops/ax-presence.ts +1 -1
  301. package/src/server/operations/ops/ax-read.ts +57 -6
  302. package/src/server/operations/ops/ax-timeline.ts +28 -2
  303. package/src/server/operations/ops/backup.ts +5 -3
  304. package/src/server/operations/ops/batch.ts +35 -4
  305. package/src/server/operations/ops/boards.ts +188 -47
  306. package/src/server/operations/ops/canvas-wire.ts +31 -1
  307. package/src/server/operations/ops/edges.ts +36 -2
  308. package/src/server/operations/ops/groups.ts +6 -1
  309. package/src/server/operations/ops/json-render.ts +5 -1
  310. package/src/server/operations/ops/lifecycle.ts +262 -0
  311. package/src/server/operations/ops/memory-graph.ts +45 -0
  312. package/src/server/operations/ops/nodes.ts +107 -24
  313. package/src/server/operations/ops/query.ts +281 -68
  314. package/src/server/operations/ops/snapshots.ts +73 -1
  315. package/src/server/operations/ops/validate.ts +5 -1
  316. package/src/server/operations/ops/viewport.ts +97 -3
  317. package/src/server/operations/registry.ts +77 -26
  318. package/src/server/placement.ts +29 -0
  319. package/src/server/scope-fence.ts +78 -6
  320. package/src/server/server.ts +115 -16
  321. package/src/server/spatial-analysis.ts +48 -23
  322. package/src/server/tour-control.ts +82 -0
  323. package/src/server/wiki-links.ts +137 -0
  324. package/src/server/workbench-auth.ts +11 -0
  325. package/src/shared/agent-context.ts +3 -0
  326. package/src/shared/agent-presence.ts +15 -15
  327. package/src/shared/auto-arrange.ts +2 -0
  328. package/src/shared/boards.ts +4 -4
  329. package/src/shared/knowledge-lifecycle.ts +22 -0
  330. package/src/shared/knowledge-presets.ts +80 -0
  331. package/src/shared/lifecycle-status.ts +26 -0
  332. package/src/shared/memory-graph.ts +74 -0
  333. package/src/shared/relations.ts +98 -0
  334. package/src/shared/semantic-attention.ts +15 -1
  335. package/src/shared/tour.ts +23 -5
  336. package/src/shared/workspace-settings.ts +13 -0
  337. package/dist/types/client/canvas/ContextPinBar.d.ts +0 -1
  338. package/dist/types/client/canvas/FocusFieldLayer.d.ts +0 -1
  339. package/dist/types/client/canvas/kind-colors.d.ts +0 -3
  340. package/dist/types/server/agent-context.d.ts +0 -22
  341. package/src/client/canvas/ContextPinBar.tsx +0 -25
@@ -45,18 +45,26 @@ export interface AgentPresence {
45
45
  opCount: number;
46
46
  /**
47
47
  * The agent's REAL context window, when its host reports it (tokens used /
48
- * window size). Null when no adapter reports — the top-bar meter then shows
49
- * the pinned-context estimate against the configured budget instead, and
50
- * says so.
48
+ * window size). Null when no adapter reports — the context chip then shows
49
+ * the brief's size against its budget instead.
51
50
  */
52
51
  contextUsage: { used: number; total: number } | null;
53
52
  lastSeenAt: string;
53
+ /**
54
+ * What this session has done since it attached (AgentContext.dc.html lens:
55
+ * "Claude · this session"): ids of the cards read, created, edited and
56
+ * pinned on the open board. Only on attached presences; counts every agent
57
+ * on the board, like the receipt.
58
+ */
59
+ session?: SessionTouches;
54
60
  }
55
61
 
56
- export interface ContextBudget {
57
- /** Estimated tokens of the pinned-context payload. */
58
- used: number;
59
- total: number;
62
+ export interface SessionTouches {
63
+ startedAt: string;
64
+ read: string[];
65
+ created: string[];
66
+ edited: string[];
67
+ pinned: string[];
60
68
  }
61
69
 
62
70
  /**
@@ -79,7 +87,6 @@ export interface AgentActivityEntry {
79
87
 
80
88
  export interface AgentPresenceSnapshot {
81
89
  presences: AgentPresence[];
82
- budget: ContextBudget;
83
90
  /** True when any presence is attached — the master gate for agent chrome. */
84
91
  sessionActive: boolean;
85
92
  /** Most recent agent writes, newest first (bounded by MAX_ACTIVITY_ENTRIES). */
@@ -102,8 +109,6 @@ export const PRESENCE_TOOLING_SETTLE_MS = 4_000;
102
109
  * "Thinking" chips on a silent board. */
103
110
  export const PRESENCE_THINKING_SETTLE_MS = 3 * 60_000;
104
111
  export const MAX_PRESENCES = 16;
105
- /** Default `budget.total` when `PMX_CANVAS_CONTEXT_BUDGET_TOKENS` is unset. */
106
- export const CONTEXT_BUDGET_DEFAULT_TOKENS = 32_000;
107
112
 
108
113
  /**
109
114
  * Source labels that name a TRANSPORT, not an agent. A write arriving under
@@ -130,11 +135,6 @@ export function externalWriters(presences: readonly AgentPresence[]): AgentPrese
130
135
  return presences.filter((presence) => !presence.attached);
131
136
  }
132
137
 
133
- /** Rough token estimate for a JSON payload (chars / 4) — good enough for a meter. */
134
- export function estimateTokens(text: string): number {
135
- return Math.ceil(text.length / 4);
136
- }
137
-
138
138
  /** Chip label per phase — shared by the top-bar chip and the on-canvas cursor chip. */
139
139
  export function agentPhaseLabel(presence: Pick<AgentPresence, 'phase' | 'detail'>): string {
140
140
  switch (presence.phase) {
@@ -21,6 +21,7 @@ export interface ArrangeEdge {
21
21
  id: string;
22
22
  from: string;
23
23
  to: string;
24
+ toBoardId?: string;
24
25
  }
25
26
 
26
27
  export interface AutoArrangeResult {
@@ -184,6 +185,7 @@ function buildUnitGraphs(
184
185
  }
185
186
 
186
187
  for (const edge of edges) {
188
+ if (edge.toBoardId !== undefined) continue;
187
189
  const fromUnit = nodeToUnit.get(edge.from);
188
190
  const toUnit = nodeToUnit.get(edge.to);
189
191
  if (!fromUnit || !toUnit || fromUnit === toUnit) continue;
@@ -1,8 +1,8 @@
1
- /** The Home category migrated 0.6 snapshots land on, so they do not bury the boards in use. */
2
- export const MIGRATED_SNAPSHOT_CATEGORY = 'From old snapshots';
1
+ /** The Home folder migrated 0.6 snapshots land on, so they do not bury the boards in use. */
2
+ export const MIGRATED_SNAPSHOT_FOLDER = 'From old snapshots';
3
3
 
4
4
  /** Slash-separated folder paths shared by the UI, HTTP/MCP and SDK. */
5
- export function normalizeBoardCategory(value: string | null): string | null {
5
+ export function normalizeBoardFolder(value: string | null): string | null {
6
6
  if (!value?.trim()) return null;
7
7
  const parts = value.split('/').map((part) => part.trim());
8
8
  if (parts.length > 8) throw new Error('Folders can be nested up to 8 levels.');
@@ -13,6 +13,6 @@ export function normalizeBoardCategory(value: string | null): string | null {
13
13
  }
14
14
 
15
15
  /** Includes implicit parents, so a board in A/B can also be moved into A. */
16
- export function categoryAncestors(path: string): string[] {
16
+ export function folderAncestors(path: string): string[] {
17
17
  return path.split('/').map((_, index, parts) => parts.slice(0, index + 1).join('/'));
18
18
  }
@@ -0,0 +1,22 @@
1
+ import { z } from 'zod';
2
+
3
+ /** The same policy contract validates workspace writes and browser drafts. */
4
+ export const lifecycleSchema = z
5
+ .strictObject({
6
+ decayType: z.enum(['reality', 'decision', 'dependency', 'relevance']),
7
+ rate: z.enum(['fast', 'medium', 'slow', 'durable']),
8
+ triggers: z.array(z.string().trim().min(1)),
9
+ revalidationMethod: z.string().trim().min(1),
10
+ cadenceDays: z.number().int().min(1).max(36500).optional(),
11
+ })
12
+ .refine((value) => value.rate !== 'medium' || value.cadenceDays !== undefined, {
13
+ path: ['cadenceDays'],
14
+ message: 'Medium lifecycle requires a cadence in days.',
15
+ });
16
+ export const categorySchema = lifecycleSchema.safeExtend({
17
+ id: z.string().trim().min(1),
18
+ name: z.string().trim().min(1),
19
+ glyph: z.string().trim().min(1),
20
+ });
21
+ export type Lifecycle = z.infer<typeof lifecycleSchema>;
22
+ export type KnowledgeCategory = z.infer<typeof categorySchema>;
@@ -0,0 +1,80 @@
1
+ import type { KnowledgeCategory } from '../server/knowledge-lifecycle.js';
2
+
3
+ const category = (
4
+ id: string,
5
+ name: string,
6
+ glyph: string,
7
+ decayType: KnowledgeCategory['decayType'],
8
+ rate: KnowledgeCategory['rate'],
9
+ revalidationMethod: string,
10
+ ): KnowledgeCategory => ({
11
+ id,
12
+ name,
13
+ glyph,
14
+ decayType,
15
+ rate,
16
+ revalidationMethod,
17
+ triggers: [],
18
+ ...(rate === 'medium' ? { cadenceDays: 90 } : {}),
19
+ });
20
+ const research = category(
21
+ 'research',
22
+ 'Research',
23
+ '⌕',
24
+ 'reality',
25
+ 'medium',
26
+ 'Check the sources and conflicting evidence.',
27
+ );
28
+ const metric = category('metric', 'Metric', '▥', 'reality', 'fast', 'Re-read the source dashboard and compare.');
29
+ const decision = category(
30
+ 'decision',
31
+ 'Decision',
32
+ '⚑',
33
+ 'decision',
34
+ 'durable',
35
+ 'Check for a superseding decision and changed dependencies.',
36
+ );
37
+ const notes = category('notes', 'Notes', '✎', 'relevance', 'slow', 'Check relevance to the resumed task.');
38
+
39
+ /** Editable starter data. No runtime behavior depends on these names or ids. */
40
+ export const KNOWLEDGE_PRESETS: Record<string, { categories: KnowledgeCategory[]; folders: Record<string, string> }> = {
41
+ 'Org · team · personal': {
42
+ categories: [
43
+ category('strategy', 'Strategy', '◎', 'relevance', 'slow', 'Check the current goals and decisions.'),
44
+ category('policy', 'Policy', '◇', 'decision', 'slow', 'Check with the policy owner.'),
45
+ decision,
46
+ research,
47
+ metric,
48
+ notes,
49
+ ],
50
+ folders: {
51
+ Org: 'strategy',
52
+ 'Org/Policies': 'policy',
53
+ Team: 'research',
54
+ 'Team/Decisions': 'decision',
55
+ 'Team/Metrics': 'metric',
56
+ Personal: 'notes',
57
+ },
58
+ },
59
+ 'Solo research': {
60
+ categories: [
61
+ category('question', 'Question', '?', 'relevance', 'slow', 'Check whether this question still matters.'),
62
+ category('source', 'Source', '▤', 'reality', 'medium', 'Re-read the original source.'),
63
+ category(
64
+ 'finding',
65
+ 'Finding',
66
+ '◉',
67
+ 'dependency',
68
+ 'durable',
69
+ 'Compare the supporting and contradicting evidence.',
70
+ ),
71
+ category('note', 'Note', '✎', 'relevance', 'slow', 'Check relevance to the resumed task.'),
72
+ ],
73
+ folders: { Projects: 'question', 'Projects/Findings': 'finding', Sources: 'source', Notes: 'note' },
74
+ },
75
+ 'Product team': {
76
+ categories: [research, metric, decision, notes],
77
+ folders: { Discovery: 'research', 'Discovery/Metrics': 'metric', Delivery: 'notes', Decisions: 'decision' },
78
+ },
79
+ Empty: { categories: [], folders: {} },
80
+ };
@@ -0,0 +1,26 @@
1
+ import type { MemoryTarget } from './memory-graph.js';
2
+
3
+ export interface SourceQualification extends MemoryTarget {
4
+ title: string;
5
+ reasons: string[];
6
+ unavailable?: true;
7
+ contentRevision?: number;
8
+ observedRevision?: number;
9
+ observedAt?: string;
10
+ }
11
+
12
+ export interface LifecycleQualification {
13
+ retired: boolean;
14
+ unknown: boolean;
15
+ due: boolean;
16
+ reasons: string[];
17
+ changedSinceRead: boolean;
18
+ lastReadAt: string | null;
19
+ lastValidatedAt: string | null;
20
+ contentRevision: number;
21
+ contentChangedAt: string | null;
22
+ /** The revisions a validating reviewer must have inspected, qualified by source relation. */
23
+ observations: Record<string, number | null>;
24
+ sources: SourceQualification[];
25
+ relevance: 'card-pinned' | 'board-pinned' | 'not-selected';
26
+ }
@@ -0,0 +1,74 @@
1
+ /** Board-qualified identities: card IDs alone are not unique across a library. */
2
+ export interface MemoryTarget {
3
+ boardId: string;
4
+ cardId?: string;
5
+ }
6
+
7
+ export interface WikiLink {
8
+ ref: string;
9
+ target?: MemoryTarget;
10
+ }
11
+
12
+ export function wikiLinksFromData(data: Record<string, unknown>): WikiLink[] {
13
+ if (!Array.isArray(data.wikiLinks)) return [];
14
+ return data.wikiLinks.flatMap((value: unknown) => {
15
+ if (!value || typeof value !== 'object') return [];
16
+ const link = value as Record<string, unknown>;
17
+ if (typeof link.ref !== 'string') return [];
18
+ const target = link.target as Record<string, unknown> | undefined;
19
+ return [
20
+ {
21
+ ref: link.ref,
22
+ ...(target && typeof target.boardId === 'string'
23
+ ? {
24
+ target: {
25
+ boardId: target.boardId,
26
+ ...(typeof target.cardId === 'string' ? { cardId: target.cardId } : {}),
27
+ },
28
+ }
29
+ : {}),
30
+ },
31
+ ];
32
+ });
33
+ }
34
+
35
+ export const memoryKey = (target: MemoryTarget): string => JSON.stringify([target.boardId, target.cardId ?? null]);
36
+
37
+ export interface MemoryGraphNode extends MemoryTarget {
38
+ key: string;
39
+ kind: 'board' | 'card';
40
+ title: string;
41
+ summary: string;
42
+ folder: string | null;
43
+ inContext: boolean;
44
+ summaryOnly: true;
45
+ /** False when the focused view omits incident relations; cannot acknowledge all links. */
46
+ linksComplete?: boolean;
47
+ /** Card link revision captured with this projection. */
48
+ linksRevision?: number;
49
+ type?: string;
50
+ nodeCount?: number;
51
+ position?: { x: number; y: number };
52
+ }
53
+
54
+ export interface MemoryGraphEdge {
55
+ id: string;
56
+ from: string;
57
+ to: string;
58
+ kind: 'member' | 'portal' | 'wiki' | 'card' | 'previous';
59
+ relationKind?: string;
60
+ reason?: string;
61
+ label?: string;
62
+ type?: string;
63
+ source: MemoryTarget;
64
+ target: MemoryTarget;
65
+ missing: boolean;
66
+ }
67
+
68
+ export interface MemoryGraph {
69
+ nodes: MemoryGraphNode[];
70
+ edges: MemoryGraphEdge[];
71
+ unresolvedLinks: Array<{ source: MemoryTarget; ref: string }>;
72
+ focus: string | null;
73
+ depth: number;
74
+ }
@@ -0,0 +1,98 @@
1
+ import { z } from 'zod';
2
+
3
+ export const relationKindSchema = z.string().trim().min(1).max(80);
4
+ export const relationReasonSchema = z.string().trim().max(4000);
5
+ export const relationDefinitionSchema = z.strictObject({
6
+ kind: relationKindSchema,
7
+ ink: z.enum(['supports', 'contradicts', 'cites', 'derived', 'informs', 'related']),
8
+ directed: z.boolean(),
9
+ });
10
+ export const relationVocabularySchema = z
11
+ .array(relationDefinitionSchema)
12
+ .max(64)
13
+ .refine(
14
+ (values) => new Set(values.map((value) => value.kind)).size === values.length,
15
+ 'Relation kinds must be unique.',
16
+ );
17
+ export type RelationDefinition = z.infer<typeof relationDefinitionSchema>;
18
+ export const relationPresetSchema = z.strictObject({
19
+ id: z.string().trim().min(1),
20
+ name: z.string().trim().min(1).max(80),
21
+ vocabulary: relationVocabularySchema,
22
+ });
23
+ export type RelationPreset = z.infer<typeof relationPresetSchema>;
24
+
25
+ export const RELATION_KINDS: RelationDefinition[] = [
26
+ { kind: 'supports', ink: 'supports', directed: true },
27
+ { kind: 'contradicts', ink: 'contradicts', directed: true },
28
+ { kind: 'cites', ink: 'cites', directed: true },
29
+ { kind: 'derived-from', ink: 'derived', directed: true },
30
+ { kind: 'answers', ink: 'informs', directed: true },
31
+ { kind: 'depends-on', ink: 'informs', directed: true },
32
+ { kind: 'supersedes', ink: 'informs', directed: true },
33
+ { kind: 'flow', ink: 'derived', directed: true },
34
+ { kind: 'related', ink: 'related', directed: false },
35
+ ];
36
+
37
+ export const RELATION_PRESETS: Record<string, RelationDefinition[]> = {
38
+ Research: RELATION_KINDS.filter((item) =>
39
+ ['supports', 'contradicts', 'cites', 'derived-from', 'related'].includes(item.kind),
40
+ ),
41
+ Dependencies: RELATION_KINDS.filter((item) => ['depends-on', 'related'].includes(item.kind)),
42
+ Decision: [
43
+ { kind: 'option', ink: 'derived', directed: true },
44
+ { kind: 'if', ink: 'cites', directed: true },
45
+ { kind: 'rejected', ink: 'related', directed: true },
46
+ { kind: 'supersedes', ink: 'informs', directed: true },
47
+ ],
48
+ Flow: [
49
+ { kind: 'next', ink: 'derived', directed: true },
50
+ { kind: 'if-fails', ink: 'contradicts', directed: true },
51
+ { kind: 'loop', ink: 'related', directed: true },
52
+ ],
53
+ };
54
+
55
+ export function relationPreset(vocabulary: RelationDefinition[] = []): string | null {
56
+ return (
57
+ Object.entries(RELATION_PRESETS).find(
58
+ ([, preset]) =>
59
+ preset.length === vocabulary.length &&
60
+ preset.every((item) =>
61
+ vocabulary.some(
62
+ (entry) => entry.kind === item.kind && entry.ink === item.ink && entry.directed === item.directed,
63
+ ),
64
+ ),
65
+ )?.[0] ?? null
66
+ );
67
+ }
68
+
69
+ export function relationKind(edge: { kind?: string; type?: string }): string {
70
+ return (
71
+ edge.kind ??
72
+ (edge.type === 'relation' ? 'related' : edge.type === 'references' ? 'cites' : (edge.type ?? 'related'))
73
+ );
74
+ }
75
+
76
+ /** Direction is semantic, not inferred from ink or arrow decoration. */
77
+ export function relationSourceEnd(edge: { kind?: string; type?: string }): 'from' | 'to' | null {
78
+ const kind = relationKind(edge);
79
+ if (kind === 'depends-on') return 'to';
80
+ if (['derived-from', 'contradicts', 'supersedes'].includes(kind)) return 'from';
81
+ return null;
82
+ }
83
+
84
+ export function relationDefinition(
85
+ edge: { kind?: string; type?: string },
86
+ vocabulary: RelationDefinition[] = [],
87
+ ): RelationDefinition {
88
+ const kind = relationKind(edge);
89
+ return (
90
+ vocabulary.find((item) => item.kind === kind) ??
91
+ RELATION_KINDS.find((item) => item.kind === kind) ?? { kind, ink: 'related', directed: true }
92
+ );
93
+ }
94
+
95
+ /** Keep the authored label and the explanation, rather than treating a label as a kind. */
96
+ export function relationText(edge: { kind?: string; type?: string; label?: string | null; reason?: string }): string {
97
+ return `${edge.kind ?? edge.type ?? 'related'}${edge.label ? ` "${edge.label}"` : ''}${edge.reason ? ` (why: ${edge.reason})` : ''}`;
98
+ }
@@ -22,6 +22,7 @@ export interface SemanticWatchEdgeSummary {
22
22
  edgeType: CanvasEdge['type'];
23
23
  fromId: string;
24
24
  toId: string;
25
+ toBoardId?: string;
25
26
  fromTitle: string | null;
26
27
  toTitle: string | null;
27
28
  }
@@ -135,8 +136,9 @@ function summarizeEdge(edge: CanvasEdge, nodeMap: Map<string, CanvasNodeState>):
135
136
  edgeType: edge.type,
136
137
  fromId: edge.from,
137
138
  toId: edge.to,
139
+ ...(edge.toBoardId !== undefined ? { toBoardId: edge.toBoardId } : {}),
138
140
  fromTitle: getNodeTitle(nodeMap.get(edge.from)),
139
- toTitle: getNodeTitle(nodeMap.get(edge.to)),
141
+ toTitle: edge.toBoardId === undefined ? getNodeTitle(nodeMap.get(edge.to)) : null,
140
142
  };
141
143
  }
142
144
 
@@ -326,6 +328,7 @@ export function formatCompactWatchEvent(event: SemanticWatchEvent): string {
326
328
  }
327
329
 
328
330
  export class SemanticWatchReducer {
331
+ private currentBoardId: string | null | undefined;
329
332
  private currentLayout: CanvasLayout | null = null;
330
333
  private currentPins = new Set<string>();
331
334
  private previousSpatial: SpatialContext | null = null;
@@ -339,6 +342,17 @@ export class SemanticWatchReducer {
339
342
  }
340
343
 
341
344
  handleMessage(message: SseMessage): SemanticWatchEvent[] {
345
+ if (message.event === 'context-pins-changed' || message.event === 'canvas-layout-update') {
346
+ const record = message.data && typeof message.data === 'object' ? (message.data as Record<string, unknown>) : {};
347
+ if (typeof record.boardId === 'string' || record.boardId === null) {
348
+ if (this.currentBoardId !== undefined && record.boardId !== this.currentBoardId) {
349
+ this.currentLayout = null;
350
+ this.currentPins.clear();
351
+ this.previousSpatial = null;
352
+ }
353
+ this.currentBoardId = record.boardId;
354
+ }
355
+ }
342
356
  if (message.event === 'context-pins-changed') {
343
357
  return this.handleContextPinsChanged(message.data);
344
358
  }
@@ -1,8 +1,20 @@
1
1
  import { z } from 'zod';
2
2
 
3
3
  export const cameraSchema = z.object({ x: z.number(), y: z.number(), scale: z.number().positive() });
4
+ /** A world-space box: `{ x, y }` is its top-left corner. */
5
+ export const rectSchema = z.object({
6
+ x: z.number(),
7
+ y: z.number(),
8
+ width: z.number().positive(),
9
+ height: z.number().positive(),
10
+ });
11
+ export const tourTargetSchema = z.union([
12
+ z.strictObject({ nodeId: z.string().min(1) }),
13
+ z.strictObject({ rect: rectSchema }),
14
+ z.strictObject({ viewport: cameraSchema }),
15
+ ]);
4
16
  export const tourStopSchema = z.object({
5
- target: z.union([z.strictObject({ nodeId: z.string().min(1) }), z.strictObject({ viewport: cameraSchema })]),
17
+ target: tourTargetSchema,
6
18
  duration: z.number().nonnegative().max(3600).optional(),
7
19
  easing: z.enum(['linear', 'ease-in-out', 'ease-out']).optional(),
8
20
  padding: z.number().nonnegative().optional(),
@@ -12,6 +24,7 @@ export const tourSchema = z.object({ stops: z.array(tourStopSchema).max(1000) })
12
24
  export type Tour = z.infer<typeof tourSchema>;
13
25
  export type TourStop = z.infer<typeof tourStopSchema>;
14
26
  export type Camera = z.infer<typeof cameraSchema>;
27
+ export type Rect = z.infer<typeof rectSchema>;
15
28
  export interface TourNode {
16
29
  id: string;
17
30
  type: string;
@@ -30,15 +43,20 @@ export function derivedTour(nodes: TourNode[]): Tour {
30
43
 
31
44
  export function resolveStop(stop: TourStop, nodes: TourNode[], width: number, height: number): Camera {
32
45
  if ('viewport' in stop.target) return stop.target.viewport;
46
+ if ('rect' in stop.target) return fitRect(stop.target.rect, stop.padding, width, height);
33
47
  const id = stop.target.nodeId;
34
48
  const node = nodes.find((n) => n.id === id);
35
49
  if (!node) throw new Error(`Tour target not found: ${id}`);
36
- const padding = stop.padding ?? 40;
50
+ return fitRect({ ...node.position, ...node.size }, stop.padding, width, height);
51
+ }
52
+
53
+ /** Centre a world-space rect in a width×height area with screen-space padding. */
54
+ function fitRect(rect: Rect, padding = 40, width: number, height: number): Camera {
37
55
  if (padding * 2 >= Math.min(width, height)) throw new Error('Tour padding exceeds viewport');
38
- const scale = Math.min((width - padding * 2) / node.size.width, (height - padding * 2) / node.size.height);
56
+ const scale = Math.min((width - padding * 2) / rect.width, (height - padding * 2) / rect.height);
39
57
  return {
40
- x: width / 2 - (node.position.x + node.size.width / 2) * scale,
41
- y: height / 2 - (node.position.y + node.size.height / 2) * scale,
58
+ x: width / 2 - (rect.x + rect.width / 2) * scale,
59
+ y: height / 2 - (rect.y + rect.height / 2) * scale,
42
60
  scale,
43
61
  };
44
62
  }
@@ -0,0 +1,13 @@
1
+ import { z } from 'zod';
2
+
3
+ export const workspaceSettingsSchema = z.strictObject({
4
+ agentSessions: z.boolean(),
5
+ knowledgeLifecycle: z.boolean(),
6
+ });
7
+
8
+ export type WorkspaceSettings = z.infer<typeof workspaceSettingsSchema>;
9
+ export const workspaceSettingsPatchSchema = workspaceSettingsSchema.partial();
10
+ export const DEFAULT_WORKSPACE_SETTINGS: WorkspaceSettings = {
11
+ agentSessions: false,
12
+ knowledgeLifecycle: false,
13
+ };
@@ -1 +0,0 @@
1
- export declare function ContextPinBar(): import("preact/src").JSX.Element | null;
@@ -1 +0,0 @@
1
- export declare function FocusFieldLayer(): import("preact/src").JSX.Element | null;
@@ -1,3 +0,0 @@
1
- import type { CanvasNodeState } from '../types';
2
- /** Kind color per node type, as CSS tokens so every theme keeps its palette. */
3
- export declare const KIND_COLOR: Record<CanvasNodeState['type'], string>;
@@ -1,22 +0,0 @@
1
- import type { CanvasNodeState } from './canvas-state.js';
2
- export interface AgentContextNode {
3
- id: string;
4
- type: CanvasNodeState['type'];
5
- kind: string;
6
- title: string | null;
7
- content: string | null;
8
- metadata?: Record<string, unknown>;
9
- position?: {
10
- x: number;
11
- y: number;
12
- };
13
- }
14
- interface AgentContextOptions {
15
- defaultTextLength?: number;
16
- webpageTextLength?: number;
17
- includePosition?: boolean;
18
- }
19
- export declare function summarizeNodeForAgentContext(node: CanvasNodeState, options?: AgentContextOptions): string;
20
- export declare function serializeNodeForAgentContext(node: CanvasNodeState, options?: AgentContextOptions): AgentContextNode;
21
- export declare function buildAgentContextPreamble(nodes: CanvasNodeState[], options?: AgentContextOptions): string;
22
- export {};
@@ -1,25 +0,0 @@
1
- import { clearContextPins, contextPinnedNodeIds } from '../state/canvas-store';
2
- import { attentionHistoryOpen } from '../state/attention-store';
3
- import { IconClose } from '../icons';
4
-
5
- export function ContextPinBar() {
6
- const count = contextPinnedNodeIds.value.size;
7
- if (count === 0 || attentionHistoryOpen.value) return null;
8
-
9
- return (
10
- <div class="context-pin-bar">
11
- <span class="context-pin-bar-count">
12
- {'\u2726'} {count} node{count !== 1 ? 's' : ''} in context
13
- </span>
14
- <button
15
- type="button"
16
- class="context-pin-bar-btn context-pin-bar-clear"
17
- onClick={clearContextPins}
18
- title="Clear all context pins"
19
- aria-label="Clear all context pins"
20
- >
21
- <IconClose size={14} />
22
- </button>
23
- </div>
24
- );
25
- }