pmx-canvas 0.1.36 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/CHANGELOG.md +447 -0
  2. package/Readme.md +2 -2
  3. package/dist/json-render/index.js +89 -334
  4. package/dist/types/mcp/canvas-access.d.ts +5 -171
  5. package/dist/types/server/ax-state-manager.d.ts +267 -0
  6. package/dist/types/server/ax-state.d.ts +3 -1
  7. package/dist/types/server/canvas-db.d.ts +13 -0
  8. package/dist/types/server/canvas-operations.d.ts +1 -12
  9. package/dist/types/server/canvas-state.d.ts +8 -23
  10. package/dist/types/server/index.d.ts +6 -24
  11. package/dist/types/server/operations/composites.d.ts +121 -0
  12. package/dist/types/server/operations/http.d.ts +7 -0
  13. package/dist/types/server/operations/index.d.ts +8 -0
  14. package/dist/types/server/operations/invoker.d.ts +13 -0
  15. package/dist/types/server/operations/mcp.d.ts +15 -0
  16. package/dist/types/server/operations/ops/annotation.d.ts +2 -0
  17. package/dist/types/server/operations/ops/app.d.ts +33 -0
  18. package/dist/types/server/operations/ops/ax-await.d.ts +2 -0
  19. package/dist/types/server/operations/ops/ax-shared.d.ts +31 -0
  20. package/dist/types/server/operations/ops/ax-state.d.ts +2 -0
  21. package/dist/types/server/operations/ops/ax-timeline.d.ts +2 -0
  22. package/dist/types/server/operations/ops/ax-work.d.ts +2 -0
  23. package/dist/types/server/operations/ops/batch.d.ts +19 -0
  24. package/dist/types/server/operations/ops/edges.d.ts +2 -0
  25. package/dist/types/server/operations/ops/groups.d.ts +2 -0
  26. package/dist/types/server/operations/ops/json-render.d.ts +31 -0
  27. package/dist/types/server/operations/ops/nodes.d.ts +62 -0
  28. package/dist/types/server/operations/ops/query.d.ts +2 -0
  29. package/dist/types/server/operations/ops/snapshots.d.ts +2 -0
  30. package/dist/types/server/operations/ops/validate.d.ts +2 -0
  31. package/dist/types/server/operations/ops/viewport.d.ts +2 -0
  32. package/dist/types/server/operations/ops/webview.d.ts +2 -0
  33. package/dist/types/server/operations/registry.d.ts +15 -0
  34. package/dist/types/server/operations/types.d.ts +116 -0
  35. package/dist/types/server/operations/webview-runner.d.ts +69 -0
  36. package/docs/RELEASE.md +5 -0
  37. package/docs/adr-001-bun-only-runtime.md +46 -0
  38. package/docs/api-stability.md +57 -0
  39. package/docs/ax-host-adapter-contract.md +19 -1
  40. package/docs/ax-state-contract.md +72 -0
  41. package/docs/http-api.md +4 -0
  42. package/docs/mcp.md +61 -12
  43. package/docs/plans/plan-005-operation-registry.md +84 -0
  44. package/docs/plans/plan-006-mcp-tool-consolidation.md +109 -0
  45. package/docs/plans/plan-007-ax-domain.md +99 -0
  46. package/docs/plans/plan-008-registry-finish.md +91 -0
  47. package/docs/tech-debt-assessment-2026-06.md +90 -0
  48. package/package.json +3 -3
  49. package/skills/pmx-canvas/SKILL.md +221 -193
  50. package/skills/pmx-canvas/evals/evals.json +3 -3
  51. package/skills/pmx-canvas/references/ax-html-control-surface.md +93 -0
  52. package/skills/pmx-canvas/references/codex-app-adapter.md +13 -14
  53. package/skills/pmx-canvas/references/github-copilot-app-adapter.md +26 -11
  54. package/src/cli/agent.ts +52 -31
  55. package/src/mcp/canvas-access.ts +30 -830
  56. package/src/mcp/server.ts +162 -2014
  57. package/src/server/ax-context.ts +8 -1
  58. package/src/server/ax-state-manager.ts +826 -0
  59. package/src/server/ax-state.ts +10 -2
  60. package/src/server/canvas-db.ts +35 -0
  61. package/src/server/canvas-operations.ts +2 -328
  62. package/src/server/canvas-schema.ts +2 -2
  63. package/src/server/canvas-state.ts +103 -465
  64. package/src/server/index.ts +54 -190
  65. package/src/server/operations/composites.ts +355 -0
  66. package/src/server/operations/http.ts +103 -0
  67. package/src/server/operations/index.ts +65 -0
  68. package/src/server/operations/invoker.ts +87 -0
  69. package/src/server/operations/mcp.ts +221 -0
  70. package/src/server/operations/ops/annotation.ts +60 -0
  71. package/src/server/operations/ops/app.ts +447 -0
  72. package/src/server/operations/ops/ax-await.ts +216 -0
  73. package/src/server/operations/ops/ax-shared.ts +38 -0
  74. package/src/server/operations/ops/ax-state.ts +249 -0
  75. package/src/server/operations/ops/ax-timeline.ts +381 -0
  76. package/src/server/operations/ops/ax-work.ts +635 -0
  77. package/src/server/operations/ops/batch.ts +365 -0
  78. package/src/server/operations/ops/edges.ts +166 -0
  79. package/src/server/operations/ops/groups.ts +176 -0
  80. package/src/server/operations/ops/json-render.ts +691 -0
  81. package/src/server/operations/ops/nodes.ts +1047 -0
  82. package/src/server/operations/ops/query.ts +281 -0
  83. package/src/server/operations/ops/snapshots.ts +366 -0
  84. package/src/server/operations/ops/validate.ts +37 -0
  85. package/src/server/operations/ops/viewport.ts +219 -0
  86. package/src/server/operations/ops/webview.ts +339 -0
  87. package/src/server/operations/registry.ts +79 -0
  88. package/src/server/operations/types.ts +150 -0
  89. package/src/server/operations/webview-runner.ts +77 -0
  90. package/src/server/server.ts +158 -2255
  91. package/src/server/web-artifacts.ts +6 -2
@@ -7,6 +7,7 @@ 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;
@@ -387,7 +389,6 @@ export declare class PmxCanvas extends EventEmitter {
387
389
  summary: import("./code-graph.js").CodeGraphSummary;
388
390
  };
389
391
  validate(): import("./canvas-validation.js").CanvasValidationResult;
390
- private findCanvasExtAppNodeId;
391
392
  describeSchema(): {
392
393
  ok: true;
393
394
  source: "running-server";
@@ -420,15 +421,10 @@ export declare class PmxCanvas extends EventEmitter {
420
421
  op: string;
421
422
  assign?: string;
422
423
  args?: Record<string, unknown>;
423
- }>): Promise<{
424
- ok: boolean;
425
- results: Array<Record<string, unknown>>;
426
- refs: Record<string, unknown>;
427
- failedIndex?: number;
428
- error?: string;
429
- }>;
424
+ }>): Promise<import("./operations/index.js").BatchEnvelope>;
430
425
  buildWebArtifact(input: WebArtifactBuildInput & {
431
426
  openInCanvas?: boolean;
427
+ includeLogs?: boolean;
432
428
  }): Promise<WebArtifactCanvasBuildResult>;
433
429
  openMcpApp(input: {
434
430
  transport: ExternalMcpTransportConfig;
@@ -442,22 +438,8 @@ export declare class PmxCanvas extends EventEmitter {
442
438
  width?: number;
443
439
  height?: number;
444
440
  timeoutMs?: number;
445
- }): Promise<{
446
- ok: true;
447
- id?: string;
448
- nodeId: string | null;
449
- toolCallId: string;
450
- sessionId: string;
451
- resourceUri: string;
452
- }>;
453
- addDiagram(input: DiagramPresetOpenInput): Promise<{
454
- ok: true;
455
- id?: string;
456
- nodeId: string | null;
457
- toolCallId: string;
458
- sessionId: string;
459
- resourceUri: string;
460
- }>;
441
+ }): Promise<OpenMcpAppCoreResult>;
442
+ addDiagram(input: DiagramPresetOpenInput): Promise<OpenMcpAppCoreResult>;
461
443
  addJsonRenderNode(input: JsonRenderNodeInput): {
462
444
  id: string;
463
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[];
@@ -0,0 +1,2 @@
1
+ import { type Operation } from '../types.js';
2
+ export declare const snapshotOperations: Operation[];
@@ -0,0 +1,2 @@
1
+ import { type Operation } from '../types.js';
2
+ export declare const validateOperations: Operation[];
@@ -0,0 +1,2 @@
1
+ import { type Operation } from '../types.js';
2
+ export declare const viewportOperations: Operation[];
@@ -0,0 +1,2 @@
1
+ import { type Operation } from '../types.js';
2
+ export declare const webviewOperations: Operation[];
@@ -0,0 +1,15 @@
1
+ import { type Operation } from './types.js';
2
+ export declare function registerOperation(op: Operation): void;
3
+ export declare function getOperation(name: string): Operation;
4
+ export declare function listOperations(): Operation[];
5
+ type OperationEventEmitter = (event: string, payload: Record<string, unknown>) => void;
6
+ export declare function setOperationEventEmitter(emitter: OperationEventEmitter | null): void;
7
+ /** True while operation SSE emits are being suppressed (inside a meta-op such as
8
+ * canvas.batch). Ops whose effect depends on a live SSE emit firing — e.g.
9
+ * mcpapp.open, whose canvas node is created as a side-effect of `ext-app-open` —
10
+ * use this to reject loudly instead of silently no-op'ing in a suppressed run. */
11
+ export declare function isEmitSuppressed(): boolean;
12
+ /** Run `fn` with all operation SSE emits suppressed; restores depth on finally. */
13
+ export declare function runWithSuppressedEmits<T>(fn: () => Promise<T>): Promise<T>;
14
+ export declare function executeOperation(name: string, rawInput: unknown): Promise<unknown>;
15
+ export {};
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Operation registry core types (plan-005).
3
+ *
4
+ * One `Operation` describes a canvas operation once: input schema, the single
5
+ * handler implementation, and how the operation surfaces over HTTP and MCP.
6
+ * `defineOperation` wraps the typed pieces into a transport-agnostic record.
7
+ *
8
+ * Modules in `operations/` must never import `../server.ts` or `../index.ts`
9
+ * (the SSE emitter is injected via `setOperationEventEmitter`; the SDK imports
10
+ * the operation cores directly).
11
+ */
12
+ import type { ZodRawShape } from 'zod';
13
+ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
14
+ export type OperationErrorStatus = 400 | 404 | 409;
15
+ /** Operation failure that maps to an HTTP status + `{ ok:false, error }` body and MCP `isError`. */
16
+ export declare class OperationError extends Error {
17
+ readonly status: OperationErrorStatus;
18
+ /** Extra fields merged into the HTTP `{ ok:false, error }` body (e.g. the legacy
19
+ * webview-failure `webview` status snapshot). Omit for the plain envelope. */
20
+ readonly details?: Record<string, unknown>;
21
+ constructor(message: string, status?: OperationErrorStatus, details?: Record<string, unknown>);
22
+ }
23
+ export interface OperationContext {
24
+ /** Emit a workbench SSE event (e.g. extra `canvas-layout-update` frames, focus, viewport). */
25
+ emit(event: string, payload?: Record<string, unknown>): void;
26
+ }
27
+ export interface OperationHttpRoute {
28
+ method: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';
29
+ /** EXACT legacy path; `:param` segments capture path parameters. */
30
+ path: string;
31
+ /**
32
+ * Per-op input reader. The default merges query params, a JSON object body
33
+ * (arrays/primitives are preserved by the shared reader — a per-op reader
34
+ * decides how to use them), and path params (params win).
35
+ */
36
+ readInput?: (req: Request, params: Record<string, string>, url: URL) => Promise<Record<string, unknown>> | Record<string, unknown>;
37
+ /** HTTP status for a successful result. Defaults to 200. */
38
+ status?: (result: unknown) => number;
39
+ /**
40
+ * Return parsed non-2xx JSON bodies to operation callers instead of throwing.
41
+ * Use only for operations whose MCP contract formats structured failure bodies
42
+ * itself (for example canvas.batch partial failures).
43
+ */
44
+ errorBodyAsResult?: boolean;
45
+ }
46
+ /** Host capabilities available to MCP result formatters. */
47
+ export interface OperationMcpToolHost {
48
+ getPinnedNodeIds(): Promise<string[]>;
49
+ /**
50
+ * Invoke another registered operation over the host's transport (local or
51
+ * HTTP) — structural subset of OperationInvoker to avoid an import cycle.
52
+ * Used by formatters that need a follow-up read (undo/redo history flags,
53
+ * restore summary).
54
+ */
55
+ invoker(): {
56
+ invoke(name: string, input: Record<string, unknown>): Promise<unknown>;
57
+ };
58
+ }
59
+ export interface OperationMcpTool {
60
+ /** Frozen legacy tool name (see tests/unit/mcp-tool-freeze.test.ts). */
61
+ toolName: string;
62
+ description: string;
63
+ /**
64
+ * MCP-only presentation flags and typed overrides merged over the operation
65
+ * input shape when advertising the tool schema (e.g. `full` / `verbose`).
66
+ */
67
+ extraShape?: ZodRawShape;
68
+ /** Map raw MCP args onto operation input. May throw OperationError. */
69
+ buildInput?: (input: Record<string, unknown>) => Record<string, unknown>;
70
+ /** Format the wire-shaped operation result into a tool result. */
71
+ formatResult?: (result: unknown, input: Record<string, unknown>, host: OperationMcpToolHost) => Promise<CallToolResult> | CallToolResult;
72
+ }
73
+ /** Registered, transport-agnostic operation record. */
74
+ export interface Operation {
75
+ name: string;
76
+ /** true → the registry emits one `canvas-layout-update` after success. */
77
+ mutates: boolean;
78
+ /** Raw zod shape (for MCP tool schemas). */
79
+ inputShape: ZodRawShape;
80
+ http: OperationHttpRoute | null;
81
+ mcp: OperationMcpTool | null;
82
+ /** Validate raw input, run the handler, serialize to the canonical wire shape. */
83
+ execute(rawInput: unknown, ctx: OperationContext): Promise<unknown>;
84
+ }
85
+ /**
86
+ * Structural view of a zod schema (avoids fighting zod's generic variance).
87
+ * Any `z.looseObject(...)` satisfies this.
88
+ */
89
+ export interface OperationInputSchema<I> {
90
+ safeParse(value: unknown): {
91
+ success: true;
92
+ data: I;
93
+ } | {
94
+ success: false;
95
+ error: {
96
+ issues: Array<{
97
+ path: PropertyKey[];
98
+ message: string;
99
+ }>;
100
+ };
101
+ };
102
+ }
103
+ export interface OperationDefinition<I extends Record<string, unknown>, O> {
104
+ name: string;
105
+ mutates: boolean;
106
+ /** MUST be loose (z.looseObject / .passthrough()) — legacy ignores unknown keys. */
107
+ input: OperationInputSchema<I>;
108
+ inputShape: ZodRawShape;
109
+ http?: OperationHttpRoute;
110
+ mcp?: OperationMcpTool;
111
+ /** The single implementation. Mutate via canvasState/canvas-operations so history records. */
112
+ handler: (input: I, ctx: OperationContext) => O | Promise<O>;
113
+ /** Map handler output to the HTTP wire body. Defaults to identity. */
114
+ serialize?: (output: O) => unknown;
115
+ }
116
+ export declare function defineOperation<I extends Record<string, unknown>, O>(def: OperationDefinition<I, O>): Operation;