pmx-canvas 0.1.35 → 0.2.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 (100) hide show
  1. package/CHANGELOG.md +461 -0
  2. package/Readme.md +14 -2
  3. package/dist/canvas/index.js +82 -41
  4. package/dist/json-render/index.js +89 -334
  5. package/dist/types/client/nodes/ExtAppFrame.d.ts +2 -0
  6. package/dist/types/mcp/canvas-access.d.ts +12 -159
  7. package/dist/types/server/ax-context.d.ts +1 -1
  8. package/dist/types/server/ax-state-manager.d.ts +256 -0
  9. package/dist/types/server/ax-state.d.ts +29 -1
  10. package/dist/types/server/ax-wait.d.ts +23 -0
  11. package/dist/types/server/canvas-operations.d.ts +1 -12
  12. package/dist/types/server/canvas-state.d.ts +46 -14
  13. package/dist/types/server/html-surface.d.ts +7 -0
  14. package/dist/types/server/index.d.ts +66 -26
  15. package/dist/types/server/operations/composites.d.ts +121 -0
  16. package/dist/types/server/operations/http.d.ts +7 -0
  17. package/dist/types/server/operations/index.d.ts +8 -0
  18. package/dist/types/server/operations/invoker.d.ts +13 -0
  19. package/dist/types/server/operations/mcp.d.ts +15 -0
  20. package/dist/types/server/operations/ops/annotation.d.ts +2 -0
  21. package/dist/types/server/operations/ops/app.d.ts +33 -0
  22. package/dist/types/server/operations/ops/ax-await.d.ts +2 -0
  23. package/dist/types/server/operations/ops/ax-shared.d.ts +31 -0
  24. package/dist/types/server/operations/ops/ax-state.d.ts +2 -0
  25. package/dist/types/server/operations/ops/ax-timeline.d.ts +2 -0
  26. package/dist/types/server/operations/ops/ax-work.d.ts +2 -0
  27. package/dist/types/server/operations/ops/batch.d.ts +19 -0
  28. package/dist/types/server/operations/ops/edges.d.ts +2 -0
  29. package/dist/types/server/operations/ops/groups.d.ts +2 -0
  30. package/dist/types/server/operations/ops/json-render.d.ts +31 -0
  31. package/dist/types/server/operations/ops/nodes.d.ts +62 -0
  32. package/dist/types/server/operations/ops/query.d.ts +2 -0
  33. package/dist/types/server/operations/ops/snapshots.d.ts +2 -0
  34. package/dist/types/server/operations/ops/validate.d.ts +2 -0
  35. package/dist/types/server/operations/ops/viewport.d.ts +2 -0
  36. package/dist/types/server/operations/ops/webview.d.ts +2 -0
  37. package/dist/types/server/operations/registry.d.ts +15 -0
  38. package/dist/types/server/operations/types.d.ts +116 -0
  39. package/dist/types/server/operations/webview-runner.d.ts +69 -0
  40. package/docs/RELEASE.md +5 -0
  41. package/docs/adr-001-bun-only-runtime.md +46 -0
  42. package/docs/api-stability.md +57 -0
  43. package/docs/ax-host-adapter-contract.md +65 -0
  44. package/docs/ax-state-contract.md +72 -0
  45. package/docs/http-api.md +34 -2
  46. package/docs/mcp.md +64 -11
  47. package/docs/plans/plan-005-operation-registry.md +84 -0
  48. package/docs/plans/plan-006-mcp-tool-consolidation.md +109 -0
  49. package/docs/plans/plan-007-ax-domain.md +99 -0
  50. package/docs/plans/plan-008-registry-finish.md +91 -0
  51. package/docs/screenshot.png +0 -0
  52. package/docs/tech-debt-assessment-2026-06.md +90 -0
  53. package/package.json +3 -3
  54. package/skills/pmx-canvas/SKILL.md +233 -185
  55. package/skills/pmx-canvas/evals/evals.json +3 -3
  56. package/skills/pmx-canvas/references/codex-app-adapter.md +24 -11
  57. package/skills/pmx-canvas/references/github-copilot-app-adapter.md +31 -1
  58. package/src/cli/agent.ts +52 -31
  59. package/src/client/nodes/ExtAppFrame.tsx +73 -5
  60. package/src/client/nodes/HtmlNode.tsx +12 -3
  61. package/src/client/nodes/McpAppNode.tsx +12 -3
  62. package/src/json-render/renderer/index.tsx +3 -0
  63. package/src/mcp/canvas-access.ts +43 -774
  64. package/src/mcp/server.ts +190 -2001
  65. package/src/server/ax-context.ts +7 -1
  66. package/src/server/ax-state-manager.ts +808 -0
  67. package/src/server/ax-state.ts +89 -2
  68. package/src/server/ax-wait.ts +56 -0
  69. package/src/server/canvas-operations.ts +2 -328
  70. package/src/server/canvas-schema.ts +2 -2
  71. package/src/server/canvas-state.ts +140 -382
  72. package/src/server/html-surface.ts +49 -11
  73. package/src/server/index.ts +136 -192
  74. package/src/server/operations/composites.ts +355 -0
  75. package/src/server/operations/http.ts +103 -0
  76. package/src/server/operations/index.ts +65 -0
  77. package/src/server/operations/invoker.ts +87 -0
  78. package/src/server/operations/mcp.ts +221 -0
  79. package/src/server/operations/ops/annotation.ts +60 -0
  80. package/src/server/operations/ops/app.ts +447 -0
  81. package/src/server/operations/ops/ax-await.ts +216 -0
  82. package/src/server/operations/ops/ax-shared.ts +38 -0
  83. package/src/server/operations/ops/ax-state.ts +249 -0
  84. package/src/server/operations/ops/ax-timeline.ts +381 -0
  85. package/src/server/operations/ops/ax-work.ts +635 -0
  86. package/src/server/operations/ops/batch.ts +365 -0
  87. package/src/server/operations/ops/edges.ts +166 -0
  88. package/src/server/operations/ops/groups.ts +176 -0
  89. package/src/server/operations/ops/json-render.ts +691 -0
  90. package/src/server/operations/ops/nodes.ts +1047 -0
  91. package/src/server/operations/ops/query.ts +281 -0
  92. package/src/server/operations/ops/snapshots.ts +366 -0
  93. package/src/server/operations/ops/validate.ts +37 -0
  94. package/src/server/operations/ops/viewport.ts +219 -0
  95. package/src/server/operations/ops/webview.ts +339 -0
  96. package/src/server/operations/registry.ts +79 -0
  97. package/src/server/operations/types.ts +150 -0
  98. package/src/server/operations/webview-runner.ts +77 -0
  99. package/src/server/server.ts +253 -2170
  100. package/src/server/web-artifacts.ts +6 -2
@@ -10,7 +10,7 @@
10
10
  * Legacy `.pmx-canvas/state.json` is auto-migrated on first boot.
11
11
  */
12
12
  import { type PersistedCanvasState, type CanvasTheme, type AxTimelineQuery } from './canvas-db.js';
13
- import { type PmxAxElicitation, type PmxAxModeRequest, type PmxAxMode, type PmxAxCommandDescriptor, type PmxAxPolicy, type PmxAxFocusState, type PmxAxSource, type PmxAxState, type PmxAxWorkItem, type PmxAxWorkItemStatus, type PmxAxApprovalGate, type PmxAxReviewAnnotation, type PmxAxReviewKind, type PmxAxReviewSeverity, type PmxAxReviewStatus, type PmxAxReviewAnchorType, type PmxAxReviewRegion, type PmxAxEvent, type PmxAxEventKind, type PmxAxEvidence, type PmxAxEvidenceKind, type PmxAxSteeringMessage, type PmxAxHostCapability, type PmxAxTimelineSummary } from './ax-state.js';
13
+ import { type PmxAxActivityKind, type PmxAxElicitation, type PmxAxModeRequest, type PmxAxMode, type PmxAxCommandDescriptor, type PmxAxPolicy, type PmxAxFocusState, type PmxAxSource, type PmxAxState, type PmxAxWorkItem, type PmxAxWorkItemStatus, type PmxAxApprovalGate, type PmxAxReviewAnnotation, type PmxAxReviewKind, type PmxAxReviewSeverity, type PmxAxReviewStatus, type PmxAxReviewAnchorType, type PmxAxReviewRegion, type PmxAxEvent, type PmxAxEventKind, type PmxAxEvidence, type PmxAxEvidenceKind, type PmxAxSteeringMessage, type PmxAxHostCapability, type PmxAxTimelineSummary } from './ax-state.js';
14
14
  export declare const PMX_CANVAS_DIR = ".pmx-canvas";
15
15
  export interface PersistedBlobRef {
16
16
  __pmxCanvasBlob: 'v1';
@@ -142,12 +142,16 @@ declare class CanvasStateManager {
142
142
  private _viewport;
143
143
  private _theme;
144
144
  private _contextPinnedNodeIds;
145
- private _axState;
146
- private _axHostCapability;
147
145
  private _workspaceRoot;
146
+ private readonly ax;
148
147
  private _changeListeners;
149
- /** Register a listener for state changes. Used by MCP server to emit resource notifications. */
150
- onChange(cb: (type: CanvasChangeType) => void): void;
148
+ /**
149
+ * Register a listener for state changes. Used by MCP server to emit resource
150
+ * notifications and by the blocking-wait endpoints to await an AX transition.
151
+ * Returns a disposer that unregisters the listener (callers that don't need it
152
+ * — e.g. the long-lived MCP subscription — may ignore the return value).
153
+ */
154
+ onChange(cb: (type: CanvasChangeType) => void): () => void;
151
155
  private notifyChange;
152
156
  private _mutationRecorder;
153
157
  private _suppressRecordingDepth;
@@ -159,8 +163,6 @@ declare class CanvasStateManager {
159
163
  private suppressed;
160
164
  private recordMutation;
161
165
  private currentNodeIdSet;
162
- private normalizeAxForCurrentNodes;
163
- private applyAxState;
164
166
  private applyResolvedGroupBounds;
165
167
  private getGroupSnapshot;
166
168
  private normalizeNode;
@@ -346,6 +348,9 @@ declare class CanvasStateManager {
346
348
  resolution?: string;
347
349
  source?: PmxAxSource;
348
350
  }): PmxAxModeRequest | null;
351
+ getApproval(id: string): PmxAxApprovalGate | null;
352
+ getElicitation(id: string): PmxAxElicitation | null;
353
+ getModeRequest(id: string): PmxAxModeRequest | null;
349
354
  getCommandRegistry(): PmxAxCommandDescriptor[];
350
355
  /** Invoke a registry-gated PMX command intent — records a timeline event (no execution). */
351
356
  invokeCommand(name: string, args?: Record<string, unknown> | null, options?: {
@@ -356,10 +361,10 @@ declare class CanvasStateManager {
356
361
  setPolicy(patch: {
357
362
  tools?: Partial<PmxAxPolicy['tools']>;
358
363
  prompt?: Partial<PmxAxPolicy['prompt']>;
359
- }, _options?: {
364
+ }, options?: {
360
365
  source?: PmxAxSource;
361
366
  }): PmxAxPolicy;
362
- setHostCapability(input: unknown, _options?: {
367
+ setHostCapability(input: unknown, options?: {
363
368
  source?: PmxAxSource;
364
369
  }): PmxAxHostCapability;
365
370
  recordAxEvent(input: {
@@ -385,16 +390,43 @@ declare class CanvasStateManager {
385
390
  source?: PmxAxSource;
386
391
  }): PmxAxSteeringMessage;
387
392
  markSteeringDelivered(id: string): boolean;
393
+ ingestActivity(input: {
394
+ kind: PmxAxActivityKind;
395
+ title: string;
396
+ summary?: string | null;
397
+ outcome?: 'success' | 'failure';
398
+ ref?: string | null;
399
+ nodeIds?: string[];
400
+ data?: Record<string, unknown> | null;
401
+ reactions?: {
402
+ workItem?: false | {
403
+ status?: PmxAxWorkItemStatus;
404
+ detail?: string | null;
405
+ };
406
+ evidence?: false | {
407
+ kind?: PmxAxEvidenceKind;
408
+ body?: string | null;
409
+ };
410
+ review?: false | {
411
+ severity?: PmxAxReviewSeverity;
412
+ kind?: PmxAxReviewKind;
413
+ anchorType?: PmxAxReviewAnchorType;
414
+ nodeId?: string | null;
415
+ };
416
+ };
417
+ }, options?: {
418
+ source?: PmxAxSource;
419
+ }): {
420
+ event: PmxAxEvent;
421
+ workItem: PmxAxWorkItem | null;
422
+ evidence: PmxAxEvidence | null;
423
+ review: PmxAxReviewAnnotation | null;
424
+ };
388
425
  getAxEvents(q?: AxTimelineQuery): PmxAxEvent[];
389
426
  getAxEvidence(q?: AxTimelineQuery): PmxAxEvidence[];
390
427
  getAxSteering(q?: AxTimelineQuery & {
391
428
  onlyPending?: boolean;
392
429
  }): PmxAxSteeringMessage[];
393
- /**
394
- * Undelivered steering for a consumer (Phase 4 delivery). Excludes messages
395
- * whose source equals the consumer to prevent delivery loops (e.g. Copilot
396
- * should not be handed back steering it originated).
397
- */
398
430
  getPendingSteering(options?: {
399
431
  consumer?: string;
400
432
  limit?: number;
@@ -27,6 +27,13 @@ export declare function normalizeSurfaceTheme(value: string | null | undefined):
27
27
  * injected when the node's AX capabilities are enabled (opt-in for `html`), and
28
28
  * the server re-validates every interaction — so this is a convenience surface,
29
29
  * not a trust boundary.
30
+ *
31
+ * `emit` returns a Promise that resolves with the interaction result once the
32
+ * parent acks it (report #55 — built-in confirmation so a click no longer looks
33
+ * like "nothing happened"). Authors can also `window.PMX_AX.on('ack', cb)` or
34
+ * listen for the `pmx-ax-ack` CustomEvent. Resolves with an `ax-ack-timeout`
35
+ * result after 10s if no ack arrives (e.g. an older parent), so `await emit()`
36
+ * never hangs.
30
37
  */
31
38
  export declare function buildAxBridge(axToken: string, nodeId: string): string;
32
39
  /**
@@ -2,11 +2,12 @@ import { EventEmitter } from 'node:events';
2
2
  import { canvasState } from './canvas-state.js';
3
3
  import type { CanvasAnnotation, CanvasNodeState, CanvasEdge, CanvasLayout } from './canvas-state.js';
4
4
  import { type AxInteractionInput, type AxInteractionPublicResult } from './ax-interaction.js';
5
- import type { PmxAxApprovalGate, PmxAxCommandDescriptor, PmxAxContext, PmxAxElicitation, PmxAxEvent, PmxAxEvidence, PmxAxEvidenceKind, PmxAxFocusState, PmxAxHostCapability, PmxAxMode, PmxAxModeRequest, PmxAxPolicy, PmxAxReviewAnchorType, PmxAxReviewAnnotation, PmxAxReviewKind, PmxAxReviewRegion, PmxAxReviewSeverity, PmxAxReviewStatus, PmxAxSource, PmxAxState, PmxAxSteeringMessage, PmxAxWorkItem, PmxAxWorkItemStatus } from './ax-state.js';
5
+ import type { PmxAxActivityKind, PmxAxApprovalGate, PmxAxCommandDescriptor, PmxAxContext, PmxAxElicitation, PmxAxEvent, PmxAxEvidence, PmxAxEvidenceKind, PmxAxFocusState, PmxAxHostCapability, PmxAxMode, PmxAxModeRequest, PmxAxPolicy, PmxAxReviewAnchorType, PmxAxReviewAnnotation, PmxAxReviewKind, PmxAxReviewRegion, PmxAxReviewSeverity, PmxAxReviewStatus, PmxAxSource, PmxAxState, PmxAxSteeringMessage, PmxAxWorkItem, PmxAxWorkItemStatus } from './ax-state.js';
6
6
  import type { AxTimelineQuery } from './canvas-db.js';
7
7
  import { searchNodes } from './spatial-analysis.js';
8
8
  import { diffLayouts } from './mutation-history.js';
9
9
  import { fitCanvasView, gcCanvasSnapshots, listCanvasSnapshots } from './canvas-operations.js';
10
+ import { type OpenMcpAppCoreResult } from './operations/index.js';
10
11
  import { type SerializedCanvasNode } from './canvas-serialization.js';
11
12
  import type { HtmlPrimitiveKind } from './html-primitives.js';
12
13
  import { type WebArtifactBuildInput, type WebArtifactCanvasBuildResult } from './web-artifacts.js';
@@ -88,6 +89,7 @@ export declare class PmxCanvas extends EventEmitter {
88
89
  error?: string;
89
90
  }>;
90
91
  updateNode(id: string, patch: Partial<CanvasNodeState> & Record<string, unknown>): void;
92
+ /** Remove a node. Missing id throws (plan-005 unifies this across surfaces). */
91
93
  removeNode(id: string): void;
92
94
  addEdge(input: {
93
95
  from?: string;
@@ -134,7 +136,9 @@ export declare class PmxCanvas extends EventEmitter {
134
136
  panned: boolean;
135
137
  } | null;
136
138
  getAxState(): PmxAxState;
137
- getAxContext(): PmxAxContext;
139
+ getAxContext(options?: {
140
+ consumer?: string;
141
+ }): PmxAxContext;
138
142
  setAxFocus(nodeIds: string[], options?: {
139
143
  source?: PmxAxSource;
140
144
  }): PmxAxFocusState;
@@ -253,6 +257,62 @@ export declare class PmxCanvas extends EventEmitter {
253
257
  resolution?: string;
254
258
  source?: PmxAxSource;
255
259
  }): PmxAxModeRequest | null;
260
+ ingestActivity(input: {
261
+ kind: PmxAxActivityKind;
262
+ title: string;
263
+ summary?: string | null;
264
+ outcome?: 'success' | 'failure';
265
+ ref?: string | null;
266
+ nodeIds?: string[];
267
+ data?: Record<string, unknown> | null;
268
+ reactions?: {
269
+ workItem?: false | {
270
+ status?: PmxAxWorkItemStatus;
271
+ detail?: string | null;
272
+ };
273
+ evidence?: false | {
274
+ kind?: PmxAxEvidenceKind;
275
+ body?: string | null;
276
+ };
277
+ review?: false | {
278
+ severity?: PmxAxReviewSeverity;
279
+ kind?: PmxAxReviewKind;
280
+ anchorType?: PmxAxReviewAnchorType;
281
+ nodeId?: string | null;
282
+ };
283
+ };
284
+ }, options?: {
285
+ source?: PmxAxSource;
286
+ }): {
287
+ event: PmxAxEvent;
288
+ workItem: PmxAxWorkItem | null;
289
+ evidence: PmxAxEvidence | null;
290
+ review: PmxAxReviewAnnotation | null;
291
+ };
292
+ getApproval(id: string): PmxAxApprovalGate | null;
293
+ getElicitation(id: string): PmxAxElicitation | null;
294
+ getModeRequest(id: string): PmxAxModeRequest | null;
295
+ awaitApproval(id: string, options?: {
296
+ timeoutMs?: number;
297
+ signal?: AbortSignal;
298
+ }): Promise<{
299
+ approvalGate: PmxAxApprovalGate | null;
300
+ pending: boolean;
301
+ }>;
302
+ awaitElicitation(id: string, options?: {
303
+ timeoutMs?: number;
304
+ signal?: AbortSignal;
305
+ }): Promise<{
306
+ elicitation: PmxAxElicitation | null;
307
+ pending: boolean;
308
+ }>;
309
+ awaitMode(id: string, options?: {
310
+ timeoutMs?: number;
311
+ signal?: AbortSignal;
312
+ }): Promise<{
313
+ modeRequest: PmxAxModeRequest | null;
314
+ pending: boolean;
315
+ }>;
256
316
  getCommandRegistry(): PmxAxCommandDescriptor[];
257
317
  invokeCommand(name: string, args?: Record<string, unknown> | null, options?: {
258
318
  source?: PmxAxSource;
@@ -329,7 +389,6 @@ export declare class PmxCanvas extends EventEmitter {
329
389
  summary: import("./code-graph.js").CodeGraphSummary;
330
390
  };
331
391
  validate(): import("./canvas-validation.js").CanvasValidationResult;
332
- private findCanvasExtAppNodeId;
333
392
  describeSchema(): {
334
393
  ok: true;
335
394
  source: "running-server";
@@ -362,15 +421,10 @@ export declare class PmxCanvas extends EventEmitter {
362
421
  op: string;
363
422
  assign?: string;
364
423
  args?: Record<string, unknown>;
365
- }>): Promise<{
366
- ok: boolean;
367
- results: Array<Record<string, unknown>>;
368
- refs: Record<string, unknown>;
369
- failedIndex?: number;
370
- error?: string;
371
- }>;
424
+ }>): Promise<import("./operations/index.js").BatchEnvelope>;
372
425
  buildWebArtifact(input: WebArtifactBuildInput & {
373
426
  openInCanvas?: boolean;
427
+ includeLogs?: boolean;
374
428
  }): Promise<WebArtifactCanvasBuildResult>;
375
429
  openMcpApp(input: {
376
430
  transport: ExternalMcpTransportConfig;
@@ -384,22 +438,8 @@ export declare class PmxCanvas extends EventEmitter {
384
438
  width?: number;
385
439
  height?: number;
386
440
  timeoutMs?: number;
387
- }): Promise<{
388
- ok: true;
389
- id?: string;
390
- nodeId: string | null;
391
- toolCallId: string;
392
- sessionId: string;
393
- resourceUri: string;
394
- }>;
395
- addDiagram(input: DiagramPresetOpenInput): Promise<{
396
- ok: true;
397
- id?: string;
398
- nodeId: string | null;
399
- toolCallId: string;
400
- sessionId: string;
401
- resourceUri: string;
402
- }>;
441
+ }): Promise<OpenMcpAppCoreResult>;
442
+ addDiagram(input: DiagramPresetOpenInput): Promise<OpenMcpAppCoreResult>;
403
443
  addJsonRenderNode(input: JsonRenderNodeInput): {
404
444
  id: string;
405
445
  url: string;
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Composite MCP tools (plan-006: MCP tool consolidation).
3
+ *
4
+ * A composite tool folds several single-purpose MCP tools into one tool with an
5
+ * `action` discriminator. It is a PRESENTATION-LAYER construct only: each action
6
+ * dispatches to an already-registered operation (`src/server/operations/ops/*`)
7
+ * via the same invoker, reusing that operation's own `mcp.buildInput` and
8
+ * `mcp.formatResult`. So `canvas_edge { action: "add", ... }` is byte-identical
9
+ * to the standalone `canvas_add_edge` — same op, same arg mapping, same result
10
+ * shape — by construction. No handler logic lives here.
11
+ *
12
+ * Migration (docs/api-stability.md + plan-006): composites land ADDITIVELY in
13
+ * v0.2 alongside the legacy single-purpose tools (the tool surface grows, then
14
+ * shrinks when the legacy tools are removed in v0.3). Every action here maps to a
15
+ * registry-backed operation (plan-005 slices 1–7 + plan-008 Wave 1).
16
+ *
17
+ * Still deferred (its legacy standalone tool keeps working; see plan-008): the
18
+ * `canvas_snapshot` composite (the v0.3 name collision). The action enums are
19
+ * forward-compatible: adding an action later is additive. (`canvas_webview`
20
+ * shipped in plan-008 Wave 3 via runner injection; `canvas_app` shipped in Wave 4
21
+ * — open-mcp-app / diagram / build-artifact. Wave 5 folded the last three legacy
22
+ * tools deprecate-only — NO per-action input-injection mechanism was needed:
23
+ * `canvas_add_html_node` / `canvas_add_html_primitive` → `canvas_node` action
24
+ * "add" (type:"html" [+ primitive]); `canvas_refresh_webpage_node` → `canvas_node`
25
+ * action "update" (refresh:true). `canvas_screenshot` stays standalone — it
26
+ * returns a binary image payload the composite/registry JSON wire shape does not
27
+ * model.)
28
+ *
29
+ * Not shipped here: the `canvas_snapshot` composite (plan-006 #7). Its target
30
+ * name is ALREADY a legacy standalone tool (the save-snapshot tool, op
31
+ * `snapshot.save`), so it cannot be added additively without a name clash, and
32
+ * repurposing `canvas_snapshot` to be action-discriminated now would break
33
+ * existing callers. It lands in v0.3, in the same change that removes the legacy
34
+ * single-purpose snapshot tools and frees the name.
35
+ *
36
+ * This module must never import server.ts or index.ts.
37
+ */
38
+ import { type ZodRawShape } from 'zod';
39
+ /**
40
+ * One composite MCP tool: a frozen tool name + its action→operation routing.
41
+ *
42
+ * Two flavours:
43
+ * - Single-discriminator (the wave-1 composites + the 4 single-discriminator AX
44
+ * composites): the flat `actions` map routes one `action` value → one op.
45
+ * - Two-discriminator (`canvas_ax_gate`, plan-007 Slice C): a `kind` × `action`
46
+ * matrix folds 9 ops into one tool. Set `extraDiscriminatorShape` (the `kind`
47
+ * enum), `memberOps` (the op names — used to derive the schema union + the
48
+ * deprecation notes), `actionEnum` (the action discriminator values), and
49
+ * `resolveOp` (maps `{ kind, action }` → op name, or undefined for an invalid
50
+ * combo → a loud error at dispatch). The flat `actions` map is left empty for
51
+ * these; the matrix path uses `resolveOp` instead.
52
+ */
53
+ export interface CompositeToolDefinition {
54
+ /** Frozen public tool name (see tests/unit/mcp-tool-freeze.test.ts). */
55
+ toolName: string;
56
+ description: string;
57
+ /** Human-readable action list for the `action` enum description. */
58
+ actionSummary: string;
59
+ /**
60
+ * Map of `action` value → registry operation name (single-discriminator
61
+ * composites). Empty for two-discriminator composites. Every referenced op
62
+ * MUST have an `mcp` block — its `buildInput`/`formatResult` are reused so the
63
+ * composite action matches the legacy standalone tool exactly.
64
+ */
65
+ actions: Record<string, string>;
66
+ /**
67
+ * Two-discriminator extension (e.g. `canvas_ax_gate`). The extra discriminator
68
+ * shape — a single `kind` enum — merged into the advertised schema alongside
69
+ * `action`.
70
+ */
71
+ extraDiscriminatorShape?: ZodRawShape;
72
+ /**
73
+ * Two-discriminator extension: the action enum values (used to build the
74
+ * `action` discriminator when there is no flat `actions` map to derive it from).
75
+ */
76
+ actionEnum?: readonly string[];
77
+ /**
78
+ * Two-discriminator extension: every member op name. Used to build the schema
79
+ * union (all member-op fields, optional) and to derive a deprecation note per
80
+ * member op (each mapped back to its (kind, action) by `describeOp`).
81
+ */
82
+ memberOps?: string[];
83
+ /**
84
+ * Two-discriminator extension: resolve the op name from the validated
85
+ * discriminators. Returns `undefined` for an invalid combo so dispatch can
86
+ * raise a loud error instead of silently no-op'ing.
87
+ */
88
+ resolveOp?: (input: {
89
+ kind: string;
90
+ action: string;
91
+ }) => string | undefined;
92
+ /**
93
+ * Two-discriminator extension: human-readable `(kind, action)` for a member op
94
+ * (the inverse of `resolveOp`), used to build that op's deprecation note. The
95
+ * `kind` field-collision is resolved here (see `gateFieldRemap`).
96
+ */
97
+ describeOp?: (opName: string) => {
98
+ kind: string;
99
+ action: string;
100
+ } | undefined;
101
+ /**
102
+ * Field-name remap applied to the composite's advertised schema and undone at
103
+ * dispatch. Resolves a collision between a discriminator name and a member-op
104
+ * field of the same name (e.g. `ax.approval.request` has its own `action`
105
+ * field — namespaced to `approvalAction` in the composite so the `action`
106
+ * discriminator wins, then mapped back before invoking the op). Keys are the
107
+ * composite (public) field names; values are the op field names.
108
+ */
109
+ fieldRemap?: Record<string, string>;
110
+ }
111
+ export declare const compositeToolDefinitions: CompositeToolDefinition[];
112
+ /**
113
+ * Deprecation notes for the legacy single-purpose tools, DERIVED from the
114
+ * composites: every operation a composite folds gets a `Deprecated: use
115
+ * canvas_x with action "y".` prefix on its standalone tool description, steering
116
+ * agents to the composite during the v0.2 overlap window (the legacy tools and
117
+ * these notes are removed together in v0.3). Keyed by registry operation name.
118
+ * Deriving it keeps the deprecation list in lockstep with the composites — a new
119
+ * folded action automatically deprecates the tool it replaces.
120
+ */
121
+ export declare function buildCompositeDeprecationNotes(definitions?: CompositeToolDefinition[]): Map<string, string>;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Shared body reader: preserves the parsed JSON value as-is (object, array,
3
+ * or primitive) — per-op `readInput` decides what to do with non-object
4
+ * bodies; the shared reader never coerces.
5
+ */
6
+ export declare function readJsonValue(req: Request): Promise<unknown>;
7
+ export declare function dispatchOperationRoute(req: Request, url: URL): Promise<Response | null>;
@@ -0,0 +1,8 @@
1
+ export { executeOperation, getOperation, listOperations, registerOperation, setOperationEventEmitter, } from './registry.js';
2
+ export { dispatchOperationRoute } from './http.js';
3
+ export { runCanvasBatchOperation, type BatchEnvelope } from './ops/batch.js';
4
+ export { type OpenMcpAppCoreResult } from './ops/app.js';
5
+ export { LocalOperationInvoker, HttpOperationInvoker, type OperationInvoker } from './invoker.js';
6
+ export { registerOperationTools, registerCompositeTools, type OperationToolHost } from './mcp.js';
7
+ export { compositeToolDefinitions, type CompositeToolDefinition } from './composites.js';
8
+ export { OperationError, defineOperation, type Operation, type OperationContext, type OperationDefinition, type OperationErrorStatus, } from './types.js';
@@ -0,0 +1,13 @@
1
+ export interface OperationInvoker {
2
+ invoke(name: string, input: Record<string, unknown>): Promise<unknown>;
3
+ }
4
+ /** Runs operations in-process against the shared canvasState singleton. */
5
+ export declare class LocalOperationInvoker implements OperationInvoker {
6
+ invoke(name: string, input: Record<string, unknown>): Promise<unknown>;
7
+ }
8
+ /** Builds the HTTP request from the op's route template (`:id` from input, GET flags to query). */
9
+ export declare class HttpOperationInvoker implements OperationInvoker {
10
+ private readonly baseUrl;
11
+ constructor(baseUrl: string);
12
+ invoke(name: string, input: Record<string, unknown>): Promise<unknown>;
13
+ }
@@ -0,0 +1,15 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { OperationInvoker } from './invoker.js';
3
+ import { type OperationMcpToolHost } from './types.js';
4
+ import { type CompositeToolDefinition } from './composites.js';
5
+ export interface OperationToolHost extends OperationMcpToolHost {
6
+ invoker(): OperationInvoker;
7
+ }
8
+ export declare function registerOperationTools(server: McpServer, getHost: () => Promise<OperationToolHost>): void;
9
+ /**
10
+ * Register composite (action-discriminated) MCP tools (plan-006). Each action
11
+ * dispatches to a registered operation, reusing that op's `mcp.buildInput` and
12
+ * `mcp.formatResult` so the composite action is byte-identical to the standalone
13
+ * tool it folds. Defaults to `compositeToolDefinitions`.
14
+ */
15
+ export declare function registerCompositeTools(server: McpServer, getHost: () => Promise<OperationToolHost>, definitions?: CompositeToolDefinition[]): void;
@@ -0,0 +1,2 @@
1
+ import { type Operation } from '../types.js';
2
+ export declare const annotationOperations: Operation[];
@@ -0,0 +1,33 @@
1
+ import { type ExternalMcpTransportConfig } from '../../mcp-app-runtime.js';
2
+ import { type Operation, type OperationContext } from '../types.js';
3
+ export interface OpenMcpAppCoreInput {
4
+ transport: ExternalMcpTransportConfig;
5
+ toolName: string;
6
+ toolArguments?: Record<string, unknown>;
7
+ nodeId?: string;
8
+ serverName?: string;
9
+ title?: string;
10
+ x?: number;
11
+ y?: number;
12
+ width?: number;
13
+ height?: number;
14
+ timeoutMs?: number;
15
+ }
16
+ export interface OpenMcpAppCoreResult {
17
+ ok: true;
18
+ id?: string;
19
+ nodeId: string | null;
20
+ toolCallId: string;
21
+ sessionId: string;
22
+ resourceUri: string;
23
+ }
24
+ /**
25
+ * Open an external MCP app: connect + call + read resource (openExternalMcpApp),
26
+ * close any prior session on an in-place node, emit `ext-app-open` +
27
+ * `ext-app-result`, then resolve the resulting canvas node id. This is the exact
28
+ * legacy SDK `openMcpApp` body, relocated; both the mcpapp.open op AND the SDK
29
+ * call it. The diagram.open op delegates here after building the Excalidraw input
30
+ * (the SSE pair fires ONCE — diagram.open does not re-emit).
31
+ */
32
+ export declare function openMcpAppCore(input: OpenMcpAppCoreInput, ctx: OperationContext): Promise<OpenMcpAppCoreResult>;
33
+ export declare const appOperations: Operation[];
@@ -0,0 +1,2 @@
1
+ import { type Operation } from '../types.js';
2
+ export declare const axAwaitOperations: Operation[];
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Shared helpers for the AX operation modules (ax-state.ts, ax-work.ts, and the
3
+ * later timeline/delivery waves). These were replicated per-file during the
4
+ * plan-007 Slice B migration; centralizing them here keeps one definition site
5
+ * as more AX op files land.
6
+ *
7
+ * `normalizeAxSource` / `normalizeAxNodeIds` are reimplemented from server.ts
8
+ * because `operations/` must never import server.ts (the SSE emitter is injected;
9
+ * see plan-005). This module likewise must not import server.ts or index.ts.
10
+ */
11
+ import { z } from 'zod';
12
+ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
13
+ import type { PmxAxSource } from '../../ax-state.js';
14
+ export declare const AX_SOURCES: readonly ["agent", "api", "browser", "cli", "codex", "copilot", "mcp", "sdk", "system"];
15
+ /** Zod schema for the optional `source` field shared by AX MCP tool shapes. */
16
+ export declare const AX_SOURCE_SHAPE: z.ZodOptional<z.ZodEnum<{
17
+ agent: "agent";
18
+ api: "api";
19
+ browser: "browser";
20
+ cli: "cli";
21
+ codex: "codex";
22
+ copilot: "copilot";
23
+ mcp: "mcp";
24
+ sdk: "sdk";
25
+ system: "system";
26
+ }>>;
27
+ /** An absent or unrecognized source falls back to the per-surface default. */
28
+ export declare function normalizeAxSource(value: unknown, fallback: PmxAxSource): PmxAxSource;
29
+ export declare function normalizeAxNodeIds(value: unknown): string[];
30
+ /** The plain JSON tool result shared by AX ops whose MCP body is the wire body. */
31
+ export declare function axJsonResult(result: unknown): CallToolResult;
@@ -0,0 +1,2 @@
1
+ import { type Operation } from '../types.js';
2
+ export declare const axStateOperations: Operation[];
@@ -0,0 +1,2 @@
1
+ import { type Operation } from '../types.js';
2
+ export declare const axTimelineOperations: Operation[];
@@ -0,0 +1,2 @@
1
+ import { type Operation } from '../types.js';
2
+ export declare const axWorkOperations: Operation[];
@@ -0,0 +1,19 @@
1
+ import { type Operation } from '../types.js';
2
+ export interface BatchEnvelope {
3
+ ok: boolean;
4
+ results: Array<Record<string, unknown>>;
5
+ refs: Record<string, unknown>;
6
+ failedIndex?: number;
7
+ error?: string;
8
+ }
9
+ export declare const batchOperations: Operation[];
10
+ /**
11
+ * Typed SDK entry point: run a batch through the registry's single execution
12
+ * path (`executeOperation('canvas.batch')`). The op emits the one final
13
+ * canvas-layout-update itself, so callers must NOT emit again.
14
+ */
15
+ export declare function runCanvasBatchOperation(operations: Array<{
16
+ op: string;
17
+ assign?: string;
18
+ args?: Record<string, unknown>;
19
+ }>): Promise<BatchEnvelope>;
@@ -0,0 +1,2 @@
1
+ import { type Operation } from '../types.js';
2
+ export declare const edgeOperations: Operation[];
@@ -0,0 +1,2 @@
1
+ import { type Operation } from '../types.js';
2
+ export declare const groupOperations: Operation[];
@@ -0,0 +1,31 @@
1
+ import { type Operation } from '../types.js';
2
+ /** Legacy server.ts parseGraphPayloadData: a graph dataset must be an array of records. */
3
+ export declare function parseGraphPayloadData(value: unknown): Array<Record<string, unknown>> | null;
4
+ export interface StreamJsonRenderInput {
5
+ nodeId?: string;
6
+ title?: string;
7
+ patches?: unknown[];
8
+ done?: boolean;
9
+ x?: number;
10
+ y?: number;
11
+ width?: number;
12
+ height?: number;
13
+ strictSize?: boolean;
14
+ }
15
+ export interface StreamJsonRenderResult {
16
+ id: string;
17
+ url: string;
18
+ ok: true;
19
+ applied: number;
20
+ skipped: number;
21
+ specVersion: number;
22
+ elementCount: number;
23
+ streamStatus: 'open' | 'closed';
24
+ }
25
+ /**
26
+ * Create-or-append core for streaming json-render nodes (the SDK's
27
+ * streamJsonRenderNode wraps this directly). Throws OperationError(400) when
28
+ * the append target is missing or not a json-render node.
29
+ */
30
+ export declare function streamJsonRenderCore(input: StreamJsonRenderInput): StreamJsonRenderResult;
31
+ export declare const jsonRenderOperations: Operation[];
@@ -0,0 +1,62 @@
1
+ import { type CanvasLayout, type CanvasNodeState } from '../../canvas-state.js';
2
+ import { type Operation } from '../types.js';
3
+ export declare const NODE_TYPES: readonly ["markdown", "status", "context", "ledger", "trace", "file", "image", "mcp-app", "webpage", "html", "group"];
4
+ /** Per-type default node frame size (formerly copy-pasted ladders). */
5
+ export declare function defaultNodeSize(type: string): {
6
+ width: number;
7
+ height: number;
8
+ };
9
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
10
+ export declare function pickFiniteNumber(record: Record<string, unknown>, key: string): number | undefined;
11
+ export declare function getRecord(value: unknown): Record<string, unknown> | undefined;
12
+ export declare function pickPositiveNumber(record: Record<string, unknown>, key: string): number | undefined;
13
+ export declare function resolveCreateGeometry(body: Record<string, unknown>): {
14
+ x?: number;
15
+ y?: number;
16
+ width?: number;
17
+ height?: number;
18
+ };
19
+ export declare function setGroupChildrenFromApi(groupId: string, childIds: string[]): boolean;
20
+ export declare function nodeAppSessionId(node: CanvasNodeState | undefined): string | null;
21
+ export declare function closeNodeAppSession(node: CanvasNodeState | undefined): void;
22
+ export declare function buildNodeResponse(node: CanvasNodeState): Record<string, unknown>;
23
+ export declare function wantsFullPayload(input?: Record<string, unknown>): boolean;
24
+ export declare function compactNodePayload(node: CanvasNodeState | undefined): Record<string, unknown> | null;
25
+ export declare function buildSummaryFromLayout(layout: CanvasLayout, pinnedIds: string[]): Record<string, unknown>;
26
+ export declare function compactLayoutPayload(layout: CanvasLayout, pinnedIds: string[]): Record<string, unknown>;
27
+ export declare function agentSafeFullLayoutPayload(layout: CanvasLayout): Record<string, unknown>;
28
+ /**
29
+ * Node-create/update MCP payload: exposes both `id` and a `nodeId` alias so
30
+ * agents using either key (or a cached schema) work — matching the
31
+ * external-app / web-artifact responses that already return both.
32
+ */
33
+ export declare function createdNodePayloadFromNode(node: CanvasNodeState, options?: Record<string, unknown>): Record<string, unknown>;
34
+ /**
35
+ * Create a basic (non-webpage / non-group / non-primitive) node. Union of the
36
+ * legacy handleCanvasAddNode generic branch; the SDK passes fileMode 'path',
37
+ * the HTTP/MCP operation passes fileMode 'auto'.
38
+ */
39
+ export declare function createBasicCanvasNode(body: Record<string, unknown>, options: {
40
+ fileMode: 'auto' | 'path';
41
+ }): {
42
+ node: CanvasNodeState;
43
+ needsCodeGraphRecompute: boolean;
44
+ };
45
+ /**
46
+ * Build a node patch with the full HTTP superset semantics (webpage
47
+ * titleSource, html top-level fields, axCapabilities merge, group children,
48
+ * structured spec/graph updates, trace fields). Throws OperationError on
49
+ * validation failures. The SDK's updateNode delegates here.
50
+ */
51
+ export declare function buildNodePatch(existing: CanvasNodeState, body: Record<string, unknown>): {
52
+ patch: Partial<CanvasNodeState>;
53
+ groupChildIds?: string[];
54
+ };
55
+ /**
56
+ * Remove a node (closing any mcp-app session). Missing id → OperationError 404
57
+ * on ALL surfaces (plan-005 deliberately unifies the old silent local success).
58
+ */
59
+ export declare function removeNodeCore(id: string): {
60
+ needsCodeGraphRecompute: boolean;
61
+ };
62
+ export declare const nodeOperations: Operation[];
@@ -0,0 +1,2 @@
1
+ import { type Operation } from '../types.js';
2
+ export declare const queryOperations: Operation[];