pmx-canvas 0.8.2 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (338) 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 +59 -0
  4. package/Readme.md +131 -532
  5. package/dist/canvas/global.css +2724 -476
  6. package/dist/canvas/index.js +168 -107
  7. package/dist/canvas/mcp-app.html +2899 -596
  8. package/dist/json-render/index.js +20 -20
  9. package/dist/types/client/canvas/BarHint.d.ts +12 -2
  10. package/dist/types/client/canvas/BoardLinkDialog.d.ts +3 -1
  11. package/dist/types/client/canvas/BoardPinButton.d.ts +10 -0
  12. package/dist/types/client/canvas/BoardSwitcher.d.ts +1 -1
  13. package/dist/types/client/canvas/CanvasViewport.d.ts +1 -1
  14. package/dist/types/client/canvas/CommandBar.d.ts +3 -4
  15. package/dist/types/client/canvas/ConnectionBanner.d.ts +2 -1
  16. package/dist/types/client/canvas/ContextChip.d.ts +3 -0
  17. package/dist/types/client/canvas/CrossBoardReferences.d.ts +5 -0
  18. package/dist/types/client/canvas/EdgeLayer.d.ts +14 -3
  19. package/dist/types/client/canvas/EmptyState.d.ts +4 -4
  20. package/dist/types/client/canvas/HomeGraphView.d.ts +2 -0
  21. package/dist/types/client/canvas/HomeMapView.d.ts +1 -0
  22. package/dist/types/client/canvas/HomeReviewView.d.ts +6 -0
  23. package/dist/types/client/canvas/HomeView.d.ts +9 -7
  24. package/dist/types/client/canvas/KnowledgeDialog.d.ts +4 -0
  25. package/dist/types/client/canvas/LifecycleChrome.d.ts +5 -0
  26. package/dist/types/client/canvas/LifecycleFields.d.ts +7 -0
  27. package/dist/types/client/canvas/LifecyclePanel.d.ts +1 -0
  28. package/dist/types/client/canvas/MemoryInspector.d.ts +7 -0
  29. package/dist/types/client/canvas/NearPinLayer.d.ts +14 -0
  30. package/dist/types/client/canvas/NodeContextMark.d.ts +40 -7
  31. package/dist/types/client/canvas/RelationPresetsDialog.d.ts +4 -0
  32. package/dist/types/client/canvas/RelationsPanel.d.ts +4 -0
  33. package/dist/types/client/canvas/SessionPanel.d.ts +2 -0
  34. package/dist/types/client/canvas/canvas-area.d.ts +1 -1
  35. package/dist/types/client/canvas/graph-geometry.d.ts +27 -0
  36. package/dist/types/client/canvas/graph-model.d.ts +37 -0
  37. package/dist/types/client/canvas/memory-layout.d.ts +23 -0
  38. package/dist/types/client/canvas/use-chrome-height.d.ts +2 -0
  39. package/dist/types/client/canvas/use-focus-trap.d.ts +1 -0
  40. package/dist/types/client/canvas/use-memory-graph.d.ts +8 -0
  41. package/dist/types/client/canvas/use-pan-zoom.d.ts +4 -1
  42. package/dist/types/client/icons.d.ts +0 -10
  43. package/dist/types/client/nodes/AxStepControls.d.ts +1 -1
  44. package/dist/types/client/nodes/InlineMarkdownEditor.d.ts +4 -4
  45. package/dist/types/client/nodes/MarkdownNode.d.ts +5 -1
  46. package/dist/types/client/nodes/McpAppNode.d.ts +4 -4
  47. package/dist/types/client/nodes/viewer-runtime-state.d.ts +1 -1
  48. package/dist/types/client/state/boards-store.d.ts +81 -9
  49. package/dist/types/client/state/canvas-store.d.ts +14 -1
  50. package/dist/types/client/state/context-chip-store.d.ts +22 -0
  51. package/dist/types/client/state/context-status-store.d.ts +35 -0
  52. package/dist/types/client/state/human-store.d.ts +2 -1
  53. package/dist/types/client/state/intent-bridge.d.ts +11 -8
  54. package/dist/types/client/state/knowledge-store.d.ts +22 -0
  55. package/dist/types/client/state/near-pin-store.d.ts +44 -1
  56. package/dist/types/client/state/presence-store.d.ts +4 -4
  57. package/dist/types/client/state/presentation.d.ts +28 -0
  58. package/dist/types/client/state/relation-store.d.ts +10 -0
  59. package/dist/types/client/state/session-store.d.ts +41 -2
  60. package/dist/types/client/state/workspace-settings.d.ts +7 -0
  61. package/dist/types/client/types.d.ts +13 -1
  62. package/dist/types/client/utils/keyboard.d.ts +2 -0
  63. package/dist/types/client/utils/time-ago.d.ts +4 -0
  64. package/dist/types/mcp/context-uri-template.d.ts +3 -1
  65. package/dist/types/server/agent-presence.d.ts +10 -3
  66. package/dist/types/server/ax-context.d.ts +1 -1
  67. package/dist/types/server/canvas-db.d.ts +30 -7
  68. package/dist/types/server/canvas-operations.d.ts +24 -2
  69. package/dist/types/server/canvas-provenance.d.ts +22 -2
  70. package/dist/types/server/canvas-serialization.d.ts +2 -0
  71. package/dist/types/server/canvas-state.d.ts +93 -15
  72. package/dist/types/server/canvas-validation.d.ts +1 -1
  73. package/dist/types/server/context-brief.d.ts +94 -6
  74. package/dist/types/server/context-reads.d.ts +33 -7
  75. package/dist/types/server/edit-summary.d.ts +20 -0
  76. package/dist/types/server/index.d.ts +71 -10
  77. package/dist/types/server/knowledge-lifecycle.d.ts +221 -0
  78. package/dist/types/server/lifecycle-status.d.ts +10 -0
  79. package/dist/types/server/memory-graph.d.ts +9 -0
  80. package/dist/types/server/mutation-history.d.ts +2 -0
  81. package/dist/types/server/operations/ops/ax-shared.d.ts +1 -1
  82. package/dist/types/server/operations/ops/boards.d.ts +2 -1
  83. package/dist/types/server/operations/ops/lifecycle.d.ts +2 -0
  84. package/dist/types/server/operations/ops/memory-graph.d.ts +2 -0
  85. package/dist/types/server/operations/ops/nodes.d.ts +5 -0
  86. package/dist/types/server/operations/ops/query.d.ts +23 -0
  87. package/dist/types/server/operations/registry.d.ts +1 -0
  88. package/dist/types/server/placement.d.ts +7 -0
  89. package/dist/types/server/scope-fence.d.ts +2 -1
  90. package/dist/types/server/spatial-analysis.d.ts +6 -3
  91. package/dist/types/server/tour-control.d.ts +32 -0
  92. package/dist/types/server/wiki-links.d.ts +14 -0
  93. package/dist/types/server/workbench-auth.d.ts +2 -0
  94. package/dist/types/shared/agent-context.d.ts +2 -0
  95. package/dist/types/shared/agent-presence.d.ts +15 -12
  96. package/dist/types/shared/auto-arrange.d.ts +1 -0
  97. package/dist/types/shared/boards.d.ts +4 -4
  98. package/dist/types/shared/knowledge-lifecycle.d.ts +41 -0
  99. package/dist/types/shared/knowledge-presets.d.ts +6 -0
  100. package/dist/types/shared/lifecycle-status.d.ts +24 -0
  101. package/dist/types/shared/memory-graph.d.ts +53 -0
  102. package/dist/types/shared/relations.d.ts +68 -0
  103. package/dist/types/shared/semantic-attention.d.ts +2 -0
  104. package/dist/types/shared/tour.d.ts +38 -0
  105. package/dist/types/shared/workspace-settings.d.ts +11 -0
  106. package/docs/RELEASE.md +21 -3
  107. package/docs/adr-001-bun-only-runtime.md +7 -1
  108. package/docs/adr-002-shared-product-separate-runtimes.md +93 -0
  109. package/docs/api-reference.md +109 -0
  110. package/docs/ax-host-adapter-contract.md +1 -1
  111. package/docs/ax-state-contract.md +5 -4
  112. package/docs/cli.md +153 -15
  113. package/docs/demos/pmx-canvas-demo.mp4 +0 -0
  114. package/docs/design/AgentContext.dc.html +3 -3
  115. package/docs/design/BoardPins.dc.html +37 -22
  116. package/docs/design/Categories.dc.html +208 -0
  117. package/docs/design/CategoryEditor.dc.html +33 -0
  118. package/docs/design/Chrome.dc.html +52 -9
  119. package/docs/design/ContextChip.dc.html +224 -0
  120. package/docs/design/ContextChip600.dc.html +136 -0
  121. package/docs/design/EmptyState.dc.html +45 -0
  122. package/docs/design/Flows.dc.html +23 -1
  123. package/docs/design/HeaderMarks.dc.html +183 -0
  124. package/docs/design/Home.dc.html +19 -1
  125. package/docs/design/HomeGraph.dc.html +86 -202
  126. package/docs/design/HomeMap.dc.html +23 -1
  127. package/docs/design/HomeTeam.dc.html +32 -14
  128. package/docs/design/Lifecycle.dc.html +22 -0
  129. package/docs/design/Lifecycle600.dc.html +18 -0
  130. package/docs/design/LifecycleVariants.dc.html +157 -0
  131. package/docs/design/LinkAuthorship.dc.html +139 -0
  132. package/docs/design/LinksChanged.dc.html +203 -0
  133. package/docs/design/LinksOptions.dc.html +137 -0
  134. package/docs/design/LinksOptions600.dc.html +134 -0
  135. package/docs/design/NearPin.dc.html +9 -10
  136. package/docs/design/Pane600.dc.html +4 -3
  137. package/docs/design/Relations.dc.html +3 -2
  138. package/docs/design/Restore.dc.html +152 -0
  139. package/docs/design/SeeChange.dc.html +267 -0
  140. package/docs/design/Uses.dc.html +12 -6
  141. package/docs/design/canvas.json +146 -19
  142. package/docs/design.md +449 -16
  143. package/docs/environment.md +0 -1
  144. package/docs/evals/curation-effect.md +128 -0
  145. package/docs/http-api.md +459 -37
  146. package/docs/mcp.md +146 -18
  147. package/docs/node-types.md +58 -3
  148. package/docs/plans/design-gaps-2026-10-08.md +53 -0
  149. package/docs/plans/plan-009-tech-debt-backlog.md +379 -0
  150. package/docs/plans/plan-016-context-made-visible-wave-1.md +150 -5
  151. package/docs/plans/plan-017-knowledge-categories-and-lifecycle.md +175 -0
  152. package/docs/plans/plan-018-connected-memory-wave-2.md +594 -0
  153. package/docs/plans/plan-019-post-0.9-roadmap.md +198 -0
  154. package/docs/product-context-vision-2026-09.md +17 -1
  155. package/docs/product-vision-2026-09.md +1082 -43
  156. package/docs/product-vision-review-2026-09.md +13 -1
  157. package/docs/screenshots/workbench-live-dark.png +0 -0
  158. package/docs/screenshots/workbench-live-light.png +0 -0
  159. package/docs/sdk.md +62 -2
  160. package/package.json +1 -1
  161. package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.json +1 -1
  162. package/plugins/pmx-canvas/plugin.json +1 -1
  163. package/plugins/pmx-canvas/runtime.json +1 -1
  164. package/plugins/pmx-canvas/tools.json +601 -22
  165. package/skills/pmx-canvas/SKILL.md +216 -42
  166. package/skills/pmx-canvas/evals/evals.json +224 -0
  167. package/skills/pmx-canvas/references/full-reference.md +120 -21
  168. package/skills/pmx-canvas/references/github-copilot-app-adapter.md +2 -2
  169. package/skills/pmx-canvas/references/importing-context.md +132 -0
  170. package/skills/pmx-canvas/references/installing-pmx-canvas.md +6 -6
  171. package/skills/pmx-canvas/references/tours-and-recording.md +16 -1
  172. package/skills/pmx-canvas-testing/SKILL.md +20 -0
  173. package/src/cli/agent.ts +2 -0
  174. package/src/cli/commands/ax.ts +12 -0
  175. package/src/cli/commands/boards.ts +88 -11
  176. package/src/cli/commands/edges.ts +28 -0
  177. package/src/cli/commands/groups.ts +7 -0
  178. package/src/cli/commands/nodes.ts +1 -0
  179. package/src/cli/commands/query.ts +34 -2
  180. package/src/cli/commands/record.ts +199 -48
  181. package/src/cli/index.ts +6 -1
  182. package/src/client/App.tsx +177 -70
  183. package/src/client/canvas/AttentionHistory.tsx +0 -2
  184. package/src/client/canvas/BarHint.tsx +40 -2
  185. package/src/client/canvas/BoardCopyDialog.tsx +1 -1
  186. package/src/client/canvas/BoardFolderDialog.tsx +7 -7
  187. package/src/client/canvas/BoardLinkDialog.tsx +138 -33
  188. package/src/client/canvas/BoardPinButton.tsx +33 -0
  189. package/src/client/canvas/BoardSwitcher.tsx +26 -40
  190. package/src/client/canvas/CanvasNode.tsx +360 -237
  191. package/src/client/canvas/CanvasViewport.tsx +45 -7
  192. package/src/client/canvas/CommandBar.tsx +29 -23
  193. package/src/client/canvas/CommandPalette.tsx +5 -12
  194. package/src/client/canvas/ConnectionBanner.tsx +3 -3
  195. package/src/client/canvas/ContextChip.tsx +296 -0
  196. package/src/client/canvas/ContextMenu.tsx +63 -5
  197. package/src/client/canvas/CrossBoardReferences.tsx +60 -0
  198. package/src/client/canvas/EdgeLayer.tsx +339 -76
  199. package/src/client/canvas/EmptyState.tsx +33 -18
  200. package/src/client/canvas/ExpandedNodeOverlay.tsx +72 -40
  201. package/src/client/canvas/ExternalWriters.tsx +57 -42
  202. package/src/client/canvas/HomeGraphView.tsx +942 -0
  203. package/src/client/canvas/HomeMapView.tsx +641 -0
  204. package/src/client/canvas/HomeReviewView.tsx +167 -0
  205. package/src/client/canvas/HomeView.tsx +687 -160
  206. package/src/client/canvas/IntentLayer.tsx +19 -12
  207. package/src/client/canvas/KnowledgeDialog.tsx +488 -0
  208. package/src/client/canvas/LifecycleChrome.tsx +133 -0
  209. package/src/client/canvas/LifecycleFields.tsx +91 -0
  210. package/src/client/canvas/LifecyclePanel.tsx +543 -0
  211. package/src/client/canvas/MemoryInspector.tsx +104 -0
  212. package/src/client/canvas/Minimap.tsx +3 -3
  213. package/src/client/canvas/NearPinLayer.tsx +187 -0
  214. package/src/client/canvas/NodeContextMark.tsx +175 -97
  215. package/src/client/canvas/Presentation.tsx +23 -57
  216. package/src/client/canvas/RelationPresetsDialog.tsx +413 -0
  217. package/src/client/canvas/RelationsPanel.tsx +82 -0
  218. package/src/client/canvas/SelectionBar.tsx +3 -6
  219. package/src/client/canvas/SessionPanel.tsx +90 -50
  220. package/src/client/canvas/SessionReceipt.tsx +311 -39
  221. package/src/client/canvas/ShortcutOverlay.tsx +1 -1
  222. package/src/client/canvas/SnapshotPanel.tsx +27 -36
  223. package/src/client/canvas/ToolRail.tsx +367 -192
  224. package/src/client/canvas/TopBar.tsx +354 -124
  225. package/src/client/canvas/canvas-area.ts +10 -3
  226. package/src/client/canvas/graph-geometry.ts +195 -0
  227. package/src/client/canvas/graph-model.ts +254 -0
  228. package/src/client/canvas/memory-layout.ts +106 -0
  229. package/src/client/canvas/use-chrome-height.ts +20 -0
  230. package/src/client/canvas/use-focus-trap.ts +31 -5
  231. package/src/client/canvas/use-memory-graph.ts +58 -0
  232. package/src/client/canvas/use-node-drag.ts +2 -1
  233. package/src/client/canvas/use-pan-zoom.ts +64 -7
  234. package/src/client/icons.tsx +0 -63
  235. package/src/client/mcp-app/style.css +0 -6
  236. package/src/client/nodes/AxStepControls.tsx +74 -50
  237. package/src/client/nodes/BoardNode.tsx +5 -1
  238. package/src/client/nodes/ContextNode.tsx +18 -11
  239. package/src/client/nodes/ExtAppFrame.tsx +6 -3
  240. package/src/client/nodes/FileNode.tsx +46 -42
  241. package/src/client/nodes/HtmlNode.tsx +32 -0
  242. package/src/client/nodes/ImageNode.tsx +1 -1
  243. package/src/client/nodes/InlineFormatBar.tsx +6 -9
  244. package/src/client/nodes/InlineMarkdownEditor.tsx +56 -17
  245. package/src/client/nodes/MarkdownNode.tsx +377 -109
  246. package/src/client/nodes/McpAppNode.tsx +50 -20
  247. package/src/client/nodes/MdFormatBar.tsx +21 -18
  248. package/src/client/nodes/PromptNode.tsx +1 -1
  249. package/src/client/nodes/ResponseNode.tsx +1 -1
  250. package/src/client/nodes/viewer-runtime-state.ts +54 -22
  251. package/src/client/state/attention-bridge.ts +64 -15
  252. package/src/client/state/boards-store.ts +193 -39
  253. package/src/client/state/canvas-store.ts +99 -12
  254. package/src/client/state/context-chip-store.ts +70 -0
  255. package/src/client/state/context-status-store.ts +53 -0
  256. package/src/client/state/human-store.ts +13 -7
  257. package/src/client/state/intent-bridge.ts +51 -30
  258. package/src/client/state/knowledge-store.ts +43 -0
  259. package/src/client/state/near-pin-store.ts +115 -6
  260. package/src/client/state/presence-store.ts +7 -10
  261. package/src/client/state/presentation.ts +91 -0
  262. package/src/client/state/relation-store.ts +81 -0
  263. package/src/client/state/session-store.ts +144 -18
  264. package/src/client/state/sse-bridge.ts +235 -32
  265. package/src/client/state/workspace-settings.ts +11 -0
  266. package/src/client/theme/global.css +2724 -476
  267. package/src/client/types.ts +12 -0
  268. package/src/client/utils/keyboard.ts +10 -0
  269. package/src/client/utils/time-ago.ts +14 -0
  270. package/src/json-render/renderer/index.tsx +31 -0
  271. package/src/mcp/canvas-app.ts +12 -2
  272. package/src/mcp/context-uri-template.ts +7 -4
  273. package/src/mcp/server.ts +63 -41
  274. package/src/mcp/workbench-app.ts +39 -6
  275. package/src/server/agent-presence.ts +128 -28
  276. package/src/server/ax-context.ts +27 -13
  277. package/src/server/board-export.ts +12 -1
  278. package/src/server/canvas-db.ts +331 -42
  279. package/src/server/canvas-operations.ts +195 -83
  280. package/src/server/canvas-provenance.ts +53 -22
  281. package/src/server/canvas-serialization.ts +2 -0
  282. package/src/server/canvas-state.ts +950 -122
  283. package/src/server/canvas-validation.ts +9 -2
  284. package/src/server/context-brief.ts +345 -38
  285. package/src/server/context-reads.ts +178 -30
  286. package/src/server/demo-state.json +355 -24
  287. package/src/server/edit-summary.ts +74 -0
  288. package/src/server/html-surface.ts +8 -1
  289. package/src/server/index.ts +181 -11
  290. package/src/server/knowledge-lifecycle.ts +304 -0
  291. package/src/server/lifecycle-status.ts +232 -0
  292. package/src/server/memory-graph.ts +166 -0
  293. package/src/server/mutation-history.ts +18 -6
  294. package/src/server/operations/composites.ts +32 -11
  295. package/src/server/operations/http.ts +7 -2
  296. package/src/server/operations/index.ts +4 -0
  297. package/src/server/operations/mcp.ts +26 -1
  298. package/src/server/operations/ops/ax-presence.ts +1 -1
  299. package/src/server/operations/ops/ax-read.ts +57 -6
  300. package/src/server/operations/ops/ax-timeline.ts +28 -2
  301. package/src/server/operations/ops/backup.ts +5 -3
  302. package/src/server/operations/ops/batch.ts +35 -4
  303. package/src/server/operations/ops/boards.ts +188 -47
  304. package/src/server/operations/ops/canvas-wire.ts +31 -1
  305. package/src/server/operations/ops/edges.ts +36 -2
  306. package/src/server/operations/ops/groups.ts +6 -1
  307. package/src/server/operations/ops/lifecycle.ts +262 -0
  308. package/src/server/operations/ops/memory-graph.ts +45 -0
  309. package/src/server/operations/ops/nodes.ts +107 -24
  310. package/src/server/operations/ops/query.ts +281 -68
  311. package/src/server/operations/ops/snapshots.ts +73 -1
  312. package/src/server/operations/ops/validate.ts +5 -1
  313. package/src/server/operations/ops/viewport.ts +97 -3
  314. package/src/server/operations/registry.ts +74 -25
  315. package/src/server/placement.ts +29 -0
  316. package/src/server/scope-fence.ts +78 -6
  317. package/src/server/server.ts +115 -16
  318. package/src/server/spatial-analysis.ts +48 -23
  319. package/src/server/tour-control.ts +82 -0
  320. package/src/server/wiki-links.ts +137 -0
  321. package/src/server/workbench-auth.ts +11 -0
  322. package/src/shared/agent-context.ts +3 -0
  323. package/src/shared/agent-presence.ts +15 -15
  324. package/src/shared/auto-arrange.ts +2 -0
  325. package/src/shared/boards.ts +4 -4
  326. package/src/shared/knowledge-lifecycle.ts +22 -0
  327. package/src/shared/knowledge-presets.ts +80 -0
  328. package/src/shared/lifecycle-status.ts +26 -0
  329. package/src/shared/memory-graph.ts +74 -0
  330. package/src/shared/relations.ts +98 -0
  331. package/src/shared/semantic-attention.ts +15 -1
  332. package/src/shared/tour.ts +23 -5
  333. package/src/shared/workspace-settings.ts +13 -0
  334. package/dist/types/client/canvas/ContextPinBar.d.ts +0 -1
  335. package/dist/types/client/canvas/FocusFieldLayer.d.ts +0 -1
  336. package/dist/types/client/canvas/kind-colors.d.ts +0 -3
  337. package/dist/types/server/agent-context.d.ts +0 -22
  338. package/src/client/canvas/ContextPinBar.tsx +0 -25
@@ -3,9 +3,14 @@ import { showToast } from './attention-bridge';
3
3
  import { computed, signal } from '@preact/signals';
4
4
  import type { AxApprovalStatus, AxEventKind, AxWorkItemStatus } from '../../shared/ax-kinds.js';
5
5
  import { HUMAN_STARTED_SESSION_LABEL } from '../../shared/agent-presence.js';
6
- import { axSurfaceState } from './canvas-store';
6
+ import { axSurfaceState, nodes } from './canvas-store';
7
7
  import { requestBestEffort, requestJson, requestOk } from './intent-bridge';
8
- import { agentActivity } from './presence-store';
8
+ import { agentActivity, agentPresences } from './presence-store';
9
+ import { activeBoardId } from './boards-store';
10
+ import { agentSessionsEnabled } from './workspace-settings';
11
+
12
+ /** Explicit panel expansion, shared by the panel handle and the agent popover. */
13
+ export const sessionPanelOpened = signal(false);
9
14
 
10
15
  // Structural views of the AX wire shapes (the client never imports server
11
16
  // modules; the status/kind unions come from shared/). Only the fields the
@@ -358,6 +363,7 @@ export async function sendSteering(message: string, target?: string | null): Pro
358
363
 
359
364
  /** Attach a human-started session; the agent's writes are attributed to it. */
360
365
  export async function startSession(): Promise<boolean> {
366
+ if (!agentSessionsEnabled.value) return false;
361
367
  const result = await requestOk('startSession', '/api/canvas/ax/presence', {
362
368
  method: 'POST',
363
369
  headers: { 'Content-Type': 'application/json' },
@@ -381,6 +387,8 @@ export interface SessionReceipt {
381
387
  labels: string[];
382
388
  /** Endings folded into this card (see applySessionReceipt). */
383
389
  sessions: number;
390
+ /** When the session attached (the receipt's "This session · start–end"); null if unknown. */
391
+ startedAt: string | null;
384
392
  endedAt: string;
385
393
  /** Why it ended — the receipt should answer this, not leave the human asking. */
386
394
  endedBy?: 'human' | 'agent' | 'idle-timeout';
@@ -390,7 +398,7 @@ export interface SessionReceipt {
390
398
  counts: { items: number; done: number; cancelled: number; rejected: number; held: number };
391
399
  /** The pre-session snapshot; null when the board was empty at attach, or on a
392
400
  * merged receipt (each session's snapshot is in History). */
393
- snapshot: { id: string; name: string } | null;
401
+ snapshot: { id: string; name: string; boardId: string | null } | null;
394
402
  /** What the session did with context (docs/design/AgentContext.dc.html receipt). */
395
403
  context: SessionContextActivity;
396
404
  }
@@ -398,9 +406,19 @@ export interface SessionReceipt {
398
406
  export interface ReceiptNode {
399
407
  id: string;
400
408
  title: string;
409
+ /** The board where this touch was recorded, for reopening a change's card. */
410
+ boardId?: string;
401
411
  reason?: string;
412
+ /** Edited cards: what the edit did ("rewrote the second paragraph"). */
413
+ change?: string;
414
+ /** Edited cards: the text before and at the session's end, cut around the first difference. */
415
+ before?: string;
416
+ after?: string;
402
417
  }
403
418
 
419
+ /** Card ids are local to a board; receipts can remain open across board switches. */
420
+ export const receiptNodeKey = (node: ReceiptNode): string => JSON.stringify([node.boardId, node.id]);
421
+
404
422
  export interface SessionContextActivity {
405
423
  read: ReceiptNode[];
406
424
  pinned: ReceiptNode[];
@@ -416,7 +434,15 @@ function receiptNodes(value: unknown): ReceiptNode[] {
416
434
  const record = entry as Record<string, unknown>;
417
435
  if (typeof record.id !== 'string' || typeof record.title !== 'string') return [];
418
436
  return [
419
- { id: record.id, title: record.title, ...(typeof record.reason === 'string' ? { reason: record.reason } : {}) },
437
+ {
438
+ id: record.id,
439
+ title: record.title,
440
+ ...(typeof record.boardId === 'string' ? { boardId: record.boardId } : {}),
441
+ ...(typeof record.reason === 'string' ? { reason: record.reason } : {}),
442
+ ...(typeof record.change === 'string' ? { change: record.change } : {}),
443
+ ...(typeof record.before === 'string' ? { before: record.before } : {}),
444
+ ...(typeof record.after === 'string' ? { after: record.after } : {}),
445
+ },
420
446
  ];
421
447
  });
422
448
  }
@@ -425,22 +451,74 @@ function receiptNodes(value: unknown): ReceiptNode[] {
425
451
  * The agent-activity lens: while on, nodes the last session did not touch
426
452
  * (read, pin, create, edit) are dimmed. Null when off.
427
453
  */
428
- export const activityLensNodeIds = signal<Set<string> | null>(null);
454
+ /**
455
+ * The activity lens ("Dim untouched nodes"): off, one live session's touches
456
+ * (the chip that was pressed), or the receipt's. A live lens hands over to the
457
+ * receipt when that session ends, and switches off if its ending has none.
458
+ */
459
+ export type ActivityLens = { kind: 'live'; sessionId: string } | { kind: 'receipt' } | null;
460
+ export const activityLens = signal<ActivityLens>(null);
461
+
462
+ const touchedIds = (lists: Array<Array<{ id: string } | string>>): Set<string> =>
463
+ new Set(lists.flat().map((entry) => (typeof entry === 'string' ? entry : entry.id)));
464
+
465
+ export const activityLensNodeIds = computed<Set<string> | null>(() => {
466
+ if (!agentSessionsEnabled.value) return null;
467
+ const lens = activityLens.value;
468
+ if (lens?.kind === 'live') {
469
+ const touches = agentPresences.value.find(
470
+ (presence) => presence.attached && presence.sessionId === lens.sessionId,
471
+ )?.session;
472
+ return touches ? touchedIds([touches.read, touches.pinned, touches.created, touches.edited]) : null;
473
+ }
474
+ const context = lens?.kind === 'receipt' ? sessionReceipt.value?.context : undefined;
475
+ return context
476
+ ? touchedIds(
477
+ [context.read, context.pinned, context.created, context.edited].map((list) =>
478
+ list.filter((node) => !node.boardId || node.boardId === activeBoardId.value),
479
+ ),
480
+ )
481
+ : null;
482
+ });
483
+
484
+ /** Cards an agent edited in a live session or in the receipt's session: they carry the violet bar. */
485
+ export const sessionEditedIds = computed<Set<string>>(
486
+ () =>
487
+ new Set([
488
+ ...agentPresences.value.flatMap((presence) => (presence.attached ? (presence.session?.edited ?? []) : [])),
489
+ ...(sessionReceipt.value?.context.edited
490
+ .filter((node) => !node.boardId || node.boardId === activeBoardId.value)
491
+ .map((node) => node.id) ?? []),
492
+ ]),
493
+ );
429
494
 
430
- export function setActivityLens(on: boolean): void {
431
- const context = sessionReceipt.value?.context;
432
- activityLensNodeIds.value =
433
- on && context
434
- ? new Set([...context.read, ...context.pinned, ...context.created, ...context.edited].map((node) => node.id))
435
- : null;
495
+ /** Turn the lens on for a live session (from its chip) or the receipt, or off. */
496
+ export function setActivityLens(on: boolean, live?: { sessionId: string }): void {
497
+ activityLens.value = on ? (live ? { kind: 'live', ...live } : { kind: 'receipt' }) : null;
436
498
  }
437
499
 
438
500
  /** The last ended session's receipt (design item 2); client-side, cleared on dismiss. */
439
501
  export const sessionReceipt = signal<SessionReceipt | null>(null);
440
502
 
503
+ /** A card both sessions edited: the first Before, the last After, both changes. */
504
+ function mergeEdits(open: ReceiptNode[], next: ReceiptNode[]): ReceiptNode[] {
505
+ const later = new Map(next.map((node) => [receiptNodeKey(node), node]));
506
+ const merged = open.map((node) => {
507
+ const again = later.get(receiptNodeKey(node));
508
+ if (!again) return node;
509
+ later.delete(receiptNodeKey(node));
510
+ return {
511
+ ...again,
512
+ change: [node.change, again.change].filter(Boolean).join('; then '),
513
+ before: node.before ?? again.before,
514
+ };
515
+ });
516
+ return [...merged, ...later.values()];
517
+ }
518
+
441
519
  function unionNodes(a: ReceiptNode[], b: ReceiptNode[]): ReceiptNode[] {
442
- const seen = new Set(a.map((node) => node.id));
443
- return [...a, ...b.filter((node) => !seen.has(node.id))];
520
+ const seen = new Set(a.map(receiptNodeKey));
521
+ return [...a, ...b.filter((node) => !seen.has(receiptNodeKey(node)))];
444
522
  }
445
523
 
446
524
  /**
@@ -452,6 +530,7 @@ function mergeReceipts(open: SessionReceipt, next: SessionReceipt): SessionRecei
452
530
  return {
453
531
  labels: [...new Set([...open.labels, ...next.labels])],
454
532
  sessions: open.sessions + 1,
533
+ startedAt: open.startedAt && next.startedAt && next.startedAt < open.startedAt ? next.startedAt : open.startedAt,
455
534
  endedAt: next.endedAt,
456
535
  endedBy: open.endedBy === next.endedBy ? next.endedBy : undefined,
457
536
  // Board-wide counts: the latest frame is the current board.
@@ -461,7 +540,7 @@ function mergeReceipts(open: SessionReceipt, next: SessionReceipt): SessionRecei
461
540
  read: unionNodes(open.context.read, next.context.read),
462
541
  pinned: unionNodes(open.context.pinned, next.context.pinned),
463
542
  created: unionNodes(open.context.created, next.context.created),
464
- edited: unionNodes(open.context.edited, next.context.edited),
543
+ edited: mergeEdits(open.context.edited, next.context.edited),
465
544
  changedSinceRead: unionNodes(open.context.changedSinceRead, next.context.changedSinceRead),
466
545
  },
467
546
  };
@@ -479,10 +558,17 @@ export function applySessionReceipt(data: Record<string, unknown>): void {
479
558
  const snapshot = data.snapshot as SessionReceipt['snapshot'] | undefined;
480
559
  const context = data.context as Record<string, unknown> | undefined;
481
560
  if (typeof data.label !== 'string' || typeof data.endedAt !== 'string') return;
482
- if (data.unchanged === true || typeof data.parentAgentId === 'string') return;
561
+ const lens = activityLens.value;
562
+ const endsLiveLens = lens?.kind === 'live' && lens.sessionId === data.writer;
563
+ if (data.unchanged === true || typeof data.parentAgentId === 'string') {
564
+ // The session the lens followed ended with nothing to show: the lens goes off.
565
+ if (endsLiveLens) activityLens.value = null;
566
+ return;
567
+ }
483
568
  const next: SessionReceipt = {
484
569
  labels: [data.label],
485
570
  sessions: 1,
571
+ startedAt: typeof data.startedAt === 'string' ? data.startedAt : null,
486
572
  endedAt: data.endedAt,
487
573
  endedBy:
488
574
  data.endedBy === 'human' || data.endedBy === 'agent' || data.endedBy === 'idle-timeout'
@@ -496,7 +582,13 @@ export function applySessionReceipt(data: Record<string, unknown>): void {
496
582
  held: Number(counts?.held ?? 0) || 0,
497
583
  },
498
584
  snapshot:
499
- snapshot && typeof snapshot.id === 'string' ? { id: snapshot.id, name: String(snapshot.name ?? '') } : null,
585
+ snapshot && typeof snapshot.id === 'string'
586
+ ? {
587
+ id: snapshot.id,
588
+ name: String(snapshot.name ?? ''),
589
+ boardId: typeof snapshot.boardId === 'string' ? snapshot.boardId : null,
590
+ }
591
+ : null,
500
592
  context: {
501
593
  read: receiptNodes(context?.read),
502
594
  pinned: receiptNodes(context?.pinned),
@@ -507,17 +599,51 @@ export function applySessionReceipt(data: Record<string, unknown>): void {
507
599
  };
508
600
  const open = sessionReceipt.value;
509
601
  sessionReceipt.value = open ? mergeReceipts(open, next) : next;
510
- if (open && activityLensNodeIds.value) setActivityLens(true);
602
+ // A live lens carries on as the receipt's once its session ends.
603
+ if (endsLiveLens) activityLens.value = { kind: 'receipt' };
604
+ }
605
+
606
+ /**
607
+ * Undo on the receipt's Edited row (AgentContext.dc.html): each card back to
608
+ * its content in the pre-session snapshot — the rest of the board stays.
609
+ */
610
+ export async function undoSessionEdits(
611
+ snapshotId: string,
612
+ edits: ReceiptNode[],
613
+ ): Promise<{ ok: boolean; restored: string[] }> {
614
+ let ok = true;
615
+ const restored: string[] = [];
616
+ for (const edit of edits) {
617
+ if (edit.boardId && edit.boardId !== activeBoardId.value) continue;
618
+ const nodeId = edit.id;
619
+ // A card you edited after the session keeps your edit.
620
+ const node = nodes.value.get(nodeId);
621
+ if (!node || node.lastEditedBy?.actor === 'human') continue;
622
+ const result = await requestJson<{ ok: boolean; restored: boolean }>(
623
+ 'undoSessionEdit',
624
+ `/api/canvas/snapshots/${encodeURIComponent(snapshotId)}/restore-node`,
625
+ { ok: false, restored: false },
626
+ {
627
+ method: 'POST',
628
+ headers: { 'Content-Type': 'application/json' },
629
+ body: JSON.stringify({ nodeId, preserveHumanEdits: true }),
630
+ },
631
+ );
632
+ ok = ok && result.ok;
633
+ if (result.ok && result.restored) restored.push(nodeId);
634
+ }
635
+ return { ok, restored };
511
636
  }
512
637
 
513
638
  export function dismissSessionReceipt(): void {
514
639
  sessionReceipt.value = null;
515
- activityLensNodeIds.value = null;
640
+ if (activityLens.value?.kind === 'receipt') activityLens.value = null;
516
641
  }
517
642
 
518
643
  export function resetSessionStore(): void {
519
644
  historyTop.value = null;
520
645
  undoneActivityIds.value = new Set();
646
+ sessionPanelOpened.value = false;
521
647
  sessionReceipt.value = null;
522
648
  axTimeline.value = { events: [], evidence: [], steering: [] };
523
649
  timelineFilter.value = 'all';