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
@@ -0,0 +1,3 @@
1
+ import type { JSX } from 'preact';
2
+ /** A scripts-only host blocks native submission before onSubmit can run. */
3
+ export declare function submitWithoutNavigation(event: JSX.TargetedMouseEvent<HTMLFormElement>): void;
@@ -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 `agent-presence`
5
- * SSE snapshot (every change, including TTL expiry) and the connect-time read,
6
- * so this store never runs its own expiry ticker.
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<"image" | "board" | "file" | "markdown" | "mcp-app" | "webpage" | "json-render" | "graph" | "prompt" | "response" | "status" | "context" | "ledger" | "trace" | "diff" | "mermaid" | "html" | "group">;
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;
@@ -1,16 +1,11 @@
1
1
  import { type CanvasLayout, type CanvasNodeState, type PmxCanvas } from '../server/index.js';
2
- import type { PmxAxSource } from '../server/ax-state.js';
3
2
  import type { ContextReadInput } from '../server/context-reads.js';
4
3
  import { type OperationInvoker } from '../server/operations/index.js';
5
4
  type AxStateResult = ReturnType<PmxCanvas['getAxState']>;
6
5
  type AxContextResult = ReturnType<PmxCanvas['getAxContext']>;
7
- type SubmitAxInteractionInput = Parameters<PmxCanvas['submitAxInteraction']>[0];
8
- type SubmitAxInteractionResult = ReturnType<PmxCanvas['submitAxInteraction']>;
9
6
  type GetPendingSteeringResult = ReturnType<PmxCanvas['getPendingSteering']>;
10
7
  type ListElicitationsResult = ReturnType<PmxCanvas['listElicitations']>;
11
8
  type ListModeRequestsResult = ReturnType<PmxCanvas['listModeRequests']>;
12
- type IngestActivityInput = Parameters<PmxCanvas['ingestActivity']>[0];
13
- type IngestActivityResult = ReturnType<PmxCanvas['ingestActivity']>;
14
9
  type GetPolicyResult = ReturnType<PmxCanvas['getPolicy']>;
15
10
  type GetAxTimelineQuery = Parameters<PmxCanvas['getAxTimeline']>[0];
16
11
  type GetAxTimelineResult = ReturnType<PmxCanvas['getAxTimeline']>;
@@ -38,18 +33,12 @@ export interface CanvasAccess {
38
33
  listWorkItems(): Promise<ListWorkItemsResult>;
39
34
  listApprovalGates(): Promise<ListApprovalGatesResult>;
40
35
  listReviewAnnotations(): Promise<ListReviewAnnotationsResult>;
41
- submitAxInteraction(input: SubmitAxInteractionInput, options?: {
42
- source?: PmxAxSource;
43
- }): Promise<SubmitAxInteractionResult>;
44
36
  getPendingSteering(options?: {
45
37
  consumer?: string;
46
38
  limit?: number;
47
39
  }): Promise<GetPendingSteeringResult>;
48
40
  listElicitations(): Promise<ListElicitationsResult>;
49
41
  listModeRequests(): Promise<ListModeRequestsResult>;
50
- ingestActivity(input: IngestActivityInput, options?: {
51
- source?: PmxAxSource;
52
- }): Promise<IngestActivityResult>;
53
42
  getPolicy(): Promise<GetPolicyResult>;
54
43
  getHistory(): Promise<HistoryResult>;
55
44
  getPinnedNodeIds(board?: string): Promise<string[]>;
@@ -62,7 +51,7 @@ export interface CanvasAccess {
62
51
  runBatch(operations: RunBatchInput): Promise<RunBatchResult>;
63
52
  getCodeGraph(): Promise<CodeGraphResult>;
64
53
  getAutomationWebViewStatus(): Promise<AutomationWebViewStatus>;
65
- screenshotAutomationWebView(options?: AutomationScreenshotOptions): Promise<Uint8Array>;
54
+ screenshotAutomationWebView(options?: AutomationScreenshotOptions, signal?: AbortSignal): Promise<Uint8Array>;
66
55
  }
67
56
  export declare function refreshCanvasAccess(access: CanvasAccess): Promise<CanvasAccess>;
68
57
  /**
@@ -0,0 +1,7 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { CanvasAccess } from './canvas-access.js';
3
+ /** One subscription set and event source per MCP connection. Notifications are not reads. */
4
+ export declare function registerResourceSubscriptions(server: McpServer, getCanvas: () => Promise<CanvasAccess>): {
5
+ attach: (canvas: CanvasAccess) => void;
6
+ dispose: () => void;
7
+ };
@@ -19,4 +19,6 @@
19
19
  * }
20
20
  * ```
21
21
  */
22
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
23
+ export declare function createMcpServer(): McpServer;
22
24
  export declare function startMcpServer(): Promise<void>;
@@ -33,4 +33,5 @@ export declare function waitForAxCondition(opts: {
33
33
  ready: () => boolean;
34
34
  timeoutMs: number;
35
35
  channel?: string;
36
+ signal?: AbortSignal;
36
37
  }): Promise<void>;
@@ -168,6 +168,7 @@ export declare function resolveCanvasNode(nodeRef: CanvasNodeLookupInput): {
168
168
  };
169
169
  export declare function refreshCanvasWebpageNode(id: string, options?: {
170
170
  url?: string;
171
+ signal?: AbortSignal;
171
172
  }): Promise<{
172
173
  ok: boolean;
173
174
  id: string;
@@ -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,8 +1,8 @@
1
1
  import { EventEmitter } from 'node:events';
2
- import { type Camera, type Tour, type TourStop } from '../shared/tour.js';
3
- import { type TourStep, type TourStepResult } from './tour-control.js';
2
+ import type { Camera, Tour, TourStop } from '../shared/tour.js';
3
+ import { type TourState, type TourStep, type TourStepResult } from './tour-control.js';
4
4
  export type { Tour, TourStop } from '../shared/tour.js';
5
- export type { TourStep, TourStepResult } from './tour-control.js';
5
+ export type { TourState, TourStep, TourStepResult } from './tour-control.js';
6
6
  import type { AgentPhase, AgentPresence, AgentPresenceSnapshot } from '../shared/agent-presence.js';
7
7
  import { canvasState } from './canvas-state.js';
8
8
  import type { CanvasAnnotation, CanvasNodeState, CanvasEdge, CanvasLayout } from './canvas-state.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 { type GraphNodeInput, type JsonRenderNodeInput, type JsonRenderSpec } from '../json-render/server.js';
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
@@ -163,7 +163,7 @@ export declare class PmxCanvas extends EventEmitter {
163
163
  ungroupNodes(groupId: string, options?: {
164
164
  intentId?: string;
165
165
  }): boolean;
166
- getTour(): Tour;
166
+ getTour(): TourState;
167
167
  setTour(tour: Tour | null): void;
168
168
  /** Present a tour stop (index, `next` or `previous`) in the workbench. Not recorded in undo history. */
169
169
  goToTourStop(step: TourStep, options?: {
@@ -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: "unknown" | "card" | "folder";
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: "unknown" | "card" | "folder";
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
  };
@@ -1,3 +1,13 @@
1
+ import { type OperationProgress } from './types.js';
2
+ /** Opt-in NDJSON; the terminal frame retains the ordinary HTTP status/body. */
3
+ export type OperationStreamFrame = {
4
+ type: 'progress';
5
+ progress: OperationProgress;
6
+ } | {
7
+ type: 'result';
8
+ status: number;
9
+ body: unknown;
10
+ };
1
11
  export declare function matchOperationPath(template: string, pathname: string): Record<string, string> | null;
2
12
  /**
3
13
  * Shared body reader: preserves the parsed JSON value as-is (object, array,
@@ -3,6 +3,6 @@ export { dispatchOperationRoute } from './http.js';
3
3
  export { runCanvasBatchOperation, type BatchEnvelope } from './ops/batch.js';
4
4
  export type { OpenMcpAppCoreResult } from './ops/app.js';
5
5
  export { LocalOperationInvoker, HttpOperationInvoker, type OperationInvoker } from './invoker.js';
6
- export { registerOperationTools, registerCompositeTools, type OperationToolHost } from './mcp.js';
6
+ export { registerOperationTools, registerCompositeTools, registerOperationHelpTool, type OperationToolHost, } from './mcp.js';
7
7
  export { compositeToolDefinitions, type CompositeToolDefinition } from './composites.js';
8
- export { OperationError, defineOperation, type Operation, type OperationContext, type OperationDefinition, type OperationErrorStatus, } from './types.js';
8
+ export { OperationError, defineOperation, type Operation, type OperationContext, type OperationDefinition, type OperationExecutionOptions, type OperationErrorStatus, type OperationProgress, } from './types.js';
@@ -1,5 +1,6 @@
1
+ import { type OperationExecutionOptions } from './types.js';
1
2
  export interface OperationInvoker {
2
- invoke(name: string, input: Record<string, unknown>): Promise<unknown>;
3
+ invoke(name: string, input: Record<string, unknown>, options?: OperationExecutionOptions): Promise<unknown>;
3
4
  }
4
5
  /**
5
6
  * The agent-presence label for a transport: `PMX_CANVAS_AGENT_SOURCE` lets a
@@ -14,7 +15,7 @@ export declare class LocalOperationInvoker implements OperationInvoker {
14
15
  private readonly proxiedRead;
15
16
  /** `source` labels this caller's agent presence ('mcp', 'sdk', …). */
16
17
  constructor(source?: string, allowExternalMcp?: boolean);
17
- invoke(name: string, input: Record<string, unknown>): Promise<unknown>;
18
+ invoke(name: string, input: Record<string, unknown>, options?: OperationExecutionOptions): Promise<unknown>;
18
19
  }
19
20
  /** Builds the HTTP request from the op's route template (`:id` from input, GET flags to query). */
20
21
  export declare class HttpOperationInvoker implements OperationInvoker {
@@ -24,5 +25,5 @@ export declare class HttpOperationInvoker implements OperationInvoker {
24
25
  private readonly proxiedRead;
25
26
  /** `source` labels this caller's agent presence on the server ('cli', 'mcp', …). */
26
27
  constructor(baseUrl: string, source?: string, allowExternalMcp?: boolean);
27
- invoke(name: string, rawInput: Record<string, unknown>): Promise<unknown>;
28
+ invoke(name: string, rawInput: Record<string, unknown>, options?: OperationExecutionOptions): Promise<unknown>;
28
29
  }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * MCP's output is the FORMATTER projection, never the larger HTTP payload.
3
+ * `result` wraps prose/arrays without pretending they are JSON objects. Each
4
+ * operation validates its stable fields; loose payloads preserve full:true and
5
+ * extensible node/app data. No schema advertises an unconstrained root record.
6
+ */
7
+ import { z } from 'zod';
8
+ import type { CallToolResult, ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';
9
+ export declare function operationOutputSchema(operations: string[]): z.ZodObject<{
10
+ result: z.ZodUnion<readonly [...z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>[], z.ZodObject<{
11
+ operation: z.ZodNull;
12
+ outcome: z.ZodLiteral<"error">;
13
+ data: z.ZodObject<{
14
+ message: z.ZodString;
15
+ }, z.core.$strict>;
16
+ }, z.core.$strict>]>;
17
+ }, z.core.$strict>;
18
+ export declare function structuredOperationResult(operation: string, formatted: CallToolResult): CallToolResult;
19
+ export declare function operationToolError(error: unknown): CallToolResult;
20
+ export declare function operationToolAnnotations(toolName: string): ToolAnnotations;
@@ -12,6 +12,8 @@ export interface OperationToolHost extends OperationMcpToolHost {
12
12
  recordContextRead(read: ContextReadInput): Promise<void>;
13
13
  }
14
14
  export declare function registerOperationTools(server: McpServer, getHost: () => Promise<OperationToolHost>): void;
15
+ /** Registry metadata only: never boots a canvas or records a content read. */
16
+ export declare function registerOperationHelpTool(server: McpServer): void;
15
17
  /**
16
18
  * Register composite (action-discriminated) MCP tools (plan-006). Each action
17
19
  * dispatches to a registered operation, reusing that op's `mcp.buildInput` and
@@ -5,6 +5,7 @@ export interface BatchEnvelope {
5
5
  refs: Record<string, unknown>;
6
6
  failedIndex?: number;
7
7
  error?: string;
8
+ cancelled?: boolean;
8
9
  }
9
10
  export declare const batchOperations: Operation[];
10
11
  /**
@@ -1,4 +1,4 @@
1
- import { type Operation } from './types.js';
1
+ import { type Operation, type OperationExecutionOptions } from './types.js';
2
2
  export declare function registerOperation(op: Operation): void;
3
3
  export declare function getOperation(name: string): Operation;
4
4
  export declare function listOperations(): Operation[];
@@ -12,7 +12,7 @@ export declare function getOperationEventEmitter(): OperationEventEmitter | null
12
12
  export declare function isEmitSuppressed(): boolean;
13
13
  /** Suppress this call chain's per-entry frames; the batch emits its final frame afterward. */
14
14
  export declare function runWithSuppressedEmits<T>(fn: () => Promise<T>): Promise<T>;
15
- export interface ExecuteOperationMeta {
15
+ export interface ExecuteOperationMeta extends OperationExecutionOptions {
16
16
  /**
17
17
  * Skip the synthesized auto-ghost: set by the workbench's own HTTP calls
18
18
  * (a human dragging a node is not agent activity) and by canvas.batch for
@@ -20,7 +20,17 @@ export declare class OperationError extends Error {
20
20
  readonly details?: Record<string, unknown>;
21
21
  constructor(message: string, status?: OperationErrorStatus, details?: Record<string, unknown>);
22
22
  }
23
- export interface OperationContext {
23
+ export interface OperationProgress {
24
+ progress: number;
25
+ total?: number;
26
+ message?: string;
27
+ }
28
+ /** Cooperative cancellation: completed writes are never rolled back. */
29
+ export interface OperationExecutionOptions {
30
+ signal?: AbortSignal;
31
+ onProgress?: (progress: OperationProgress) => void | Promise<void>;
32
+ }
33
+ export interface OperationContext extends OperationExecutionOptions {
24
34
  /** Emit a workbench SSE event (e.g. extra `canvas-layout-update` frames, focus, viewport). */
25
35
  emit(event: string, payload?: Record<string, unknown>): void;
26
36
  }
@@ -53,7 +63,7 @@ export interface OperationMcpToolHost {
53
63
  * restore summary).
54
64
  */
55
65
  invoker(): {
56
- invoke(name: string, input: Record<string, unknown>): Promise<unknown>;
66
+ invoke(name: string, input: Record<string, unknown>, options?: OperationExecutionOptions): Promise<unknown>;
57
67
  };
58
68
  }
59
69
  export interface OperationMcpTool {
@@ -80,6 +80,8 @@ export interface PrimaryWorkbenchCanvasPromptRequest {
80
80
  contextNodeIds: string[];
81
81
  }
82
82
  type PrimaryWorkbenchCanvasPromptHandler = (request: PrimaryWorkbenchCanvasPromptRequest) => Promise<void>;
83
+ /** In-process invalidation observers share the same events as daemon SSE clients. */
84
+ export declare function onWorkbenchEvent(listener: (event: string) => void): () => void;
83
85
  export declare function setPrimaryWorkbenchAutoOpenEnabled(enabled: boolean): void;
84
86
  export declare function isPrimaryWorkbenchAutoOpenEnabled(): boolean;
85
87
  export declare function hasWorkbenchSubscribers(): boolean;
@@ -15,9 +15,12 @@ export interface TourStepResult {
15
15
  total: number;
16
16
  stop: TourStop;
17
17
  }
18
- export declare function currentTour(): Tour;
19
- /** The stop the tour is on for the active board, or null when not touring. */
20
- export declare function tourPosition(tour?: Tour): number | null;
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;
@@ -37,6 +37,7 @@ export declare function checkWebpageHop(originScope: AddressScope, hopScope: Add
37
37
  export declare function fetchWebpageSnapshot(inputUrl: string, options?: {
38
38
  resolveHost?: ResolveHost;
39
39
  timeoutMs?: number;
40
+ signal?: AbortSignal;
40
41
  }): Promise<WebpageSnapshot>;
41
42
  export declare function getWebpageFetchErrorDetails(error: unknown): {
42
43
  message: string;
@@ -6,7 +6,7 @@
6
6
 
7
7
  ## MCP Server
8
8
 
9
- 24 tools: 18 action-discriminated composites (`canvas_import`, `canvas_board`, `canvas_node`, `canvas_render`, `canvas_edge`, `canvas_group`, `canvas_history`, `canvas_view`, `canvas_query`, `canvas_webview`, `canvas_app`, `canvas_snapshot`, `canvas_ax_state`, `canvas_ax_work`, `canvas_ax_gate`, `canvas_ax_timeline`, `canvas_ax_delivery`, `canvas_intent`) plus 6 standalone tools (`canvas_batch`, `canvas_pin_nodes`, `canvas_invoke_command`, `canvas_ax_interaction`, `canvas_ingest_activity`, `canvas_screenshot`). `canvas_import` is the agent half of the human-consented attachment workflow: agents list/read requested imports, submit reviewable Markdown, or report unavailable; they cannot request, cancel, or commit. The 57 legacy single-purpose tools the composites replaced were removed in v0.3.0; v0.4.0 shipped the `canvas_snapshot` composite and removed the 6 deprecated snapshot standalones. Prefer composites for new MCP calls; see `docs/mcp.md` for the authoritative tool list and the legacy-to-composite migration reference.
9
+ 25 tools: 18 action-discriminated composites (`canvas_import`, `canvas_board`, `canvas_node`, `canvas_render`, `canvas_edge`, `canvas_group`, `canvas_history`, `canvas_view`, `canvas_query`, `canvas_webview`, `canvas_app`, `canvas_snapshot`, `canvas_ax_state`, `canvas_ax_work`, `canvas_ax_gate`, `canvas_ax_timeline`, `canvas_ax_delivery`, `canvas_intent`) plus 7 standalone tools (`canvas_help`, `canvas_batch`, `canvas_pin_nodes`, `canvas_invoke_command`, `canvas_ax_interaction`, `canvas_ingest_activity`, `canvas_screenshot`). `canvas_help { op }` returns one MCP registry operation's public arguments and guidance without accessing the canvas; omit `op` to list names. `canvas_import` is the agent half of the human-consented attachment workflow: agents list/read requested imports, submit reviewable Markdown, or report unavailable; they cannot request, cancel, or commit. The 57 legacy single-purpose tools the composites replaced were removed in v0.3.0; v0.4.0 shipped the `canvas_snapshot` composite and removed the 6 deprecated snapshot standalones. Prefer composites for new MCP calls; see `docs/mcp.md` for the authoritative tool list and the legacy-to-composite migration reference.
10
10
 
11
11
  ### Agent-assisted document imports
12
12
 
@@ -18,7 +18,7 @@ The `diagram` action of `canvas_app` is a thin preset in `src/server/diagram-pre
18
18
 
19
19
  15 static canvas resources: `canvas://boards`, `canvas://pinned-context`, `canvas://schema`, `canvas://layout`, `canvas://summary`, `canvas://spatial-context`, `canvas://history`, `canvas://code-graph`, `canvas://ax`, `canvas://ax-context`, `canvas://ax-timeline`, `canvas://ax-work`, `canvas://ax-pending-steering`, `canvas://ax-delivery`, `canvas://skills`; two canvas resource templates: `canvas://context{?budget,consumer,since}` and `canvas://graph{?focus,depth}`. Bundled skills additionally register per-skill `canvas://skills/<name>` resources and the `skill://{name}/{+path}` file template.
20
20
 
21
- Resource change notifications: the MCP server emits `notifications/resources/updated` when canvas state changes. Pin changes notify `canvas://pinned-context`; all mutations notify `canvas://layout`, `canvas://summary`, `canvas://spatial-context`, `canvas://history`, `canvas://code-graph`, and `canvas://graph`. This enables real-time human→agent collaboration — humans pin nodes in the browser, agents are notified immediately.
21
+ Resource change notifications: subscribe to each live URI to receive `notifications/resources/updated` when canvas state or knowledge policies change. Notifications invalidate cached data without reading it. Subscriptions belong to the connection; subscribe again after reconnecting. See [Resource subscriptions](mcp.md#resource-subscriptions-010).
22
22
 
23
23
  ### AX Primitives (host-agnostic)
24
24
 
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
+ `{ tour, derived, position }` as of 0.10; read stops from `.tour.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">&lt;/&gt; 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; ordinary documents keep their bottom-right Source button.
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/http-api.md CHANGED
@@ -3,6 +3,37 @@
3
3
  REST endpoints for all canvas operations + an SSE event stream. Works from
4
4
  any language. Default base URL: `http://localhost:4313`.
5
5
 
6
+ ## Operation cancellation and progress (0.10)
7
+
8
+ Registry-backed operation routes use the request's abort signal. Disconnecting
9
+ stops supported AX waits, cancels webpage fetching and prevents later batch
10
+ operations from starting. Completed writes remain. Cancellation is cooperative:
11
+ external app builds/runtime calls and local WebView captures cannot be interrupted
12
+ mid-call. A standalone cancelled operation returns HTTP 499 with
13
+ `{ "ok": false, "cancelled": true, "error": "Operation cancelled." }` when the
14
+ connection still permits a response. An interrupted batch retains completed
15
+ `results` and `refs` in its partial-failure body and adds `cancelled: true`.
16
+ Disconnected clients must inspect state before retrying; they may never receive
17
+ that body. Cancellation is not rollback.
18
+
19
+ Send `Accept: application/x-ndjson` to opt into batch and supported AX wait progress
20
+ over the operation response stream. Each newline-delimited JSON frame is one of:
21
+
22
+ ```json
23
+ {"type":"progress","progress":{"progress":1,"total":3,"message":"node.add"}}
24
+ {"type":"result","status":200,"body":{"ok":true,"results":[],"refs":{}}}
25
+ ```
26
+
27
+ The examples show frame shapes, not one complete batch. The terminal `result`
28
+ frame carries the ordinary operation status and body; the stream's HTTP 200 does
29
+ not establish operation success. Without this header, routes keep their ordinary
30
+ JSON status/body. Early validation errors may still be ordinary JSON. The
31
+ in-process and HTTP operation invokers accept optional `{ signal, onProgress }`;
32
+ the HTTP invoker negotiates this stream only when given a progress callback.
33
+
34
+ AX wait routes accept `timeoutMs` as well as the direct-HTTP `waitMs` parameter;
35
+ `timeoutMs` takes precedence when both are supplied.
36
+
6
37
  ## Knowledge lifecycle data (plan 017 slice 1)
7
38
 
8
39
  The slice-1 data contract now also drives the workbench, due engine and qualified agent context.