pmx-canvas 0.9.2 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/.github/extensions/pmx-canvas/extension.mjs +9 -3
  2. package/.github/extensions/pmx-canvas/runtime.json +1 -1
  3. package/.github/extensions/pmx-canvas/tools.json +9278 -2014
  4. package/CHANGELOG.md +34 -1
  5. package/dist/canvas/global.css +12 -17
  6. package/dist/canvas/index.js +58 -58
  7. package/dist/canvas/mcp-app.html +75 -80
  8. package/dist/types/client/canvas/submit-without-navigation.d.ts +3 -0
  9. package/dist/types/client/state/intent-bridge.d.ts +0 -3
  10. package/dist/types/client/state/presence-store.d.ts +3 -3
  11. package/dist/types/client/types.d.ts +1 -1
  12. package/dist/types/mcp/canvas-access.d.ts +1 -12
  13. package/dist/types/mcp/resource-subscriptions.d.ts +7 -0
  14. package/dist/types/mcp/server.d.ts +2 -0
  15. package/dist/types/server/ax-wait.d.ts +1 -0
  16. package/dist/types/server/canvas-operations.d.ts +1 -0
  17. package/dist/types/server/canvas-state.d.ts +2 -0
  18. package/dist/types/server/index.d.ts +5 -5
  19. package/dist/types/server/knowledge-lifecycle.d.ts +11 -2
  20. package/dist/types/server/lifecycle-status.d.ts +3 -1
  21. package/dist/types/server/operations/http.d.ts +10 -0
  22. package/dist/types/server/operations/index.d.ts +2 -2
  23. package/dist/types/server/operations/invoker.d.ts +4 -3
  24. package/dist/types/server/operations/mcp-contracts.d.ts +20 -0
  25. package/dist/types/server/operations/mcp.d.ts +2 -0
  26. package/dist/types/server/operations/ops/batch.d.ts +1 -0
  27. package/dist/types/server/operations/registry.d.ts +2 -2
  28. package/dist/types/server/operations/types.d.ts +12 -2
  29. package/dist/types/server/server.d.ts +2 -0
  30. package/dist/types/server/tour-control.d.ts +6 -3
  31. package/dist/types/server/webpage-node.d.ts +1 -0
  32. package/docs/api-reference.md +2 -2
  33. package/docs/cli.md +10 -1
  34. package/docs/design/Chrome.dc.html +1 -0
  35. package/docs/design.md +3 -1
  36. package/docs/http-api.md +31 -0
  37. package/docs/mcp.md +98 -22
  38. package/docs/node-types.md +3 -0
  39. package/docs/plans/plan-019-post-0.9-roadmap.md +187 -11
  40. package/docs/sdk.md +10 -0
  41. package/package.json +1 -1
  42. package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/extension.mjs +9 -3
  43. package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.json +1 -1
  44. package/plugins/pmx-canvas/plugin.json +1 -1
  45. package/plugins/pmx-canvas/runtime.json +1 -1
  46. package/plugins/pmx-canvas/tools.json +9278 -2014
  47. package/skills/pmx-canvas/SKILL.md +12 -7
  48. package/skills/pmx-canvas/references/full-reference.md +35 -2
  49. package/skills/pmx-canvas/references/github-copilot-app-adapter.md +2 -2
  50. package/skills/pmx-canvas/references/installing-pmx-canvas.md +7 -7
  51. package/src/client/canvas/BoardCopyDialog.tsx +2 -1
  52. package/src/client/canvas/KnowledgeDialog.tsx +3 -0
  53. package/src/client/canvas/LifecyclePanel.tsx +4 -0
  54. package/src/client/canvas/RelationPresetsDialog.tsx +2 -0
  55. package/src/client/canvas/ToolRail.tsx +2 -0
  56. package/src/client/canvas/submit-without-navigation.ts +14 -0
  57. package/src/client/state/canvas-store.ts +12 -24
  58. package/src/client/state/intent-bridge.ts +0 -6
  59. package/src/client/state/presence-store.ts +3 -3
  60. package/src/client/state/sse-bridge.ts +11 -6
  61. package/src/client/theme/global.css +12 -17
  62. package/src/mcp/canvas-access.ts +6 -47
  63. package/src/mcp/resource-subscriptions.ts +181 -0
  64. package/src/mcp/server.ts +185 -219
  65. package/src/server/ax-wait.ts +21 -6
  66. package/src/server/canvas-operations.ts +6 -2
  67. package/src/server/canvas-state.ts +10 -0
  68. package/src/server/index.ts +20 -32
  69. package/src/server/knowledge-lifecycle.ts +66 -0
  70. package/src/server/lifecycle-status.ts +79 -7
  71. package/src/server/operations/http.ts +69 -21
  72. package/src/server/operations/index.ts +8 -1
  73. package/src/server/operations/invoker.ts +53 -5
  74. package/src/server/operations/mcp-contracts.ts +339 -0
  75. package/src/server/operations/mcp.ts +153 -18
  76. package/src/server/operations/ops/ax-await.ts +18 -10
  77. package/src/server/operations/ops/ax-read.ts +2 -0
  78. package/src/server/operations/ops/ax-timeline.ts +2 -2
  79. package/src/server/operations/ops/batch.ts +27 -8
  80. package/src/server/operations/ops/lifecycle.ts +3 -7
  81. package/src/server/operations/ops/nodes.ts +20 -8
  82. package/src/server/operations/ops/viewport.ts +3 -7
  83. package/src/server/operations/registry.ts +24 -4
  84. package/src/server/operations/types.ts +16 -2
  85. package/src/server/server.ts +23 -10
  86. package/src/server/tour-control.ts +17 -8
  87. package/src/server/webpage-node.ts +9 -4
package/docs/mcp.md CHANGED
@@ -1,10 +1,92 @@
1
1
  # MCP reference
2
2
 
3
- PMX Canvas ships an MCP stdio server with **24 tools**, **15 static canvas
3
+ PMX Canvas ships an MCP stdio server with **25 tools**, **15 static canvas
4
4
  resources**, and two canvas resource templates, plus per-skill resources at
5
5
  `canvas://skills/<name>` and the `skill://{name}/{+path}` template. The server emits
6
- `notifications/resources/updated` when canvas state changes — humans pin
7
- nodes in the browser, agents are notified immediately.
6
+ `notifications/resources/updated` to subscribed connections when canvas state changes.
7
+ This uses MCP SDK v1, not SDK v2.
8
+
9
+ ## Operation help (0.10)
10
+
11
+ Call `canvas_help {}` for the sorted registry names exposed through MCP, then
12
+ `canvas_help { op: "node.add" }` for one operation's public `tool`, `description`
13
+ and JSON `inputSchema`. The schema includes required fields, public field names
14
+ and constant routing selectors (`action`, and `kind` for gates). For example,
15
+ `ax.approval.request` maps to `canvas_ax_gate` with `kind: "approval"`,
16
+ `action: "request"` and the optional `approvalAction` field.
17
+
18
+ Text contains the JSON result; structured content wraps it in `{ result }`.
19
+ Unknown names, internal/human-only operations and non-registry tools such as
20
+ `canvas_screenshot` return text-only `isError`; use `tools/list` for those tools.
21
+ Help reads registry metadata only: it never starts a canvas, writes state, records
22
+ a content read or advances a cursor. It is read-only and idempotent.
23
+
24
+ Help derives its schema and guidance from the operation registry, not from live
25
+ board state. Handlers still enforce conditional requirements and authorization.
26
+ Existing input schemas stay in `tools/list`; help does not enable lazy discovery
27
+ in hosts. Repeated equivalent output branches are grouped within each tool,
28
+ preserving operation/payload validation and partial-error receipts.
29
+
30
+ ## Tool results and effect hints (0.10)
31
+
32
+ All 25 tools advertise an object `outputSchema`. Registry-dispatched tools return
33
+ `structuredContent.result = { operation, outcome, data }`. `operation` names the
34
+ dispatched action (for example `node.add`), `outcome` is `success`, `error` or
35
+ `partial`, and `data` is the existing compact tool result. Prose results use
36
+ `data.message`. Exceptions use `operation: null`, `outcome: "error"` and
37
+ `data.message`; this does not guarantee that nothing changed before the exception.
38
+ Batch failures retain completed `results` and `refs`, with `partial` when at least
39
+ one entry completed. Check both `isError` and the outcome before using a result.
40
+
41
+ The three handwritten tools keep direct structured objects: `canvas_ax_interaction`
42
+ returns its accepted interaction, `canvas_ingest_activity` its event and reaction
43
+ records, and `canvas_screenshot` capture metadata alongside model-visible image
44
+ content. Their errors are text-only `isError` replies. Existing model-facing text
45
+ and compact/full projections remain unchanged; structured results do not introduce
46
+ additional full-card or UI payloads. Inspect the advertised schema for each tool.
47
+
48
+ All tools declare `readOnlyHint`, `destructiveHint`, `idempotentHint` and
49
+ `openWorldHint`. Composite hints cover every action, not only their read actions.
50
+ `canvas_query` is not read-only because context reads can advance a durable
51
+ consumer cursor. Hints describe effects; they grant no permission or retry guarantee.
52
+
53
+ ## Cancellation and progress (0.10)
54
+
55
+ MCP cancellation reaches the operation layer in both local and daemon-attached
56
+ connections. Supported AX waits release their listeners and timers immediately;
57
+ batches stop before the next operation and keep completed writes. Webpage fetches
58
+ receive the signal and stop before writing fetched content after cancellation.
59
+ Cancellation does not roll back a created node or other completed mutations.
60
+ If webpage creation already committed, its receipt retains `fetch.cancelled: true`.
61
+ External app builds/runtime calls and local WebView captures are not interruptible
62
+ mid-call; do not treat cancellation as proof that those side effects stopped.
63
+
64
+ Calls supplying `_meta.progressToken` receive `notifications/progress` at batch
65
+ entry boundaries and supported AX wait start/completion, with `progress`, optional
66
+ `total`, optional `message` and the same token. This works over the daemon bridge
67
+ as well as local stdio. No token means
68
+ no progress notifications. A host that aborts its connection may not receive a
69
+ partial result; inspect canvas state before retrying. See the
70
+ [HTTP streaming contract](http-api.md#operation-cancellation-and-progress-010).
71
+
72
+ ## Resource subscriptions (0.10)
73
+
74
+ The server advertises `resources.subscribe`. Send `resources/subscribe` with an
75
+ exact live URI, then `resources/unsubscribe` to stop. Subscriptions belong to the
76
+ connection and disappear on close; reconnecting clients must subscribe again.
77
+
78
+ Live resources are `canvas://layout`, `boards`, `summary`, `context`, `graph`,
79
+ `spatial-context`, `history`, `code-graph`, `pinned-context`, `ax`, `ax-context`,
80
+ `ax-timeline`, `ax-work`, `ax-pending-steering` and `ax-delivery` (each with the
81
+ `canvas://` prefix). Parameterized context and graph URIs are supported, for example
82
+ `canvas://context?consumer=my-agent&budget=3000`; notifications echo the exact
83
+ subscribed URI. Static schema and bundled skill resources do not support subscriptions.
84
+
85
+ Notifications invalidate cached data, not deliver it. Read the resource when needed.
86
+ They can be broader than the underlying change, and daemon reconnection invalidates
87
+ subscribed resources to cover missed events. Notification delivery does not record
88
+ a context read or advance a consumer cursor. Explicit `resources/read` works without
89
+ subscriptions, including on hosts that do not use this capability.
8
90
 
9
91
  > **Consolidation complete (plan-006/008).** The MCP surface shrank from 84
10
92
  > tools to 22: 16 action-discriminated **composites** (recommended — see
@@ -57,7 +139,7 @@ on its source board. It appears as a `portal` edge in `canvas://graph`, independ
57
139
 
58
140
  ## Board tours
59
141
 
60
- `canvas_view { action: "get-tour" }` returns `{ tour, derived }`, using group
142
+ `canvas_view { action: "get-tour" }` returns `{ tour, derived, position }`, using group
61
143
  reading order when there is no saved tour. `canvas_view { action: "set-tour",
62
144
  tour: { stops: [...] } }` persists an ordered tour; `tour: null` resets it.
63
145
  `canvas_view { action: "tour-go", stop: 0 }` (or `step: "next" | "previous"`)
@@ -83,12 +165,12 @@ Add to your agent's MCP config:
83
165
  }
84
166
  ```
85
167
 
86
- The canvas auto-starts on first tool call.
168
+ The canvas auto-starts on first canvas operation, not on `canvas_help`.
87
169
 
88
170
  ## Private MCP App preview
89
171
 
90
172
  `pmx-canvas --mcp-app` runs a **separate, experimental stdio profile**; the
91
- 24-tool `--mcp` profile above is unchanged. Build from this checkout first:
173
+ 25-tool `--mcp` profile above is separate. Build from this checkout first:
92
174
 
93
175
  ```bash
94
176
  bun run build
@@ -170,6 +252,10 @@ in the user's browser. Standalone Markdown edits carry captured board/content
170
252
  revisions; a refused save keeps the current editor draft. Copy that draft before
171
253
  closing the editor or switching boards; unsaved drafts do not survive remounts.
172
254
 
255
+ Knowledge review, categories, relation presets and board-copy forms handle saves
256
+ in JavaScript, including Enter and input validation. They do not require the
257
+ outer host to grant `allow-forms`; the SDK test host keeps `sandbox="allow-scripts"`.
258
+
173
259
  **Host constraints still apply.** Native PMX controls work without child frames.
174
260
  HTML, Mermaid, JSON/graph and nested app renderers need the host to permit local
175
261
  `srcdoc` frames. The portable MCP Apps default with empty `frameDomains` is
@@ -354,7 +440,7 @@ Single-card and layout reads record delivered content, not incident relations.
354
440
  Use a complete graph or brief relation entry to acknowledge those relations.
355
441
  Reads never mark full card content read or
356
442
  switch the workbench. Pull needed cards in full separately.
357
- No new tool is added: the tool count stays 24.
443
+ These reads add no tools beyond the catalog listed above.
358
444
 
359
445
  Library search is `canvas_query { action: "search", query: "auth", scope:
360
446
  "library" }`. It returns board/card IDs and never switches the active board.
@@ -487,13 +573,14 @@ agent intends to publish its own cursor or attention target.
487
573
 
488
574
  ## Standalone tools
489
575
 
490
- 6 tools that intentionally stay outside the composites — folding them would
576
+ 7 tools that intentionally stay outside the composites — folding them would
491
577
  hurt (distinct callers, binary payloads, trust-boundary or execution-intent
492
578
  semantics). See [Migration reference](#migration-reference) below for the
493
579
  legacy tools the composites replaced.
494
580
 
495
581
  | Tool | Description |
496
582
  |------|-------------|
583
+ | `canvas_help` | List MCP registry operations or fetch one operation's public arguments and guidance (`op` optional); no canvas access |
497
584
  | `canvas_batch` | Run a batch of canvas operations with `$ref` support |
498
585
  | `canvas_pin_nodes` | Pin nodes to include in agent context (`nodeIds`, `mode` set/add/remove, optional `reason` shown to the human) |
499
586
  | `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 |
@@ -699,20 +786,9 @@ interaction against the node's effective capabilities.
699
786
 
700
787
  ## Change notifications
701
788
 
702
- The MCP server emits `notifications/resources/updated` whenever canvas state
703
- changes:
704
-
705
- - Pin changes notify `canvas://pinned-context`, `canvas://ax`, and `canvas://ax-context`
706
- - AX focus changes notify `canvas://ax` and `canvas://ax-context`
707
- - Canvas-bound AX mutations (work items, approval gates, review annotations,
708
- host capability) notify `canvas://ax`, `canvas://ax-work`, and `canvas://ax-context`
709
- - AX timeline mutations (agent-events, evidence, steering) notify
710
- `canvas://ax-timeline` and `canvas://ax-context`
711
- - All mutations notify `canvas://layout`, `canvas://summary`,
712
- `canvas://spatial-context`, `canvas://history`, `canvas://code-graph`, and `canvas://graph`
713
-
714
- This closes the human-to-agent loop: spatial curation in the browser becomes
715
- an immediate signal in the agent's context.
789
+ Notifications require an explicit subscription to each live resource URI. See
790
+ [Resource subscriptions](#resource-subscriptions-010) for connection lifetime,
791
+ reconnection and broad cache invalidation. Receiving a notification is not a content read.
716
792
 
717
793
  ## Codex App Adapter
718
794
 
@@ -77,6 +77,9 @@ Tick a task directly on its card, including quoted and nested tasks. A card clic
77
77
  only that task's `[ ]` or `[x]` marker, preserving the rest of the source exactly.
78
78
  Task-shaped text in code examples and raw HTML checkboxes are not Markdown task controls.
79
79
 
80
+ Expand a Markdown card to read and edit the full document. Source stays outside the
81
+ scrolling text, below ordinary documents and above documents with flow actions.
82
+
80
83
  ## File nodes
81
84
 
82
85
  File nodes display project files with line numbers and language detection.
@@ -1,10 +1,11 @@
1
1
  # Plan 019: After 0.9.0
2
2
 
3
- **Status (2026-10-19):** proposed sequence, agreed in direction with the maintainer. Nothing
4
- here is started. 0.9.0 itself (the CHANGELOG's Unreleased section) still needs the owner's
5
- acceptance and release approval; this plan begins once it ships. Version numbers below are
6
- proposals, not assignments; each release still needs explicit approval and the
7
- [release gates](../RELEASE.md).
3
+ **Status (2026-10-24):** 0.9.3 is released to npm and GitHub, with fresh registry smoke
4
+ passing. Step 2, MCP hardening and SDK tour result parity on SDK v1, is implemented
5
+ and pushed with the Settings tooltip fix, operation help and schema-size reduction.
6
+ The maintainer authorized review, testing and release of 0.10.0; release gates are in progress.
7
+ Later version numbers are proposals, not publication approval; every release must pass
8
+ the [release gates](../RELEASE.md).
8
9
  **Sources:** [product vision](../product-vision-2026-09.md) (moves and the 2026-10-19 sequence),
9
10
  [design.md build plan](../design.md#build-plan) (Waves 4, 5, Later),
10
11
  [plan 009](plan-009-tech-debt-backlog.md) (open debt),
@@ -21,8 +22,8 @@ measured; the maintainer carries that risk rather than reinstating an earlier ga
21
22
 
22
23
  | Step | Proposed version | Scope | Vision | Size |
23
24
  |---|---|---|---|---|
24
- | 1 | 0.9.1 | Cleanup: tour debt, large-board costs, bounded due evaluator, MCP reconnect, lint | 12, 1 | S–M |
25
- | 2 | 0.10 | MCP hardening: contracts, annotations, cancellation, resource subscriptions | 6a, 1 | M |
25
+ | 1 | 0.9.3 | Cleanup: tour debt, large-board costs, bounded due evaluator, MCP reconnect, lint | 12, 1 | S–M |
26
+ | 2 | 0.10 | MCP hardening, operation help and bounded discovery-schema reduction | 6a, 1 | M |
26
27
  | 3 | 0.11 | One frame host, portal-layer mount, Wave-0 design pass | 5, 9 | M–L |
27
28
  | 4 | 0.12 | Wave 5: agent output as nodes, make/inline board, group drill-in, relations sketch parity | 3, 0 | L |
28
29
  | 5 | later | Trust (M half), journal, concurrent boards, binary | 7, 4, 8 | L–XL |
@@ -31,10 +32,66 @@ measured; the maintainer carries that risk rather than reinstating an earlier ga
31
32
  Each step starts after the previous release ships, except that a concrete defect found in
32
33
  use can be fixed narrowly in a patch release without pulling later scope forward.
33
34
 
34
- ## Step 1: 0.9.1 cleanup
35
+ ## Step 1: 0.9.3 cleanup
35
36
 
36
37
  Debt deliberately kept out of 0.9.0, plus measured performance leads.
37
38
 
39
+ **Local checkpoint, 2026-10-24 (not release approval):**
40
+ - Camera moves now persist only the viewport. A headed browser regression failed on the
41
+ baseline's two full-card geometry writes, then passed without them; the complete seven-case
42
+ presentation/recording file passes. Both camera paths use one animation driver while retaining
43
+ their existing interpolation. Tour resolution reads geometry without hydrating card contents.
44
+ - Tour reads share one result builder. HTTP and MCP return `{ tour, derived, position }`;
45
+ the SDK preserves its existing direct tour result (`.stops`). Result-shape parity is
46
+ deferred to 0.10 because changing the SDK to `.tour.stops` breaks callers.
47
+ - `bun run scripts/probe-mcp-reconnect.ts` opened 12 local stdio fixture apps concurrently,
48
+ then restored them through five server restarts and queried every replacement session's live
49
+ tool catalog. All 60 restored sessions passed, with no `-32001` timeout. The native-host/remote
50
+ backend failure remains open; this probe is not evidence that it is fixed.
51
+ - Saved Markdown skips unused reference parsing; saved targets, unresolved links, drafts and
52
+ inline images retain their behavior. The measurements below leave serialization, offscreen
53
+ rendering and SSE unchanged because each needs a separate contract or lifecycle decision.
54
+ - Lifecycle-get, node-get and review qualification use the dependency/portal closure, including
55
+ incoming cross-board sources and unsaved active-board edges. Latest evidence and retirement
56
+ summaries use indexed queries; requested audit history remains complete. Home counts are
57
+ unchanged. **Deferred:** aggregate-only Home reads, per-card storage reads within closure boards,
58
+ and whole-library mutation broadcasts. The current Home client consumes `knowledge.list.cards`.
59
+ - Lint passes with 55 warnings and 12 infos, down from 63 warnings and 12 infos. Eight unused
60
+ import diagnostics were removed in the touched SDK module. Remaining diagnostics are listed below.
61
+ - Combined checkout verification passed: `bun run build`, `bun run typecheck`, `bun run test`
62
+ (1,503 tests), `bun run test:client` (332 tests), and
63
+ `xvfb-run -a bash scripts/run-playwright.sh --headed` (333 tests). The final node-get
64
+ integration assertion also passed its 32-case relation suite, typecheck and lint. The inspected
65
+ tour recording shows both stops and the restored editor after exit. These are local source
66
+ checks, not release smoke, CI, installed-adapter or native-host verification. The patch-only
67
+ decision is settled; release gates are being run against the final 0.9.3 candidate.
68
+
69
+ **Reproducible measurements (local Bun 1.4.4, synthetic data):**
70
+
71
+ | Workload and command | Before | After | Limits |
72
+ |---|---|---|---|
73
+ | 12 Markdown cards, five decodable 400×300 PNGs, 2,459,314 source bytes; `bun run scripts/benchmark-markdown-load.ts --browser --expect-no-reference-lex` | 24 extra reference parses scanning 4,918,628 bytes | Zero extra reference parses; all 12 normalized HTML hashes match | Three warmups and nine HTTP samples; headed Chromium at 1440×900, 2×, one warmup and three reloads. Timing varied on the shared orb, so no board-load speedup claim |
74
+ | Same Markdown board, public state serialization | 4,927,936 bytes | Unchanged | 2,460,294 bytes are duplicated top-level content; removing that alias is only a counterfactual measurement |
75
+ | Same Markdown board, offscreen rendering | Six offscreen POSTs, 508,608 source bytes | Unchanged | All 12 cards render; all five images decode |
76
+ | Same Markdown board, one-character edit | First layout SSE frame: 2,466,713 bytes, 12 nodes | Unchanged | First frame only, not total event traffic |
77
+ | 12 boards × 24 cards × 32 reviews; `bun run scripts/benchmark-lifecycle.ts` | 13 board reads and 9,248 full-history records per request | Two board reads and 32 records per request | One independent inactive-card request; all 32 requested audit records returned. Closure boards still load whole board bodies |
78
+ | Same lifecycle workload, warm medians from three independent runs | 16.016 / 14.938 / 16.648 ms | 0.832 / 1.101 / 0.609 ms | Five warmups and 20 samples per run. First request initializes indexes and was not consistently faster; no cold-start claim |
79
+
80
+ Remove `--expect-no-reference-lex` when running the Markdown script against the baseline.
81
+ Both benchmarks isolate their databases in disposable workspaces. Dependency-heavy lifecycle
82
+ behavior is covered by cycle, provenance, missing-source and cross-board tests, not this timing workload.
83
+
84
+ **Remaining lint diagnostics:** `bunx --no-install biome check . --max-diagnostics=200`
85
+ lists the exact locations. These remain outside the narrow behavioral fixes:
86
+
87
+ | Diagnostics | Count | Reason retained |
88
+ |---|---:|---|
89
+ | Unused imports / variables / parameters / private members | 11 / 8 / 6 / 1 | Spread across unrelated client, server and test modules; remove with their owning cleanup |
90
+ | Optional-chain suggestions | 23 | Existing style suggestions, including untouched regions of changed files |
91
+ | Literal template placeholders | 3 | Tests intentionally exercise literal `${…}` text |
92
+ | Global `isNaN`, confusing `void`, comma operator | 1 each | Existing chart coercion, frame callback and presence expressions need individual semantic review |
93
+ | Infos: template strings / switch cases / index lookup / config deprecation | 6 / 3 / 2 / 1 | Non-gating style/config follow-up, no rule disabled |
94
+
38
95
  - **Tour debt** ([vision move 12](../product-vision-2026-09.md#12-tours-the-board-is-the-presentation-m)):
39
96
  a tour camera move saves every node's layout (`commitViewport` → `persistLayout` from the SSE
40
97
  camera handler) and re-reads the whole layout (`tour-control.ts`); `animateCameraPath`
@@ -55,8 +112,8 @@ Debt deliberately kept out of 0.9.0, plus measured performance leads.
55
112
  **Done when**
56
113
  - A tour step changes the viewport and writes no node layout: a test asserts the intended
57
114
  viewport change and that no layout-persistence write occurs during `tour go`/`next`
58
- (unchanged positions alone would also pass a needless identical rewrite). SDK, HTTP and MCP
59
- return the same tour shape, including position. One camera-animation path remains.
115
+ (unchanged positions alone would also pass a needless identical rewrite). The SDK keeps its
116
+ existing tour shape; result-shape parity moves to step 2. One camera-animation path remains.
60
117
  - Each WI-01 amplifier has a reproducible measurement script and a before/after number on the
61
118
  same workload, per the measurement rules in `CLAUDE.md`; unimplicated ones are recorded, not changed.
62
119
  - A one-card lifecycle request on a board with populated review histories reads only its
@@ -68,6 +125,121 @@ Debt deliberately kept out of 0.9.0, plus measured performance leads.
68
125
  [Vision move 6a](../product-vision-2026-09.md#6a-make-the-tool-surface-cheap-s--staged-follow-up)
69
126
  and move 1's context delivery, on the current SDK v1.
70
127
 
128
+ **Local implementation checkpoint (2026-10-24):**
129
+ - All 24 tools advertise output schemas and explicit effect hints. Registry results use
130
+ `{ result: { operation, outcome, data } }`; handwritten interaction, activity and screenshot
131
+ tools retain their direct objects. Existing model-facing text/projections stay unchanged.
132
+ Schemas distinguish batch success, empty failure and retained partial outcomes.
133
+ - Cancellation reaches AX waits and webpage I/O through local and HTTP invokers. Batches
134
+ retain completed receipts and stop before subsequent operations. Progress is opt-in through
135
+ MCP tokens and HTTP NDJSON. External runtime/build calls and local WebView captures remain
136
+ noninterruptible mid-call. Disconnects may lose the response, never undo completed writes.
137
+ - Subscriptions belong to each connection, include exact parameterized URIs, and dispose
138
+ on unsubscribe/close. Local state and workbench events cover library-only changes as well
139
+ as active nodes; remote SSE reconnects invalidate subscribed resources. Notifications never
140
+ record reads or advance cursors. A library-only-change regression failed before integration
141
+ and passes with the shared event source.
142
+ - SDK `getTour()` now returns `{ tour, derived, position }`; `.tour.stops` is documented as
143
+ the minor-version breaking change. No SDK/dependency upgrade or frame-host work.
144
+
145
+ **Approved follow-up, 2026-10-24:** `canvas_help { op }` derives one operation's
146
+ public arguments, required fields, routing selectors and guidance from the registry.
147
+ Omitting `op` lists MCP registry names; internal/human-only and non-registry tools are
148
+ excluded. Metadata reads neither boot a canvas nor record content delivery. The catalog
149
+ now has 25 tools. Conditional handler validation still applies; no lazy host discovery
150
+ or smaller input schemas are claimed.
151
+
152
+ Equivalent success schemas shared by identity are grouped within each tool. Distinct
153
+ payload contracts, error branches and batch partial receipts remain unchanged. Two
154
+ real stdio measurements with `bun run scripts/measure-mcp-catalog.ts` were byte-identical:
155
+ the previous 24-tool candidate's 144,680 UTF-8 JSON bytes become 137,381 for the same
156
+ tools (7,299 fewer, 5.05%). Existing output schemas drop from 77,105 to 69,806 bytes.
157
+ Adding help costs 1,156 catalog bytes including its separator: 138,537 bytes total,
158
+ 6,143 fewer than the previous candidate (4.25%). Input-schema byte counts are unchanged.
159
+ This remains larger than published 0.9.3's 65,219 bytes. Bytes are not tokens, latency
160
+ or measured agent effectiveness. `session.start` and composite-input redesign remain deferred.
161
+
162
+ **Follow-up verification:** build, typecheck and `bun run test` pass (1,546 tests).
163
+ Real SDK stdio checks enumerate help for every exposed registry operation, validate
164
+ required fields, aliases and gate selectors, and create/remove a card using returned
165
+ arguments. Schema controls reject mismatched operations, malformed receipts and extra
166
+ envelope fields while accepting extensible payloads. Lint passes with 55 warnings and
167
+ 12 infos. The first combined run caught a second frozen tool list still expecting 24;
168
+ that assertion now includes help. Test-fixture/typecheck failures were corrected, with
169
+ first-failure logs retained.
170
+
171
+ **Full candidate rerun requested by the maintainer:** `bun run test:coverage` passes
172
+ 1,546 tests (67.27% lines, 73.62% functions, above the 60%/64% floors);
173
+ `bun run test:client` passes 332; `xvfb-run -a bash scripts/run-playwright.sh --headed`
174
+ passes all 333 desktop and 600/1024/1920 reference cases. Headed `test:e2e-cli`,
175
+ `release:smoke`, `pack:dry-run`, build, typecheck, lint and generated-catalog mirror
176
+ checks pass. A fresh packed MCP consumer discovers 25 tools, reads help without
177
+ creating state, and validates add/get results. Its allowed dependency versions
178
+ (SDK 1.32.2, Zod 4.6.7 versus checkout 1.28.0/4.3.6) encode nullable primitive schemas
179
+ as type arrays instead of `anyOf`. The initial byte-identity assertion failed;
180
+ comparison passes when only that equivalent encoding is normalized. No dependency changed.
181
+
182
+ The live portal serves the exact rebuilt bundle. Real stdio help-guided calls and
183
+ headed browser checks pass human pin → notification → explicit read → rendered edit,
184
+ unsubscribe, numeric progress tokens, cancelled approval waits, partial batch receipts
185
+ and refresh persistence. Both minimap toggles leave no tooltip and preserve Settings
186
+ focus. The first recording encoder fell behind; a later recording with the large suite
187
+ finished and a smaller viewport succeeded and was inspected frame by frame.
188
+ Native Amp/Copilot acceptance, Windows/macOS and remote/native reconnect remain unverified.
189
+
190
+ **Initial hardening verification:** build and typecheck pass; `bun run test` passes 1,544 tests and
191
+ `bun run test:client` passes 332. `xvfb-run -a bash scripts/run-playwright.sh --headed`
192
+ passes 333 cases; three pin/viewport/polling controls also pass after the final local
193
+ subscription event hook. `bun run release:smoke` passes on a disposable packed install,
194
+ not a registry publication. Lint passes with the same 55 warnings and 12 infos; generated
195
+ tool catalogs match. Real stdio tests cover local and daemon-attached progress/cancellation
196
+ and resource isolation. Initial test typing/SDK error-envelope failures and the red
197
+ library-notification regression were corrected; this is not a first-run-green claim.
198
+ Native 0.10 host acceptance, Windows/macOS and remote/native reconnect remain unverified.
199
+ Bounded simplify/harden review found no further blockers and made no additional changes.
200
+
201
+ **Independent baseline evidence:** the
202
+ [0.9.3 test thread](https://ampcode.com/threads/T-01a048cf-adb3-71bd-a7c6-472053b691d1)
203
+ reports no new confirmed functional defect after reruns, including installed SDK direct-tour
204
+ compatibility and native Amp pin/read/edit/ack acceptance. It does not establish 0.10 MCP
205
+ structured-output, cancellation or subscription conformance. Its low-severity KaTeX advisory,
206
+ unresolved first-run coverage timeout and remote/native reconnect gap remain separate from
207
+ this candidate; no dependency or reconnect fix is claimed here.
208
+
209
+ **Discovery cost:** `bun run scripts/measure-mcp-catalog.ts` measures UTF-8 bytes of the
210
+ real SDK client's `tools/list` JSON in a disposable stdio workspace, without canvas reads.
211
+ The 0.9.3 baseline is 65,219 bytes; the initial hardening candidate was 144,680 bytes, both 24 tools.
212
+ Output schemas contributed 77,105 bytes before the follow-up above. This is an increase in discovery overhead, not a
213
+ claim of cheaper prompts or a token estimate. Existing result text is unchanged; structured
214
+ results duplicate that projection for machine consumers. Host-specific prompt costs are unmeasured.
215
+
216
+ | Tool | 0.9.3 bytes | Initial hardening bytes |
217
+ |---|---:|---:|
218
+ | `canvas_pin_nodes` | 782 | 2227 |
219
+ | `canvas_invoke_command` | 804 | 2312 |
220
+ | `canvas_batch` | 1367 | 5184 |
221
+ | `canvas_import` | 1152 | 4251 |
222
+ | `canvas_snapshot` | 2238 | 6365 |
223
+ | `canvas_board` | 5859 | 14803 |
224
+ | `canvas_node` | 8184 | 13485 |
225
+ | `canvas_render` | 6321 | 11165 |
226
+ | `canvas_edge` | 2186 | 4423 |
227
+ | `canvas_group` | 1953 | 4532 |
228
+ | `canvas_history` | 494 | 2430 |
229
+ | `canvas_view` | 4341 | 12821 |
230
+ | `canvas_query` | 2476 | 7000 |
231
+ | `canvas_app` | 4729 | 7496 |
232
+ | `canvas_webview` | 1810 | 4834 |
233
+ | `canvas_ax_state` | 4462 | 7652 |
234
+ | `canvas_ax_work` | 2074 | 4527 |
235
+ | `canvas_ax_gate` | 2417 | 8285 |
236
+ | `canvas_ax_timeline` | 2637 | 6135 |
237
+ | `canvas_ax_delivery` | 1366 | 3284 |
238
+ | `canvas_intent` | 3127 | 5453 |
239
+ | `canvas_ax_interaction` | 1460 | 1969 |
240
+ | `canvas_ingest_activity` | 2341 | 2872 |
241
+ | `canvas_screenshot` | 604 | 1140 |
242
+
71
243
  1. Remeasure `tools/list` size per tool before any schema change.
72
244
  2. `outputSchema` + `structuredContent` for stable results (IDs, revisions, mutation
73
245
  outcomes); compact model-facing content; UI-only payloads in `_meta` where supported.
@@ -77,8 +249,12 @@ and move 1's context delivery, on the current SDK v1.
77
249
  for long operations where the host supports it; partial outcomes reported honestly.
78
250
  5. Resource subscriptions: advertise the capability, track subscribed URIs per connection,
79
251
  notify only subscribers. Explicit context reads keep working for hosts without them.
252
+ 6. Align SDK tour reads with HTTP/MCP `{ tour, derived, position }`; document the breaking
253
+ change from `.stops` to `.tour.stops` at this minor-version boundary.
254
+
255
+ 7. Approved follow-up: `canvas_help { op }` and bounded output-schema deduplication, as above.
80
256
 
81
- Candidates, not automatic scope: `canvas_help { op }`, `session.start`, per-action schemas.
257
+ Candidates, not automatic scope: `session.start`, composite-input redesign.
82
258
 
83
259
  **Done when**
84
260
  - Representative read and mutation results validate against their advertised schemas.
package/docs/sdk.md CHANGED
@@ -36,6 +36,16 @@ Missing/stale validating preconditions fail. Reviewed content must persist befor
36
36
  record. Read, edit, refresh and explicit retirement restore never renew validation; historical
37
37
  records retain their reviewed revision, and legacy unversioned evidence remains unknown.
38
38
 
39
+ ## Board tours
40
+
41
+ `canvas.getTour()` returns `{ tour, derived, position }`, matching HTTP and MCP.
42
+ Read stops from `canvas.getTour().tour.stops`. `derived` is true when stops come
43
+ from group reading order rather than a saved tour. `position` is the current
44
+ stop index, or null outside a tour.
45
+ This is a breaking change in 0.10: replace `.getTour().stops` with `.getTour().tour.stops`.
46
+ `canvas.setTour(tourOrNull)` saves the tour or resets it to derived group order.
47
+ See the [tour contract](http-api.md#board-tours) for navigation and camera moves.
48
+
39
49
  ## Quick example
40
50
 
41
51
  `canvas.addNode` also accepts `destinationBoardId`, `requestId`, and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmx-canvas",
3
- "version": "0.9.2",
3
+ "version": "0.10.0",
4
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",
@@ -312,7 +312,11 @@ async function startPanelServer(instanceId, ctx, pmx) {
312
312
  jsonResponse(res, 400, { ok: false, error: "prompt is required" });
313
313
  return;
314
314
  }
315
- await copilotSession?.send({ prompt: body.prompt });
315
+ if (!copilotSession) {
316
+ jsonResponse(res, 503, { ok: false, error: "Copilot session is unavailable." });
317
+ return;
318
+ }
319
+ await copilotSession.send({ prompt: body.prompt, mode: "immediate" });
316
320
  // Explicit user instruction from the panel → mirror onto the AX
317
321
  // timeline as a steering message (fire-and-forget).
318
322
  const target = await verifiedEntryTarget(entry);
@@ -635,7 +639,8 @@ const pmxCanvas = createCanvas({
635
639
  handler: async (ctx) => {
636
640
  const prompt = typeof ctx.input?.prompt === "string" ? ctx.input.prompt.trim() : "";
637
641
  if (!prompt) throw new CanvasError("prompt_required", "prompt is required");
638
- await copilotSession?.send({ prompt });
642
+ if (!copilotSession) throw new CanvasError("copilot_unavailable", "Copilot session is unavailable.");
643
+ await copilotSession.send({ prompt, mode: "immediate" });
639
644
  // Mirror the explicit user instruction onto the AX timeline as a
640
645
  // steering message (fire-and-forget). This is an explicit action
641
646
  // flow, never a sync from the prompt hook.
@@ -867,7 +872,8 @@ sessionWorkspaceRoot = resolve((await copilotSession.rpc.metadata.snapshot()).wo
867
872
  steeringDeliveryPump = createSteeringDeliveryPump({
868
873
  consumer: "copilot",
869
874
  claim: claimCopilotSteering,
870
- send: async (message) => await copilotSession.send({ prompt: message }),
875
+ // The SDK defaults to enqueue; canvas steering must target the active turn.
876
+ send: async (message) => await copilotSession.send({ prompt: message, mode: "immediate" }),
871
877
  mark: markCopilotSteering,
872
878
  getPresence: getCopilotPresence,
873
879
  setPresence: async (patch) => await postPresence(sessionWorkspaceRoot, patch),
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "0.9.2"
2
+ "version": "0.10.0"
3
3
  }
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
3
  "name": "pmx-canvas",
4
4
  "description": "PMX Canvas spatial workbench and MCP tools, with a native canvas panel in the GitHub Copilot app.",
5
- "version": "0.9.2",
5
+ "version": "0.10.0",
6
6
  "author": {
7
7
  "name": "pskoett",
8
8
  "url": "https://github.com/pskoett"
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "0.9.2"
2
+ "version": "0.10.0"
3
3
  }