pmx-canvas 0.4.5 → 0.4.7
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 +118 -0
- package/Readme.md +76 -36
- package/dist/canvas/global.css +169 -3
- package/dist/canvas/index.js +112 -87
- package/dist/canvas/mermaid-entry.js +3563 -0
- package/dist/json-render/index.css +1 -1
- package/dist/types/cli/agent.d.ts +1 -0
- package/dist/types/cli/commands/smoke.d.ts +1 -0
- package/dist/types/cli/shared.d.ts +13 -1
- package/dist/types/client/canvas/CanvasNode.d.ts +11 -0
- package/dist/types/client/canvas/EdgeLayer.d.ts +8 -0
- package/dist/types/client/mermaid-entry.d.ts +11 -0
- package/dist/types/client/nodes/AxStepControls.d.ts +4 -0
- package/dist/types/client/nodes/DiffNode.d.ts +20 -0
- package/dist/types/client/nodes/ExtAppFrame.d.ts +0 -2
- package/dist/types/client/nodes/FileNode.d.ts +14 -0
- package/dist/types/client/nodes/MermaidNode.d.ts +12 -0
- package/dist/types/client/state/canvas-store.d.ts +10 -0
- package/dist/types/client/state/intent-bridge.d.ts +9 -0
- package/dist/types/client/types.d.ts +2 -2
- package/dist/types/server/ax-flow-loop.d.ts +5 -0
- package/dist/types/server/ax-interaction.d.ts +21 -2
- package/dist/types/server/canvas-operations.d.ts +5 -0
- package/dist/types/server/canvas-provenance.d.ts +1 -1
- package/dist/types/server/canvas-state.d.ts +18 -1
- package/dist/types/server/canvas-validation.d.ts +28 -1
- package/dist/types/server/file-content.d.ts +46 -0
- package/dist/types/server/html-primitives.d.ts +13 -1
- package/dist/types/server/html-surface.d.ts +8 -0
- package/dist/types/server/index.d.ts +5 -0
- package/dist/types/server/operations/ops/ax-work.d.ts +1 -0
- package/dist/types/server/operations/ops/nodes.d.ts +1 -1
- package/dist/types/server/server.d.ts +1 -0
- package/dist/types/server/web-artifacts.d.ts +14 -0
- package/dist/types/server/workboard.d.ts +46 -0
- package/dist/types/shared/ax-flow.d.ts +51 -0
- package/dist/types/shared/ax-surface-protocol.d.ts +7 -0
- package/dist/types/shared/node-sizes.d.ts +33 -0
- package/docs/cli.md +31 -2
- package/docs/environment.md +1 -0
- package/docs/http-api.md +29 -0
- package/docs/mcp.md +13 -1
- package/docs/node-types.md +47 -1
- package/docs/screenshot.png +0 -0
- package/package.json +5 -4
- package/skills/pmx-canvas/SKILL.md +123 -25
- package/skills/pmx-canvas/evals/evals.json +54 -3
- package/skills/pmx-canvas/references/full-reference.md +11 -6
- package/skills/pmx-canvas-testing/SKILL.md +3 -0
- package/src/cli/agent.ts +2 -0
- package/src/cli/commands/ax.ts +28 -3
- package/src/cli/commands/history.ts +1 -1
- package/src/cli/commands/nodes.ts +2 -2
- package/src/cli/commands/query.ts +1 -1
- package/src/cli/commands/smoke.ts +212 -0
- package/src/cli/commands/view.ts +1 -1
- package/src/cli/index.ts +7 -0
- package/src/cli/shared.ts +35 -5
- package/src/client/App.tsx +28 -15
- package/src/client/canvas/CanvasNode.tsx +54 -5
- package/src/client/canvas/CanvasViewport.tsx +6 -0
- package/src/client/canvas/EdgeLayer.tsx +44 -24
- package/src/client/canvas/ExpandedNodeOverlay.tsx +21 -0
- package/src/client/canvas/Minimap.tsx +2 -0
- package/src/client/canvas/auto-fit.ts +12 -1
- package/src/client/canvas/use-pan-zoom.ts +16 -1
- package/src/client/icons.tsx +4 -0
- package/src/client/mermaid-entry.ts +76 -0
- package/src/client/nodes/AxStepControls.tsx +226 -0
- package/src/client/nodes/DiffNode.tsx +114 -0
- package/src/client/nodes/ExtAppFrame.tsx +259 -84
- package/src/client/nodes/FileNode.tsx +249 -51
- package/src/client/nodes/MermaidNode.tsx +81 -0
- package/src/client/state/canvas-store.ts +19 -0
- package/src/client/state/intent-bridge.ts +32 -0
- package/src/client/state/node-factory.ts +2 -0
- package/src/client/state/sse-bridge.ts +32 -1
- package/src/client/theme/global.css +169 -3
- package/src/client/types.ts +6 -0
- package/src/mcp/server.ts +19 -1
- package/src/server/ax-flow-loop.ts +114 -0
- package/src/server/ax-interaction.ts +305 -4
- package/src/server/canvas-operations.ts +83 -26
- package/src/server/canvas-provenance.ts +2 -0
- package/src/server/canvas-schema.ts +33 -0
- package/src/server/canvas-state.ts +46 -10
- package/src/server/canvas-validation.ts +88 -0
- package/src/server/demo-state.json +4605 -523
- package/src/server/demo.ts +24 -0
- package/src/server/file-content.ts +152 -0
- package/src/server/file-watcher.ts +7 -10
- package/src/server/html-primitives.ts +636 -0
- package/src/server/html-surface.ts +11 -0
- package/src/server/index.ts +25 -6
- package/src/server/operations/composites.ts +3 -2
- package/src/server/operations/ops/ax-work.ts +5 -0
- package/src/server/operations/ops/batch.ts +8 -0
- package/src/server/operations/ops/json-render.ts +44 -0
- package/src/server/operations/ops/nodes.ts +24 -2
- package/src/server/operations/ops/viewport.ts +15 -2
- package/src/server/server.ts +154 -6
- package/src/server/web-artifacts.ts +109 -0
- package/src/server/workboard.ts +159 -0
- package/src/shared/ax-flow.ts +102 -0
- package/src/shared/ax-surface-protocol.ts +8 -0
- package/src/shared/node-sizes.ts +52 -0
- package/src/shared/surface.ts +2 -0
- package/dist/types/server/chart-template.d.ts +0 -29
- package/src/server/chart-template.ts +0 -448
package/docs/node-types.md
CHANGED
|
@@ -14,7 +14,9 @@ see [MCP tools](mcp.md), [HTTP API](http-api.md), and [SDK](sdk.md).
|
|
|
14
14
|
| `context` | Context cards, token usage, workspace grounding |
|
|
15
15
|
| `ledger` | Execution ledger summary |
|
|
16
16
|
| `trace` | Agent trace pills (tool calls, subagent activity) |
|
|
17
|
-
| `file` | Live file viewer with auto-update on disk changes |
|
|
17
|
+
| `file` | Live file viewer with auto-update on disk changes — also renders CSV/TSV as tables and PDFs inline |
|
|
18
|
+
| `diff` | Unified diff viewer: per-file headers, hunk rows, +/− colored lines |
|
|
19
|
+
| `mermaid` | Mermaid diagram rendered client-side from text (flowchart, sequence, state, …) |
|
|
18
20
|
| `image` | Image viewer (file paths, data URIs, URLs) |
|
|
19
21
|
| `webpage` | Persisted webpage snapshot with stored URL, extracted text, refresh |
|
|
20
22
|
| `mcp-app` | Tool-backed hosted MCP App iframes (Excalidraw, etc.) |
|
|
@@ -48,6 +50,20 @@ automatically via `fs.watch()`.
|
|
|
48
50
|
canvas_node({ action: 'add', type: 'file', content: 'src/server/index.ts' })
|
|
49
51
|
```
|
|
50
52
|
|
|
53
|
+
The same node type handles non-source files, so you never have to pick a
|
|
54
|
+
different type by extension:
|
|
55
|
+
|
|
56
|
+
- **`.csv` / `.tsv`** render as a real table (quoted fields, embedded newlines,
|
|
57
|
+
and escaped quotes are parsed properly) instead of raw delimited text. Very
|
|
58
|
+
long files render a bounded number of rows and say how many were omitted.
|
|
59
|
+
- **`.pdf`** renders in an embedded viewer, served from
|
|
60
|
+
`GET /api/canvas/file-bytes?nodeId=…`. In hosts that block src-URL iframes
|
|
61
|
+
(Amp orb portals) the node offers an "Open PDF" link instead of a blank frame.
|
|
62
|
+
- **Other binary files** show a placeholder with the name and size rather than
|
|
63
|
+
decoding bytes into mojibake.
|
|
64
|
+
- **Text files over 2 MB** render truncated with a banner saying so, so a huge
|
|
65
|
+
log can never exhaust memory.
|
|
66
|
+
|
|
51
67
|
## Image nodes
|
|
52
68
|
|
|
53
69
|
Image nodes display local paths, remote URLs, and data URIs. File-backed and
|
|
@@ -219,6 +235,36 @@ component gallery, interaction prototype, flowchart, SVG illustration set,
|
|
|
219
235
|
presentation, explainer, status report, incident report, triage board, config
|
|
220
236
|
editor, or prompt tuner.
|
|
221
237
|
|
|
238
|
+
Two kinds are **live AX control surfaces** rather than static documents, and are
|
|
239
|
+
the only primitives created with AX enabled:
|
|
240
|
+
|
|
241
|
+
- **`ax-board`** — give the agent tasks, watch their status change as it works,
|
|
242
|
+
send steering, and run a bounded task loop. Task rows read from live AX state,
|
|
243
|
+
so a status the agent sets anywhere shows up here.
|
|
244
|
+
- **`ax-flow`** — the same controls drawn as a task flow with an explicit
|
|
245
|
+
loop-back rail, plus **Materialize to board**: the flow is laid out as real
|
|
246
|
+
canvas nodes joined by `flow` edges (and a dashed `references` loop edge),
|
|
247
|
+
each linked to a work item so the node's status chip tracks progress.
|
|
248
|
+
Re-materializing replaces the previous flow instead of duplicating it.
|
|
249
|
+
|
|
250
|
+
A steer emitted from either surface is *queued* on the timeline — it reaches the
|
|
251
|
+
agent on its next turn rather than waking it. Both stop their loop if the panel
|
|
252
|
+
reloads; the work items are the durable record.
|
|
253
|
+
|
|
254
|
+
### The flow on ordinary nodes
|
|
255
|
+
|
|
256
|
+
You do not need the panel to drive a flow. A materialized step node carries
|
|
257
|
+
`data.axStep`, and the canvas renders native controls on it — **Start / Done /
|
|
258
|
+
Blocked** on every step, plus **Run loop / Stop** and a steer box on the first
|
|
259
|
+
one. These are canvas chrome, not sandboxed content, so they work on any node
|
|
260
|
+
type carrying the stamp.
|
|
261
|
+
|
|
262
|
+
The native loop runs **server-side**, which is the one real difference from the
|
|
263
|
+
panel: it advances when a step's work item is completed, survives a browser
|
|
264
|
+
reload, and keeps going while the tab is closed. Same bounds either way — it only
|
|
265
|
+
advances while running, stops at its run cap (hard limit 20), and Stop is
|
|
266
|
+
persisted immediately. `blocked` or `cancelled` halts it without disarming.
|
|
267
|
+
|
|
222
268
|
```ts
|
|
223
269
|
canvas_node({
|
|
224
270
|
action: 'add',
|
package/docs/screenshot.png
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmx-canvas",
|
|
3
|
-
"version": "0.4.
|
|
4
|
-
"description": "Spatial canvas workbench for coding agents
|
|
3
|
+
"version": "0.4.7",
|
|
4
|
+
"description": "Spatial canvas workbench for coding agents — infinite 2D canvas with agent-native CLI, MCP integration, nodes, edges, file watching, and snapshots",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/server/index.ts",
|
|
7
7
|
"module": "./src/server/index.ts",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"dev:demo": "bun run src/cli/index.ts --demo",
|
|
33
33
|
"dev:portless": "portless run --name pmx --app-port 4313 bun run src/cli/index.ts --no-open --port=4313",
|
|
34
34
|
"dev:portless:demo": "portless run --name pmx --app-port 4313 bun run src/cli/index.ts --no-open --port=4313 --demo",
|
|
35
|
-
"build:client": "bun build src/client/index.tsx --outdir dist/canvas --minify && cp src/client/theme/global.css dist/canvas/global.css && cp src/client/theme/surface-theme.css dist/canvas/surface-theme.css",
|
|
35
|
+
"build:client": "bun build src/client/index.tsx --outdir dist/canvas --minify && bun build src/client/mermaid-entry.ts --outdir dist/canvas --minify && cp src/client/theme/global.css dist/canvas/global.css && cp src/client/theme/surface-theme.css dist/canvas/surface-theme.css",
|
|
36
36
|
"build:json-render": "bash scripts/build-json-render.sh",
|
|
37
37
|
"build:types": "tsc -p tsconfig.types.json",
|
|
38
38
|
"build": "bun run build:client && bun run build:json-render && bun run build:types",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"test": "if [ -d tests/unit ]; then PMX_MCP_APP_HOST_STATE_FILE=/tmp/pmx-canvas-test-app-host-state.json PMX_CANVAS_DISABLE_BROWSER_OPEN=1 bun test tests/unit; else echo 'tests/ is not shipped in the npm package — clone the repo to run the suite'; fi",
|
|
47
47
|
"test:unit": "PMX_MCP_APP_HOST_STATE_FILE=/tmp/pmx-canvas-test-app-host-state.json PMX_CANVAS_DISABLE_BROWSER_OPEN=1 bun test tests/unit",
|
|
48
48
|
"test:client": "if [ -d tests/client ]; then bun test --preload ./tests/client/setup.ts tests/client; else echo 'tests/ is not shipped in the npm package — clone the repo to run the suite'; fi",
|
|
49
|
-
"test:coverage": "PMX_MCP_APP_HOST_STATE_FILE=/tmp/pmx-canvas-test-app-host-state.json bun test tests/unit --coverage --coverage-reporter=text --coverage-reporter=lcov --coverage-dir coverage && bun scripts/check-coverage.ts",
|
|
49
|
+
"test:coverage": "PMX_MCP_APP_HOST_STATE_FILE=/tmp/pmx-canvas-test-app-host-state.json PMX_CANVAS_DISABLE_BROWSER_OPEN=1 bun test tests/unit --coverage --coverage-reporter=text --coverage-reporter=lcov --coverage-dir coverage && bun scripts/check-coverage.ts",
|
|
50
50
|
"test:web-canvas": "PMX_CANVAS_DISABLE_BROWSER_OPEN=1 bun run build && PMX_CANVAS_DISABLE_BROWSER_OPEN=1 bash scripts/run-playwright.sh",
|
|
51
51
|
"test:e2e": "bun run test:web-canvas",
|
|
52
52
|
"test:e2e-cli": "bash scripts/e2e-cli-coverage.sh",
|
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
69
69
|
"@preact/signals": "^2.0.0",
|
|
70
70
|
"marked": "^15.0.0",
|
|
71
|
+
"mermaid": "^11.16.1",
|
|
71
72
|
"preact": "^10.25.0",
|
|
72
73
|
"react": "^19.2.3",
|
|
73
74
|
"react-dom": "^19.2.3",
|
|
@@ -52,6 +52,10 @@ Humans curate agent context by pinning nodes; agents read that curation through
|
|
|
52
52
|
6. **Mutate through current composites.** Prefer the 16 composite MCP tools below.
|
|
53
53
|
7. **Arrange and validate.** After batch changes, use `canvas_view { action: "arrange" }` when
|
|
54
54
|
appropriate and always finish with `canvas_query { action: "validate" }`.
|
|
55
|
+
7b. **Show the human.** After creating user-facing output, bring the camera to it: a single node
|
|
56
|
+
gets `canvas_view { action: "focus", id }` (pans by default), a small cluster gets
|
|
57
|
+
`canvas_view { action: "fit", nodeIds: [...] }` with exactly the new ids. See
|
|
58
|
+
**In-View Placement & Sizing** below — auto-placement is board-relative, not camera-relative.
|
|
55
59
|
8. **Verify context pins.** Pin with `canvas_pin_nodes` or the browser's **Pin as context**, then
|
|
56
60
|
read `canvas://pinned-context`.
|
|
57
61
|
9. **Clean up temporary nodes.** Remove retry/test fixtures and restore the baseline snapshot when
|
|
@@ -67,6 +71,10 @@ pmx-canvas serve status
|
|
|
67
71
|
```
|
|
68
72
|
|
|
69
73
|
Both surfaces report `workspace`. It must match the intended workspace root.
|
|
74
|
+
For a full environment check in one command (health + workspace, CLI/server
|
|
75
|
+
version skew, MCP initialize handshake, temp-node create/search/remove
|
|
76
|
+
round-trip, board validation), run `pmx-canvas smoke` (0.4.6+) — JSON report,
|
|
77
|
+
exit 1 on failure.
|
|
70
78
|
|
|
71
79
|
- If `responsive: true` but `pidRunning: false`, treat the listener as potentially stale.
|
|
72
80
|
- On mismatch, do not mutate. Start the intended workspace on an explicit free port:
|
|
@@ -97,14 +105,21 @@ Both surfaces report `workspace`. It must match the intended workspace root.
|
|
|
97
105
|
| Persistent context cards | `context` via `canvas_node` |
|
|
98
106
|
| Event/check stream | `ledger` or `trace` via `canvas_node` |
|
|
99
107
|
| Local source with live updates | `file` via `canvas_node` |
|
|
108
|
+
| Tabular data (`.csv`/`.tsv`), a PDF, or any other file on disk (0.4.6+) | `file` via `canvas_node` — CSV/TSV render as tables, PDFs render inline, other binaries show a size placeholder. Never paste a CSV into a markdown fence. |
|
|
109
|
+
| Code review / unified diff (0.4.6+) | `diff` via `canvas_node` (content = diff text; link to its file node with a `references` edge) |
|
|
110
|
+
| Flowchart / sequence / state diagram (0.4.6+) | `mermaid` via `canvas_node` (content = mermaid source; renders client-side, no hosted app) |
|
|
100
111
|
| Image | `image` via `canvas_node` |
|
|
101
112
|
| Cached URL content | `webpage` via `canvas_node` |
|
|
102
113
|
| Structured UI | `json-render` via `canvas_render` |
|
|
103
114
|
| Chart | `graph` via `canvas_render` |
|
|
115
|
+
| Live work-item board (0.4.6+) | `canvas_render { action: "workboard" }` — one board node, auto-refreshes on work-item changes |
|
|
104
116
|
| Generated communication surface | HTML primitive via `canvas_node` |
|
|
117
|
+
| Give the agent tasks + watch progress + loop (0.4.7+) | `ax-board` HTML primitive — a live AX control surface, created AX-enabled |
|
|
118
|
+
| The same, drawn as a task flow, and materializable to real nodes+edges (0.4.7+) | `ax-flow` HTML primitive — **Materialize to board** lays the steps out as nodes joined by `flow` edges with a loop-back edge, each linked to a work item |
|
|
119
|
+
| Drive a flow WITHOUT the panel (0.4.7+) | Materialized step nodes carry native Start/Done/Blocked controls, and the anchor adds Run loop/Stop + steer. The native loop runs server-side, so it survives a browser reload and keeps advancing while the tab is closed. |
|
|
105
120
|
| Self-contained HTML/JS | `html` via `canvas_node` |
|
|
106
121
|
| Hosted interactive MCP app | `canvas_app { action: "open-mcp-app" }` |
|
|
107
|
-
| Excalidraw diagram | `canvas_app { action: "diagram" }` |
|
|
122
|
+
| Excalidraw diagram (interactive/human drawing) | `canvas_app { action: "diagram" }` — prefer `mermaid` for agent-authored diagrams |
|
|
108
123
|
| Bundled React artifact | `canvas_app { action: "build-artifact" }` |
|
|
109
124
|
|
|
110
125
|
Use the lightest tier that communicates the result. Do not build a web artifact when markdown,
|
|
@@ -164,6 +179,48 @@ v0.4.0 after their deprecated 0.3.x window.
|
|
|
164
179
|
- Edge types: `flow`, `depends-on`, `relation`, `references`.
|
|
165
180
|
- After manual or batch layout changes, run `canvas_query { action: "validate" }`.
|
|
166
181
|
|
|
182
|
+
## In-View Placement & Sizing (required for user-facing nodes)
|
|
183
|
+
|
|
184
|
+
Auto-placement (omitting `x`/`y`) is **board-relative, not camera-relative**: it places right of
|
|
185
|
+
the last node or scans rows from the origin, ignoring where the human is looking. On a board with
|
|
186
|
+
distant nodes, an auto-placed node lands off-camera.
|
|
187
|
+
|
|
188
|
+
1. Omit `x`/`y` only on an empty or locally dense board. Otherwise place near the human's
|
|
189
|
+
attention: the pinned/focused neighborhood, or explicit coordinates beside the last
|
|
190
|
+
user-facing output (gap ≥ 24–48 px).
|
|
191
|
+
2. After creating nodes the human should see, pan the camera (operating-sequence step 7b):
|
|
192
|
+
`focus` for one node, `fit` with exactly the new `nodeIds` for a cluster. Use `noPan`
|
|
193
|
+
(`focus --no-pan`) only when you must not steal the camera.
|
|
194
|
+
3. Never fit the whole board to "show" new work — on a board with outliers that miniaturizes
|
|
195
|
+
everything. Always pass explicit `nodeIds` to `fit`.
|
|
196
|
+
4. Never leave user-facing output at far coordinates without a focus/fit.
|
|
197
|
+
5. **Do not hand-compute a whole board's coordinates.** Create the nodes (omitting x/y), then
|
|
198
|
+
`canvas_view { action: "arrange", layout: "grid" | "column" | "flow" }` and finish with a
|
|
199
|
+
`fit` over the new ids. Manual pixel math is what produces long-line, unbalanced boards.
|
|
200
|
+
6. `fit` sizes itself to the connected browser window (0.4.6+) — you do not need to guess
|
|
201
|
+
`width`/`height`. Pass them only to fit for a window other than the human's.
|
|
202
|
+
7. **Docking is explicit (0.4.6+).** Nodes render on the canvas where you put them; a node is
|
|
203
|
+
only a HUD pill if you set `dockPosition`. An edge whose endpoint is docked cannot be drawn —
|
|
204
|
+
`validate` reports it as `hiddenEdgeEndpoints` and fails `ok`, because the edge visually
|
|
205
|
+
trails off into empty canvas.
|
|
206
|
+
|
|
207
|
+
**Size for content.** Omitting `width`/`height` gives readable per-type defaults — prefer them:
|
|
208
|
+
markdown 640×420, status 360×200, file 520×360, diff 640×420, mermaid 640×460, html 720×640,
|
|
209
|
+
graph 760×520, mcp-app 960×600, web-artifact 960×720. A *hosted* app opened with
|
|
210
|
+
`canvas_app { action: "open-mcp-app" | "diagram" }` — including the Excalidraw diagram preset — is
|
|
211
|
+
the exception: it opens at 720×500, not 960×600 (960×600 is the default for an `mcp-app` node you
|
|
212
|
+
create directly). Pass `width`/`height` to that action when you want a bigger diagram tile.
|
|
213
|
+
Since 0.4.6 the server clamps explicit creation sizes UP to per-type
|
|
214
|
+
readability floors (e.g. markdown 360×180, graph/json-render/html 420×280, mcp-app 480×320) —
|
|
215
|
+
a tiny probe size silently becomes the floor. `strictSize: true` is the only opt-out (a fixed
|
|
216
|
+
scrolling frame you genuinely want small). `canvas_query { action: "validate" }` additionally
|
|
217
|
+
reports any node below its floor as an advisory `sizeWarnings` entry — treat a non-empty list
|
|
218
|
+
as layout work left to do.
|
|
219
|
+
|
|
220
|
+
**Token hygiene.** For routine state checks use `canvas_ax_state { action: "get" }` WITHOUT
|
|
221
|
+
`includeContext` — the full AX context payload is ~10× larger; request it only when you are
|
|
222
|
+
actually consuming context.
|
|
223
|
+
|
|
167
224
|
## Context Pins
|
|
168
225
|
|
|
169
226
|
Context pins are the primary human-to-agent bridge:
|
|
@@ -218,6 +275,49 @@ or host actions.
|
|
|
218
275
|
Read [AX HTML control surfaces](references/ax-html-control-surface.md) before building an
|
|
219
276
|
interactive AX-enabled HTML node.
|
|
220
277
|
|
|
278
|
+
### Ready-made control surfaces (0.4.7+) — prefer these over hand-rolling
|
|
279
|
+
|
|
280
|
+
Two HTML primitives ship AX-enabled, so you do not need to author a control surface or pass
|
|
281
|
+
`axCapabilities` yourself. They are the ONLY primitives created with AX on; every other kind is
|
|
282
|
+
still a static document.
|
|
283
|
+
|
|
284
|
+
- **`ax-board`** — task list: create tasks, watch status change live, steer, bounded loop.
|
|
285
|
+
- **`ax-flow`** — the same controls drawn as a task flow with a loop-back rail, plus
|
|
286
|
+
**Materialize to board**.
|
|
287
|
+
|
|
288
|
+
**`ax.flow.materialize`** is the one interaction that creates canvas nodes, and it is deliberately
|
|
289
|
+
narrow: you supply TEXT ONLY (<= 12 steps, title <= 120 chars) and the server owns the result —
|
|
290
|
+
one `markdown` node per step, `flow` edges between them, a dashed `references` loop edge, and one
|
|
291
|
+
work item per step linked to its node. Re-materializing REPLACES the previous flow (the source
|
|
292
|
+
node keeps a manifest of what it created), so it is safe to call repeatedly.
|
|
293
|
+
|
|
294
|
+
**The flow also works without the panel.** Materialized step nodes carry `data.axStep`, and the
|
|
295
|
+
canvas renders native Start / Done / Blocked controls on them, with Run loop / Stop and a steer box
|
|
296
|
+
on the anchor (first) step. Those controls also follow the node into focus mode. The native loop
|
|
297
|
+
runs **server-side**: it advances when a step's work item completes, survives a browser reload, and
|
|
298
|
+
keeps going while the tab is closed — unlike the panel's loop, which dies with the iframe. Bounds
|
|
299
|
+
are the same either way: advances only while running, hard cap 20 runs, Stop persists immediately,
|
|
300
|
+
and `blocked`/`cancelled` halts it.
|
|
301
|
+
|
|
302
|
+
When you want the agent to work through a checklist the human can watch and steer, reach for
|
|
303
|
+
`ax-flow` + materialize rather than a markdown to-do list — the status chips and the loop come free.
|
|
304
|
+
|
|
305
|
+
### Working a flow: keep the board honest as you go
|
|
306
|
+
|
|
307
|
+
A materialized flow is only useful if it tracks reality. While you work one:
|
|
308
|
+
|
|
309
|
+
1. **Move the step to `in-progress` BEFORE you start it, and to `done` when it is actually done** —
|
|
310
|
+
not in a batch at the end. The human is watching the chips to know where you are; a board that
|
|
311
|
+
only updates on completion tells them nothing while it matters.
|
|
312
|
+
2. **Pin the in-progress step as context** (`canvas_pin_nodes` with just that node, or
|
|
313
|
+
`POST /api/canvas/context-pins` mode `set`). This makes the active step the one thing in
|
|
314
|
+
`canvas://pinned-context`, so the board's "what am I working on" and your own context are the
|
|
315
|
+
same answer instead of drifting apart. Re-pin as you advance.
|
|
316
|
+
3. **Mark `blocked` rather than going quiet** when you are stuck — a stalled `in-progress` chip is
|
|
317
|
+
indistinguishable from a crashed agent.
|
|
318
|
+
4. Read steering back with `canvas_ax_delivery` as you go; a human watching a live flow steers
|
|
319
|
+
mid-run, and a steer you never claim is a correction you ignored.
|
|
320
|
+
|
|
221
321
|
## Resources
|
|
222
322
|
|
|
223
323
|
Read the smallest resource that answers the question:
|
|
@@ -245,24 +345,21 @@ Prefer `canvas_query { action: "search" }` over parsing the full layout.
|
|
|
245
345
|
renders the same content and theme, but `window.PMX_AX` is not injected without the canvas
|
|
246
346
|
iframe's per-mount nonce, so AX buttons only work inside the in-canvas node (0.4.4 Codex note).
|
|
247
347
|
Do not tell a user a standalone tab's controls will steer the agent.
|
|
248
|
-
- A hosted ext-app (Excalidraw) node
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
`
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
(Chrome). Do not diagnose a healthy app session as a broken node. When reporting a black tile,
|
|
264
|
-
fetch `GET /api/canvas/debug/ext-app-recovery` from the daemon and attach the trace — it shows
|
|
265
|
-
what the recovery queue actually did in that panel.
|
|
348
|
+
- A hosted ext-app (Excalidraw) node in a **WebKit** host panel (e.g. the GitHub Copilot app's
|
|
349
|
+
embedded WKWebView) historically could render as a black tile — a host compositor paint race
|
|
350
|
+
on the nested iframe, **not** a broken node (the session is healthy, `sessionStatus` is
|
|
351
|
+
`ready`, and it renders fine in Chrome/Codex). Since 0.4.6 the canvas runs a **paint oracle +
|
|
352
|
+
recovery ladder** under WebKit: present-at-load ext-apps mount strictly one at a time (the
|
|
353
|
+
cold burst was the trigger), each frame answers a double-rAF **paint probe** after settle, and
|
|
354
|
+
on silence the ladder escalates — soft-expand cycle (the automatic analogue of the proven
|
|
355
|
+
enlarge+close) → serialized remount → an explicit "App surface failed to paint / Retry"
|
|
356
|
+
affordance. The connecting overlay stays up until paint is confirmed, so a black layer is
|
|
357
|
+
never presented as ready. The recovery trail
|
|
358
|
+
(`GET /api/canvas/debug/ext-app-recovery` / `window.__PMX_EXTAPP_LOG`) now records
|
|
359
|
+
`mount-slot`, `paint-ok`, `paint-fail`, `soft-expand-cycle`, and `recovery-exhausted` — when
|
|
360
|
+
diagnosing, trust `paint-ok`/`paint-fail`, and never assert health from `settled` alone. If a
|
|
361
|
+
tile still shows the Retry affordance, click Retry (fresh recovery budget) or expand-then-close;
|
|
362
|
+
attach the recovery trail when reporting.
|
|
266
363
|
- Ext-app frame documents live in server memory. Through 0.4.0, killing/restarting the daemon
|
|
267
364
|
while a panel stays open leaves ext-app tiles on dead frame URLs (`Frame document not found`,
|
|
268
365
|
0.4.0 report Finding S) until a full workbench reload. Since 0.4.1 the browser revalidates its
|
|
@@ -271,8 +368,8 @@ Prefer `canvas_query { action: "search" }` over parsing the full layout.
|
|
|
271
368
|
- A hosted ext-app (Excalidraw) resized NARROW/TALL (e.g. 360x529) can show its diagram in the
|
|
272
369
|
upper region with the app's own dark fill below it — in every engine (0.3.4 report Finding Q).
|
|
273
370
|
This letterboxing lives inside the hosted app bundle's root container, not in PMX (a body-level
|
|
274
|
-
background override ships but cannot reach the app's inner root). Keep ext-app tiles
|
|
275
|
-
|
|
371
|
+
background override ships but cannot reach the app's inner root). Keep ext-app tiles landscape —
|
|
372
|
+
at or above the 720x500 they open at, or the same ratio — or expand the node: the fullscreen
|
|
276
373
|
overlay renders full-bleed. The durable fix is upstream in the excalidraw-mcp app.
|
|
277
374
|
- Behind proxies that buffer streaming responses (e.g. portal hosts), the workbench auto-falls
|
|
278
375
|
back from SSE to a polling transport within ~3s, so the board still boots and stays live.
|
|
@@ -290,10 +387,11 @@ Prefer `canvas_query { action: "search" }` over parsing the full layout.
|
|
|
290
387
|
URLs cannot be inlined (cross-origin) and may stay blocked in such hosts. Force a mode with
|
|
291
388
|
`/workbench?iframe-mode=srcdoc` (or `iframe-mode=src`) when diagnosing.
|
|
292
389
|
- Graph and json-render standalone surfaces use `display=site` and fill the browser viewport, and
|
|
293
|
-
reflow on a live window resize
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
390
|
+
reflow on a live window resize. Some single-tab host browsers historically didn't deliver
|
|
391
|
+
live-resize events; current Codex builds do — the 0.4.6 pass watched a standalone graph reflow
|
|
392
|
+
live (SVG 1550×783 → 850×483 on a 1600×900 → 900×600 window resize) with no reload. Do not
|
|
393
|
+
present a system browser as a workaround for stale resizing; it is only a preference for viewing
|
|
394
|
+
a full page beside the canvas.
|
|
297
395
|
- Some hosts cannot automate inside sandboxed workbench iframes. Verify those interactions in a
|
|
298
396
|
system browser or through server-side AX state.
|
|
299
397
|
- `pmx-canvas screenshot` requires an active WebView. Start it with
|
|
@@ -417,7 +417,7 @@
|
|
|
417
417
|
"id": 17,
|
|
418
418
|
"name": "standalone-surface-open-as-site",
|
|
419
419
|
"prompt": "Open the bar-chart graph node as a standalone full-page site for a screenshot, and also try to open the Excalidraw diagram node the same way. Tell me the correct surface URL for each.",
|
|
420
|
-
"expected_output": "For the graph/json-render node, uses the stable surface URL /api/canvas/surface/<id> which redirects to the full-viewport display=site viewer (reads surfaceUrl from the node, does not fabricate a path). Recognizes the hosted Excalidraw/ext-app node is NOT an open-as-site target (its surface route returns a clean 404 \u2014 it renders only with the in-canvas host bridge) and does not claim a standalone tab will work.
|
|
420
|
+
"expected_output": "For the graph/json-render node, uses the stable surface URL /api/canvas/surface/<id> which redirects to the full-viewport display=site viewer (reads surfaceUrl from the node, does not fabricate a path). Recognizes the hosted Excalidraw/ext-app node is NOT an open-as-site target (its surface route returns a clean 404 \u2014 it renders only with the in-canvas host bridge, and open-external refuses it with the same message) and does not claim a standalone tab will work. Describes the standalone surface as reflowing live on a window resize; does not claim host browsers leave a resized chart stale until reload, and does not prescribe a system browser as a workaround.",
|
|
421
421
|
"assertions": [
|
|
422
422
|
{
|
|
423
423
|
"name": "graph-site-surface-url",
|
|
@@ -430,8 +430,59 @@
|
|
|
430
430
|
"type": "output_check"
|
|
431
431
|
},
|
|
432
432
|
{
|
|
433
|
-
"name": "
|
|
434
|
-
"description": "
|
|
433
|
+
"name": "no-stale-resize-claim",
|
|
434
|
+
"description": "Does not assert that host browsers fail to deliver live resize (current Codex builds do — the 0.4.6 pass saw a standalone graph reflow live with no reload); a system browser may be offered as a viewing preference, never as a resize workaround",
|
|
435
|
+
"type": "output_check"
|
|
436
|
+
}
|
|
437
|
+
]
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
"id": 18,
|
|
441
|
+
"name": "in-view-placement-after-create",
|
|
442
|
+
"prompt": "The board already has a cluster of nodes from this morning, way off to the far right. Add a markdown summary of the incident and a status node for the follow-up, and make sure I can actually see them.",
|
|
443
|
+
"expected_output": "Places the new nodes near the human's attention (pinned/focused neighborhood or beside the last user-facing output) instead of trusting board-relative auto-placement on a spread-out board, then pans the camera: canvas_view {action:focus} for a single node, or canvas_view {action:fit, nodeIds:[...]} with exactly the new ids for the pair. Does not hand-compute the whole board's coordinates and does not fit the whole board to 'show' the new work — an outlier cluster would miniaturize everything.",
|
|
444
|
+
"assertions": [
|
|
445
|
+
{
|
|
446
|
+
"name": "ends-with-focus-or-fit",
|
|
447
|
+
"description": "Finishes with canvas_view {action:focus} (single node) or {action:fit} after creating the nodes, rather than leaving them off-camera",
|
|
448
|
+
"type": "output_check"
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
"name": "fit-targets-new-ids-only",
|
|
452
|
+
"description": "Passes exactly the new node ids to fit instead of fitting the whole board (a distant cluster would shrink everything)",
|
|
453
|
+
"type": "output_check"
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
"name": "no-whole-board-coordinate-math",
|
|
457
|
+
"description": "Places relative to the human's attention or uses create-then-arrange; does not hand-compute a full board layout in pixels",
|
|
458
|
+
"type": "output_check"
|
|
459
|
+
}
|
|
460
|
+
]
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
"id": 19,
|
|
464
|
+
"name": "node-size-discipline",
|
|
465
|
+
"prompt": "Add a markdown write-up of the outage and a bar chart of the error counts. Keep them tight — I don't want huge tiles.",
|
|
466
|
+
"expected_output": "Omits width/height and takes the per-type defaults (markdown 640×420, graph 760×520) rather than passing small probe sizes for human-readable content. Uses strictSize:true only for a deliberately fixed small frame, not to dodge the floor. Reads the size back from the create result or the layout before any layout math, because the server clamps an undersized explicit frame UP to the per-type floor (markdown 360×180, graph 420×280) — the returned size can differ from what was requested.",
|
|
467
|
+
"assertions": [
|
|
468
|
+
{
|
|
469
|
+
"name": "uses-type-defaults",
|
|
470
|
+
"description": "Omits width/height (or passes a readable size) instead of guessing a frame for readable content",
|
|
471
|
+
"type": "output_check"
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
"name": "no-tiny-probe-sizes",
|
|
475
|
+
"description": "Does not create human-readable nodes at tiny probe sizes that the floor would silently raise",
|
|
476
|
+
"type": "output_check"
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
"name": "strict-size-only-when-deliberate",
|
|
480
|
+
"description": "Reaches for strictSize:true only for an intentionally fixed small frame, not as a way around the readability floor",
|
|
481
|
+
"type": "output_check"
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
"name": "reads-back-created-size",
|
|
485
|
+
"description": "Reads the actual created size from the create result / canvas_query layout before positioning or sizing anything else, rather than assuming the requested size was kept",
|
|
435
486
|
"type": "output_check"
|
|
436
487
|
}
|
|
437
488
|
]
|
|
@@ -952,12 +952,15 @@ the host's embedded browser (e.g. Codex) opens `_blank` tabs in-place.
|
|
|
952
952
|
renders with the in-canvas host bridge — a bare tab has no peer, so the surface route
|
|
953
953
|
returns `404` ("This MCP app renders in the canvas and cannot be opened as a standalone
|
|
954
954
|
site"); view/edit them in the canvas, or open them externally through their own app
|
|
955
|
-
(report #61).
|
|
956
|
-
|
|
955
|
+
(report #61). `POST /api/canvas/open-external` refuses those nodes with the same message
|
|
956
|
+
instead of launching a browser at a URL that 404s. Only bundled `web-artifact` apps
|
|
957
|
+
(redirect to `/artifact`) and URL-backed `mcp-app` / `webpage` viewers redirect to their
|
|
958
|
+
external site.
|
|
957
959
|
- `graph` / `json-render` nodes redirect to the full-viewport `display=site` viewer; the chart
|
|
958
|
-
fills the window and reflows on a live resize
|
|
959
|
-
|
|
960
|
-
|
|
960
|
+
fills the window and reflows on a live resize. Current Codex builds deliver live-resize events —
|
|
961
|
+
the 0.4.6 pass watched a standalone graph reflow live (SVG 1550×783 → 850×483 on a
|
|
962
|
+
1600×900 → 900×600 window resize) with no reload — so do not present a system browser as a
|
|
963
|
+
workaround for stale resizing (report #67).
|
|
961
964
|
- This is additive — opening a site never evicts or replaces canvas nodes.
|
|
962
965
|
|
|
963
966
|
### Choosing the Right Visual Tier
|
|
@@ -1112,7 +1115,7 @@ AX interactions are gated per node type. The lists below are each type's **ceili
|
|
|
1112
1115
|
|
|
1113
1116
|
| Node type | Allowed AX interaction types |
|
|
1114
1117
|
|-----------|------------------------------|
|
|
1115
|
-
| `markdown` | `ax.steer`, `ax.work.create`, `ax.evidence.add`, `ax.command.invoke`, `ax.event.record` |
|
|
1118
|
+
| `markdown` | `ax.steer`, `ax.work.create`, `ax.work.update`, `ax.evidence.add`, `ax.command.invoke`, `ax.event.record` |
|
|
1116
1119
|
| `context` | `ax.focus.set`, `ax.steer`, `ax.evidence.add`, `ax.command.invoke`, `ax.event.record` |
|
|
1117
1120
|
| `status` | `ax.work.create`, `ax.work.update`, `ax.approval.request`, `ax.mode.request`, `ax.event.record` |
|
|
1118
1121
|
| `file` | `ax.evidence.add`, `ax.review.add`, `ax.focus.set`, `ax.event.record` |
|
|
@@ -1121,6 +1124,8 @@ AX interactions are gated per node type. The lists below are each type's **ceili
|
|
|
1121
1124
|
| `ledger` | `ax.evidence.add`, `ax.event.record` |
|
|
1122
1125
|
| `trace` | `ax.evidence.add`, `ax.event.record` |
|
|
1123
1126
|
| `image` | `ax.evidence.add`, `ax.review.add` |
|
|
1127
|
+
| `diff` | `ax.evidence.add`, `ax.review.add`, `ax.focus.set`, `ax.event.record` |
|
|
1128
|
+
| `mermaid` | `ax.steer`, `ax.work.create`, `ax.evidence.add`, `ax.command.invoke`, `ax.event.record` |
|
|
1124
1129
|
| `webpage` | `ax.evidence.add`, `ax.review.add`, `ax.focus.set`, `ax.event.record` |
|
|
1125
1130
|
| `group` | `ax.focus.set`, `ax.work.create`, `ax.command.invoke`, `ax.event.record` |
|
|
1126
1131
|
|
|
@@ -95,6 +95,9 @@ Prefer extending the existing suites before inventing a one-off script.
|
|
|
95
95
|
- When checking embedded frame fit manually, start from a clean seeded state, rebuild stale bundles,
|
|
96
96
|
and inspect the actual iframe document in a browser. Server dimensions can look correct while the
|
|
97
97
|
embedded content is still clipped.
|
|
98
|
+
- User-facing creation flows must end with the new nodes visible: after the create (plus the
|
|
99
|
+
skill-mandated focus/fit), a screenshot must show them without any manual pan. A `panned: true`
|
|
100
|
+
API result alone is not proof — verify the frame is on-screen and unobscured.
|
|
98
101
|
|
|
99
102
|
## Failure Handling
|
|
100
103
|
|
package/src/cli/agent.ts
CHANGED
|
@@ -32,6 +32,7 @@ import './commands/webview.js';
|
|
|
32
32
|
import './commands/apps.js';
|
|
33
33
|
import './commands/copilot.js';
|
|
34
34
|
import './commands/skills.js';
|
|
35
|
+
import './commands/smoke.js';
|
|
35
36
|
|
|
36
37
|
// Re-exported for existing importers (tests import it from this module).
|
|
37
38
|
export { extractGlobalTargetFlags };
|
|
@@ -75,6 +76,7 @@ Canvas commands:
|
|
|
75
76
|
pmx-canvas arrange [--layout MODE] Auto-arrange (grid|column|flow)
|
|
76
77
|
pmx-canvas batch [--file FILE] Run many canvas operations at once
|
|
77
78
|
pmx-canvas validate Check collisions and containment issues
|
|
79
|
+
pmx-canvas smoke One-command environment check of a running canvas
|
|
78
80
|
pmx-canvas validate spec Validate json-render/graph payloads without creating nodes
|
|
79
81
|
pmx-canvas watch [options] Watch semantic canvas changes over SSE
|
|
80
82
|
pmx-canvas focus <id> Pan viewport to node
|
package/src/cli/commands/ax.ts
CHANGED
|
@@ -53,6 +53,7 @@ cmd(
|
|
|
53
53
|
[
|
|
54
54
|
'pmx-canvas ax event add --kind tool-start --summary "ran tests"',
|
|
55
55
|
'pmx-canvas ax event add --kind failure --summary "build broke" --detail "..." node1 node2',
|
|
56
|
+
'pmx-canvas ax event add --kind tool-start --summary "ran tests" --agent-id builder-1',
|
|
56
57
|
],
|
|
57
58
|
async (args) => {
|
|
58
59
|
const { positional, flags } = parseFlags(args);
|
|
@@ -61,12 +62,14 @@ cmd(
|
|
|
61
62
|
const kind = requireFlag(flags, 'kind', 'pmx-canvas ax event add --kind <kind> --summary <text>');
|
|
62
63
|
const summary = requireFlag(flags, 'summary', 'pmx-canvas ax event add --kind <kind> --summary <text>');
|
|
63
64
|
const detail = getStringFlag(flags, 'detail');
|
|
65
|
+
const agentId = getStringFlag(flags, 'agent-id');
|
|
64
66
|
|
|
65
67
|
output(
|
|
66
68
|
await invokeOperation('ax.event.record', {
|
|
67
69
|
kind,
|
|
68
70
|
summary,
|
|
69
71
|
...(detail ? { detail } : {}),
|
|
72
|
+
...(agentId ? { agentId } : {}),
|
|
70
73
|
...(positional.length > 0 ? { nodeIds: positional } : {}),
|
|
71
74
|
source: resolveAxSource(flags),
|
|
72
75
|
}),
|
|
@@ -77,7 +80,11 @@ cmd(
|
|
|
77
80
|
cmd(
|
|
78
81
|
'ax steer',
|
|
79
82
|
'Send a steering message to the active agent session',
|
|
80
|
-
[
|
|
83
|
+
[
|
|
84
|
+
'pmx-canvas ax steer "focus on the failing test first"',
|
|
85
|
+
'pmx-canvas ax steer --message "stop and re-plan"',
|
|
86
|
+
'pmx-canvas ax steer "wave 2 is unblocked" --agent-id orchestrator --target builder-1',
|
|
87
|
+
],
|
|
81
88
|
async (args) => {
|
|
82
89
|
const { positional, flags } = parseFlags(args);
|
|
83
90
|
if (flags.help || flags.h) return showCommandHelp('ax steer');
|
|
@@ -86,8 +93,17 @@ cmd(
|
|
|
86
93
|
if (!message) {
|
|
87
94
|
die('Missing steering message', 'pmx-canvas ax steer <message>');
|
|
88
95
|
}
|
|
96
|
+
const agentId = getStringFlag(flags, 'agent-id');
|
|
97
|
+
const target = getStringFlag(flags, 'target');
|
|
89
98
|
|
|
90
|
-
output(
|
|
99
|
+
output(
|
|
100
|
+
await invokeOperation('ax.steer', {
|
|
101
|
+
message,
|
|
102
|
+
...(agentId ? { agentId } : {}),
|
|
103
|
+
...(target ? { target } : {}),
|
|
104
|
+
source: resolveAxSource(flags),
|
|
105
|
+
}),
|
|
106
|
+
);
|
|
91
107
|
},
|
|
92
108
|
);
|
|
93
109
|
|
|
@@ -374,6 +390,7 @@ cmd(
|
|
|
374
390
|
[
|
|
375
391
|
'pmx-canvas ax work add --title "Wire up auth" --status in-progress',
|
|
376
392
|
'pmx-canvas ax work add --title "Review API" node1 node2',
|
|
393
|
+
'pmx-canvas ax work add --title "Wire up auth" --agent-id builder-1',
|
|
377
394
|
],
|
|
378
395
|
async (args) => {
|
|
379
396
|
const { positional, flags } = parseFlags(args);
|
|
@@ -382,12 +399,14 @@ cmd(
|
|
|
382
399
|
const title = requireFlag(flags, 'title', 'pmx-canvas ax work add --title <text>');
|
|
383
400
|
const status = getStringFlag(flags, 'status');
|
|
384
401
|
const detail = getStringFlag(flags, 'detail');
|
|
402
|
+
const agentId = getStringFlag(flags, 'agent-id');
|
|
385
403
|
|
|
386
404
|
output(
|
|
387
405
|
await invokeOperation('ax.work.create', {
|
|
388
406
|
title,
|
|
389
407
|
...(status ? { status } : {}),
|
|
390
408
|
...(detail ? { detail } : {}),
|
|
409
|
+
...(agentId ? { agentId } : {}),
|
|
391
410
|
...(positional.length > 0 ? { nodeIds: positional } : {}),
|
|
392
411
|
source: resolveAxSource(flags),
|
|
393
412
|
}),
|
|
@@ -398,7 +417,11 @@ cmd(
|
|
|
398
417
|
cmd(
|
|
399
418
|
'ax work update',
|
|
400
419
|
'Update a canvas-bound AX work item by ID',
|
|
401
|
-
[
|
|
420
|
+
[
|
|
421
|
+
'pmx-canvas ax work update <id> --status done',
|
|
422
|
+
'pmx-canvas ax work update <id> --title "New title" --detail "..."',
|
|
423
|
+
'pmx-canvas ax work update <id> --agent-id builder-2',
|
|
424
|
+
],
|
|
402
425
|
async (args) => {
|
|
403
426
|
const { positional, flags } = parseFlags(args);
|
|
404
427
|
if (flags.help || flags.h) return showCommandHelp('ax work update');
|
|
@@ -408,6 +431,7 @@ cmd(
|
|
|
408
431
|
const title = getStringFlag(flags, 'title');
|
|
409
432
|
const status = getStringFlag(flags, 'status');
|
|
410
433
|
const detail = getStringFlag(flags, 'detail');
|
|
434
|
+
const agentId = getStringFlag(flags, 'agent-id');
|
|
411
435
|
|
|
412
436
|
output(
|
|
413
437
|
await invokeOperation('ax.work.update', {
|
|
@@ -415,6 +439,7 @@ cmd(
|
|
|
415
439
|
...(title ? { title } : {}),
|
|
416
440
|
...(status ? { status } : {}),
|
|
417
441
|
...(detail ? { detail } : {}),
|
|
442
|
+
...(agentId ? { agentId } : {}),
|
|
418
443
|
...(positional.length > 1 ? { nodeIds: positional.slice(1) } : {}),
|
|
419
444
|
source: resolveAxSource(flags),
|
|
420
445
|
}),
|
|
@@ -81,7 +81,7 @@ cmd(
|
|
|
81
81
|
'Show canvas mutation history',
|
|
82
82
|
['pmx-canvas history', 'pmx-canvas history --summary', 'pmx-canvas history --compact'],
|
|
83
83
|
async (args) => {
|
|
84
|
-
const { flags } = parseFlags(args);
|
|
84
|
+
const { flags } = parseFlags(args, { boolFlags: ['summary'] });
|
|
85
85
|
if (flags.help || flags.h) return showCommandHelp('history');
|
|
86
86
|
|
|
87
87
|
const result = (await invokeOperation('history.get', {})) as Record<string, unknown>;
|
|
@@ -691,7 +691,7 @@ cmd(
|
|
|
691
691
|
'pmx-canvas node schema --summary',
|
|
692
692
|
],
|
|
693
693
|
async (args) => {
|
|
694
|
-
const { flags } = parseFlags(args);
|
|
694
|
+
const { flags } = parseFlags(args, { boolFlags: ['summary'] });
|
|
695
695
|
if (flags.help || flags.h) return showCommandHelp('node schema');
|
|
696
696
|
|
|
697
697
|
const result = await loadCanvasSchema();
|
|
@@ -783,7 +783,7 @@ cmd(
|
|
|
783
783
|
'pmx-canvas node get node-abc123 --field title --field graphConfig',
|
|
784
784
|
],
|
|
785
785
|
async (args) => {
|
|
786
|
-
const { positional, flags } = parseFlags(args);
|
|
786
|
+
const { positional, flags } = parseFlags(args, { boolFlags: ['summary'] });
|
|
787
787
|
if (flags.help || flags.h) return showCommandHelp('node get');
|
|
788
788
|
|
|
789
789
|
const id = positional[0];
|
|
@@ -65,7 +65,7 @@ cmd(
|
|
|
65
65
|
'pmx-canvas validate spec --type json-render --spec-file ./dashboard.json --summary',
|
|
66
66
|
],
|
|
67
67
|
async (args) => {
|
|
68
|
-
const { flags } = parseFlags(args);
|
|
68
|
+
const { flags } = parseFlags(args, { boolFlags: ['summary'] });
|
|
69
69
|
if (flags.help || flags.h) return showCommandHelp('validate spec');
|
|
70
70
|
|
|
71
71
|
const type = getStringFlag(flags, 'type');
|