pmx-canvas 0.11.0 → 0.12.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 (298) hide show
  1. package/.github/extensions/pmx-canvas/extension.mjs +3 -1
  2. package/.github/extensions/pmx-canvas/runtime.json +1 -1
  3. package/.github/extensions/pmx-canvas/tools.json +440 -18
  4. package/CHANGELOG.md +37 -0
  5. package/dist/amp-plugin/pmx-canvas/LICENSE +21 -0
  6. package/dist/amp-plugin/pmx-canvas/THIRD_PARTY_LICENSES.txt +331 -0
  7. package/dist/amp-plugin/pmx-canvas/index.js +5445 -0
  8. package/dist/amp-plugin/pmx-canvas/runtime.json +3 -0
  9. package/dist/amp-plugin/pmx-canvas/shared-runtime.mjs +194 -0
  10. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/SKILL.md +251 -0
  11. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/evals.json +1086 -0
  12. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/fixtures/code-exploration/src/auth/jwt.ts +17 -0
  13. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/fixtures/code-exploration/src/auth/login.ts +12 -0
  14. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/fixtures/code-exploration/src/auth/middleware.ts +13 -0
  15. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/fixtures/code-exploration/src/routes/auth.ts +13 -0
  16. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/fixtures/investigation-board/src/handlers/users.ts +27 -0
  17. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/mcp.json +36 -0
  18. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/ax-html-control-surface.md +101 -0
  19. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/codex-app-adapter.md +151 -0
  20. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/excalidraw-diagram-authoring.md +150 -0
  21. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/full-reference.md +1785 -0
  22. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/github-copilot-app-adapter.md +223 -0
  23. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/html-primitives.md +139 -0
  24. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/importing-context.md +132 -0
  25. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/installing-pmx-canvas.md +251 -0
  26. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/tours-and-recording.md +111 -0
  27. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/workbench-workflows.md +900 -0
  28. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas-orchestration/SKILL.md +141 -0
  29. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas-testing/SKILL.md +266 -0
  30. package/dist/amp-plugin/pmx-canvas/start-mcp.mjs +14857 -0
  31. package/dist/amp-plugin/pmx-canvas/tools.json +10963 -0
  32. package/dist/canvas/chunks/{abnfDiagram-N423BO3Z-p8dtp6va.js → abnfDiagram-N423BO3Z-cwtsds5d.js} +1 -1
  33. package/dist/canvas/chunks/architecture-TIHT7OUA-x63cwejs.js +1 -0
  34. package/dist/canvas/chunks/{architectureDiagram-T3A2C74G-5jhmxrzf.js → architectureDiagram-T3A2C74G-b4xc9tpz.js} +3 -3
  35. package/dist/canvas/chunks/{blockDiagram-VBNYF7ZC-pw1fa9yb.js → blockDiagram-VBNYF7ZC-ahp3yxxs.js} +7 -7
  36. package/dist/canvas/chunks/{c4Diagram-5PPSVZJV-x07nq0tt.js → c4Diagram-5PPSVZJV-nappnay7.js} +4 -4
  37. package/dist/canvas/chunks/classDiagram-JCYQIIEL-k1j2xc1t.js +1 -0
  38. package/dist/canvas/chunks/classDiagram-v2-OCEON4UE-33x08xf2.js +1 -0
  39. package/dist/canvas/chunks/{cose-bilkent-JH36ORCC-nm39npq0.js → cose-bilkent-JH36ORCC-a428syda.js} +1 -1
  40. package/dist/canvas/chunks/cynefin-VYW2F7L2-8v6jxfz8.js +1 -0
  41. package/dist/canvas/chunks/{cynefinDiagram-MW4NZA55-vdxrwahz.js → cynefinDiagram-MW4NZA55-nspsqebj.js} +1 -1
  42. package/dist/canvas/chunks/{dagre-VZM6K2ZE-bqdm7wpd.js → dagre-VZM6K2ZE-8y6177fh.js} +2 -2
  43. package/dist/canvas/chunks/{diagram-7IWD3JNH-gs5gpr93.js → diagram-7IWD3JNH-vym028wn.js} +4 -4
  44. package/dist/canvas/chunks/{diagram-B4RE2ZJO-cgwjg47s.js → diagram-B4RE2ZJO-jddqb8sy.js} +1 -1
  45. package/dist/canvas/chunks/{diagram-LBJQPF4R-w3vahpav.js → diagram-LBJQPF4R-bjwbpkrn.js} +1 -1
  46. package/dist/canvas/chunks/{diagram-Q27KOJAE-42837z6h.js → diagram-Q27KOJAE-j6ph6hcx.js} +2 -2
  47. package/dist/canvas/chunks/{diagram-UB23O5K3-4fz83q59.js → diagram-UB23O5K3-p09w8jej.js} +1 -1
  48. package/dist/canvas/chunks/{ebnfDiagram-BXEA7PRR-zphtgegj.js → ebnfDiagram-BXEA7PRR-j975ycq5.js} +1 -1
  49. package/dist/canvas/chunks/{erDiagram-JOGREHBK-zvbbdxp4.js → erDiagram-JOGREHBK-sxp6hjn8.js} +5 -5
  50. package/dist/canvas/chunks/eventmodeling-45OFAUF4-xzfgn6xg.js +1 -0
  51. package/dist/canvas/chunks/flowDiagram-UKHOOZJN-2nsmkskb.js +1 -0
  52. package/dist/canvas/chunks/{ganttDiagram-PKOTCBZU-282jzycm.js → ganttDiagram-PKOTCBZU-4tv230y2.js} +2 -2
  53. package/dist/canvas/chunks/gitGraph-TEB2WS4Q-macbhand.js +1 -0
  54. package/dist/canvas/chunks/{gitGraphDiagram-DS77QQ5N-vqqy6p1b.js → gitGraphDiagram-DS77QQ5N-mqgmnw9g.js} +1 -1
  55. package/dist/canvas/chunks/{index-wgv7en21.js → index-037z5dz2.js} +2 -2
  56. package/dist/canvas/chunks/{index-w2mra17t.js → index-081n3har.js} +1 -1
  57. package/dist/canvas/chunks/{index-353m7b0p.js → index-5n03dmb9.js} +1 -1
  58. package/dist/canvas/chunks/{index-yw8h9vha.js → index-60pgzdqe.js} +1 -1
  59. package/dist/canvas/chunks/index-8g1vb57n.js +2 -0
  60. package/dist/canvas/chunks/{index-vv56h019.js → index-8rjes9r6.js} +1 -1
  61. package/dist/canvas/chunks/{index-nprm96xp.js → index-9022d1n9.js} +1 -1
  62. package/dist/canvas/chunks/{index-194m3hc3.js → index-9b02042t.js} +1 -1
  63. package/dist/canvas/chunks/{index-0rk4gp1g.js → index-ap4q4vez.js} +1 -1
  64. package/dist/canvas/chunks/{index-rh7nv68n.js → index-dyp2nhd3.js} +1 -1
  65. package/dist/canvas/chunks/index-frvta4tm.js +2 -0
  66. package/dist/canvas/chunks/{index-s4j678gj.js → index-h9ycj8n2.js} +1 -1
  67. package/dist/canvas/chunks/{index-fjtdggjn.js → index-mergmj97.js} +2 -2
  68. package/dist/canvas/chunks/index-nwsya6dj.js +301 -0
  69. package/dist/canvas/chunks/{index-3cy1bzwh.js → index-nz8qbd1p.js} +1 -1
  70. package/dist/canvas/chunks/{index-d3b3gqe9.js → index-qhfrazv3.js} +1 -1
  71. package/dist/canvas/chunks/{index-6qmsnxwy.js → index-v119nznk.js} +4 -4
  72. package/dist/canvas/chunks/{index-r2fyc9w1.js → index-vpt37m3d.js} +6 -6
  73. package/dist/canvas/chunks/{index-qj8fxwsf.js → index-y9ma6azy.js} +1 -1
  74. package/dist/canvas/chunks/{index-tb1wf525.js → index-zx0j3a2z.js} +26 -26
  75. package/dist/canvas/chunks/info-DKCQHKI2-a0ykzs9z.js +1 -0
  76. package/dist/canvas/chunks/{infoDiagram-6WML65LV-v1yvggqj.js → infoDiagram-6WML65LV-qv97qq6a.js} +2 -2
  77. package/dist/canvas/chunks/ishikawaDiagram-WSZJBQD7-p9306gh0.js +70 -0
  78. package/dist/canvas/chunks/{journeyDiagram-NVQOT4AX-zjcffveh.js → journeyDiagram-NVQOT4AX-sv67zats.js} +5 -5
  79. package/dist/canvas/chunks/{kanban-definition-27J2QSJJ-dakqtfp1.js → kanban-definition-27J2QSJJ-k2pfam6p.js} +1 -1
  80. package/dist/canvas/chunks/{mermaid.core-j2geswrd.js → mermaid.core-83r0fdce.js} +4 -4
  81. package/dist/canvas/chunks/{mindmap-definition-FAOFIHXS-142xhr3k.js → mindmap-definition-FAOFIHXS-vg2newm1.js} +1 -1
  82. package/dist/canvas/chunks/packet-7NZHBO7P-tqnabcdv.js +1 -0
  83. package/dist/canvas/chunks/{pegDiagram-VL7TDLO6-5wvk7vsd.js → pegDiagram-VL7TDLO6-k2h7zc5n.js} +1 -1
  84. package/dist/canvas/chunks/pie-RZYD4A2V-wke6gyhe.js +1 -0
  85. package/dist/canvas/chunks/{pieDiagram-7S7Q4E2Y-ztdhwycc.js → pieDiagram-7S7Q4E2Y-hg169p1v.js} +2 -2
  86. package/dist/canvas/chunks/{quadrantDiagram-CIZ2JOQS-c5p9gmg6.js → quadrantDiagram-CIZ2JOQS-bxtvsq0b.js} +5 -5
  87. package/dist/canvas/chunks/radar-I7S5WNFK-w7ka0drw.js +1 -0
  88. package/dist/canvas/chunks/railroad-3IZDKUUU-y3vg318x.js +1 -0
  89. package/dist/canvas/chunks/railroad-abnf-AHOZXSZD-6hqvcz3c.js +1 -0
  90. package/dist/canvas/chunks/railroad-ebnf-EBAXGLYW-vcae260m.js +1 -0
  91. package/dist/canvas/chunks/railroad-peg-LSFZ7HO6-0dbyme23.js +1 -0
  92. package/dist/canvas/chunks/{railroadDiagram-AXF67PYL-xkmqkjwf.js → railroadDiagram-AXF67PYL-njyhcp4p.js} +1 -1
  93. package/dist/canvas/chunks/{requirementDiagram-LRYGKXZP-9wm2dyaw.js → requirementDiagram-LRYGKXZP-2z6v09gm.js} +4 -4
  94. package/dist/canvas/chunks/{sankeyDiagram-W5VNT64P-2w19etgb.js → sankeyDiagram-W5VNT64P-15mzam0e.js} +1 -1
  95. package/dist/canvas/chunks/{sequenceDiagram-SI44F4Z6-0jn0vwmz.js → sequenceDiagram-SI44F4Z6-rbshyd2c.js} +12 -12
  96. package/dist/canvas/chunks/{sizeCapture-X5ZJPWSS-x75epwdq.js → sizeCapture-X5ZJPWSS-sg26xwts.js} +1 -1
  97. package/dist/canvas/chunks/{stateDiagram-OKZ733FA-schjpy6d.js → stateDiagram-OKZ733FA-6eahp98e.js} +1 -1
  98. package/dist/canvas/chunks/stateDiagram-v2-UEYNNEHI-tty0ggn5.js +1 -0
  99. package/dist/canvas/chunks/{swimlanes-SLNWSIFB-gkw4xce3.js → swimlanes-SLNWSIFB-ze5db10t.js} +1 -1
  100. package/dist/canvas/chunks/swimlanesDiagram-ULZ7WXOC-thze75gq.js +8 -0
  101. package/dist/canvas/chunks/{timeline-definition-Z64GVDOM-x7nkcy01.js → timeline-definition-Z64GVDOM-vnp7jyy8.js} +3 -3
  102. package/dist/canvas/chunks/treeView-QDETBFTQ-6ysffnm6.js +1 -0
  103. package/dist/canvas/chunks/treemap-6X3UGDF4-m3h9p066.js +1 -0
  104. package/dist/canvas/chunks/vennDiagram-T6HMQDX7-0qrnb7mj.js +34 -0
  105. package/dist/canvas/chunks/wardley-OPB4EBWU-dp37drwx.js +1 -0
  106. package/dist/canvas/chunks/{wardleyDiagram-T6FBY63Y-t1j52t9r.js → wardleyDiagram-T6FBY63Y-8nxg8wqt.js} +4 -4
  107. package/dist/canvas/chunks/{xychartDiagram-ELKLHX3M-38t16pme.js → xychartDiagram-ELKLHX3M-nj921e7s.js} +5 -5
  108. package/dist/canvas/global.css +94 -1
  109. package/dist/canvas/index.js +154 -145
  110. package/dist/canvas/mcp-app.html +600 -498
  111. package/dist/json-render/index.css +1 -1
  112. package/dist/types/client/canvas/BoardMoveDialog.d.ts +2 -0
  113. package/dist/types/client/canvas/EdgeEditor.d.ts +19 -0
  114. package/dist/types/client/canvas/GroupNavigation.d.ts +2 -0
  115. package/dist/types/client/canvas/TextPrompt.d.ts +4 -0
  116. package/dist/types/client/nodes/PromptNode.d.ts +1 -0
  117. package/dist/types/client/nodes/ViewerFrame.d.ts +1 -1
  118. package/dist/types/client/nodes/WorkCard.d.ts +7 -0
  119. package/dist/types/client/nodes/use-ax-surface-bridge.d.ts +1 -0
  120. package/dist/types/client/state/canvas-store.d.ts +15 -0
  121. package/dist/types/client/state/human-store.d.ts +2 -0
  122. package/dist/types/client/state/intent-bridge.d.ts +4 -2
  123. package/dist/types/client/state/relation-store.d.ts +11 -0
  124. package/dist/types/client/types.d.ts +1 -1
  125. package/dist/types/json-render/catalog.d.ts +2 -2
  126. package/dist/types/server/agent-presence.d.ts +1 -0
  127. package/dist/types/server/ax-interaction.d.ts +1 -0
  128. package/dist/types/server/ax-state-manager.d.ts +10 -2
  129. package/dist/types/server/board-local.d.ts +4 -0
  130. package/dist/types/server/board-move.d.ts +38 -0
  131. package/dist/types/server/canvas-db.d.ts +12 -2
  132. package/dist/types/server/canvas-operations.d.ts +5 -6
  133. package/dist/types/server/canvas-provenance.d.ts +1 -1
  134. package/dist/types/server/canvas-serialization.d.ts +1 -0
  135. package/dist/types/server/canvas-state.d.ts +84 -20
  136. package/dist/types/server/card-relocation.d.ts +6 -0
  137. package/dist/types/server/context-reads.d.ts +3 -1
  138. package/dist/types/server/human-presence.d.ts +1 -1
  139. package/dist/types/server/index.d.ts +16 -5
  140. package/dist/types/server/intent-registry.d.ts +8 -4
  141. package/dist/types/server/knowledge-lifecycle.d.ts +4 -2
  142. package/dist/types/server/mutation-history.d.ts +7 -5
  143. package/dist/types/server/operations/registry.d.ts +2 -0
  144. package/dist/types/server/trace-manager.d.ts +1 -0
  145. package/dist/types/shared/ax-kinds.d.ts +6 -0
  146. package/dist/types/shared/ax-surface-protocol.d.ts +8 -0
  147. package/dist/types/shared/human-presence.d.ts +1 -0
  148. package/dist/types/shared/lifecycle-status.d.ts +2 -0
  149. package/docs/RELEASE.md +21 -14
  150. package/docs/agent-plugin.md +16 -2
  151. package/docs/amp-plugin.md +271 -0
  152. package/docs/cli.md +42 -3
  153. package/docs/design/GuestName.dc.html +50 -0
  154. package/docs/design/PluginLogo.svg +7 -0
  155. package/docs/design/WorkCards.dc.html +39 -0
  156. package/docs/design/canvas.json +16 -0
  157. package/docs/design/plugin-logo.png +0 -0
  158. package/docs/design.md +111 -8
  159. package/docs/evals/agents-md-sections.md +38 -0
  160. package/docs/http-api.md +128 -10
  161. package/docs/mcp.md +61 -9
  162. package/docs/node-types.md +71 -1
  163. package/docs/plans/plan-009-tech-debt-backlog.md +1 -1
  164. package/docs/plans/plan-019-post-0.9-roadmap.md +44 -20
  165. package/docs/plans/plan-020-wave-5-work-planning.md +553 -0
  166. package/docs/product-vision-2026-09.md +33 -17
  167. package/docs/sdk.md +18 -0
  168. package/package.json +6 -3
  169. package/plugins/pmx-canvas/assets/preview.png +0 -0
  170. package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/extension.mjs +3 -1
  171. package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.json +1 -1
  172. package/plugins/pmx-canvas/plugin.json +2 -2
  173. package/plugins/pmx-canvas/runtime.json +1 -1
  174. package/plugins/pmx-canvas/tools.json +440 -18
  175. package/skills/data-analysis/SKILL.md +13 -26
  176. package/skills/frontend-design/LICENSE.txt +177 -0
  177. package/skills/frontend-design/SKILL.md +3 -0
  178. package/skills/pmx-canvas/SKILL.md +232 -842
  179. package/skills/pmx-canvas/evals/evals.json +26 -3
  180. package/skills/pmx-canvas/references/full-reference.md +80 -20
  181. package/skills/pmx-canvas/references/html-primitives.md +7 -0
  182. package/skills/pmx-canvas/references/installing-pmx-canvas.md +6 -6
  183. package/skills/pmx-canvas/references/tours-and-recording.md +29 -2
  184. package/skills/pmx-canvas/references/workbench-workflows.md +900 -0
  185. package/skills/pmx-canvas-testing/SKILL.md +7 -5
  186. package/skills/published-consumer-e2e/SKILL.md +25 -8
  187. package/skills/published-consumer-e2e/scripts/run-published-consumer-e2e.sh +51 -75
  188. package/skills/web-artifacts-builder/LICENSE.txt +202 -0
  189. package/skills/web-artifacts-builder/SKILL.md +6 -0
  190. package/skills/web-artifacts-builder/scripts/init-artifact.sh +2 -2
  191. package/src/cli/commands/apps.ts +1 -0
  192. package/src/cli/commands/boards.ts +30 -0
  193. package/src/client/App.tsx +20 -4
  194. package/src/client/canvas/AttentionHistory.tsx +10 -2
  195. package/src/client/canvas/BoardMoveDialog.tsx +225 -0
  196. package/src/client/canvas/CanvasNode.tsx +45 -2
  197. package/src/client/canvas/CanvasViewport.tsx +38 -9
  198. package/src/client/canvas/ContextMenu.tsx +34 -12
  199. package/src/client/canvas/EdgeEditor.tsx +149 -0
  200. package/src/client/canvas/EdgeLayer.tsx +87 -16
  201. package/src/client/canvas/ExpandedNodeOverlay.tsx +4 -0
  202. package/src/client/canvas/GroupNavigation.tsx +85 -0
  203. package/src/client/canvas/RelationsPanel.tsx +9 -0
  204. package/src/client/canvas/SelectionBar.tsx +21 -0
  205. package/src/client/canvas/SessionPanel.tsx +35 -1
  206. package/src/client/canvas/TextPrompt.tsx +18 -1
  207. package/src/client/canvas/TopBar.tsx +30 -0
  208. package/src/client/canvas/canvas-area.ts +1 -1
  209. package/src/client/nodes/ExtAppFrame.tsx +26 -10
  210. package/src/client/nodes/MarkdownNode.tsx +6 -7
  211. package/src/client/nodes/PromptNode.tsx +32 -3
  212. package/src/client/nodes/StatusNode.tsx +12 -0
  213. package/src/client/nodes/StatusSummary.tsx +11 -0
  214. package/src/client/nodes/ViewerFrame.tsx +21 -10
  215. package/src/client/nodes/WorkCard.tsx +175 -0
  216. package/src/client/nodes/use-ax-surface-bridge.ts +18 -14
  217. package/src/client/state/attention-bridge.ts +5 -0
  218. package/src/client/state/attention-store.ts +1 -0
  219. package/src/client/state/canvas-store.ts +185 -15
  220. package/src/client/state/human-store.ts +22 -2
  221. package/src/client/state/intent-bridge.ts +19 -5
  222. package/src/client/state/relation-store.ts +127 -1
  223. package/src/client/theme/global.css +94 -1
  224. package/src/client/types.ts +1 -0
  225. package/src/mcp/server.ts +13 -3
  226. package/src/server/agent-presence.ts +43 -23
  227. package/src/server/ax-flow-loop.ts +6 -0
  228. package/src/server/ax-gate-ttl.ts +8 -1
  229. package/src/server/ax-interaction.ts +16 -10
  230. package/src/server/ax-state-manager.ts +17 -4
  231. package/src/server/ax-wait.ts +20 -6
  232. package/src/server/board-export.ts +19 -6
  233. package/src/server/board-local.ts +20 -0
  234. package/src/server/board-move.ts +297 -0
  235. package/src/server/canvas-db.ts +249 -158
  236. package/src/server/canvas-operations.ts +139 -109
  237. package/src/server/canvas-schema.ts +21 -0
  238. package/src/server/canvas-serialization.ts +2 -0
  239. package/src/server/canvas-state.ts +792 -159
  240. package/src/server/card-relocation.ts +53 -0
  241. package/src/server/code-graph.ts +4 -3
  242. package/src/server/context-reads.ts +83 -55
  243. package/src/server/demo-state.json +35 -15
  244. package/src/server/file-watcher.ts +30 -10
  245. package/src/server/human-presence.ts +18 -5
  246. package/src/server/index.ts +35 -15
  247. package/src/server/intent-registry.ts +43 -5
  248. package/src/server/knowledge-lifecycle.ts +32 -9
  249. package/src/server/lifecycle-status.ts +29 -1
  250. package/src/server/mcp-app-runtime.ts +26 -7
  251. package/src/server/mutation-history.ts +37 -8
  252. package/src/server/operations/composites.ts +6 -3
  253. package/src/server/operations/mcp-contracts.ts +15 -0
  254. package/src/server/operations/mcp.ts +36 -9
  255. package/src/server/operations/ops/annotation.ts +22 -1
  256. package/src/server/operations/ops/ax-await.ts +1 -0
  257. package/src/server/operations/ops/ax-timeline.ts +28 -2
  258. package/src/server/operations/ops/batch.ts +25 -11
  259. package/src/server/operations/ops/boards.ts +44 -3
  260. package/src/server/operations/ops/nodes.ts +70 -0
  261. package/src/server/operations/ops/query.ts +32 -3
  262. package/src/server/operations/ops/snapshots.ts +1 -0
  263. package/src/server/operations/registry.ts +49 -4
  264. package/src/server/server.ts +62 -16
  265. package/src/server/tour-control.ts +37 -12
  266. package/src/server/trace-manager.ts +4 -0
  267. package/src/server/web-artifacts/scripts/init-artifact.sh +2 -2
  268. package/src/shared/agent-context.ts +7 -1
  269. package/src/shared/auto-arrange.ts +5 -4
  270. package/src/shared/ax-kinds.ts +13 -0
  271. package/src/shared/ax-surface-protocol.ts +22 -0
  272. package/src/shared/human-presence.ts +1 -0
  273. package/src/shared/lifecycle-status.ts +2 -0
  274. package/dist/canvas/chunks/architecture-TIHT7OUA-9tffvpsv.js +0 -1
  275. package/dist/canvas/chunks/classDiagram-JCYQIIEL-nc6g5x1r.js +0 -1
  276. package/dist/canvas/chunks/classDiagram-v2-OCEON4UE-0qnbpwvh.js +0 -1
  277. package/dist/canvas/chunks/cynefin-VYW2F7L2-ys8p46ts.js +0 -1
  278. package/dist/canvas/chunks/eventmodeling-45OFAUF4-v9q6dn4c.js +0 -1
  279. package/dist/canvas/chunks/flowDiagram-UKHOOZJN-yrr5z8tc.js +0 -1
  280. package/dist/canvas/chunks/gitGraph-TEB2WS4Q-kdwb8m0a.js +0 -1
  281. package/dist/canvas/chunks/index-1r72hdzb.js +0 -301
  282. package/dist/canvas/chunks/index-g3pwnqeb.js +0 -2
  283. package/dist/canvas/chunks/index-rft2hcz5.js +0 -2
  284. package/dist/canvas/chunks/info-DKCQHKI2-wpzs6sz3.js +0 -1
  285. package/dist/canvas/chunks/ishikawaDiagram-WSZJBQD7-5hnq1a9p.js +0 -70
  286. package/dist/canvas/chunks/packet-7NZHBO7P-xn439f0s.js +0 -1
  287. package/dist/canvas/chunks/pie-RZYD4A2V-0kf16rwh.js +0 -1
  288. package/dist/canvas/chunks/radar-I7S5WNFK-0g2pz36t.js +0 -1
  289. package/dist/canvas/chunks/railroad-3IZDKUUU-fcaran6j.js +0 -1
  290. package/dist/canvas/chunks/railroad-abnf-AHOZXSZD-wgdya41q.js +0 -1
  291. package/dist/canvas/chunks/railroad-ebnf-EBAXGLYW-gy07a4ka.js +0 -1
  292. package/dist/canvas/chunks/railroad-peg-LSFZ7HO6-ewk6wtkh.js +0 -1
  293. package/dist/canvas/chunks/stateDiagram-v2-UEYNNEHI-k8sfp2e7.js +0 -1
  294. package/dist/canvas/chunks/swimlanesDiagram-ULZ7WXOC-nqysw67j.js +0 -8
  295. package/dist/canvas/chunks/treeView-QDETBFTQ-cesw6gp0.js +0 -1
  296. package/dist/canvas/chunks/treemap-6X3UGDF4-zdv4knp9.js +0 -1
  297. package/dist/canvas/chunks/vennDiagram-T6HMQDX7-x8dqn9yf.js +0 -34
  298. package/dist/canvas/chunks/wardley-OPB4EBWU-44pdddez.js +0 -1
@@ -8,6 +8,8 @@
8
8
  import { Database } from 'bun:sqlite';
9
9
  import type { ActorAttribution } from './attribution.js';
10
10
  import { CONTEXT_READS_SCHEMA_SQL } from './context-reads.js';
11
+ import { OperationError } from './operations/types.js';
12
+ import { boardMoveBoundary } from './card-relocation.js';
11
13
  import { MIGRATED_SNAPSHOT_FOLDER, normalizeBoardFolder } from '../shared/boards.js';
12
14
  import { wikiLinksFromData } from '../shared/memory-graph.js';
13
15
  import { relationVocabularySchema, type RelationDefinition } from '../shared/relations.js';
@@ -56,6 +58,20 @@ export function normalizeCanvasTheme(value: unknown, fallback: CanvasTheme = 'ha
56
58
  }
57
59
 
58
60
  const SCHEMA_SQL = `
61
+ CREATE TABLE IF NOT EXISTS board_moves (
62
+ request_id TEXT PRIMARY KEY, fingerprint TEXT NOT NULL, result TEXT NOT NULL, created_at TEXT NOT NULL
63
+ );
64
+ CREATE TABLE IF NOT EXISTS board_move_boundaries (
65
+ board_id TEXT PRIMARY KEY, request_id TEXT NOT NULL
66
+ );
67
+ CREATE TABLE IF NOT EXISTS card_relocations (
68
+ sequence INTEGER PRIMARY KEY AUTOINCREMENT, request_id TEXT NOT NULL, node_id TEXT NOT NULL,
69
+ from_board TEXT NOT NULL, to_board TEXT NOT NULL,
70
+ review_after INTEGER NOT NULL, review_through INTEGER NOT NULL,
71
+ read_after INTEGER NOT NULL, read_through INTEGER NOT NULL
72
+ );
73
+ CREATE INDEX IF NOT EXISTS relocation_destination ON card_relocations(to_board, node_id, sequence);
74
+ CREATE INDEX IF NOT EXISTS relocation_source ON card_relocations(from_board, node_id, sequence);
59
75
  CREATE TABLE IF NOT EXISTS node_creation_requests (
60
76
  request_id TEXT PRIMARY KEY, board_id TEXT NOT NULL, fingerprint TEXT NOT NULL, node TEXT NOT NULL
61
77
  );
@@ -357,6 +373,10 @@ function parsePersistedAxState(raw: string | null | undefined): PmxAxState {
357
373
  // ── Persisted State Interface ───────────────────────────────────
358
374
 
359
375
  export interface PersistedCanvasState {
376
+ /** Optimistic storage baseline, independent of content/read/review clocks. New boards start at zero. */
377
+ storageVersion?: number;
378
+ /** Snapshot restore boundary, not an authored content version. */
379
+ moveBoundary?: string | null;
360
380
  tour?: Tour;
361
381
  version: number;
362
382
  theme?: CanvasTheme;
@@ -513,6 +533,28 @@ export function openCanvasDb(dbPath: string, options: OpenCanvasDbOptions = {}):
513
533
  ensureColumn(db, 'snapshot_edges', 'revision', 'revision INTEGER NOT NULL DEFAULT 0');
514
534
  ensureColumn(db, 'snapshot_edges', 'attribution', "attribution TEXT NOT NULL DEFAULT '{}'");
515
535
  ensureColumn(db, 'document_imports', 'committed_node_ids', 'committed_node_ids TEXT');
536
+ ensureColumn(db, 'boards', 'storage_version', 'storage_version INTEGER NOT NULL DEFAULT 0');
537
+ // Cover direct writers as well as full-state saves. A relocation updates both
538
+ // board baselines; a stale source or destination payload must never recreate it.
539
+ for (const table of ['nodes', 'edges', 'annotations', 'context_pins', 'ax_state']) {
540
+ db.exec(`
541
+ CREATE TRIGGER IF NOT EXISTS ${table}_storage_insert AFTER INSERT ON ${table} BEGIN
542
+ UPDATE boards SET storage_version = storage_version + 1 WHERE id = NEW.board_id;
543
+ END;
544
+ CREATE TRIGGER IF NOT EXISTS ${table}_storage_delete AFTER DELETE ON ${table} BEGIN
545
+ UPDATE boards SET storage_version = storage_version + 1 WHERE id = OLD.board_id;
546
+ END;
547
+ CREATE TRIGGER IF NOT EXISTS ${table}_storage_update AFTER UPDATE ON ${table} BEGIN
548
+ UPDATE boards SET storage_version = storage_version + 1 WHERE id IN (OLD.board_id, NEW.board_id);
549
+ END;
550
+ `);
551
+ }
552
+ db.exec(`
553
+ CREATE TRIGGER IF NOT EXISTS boards_storage_view
554
+ AFTER UPDATE OF viewport_x, viewport_y, viewport_scale, tour ON boards BEGIN
555
+ UPDATE boards SET storage_version = storage_version + 1 WHERE id = NEW.id;
556
+ END;
557
+ `);
516
558
 
517
559
  // Set schema version if not present
518
560
  const row = db.query<{ value: string }, [string]>('SELECT value FROM meta WHERE key = ?').get('schema_version');
@@ -762,9 +804,26 @@ export function readThemeFromDB(db: Database): CanvasTheme | undefined {
762
804
  return value ? normalizeCanvasTheme(value) : undefined;
763
805
  }
764
806
 
765
- /** Saves one board's state; every other board's rows are untouched. */
766
- export function saveStateToDB(db: Database, boardId: string, state: PersistedCanvasState): void {
807
+ export function boardStorageVersion(db: Database, boardId: string): number {
808
+ const board = db
809
+ .query<{ storage_version: number }, [string]>('SELECT storage_version FROM boards WHERE id = ?')
810
+ .get(boardId);
811
+ if (!board) throw new OperationError(`Board "${boardId}" not found.`, 404);
812
+ return board.storage_version;
813
+ }
814
+
815
+ export function assertBoardStorageVersion(db: Database, boardId: string, expected: number): void {
816
+ if (boardStorageVersion(db, boardId) !== expected)
817
+ throw new OperationError(
818
+ `Board "${boardId}" changed in another writer. Unsaved edits have been retained; reconcile them before saving.`,
819
+ 409,
820
+ );
821
+ }
822
+
823
+ /** Save one board, returning its new baseline. Publish the baseline only after the outer transaction commits. */
824
+ export function saveStateToDB(db: Database, boardId: string, state: PersistedCanvasState): number {
767
825
  const transaction = db.transaction(() => {
826
+ assertBoardStorageVersion(db, boardId, state.storageVersion ?? 0);
768
827
  // Rows are upserted only when a column actually differs, and rows that are
769
828
  // gone are deleted by id. This used to be DELETE-all + INSERT-all, which
770
829
  // rewrote every row on every debounced save: dragging one node rewrote the
@@ -920,157 +979,164 @@ export function saveStateToDB(db: Database, boardId: string, state: PersistedCan
920
979
  WHERE value IS NOT excluded.value`,
921
980
  [boardId, 'revision', JSON.stringify(state.revisionState ?? { revision: 0, floor: 0, tombstones: [] })],
922
981
  );
982
+ return boardStorageVersion(db, boardId);
923
983
  });
924
984
 
925
- transaction();
985
+ return transaction.immediate();
926
986
  }
927
987
 
928
988
  /** Loads one board (the open board when `boardId` is omitted); null when there is no such board. */
929
989
  export function loadStateFromDB(db: Database, boardId?: string): PersistedCanvasState | null {
930
- const id = boardId ?? getActiveBoardIdFromDB(db);
931
- if (!id) return null;
932
- interface BoardRow {
933
- viewport_x: number;
934
- viewport_y: number;
935
- viewport_scale: number;
936
- tour: string;
937
- }
938
- const board = db
939
- .query<BoardRow, [string]>('SELECT viewport_x, viewport_y, viewport_scale, tour FROM boards WHERE id = ?')
940
- .get(id);
941
- if (!board) return null;
942
-
943
- const viewport: ViewportState = {
944
- x: board.viewport_x,
945
- y: board.viewport_y,
946
- scale: board.viewport_scale || 1,
947
- };
948
- const theme = readThemeFromDB(db);
990
+ return db.transaction(() => {
991
+ const id = boardId ?? getActiveBoardIdFromDB(db);
992
+ if (!id) return null;
993
+ interface BoardRow {
994
+ viewport_x: number;
995
+ viewport_y: number;
996
+ viewport_scale: number;
997
+ tour: string;
998
+ storage_version: number;
999
+ }
1000
+ const board = db
1001
+ .query<BoardRow, [string]>(
1002
+ 'SELECT viewport_x, viewport_y, viewport_scale, tour, storage_version FROM boards WHERE id = ?',
1003
+ )
1004
+ .get(id);
1005
+ if (!board) return null;
949
1006
 
950
- // Load nodes
951
- interface NodeRow {
952
- id: string;
953
- type: string;
954
- pos_x: number;
955
- pos_y: number;
956
- width: number;
957
- height: number;
958
- z_index: number;
959
- collapsed: number;
960
- pinned: number;
961
- data: string;
962
- attribution: string;
963
- content_revision: number;
964
- links_revision: number;
965
- }
966
- const nodeRows = db.query<NodeRow, [string]>('SELECT * FROM nodes WHERE board_id = ?').all(id);
967
- const nodes: CanvasNodeState[] = nodeRows.map((row) => {
968
- const attribution = JSON.parse(row.attribution || '{}') as Partial<
969
- Pick<CanvasNodeState, 'createdBy' | 'lastEditedBy' | 'contentChangedAt' | 'freshnessStartedAt'>
970
- >;
971
- return {
972
- id: row.id,
973
- type: row.type as CanvasNodeState['type'],
974
- position: { x: row.pos_x, y: row.pos_y },
975
- size: { width: row.width, height: row.height },
976
- zIndex: row.z_index,
977
- collapsed: row.collapsed === 1,
978
- pinned: row.pinned === 1,
979
- data: JSON.parse(row.data) as Record<string, unknown>,
980
- createdBy: attribution.createdBy,
981
- lastEditedBy: attribution.lastEditedBy,
982
- contentChangedAt: attribution.contentChangedAt,
983
- freshnessStartedAt: attribution.freshnessStartedAt,
984
- contentRevision: row.content_revision,
985
- linksRevision: row.links_revision,
1007
+ const viewport: ViewportState = {
1008
+ x: board.viewport_x,
1009
+ y: board.viewport_y,
1010
+ scale: board.viewport_scale || 1,
986
1011
  };
987
- });
988
-
989
- // Load edges
990
- interface EdgeRow {
991
- id: string;
992
- from_node: string;
993
- to_node: string;
994
- to_board_id: string | null;
995
- type: string;
996
- kind: string | null;
997
- reason: string | null;
998
- label: string | null;
999
- style: string | null;
1000
- animated: number;
1001
- revision: number;
1002
- attribution: string;
1003
- }
1004
- const edgeRows = db.query<EdgeRow, [string]>('SELECT * FROM edges WHERE board_id = ?').all(id);
1005
- const edges: CanvasEdge[] = edgeRows.map((row) => ({
1006
- id: row.id,
1007
- from: row.from_node,
1008
- to: row.to_node,
1009
- ...(row.to_board_id ? { toBoardId: row.to_board_id } : {}),
1010
- type: row.type as CanvasEdge['type'],
1011
- ...(row.kind ? { kind: row.kind } : {}),
1012
- ...(row.reason ? { reason: row.reason } : {}),
1013
- ...(row.label ? { label: row.label } : {}),
1014
- ...(row.style ? { style: row.style as CanvasEdge['style'] } : {}),
1015
- ...(row.animated ? { animated: true } : {}),
1016
- ...(row.revision ? { revision: row.revision } : {}),
1017
- ...(JSON.parse(row.attribution || '{}') as EdgeAttribution),
1018
- }));
1019
-
1020
- // Load annotations
1021
- interface AnnotationRow {
1022
- id: string;
1023
- type: string;
1024
- points: string;
1025
- bounds: string;
1026
- color: string;
1027
- width: number;
1028
- text: string | null;
1029
- label: string | null;
1030
- created_at: string;
1031
- }
1032
- const annotationRows = db.query<AnnotationRow, [string]>('SELECT * FROM annotations WHERE board_id = ?').all(id);
1033
- const annotations: CanvasAnnotation[] = annotationRows.map((row) => ({
1034
- id: row.id,
1035
- type: row.type as CanvasAnnotation['type'],
1036
- points: JSON.parse(row.points),
1037
- bounds: JSON.parse(row.bounds),
1038
- color: row.color,
1039
- width: row.width,
1040
- ...(row.text ? { text: row.text } : {}),
1041
- ...(row.label ? { label: row.label } : {}),
1042
- createdAt: row.created_at,
1043
- }));
1044
-
1045
- // Load context pins
1046
- interface PinRow {
1047
- node_id: string;
1048
- meta: string | null;
1049
- }
1050
- const pinRows = db.query<PinRow, [string]>('SELECT node_id, meta FROM context_pins WHERE board_id = ?').all(id);
1051
- const contextPins = pinRows.map((row) => row.node_id);
1052
- const contextPinMeta = pinMetaFromRows(pinRows);
1012
+ const theme = readThemeFromDB(db);
1013
+
1014
+ // Load nodes
1015
+ interface NodeRow {
1016
+ id: string;
1017
+ type: string;
1018
+ pos_x: number;
1019
+ pos_y: number;
1020
+ width: number;
1021
+ height: number;
1022
+ z_index: number;
1023
+ collapsed: number;
1024
+ pinned: number;
1025
+ data: string;
1026
+ attribution: string;
1027
+ content_revision: number;
1028
+ links_revision: number;
1029
+ }
1030
+ const nodeRows = db.query<NodeRow, [string]>('SELECT * FROM nodes WHERE board_id = ?').all(id);
1031
+ const nodes: CanvasNodeState[] = nodeRows.map((row) => {
1032
+ const attribution = JSON.parse(row.attribution || '{}') as Partial<
1033
+ Pick<CanvasNodeState, 'createdBy' | 'lastEditedBy' | 'contentChangedAt' | 'freshnessStartedAt'>
1034
+ >;
1035
+ return {
1036
+ id: row.id,
1037
+ type: row.type as CanvasNodeState['type'],
1038
+ position: { x: row.pos_x, y: row.pos_y },
1039
+ size: { width: row.width, height: row.height },
1040
+ zIndex: row.z_index,
1041
+ collapsed: row.collapsed === 1,
1042
+ pinned: row.pinned === 1,
1043
+ data: JSON.parse(row.data) as Record<string, unknown>,
1044
+ createdBy: attribution.createdBy,
1045
+ lastEditedBy: attribution.lastEditedBy,
1046
+ contentChangedAt: attribution.contentChangedAt,
1047
+ freshnessStartedAt: attribution.freshnessStartedAt,
1048
+ contentRevision: row.content_revision,
1049
+ linksRevision: row.links_revision,
1050
+ };
1051
+ });
1052
+
1053
+ // Load edges
1054
+ interface EdgeRow {
1055
+ id: string;
1056
+ from_node: string;
1057
+ to_node: string;
1058
+ to_board_id: string | null;
1059
+ type: string;
1060
+ kind: string | null;
1061
+ reason: string | null;
1062
+ label: string | null;
1063
+ style: string | null;
1064
+ animated: number;
1065
+ revision: number;
1066
+ attribution: string;
1067
+ }
1068
+ const edgeRows = db.query<EdgeRow, [string]>('SELECT * FROM edges WHERE board_id = ?').all(id);
1069
+ const edges: CanvasEdge[] = edgeRows.map((row) => ({
1070
+ id: row.id,
1071
+ from: row.from_node,
1072
+ to: row.to_node,
1073
+ ...(row.to_board_id ? { toBoardId: row.to_board_id } : {}),
1074
+ type: row.type as CanvasEdge['type'],
1075
+ ...(row.kind ? { kind: row.kind } : {}),
1076
+ ...(row.reason ? { reason: row.reason } : {}),
1077
+ ...(row.label ? { label: row.label } : {}),
1078
+ ...(row.style ? { style: row.style as CanvasEdge['style'] } : {}),
1079
+ ...(row.animated ? { animated: true } : {}),
1080
+ ...(row.revision ? { revision: row.revision } : {}),
1081
+ ...(JSON.parse(row.attribution || '{}') as EdgeAttribution),
1082
+ }));
1083
+
1084
+ // Load annotations
1085
+ interface AnnotationRow {
1086
+ id: string;
1087
+ type: string;
1088
+ points: string;
1089
+ bounds: string;
1090
+ color: string;
1091
+ width: number;
1092
+ text: string | null;
1093
+ label: string | null;
1094
+ created_at: string;
1095
+ }
1096
+ const annotationRows = db.query<AnnotationRow, [string]>('SELECT * FROM annotations WHERE board_id = ?').all(id);
1097
+ const annotations: CanvasAnnotation[] = annotationRows.map((row) => ({
1098
+ id: row.id,
1099
+ type: row.type as CanvasAnnotation['type'],
1100
+ points: JSON.parse(row.points),
1101
+ bounds: JSON.parse(row.bounds),
1102
+ color: row.color,
1103
+ width: row.width,
1104
+ ...(row.text ? { text: row.text } : {}),
1105
+ ...(row.label ? { label: row.label } : {}),
1106
+ createdAt: row.created_at,
1107
+ }));
1108
+
1109
+ // Load context pins
1110
+ interface PinRow {
1111
+ node_id: string;
1112
+ meta: string | null;
1113
+ }
1114
+ const pinRows = db.query<PinRow, [string]>('SELECT node_id, meta FROM context_pins WHERE board_id = ?').all(id);
1115
+ const contextPins = pinRows.map((row) => row.node_id);
1116
+ const contextPinMeta = pinMetaFromRows(pinRows);
1053
1117
 
1054
- const axRow = db
1055
- .query<{ value: string }, [string, string]>('SELECT value FROM ax_state WHERE board_id = ? AND key = ?')
1056
- .get(id, 'state');
1118
+ const axRow = db
1119
+ .query<{ value: string }, [string, string]>('SELECT value FROM ax_state WHERE board_id = ? AND key = ?')
1120
+ .get(id, 'state');
1057
1121
 
1058
- const revisionRaw = db
1059
- .query<{ value: string }, [string, string]>('SELECT value FROM ax_state WHERE board_id = ? AND key = ?')
1060
- .get(id, 'revision')?.value;
1061
- return {
1062
- version: 1,
1063
- theme,
1064
- tour: readTour(board.tour),
1065
- viewport,
1066
- nodes,
1067
- edges,
1068
- annotations,
1069
- contextPins,
1070
- contextPinMeta,
1071
- ax: parsePersistedAxState(axRow?.value),
1072
- revisionState: revisionRaw ? (JSON.parse(revisionRaw) as PersistedRevisionState) : undefined,
1073
- };
1122
+ const revisionRaw = db
1123
+ .query<{ value: string }, [string, string]>('SELECT value FROM ax_state WHERE board_id = ? AND key = ?')
1124
+ .get(id, 'revision')?.value;
1125
+ return {
1126
+ storageVersion: board.storage_version,
1127
+ version: 1,
1128
+ theme,
1129
+ tour: readTour(board.tour),
1130
+ viewport,
1131
+ nodes,
1132
+ edges,
1133
+ annotations,
1134
+ contextPins,
1135
+ contextPinMeta,
1136
+ ax: parsePersistedAxState(axRow?.value),
1137
+ revisionState: revisionRaw ? (JSON.parse(revisionRaw) as PersistedRevisionState) : undefined,
1138
+ };
1139
+ })();
1074
1140
  }
1075
1141
 
1076
1142
  // ── Boards ──────────────────────────────────────────────────────
@@ -1387,6 +1453,7 @@ export function saveSnapshotToDB(
1387
1453
  state: PersistedCanvasState,
1388
1454
  ): void {
1389
1455
  const transaction = db.transaction(() => {
1456
+ assertBoardStorageVersion(db, boardId, state.storageVersion ?? 0);
1390
1457
  // Insert snapshot metadata
1391
1458
  db.run('INSERT INTO snapshots (id, board_id, name, created_at, node_count, edge_count) VALUES (?, ?, ?, ?, ?, ?)', [
1392
1459
  snapshot.id,
@@ -1402,6 +1469,11 @@ export function saveSnapshotToDB(
1402
1469
  'tour',
1403
1470
  JSON.stringify(state.tour ?? null),
1404
1471
  ]);
1472
+ db.run('INSERT INTO snapshot_meta (snapshot_id, key, value) VALUES (?, ?, ?)', [
1473
+ snapshot.id,
1474
+ 'move_boundary',
1475
+ boardMoveBoundary(db, boardId) ?? '',
1476
+ ]);
1405
1477
  // Insert snapshot viewport meta
1406
1478
  db.run('INSERT INTO snapshot_meta (snapshot_id, key, value) VALUES (?, ?, ?)', [
1407
1479
  snapshot.id,
@@ -1508,7 +1580,7 @@ export function saveSnapshotToDB(
1508
1580
  ]);
1509
1581
  });
1510
1582
 
1511
- transaction();
1583
+ transaction.immediate();
1512
1584
  }
1513
1585
 
1514
1586
  /** By id or name (most recent match) on `boardId`. */
@@ -1679,6 +1751,7 @@ export function loadSnapshotFromDB(
1679
1751
  return {
1680
1752
  snapshot,
1681
1753
  state: {
1754
+ moveBoundary: metaMap.get('move_boundary') || null,
1682
1755
  version: 1,
1683
1756
  tour: readTour(metaMap.get('tour')),
1684
1757
  viewport,
@@ -2019,28 +2092,37 @@ export function loadAxSteeringFromDB(
2019
2092
 
2020
2093
  export function loadPendingAxSteeringFromDB(
2021
2094
  db: Database,
2022
- options: { consumer?: string; limit?: number } = {},
2095
+ options: { consumer?: string; limit?: number; addressedOnly?: boolean; offset?: number } = {},
2023
2096
  ): PmxAxSteeringMessage[] {
2097
+ if (options.addressedOnly && !options.consumer) return [];
2024
2098
  // FIFO (oldest undelivered first); exclude the consumer's own steering (loop
2025
2099
  // prevention) and any steering addressed to a DIFFERENT consumer (target scoping)
2026
2100
  // in SQL so the LIMIT is applied AFTER both filters, not before.
2027
2101
  const limit = clampTimelineLimit(options.limit);
2028
2102
  const rows = options.consumer
2029
2103
  ? db
2030
- .query<AxSteeringRow, [string, string, string, string, string, number]>(
2104
+ .query<AxSteeringRow, [string, string, string, string, string, number, number]>(
2031
2105
  // A broadcast stays pending PER CONSUMER: it leaves this consumer's
2032
2106
  // queue only once THIS consumer marked it (or an anonymous global ack
2033
2107
  // set delivered).
2034
- 'SELECT * FROM ax_steering s WHERE s.delivered = 0 AND (s.source IS NULL OR s.source != ?) AND (s.agent_id IS NULL OR s.agent_id != ?) AND (s.target IS NULL OR s.target = ?) AND (s.target IS NOT NULL OR s.created_at > ?) AND NOT EXISTS (SELECT 1 FROM ax_steering_deliveries d WHERE d.steering_id = s.id AND d.consumer = ?) ORDER BY s.seq ASC LIMIT ?',
2108
+ `SELECT * FROM ax_steering s WHERE s.delivered = 0 AND (s.source IS NULL OR s.source != ?) AND (s.agent_id IS NULL OR s.agent_id != ?) AND (s.target IS NULL OR s.target = ?) ${options.addressedOnly ? 'AND s.target IS NOT NULL' : ''} AND (s.target IS NOT NULL OR s.created_at > ?) AND NOT EXISTS (SELECT 1 FROM ax_steering_deliveries d WHERE d.steering_id = s.id AND d.consumer = ?) ORDER BY s.seq ASC LIMIT ? OFFSET ?`,
2109
+ )
2110
+ .all(
2111
+ options.consumer,
2112
+ options.consumer,
2113
+ options.consumer,
2114
+ broadcastCutoffIso(),
2115
+ options.consumer,
2116
+ limit,
2117
+ options.offset ?? 0,
2035
2118
  )
2036
- .all(options.consumer, options.consumer, options.consumer, broadcastCutoffIso(), options.consumer, limit)
2037
2119
  : db
2038
- .query<AxSteeringRow, [string, number]>(
2120
+ .query<AxSteeringRow, [string, number, number]>(
2039
2121
  // No consumer given: only broadcasts — addressed steering is claimable
2040
2122
  // solely by the consumer it names.
2041
- 'SELECT * FROM ax_steering WHERE delivered = 0 AND target IS NULL AND created_at > ? ORDER BY seq ASC LIMIT ?',
2123
+ 'SELECT * FROM ax_steering WHERE delivered = 0 AND target IS NULL AND created_at > ? ORDER BY seq ASC LIMIT ? OFFSET ?',
2042
2124
  )
2043
- .all(broadcastCutoffIso(), limit);
2125
+ .all(broadcastCutoffIso(), limit, options.offset ?? 0);
2044
2126
  return rows.map(mapAxSteeringRow).filter((s): s is PmxAxSteeringMessage => s !== null);
2045
2127
  }
2046
2128
 
@@ -2054,20 +2136,29 @@ export function loadPendingAxSteeringFromDB(
2054
2136
  */
2055
2137
  export function loadNewestPendingAxSteeringFromDB(
2056
2138
  db: Database,
2057
- options: { consumer?: string; limit?: number } = {},
2139
+ options: { consumer?: string; limit?: number; addressedOnly?: boolean; offset?: number } = {},
2058
2140
  ): PmxAxSteeringMessage[] {
2141
+ if (options.addressedOnly && !options.consumer) return [];
2059
2142
  const limit = clampTimelineLimit(options.limit);
2060
2143
  const rows = options.consumer
2061
2144
  ? db
2062
- .query<AxSteeringRow, [string, string, string, string, string, number]>(
2063
- 'SELECT * FROM ax_steering s WHERE s.delivered = 0 AND (s.source IS NULL OR s.source != ?) AND (s.agent_id IS NULL OR s.agent_id != ?) AND (s.target IS NULL OR s.target = ?) AND (s.target IS NOT NULL OR s.created_at > ?) AND NOT EXISTS (SELECT 1 FROM ax_steering_deliveries d WHERE d.steering_id = s.id AND d.consumer = ?) ORDER BY s.seq DESC LIMIT ?',
2145
+ .query<AxSteeringRow, [string, string, string, string, string, number, number]>(
2146
+ `SELECT * FROM ax_steering s WHERE s.delivered = 0 AND (s.source IS NULL OR s.source != ?) AND (s.agent_id IS NULL OR s.agent_id != ?) AND (s.target IS NULL OR s.target = ?) ${options.addressedOnly ? 'AND s.target IS NOT NULL' : ''} AND (s.target IS NOT NULL OR s.created_at > ?) AND NOT EXISTS (SELECT 1 FROM ax_steering_deliveries d WHERE d.steering_id = s.id AND d.consumer = ?) ORDER BY s.seq DESC LIMIT ? OFFSET ?`,
2147
+ )
2148
+ .all(
2149
+ options.consumer,
2150
+ options.consumer,
2151
+ options.consumer,
2152
+ broadcastCutoffIso(),
2153
+ options.consumer,
2154
+ limit,
2155
+ options.offset ?? 0,
2064
2156
  )
2065
- .all(options.consumer, options.consumer, options.consumer, broadcastCutoffIso(), options.consumer, limit)
2066
2157
  : db
2067
- .query<AxSteeringRow, [number]>(
2068
- 'SELECT * FROM ax_steering WHERE delivered = 0 AND target IS NULL ORDER BY seq DESC LIMIT ?',
2158
+ .query<AxSteeringRow, [number, number]>(
2159
+ 'SELECT * FROM ax_steering WHERE delivered = 0 AND target IS NULL ORDER BY seq DESC LIMIT ? OFFSET ?',
2069
2160
  )
2070
- .all(limit);
2161
+ .all(limit, options.offset ?? 0);
2071
2162
  return rows.map(mapAxSteeringRow).filter((s): s is PmxAxSteeringMessage => s !== null);
2072
2163
  }
2073
2164