pmx-canvas 0.1.17 → 0.1.19
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/CHANGELOG.md +99 -0
- package/Readme.md +12 -5
- package/dist/types/server/canvas-serialization.d.ts +2 -0
- package/dist/types/server/canvas-state.d.ts +2 -0
- package/docs/RELEASE.md +153 -0
- package/docs/bun-webview-integration.md +296 -0
- package/docs/cli.md +140 -0
- package/docs/evals/e2e-cli-coverage.md +61 -0
- package/docs/http-api.md +191 -0
- package/docs/mcp.md +135 -0
- package/docs/node-types.md +244 -0
- package/docs/plans/.gitkeep +0 -0
- package/docs/plans/plan-001-semantic-watch-mvp.md +335 -0
- package/docs/plans/plan-002-human-attention-layer-design-spec.md +679 -0
- package/docs/plans/plan-003-human-attention-layer-implementation-plan.md +572 -0
- package/docs/reactive-canvas-proposal.md +578 -0
- package/docs/release-review-0.1.0.md +38 -0
- package/docs/screenshot.png +0 -0
- package/docs/screenshots/demo-workbench-dark.png +0 -0
- package/docs/screenshots/demo-workbench-light.png +0 -0
- package/docs/screenshots/welcome-dark.png +0 -0
- package/docs/screenshots/welcome-light.png +0 -0
- package/docs/sdk.md +92 -0
- package/package.json +2 -1
- package/skills/pmx-canvas/SKILL.md +2 -0
- package/skills/pmx-canvas/references/excalidraw-diagram-authoring.md +145 -0
- package/src/cli/agent.ts +17 -0
- package/src/mcp/canvas-access.ts +2 -0
- package/src/mcp/server.ts +12 -4
- package/src/server/canvas-serialization.ts +48 -0
- package/src/server/canvas-state.ts +18 -5
- package/src/server/server.ts +2 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# Node types
|
|
2
|
+
|
|
3
|
+
Canvas nodes are typed. Each type has a dedicated renderer, schema, and (for
|
|
4
|
+
structured types) a dedicated MCP tool. This page is the user-facing reference
|
|
5
|
+
for what each type is for and how to create one. For tool/HTTP/SDK signatures,
|
|
6
|
+
see [MCP tools](mcp.md), [HTTP API](http-api.md), and [SDK](sdk.md).
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
| Type | Purpose |
|
|
11
|
+
|------|---------|
|
|
12
|
+
| `markdown` | Rich markdown with rendered preview |
|
|
13
|
+
| `status` | Compact status indicator (phase, message, elapsed time) |
|
|
14
|
+
| `context` | Context cards, token usage, workspace grounding |
|
|
15
|
+
| `ledger` | Execution ledger summary |
|
|
16
|
+
| `trace` | Agent trace pills (tool calls, subagent activity) |
|
|
17
|
+
| `file` | Live file viewer with auto-update on disk changes |
|
|
18
|
+
| `image` | Image viewer (file paths, data URIs, URLs) |
|
|
19
|
+
| `webpage` | Persisted webpage snapshot with stored URL, extracted text, refresh |
|
|
20
|
+
| `mcp-app` | Tool-backed hosted MCP App iframes (Excalidraw, etc.) |
|
|
21
|
+
| `json-render` | Structured UI from JSON specs (cards, tables, forms) |
|
|
22
|
+
| `graph` | Charts (line, bar, pie, area, scatter, radar, stacked-bar, composed) |
|
|
23
|
+
| `html` | Self-contained HTML/JS in a sandboxed iframe |
|
|
24
|
+
| `web-artifact` | Bundled React/Tailwind artifact (full single-file app) |
|
|
25
|
+
| `group` | Spatial container/frame around other nodes |
|
|
26
|
+
|
|
27
|
+
Thread node types `prompt` and `response` exist internally for agent
|
|
28
|
+
conversation rendering and are not created through public APIs.
|
|
29
|
+
|
|
30
|
+
## Choosing the right visual tier
|
|
31
|
+
|
|
32
|
+
Three rendering tiers cover increasing levels of complexity. Pick the lowest
|
|
33
|
+
that fits the work — each step adds capability and bundle weight.
|
|
34
|
+
|
|
35
|
+
| Tier | Type | Use when | Bundle weight |
|
|
36
|
+
|------|------|----------|---------------|
|
|
37
|
+
| 1 | `json-render` | You can describe the UI as a spec (forms, tables, dashboards from a component catalog) | None — runtime already loaded |
|
|
38
|
+
| 2 | `html` | You have/can write self-contained HTML+JS (Chart.js, D3, custom widgets, interactive demos) | None — sandboxed iframe |
|
|
39
|
+
| 3 | `web-artifact` | You need a full React/Tailwind app with shadcn components, routing, or shared state | Build step |
|
|
40
|
+
|
|
41
|
+
## File nodes
|
|
42
|
+
|
|
43
|
+
File nodes display project files with line numbers and language detection.
|
|
44
|
+
When an agent edits a file through its normal tools, the canvas node updates
|
|
45
|
+
automatically via `fs.watch()`.
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
canvas_add_node({ type: 'file', content: 'src/server/index.ts' })
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Image nodes
|
|
52
|
+
|
|
53
|
+
Image nodes display local paths, remote URLs, and data URIs. File-backed and
|
|
54
|
+
HTTP(S)-backed images preserve provenance so agents can tell where evidence
|
|
55
|
+
came from. Nodes can carry validation status or warnings.
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
canvas_add_node({
|
|
59
|
+
type: 'image',
|
|
60
|
+
content: 'artifacts/dashboard.png',
|
|
61
|
+
data: {
|
|
62
|
+
validationStatus: 'passed',
|
|
63
|
+
validationMessage: 'Screenshot matches the requested dashboard state.',
|
|
64
|
+
},
|
|
65
|
+
})
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Webpage nodes
|
|
69
|
+
|
|
70
|
+
Webpage nodes store the source URL on the node, fetch the page server-side,
|
|
71
|
+
and cache extracted text for search, pins, and agent context. Saved canvases
|
|
72
|
+
keep enough information for an agent to refresh the node from the original
|
|
73
|
+
URL later.
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
canvas_add_node({ type: 'webpage', url: 'https://example.com/docs' })
|
|
77
|
+
canvas_refresh_webpage_node({ id: 'node-abc123' })
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## MCP App nodes
|
|
81
|
+
|
|
82
|
+
`mcp-app` nodes embed other MCP servers' UI resources (`ui://...`) directly
|
|
83
|
+
on the canvas as sandboxed iframes. Any server implementing the
|
|
84
|
+
[MCP Apps extension](https://modelcontextprotocol.io/docs/extensions/apps)
|
|
85
|
+
can be opened with `canvas_open_mcp_app`.
|
|
86
|
+
|
|
87
|
+
Generic `pmx-canvas node add --type mcp-app` is intentionally rejected —
|
|
88
|
+
these nodes need tool/session metadata. Use `canvas_open_mcp_app` (or the
|
|
89
|
+
`canvas_add_diagram` Excalidraw preset) instead.
|
|
90
|
+
|
|
91
|
+
### Excalidraw preset (hand-drawn diagrams)
|
|
92
|
+
|
|
93
|
+
[Excalidraw](https://github.com/excalidraw/excalidraw-mcp) ships a hosted MCP
|
|
94
|
+
server at `https://mcp.excalidraw.com/mcp`. PMX Canvas exposes a one-call
|
|
95
|
+
preset:
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
canvas_add_diagram({
|
|
99
|
+
elements: [
|
|
100
|
+
{ type: 'rectangle', id: 'a', x: 80, y: 120, width: 180, height: 80,
|
|
101
|
+
roundness: { type: 3 }, backgroundColor: '#a5d8ff', fillStyle: 'solid',
|
|
102
|
+
label: { text: 'Agent', fontSize: 18 } },
|
|
103
|
+
{ type: 'rectangle', id: 'b', x: 380, y: 120, width: 180, height: 80,
|
|
104
|
+
roundness: { type: 3 }, backgroundColor: '#d0bfff', fillStyle: 'solid',
|
|
105
|
+
label: { text: 'PMX Canvas', fontSize: 18 } },
|
|
106
|
+
{ type: 'arrow', id: 'a1', x: 260, y: 160, width: 120, height: 0,
|
|
107
|
+
startBinding: { elementId: 'a' }, endBinding: { elementId: 'b' },
|
|
108
|
+
label: { text: 'adds nodes' } },
|
|
109
|
+
],
|
|
110
|
+
title: 'Agent → Canvas',
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
For any other MCP App, call `canvas_open_mcp_app` directly with the server's
|
|
115
|
+
transport, tool name, and arguments.
|
|
116
|
+
|
|
117
|
+
## json-render nodes
|
|
118
|
+
|
|
119
|
+
`json-render` nodes turn structured JSON specs into rendered UI panels
|
|
120
|
+
(dashboards, tables, forms, cards) without writing HTML. PMX Canvas ships the
|
|
121
|
+
[`@json-render/*`](https://www.npmjs.com/package/@json-render/core) runtime
|
|
122
|
+
and component catalog (core + react + shadcn).
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
canvas_add_json_render_node({
|
|
126
|
+
title: 'Deploy status',
|
|
127
|
+
spec: {
|
|
128
|
+
root: 'card',
|
|
129
|
+
elements: {
|
|
130
|
+
card: { type: 'Card', props: { title: 'Deploy' }, children: ['status'] },
|
|
131
|
+
status: { type: 'Badge', props: { variant: 'default', text: 'Healthy' } },
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
`Badge` uses shadcn variants: `default`, `secondary`, `destructive`,
|
|
138
|
+
`outline`. Older saved specs using `label` or status variants such as
|
|
139
|
+
`success`/`warning` are normalized during validation.
|
|
140
|
+
|
|
141
|
+
Use `canvas_describe_schema` / `canvas_validate_spec` to introspect the
|
|
142
|
+
component catalog before building a spec.
|
|
143
|
+
|
|
144
|
+
## HTML nodes
|
|
145
|
+
|
|
146
|
+
`html` nodes render a self-contained HTML/JS document in a sandboxed iframe.
|
|
147
|
+
They sit between `json-render` (no custom JS) and `web-artifact` (full bundled
|
|
148
|
+
React app) — perfect for Chart.js, D3, custom widgets, and any HTML you can
|
|
149
|
+
write or paste.
|
|
150
|
+
|
|
151
|
+
The sandbox runs with `allow-scripts` only — no same-origin access, no
|
|
152
|
+
top-level navigation, no form submission. Inline `<script>` and CDN
|
|
153
|
+
`<script src>` both work. The canvas auto-injects its theme tokens
|
|
154
|
+
(`--c-*` and `--color-*` aliases) into the iframe `<head>` so artifacts can
|
|
155
|
+
match the active theme.
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
canvas_add_html_node({
|
|
159
|
+
title: 'Cost projection',
|
|
160
|
+
html: '<canvas id="c"></canvas><script src="https://cdn.jsdelivr.net/npm/chart.js"></script><script>...</script>',
|
|
161
|
+
})
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
A fragment without `<html>`/`<head>` is wrapped in a full document
|
|
165
|
+
automatically. Default size is 720×640.
|
|
166
|
+
|
|
167
|
+
## Web artifacts
|
|
168
|
+
|
|
169
|
+
A **web artifact** is a single-file, fully bundled HTML app (React + Tailwind
|
|
170
|
+
+ shadcn) the agent builds from TSX source. Use it when the work calls for a
|
|
171
|
+
real interactive app — charts, forms, mini-dashboards — beyond what a static
|
|
172
|
+
node or `html` snippet can express.
|
|
173
|
+
|
|
174
|
+
`canvas_build_web_artifact` takes source strings (`App.tsx`, optional
|
|
175
|
+
`index.css`, `main.tsx`, `index.html`, plus extra files), runs the bundled
|
|
176
|
+
web-artifacts-builder scripts, writes the self-contained HTML to
|
|
177
|
+
`.pmx-canvas/artifacts/<slug>.html`, and (by default) opens it in the canvas.
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
pmx-canvas web-artifact build --title "Dashboard" --app-file ./App.tsx --deps recharts --include-logs
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
The scaffold includes `recharts`. Pass `--deps name,name2` for additional
|
|
184
|
+
package dependencies. Failed or empty CLI bundles print `ok: false`, exit
|
|
185
|
+
non-zero, and do not create a canvas node.
|
|
186
|
+
|
|
187
|
+
The matching agent skill is at
|
|
188
|
+
[`skills/web-artifacts-builder/SKILL.md`](../skills/web-artifacts-builder/SKILL.md).
|
|
189
|
+
|
|
190
|
+
## Groups
|
|
191
|
+
|
|
192
|
+
Groups are spatial containers that visually contain other nodes. They render
|
|
193
|
+
as dashed-border frames with a title bar and optional accent color.
|
|
194
|
+
|
|
195
|
+
- Select 2+ nodes and click "Group" in the selection bar
|
|
196
|
+
- Right-click a group to ungroup
|
|
197
|
+
- Collapsing a group hides children and shows a summary
|
|
198
|
+
- By default, group creation preserves the children's current positions and
|
|
199
|
+
expands the frame around them
|
|
200
|
+
- Pass `childLayout` to auto-pack children (`grid`, `column`, `flow`)
|
|
201
|
+
- Pass explicit `x`, `y`, `width`, and `height` to create a manual frame and
|
|
202
|
+
lay children out inside it
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
canvas_create_group({ title: 'Auth Module', childIds: ['node-1', 'node-2'], color: '#4a9eff' })
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Edge types
|
|
209
|
+
|
|
210
|
+
All edges support labels, styles (solid/dashed/dotted), and animation.
|
|
211
|
+
|
|
212
|
+
| Type | Use case |
|
|
213
|
+
|------|----------|
|
|
214
|
+
| `flow` | Sequential steps, data flow |
|
|
215
|
+
| `depends-on` | Dependencies between tasks |
|
|
216
|
+
| `relation` | General relationships |
|
|
217
|
+
| `references` | Cross-references, evidence links |
|
|
218
|
+
|
|
219
|
+
## Schema-driven discovery
|
|
220
|
+
|
|
221
|
+
Agents don't have to guess node shapes. The running server exposes its create
|
|
222
|
+
schemas, json-render component catalog, and node-type examples:
|
|
223
|
+
|
|
224
|
+
- `canvas_describe_schema` / `GET /api/canvas/schema` — list all node-create
|
|
225
|
+
schemas, required fields, json-render components, and sample payloads
|
|
226
|
+
- `canvas_validate_spec` / `POST /api/canvas/schema/validate` — validate a
|
|
227
|
+
json-render spec or graph payload **without** creating a node
|
|
228
|
+
- `canvas_validate` / `GET /api/canvas/validate` — validate the current
|
|
229
|
+
layout for collisions, containment, and missing edge endpoints
|
|
230
|
+
- `canvas://schema` — the same data as an MCP resource
|
|
231
|
+
|
|
232
|
+
The CLI's `node schema` / `validate spec` subcommands surface the same data
|
|
233
|
+
from the terminal.
|
|
234
|
+
|
|
235
|
+
MCP node creation uses dedicated tools for structured node families. Read
|
|
236
|
+
`mcp.nodeTypeRouting` from `canvas_describe_schema` when in doubt:
|
|
237
|
+
`json-render` → `canvas_add_json_render_node`,
|
|
238
|
+
`graph` → `canvas_add_graph_node`,
|
|
239
|
+
`html` → `canvas_add_html_node`,
|
|
240
|
+
`web-artifact` → `canvas_build_web_artifact`,
|
|
241
|
+
`mcp-app` → `canvas_open_mcp_app`,
|
|
242
|
+
`group` → `canvas_create_group`.
|
|
243
|
+
Basic nodes (`markdown`, `status`, `file`, `image`, `webpage`) use
|
|
244
|
+
`canvas_add_node`.
|
|
File without changes
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Semantic Watch CLI MVP
|
|
3
|
+
status: draft
|
|
4
|
+
date: 2026-04-18
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Semantic Watch CLI MVP
|
|
8
|
+
|
|
9
|
+
## Summary
|
|
10
|
+
|
|
11
|
+
Add a new agent-facing CLI command:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pmx-canvas watch
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The command connects to the existing `/api/workbench/events` SSE stream and emits
|
|
18
|
+
compact, low-token summaries of **meaningful human intent changes** on the canvas.
|
|
19
|
+
|
|
20
|
+
This is **not** a raw browser telemetry tap. The watcher consumes authoritative
|
|
21
|
+
server events, keeps prior layout state locally, computes semantic diffs locally,
|
|
22
|
+
and emits one compact line or one JSON object only when the canvas state makes a
|
|
23
|
+
changed intent legible.
|
|
24
|
+
|
|
25
|
+
## Problem
|
|
26
|
+
|
|
27
|
+
PMX Canvas promises that spatial arrangement is communication and that pinned
|
|
28
|
+
context closes the human-to-agent loop. In practice, only pins cross that
|
|
29
|
+
boundary reliably today.
|
|
30
|
+
|
|
31
|
+
The current gap:
|
|
32
|
+
|
|
33
|
+
- agents can poll resources like `canvas://pinned-context` and
|
|
34
|
+
`canvas://spatial-context`
|
|
35
|
+
- browsers receive rich SSE updates
|
|
36
|
+
- there is no CLI primitive that turns those updates into a low-noise,
|
|
37
|
+
low-token stream that an agent or hook can follow
|
|
38
|
+
|
|
39
|
+
## Goals
|
|
40
|
+
|
|
41
|
+
- Provide a long-running `watch` command for agents and hooks.
|
|
42
|
+
- Reuse the existing `/api/workbench/events` SSE stream.
|
|
43
|
+
- Reduce full layout snapshots into compact semantic deltas locally.
|
|
44
|
+
- Keep token cost low by suppressing raw gesture noise.
|
|
45
|
+
- Expose a machine-readable mode for hooks and automation.
|
|
46
|
+
|
|
47
|
+
## Non-Goals
|
|
48
|
+
|
|
49
|
+
- No new browser-only collaboration protocol.
|
|
50
|
+
- No raw pointer, drag-frame, or selection telemetry.
|
|
51
|
+
- No `select` event in v1.
|
|
52
|
+
- No `collapse` event in v1 until collapsed state is synced immediately and
|
|
53
|
+
authoritatively to the server.
|
|
54
|
+
- No node content/body text in watch output.
|
|
55
|
+
- No attempt to narrate every movement; only semantic movement changes.
|
|
56
|
+
|
|
57
|
+
## Authoritative Signal Sources
|
|
58
|
+
|
|
59
|
+
The command must only depend on signals the server can already observe.
|
|
60
|
+
|
|
61
|
+
### Existing sources
|
|
62
|
+
|
|
63
|
+
- `GET /api/workbench/events`
|
|
64
|
+
- initial `canvas-layout-update` snapshot on connect
|
|
65
|
+
- subsequent `canvas-layout-update` envelopes on node/edge/layout mutations
|
|
66
|
+
- `context-pins-changed` on pin updates
|
|
67
|
+
- `GET /api/canvas/pinned-context`
|
|
68
|
+
- initial current pin set on watcher startup
|
|
69
|
+
- local semantic derivation using `buildSpatialContext(...)`
|
|
70
|
+
|
|
71
|
+
### Explicitly excluded from v1
|
|
72
|
+
|
|
73
|
+
- client-only `selectedNodeIds`
|
|
74
|
+
- locally toggled `collapsed` state that has not yet been persisted
|
|
75
|
+
|
|
76
|
+
## CLI UX
|
|
77
|
+
|
|
78
|
+
### Command
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
pmx-canvas watch [options]
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Output modes
|
|
85
|
+
|
|
86
|
+
- Default: compact human-readable lines
|
|
87
|
+
- `--compact`: explicit compact mode (same as default)
|
|
88
|
+
- `--json`: emit one JSON object per semantic event, JSONL-style
|
|
89
|
+
|
|
90
|
+
`--compact` and `--json` are mutually exclusive.
|
|
91
|
+
|
|
92
|
+
### Filters
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
--events context-pin,move-end,group,connect,remove
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Supported semantic event kinds in v1:
|
|
99
|
+
|
|
100
|
+
- `context-pin`
|
|
101
|
+
- `move-end`
|
|
102
|
+
- `group`
|
|
103
|
+
- `connect`
|
|
104
|
+
- `remove`
|
|
105
|
+
|
|
106
|
+
If omitted, all v1 semantic kinds are enabled.
|
|
107
|
+
|
|
108
|
+
### Control flags
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
--max-events 3
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Stop after emitting N semantic events. This exists mainly to support scripted
|
|
115
|
+
usage and unit tests without changing the default streaming behavior.
|
|
116
|
+
|
|
117
|
+
## Event Model
|
|
118
|
+
|
|
119
|
+
### 1. `context-pin`
|
|
120
|
+
|
|
121
|
+
Source:
|
|
122
|
+
|
|
123
|
+
- direct `context-pins-changed` SSE event
|
|
124
|
+
|
|
125
|
+
Payload:
|
|
126
|
+
|
|
127
|
+
- added pinned node IDs/titles
|
|
128
|
+
- removed pinned node IDs/titles
|
|
129
|
+
|
|
130
|
+
Rules:
|
|
131
|
+
|
|
132
|
+
- emit only when the pin set actually changed
|
|
133
|
+
- do not emit on startup bootstrap
|
|
134
|
+
|
|
135
|
+
Compact example:
|
|
136
|
+
|
|
137
|
+
```text
|
|
138
|
+
context-pin +2 -1: "Bug report", "auth.ts" | removed: "old note"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
JSON example:
|
|
142
|
+
|
|
143
|
+
```json
|
|
144
|
+
{"type":"context-pin","added":[{"id":"n1","title":"Bug report","nodeType":"markdown"}],"removed":[]}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### 2. `connect`
|
|
148
|
+
|
|
149
|
+
Source:
|
|
150
|
+
|
|
151
|
+
- added edges in a `canvas-layout-update` diff
|
|
152
|
+
|
|
153
|
+
Payload:
|
|
154
|
+
|
|
155
|
+
- added edges with `from`, `to`, edge type, and node titles when available
|
|
156
|
+
|
|
157
|
+
Rules:
|
|
158
|
+
|
|
159
|
+
- emit for newly added edges only
|
|
160
|
+
- ignore unchanged edges
|
|
161
|
+
|
|
162
|
+
Compact example:
|
|
163
|
+
|
|
164
|
+
```text
|
|
165
|
+
connect 1: "Bug report" -> "auth.ts" (relation)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 3. `remove`
|
|
169
|
+
|
|
170
|
+
Source:
|
|
171
|
+
|
|
172
|
+
- removed nodes and removed edges in a `canvas-layout-update` diff
|
|
173
|
+
|
|
174
|
+
Payload:
|
|
175
|
+
|
|
176
|
+
- removed node summaries
|
|
177
|
+
- removed edge summaries
|
|
178
|
+
|
|
179
|
+
Rules:
|
|
180
|
+
|
|
181
|
+
- emit only for removals after bootstrap
|
|
182
|
+
- group all removals from one layout update into one semantic event
|
|
183
|
+
|
|
184
|
+
Compact example:
|
|
185
|
+
|
|
186
|
+
```text
|
|
187
|
+
remove 2 nodes: "Old note", "scratch.ts"
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### 4. `group`
|
|
191
|
+
|
|
192
|
+
Source:
|
|
193
|
+
|
|
194
|
+
- group node creation or group membership change detected from layout diffs
|
|
195
|
+
|
|
196
|
+
Payload:
|
|
197
|
+
|
|
198
|
+
- created groups
|
|
199
|
+
- membership changes for existing groups
|
|
200
|
+
- child additions/removals by group
|
|
201
|
+
|
|
202
|
+
Rules:
|
|
203
|
+
|
|
204
|
+
- treat new `group` nodes as group creation
|
|
205
|
+
- compare `group.data.children` arrays for membership deltas
|
|
206
|
+
- emit one grouped summary per layout update
|
|
207
|
+
|
|
208
|
+
Compact examples:
|
|
209
|
+
|
|
210
|
+
```text
|
|
211
|
+
group created: "API Group" (2 children)
|
|
212
|
+
group updated: "API Group" +1 -0 children
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### 5. `move-end`
|
|
216
|
+
|
|
217
|
+
Source:
|
|
218
|
+
|
|
219
|
+
- node position changes between two `canvas-layout-update` snapshots
|
|
220
|
+
|
|
221
|
+
Important:
|
|
222
|
+
|
|
223
|
+
- derive from **layout diffs**, not pointer events
|
|
224
|
+
- emit only when movement caused a semantic change
|
|
225
|
+
|
|
226
|
+
Semantic triggers:
|
|
227
|
+
|
|
228
|
+
- the moved node’s proximity-cluster peers changed
|
|
229
|
+
- the moved node entered or left a pinned neighborhood
|
|
230
|
+
- a pinned node moved and its neighborhood changed
|
|
231
|
+
|
|
232
|
+
Rules:
|
|
233
|
+
|
|
234
|
+
- suppress pure coordinate churn
|
|
235
|
+
- suppress movement that did not change spatial meaning
|
|
236
|
+
- ignore newly created or removed nodes when computing move-end
|
|
237
|
+
|
|
238
|
+
Compact examples:
|
|
239
|
+
|
|
240
|
+
```text
|
|
241
|
+
move-end: "auth.ts" cluster changed
|
|
242
|
+
move-end: "session.ts" entered pinned neighborhood of "Bug report"
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Bootstrap Behavior
|
|
246
|
+
|
|
247
|
+
On startup:
|
|
248
|
+
|
|
249
|
+
1. fetch current pins from `/api/canvas/pinned-context`
|
|
250
|
+
2. connect to `/api/workbench/events`
|
|
251
|
+
3. accept the initial `canvas-layout-update` snapshot as the baseline
|
|
252
|
+
4. emit nothing during bootstrap
|
|
253
|
+
|
|
254
|
+
This avoids startup noise and prevents the watcher from replaying the whole
|
|
255
|
+
canvas as if it were a fresh user action stream.
|
|
256
|
+
|
|
257
|
+
## Local Reduction Algorithm
|
|
258
|
+
|
|
259
|
+
Maintain local watcher state:
|
|
260
|
+
|
|
261
|
+
- `currentLayout`
|
|
262
|
+
- `currentPinnedIds`
|
|
263
|
+
- derived `previousSpatialContext`
|
|
264
|
+
|
|
265
|
+
On each relevant SSE event:
|
|
266
|
+
|
|
267
|
+
### `context-pins-changed`
|
|
268
|
+
|
|
269
|
+
- diff old pin set vs new pin set
|
|
270
|
+
- emit `context-pin` if changed
|
|
271
|
+
- update `currentPinnedIds`
|
|
272
|
+
- recompute local spatial context baseline
|
|
273
|
+
|
|
274
|
+
### `canvas-layout-update`
|
|
275
|
+
|
|
276
|
+
- if no baseline exists, store it and stop
|
|
277
|
+
- diff previous layout vs next layout
|
|
278
|
+
- emit `connect`, `remove`, and `group` events from structural diffs
|
|
279
|
+
- compute old/new spatial context with the current pin set
|
|
280
|
+
- detect moved nodes
|
|
281
|
+
- emit `move-end` only for nodes whose movement changed:
|
|
282
|
+
- cluster peers
|
|
283
|
+
- pinned-neighborhood membership
|
|
284
|
+
- replace baseline with the new layout/spatial context
|
|
285
|
+
|
|
286
|
+
## Token Budget Rules
|
|
287
|
+
|
|
288
|
+
The watcher exists to save tokens, so its output contract must stay strict.
|
|
289
|
+
|
|
290
|
+
Rules:
|
|
291
|
+
|
|
292
|
+
- no full layout snapshots in emitted output
|
|
293
|
+
- no node body text or file content
|
|
294
|
+
- no duplicate emission for unchanged semantic state
|
|
295
|
+
- one semantic event object/line per meaning change, not per low-level SSE frame
|
|
296
|
+
- compact mode should usually stay under one short line per event
|
|
297
|
+
|
|
298
|
+
## Implementation Plan
|
|
299
|
+
|
|
300
|
+
### Files
|
|
301
|
+
|
|
302
|
+
- add a new CLI watcher module for:
|
|
303
|
+
- SSE parsing
|
|
304
|
+
- semantic reduction
|
|
305
|
+
- compact/JSON formatting
|
|
306
|
+
- wire the command into `src/cli/agent.ts`
|
|
307
|
+
- add `watch` to `src/cli/index.ts` command help/router
|
|
308
|
+
- add unit coverage for:
|
|
309
|
+
- context pin diffs
|
|
310
|
+
- connect/remove diffs
|
|
311
|
+
- group diffs
|
|
312
|
+
- move-end semantic suppression and emission
|
|
313
|
+
|
|
314
|
+
### Reuse
|
|
315
|
+
|
|
316
|
+
- reuse `/api/workbench/events`
|
|
317
|
+
- reuse `/api/canvas/pinned-context`
|
|
318
|
+
- reuse `buildSpatialContext(...)`
|
|
319
|
+
|
|
320
|
+
## Verification Plan
|
|
321
|
+
|
|
322
|
+
- unit tests for semantic reducer behavior
|
|
323
|
+
- unit tests for CLI watch output/filtering
|
|
324
|
+
- `bun run test`
|
|
325
|
+
|
|
326
|
+
If implementation touches browser-visible UI or client code later, escalate
|
|
327
|
+
verification. For this MVP, CLI/server-side verification is expected to be
|
|
328
|
+
sufficient.
|
|
329
|
+
|
|
330
|
+
## Deferred Work
|
|
331
|
+
|
|
332
|
+
- watchable `collapse` after immediate server sync exists
|
|
333
|
+
- explicit `cluster-changed` or `neighborhood-changed` top-level event kinds
|
|
334
|
+
- reconnect/backoff behavior for long-lived watches
|
|
335
|
+
- MCP-side semantic watcher surface if needed later
|