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.
- package/.github/extensions/pmx-canvas/extension.mjs +3 -1
- package/.github/extensions/pmx-canvas/runtime.json +1 -1
- package/.github/extensions/pmx-canvas/tools.json +440 -18
- package/CHANGELOG.md +37 -0
- package/dist/amp-plugin/pmx-canvas/LICENSE +21 -0
- package/dist/amp-plugin/pmx-canvas/THIRD_PARTY_LICENSES.txt +331 -0
- package/dist/amp-plugin/pmx-canvas/index.js +5445 -0
- package/dist/amp-plugin/pmx-canvas/runtime.json +3 -0
- package/dist/amp-plugin/pmx-canvas/shared-runtime.mjs +194 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/SKILL.md +251 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/evals.json +1086 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/fixtures/code-exploration/src/auth/jwt.ts +17 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/fixtures/code-exploration/src/auth/login.ts +12 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/fixtures/code-exploration/src/auth/middleware.ts +13 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/fixtures/code-exploration/src/routes/auth.ts +13 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/fixtures/investigation-board/src/handlers/users.ts +27 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/mcp.json +36 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/ax-html-control-surface.md +101 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/codex-app-adapter.md +151 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/excalidraw-diagram-authoring.md +150 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/full-reference.md +1785 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/github-copilot-app-adapter.md +223 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/html-primitives.md +139 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/importing-context.md +132 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/installing-pmx-canvas.md +251 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/tours-and-recording.md +111 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/workbench-workflows.md +900 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas-orchestration/SKILL.md +141 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas-testing/SKILL.md +266 -0
- package/dist/amp-plugin/pmx-canvas/start-mcp.mjs +14857 -0
- package/dist/amp-plugin/pmx-canvas/tools.json +10963 -0
- package/dist/canvas/chunks/{abnfDiagram-N423BO3Z-p8dtp6va.js → abnfDiagram-N423BO3Z-cwtsds5d.js} +1 -1
- package/dist/canvas/chunks/architecture-TIHT7OUA-x63cwejs.js +1 -0
- package/dist/canvas/chunks/{architectureDiagram-T3A2C74G-5jhmxrzf.js → architectureDiagram-T3A2C74G-b4xc9tpz.js} +3 -3
- package/dist/canvas/chunks/{blockDiagram-VBNYF7ZC-pw1fa9yb.js → blockDiagram-VBNYF7ZC-ahp3yxxs.js} +7 -7
- package/dist/canvas/chunks/{c4Diagram-5PPSVZJV-x07nq0tt.js → c4Diagram-5PPSVZJV-nappnay7.js} +4 -4
- package/dist/canvas/chunks/classDiagram-JCYQIIEL-k1j2xc1t.js +1 -0
- package/dist/canvas/chunks/classDiagram-v2-OCEON4UE-33x08xf2.js +1 -0
- package/dist/canvas/chunks/{cose-bilkent-JH36ORCC-nm39npq0.js → cose-bilkent-JH36ORCC-a428syda.js} +1 -1
- package/dist/canvas/chunks/cynefin-VYW2F7L2-8v6jxfz8.js +1 -0
- package/dist/canvas/chunks/{cynefinDiagram-MW4NZA55-vdxrwahz.js → cynefinDiagram-MW4NZA55-nspsqebj.js} +1 -1
- package/dist/canvas/chunks/{dagre-VZM6K2ZE-bqdm7wpd.js → dagre-VZM6K2ZE-8y6177fh.js} +2 -2
- package/dist/canvas/chunks/{diagram-7IWD3JNH-gs5gpr93.js → diagram-7IWD3JNH-vym028wn.js} +4 -4
- package/dist/canvas/chunks/{diagram-B4RE2ZJO-cgwjg47s.js → diagram-B4RE2ZJO-jddqb8sy.js} +1 -1
- package/dist/canvas/chunks/{diagram-LBJQPF4R-w3vahpav.js → diagram-LBJQPF4R-bjwbpkrn.js} +1 -1
- package/dist/canvas/chunks/{diagram-Q27KOJAE-42837z6h.js → diagram-Q27KOJAE-j6ph6hcx.js} +2 -2
- package/dist/canvas/chunks/{diagram-UB23O5K3-4fz83q59.js → diagram-UB23O5K3-p09w8jej.js} +1 -1
- package/dist/canvas/chunks/{ebnfDiagram-BXEA7PRR-zphtgegj.js → ebnfDiagram-BXEA7PRR-j975ycq5.js} +1 -1
- package/dist/canvas/chunks/{erDiagram-JOGREHBK-zvbbdxp4.js → erDiagram-JOGREHBK-sxp6hjn8.js} +5 -5
- package/dist/canvas/chunks/eventmodeling-45OFAUF4-xzfgn6xg.js +1 -0
- package/dist/canvas/chunks/flowDiagram-UKHOOZJN-2nsmkskb.js +1 -0
- package/dist/canvas/chunks/{ganttDiagram-PKOTCBZU-282jzycm.js → ganttDiagram-PKOTCBZU-4tv230y2.js} +2 -2
- package/dist/canvas/chunks/gitGraph-TEB2WS4Q-macbhand.js +1 -0
- package/dist/canvas/chunks/{gitGraphDiagram-DS77QQ5N-vqqy6p1b.js → gitGraphDiagram-DS77QQ5N-mqgmnw9g.js} +1 -1
- package/dist/canvas/chunks/{index-wgv7en21.js → index-037z5dz2.js} +2 -2
- package/dist/canvas/chunks/{index-w2mra17t.js → index-081n3har.js} +1 -1
- package/dist/canvas/chunks/{index-353m7b0p.js → index-5n03dmb9.js} +1 -1
- package/dist/canvas/chunks/{index-yw8h9vha.js → index-60pgzdqe.js} +1 -1
- package/dist/canvas/chunks/index-8g1vb57n.js +2 -0
- package/dist/canvas/chunks/{index-vv56h019.js → index-8rjes9r6.js} +1 -1
- package/dist/canvas/chunks/{index-nprm96xp.js → index-9022d1n9.js} +1 -1
- package/dist/canvas/chunks/{index-194m3hc3.js → index-9b02042t.js} +1 -1
- package/dist/canvas/chunks/{index-0rk4gp1g.js → index-ap4q4vez.js} +1 -1
- package/dist/canvas/chunks/{index-rh7nv68n.js → index-dyp2nhd3.js} +1 -1
- package/dist/canvas/chunks/index-frvta4tm.js +2 -0
- package/dist/canvas/chunks/{index-s4j678gj.js → index-h9ycj8n2.js} +1 -1
- package/dist/canvas/chunks/{index-fjtdggjn.js → index-mergmj97.js} +2 -2
- package/dist/canvas/chunks/index-nwsya6dj.js +301 -0
- package/dist/canvas/chunks/{index-3cy1bzwh.js → index-nz8qbd1p.js} +1 -1
- package/dist/canvas/chunks/{index-d3b3gqe9.js → index-qhfrazv3.js} +1 -1
- package/dist/canvas/chunks/{index-6qmsnxwy.js → index-v119nznk.js} +4 -4
- package/dist/canvas/chunks/{index-r2fyc9w1.js → index-vpt37m3d.js} +6 -6
- package/dist/canvas/chunks/{index-qj8fxwsf.js → index-y9ma6azy.js} +1 -1
- package/dist/canvas/chunks/{index-tb1wf525.js → index-zx0j3a2z.js} +26 -26
- package/dist/canvas/chunks/info-DKCQHKI2-a0ykzs9z.js +1 -0
- package/dist/canvas/chunks/{infoDiagram-6WML65LV-v1yvggqj.js → infoDiagram-6WML65LV-qv97qq6a.js} +2 -2
- package/dist/canvas/chunks/ishikawaDiagram-WSZJBQD7-p9306gh0.js +70 -0
- package/dist/canvas/chunks/{journeyDiagram-NVQOT4AX-zjcffveh.js → journeyDiagram-NVQOT4AX-sv67zats.js} +5 -5
- package/dist/canvas/chunks/{kanban-definition-27J2QSJJ-dakqtfp1.js → kanban-definition-27J2QSJJ-k2pfam6p.js} +1 -1
- package/dist/canvas/chunks/{mermaid.core-j2geswrd.js → mermaid.core-83r0fdce.js} +4 -4
- package/dist/canvas/chunks/{mindmap-definition-FAOFIHXS-142xhr3k.js → mindmap-definition-FAOFIHXS-vg2newm1.js} +1 -1
- package/dist/canvas/chunks/packet-7NZHBO7P-tqnabcdv.js +1 -0
- package/dist/canvas/chunks/{pegDiagram-VL7TDLO6-5wvk7vsd.js → pegDiagram-VL7TDLO6-k2h7zc5n.js} +1 -1
- package/dist/canvas/chunks/pie-RZYD4A2V-wke6gyhe.js +1 -0
- package/dist/canvas/chunks/{pieDiagram-7S7Q4E2Y-ztdhwycc.js → pieDiagram-7S7Q4E2Y-hg169p1v.js} +2 -2
- package/dist/canvas/chunks/{quadrantDiagram-CIZ2JOQS-c5p9gmg6.js → quadrantDiagram-CIZ2JOQS-bxtvsq0b.js} +5 -5
- package/dist/canvas/chunks/radar-I7S5WNFK-w7ka0drw.js +1 -0
- package/dist/canvas/chunks/railroad-3IZDKUUU-y3vg318x.js +1 -0
- package/dist/canvas/chunks/railroad-abnf-AHOZXSZD-6hqvcz3c.js +1 -0
- package/dist/canvas/chunks/railroad-ebnf-EBAXGLYW-vcae260m.js +1 -0
- package/dist/canvas/chunks/railroad-peg-LSFZ7HO6-0dbyme23.js +1 -0
- package/dist/canvas/chunks/{railroadDiagram-AXF67PYL-xkmqkjwf.js → railroadDiagram-AXF67PYL-njyhcp4p.js} +1 -1
- package/dist/canvas/chunks/{requirementDiagram-LRYGKXZP-9wm2dyaw.js → requirementDiagram-LRYGKXZP-2z6v09gm.js} +4 -4
- package/dist/canvas/chunks/{sankeyDiagram-W5VNT64P-2w19etgb.js → sankeyDiagram-W5VNT64P-15mzam0e.js} +1 -1
- package/dist/canvas/chunks/{sequenceDiagram-SI44F4Z6-0jn0vwmz.js → sequenceDiagram-SI44F4Z6-rbshyd2c.js} +12 -12
- package/dist/canvas/chunks/{sizeCapture-X5ZJPWSS-x75epwdq.js → sizeCapture-X5ZJPWSS-sg26xwts.js} +1 -1
- package/dist/canvas/chunks/{stateDiagram-OKZ733FA-schjpy6d.js → stateDiagram-OKZ733FA-6eahp98e.js} +1 -1
- package/dist/canvas/chunks/stateDiagram-v2-UEYNNEHI-tty0ggn5.js +1 -0
- package/dist/canvas/chunks/{swimlanes-SLNWSIFB-gkw4xce3.js → swimlanes-SLNWSIFB-ze5db10t.js} +1 -1
- package/dist/canvas/chunks/swimlanesDiagram-ULZ7WXOC-thze75gq.js +8 -0
- package/dist/canvas/chunks/{timeline-definition-Z64GVDOM-x7nkcy01.js → timeline-definition-Z64GVDOM-vnp7jyy8.js} +3 -3
- package/dist/canvas/chunks/treeView-QDETBFTQ-6ysffnm6.js +1 -0
- package/dist/canvas/chunks/treemap-6X3UGDF4-m3h9p066.js +1 -0
- package/dist/canvas/chunks/vennDiagram-T6HMQDX7-0qrnb7mj.js +34 -0
- package/dist/canvas/chunks/wardley-OPB4EBWU-dp37drwx.js +1 -0
- package/dist/canvas/chunks/{wardleyDiagram-T6FBY63Y-t1j52t9r.js → wardleyDiagram-T6FBY63Y-8nxg8wqt.js} +4 -4
- package/dist/canvas/chunks/{xychartDiagram-ELKLHX3M-38t16pme.js → xychartDiagram-ELKLHX3M-nj921e7s.js} +5 -5
- package/dist/canvas/global.css +94 -1
- package/dist/canvas/index.js +154 -145
- package/dist/canvas/mcp-app.html +600 -498
- package/dist/json-render/index.css +1 -1
- package/dist/types/client/canvas/BoardMoveDialog.d.ts +2 -0
- package/dist/types/client/canvas/EdgeEditor.d.ts +19 -0
- package/dist/types/client/canvas/GroupNavigation.d.ts +2 -0
- package/dist/types/client/canvas/TextPrompt.d.ts +4 -0
- package/dist/types/client/nodes/PromptNode.d.ts +1 -0
- package/dist/types/client/nodes/ViewerFrame.d.ts +1 -1
- package/dist/types/client/nodes/WorkCard.d.ts +7 -0
- package/dist/types/client/nodes/use-ax-surface-bridge.d.ts +1 -0
- package/dist/types/client/state/canvas-store.d.ts +15 -0
- package/dist/types/client/state/human-store.d.ts +2 -0
- package/dist/types/client/state/intent-bridge.d.ts +4 -2
- package/dist/types/client/state/relation-store.d.ts +11 -0
- package/dist/types/client/types.d.ts +1 -1
- package/dist/types/json-render/catalog.d.ts +2 -2
- package/dist/types/server/agent-presence.d.ts +1 -0
- package/dist/types/server/ax-interaction.d.ts +1 -0
- package/dist/types/server/ax-state-manager.d.ts +10 -2
- package/dist/types/server/board-local.d.ts +4 -0
- package/dist/types/server/board-move.d.ts +38 -0
- package/dist/types/server/canvas-db.d.ts +12 -2
- package/dist/types/server/canvas-operations.d.ts +5 -6
- package/dist/types/server/canvas-provenance.d.ts +1 -1
- package/dist/types/server/canvas-serialization.d.ts +1 -0
- package/dist/types/server/canvas-state.d.ts +84 -20
- package/dist/types/server/card-relocation.d.ts +6 -0
- package/dist/types/server/context-reads.d.ts +3 -1
- package/dist/types/server/human-presence.d.ts +1 -1
- package/dist/types/server/index.d.ts +16 -5
- package/dist/types/server/intent-registry.d.ts +8 -4
- package/dist/types/server/knowledge-lifecycle.d.ts +4 -2
- package/dist/types/server/mutation-history.d.ts +7 -5
- package/dist/types/server/operations/registry.d.ts +2 -0
- package/dist/types/server/trace-manager.d.ts +1 -0
- package/dist/types/shared/ax-kinds.d.ts +6 -0
- package/dist/types/shared/ax-surface-protocol.d.ts +8 -0
- package/dist/types/shared/human-presence.d.ts +1 -0
- package/dist/types/shared/lifecycle-status.d.ts +2 -0
- package/docs/RELEASE.md +21 -14
- package/docs/agent-plugin.md +16 -2
- package/docs/amp-plugin.md +271 -0
- package/docs/cli.md +42 -3
- package/docs/design/GuestName.dc.html +50 -0
- package/docs/design/PluginLogo.svg +7 -0
- package/docs/design/WorkCards.dc.html +39 -0
- package/docs/design/canvas.json +16 -0
- package/docs/design/plugin-logo.png +0 -0
- package/docs/design.md +111 -8
- package/docs/evals/agents-md-sections.md +38 -0
- package/docs/http-api.md +128 -10
- package/docs/mcp.md +61 -9
- package/docs/node-types.md +71 -1
- package/docs/plans/plan-009-tech-debt-backlog.md +1 -1
- package/docs/plans/plan-019-post-0.9-roadmap.md +44 -20
- package/docs/plans/plan-020-wave-5-work-planning.md +553 -0
- package/docs/product-vision-2026-09.md +33 -17
- package/docs/sdk.md +18 -0
- package/package.json +6 -3
- package/plugins/pmx-canvas/assets/preview.png +0 -0
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/extension.mjs +3 -1
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.json +1 -1
- package/plugins/pmx-canvas/plugin.json +2 -2
- package/plugins/pmx-canvas/runtime.json +1 -1
- package/plugins/pmx-canvas/tools.json +440 -18
- package/skills/data-analysis/SKILL.md +13 -26
- package/skills/frontend-design/LICENSE.txt +177 -0
- package/skills/frontend-design/SKILL.md +3 -0
- package/skills/pmx-canvas/SKILL.md +232 -842
- package/skills/pmx-canvas/evals/evals.json +26 -3
- package/skills/pmx-canvas/references/full-reference.md +80 -20
- package/skills/pmx-canvas/references/html-primitives.md +7 -0
- package/skills/pmx-canvas/references/installing-pmx-canvas.md +6 -6
- package/skills/pmx-canvas/references/tours-and-recording.md +29 -2
- package/skills/pmx-canvas/references/workbench-workflows.md +900 -0
- package/skills/pmx-canvas-testing/SKILL.md +7 -5
- package/skills/published-consumer-e2e/SKILL.md +25 -8
- package/skills/published-consumer-e2e/scripts/run-published-consumer-e2e.sh +51 -75
- package/skills/web-artifacts-builder/LICENSE.txt +202 -0
- package/skills/web-artifacts-builder/SKILL.md +6 -0
- package/skills/web-artifacts-builder/scripts/init-artifact.sh +2 -2
- package/src/cli/commands/apps.ts +1 -0
- package/src/cli/commands/boards.ts +30 -0
- package/src/client/App.tsx +20 -4
- package/src/client/canvas/AttentionHistory.tsx +10 -2
- package/src/client/canvas/BoardMoveDialog.tsx +225 -0
- package/src/client/canvas/CanvasNode.tsx +45 -2
- package/src/client/canvas/CanvasViewport.tsx +38 -9
- package/src/client/canvas/ContextMenu.tsx +34 -12
- package/src/client/canvas/EdgeEditor.tsx +149 -0
- package/src/client/canvas/EdgeLayer.tsx +87 -16
- package/src/client/canvas/ExpandedNodeOverlay.tsx +4 -0
- package/src/client/canvas/GroupNavigation.tsx +85 -0
- package/src/client/canvas/RelationsPanel.tsx +9 -0
- package/src/client/canvas/SelectionBar.tsx +21 -0
- package/src/client/canvas/SessionPanel.tsx +35 -1
- package/src/client/canvas/TextPrompt.tsx +18 -1
- package/src/client/canvas/TopBar.tsx +30 -0
- package/src/client/canvas/canvas-area.ts +1 -1
- package/src/client/nodes/ExtAppFrame.tsx +26 -10
- package/src/client/nodes/MarkdownNode.tsx +6 -7
- package/src/client/nodes/PromptNode.tsx +32 -3
- package/src/client/nodes/StatusNode.tsx +12 -0
- package/src/client/nodes/StatusSummary.tsx +11 -0
- package/src/client/nodes/ViewerFrame.tsx +21 -10
- package/src/client/nodes/WorkCard.tsx +175 -0
- package/src/client/nodes/use-ax-surface-bridge.ts +18 -14
- package/src/client/state/attention-bridge.ts +5 -0
- package/src/client/state/attention-store.ts +1 -0
- package/src/client/state/canvas-store.ts +185 -15
- package/src/client/state/human-store.ts +22 -2
- package/src/client/state/intent-bridge.ts +19 -5
- package/src/client/state/relation-store.ts +127 -1
- package/src/client/theme/global.css +94 -1
- package/src/client/types.ts +1 -0
- package/src/mcp/server.ts +13 -3
- package/src/server/agent-presence.ts +43 -23
- package/src/server/ax-flow-loop.ts +6 -0
- package/src/server/ax-gate-ttl.ts +8 -1
- package/src/server/ax-interaction.ts +16 -10
- package/src/server/ax-state-manager.ts +17 -4
- package/src/server/ax-wait.ts +20 -6
- package/src/server/board-export.ts +19 -6
- package/src/server/board-local.ts +20 -0
- package/src/server/board-move.ts +297 -0
- package/src/server/canvas-db.ts +249 -158
- package/src/server/canvas-operations.ts +139 -109
- package/src/server/canvas-schema.ts +21 -0
- package/src/server/canvas-serialization.ts +2 -0
- package/src/server/canvas-state.ts +792 -159
- package/src/server/card-relocation.ts +53 -0
- package/src/server/code-graph.ts +4 -3
- package/src/server/context-reads.ts +83 -55
- package/src/server/demo-state.json +35 -15
- package/src/server/file-watcher.ts +30 -10
- package/src/server/human-presence.ts +18 -5
- package/src/server/index.ts +35 -15
- package/src/server/intent-registry.ts +43 -5
- package/src/server/knowledge-lifecycle.ts +32 -9
- package/src/server/lifecycle-status.ts +29 -1
- package/src/server/mcp-app-runtime.ts +26 -7
- package/src/server/mutation-history.ts +37 -8
- package/src/server/operations/composites.ts +6 -3
- package/src/server/operations/mcp-contracts.ts +15 -0
- package/src/server/operations/mcp.ts +36 -9
- package/src/server/operations/ops/annotation.ts +22 -1
- package/src/server/operations/ops/ax-await.ts +1 -0
- package/src/server/operations/ops/ax-timeline.ts +28 -2
- package/src/server/operations/ops/batch.ts +25 -11
- package/src/server/operations/ops/boards.ts +44 -3
- package/src/server/operations/ops/nodes.ts +70 -0
- package/src/server/operations/ops/query.ts +32 -3
- package/src/server/operations/ops/snapshots.ts +1 -0
- package/src/server/operations/registry.ts +49 -4
- package/src/server/server.ts +62 -16
- package/src/server/tour-control.ts +37 -12
- package/src/server/trace-manager.ts +4 -0
- package/src/server/web-artifacts/scripts/init-artifact.sh +2 -2
- package/src/shared/agent-context.ts +7 -1
- package/src/shared/auto-arrange.ts +5 -4
- package/src/shared/ax-kinds.ts +13 -0
- package/src/shared/ax-surface-protocol.ts +22 -0
- package/src/shared/human-presence.ts +1 -0
- package/src/shared/lifecycle-status.ts +2 -0
- package/dist/canvas/chunks/architecture-TIHT7OUA-9tffvpsv.js +0 -1
- package/dist/canvas/chunks/classDiagram-JCYQIIEL-nc6g5x1r.js +0 -1
- package/dist/canvas/chunks/classDiagram-v2-OCEON4UE-0qnbpwvh.js +0 -1
- package/dist/canvas/chunks/cynefin-VYW2F7L2-ys8p46ts.js +0 -1
- package/dist/canvas/chunks/eventmodeling-45OFAUF4-v9q6dn4c.js +0 -1
- package/dist/canvas/chunks/flowDiagram-UKHOOZJN-yrr5z8tc.js +0 -1
- package/dist/canvas/chunks/gitGraph-TEB2WS4Q-kdwb8m0a.js +0 -1
- package/dist/canvas/chunks/index-1r72hdzb.js +0 -301
- package/dist/canvas/chunks/index-g3pwnqeb.js +0 -2
- package/dist/canvas/chunks/index-rft2hcz5.js +0 -2
- package/dist/canvas/chunks/info-DKCQHKI2-wpzs6sz3.js +0 -1
- package/dist/canvas/chunks/ishikawaDiagram-WSZJBQD7-5hnq1a9p.js +0 -70
- package/dist/canvas/chunks/packet-7NZHBO7P-xn439f0s.js +0 -1
- package/dist/canvas/chunks/pie-RZYD4A2V-0kf16rwh.js +0 -1
- package/dist/canvas/chunks/radar-I7S5WNFK-0g2pz36t.js +0 -1
- package/dist/canvas/chunks/railroad-3IZDKUUU-fcaran6j.js +0 -1
- package/dist/canvas/chunks/railroad-abnf-AHOZXSZD-wgdya41q.js +0 -1
- package/dist/canvas/chunks/railroad-ebnf-EBAXGLYW-gy07a4ka.js +0 -1
- package/dist/canvas/chunks/railroad-peg-LSFZ7HO6-ewk6wtkh.js +0 -1
- package/dist/canvas/chunks/stateDiagram-v2-UEYNNEHI-k8sfp2e7.js +0 -1
- package/dist/canvas/chunks/swimlanesDiagram-ULZ7WXOC-nqysw67j.js +0 -8
- package/dist/canvas/chunks/treeView-QDETBFTQ-cesw6gp0.js +0 -1
- package/dist/canvas/chunks/treemap-6X3UGDF4-zdv4knp9.js +0 -1
- package/dist/canvas/chunks/vennDiagram-T6HMQDX7-x8dqn9yf.js +0 -34
- package/dist/canvas/chunks/wardley-OPB4EBWU-44pdddez.js +0 -1
|
@@ -289,7 +289,17 @@ const axTimelineGetOperation = defineOperation<z.infer<typeof axTimelineGetSchem
|
|
|
289
289
|
const axDeliveryPendingShape = {
|
|
290
290
|
consumer: z.unknown().optional().describe('Consumer/source label to exclude from results (e.g. copilot, mcp).'),
|
|
291
291
|
limit: z.unknown().optional().describe('Max steering messages to return.'),
|
|
292
|
+
offset: z.coerce
|
|
293
|
+
.number()
|
|
294
|
+
.int()
|
|
295
|
+
.nonnegative()
|
|
296
|
+
.optional()
|
|
297
|
+
.describe('Skip this many matching pending messages. Restart at zero after an empty page.'),
|
|
292
298
|
order: z.unknown().optional().describe('"oldest" (FIFO, default) or "newest" first.'),
|
|
299
|
+
addressedOnly: z
|
|
300
|
+
.boolean()
|
|
301
|
+
.optional()
|
|
302
|
+
.describe('Only messages addressed to consumer, excluding broadcasts before applying the limit.'),
|
|
293
303
|
timeoutMs: z
|
|
294
304
|
.unknown()
|
|
295
305
|
.optional()
|
|
@@ -310,10 +320,13 @@ const axDeliveryPendingOperation = defineOperation<z.infer<typeof axDeliveryPend
|
|
|
310
320
|
// consumer arrives or the timeout elapses. Custom readInput replaces the
|
|
311
321
|
// default query merge, so re-read the standard params here too.
|
|
312
322
|
readInput: (_req, params, url) => {
|
|
313
|
-
const raw = Number(url.searchParams.get('waitMs') ?? '');
|
|
323
|
+
const raw = Number(url.searchParams.get('timeoutMs') ?? url.searchParams.get('waitMs') ?? '');
|
|
314
324
|
return {
|
|
315
325
|
...Object.fromEntries(url.searchParams),
|
|
316
326
|
...params,
|
|
327
|
+
...(url.searchParams.has('addressedOnly')
|
|
328
|
+
? { addressedOnly: url.searchParams.get('addressedOnly') === 'true' }
|
|
329
|
+
: {}),
|
|
317
330
|
timeoutMs: Number.isFinite(raw) && raw > 0 ? raw : 0,
|
|
318
331
|
};
|
|
319
332
|
},
|
|
@@ -353,7 +366,13 @@ const axDeliveryPendingOperation = defineOperation<z.infer<typeof axDeliveryPend
|
|
|
353
366
|
// can't bury the human's current action behind stale undelivered rows. Both
|
|
354
367
|
// queries apply the same loop-safe consumer filter before the limit.
|
|
355
368
|
const newest = input.order === 'newest';
|
|
356
|
-
|
|
369
|
+
if (input.addressedOnly && !consumer) throw new OperationError('addressedOnly requires consumer.', 400);
|
|
370
|
+
const scope = {
|
|
371
|
+
...(consumer ? { consumer } : {}),
|
|
372
|
+
...(limit ? { limit } : {}),
|
|
373
|
+
addressedOnly: input.addressedOnly,
|
|
374
|
+
offset: input.offset,
|
|
375
|
+
};
|
|
357
376
|
const readPending = () =>
|
|
358
377
|
newest ? canvasState.getPendingSteeringForContext(scope) : canvasState.getPendingSteering(scope);
|
|
359
378
|
let pending = readPending();
|
|
@@ -495,6 +514,12 @@ const axReadsListOperation = defineOperation<z.infer<typeof axReadsListSchema>,
|
|
|
495
514
|
});
|
|
496
515
|
|
|
497
516
|
const axReadsRecordShape = {
|
|
517
|
+
deliveryId: z
|
|
518
|
+
.string()
|
|
519
|
+
.min(1)
|
|
520
|
+
.max(200)
|
|
521
|
+
.optional()
|
|
522
|
+
.describe('Idempotent adapter receipt ID, scoped to source, consumer and board.'),
|
|
498
523
|
channel: z.enum(CONTEXT_READ_CHANNELS).describe('How the read reached the agent.'),
|
|
499
524
|
resource: z.string().min(1).describe('Resource URI or operation name that was read.'),
|
|
500
525
|
consumer: z.string().nullable().optional().describe('Reader identity (MCP client name, adapter host).'),
|
|
@@ -532,6 +557,7 @@ const axReadsRecordOperation = defineOperation<z.infer<typeof axReadsRecordSchem
|
|
|
532
557
|
handler: (input) => {
|
|
533
558
|
const read = canvasState.recordContextRead(
|
|
534
559
|
{
|
|
560
|
+
deliveryId: input.deliveryId,
|
|
535
561
|
channel: input.channel,
|
|
536
562
|
resource: input.resource,
|
|
537
563
|
source: input.source ?? 'api',
|
|
@@ -26,7 +26,8 @@ import { z } from 'zod';
|
|
|
26
26
|
import { canvasState } from '../../canvas-state.js';
|
|
27
27
|
import { serializeCanvasNode, serializeCanvasNodeCompact } from '../../canvas-serialization.js';
|
|
28
28
|
import { addCanvasNode } from '../../canvas-operations.js';
|
|
29
|
-
import {
|
|
29
|
+
import { currentActor } from '../../attribution.js';
|
|
30
|
+
import { executeOperation, runWithSuppressedEmits, validateOperationTarget } from '../registry.js';
|
|
30
31
|
import { defineOperation, type Operation, type OperationContext } from '../types.js';
|
|
31
32
|
import { readJsonValue } from '../http.js';
|
|
32
33
|
import { isRecord } from './nodes.js';
|
|
@@ -57,6 +58,8 @@ const SUPPORTED_BATCH_OPS = new Set([
|
|
|
57
58
|
'group.create',
|
|
58
59
|
'group.add',
|
|
59
60
|
'group.remove',
|
|
61
|
+
'annotation.add',
|
|
62
|
+
'annotation.remove',
|
|
60
63
|
'pin.set',
|
|
61
64
|
'pin.add',
|
|
62
65
|
'pin.remove',
|
|
@@ -224,6 +227,7 @@ function shapeBatchEntry(op: string, result: unknown, creationReceipt = false):
|
|
|
224
227
|
async function runBatch(operations: BatchEntry[], ctx: OperationContext): Promise<BatchEnvelope> {
|
|
225
228
|
const refs: Record<string, unknown> = {};
|
|
226
229
|
const results: Array<Record<string, unknown>> = [];
|
|
230
|
+
const actor = currentActor('api');
|
|
227
231
|
|
|
228
232
|
for (let index = 0; index < operations.length; index++) {
|
|
229
233
|
const operation = operations[index]!;
|
|
@@ -233,6 +237,7 @@ async function runBatch(operations: BatchEntry[], ctx: OperationContext): Promis
|
|
|
233
237
|
}
|
|
234
238
|
try {
|
|
235
239
|
ctx.signal?.throwIfAborted();
|
|
240
|
+
validateOperationTarget(operation.op, args);
|
|
236
241
|
if (ctx.onProgress) {
|
|
237
242
|
const boardGeneration = canvasState.boardGeneration;
|
|
238
243
|
await ctx.onProgress({ progress: index, total: operations.length, message: operation.op });
|
|
@@ -268,14 +273,19 @@ async function runBatch(operations: BatchEntry[], ctx: OperationContext): Promis
|
|
|
268
273
|
// Batch churn is exempt from auto-ghosts (matches the skill's explicit
|
|
269
274
|
// ghost-cursor exemption for batch operations). No `fromWorkbench`: the
|
|
270
275
|
// browser never issues batches, so inner writes are agent writes and the
|
|
271
|
-
// scope fence applies to each of them.
|
|
272
|
-
const pending = executeOperation(
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
:
|
|
278
|
-
|
|
276
|
+
// scope fence applies to each of them using the original writer identity.
|
|
277
|
+
const pending = executeOperation(
|
|
278
|
+
dispatch.name,
|
|
279
|
+
{ agentId: actor.agentId, ...dispatch.args },
|
|
280
|
+
{
|
|
281
|
+
source: actor.source,
|
|
282
|
+
suppressAutoGhost: true,
|
|
283
|
+
signal: ctx.signal,
|
|
284
|
+
onProgress: ctx.onProgress
|
|
285
|
+
? (progress) => ctx.onProgress!({ progress: index, total: operations.length, message: progress.message })
|
|
286
|
+
: undefined,
|
|
287
|
+
},
|
|
288
|
+
);
|
|
279
289
|
// The first write may synchronously open a board from Home. Capture after
|
|
280
290
|
// dispatch, then refuse to continue a partially completed batch elsewhere.
|
|
281
291
|
const boardGeneration = canvasState.boardGeneration;
|
|
@@ -319,6 +329,7 @@ const batchShape = {
|
|
|
319
329
|
}),
|
|
320
330
|
)
|
|
321
331
|
.describe('Ordered array of batch operations'),
|
|
332
|
+
agentId: z.string().optional().describe('Default writer identity for entries without their own agentId'),
|
|
322
333
|
full: z
|
|
323
334
|
.boolean()
|
|
324
335
|
.optional()
|
|
@@ -404,13 +415,16 @@ const batchOperation = defineOperation<z.infer<typeof batchSchema>, BatchEnvelop
|
|
|
404
415
|
: isRecord(body) && Array.isArray(body.operations)
|
|
405
416
|
? body.operations
|
|
406
417
|
: [];
|
|
407
|
-
return {
|
|
418
|
+
return {
|
|
419
|
+
operations,
|
|
420
|
+
...(isRecord(body) ? { agentId: body.agentId, targetBoardId: body.targetBoardId } : {}),
|
|
421
|
+
};
|
|
408
422
|
},
|
|
409
423
|
},
|
|
410
424
|
mcp: {
|
|
411
425
|
toolName: 'canvas_batch',
|
|
412
426
|
description:
|
|
413
|
-
'Run a non-atomic batch of canvas operations with optional assigned references. Use assign to name a result, then reference it later as "$name" for the created node id or "$name.id" for a specific result field. On failure, earlier successful operations remain applied and the response includes ok:false, failedIndex, error, results, and refs. Supports node.add, node.update, node.remove, graph.add, edge.add, edge.remove, group.create, group.add, group.remove, pin.set/add/remove, snapshot.save, and arrange.',
|
|
427
|
+
'Run a non-atomic batch of canvas operations with optional assigned references. Use assign to name a result, then reference it later as "$name" for the created node id or "$name.id" for a specific result field. On failure, earlier successful operations remain applied and the response includes ok:false, failedIndex, error, results, and refs. Supports node.add, node.update, node.remove, graph.add, edge.add, edge.remove, group.create, group.add, group.remove, annotation.add, annotation.remove, pin.set/add/remove, snapshot.save, and arrange.',
|
|
414
428
|
formatResult: (result, input) => {
|
|
415
429
|
const envelope = result as BatchEnvelope;
|
|
416
430
|
const payload = wantsFullBatch(input) ? envelope : compactBatchResult(envelope);
|
|
@@ -17,6 +17,7 @@ import { normalizeBoardFolder } from '../../../shared/boards.js';
|
|
|
17
17
|
import { relationVocabularySchema } from '../../../shared/relations.js';
|
|
18
18
|
import { boardCatalogEpoch, canvasState, type CanvasLayout } from '../../canvas-state.js';
|
|
19
19
|
import { openCanvasBoard } from '../../canvas-operations.js';
|
|
20
|
+
import { rewatchAllFileNodes } from '../../file-watcher.js';
|
|
20
21
|
import { summarizeNodeForAgentContext } from '../../../shared/agent-context.js';
|
|
21
22
|
import { BRIEF_SUMMARY_LENGTH } from '../../context-brief.js';
|
|
22
23
|
import { memoryGraph, boardConnections } from '../../memory-graph.js';
|
|
@@ -66,8 +67,8 @@ export function boardsPayload(reloaded = false): Record<string, unknown> {
|
|
|
66
67
|
catalogEpoch: boardCatalogEpoch,
|
|
67
68
|
catalogRevision: revision,
|
|
68
69
|
reloadRevision,
|
|
69
|
-
boardGeneration: canvasState.
|
|
70
|
-
activeBoardId: canvasState.
|
|
70
|
+
boardGeneration: canvasState.visibleBoardGeneration,
|
|
71
|
+
activeBoardId: canvasState.visibleBoardId,
|
|
71
72
|
boards: boards.map((board) => {
|
|
72
73
|
const read = canvasState.readBoard(board.id, true);
|
|
73
74
|
const readme = read?.layout.nodes.find((node) => node.id === board.readmeNodeId && node.type === 'markdown');
|
|
@@ -216,7 +217,8 @@ const boardGetOperation = defineOperation<z.infer<typeof getSchema>, Record<stri
|
|
|
216
217
|
const id = typeof input.id === 'string' && input.id.trim() ? input.id.trim() : canvasState.activeBoardId;
|
|
217
218
|
const board = id ? canvasState.listBoards().find((entry) => entry.id === id) : undefined;
|
|
218
219
|
if (!board) throw new OperationError(id ? `Board "${id}" not found.` : 'No board is open.', 404);
|
|
219
|
-
|
|
220
|
+
const contentRevision = canvasState.readBoard(board.id, false)!.state.revisionState?.revision ?? 0;
|
|
221
|
+
return { ok: true, board, contentRevision, open: board.id === canvasState.activeBoardId };
|
|
220
222
|
},
|
|
221
223
|
});
|
|
222
224
|
|
|
@@ -404,6 +406,44 @@ const boardCreateFromOperation = defineOperation<z.infer<typeof fromSchema>, Rec
|
|
|
404
406
|
},
|
|
405
407
|
});
|
|
406
408
|
|
|
409
|
+
const moveShape = {
|
|
410
|
+
sourceBoardId: z.string().min(1),
|
|
411
|
+
nodeIds: z.array(z.string().min(1)).min(1),
|
|
412
|
+
name: z.string().trim().min(1).max(120),
|
|
413
|
+
expectedRevision: z.number().int().nonnegative(),
|
|
414
|
+
requestId: z.string().trim().min(1).max(200),
|
|
415
|
+
preview: z.boolean().optional(),
|
|
416
|
+
};
|
|
417
|
+
const moveSchema = z.object(moveShape);
|
|
418
|
+
const boardMoveOperation = defineOperation<z.infer<typeof moveSchema>, Record<string, unknown>>({
|
|
419
|
+
name: 'board.move-selection',
|
|
420
|
+
// Own validation precedes activation, and preview must never emit mutation/intent events.
|
|
421
|
+
mutates: false,
|
|
422
|
+
input: moveSchema,
|
|
423
|
+
inputShape: moveShape,
|
|
424
|
+
http: { method: 'POST', path: '/api/canvas/boards/move-selection' },
|
|
425
|
+
mcp: {
|
|
426
|
+
toolName: 'canvas_move_selection_to_board',
|
|
427
|
+
description:
|
|
428
|
+
'Preview or atomically move selected cards/group descendants to a new board, preserving identities and leaving a portal. Requires explicit sourceBoardId, expectedRevision and a durable requestId. Retry with identical input; never opens the destination. Preview lists unsafe bindings and the Undo/snapshot boundary.',
|
|
429
|
+
formatResult: jsonResult,
|
|
430
|
+
},
|
|
431
|
+
handler: (input, ctx) => {
|
|
432
|
+
const outcome = canvasState.moveSelectionToNewBoard(input);
|
|
433
|
+
if ('preview' in outcome) return { ok: true, ...outcome };
|
|
434
|
+
if (!outcome.replayed) {
|
|
435
|
+
rewatchAllFileNodes();
|
|
436
|
+
emitBoardsChanged(ctx);
|
|
437
|
+
ctx.emit('context-pins-changed', {
|
|
438
|
+
count: canvasState.contextPinnedNodeIds.size,
|
|
439
|
+
nodeIds: [...canvasState.contextPinnedNodeIds],
|
|
440
|
+
});
|
|
441
|
+
ctx.emit('ax-state-changed', {});
|
|
442
|
+
}
|
|
443
|
+
return { ok: true, ...outcome.result, replayed: outcome.replayed };
|
|
444
|
+
},
|
|
445
|
+
});
|
|
446
|
+
|
|
407
447
|
// ── board.pin / board.unpin ───────────────────────────────────
|
|
408
448
|
// A pinned board joins the agent's working set: its README and pinned cards
|
|
409
449
|
// travel in the brief whichever board is open. Pinning is context, never a
|
|
@@ -490,6 +530,7 @@ export const boardOperations: Operation[] = [
|
|
|
490
530
|
boardGetOperation,
|
|
491
531
|
boardCreateOperation,
|
|
492
532
|
boardCreateFromOperation,
|
|
533
|
+
boardMoveOperation,
|
|
493
534
|
boardUpdateOperation,
|
|
494
535
|
boardPinOperation,
|
|
495
536
|
boardUnpinOperation,
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
} from '../../canvas-operations.js';
|
|
33
33
|
import { resolveCreateGeometry, resolvePatchGeometry, validateCanvasNodePatch } from '../../canvas-geometry.js';
|
|
34
34
|
import { normalizeNodeAxCapabilities } from '../../ax-interaction.js';
|
|
35
|
+
import { isWorkCardState } from '../../../shared/ax-kinds.js';
|
|
35
36
|
import { applyFileContentToNodeData, readFileNodeContent } from '../../file-content.js';
|
|
36
37
|
import { unwatchFileForNode, watchFileForNode } from '../../file-watcher.js';
|
|
37
38
|
import {
|
|
@@ -327,6 +328,21 @@ export function createdNodePayloadFromNode(
|
|
|
327
328
|
|
|
328
329
|
// ── Operation cores (the SDK wraps these directly) ────────────
|
|
329
330
|
|
|
331
|
+
function validateWorkCardData(data: Record<string, unknown> | undefined): void {
|
|
332
|
+
if (!data || data.workState === undefined) return;
|
|
333
|
+
if (!isWorkCardState(data.workState))
|
|
334
|
+
throw new OperationError('workState must be todo, in-progress, blocked, done or cancelled.');
|
|
335
|
+
if (data.detail !== undefined && typeof data.detail !== 'string')
|
|
336
|
+
throw new OperationError('Work card detail must be a string.');
|
|
337
|
+
if (
|
|
338
|
+
data.progress !== undefined &&
|
|
339
|
+
data.progress !== null &&
|
|
340
|
+
(typeof data.progress !== 'number' || !Number.isFinite(data.progress) || data.progress < 0 || data.progress > 100)
|
|
341
|
+
) {
|
|
342
|
+
throw new OperationError('Work card progress must be a number from 0 to 100, or null to clear it.');
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
330
346
|
/**
|
|
331
347
|
* Create a basic (non-webpage / non-group / non-primitive) node. Union of the
|
|
332
348
|
* legacy handleCanvasAddNode generic branch; the SDK passes fileMode 'path',
|
|
@@ -338,6 +354,34 @@ export function createBasicCanvasNode(
|
|
|
338
354
|
): { node: CanvasNodeState; needsCodeGraphRecompute: boolean; sizeAdjustment?: CanvasSizeAdjustment } {
|
|
339
355
|
const type = typeof body.type === 'string' ? body.type : '';
|
|
340
356
|
const extraData = isRecord(body.data) ? body.data : undefined;
|
|
357
|
+
if (type === 'status' && extraData?.workItemId !== undefined) {
|
|
358
|
+
if (body.boardId !== undefined || options.prepareOnlyNodes)
|
|
359
|
+
throw new OperationError('Live work cards must be created on the active board.');
|
|
360
|
+
const owner = extraData.workItemBoardId ?? canvasState.activeBoardId;
|
|
361
|
+
if (
|
|
362
|
+
typeof extraData.workItemId !== 'string' ||
|
|
363
|
+
(owner !== null && typeof owner !== 'string') ||
|
|
364
|
+
!(owner === null ? canvasState.getWorkItems() : canvasState.getWorkItemsOnBoard(owner)).some(
|
|
365
|
+
(item) => item.id === extraData.workItemId,
|
|
366
|
+
)
|
|
367
|
+
)
|
|
368
|
+
throw new OperationError('Live work card requires an existing work item on its owning board.');
|
|
369
|
+
if (
|
|
370
|
+
['title', 'content'].some((key) => body[key] !== undefined) ||
|
|
371
|
+
[
|
|
372
|
+
'title',
|
|
373
|
+
'content',
|
|
374
|
+
'workState',
|
|
375
|
+
'detail',
|
|
376
|
+
'progress',
|
|
377
|
+
'axWorkStatus',
|
|
378
|
+
'liveWorkAvailable',
|
|
379
|
+
'liveWorkSource',
|
|
380
|
+
].some((key) => key in extraData)
|
|
381
|
+
)
|
|
382
|
+
throw new OperationError('Live work card fields are read-only; update the work item instead.');
|
|
383
|
+
}
|
|
384
|
+
if (type === 'status') validateWorkCardData(extraData);
|
|
341
385
|
if (
|
|
342
386
|
extraData &&
|
|
343
387
|
('attachmentId' in extraData || (isRecord(extraData.source) && 'attachmentId' in extraData.source))
|
|
@@ -415,6 +459,9 @@ export function createBasicCanvasNode(
|
|
|
415
459
|
: {}),
|
|
416
460
|
...(typeof content === 'string' ? { content } : {}),
|
|
417
461
|
...(htmlMergedData && Object.keys(htmlMergedData).length > 0 ? { data: htmlMergedData } : {}),
|
|
462
|
+
...(type === 'status' && typeof extraData?.workItemId === 'string'
|
|
463
|
+
? { data: { ...extraData, workItemBoardId: extraData.workItemBoardId ?? canvasState.activeBoardId } }
|
|
464
|
+
: {}),
|
|
418
465
|
...(type === 'trace' && typeof body.toolName === 'string' ? { toolName: body.toolName } : {}),
|
|
419
466
|
...(type === 'trace' && typeof body.category === 'string' ? { category: body.category } : {}),
|
|
420
467
|
...(type === 'trace' && typeof body.status === 'string' ? { status: body.status } : {}),
|
|
@@ -446,6 +493,28 @@ export function buildNodePatch(
|
|
|
446
493
|
body: Record<string, unknown>,
|
|
447
494
|
): { patch: Partial<CanvasNodeState>; groupChildIds?: string[] } {
|
|
448
495
|
if (body.type !== undefined && body.type !== existing.type) throw new OperationError('Node type is immutable.');
|
|
496
|
+
if (existing.type === 'status') {
|
|
497
|
+
const data = isRecord(body.data) ? body.data : {};
|
|
498
|
+
if (
|
|
499
|
+
'workItemId' in data ||
|
|
500
|
+
'workItemBoardId' in data ||
|
|
501
|
+
(typeof existing.data.workItemId === 'string' &&
|
|
502
|
+
(body.title !== undefined ||
|
|
503
|
+
body.content !== undefined ||
|
|
504
|
+
[
|
|
505
|
+
'title',
|
|
506
|
+
'content',
|
|
507
|
+
'workState',
|
|
508
|
+
'detail',
|
|
509
|
+
'progress',
|
|
510
|
+
'axWorkStatus',
|
|
511
|
+
'liveWorkAvailable',
|
|
512
|
+
'liveWorkSource',
|
|
513
|
+
].some((key) => key in data)))
|
|
514
|
+
) {
|
|
515
|
+
throw new OperationError('Live work cards are read-only; create a presentation or update the work item instead.');
|
|
516
|
+
}
|
|
517
|
+
}
|
|
449
518
|
if (
|
|
450
519
|
existing.type === 'file' &&
|
|
451
520
|
typeof existing.data.attachmentId === 'string' &&
|
|
@@ -561,6 +630,7 @@ export function buildNodePatch(
|
|
|
561
630
|
}
|
|
562
631
|
patch.data = existing.type === 'trace' ? mergeTraceNodeDataFields(data, body) : data;
|
|
563
632
|
}
|
|
633
|
+
if (existing.type === 'status' && isRecord(patch.data)) validateWorkCardData(patch.data);
|
|
564
634
|
const error = validateCanvasNodePatch({
|
|
565
635
|
...(patch.position ? { position: patch.position as { x: number; y: number } } : {}),
|
|
566
636
|
...(patch.size ? { size: patch.size as { width: number; height: number } } : {}),
|
|
@@ -284,6 +284,11 @@ const contextShape = {
|
|
|
284
284
|
consumer: z.unknown().optional().describe('Stable consumer id used for the durable board-scoped cursor'),
|
|
285
285
|
since: z.unknown().optional().describe('Explicit content revision; bypasses the durable consumer cursor'),
|
|
286
286
|
budget: z.unknown().optional().describe('Maximum response size in UTF-16 code units'),
|
|
287
|
+
contextDelivery: z
|
|
288
|
+
.boolean()
|
|
289
|
+
.optional()
|
|
290
|
+
.describe('False prepares a brief without recording delivery or advancing a cursor.'),
|
|
291
|
+
includeSteering: z.boolean().optional().describe('False omits steering for hosts that deliver it separately.'),
|
|
287
292
|
};
|
|
288
293
|
const contextSchema = z.looseObject(contextShape);
|
|
289
294
|
|
|
@@ -292,8 +297,20 @@ const contextOperation = defineOperation<z.infer<typeof contextSchema>, Record<s
|
|
|
292
297
|
mutates: false,
|
|
293
298
|
input: contextSchema,
|
|
294
299
|
inputShape: contextShape,
|
|
295
|
-
http: {
|
|
300
|
+
http: {
|
|
301
|
+
method: 'GET',
|
|
302
|
+
path: '/api/canvas/context',
|
|
303
|
+
readInput: (_req, params, url) => {
|
|
304
|
+
const input: Record<string, unknown> = { ...Object.fromEntries(url.searchParams), ...params };
|
|
305
|
+
for (const key of ['contextDelivery', 'includeSteering']) {
|
|
306
|
+
if (input[key] === 'false') input[key] = false;
|
|
307
|
+
else if (input[key] === 'true') input[key] = true;
|
|
308
|
+
}
|
|
309
|
+
return input;
|
|
310
|
+
},
|
|
311
|
+
},
|
|
296
312
|
handler: (input) => {
|
|
313
|
+
canvasState.refreshContextWorkCards();
|
|
297
314
|
const active = canvasState.getActiveBoard();
|
|
298
315
|
if (!active) throw new OperationError('Open a board before requesting its context.', 409);
|
|
299
316
|
const consumer =
|
|
@@ -507,7 +524,10 @@ const contextOperation = defineOperation<z.infer<typeof contextSchema>, Record<s
|
|
|
507
524
|
})),
|
|
508
525
|
);
|
|
509
526
|
entries.push(
|
|
510
|
-
...
|
|
527
|
+
...(input.includeSteering === false
|
|
528
|
+
? []
|
|
529
|
+
: canvasState.getPendingSteering({ consumer: consumer ?? undefined, limit: 50 })
|
|
530
|
+
).map((item) => ({
|
|
511
531
|
sourceBoardId: active.id,
|
|
512
532
|
nodeId: `steering:${item.id}`,
|
|
513
533
|
reason: 'steer' as const,
|
|
@@ -553,7 +573,14 @@ const contextOperation = defineOperation<z.infer<typeof contextSchema>, Record<s
|
|
|
553
573
|
entries,
|
|
554
574
|
budget,
|
|
555
575
|
});
|
|
556
|
-
if (
|
|
576
|
+
if (
|
|
577
|
+
input.contextDelivery !== false &&
|
|
578
|
+
consumer &&
|
|
579
|
+
!explicitSince &&
|
|
580
|
+
result.document &&
|
|
581
|
+
result.nextCursor !== null &&
|
|
582
|
+
!result.invalidCursor
|
|
583
|
+
) {
|
|
557
584
|
canvasState.advanceContextBriefCursor(active.id, consumer, result.nextCursor);
|
|
558
585
|
}
|
|
559
586
|
return (result.document ?? {
|
|
@@ -634,6 +661,7 @@ const undoOperation = defineOperation<z.infer<typeof undoRedoSchema>, Record<str
|
|
|
634
661
|
count: canvasState.contextPinnedNodeIds.size,
|
|
635
662
|
nodeIds: Array.from(canvasState.contextPinnedNodeIds),
|
|
636
663
|
});
|
|
664
|
+
ctx.emit('ax-state-changed', {});
|
|
637
665
|
return { ok: true, description: `Undid: ${entry.description}` };
|
|
638
666
|
},
|
|
639
667
|
});
|
|
@@ -662,6 +690,7 @@ const redoOperation = defineOperation<z.infer<typeof undoRedoSchema>, Record<str
|
|
|
662
690
|
count: canvasState.contextPinnedNodeIds.size,
|
|
663
691
|
nodeIds: Array.from(canvasState.contextPinnedNodeIds),
|
|
664
692
|
});
|
|
693
|
+
ctx.emit('ax-state-changed', {});
|
|
665
694
|
return { ok: true, description: `Redid: ${entry.description}` };
|
|
666
695
|
},
|
|
667
696
|
});
|
|
@@ -364,6 +364,7 @@ const snapshotRestoreNodeOperation = defineOperation<
|
|
|
364
364
|
handler: ({ id, nodeId, preserveHumanEdits }) => {
|
|
365
365
|
const snapshot = canvasState.getSnapshotData(id ?? '');
|
|
366
366
|
if (!snapshot) throw new OperationError('Snapshot not found', 404);
|
|
367
|
+
canvasState.assertSnapshotRestorable(id ?? '');
|
|
367
368
|
const current = canvasState.getNode(nodeId ?? '');
|
|
368
369
|
const earlier = snapshot.nodes.find((node) => node.id === nodeId);
|
|
369
370
|
if (!current || !earlier)
|
|
@@ -359,6 +359,7 @@ function notePresence(
|
|
|
359
359
|
): void {
|
|
360
360
|
if (!isPresenceWrite(op) || meta.suppressAutoGhost) return;
|
|
361
361
|
const input = asRecord(rawInput);
|
|
362
|
+
if (name === 'board.move-selection' && (input.preview === true || asRecord(result).replayed === true)) return;
|
|
362
363
|
// Delivery plumbing (claim/mark) must not book an anonymous transport
|
|
363
364
|
// writer nor spam the timeline: attribute it to the consumer that marked,
|
|
364
365
|
// and record no activity row — the Steer row flipping to delivered IS the
|
|
@@ -388,11 +389,52 @@ function notePresence(
|
|
|
388
389
|
}
|
|
389
390
|
}
|
|
390
391
|
|
|
392
|
+
const operationTarget = new AsyncLocalStorage<{ boardId: string; agentId?: string }>();
|
|
393
|
+
|
|
394
|
+
/** Also used by batch's internal node path, which does not dispatch an operation. */
|
|
395
|
+
export function validateOperationTarget(name: string, input: Record<string, unknown>): string | undefined {
|
|
396
|
+
const inherited = operationTarget.getStore();
|
|
397
|
+
const target = input.targetBoardId === undefined ? inherited?.boardId : input.targetBoardId;
|
|
398
|
+
if (target === undefined) return undefined;
|
|
399
|
+
if (typeof target !== 'string' || !target.trim())
|
|
400
|
+
throw new OperationError('targetBoardId must be an existing board ID.');
|
|
401
|
+
if (
|
|
402
|
+
inherited &&
|
|
403
|
+
(target !== inherited.boardId || (input.agentId !== undefined && input.agentId !== inherited.agentId))
|
|
404
|
+
)
|
|
405
|
+
throw new OperationError('Nested operations must retain their board and agent identity.', 409);
|
|
406
|
+
if (name === 'board.update' && input.id !== target)
|
|
407
|
+
throw new OperationError('Board id conflicts with targetBoardId. Rebind before editing another board.', 409);
|
|
408
|
+
for (const key of [
|
|
409
|
+
'board',
|
|
410
|
+
'expectedBoardId',
|
|
411
|
+
'destinationBoardId',
|
|
412
|
+
...(name === 'board.move-selection' ? ['sourceBoardId'] : []),
|
|
413
|
+
]) {
|
|
414
|
+
if (input[key] !== undefined && input[key] !== target)
|
|
415
|
+
throw new OperationError(`${key} conflicts with targetBoardId. Rebind before targeting another board.`, 409);
|
|
416
|
+
}
|
|
417
|
+
if (['board.open', 'board.delete', 'backup.restore'].includes(name) || name.startsWith('webview.'))
|
|
418
|
+
throw new OperationError('This operation controls the shared view or workspace, not a scoped board.', 409);
|
|
419
|
+
return target;
|
|
420
|
+
}
|
|
421
|
+
|
|
391
422
|
export async function executeOperation(
|
|
392
423
|
name: string,
|
|
393
424
|
rawInput: unknown,
|
|
394
425
|
meta: ExecuteOperationMeta = {},
|
|
395
426
|
): Promise<unknown> {
|
|
427
|
+
const input = asRecord(rawInput);
|
|
428
|
+
const target = validateOperationTarget(name, input);
|
|
429
|
+
if (target === undefined) return executeOnCurrentBoard(name, rawInput, meta);
|
|
430
|
+
const agentId =
|
|
431
|
+
operationTarget.getStore()?.agentId ?? (typeof input.agentId === 'string' ? input.agentId : undefined);
|
|
432
|
+
return operationTarget.run({ boardId: target, agentId }, () =>
|
|
433
|
+
canvasState.withBoard(target, () => executeOnCurrentBoard(name, { ...input, agentId }, meta)),
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
async function executeOnCurrentBoard(name: string, rawInput: unknown, meta: ExecuteOperationMeta): Promise<unknown> {
|
|
396
438
|
// Check before attribution, board auto-open, ghosts, or any handler writes.
|
|
397
439
|
meta.signal?.throwIfAborted();
|
|
398
440
|
// Attribution follows this call chain across awaits and nested operations.
|
|
@@ -406,7 +448,8 @@ export async function executeOperation(
|
|
|
406
448
|
async () => {
|
|
407
449
|
const input = asRecord(rawInput);
|
|
408
450
|
const deliversContext =
|
|
409
|
-
|
|
451
|
+
!['node.get', 'context.get'].includes(name) ||
|
|
452
|
+
(input.contextDelivery !== false && input.contextDelivery !== 'false');
|
|
410
453
|
const recordRead = CONTEXT_READ_OPS.has(name) && deliversContext && !meta.fromWorkbench && !meta.proxiedRead;
|
|
411
454
|
const requestedBoard =
|
|
412
455
|
recordRead && 'board' in getOperation(name).inputShape && isString(input.board) && input.board.trim()
|
|
@@ -510,7 +553,8 @@ async function executeOperationInner(name: string, rawInput: unknown, meta: Exec
|
|
|
510
553
|
const rawExpectedBoard = asRecord(rawInput).expectedBoardId;
|
|
511
554
|
// HTTP DELETE query values are strings, including Home's JSON null.
|
|
512
555
|
const expectedBoard = rawExpectedBoard === 'null' ? null : rawExpectedBoard;
|
|
513
|
-
|
|
556
|
+
// Relocations check their durable receipt first, then their explicit source board.
|
|
557
|
+
if (name !== 'board.move-selection' && expectedBoard !== undefined && expectedBoard !== canvasState.activeBoardId) {
|
|
514
558
|
throw new OperationError('The active board changed. Refresh before retrying this action.', 409);
|
|
515
559
|
}
|
|
516
560
|
// File drops carry their captured board through the HTTP round trip. Check
|
|
@@ -638,8 +682,9 @@ async function executeOperationInner(name: string, rawInput: unknown, meta: Exec
|
|
|
638
682
|
}
|
|
639
683
|
|
|
640
684
|
const beforeBoard = canvasState.activeBoardId;
|
|
641
|
-
const beforeRevision =
|
|
642
|
-
|
|
685
|
+
const beforeRevision = ['board.create-from', 'board.delete', 'board.move-selection'].includes(name)
|
|
686
|
+
? canvasState.getContentRevision().revision
|
|
687
|
+
: null;
|
|
643
688
|
meta.signal?.throwIfAborted();
|
|
644
689
|
const result = await op.execute(rawInput, operationContext);
|
|
645
690
|
// Library writes can invalidate relations on the open board without being
|