pmx-canvas 0.9.1 → 0.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/extensions/pmx-canvas/extension.mjs +9 -3
- package/.github/extensions/pmx-canvas/runtime.json +1 -1
- package/.github/extensions/pmx-canvas/start-mcp.mjs +29 -29
- package/CHANGELOG.md +19 -0
- package/Readme.md +1 -1
- package/dist/canvas/global.css +12 -17
- package/dist/canvas/index.js +58 -58
- package/dist/canvas/mcp-app.html +75 -80
- package/dist/json-render/index.js +50 -50
- package/dist/types/client/canvas/submit-without-navigation.d.ts +3 -0
- package/dist/types/client/state/intent-bridge.d.ts +0 -3
- package/dist/types/client/state/presence-store.d.ts +3 -3
- package/dist/types/client/types.d.ts +1 -1
- package/dist/types/server/canvas-state.d.ts +2 -0
- package/dist/types/server/index.d.ts +2 -2
- package/dist/types/server/knowledge-lifecycle.d.ts +11 -2
- package/dist/types/server/lifecycle-status.d.ts +3 -1
- package/dist/types/server/tour-control.d.ts +6 -3
- package/docs/bun-webview-integration.md +2 -2
- package/docs/cli.md +10 -1
- package/docs/design/Chrome.dc.html +1 -0
- package/docs/design.md +3 -1
- package/docs/mcp.md +5 -1
- package/docs/node-types.md +3 -0
- package/docs/plans/plan-019-post-0.9-roadmap.md +68 -9
- package/docs/sdk.md +7 -0
- package/package.json +2 -2
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/extension.mjs +9 -3
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.json +1 -1
- package/plugins/pmx-canvas/plugin.json +1 -1
- package/plugins/pmx-canvas/runtime.json +1 -1
- package/plugins/pmx-canvas/start-mcp.mjs +29 -29
- package/skills/pmx-canvas/SKILL.md +4 -4
- package/skills/pmx-canvas/references/github-copilot-app-adapter.md +2 -2
- package/skills/pmx-canvas/references/installing-pmx-canvas.md +11 -6
- package/src/client/canvas/BoardCopyDialog.tsx +2 -1
- package/src/client/canvas/KnowledgeDialog.tsx +3 -0
- package/src/client/canvas/LifecyclePanel.tsx +4 -0
- package/src/client/canvas/RelationPresetsDialog.tsx +2 -0
- package/src/client/canvas/submit-without-navigation.ts +14 -0
- package/src/client/state/canvas-store.ts +12 -24
- package/src/client/state/intent-bridge.ts +0 -6
- package/src/client/state/presence-store.ts +3 -3
- package/src/client/state/sse-bridge.ts +11 -6
- package/src/client/theme/global.css +12 -17
- package/src/server/canvas-state.ts +10 -0
- package/src/server/index.ts +10 -30
- package/src/server/knowledge-lifecycle.ts +66 -0
- package/src/server/lifecycle-status.ts +79 -7
- package/src/server/operations/ops/lifecycle.ts +3 -7
- package/src/server/operations/ops/nodes.ts +2 -5
- package/src/server/operations/ops/viewport.ts +3 -7
- package/src/server/server.ts +13 -10
- package/src/server/tour-control.ts +17 -8
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { AgentPresenceSnapshot } from '../../shared/agent-presence.js';
|
|
2
1
|
import type { CanvasNodeState } from '../types';
|
|
3
2
|
/**
|
|
4
3
|
* Per-tab identity: an AGENT driving the workbench UI from its own pane opens
|
|
@@ -156,8 +155,6 @@ export interface AxInteractionResponse {
|
|
|
156
155
|
}
|
|
157
156
|
/** Fetch the compact AX state snapshot pushed into AX-enabled surfaces. */
|
|
158
157
|
export declare function fetchAxSurfaceState(): Promise<unknown>;
|
|
159
|
-
/** Connect-time agent-presence read; live updates arrive as `agent-presence` SSE frames. */
|
|
160
|
-
export declare function fetchAgentPresence(): Promise<Partial<AgentPresenceSnapshot> | null>;
|
|
161
158
|
/** Ask the server to open a node's surface in the system browser. */
|
|
162
159
|
export declare function openNodeInSystemBrowserRequest(nodeId: string, url?: string): Promise<{
|
|
163
160
|
ok: boolean;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { PmxAxIntent } from '../../shared/ax-intent.js';
|
|
2
2
|
import { type AgentActivityEntry, type AgentPresence, type AgentPresenceSnapshot } from '../../shared/agent-presence.js';
|
|
3
3
|
/**
|
|
4
|
-
* Agent presence (rail-chrome-v2 phase 2). Fed by the server's
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Agent presence (rail-chrome-v2 phase 2). Fed by the server's ordered
|
|
5
|
+
* `agent-presence` frames: a connect snapshot and every change, including TTL
|
|
6
|
+
* expiry, over SSE or polling. This store never runs its own expiry ticker.
|
|
7
7
|
*
|
|
8
8
|
* `sessionActive` is the ONE selector every agent surface reads — the session
|
|
9
9
|
* panel, command bar, presence layer, and top-bar chip all mount on it.
|
|
@@ -75,7 +75,7 @@ export type AnnotationTool = 'pen' | 'eraser' | 'text' | null;
|
|
|
75
75
|
export type ConnectionStatus = 'connecting' | 'connected' | 'disconnected';
|
|
76
76
|
export declare const TYPE_LABELS: Record<CanvasNodeState['type'], string>;
|
|
77
77
|
/** Node types that support the full-viewport expand/focus overlay. */
|
|
78
|
-
export declare const EXPANDABLE_TYPES: Set<"
|
|
78
|
+
export declare const EXPANDABLE_TYPES: Set<"board" | "image" | "file" | "markdown" | "mcp-app" | "webpage" | "json-render" | "graph" | "prompt" | "response" | "status" | "context" | "ledger" | "trace" | "diff" | "mermaid" | "html" | "group">;
|
|
79
79
|
export declare const EXCALIDRAW_SERVER_NAME = "Excalidraw";
|
|
80
80
|
export declare const EXCALIDRAW_CREATE_VIEW_TOOL = "create_view";
|
|
81
81
|
export declare function isExcalidrawNode(node: CanvasNodeState): boolean;
|
|
@@ -432,6 +432,8 @@ declare class CanvasStateManager {
|
|
|
432
432
|
updateNode(id: string, patch: Partial<CanvasNodeState>, savedWikiLinks?: WikiLink[] | null): void;
|
|
433
433
|
removeNode(id: string): void;
|
|
434
434
|
getNode(id: string): CanvasNodeState | undefined;
|
|
435
|
+
/** Camera targets need geometry, not hydrated documents, frames or image blobs. */
|
|
436
|
+
nodeGeometry(): Array<Pick<CanvasNodeState, 'id' | 'type' | 'position' | 'size'>>;
|
|
435
437
|
/** Who made and last edited each card, and its content revision — no content, no copies. */
|
|
436
438
|
nodeStamps(): Array<Pick<CanvasNodeState, 'id' | 'createdBy' | 'lastEditedBy' | 'contentRevision'>>;
|
|
437
439
|
getNodeForPersistence(id: string): CanvasNodeState | undefined;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
|
-
import {
|
|
2
|
+
import type { Camera, Tour, TourStop } from '../shared/tour.js';
|
|
3
3
|
import { type TourStep, type TourStepResult } from './tour-control.js';
|
|
4
4
|
export type { Tour, TourStop } from '../shared/tour.js';
|
|
5
5
|
export type { TourStep, TourStepResult } from './tour-control.js';
|
|
@@ -22,7 +22,7 @@ import type { HtmlPrimitiveKind } from './html-primitives.js';
|
|
|
22
22
|
import { type WebArtifactBuildInput, type WebArtifactCanvasBuildResult } from './web-artifacts.js';
|
|
23
23
|
import { type ExternalMcpTransportConfig } from './mcp-app-runtime.js';
|
|
24
24
|
import type { DiagramPresetOpenInput } from './diagram-presets.js';
|
|
25
|
-
import {
|
|
25
|
+
import type { GraphNodeInput, JsonRenderNodeInput, JsonRenderSpec } from '../json-render/server.js';
|
|
26
26
|
import type { CanvasAutomationWebViewOptions, CanvasAutomationWebViewStatus } from './server.js';
|
|
27
27
|
/**
|
|
28
28
|
* Node object returned by the SDK's create/get methods. It is the fully
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Database } from 'bun:sqlite';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
+
import { type MemoryTarget } from '../shared/memory-graph.js';
|
|
3
4
|
import { type ActorAttribution } from './attribution.js';
|
|
4
5
|
import type { LifecycleQualification } from '../shared/lifecycle-status.js';
|
|
5
6
|
import type { CanvasNodeState } from './canvas-state.js';
|
|
@@ -182,7 +183,7 @@ export declare class KnowledgeLifecycle {
|
|
|
182
183
|
cadenceDays?: number | undefined;
|
|
183
184
|
} | null;
|
|
184
185
|
lifecycleSourceFolder: string | null;
|
|
185
|
-
lifecycleSource: "
|
|
186
|
+
lifecycleSource: "card" | "unknown" | "folder";
|
|
186
187
|
};
|
|
187
188
|
resolveCard(folderPath: string | null, node: Pick<CanvasNodeState, 'type' | 'data'>, catalog?: {
|
|
188
189
|
categories: KnowledgeCategory[];
|
|
@@ -208,8 +209,16 @@ export declare class KnowledgeLifecycle {
|
|
|
208
209
|
cadenceDays?: number | undefined;
|
|
209
210
|
} | null;
|
|
210
211
|
lifecycleSourceFolder: string | null;
|
|
211
|
-
lifecycleSource: "
|
|
212
|
+
lifecycleSource: "card" | "unknown" | "folder";
|
|
212
213
|
};
|
|
214
|
+
private indexedDb;
|
|
215
|
+
/** Stored owners of semantic sources; the caller overlays the active board's unsaved edges. */
|
|
216
|
+
incomingSourceBoards(target: MemoryTarget, activeBoardId: string | null): string[];
|
|
217
|
+
/** One indexed latest validation and retirement per card, never its full audit history. */
|
|
218
|
+
reviewSummaries(targets: MemoryTarget[]): Map<string, {
|
|
219
|
+
lastValidation: ReviewRecord | null;
|
|
220
|
+
retired: boolean;
|
|
221
|
+
}>;
|
|
213
222
|
reviews(boardId: string, nodeId: string): {
|
|
214
223
|
records: ReviewRecord[];
|
|
215
224
|
retired: boolean;
|
|
@@ -3,8 +3,10 @@ import type { LifecycleQualification } from '../shared/lifecycle-status.js';
|
|
|
3
3
|
import type { ReviewCardSummary } from './knowledge-lifecycle.js';
|
|
4
4
|
/** A read-only qualification, shared by briefs, search, Home and card review. No clock is renewed. */
|
|
5
5
|
export declare function lifecycleQualifications(now?: number, atUse?: MemoryTarget[]): Record<string, LifecycleQualification>;
|
|
6
|
+
/** A card being inspected is in use, including its transitive sources and portal contents. */
|
|
7
|
+
export declare function lifecycleQualification(target: MemoryTarget, now?: number): LifecycleQualification | undefined;
|
|
6
8
|
/** Compact queue metadata and qualifications come from the same authoritative read. */
|
|
7
|
-
export declare function lifecycleSnapshot(now?: number, atUse?: MemoryTarget[]): {
|
|
9
|
+
export declare function lifecycleSnapshot(now?: number, atUse?: MemoryTarget[], only?: MemoryTarget): {
|
|
8
10
|
cards: Record<string, LifecycleQualification>;
|
|
9
11
|
reviewCards: ReviewCardSummary[];
|
|
10
12
|
};
|
|
@@ -15,9 +15,12 @@ export interface TourStepResult {
|
|
|
15
15
|
total: number;
|
|
16
16
|
stop: TourStop;
|
|
17
17
|
}
|
|
18
|
-
export
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
export interface TourState {
|
|
19
|
+
tour: Tour;
|
|
20
|
+
derived: boolean;
|
|
21
|
+
position: number | null;
|
|
22
|
+
}
|
|
23
|
+
export declare function currentTour(): TourState;
|
|
21
24
|
/** Move the shared cursor. `next`/`previous` clamp at the ends; an explicit index must exist. */
|
|
22
25
|
export declare function goToTourStop(step: TourStep): TourStepResult;
|
|
23
26
|
export declare function exitTour(): void;
|
|
@@ -12,7 +12,7 @@ This document tracks:
|
|
|
12
12
|
|
|
13
13
|
**Implemented** for Phase 1.
|
|
14
14
|
|
|
15
|
-
The repo now targets Bun `>=1.4.
|
|
15
|
+
The repo now targets Bun `>=1.4.4`, and PMX Canvas ships an opt-in, server-owned WebView automation session that can:
|
|
16
16
|
|
|
17
17
|
- start a headless browser session for `/workbench`
|
|
18
18
|
- report WebView runtime status
|
|
@@ -192,7 +192,7 @@ If `chromePath` or `chromeArgv` is provided, PMX Canvas forces a Chrome-backed s
|
|
|
192
192
|
|
|
193
193
|
The repo now requires:
|
|
194
194
|
|
|
195
|
-
- Bun `>=1.4.
|
|
195
|
+
- Bun `>=1.4.4`
|
|
196
196
|
|
|
197
197
|
That change is reflected in:
|
|
198
198
|
|
package/docs/cli.md
CHANGED
|
@@ -99,7 +99,8 @@ offset mid-move without changing endpoints. Tours allow at most 1000 stops.
|
|
|
99
99
|
Tours persist with the board and snapshots, participate in undo, and clear with
|
|
100
100
|
the board. A file containing `null` resets the tour: when none is saved, groups
|
|
101
101
|
are sorted by y, then x, then ID. A saved empty `stops` array deliberately has no
|
|
102
|
-
stops. SDK: `canvas.setTour(tourOrNull)` / `canvas.getTour()
|
|
102
|
+
stops. SDK: `canvas.setTour(tourOrNull)` / `canvas.getTour()`, which returns
|
|
103
|
+
the tour directly; read stops from `.stops`.
|
|
103
104
|
|
|
104
105
|
### Driving a tour (agents)
|
|
105
106
|
|
|
@@ -593,6 +594,14 @@ pmx-canvas copilot install-extension --dry-run # Preview target, writes nothin
|
|
|
593
594
|
pmx-canvas copilot install-extension --yes # Install/overwrite into .github/extensions/pmx-canvas/
|
|
594
595
|
```
|
|
595
596
|
|
|
597
|
+
After updating the adapter, start a new Copilot project session to load it.
|
|
598
|
+
Panel instructions, `send_instruction` and canvas steering request the SDK's
|
|
599
|
+
`immediate` mode: during an active turn, Copilot receives them before its next
|
|
600
|
+
model call, without aborting an executing tool. If the turn has already ended,
|
|
601
|
+
the SDK queues the instruction instead. A successful send means the host accepted
|
|
602
|
+
the instruction, not that the model has consumed it. Failed sends are not marked
|
|
603
|
+
delivered; the delivery pump retries them.
|
|
604
|
+
|
|
596
605
|
## Skill sync
|
|
597
606
|
|
|
598
607
|
Refresh the pmx-canvas skill copies **already installed** in this workspace.
|
|
@@ -125,6 +125,7 @@ body{margin:0}
|
|
|
125
125
|
<div style="height: 350px; display: flex; flex-direction: column; border: 1px solid #1b2c44; border-radius: 14px; overflow: hidden; background: #0f1d31">
|
|
126
126
|
<div style="flex-shrink: 0; height: 46px; display: flex; align-items: center; gap: 10px; padding: 0 16px; border-bottom: 1px solid #1b2c44; font-size: 13px; font-weight: 600">Research index <span style="margin-left: auto; font-size: 12px; font-weight: 400">Relations</span><span style="margin-left: 12px">×</span></div>
|
|
127
127
|
<div style="flex: 1; min-height: 0; overflow: auto; padding: 16px; font-size: 13px; color: #b9c6d6">Read and edit the source here. Actual edits finish saving before a reference opens; a refused save keeps this reader and its draft.</div>
|
|
128
|
+
<button style="flex-shrink: 0; align-self: flex-end; margin: 16px; padding: 8px 18px; border: 1px solid #1b2c44; border-radius: 6px; font: 600 13px 'IBM Plex Sans', sans-serif; background: #13243b; color: #8ea3bd"></> Source</button>
|
|
128
129
|
<div role="region" aria-label="Cross-board references" tabindex="0" style="flex-shrink: 0; max-height: min(40%, 132px); overflow: auto; overscroll-behavior: contain">
|
|
129
130
|
<div style="padding: 6px 12px; border-top: 1px solid #1b2c44; font-size: 12px; color: #b9c6d6">↗ references: Research / Interview notes</div>
|
|
130
131
|
<div style="padding: 6px 12px; border-top: 1px solid #1b2c44; font-size: 12px; color: #b9c6d6">↗ references: Research / Findings</div>
|
package/docs/design.md
CHANGED
|
@@ -504,7 +504,9 @@ retain their hit targets. Grab areas yield to every card, including neighboring
|
|
|
504
504
|
but outrank connection labels and lines. Group interiors still select and open their menus,
|
|
505
505
|
below child ports and labels; the frames and labels keep their existing visual stacking.
|
|
506
506
|
Expanded flow documents put Source in a separate row above the reader, clear of the
|
|
507
|
-
bottom dock's action hints
|
|
507
|
+
bottom dock's action hints. Ordinary documents keep Source at the bottom right in a
|
|
508
|
+
reserved row below the scroll viewport, so it cannot cover text at narrow widths
|
|
509
|
+
or intermediate scroll positions (0.9.2 review follow-up, 2026-10-24).
|
|
508
510
|
Hovering a flow action temporarily
|
|
509
511
|
hides another action's keyboard hint without moving focus; leaving restores the focused hint.
|
|
510
512
|
Card checkbox clicks address parsed Markdown tasks, not fenced/indented examples or raw
|
package/docs/mcp.md
CHANGED
|
@@ -57,7 +57,7 @@ on its source board. It appears as a `portal` edge in `canvas://graph`, independ
|
|
|
57
57
|
|
|
58
58
|
## Board tours
|
|
59
59
|
|
|
60
|
-
`canvas_view { action: "get-tour" }` returns `{ tour, derived }`, using group
|
|
60
|
+
`canvas_view { action: "get-tour" }` returns `{ tour, derived, position }`, using group
|
|
61
61
|
reading order when there is no saved tour. `canvas_view { action: "set-tour",
|
|
62
62
|
tour: { stops: [...] } }` persists an ordered tour; `tour: null` resets it.
|
|
63
63
|
`canvas_view { action: "tour-go", stop: 0 }` (or `step: "next" | "previous"`)
|
|
@@ -170,6 +170,10 @@ in the user's browser. Standalone Markdown edits carry captured board/content
|
|
|
170
170
|
revisions; a refused save keeps the current editor draft. Copy that draft before
|
|
171
171
|
closing the editor or switching boards; unsaved drafts do not survive remounts.
|
|
172
172
|
|
|
173
|
+
Knowledge review, categories, relation presets and board-copy forms handle saves
|
|
174
|
+
in JavaScript, including Enter and input validation. They do not require the
|
|
175
|
+
outer host to grant `allow-forms`; the SDK test host keeps `sandbox="allow-scripts"`.
|
|
176
|
+
|
|
173
177
|
**Host constraints still apply.** Native PMX controls work without child frames.
|
|
174
178
|
HTML, Mermaid, JSON/graph and nested app renderers need the host to permit local
|
|
175
179
|
`srcdoc` frames. The portable MCP Apps default with empty `frameDomains` is
|
package/docs/node-types.md
CHANGED
|
@@ -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-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
**Status (2026-10-24):** 0.9.0–0.9.2 are released. The maintainer authorized review,
|
|
4
|
+
testing and release of the patch-only 0.9.3 cleanup after the prerequisite fixes reached
|
|
5
|
+
`origin/main` at `bed57fe9a4b79bd54f8b5ee51cefd65fec3dbe1a`. The SDK tour return shape
|
|
6
|
+
stays unchanged. MCP hardening and SDK tour result parity move to 0.10 on SDK v1.
|
|
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,7 +22,7 @@ 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.
|
|
25
|
+
| 1 | 0.9.3 | Cleanup: tour debt, large-board costs, bounded due evaluator, MCP reconnect, lint | 12, 1 | S–M |
|
|
25
26
|
| 2 | 0.10 | MCP hardening: contracts, annotations, cancellation, resource subscriptions | 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 |
|
|
@@ -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.
|
|
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
|
|
59
|
-
|
|
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
|
|
@@ -77,6 +134,8 @@ and move 1's context delivery, on the current SDK v1.
|
|
|
77
134
|
for long operations where the host supports it; partial outcomes reported honestly.
|
|
78
135
|
5. Resource subscriptions: advertise the capability, track subscribed URIs per connection,
|
|
79
136
|
notify only subscribers. Explicit context reads keep working for hosts without them.
|
|
137
|
+
6. Align SDK tour reads with HTTP/MCP `{ tour, derived, position }`; document the breaking
|
|
138
|
+
change from `.stops` to `.tour.stops` at this minor-version boundary.
|
|
80
139
|
|
|
81
140
|
Candidates, not automatic scope: `canvas_help { op }`, `session.start`, per-action schemas.
|
|
82
141
|
|
package/docs/sdk.md
CHANGED
|
@@ -36,6 +36,13 @@ 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 the tour directly. Read stops from `canvas.getTour().stops`.
|
|
42
|
+
When no tour is saved, stops come from group reading order.
|
|
43
|
+
`canvas.setTour(tourOrNull)` saves the tour or resets it to derived group order.
|
|
44
|
+
See the [tour contract](http-api.md#board-tours) for navigation and camera moves.
|
|
45
|
+
|
|
39
46
|
## Quick example
|
|
40
47
|
|
|
41
48
|
`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.
|
|
3
|
+
"version": "0.9.3",
|
|
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",
|
|
@@ -123,6 +123,6 @@
|
|
|
123
123
|
},
|
|
124
124
|
"homepage": "https://github.com/pskoett/pmx-canvas#readme",
|
|
125
125
|
"engines": {
|
|
126
|
-
"bun": ">=1.4.
|
|
126
|
+
"bun": ">=1.4.4"
|
|
127
127
|
}
|
|
128
128
|
}
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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),
|
|
@@ -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.
|
|
5
|
+
"version": "0.9.3",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "pskoett",
|
|
8
8
|
"url": "https://github.com/pskoett"
|