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
@@ -0,0 +1,74 @@
1
+ import type { CanvasNodeState } from './canvas-state.js';
2
+
3
+ const ORDINALS = ['first', 'second', 'third', 'fourth', 'fifth', 'sixth', 'seventh', 'eighth', 'ninth', 'tenth'];
4
+ /** "See change" shows this much of each side, centred on the first difference. */
5
+ export const CHANGE_EXCERPT = 1200;
6
+
7
+ export function cardText(node: CanvasNodeState): string {
8
+ const { content, text } = node.data;
9
+ return typeof content === 'string' ? content : typeof text === 'string' ? text : '';
10
+ }
11
+
12
+ function cardTitle(node: CanvasNodeState): string {
13
+ return typeof node.data.title === 'string' ? node.data.title : '';
14
+ }
15
+
16
+ const paragraphs = (text: string) =>
17
+ text
18
+ .split(/\n\s*\n/)
19
+ .map((paragraph) => paragraph.trim().replace(/\s+/g, ' '))
20
+ .filter(Boolean);
21
+
22
+ /**
23
+ * What an agent's edit did to a card, in a few words, from the card before
24
+ * the session and now (AgentContext.dc.html: "rewrote the second paragraph").
25
+ * Paragraphs are aligned on their shared start and end, so a paragraph added
26
+ * at the top reads as added, not as everything rewritten.
27
+ */
28
+ export function describeEdit(before: CanvasNodeState, after: CanvasNodeState): string {
29
+ const parts: string[] = [];
30
+ if (cardTitle(before) !== cardTitle(after)) parts.push(`renamed it “${cardTitle(after)}”`);
31
+ const was = cardText(before);
32
+ const now = cardText(after);
33
+ if (was !== now) {
34
+ const a = paragraphs(was);
35
+ const b = paragraphs(now);
36
+ let head = 0;
37
+ while (head < a.length && head < b.length && a[head] === b[head]) head += 1;
38
+ let tail = 0;
39
+ while (tail < a.length - head && tail < b.length - head && a[a.length - 1 - tail] === b[b.length - 1 - tail])
40
+ tail += 1;
41
+ const removed = a.length - head - tail;
42
+ const added = b.length - head - tail;
43
+ const count = (n: number) => `${n} paragraph${n === 1 ? '' : 's'}`;
44
+ if (removed === 0 && added === 0) parts.push('reformatted its text');
45
+ else if (a.length === 0) parts.push('wrote its text');
46
+ else if (b.length === 0) parts.push('cleared its text');
47
+ else if (removed === 0) parts.push(`added ${count(added)}`);
48
+ else if (added === 0) parts.push(`removed ${count(removed)}`);
49
+ else if (removed === 1 && added === 1) parts.push(`rewrote the ${ORDINALS[head] ?? `#${head + 1}`} paragraph`);
50
+ else if (Math.max(removed, added) > Math.max(a.length, b.length) / 2) parts.push('rewrote most of it');
51
+ else parts.push(`changed ${count(Math.max(removed, added))} of ${Math.max(a.length, b.length)}`);
52
+ }
53
+ return parts.join(' and ') || 'changed its settings';
54
+ }
55
+
56
+ /**
57
+ * Before and after for "See change": each side cut to CHANGE_EXCERPT around
58
+ * the first character that differs, with "…" where text was cut, so a change
59
+ * deep in a long card is what shows.
60
+ */
61
+ export function changeExcerpts(was: string, now: string): { before: string; after: string } {
62
+ let at = 0;
63
+ while (at < was.length && at < now.length && was[at] === now[at]) at += 1;
64
+ const from = Math.max(0, at - Math.floor(CHANGE_EXCERPT / 4));
65
+ // Never cut between the two halves of a surrogate pair (an emoji).
66
+ const low = (text: string, index: number) => /[\uDC00-\uDFFF]/.test(text[index] ?? '');
67
+ const cut = (text: string) => {
68
+ const start = low(text, from) ? from - 1 : from;
69
+ let end = start + CHANGE_EXCERPT;
70
+ if (low(text, end)) end -= 1;
71
+ return `${start > 0 ? '…' : ''}${text.slice(start, end)}${end < text.length ? '…' : ''}`;
72
+ };
73
+ return { before: cut(was), after: cut(now) };
74
+ }
@@ -60,7 +60,14 @@ function buildThemeBridge(themeToken: string): string {
60
60
  const PMX_CANVAS_THEME_TOKEN = ${token};
61
61
  window.addEventListener('message', (event) => {
62
62
  const message = event.data;
63
- if (!message || message.source !== '${HTML_SURFACE_PUSH_SOURCE}' || message.type !== 'theme-update' || message.token !== PMX_CANVAS_THEME_TOKEN) return;
63
+ if (event.source !== window.parent || !message || message.source !== '${HTML_SURFACE_PUSH_SOURCE}' || message.token !== PMX_CANVAS_THEME_TOKEN) return;
64
+ if (message.type === 'check-ready') {
65
+ const reply = (type) => window.parent.postMessage({ source: '${HTML_SURFACE_PUSH_SOURCE}', type, token: PMX_CANVAS_THEME_TOKEN }, '*');
66
+ reply('mounted');
67
+ requestAnimationFrame(() => requestAnimationFrame(() => reply('ready')));
68
+ return;
69
+ }
70
+ if (message.type !== 'theme-update') return;
64
71
  if (typeof message.theme !== 'string') return;
65
72
  document.documentElement.setAttribute('data-pmx-canvas-theme', message.theme);
66
73
  document.documentElement.setAttribute('data-theme', message.theme);
@@ -1,6 +1,8 @@
1
1
  import { EventEmitter } from 'node:events';
2
- import { derivedTour, type Tour } from '../shared/tour.js';
2
+ import { derivedTour, type Camera, type Tour, type TourStop } from '../shared/tour.js';
3
+ import { exitTour, goToTourStop, moveCamera, type TourStep, type TourStepResult } from './tour-control.js';
3
4
  export type { Tour, TourStop } from '../shared/tour.js';
5
+ export type { TourStep, TourStepResult } from './tour-control.js';
4
6
  import { agentPresence } from './agent-presence.js';
5
7
  import { checkFenceTarget, type FenceTarget } from './scope-fence.js';
6
8
  import type { AgentPhase, AgentPresence, AgentPresenceSnapshot } from '../shared/agent-presence.js';
@@ -38,6 +40,7 @@ import type {
38
40
  PmxAxWorkItemStatus,
39
41
  } from './ax-state.js';
40
42
  import type { AxTimelineQuery, CanvasBoard } from './canvas-db.js';
43
+ import type { MemoryGraph } from '../shared/memory-graph.js';
41
44
  import type { Attachment, DocumentImport, ImportSection } from './document-import.js';
42
45
  import { workbenchToken } from './workbench-auth.js';
43
46
  import type { BackupFile, BackupStatus } from './backup.js';
@@ -45,7 +48,8 @@ import type { ExportManifest } from './board-export.js';
45
48
  import { onFileNodeChanged } from './file-watcher.js';
46
49
  import { findOpenCanvasPosition, computeGroupBounds } from './placement.js';
47
50
  import { overlapsAny } from '../shared/placement.js';
48
- import { searchNodes, buildSpatialContext } from './spatial-analysis.js';
51
+ import { buildSpatialContext } from './spatial-analysis.js';
52
+ import { searchCanvasNodes } from './operations/ops/query.js';
49
53
  import { mutationHistory, diffLayouts, formatDiff } from './mutation-history.js';
50
54
  import { recomputeCodeGraph, buildCodeGraphSummary, formatCodeGraph } from './code-graph.js';
51
55
  import {
@@ -75,14 +79,17 @@ import {
75
79
  import {
76
80
  buildNodePatch,
77
81
  createBasicCanvasNode,
82
+ createDestinationNode,
78
83
  removeNodeCore,
79
84
  setGroupChildrenFromApi,
80
85
  } from './operations/ops/nodes.js';
81
86
  import { streamJsonRenderCore } from './operations/ops/json-render.js';
87
+ import { boardsPayload } from './operations/ops/boards.js';
82
88
  import { executeOperation, runCanvasBatchOperation, type OpenMcpAppCoreResult } from './operations/index.js';
83
89
  import { validateCanvasLayout } from './canvas-validation.js';
84
90
  import { describeCanvasSchema, validateStructuredCanvasPayload } from './canvas-schema.js';
85
91
  import { serializeCanvasNode, type SerializedCanvasNode } from './canvas-serialization.js';
92
+ import { contextReadFromPayload } from './context-reads.js';
86
93
  import {
87
94
  buildHtmlPrimitive,
88
95
  getHtmlPrimitiveSemanticMetadata,
@@ -288,10 +295,14 @@ export class PmxCanvas extends EventEmitter {
288
295
  * or keep the whole node — both work. (Previously returned a bare id string.)
289
296
  */
290
297
  addNode(input: {
298
+ destinationBoardId?: string;
299
+ requestId?: string;
300
+ expectedBoardId?: string | null;
291
301
  intentId?: string;
292
302
  type: CanvasNodeState['type'];
293
303
  title?: string;
294
304
  content?: string;
305
+ data?: Record<string, unknown>;
295
306
  children?: string[];
296
307
  childIds?: string[];
297
308
  childLayout?: 'grid' | 'column' | 'flow';
@@ -308,6 +319,19 @@ export class PmxCanvas extends EventEmitter {
308
319
  height?: number;
309
320
  strictSize?: boolean;
310
321
  }): SdkCanvasNode {
322
+ if (input.expectedBoardId !== undefined && input.expectedBoardId !== canvasState.activeBoardId)
323
+ throw new Error('The active board changed. Refresh before retrying this action.');
324
+ if (input.destinationBoardId !== undefined || input.requestId !== undefined) {
325
+ const revision = canvasState.getContentRevision().revision;
326
+ const node = createDestinationNode(input);
327
+ if (
328
+ (input.destinationBoardId ?? canvasState.activeBoardId) === canvasState.activeBoardId ||
329
+ revision !== canvasState.getContentRevision().revision
330
+ )
331
+ emitPrimaryWorkbenchEvent('canvas-layout-update', { layout: canvasState.getLayout() });
332
+ emitPrimaryWorkbenchEvent('boards-changed', boardsPayload());
333
+ return toSdkNode(node);
334
+ }
311
335
  assertInsideFence(
312
336
  'node.add',
313
337
  createTarget(input, input.type === 'group' ? (input.childIds ?? input.children ?? []) : []),
@@ -441,17 +465,41 @@ export class PmxCanvas extends EventEmitter {
441
465
  }
442
466
 
443
467
  addEdge(input: {
468
+ destinationBoardId?: string;
469
+ requestId?: string;
470
+ expectedBoardId?: string | null;
444
471
  intentId?: string;
445
472
  from?: string;
446
473
  to?: string;
474
+ toBoardId?: string;
447
475
  fromSearch?: string;
448
476
  toSearch?: string;
449
477
  type: CanvasEdge['type'];
478
+ kind?: string;
479
+ reason?: string;
450
480
  label?: string;
451
481
  style?: CanvasEdge['style'];
452
482
  animated?: boolean;
453
483
  }): string {
454
- assertInsideFence('edge.add', { nodeIds: [input.from ?? '', input.to ?? ''] });
484
+ if (input.expectedBoardId !== undefined && input.expectedBoardId !== canvasState.activeBoardId)
485
+ throw new Error('The active board changed. Refresh before retrying this action.');
486
+ if (input.destinationBoardId !== undefined || input.requestId !== undefined) {
487
+ const revision = canvasState.getContentRevision().revision;
488
+ const edge = addCanvasEdge(input);
489
+ if (
490
+ (input.destinationBoardId ?? canvasState.activeBoardId) === canvasState.activeBoardId ||
491
+ revision !== canvasState.getContentRevision().revision
492
+ )
493
+ emitPrimaryWorkbenchEvent('canvas-layout-update', { layout: canvasState.getLayout() });
494
+ emitPrimaryWorkbenchEvent('boards-changed', boardsPayload());
495
+ return edge.id;
496
+ }
497
+ assertInsideFence('edge.add', {
498
+ nodeIds:
499
+ input.toBoardId && input.toBoardId !== canvasState.activeBoardId
500
+ ? [input.from ?? '']
501
+ : [input.from ?? '', input.to ?? ''],
502
+ });
455
503
  return this.runIntentCommit(
456
504
  input.intentId,
457
505
  ['connect'],
@@ -464,6 +512,13 @@ export class PmxCanvas extends EventEmitter {
464
512
  );
465
513
  }
466
514
 
515
+ async updateEdge(
516
+ id: string,
517
+ patch: Partial<Pick<CanvasEdge, 'type' | 'kind' | 'reason' | 'label' | 'style' | 'animated'>>,
518
+ ): Promise<CanvasEdge> {
519
+ return (await executeOperation('edge.update', { id, ...patch }, { source: 'sdk' })) as CanvasEdge;
520
+ }
521
+
467
522
  addAnnotation(input: Omit<CanvasAnnotation, 'id' | 'createdAt'> & { id?: string; createdAt?: string }): string {
468
523
  const id = input.id ?? `ann-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
469
524
  canvasState.addAnnotation({
@@ -483,7 +538,8 @@ export class PmxCanvas extends EventEmitter {
483
538
 
484
539
  removeEdge(id: string): void {
485
540
  const edge = canvasState.getEdges().find((entry) => entry.id === id);
486
- if (edge) assertInsideFence('edge.remove', { nodeIds: [edge.from, edge.to] });
541
+ if (edge)
542
+ assertInsideFence('edge.remove', { nodeIds: edge.toBoardId !== undefined ? [edge.from] : [edge.from, edge.to] });
487
543
  removeCanvasEdge(id);
488
544
  emitPrimaryWorkbenchEvent('canvas-layout-update', { layout: canvasState.getLayout() });
489
545
  }
@@ -493,6 +549,9 @@ export class PmxCanvas extends EventEmitter {
493
549
  * If childIds are provided, the group auto-sizes to contain them with padding.
494
550
  */
495
551
  createGroup(input: {
552
+ destinationBoardId?: string;
553
+ requestId?: string;
554
+ expectedBoardId?: string | null;
496
555
  intentId?: string;
497
556
  title?: string;
498
557
  childIds?: string[];
@@ -503,6 +562,19 @@ export class PmxCanvas extends EventEmitter {
503
562
  color?: string;
504
563
  childLayout?: 'grid' | 'column' | 'flow';
505
564
  }): string {
565
+ if (input.expectedBoardId !== undefined && input.expectedBoardId !== canvasState.activeBoardId)
566
+ throw new Error('The active board changed. Refresh before retrying this action.');
567
+ if (input.destinationBoardId !== undefined || input.requestId !== undefined) {
568
+ const revision = canvasState.getContentRevision().revision;
569
+ const { id } = createCanvasGroup(input);
570
+ if (
571
+ (input.destinationBoardId ?? canvasState.activeBoardId) === canvasState.activeBoardId ||
572
+ revision !== canvasState.getContentRevision().revision
573
+ )
574
+ emitPrimaryWorkbenchEvent('canvas-layout-update', { layout: canvasState.getLayout() });
575
+ emitPrimaryWorkbenchEvent('boards-changed', boardsPayload());
576
+ return id;
577
+ }
506
578
  assertInsideFence('group.create', { nodeIds: input.childIds ?? [] });
507
579
  return this.runIntentCommit(
508
580
  input.intentId,
@@ -570,6 +642,26 @@ export class PmxCanvas extends EventEmitter {
570
642
  emitPrimaryWorkbenchEvent('canvas-layout-update', { layout: canvasState.getLayout() });
571
643
  }
572
644
 
645
+ /** Present a tour stop (index, `next` or `previous`) in the workbench. Not recorded in undo history. */
646
+ goToTourStop(step: TourStep, options?: { present?: boolean }): TourStepResult {
647
+ const result = goToTourStop(step);
648
+ emitPrimaryWorkbenchEvent('canvas-tour-step', { ...result, present: options?.present !== false });
649
+ return result;
650
+ }
651
+
652
+ /** Leave presentation in every viewer. */
653
+ exitTour(): void {
654
+ exitTour();
655
+ emitPrimaryWorkbenchEvent('canvas-tour-exit', {});
656
+ }
657
+
658
+ /** Ease the camera to a node, world rect or viewport. Not recorded in undo history. */
659
+ moveCamera(stop: TourStop): { viewport: Camera } {
660
+ const result = moveCamera(stop);
661
+ emitPrimaryWorkbenchEvent('canvas-camera-move', result);
662
+ return { viewport: result.viewport };
663
+ }
664
+
573
665
  clear(): void {
574
666
  assertInsideFence('canvas.clear', { boardWide: true });
575
667
  for (const node of canvasState.getLayout().nodes) {
@@ -804,10 +896,10 @@ export class PmxCanvas extends EventEmitter {
804
896
  )) as { import: DocumentImport; nodeIds: string[] };
805
897
  }
806
898
 
807
- async createBoard(name: string, category?: string): Promise<CanvasBoard> {
899
+ async createBoard(name: string, folder?: string): Promise<CanvasBoard> {
808
900
  const result = (await executeOperation(
809
901
  'board.create',
810
- { name, ...(category ? { category } : {}) },
902
+ { name, ...(folder ? { folder } : {}) },
811
903
  { source: 'sdk' },
812
904
  )) as {
813
905
  board: CanvasBoard;
@@ -815,8 +907,27 @@ export class PmxCanvas extends EventEmitter {
815
907
  return result.board;
816
908
  }
817
909
 
818
- /** Rename a board and/or file it under a category (`category: null` removes it). */
819
- async updateBoard(id: string, patch: { name?: string; category?: string | null }): Promise<void> {
910
+ /** Workspace category and folder actions do not open a board. */
911
+ async knowledge<T extends import('./knowledge-lifecycle.js').KnowledgeInput>(
912
+ input: T,
913
+ ): Promise<import('./knowledge-lifecycle.js').KnowledgeResults[T['action']]> {
914
+ const { action, ...payload } = input;
915
+ return (await executeOperation(`knowledge.${action}`, payload, {
916
+ source: 'sdk',
917
+ })) as import('./knowledge-lifecycle.js').KnowledgeResults[T['action']];
918
+ }
919
+
920
+ async lifecycle<T extends import('./knowledge-lifecycle.js').LifecycleInput>(
921
+ input: T,
922
+ ): Promise<import('./knowledge-lifecycle.js').LifecycleResults[T['action']]> {
923
+ const { action, ...payload } = input;
924
+ return (await executeOperation(action === 'review' ? 'node.review' : `node.lifecycle-${action}`, payload, {
925
+ source: 'sdk',
926
+ })) as import('./knowledge-lifecycle.js').LifecycleResults[T['action']];
927
+ }
928
+
929
+ /** Rename a board and/or file it under a folder (`folder: null` unfiles it). */
930
+ async updateBoard(id: string, patch: Parameters<typeof canvasState.updateBoard>[1]): Promise<void> {
820
931
  await executeOperation('board.update', { id, ...patch }, { source: 'sdk' });
821
932
  }
822
933
 
@@ -824,10 +935,19 @@ export class PmxCanvas extends EventEmitter {
824
935
  await executeOperation('board.update', { id, readmeNodeId }, { source: 'sdk' });
825
936
  }
826
937
 
938
+ /** Pin a board into the agent's working set (its README and pinned cards reach every brief). */
939
+ async pinBoard(id: string, reason?: string): Promise<void> {
940
+ await executeOperation('board.pin', { id, ...(reason ? { reason } : {}) }, { source: 'sdk' });
941
+ }
942
+
943
+ async unpinBoard(id: string): Promise<void> {
944
+ await executeOperation('board.unpin', { id }, { source: 'sdk' });
945
+ }
946
+
827
947
  async createBoardFrom(input: {
828
948
  sourceBoardId: string;
829
949
  name: string;
830
- category?: string;
950
+ folder?: string;
831
951
  nodeIds?: string[];
832
952
  includeReadme?: boolean;
833
953
  includeStructure?: boolean;
@@ -843,6 +963,13 @@ export class PmxCanvas extends EventEmitter {
843
963
  >;
844
964
  }
845
965
 
966
+ /** Same projection as Home Graph. Discovery summaries never count as full-content reads. */
967
+ async getMemoryGraph(
968
+ options: { focus?: string; depth?: number; projection?: 'full' | 'boards' } = {},
969
+ ): Promise<MemoryGraph> {
970
+ return (await executeOperation('memory-graph.get', options, { source: 'sdk' })) as MemoryGraph;
971
+ }
972
+
846
973
  /** Budgeted cross-board brief. Budget is measured in UTF-16 characters. */
847
974
  async getContextBrief(
848
975
  options: { consumer?: string; since?: number; budget?: number } = {},
@@ -911,6 +1038,11 @@ export class PmxCanvas extends EventEmitter {
911
1038
  return canvasState.getNodeReadStatus(board ?? canvasState.activeBoardId);
912
1039
  }
913
1040
 
1041
+ /** A board's latest agent read of any kind (a pinned board's map counts), or null. */
1042
+ getBoardLastRead(board?: string): ReturnType<typeof canvasState.getBoardLastRead> {
1043
+ return canvasState.getBoardLastRead(board ?? canvasState.activeBoardId);
1044
+ }
1045
+
914
1046
  listWorkItems(): PmxAxWorkItem[] {
915
1047
  return canvasState.getWorkItems();
916
1048
  }
@@ -1253,14 +1385,36 @@ export class PmxCanvas extends EventEmitter {
1253
1385
  }
1254
1386
 
1255
1387
  getNode(id: string, options?: { board?: string }): SdkCanvasNode | undefined {
1388
+ const boardId = options?.board ?? canvasState.activeBoardId;
1389
+ const board = options?.board ? canvasState.readBoard(options.board) : null;
1390
+ if (options?.board && !board) throw new Error(`Board "${options.board}" not found.`);
1256
1391
  const node = options?.board
1257
- ? this.getLayout(options).nodes.find((candidate) => candidate.id === id)
1392
+ ? board?.layout.nodes.find((candidate) => candidate.id === id)
1258
1393
  : canvasState.getNode(id);
1259
- return node ? toSdkNode(node) : undefined;
1394
+ if (!node) return undefined;
1395
+ const result = toSdkNode(node);
1396
+ canvasState.recordContextRead(
1397
+ contextReadFromPayload(
1398
+ {
1399
+ channel: 'operation',
1400
+ resource: 'node.get',
1401
+ source: 'sdk',
1402
+ consumer: null,
1403
+ agentId: null,
1404
+ pinnedNodeIds: options?.board ? (board?.state.contextPins ?? []) : [...canvasState.contextPinnedNodeIds],
1405
+ },
1406
+ result,
1407
+ ),
1408
+ boardId,
1409
+ );
1410
+ return result;
1260
1411
  }
1261
1412
 
1262
- search(query: string): ReturnType<typeof searchNodes> {
1263
- return searchNodes(canvasState.getLayout().nodes, query);
1413
+ search(
1414
+ query: string,
1415
+ options: { scope?: 'active' | 'library'; boardId?: string; folder?: string; limit?: number } = {},
1416
+ ) {
1417
+ return searchCanvasNodes(query, options).results;
1264
1418
  }
1265
1419
 
1266
1420
  getSpatialContext() {
@@ -1273,6 +1427,10 @@ export class PmxCanvas extends EventEmitter {
1273
1427
  if (!entry) return { ok: false, description: 'Nothing to undo' };
1274
1428
  await syncCanvasRuntimeBackends();
1275
1429
  emitPrimaryWorkbenchEvent('canvas-layout-update', { layout: canvasState.getLayout() });
1430
+ emitPrimaryWorkbenchEvent('context-pins-changed', {
1431
+ count: canvasState.contextPinnedNodeIds.size,
1432
+ nodeIds: Array.from(canvasState.contextPinnedNodeIds),
1433
+ });
1276
1434
  // Undo can reverse an AX mutation (work item, focus, …); nudge AX surfaces to
1277
1435
  // re-fetch so a live board reflects the reversal (debounced client-side).
1278
1436
  emitPrimaryWorkbenchEvent('ax-state-changed', {});
@@ -1284,6 +1442,10 @@ export class PmxCanvas extends EventEmitter {
1284
1442
  if (!entry) return { ok: false, description: 'Nothing to redo' };
1285
1443
  await syncCanvasRuntimeBackends();
1286
1444
  emitPrimaryWorkbenchEvent('canvas-layout-update', { layout: canvasState.getLayout() });
1445
+ emitPrimaryWorkbenchEvent('context-pins-changed', {
1446
+ count: canvasState.contextPinnedNodeIds.size,
1447
+ nodeIds: Array.from(canvasState.contextPinnedNodeIds),
1448
+ });
1287
1449
  emitPrimaryWorkbenchEvent('ax-state-changed', {});
1288
1450
  return { ok: true, description: `Redid: ${entry.description}` };
1289
1451
  }
@@ -1339,6 +1501,24 @@ export class PmxCanvas extends EventEmitter {
1339
1501
  return result;
1340
1502
  }
1341
1503
 
1504
+ /** Put one card back to its content in a snapshot (the receipt's Undo on an edit). */
1505
+ async restoreSnapshotNode(
1506
+ snapshotId: string,
1507
+ nodeId: string,
1508
+ options: { preserveHumanEdits?: boolean } = {},
1509
+ ): Promise<{ ok: boolean; id: string; restored: boolean; skipped?: 'human-edit' }> {
1510
+ return (await executeOperation(
1511
+ 'snapshot.restore-node',
1512
+ { id: snapshotId, nodeId, ...options },
1513
+ { source: 'sdk' },
1514
+ )) as {
1515
+ ok: boolean;
1516
+ id: string;
1517
+ restored: boolean;
1518
+ skipped?: 'human-edit';
1519
+ };
1520
+ }
1521
+
1342
1522
  deleteSnapshot(id: string): { ok: boolean } {
1343
1523
  return deleteCanvasSnapshot(id);
1344
1524
  }
@@ -1759,3 +1939,13 @@ export type {
1759
1939
  PmxAxWorkItemStatus,
1760
1940
  } from './ax-state.js';
1761
1941
  export type { AxTimelineQuery } from './canvas-db.js';
1942
+ export type {
1943
+ Lifecycle,
1944
+ KnowledgeCategory,
1945
+ FolderProperties,
1946
+ ReviewRecord,
1947
+ KnowledgeInput,
1948
+ KnowledgeResults,
1949
+ LifecycleInput,
1950
+ LifecycleResults,
1951
+ } from './knowledge-lifecycle.js';