pmx-canvas 0.7.3 → 0.8.2
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/THIRD_PARTY_LICENSES.txt +331 -0
- package/.github/extensions/pmx-canvas/extension.mjs +25 -172
- package/.github/extensions/pmx-canvas/mcp-bootstrap.ts +198 -0
- package/.github/extensions/pmx-canvas/runtime.d.mts +41 -0
- package/.github/extensions/pmx-canvas/runtime.json +3 -0
- package/.github/extensions/pmx-canvas/runtime.mjs +194 -0
- package/.github/extensions/pmx-canvas/start-mcp.mjs +51 -0
- package/.github/extensions/pmx-canvas/tools.json +2560 -0
- package/CHANGELOG.md +64 -0
- package/Readme.md +21 -7
- package/dist/canvas/fonts/OFL.txt +93 -0
- package/dist/canvas/fonts/ibm-plex-mono-latin-400-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-mono-latin-500-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-mono-latin-ext-400-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-mono-latin-ext-500-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-sans-latin-400-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-sans-latin-500-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-sans-latin-600-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-sans-latin-ext-400-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-sans-latin-ext-500-normal.woff2 +0 -0
- package/dist/canvas/fonts/ibm-plex-sans-latin-ext-600-normal.woff2 +0 -0
- package/dist/canvas/global.css +726 -674
- package/dist/canvas/index.js +90 -88
- package/dist/canvas/mcp-app.html +7879 -0
- package/dist/canvas/mermaid-entry.js +1 -1
- package/dist/canvas/surface-theme.css +151 -46
- package/dist/json-render/index.css +81 -1
- package/dist/json-render/index.js +1 -1
- package/dist/types/cli/commands/record.d.ts +1 -1
- package/dist/types/client/canvas/NodeContextMark.d.ts +24 -0
- package/dist/types/client/canvas/SessionReceipt.d.ts +3 -1
- package/dist/types/client/icons.d.ts +1 -2
- package/dist/types/client/mcp-app/index.d.ts +1 -0
- package/dist/types/client/nodes/HtmlNode.d.ts +1 -0
- package/dist/types/client/nodes/ax-node-actions.d.ts +2 -2
- package/dist/types/client/nodes/markdown-sanitize.d.ts +2 -0
- package/dist/types/client/nodes/use-workbench-asset.d.ts +2 -0
- package/dist/types/client/state/attention-store.d.ts +0 -5
- package/dist/types/client/state/context-status-store.d.ts +53 -0
- package/dist/types/client/state/intent-bridge.d.ts +3 -0
- package/dist/types/client/state/near-pin-store.d.ts +16 -0
- package/dist/types/client/state/presence-store.d.ts +5 -0
- package/dist/types/client/state/session-store.d.ts +33 -3
- package/dist/types/client/state/workbench-transport.d.ts +16 -0
- package/dist/types/client/theme/tokens.d.ts +1 -3
- package/dist/types/client/types.d.ts +9 -0
- package/dist/types/json-render/server.d.ts +1 -1
- package/dist/types/mcp/app-server.d.ts +3 -0
- package/dist/types/mcp/canvas-access.d.ts +1 -1
- package/dist/types/mcp/canvas-app.d.ts +4 -0
- package/dist/types/mcp/workbench-app.d.ts +4 -0
- package/dist/types/server/agent-presence.d.ts +1 -1
- package/dist/types/server/ax-state.d.ts +1 -1
- package/dist/types/server/canvas-db.d.ts +8 -0
- package/dist/types/server/canvas-geometry.d.ts +25 -0
- package/dist/types/server/canvas-operations.d.ts +1 -11
- package/dist/types/server/canvas-state.d.ts +25 -3
- package/dist/types/server/context-brief.d.ts +8 -1
- package/dist/types/server/context-reads.d.ts +30 -7
- package/dist/types/server/index.d.ts +6 -1
- package/dist/types/server/mcp-app-runtime.d.ts +1 -0
- package/dist/types/server/operations/http.d.ts +2 -0
- package/dist/types/server/operations/invoker.d.ts +4 -2
- package/dist/types/server/operations/ops/nodes.d.ts +0 -6
- package/dist/types/server/server.d.ts +1 -0
- package/dist/types/server/spatial-analysis.d.ts +2 -0
- package/dist/types/shared/agent-context.d.ts +22 -0
- package/dist/types/shared/agent-presence.d.ts +3 -10
- package/dist/types/shared/canvas-app.d.ts +54 -0
- package/dist/types/shared/themes.d.ts +1 -1
- package/docs/RELEASE.md +7 -3
- package/docs/agent-plugin.md +123 -0
- package/docs/ax-state-contract.md +40 -0
- package/docs/cli.md +7 -2
- package/docs/design/AgentContext.dc.html +186 -0
- package/docs/design/Before.dc.html +208 -0
- package/docs/design/BoardPins.dc.html +151 -0
- package/docs/design/Chrome.dc.html +211 -0
- package/docs/design/Context.dc.html +239 -0
- package/docs/design/Flows.dc.html +181 -0
- package/docs/design/Home.dc.html +209 -0
- package/docs/design/HomeGraph.dc.html +211 -0
- package/docs/design/HomeMap.dc.html +117 -0
- package/docs/design/HomeTeam.dc.html +176 -0
- package/docs/design/Light.dc.html +28 -0
- package/docs/design/Main.dc.html +280 -0
- package/docs/design/NearPin.dc.html +260 -0
- package/docs/design/Pane600.dc.html +120 -0
- package/docs/design/Relations.dc.html +297 -0
- package/docs/design/Themes.dc.html +121 -0
- package/docs/design/TokenSystem.dc.html +136 -0
- package/docs/design/Tokens.dc.html +163 -0
- package/docs/design/Types1.dc.html +177 -0
- package/docs/design/Types2.dc.html +226 -0
- package/docs/design/Types3.dc.html +195 -0
- package/docs/design/Uses.dc.html +152 -0
- package/docs/design/Vision.dc.html +116 -0
- package/docs/design/Zoom.dc.html +82 -0
- package/docs/design/canvas.json +274 -0
- package/docs/design.md +217 -0
- package/docs/environment.md +1 -1
- package/docs/http-api.md +86 -16
- package/docs/mcp.md +202 -4
- package/docs/node-types.md +34 -10
- package/docs/plans/plan-009-tech-debt-backlog.md +1 -1
- package/docs/plans/plan-015-design-foundation-wave-0.md +177 -0
- package/docs/plans/plan-016-context-made-visible-wave-1.md +148 -0
- package/docs/product-vision-2026-09.md +47 -7
- package/docs/screenshot.png +0 -0
- package/docs/sdk.md +1 -0
- package/package.json +10 -4
- package/plugins/pmx-canvas/LICENSE +21 -0
- package/plugins/pmx-canvas/THIRD_PARTY_LICENSES.txt +331 -0
- package/plugins/pmx-canvas/assets/preview.png +0 -0
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/extension.mjs +930 -0
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.json +3 -0
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.mjs +194 -0
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/steering-delivery.mjs +79 -0
- package/plugins/pmx-canvas/mcp.json +12 -0
- package/plugins/pmx-canvas/plugin.json +24 -0
- package/plugins/pmx-canvas/runtime.json +3 -0
- package/plugins/pmx-canvas/runtime.mjs +194 -0
- package/plugins/pmx-canvas/start-mcp.mjs +51 -0
- package/plugins/pmx-canvas/tools.json +2560 -0
- package/skills/pmx-canvas/SKILL.md +29 -9
- package/skills/pmx-canvas/evals/evals.json +62 -0
- package/skills/pmx-canvas/references/ax-html-control-surface.md +6 -0
- package/skills/pmx-canvas/references/full-reference.md +11 -7
- package/skills/pmx-canvas/references/github-copilot-app-adapter.md +20 -8
- package/skills/pmx-canvas/references/installing-pmx-canvas.md +6 -6
- package/skills/pmx-canvas-testing/SKILL.md +5 -4
- package/src/cli/commands/copilot.ts +5 -0
- package/src/cli/commands/nodes.ts +3 -2
- package/src/cli/commands/view.ts +1 -1
- package/src/cli/index.ts +10 -4
- package/src/client/App.tsx +6 -1
- package/src/client/canvas/AgentPresenceLayer.tsx +3 -3
- package/src/client/canvas/AnnotationLayer.tsx +1 -1
- package/src/client/canvas/CanvasNode.tsx +32 -49
- package/src/client/canvas/CanvasViewport.tsx +0 -2
- package/src/client/canvas/CommandPalette.tsx +3 -2
- package/src/client/canvas/ContextMenu.tsx +11 -0
- package/src/client/canvas/EdgeLayer.tsx +1 -1
- package/src/client/canvas/ExportDialog.tsx +33 -6
- package/src/client/canvas/IntentLayer.tsx +2 -2
- package/src/client/canvas/Minimap.tsx +12 -7
- package/src/client/canvas/NodeContextMark.tsx +156 -0
- package/src/client/canvas/SessionPanel.tsx +4 -7
- package/src/client/canvas/SessionReceipt.tsx +114 -17
- package/src/client/canvas/ShortcutOverlay.tsx +6 -1
- package/src/client/canvas/SnapshotPanel.tsx +11 -2
- package/src/client/canvas/ToolRail.tsx +12 -9
- package/src/client/canvas/TopBar.tsx +16 -11
- package/src/client/icons.tsx +2 -12
- package/src/client/mcp-app/index.tsx +137 -0
- package/src/client/mcp-app/style.css +10 -0
- package/src/client/nodes/AttachmentNode.tsx +8 -1
- package/src/client/nodes/AxStepControls.tsx +1 -1
- package/src/client/nodes/ContextNode.tsx +9 -9
- package/src/client/nodes/ExtAppFrame.tsx +13 -8
- package/src/client/nodes/FileNode.tsx +8 -5
- package/src/client/nodes/HtmlNode.tsx +12 -2
- package/src/client/nodes/ImageNode.tsx +4 -2
- package/src/client/nodes/InlineMarkdownEditor.tsx +2 -1
- package/src/client/nodes/LedgerNode.tsx +2 -2
- package/src/client/nodes/MarkdownNode.tsx +44 -9
- package/src/client/nodes/McpAppNode.tsx +6 -4
- package/src/client/nodes/PromptNode.tsx +5 -17
- package/src/client/nodes/ResponseNode.tsx +3 -15
- package/src/client/nodes/StatusNode.tsx +2 -2
- package/src/client/nodes/StatusSummary.tsx +3 -1
- package/src/client/nodes/TraceNode.tsx +6 -4
- package/src/client/nodes/WebpageNode.tsx +1 -1
- package/src/client/nodes/ax-node-actions.ts +2 -2
- package/src/client/nodes/iframe-document-url.ts +3 -2
- package/src/client/nodes/markdown-sanitize.ts +10 -0
- package/src/client/nodes/surface-url.ts +10 -0
- package/src/client/nodes/use-surface-frame.ts +3 -1
- package/src/client/nodes/use-workbench-asset.ts +26 -0
- package/src/client/state/attention-bridge.ts +0 -8
- package/src/client/state/attention-store.ts +0 -17
- package/src/client/state/canvas-store.ts +4 -2
- package/src/client/state/context-status-store.ts +66 -0
- package/src/client/state/human-store.ts +2 -0
- package/src/client/state/iframe-mode.ts +5 -0
- package/src/client/state/intent-bridge.ts +10 -4
- package/src/client/state/near-pin-store.ts +53 -0
- package/src/client/state/presence-store.ts +5 -10
- package/src/client/state/session-store.ts +106 -9
- package/src/client/state/sse-bridge.ts +26 -7
- package/src/client/state/theme-override.ts +1 -1
- package/src/client/state/workbench-transport.ts +36 -0
- package/src/client/theme/global.css +726 -674
- package/src/client/theme/surface-theme.css +151 -46
- package/src/client/theme/tokens.ts +7 -11
- package/src/client/types.ts +10 -0
- package/src/json-render/renderer/index.css +6 -1
- package/src/json-render/renderer/index.tsx +0 -1
- package/src/json-render/server.ts +71 -14
- package/src/mcp/app-server.ts +64 -0
- package/src/mcp/canvas-access.ts +14 -11
- package/src/mcp/canvas-app.ts +318 -0
- package/src/mcp/server.ts +1 -1
- package/src/mcp/workbench-app.ts +211 -0
- package/src/server/agent-presence.ts +45 -6
- package/src/server/ax-context.ts +1 -1
- package/src/server/ax-state.ts +1 -1
- package/src/server/board-export.ts +5 -2
- package/src/server/canvas-db.ts +46 -12
- package/src/server/canvas-geometry.ts +64 -0
- package/src/server/canvas-operations.ts +7 -25
- package/src/server/canvas-state.ts +105 -15
- package/src/server/canvas-validation.ts +19 -4
- package/src/server/context-brief.ts +39 -2
- package/src/server/context-reads.ts +87 -15
- package/src/server/demo-state.json +31 -31
- package/src/server/html-surface.ts +4 -5
- package/src/server/index.ts +41 -5
- package/src/server/mcp-app-runtime.ts +11 -0
- package/src/server/operations/composites.ts +4 -3
- package/src/server/operations/http.ts +4 -4
- package/src/server/operations/invoker.ts +13 -4
- package/src/server/operations/mcp.ts +6 -0
- package/src/server/operations/ops/ax-read.ts +1 -1
- package/src/server/operations/ops/ax-timeline.ts +37 -0
- package/src/server/operations/ops/boards.ts +1 -1
- package/src/server/operations/ops/canvas-wire.ts +3 -6
- package/src/server/operations/ops/intent.ts +31 -2
- package/src/server/operations/ops/json-render.ts +6 -2
- package/src/server/operations/ops/nodes.ts +8 -76
- package/src/server/operations/ops/query.ts +8 -2
- package/src/server/operations/ops/snapshots.ts +4 -1
- package/src/server/operations/ops/viewport.ts +29 -5
- package/src/server/operations/registry.ts +41 -5
- package/src/server/scope-fence.ts +55 -10
- package/src/server/server.ts +83 -5
- package/src/server/spatial-analysis.ts +2 -2
- package/src/server/workboard.ts +6 -1
- package/src/{server → shared}/agent-context.ts +2 -2
- package/src/shared/agent-presence.ts +3 -15
- package/src/shared/canvas-app.ts +62 -0
- package/src/shared/themes.ts +5 -5
- package/src/client/canvas/FocusFieldLayer.tsx +0 -116
- package/src/client/canvas/kind-colors.ts +0 -23
package/docs/mcp.md
CHANGED
|
@@ -42,6 +42,196 @@ Add to your agent's MCP config:
|
|
|
42
42
|
|
|
43
43
|
The canvas auto-starts on first tool call.
|
|
44
44
|
|
|
45
|
+
## Private MCP App preview
|
|
46
|
+
|
|
47
|
+
`pmx-canvas --mcp-app` runs a **separate, experimental stdio profile**; the
|
|
48
|
+
24-tool `--mcp` profile above is unchanged. Build from this checkout first:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
bun run build
|
|
52
|
+
PMX_CANVAS_WORKSPACE_ROOT=/absolute/project bun run src/cli/index.ts --mcp-app
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Configure a compatible MCP Apps host to launch that command. Pin the workspace
|
|
56
|
+
root explicitly; `PMX_CANVAS_URL` can attach it to an existing same-workspace
|
|
57
|
+
daemon. This profile shares the daemon's **one active board** across all app
|
|
58
|
+
instances and conversations. Choose or create a board in the app toolbar.
|
|
59
|
+
|
|
60
|
+
Attached daemons must advertise `pmx-embedded-workbench-v4` in `/health` on every
|
|
61
|
+
access. Matching package versions alone do not prove support for board/revision
|
|
62
|
+
guards and embedded execution restrictions. An incompatible daemon is refused
|
|
63
|
+
before any operation; restart it with this preview build and retry.
|
|
64
|
+
|
|
65
|
+
The standard `ui://pmx/canvas` resource is self-contained HTML with MIME type
|
|
66
|
+
`text/html;profile=mcp-app`. Only the launcher descriptor has ChatGPT-specific
|
|
67
|
+
`openai/ui` metadata, advertising both `global` (sidebar/fullscreen) and `thread`
|
|
68
|
+
(conversation panel) entrypoints. Actual availability depends on the host.
|
|
69
|
+
|
|
70
|
+
The embedded view sends selected, pinned and AX-focused nodes to the host,
|
|
71
|
+
including summaries of structured cards. Its context badge sits at the bottom
|
|
72
|
+
left to avoid the host composer. Export downloads use the host's `downloadFile`
|
|
73
|
+
capability; hosts without it show a local-retrieval message. Local-only trace
|
|
74
|
+
capture and shared undo/redo are unavailable in the embedded view. Save a
|
|
75
|
+
snapshot before restoring another one. Local surfaces can be expanded or
|
|
76
|
+
exported, but cannot be opened as public sites without a public URL.
|
|
77
|
+
|
|
78
|
+
| Tool | Input | Effect |
|
|
79
|
+
|------|-------|--------|
|
|
80
|
+
| `pmx_open_canvas` | `{}` | Open the real PMX workbench; standard UI resource plus ChatGPT entrypoints |
|
|
81
|
+
| `pmx_read_canvas` | `{}` | Read board list, identity, projection revision, node IDs/titles/types/content revisions, counts and pin IDs |
|
|
82
|
+
| `pmx_read_node` | `boardId`, `id` | Read one standalone note's full editable content, revision, position, size and incident edges in model-visible results |
|
|
83
|
+
| `pmx_add_note` | `boardId`, `title`, `content`, `x`, `y` | Add Markdown through `node.add`; rejects a stale board ID |
|
|
84
|
+
| `pmx_connect_nodes` | `boardId`, `from`, `to`, `type`, optional `label` | Add a `flow`, `depends-on`, `relation` or `references` edge through `edge.add` |
|
|
85
|
+
| `pmx_create_board` | `name` | Create an empty board without opening it |
|
|
86
|
+
| `pmx_open_board` | `id` (or null for Home), `expectedBoardId` | Switch the shared active board, refusing if it changed since the last read |
|
|
87
|
+
| `pmx_update_note` | `boardId`, `id`, `expectedContentRevision`, `title`, `content` | Edit standalone Markdown; reject stale revisions, file-linked notes and oversized originals |
|
|
88
|
+
| `pmx_move_node` | `boardId`, `id`, `x`, `y` | Move through `node.update`; group children follow |
|
|
89
|
+
| `pmx_pin_nodes` | `boardId`, `nodeIds`, `mode` (`add` or `remove`) | Curate pins without replacing unrelated pins |
|
|
90
|
+
|
|
91
|
+
Read tools declare read-only, non-destructive, closed-world annotations; the
|
|
92
|
+
three additive tools declare non-read-only, non-destructive, closed-world
|
|
93
|
+
annotations. Editing, moving, pin changes and board switching conservatively
|
|
94
|
+
declare destructive writes. These assistant tools are closed-world. Writes retain the
|
|
95
|
+
operation registry's scope fences, locks and attribution. **App clicks are
|
|
96
|
+
agent-attributed**: MCP does not prove a human clicked, so the app cannot bypass
|
|
97
|
+
these safeguards or acquire workbench authority.
|
|
98
|
+
|
|
99
|
+
An eleventh tool, **`pmx_workbench_request`**, is app-only (`ui.visibility: ["app"]`),
|
|
100
|
+
not an assistant tool. It accepts a relative PMX path, method, optional JSON body,
|
|
101
|
+
and captured `expectedBoardId`. An explicit allowlist routes workbench operations
|
|
102
|
+
through the same canonical registry; raw reads hydrate local assets. Replies stay
|
|
103
|
+
in `_meta.response`. It is not an arbitrary HTTP proxy, shell executor, external
|
|
104
|
+
MCP tool dispatcher, or human-authority bypass. This app-only tool declares
|
|
105
|
+
open-world behavior because webpage/image actions may fetch external URLs.
|
|
106
|
+
|
|
107
|
+
Embedded writes cannot supply external MCP launch metadata. App startup, board
|
|
108
|
+
switches and snapshot restore cannot launch saved external backends; open them
|
|
109
|
+
explicitly in local PMX. Undo/redo, flow materialization and approval resolve/reopen
|
|
110
|
+
(including node interactions) stay local. Embedded source attribution is fixed
|
|
111
|
+
to `mcp`. Child updates require permission for auto-sizing parent frames too;
|
|
112
|
+
the fence and human-lock checks share that target calculation. Manual frames
|
|
113
|
+
do not inherit child-update restrictions because their bounds remain fixed.
|
|
114
|
+
Child geometry changes re-fit only affected automatic ancestors, not unrelated
|
|
115
|
+
frames. PATCH aliases and partial coordinates resolve before destination checks;
|
|
116
|
+
bulk guards and execution filter invalid patches identically before deciding
|
|
117
|
+
which child positions override group translation.
|
|
118
|
+
Rendered Markdown is sanitized before entering the workbench document; active
|
|
119
|
+
HTML remains confined to sandboxed node frames.
|
|
120
|
+
|
|
121
|
+
The app bundles the **existing PMX workbench**, not a second editor: Home, board
|
|
122
|
+
navigation, tool rail, spatial nodes, edges, dragging, grouping, pins, themes,
|
|
123
|
+
and the full Markdown editor use their normal components. A transport adapter
|
|
124
|
+
replaces HTTP with host `tools/call`; the existing workbench poll cursor supplies
|
|
125
|
+
live updates every two seconds. Local assets travel over MCP, not localhost URLs
|
|
126
|
+
in the user's browser. Standalone Markdown edits carry captured board/content
|
|
127
|
+
revisions; a refused save keeps the current editor draft. Copy that draft before
|
|
128
|
+
closing the editor or switching boards; unsaved drafts do not survive remounts.
|
|
129
|
+
|
|
130
|
+
**Host constraints still apply.** Native PMX controls work without child frames.
|
|
131
|
+
HTML, Mermaid, JSON/graph and nested app renderers need the host to permit local
|
|
132
|
+
`srcdoc` frames. The portable MCP Apps default with empty `frameDomains` is
|
|
133
|
+
`frame-src 'none'`; there is no portable local-frame permission, so full rich
|
|
134
|
+
rendering in ChatGPT is an outstanding native-host check. The local browser test
|
|
135
|
+
explicitly enables local frames for that case. External assets remain subject
|
|
136
|
+
to host CSP. File uploads, file-linked editing, shell/webview automation, external
|
|
137
|
+
MCP tool calls, approval resolution, and trusted-human operations such as board
|
|
138
|
+
deletion require the local workbench. Unsupported writes are refused, never
|
|
139
|
+
silently granted human authority.
|
|
140
|
+
|
|
141
|
+
Selecting nodes shares up to 20 selections plus 20 pins, with 700 characters
|
|
142
|
+
per node, through `ui/update-model-context` when supported. This does **not**
|
|
143
|
+
start inference. Use **ChatGPT's own composer** to ask about the canvas; the
|
|
144
|
+
embedded workbench has no second composer and sends no `ui/message` requests.
|
|
145
|
+
The normal local PMX composer remains unchanged. Multiple conversations may
|
|
146
|
+
connect, but share one active board and the `mcp` writer label; distinct
|
|
147
|
+
ChatGPT conversation identities are not yet represented in PMX presence.
|
|
148
|
+
Board switches clear selection.
|
|
149
|
+
Context synchronization runs behind the scenes, with no hosted footer. Failed
|
|
150
|
+
updates retry automatically, including unchanged context, and only a host
|
|
151
|
+
acknowledgement marks a delivery successful. Launcher results do not hydrate
|
|
152
|
+
the workbench, so a delayed result cannot roll back the polling stream.
|
|
153
|
+
|
|
154
|
+
Bulk preview and image hydration stay in `_meta.canvas`, not model-visible
|
|
155
|
+
`structuredContent`; the canvas revision is a projection digest, not a content
|
|
156
|
+
cursor. Before editing or appending a note, the assistant must use `pmx_read_node`
|
|
157
|
+
for its complete source and content revision. Never replace content from a
|
|
158
|
+
700-character selection excerpt. File-linked, oversized and non-Markdown nodes
|
|
159
|
+
are not editable through the assistant's focused note tools.
|
|
160
|
+
|
|
161
|
+
This is not a published or hosted ChatGPT plugin. Private ChatGPT testing needs
|
|
162
|
+
an account-supported local MCP connection or Secure MCP Tunnel; no HTTP MCP
|
|
163
|
+
endpoint is added here. Public deployment, authentication, tenant isolation,
|
|
164
|
+
directory submission and real ChatGPT validation remain separate work. Do not
|
|
165
|
+
expose the local workbench HTTP API as a public service.
|
|
166
|
+
|
|
167
|
+
### Test privately in ChatGPT
|
|
168
|
+
|
|
169
|
+
This is the MCP connection test, not a directory publication or packaged marketplace
|
|
170
|
+
plugin. No plugin manifest or invented plugin ID is needed for this step. Follow
|
|
171
|
+
OpenAI's [connection guide](https://developers.openai.com/plugins/deploy/connect-chatgpt)
|
|
172
|
+
and [Secure MCP Tunnel guide](https://developers.openai.com/api/docs/guides/secure-mcp-tunnels):
|
|
173
|
+
|
|
174
|
+
1. Enable **Settings → Security and login → Developer mode** in ChatGPT (subject
|
|
175
|
+
to your account/workspace policy).
|
|
176
|
+
2. Create a tunnel in [Platform tunnel settings](https://platform.openai.com/settings/organization/tunnels)
|
|
177
|
+
and associate it with your target **ChatGPT workspace**. Tunnel creation needs
|
|
178
|
+
Tunnels Read + Manage; running/selecting it needs Read + Use.
|
|
179
|
+
3. Install `tunnel-client` from the download link in those settings or its
|
|
180
|
+
[latest release](https://github.com/openai/tunnel-client/releases/latest).
|
|
181
|
+
Supply its runtime key privately as `CONTROL_PLANE_API_KEY` in your shell.
|
|
182
|
+
Do not paste the key into chat, commit it or put it in a command argument.
|
|
183
|
+
4. Run the following from the root of **this modified checkout**, with Bun installed:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
bun install --frozen-lockfile
|
|
187
|
+
bun run build
|
|
188
|
+
export PMX_CANVAS_WORKSPACE_ROOT="$HOME/pmx-chatgpt-test"
|
|
189
|
+
mkdir -p "$PMX_CANVAS_WORKSPACE_ROOT"
|
|
190
|
+
export PMX_CANVAS_ALLOW_WORKSPACE_SPLIT=1
|
|
191
|
+
export PMX_CANVAS_DISABLE_BROWSER_OPEN=1
|
|
192
|
+
unset PMX_CANVAS_URL PMX_CANVAS_DB_PATH PMX_CANVAS_STATE_FILE
|
|
193
|
+
tunnel-client init \
|
|
194
|
+
--sample sample_mcp_stdio_local \
|
|
195
|
+
--profile pmx-canvas \
|
|
196
|
+
--tunnel-id "<your actual tunnel ID>" \
|
|
197
|
+
--mcp-command "bun run '$PWD/src/cli/index.ts' --mcp-app"
|
|
198
|
+
tunnel-client doctor --profile pmx-canvas --explain
|
|
199
|
+
tunnel-client run --profile pmx-canvas
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Keep the process running in this shell with the workspace variables set. Run
|
|
203
|
+
only one client for this stdio tunnel. This uses a disposable test workspace;
|
|
204
|
+
its data persists under `~/pmx-chatgpt-test/.pmx-canvas/`. To test an existing
|
|
205
|
+
board instead, explicitly set that project's workspace root.
|
|
206
|
+
|
|
207
|
+
For an **unpublished preview tarball**, install it into a clean directory with
|
|
208
|
+
`bun add /absolute/path/pmx-canvas-chatgpt-preview.tgz`; skip the checkout build
|
|
209
|
+
and use `bun run '$PWD/node_modules/pmx-canvas/src/cli/index.ts' --mcp-app` as
|
|
210
|
+
the MCP command above. The tarball includes the built app HTML. Do not use
|
|
211
|
+
`bunx pmx-canvas` for this preview: that fetches the published version.
|
|
212
|
+
5. Open [ChatGPT Plugins](https://chatgpt.com/plugins), select **+**, name it
|
|
213
|
+
**PMX Canvas**, choose **Connection → Tunnel**, select your tunnel and create
|
|
214
|
+
the connection. Confirm discovery of **ten assistant tools**, including
|
|
215
|
+
`pmx_open_canvas`, plus the app-only `pmx_workbench_request` transport (eleven
|
|
216
|
+
descriptors in `tools/list`), then start a new conversation with it enabled.
|
|
217
|
+
|
|
218
|
+
Test both the conversation entrypoint and navigation sidebar launcher. Create a
|
|
219
|
+
board, add/edit/move a note, pin it, select it and send a question using
|
|
220
|
+
**ChatGPT's composer**. Selection alone must not start an answer. Ask ChatGPT to add a second
|
|
221
|
+
note using `pmx_add_note`; it should appear within a few seconds without replacing
|
|
222
|
+
the first. Reopen the app and verify persistence. With a second app/workbench
|
|
223
|
+
instance, verify that board switches clear selection and stale saves refuse
|
|
224
|
+
instead of overwriting another edit. Test HTML, Mermaid, and JSON/graph cards
|
|
225
|
+
separately and record any CSP frame denial; native controls passing does not
|
|
226
|
+
prove rich-frame support.
|
|
227
|
+
|
|
228
|
+
If a launcher is missing, record the ChatGPT account/workspace and discovered
|
|
229
|
+
`pmx_open_canvas` metadata; host entrypoint support is not proven by local tests.
|
|
230
|
+
After changing the server, restart the tunnel client, use **Refresh** on the
|
|
231
|
+
ChatGPT connection and start a new conversation. If the tunnel is missing, check
|
|
232
|
+
its workspace association and permissions first. Do not substitute the workbench
|
|
233
|
+
HTTP URL for an MCP endpoint.
|
|
234
|
+
|
|
45
235
|
## Composite tools (recommended)
|
|
46
236
|
|
|
47
237
|
Action-discriminated tools that consolidate the single-purpose tools. Each maps
|
|
@@ -62,7 +252,7 @@ its `action` to the same operation the legacy tool used, so results are identica
|
|
|
62
252
|
| `canvas_ax_state` | `get` · `set-focus` · `set-policy` · `report-capability` · `presence` · `set-presence` | `canvas_get_ax`, `canvas_set_ax_focus`, `canvas_set_ax_policy`, `canvas_report_host_capability` |
|
|
63
253
|
| `canvas_ax_work` | `add` · `update` · `annotate` | `canvas_add_work_item`, `canvas_update_work_item`, `canvas_add_review_annotation` |
|
|
64
254
|
| `canvas_ax_gate` | `request` · `resolve` · `await` × kind `approval` \| `elicitation` \| `mode` | `canvas_request_approval`, `canvas_resolve_approval`, `canvas_await_approval`, `canvas_request_elicitation`, `canvas_respond_elicitation`, `canvas_await_elicitation`, `canvas_request_mode`, `canvas_resolve_mode`, `canvas_await_mode` (9 → 1) |
|
|
65
|
-
| `canvas_ax_timeline` | `read` · `record-event` · `add-evidence` · `send-steering` · `reads` | `canvas_get_ax_timeline`, `canvas_record_ax_event`, `canvas_add_evidence`, `canvas_send_steering` (`reads` is
|
|
255
|
+
| `canvas_ax_timeline` | `read` · `record-event` · `add-evidence` · `send-steering` · `reads` · `read-status` | `canvas_get_ax_timeline`, `canvas_record_ax_event`, `canvas_add_evidence`, `canvas_send_steering` (`reads` is the context read log; `read-status` is per node: last read, by whom, at which content revision) |
|
|
66
256
|
| `canvas_ax_delivery` | `claim` (long-polls with `timeoutMs`) · `mark` | `canvas_claim_ax_delivery`, `canvas_mark_ax_delivery` |
|
|
67
257
|
| `canvas_intent` | `signal` · `update` · `clear` | _(new — Ghost Cursor of Intent; no legacy standalone tool)_ |
|
|
68
258
|
| `canvas_board` | `list` · `get` · `create` · `create-from` · `open` · `update` | Boards, explicit shared-workbench switching, README designation, and transactional inactive copies |
|
|
@@ -133,7 +323,7 @@ Announce the spatial move you are **about** to make so the canvas paints a faint
|
|
|
133
323
|
pre-commit placeholder (a "ghost"). The human sees the next move forming — and can
|
|
134
324
|
veto it — before the mutation lands.
|
|
135
325
|
|
|
136
|
-
- `signal` — register an intent: `kind` (`create` \| `move` \| `connect` \| `remove` \| `edit`) plus the anchor it renders against (`position` for
|
|
326
|
+
- `signal` — register an intent: `kind` (`create` \| `move` \| `connect` \| `remove` \| `edit`) plus the anchor it renders against (`position` for move, optional for create; `nodeId` for move/edit/remove; `edge` for connect). A create without `position` forms where the canvas will auto-place the node, and the linked create lands there. Optional `label`, `reason`, `confidence` (0..1 → ghost opacity), `seq` (staged-batch ordering), `ttlMs` (default ~8s), and a stable `id` to update/clear later.
|
|
137
327
|
- `update` — patch a live intent by `id` (position/label/reason/confidence/ttlMs).
|
|
138
328
|
- `clear` — abandon/dissolve it explicitly. Normal linked mutations settle automatically.
|
|
139
329
|
|
|
@@ -172,6 +362,11 @@ as a writer operation or refreshing agent presence. Viewport translation uses
|
|
|
172
362
|
screen-space canvas-area coordinates (`screen = world * scale + viewport`). Fit
|
|
173
363
|
dimensions and padding likewise refer to the canvas area after excluding the
|
|
174
364
|
tool rail, top bar, and surrounding page chrome—not the full browser window.
|
|
365
|
+
The canvas area starts right of the 52 px tool rail and below the 44 px top bar
|
|
366
|
+
in the default layout, so world `(wx, wy)` lands at browser window point
|
|
367
|
+
`(52 + wx * scale + x, 44 + wy * scale + y)`. To set the camera directly, use
|
|
368
|
+
HTTP `POST /api/canvas/viewport` with `{x, y, scale}` or with `{center: {x, y},
|
|
369
|
+
scale?}` to centre a world point in the canvas area (see `docs/http-api.md`).
|
|
175
370
|
Use `canvas_ax_state { action: "set-presence", cursor, focusNodeId }` when the
|
|
176
371
|
agent intends to publish its own cursor or attention target.
|
|
177
372
|
|
|
@@ -185,7 +380,7 @@ legacy tools the composites replaced.
|
|
|
185
380
|
| Tool | Description |
|
|
186
381
|
|------|-------------|
|
|
187
382
|
| `canvas_batch` | Run a batch of canvas operations with `$ref` support |
|
|
188
|
-
| `canvas_pin_nodes` | Pin nodes to include in agent context |
|
|
383
|
+
| `canvas_pin_nodes` | Pin nodes to include in agent context (`nodeIds`, `mode` set/add/remove, optional `reason` shown to the human) |
|
|
189
384
|
| `canvas_invoke_command` | Invoke a registry command (`pmx.plan`, `pmx.execute`, `pmx.promote-context`, `pmx.summarize`, `pmx.review`); records a `command` agent-event, unknown names rejected |
|
|
190
385
|
| `canvas_ax_interaction` | Submit one capability-gated AX interaction envelope (`{ type, sourceNodeId, payload }`) that maps onto an AX operation; the server re-validates and clamps sandboxed surfaces to their own node |
|
|
191
386
|
| `canvas_ingest_activity` | Ingest a harness-forwarded agent activity (tool/session event); the board auto-reacts with kind-driven, overridable defaults (failure → work item + review + evidence; `tool-result`+success → evidence). Makes AX bidirectional |
|
|
@@ -296,7 +491,10 @@ wanted. Omitting both `consumer` and `since` starts from the beginning; explicit
|
|
|
296
491
|
`since` does not update a cursor. The budget is UTF-16 code units (default
|
|
297
492
|
16,000; maximum 100,000), not model tokens. Pins are prioritized. If an entry is
|
|
298
493
|
truncated, follow its source board/card IDs with `canvas_board get` and
|
|
299
|
-
`canvas_node get` rather than inferring the missing text.
|
|
494
|
+
`canvas_node { action: "get", board, id, full: true }` rather than inferring the missing text.
|
|
495
|
+
Full node reads record only that node's delivered content revision; compact metadata
|
|
496
|
+
does not. Inspect `canvas_ax_timeline { action: "read-status", board }` and re-read
|
|
497
|
+
stale cards before acting. Read marks prove delivery, not understanding or approval.
|
|
300
498
|
|
|
301
499
|
### Skills extension (SEP-2640)
|
|
302
500
|
|
package/docs/node-types.md
CHANGED
|
@@ -228,15 +228,39 @@ Specs preserve top-level `state` and element-level `repeat`, `watch`, and
|
|
|
228
228
|
`$state` only reads a value. Nested repeats can use `statePath: { "$item": "tasks" }`
|
|
229
229
|
to iterate the current item's tasks. Watchers run after changes, not on mount.
|
|
230
230
|
|
|
231
|
-
**
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
`
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
231
|
+
**Literal form values are bound for you.** In shadcn 0.21, `Input`,
|
|
232
|
+
`Textarea`, `Select`, `Radio`, `Slider`, `ToggleGroup`, and `Tabs` ignore an
|
|
233
|
+
unbound `value`, and `ButtonGroup` ignores an unbound `selected`, so the control
|
|
234
|
+
would render empty. During validation PMX rewrites a literal value on these
|
|
235
|
+
props into `{ "$bindState": "/form/<elementKey>" }` and seeds
|
|
236
|
+
`state.form.<elementKey>` with it (adding `-2`, `-3`, … if that field already
|
|
237
|
+
exists). This input:
|
|
238
|
+
|
|
239
|
+
```json
|
|
240
|
+
{ "type": "Input", "props": { "label": "Postcode", "value": "nw1 6xe" } }
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
is stored as (bare elements are wrapped under the key `root`):
|
|
244
|
+
|
|
245
|
+
```json
|
|
246
|
+
{
|
|
247
|
+
"root": "root",
|
|
248
|
+
"state": { "form": { "root": "nw1 6xe" } },
|
|
249
|
+
"elements": {
|
|
250
|
+
"root": {
|
|
251
|
+
"type": "Input",
|
|
252
|
+
"props": { "label": "Postcode", "name": "root", "value": { "$bindState": "/form/root" } }
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
PMX does not rewrite `$state` or other expressions, controls inside a `repeat`
|
|
259
|
+
(one path would be shared by every item), or specs whose `state.form` is not an
|
|
260
|
+
object. For those it returns a `warnings` array naming the element/property from
|
|
261
|
+
`POST /api/canvas/json-render`, `POST /api/canvas/schema/validate`, and
|
|
262
|
+
`canvas_render` actions `add-json-render` and `validate`. Bind them yourself
|
|
263
|
+
when the field needs a known path for actions, watchers, or `$state` reads:
|
|
240
264
|
|
|
241
265
|
```json
|
|
242
266
|
{
|
|
@@ -259,7 +283,7 @@ Inside repeats, use `$bindItem` with seeded item data. `Checkbox`/`Switch`
|
|
|
259
283
|
honor literal `checked`, `Toggle` honors literal `pressed`, and `Tabs` honors
|
|
260
284
|
`defaultValue` for an uncontrolled initial tab. `Pagination` reads `page`
|
|
261
285
|
directly; `DropdownMenu.value` is an output binding for selection, not a
|
|
262
|
-
displayed selection. These
|
|
286
|
+
displayed selection. These are neither rewritten nor warned about.
|
|
263
287
|
Use two-way bindings whenever edits or form checks must participate in state.
|
|
264
288
|
|
|
265
289
|
Cards keep their body in `children` and accept
|
|
@@ -82,7 +82,7 @@ The remaining items were re-analyzed with pros/cons and tiered; the maintainer a
|
|
|
82
82
|
|
|
83
83
|
## Added during rail-chrome-v2 (2026-08-23, phases 1–5)
|
|
84
84
|
|
|
85
|
-
Recorded while the redesign's phases land so nothing rides in silently.
|
|
85
|
+
Recorded while the redesign's phases land so nothing rides in silently. The redesign's plan was retired into `docs/design.md` and `docs/ax-state-contract.md` (2026-10-04).
|
|
86
86
|
|
|
87
87
|
| Item | What's open |
|
|
88
88
|
|---|---|
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Plan 015 — Design foundation (wave 0)
|
|
2
|
+
|
|
3
|
+
**Status:** Wave 0 done (2026-10-05): steps 1–4 are on main.
|
|
4
|
+
**Date:** 2026-10-05
|
|
5
|
+
**Source:** [design.md](../design.md#build-plan) wave 0 ("ships alone, first"); vision moves 5, 9, 10.
|
|
6
|
+
Drawings: [`TokenSystem`](../design/TokenSystem.dc.html), [`Tokens`](../design/Tokens.dc.html),
|
|
7
|
+
[`Themes`](../design/Themes.dc.html), [`Chrome`](../design/Chrome.dc.html),
|
|
8
|
+
[`Context`](../design/Context.dc.html), [`Main`](../design/Main.dc.html) / [`Light`](../design/Light.dc.html).
|
|
9
|
+
|
|
10
|
+
## Why first
|
|
11
|
+
|
|
12
|
+
Every later wave paints with the meaning colours, the scale and the font. Today none of them
|
|
13
|
+
exist: pins and agent attention borrow `--c-warn`, the light theme hard-codes `#4BBCFF` for pins,
|
|
14
|
+
Plex is named in `--font` but not bundled (controls fall back to Arial or the system font), and
|
|
15
|
+
there is no type, spacing, radius or elevation scale and no check that keeps any of it honest.
|
|
16
|
+
|
|
17
|
+
## Wave done when (from design.md)
|
|
18
|
+
|
|
19
|
+
No control renders in a fallback font; the token test passes for all nine themes; the reference
|
|
20
|
+
cases pass at 600, 1024 and 1920 px; the demo board is regenerated.
|
|
21
|
+
|
|
22
|
+
## Steps
|
|
23
|
+
|
|
24
|
+
Each step is its own commit and leaves the suite green.
|
|
25
|
+
|
|
26
|
+
### 1. Meaning and scale tokens, with the token test (no component changes)
|
|
27
|
+
|
|
28
|
+
- Add the meaning layer to every theme block in `global.css` and `surface-theme.css`:
|
|
29
|
+
`--c-pin`, `--c-agent`, `--c-rel-supports`, `--c-rel-contradicts`, `--c-rel-cites`,
|
|
30
|
+
`--c-rel-derived`, `--c-rel-informs`, `--c-rel-related`. `--c-warn` already exists; it takes the
|
|
31
|
+
drawn value (changes Light `#c89b2a` → `#94600A` and Sepia `#A97B1E` → `#8A5A00`, with their
|
|
32
|
+
fixed `--c-warn-*` copies; every other theme already matches).
|
|
33
|
+
- Values come from `Themes.dc.html` `renderVals()`. Where the drawing is silent (cites, derived,
|
|
34
|
+
informs, related outside Harbor) they are tuned here — see the table below.
|
|
35
|
+
- Add the scale to `:root` in both files: `--fs-meta|ui|body|title|heading` (11/12/13/15/20),
|
|
36
|
+
`--space-1|2|3|4|6|8` (4/8/12/16/24/32), `--r-control|node|overlay|pill` (6/10/14/999),
|
|
37
|
+
`--e-1`, `--e-2` (shadows as drawn on `Tokens.dc.html`).
|
|
38
|
+
- Token test (`tests/unit/theme-meaning-tokens.test.ts`), all nine themes:
|
|
39
|
+
- every meaning token is present, in both files, with the same value;
|
|
40
|
+
- each has at least 3:1 contrast on that theme's `--c-panel`;
|
|
41
|
+
- meaning colours are apart: OKLab distance ≥ 0.06 (≈3× a just-noticeable difference)
|
|
42
|
+
between `pin`/`agent`/`warn`, between each of those and every relation ink, and between
|
|
43
|
+
relation inks.
|
|
44
|
+
- Nothing consumes the new tokens yet, so nothing on screen changes except the warning amber in
|
|
45
|
+
Light and Sepia.
|
|
46
|
+
|
|
47
|
+
**Verify:** `bun run test:unit` (token + surface sync tests), `bun run build`, `bun run lint`.
|
|
48
|
+
|
|
49
|
+
### 2. Bundle IBM Plex; every control inherits the font
|
|
50
|
+
|
|
51
|
+
- Ship Plex Sans (400/500/600) and Plex Mono (400/500) woff2 with the client bundle, `@font-face`
|
|
52
|
+
in `global.css`, no CDN. Confirm the npm `files` list carries them.
|
|
53
|
+
*Done:* `@fontsource` packages are dev dependencies; `scripts/copy-fonts.ts` copies the latin
|
|
54
|
+
and latin-ext subsets (192 KB) and the OFL licence to `dist/canvas/fonts/`, which ships. The
|
|
55
|
+
single-file MCP app inlines them as data URIs. HTML nodes, json-render and exports still use the
|
|
56
|
+
`--font-sans`/`--font-mono` aliases without the faces until the frame host (wave 4).
|
|
57
|
+
- Rename `--font`/`--mono` to `--font-ui`/`--font-code` (no aliases); `button, input, select,
|
|
58
|
+
textarea { font: inherit }`; remove Arial fallbacks.
|
|
59
|
+
- Check: a browser test that every visible control computes `--font-ui` and that the Plex faces
|
|
60
|
+
are loaded (`document.fonts`).
|
|
61
|
+
*Done:* in `reference-pane.pw.ts`. Before: 365 of 400 controls on the demo board rendered in
|
|
62
|
+
Arial and no Plex face loaded; after: 0 of 389, all five faces loaded.
|
|
63
|
+
|
|
64
|
+
### 3. Pins and agent marks move to their meaning tokens
|
|
65
|
+
|
|
66
|
+
Split into three slices after reading the code (2026-10-05):
|
|
67
|
+
|
|
68
|
+
**3a. Node states and the context pin (done).** Built to `Context.dc.html` and `Chrome.dc.html`:
|
|
69
|
+
- In context: 1.5 px `--c-pin` border, header tinted 12% pin, pin-blue kind icon, and the header
|
|
70
|
+
pin control always visible — outline pin in a circle when out of context, the filled `--c-pin`
|
|
71
|
+
badge (`--c-on-pin` glyph, new token) when in. The light theme's hard-coded `#4BBCFF` overrides
|
|
72
|
+
are gone.
|
|
73
|
+
- The amber "attention" halo was not agent activity: it was semantic attention derived from pins
|
|
74
|
+
(primary = the pinned nodes, secondary = their unpinned neighbours, plus a glow around pinned
|
|
75
|
+
clusters). Decided with the maintainer: pin style only — the halos, neighbour halos and the
|
|
76
|
+
focus field (`FocusFieldLayer`) are removed. The change pulse stays, in the neutral accent.
|
|
77
|
+
- The violet agent bar (2.5 px `--c-agent` across the header) marks live agent edits
|
|
78
|
+
(`agent-mutating`), replacing the accent-blue shimmer; wave 1 extends it to read, created and
|
|
79
|
+
edited. The design session corrected the Context board's "Today" row to match.
|
|
80
|
+
- Selection is a ring with a gap (3 px background, 2 px accent) outside the border, so pin styling
|
|
81
|
+
survives it; base nodes take `--r-node` and `--e-1`.
|
|
82
|
+
|
|
83
|
+
**3b. Node header overflow (done).** Per `Chrome.dc.html`: expand and the context pin always show;
|
|
84
|
+
⋯ and × appear on hover or keyboard focus. ⋯ opens the existing node menu (the same one as
|
|
85
|
+
right-click), which already holds Collapse, Rename and Delete; "Open in new tab" (open as site)
|
|
86
|
+
moves into it from the header. The drawn "Ask agent" entry has no function yet and waits for one;
|
|
87
|
+
"Set as README" stays in the markdown card footer for now.
|
|
88
|
+
|
|
89
|
+
**3c. The rest of the old colours (done).**
|
|
90
|
+
- Every remaining `--c-warn` use was sorted by meaning. Pin blue: the command-bar chips, the
|
|
91
|
+
context pin bar, the expanded overlay's pinned state and toggle, the pin HUD and the "Context
|
|
92
|
+
updated" toast. Agent violet: the "agent yielded" pill and a status node's active tool.
|
|
93
|
+
`--c-rel-informs`: `depends-on` edges. Neutral (accent or muted): generic toast and history
|
|
94
|
+
borders, cluster/neighbourhood toasts, group drop feedback, the budget bar at rest, "saving",
|
|
95
|
+
webpage loading, the "required" source tag. Real warnings stay amber: approval gates and
|
|
96
|
+
waiting phases, blocked status, reconnecting, budget warnings, image and app warnings, pending
|
|
97
|
+
asks.
|
|
98
|
+
- Tints are `color-mix(in srgb, var(--c-…) N%, transparent)`; the fixed alpha copies,
|
|
99
|
+
`--c-warn-alt`, `--c-glow-accent` and `--c-thinking` are gone (`--c-agent` replaces it).
|
|
100
|
+
- `tests/unit/raw-hex-guard.test.ts`: no hex colour in client code or outside `global.css`'s theme
|
|
101
|
+
blocks. Allowlisted as content, not theme: group frame colours, the annotation default, the
|
|
102
|
+
white behind webpage previews, and the embedded Mermaid viewer's fallback (goes in wave 4).
|
|
103
|
+
|
|
104
|
+
**3d. Kind colours and the writer palette (done).** Per the design session's rule in `design.md`
|
|
105
|
+
(2b74c7b4): kinds carry no colour. Type icons, group kind dots and minimap rectangles are
|
|
106
|
+
`--c-muted`; the minimap marks only meaning — pinned nodes in `--c-pin`, nodes an agent is
|
|
107
|
+
editing in `--c-agent`. `KIND_COLOR`, the `--kind-accent` rules and `--c-purple` are gone (agent
|
|
108
|
+
chrome that borrowed purple now uses `--c-agent`). Writers are told apart by name and initial: the
|
|
109
|
+
writer palette and the per-agent `agentIdentityHue` are gone; an agent's cursor, chip, minimap dot
|
|
110
|
+
and activity rows take `--c-agent`, a subagent's `--c-subagent`. Trace categories go neutral except
|
|
111
|
+
subagent rows.
|
|
112
|
+
|
|
113
|
+
### 4. Theme renames and the wave's definition of done (done)
|
|
114
|
+
|
|
115
|
+
- `dark` → `harbor` (label Harbor, still the default) and `light` → `daylight` (label Daylight) in
|
|
116
|
+
`src/shared/themes.ts`, the CSS selectors, the defaults (state, persistence, client), the command
|
|
117
|
+
palette toggle (now by scheme), the CLI help, the bundled Copilot extension (`?theme=daylight`),
|
|
118
|
+
docs, skills, CLAUDE.md and AGENTS.md. No aliases: a stored or passed `dark`/`light` falls back
|
|
119
|
+
to Harbor, so anyone who had chosen the light theme picks Daylight once.
|
|
120
|
+
- Viewer scheme parameters stay `dark`/`light` (the json-render viewer and embedded MCP apps speak
|
|
121
|
+
schemes, not theme ids); the static export maps its scheme to Harbor or Daylight for surface
|
|
122
|
+
documents.
|
|
123
|
+
- Daylight takes the drawn neutral surfaces from `Main.dc.html` (`#F6F7F9` / `#FFFFFF` /
|
|
124
|
+
`#F2F4F7` / `#DDE2E9`, text `#0B1726`, ok `#1E7F51`, danger `#B8344A`) instead of beige.
|
|
125
|
+
- Definition of done: reference cases at 600, 1024 and 1920 px in the e2e gate; the token test
|
|
126
|
+
covers all nine themes; the demo board regenerated (deterministic: two runs, same file).
|
|
127
|
+
|
|
128
|
+
## Step 1 colour table
|
|
129
|
+
|
|
130
|
+
Drawn values are from `Themes.dc.html`; *tuned* values are chosen in this plan.
|
|
131
|
+
|
|
132
|
+
| Theme | pin | agent | warn | supports | contradicts | cites | derived | informs | related |
|
|
133
|
+
|---|---|---|---|---|---|---|---|---|---|
|
|
134
|
+
| Harbor (`dark`) | `#4BBCFF` | `#B388FF` | `#f4c542` | `#5FCFC0` | `#E59B6B` | `#7C9CFF` | `#D5DEEA` | `#E58FB0` | `#8ea3bd` |
|
|
135
|
+
| Daylight (`light`) | `#1A7ABF` | `#7C4DDB` | `#94600A` | `#1E8C80` | `#B8572A` | *`#4257C9`* | *`#4A5566`* | *`#B03F72`* | *`#5c6b80`* |
|
|
136
|
+
| High contrast | `#00ffff` | `#e040fb` | `#ffff00` | `#00e5c0` | `#ff8a00` | *`#8c9eff`* | *`#ffffff`* | *`#ff80ab`* | *`#aaaaaa`* |
|
|
137
|
+
| Midnight | Harbor | Harbor | `#F5C452` | Harbor | Harbor | Harbor | Harbor | Harbor | *`#8D96BC`* |
|
|
138
|
+
| Sepia | `#1F6FA8` | `#7046A6` | `#8A5A00` | `#1E8C80` | `#A3461C` | *`#3D50B0`* | *`#4E4433`* | *`#9C3A66`* | *`#6B6A66`* |
|
|
139
|
+
| Arctic | `#6EC1F5` | Harbor | `#E8C476` | Harbor | Harbor | Harbor | Harbor | Harbor | *`#939DB1`* |
|
|
140
|
+
| Ember | Harbor | Harbor | `#F5B944` | Harbor | Harbor | Harbor | Harbor | Harbor | *`#96948D`* |
|
|
141
|
+
| Forest | Harbor | Harbor | `#E0B84F` | `#4FC9D9` | Harbor | Harbor | Harbor | Harbor | *`#8BA695`* |
|
|
142
|
+
| Volt | Harbor | Harbor | `#E4C465` | Harbor | Harbor | Harbor | Harbor | Harbor | *`#9CA49C`* |
|
|
143
|
+
|
|
144
|
+
"Related" is the theme's own `--c-muted` except in Sepia, where muted brown sits too close to the
|
|
145
|
+
warning amber.
|
|
146
|
+
|
|
147
|
+
## Decided 2026-10-05
|
|
148
|
+
|
|
149
|
+
- **Tight pairs stay as drawn.** The closest drawn pairs (OKLab distance) pass the 0.06 floor:
|
|
150
|
+
Forest supports/pin 0.075, Daylight contradicts/warning 0.075, Sepia contradicts/warning 0.070.
|
|
151
|
+
The alternatives were compared side by side and rejected: Forest teal `#5FCFC0` lands on the
|
|
152
|
+
done green (0.062), and a redder contradicts in the light themes lands on `--c-danger`. Sepia
|
|
153
|
+
contradicts is already close to its error red (0.038); red is not a meaning colour, so the
|
|
154
|
+
test allows it.
|
|
155
|
+
- **Light-theme elevation.** Daylight and Sepia override `--e-1`/`--e-2` with ink-tinted shadows
|
|
156
|
+
(10/8% and 18%) instead of the drawn black ones; recorded as the one per-theme scale exception
|
|
157
|
+
in `design.md`.
|
|
158
|
+
|
|
159
|
+
## Decided 2026-10-06
|
|
160
|
+
|
|
161
|
+
- **Folders, not levels.** Folders are the one browsing hierarchy; portals and links stay a graph
|
|
162
|
+
the agent navigates (links/backlinks on `canvas://boards`) and Map/Graph draw (vision move 0,
|
|
163
|
+
`design.md`). Unblocks wave 2.
|
|
164
|
+
- **The theme rename ships as a breaking change** (no aliases): the release changelog lists it
|
|
165
|
+
under Breaking, with `--theme=light`, `PMX_CANVAS_THEME=light`, a saved Light choice and
|
|
166
|
+
`?theme=light` all falling back to Harbor.
|
|
167
|
+
|
|
168
|
+
## Open
|
|
169
|
+
|
|
170
|
+
- **Near a pin (decided 2026-10-05).** Nodes near a pin (up to 5 within 600 px,
|
|
171
|
+
`findNeighborhoods`) are treated as related, but the agent gets them only as titles, in
|
|
172
|
+
`canvas://pinned-context` and `canvas://spatial-context`; the main brief `canvas://context` does
|
|
173
|
+
not include them. Decided with the maintainer: no near-a-pin mark on the board until the brief
|
|
174
|
+
carries neighbours (vision move 1's ranker). The mark and that brief change ship together, in
|
|
175
|
+
wave 1; the design session draws the state ahead of it. Not part of wave 0. **Built** in plan 016
|
|
176
|
+
slice 4a (brief + chip); the hover lines, drag preview and Updates entry follow in 4b.
|
|
177
|
+
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Plan 016 — Context made visible (wave 1)
|
|
2
|
+
|
|
3
|
+
**Status:** Slices 1–3 and 4a done (2026-10-05); 4b (tethers, drag preview, Updates entry) next.
|
|
4
|
+
**Date:** 2026-10-05
|
|
5
|
+
**Source:** [design.md](../design.md#build-plan) wave 1; vision Part 1 bet (item 4), moves 0a, 2, 7.
|
|
6
|
+
Drawings: [`AgentContext`](../design/AgentContext.dc.html), [`Context`](../design/Context.dc.html),
|
|
7
|
+
[`NearPin`](../design/NearPin.dc.html), [`BoardPins`](../design/BoardPins.dc.html),
|
|
8
|
+
[`Home`](../design/Home.dc.html) (Folders).
|
|
9
|
+
|
|
10
|
+
## Done when (from design.md)
|
|
11
|
+
|
|
12
|
+
From the board alone, a person can answer "did the agent read what I pinned, and is its copy
|
|
13
|
+
current?" The evaluation in `docs/evals/` reads the same data.
|
|
14
|
+
|
|
15
|
+
## What exists and what is missing
|
|
16
|
+
|
|
17
|
+
| Need | Today | Gap |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| Who wrote a node | `createdBy` / `lastEditedBy` per node, per-board `contentRevision` (plan 014) | none |
|
|
20
|
+
| What an agent read | `context_reads`: per read, which **pinned** ids were in the payload (plan 011) | every delivered node, and the content revision it had when delivered |
|
|
21
|
+
| Who pinned a node | `context_pins (board_id, node_id)` | who, when, and an optional why |
|
|
22
|
+
| Board pins | none | `board_pins`, brief tiers, `canvas_board pin/unpin` in all four layers |
|
|
23
|
+
| Neighbours in the brief | titles only, in `canvas://pinned-context` / `spatial-context` | the brief carries them (title + summary, reason "near <pin>") |
|
|
24
|
+
|
|
25
|
+
## Slices
|
|
26
|
+
|
|
27
|
+
Each slice is its own commit and leaves the suite green. Design ships with its function: no mark
|
|
28
|
+
appears before the data that backs it.
|
|
29
|
+
|
|
30
|
+
### 1. Data: what was read, at which revision, and who pinned (no UI)
|
|
31
|
+
|
|
32
|
+
Split in two: 1a (reads) is enough for read / not read / changed since read; 1b (pin attribution)
|
|
33
|
+
adds "pinned by the agent".
|
|
34
|
+
|
|
35
|
+
- **1a, done.** `context_reads` records every node whose serialized form was delivered (not just
|
|
36
|
+
pins) and its `contentRevision` at read time, in a `read_nodes` column. Same "delivered" rule as
|
|
37
|
+
plan 011 (a serialized node carrying the id; a bare id list or a title does not count). The reader
|
|
38
|
+
side reports every node-shaped id it delivered (`readNodeIds`, so an MCP server attached to a
|
|
39
|
+
daemon only posts the record); the server keeps the ids that are nodes on the read board and
|
|
40
|
+
stamps their revisions. `ax.reads.status` (`GET /api/canvas/ax/context-status`,
|
|
41
|
+
`canvas_ax_timeline { action: "read-status" }`, `PmxCanvas.getNodeReadStatus`) returns, per node,
|
|
42
|
+
the latest agent read: when, by whom, at which revision.
|
|
43
|
+
- **1b, done.** Pins record who pinned them (`pinnedBy`, the same actor attribution as node
|
|
44
|
+
authorship: a person only with the workbench token), when, and an optional `reason`
|
|
45
|
+
(`canvas_pin_nodes` / `POST /api/canvas/context-pins` / `setContextPins(..., reason)`). Pins that
|
|
46
|
+
stay keep their attribution; undo restores it; it persists with the board and its snapshots
|
|
47
|
+
(`meta` on `context_pins` and `snapshot_pins`). Pins from before have an unknown pinner. The read
|
|
48
|
+
status returns `pins` alongside `nodes`.
|
|
49
|
+
- `context_pins` gains `pinned_by` (actor attribution, same shape as `createdBy`), `pinned_at` and an
|
|
50
|
+
optional `reason`. Pins made before this have an unknown pinner, not a guessed human.
|
|
51
|
+
- One server read model, `nodeContextStatus(boardId)`: per node, last read (when, by whom, revision
|
|
52
|
+
read), current revision, pinned (by whom, when, why). Served over HTTP and SDK and MCP alongside
|
|
53
|
+
the existing context-reads surface (four layers in step); the workbench reads it through SSE.
|
|
54
|
+
- **Check:** unit tests for delivered-all-nodes, revision capture, pin attribution through every
|
|
55
|
+
write path (workbench → human, agent writes → agent id/source), and the status model.
|
|
56
|
+
|
|
57
|
+
### 2. Node marks: read, not read yet, changed since read, pinned by the agent, created, edited
|
|
58
|
+
|
|
59
|
+
Built to `AgentContext.dc.html`: eye + "read" (hover: who, when); eye-off + "not read" on a pin the
|
|
60
|
+
agent has not loaded since it was pinned; amber warn + "changed since read" when the current revision
|
|
61
|
+
is newer than the revision read; violet dot on the pin badge when an agent pinned it (hover: why);
|
|
62
|
+
"by <agent>" byline on agent-created nodes until a person edits them; violet bar + "edited" for the
|
|
63
|
+
session, with "see change" opening the diff and undo.
|
|
64
|
+
- **Check:** client tests per state; e2e at 600/1024/1920 that a pinned-then-changed node shows
|
|
65
|
+
"changed since read" after an agent read, and clears after the next read.
|
|
66
|
+
- **Done.** Decided with the maintainer (2026-10-05): the read / not read / changed since read marks
|
|
67
|
+
show on **pinned** nodes only (a full-layout read delivers every node, so marking all of them
|
|
68
|
+
would cover the board); unpinned reads go to the lens (slice 3). "by <agent>" and "edited" stay
|
|
69
|
+
until a person edits the node. One chip per node (`NodeContextMark`), glyph + word, explained
|
|
70
|
+
on hover; agent pins get the violet dot. The workbench refetches the read status on connect, on
|
|
71
|
+
pin changes and when a read lands (`context-status-changed`, sent to the browser only), and
|
|
72
|
+
ignores an answer for a board it has left. Not built: "see change" (diff and undo of an agent
|
|
73
|
+
edit) needs stored previous content, which revisions do not keep yet.
|
|
74
|
+
- **Note.** The demo board is generated through the API as agent writes, so its nodes show
|
|
75
|
+
"by sdk" until edited.
|
|
76
|
+
|
|
77
|
+
### 3. After a session: the agent-activity lens and receipt
|
|
78
|
+
|
|
79
|
+
The lens from `AgentContext.dc.html`: counts (read, created, edited, pinned) and the receipt rows
|
|
80
|
+
with their actions (Unpin, Undo). Data from presence activity, revisions and slice 1.
|
|
81
|
+
- **Done.** The existing session receipt (`agent-session-ended`) gains `context`: nodes read since
|
|
82
|
+
the session attached (presence now records `startedAt`), pins the agent made with their reason,
|
|
83
|
+
nodes it created and edited (the pre-session snapshot diff, filtered by authorship), and pinned
|
|
84
|
+
nodes changed since their latest read. A session that only read or pinned now gets a receipt.
|
|
85
|
+
The receipt card shows an amber line for out-of-date pins, Read / Pinned / Created / Edited rows
|
|
86
|
+
(Unpin on agent pins) and the lens switch "Dim untouched nodes". The read mark's hover says how
|
|
87
|
+
many times a node was read. Reads are counted per board in the session window, not per reader:
|
|
88
|
+
on a board with two agents at once, each receipt includes the other's reads.
|
|
89
|
+
The receipt also reads the board open when the session ends; a session whose board the person
|
|
90
|
+
switched away from mid-session reports the newly open board (known limit, 0.8.0 review).
|
|
91
|
+
- **Not built:** per-row Undo of an edit (needs stored previous content); the pre-session snapshot
|
|
92
|
+
restore still undoes the whole session. The drawn bottom-bar placement of the lens is folded into
|
|
93
|
+
the receipt card, which already sits where session results appear.
|
|
94
|
+
|
|
95
|
+
### 4. Near a pin: neighbours in the brief, and the near mark
|
|
96
|
+
|
|
97
|
+
- The brief (`canvas://context`) carries each pin's neighbours (up to 5 within 600 px, nearest
|
|
98
|
+
first) as title + short summary, reason "near <pin title>", never full content; they share the
|
|
99
|
+
budget after pinned cards.
|
|
100
|
+
- The `NearPin.dc.html` mark: dotted "near" chip, tethers on hovering a pin, the drag preview and
|
|
101
|
+
the single Updates entry that replaces "Neighborhood changed". "read" from slice 2 then also
|
|
102
|
+
covers neighbours honestly (title + summary delivered).
|
|
103
|
+
- **4a, done.** The brief carries `near` entries right after the pinned cards (each whole or
|
|
104
|
+
absent, title + summary capped at 280 characters, `near: { pinNodeId, pinTitle }`); a neighbour
|
|
105
|
+
that changed since the cursor arrives in full as `changed` instead; a first read delivers every
|
|
106
|
+
node in full, so it has no near entries. The client computes the same `findNeighborhoods` and
|
|
107
|
+
shows the dotted "near" / "near N" chip on unpinned nodes, with the pins and distances on hover.
|
|
108
|
+
- **4b, next.** Tethers and the 600 px radius when hovering a pin, the drag preview, the single
|
|
109
|
+
Updates entry that replaces "Neighborhood changed", the context chip's "N cards · M near" count,
|
|
110
|
+
and "Pin to send content" in the chip's hover. The maintainer agreed the design (2026-10-06) on
|
|
111
|
+
the condition that performance stays good:
|
|
112
|
+
- **Budget.** No added work per drag frame beyond the neighbour calculation for the board
|
|
113
|
+
(measured 0.8 ms at 1,000 nodes / 40 pins; guarded at < 8 ms by
|
|
114
|
+
`tests/unit/near-pin-performance.test.ts`) and the dragged card against the pins (O(pins)).
|
|
115
|
+
- **No fan-out.** The near map keeps its identity while no card's set of pins changes, so the
|
|
116
|
+
chips re-render only when a neighbourhood does, not on every frame (done ahead of 4b, guarded
|
|
117
|
+
by a client test). Distances, which change every frame, are computed only for an open hover.
|
|
118
|
+
- **Lines and radius** are drawn only while a pin is hovered or selected, for that pin alone, in
|
|
119
|
+
one SVG layer; nothing renders for pins at rest.
|
|
120
|
+
- **Check.** A headed drag on a 300-node board with 20 pins records no long task (> 50 ms)
|
|
121
|
+
attributable to near-a-pin.
|
|
122
|
+
|
|
123
|
+
### 5. Board pins
|
|
124
|
+
|
|
125
|
+
Per vision move 0a and `BoardPins.dc.html`: `board_pins` table (board, who, when); `canvas_board`
|
|
126
|
+
`pin`/`unpin` across PmxCanvas, HTTP, MCP and CLI; the tiered brief (active board's pins, then each
|
|
127
|
+
pinned board's README + pinned cards, then discovery); the context chip counts "N cards · M boards";
|
|
128
|
+
Home's pinned-boards section per `Home.dc.html`. Read status per board from slice 1.
|
|
129
|
+
|
|
130
|
+
### 6. The curation evaluation
|
|
131
|
+
|
|
132
|
+
`docs/evals/curation-effect.md`: one fixed task, run with the brief delivered on a curated and an
|
|
133
|
+
uncurated board, scored on criteria written before the first run (vision Part 1, item 4). It reads
|
|
134
|
+
the same `context_reads` data as slice 1 to confirm delivery before scoring effect. Running it
|
|
135
|
+
needs the maintainer's real agent sessions; the decision rule is the vision's.
|
|
136
|
+
|
|
137
|
+
## Out of wave 1
|
|
138
|
+
|
|
139
|
+
- "Suggested by the agent" (dashed node, accept/reject): needs agent suggestions as a function
|
|
140
|
+
(vision move 3, wave 5). The drawing exists; it ships with that function.
|
|
141
|
+
- Home Map and Graph (wave 2).
|
|
142
|
+
|
|
143
|
+
## Defaults (change if the maintainer disagrees)
|
|
144
|
+
|
|
145
|
+
- **Read by whom.** Any agent reader on that board counts; the mark shows the most recent reader
|
|
146
|
+
and time on hover. The workbench's own reads never count.
|
|
147
|
+
- **Changed since read** compares against the most recent read of that node by any agent.
|
|
148
|
+
- **Unknown pinner.** Existing pins show no agent dot; only pins attributed to an agent get it.
|