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
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Database } from 'bun:sqlite';
|
|
2
|
+
|
|
3
|
+
/** JSON target list parameter; event ranges prevent a reused old ID inheriting the moved card's history. */
|
|
4
|
+
export const CARD_HISTORY_CTE = `WITH RECURSIVE
|
|
5
|
+
targets AS (SELECT json_extract(value, '$.boardId') AS current_board,
|
|
6
|
+
json_extract(value, '$.cardId') AS node_id FROM json_each(?)),
|
|
7
|
+
history(current_board, node_id, board_id, review_after, review_through, read_after, read_through, move_before) AS (
|
|
8
|
+
SELECT current_board, node_id, current_board,
|
|
9
|
+
COALESCE((SELECT MAX(review_through) FROM card_relocations r WHERE (r.from_board = t.current_board OR r.to_board = t.current_board) AND r.node_id = t.node_id), 0),
|
|
10
|
+
9223372036854775807,
|
|
11
|
+
COALESCE((SELECT MAX(read_through) FROM card_relocations r WHERE (r.from_board = t.current_board OR r.to_board = t.current_board) AND r.node_id = t.node_id), 0),
|
|
12
|
+
9223372036854775807, 9223372036854775807 FROM targets t
|
|
13
|
+
UNION ALL
|
|
14
|
+
SELECT h.current_board, h.node_id, r.from_board, r.review_after, r.review_through,
|
|
15
|
+
r.read_after, r.read_through, r.sequence
|
|
16
|
+
FROM history h JOIN card_relocations r ON r.to_board = h.board_id AND r.node_id = h.node_id
|
|
17
|
+
AND r.sequence = (SELECT MAX(sequence) FROM card_relocations prior
|
|
18
|
+
WHERE (prior.to_board = h.board_id OR prior.from_board = h.board_id)
|
|
19
|
+
AND prior.node_id = h.node_id AND prior.sequence < h.move_before)
|
|
20
|
+
)`;
|
|
21
|
+
|
|
22
|
+
/** Record locations, not new reads or validations. Called inside the move transaction. */
|
|
23
|
+
export function recordCardRelocation(
|
|
24
|
+
db: Database,
|
|
25
|
+
requestId: string,
|
|
26
|
+
from: string,
|
|
27
|
+
to: string,
|
|
28
|
+
nodeIds: string[],
|
|
29
|
+
): void {
|
|
30
|
+
const reviewThrough = db
|
|
31
|
+
.query<{ value: number }, []>('SELECT COALESCE(MAX(sequence), 0) AS value FROM review_records')
|
|
32
|
+
.get()!.value;
|
|
33
|
+
const readThrough = db
|
|
34
|
+
.query<{ value: number }, []>('SELECT COALESCE(MAX(seq), 0) AS value FROM context_reads')
|
|
35
|
+
.get()!.value;
|
|
36
|
+
for (const id of nodeIds)
|
|
37
|
+
db.run(
|
|
38
|
+
`
|
|
39
|
+
INSERT INTO card_relocations(request_id, node_id, from_board, to_board, review_after, review_through, read_after, read_through)
|
|
40
|
+
VALUES (?, ?, ?, ?,
|
|
41
|
+
COALESCE((SELECT MAX(review_through) FROM card_relocations WHERE (from_board = ? OR to_board = ?) AND node_id = ?), 0), ?,
|
|
42
|
+
COALESCE((SELECT MAX(read_through) FROM card_relocations WHERE (from_board = ? OR to_board = ?) AND node_id = ?), 0), ?)`,
|
|
43
|
+
[requestId, id, from, to, from, from, id, reviewThrough, from, from, id, readThrough],
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function boardMoveBoundary(db: Database, boardId: string): string | null {
|
|
48
|
+
return (
|
|
49
|
+
db
|
|
50
|
+
.query<{ request_id: string }, [string]>('SELECT request_id FROM board_move_boundaries WHERE board_id = ?')
|
|
51
|
+
.get(boardId)?.request_id ?? null
|
|
52
|
+
);
|
|
53
|
+
}
|
package/src/server/code-graph.ts
CHANGED
|
@@ -18,6 +18,7 @@ import { resolve, dirname, basename, extname, relative } from 'node:path';
|
|
|
18
18
|
import { existsSync } from 'node:fs';
|
|
19
19
|
import { canvasState } from './canvas-state.js';
|
|
20
20
|
import type { CanvasNodeState, CanvasEdge } from './canvas-state.js';
|
|
21
|
+
import { createBoardLocal } from './board-local.js';
|
|
21
22
|
|
|
22
23
|
// ── Types ────────────────────────────────────────────────────────────
|
|
23
24
|
|
|
@@ -176,7 +177,7 @@ function tryResolveWithExtensions(basePath: string): string | null {
|
|
|
176
177
|
const AUTO_EDGE_PREFIX = 'codegraph-';
|
|
177
178
|
|
|
178
179
|
/** Cache of last recomputed edges for buildCodeGraphSummary() to reuse. */
|
|
179
|
-
|
|
180
|
+
const discovered = createBoardLocal(() => ({ edges: [] as CodeGraphEdge[] }));
|
|
180
181
|
|
|
181
182
|
/**
|
|
182
183
|
* Get all file nodes currently on the canvas.
|
|
@@ -263,7 +264,7 @@ export function recomputeCodeGraph(): CodeGraphEdge[] {
|
|
|
263
264
|
}
|
|
264
265
|
});
|
|
265
266
|
|
|
266
|
-
|
|
267
|
+
discovered.current.edges = discoveredEdges;
|
|
267
268
|
return discoveredEdges;
|
|
268
269
|
}
|
|
269
270
|
|
|
@@ -295,7 +296,7 @@ export function buildCodeGraphSummary(): CodeGraphSummary {
|
|
|
295
296
|
for (const node of fileNodes) idToPath.set(node.id, node.data.path as string);
|
|
296
297
|
|
|
297
298
|
// Group import specifiers by source node from cached edges
|
|
298
|
-
for (const edge of
|
|
299
|
+
for (const edge of discovered.current.edges) {
|
|
299
300
|
const specs = importSpecs.get(edge.fromNodeId);
|
|
300
301
|
if (specs && !specs.includes(edge.importSpecifier)) {
|
|
301
302
|
specs.push(edge.importSpecifier);
|
|
@@ -6,9 +6,11 @@
|
|
|
6
6
|
* recording never notifies (a read must not trigger more reads).
|
|
7
7
|
*/
|
|
8
8
|
import type { Database } from 'bun:sqlite';
|
|
9
|
-
import { randomUUID } from 'node:crypto';
|
|
9
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
10
|
+
import { CARD_HISTORY_CTE } from './card-relocation.js';
|
|
10
11
|
import type { MemoryGraph } from '../shared/memory-graph.js';
|
|
11
12
|
import type { CompiledContextEntry } from './context-brief.js';
|
|
13
|
+
import { OperationError } from './operations/types.js';
|
|
12
14
|
|
|
13
15
|
export const CONTEXT_READ_RETENTION = 5000;
|
|
14
16
|
const CONTEXT_READ_DEFAULT_LIMIT = 50;
|
|
@@ -51,6 +53,8 @@ export interface ContextRead {
|
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
export type ContextReadInput = Omit<ContextRead, 'seq' | 'id' | 'at' | 'boardId' | 'readNodes' | 'seenLinks'> & {
|
|
56
|
+
/** Stable adapter receipt identity. Retries record this delivery once per consumer and board. */
|
|
57
|
+
deliveryId?: string;
|
|
54
58
|
/** Explicit read target, captured before asynchronous formatting/proxy work. */
|
|
55
59
|
boardId?: string | null;
|
|
56
60
|
/** Ids of every serialized node in what the reader received; the server keeps those on the board. */
|
|
@@ -264,6 +268,7 @@ export function graphReads(
|
|
|
264
268
|
}
|
|
265
269
|
|
|
266
270
|
export const CONTEXT_READS_SCHEMA_SQL = `
|
|
271
|
+
CREATE TABLE IF NOT EXISTS context_read_receipts (id TEXT PRIMARY KEY);
|
|
267
272
|
CREATE TABLE IF NOT EXISTS context_reads (
|
|
268
273
|
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
269
274
|
id TEXT NOT NULL UNIQUE,
|
|
@@ -289,30 +294,47 @@ export function appendContextReadToDB(
|
|
|
289
294
|
readNodes: Record<string, number> = {},
|
|
290
295
|
seenLinks: Record<string, number> = {},
|
|
291
296
|
): ContextRead {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
297
|
+
return db.transaction(() => {
|
|
298
|
+
const id = input.deliveryId
|
|
299
|
+
? `read-${createHash('sha256')
|
|
300
|
+
.update(JSON.stringify([input.source, input.consumer, boardId, input.deliveryId]))
|
|
301
|
+
.digest('hex')}`
|
|
302
|
+
: `read-${randomUUID()}`;
|
|
303
|
+
if (input.deliveryId) {
|
|
304
|
+
const existing = db.query<ContextReadRow, [string]>('SELECT * FROM context_reads WHERE id = ?').get(id);
|
|
305
|
+
if (existing) {
|
|
306
|
+
db.run('INSERT OR IGNORE INTO context_read_receipts VALUES (?)', [id]);
|
|
307
|
+
return rowToContextRead(existing);
|
|
308
|
+
}
|
|
309
|
+
if (db.query('SELECT id FROM context_read_receipts WHERE id = ?').get(id))
|
|
310
|
+
throw new OperationError('Context receipt expired. Obtain fresh context instead of retrying this token.', 409, {
|
|
311
|
+
code: 'context_receipt_expired',
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
const at = new Date().toISOString();
|
|
315
|
+
db.run(
|
|
316
|
+
'INSERT INTO context_reads (id, at, channel, resource, source, consumer, agent_id, pinned_node_ids, delivered_node_ids, bytes, board_id, read_nodes, seen_links) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(id) DO NOTHING',
|
|
317
|
+
[
|
|
318
|
+
id,
|
|
319
|
+
at,
|
|
320
|
+
input.channel,
|
|
321
|
+
input.resource,
|
|
322
|
+
input.source,
|
|
323
|
+
input.consumer,
|
|
324
|
+
input.agentId,
|
|
325
|
+
JSON.stringify(input.pinnedNodeIds),
|
|
326
|
+
JSON.stringify(input.deliveredNodeIds),
|
|
327
|
+
input.bytes,
|
|
328
|
+
boardId,
|
|
329
|
+
JSON.stringify(readNodes),
|
|
330
|
+
JSON.stringify(seenLinks),
|
|
331
|
+
],
|
|
332
|
+
);
|
|
333
|
+
const row = db.query<ContextReadRow, [string]>('SELECT * FROM context_reads WHERE id = ?').get(id)!;
|
|
334
|
+
if (input.deliveryId) db.run('INSERT INTO context_read_receipts VALUES (?)', [id]);
|
|
335
|
+
db.run('DELETE FROM context_reads WHERE seq <= ?', [row.seq - CONTEXT_READ_RETENTION]);
|
|
336
|
+
return rowToContextRead(row);
|
|
337
|
+
})();
|
|
316
338
|
}
|
|
317
339
|
|
|
318
340
|
interface ContextReadRow {
|
|
@@ -351,27 +373,43 @@ function rowToContextRead(row: ContextReadRow): ContextRead {
|
|
|
351
373
|
};
|
|
352
374
|
}
|
|
353
375
|
|
|
376
|
+
/** Read only the matching card's observation, not a copy of the whole receipt for every card. */
|
|
377
|
+
function loadCardReads(db: Database, boardId: string, field: 'read_nodes' | 'seen_links') {
|
|
378
|
+
const ids = db
|
|
379
|
+
.query<{ id: string }, [string, string, string]>(`
|
|
380
|
+
SELECT id FROM nodes WHERE board_id = ?
|
|
381
|
+
UNION SELECT j.key AS id FROM context_reads r, json_each(r.read_nodes) j WHERE r.board_id = ?
|
|
382
|
+
UNION SELECT j.key AS id FROM context_reads r, json_each(r.seen_links) j WHERE r.board_id = ?
|
|
383
|
+
`)
|
|
384
|
+
.all(boardId, boardId, boardId);
|
|
385
|
+
return db
|
|
386
|
+
.query<{ nodeId: string; revision: number; at: string; who: string }, [string]>(`
|
|
387
|
+
${CARD_HISTORY_CTE} SELECT h.node_id AS nodeId, j.value AS revision, r.at,
|
|
388
|
+
COALESCE(r.consumer, r.agent_id, r.source) AS who FROM history h JOIN context_reads r
|
|
389
|
+
ON r.board_id = h.board_id AND r.seq > h.read_after AND r.seq <= h.read_through
|
|
390
|
+
JOIN json_each(r.${field}) j ON j.key = h.node_id
|
|
391
|
+
ORDER BY r.seq DESC
|
|
392
|
+
`)
|
|
393
|
+
.all(JSON.stringify(ids.map(({ id }) => ({ boardId, cardId: id }))));
|
|
394
|
+
}
|
|
395
|
+
|
|
354
396
|
/** Per node on a board, the latest agent read that delivered its content (newest row wins). */
|
|
355
397
|
export function loadNodeReadStatusFromDB(db: Database, boardId: string): NodeReadStatus[] {
|
|
356
|
-
const rows = db
|
|
357
|
-
.query<ContextReadRow, [string]>('SELECT * FROM context_reads WHERE board_id = ? ORDER BY seq DESC')
|
|
358
|
-
.all(boardId);
|
|
359
398
|
const status = new Map<string, NodeReadStatus>();
|
|
360
|
-
for (const read of
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
}
|
|
367
|
-
status.set(nodeId, {
|
|
368
|
-
nodeId,
|
|
369
|
-
lastReadAt: read.at,
|
|
370
|
-
lastReadBy: read.consumer ?? read.agentId ?? read.source,
|
|
371
|
-
readRevision,
|
|
372
|
-
readCount: 1,
|
|
373
|
-
});
|
|
399
|
+
for (const read of loadCardReads(db, boardId, 'read_nodes')) {
|
|
400
|
+
const nodeId = read.nodeId;
|
|
401
|
+
const known = status.get(nodeId);
|
|
402
|
+
if (known) {
|
|
403
|
+
known.readCount += 1;
|
|
404
|
+
continue;
|
|
374
405
|
}
|
|
406
|
+
status.set(nodeId, {
|
|
407
|
+
nodeId,
|
|
408
|
+
lastReadAt: read.at,
|
|
409
|
+
lastReadBy: read.who,
|
|
410
|
+
readRevision: read.revision,
|
|
411
|
+
readCount: 1,
|
|
412
|
+
});
|
|
375
413
|
}
|
|
376
414
|
return [...status.values()];
|
|
377
415
|
}
|
|
@@ -391,27 +429,17 @@ export function loadBoardLastReadFromDB(
|
|
|
391
429
|
|
|
392
430
|
/** Per node on a board, the newest links revision any agent read has carried (connection marks). */
|
|
393
431
|
export function loadSeenLinksFromDB(db: Database, boardId: string): Record<string, number> {
|
|
394
|
-
const rows = db
|
|
395
|
-
.query<{ seen_links: string }, [string]>('SELECT seen_links FROM context_reads WHERE board_id = ?')
|
|
396
|
-
.all(boardId);
|
|
397
432
|
const seen: Record<string, number> = {};
|
|
398
|
-
for (const
|
|
399
|
-
|
|
400
|
-
if (revision > (seen[nodeId] ?? -1)) seen[nodeId] = revision;
|
|
401
|
-
}
|
|
433
|
+
for (const { nodeId, revision } of loadCardReads(db, boardId, 'seen_links')) {
|
|
434
|
+
if (revision > (seen[nodeId] ?? -1)) seen[nodeId] = revision;
|
|
402
435
|
}
|
|
403
436
|
return seen;
|
|
404
437
|
}
|
|
405
438
|
|
|
406
439
|
/** Every node on a board whose content an agent read at or after `since` (ISO). */
|
|
407
440
|
export function loadReadNodeIdsSince(db: Database, boardId: string, since: string): string[] {
|
|
408
|
-
const rows = db
|
|
409
|
-
.query<{ read_nodes: string }, [string, string]>(
|
|
410
|
-
'SELECT read_nodes FROM context_reads WHERE board_id = ? AND at >= ? ORDER BY seq',
|
|
411
|
-
)
|
|
412
|
-
.all(boardId, since);
|
|
413
441
|
const ids = new Set<string>();
|
|
414
|
-
for (const row of
|
|
442
|
+
for (const row of loadCardReads(db, boardId, 'read_nodes')) if (row.at >= since) ids.add(row.nodeId);
|
|
415
443
|
return [...ids];
|
|
416
444
|
}
|
|
417
445
|
|
|
@@ -204,7 +204,7 @@
|
|
|
204
204
|
"source": "api"
|
|
205
205
|
},
|
|
206
206
|
"contentRevision": 5,
|
|
207
|
-
"linksRevision":
|
|
207
|
+
"linksRevision": 94,
|
|
208
208
|
"contentChangedAt": "2026-08-01T09:00:00.000Z",
|
|
209
209
|
"freshnessStartedAt": "2026-08-01T09:00:00.000Z",
|
|
210
210
|
"kind": "trace",
|
|
@@ -247,7 +247,7 @@
|
|
|
247
247
|
"source": "api"
|
|
248
248
|
},
|
|
249
249
|
"contentRevision": 6,
|
|
250
|
-
"linksRevision":
|
|
250
|
+
"linksRevision": 94,
|
|
251
251
|
"contentChangedAt": "2026-08-01T09:00:00.000Z",
|
|
252
252
|
"freshnessStartedAt": "2026-08-01T09:00:00.000Z",
|
|
253
253
|
"kind": "trace",
|
|
@@ -319,7 +319,7 @@
|
|
|
319
319
|
"source": "api"
|
|
320
320
|
},
|
|
321
321
|
"contentRevision": 7,
|
|
322
|
-
"linksRevision":
|
|
322
|
+
"linksRevision": 95,
|
|
323
323
|
"contentChangedAt": "2026-08-01T09:00:00.000Z",
|
|
324
324
|
"freshnessStartedAt": "2026-08-01T09:00:00.000Z",
|
|
325
325
|
"kind": "context",
|
|
@@ -362,7 +362,7 @@
|
|
|
362
362
|
"source": "api"
|
|
363
363
|
},
|
|
364
364
|
"contentRevision": 8,
|
|
365
|
-
"linksRevision":
|
|
365
|
+
"linksRevision": 95,
|
|
366
366
|
"contentChangedAt": "2026-08-01T09:00:00.000Z",
|
|
367
367
|
"freshnessStartedAt": "2026-08-01T09:00:00.000Z",
|
|
368
368
|
"kind": "ledger",
|
|
@@ -444,7 +444,7 @@
|
|
|
444
444
|
"source": "api"
|
|
445
445
|
},
|
|
446
446
|
"contentRevision": 10,
|
|
447
|
-
"linksRevision":
|
|
447
|
+
"linksRevision": 96,
|
|
448
448
|
"contentChangedAt": "2026-08-01T09:00:00.000Z",
|
|
449
449
|
"freshnessStartedAt": "2026-08-01T09:00:00.000Z",
|
|
450
450
|
"kind": "file",
|
|
@@ -483,7 +483,7 @@
|
|
|
483
483
|
"source": "api"
|
|
484
484
|
},
|
|
485
485
|
"contentRevision": 11,
|
|
486
|
-
"linksRevision":
|
|
486
|
+
"linksRevision": 96,
|
|
487
487
|
"contentChangedAt": "2026-08-01T09:00:00.000Z",
|
|
488
488
|
"freshnessStartedAt": "2026-08-01T09:00:00.000Z",
|
|
489
489
|
"kind": "diff",
|
|
@@ -778,7 +778,7 @@
|
|
|
778
778
|
"source": "api"
|
|
779
779
|
},
|
|
780
780
|
"contentRevision": 19,
|
|
781
|
-
"linksRevision":
|
|
781
|
+
"linksRevision": 97,
|
|
782
782
|
"contentChangedAt": "2026-08-01T09:00:00.000Z",
|
|
783
783
|
"freshnessStartedAt": "2026-08-01T09:00:00.000Z",
|
|
784
784
|
"kind": "mermaid",
|
|
@@ -817,7 +817,7 @@
|
|
|
817
817
|
"source": "api"
|
|
818
818
|
},
|
|
819
819
|
"contentRevision": 20,
|
|
820
|
-
"linksRevision":
|
|
820
|
+
"linksRevision": 97,
|
|
821
821
|
"contentChangedAt": "2026-08-01T09:00:00.000Z",
|
|
822
822
|
"freshnessStartedAt": "2026-08-01T09:00:00.000Z",
|
|
823
823
|
"kind": "mermaid",
|
|
@@ -886,7 +886,7 @@
|
|
|
886
886
|
"pinned": false,
|
|
887
887
|
"data": {
|
|
888
888
|
"mode": "ext-app",
|
|
889
|
-
"html": "<!doctype html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"><style>\n body { margin:0; font:14px/1.5 var(--font-ui); background:var(--c-panel); color:var(--c-text); }\n main { padding:16px; display:grid; gap:12px; }\n .row { display:grid; grid-template-columns:1fr auto; align-items:center; gap:12px; padding:11px 13px; border:1px solid var(--c-line); border-radius:6px; background:var(--c-panel-soft); }\n .row b { font-weight:600; font-size:13px; } .row small { display:block; color:var(--c-muted); font-size:12px; }\n .pill { font:11px var(--font-mono); padding:3px 9px; border-radius:99px; background:var(--c-panel); color:var(--c-text-soft); border:1px solid var(--c-line); }\n footer { padding:12px 16px; border-top:1px solid var(--c-line); color:var(--c-muted); font-size:12px; }\n</style></head><body>\n <main>\n <div class=\"row\"><span><b>Nodes on board</b><small>across 8 spatial bands</small></span><span class=\"pill\">69</span></div>\n <div class=\"row\"><span><b>Edges</b><small>flow · depends-on · relation · references</small></span><span class=\"pill\">
|
|
889
|
+
"html": "<!doctype html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"><style>\n body { margin:0; font:14px/1.5 var(--font-ui); background:var(--c-panel); color:var(--c-text); }\n main { padding:16px; display:grid; gap:12px; }\n .row { display:grid; grid-template-columns:1fr auto; align-items:center; gap:12px; padding:11px 13px; border:1px solid var(--c-line); border-radius:6px; background:var(--c-panel-soft); }\n .row b { font-weight:600; font-size:13px; } .row small { display:block; color:var(--c-muted); font-size:12px; }\n .pill { font:11px var(--font-mono); padding:3px 9px; border-radius:99px; background:var(--c-panel); color:var(--c-text-soft); border:1px solid var(--c-line); }\n footer { padding:12px 16px; border-top:1px solid var(--c-line); color:var(--c-muted); font-size:12px; }\n</style></head><body>\n <main>\n <div class=\"row\"><span><b>Nodes on board</b><small>across 8 spatial bands</small></span><span class=\"pill\">69</span></div>\n <div class=\"row\"><span><b>Edges</b><small>flow · depends-on · relation · references</small></span><span class=\"pill\">7</span></div>\n <div class=\"row\"><span><b>Context pins</b><small>read via canvas://pinned-context</small></span><span class=\"pill\">3</span></div>\n <div class=\"row\"><span><b>Groups</b><small>one example frame</small></span><span class=\"pill\">1</span></div>\n </main>\n <footer>An MCP App surface rendered through the app bridge from inline HTML — no live server session required.</footer>\n</body></html>",
|
|
890
890
|
"serverName": "pmx-demo",
|
|
891
891
|
"toolName": "inspect_board",
|
|
892
892
|
"title": "Board inspector",
|
|
@@ -3264,7 +3264,7 @@
|
|
|
3264
3264
|
"source": "api"
|
|
3265
3265
|
},
|
|
3266
3266
|
"contentRevision": 46,
|
|
3267
|
-
"linksRevision":
|
|
3267
|
+
"linksRevision": 98,
|
|
3268
3268
|
"contentChangedAt": "2026-08-01T09:00:00.000Z",
|
|
3269
3269
|
"freshnessStartedAt": "2026-08-01T09:00:00.000Z",
|
|
3270
3270
|
"kind": "markdown",
|
|
@@ -3308,7 +3308,7 @@
|
|
|
3308
3308
|
"source": "api"
|
|
3309
3309
|
},
|
|
3310
3310
|
"contentRevision": 47,
|
|
3311
|
-
"linksRevision":
|
|
3311
|
+
"linksRevision": 98,
|
|
3312
3312
|
"contentChangedAt": "2026-08-01T09:00:00.000Z",
|
|
3313
3313
|
"freshnessStartedAt": "2026-08-01T09:00:00.000Z",
|
|
3314
3314
|
"kind": "status",
|
|
@@ -5966,6 +5966,26 @@
|
|
|
5966
5966
|
},
|
|
5967
5967
|
"createdAt": "2026-08-01T09:00:00.000Z"
|
|
5968
5968
|
},
|
|
5969
|
+
{
|
|
5970
|
+
"id": "edge-review-loop",
|
|
5971
|
+
"from": "trace-apply",
|
|
5972
|
+
"to": "trace-plan",
|
|
5973
|
+
"type": "flow",
|
|
5974
|
+
"kind": "loop",
|
|
5975
|
+
"reason": "An illustrated review loop, not an execution trigger.",
|
|
5976
|
+
"label": "review again",
|
|
5977
|
+
"revision": 94,
|
|
5978
|
+
"changedBy": {
|
|
5979
|
+
"actor": "agent",
|
|
5980
|
+
"source": "api"
|
|
5981
|
+
},
|
|
5982
|
+
"changedAt": "2026-08-01T09:00:00.000Z",
|
|
5983
|
+
"createdBy": {
|
|
5984
|
+
"actor": "agent",
|
|
5985
|
+
"source": "api"
|
|
5986
|
+
},
|
|
5987
|
+
"createdAt": "2026-08-01T09:00:00.000Z"
|
|
5988
|
+
},
|
|
5969
5989
|
{
|
|
5970
5990
|
"id": "edge-decisions",
|
|
5971
5991
|
"from": "context",
|
|
@@ -5974,7 +5994,7 @@
|
|
|
5974
5994
|
"kind": "supports",
|
|
5975
5995
|
"reason": "The working context explains the recorded decisions.",
|
|
5976
5996
|
"label": "decisions",
|
|
5977
|
-
"revision":
|
|
5997
|
+
"revision": 95,
|
|
5978
5998
|
"changedBy": {
|
|
5979
5999
|
"actor": "agent",
|
|
5980
6000
|
"source": "api"
|
|
@@ -5993,7 +6013,7 @@
|
|
|
5993
6013
|
"type": "depends-on",
|
|
5994
6014
|
"label": "changed in",
|
|
5995
6015
|
"sourceRevision": 11,
|
|
5996
|
-
"revision":
|
|
6016
|
+
"revision": 96,
|
|
5997
6017
|
"changedBy": {
|
|
5998
6018
|
"actor": "agent",
|
|
5999
6019
|
"source": "api"
|
|
@@ -6012,7 +6032,7 @@
|
|
|
6012
6032
|
"type": "references",
|
|
6013
6033
|
"label": "approval gate",
|
|
6014
6034
|
"style": "dashed",
|
|
6015
|
-
"revision":
|
|
6035
|
+
"revision": 97,
|
|
6016
6036
|
"changedBy": {
|
|
6017
6037
|
"actor": "agent",
|
|
6018
6038
|
"source": "api"
|
|
@@ -6030,7 +6050,7 @@
|
|
|
6030
6050
|
"to": "group-status",
|
|
6031
6051
|
"type": "relation",
|
|
6032
6052
|
"style": "dashed",
|
|
6033
|
-
"revision":
|
|
6053
|
+
"revision": 98,
|
|
6034
6054
|
"changedBy": {
|
|
6035
6055
|
"actor": "agent",
|
|
6036
6056
|
"source": "api"
|
|
@@ -10,6 +10,7 @@ import { watch, existsSync, statSync } from 'node:fs';
|
|
|
10
10
|
import type { FSWatcher } from 'node:fs';
|
|
11
11
|
import { canvasState } from './canvas-state.js';
|
|
12
12
|
import { applyFileContentToNodeData, readFileNodeContent } from './file-content.js';
|
|
13
|
+
import { createBoardLocal } from './board-local.js';
|
|
13
14
|
|
|
14
15
|
function logFileWatcherWarning(action: string, error: unknown, details?: Record<string, unknown>): void {
|
|
15
16
|
console.warn(`[file-watcher] ${action}`, { error, ...(details ?? {}) });
|
|
@@ -22,7 +23,22 @@ interface WatchedFile {
|
|
|
22
23
|
lastMtime: number;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
const
|
|
26
|
+
const watcherSets = new Set<Map<string, WatchedFile>>();
|
|
27
|
+
function stopWatching(watched: Map<string, WatchedFile>): void {
|
|
28
|
+
for (const entry of watched.values()) entry.watcher.close();
|
|
29
|
+
watched.clear();
|
|
30
|
+
}
|
|
31
|
+
const boardWatchers = createBoardLocal(
|
|
32
|
+
() => {
|
|
33
|
+
const watched = new Map<string, WatchedFile>();
|
|
34
|
+
watcherSets.add(watched);
|
|
35
|
+
return watched;
|
|
36
|
+
},
|
|
37
|
+
(watched) => {
|
|
38
|
+
stopWatching(watched);
|
|
39
|
+
watcherSets.delete(watched);
|
|
40
|
+
},
|
|
41
|
+
);
|
|
26
42
|
let onFileChanged: ((nodeId: string) => void) | null = null;
|
|
27
43
|
|
|
28
44
|
/** Register a callback for when a watched file changes. */
|
|
@@ -34,6 +50,7 @@ export function onFileNodeChanged(cb: (nodeId: string) => void): void {
|
|
|
34
50
|
export function watchFileForNode(nodeId: string, filePath: string): void {
|
|
35
51
|
if (!filePath || !existsSync(filePath)) return;
|
|
36
52
|
|
|
53
|
+
const watched = boardWatchers.current;
|
|
37
54
|
const existing = watched.get(filePath);
|
|
38
55
|
if (existing) {
|
|
39
56
|
existing.nodeIds.add(nodeId);
|
|
@@ -42,10 +59,14 @@ export function watchFileForNode(nodeId: string, filePath: string): void {
|
|
|
42
59
|
|
|
43
60
|
try {
|
|
44
61
|
const stat = statSync(filePath);
|
|
45
|
-
const watcher = watch(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
62
|
+
const watcher = watch(
|
|
63
|
+
filePath,
|
|
64
|
+
{ persistent: false },
|
|
65
|
+
canvasState.bindBoard((eventType) => {
|
|
66
|
+
if (eventType !== 'change') return;
|
|
67
|
+
handleFileChange(filePath);
|
|
68
|
+
}),
|
|
69
|
+
);
|
|
49
70
|
|
|
50
71
|
watched.set(filePath, {
|
|
51
72
|
path: filePath,
|
|
@@ -60,6 +81,7 @@ export function watchFileForNode(nodeId: string, filePath: string): void {
|
|
|
60
81
|
|
|
61
82
|
/** Stop watching a file for a given node. */
|
|
62
83
|
export function unwatchFileForNode(nodeId: string, filePath?: string): void {
|
|
84
|
+
const watched = boardWatchers.current;
|
|
63
85
|
for (const [path, entry] of watched) {
|
|
64
86
|
if (filePath && path !== filePath) continue;
|
|
65
87
|
entry.nodeIds.delete(nodeId);
|
|
@@ -72,14 +94,11 @@ export function unwatchFileForNode(nodeId: string, filePath?: string): void {
|
|
|
72
94
|
|
|
73
95
|
/** Stop all watchers. */
|
|
74
96
|
export function unwatchAll(): void {
|
|
75
|
-
for (const
|
|
76
|
-
entry.watcher.close();
|
|
77
|
-
}
|
|
78
|
-
watched.clear();
|
|
97
|
+
for (const watched of watcherSets) stopWatching(watched);
|
|
79
98
|
}
|
|
80
99
|
|
|
81
100
|
export function rewatchAllFileNodes(): void {
|
|
82
|
-
|
|
101
|
+
stopWatching(boardWatchers.current);
|
|
83
102
|
for (const node of canvasState.getLayout().nodes) {
|
|
84
103
|
if (node.type === 'file' && typeof node.data.path === 'string') {
|
|
85
104
|
watchFileForNode(node.id, node.data.path);
|
|
@@ -88,6 +107,7 @@ export function rewatchAllFileNodes(): void {
|
|
|
88
107
|
}
|
|
89
108
|
|
|
90
109
|
function handleFileChange(filePath: string): void {
|
|
110
|
+
const watched = boardWatchers.current;
|
|
91
111
|
const entry = watched.get(filePath);
|
|
92
112
|
if (!entry) return;
|
|
93
113
|
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import {
|
|
3
3
|
HUMAN_GRAB_TTL_MS,
|
|
4
|
+
HUMAN_NAME_MAX_LENGTH,
|
|
4
5
|
HUMAN_PRESENCE_TTL_MS,
|
|
5
6
|
type HumanPresence,
|
|
6
7
|
type HumanPresenceSnapshot,
|
|
7
8
|
MAX_HUMANS,
|
|
8
9
|
} from '../shared/human-presence.js';
|
|
9
10
|
import { OperationError } from './operations/types.js';
|
|
11
|
+
import { canvasState } from './canvas-state.js';
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* Registry of open workbench tabs (rail-chrome-v2 phase 8). Fed by the
|
|
@@ -17,7 +19,7 @@ import { OperationError } from './operations/types.js';
|
|
|
17
19
|
*/
|
|
18
20
|
export const HUMAN_PRESENCE_SHAPE = {
|
|
19
21
|
clientId: z.string().min(1).max(64),
|
|
20
|
-
name: z.string().min(1).max(
|
|
22
|
+
name: z.string().min(1).max(HUMAN_NAME_MAX_LENGTH).optional(),
|
|
21
23
|
cursor: z.object({ x: z.number().finite(), y: z.number().finite() }).nullable().optional(),
|
|
22
24
|
grabbingNodeId: z.string().max(200).nullable().optional(),
|
|
23
25
|
/** Explicit leave (tab closing): drop the presence now. */
|
|
@@ -26,6 +28,7 @@ export const HUMAN_PRESENCE_SHAPE = {
|
|
|
26
28
|
const presenceSchema = z.object(HUMAN_PRESENCE_SHAPE);
|
|
27
29
|
|
|
28
30
|
interface Stored extends HumanPresence {
|
|
31
|
+
boardId: string | null;
|
|
29
32
|
lastSeenMs: number;
|
|
30
33
|
grabSinceMs: number | null;
|
|
31
34
|
}
|
|
@@ -57,6 +60,7 @@ export class HumanPresenceRegistry {
|
|
|
57
60
|
}
|
|
58
61
|
const existing = this.humans.get(input.clientId);
|
|
59
62
|
const stored: Stored = existing ?? {
|
|
63
|
+
boardId: canvasState.activeBoardId,
|
|
60
64
|
clientId: input.clientId,
|
|
61
65
|
name: input.name ?? 'Human',
|
|
62
66
|
cursor: null,
|
|
@@ -65,6 +69,11 @@ export class HumanPresenceRegistry {
|
|
|
65
69
|
lastSeenMs: now,
|
|
66
70
|
grabSinceMs: null,
|
|
67
71
|
};
|
|
72
|
+
if (stored.boardId !== canvasState.activeBoardId) {
|
|
73
|
+
stored.boardId = canvasState.activeBoardId;
|
|
74
|
+
stored.grabbingNodeId = null;
|
|
75
|
+
stored.grabSinceMs = null;
|
|
76
|
+
}
|
|
68
77
|
stored.lastSeenMs = now;
|
|
69
78
|
stored.lastSeenAt = new Date(now).toISOString();
|
|
70
79
|
if (input.name) stored.name = input.name;
|
|
@@ -91,9 +100,10 @@ export class HumanPresenceRegistry {
|
|
|
91
100
|
}
|
|
92
101
|
|
|
93
102
|
/** Nodes a human is holding right now → that human's name. The edit lock. */
|
|
94
|
-
lockedNodes(now = Date.now()): Map<string, string> {
|
|
103
|
+
lockedNodes(now = Date.now(), boardId = canvasState.activeBoardId): Map<string, string> {
|
|
95
104
|
const locked = new Map<string, string>();
|
|
96
105
|
for (const human of this.humans.values()) {
|
|
106
|
+
if (human.boardId !== boardId) continue;
|
|
97
107
|
if (human.grabbingNodeId && human.grabSinceMs !== null && now - human.grabSinceMs <= HUMAN_GRAB_TTL_MS) {
|
|
98
108
|
locked.set(human.grabbingNodeId, human.name);
|
|
99
109
|
}
|
|
@@ -111,13 +121,16 @@ export class HumanPresenceRegistry {
|
|
|
111
121
|
}
|
|
112
122
|
|
|
113
123
|
private publicView(stored: Stored): HumanPresence {
|
|
114
|
-
const { lastSeenMs: _lastSeenMs, grabSinceMs, ...human } = stored;
|
|
124
|
+
const { lastSeenMs: _lastSeenMs, grabSinceMs, boardId, ...human } = stored;
|
|
115
125
|
// An expired grab reads as released even before the sweep drops it.
|
|
116
126
|
const grabbing =
|
|
117
|
-
|
|
127
|
+
boardId === canvasState.activeBoardId &&
|
|
128
|
+
human.grabbingNodeId &&
|
|
129
|
+
grabSinceMs !== null &&
|
|
130
|
+
Date.now() - grabSinceMs <= HUMAN_GRAB_TTL_MS
|
|
118
131
|
? human.grabbingNodeId
|
|
119
132
|
: null;
|
|
120
|
-
return { ...human, grabbingNodeId: grabbing };
|
|
133
|
+
return { ...human, cursor: boardId === canvasState.activeBoardId ? human.cursor : null, grabbingNodeId: grabbing };
|
|
121
134
|
}
|
|
122
135
|
|
|
123
136
|
private sweep(now = Date.now(), options: { emit?: boolean } = {}): void {
|