pmx-canvas 0.11.0 → 0.12.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 (300) hide show
  1. package/.github/extensions/pmx-canvas/extension.mjs +4 -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 +51 -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 +267 -0
  11. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/evals.json +1168 -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 +1839 -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 +301 -0
  26. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/tours-and-recording.md +117 -0
  27. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/workbench-workflows.md +908 -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 +22 -2
  151. package/docs/amp-plugin.md +270 -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 +4 -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 +248 -842
  179. package/skills/pmx-canvas/evals/evals.json +110 -5
  180. package/skills/pmx-canvas/references/full-reference.md +142 -28
  181. package/skills/pmx-canvas/references/html-primitives.md +7 -0
  182. package/skills/pmx-canvas/references/installing-pmx-canvas.md +61 -11
  183. package/skills/pmx-canvas/references/tours-and-recording.md +39 -6
  184. package/skills/pmx-canvas/references/workbench-workflows.md +908 -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/bundle-artifact.sh +2 -2
  191. package/skills/web-artifacts-builder/scripts/init-artifact.sh +8 -8
  192. package/src/cli/commands/apps.ts +1 -0
  193. package/src/cli/commands/boards.ts +30 -0
  194. package/src/client/App.tsx +20 -4
  195. package/src/client/canvas/AttentionHistory.tsx +10 -2
  196. package/src/client/canvas/BoardMoveDialog.tsx +225 -0
  197. package/src/client/canvas/CanvasNode.tsx +45 -2
  198. package/src/client/canvas/CanvasViewport.tsx +38 -9
  199. package/src/client/canvas/ContextMenu.tsx +34 -12
  200. package/src/client/canvas/EdgeEditor.tsx +149 -0
  201. package/src/client/canvas/EdgeLayer.tsx +87 -16
  202. package/src/client/canvas/ExpandedNodeOverlay.tsx +4 -0
  203. package/src/client/canvas/GroupNavigation.tsx +85 -0
  204. package/src/client/canvas/RelationsPanel.tsx +9 -0
  205. package/src/client/canvas/SelectionBar.tsx +21 -0
  206. package/src/client/canvas/SessionPanel.tsx +35 -1
  207. package/src/client/canvas/TextPrompt.tsx +18 -1
  208. package/src/client/canvas/TopBar.tsx +30 -0
  209. package/src/client/canvas/canvas-area.ts +1 -1
  210. package/src/client/nodes/ExtAppFrame.tsx +26 -10
  211. package/src/client/nodes/MarkdownNode.tsx +6 -7
  212. package/src/client/nodes/PromptNode.tsx +32 -3
  213. package/src/client/nodes/StatusNode.tsx +12 -0
  214. package/src/client/nodes/StatusSummary.tsx +11 -0
  215. package/src/client/nodes/ViewerFrame.tsx +21 -10
  216. package/src/client/nodes/WorkCard.tsx +175 -0
  217. package/src/client/nodes/use-ax-surface-bridge.ts +18 -14
  218. package/src/client/state/attention-bridge.ts +5 -0
  219. package/src/client/state/attention-store.ts +1 -0
  220. package/src/client/state/canvas-store.ts +185 -15
  221. package/src/client/state/human-store.ts +22 -2
  222. package/src/client/state/intent-bridge.ts +19 -5
  223. package/src/client/state/relation-store.ts +127 -1
  224. package/src/client/theme/global.css +94 -1
  225. package/src/client/types.ts +1 -0
  226. package/src/mcp/server.ts +13 -3
  227. package/src/server/agent-presence.ts +43 -23
  228. package/src/server/ax-flow-loop.ts +6 -0
  229. package/src/server/ax-gate-ttl.ts +8 -1
  230. package/src/server/ax-interaction.ts +16 -10
  231. package/src/server/ax-state-manager.ts +17 -4
  232. package/src/server/ax-wait.ts +20 -6
  233. package/src/server/board-export.ts +19 -6
  234. package/src/server/board-local.ts +20 -0
  235. package/src/server/board-move.ts +297 -0
  236. package/src/server/canvas-db.ts +262 -160
  237. package/src/server/canvas-operations.ts +139 -109
  238. package/src/server/canvas-schema.ts +21 -0
  239. package/src/server/canvas-serialization.ts +2 -0
  240. package/src/server/canvas-state.ts +792 -159
  241. package/src/server/card-relocation.ts +53 -0
  242. package/src/server/code-graph.ts +4 -3
  243. package/src/server/context-reads.ts +83 -55
  244. package/src/server/demo-state.json +35 -15
  245. package/src/server/file-watcher.ts +30 -10
  246. package/src/server/human-presence.ts +18 -5
  247. package/src/server/index.ts +35 -15
  248. package/src/server/intent-registry.ts +43 -5
  249. package/src/server/knowledge-lifecycle.ts +32 -9
  250. package/src/server/lifecycle-status.ts +29 -1
  251. package/src/server/mcp-app-runtime.ts +26 -7
  252. package/src/server/mutation-history.ts +37 -8
  253. package/src/server/operations/composites.ts +6 -3
  254. package/src/server/operations/mcp-contracts.ts +15 -0
  255. package/src/server/operations/mcp.ts +36 -9
  256. package/src/server/operations/ops/annotation.ts +22 -1
  257. package/src/server/operations/ops/ax-await.ts +1 -0
  258. package/src/server/operations/ops/ax-timeline.ts +28 -2
  259. package/src/server/operations/ops/batch.ts +25 -11
  260. package/src/server/operations/ops/boards.ts +44 -3
  261. package/src/server/operations/ops/nodes.ts +70 -0
  262. package/src/server/operations/ops/query.ts +32 -3
  263. package/src/server/operations/ops/snapshots.ts +1 -0
  264. package/src/server/operations/registry.ts +49 -4
  265. package/src/server/server.ts +62 -16
  266. package/src/server/tour-control.ts +37 -12
  267. package/src/server/trace-manager.ts +4 -0
  268. package/src/server/web-artifacts/scripts/bundle-artifact.sh +2 -2
  269. package/src/server/web-artifacts/scripts/init-artifact.sh +8 -8
  270. package/src/shared/agent-context.ts +7 -1
  271. package/src/shared/auto-arrange.ts +5 -4
  272. package/src/shared/ax-kinds.ts +13 -0
  273. package/src/shared/ax-surface-protocol.ts +22 -0
  274. package/src/shared/human-presence.ts +1 -0
  275. package/src/shared/lifecycle-status.ts +2 -0
  276. package/dist/canvas/chunks/architecture-TIHT7OUA-9tffvpsv.js +0 -1
  277. package/dist/canvas/chunks/classDiagram-JCYQIIEL-nc6g5x1r.js +0 -1
  278. package/dist/canvas/chunks/classDiagram-v2-OCEON4UE-0qnbpwvh.js +0 -1
  279. package/dist/canvas/chunks/cynefin-VYW2F7L2-ys8p46ts.js +0 -1
  280. package/dist/canvas/chunks/eventmodeling-45OFAUF4-v9q6dn4c.js +0 -1
  281. package/dist/canvas/chunks/flowDiagram-UKHOOZJN-yrr5z8tc.js +0 -1
  282. package/dist/canvas/chunks/gitGraph-TEB2WS4Q-kdwb8m0a.js +0 -1
  283. package/dist/canvas/chunks/index-1r72hdzb.js +0 -301
  284. package/dist/canvas/chunks/index-g3pwnqeb.js +0 -2
  285. package/dist/canvas/chunks/index-rft2hcz5.js +0 -2
  286. package/dist/canvas/chunks/info-DKCQHKI2-wpzs6sz3.js +0 -1
  287. package/dist/canvas/chunks/ishikawaDiagram-WSZJBQD7-5hnq1a9p.js +0 -70
  288. package/dist/canvas/chunks/packet-7NZHBO7P-xn439f0s.js +0 -1
  289. package/dist/canvas/chunks/pie-RZYD4A2V-0kf16rwh.js +0 -1
  290. package/dist/canvas/chunks/radar-I7S5WNFK-0g2pz36t.js +0 -1
  291. package/dist/canvas/chunks/railroad-3IZDKUUU-fcaran6j.js +0 -1
  292. package/dist/canvas/chunks/railroad-abnf-AHOZXSZD-wgdya41q.js +0 -1
  293. package/dist/canvas/chunks/railroad-ebnf-EBAXGLYW-gy07a4ka.js +0 -1
  294. package/dist/canvas/chunks/railroad-peg-LSFZ7HO6-ewk6wtkh.js +0 -1
  295. package/dist/canvas/chunks/stateDiagram-v2-UEYNNEHI-k8sfp2e7.js +0 -1
  296. package/dist/canvas/chunks/swimlanesDiagram-ULZ7WXOC-nqysw67j.js +0 -8
  297. package/dist/canvas/chunks/treeView-QDETBFTQ-cesw6gp0.js +0 -1
  298. package/dist/canvas/chunks/treemap-6X3UGDF4-zdv4knp9.js +0 -1
  299. package/dist/canvas/chunks/vennDiagram-T6HMQDX7-x8dqn9yf.js +0 -34
  300. package/dist/canvas/chunks/wardley-OPB4EBWU-44pdddez.js +0 -1
@@ -38,6 +38,10 @@ import {
38
38
  } from '../state/session-store';
39
39
  import { formatCountdown, gateRemainingMs } from '../../shared/approval-gates.js';
40
40
  import { agentSessionsEnabled } from '../state/workspace-settings';
41
+ import { humanName, setHumanName } from '../state/human-store';
42
+ import { tabAgentKey } from '../state/intent-bridge';
43
+ import { HUMAN_NAME_MAX_LENGTH } from '../../shared/human-presence.js';
44
+ import { askText } from './TextPrompt';
41
45
 
42
46
  const PRESENT_DESCRIPTION = (
43
47
  <span style={{ whiteSpace: 'pre-line' }}>
@@ -536,6 +540,32 @@ export function TopBar() {
536
540
  <TopBarMore boardOpen={!!activeBoardId.value} onFit={handleFit} />
537
541
  </>
538
542
  )}
543
+ {!isHostedWorkbench() && !tabAgentKey && (
544
+ <span class="human-name-control">
545
+ <BarHint label="Your name" body={`Shown on your cursor: ${humanName.value}`} align="end">
546
+ <button
547
+ type="button"
548
+ class="top-bar-btn human-name-button"
549
+ aria-label={`Your name: ${humanName.value}`}
550
+ aria-haspopup="dialog"
551
+ onClick={async (event) => {
552
+ const button = event.currentTarget;
553
+ const name = await askText('Your name on this canvas', 'e.g. Alex', {
554
+ initial: humanName.value,
555
+ confirm: 'Save name',
556
+ maxLength: HUMAN_NAME_MAX_LENGTH,
557
+ description:
558
+ 'Shown on your cursor. Self-reported, not a verified identity or an access grant. Saved in this browser.',
559
+ });
560
+ if (name !== null) setHumanName(name);
561
+ button.focus({ preventScroll: true });
562
+ }}
563
+ >
564
+ {writerInitial(humanName.value)}
565
+ </button>
566
+ </BarHint>
567
+ </span>
568
+ )}
539
569
  </div>
540
570
  </div>
541
571
  );
@@ -60,7 +60,7 @@ export function canvasFitInsets(): CanvasFitInsets {
60
60
  try {
61
61
  const area = canvasArea();
62
62
  const areaBottom = area.top + area.height;
63
- for (const selector of ['.lifecycle-filters', '.relation-legend']) {
63
+ for (const selector of ['.lifecycle-filters', '.relation-legend', '.group-breadcrumbs']) {
64
64
  const el = document.querySelector(selector);
65
65
  if (!(el instanceof HTMLElement) || !el.isConnected) continue;
66
66
  const rect = el.getBoundingClientRect();
@@ -11,6 +11,8 @@ import {
11
11
  AX_SURFACE_ACK_SOURCE,
12
12
  AX_SURFACE_EMIT_SOURCE,
13
13
  HTML_SURFACE_PUSH_SOURCE,
14
+ MCP_SURFACE_METHOD,
15
+ surfaceNotification,
14
16
  } from '../../shared/ax-surface-protocol.js';
15
17
  import { extAppToolResultsMatch } from '../../shared/ext-app-tool-result.js';
16
18
  import { DEFAULT_EXT_APP_SANDBOX } from '../../shared/surface.js';
@@ -57,17 +59,24 @@ export function buildExtAppAxBridgeScript(axToken: string, nodeId: string): stri
57
59
  (function () {
58
60
  const PMX_AX_TOKEN = ${JSON.stringify(axToken)};
59
61
  const PMX_AX_NODE_ID = ${JSON.stringify(nodeId)};
60
- window.addEventListener('message', function (event) {
62
+ const notify = function (params) {
63
+ parent.postMessage({ jsonrpc: '2.0', method: '${MCP_SURFACE_METHOD}', params: params }, '*');
64
+ };
65
+ const read = function (event) {
61
66
  const m = event.data;
67
+ return event.source === parent && m && m.jsonrpc === '2.0' && m.method === '${MCP_SURFACE_METHOD}' && !('id' in m) ? m.params : null;
68
+ };
69
+ window.addEventListener('message', function (event) {
70
+ const m = read(event);
62
71
  if (event.source !== parent || !m || m.source !== 'pmx-viewer-theme' || m.token !== PMX_AX_TOKEN) return;
63
72
  for (const [name, value] of Object.entries(m.variables)) document.documentElement.style.setProperty(name, value);
64
73
  });
65
74
  window.addEventListener('message', function (event) {
66
- const m = event.data;
75
+ const m = read(event);
67
76
  if (event.source !== parent || !m || m.source !== '${HTML_SURFACE_PUSH_SOURCE}' || m.token !== PMX_AX_TOKEN || m.type !== 'check-ready') return;
68
- parent.postMessage({ source: '${HTML_SURFACE_PUSH_SOURCE}', type: 'mounted', token: PMX_AX_TOKEN }, '*');
77
+ notify({ source: '${HTML_SURFACE_PUSH_SOURCE}', type: 'mounted', token: PMX_AX_TOKEN });
69
78
  requestAnimationFrame(function () { requestAnimationFrame(function () {
70
- parent.postMessage({ source: '${HTML_SURFACE_PUSH_SOURCE}', type: 'ready', token: PMX_AX_TOKEN }, '*');
79
+ notify({ source: '${HTML_SURFACE_PUSH_SOURCE}', type: 'ready', token: PMX_AX_TOKEN });
71
80
  }); });
72
81
  });
73
82
  window.PMX_AX = window.PMX_AX || {};
@@ -83,18 +92,18 @@ export function buildExtAppAxBridgeScript(axToken: string, nodeId: string): stri
83
92
  resolve({ ok: false, status: 504, code: 'ax-ack-timeout', error: 'ax-ack-timeout' });
84
93
  }, 10000);
85
94
  pending.set(correlationId, function (result) { clearTimeout(timer); resolve(result); });
86
- window.parent.postMessage({
95
+ notify({
87
96
  source: '${AX_SURFACE_EMIT_SOURCE}', token: PMX_AX_TOKEN, nodeId: PMX_AX_NODE_ID,
88
97
  correlationId: correlationId,
89
98
  interaction: { type: String(type), payload: payload && typeof payload === 'object' ? payload : {} },
90
- }, '*');
99
+ });
91
100
  });
92
101
  };
93
102
  window.PMX_AX.on = function (eventType, cb) {
94
103
  if (eventType === 'ack' && typeof cb === 'function') ackListeners.push(cb);
95
104
  };
96
105
  window.addEventListener('message', function (event) {
97
- const m = event.data;
106
+ const m = read(event);
98
107
  if (event.source !== window.parent || !m || m.source !== '${AX_SURFACE_ACK_SOURCE}' || m.token !== PMX_AX_TOKEN) return;
99
108
  const result = m.result || { ok: false };
100
109
  const resolver = m.correlationId ? pending.get(m.correlationId) : undefined;
@@ -194,7 +203,14 @@ export function ExtAppFrame({ node, expanded = false }: { node: CanvasNodeState;
194
203
  buildExtAppSurfaceBaseStyles() + buildExtAppAxBridgeScript(axToken, node.id),
195
204
  );
196
205
  const frameDocument = useIframeDocument(documentHtml, DEFAULT_EXT_APP_SANDBOX);
197
- useAxSurfaceBridge({ enabled: axEnabled, token: axToken, nodeId: node.id, sourceSurface: 'mcp-app', iframeRef });
206
+ useAxSurfaceBridge({
207
+ enabled: axEnabled,
208
+ token: axToken,
209
+ nodeId: node.id,
210
+ sourceSurface: 'mcp-app',
211
+ iframeRef,
212
+ jsonRpc: true,
213
+ });
198
214
 
199
215
  const sessionMessage =
200
216
  node.data.sessionStatus === 'error'
@@ -265,7 +281,7 @@ export function ExtAppFrame({ node, expanded = false }: { node: CanvasNodeState;
265
281
  const sendContext = () => {
266
282
  if (disposed) return;
267
283
  iframe.contentWindow?.postMessage(
268
- { source: 'pmx-viewer-theme', token: axToken, variables: canvasVariables() },
284
+ surfaceNotification({ source: 'pmx-viewer-theme', token: axToken, variables: canvasVariables() }),
269
285
  '*',
270
286
  );
271
287
  if (!initialized) return;
@@ -409,7 +425,7 @@ export function ExtAppFrame({ node, expanded = false }: { node: CanvasNodeState;
409
425
  const connection = connections.current.get(frame);
410
426
  if (!connection || !(await connection.loaded()) || signal.aborted) return false;
411
427
  connection.theme();
412
- return waitForViewerPaint(frame, axToken, signal);
428
+ return waitForViewerPaint(frame, axToken, signal, true);
413
429
  }}
414
430
  onOpen={expanded ? undefined : () => expandNode(node.id)}
415
431
  sandbox={DEFAULT_EXT_APP_SANDBOX}
@@ -276,10 +276,8 @@ export function MarkdownNode({ node, expanded = false }: { node: CanvasNodeState
276
276
  if (!renderingDraft && !draft.dirty.value) {
277
277
  latestMdRef.current = raw;
278
278
  setContent(raw);
279
- updateNodeData(node.id, { content: raw, rendered: html });
280
- } else if (nodes.value.get(node.id)?.data.content === raw) {
281
- // A pending save may already contain this text; cache only its HTML.
282
- updateNodeData(node.id, { rendered: html });
279
+ // The HTML cache is local. Publishing each preview redraws the whole board.
280
+ if (nodes.value.get(node.id)?.data.content !== raw) updateNodeData(node.id, { content: raw });
283
281
  }
284
282
  })();
285
283
  return () => {
@@ -303,10 +301,10 @@ export function MarkdownNode({ node, expanded = false }: { node: CanvasNodeState
303
301
 
304
302
  const persistContent = useCallback(
305
303
  async (newContent: string) => {
306
- // Human wins over already-observed work, not work arriving while this
307
- // particular attempt waits in the queue or for its rendered preview.
304
+ // A peer update cannot grant an older draft permission to overwrite it.
305
+ // Only our own successful saves advance the draft's revision.
308
306
  const incarnation = boardIncarnation;
309
- const attempt = { revision: nodes.value.get(node.id)?.contentRevision ?? 0 };
307
+ const attempt = { revision: editBase.current.revision };
310
308
  draft.nativeSaveAttempts.add(attempt);
311
309
  draft.pending.value++;
312
310
  // Order the complete save, including disk writes and rendering. A later
@@ -376,6 +374,7 @@ export function MarkdownNode({ node, expanded = false }: { node: CanvasNodeState
376
374
  if (result.ok && result.contentRevision !== undefined) {
377
375
  // Advance only through a successful predecessor in this board/card's
378
376
  // queue. An external revision or a refused save grants no retry.
377
+ editBase.current.revision = result.contentRevision;
379
378
  for (const queued of draft.nativeSaveAttempts) {
380
379
  if (queued.revision === expectedContentRevision) queued.revision = result.contentRevision;
381
380
  }
@@ -1,10 +1,17 @@
1
- import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'preact/hooks';
2
2
  import { IconClose } from '../icons';
3
+ import { activeBoardId } from '../state/boards-store';
3
4
  import { updateNodeData } from '../state/canvas-store';
4
5
  import { fetchSlashCommands, renderMarkdown, submitCanvasPrompt, submitThreadReply } from '../state/intent-bridge';
5
6
  import type { CanvasNodeState } from '../types';
6
7
  import { sanitizeMarkdownHtml } from './markdown-sanitize';
7
8
 
9
+ // Multiple inline/focus renderers may own inputs for the same qualified card.
10
+ const unsentInputs = new Set<{ boardId: string | null; nodeId: string; draft: string; reply: string }>();
11
+ export function hasUnsavedPrompt(boardId: string, nodeId: string): boolean {
12
+ return [...unsentInputs].some((input) => input.boardId === boardId && input.nodeId === nodeId);
13
+ }
14
+
8
15
  // Cached slash commands — fetched once on first use.
9
16
  let cachedCommands: Array<{ name: string; description: string }> | null = null;
10
17
  async function getCommands() {
@@ -117,8 +124,30 @@ export function PromptNode({ node, expanded = false }: { node: CanvasNodeState;
117
124
  const isStreaming = threadStatus === 'streaming';
118
125
  const isAnswered = threadStatus === 'answered';
119
126
 
120
- const [draft, setDraft] = useState('');
121
- const [replyDraft, setReplyDraft] = useState('');
127
+ const [draft, updateDraft] = useState('');
128
+ const [replyDraft, updateReplyDraft] = useState('');
129
+ const boardId = activeBoardId.value;
130
+ const input = useMemo(() => ({ boardId, nodeId: node.id, draft: '', reply: '' }), [boardId, node.id]);
131
+ const remember = () => {
132
+ if (input.draft || input.reply) unsentInputs.add(input);
133
+ else unsentInputs.delete(input);
134
+ };
135
+ const setDraft = (text: string) => {
136
+ input.draft = text;
137
+ remember();
138
+ updateDraft(text);
139
+ };
140
+ const setReplyDraft = (text: string) => {
141
+ input.reply = text;
142
+ remember();
143
+ updateReplyDraft(text);
144
+ };
145
+ useEffect(
146
+ () => () => {
147
+ unsentInputs.delete(input);
148
+ },
149
+ [input],
150
+ );
122
151
  const [error, setError] = useState<string | null>(null);
123
152
  const [renderedTurns, setRenderedTurns] = useState<Map<number, string>>(new Map());
124
153
  const textareaRef = useRef<HTMLTextAreaElement>(null);
@@ -1,5 +1,7 @@
1
1
  import { PHASE_COLORS } from '../theme/tokens';
2
2
  import type { CanvasNodeState } from '../types';
3
+ import { isWorkCardState } from '../../shared/ax-kinds.js';
4
+ import { WorkCard } from './WorkCard';
3
5
 
4
6
  export function getStatusDisplayPhase(node: CanvasNodeState): string {
5
7
  const phase = typeof node.data.phase === 'string' && node.data.phase.trim().length > 0 ? node.data.phase.trim() : '';
@@ -13,6 +15,16 @@ export function getStatusDisplayPhase(node: CanvasNodeState): string {
13
15
  }
14
16
 
15
17
  export function StatusNode({ node }: { node: CanvasNodeState }) {
18
+ if (typeof node.data.workItemId === 'string' && node.data.liveWorkAvailable === false) {
19
+ return (
20
+ <div class="work-card">
21
+ <div class="work-card-error">⚠ Task unavailable</div>
22
+ <div>The linked task is no longer on this board.</div>
23
+ <div class="work-card-live">Read-only · removing this card does not cancel work.</div>
24
+ </div>
25
+ );
26
+ }
27
+ if (isWorkCardState(node.data.workState)) return <WorkCard node={node} state={node.data.workState} />;
16
28
  const phase = getStatusDisplayPhase(node);
17
29
  const detail = (node.data.detail as string) || '';
18
30
  const message = (node.data.message as string) || '';
@@ -1,8 +1,19 @@
1
1
  import { PHASE_COLORS } from '../theme/tokens';
2
2
  import type { CanvasNodeState } from '../types';
3
3
  import { getStatusDisplayPhase } from './StatusNode';
4
+ import { isWorkCardState, WORK_CARD_STATES } from '../../shared/ax-kinds.js';
4
5
 
5
6
  export function StatusSummary({ node }: { node: CanvasNodeState }) {
7
+ if (typeof node.data.workItemId === 'string' && node.data.liveWorkAvailable === false)
8
+ return <span class="work-card-error">⚠ Task unavailable</span>;
9
+ if (isWorkCardState(node.data.workState)) {
10
+ const state = WORK_CARD_STATES[node.data.workState];
11
+ return (
12
+ <span class={`work-card-state work-card-${node.data.workState}`}>
13
+ <span aria-hidden="true">{state.glyph}</span> {state.label}
14
+ </span>
15
+ );
16
+ }
6
17
  const phase = getStatusDisplayPhase(node);
7
18
  const activeTool = node.data.activeTool as string | null;
8
19
  const subagent = node.data.subagent as { state: string; name: string } | undefined;
@@ -1,6 +1,10 @@
1
1
  import type { JSX, RefObject } from 'preact';
2
2
  import { useEffect, useLayoutEffect, useRef, useState } from 'preact/hooks';
3
- import { HTML_SURFACE_PUSH_SOURCE } from '../../shared/ax-surface-protocol.js';
3
+ import {
4
+ HTML_SURFACE_PUSH_SOURCE,
5
+ readSurfaceNotification,
6
+ surfaceNotification,
7
+ } from '../../shared/ax-surface-protocol.js';
4
8
 
5
9
  export const VIEWER_LOAD_DEADLINE_MS = 10_000;
6
10
  export type ViewerSource = { src?: string; srcdoc?: string; error?: boolean; retry?: () => void };
@@ -12,7 +16,12 @@ type Entry = { key: string; source: ViewerSource; attempt: number; load?: LoadDo
12
16
  type MountFrame = (frame: HTMLIFrameElement, fail: () => void) => () => void;
13
17
 
14
18
  /** The document acknowledges mounting before its frame-gated paint signal. */
15
- export function waitForViewerPaint(frame: HTMLIFrameElement, token: string, signal: AbortSignal): Promise<boolean> {
19
+ export function waitForViewerPaint(
20
+ frame: HTMLIFrameElement,
21
+ token: string,
22
+ signal: AbortSignal,
23
+ jsonRpc = false,
24
+ ): Promise<boolean> {
16
25
  if (signal.aborted) return Promise.resolve(false);
17
26
  return new Promise((resolve) => {
18
27
  const finish = (ready: boolean) => {
@@ -23,21 +32,23 @@ export function waitForViewerPaint(frame: HTMLIFrameElement, token: string, sign
23
32
  };
24
33
  const cancel = () => finish(false);
25
34
  const receive = (event: MessageEvent) => {
26
- if (
27
- event.source !== frame.contentWindow ||
28
- event.data?.source !== HTML_SURFACE_PUSH_SOURCE ||
29
- event.data?.token !== token
30
- )
35
+ const data = (jsonRpc ? readSurfaceNotification(event.data) : event.data) as {
36
+ source?: string;
37
+ token?: string;
38
+ type?: string;
39
+ } | null;
40
+ if (event.source !== frame.contentWindow || data?.source !== HTML_SURFACE_PUSH_SOURCE || data?.token !== token)
31
41
  return;
32
- if (event.data.type === 'mounted') {
42
+ if (data.type === 'mounted') {
33
43
  clearTimeout(timer);
34
44
  }
35
- if (event.data.type === 'ready') finish(true);
45
+ if (data.type === 'ready') finish(true);
36
46
  };
37
47
  const timer = setTimeout(cancel, VIEWER_LOAD_DEADLINE_MS);
38
48
  window.addEventListener('message', receive);
39
49
  signal.addEventListener('abort', cancel, { once: true });
40
- frame.contentWindow?.postMessage({ source: HTML_SURFACE_PUSH_SOURCE, type: 'check-ready', token }, '*');
50
+ const message = { source: HTML_SURFACE_PUSH_SOURCE, type: 'check-ready', token };
51
+ frame.contentWindow?.postMessage(jsonRpc ? surfaceNotification(message) : message, '*');
41
52
  });
42
53
  }
43
54
 
@@ -0,0 +1,175 @@
1
+ import { signal, type Signal } from '@preact/signals';
2
+ import { useMemo } from 'preact/hooks';
3
+ import { WORK_CARD_STATES, type AxWorkItemStatus } from '../../shared/ax-kinds.js';
4
+ import { activeBoardId } from '../state/boards-store';
5
+ import { updateNodeFromClient } from '../state/intent-bridge';
6
+ import type { CanvasNodeState } from '../types';
7
+
8
+ type WorkDraft = {
9
+ title: string;
10
+ state: AxWorkItemStatus;
11
+ detail: string;
12
+ progress: string;
13
+ boardId: string | null;
14
+ revision: number;
15
+ saving: boolean;
16
+ error: string;
17
+ };
18
+ // Like Markdown drafts, these belong to the qualified card, not its inline/focus renderer.
19
+ const drafts = new Map<string, Signal<WorkDraft | null>>();
20
+
21
+ export function hasUnsavedWorkCard(boardId: string, nodeId: string): boolean {
22
+ return !!drafts.get(JSON.stringify([boardId, nodeId]))?.value;
23
+ }
24
+
25
+ export function WorkCard({ node, state }: { node: CanvasNodeState; state: AxWorkItemStatus }) {
26
+ const key = JSON.stringify([activeBoardId.value, node.id]);
27
+ const editing = useMemo(() => drafts.get(key) ?? signal<WorkDraft | null>(null), [key]);
28
+ const draft = editing.value;
29
+ const saving = draft?.saving ?? false;
30
+ const error = draft?.error ?? '';
31
+ const setDraft = (next: WorkDraft | null) => {
32
+ if (next) drafts.set(key, editing);
33
+ else drafts.delete(key);
34
+ editing.value = next;
35
+ };
36
+ const detail = typeof node.data.detail === 'string' ? node.data.detail : '';
37
+ const progress = typeof node.data.progress === 'number' ? node.data.progress : null;
38
+
39
+ const edit = () => {
40
+ setDraft({
41
+ title: typeof node.data.title === 'string' ? node.data.title : '',
42
+ state,
43
+ detail,
44
+ progress: progress === null ? '' : String(progress),
45
+ boardId: activeBoardId.value,
46
+ revision: node.contentRevision ?? 0,
47
+ saving: false,
48
+ error: '',
49
+ });
50
+ };
51
+ const save = async () => {
52
+ if (!draft || saving) return;
53
+ setDraft({ ...draft, saving: true, error: '' });
54
+ const result = await updateNodeFromClient(node.id, {
55
+ title: draft.title.trim(),
56
+ data: {
57
+ workState: draft.state,
58
+ detail: draft.detail,
59
+ progress: draft.progress === '' ? null : Number(draft.progress),
60
+ },
61
+ expectedBoardId: draft.boardId,
62
+ expectedContentRevision: draft.revision,
63
+ });
64
+ if (result.ok) setDraft(null);
65
+ else
66
+ setDraft({
67
+ ...draft,
68
+ saving: false,
69
+ error: 'Could not save. Your draft is still here. Check the connection or whether the card changed.',
70
+ });
71
+ };
72
+
73
+ if (draft) {
74
+ return (
75
+ <form
76
+ class="work-card-editor"
77
+ onSubmit={(event) => {
78
+ event.preventDefault();
79
+ void save();
80
+ }}
81
+ onKeyDown={(event) => {
82
+ event.stopPropagation();
83
+ if (event.key === 'Escape' && !saving) {
84
+ event.preventDefault();
85
+ setDraft(null);
86
+ }
87
+ }}
88
+ >
89
+ <fieldset disabled={saving}>
90
+ <label>
91
+ Title
92
+ <input required value={draft.title} onInput={(e) => setDraft({ ...draft, title: e.currentTarget.value })} />
93
+ </label>
94
+ <label>
95
+ Work state
96
+ <select
97
+ value={draft.state}
98
+ onChange={(e) => setDraft({ ...draft, state: e.currentTarget.value as AxWorkItemStatus })}
99
+ >
100
+ {Object.entries(WORK_CARD_STATES).map(([value, item]) => (
101
+ <option key={value} value={value}>
102
+ {item.label}
103
+ </option>
104
+ ))}
105
+ </select>
106
+ </label>
107
+ <label>
108
+ Detail
109
+ <textarea value={draft.detail} onInput={(e) => setDraft({ ...draft, detail: e.currentTarget.value })} />
110
+ </label>
111
+ <label>
112
+ Progress (%) · optional
113
+ <input
114
+ type="number"
115
+ min="0"
116
+ max="100"
117
+ step="any"
118
+ value={draft.progress}
119
+ onInput={(e) => setDraft({ ...draft, progress: e.currentTarget.value })}
120
+ />
121
+ </label>
122
+ {error && (
123
+ <div class="work-card-error" role="alert">
124
+ {error}
125
+ </div>
126
+ )}
127
+ <footer>
128
+ <button type="button" onClick={() => setDraft(null)}>
129
+ Cancel
130
+ </button>
131
+ <button class="work-card-save" type="submit">
132
+ {saving ? 'Saving…' : 'Save'}
133
+ </button>
134
+ </footer>
135
+ </fieldset>
136
+ </form>
137
+ );
138
+ }
139
+
140
+ return (
141
+ <div class={`work-card work-card-${state}`}>
142
+ {typeof node.data.workItemId === 'string' && (
143
+ <div class="work-card-live">
144
+ ↗ Live task{node.data.liveWorkSource ? ` · ${node.data.liveWorkSource}` : ''} · Read-only
145
+ </div>
146
+ )}
147
+ <div class="work-card-state">
148
+ <span aria-hidden="true">{WORK_CARD_STATES[state].glyph}</span> {WORK_CARD_STATES[state].label}
149
+ </div>
150
+ {detail && <div class="work-card-detail">{detail}</div>}
151
+ {progress !== null && (
152
+ <div class="work-card-progress">
153
+ <span>{progress}% complete</span>
154
+ <div
155
+ class="status-progress"
156
+ role="progressbar"
157
+ aria-label="Progress"
158
+ aria-valuemin={0}
159
+ aria-valuemax={100}
160
+ aria-valuenow={progress}
161
+ >
162
+ <div style={{ width: `${progress}%`, background: 'currentColor' }} />
163
+ </div>
164
+ </div>
165
+ )}
166
+ {typeof node.data.workItemId !== 'string' && (
167
+ <footer>
168
+ <button type="button" onClick={edit}>
169
+ Edit
170
+ </button>
171
+ </footer>
172
+ )}
173
+ </div>
174
+ );
175
+ }
@@ -1,6 +1,11 @@
1
1
  import type { RefObject } from 'preact';
2
2
  import { useLayoutEffect } from 'preact/hooks';
3
- import { AX_SURFACE_ACK_SOURCE, AX_SURFACE_EMIT_SOURCE } from '../../shared/ax-surface-protocol.js';
3
+ import {
4
+ AX_SURFACE_ACK_SOURCE,
5
+ AX_SURFACE_EMIT_SOURCE,
6
+ readSurfaceNotification,
7
+ surfaceNotification,
8
+ } from '../../shared/ax-surface-protocol.js';
4
9
  import { showToast } from '../state/attention-bridge';
5
10
  import { submitAxInteractionFromClient } from '../state/intent-bridge';
6
11
 
@@ -23,8 +28,9 @@ export function useAxSurfaceBridge(options: {
23
28
  nodeId: string;
24
29
  sourceSurface: AxSurfaceBridgeSurface;
25
30
  iframeRef: RefObject<HTMLIFrameElement>;
31
+ jsonRpc?: boolean;
26
32
  }): void {
27
- const { enabled, token, nodeId, sourceSurface, iframeRef } = options;
33
+ const { enabled, token, nodeId, sourceSurface, iframeRef, jsonRpc = false } = options;
28
34
  // Register before paint: a fast iframe can accept input before passive effects
29
35
  // run, and its first emit must not be lost in that gap.
30
36
  useLayoutEffect(() => {
@@ -33,7 +39,7 @@ export function useAxSurfaceBridge(options: {
33
39
  // Bind to THIS node's own iframe; the nonce + nodeId are a second gate,
34
40
  // not the only one.
35
41
  if (event.source !== iframeRef.current?.contentWindow) return;
36
- const data = event.data as {
42
+ const data = (jsonRpc ? readSurfaceNotification(event.data) : event.data) as {
37
43
  source?: string;
38
44
  token?: string;
39
45
  nodeId?: string;
@@ -54,20 +60,18 @@ export function useAxSurfaceBridge(options: {
54
60
  }).then((res) => {
55
61
  // A refusal is toasted by the bridge with the server's reason.
56
62
  if (res.ok) showToast('context', 'AX interaction', interactionType, [nodeId]);
57
- iframeRef.current?.contentWindow?.postMessage(
58
- {
59
- source: AX_SURFACE_ACK_SOURCE,
60
- token,
61
- ...(data.correlationId ? { correlationId: data.correlationId } : {}),
62
- interaction: { type: interactionType },
63
- result: res,
64
- },
65
- '*',
66
- );
63
+ const message = {
64
+ source: AX_SURFACE_ACK_SOURCE,
65
+ token,
66
+ ...(data.correlationId ? { correlationId: data.correlationId } : {}),
67
+ interaction: { type: interactionType },
68
+ result: res,
69
+ };
70
+ iframeRef.current?.contentWindow?.postMessage(jsonRpc ? surfaceNotification(message) : message, '*');
67
71
  });
68
72
  }
69
73
  window.addEventListener('message', onAxMessage);
70
74
  return () => window.removeEventListener('message', onAxMessage);
71
75
  // eslint-disable-next-line react-hooks/exhaustive-deps
72
- }, [enabled, token, nodeId, sourceSurface]);
76
+ }, [enabled, token, nodeId, sourceSurface, jsonRpc]);
73
77
  }
@@ -9,6 +9,7 @@ import {
9
9
  type SseMessage,
10
10
  } from '../../shared/semantic-attention.js';
11
11
  import {
12
+ attentionHistoryOpen,
12
13
  pushAttentionHistory,
13
14
  resetAttentionState,
14
15
  setAttentionPulse,
@@ -291,6 +292,10 @@ function entryFromEvent(event: SemanticWatchEvent): AttentionEntry | null {
291
292
  }
292
293
 
293
294
  function flushToastQueue(): void {
295
+ if (attentionHistoryOpen.value) {
296
+ toastQueue = [];
297
+ return;
298
+ }
294
299
  if (toastTimer !== null) return;
295
300
  const next = toastQueue.shift() ?? null;
296
301
  if (!next) {
@@ -26,6 +26,7 @@ export function resetAttentionState(): void {
26
26
  }
27
27
 
28
28
  export function openAttentionHistory(): void {
29
+ attentionToast.value = null;
29
30
  attentionHistoryOpen.value = true;
30
31
  attentionHistoryUnread.value = 0;
31
32
  }