pmx-canvas 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/extensions/pmx-canvas/extension.mjs +3 -1
- package/.github/extensions/pmx-canvas/runtime.json +1 -1
- package/.github/extensions/pmx-canvas/tools.json +688 -128
- package/CHANGELOG.md +69 -0
- package/dist/amp-plugin/pmx-canvas/LICENSE +21 -0
- package/dist/amp-plugin/pmx-canvas/THIRD_PARTY_LICENSES.txt +331 -0
- package/dist/amp-plugin/pmx-canvas/index.js +5445 -0
- package/dist/amp-plugin/pmx-canvas/runtime.json +3 -0
- package/dist/amp-plugin/pmx-canvas/shared-runtime.mjs +194 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/SKILL.md +251 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/evals.json +1086 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/fixtures/code-exploration/src/auth/jwt.ts +17 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/fixtures/code-exploration/src/auth/login.ts +12 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/fixtures/code-exploration/src/auth/middleware.ts +13 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/fixtures/code-exploration/src/routes/auth.ts +13 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/fixtures/investigation-board/src/handlers/users.ts +27 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/mcp.json +36 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/ax-html-control-surface.md +101 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/codex-app-adapter.md +151 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/excalidraw-diagram-authoring.md +150 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/full-reference.md +1785 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/github-copilot-app-adapter.md +223 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/html-primitives.md +139 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/importing-context.md +132 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/installing-pmx-canvas.md +251 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/tours-and-recording.md +111 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/workbench-workflows.md +900 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas-orchestration/SKILL.md +141 -0
- package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas-testing/SKILL.md +266 -0
- package/dist/amp-plugin/pmx-canvas/start-mcp.mjs +14857 -0
- package/dist/amp-plugin/pmx-canvas/tools.json +10963 -0
- package/dist/canvas/chunks/abnfDiagram-N423BO3Z-cwtsds5d.js +1 -0
- package/dist/canvas/chunks/architecture-TIHT7OUA-x63cwejs.js +1 -0
- package/dist/canvas/chunks/architectureDiagram-T3A2C74G-b4xc9tpz.js +36 -0
- package/dist/canvas/chunks/blockDiagram-VBNYF7ZC-ahp3yxxs.js +132 -0
- package/dist/canvas/chunks/c4Diagram-5PPSVZJV-nappnay7.js +10 -0
- 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-a428syda.js +1 -0
- package/dist/canvas/chunks/cynefin-VYW2F7L2-8v6jxfz8.js +1 -0
- package/dist/canvas/chunks/cynefinDiagram-MW4NZA55-nspsqebj.js +62 -0
- package/dist/canvas/chunks/dagre-VZM6K2ZE-8y6177fh.js +4 -0
- package/dist/canvas/chunks/diagram-7IWD3JNH-vym028wn.js +30 -0
- package/dist/canvas/chunks/diagram-B4RE2ZJO-jddqb8sy.js +3 -0
- package/dist/canvas/chunks/diagram-LBJQPF4R-bjwbpkrn.js +24 -0
- package/dist/canvas/chunks/diagram-Q27KOJAE-j6ph6hcx.js +24 -0
- package/dist/canvas/chunks/diagram-UB23O5K3-p09w8jej.js +41 -0
- package/dist/canvas/chunks/ebnfDiagram-BXEA7PRR-j975ycq5.js +1 -0
- package/dist/canvas/chunks/erDiagram-JOGREHBK-sxp6hjn8.js +85 -0
- 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-4tv230y2.js +292 -0
- package/dist/canvas/chunks/gitGraph-TEB2WS4Q-macbhand.js +1 -0
- package/dist/canvas/chunks/gitGraphDiagram-DS77QQ5N-mqgmnw9g.js +106 -0
- package/dist/canvas/chunks/index-037z5dz2.js +232 -0
- package/dist/canvas/chunks/index-081n3har.js +207 -0
- package/dist/canvas/chunks/index-5n03dmb9.js +2 -0
- package/dist/canvas/chunks/index-5tv8v1hk.js +1 -0
- package/dist/canvas/chunks/index-60pgzdqe.js +157 -0
- package/dist/canvas/chunks/index-6ceya556.js +332 -0
- package/dist/canvas/chunks/index-8g1vb57n.js +2 -0
- package/dist/canvas/chunks/index-8rjes9r6.js +2 -0
- package/dist/canvas/chunks/index-9022d1n9.js +16 -0
- package/dist/canvas/chunks/index-9b02042t.js +2 -0
- package/dist/canvas/chunks/index-ap4q4vez.js +2 -0
- package/dist/canvas/chunks/index-dyp2nhd3.js +2 -0
- package/dist/canvas/chunks/index-frvta4tm.js +2 -0
- package/dist/canvas/chunks/index-gf4e0et4.js +2 -0
- package/dist/canvas/chunks/index-h9ycj8n2.js +2 -0
- package/dist/canvas/chunks/index-hj99nz1f.js +1 -0
- package/dist/canvas/chunks/index-mergmj97.js +2 -0
- package/dist/canvas/chunks/index-nwsya6dj.js +301 -0
- package/dist/canvas/chunks/index-nz8qbd1p.js +2 -0
- package/dist/canvas/chunks/index-qhfrazv3.js +2 -0
- package/dist/canvas/chunks/index-v119nznk.js +4 -0
- package/dist/canvas/chunks/index-vpt37m3d.js +89 -0
- package/dist/canvas/chunks/index-y9ma6azy.js +2 -0
- package/dist/canvas/chunks/index-zx0j3a2z.js +179 -0
- package/dist/canvas/chunks/info-DKCQHKI2-a0ykzs9z.js +1 -0
- package/dist/canvas/chunks/infoDiagram-6WML65LV-qv97qq6a.js +2 -0
- package/dist/canvas/chunks/ishikawaDiagram-WSZJBQD7-p9306gh0.js +70 -0
- package/dist/canvas/chunks/journeyDiagram-NVQOT4AX-sv67zats.js +139 -0
- package/dist/canvas/chunks/kanban-definition-27J2QSJJ-k2pfam6p.js +89 -0
- package/dist/canvas/chunks/katex-dmg8k4zm.js +257 -0
- package/dist/canvas/chunks/mermaid.core-83r0fdce.js +12 -0
- package/dist/canvas/chunks/mindmap-definition-FAOFIHXS-vg2newm1.js +96 -0
- package/dist/canvas/chunks/packet-7NZHBO7P-tqnabcdv.js +1 -0
- package/dist/canvas/chunks/pegDiagram-VL7TDLO6-k2h7zc5n.js +1 -0
- package/dist/canvas/chunks/pie-RZYD4A2V-wke6gyhe.js +1 -0
- package/dist/canvas/chunks/pieDiagram-7S7Q4E2Y-hg169p1v.js +39 -0
- package/dist/canvas/chunks/quadrantDiagram-CIZ2JOQS-bxtvsq0b.js +7 -0
- 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-njyhcp4p.js +1 -0
- package/dist/canvas/chunks/requirementDiagram-LRYGKXZP-2z6v09gm.js +84 -0
- package/dist/canvas/chunks/sankeyDiagram-W5VNT64P-15mzam0e.js +40 -0
- package/dist/canvas/chunks/sequenceDiagram-SI44F4Z6-rbshyd2c.js +162 -0
- package/dist/canvas/chunks/sizeCapture-X5ZJPWSS-sg26xwts.js +1 -0
- package/dist/canvas/chunks/stateDiagram-OKZ733FA-6eahp98e.js +1 -0
- package/dist/canvas/chunks/stateDiagram-v2-UEYNNEHI-tty0ggn5.js +1 -0
- package/dist/canvas/chunks/swimlanes-SLNWSIFB-ze5db10t.js +1 -0
- package/dist/canvas/chunks/swimlanesDiagram-ULZ7WXOC-thze75gq.js +8 -0
- package/dist/canvas/chunks/timeline-definition-Z64GVDOM-vnp7jyy8.js +120 -0
- 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-8nxg8wqt.js +78 -0
- package/dist/canvas/chunks/xychartDiagram-ELKLHX3M-nj921e7s.js +7 -0
- package/dist/canvas/global.css +483 -230
- package/dist/canvas/index.js +158 -219
- package/dist/canvas/mcp-app.html +4301 -548
- package/dist/canvas/mermaid-entry.js +338 -338
- package/dist/canvas/surface-theme.css +6 -6
- package/dist/json-render/index.css +1 -1
- package/dist/json-render/index.js +49 -49
- package/dist/types/client/canvas/BarHint.d.ts +5 -3
- package/dist/types/client/canvas/BoardMoveDialog.d.ts +2 -0
- package/dist/types/client/canvas/CanvasNode.d.ts +0 -11
- package/dist/types/client/canvas/EdgeEditor.d.ts +19 -0
- package/dist/types/client/canvas/EdgeLayer.d.ts +1 -2
- package/dist/types/client/canvas/GroupNavigation.d.ts +2 -0
- package/dist/types/client/canvas/TextPrompt.d.ts +4 -0
- package/dist/types/client/canvas/auto-fit.d.ts +4 -5
- package/dist/types/client/mermaid-entry.d.ts +1 -2
- package/dist/types/client/mermaid-render.d.ts +1 -0
- package/dist/types/client/nodes/BoardNode.d.ts +1 -1
- package/dist/types/client/nodes/ExtAppFrame.d.ts +7 -90
- package/dist/types/client/nodes/McpAppNode.d.ts +1 -25
- package/dist/types/client/nodes/MermaidNode.d.ts +1 -8
- package/dist/types/client/nodes/PromptNode.d.ts +1 -0
- package/dist/types/client/nodes/ViewerFrame.d.ts +29 -0
- package/dist/types/client/nodes/ViewerHost.d.ts +6 -0
- package/dist/types/client/nodes/WorkCard.d.ts +7 -0
- package/dist/types/client/nodes/iframe-document-url.d.ts +3 -9
- package/dist/types/client/nodes/primitive-copy.d.ts +5 -0
- package/dist/types/client/nodes/use-ax-surface-bridge.d.ts +1 -0
- package/dist/types/client/nodes/use-iframe-content-height.d.ts +1 -1
- package/dist/types/client/nodes/use-surface-frame.d.ts +4 -6
- package/dist/types/client/nodes/viewer-runtime-state.d.ts +2 -1
- package/dist/types/client/state/canvas-store.d.ts +18 -1
- package/dist/types/client/state/human-store.d.ts +2 -0
- package/dist/types/client/state/intent-bridge.d.ts +5 -2
- package/dist/types/client/state/presentation.d.ts +17 -6
- 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/json-render/server.d.ts +2 -1
- 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 +86 -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 +26 -10
- 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/ops/boards.d.ts +0 -1
- package/dist/types/server/operations/registry.d.ts +2 -0
- package/dist/types/server/tour-control.d.ts +13 -3
- 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 -9
- package/dist/types/shared/human-presence.d.ts +1 -0
- package/dist/types/shared/lifecycle-status.d.ts +2 -0
- package/dist/types/shared/tour.d.ts +8 -0
- package/docs/RELEASE.md +21 -14
- package/docs/agent-plugin.md +16 -2
- package/docs/amp-plugin.md +271 -0
- package/docs/cli.md +42 -3
- package/docs/design/Chrome.dc.html +32 -6
- package/docs/design/GuestName.dc.html +50 -0
- package/docs/design/PluginLogo.svg +7 -0
- package/docs/design/Types1.dc.html +3 -2
- package/docs/design/Types2.dc.html +1 -1
- package/docs/design/Types3.dc.html +1 -1
- package/docs/design/WorkCards.dc.html +39 -0
- package/docs/design/Zoom.dc.html +37 -60
- package/docs/design/canvas.json +17 -1
- package/docs/design/plugin-logo.png +0 -0
- package/docs/design.md +280 -12
- package/docs/evals/agents-md-sections.md +38 -0
- package/docs/http-api.md +179 -11
- package/docs/mcp.md +69 -9
- package/docs/node-types.md +109 -4
- package/docs/plans/plan-009-tech-debt-backlog.md +3 -3
- package/docs/plans/plan-019-post-0.9-roadmap.md +327 -31
- package/docs/plans/plan-020-wave-5-work-planning.md +553 -0
- package/docs/product-vision-2026-09.md +42 -17
- package/docs/sdk.md +18 -0
- package/package.json +7 -4
- package/plugins/pmx-canvas/assets/preview.png +0 -0
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/extension.mjs +3 -1
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.json +1 -1
- package/plugins/pmx-canvas/plugin.json +2 -2
- package/plugins/pmx-canvas/runtime.json +1 -1
- package/plugins/pmx-canvas/tools.json +688 -128
- package/skills/data-analysis/SKILL.md +13 -26
- package/skills/frontend-design/LICENSE.txt +177 -0
- package/skills/frontend-design/SKILL.md +3 -0
- package/skills/pmx-canvas/SKILL.md +232 -849
- package/skills/pmx-canvas/evals/evals.json +26 -3
- package/skills/pmx-canvas/references/full-reference.md +89 -24
- package/skills/pmx-canvas/references/html-primitives.md +7 -0
- package/skills/pmx-canvas/references/installing-pmx-canvas.md +6 -6
- package/skills/pmx-canvas/references/tours-and-recording.md +29 -2
- package/skills/pmx-canvas/references/workbench-workflows.md +900 -0
- package/skills/pmx-canvas-testing/SKILL.md +7 -5
- package/skills/published-consumer-e2e/SKILL.md +25 -8
- package/skills/published-consumer-e2e/scripts/run-published-consumer-e2e.sh +51 -77
- package/skills/web-artifacts-builder/LICENSE.txt +202 -0
- package/skills/web-artifacts-builder/SKILL.md +6 -0
- package/skills/web-artifacts-builder/scripts/init-artifact.sh +2 -2
- package/src/cli/commands/apps.ts +1 -0
- package/src/cli/commands/boards.ts +30 -0
- package/src/client/App.tsx +20 -4
- package/src/client/canvas/AttentionHistory.tsx +10 -2
- package/src/client/canvas/BarHint.tsx +6 -3
- package/src/client/canvas/BoardMoveDialog.tsx +225 -0
- package/src/client/canvas/CanvasNode.tsx +79 -49
- package/src/client/canvas/CanvasViewport.tsx +42 -14
- package/src/client/canvas/ContextMenu.tsx +97 -15
- package/src/client/canvas/EdgeEditor.tsx +149 -0
- package/src/client/canvas/EdgeLayer.tsx +441 -174
- package/src/client/canvas/ExpandedNodeOverlay.tsx +5 -2
- package/src/client/canvas/GroupNavigation.tsx +85 -0
- package/src/client/canvas/Presentation.tsx +53 -3
- 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 +54 -9
- package/src/client/canvas/auto-fit.ts +9 -11
- package/src/client/canvas/canvas-area.ts +2 -2
- package/src/client/canvas/use-focus-trap.ts +6 -1
- package/src/client/mermaid-entry.ts +6 -20
- package/src/client/mermaid-render.ts +39 -0
- package/src/client/nodes/AxStepControls.tsx +45 -69
- package/src/client/nodes/BoardNode.tsx +65 -9
- package/src/client/nodes/ContextNode.tsx +88 -396
- package/src/client/nodes/ExtAppFrame.tsx +302 -1465
- package/src/client/nodes/FileNode.tsx +92 -81
- package/src/client/nodes/HtmlNode.tsx +8 -36
- package/src/client/nodes/LedgerNode.tsx +31 -17
- package/src/client/nodes/MarkdownNode.tsx +14 -24
- package/src/client/nodes/McpAppNode.tsx +48 -154
- package/src/client/nodes/MermaidNode.tsx +80 -69
- package/src/client/nodes/PromptNode.tsx +44 -17
- package/src/client/nodes/ResponseNode.tsx +8 -6
- package/src/client/nodes/StatusNode.tsx +34 -7
- package/src/client/nodes/StatusSummary.tsx +16 -3
- package/src/client/nodes/TraceNode.tsx +8 -8
- package/src/client/nodes/ViewerFrame.tsx +273 -0
- package/src/client/nodes/ViewerHost.tsx +41 -0
- package/src/client/nodes/WebpageNode.tsx +22 -24
- package/src/client/nodes/WorkCard.tsx +175 -0
- package/src/client/nodes/iframe-document-url.ts +20 -27
- package/src/client/nodes/primitive-copy.ts +97 -0
- package/src/client/nodes/use-ax-surface-bridge.ts +18 -14
- package/src/client/nodes/use-iframe-content-height.ts +34 -10
- package/src/client/nodes/use-surface-frame.ts +22 -11
- package/src/client/nodes/viewer-runtime-state.ts +41 -35
- 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 +202 -26
- package/src/client/state/human-store.ts +22 -2
- package/src/client/state/intent-bridge.ts +20 -5
- package/src/client/state/presentation.ts +135 -15
- package/src/client/state/relation-store.ts +127 -1
- package/src/client/state/sse-bridge.ts +18 -19
- package/src/client/theme/global.css +483 -230
- package/src/client/theme/surface-theme.css +6 -6
- package/src/client/types.ts +1 -0
- package/src/json-render/charts/tufte-components.tsx +44 -2
- package/src/json-render/renderer/index.css +62 -2
- package/src/json-render/renderer/index.tsx +31 -7
- package/src/json-render/server.ts +3 -2
- 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 +54 -30
- package/src/server/board-local.ts +20 -0
- package/src/server/board-move.ts +297 -0
- package/src/server/canvas-db.ts +249 -158
- package/src/server/canvas-geometry.ts +1 -0
- 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 +797 -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 +140 -114
- package/src/server/file-watcher.ts +30 -10
- package/src/server/html-primitives.ts +3 -9
- package/src/server/html-surface.ts +8 -0
- package/src/server/human-presence.ts +18 -5
- package/src/server/index.ts +54 -24
- 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 +32 -2
- 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 +45 -6
- 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/ops/viewport.ts +11 -9
- package/src/server/operations/registry.ts +49 -4
- package/src/server/server.ts +69 -75
- package/src/server/tour-control.ts +97 -12
- package/src/server/trace-manager.ts +4 -0
- package/src/server/web-artifacts/scripts/init-artifact.sh +2 -2
- package/src/shared/agent-context.ts +7 -1
- package/src/shared/auto-arrange.ts +5 -4
- package/src/shared/ax-kinds.ts +13 -0
- package/src/shared/ax-surface-protocol.ts +20 -9
- package/src/shared/content-height-reporter.ts +4 -3
- package/src/shared/human-presence.ts +1 -0
- package/src/shared/lifecycle-status.ts +2 -0
- package/src/shared/tour.ts +8 -0
|
@@ -21,12 +21,12 @@
|
|
|
21
21
|
/* ── Core palette (dark · ink + cyan) ────────────────────── */
|
|
22
22
|
--c-bg: #081524;
|
|
23
23
|
--c-panel: #0f1d31;
|
|
24
|
-
--c-panel-soft: #
|
|
24
|
+
--c-panel-soft: #13243b;
|
|
25
25
|
--c-line: #1b2c44;
|
|
26
26
|
--c-text: #e6eef7;
|
|
27
|
-
--c-text-soft: #
|
|
27
|
+
--c-text-soft: #b9c6d6;
|
|
28
28
|
--c-muted: #8ea3bd;
|
|
29
|
-
--c-dim: #
|
|
29
|
+
--c-dim: #6b7d94;
|
|
30
30
|
--c-accent: #4BBCFF;
|
|
31
31
|
--c-ok: #2fd07f;
|
|
32
32
|
--c-warn: #f4c542;
|
|
@@ -46,13 +46,13 @@
|
|
|
46
46
|
/* Common aliases authored HTML might use. */
|
|
47
47
|
--color-bg: #081524;
|
|
48
48
|
--color-panel: #0f1d31;
|
|
49
|
-
--color-surface: #
|
|
49
|
+
--color-surface: #13243b;
|
|
50
50
|
--color-border: #1b2c44;
|
|
51
51
|
--color-text: #e6eef7;
|
|
52
52
|
--color-text-primary: #e6eef7;
|
|
53
|
-
--color-text-secondary: #
|
|
53
|
+
--color-text-secondary: #b9c6d6;
|
|
54
54
|
--color-text-muted: #8ea3bd;
|
|
55
|
-
--color-text-dim: #
|
|
55
|
+
--color-text-dim: #6b7d94;
|
|
56
56
|
--color-accent: #4BBCFF;
|
|
57
57
|
--color-success: #2fd07f;
|
|
58
58
|
--color-warning: #f4c542;
|
package/src/client/types.ts
CHANGED
|
@@ -115,6 +115,7 @@ export const TYPE_LABELS: Record<CanvasNodeState['type'], string> = {
|
|
|
115
115
|
/** Node types that support the full-viewport expand/focus overlay. */
|
|
116
116
|
export const EXPANDABLE_TYPES = new Set<CanvasNodeState['type']>([
|
|
117
117
|
'markdown',
|
|
118
|
+
'status',
|
|
118
119
|
'mcp-app',
|
|
119
120
|
'webpage',
|
|
120
121
|
'json-render',
|
|
@@ -336,6 +336,18 @@ interface SlopegraphProps {
|
|
|
336
336
|
height?: number | null;
|
|
337
337
|
}
|
|
338
338
|
|
|
339
|
+
/** Separate close labels without moving the data points they describe. */
|
|
340
|
+
function slopeLabelPositions(values: number[], top: number, bottom: number): number[] {
|
|
341
|
+
const order = values.map((y, index) => ({ y, index })).sort((a, b) => a.y - b.y);
|
|
342
|
+
const gap = Math.min(14, (bottom - top) / Math.max(1, order.length - 1));
|
|
343
|
+
for (let i = 1; i < order.length; i++) order[i].y = Math.max(order[i].y, order[i - 1].y + gap);
|
|
344
|
+
if (order.length) order[order.length - 1].y = Math.min(order[order.length - 1].y, bottom);
|
|
345
|
+
for (let i = order.length - 2; i >= 0; i--) order[i].y = Math.min(order[i].y, order[i + 1].y - gap);
|
|
346
|
+
const result = [...values];
|
|
347
|
+
for (const { y, index } of order) result[index] = y;
|
|
348
|
+
return result;
|
|
349
|
+
}
|
|
350
|
+
|
|
339
351
|
function ChartSlopegraph({ props }: BaseComponentProps<SlopegraphProps>) {
|
|
340
352
|
const stroke = props.color ?? ACCENT;
|
|
341
353
|
const rows = (props.data ?? []).map((row) => ({
|
|
@@ -359,6 +371,16 @@ function ChartSlopegraph({ props }: BaseComponentProps<SlopegraphProps>) {
|
|
|
359
371
|
// fills without pushing a scrollbar onto the iframe document.
|
|
360
372
|
const plotH = chartPlotHeight(height, Boolean(props.title));
|
|
361
373
|
const scaleY = (v: number) => topPad + (1 - (v - min) / (max - min)) * (plotH - topPad - botPad);
|
|
374
|
+
const beforeLabels = slopeLabelPositions(
|
|
375
|
+
rows.map((row) => scaleY(row.before)),
|
|
376
|
+
topPad,
|
|
377
|
+
plotH - botPad,
|
|
378
|
+
);
|
|
379
|
+
const afterLabels = slopeLabelPositions(
|
|
380
|
+
rows.map((row) => scaleY(row.after)),
|
|
381
|
+
topPad,
|
|
382
|
+
plotH - botPad,
|
|
383
|
+
);
|
|
362
384
|
|
|
363
385
|
return (
|
|
364
386
|
<div ref={frameRef} className="pmx-chart pmx-chart--slopegraph" style={{ height }}>
|
|
@@ -390,10 +412,30 @@ function ChartSlopegraph({ props }: BaseComponentProps<SlopegraphProps>) {
|
|
|
390
412
|
<line x1={leftX} y1={y1} x2={rightX} y2={y2} stroke={lineColor} strokeWidth={1.5} />
|
|
391
413
|
<circle cx={leftX} cy={y1} r={2.5} fill={lineColor} />
|
|
392
414
|
<circle cx={rightX} cy={y2} r={2.5} fill={lineColor} />
|
|
393
|
-
|
|
415
|
+
{beforeLabels[i] !== y1 && (
|
|
416
|
+
<line x1={leftX} y1={y1} x2={leftX - 5} y2={beforeLabels[i]} stroke={lineColor} />
|
|
417
|
+
)}
|
|
418
|
+
{afterLabels[i] !== y2 && (
|
|
419
|
+
<line x1={rightX} y1={y2} x2={rightX + 5} y2={afterLabels[i]} stroke={lineColor} />
|
|
420
|
+
)}
|
|
421
|
+
<text
|
|
422
|
+
x={leftX - 8}
|
|
423
|
+
y={beforeLabels[i]}
|
|
424
|
+
textAnchor="end"
|
|
425
|
+
dominantBaseline="central"
|
|
426
|
+
fontSize={11}
|
|
427
|
+
fill={INK}
|
|
428
|
+
>
|
|
394
429
|
{`${row.label} ${row.before}`}
|
|
395
430
|
</text>
|
|
396
|
-
<text
|
|
431
|
+
<text
|
|
432
|
+
x={rightX + 8}
|
|
433
|
+
y={afterLabels[i]}
|
|
434
|
+
textAnchor="start"
|
|
435
|
+
dominantBaseline="central"
|
|
436
|
+
fontSize={11}
|
|
437
|
+
fill={INK}
|
|
438
|
+
>
|
|
397
439
|
{`${row.after} ${row.label}`}
|
|
398
440
|
</text>
|
|
399
441
|
</g>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
@source "../../../../node_modules/@json-render/shadcn/dist/index.js";
|
|
5
5
|
|
|
6
|
-
@custom-variant dark (&:is(
|
|
6
|
+
@custom-variant dark (&:is(.dark *));
|
|
7
7
|
|
|
8
8
|
@theme {
|
|
9
9
|
--font-sans: "IBM Plex Sans", system-ui, sans-serif;
|
|
@@ -99,6 +99,66 @@
|
|
|
99
99
|
--chart-6: #ffffff;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
/* Named themes share the canvas surface, including nested cards and charts.
|
|
103
|
+
Explicit dark/light remain available for authored design experiments. */
|
|
104
|
+
:root[data-canvas-theme] {
|
|
105
|
+
--radius: 6px;
|
|
106
|
+
--text-xs: 11px;
|
|
107
|
+
--text-sm: 12px;
|
|
108
|
+
--text-base: 13px;
|
|
109
|
+
--text-lg: 15px;
|
|
110
|
+
--text-xl: 20px;
|
|
111
|
+
--background: var(--c-panel);
|
|
112
|
+
--foreground: var(--c-text);
|
|
113
|
+
--card: var(--c-panel-soft);
|
|
114
|
+
--card-foreground: var(--c-text);
|
|
115
|
+
--popover: var(--c-panel);
|
|
116
|
+
--popover-foreground: var(--c-text);
|
|
117
|
+
--primary: var(--c-accent);
|
|
118
|
+
--primary-foreground: var(--c-on-accent, var(--c-on-pin));
|
|
119
|
+
--secondary: var(--c-panel-soft);
|
|
120
|
+
--secondary-foreground: var(--c-text);
|
|
121
|
+
--muted: var(--c-panel-soft);
|
|
122
|
+
--muted-foreground: var(--c-muted);
|
|
123
|
+
--accent: var(--c-panel-soft);
|
|
124
|
+
--accent-foreground: var(--c-text);
|
|
125
|
+
--destructive: var(--c-danger);
|
|
126
|
+
--border: var(--c-line);
|
|
127
|
+
--input: var(--c-line);
|
|
128
|
+
--ring: var(--c-accent);
|
|
129
|
+
--chart-1: var(--c-accent);
|
|
130
|
+
--chart-2: var(--c-rel-supports);
|
|
131
|
+
--chart-3: var(--c-rel-contradicts);
|
|
132
|
+
--chart-4: var(--c-rel-cites);
|
|
133
|
+
--chart-5: var(--c-rel-informs);
|
|
134
|
+
--chart-6: var(--c-muted);
|
|
135
|
+
font-size: 16px;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* surface-theme's unlayered body rule otherwise wins over Tailwind's base layer. */
|
|
139
|
+
body {
|
|
140
|
+
background: var(--background);
|
|
141
|
+
color: var(--foreground);
|
|
142
|
+
padding: 0;
|
|
143
|
+
font-family: var(--font-sans);
|
|
144
|
+
font-size: 13px;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.pmx-viewer-root { width: 100%; min-width: 0; }
|
|
148
|
+
[data-embedded] .pmx-viewer-root:not(:has(> [data-slot="card"])) { padding: 12px 14px; }
|
|
149
|
+
[data-embedded] .pmx-viewer-root > [data-slot="card"] {
|
|
150
|
+
width: 100%;
|
|
151
|
+
max-width: none;
|
|
152
|
+
border: 0;
|
|
153
|
+
border-radius: 0;
|
|
154
|
+
box-shadow: none;
|
|
155
|
+
background: transparent;
|
|
156
|
+
padding: 12px 14px;
|
|
157
|
+
}
|
|
158
|
+
[data-embedded][data-duplicate-title] .pmx-viewer-root > [data-slot="card"] > [data-slot="card-header"] > [data-slot="card-title"] { display: none; }
|
|
159
|
+
[data-embedded][data-duplicate-title] .pmx-viewer-root > [data-slot="card"] > [data-slot="card-header"]:not(:has([data-slot="card-description"])) { display: none; }
|
|
160
|
+
[data-embedded] .pmx-viewer-root > [data-slot="card"] > [data-slot="card-content"] { padding: 0; }
|
|
161
|
+
|
|
102
162
|
@theme inline {
|
|
103
163
|
--radius-sm: calc(var(--radius) - 4px);
|
|
104
164
|
--radius-md: calc(var(--radius) - 2px);
|
|
@@ -159,7 +219,7 @@ button {
|
|
|
159
219
|
border: 1px solid transparent;
|
|
160
220
|
border-radius: 9999px;
|
|
161
221
|
padding: 0.125rem 0.5rem;
|
|
162
|
-
font-size: 0.75rem;
|
|
222
|
+
font-size: max(11px, 0.75rem);
|
|
163
223
|
font-weight: 500;
|
|
164
224
|
line-height: 1.25rem;
|
|
165
225
|
}
|
|
@@ -23,6 +23,7 @@ import { extraChartComponents } from '../charts/extra-components';
|
|
|
23
23
|
import { tufteChartComponents } from '../charts/tufte-components';
|
|
24
24
|
import { pmxCanvasDirectives } from '../directives';
|
|
25
25
|
import { JsonRenderDevtools } from '@json-render/devtools-react';
|
|
26
|
+
import { canvasThemeScheme, isCanvasTheme } from '../../shared/themes.js';
|
|
26
27
|
|
|
27
28
|
type BadgeVariant =
|
|
28
29
|
| 'default'
|
|
@@ -167,10 +168,12 @@ function syncPreferredTheme(): void {
|
|
|
167
168
|
}
|
|
168
169
|
|
|
169
170
|
function applyTheme(theme: unknown): void {
|
|
170
|
-
if (theme !== 'dark' && theme !== 'light' && theme
|
|
171
|
+
if (theme !== 'dark' && theme !== 'light' && !isCanvasTheme(theme)) return;
|
|
171
172
|
document.documentElement.setAttribute('data-theme', theme);
|
|
172
|
-
document.documentElement.
|
|
173
|
-
|
|
173
|
+
document.documentElement.toggleAttribute('data-canvas-theme', isCanvasTheme(theme));
|
|
174
|
+
const scheme = theme === 'light' ? 'light' : canvasThemeScheme(theme);
|
|
175
|
+
document.documentElement.classList.toggle('dark', scheme === 'dark');
|
|
176
|
+
document.documentElement.style.colorScheme = scheme;
|
|
174
177
|
}
|
|
175
178
|
|
|
176
179
|
function App({ store }: { store: StateStore }) {
|
|
@@ -235,11 +238,15 @@ function App({ store }: { store: StateStore }) {
|
|
|
235
238
|
padding: 0,
|
|
236
239
|
boxSizing: 'border-box' as const,
|
|
237
240
|
}
|
|
238
|
-
: { minHeight: '100vh', padding:
|
|
241
|
+
: { minHeight: '100vh', padding: 0, boxSizing: 'border-box' as const };
|
|
239
242
|
return (
|
|
240
|
-
<div
|
|
243
|
+
<div
|
|
244
|
+
style={containerStyle}
|
|
245
|
+
data-embedded={!isSite ? '' : undefined}
|
|
246
|
+
data-duplicate-title={spec.elements[spec.root]?.props?.title === document.title ? '' : undefined}
|
|
247
|
+
>
|
|
241
248
|
<JSONUIProvider registry={registry} store={store} directives={pmxCanvasDirectives} handlers={buildAxHandlers()}>
|
|
242
|
-
<div style={isSite ? { flex: 1, minHeight: 0 } : undefined}>
|
|
249
|
+
<div className="pmx-viewer-root" style={isSite ? { flex: 1, minHeight: 0 } : undefined}>
|
|
243
250
|
<Renderer spec={spec} registry={registry} loading={false} />
|
|
244
251
|
</div>
|
|
245
252
|
{window.__PMX_CANVAS_JSON_RENDER_DEVTOOLS__ ? <JsonRenderDevtools position="right" /> : null}
|
|
@@ -282,8 +289,25 @@ if (root) {
|
|
|
282
289
|
// Standalone viewers have no state token and render immediately.
|
|
283
290
|
window.addEventListener('message', (event: MessageEvent) => {
|
|
284
291
|
if (event.source !== window.parent) return;
|
|
285
|
-
const m = event.data as {
|
|
292
|
+
const m = event.data as {
|
|
293
|
+
source?: string;
|
|
294
|
+
type?: string;
|
|
295
|
+
token?: string;
|
|
296
|
+
state?: unknown;
|
|
297
|
+
theme?: unknown;
|
|
298
|
+
title?: unknown;
|
|
299
|
+
} | null;
|
|
286
300
|
if (!m) return;
|
|
301
|
+
if (m.source === HTML_SURFACE_PUSH_SOURCE && m.type === 'theme-update' && stateToken && m.token === stateToken) {
|
|
302
|
+
applyTheme(m.theme);
|
|
303
|
+
if (typeof m.title === 'string') {
|
|
304
|
+
document.title = m.title;
|
|
305
|
+
const spec = window.__PMX_CANVAS_JSON_RENDER_SPEC__;
|
|
306
|
+
document
|
|
307
|
+
.querySelector('[data-embedded]')
|
|
308
|
+
?.toggleAttribute('data-duplicate-title', spec?.elements[spec.root]?.props?.title === m.title);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
287
311
|
if (m.source === JSON_VIEWER_STATE_SOURCE && m.type === 'restore' && stateToken && m.token === stateToken) {
|
|
288
312
|
if (m.state === null || (typeof m.state === 'object' && !Array.isArray(m.state)))
|
|
289
313
|
mount(m.state as Record<string, unknown> | null);
|
|
@@ -6,6 +6,7 @@ import { applySpecPatch, parseSpecStreamLine, type Spec, type SpecStreamLine } f
|
|
|
6
6
|
import { allComponentDefinitions, catalog, validateShadcnElementProps, type JsonRenderIssue } from './catalog.js';
|
|
7
7
|
import { findUnknownDirectiveKey, isDynamicPropValue } from './directives.js';
|
|
8
8
|
import { contentHeightReporterTag } from '../shared/content-height-reporter.js';
|
|
9
|
+
import type { CanvasThemeName } from '../shared/themes.js';
|
|
9
10
|
|
|
10
11
|
export interface JsonRenderSpec {
|
|
11
12
|
root: string;
|
|
@@ -1030,7 +1031,7 @@ export async function readJsonRenderBundle(): Promise<{ js: string; css: string
|
|
|
1030
1031
|
export async function buildJsonRenderViewerHtml(options: {
|
|
1031
1032
|
title: string;
|
|
1032
1033
|
spec: JsonRenderSpec;
|
|
1033
|
-
theme?:
|
|
1034
|
+
theme?: CanvasThemeName | 'dark' | 'light';
|
|
1034
1035
|
display?: 'expanded' | 'site';
|
|
1035
1036
|
devtools?: boolean;
|
|
1036
1037
|
nodeId?: string;
|
|
@@ -1076,7 +1077,7 @@ export async function buildJsonRenderViewerHtml(options: {
|
|
|
1076
1077
|
const heightReporter = options.frameToken ? contentHeightReporterTag(options.frameToken) : '';
|
|
1077
1078
|
return buildAppHtml({
|
|
1078
1079
|
title: options.title,
|
|
1079
|
-
css: cssBundle
|
|
1080
|
+
css: `${readFileSync(join(import.meta.dir, '../client/theme/surface-theme.css'), 'utf-8')}\n${cssBundle}`,
|
|
1080
1081
|
js: escapeInlineScriptSource(boot),
|
|
1081
1082
|
head: `<meta name="color-scheme" content="light dark" />${heightReporter}`,
|
|
1082
1083
|
});
|
package/src/mcp/server.ts
CHANGED
|
@@ -200,6 +200,8 @@ export function createMcpServer(): McpServer {
|
|
|
200
200
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
|
|
201
201
|
outputSchema: interactionResult,
|
|
202
202
|
inputSchema: {
|
|
203
|
+
targetBoardId: z.string().optional(),
|
|
204
|
+
agentId: z.string().optional(),
|
|
203
205
|
type: z
|
|
204
206
|
.enum(AX_INTERACTION_TYPES)
|
|
205
207
|
.describe('Interaction type, e.g. ax.work.create, ax.evidence.add, ax.focus.set.'),
|
|
@@ -213,13 +215,15 @@ export function createMcpServer(): McpServer {
|
|
|
213
215
|
source: z.enum(AX_SOURCES).optional().describe('Optional host/source label. Defaults to mcp.'),
|
|
214
216
|
},
|
|
215
217
|
},
|
|
216
|
-
async ({ type, sourceNodeId, payload, sourceSurface, correlationId, source }, extra) => {
|
|
218
|
+
async ({ type, sourceNodeId, payload, sourceSurface, correlationId, source, targetBoardId, agentId }, extra) => {
|
|
217
219
|
try {
|
|
218
220
|
const c = await ensureCanvas();
|
|
219
221
|
extra.signal.throwIfAborted();
|
|
220
222
|
const result = await c.invoker().invoke(
|
|
221
223
|
'ax.interaction.submit',
|
|
222
224
|
{
|
|
225
|
+
targetBoardId,
|
|
226
|
+
agentId,
|
|
223
227
|
type,
|
|
224
228
|
sourceNodeId,
|
|
225
229
|
...(payload ? { payload } : {}),
|
|
@@ -269,6 +273,8 @@ export function createMcpServer(): McpServer {
|
|
|
269
273
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
270
274
|
outputSchema: activityResult,
|
|
271
275
|
inputSchema: {
|
|
276
|
+
targetBoardId: z.string().optional(),
|
|
277
|
+
agentId: z.string().optional(),
|
|
272
278
|
kind: z.enum([
|
|
273
279
|
'tool-start',
|
|
274
280
|
'tool-result',
|
|
@@ -327,13 +333,15 @@ export function createMcpServer(): McpServer {
|
|
|
327
333
|
source: z.enum(AX_SOURCES).optional(),
|
|
328
334
|
},
|
|
329
335
|
},
|
|
330
|
-
async ({ kind, title, summary, outcome, ref, nodeIds, data, reactions, source }, extra) => {
|
|
336
|
+
async ({ kind, title, summary, outcome, ref, nodeIds, data, reactions, source, targetBoardId, agentId }, extra) => {
|
|
331
337
|
try {
|
|
332
338
|
const c = await ensureCanvas();
|
|
333
339
|
extra.signal.throwIfAborted();
|
|
334
340
|
const result = await c.invoker().invoke(
|
|
335
341
|
'ax.activity.ingest',
|
|
336
342
|
{
|
|
343
|
+
targetBoardId,
|
|
344
|
+
agentId,
|
|
337
345
|
kind,
|
|
338
346
|
title,
|
|
339
347
|
...(summary !== undefined ? { summary } : {}),
|
|
@@ -389,6 +397,7 @@ export function createMcpServer(): McpServer {
|
|
|
389
397
|
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
390
398
|
outputSchema: screenshotResult,
|
|
391
399
|
inputSchema: {
|
|
400
|
+
targetBoardId: z.string().optional().describe('Not supported: screenshots inspect the shared WebView.'),
|
|
392
401
|
format: z
|
|
393
402
|
.enum(['png', 'jpeg', 'webp'])
|
|
394
403
|
.optional()
|
|
@@ -396,8 +405,9 @@ export function createMcpServer(): McpServer {
|
|
|
396
405
|
quality: z.number().optional().describe('Optional quality for lossy formats'),
|
|
397
406
|
},
|
|
398
407
|
},
|
|
399
|
-
async ({ format, quality }, extra) => {
|
|
408
|
+
async ({ format, quality, targetBoardId }, extra) => {
|
|
400
409
|
try {
|
|
410
|
+
if (targetBoardId !== undefined) throw new Error('Screenshots inspect the shared WebView, not a scoped board.');
|
|
401
411
|
const c = await ensureCanvas();
|
|
402
412
|
extra.signal.throwIfAborted();
|
|
403
413
|
const bytes = await c.screenshotAutomationWebView(
|
|
@@ -203,6 +203,8 @@ export const PRESENCE_SET_SHAPE = {
|
|
|
203
203
|
const presenceSetSchema = z.object(PRESENCE_SET_SHAPE);
|
|
204
204
|
|
|
205
205
|
interface StoredPresence extends AgentPresence {
|
|
206
|
+
spatialBoardId: string | null;
|
|
207
|
+
sessionBoardId: string | null;
|
|
206
208
|
lastSeenMs: number;
|
|
207
209
|
/** Last explicit phase report; cursor/focus heartbeats must not keep a stale phase alive. */
|
|
208
210
|
phaseSetMs: number;
|
|
@@ -296,7 +298,7 @@ type SessionEndListener = (
|
|
|
296
298
|
export class AgentPresenceRegistry {
|
|
297
299
|
private readonly presences = new Map<string, StoredPresence>();
|
|
298
300
|
/** Newest first; bounded. Survives a writer fading — the feed is history, the writer list is presence. */
|
|
299
|
-
private activity: AgentActivityEntry
|
|
301
|
+
private activity: Array<AgentActivityEntry & { boardId: string | null }> = [];
|
|
300
302
|
private activitySeq = 0;
|
|
301
303
|
private emit: PresenceEmitter = () => {};
|
|
302
304
|
/** Single slots (like the emitter): server.ts owns the pre-session snapshot + receipt. */
|
|
@@ -435,6 +437,8 @@ export class AgentPresenceRegistry {
|
|
|
435
437
|
}
|
|
436
438
|
const existing = this.presences.get(key);
|
|
437
439
|
const stored: StoredPresence = existing ?? {
|
|
440
|
+
spatialBoardId: canvasState.activeBoardId,
|
|
441
|
+
sessionBoardId: canvasState.activeBoardId,
|
|
438
442
|
sessionId: key,
|
|
439
443
|
source: input.source,
|
|
440
444
|
agentId: input.agentId?.trim() || null,
|
|
@@ -458,6 +462,9 @@ export class AgentPresenceRegistry {
|
|
|
458
462
|
touchesCache: null,
|
|
459
463
|
};
|
|
460
464
|
const wasAttached = stored.attached;
|
|
465
|
+
if (input.op || input.cursor !== undefined || input.focusNodeId !== undefined)
|
|
466
|
+
stored.spatialBoardId = canvasState.activeBoardId;
|
|
467
|
+
if (stored.attached && stored.sessionBoardId === null) stored.sessionBoardId = canvasState.activeBoardId;
|
|
461
468
|
stored.lastSeenMs = now;
|
|
462
469
|
stored.lastSeenAt = new Date(now).toISOString();
|
|
463
470
|
if (input.label) stored.label = input.label;
|
|
@@ -466,6 +473,7 @@ export class AgentPresenceRegistry {
|
|
|
466
473
|
if (input.activity?.summary) {
|
|
467
474
|
this.activitySeq += 1;
|
|
468
475
|
this.activity.unshift({
|
|
476
|
+
boardId: canvasState.activeBoardId,
|
|
469
477
|
id: `act-${this.activitySeq}`,
|
|
470
478
|
at: stored.lastSeenAt,
|
|
471
479
|
sessionId: key,
|
|
@@ -480,6 +488,7 @@ export class AgentPresenceRegistry {
|
|
|
480
488
|
}
|
|
481
489
|
if (input.attached !== undefined) stored.attached = input.attached;
|
|
482
490
|
if (!wasAttached && input.attached === true) {
|
|
491
|
+
stored.sessionBoardId = canvasState.activeBoardId;
|
|
483
492
|
// *Start agent session* with one agent-less writer already on the board
|
|
484
493
|
// adopts that writer — the human is claiming the agent they can see.
|
|
485
494
|
if (stored.source === 'browser') {
|
|
@@ -539,13 +548,7 @@ export class AgentPresenceRegistry {
|
|
|
539
548
|
captureBaseline(stored.startBoards, stored.startedAt);
|
|
540
549
|
}
|
|
541
550
|
if (input.attached === false) {
|
|
542
|
-
if (wasAttached)
|
|
543
|
-
this.onSessionEnd(
|
|
544
|
-
this.publicView(stored, now),
|
|
545
|
-
stored.startSnapshotId,
|
|
546
|
-
input.endedBy ?? 'agent',
|
|
547
|
-
stored.startedAt,
|
|
548
|
-
);
|
|
551
|
+
if (wasAttached) this.endSession(stored, input.endedBy ?? 'agent', now);
|
|
549
552
|
// An explicit detach always removes the presence. In particular, a
|
|
550
553
|
// worker that reports attached:false must not be refreshed into another
|
|
551
554
|
// full activity TTL as an external writer.
|
|
@@ -690,13 +693,21 @@ export class AgentPresenceRegistry {
|
|
|
690
693
|
}
|
|
691
694
|
|
|
692
695
|
/** Remove a writer (session-end). */
|
|
696
|
+
private endSession(stored: StoredPresence, endedBy: 'human' | 'agent' | 'idle-timeout', now: number): void {
|
|
697
|
+
const id = stored.sessionBoardId;
|
|
698
|
+
if (id && !canvasState.listBoards().some((board) => board.id === id)) return;
|
|
699
|
+
const finish = () =>
|
|
700
|
+
this.onSessionEnd(this.publicView(stored, now), stored.startSnapshotId, endedBy, stored.startedAt);
|
|
701
|
+
if (id) canvasState.withBoard(id, finish);
|
|
702
|
+
else canvasState.withVisibleBoard(finish);
|
|
703
|
+
}
|
|
704
|
+
|
|
693
705
|
detach(sessionId: string): boolean {
|
|
694
706
|
const stored = this.presences.get(sessionId);
|
|
695
707
|
const removed = this.presences.delete(sessionId);
|
|
696
708
|
if (removed) {
|
|
697
709
|
this.dropAliasesTo(sessionId);
|
|
698
|
-
if (stored?.attached)
|
|
699
|
-
this.onSessionEnd(this.publicView(stored, Date.now()), stored.startSnapshotId, 'agent', stored.startedAt);
|
|
710
|
+
if (stored?.attached) this.endSession(stored, 'agent', Date.now());
|
|
700
711
|
this.scheduleEmit();
|
|
701
712
|
}
|
|
702
713
|
this.maybeStopSweeper();
|
|
@@ -709,7 +720,10 @@ export class AgentPresenceRegistry {
|
|
|
709
720
|
return {
|
|
710
721
|
presences,
|
|
711
722
|
sessionActive: isSessionActive(presences),
|
|
712
|
-
activity: this.activity.map((entry) => ({
|
|
723
|
+
activity: this.activity.map(({ boardId, ...entry }) => ({
|
|
724
|
+
...entry,
|
|
725
|
+
nodeId: boardId === canvasState.activeBoardId ? entry.nodeId : null,
|
|
726
|
+
})),
|
|
713
727
|
};
|
|
714
728
|
}
|
|
715
729
|
|
|
@@ -744,6 +758,8 @@ export class AgentPresenceRegistry {
|
|
|
744
758
|
startBoards: _startBoards,
|
|
745
759
|
touchesCache: _touchesCache,
|
|
746
760
|
lastActivityNodeId,
|
|
761
|
+
spatialBoardId,
|
|
762
|
+
sessionBoardId: _sessionBoardId,
|
|
747
763
|
...presence
|
|
748
764
|
} = stored;
|
|
749
765
|
if (withTouches) {
|
|
@@ -764,11 +780,15 @@ export class AgentPresenceRegistry {
|
|
|
764
780
|
// Park-at fallback (joint-gaps #3): an attached-but-focusless writer is
|
|
765
781
|
// invisible everywhere — fall back to the last node its activity touched.
|
|
766
782
|
if (!presence.focusNodeId && lastActivityNodeId) presence.focusNodeId = lastActivityNodeId;
|
|
783
|
+
if (spatialBoardId !== canvasState.activeBoardId) {
|
|
784
|
+
presence.focusNodeId = null;
|
|
785
|
+
presence.cursor = null;
|
|
786
|
+
}
|
|
767
787
|
// An attached session blocked on a human decision reads as waiting — the
|
|
768
788
|
// gate itself lives in the AX state, this is only the presence view of it.
|
|
769
789
|
// It overrides tooling too: the gate REQUEST is itself a write, and the
|
|
770
790
|
// human's decision is the state that matters while it is pending.
|
|
771
|
-
if (presence.attached && this.hasPendingGate()) {
|
|
791
|
+
if (presence.attached && spatialBoardId === canvasState.activeBoardId && this.hasPendingGate()) {
|
|
772
792
|
phase = 'waiting-approval';
|
|
773
793
|
detail = null;
|
|
774
794
|
}
|
|
@@ -824,13 +844,7 @@ export class AgentPresenceRegistry {
|
|
|
824
844
|
if (presence.lastSeenMs + ttl <= now) {
|
|
825
845
|
this.presences.delete(key);
|
|
826
846
|
this.dropAliasesTo(key);
|
|
827
|
-
if (presence.attached)
|
|
828
|
-
this.onSessionEnd(
|
|
829
|
-
this.publicView(presence, now),
|
|
830
|
-
presence.startSnapshotId,
|
|
831
|
-
'idle-timeout',
|
|
832
|
-
presence.startedAt,
|
|
833
|
-
);
|
|
847
|
+
if (presence.attached) this.endSession(presence, 'idle-timeout', now);
|
|
834
848
|
changed = true;
|
|
835
849
|
continue;
|
|
836
850
|
}
|
|
@@ -866,14 +880,16 @@ export class AgentPresenceRegistry {
|
|
|
866
880
|
if (this.emitTimer) return;
|
|
867
881
|
this.emitTimer = setTimeout(() => {
|
|
868
882
|
this.emitTimer = null;
|
|
869
|
-
|
|
883
|
+
canvasState.withVisibleBoard(() =>
|
|
884
|
+
this.emit('agent-presence', this.snapshot() as unknown as Record<string, unknown>),
|
|
885
|
+
);
|
|
870
886
|
}, 50);
|
|
871
887
|
(this.emitTimer as { unref?: () => void }).unref?.();
|
|
872
888
|
}
|
|
873
889
|
|
|
874
890
|
private ensureSweeper(): void {
|
|
875
891
|
if (this.sweepTimer || this.presences.size === 0) return;
|
|
876
|
-
this.sweepTimer = setInterval(() => this.sweep(), 1000);
|
|
892
|
+
this.sweepTimer = setInterval(() => canvasState.withVisibleBoard(() => this.sweep()), 1000);
|
|
877
893
|
(this.sweepTimer as { unref?: () => void }).unref?.();
|
|
878
894
|
}
|
|
879
895
|
|
|
@@ -894,10 +910,14 @@ export const agentPresence = new AgentPresenceRegistry();
|
|
|
894
910
|
// the operation registry are covered too.
|
|
895
911
|
// The live lens also follows card content (a person's edit takes a card out of
|
|
896
912
|
// the agent's session) and the open board — but not moves, so a drag stays quiet.
|
|
897
|
-
let lastBoardId = canvasState.
|
|
913
|
+
let lastBoardId = canvasState.visibleBoardId;
|
|
898
914
|
let lastRevision = canvasState.getContentRevision().revision;
|
|
899
915
|
canvasState.onChange((type) => {
|
|
900
|
-
|
|
916
|
+
if (canvasState.isBackgroundBoard) {
|
|
917
|
+
agentPresence.refresh();
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
const boardId = canvasState.visibleBoardId;
|
|
901
921
|
const revision = canvasState.getContentRevision().revision;
|
|
902
922
|
if (boardId !== lastBoardId) agentPresence.boardOpened();
|
|
903
923
|
else if (type === 'ax' || type === 'pins' || revision !== lastRevision) agentPresence.refresh();
|
|
@@ -111,4 +111,10 @@ export function advanceAxFlowLoops(): void {
|
|
|
111
111
|
canvasState.setWorkItemsChangedListener(() => {
|
|
112
112
|
refreshWorkboardNodes();
|
|
113
113
|
advanceAxFlowLoops();
|
|
114
|
+
// Task title/detail changes also repaint explicitly presented live cards.
|
|
115
|
+
if (
|
|
116
|
+
canvasState.getLayout().nodes.some((node) => node.type === 'status' && typeof node.data.workItemId === 'string')
|
|
117
|
+
) {
|
|
118
|
+
emitCanvasLayoutUpdate();
|
|
119
|
+
}
|
|
114
120
|
});
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { formatCountdown } from '../shared/approval-gates.js';
|
|
13
13
|
import { canvasState } from './canvas-state.js';
|
|
14
|
+
import { withCurrentActor } from './attribution.js';
|
|
14
15
|
|
|
15
16
|
type GateEmitter = (event: string, payload: Record<string, unknown>) => void;
|
|
16
17
|
|
|
@@ -50,7 +51,13 @@ export function sweepExpiredGates(now = Date.now()): string[] {
|
|
|
50
51
|
export function startGateTtlSweeper(emitter: GateEmitter): void {
|
|
51
52
|
emit = emitter;
|
|
52
53
|
if (sweepTimer) return;
|
|
53
|
-
sweepTimer = setInterval(
|
|
54
|
+
sweepTimer = setInterval(
|
|
55
|
+
() =>
|
|
56
|
+
withCurrentActor({ actor: 'system', source: 'system' }, () => {
|
|
57
|
+
canvasState.forEachLiveBoard(() => sweepExpiredGates());
|
|
58
|
+
}),
|
|
59
|
+
1000,
|
|
60
|
+
);
|
|
54
61
|
(sweepTimer as { unref?: () => void }).unref?.();
|
|
55
62
|
}
|
|
56
63
|
|
|
@@ -379,6 +379,7 @@ const PAYLOAD_SCHEMAS: Record<string, z.ZodType> = {
|
|
|
379
379
|
* Injected so this module stays free of a runtime canvas-state import.
|
|
380
380
|
*/
|
|
381
381
|
export interface AxInteractionManager {
|
|
382
|
+
readonly boardIdentity: object;
|
|
382
383
|
getNode(id: string): CanvasNodeState | undefined;
|
|
383
384
|
/** Canvas read for `ax.flow.materialize` (placement obstacles + stale flow sweep). */
|
|
384
385
|
getLayout(): CanvasLayout;
|
|
@@ -722,22 +723,27 @@ function materializeFlow(
|
|
|
722
723
|
*/
|
|
723
724
|
// ── ax.work.create idempotency window ─────────────
|
|
724
725
|
const WORK_CREATE_DEDUPE_MS = 2_500;
|
|
725
|
-
const recentWorkCreates = new Map<string, { item: PmxAxWorkItem; at: number }
|
|
726
|
+
const recentWorkCreates = new WeakMap<object, Map<string, { item: PmxAxWorkItem; at: number }>>();
|
|
726
727
|
|
|
727
|
-
function recentWorkCreate(sourceNodeId: string, title: string): PmxAxWorkItem | null {
|
|
728
|
+
function recentWorkCreate(owner: object, sourceNodeId: string, title: string): PmxAxWorkItem | null {
|
|
728
729
|
const key = `${sourceNodeId}\u0000${title}`;
|
|
729
|
-
const hit = recentWorkCreates.get(key);
|
|
730
|
+
const hit = recentWorkCreates.get(owner)?.get(key);
|
|
730
731
|
if (hit && Date.now() - hit.at <= WORK_CREATE_DEDUPE_MS) return hit.item;
|
|
731
732
|
return null;
|
|
732
733
|
}
|
|
733
734
|
|
|
734
|
-
function rememberWorkCreate(sourceNodeId: string, title: string, item: PmxAxWorkItem): void {
|
|
735
|
+
function rememberWorkCreate(owner: object, sourceNodeId: string, title: string, item: PmxAxWorkItem): void {
|
|
735
736
|
const key = `${sourceNodeId}\u0000${title}`;
|
|
736
|
-
|
|
737
|
-
if (
|
|
737
|
+
let entries = recentWorkCreates.get(owner);
|
|
738
|
+
if (!entries) {
|
|
739
|
+
entries = new Map();
|
|
740
|
+
recentWorkCreates.set(owner, entries);
|
|
741
|
+
}
|
|
742
|
+
entries.set(key, { item, at: Date.now() });
|
|
743
|
+
if (entries.size > 64) {
|
|
738
744
|
const now = Date.now();
|
|
739
|
-
for (const [k, v] of
|
|
740
|
-
if (now - v.at > WORK_CREATE_DEDUPE_MS)
|
|
745
|
+
for (const [k, v] of entries) {
|
|
746
|
+
if (now - v.at > WORK_CREATE_DEDUPE_MS) entries.delete(k);
|
|
741
747
|
}
|
|
742
748
|
}
|
|
743
749
|
}
|
|
@@ -851,7 +857,7 @@ export function applyAxInteraction(
|
|
|
851
857
|
// booked two identical work items ~1s apart): the SAME node creating a
|
|
852
858
|
// work item with the SAME title inside the window gets the first item
|
|
853
859
|
// back instead of a duplicate.
|
|
854
|
-
const dupe = recentWorkCreate(sourceNodeId, p.title);
|
|
860
|
+
const dupe = recentWorkCreate(manager.boardIdentity, sourceNodeId, p.title);
|
|
855
861
|
if (dupe) return accept(type, sourceNodeId, dupe, 'ax-state-changed', { workItem: dupe });
|
|
856
862
|
const workItem = manager.addWorkItem(
|
|
857
863
|
{
|
|
@@ -862,7 +868,7 @@ export function applyAxInteraction(
|
|
|
862
868
|
},
|
|
863
869
|
opts,
|
|
864
870
|
);
|
|
865
|
-
rememberWorkCreate(sourceNodeId, p.title, workItem);
|
|
871
|
+
rememberWorkCreate(manager.boardIdentity, sourceNodeId, p.title, workItem);
|
|
866
872
|
return withLayoutBroadcast(accept(type, sourceNodeId, workItem, 'ax-state-changed', { workItem }), manager, [
|
|
867
873
|
workItem,
|
|
868
874
|
]);
|