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.
- package/.github/extensions/pmx-canvas/extension.mjs +4 -1
- package/.github/extensions/pmx-canvas/runtime.json +1 -1
- package/.github/extensions/pmx-canvas/tools.json +440 -18
- package/CHANGELOG.md +51 -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 +267 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/evals.json +1168 -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 +1839 -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 +301 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/tours-and-recording.md +117 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/workbench-workflows.md +908 -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 +22 -2
- package/docs/amp-plugin.md +270 -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 +4 -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 +248 -842
- package/skills/pmx-canvas/evals/evals.json +110 -5
- package/skills/pmx-canvas/references/full-reference.md +142 -28
- package/skills/pmx-canvas/references/html-primitives.md +7 -0
- package/skills/pmx-canvas/references/installing-pmx-canvas.md +61 -11
- package/skills/pmx-canvas/references/tours-and-recording.md +39 -6
- package/skills/pmx-canvas/references/workbench-workflows.md +908 -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/bundle-artifact.sh +2 -2
- package/skills/web-artifacts-builder/scripts/init-artifact.sh +8 -8
- 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 +262 -160
- 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/bundle-artifact.sh +2 -2
- package/src/server/web-artifacts/scripts/init-artifact.sh +8 -8
- 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
package/src/server/index.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
|
+
import type { BoardMoveInput, BoardMovePreview, BoardMoveResult } from './board-move.js';
|
|
3
|
+
export type { BoardMoveInput, BoardMovePreview, BoardMoveResult } from './board-move.js';
|
|
2
4
|
import type { Camera, Tour, TourPresenter, TourStop } from '../shared/tour.js';
|
|
3
5
|
import {
|
|
4
6
|
currentTour,
|
|
@@ -508,21 +510,19 @@ export class PmxCanvas extends EventEmitter {
|
|
|
508
510
|
return (await executeOperation('edge.update', { id, ...patch }, { source: 'sdk' })) as CanvasEdge;
|
|
509
511
|
}
|
|
510
512
|
|
|
511
|
-
addAnnotation(
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
return id;
|
|
513
|
+
async addAnnotation(
|
|
514
|
+
input: Pick<CanvasAnnotation, 'points'> &
|
|
515
|
+
Partial<Pick<CanvasAnnotation, 'id' | 'type' | 'color' | 'width' | 'text' | 'label'>>,
|
|
516
|
+
): Promise<string> {
|
|
517
|
+
const result = (await executeOperation('annotation.add', input, { source: 'sdk' })) as {
|
|
518
|
+
annotation: { id: string };
|
|
519
|
+
};
|
|
520
|
+
return result.annotation.id;
|
|
520
521
|
}
|
|
521
522
|
|
|
522
|
-
removeAnnotation(id: string): boolean {
|
|
523
|
-
const
|
|
524
|
-
|
|
525
|
-
return removed;
|
|
523
|
+
async removeAnnotation(id: string): Promise<boolean> {
|
|
524
|
+
const result = (await executeOperation('annotation.remove', { id }, { source: 'sdk' })) as { ok: boolean };
|
|
525
|
+
return result.ok;
|
|
526
526
|
}
|
|
527
527
|
|
|
528
528
|
removeEdge(id: string): void {
|
|
@@ -764,7 +764,12 @@ export class PmxCanvas extends EventEmitter {
|
|
|
764
764
|
}
|
|
765
765
|
|
|
766
766
|
/** Undelivered steering for a consumer (loop-safe; excludes consumer-originated). */
|
|
767
|
-
getPendingSteering(options?: {
|
|
767
|
+
getPendingSteering(options?: {
|
|
768
|
+
consumer?: string;
|
|
769
|
+
limit?: number;
|
|
770
|
+
addressedOnly?: boolean;
|
|
771
|
+
offset?: number;
|
|
772
|
+
}): PmxAxSteeringMessage[] {
|
|
768
773
|
return canvasState.getPendingSteering(options ?? {});
|
|
769
774
|
}
|
|
770
775
|
|
|
@@ -954,6 +959,15 @@ export class PmxCanvas extends EventEmitter {
|
|
|
954
959
|
return (await executeOperation('board.create-from', input, { source: 'sdk' })) as Record<string, unknown>;
|
|
955
960
|
}
|
|
956
961
|
|
|
962
|
+
/** Relocate identities atomically; preview is read-only and retries reuse the original receipt. */
|
|
963
|
+
async moveSelectionToNewBoard(
|
|
964
|
+
input: BoardMoveInput,
|
|
965
|
+
): Promise<{ ok: true; preview: BoardMovePreview } | (BoardMoveResult & { ok: true; replayed: boolean })> {
|
|
966
|
+
return (await executeOperation('board.move-selection', input, { source: 'sdk' })) as
|
|
967
|
+
| { ok: true; preview: BoardMovePreview }
|
|
968
|
+
| (BoardMoveResult & { ok: true; replayed: boolean });
|
|
969
|
+
}
|
|
970
|
+
|
|
957
971
|
async searchLibrary(query: string, limit?: number): Promise<Record<string, unknown>> {
|
|
958
972
|
return (await executeOperation('search', { q: query, scope: 'library', limit }, { source: 'sdk' })) as Record<
|
|
959
973
|
string,
|
|
@@ -970,7 +984,13 @@ export class PmxCanvas extends EventEmitter {
|
|
|
970
984
|
|
|
971
985
|
/** Budgeted cross-board brief. Budget is measured in UTF-16 characters. */
|
|
972
986
|
async getContextBrief(
|
|
973
|
-
options: {
|
|
987
|
+
options: {
|
|
988
|
+
consumer?: string;
|
|
989
|
+
since?: number;
|
|
990
|
+
budget?: number;
|
|
991
|
+
contextDelivery?: boolean;
|
|
992
|
+
includeSteering?: boolean;
|
|
993
|
+
} = {},
|
|
974
994
|
): Promise<Record<string, unknown>> {
|
|
975
995
|
return (await executeOperation('context.get', options, { source: 'sdk' })) as Record<string, unknown>;
|
|
976
996
|
}
|
|
@@ -25,6 +25,8 @@ import {
|
|
|
25
25
|
type PmxAxIntentKind,
|
|
26
26
|
} from '../shared/ax-intent.js';
|
|
27
27
|
import { OperationError } from './operations/types.js';
|
|
28
|
+
import { createBoardLocal } from './board-local.js';
|
|
29
|
+
import { canvasState } from './canvas-state.js';
|
|
28
30
|
|
|
29
31
|
type IntentEmitter = (event: string, payload: Record<string, unknown>) => void;
|
|
30
32
|
const MAX_VETO_TOMBSTONES = 128;
|
|
@@ -103,11 +105,33 @@ function nextIntentId(): string {
|
|
|
103
105
|
}
|
|
104
106
|
|
|
105
107
|
export class IntentRegistry {
|
|
106
|
-
private readonly
|
|
107
|
-
|
|
108
|
-
|
|
108
|
+
private readonly board = createBoardLocal(
|
|
109
|
+
() => ({
|
|
110
|
+
intents: new Map<string, PmxAxIntent>(),
|
|
111
|
+
vetoedIntentIds: new Map<string, number>(),
|
|
112
|
+
committingIntentIds: new Set<string>(),
|
|
113
|
+
sweepTimer: null as ReturnType<typeof setInterval> | null,
|
|
114
|
+
}),
|
|
115
|
+
(state) => {
|
|
116
|
+
if (state.sweepTimer) clearInterval(state.sweepTimer);
|
|
117
|
+
},
|
|
118
|
+
);
|
|
119
|
+
private get intents() {
|
|
120
|
+
return this.board.current.intents;
|
|
121
|
+
}
|
|
122
|
+
private get vetoedIntentIds() {
|
|
123
|
+
return this.board.current.vetoedIntentIds;
|
|
124
|
+
}
|
|
125
|
+
private get committingIntentIds() {
|
|
126
|
+
return this.board.current.committingIntentIds;
|
|
127
|
+
}
|
|
109
128
|
private emit: IntentEmitter = () => {};
|
|
110
|
-
private sweepTimer
|
|
129
|
+
private get sweepTimer() {
|
|
130
|
+
return this.board.current.sweepTimer;
|
|
131
|
+
}
|
|
132
|
+
private set sweepTimer(value: ReturnType<typeof setInterval> | null) {
|
|
133
|
+
this.board.current.sweepTimer = value;
|
|
134
|
+
}
|
|
111
135
|
|
|
112
136
|
/** Inject the workbench SSE emitter (server.ts wires this at module load). */
|
|
113
137
|
setEmitter(emitter: IntentEmitter | null): void {
|
|
@@ -118,6 +142,17 @@ export class IntentRegistry {
|
|
|
118
142
|
return [...this.intents.values()];
|
|
119
143
|
}
|
|
120
144
|
|
|
145
|
+
/** The first write from Home adopts its ghosts and vetoes without opening on signal. */
|
|
146
|
+
moveHomeIntentsToBoard(boardId: string): void {
|
|
147
|
+
const home = this.board.current;
|
|
148
|
+
canvasState.withBoard(boardId, () => {
|
|
149
|
+
for (const [id, intent] of home.intents) this.intents.set(id, intent);
|
|
150
|
+
for (const [id, expires] of home.vetoedIntentIds) this.vetoedIntentIds.set(id, expires);
|
|
151
|
+
this.ensureSweeper();
|
|
152
|
+
});
|
|
153
|
+
this.reset();
|
|
154
|
+
}
|
|
155
|
+
|
|
121
156
|
/** Signal a new (or replace an existing) intent. Returns the stored envelope. */
|
|
122
157
|
signal(raw: unknown): PmxAxIntent {
|
|
123
158
|
const input = parseOrThrow(intentSignalSchema, raw, 'intent');
|
|
@@ -335,7 +370,10 @@ export class IntentRegistry {
|
|
|
335
370
|
|
|
336
371
|
private ensureSweeper(): void {
|
|
337
372
|
if (this.sweepTimer || this.intents.size === 0) return;
|
|
338
|
-
this.sweepTimer = setInterval(
|
|
373
|
+
this.sweepTimer = setInterval(
|
|
374
|
+
canvasState.bindBoard(() => this.sweep()),
|
|
375
|
+
1000,
|
|
376
|
+
);
|
|
339
377
|
// Don't keep the process (or a test runner) alive just for ghost expiry.
|
|
340
378
|
(this.sweepTimer as { unref?: () => void }).unref?.();
|
|
341
379
|
}
|
|
@@ -4,6 +4,7 @@ import { normalizeBoardFolder } from '../shared/boards.js';
|
|
|
4
4
|
import { memoryKey, type MemoryTarget } from '../shared/memory-graph.js';
|
|
5
5
|
import { currentActor, type ActorAttribution } from './attribution.js';
|
|
6
6
|
import { OperationError } from './operations/types.js';
|
|
7
|
+
import { CARD_HISTORY_CTE } from './card-relocation.js';
|
|
7
8
|
import type { LifecycleQualification } from '../shared/lifecycle-status.js';
|
|
8
9
|
import type { CanvasNodeState } from './canvas-state.js';
|
|
9
10
|
import { RELATION_PRESETS, relationPresetSchema, type RelationPreset } from '../shared/relations.js';
|
|
@@ -281,15 +282,19 @@ export class KnowledgeLifecycle {
|
|
|
281
282
|
if (!targets.length) return new Map();
|
|
282
283
|
const rows = this.indexedDb()
|
|
283
284
|
.query<{ boardId: string; nodeId: string; validation: string | null; retirement: string | null }, [string]>(
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
FROM
|
|
287
|
-
|
|
288
|
-
|
|
285
|
+
`${CARD_HISTORY_CTE}
|
|
286
|
+
SELECT t.current_board AS boardId, t.node_id AS nodeId,
|
|
287
|
+
(SELECT value FROM review_records r JOIN history h
|
|
288
|
+
ON r.board_id = h.board_id AND r.node_id = h.node_id
|
|
289
|
+
AND r.sequence > h.review_after AND r.sequence <= h.review_through
|
|
290
|
+
WHERE h.current_board = t.current_board AND h.node_id = t.node_id
|
|
289
291
|
AND ${validationPredicate} ORDER BY sequence DESC LIMIT 1) AS validation,
|
|
290
|
-
(SELECT json_extract(value, '$.outcome') FROM review_records
|
|
292
|
+
(SELECT json_extract(value, '$.outcome') FROM review_records r JOIN history h
|
|
293
|
+
ON r.board_id = h.board_id AND r.node_id = h.node_id
|
|
294
|
+
AND r.sequence > h.review_after AND r.sequence <= h.review_through
|
|
295
|
+
WHERE h.current_board = t.current_board AND h.node_id = t.node_id
|
|
291
296
|
AND ${retirementPredicate} ORDER BY sequence DESC LIMIT 1) AS retirement
|
|
292
|
-
FROM targets`,
|
|
297
|
+
FROM targets t`,
|
|
293
298
|
)
|
|
294
299
|
.all(JSON.stringify(targets));
|
|
295
300
|
return new Map(
|
|
@@ -306,11 +311,29 @@ export class KnowledgeLifecycle {
|
|
|
306
311
|
);
|
|
307
312
|
}
|
|
308
313
|
|
|
314
|
+
/** Resolve a current card's location when evidence was recorded, without rewriting that evidence. */
|
|
315
|
+
locationAtReview(review: ReviewRecord, target: MemoryTarget): MemoryTarget {
|
|
316
|
+
const sequence = this.db()
|
|
317
|
+
.query<{ sequence: number }, [string, string, string]>(
|
|
318
|
+
"SELECT sequence FROM review_records WHERE board_id = ? AND node_id = ? AND json_extract(value, '$.id') = ?",
|
|
319
|
+
)
|
|
320
|
+
.get(review.boardId, review.nodeId, review.id)?.sequence;
|
|
321
|
+
if (sequence === undefined) return target;
|
|
322
|
+
const location = this.db()
|
|
323
|
+
.query<{ board_id: string }, [string, number, number]>(
|
|
324
|
+
`${CARD_HISTORY_CTE} SELECT board_id FROM history WHERE review_after < ? AND review_through >= ? LIMIT 1`,
|
|
325
|
+
)
|
|
326
|
+
.get(JSON.stringify([target]), sequence, sequence);
|
|
327
|
+
return location ? { ...target, boardId: location.board_id } : target;
|
|
328
|
+
}
|
|
329
|
+
|
|
309
330
|
reviews(boardId: string, nodeId: string) {
|
|
310
331
|
const records = (
|
|
311
332
|
this.db()
|
|
312
|
-
.query(
|
|
313
|
-
|
|
333
|
+
.query(`${CARD_HISTORY_CTE} SELECT value FROM review_records r JOIN history h
|
|
334
|
+
ON r.board_id = h.board_id AND r.node_id = h.node_id
|
|
335
|
+
AND r.sequence > h.review_after AND r.sequence <= h.review_through ORDER BY sequence`)
|
|
336
|
+
.all(JSON.stringify([{ boardId, cardId: nodeId }])) as { value: string }[]
|
|
314
337
|
).map((r): ReviewRecord => {
|
|
315
338
|
const record = JSON.parse(r.value) as ReviewRecord;
|
|
316
339
|
return { ...record, contentRevision: record.contentRevision ?? null };
|
|
@@ -11,6 +11,7 @@ const processStartedAt = Date.now();
|
|
|
11
11
|
interface Source {
|
|
12
12
|
key: string;
|
|
13
13
|
target: MemoryTarget;
|
|
14
|
+
edge?: { owner: MemoryTarget; id: string };
|
|
14
15
|
baseline?: number;
|
|
15
16
|
kind: string;
|
|
16
17
|
observedAt?: string;
|
|
@@ -179,6 +180,7 @@ export function lifecycleSnapshot(now = Date.now(), atUse: MemoryTarget[] = [],
|
|
|
179
180
|
existing.push({
|
|
180
181
|
key: JSON.stringify([board.id, edge.id, kind, memoryKey(target)]),
|
|
181
182
|
target,
|
|
183
|
+
edge: { owner: from, id: edge.id },
|
|
182
184
|
kind,
|
|
183
185
|
baseline: edge.sourceRevision,
|
|
184
186
|
});
|
|
@@ -219,13 +221,30 @@ export function lifecycleSnapshot(now = Date.now(), atUse: MemoryTarget[] = [],
|
|
|
219
221
|
const current = cards.get(memoryKey(source.target));
|
|
220
222
|
const revision = source.availability === 'unavailable' ? null : (current?.contentRevision ?? null);
|
|
221
223
|
status.observations[source.key] = revision;
|
|
222
|
-
|
|
224
|
+
let observed = validation ? validation.sourceRevisions?.[source.key] : source.baseline;
|
|
225
|
+
if (validation?.sourceRevisions && observed === undefined) {
|
|
226
|
+
const target = canvasState.knowledge.locationAtReview(validation, source.target);
|
|
227
|
+
const historicalKey = source.edge
|
|
228
|
+
? JSON.stringify([
|
|
229
|
+
canvasState.knowledge.locationAtReview(validation, source.edge.owner).boardId,
|
|
230
|
+
source.edge.id,
|
|
231
|
+
source.kind,
|
|
232
|
+
memoryKey(target),
|
|
233
|
+
])
|
|
234
|
+
: `provenance:${memoryKey(target)}`;
|
|
235
|
+
observed = validation.sourceRevisions[historicalKey];
|
|
236
|
+
}
|
|
223
237
|
const title = current ? String(current.data.title ?? current.id) : 'Unavailable source';
|
|
238
|
+
const before = status.reasons.length;
|
|
224
239
|
if (!current) status.reasons.push('A dependency source is unavailable; no fresh check.');
|
|
225
240
|
else if (source.availability === 'unavailable')
|
|
226
241
|
status.reasons.push(`${title}: source unavailable; cached content is not a fresh check.`);
|
|
227
242
|
else if (observed !== revision || (!validation && ['contradicts', 'supersedes'].includes(source.kind)))
|
|
228
243
|
status.reasons.push(`${source.kind}: ${title}${observed === undefined ? ' needs review' : ' changed'}.`);
|
|
244
|
+
if (status.reasons.length > before) {
|
|
245
|
+
status.sourceWarnings ??= {};
|
|
246
|
+
status.sourceWarnings[source.key] = status.reasons.slice(before);
|
|
247
|
+
}
|
|
229
248
|
}
|
|
230
249
|
}
|
|
231
250
|
// Fixed point, not recursive traversal: cycles cannot overflow and unrelated links never propagate.
|
|
@@ -246,6 +265,15 @@ export function lifecycleSnapshot(now = Date.now(), atUse: MemoryTarget[] = [],
|
|
|
246
265
|
}
|
|
247
266
|
}
|
|
248
267
|
for (const [key, status] of Object.entries(result)) {
|
|
268
|
+
for (const source of sources.get(key) ?? []) {
|
|
269
|
+
const upstream = result[memoryKey(source.target)];
|
|
270
|
+
if (upstream?.due || upstream?.retired) {
|
|
271
|
+
status.sourceWarnings ??= {};
|
|
272
|
+
status.sourceWarnings[source.key] ??= [
|
|
273
|
+
upstream.retired ? 'Source is retired.' : 'An explicit source needs review.',
|
|
274
|
+
];
|
|
275
|
+
}
|
|
276
|
+
}
|
|
249
277
|
const references: Source[] = [...(sources.get(key) ?? [])];
|
|
250
278
|
const boardId = portals.get(key);
|
|
251
279
|
if (boardId) {
|
|
@@ -18,6 +18,7 @@ import { EXTENSION_ID, RESOURCE_MIME_TYPE } from '@modelcontextprotocol/ext-apps
|
|
|
18
18
|
import type { McpUiClientCapabilities, McpUiResourceCsp, McpUiResourceMeta } from '@modelcontextprotocol/ext-apps';
|
|
19
19
|
import { getToolUiResourceUri } from '@modelcontextprotocol/ext-apps/app-bridge';
|
|
20
20
|
import { normalizeExtAppToolResult } from './ext-app-tool-result.js';
|
|
21
|
+
import { canvasState } from './canvas-state.js';
|
|
21
22
|
|
|
22
23
|
// A restriction follows async rehydration spawned by an embedded operation,
|
|
23
24
|
// without disabling independent requests from the local workbench.
|
|
@@ -71,10 +72,12 @@ type RuntimeTransport = StdioClientTransport | StreamableHTTPClientTransport;
|
|
|
71
72
|
|
|
72
73
|
interface McpAppSession {
|
|
73
74
|
id: string;
|
|
75
|
+
board: object;
|
|
74
76
|
serverName: string;
|
|
75
77
|
client: Client;
|
|
76
78
|
transport: RuntimeTransport;
|
|
77
79
|
tools: Tool[];
|
|
80
|
+
unregisterBoardDispose: () => void;
|
|
78
81
|
}
|
|
79
82
|
|
|
80
83
|
const uiCapabilities: McpUiClientCapabilities = {
|
|
@@ -223,21 +226,35 @@ async function createSession(
|
|
|
223
226
|
}
|
|
224
227
|
const transport = buildTransport(transportConfig);
|
|
225
228
|
const client = new Client({ name: 'pmx-canvas-app-host', version: '0.1.0' }, { capabilities: clientCapabilities });
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const toolList = await client.listTools(undefined, requestOptions(timeoutMs));
|
|
229
|
+
const id = randomId('mcp-app-session');
|
|
230
|
+
let disposed = false;
|
|
229
231
|
const session: McpAppSession = {
|
|
230
|
-
id
|
|
232
|
+
id,
|
|
233
|
+
board: canvasState.boardIdentity,
|
|
231
234
|
serverName: normalizeServerName(serverName, transportConfig),
|
|
232
235
|
client,
|
|
233
236
|
transport,
|
|
234
|
-
tools:
|
|
237
|
+
tools: [],
|
|
238
|
+
unregisterBoardDispose: canvasState.onBoardDispose(() => {
|
|
239
|
+
disposed = true;
|
|
240
|
+
closeMcpAppSession(id);
|
|
241
|
+
}),
|
|
235
242
|
};
|
|
236
243
|
sessions.set(session.id, session);
|
|
237
|
-
|
|
244
|
+
try {
|
|
245
|
+
await client.connect(transport, requestOptions(timeoutMs));
|
|
246
|
+
const toolList = await client.listTools(undefined, requestOptions(timeoutMs));
|
|
247
|
+
if (disposed) throw new Error('The app board was deleted while connecting.');
|
|
248
|
+
session.tools = toolList.tools;
|
|
249
|
+
return session;
|
|
250
|
+
} catch (error) {
|
|
251
|
+
await closeSession(session);
|
|
252
|
+
throw error;
|
|
253
|
+
}
|
|
238
254
|
}
|
|
239
255
|
|
|
240
256
|
async function closeSession(session: McpAppSession): Promise<void> {
|
|
257
|
+
session.unregisterBoardDispose();
|
|
241
258
|
sessions.delete(session.id);
|
|
242
259
|
await session.transport.close();
|
|
243
260
|
}
|
|
@@ -468,5 +485,7 @@ export function hasMcpAppSession(sessionId: string): boolean {
|
|
|
468
485
|
}
|
|
469
486
|
|
|
470
487
|
export function listMcpAppSessionIds(): string[] {
|
|
471
|
-
return [...sessions.
|
|
488
|
+
return [...sessions.values()]
|
|
489
|
+
.filter((session) => session.board === canvasState.boardIdentity)
|
|
490
|
+
.map((session) => session.id);
|
|
472
491
|
}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
import type { CanvasNodeState, CanvasEdge, CanvasLayout } from './canvas-state.js';
|
|
16
16
|
import { currentActor as getActor, setCurrentActor } from './attribution.js';
|
|
17
|
+
import { createBoardLocal } from './board-local.js';
|
|
17
18
|
|
|
18
19
|
// ── Types ────────────────────────────────────────────────────────────
|
|
19
20
|
|
|
@@ -88,11 +89,25 @@ export interface SnapshotDiffResult {
|
|
|
88
89
|
removedEdges: { id: string; from: string; to: string; toBoardId?: string; type: string }[];
|
|
89
90
|
}
|
|
90
91
|
|
|
91
|
-
function comparableNodeData(data: Record<string, unknown>):
|
|
92
|
+
function comparableNodeData(data: Record<string, unknown>): string {
|
|
92
93
|
const comparable = { ...data };
|
|
93
94
|
delete comparable.title;
|
|
94
95
|
delete comparable.content;
|
|
95
|
-
|
|
96
|
+
// Match content-revision semantics: fetching the same page again is not a
|
|
97
|
+
// content change. Keep authored timestamps elsewhere, including nested data.
|
|
98
|
+
delete comparable.fetchedAt;
|
|
99
|
+
if (comparable.provenance && typeof comparable.provenance === 'object') {
|
|
100
|
+
const provenance = { ...(comparable.provenance as Record<string, unknown>) };
|
|
101
|
+
delete provenance.syncedAt;
|
|
102
|
+
comparable.provenance = provenance;
|
|
103
|
+
}
|
|
104
|
+
// Canonical JSON preserves array order and the persisted JSON semantics for
|
|
105
|
+
// undefined properties, while ignoring object-key insertion order at any depth.
|
|
106
|
+
return JSON.stringify(comparable, (_key, value: unknown) =>
|
|
107
|
+
value && typeof value === 'object' && !Array.isArray(value)
|
|
108
|
+
? Object.fromEntries(Object.entries(value).sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)))
|
|
109
|
+
: value,
|
|
110
|
+
);
|
|
96
111
|
}
|
|
97
112
|
|
|
98
113
|
// ── Ring Buffer ──────────────────────────────────────────────────────
|
|
@@ -100,11 +115,25 @@ function comparableNodeData(data: Record<string, unknown>): Record<string, unkno
|
|
|
100
115
|
const MAX_ENTRIES = 200;
|
|
101
116
|
|
|
102
117
|
class MutationHistory {
|
|
103
|
-
private entries:
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
private
|
|
118
|
+
private readonly board = createBoardLocal(() => ({ entries: [] as MutationEntry[], cursor: -1, replaying: false }));
|
|
119
|
+
private get entries() {
|
|
120
|
+
return this.board.current.entries;
|
|
121
|
+
}
|
|
122
|
+
private set entries(value: MutationEntry[]) {
|
|
123
|
+
this.board.current.entries = value;
|
|
124
|
+
}
|
|
125
|
+
private get cursor() {
|
|
126
|
+
return this.board.current.cursor;
|
|
127
|
+
}
|
|
128
|
+
private set cursor(value: number) {
|
|
129
|
+
this.board.current.cursor = value;
|
|
130
|
+
}
|
|
131
|
+
private get _replaying() {
|
|
132
|
+
return this.board.current.replaying;
|
|
133
|
+
}
|
|
134
|
+
private set _replaying(value: boolean) {
|
|
135
|
+
this.board.current.replaying = value;
|
|
136
|
+
}
|
|
108
137
|
|
|
109
138
|
get isReplaying(): boolean {
|
|
110
139
|
return this._replaying;
|
|
@@ -315,7 +344,7 @@ export function diffLayouts(
|
|
|
315
344
|
changes.push(`content changed (${lenDiff >= 0 ? '+' : ''}${lenDiff} chars)`);
|
|
316
345
|
}
|
|
317
346
|
|
|
318
|
-
if (
|
|
347
|
+
if (comparableNodeData(snapNode.data) !== comparableNodeData(curNode.data)) {
|
|
319
348
|
changes.push('data changed');
|
|
320
349
|
}
|
|
321
350
|
|
|
@@ -152,15 +152,17 @@ export const compositeToolDefinitions: CompositeToolDefinition[] = [
|
|
|
152
152
|
{
|
|
153
153
|
toolName: 'canvas_board',
|
|
154
154
|
description:
|
|
155
|
+
'"move-selection" previews or atomically relocates selected cards and group descendants to a new board, preserves identity and leaves a portal. Supply sourceBoardId, nodeIds, name, expectedRevision and requestId; preview:true reports blockers and the Undo/snapshot boundary. Retry identical input with the same requestId. It never opens the destination. ' +
|
|
155
156
|
'Knowledge data: knowledge-list reads categories/folders/shared relation presets; category-set replaces category (id/name/glyph/decayType/rate/triggers/revalidationMethod); category-remove deletes an unused category by id (explicit folder assignments, including empty folders, refuse deletion); relation-preset-set saves preset (id/name/vocabulary); relation-preset-remove deletes a template without changing existing board vocabularies; folder-set replaces path/categoryId/lifecycleOverride (null inherits). These workspace actions never open a board and are refused under agent scope fences. ' +
|
|
156
157
|
'Boards in this workspace — the library of the human\'s and your working memory, filed in folders on Home. Your writes always go to the open board (activeBoardId); on Home (no board open) your first write opens the most recent board, or a new one when there are none. Action "list" returns every board (with its folder), most recently opened first; "get" reads one (default: the open board); "create" makes a new empty board WITHOUT opening it (optional folder); "open" explicitly switches the server active board to id (null or omitted opens Home), changing the subsequent write target. Humans browsing Map/Graph stay in the library until they explicitly open a board/card; do not infer activeBoardId from their visible library. After creating a board, open its returned id and verify activeBoardId before writing; another participant can switch the board later. "update" renames a board, files it under a folder (folder "" removes it), or designates its README via readmeNodeId. As part of normal board authoring, read the board metadata; if readmeNodeId is null, choose or create a concise markdown introduction explaining its purpose and key context, then designate that node with "update". Preserve an existing README unless the human asks to change it; humans can override the designation manually. "pin" adds a board to the working set (its README and pinned cards reach your brief whichever board is open; optional reason), "unpin" removes it; a pinned board is read-only context, never a write target. Only the human deletes boards.',
|
|
157
158
|
actionSummary:
|
|
158
|
-
'list | get | create | create-from | open | update | pin | unpin | knowledge-list | category-set | category-remove | relation-preset-set | relation-preset-remove | folder-set',
|
|
159
|
+
'list | get | create | create-from | move-selection | open | update | pin | unpin | knowledge-list | category-set | category-remove | relation-preset-set | relation-preset-remove | folder-set',
|
|
159
160
|
actions: {
|
|
160
161
|
list: 'board.list',
|
|
161
162
|
get: 'board.get',
|
|
162
163
|
create: 'board.create',
|
|
163
164
|
'create-from': 'board.create-from',
|
|
165
|
+
'move-selection': 'board.move-selection',
|
|
164
166
|
open: 'board.open',
|
|
165
167
|
update: 'board.update',
|
|
166
168
|
'knowledge-list': 'knowledge.list',
|
|
@@ -239,9 +241,9 @@ export const compositeToolDefinitions: CompositeToolDefinition[] = [
|
|
|
239
241
|
{
|
|
240
242
|
toolName: 'canvas_view',
|
|
241
243
|
description:
|
|
242
|
-
'Canvas viewport and layout control. Action "arrange" auto-lays-out nodes (grid/columns/etc.); "focus" pans/zooms the viewport to a node; "fit" zooms to fit all nodes in view; "clear" removes every node and edge from the canvas; "remove-annotation"
|
|
244
|
+
'Canvas viewport and layout control. Action "arrange" auto-lays-out nodes (grid/columns/etc.); "focus" pans/zooms the viewport to a node; "fit" zooms to fit all nodes in view; "clear" removes every node and edge from the canvas; "add-annotation" draws freehand points or text in world coordinates; "remove-annotation" erases a whole mark by id; "get-tour"/"set-tour" read or persist the presentation tour; "tour-go" presents a stop (stop index or step next/previous); "tour-exit" leaves presentation; "move" eases the camera to a nodeId, world rect or viewport over a duration. Camera actions never enter undo history.',
|
|
243
245
|
actionSummary:
|
|
244
|
-
'arrange | focus | fit | clear | remove-annotation | get-tour | set-tour | tour-go | tour-exit | move',
|
|
246
|
+
'arrange | focus | fit | clear | add-annotation | remove-annotation | get-tour | set-tour | tour-go | tour-exit | move',
|
|
245
247
|
actions: {
|
|
246
248
|
'get-tour': 'tour.get',
|
|
247
249
|
'set-tour': 'tour.set',
|
|
@@ -252,6 +254,7 @@ export const compositeToolDefinitions: CompositeToolDefinition[] = [
|
|
|
252
254
|
focus: 'node.focus',
|
|
253
255
|
fit: 'view.fit',
|
|
254
256
|
clear: 'canvas.clear',
|
|
257
|
+
'add-annotation': 'annotation.add',
|
|
255
258
|
'remove-annotation': 'annotation.remove',
|
|
256
259
|
},
|
|
257
260
|
},
|
|
@@ -86,6 +86,20 @@ const payloads: Record<string, ZodType> = {
|
|
|
86
86
|
board,
|
|
87
87
|
z.looseObject({ ok: z.boolean(), sourceBoardId: id, cards: z.array(node), edges: z.array(edge) }),
|
|
88
88
|
]),
|
|
89
|
+
'board.move-selection': z.union([
|
|
90
|
+
z.looseObject({
|
|
91
|
+
ok: z.boolean(),
|
|
92
|
+
preview: z.looseObject({ sourceBoardId: id, nodeIds: z.array(id), blockers: z.array(z.string()) }),
|
|
93
|
+
}),
|
|
94
|
+
z.looseObject({
|
|
95
|
+
ok: z.boolean(),
|
|
96
|
+
sourceBoardId: id,
|
|
97
|
+
destinationBoardId: id,
|
|
98
|
+
portalId: id,
|
|
99
|
+
nodeIds: z.array(id),
|
|
100
|
+
replayed: z.boolean(),
|
|
101
|
+
}),
|
|
102
|
+
]),
|
|
89
103
|
'board.open': boards,
|
|
90
104
|
'board.update': boards,
|
|
91
105
|
'board.pin': boards,
|
|
@@ -145,6 +159,7 @@ const payloads: Record<string, ZodType> = {
|
|
|
145
159
|
'node.focus': z.looseObject({ ok: z.boolean(), focused: id, panned: z.boolean() }),
|
|
146
160
|
'view.fit': z.looseObject({ ok: z.boolean(), viewport }),
|
|
147
161
|
'canvas.clear': z.looseObject({ ok: z.boolean(), cleared: z.literal(true) }),
|
|
162
|
+
'annotation.add': entity('annotation'),
|
|
148
163
|
'annotation.remove': ok,
|
|
149
164
|
'context.get': z.looseObject({ version: z.literal(1), budgetUnit: z.literal('utf16-code-units') }),
|
|
150
165
|
'memory-graph.get': z.looseObject({
|
|
@@ -36,6 +36,17 @@ export interface OperationToolHost extends OperationMcpToolHost {
|
|
|
36
36
|
recordContextRead(read: ContextReadInput): Promise<void>;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
const routingShape = {
|
|
40
|
+
targetBoardId: z
|
|
41
|
+
.string()
|
|
42
|
+
.min(1)
|
|
43
|
+
.optional()
|
|
44
|
+
.describe(
|
|
45
|
+
'Execute on this board without changing the shared workbench view. Nested operations retain this target.',
|
|
46
|
+
),
|
|
47
|
+
agentId: z.string().min(1).optional().describe('Stable agent/thread identity for attribution and scope fences.'),
|
|
48
|
+
};
|
|
49
|
+
|
|
39
50
|
function executionOptions(extra: RequestHandlerExtra<ServerRequest, ServerNotification>): OperationExecutionOptions {
|
|
40
51
|
const progressToken = extra._meta?.progressToken;
|
|
41
52
|
return {
|
|
@@ -58,9 +69,23 @@ async function invokeMcpOperation(
|
|
|
58
69
|
options: OperationExecutionOptions,
|
|
59
70
|
) {
|
|
60
71
|
options.signal?.throwIfAborted();
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
72
|
+
// buildInput may project operation-specific fields; routing belongs to the
|
|
73
|
+
// invocation and must also cover formatter follow-up reads and accounting.
|
|
74
|
+
const routing = {
|
|
75
|
+
...(formatInput.targetBoardId !== undefined ? { targetBoardId: formatInput.targetBoardId } : {}),
|
|
76
|
+
...(formatInput.agentId !== undefined ? { agentId: formatInput.agentId } : {}),
|
|
77
|
+
};
|
|
78
|
+
opInput = { ...opInput, ...routing };
|
|
79
|
+
if (typeof routing.agentId === 'string') consumer = routing.agentId;
|
|
80
|
+
const board =
|
|
81
|
+
typeof routing.targetBoardId === 'string'
|
|
82
|
+
? routing.targetBoardId
|
|
83
|
+
: typeof opInput.board === 'string' && opInput.board.trim()
|
|
84
|
+
? opInput.board.trim()
|
|
85
|
+
: undefined;
|
|
86
|
+
const preparesContext = op.name === 'context.get' && opInput.contextDelivery === false;
|
|
87
|
+
const readTarget = CONTEXT_READ_OPS.has(op.name) && !preparesContext ? await host.prepareContextRead(board) : null;
|
|
88
|
+
if (readTarget?.boardId && 'board' in op.inputShape && opInput.board === undefined) {
|
|
64
89
|
opInput = { ...opInput, board: readTarget.boardId };
|
|
65
90
|
formatInput = { ...formatInput, board: readTarget.boardId };
|
|
66
91
|
}
|
|
@@ -70,8 +95,10 @@ async function invokeMcpOperation(
|
|
|
70
95
|
// Preserve the host receiver, and bind options to formatter follow-up reads
|
|
71
96
|
// (history flags, snapshot summary, streaming node metadata).
|
|
72
97
|
const formatterHost: OperationMcpToolHost = {
|
|
73
|
-
getPinnedNodeIds: (target) => host.getPinnedNodeIds(target),
|
|
74
|
-
invoker: () => ({
|
|
98
|
+
getPinnedNodeIds: (target) => host.getPinnedNodeIds(target ?? board),
|
|
99
|
+
invoker: () => ({
|
|
100
|
+
invoke: (name, input, followUp) => invoker.invoke(name, { ...input, ...routing }, { ...options, ...followUp }),
|
|
101
|
+
}),
|
|
75
102
|
};
|
|
76
103
|
const formatted = structuredOperationResult(
|
|
77
104
|
op.name,
|
|
@@ -151,7 +178,7 @@ export function registerOperationTools(server: McpServer, getHost: () => Promise
|
|
|
151
178
|
// successful over MCP while the same HTTP call 400'd (0.5.1 Amp
|
|
152
179
|
// finding A). Loose parsing forwards unknown keys to the op layer,
|
|
153
180
|
// the single authority on dead-field rejection, HTTP and MCP alike.
|
|
154
|
-
inputSchema: z.looseObject({ ...op.inputShape, ...(tool.extraShape ?? {}) }),
|
|
181
|
+
inputSchema: z.looseObject({ ...op.inputShape, ...(tool.extraShape ?? {}), ...routingShape }),
|
|
155
182
|
outputSchema: operationOutputSchema([op.name]),
|
|
156
183
|
annotations: operationToolAnnotations(tool.toolName),
|
|
157
184
|
},
|
|
@@ -376,8 +403,8 @@ export function registerOperationHelpTool(server: McpServer): void {
|
|
|
376
403
|
const composite = compositeToolDefinitions.find((def) => compositeMemberOps(def).includes(name));
|
|
377
404
|
const publicName = (field: string) =>
|
|
378
405
|
Object.entries(composite?.fieldRemap ?? {}).find(([, original]) => original === field)?.[0] ?? field;
|
|
379
|
-
const shape = Object.fromEntries(
|
|
380
|
-
Object.entries({ ...op.inputShape, ...op.mcp?.extraShape }).map(([field, schema]) => [
|
|
406
|
+
const shape: Record<string, ZodTypeAny> = Object.fromEntries(
|
|
407
|
+
Object.entries({ ...op.inputShape, ...op.mcp?.extraShape, ...routingShape }).map(([field, schema]) => [
|
|
381
408
|
publicName(field),
|
|
382
409
|
schema,
|
|
383
410
|
]),
|
|
@@ -444,7 +471,7 @@ export function registerCompositeTools(
|
|
|
444
471
|
def.toolName,
|
|
445
472
|
{
|
|
446
473
|
description: def.description,
|
|
447
|
-
inputSchema: z.looseObject(buildCompositeShape(def)),
|
|
474
|
+
inputSchema: z.looseObject({ ...buildCompositeShape(def), ...routingShape }),
|
|
448
475
|
outputSchema: operationOutputSchema(compositeMemberOps(def)),
|
|
449
476
|
annotations: operationToolAnnotations(def.toolName),
|
|
450
477
|
},
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*
|
|
14
14
|
* This module must never import server.ts or index.ts.
|
|
15
15
|
*/
|
|
16
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
16
17
|
import { z } from 'zod';
|
|
17
18
|
import { canvasState, type CanvasAnnotation } from '../../canvas-state.js';
|
|
18
19
|
import { summarizeCanvasAnnotation } from '../../canvas-serialization.js';
|
|
@@ -35,7 +36,7 @@ const annotationRemoveOperation = defineOperation<z.infer<typeof annotationRemov
|
|
|
35
36
|
},
|
|
36
37
|
mcp: {
|
|
37
38
|
toolName: 'canvas_remove_annotation',
|
|
38
|
-
description: '
|
|
39
|
+
description: 'Erase a whole canvas mark by ID, whether drawn by a human or agent.',
|
|
39
40
|
extraShape: {
|
|
40
41
|
id: z.string().describe('Annotation ID to remove'),
|
|
41
42
|
},
|
|
@@ -119,6 +120,19 @@ const annotationAddOperation = defineOperation<z.infer<typeof annotationAddSchem
|
|
|
119
120
|
method: 'POST',
|
|
120
121
|
path: '/api/canvas/annotation',
|
|
121
122
|
},
|
|
123
|
+
mcp: {
|
|
124
|
+
toolName: 'canvas_add_annotation',
|
|
125
|
+
description: 'Draw canvas ink or write a text note in world coordinates. No session required.',
|
|
126
|
+
extraShape: {
|
|
127
|
+
type: z.enum(['freehand', 'text']).optional(),
|
|
128
|
+
points: z.array(z.object({ x: z.number(), y: z.number() })).min(1),
|
|
129
|
+
width: z.number().optional(),
|
|
130
|
+
color: z.string().optional(),
|
|
131
|
+
label: z.string().optional(),
|
|
132
|
+
text: z.string().optional(),
|
|
133
|
+
id: z.string().optional(),
|
|
134
|
+
},
|
|
135
|
+
},
|
|
122
136
|
handler: (input) => {
|
|
123
137
|
const body: Record<string, unknown> = input;
|
|
124
138
|
const type = body.type === 'text' ? 'text' : 'freehand';
|
|
@@ -164,6 +178,13 @@ const annotationAddOperation = defineOperation<z.infer<typeof annotationAddSchem
|
|
|
164
178
|
createdAt: new Date().toISOString(),
|
|
165
179
|
};
|
|
166
180
|
|
|
181
|
+
const existing = canvasState.getAnnotations().find((entry) => entry.id === id);
|
|
182
|
+
if (existing) {
|
|
183
|
+
if (!isDeepStrictEqual(existing, { ...annotation, createdAt: existing.createdAt })) {
|
|
184
|
+
throw new OperationError(`Annotation "${id}" already exists with different content.`, 409);
|
|
185
|
+
}
|
|
186
|
+
return { ok: true, annotation: summarizeCanvasAnnotation(existing) };
|
|
187
|
+
}
|
|
167
188
|
canvasState.addAnnotation(annotation);
|
|
168
189
|
return { ok: true, annotation: summarizeCanvasAnnotation(annotation) };
|
|
169
190
|
},
|
|
@@ -69,6 +69,7 @@ function clampTimeoutMs(value: unknown): number {
|
|
|
69
69
|
/** Accept the operation's timeoutMs as well as the direct HTTP waitMs spelling. */
|
|
70
70
|
function readWaitMsInput(_req: Request, params: Record<string, string>, url: URL): Record<string, unknown> {
|
|
71
71
|
return {
|
|
72
|
+
...Object.fromEntries(url.searchParams),
|
|
72
73
|
...params,
|
|
73
74
|
timeoutMs: clampTimeoutMs(url.searchParams.get('timeoutMs') ?? url.searchParams.get('waitMs') ?? ''),
|
|
74
75
|
};
|