pmx-canvas 0.12.0 → 0.12.1

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 (28) hide show
  1. package/.github/extensions/pmx-canvas/extension.mjs +1 -0
  2. package/.github/extensions/pmx-canvas/runtime.json +1 -1
  3. package/CHANGELOG.md +14 -0
  4. package/dist/amp-plugin/pmx-canvas/runtime.json +1 -1
  5. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/SKILL.md +27 -11
  6. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/evals/evals.json +88 -6
  7. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/full-reference.md +62 -8
  8. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/installing-pmx-canvas.md +61 -11
  9. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/tours-and-recording.md +14 -8
  10. package/dist/amp-plugin/pmx-canvas/skills/pmx-canvas/references/workbench-workflows.md +11 -3
  11. package/docs/agent-plugin.md +6 -0
  12. package/docs/amp-plugin.md +3 -4
  13. package/package.json +1 -1
  14. package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/extension.mjs +1 -0
  15. package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.json +1 -1
  16. package/plugins/pmx-canvas/plugin.json +1 -1
  17. package/plugins/pmx-canvas/runtime.json +1 -1
  18. package/skills/pmx-canvas/SKILL.md +27 -11
  19. package/skills/pmx-canvas/evals/evals.json +88 -6
  20. package/skills/pmx-canvas/references/full-reference.md +62 -8
  21. package/skills/pmx-canvas/references/installing-pmx-canvas.md +61 -11
  22. package/skills/pmx-canvas/references/tours-and-recording.md +14 -8
  23. package/skills/pmx-canvas/references/workbench-workflows.md +11 -3
  24. package/skills/web-artifacts-builder/scripts/bundle-artifact.sh +2 -2
  25. package/skills/web-artifacts-builder/scripts/init-artifact.sh +6 -6
  26. package/src/server/canvas-db.ts +13 -2
  27. package/src/server/web-artifacts/scripts/bundle-artifact.sh +2 -2
  28. package/src/server/web-artifacts/scripts/init-artifact.sh +6 -6
@@ -50,6 +50,7 @@ function delay(ms) {
50
50
  async function fetchJson(baseUrl, path, options = {}) {
51
51
  const response = await fetch(`${baseUrl}${path}`, {
52
52
  ...options,
53
+ headers: { "x-pmx-source": "copilot", ...options.headers },
53
54
  signal: AbortSignal.timeout(options.timeoutMs ?? HEALTH_TIMEOUT_MS),
54
55
  });
55
56
  if (!response.ok) {
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "0.12.0"
2
+ "version": "0.12.1"
3
3
  }
package/CHANGELOG.md CHANGED
@@ -3,6 +3,20 @@
3
3
  All notable changes to `pmx-canvas` are documented here. This project follows
4
4
  [Semantic Versioning](https://semver.org/).
5
5
 
6
+ ## [0.12.1] - 2026-11-05
7
+
8
+ ### Fixed
9
+
10
+ - Copilot adapter actions are attributed to Copilot instead of appearing as a separate API writer.
11
+ - Bundled agent guidance now describes current delivery, consent, live-card, batch and tour behavior accurately.
12
+ - Web artifact setup stops on dependency-install failures and shows the package-manager error.
13
+ - Reduced disk writes when creating a workspace database without weakening save durability.
14
+
15
+ ### Internal
16
+
17
+ - Browser CI uses Ubuntu 24.04, which the installed Playwright version supports.
18
+ - Editor-history checks now verify that the history panel opens before continuing.
19
+
6
20
  ## [0.12.0] - 2026-10-31
7
21
 
8
22
  ### Added
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "0.12.0"
2
+ "version": "0.12.1"
3
3
  }
@@ -15,11 +15,13 @@ Use this entry guide for every task, then load only the task-specific reference
15
15
 
16
16
  ## Runtime and skill identity
17
17
 
18
- PMX Canvas 0.12.0 requires **Bun >=1.4.4**, including npm installations. Check `bun --version`
18
+ PMX Canvas 0.12.1 requires **Bun >=1.4.4**, including npm installations. Check `bun --version`
19
19
  and the MCP host's PATH. Install or configure only when requested. Read
20
20
  [Installing PMX Canvas](references/installing-pmx-canvas.md) for pinned setup and managed services.
21
21
  In Amp, portal access and native MCP discovery are separate; workspace commands need human trust
22
- approval. A connected MCP server does not install an idle-thread wake-up adapter.
22
+ approval. Raw MCP does not install an idle-thread wake-up adapter. The opt-in
23
+ [Amp plugin](references/installing-pmx-canvas.md#amp-plugin-bound-threads) adds board binding,
24
+ receipt-based delivery and steering while its executor is awake.
23
25
 
24
26
  After upgrading, run `pmx-canvas skills sync --check` first (exit 1 means drift). Review whole-tree
25
27
  differences and preserve local edits. `skills sync --yes` replaces all drifted installed copies
@@ -60,12 +62,15 @@ See [workspace targeting](references/workbench-workflows.md#workspace-safety) fo
60
62
 
61
63
  ## Read, then act on the intended board
62
64
 
63
- 1. Reuse one visible workbench. Call `canvas_board { action: "list" }` before board work.
64
- Home (`activeBoardId: null`) is not an empty library. Open the intended board when active-board
65
- work requires it and verify the returned `activeBoardId`. Opening changes the shared workbench.
65
+ 1. With the Amp plugin, inspect `pmx_canvas_session { action: "status" }` and use its bound
66
+ board without opening it. Use routed MCP query tools, not resource reads or CLI calls;
67
+ those bypass binding. Without a binding, reuse one visible workbench and call
68
+ `canvas_board { action: "list" }` before board work.
69
+ Home (`activeBoardId: null`) is not an empty library. For unbound active-board work, open
70
+ the intended board and verify `activeBoardId`. Opening changes the shared workbench.
66
71
  Library discovery and supported background creation need no switch.
67
- 2. Read `canvas://context?consumer=<stable-unique-id>` or
68
- `canvas_query { action: "context", consumer: "<stable-unique-id>" }`. Use a stable consumer
72
+ 2. Read `canvas_query { action: "context", consumer: "<stable-unique-id>" }`, or, without
73
+ a binding, `canvas://context?consumer=<stable-unique-id>`. Use a stable consumer
69
74
  only when durable deltas are wanted; omit it for a fresh read. Follow `cursor.next` as `since`
70
75
  to finish a budgeted brief. Budgets are UTF-16 code units, not tokens.
71
76
  3. The brief is a map of summaries, pins and relations, not a full-content read. Pull needed cards
@@ -77,7 +82,9 @@ See [workspace targeting](references/workbench-workflows.md#workspace-safety) fo
77
82
  marks prove delivery, not understanding or approval. Refresh stale context after interruptions.
78
83
  5. Snapshot before clear, restore or major rearrangement. Confirm destructive changes first.
79
84
  6. Signal meaningful mutations with `canvas_intent { action: "signal", ttlMs: 30000, ... }`,
80
- then pass the returned `intent.id` as `intentId`. Only an explicit intent gives a pre-write
85
+ then pass the returned `intent.id` on a supported node/edge/group mutation. For batches,
86
+ link the supported leaf's `args.intentId`, never the batch envelope. Quiet expiry proceeds
87
+ unlinked; a human veto refuses the linked write. Only an explicit intent gives a pre-write
81
88
  veto window; automatic ghosts are not a substitute. Skip signals for trivial/high-frequency churn.
82
89
  7. Reuse an adapter-owned session. Inspect `canvas_ax_state { action: "presence" }` first.
83
90
  Copilot's `copilot` identity is not a new task-named agent. Put task descriptions in `detail`.
@@ -86,7 +93,9 @@ See [workspace targeting](references/workbench-workflows.md#workspace-safety) fo
86
93
  Inspect rendered content, sizes and relations before reporting success. A stored payload or
87
94
  `panned: true` is not evidence of what the human sees.
88
95
  9. Read steering as your own consumer, act, verify the visible result, then mark that message
89
- delivered. Remove owned temporary nodes when the task calls for leaving the board unchanged.
96
+ delivered. With the Amp plugin, first `receive` the supplied token, then `acknowledge` only
97
+ after verified action; receiving a context map is not a full card read or completion.
98
+ Remove owned temporary nodes when the task calls for leaving the board unchanged.
90
99
 
91
100
  When authoring a board, inspect its `readmeNodeId`. If absent, choose/create a concise Markdown
92
101
  introduction and designate it through `canvas_board update`. Preserve an existing human choice.
@@ -121,6 +130,9 @@ Standalone tools: `canvas_batch`, `canvas_pin_nodes`, `canvas_screenshot`, `canv
121
130
  `canvas_ingest_activity`, `canvas_invoke_command`, `canvas_help`. Do not invent removed legacy tools.
122
131
  See [full API reference](references/full-reference.md) for payloads and
123
132
  [composite action lists](references/workbench-workflows.md#current-mcp-composites).
133
+ Before interpreting a reply, read [MCP result shapes](references/full-reference.md#mcp-results-and-cancellation-010):
134
+ registry results use `result.data`, help uses `result`, and direct tools use their own schema.
135
+ Do not apply one unwrap rule to every tool; retain partial-write and error information.
124
136
 
125
137
  ## Choose the lightest useful surface
126
138
 
@@ -138,6 +150,9 @@ For ordinary work planning, deliberately create `status` cards with `data.workSt
138
150
  session, task or approval; do not generate a card for every work event. Connect useful steps,
139
151
  alternatives and loops with existing relations. Humans can Edit/Save/Cancel inside each card.
140
152
  See [work planning](references/full-reference.md#task-plan-with-dependencies) for examples.
153
+ To display an existing task instead, create a [live task card](references/full-reference.md#live-task-cards)
154
+ with `data.workItemId`. Its task-derived fields are read-only and its task owner is immutable;
155
+ update the underlying task, not the presentation. Deleting the card does not cancel the task.
141
156
 
142
157
  Read [HTML primitives](references/html-primitives.md) before choosing their payloads or copy/export
143
158
  flows, [AX control surfaces](references/ax-html-control-surface.md) before authoring interactive
@@ -213,8 +228,9 @@ otherwise recheck before reuse.
213
228
  Read [Tours and recording](references/tours-and-recording.md) before authoring, navigating or
214
229
  capturing a tour. `set-tour` persists stops; deterministic capture uses the same model in its own
215
230
  viewer. Rendering and free pan are local, but human Next/Back and agent `tour-go` share a cursor
216
- and affect presenting viewers; `tour-exit` exits everywhere. Optional presenter fences reject stale
217
- commands, not other viewers. A superseded result is not applied navigation.
231
+ for the target board and affect its presenting viewers; `tour-exit` exits those viewers.
232
+ Optional presenter fences reject stale commands and presenter-bearing background calls,
233
+ not other viewers. A superseded result is not applied navigation.
218
234
 
219
235
  Use the [browser workflow reference](references/workbench-workflows.md#browser-workflows) for
220
236
  keyboard, groups, menus, reconnect/draft behavior and host-local theme overrides. Use the picker
@@ -564,17 +564,17 @@
564
564
  {
565
565
  "id": 44,
566
566
  "name": "presenter-superseded-is-not-applied-navigation",
567
- "prompt": "In a disposable board, verify optional presenter ordering. Send a valid go with intent 2, then an exit with intent 1 for the same document and activation. Repeat with a stale activation. Include an independent-document control and an ordinary unfenced call. Can ok:true or the SDK exit return prove the stale command ran?",
568
- "expected_output": "Reads the actual activation stamps and uses positive safe-integer intents with independent document IDs. Checks applied delivery stamps and cursor/event behavior. Older intent and stale activation return ok:true,superseded:true without a navigation event or cursor change. Independent and unfenced controls still apply. Explains that the fence orders requests, does not grant authority or isolate viewers, and that SDK exit returns void while fenced go may return superseded:true. Does not count eval definitions as executed evidence.",
567
+ "prompt": "In disposable boards A and B, verify optional presenter ordering. On visible A send a valid go with intent 2, then an exit with intent 1 for the same document and activation. Repeat with a stale activation. Include an independent-document control and an ordinary unfenced call. Then target background B with a presenter-bearing call and an unfenced control. Can ok:true or the SDK exit return prove the stale command ran?",
568
+ "expected_output": "Reads the actual activation stamps and uses positive safe-integer intents with independent document IDs. Checks applied delivery stamps and cursor/event behavior. Older intent, stale activation and presenter-bearing background calls return ok:true,superseded:true without navigation or cursor change. Independent and unfenced controls still apply on their target board; B's tour does not replace A's cursor or navigate its viewers. Explains that the fence orders requests, does not grant authority or isolate same-board viewers, and that SDK exit returns void while fenced go may return superseded:true. Does not count eval definitions as executed evidence.",
569
569
  "assertions": [
570
570
  {
571
571
  "name": "checks-superseded-side-effects",
572
- "description": "Observes no navigation event or cursor change for stale intent and activation, rather than treating ok:true as applied navigation",
572
+ "description": "Observes no navigation event or cursor change for stale intent, stale activation and presenter-bearing background calls, rather than treating ok:true as applied navigation",
573
573
  "type": "output_check"
574
574
  },
575
575
  {
576
576
  "name": "positive-controls-and-stamps",
577
- "description": "Verifies applied epoch/generation/sequence for the newer request and exercises independent-document and unfenced calls without inventing mandatory presenter credentials",
577
+ "description": "Verifies applied epoch/generation/sequence for the newer request and independent-document/unfenced controls; B's unfenced call changes only B's cursor while A remains visible with its own cursor",
578
578
  "type": "output_check"
579
579
  },
580
580
  {
@@ -587,7 +587,7 @@
587
587
  {
588
588
  "id": 23,
589
589
  "name": "multi-board-targeting",
590
- "prompt": "I have Roadmap and Incident Review boards. Add a decision note only to Incident Review and set its tour to the Summary group. Do not disturb Roadmap.",
590
+ "prompt": "Using raw MCP without an Amp plugin binding, I have Roadmap and Incident Review boards. Add a decision note only to Incident Review and set its tour to the Summary group. Preserve Roadmap content; switching the shared view is allowed.",
591
591
  "expected_output": "Lists/gets boards, resolves the exact Incident Review board and calls canvas_board action open or CLI board open with its ID, verifies the returned activeBoardId, searches for the Summary group there, then adds the note and sets that board's tour. It explains that opening switches the shared view but preserves Roadmap content. It does not infer IDs from titles, mutate Roadmap, invent inactive-board write targeting, or assume that a board switch succeeded. It validates Incident Review and reports board/node IDs.",
592
592
  "assertions": [
593
593
  {
@@ -753,7 +753,7 @@
753
753
  {
754
754
  "id": 30,
755
755
  "name": "amp-native-steering-evidence-boundary",
756
- "prompt": "On this disposable board, native Amp MCP is connected and your source/consumer is amp-review. The browser pinned the Review card and sent amp-review a fresh instruction to append receipt CHECK-30. Prove the round trip, then confirm that an idle Amp thread will now wake automatically and that native Skills discovery works.",
756
+ "prompt": "On this disposable board, raw native Amp MCP is connected without the directory plugin and your source/consumer is amp-review. The browser pinned the Review card and sent amp-review a fresh instruction to append receipt CHECK-30. Prove the round trip, then confirm that an idle Amp thread will now wake automatically and that native Skills discovery works.",
757
757
  "expected_output": "Uses actual Amp Canvas tools to read the pinned card, claims only as amp-review, signals intent and appends the requested receipt without losing existing content. Verifies the rendered receipt before marking that delivery for amp-review and confirms it is no longer pending. Uses a different recipient as a negative control and detaches only self-owned test sessions. Reports active-agent polling as verified but declines to infer idle-thread wake-up, persistent pumping or native MCP Skills discovery from this evidence.",
758
758
  "assertions": [
759
759
  {
@@ -1081,6 +1081,88 @@
1081
1081
  "type": "output_check"
1082
1082
  }
1083
1083
  ]
1084
+ },
1085
+ {
1086
+ "id": 45,
1087
+ "name": "quiet-expiry-versus-veto-and-leaf-linkage",
1088
+ "prompt": "In a disposable canvas, signal separate create intents for three Markdown cards. Let the harness quietly expire one, human-veto another, and leave the third live. Create each through a separate batch leaf using the returned intent ID. Explain whether putting one intentId on the batch envelope would protect every sibling. Do not bypass the veto or treat expiry as approval.",
1089
+ "expected_output": "Uses operations[].args.intentId on supported node.add leaves, never the batch envelope. Verifies the expired case proceeds unlinked, the vetoed case creates no card, and the live case settles once. Explains the standalone-MCP versus attached-HTTP top-level mismatch without relying on either behavior. Ordinary scope/revision rules still apply; no new ID is minted to evade the veto.",
1090
+ "assertions": [
1091
+ {
1092
+ "name": "distinguishes-expiry-from-human-refusal",
1093
+ "description": "Readback finds the expired control's card but no vetoed card; the agent neither labels quiet expiry a veto nor retries the vetoed plan under another ID",
1094
+ "type": "output_check"
1095
+ },
1096
+ {
1097
+ "name": "links-only-the-supported-leaf",
1098
+ "description": "Trace puts intentId in node.add args, verifies live settlement, and does not claim one settled intent protects later siblings or a top-level batch intent is portable",
1099
+ "type": "output_check"
1100
+ }
1101
+ ]
1102
+ },
1103
+ {
1104
+ "id": 46,
1105
+ "name": "amp-bound-delivery-and-separate-jev-consents",
1106
+ "prompt": "The actual Amp plugin binds this thread to A while the human views B. Its context packet contains a receive token and a summary of A/Review. Addressed steering asks you to append CHECK-46 there. Context ranking is enabled but Jev advice is off. Show the edit, acknowledge it, and ask Jev to rank your proposed changes. Can you read canvas://context instead, open A, or wake a sleeping orb? Do not change consent yourself.",
1107
+ "expected_output": "Checks session status, receives each supplied packet token, reads full A/Review through routed MCP, edits without switching B, and verifies the result before acknowledging steering. Does not mistake a context receipt for a full read or completed action. Declines Jev inference without the separate advice consent, using deterministic fallback or requesting human opt-in. Reports awake-executor idle wake separately from sleeping-orb resume.",
1108
+ "assertions": [
1109
+ {
1110
+ "name": "uses-bound-tools-without-navigation",
1111
+ "description": "Trace uses routed MCP rather than CLI or resources/read, preserves B's active view, and never overrides the bound destination",
1112
+ "type": "output_check"
1113
+ },
1114
+ {
1115
+ "name": "receive-is-not-acknowledge",
1116
+ "description": "Packet reception precedes full content reading and action; visible CHECK-46 verification precedes steering acknowledgment, and failed/unverified work is not acknowledged",
1117
+ "type": "output_check"
1118
+ },
1119
+ {
1120
+ "name": "consent-and-wake-boundaries",
1121
+ "description": "Ranking consent is not reused for advice, no consent is enabled by the agent, and native awake-executor wake is not claimed to wake a sleeping orb",
1122
+ "type": "output_check"
1123
+ }
1124
+ ]
1125
+ },
1126
+ {
1127
+ "id": 47,
1128
+ "name": "authored-versus-live-task-card-ownership",
1129
+ "prompt": "In a disposable library, A and B each have a task named Review. Show A's existing task as a live status card and also create an independent authored plan card. Mark A's task in-progress, move its presentation to B, then delete that presentation. Do not transfer or cancel either task. Can you edit the live card's title/progress or rebind it to B's same-named task?",
1130
+ "expected_output": "Resolves task identity on A, creates the live card using workItemId without derived fields, and creates the authored card separately with workState. Updates the task through canvas_ax_work on A. After moving, confirms immutable workItemBoardId A and fresh task-derived display; rejects title/progress edits and rebinding. Deleting the presentation leaves both tasks intact and the authored card independent.",
1131
+ "assertions": [
1132
+ {
1133
+ "name": "separates-task-from-presentation",
1134
+ "description": "Live creation includes the resolved task ID but no derived title/content/workState/detail/progress; authored creation uses workState without a task binding",
1135
+ "type": "output_check"
1136
+ },
1137
+ {
1138
+ "name": "preserves-owner-across-move",
1139
+ "description": "Readback after relocation shows owner A, the original task ID and updated state, never B's same-title task; no rebinding or direct display-field edit is attempted as a solution",
1140
+ "type": "output_check"
1141
+ },
1142
+ {
1143
+ "name": "deletion-is-not-cancellation",
1144
+ "description": "After deleting only the presentation, qualified task reads confirm A's task is still in-progress and B's task unchanged; authored plan data remains independent",
1145
+ "type": "output_check"
1146
+ }
1147
+ ]
1148
+ },
1149
+ {
1150
+ "id": 48,
1151
+ "name": "batch-annotation-partial-result-and-tool-envelopes",
1152
+ "prompt": "In a disposable canvas, read canvas_help for supported batch operations. Add a text annotation with an explicit ID, then submit a deliberate unsupported edge.update as the second batch entry. Inspect the failure before retrying the annotation, then remove it. Compare the raw structured replies from canvas_help, canvas_batch and canvas_ingest_activity. Can you always unwrap result.data or retry the entire failed batch?",
1153
+ "expected_output": "Uses annotation.add/remove, identifies edge.update as unsupported, and checks the failedIndex plus committed results/refs before retrying. Equal normalized annotation content with the same ID reuses the mark; removal is not a durable replay receipt. Distinguishes registry result.operation/outcome/data, help metadata under result and direct activity fields. It preserves isError/partial state instead of discarding it or blindly replaying writes.",
1154
+ "assertions": [
1155
+ {
1156
+ "name": "partial-failure-keeps-earlier-annotation",
1157
+ "description": "Failure at index 1 retains one annotation; identical-ID/content retry does not duplicate it, and removal erases that mark only",
1158
+ "type": "output_check"
1159
+ },
1160
+ {
1161
+ "name": "decodes-per-tool-contract",
1162
+ "description": "Extracts batch results from structuredContent.result.data and checks outcome partial, reads help from result, and reads activity fields directly without blanket unwrapping",
1163
+ "type": "output_check"
1164
+ }
1165
+ ]
1084
1166
  }
1085
1167
  ]
1086
1168
  }
@@ -624,8 +624,11 @@ persisted, auto-expiring (~8s), never in `canvas_query layout`.
624
624
  Narrate → linked mutation → automatic settle:
625
625
  1. `canvas_intent { action: "signal", kind: "create", nodeType: "markdown", label: "Add evidence", reason: "capturing the failing test", confidence: 0.8 }` → returns `intent.id` and the `position` the canvas chose.
626
626
  2. Make the real move and pass that id: `canvas_node { action: "add", intentId: <intent.id>, ... }`.
627
- 3. PMX rejects the mutation if the intent was vetoed or expired; otherwise the
628
- successful mutation settles the ghost into the resulting node automatically.
627
+ 3. A human-vetoed intent rejects its linked mutation. A quietly expired ghost does not:
628
+ the write proceeds unlinked, subject to the ordinary permissions and revision checks.
629
+ A successful write linked to a still-live intent settles the ghost automatically.
630
+ Expiry is neither approval nor a human veto. Never retry a vetoed plan under a new ID
631
+ just to bypass the human's decision.
629
632
 
630
633
  Use `canvas_intent { action: "clear", id }` only when abandoning a plan without
631
634
  performing the linked mutation.
@@ -650,9 +653,18 @@ edits; destructive actions (clear/restore/remove); and creating
650
653
  artifact/report/dashboard nodes. Skip for tiny metadata fixes, API-only pin/unpin
651
654
  verification, deterministic report-node refreshes the human just asked for,
652
655
  post-restore cleanup, and bulk fixture churn. **For batch work, signal one ghost
653
- per human-meaningful move, not one per low-level op** — e.g. one "lay out the
654
- investigation board" intent, then run the batch with that linked `intentId` (use
655
- `seq` to order staged previews) so the human watches the wireframe before it fills.
656
+ per human-meaningful move, not one per low-level op.** Put its `intentId` inside the
657
+ supported leaf operation's `args`, never on the `canvas_batch` envelope. Each live
658
+ intent settles once; it is not a veto gate for every sibling in a batch. For example,
659
+ after signalling a create intent, use its actual returned ID in this leaf:
660
+
661
+ ```json
662
+ {"operations":[{"op":"node.add","args":{"type":"markdown","title":"Evidence","content":"Observed failure","intentId":"<returned-intent-id>"}}]}
663
+ ```
664
+
665
+ Do not rely on a top-level batch `intentId`: standalone MCP can refuse it, while the
666
+ attached HTTP batch reader does not forward it. Unsupported operations cannot be made
667
+ veto-linked by wrapping them in a batch. Use `seq` to order separate staged previews.
656
668
 
657
669
  ### Standalones (first-class — not deprecated)
658
670
 
@@ -663,10 +675,12 @@ composite in v0.4.0 (actions `save | list | restore | delete | gc | diff`); the
663
675
  standalones were removed after their deprecated 0.3.x window.
664
676
 
665
677
  `canvas_batch` supports exactly these ops: `node.add`, `node.update`, `node.remove`, `graph.add`,
666
- `edge.add`, `edge.update`, `edge.remove`, `group.create`, `group.add`, `group.remove`, `pin.set`/`pin.add`/
667
- `pin.remove`, `snapshot.save`, and `arrange`. Anything else fails with "Unsupported canvas_batch
678
+ `edge.add`, `edge.remove`, `group.create`, `group.add`, `group.remove`, `annotation.add`,
679
+ `annotation.remove`, `pin.set`, `pin.add`, `pin.remove`, `snapshot.save`, and `arrange`.
680
+ `edge.update` is not supported. Anything else fails with "Unsupported canvas_batch
668
681
  operation" — batch is non-atomic, so earlier ops stay applied and the response carries
669
- `failedIndex`. `canvas_screenshot` requires an active automation WebView: call
682
+ `failedIndex`, `results` and `refs`. Inspect that committed prefix before retrying; an
683
+ error does not roll it back. `canvas_screenshot` requires an active automation WebView: call
670
684
  `canvas_webview { action: "start" }` first.
671
685
 
672
686
  > **Removed in v0.3.0 → use the composite instead**: `canvas_open_mcp_app` / `canvas_add_diagram` /
@@ -1186,6 +1200,22 @@ writes occurred. Interaction, activity-ingestion and screenshot tools instead
1186
1200
  use direct structured objects matching their schemas. Check `isError` and domain
1187
1201
  outcomes. A composite's annotations cover all its actions, not just reads.
1188
1202
 
1203
+ Paired example: `canvas_node { action: "add", type: "markdown", content: "Evidence" }`
1204
+ returns registry data under `structuredContent.result.data` (excerpt; the ID varies):
1205
+
1206
+ ```json
1207
+ {"result":{"operation":"node.add","outcome":"success","data":{"ok":true,"id":"example-node"}}}
1208
+ ```
1209
+
1210
+ By contrast, `canvas_help {}` returns `structuredContent.result.operations`, not
1211
+ `result.data`. `canvas_help { op: "node.add" }` returns metadata with `operation`,
1212
+ `tool`, `description` and `inputSchema` directly under `result`.
1213
+ For direct tools (`canvas_ax_interaction`, `canvas_ingest_activity`, `canvas_screenshot`),
1214
+ read fields directly from `structuredContent` according to their `outputSchema`.
1215
+ These are different contracts, not optional wrappers to strip recursively. Text blocks
1216
+ are a separate representation; an SDK/host may already return `structuredContent`.
1217
+ On a failed call, inspect its error response and partial results before retrying writes.
1218
+
1189
1219
  Cancellation releases supported AX waits, aborts webpage fetches before late
1190
1220
  content writes, and stops subsequent batch operations. Completed writes remain;
1191
1221
  batch partial results retain `results` and `refs`. A created webpage can remain
@@ -1645,6 +1675,30 @@ Without `data.workState`, status cards retain their phase/tool display. Session-
1645
1675
  is not provided by these authored cards. Questions can be drawn or written beside the plan;
1646
1676
  the human can answer in the external agent host's composer, without a PMX reply card.
1647
1677
 
1678
+ ### Live task cards
1679
+
1680
+ A live card presents an existing AX work item; it is not another task or an authored
1681
+ work plan. Read the task first with `canvas_ax_work { action: "list" }` on its owning
1682
+ board, then create a `status` presentation using that task's actual ID:
1683
+
1684
+ ```json
1685
+ {"action":"add","type":"status","data":{"workItemId":"<existing-task-id>"}}
1686
+ ```
1687
+
1688
+ The owner defaults to the current board. To present a task from another board, supply
1689
+ its explicit `data.workItemBoardId` too. Both task ID and owner are fixed at creation;
1690
+ moving the card does not transfer the task. Create through the current board owner
1691
+ (or the Amp plugin's bound board), not `destinationBoardId` background creation.
1692
+
1693
+ Omit `title`, `content`, `workState`, `detail`, `progress`, `axWorkStatus`,
1694
+ `liveWorkAvailable` and `liveWorkSource` from creation and updates, including nested
1695
+ data fields. The server derives title, detail, status and source from the task; these
1696
+ are read-only display fields. Update the underlying work item on its owning board with
1697
+ `canvas_ax_work { action: "update", id: "<existing-task-id>", status: "in-progress" }`.
1698
+ Position and size still belong to the card. Deleting the presentation does not cancel
1699
+ or delete the task. If the task is unavailable, the card displays that fact rather than
1700
+ becoming an editable authored card. To change the binding, create a new presentation.
1701
+
1648
1702
  ### Code Exploration
1649
1703
 
1650
1704
  Understand a codebase by visualizing file relationships:
@@ -4,7 +4,7 @@ Use this reference when the `pmx-canvas` skill is installed but the `pmx-canvas`
4
4
 
5
5
  ## Prerequisites and version
6
6
 
7
- The examples pin **PMX Canvas 0.12.0**, which requires **Bun >=1.4.4** even when
7
+ The examples pin **PMX Canvas 0.12.1**, which requires **Bun >=1.4.4** even when
8
8
  installed through npm: the CLI runs through Bun, not Node. Check `bun --version`
9
9
  first. If missing or older, follow the [Bun installation instructions](https://bun.sh/docs/installation)
10
10
  with the user's authorization. Ensure Bun is on the MCP host's PATH as well as the
@@ -22,11 +22,11 @@ in `SKILL.md`. Do not pair newer checkout guidance with an older runtime unknowi
22
22
  ## Install from npm
23
23
 
24
24
  ```bash
25
- npm install -g pmx-canvas@0.12.0
25
+ npm install -g pmx-canvas@0.12.1
26
26
  pmx-canvas --version
27
27
  ```
28
28
 
29
- Without a global install, use `bunx pmx-canvas@0.12.0` in place of `pmx-canvas` in
29
+ Without a global install, use `bunx pmx-canvas@0.12.1` in place of `pmx-canvas` in
30
30
  the commands below. Do not use unpinned `bunx pmx-canvas` for a version-specific check.
31
31
 
32
32
  ## Choose the workspace and target
@@ -96,7 +96,7 @@ discards empty env values, remove `PMX_CANVAS_URL` from its launch environment i
96
96
  "mcpServers": {
97
97
  "canvas": {
98
98
  "command": "bunx",
99
- "args": ["pmx-canvas@0.12.0", "--mcp"],
99
+ "args": ["pmx-canvas@0.12.1", "--mcp"],
100
100
  "env": {
101
101
  "PMX_CANVAS_WORKSPACE_ROOT": "/absolute/path/to/project",
102
102
  "PMX_CANVAS_PORT": "14313",
@@ -130,9 +130,59 @@ fallback ports; do not assume the configured port is the one the MCP server boun
130
130
 
131
131
  ## Amp: connect the agent separately from the portal
132
132
 
133
- PMX does not ship a dedicated Amp lifecycle adapter. Amp can use the native MCP tools;
134
- the portal is only the human's browser UI. Configure MCP inside the orb or runner where
135
- Amp executes, not only on the user's laptop. Merge this into that workspace's
133
+ PMX ships an opt-in directory plugin with board binding and receipt-based steering, or
134
+ you can configure raw MCP for active polling. The portal is the human's browser UI;
135
+ neither opening it nor configuring raw MCP installs the plugin. Choose one integration
136
+ for the task rather than creating duplicate writers.
137
+
138
+ ### Amp plugin: bound threads
139
+
140
+ When installation is requested, copy the complete installed package directory
141
+ `node_modules/pmx-canvas/dist/amp-plugin/pmx-canvas` to the project's
142
+ `.amp/plugins/pmx-canvas`. Inspect existing plugin files and preserve local changes;
143
+ replacement needs owner approval. Reload Amp plugins and load the qualified
144
+ `pmx-canvas:pmx-canvas` skill. Approve its workspace MCP command through the normal
145
+ human trust flow, never by bypassing it. No global plugin installation is implied.
146
+
147
+ Use `pmx_canvas_session { action: "open" }` to start/reconnect and verify the service,
148
+ then `bind` with an existing explicit `boardId`. `status` reports the binding and
149
+ shared active board; `unbind` removes only this thread's binding. Binding does not
150
+ open a board, change sharing, or create an access-control boundary.
151
+
152
+ Bound MCP tool calls route to that board with identity `amp:<thread-id>`, even when
153
+ the human views another board or Home. Use `canvas_query` for context and full
154
+ `canvas_node` reads for needed content. CLI calls and MCP resource reads are not
155
+ intercepted. Do not navigate the shared workbench through bound calls, override the
156
+ destination, or open the board to make a write work. A missing board or changed
157
+ binding fails instead of silently targeting the visible board; a binding change
158
+ during a turn requires a new turn.
159
+
160
+ At turn start, return the supplied context token through
161
+ `pmx_canvas_delivery { action: "receive", token }`. Reception records the delivered
162
+ map, not full card reads, understanding or approval. For a steering token, receive it,
163
+ read the relevant content, act, verify the result, and only then call
164
+ `pmx_canvas_delivery { action: "acknowledge", token }`. Do not acknowledge failed or
165
+ unverified work. Uncertain sends require human resolution, not blind retries or
166
+ deleting the transport journal.
167
+
168
+ Addressed steering can start an idle thread or queue into a busy turn only while
169
+ the plugin executor is awake. It does not wake a sleeping orb or hold a keep-alive
170
+ lease. A raw MCP polling test does not prove this native delivery path.
171
+
172
+ **Context ranking** and **Jev advice** are separate opt-ins, both off by default.
173
+ Their Amp commands require separate human confirmation for the current binding and
174
+ disclose data sent to TypeSafe Jev and the invoking thread's inference billing.
175
+ Ranking reorders optional summaries; advice suggests formats or review order without
176
+ creating output or verifying work. One consent never enables the other. Rebinding
177
+ does not carry either consent. Respect deterministic fallback and report it rather
178
+ than retrying with another provider. See the
179
+ [plugin contract](https://github.com/pskoett/pmx-canvas/blob/main/docs/amp-plugin.md)
180
+ for request fields, retry identities and native acceptance limits.
181
+
182
+ ### Raw MCP: active polling
183
+
184
+ Configure MCP inside the orb or runner where Amp executes, not only on the user's
185
+ laptop. Merge this into that workspace's
136
186
  `.amp/settings.json`, preserving other settings (Amp does not use the generic `.mcp.json`):
137
187
 
138
188
  ```json
@@ -140,7 +190,7 @@ Amp executes, not only on the user's laptop. Merge this into that workspace's
140
190
  "amp.mcpServers": {
141
191
  "canvas": {
142
192
  "command": "bunx",
143
- "args": ["pmx-canvas@0.12.0", "--mcp"],
193
+ "args": ["pmx-canvas@0.12.1", "--mcp"],
144
194
  "includeTools": ["canvas_*"],
145
195
  "env": {
146
196
  "PMX_CANVAS_WORKSPACE_ROOT": "/absolute/path/to/project",
@@ -174,7 +224,7 @@ The agent must not run this approval itself or move the command to global settin
174
224
  `--mcp-config` to bypass workspace trust. Approval authorizes execution of this MCP
175
225
  command; it is not a new Canvas import consent or permission to mutate arbitrary boards.
176
226
  After approval, reload MCP connections (`reload_mcp` in Amp) and require `connected`.
177
- Discover `canvas` tools with `tool_search`; the full unfiltered 0.12.0 server has 25 tools,
227
+ Discover `canvas` tools with `tool_search`; the full unfiltered 0.12.1 server has 25 tools,
178
228
  including `canvas_help`. If still absent, inspect the server status, command/PATH and target instead
179
229
  of reinstalling blindly. See [Amp MCP documentation](https://ampcode.com/docs/customize/mcp).
180
230
 
@@ -194,8 +244,8 @@ of reinstalling blindly. See [Amp MCP documentation](https://ampcode.com/docs/cu
194
244
  4. Verify the visible result in the browser before marking the message delivered for
195
245
  that consumer. A subsequent claim should omit it. Detach only test-owned sessions.
196
246
 
197
- This proves an **active agent can poll and act**, not automatic wake-up of an idle Amp
198
- thread, background polling, or automatic agent launch for imports. An MCP connection
247
+ This raw-MCP check proves an **active agent can poll and act**, not automatic wake-up
248
+ of an idle Amp thread, background polling, or automatic agent launch for imports. An MCP connection
199
249
  does not install a steering pump. Only configure a pump/host integration when requested;
200
250
  its startup backlog rules are in the main skill. Tool discovery and resource reads also
201
251
  do not establish Amp support for native MCP Skills-extension discovery or activation.
@@ -32,8 +32,8 @@ Open `/workbench?present=1` for chrome-free viewing. Right, Down, or Space advan
32
32
  goes back; Escape or **Exit presentation** exits. Right-drag pans and interrupts an ongoing
33
33
  transition without editing cards. Stops do not auto-advance. A missing node target reports an
34
34
  error and may be skipped. Camera rendering and free pan are viewer-local, but human Back/Next
35
- and arrow keys advance the shared cursor for all presenting viewers; Exit leaves presentation
36
- everywhere. `present: false` does not isolate navigation from another presenting viewer.
35
+ and arrow keys advance the target board's shared cursor for its presenting viewers; Exit leaves
36
+ presentation for those viewers. `present: false` does not isolate viewers of the same board.
37
37
  Deterministic capture uses its own viewer and ignores live navigation. Recording hides the exit control.
38
38
 
39
39
  Tours are board-scoped, persist with that board and its snapshots, participate in undo, and clear
@@ -44,11 +44,15 @@ setting or reading it.
44
44
 
45
45
  ## Drive a tour
46
46
 
47
- The server holds one in-memory tour cursor, tied to the board it was started on (a tour on another board replaces it). `canvas_view { action: "tour-go", stop: 0 }`
48
- starts presenting in open workbenches; `step: "next" | "previous"` moves relative to wherever the
49
- audience is (a human's arrow keys move the same cursor) and clamps at the ends. Pass
47
+ The server keeps board-local in-memory tour cursors, not one workspace-wide cursor.
48
+ `canvas_view { action: "tour-go", stop: 0 }` starts presenting in workbenches showing the
49
+ target board; `step: "next" | "previous"` moves relative to that board's cursor
50
+ (a human's arrow keys move the same cursor) and clamps at the ends. Pass
50
51
  `present: false` to move only viewers already presenting. `canvas_view { action: "tour-exit" }`
51
- leaves presentation everywhere. `get-tour` reports `position` (the current stop, or null).
52
+ exits presentation on the target board. `get-tour` reports `position` (the current stop, or null).
53
+ Bound background calls do not navigate the human's visible board. Cursors are not durable:
54
+ tour edits or activation changes can invalidate them, so read `get-tour` rather than assuming
55
+ a previous stop survives reopening a board.
52
56
 
53
57
  `canvas_view { action: "move", nodeId | rect | viewport, duration, easing, padding, pullback }`
54
58
  eases the camera without a tour. Outside a presentation the end camera becomes the board
@@ -68,8 +72,10 @@ Use a distinct `clientId` per document, an increasing positive safe-integer `int
68
72
  and the actual server activation's epoch and nonnegative safe-integer generation. Ordinary
69
73
  agent calls may omit this object. It orders requests; it is not authorization or viewer isolation.
70
74
 
71
- A stale activation or duplicate/older intent returns `{ ok: true, superseded: true }` with no
72
- navigation event or cursor change. Do not claim navigation from `ok: true` alone. Applied results
75
+ A stale activation, duplicate/older intent, or presenter-bearing call on a background board
76
+ returns `{ ok: true, superseded: true }` with no navigation event or cursor change.
77
+ Ordinary unfenced background calls still target their own board, not the visible one.
78
+ Do not claim navigation from `ok: true` alone. Applied results
73
79
  and events carry `catalogEpoch`, `boardGeneration` and `sequence`; presenter-aware clients use
74
80
  these to reject stale acknowledgements and events. A structurally valid newer intent is claimed
75
81
  before resolving the stop, so even a missing newer stop supersedes older requests.
@@ -104,7 +104,10 @@ For pinned installation, MCP configuration, managed services, and disposable ver
104
104
  read [Installing PMX Canvas](installing-pmx-canvas.md).
105
105
  In Amp, follow that reference's [Amp setup](installing-pmx-canvas.md#amp-connect-the-agent-separately-from-the-portal):
106
106
  portal access is separate from native MCP, workspace commands need human trust approval,
107
- and a connected MCP server does not install an idle-thread wake-up adapter.
107
+ and raw MCP does not install an idle-thread wake-up adapter. The opt-in
108
+ [Amp plugin](installing-pmx-canvas.md#amp-plugin-bound-threads) adds board binding and
109
+ receipt-based steering while its executor is awake. Use routed query tools for bound
110
+ context; CLI calls and resource reads are not intercepted.
108
111
 
109
112
  ## Reading bundled skills over MCP
110
113
 
@@ -130,8 +133,10 @@ the served catalog is a startup snapshot. Local mirrors still use `skills sync`
130
133
  Use a disposable consumer when testing released guidance without permission to replace
131
134
  owned mirrors. After an authorized upgrade, restart the matching HTTP daemon and `--mcp`
132
135
  processes; Bun does not hot-reload a running process.
133
- 1. **Open or focus the workbench before mutating.** Reuse one visible canvas surface for the
134
- session.
136
+ 1. **Check the session's target before mutating.** With the Amp plugin, read
137
+ `pmx_canvas_session { action: "status" }` and keep using its bound board without opening
138
+ it or navigating the shared workbench. Without a binding, reuse one visible canvas surface
139
+ and open the intended board when active-board work requires it.
135
140
  2. **Verify workspace identity at the actual target.** Read `GET /health` at the URL your
136
141
  CLI/MCP/browser is using; its `workspace` must equal the intended canonical absolute root.
137
142
  `pmx-canvas serve status --port=<port>` checks the local listener on that port, not an
@@ -149,6 +154,9 @@ the served catalog is a startup snapshot. Local mirrors still use `skills sync`
149
154
  human watches intent form and can veto mid-thought. Skip it only for trivial in-place tweaks or
150
155
  high-frequency batch churn. The default TTL (~8s) expires between agent turns: signal with
151
156
  `ttlMs: 30000` and settle by passing `intentId` on the mutation in the same or next call.
157
+ Link only supported node/edge/group mutations; in a batch put the ID on the leaf's
158
+ `args.intentId`, not the envelope. Quiet expiry proceeds unlinked; a human veto refuses
159
+ the linked write. An intent is not permission or a batch-wide gate.
152
160
  Since 0.4.5, an agent mutation WITHOUT a signal still shows a server-synthesized **auto-ghost**
153
161
  (rendered lighter — dimmer, dotted, no veto — and settled instantly). That is a visibility
154
162
  floor, not a replacement: only an explicit signal gives the human a real pre-mutation veto
@@ -90,6 +90,12 @@ The adapter uses the session's **working directory**, not Copilot's session-stat
90
90
  `workspacePath` or the plugin installation directory. Boards remain in the project's
91
91
  `.pmx-canvas/canvas.db`.
92
92
 
93
+ Copilot's native adapter labels its HTTP operations as `copilot`, including AX writes.
94
+ To use that same writer identity over MCP, configure the host's MCP process with
95
+ `PMX_CANVAS_AGENT_SOURCE=copilot`; the portable launcher forwards it to the runtime.
96
+ It does not assume every plugin client is Copilot. Give independent clients their own
97
+ source label, or use an explicit `agentId` on their operations, to keep them distinct.
98
+
93
99
  The daemon belongs to the workspace and stays running when one client closes, so closing
94
100
  the native panel cannot break another MCP client. Stop it explicitly from the project:
95
101
 
@@ -122,10 +122,9 @@ launcher. `PMX_CANVAS_URL` is an explicit target, not a fallback. An unavailable
122
122
  fails rather than starting a second server. Version mismatch or a missing/unknown bundle stamp
123
123
  also fails, including at MCP connection and subsequent requests.
124
124
 
125
- **Release limitation:** the published `0.11.0` runtime lacks the required bundle stamp. This
126
- foundation is verified with the locally packed package, not registry-only startup. A compatible
127
- release is required before recommending the registry fallback. No publication is part of this
128
- increment.
125
+ The `0.12.0` package includes the required bundle stamp and generated Amp plugin. The older
126
+ `0.11.0` runtime lacks that stamp and cannot serve this integration. Verify the actual runtime
127
+ version and stamp on each connection; a local build alone does not prove registry startup.
129
128
 
130
129
  Amp supplies the Portal URL and access controls. Open does not change sharing or multiplayer.
131
130