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
@@ -6,95 +6,48 @@ import {
6
6
  type CanvasEdge,
7
7
  type CanvasLayout,
8
8
  type CanvasNodeState,
9
- type CanvasSnapshot,
10
9
  type PmxCanvas,
11
10
  } from '../server/index.js';
12
11
  import type { PmxAxSource } from '../server/ax-state.js';
12
+ import {
13
+ HttpOperationInvoker,
14
+ LocalOperationInvoker,
15
+ type OperationInvoker,
16
+ } from '../server/operations/index.js';
13
17
 
14
- type AddNodeInput = Parameters<PmxCanvas['addNode']>[0];
15
- type AddWebpageNodeInput = Parameters<PmxCanvas['addWebpageNode']>[0];
16
18
  type RefreshWebpageNodeResult = Awaited<ReturnType<PmxCanvas['refreshWebpageNode']>>;
17
- type OpenMcpAppInput = Parameters<PmxCanvas['openMcpApp']>[0];
18
- type OpenMcpAppResult = Awaited<ReturnType<PmxCanvas['openMcpApp']>>;
19
- type AddDiagramInput = Parameters<PmxCanvas['addDiagram']>[0];
20
- type AddJsonRenderNodeInput = Parameters<PmxCanvas['addJsonRenderNode']>[0];
21
- type AddJsonRenderNodeResult = ReturnType<PmxCanvas['addJsonRenderNode']>;
22
- type StreamJsonRenderNodeInput = Parameters<PmxCanvas['streamJsonRenderNode']>[0];
23
- type StreamJsonRenderNodeResult = ReturnType<PmxCanvas['streamJsonRenderNode']>;
19
+ // openMcpApp / addDiagram / buildWebArtifact CanvasAccess methods + their type
20
+ // aliases removed with the standalone MCP tools (plan-008 Wave 4): those tools
21
+ // migrated to the operation registry (mcpapp.open / diagram.open /
22
+ // webartifact.build) and the composite/registry tools dispatch via the invoker,
23
+ // not CanvasAccess. The public SDK PmxCanvas methods are unchanged.
24
24
  type AddHtmlNodeInput = Parameters<PmxCanvas['addHtmlNode']>[0];
25
25
  type AddHtmlPrimitiveInput = Parameters<PmxCanvas['addHtmlPrimitive']>[0];
26
26
  type AddHtmlPrimitiveResult = ReturnType<PmxCanvas['addHtmlPrimitive']>;
27
- type AddGraphNodeInput = Parameters<PmxCanvas['addGraphNode']>[0];
28
- type AddGraphNodeResult = ReturnType<PmxCanvas['addGraphNode']>;
29
- type UpdateNodePatch = Parameters<PmxCanvas['updateNode']>[1];
30
- type AddEdgeInput = Parameters<PmxCanvas['addEdge']>[0];
31
- type CreateGroupInput = Parameters<PmxCanvas['createGroup']>[0];
32
- type GroupNodesOptions = Parameters<PmxCanvas['groupNodes']>[2];
33
- type ArrangeLayout = Parameters<PmxCanvas['arrange']>[0];
34
- type FocusNodeResult = ReturnType<PmxCanvas['focusNode']>;
35
- type FitViewOptions = Parameters<PmxCanvas['fitView']>[0];
36
- type FitViewResult = ReturnType<PmxCanvas['fitView']>;
37
27
  type AxStateResult = ReturnType<PmxCanvas['getAxState']>;
38
28
  type AxContextResult = ReturnType<PmxCanvas['getAxContext']>;
39
- type SetAxFocusResult = ReturnType<PmxCanvas['setAxFocus']>;
40
- type RecordAxEventInput = Parameters<PmxCanvas['recordAxEvent']>[0];
41
- type RecordAxEventResult = ReturnType<PmxCanvas['recordAxEvent']>;
42
- type SendSteeringResult = ReturnType<PmxCanvas['sendSteering']>;
43
29
  type SubmitAxInteractionInput = Parameters<PmxCanvas['submitAxInteraction']>[0];
44
30
  type SubmitAxInteractionResult = ReturnType<PmxCanvas['submitAxInteraction']>;
45
31
  type GetPendingSteeringResult = ReturnType<PmxCanvas['getPendingSteering']>;
46
32
  type ListElicitationsResult = ReturnType<PmxCanvas['listElicitations']>;
47
- type RequestElicitationInput = Parameters<PmxCanvas['requestElicitation']>[0];
48
- type RequestElicitationResult = ReturnType<PmxCanvas['requestElicitation']>;
49
- type RespondElicitationResult = ReturnType<PmxCanvas['respondElicitation']>;
50
33
  type ListModeRequestsResult = ReturnType<PmxCanvas['listModeRequests']>;
51
- type RequestModeInput = Parameters<PmxCanvas['requestMode']>[0];
52
- type RequestModeResult = ReturnType<PmxCanvas['requestMode']>;
53
- type ResolveModeRequestResult = ReturnType<PmxCanvas['resolveModeRequest']>;
54
- type GetCommandRegistryResult = ReturnType<PmxCanvas['getCommandRegistry']>;
55
- type InvokeCommandResult = ReturnType<PmxCanvas['invokeCommand']>;
34
+ type IngestActivityInput = Parameters<PmxCanvas['ingestActivity']>[0];
35
+ type IngestActivityResult = ReturnType<PmxCanvas['ingestActivity']>;
56
36
  type GetPolicyResult = ReturnType<PmxCanvas['getPolicy']>;
57
- type SetPolicyInput = Parameters<PmxCanvas['setPolicy']>[0];
58
- type SetPolicyResult = ReturnType<PmxCanvas['setPolicy']>;
59
37
  type GetAxTimelineQuery = Parameters<PmxCanvas['getAxTimeline']>[0];
60
38
  type GetAxTimelineResult = ReturnType<PmxCanvas['getAxTimeline']>;
61
- type AddWorkItemInput = Parameters<PmxCanvas['addWorkItem']>[0];
62
- type AddWorkItemResult = ReturnType<PmxCanvas['addWorkItem']>;
63
- type UpdateWorkItemPatch = Parameters<PmxCanvas['updateWorkItem']>[1];
64
- type UpdateWorkItemResult = ReturnType<PmxCanvas['updateWorkItem']>;
65
39
  type ListWorkItemsResult = ReturnType<PmxCanvas['listWorkItems']>;
66
- type RequestApprovalInput = Parameters<PmxCanvas['requestApproval']>[0];
67
- type RequestApprovalResult = ReturnType<PmxCanvas['requestApproval']>;
68
- type ResolveApprovalResult = ReturnType<PmxCanvas['resolveApproval']>;
69
40
  type ListApprovalGatesResult = ReturnType<PmxCanvas['listApprovalGates']>;
70
- type AddEvidenceInput = Parameters<PmxCanvas['addEvidence']>[0];
71
- type AddEvidenceResult = ReturnType<PmxCanvas['addEvidence']>;
72
- type AddReviewAnnotationInput = Parameters<PmxCanvas['addReviewAnnotation']>[0];
73
- type AddReviewAnnotationResult = ReturnType<PmxCanvas['addReviewAnnotation']>;
74
- type UpdateReviewAnnotationPatch = Parameters<PmxCanvas['updateReviewAnnotation']>[1];
75
- type UpdateReviewAnnotationResult = ReturnType<PmxCanvas['updateReviewAnnotation']>;
76
41
  type ListReviewAnnotationsResult = ReturnType<PmxCanvas['listReviewAnnotations']>;
77
- type GetHostCapabilityResult = ReturnType<PmxCanvas['getHostCapability']>;
78
- type ReportHostCapabilityResult = ReturnType<PmxCanvas['reportHostCapability']>;
79
- type SearchResult = ReturnType<PmxCanvas['search']>;
80
- type UndoRedoResult = Awaited<ReturnType<PmxCanvas['undo']>>;
81
42
  type HistoryResult = ReturnType<PmxCanvas['getHistory']>;
82
- type SetContextPinsResult = ReturnType<PmxCanvas['setContextPins']>;
83
43
  type RunBatchInput = Parameters<PmxCanvas['runBatch']>[0];
84
44
  type RunBatchResult = Awaited<ReturnType<PmxCanvas['runBatch']>>;
85
- type SnapshotListOptions = Parameters<PmxCanvas['listSnapshots']>[0];
86
- type SnapshotList = ReturnType<PmxCanvas['listSnapshots']>;
87
- type DeleteSnapshotResult = ReturnType<PmxCanvas['deleteSnapshot']>;
88
- type GcSnapshotsOptions = Parameters<PmxCanvas['gcSnapshots']>[0];
89
- type GcSnapshotsResult = ReturnType<PmxCanvas['gcSnapshots']>;
90
- type DiffSnapshotResult = ReturnType<PmxCanvas['diffSnapshot']>;
91
45
  type CodeGraphResult = ReturnType<PmxCanvas['getCodeGraph']>;
92
- type ValidationResult = ReturnType<PmxCanvas['validate']>;
93
- type WebArtifactInput = Parameters<PmxCanvas['buildWebArtifact']>[0];
94
- type WebArtifactResult = Awaited<ReturnType<PmxCanvas['buildWebArtifact']>>;
95
- type AutomationWebViewOptions = Parameters<PmxCanvas['startAutomationWebView']>[0];
96
- type AutomationWebViewStatus = Awaited<ReturnType<PmxCanvas['startAutomationWebView']>>;
97
- type AutomationEvaluateResult = Awaited<ReturnType<PmxCanvas['evaluateAutomationWebView']>>;
46
+ // canvas_screenshot (the only webview tool still hand-written) needs the status
47
+ // + screenshot accessors; the other four webview methods (start/stop/evaluate/
48
+ // resize) migrated to the operation registry (plan-008 Wave 3) and were removed
49
+ // from CanvasAccess.
50
+ type AutomationWebViewStatus = Awaited<ReturnType<PmxCanvas['getAutomationWebViewStatus']>>;
98
51
  type AutomationScreenshotOptions = Parameters<PmxCanvas['screenshotAutomationWebView']>[0];
99
52
 
100
53
  interface HealthResponse {
@@ -107,120 +60,41 @@ interface NodeResponse {
107
60
  node?: { id?: string };
108
61
  }
109
62
 
110
- interface JsonRenderNodeResponse extends NodeResponse {
111
- url: string;
112
- spec: AddJsonRenderNodeResult['spec'];
113
- }
114
-
115
- interface GraphNodeResponse extends NodeResponse {
116
- url: string;
117
- spec: AddGraphNodeResult['spec'];
118
- }
119
-
120
- interface SearchResponse {
121
- results?: SearchResult;
122
- }
123
-
124
- interface SnapshotSaveResponse {
125
- snapshot?: CanvasSnapshot;
126
- }
127
-
128
- interface WebViewEnvelope {
129
- webview?: AutomationWebViewStatus;
130
- }
131
-
132
- interface WebViewStopEnvelope extends WebViewEnvelope {
133
- stopped?: boolean;
134
- }
135
-
136
- interface WebViewEvaluateEnvelope {
137
- value?: AutomationEvaluateResult;
138
- }
139
-
140
63
  export interface CanvasAccess {
141
64
  readonly port: number;
142
65
  readonly remoteBaseUrl: string | null;
66
+ /** Operation-registry invoker (plan-005): local in-process or HTTP, matching the access mode. */
67
+ invoker(): OperationInvoker;
143
68
  getLayout(): Promise<CanvasLayout>;
144
69
  getNode(id: string): Promise<CanvasNodeState | undefined>;
145
- addNode(input: AddNodeInput): Promise<string>;
146
- addWebpageNode(input: AddWebpageNodeInput): Promise<Awaited<ReturnType<PmxCanvas['addWebpageNode']>>>;
147
70
  refreshWebpageNode(id: string, url?: string): Promise<RefreshWebpageNodeResult>;
148
- openMcpApp(input: OpenMcpAppInput): Promise<OpenMcpAppResult>;
149
- addDiagram(input: AddDiagramInput): Promise<OpenMcpAppResult>;
150
- addJsonRenderNode(input: AddJsonRenderNodeInput): Promise<AddJsonRenderNodeResult>;
151
- streamJsonRenderNode(input: StreamJsonRenderNodeInput): Promise<StreamJsonRenderNodeResult>;
152
71
  addHtmlNode(input: AddHtmlNodeInput): Promise<string>;
153
72
  addHtmlPrimitive(input: AddHtmlPrimitiveInput): Promise<AddHtmlPrimitiveResult>;
154
- addGraphNode(input: AddGraphNodeInput): Promise<AddGraphNodeResult>;
155
- buildWebArtifact(input: WebArtifactInput): Promise<WebArtifactResult>;
156
- updateNode(id: string, patch: UpdateNodePatch): Promise<void>;
157
- removeNode(id: string): Promise<void>;
158
- removeAnnotation(id: string): Promise<boolean>;
159
- addEdge(input: AddEdgeInput): Promise<string>;
160
- removeEdge(id: string): Promise<void>;
161
- createGroup(input: CreateGroupInput): Promise<string>;
162
- groupNodes(groupId: string, childIds: string[], options?: GroupNodesOptions): Promise<boolean>;
163
- ungroupNodes(groupId: string): Promise<boolean>;
164
- arrange(layout?: ArrangeLayout): Promise<void>;
165
- focusNode(id: string, options?: { noPan?: boolean }): Promise<FocusNodeResult>;
166
- fitView(options?: FitViewOptions): Promise<FitViewResult>;
167
73
  getAxState(): Promise<AxStateResult>;
168
- getAxContext(): Promise<AxContextResult>;
169
- setAxFocus(nodeIds: string[], options?: { source?: PmxAxSource }): Promise<SetAxFocusResult>;
170
- recordAxEvent(input: RecordAxEventInput, options?: { source?: PmxAxSource }): Promise<RecordAxEventResult>;
171
- sendSteering(message: string, options?: { source?: PmxAxSource }): Promise<SendSteeringResult>;
74
+ getAxContext(options?: { consumer?: string }): Promise<AxContextResult>;
172
75
  getAxTimeline(query?: GetAxTimelineQuery): Promise<GetAxTimelineResult>;
173
- addWorkItem(input: AddWorkItemInput, options?: { source?: PmxAxSource }): Promise<AddWorkItemResult>;
174
- updateWorkItem(id: string, patch: UpdateWorkItemPatch, options?: { source?: PmxAxSource }): Promise<UpdateWorkItemResult>;
175
76
  listWorkItems(): Promise<ListWorkItemsResult>;
176
- requestApproval(input: RequestApprovalInput, options?: { source?: PmxAxSource }): Promise<RequestApprovalResult>;
177
- resolveApproval(id: string, decision: 'approved' | 'rejected', options?: { resolution?: string; source?: PmxAxSource }): Promise<ResolveApprovalResult>;
178
77
  listApprovalGates(): Promise<ListApprovalGatesResult>;
179
- addEvidence(input: AddEvidenceInput, options?: { source?: PmxAxSource }): Promise<AddEvidenceResult>;
180
- addReviewAnnotation(input: AddReviewAnnotationInput, options?: { source?: PmxAxSource }): Promise<AddReviewAnnotationResult>;
181
- updateReviewAnnotation(id: string, patch: UpdateReviewAnnotationPatch, options?: { source?: PmxAxSource }): Promise<UpdateReviewAnnotationResult>;
182
78
  listReviewAnnotations(): Promise<ListReviewAnnotationsResult>;
183
- getHostCapability(): Promise<GetHostCapabilityResult>;
184
- reportHostCapability(input: unknown, options?: { source?: PmxAxSource }): Promise<ReportHostCapabilityResult>;
185
79
  submitAxInteraction(input: SubmitAxInteractionInput, options?: { source?: PmxAxSource }): Promise<SubmitAxInteractionResult>;
186
80
  getPendingSteering(options?: { consumer?: string; limit?: number }): Promise<GetPendingSteeringResult>;
187
- markSteeringDelivered(id: string): Promise<boolean>;
188
81
  listElicitations(): Promise<ListElicitationsResult>;
189
- requestElicitation(input: RequestElicitationInput, options?: { source?: PmxAxSource }): Promise<RequestElicitationResult>;
190
- respondElicitation(id: string, response: Record<string, unknown>, options?: { source?: PmxAxSource }): Promise<RespondElicitationResult>;
191
82
  listModeRequests(): Promise<ListModeRequestsResult>;
192
- requestMode(input: RequestModeInput, options?: { source?: PmxAxSource }): Promise<RequestModeResult>;
193
- resolveModeRequest(id: string, decision: 'approved' | 'rejected', options?: { resolution?: string; source?: PmxAxSource }): Promise<ResolveModeRequestResult>;
194
- getCommandRegistry(): Promise<GetCommandRegistryResult>;
195
- invokeCommand(name: string, args?: Record<string, unknown> | null, options?: { source?: PmxAxSource }): Promise<InvokeCommandResult>;
83
+ ingestActivity(input: IngestActivityInput, options?: { source?: PmxAxSource }): Promise<IngestActivityResult>;
196
84
  getPolicy(): Promise<GetPolicyResult>;
197
- setPolicy(patch: SetPolicyInput, options?: { source?: PmxAxSource }): Promise<SetPolicyResult>;
198
- clear(): Promise<void>;
199
- search(query: string): Promise<SearchResult>;
200
- undo(): Promise<UndoRedoResult>;
201
- redo(): Promise<UndoRedoResult>;
202
85
  getHistory(): Promise<HistoryResult>;
203
- setContextPins(nodeIds: string[], mode?: 'set' | 'add' | 'remove'): Promise<SetContextPinsResult>;
204
86
  getPinnedNodeIds(): Promise<string[]>;
205
87
  runBatch(operations: RunBatchInput): Promise<RunBatchResult>;
206
- listSnapshots(options?: SnapshotListOptions): Promise<SnapshotList>;
207
- saveSnapshot(name: string): Promise<CanvasSnapshot | null>;
208
- restoreSnapshot(id: string): Promise<{ ok: boolean }>;
209
- deleteSnapshot(id: string): Promise<DeleteSnapshotResult>;
210
- gcSnapshots(options?: GcSnapshotsOptions): Promise<GcSnapshotsResult>;
211
- diffSnapshot(idOrName: string): Promise<DiffSnapshotResult>;
212
88
  getCodeGraph(): Promise<CodeGraphResult>;
213
- validate(): Promise<ValidationResult>;
89
+ // canvas_screenshot (still hand-written — binary payload) is the only webview
90
+ // tool left on CanvasAccess; it needs the status + screenshot accessors.
214
91
  getAutomationWebViewStatus(): Promise<AutomationWebViewStatus>;
215
- startAutomationWebView(options?: AutomationWebViewOptions): Promise<AutomationWebViewStatus>;
216
- stopAutomationWebView(): Promise<boolean>;
217
- evaluateAutomationWebView(expression: string): Promise<AutomationEvaluateResult>;
218
- resizeAutomationWebView(width: number, height: number): Promise<AutomationWebViewStatus>;
219
92
  screenshotAutomationWebView(options?: AutomationScreenshotOptions): Promise<Uint8Array>;
220
93
  }
221
94
 
222
95
  class LocalCanvasAccess implements CanvasAccess {
223
96
  readonly remoteBaseUrl = null;
97
+ private readonly operationInvoker = new LocalOperationInvoker();
224
98
 
225
99
  constructor(
226
100
  private readonly canvas: PmxCanvas,
@@ -232,6 +106,10 @@ class LocalCanvasAccess implements CanvasAccess {
232
106
  return this.canvas.port;
233
107
  }
234
108
 
109
+ invoker(): OperationInvoker {
110
+ return this.operationInvoker;
111
+ }
112
+
235
113
  async getLayout(): Promise<CanvasLayout> {
236
114
  return this.canvas.getLayout();
237
115
  }
@@ -240,36 +118,10 @@ class LocalCanvasAccess implements CanvasAccess {
240
118
  return this.canvas.getNode(id);
241
119
  }
242
120
 
243
- async addNode(input: AddNodeInput): Promise<string> {
244
- // PmxCanvas.addNode returns the created node; the CanvasAccess contract
245
- // (shared with the remote proxy + MCP) stays id-only.
246
- return this.canvas.addNode(input).id;
247
- }
248
-
249
- async addWebpageNode(input: AddWebpageNodeInput): Promise<Awaited<ReturnType<PmxCanvas['addWebpageNode']>>> {
250
- return await this.canvas.addWebpageNode(input);
251
- }
252
-
253
121
  async refreshWebpageNode(id: string, url?: string): Promise<RefreshWebpageNodeResult> {
254
122
  return await this.canvas.refreshWebpageNode(id, url);
255
123
  }
256
124
 
257
- async openMcpApp(input: OpenMcpAppInput): Promise<OpenMcpAppResult> {
258
- return await this.canvas.openMcpApp(input);
259
- }
260
-
261
- async addDiagram(input: AddDiagramInput): Promise<OpenMcpAppResult> {
262
- return await this.canvas.addDiagram(input);
263
- }
264
-
265
- async addJsonRenderNode(input: AddJsonRenderNodeInput): Promise<AddJsonRenderNodeResult> {
266
- return this.canvas.addJsonRenderNode(input);
267
- }
268
-
269
- async streamJsonRenderNode(input: StreamJsonRenderNodeInput): Promise<StreamJsonRenderNodeResult> {
270
- return this.canvas.streamJsonRenderNode(input);
271
- }
272
-
273
125
  async addHtmlNode(input: AddHtmlNodeInput): Promise<string> {
274
126
  // PmxCanvas.addHtmlNode returns the created node; the CanvasAccess contract
275
127
  // is a bare id string, so extract it (mirrors addNode above).
@@ -280,86 +132,18 @@ class LocalCanvasAccess implements CanvasAccess {
280
132
  return this.canvas.addHtmlPrimitive(input);
281
133
  }
282
134
 
283
- async addGraphNode(input: AddGraphNodeInput): Promise<AddGraphNodeResult> {
284
- return this.canvas.addGraphNode(input);
285
- }
286
-
287
- async buildWebArtifact(input: WebArtifactInput): Promise<WebArtifactResult> {
288
- return await this.canvas.buildWebArtifact(input);
289
- }
290
-
291
- async updateNode(id: string, patch: UpdateNodePatch): Promise<void> {
292
- this.canvas.updateNode(id, patch);
293
- }
294
-
295
- async removeNode(id: string): Promise<void> {
296
- this.canvas.removeNode(id);
297
- }
298
-
299
- async removeAnnotation(id: string): Promise<boolean> {
300
- return this.canvas.removeAnnotation(id);
301
- }
302
-
303
- async addEdge(input: AddEdgeInput): Promise<string> {
304
- return this.canvas.addEdge(input);
305
- }
306
-
307
- async removeEdge(id: string): Promise<void> {
308
- this.canvas.removeEdge(id);
309
- }
310
-
311
- async createGroup(input: CreateGroupInput): Promise<string> {
312
- return this.canvas.createGroup(input);
313
- }
314
-
315
- async groupNodes(groupId: string, childIds: string[], options?: GroupNodesOptions): Promise<boolean> {
316
- return this.canvas.groupNodes(groupId, childIds, options);
317
- }
318
-
319
- async ungroupNodes(groupId: string): Promise<boolean> {
320
- return this.canvas.ungroupNodes(groupId);
321
- }
322
-
323
- async arrange(layout?: ArrangeLayout): Promise<void> {
324
- this.canvas.arrange(layout);
325
- }
326
-
327
- async focusNode(id: string, options?: { noPan?: boolean }): Promise<FocusNodeResult> {
328
- return this.canvas.focusNode(id, options);
329
- }
330
-
331
- async fitView(options?: FitViewOptions): Promise<FitViewResult> {
332
- return this.canvas.fitView(options);
333
- }
334
-
335
135
  async getAxState(): Promise<AxStateResult> {
336
136
  return this.canvas.getAxState();
337
137
  }
338
138
 
339
- async getAxContext(): Promise<AxContextResult> {
340
- return this.canvas.getAxContext();
341
- }
342
-
343
- async setAxFocus(nodeIds: string[], options?: { source?: PmxAxSource }): Promise<SetAxFocusResult> {
344
- return this.canvas.setAxFocus(nodeIds, { source: options?.source ?? 'mcp' });
345
- }
346
-
347
- async recordAxEvent(input: RecordAxEventInput, options?: { source?: PmxAxSource }): Promise<RecordAxEventResult> {
348
- return this.canvas.recordAxEvent(input, { source: options?.source ?? 'mcp' });
349
- }
350
-
351
- async sendSteering(message: string, options?: { source?: PmxAxSource }): Promise<SendSteeringResult> {
352
- return this.canvas.sendSteering(message, { source: options?.source ?? 'mcp' });
139
+ async getAxContext(options?: { consumer?: string }): Promise<AxContextResult> {
140
+ return this.canvas.getAxContext(options);
353
141
  }
354
142
 
355
143
  async getAxTimeline(query?: GetAxTimelineQuery): Promise<GetAxTimelineResult> {
356
144
  return this.canvas.getAxTimeline(query);
357
145
  }
358
146
 
359
- async addWorkItem(input: AddWorkItemInput, options?: { source?: PmxAxSource }): Promise<AddWorkItemResult> {
360
- return this.canvas.addWorkItem(input, { source: options?.source ?? 'mcp' });
361
- }
362
-
363
147
  async submitAxInteraction(input: SubmitAxInteractionInput, options?: { source?: PmxAxSource }): Promise<SubmitAxInteractionResult> {
364
148
  return this.canvas.submitAxInteraction(input, { source: options?.source ?? 'mcp' });
365
149
  }
@@ -368,121 +152,38 @@ class LocalCanvasAccess implements CanvasAccess {
368
152
  return this.canvas.getPendingSteering(options);
369
153
  }
370
154
 
371
- async markSteeringDelivered(id: string): Promise<boolean> {
372
- return this.canvas.markSteeringDelivered(id);
373
- }
374
-
375
155
  async listElicitations(): Promise<ListElicitationsResult> {
376
156
  return this.canvas.listElicitations();
377
157
  }
378
158
 
379
- async requestElicitation(input: RequestElicitationInput, options?: { source?: PmxAxSource }): Promise<RequestElicitationResult> {
380
- return this.canvas.requestElicitation(input, { source: options?.source ?? 'mcp' });
381
- }
382
-
383
- async respondElicitation(id: string, response: Record<string, unknown>, options?: { source?: PmxAxSource }): Promise<RespondElicitationResult> {
384
- return this.canvas.respondElicitation(id, response, { source: options?.source ?? 'mcp' });
385
- }
386
-
387
159
  async listModeRequests(): Promise<ListModeRequestsResult> {
388
160
  return this.canvas.listModeRequests();
389
161
  }
390
162
 
391
- async requestMode(input: RequestModeInput, options?: { source?: PmxAxSource }): Promise<RequestModeResult> {
392
- return this.canvas.requestMode(input, { source: options?.source ?? 'mcp' });
393
- }
394
-
395
- async resolveModeRequest(id: string, decision: 'approved' | 'rejected', options?: { resolution?: string; source?: PmxAxSource }): Promise<ResolveModeRequestResult> {
396
- return this.canvas.resolveModeRequest(id, decision, { ...(options ?? {}), source: options?.source ?? 'mcp' });
397
- }
398
-
399
- async getCommandRegistry(): Promise<GetCommandRegistryResult> {
400
- return this.canvas.getCommandRegistry();
401
- }
402
-
403
- async invokeCommand(name: string, args?: Record<string, unknown> | null, options?: { source?: PmxAxSource }): Promise<InvokeCommandResult> {
404
- return this.canvas.invokeCommand(name, args ?? null, { source: options?.source ?? 'mcp' });
163
+ async ingestActivity(input: IngestActivityInput, options?: { source?: PmxAxSource }): Promise<IngestActivityResult> {
164
+ return this.canvas.ingestActivity(input, { source: options?.source ?? 'mcp' });
405
165
  }
406
166
 
407
167
  async getPolicy(): Promise<GetPolicyResult> {
408
168
  return this.canvas.getPolicy();
409
169
  }
410
170
 
411
- async setPolicy(patch: SetPolicyInput, options?: { source?: PmxAxSource }): Promise<SetPolicyResult> {
412
- return this.canvas.setPolicy(patch, { source: options?.source ?? 'mcp' });
413
- }
414
-
415
- async updateWorkItem(id: string, patch: UpdateWorkItemPatch, options?: { source?: PmxAxSource }): Promise<UpdateWorkItemResult> {
416
- return this.canvas.updateWorkItem(id, patch, { source: options?.source ?? 'mcp' });
417
- }
418
-
419
171
  async listWorkItems(): Promise<ListWorkItemsResult> {
420
172
  return this.canvas.listWorkItems();
421
173
  }
422
174
 
423
- async requestApproval(input: RequestApprovalInput, options?: { source?: PmxAxSource }): Promise<RequestApprovalResult> {
424
- return this.canvas.requestApproval(input, { source: options?.source ?? 'mcp' });
425
- }
426
-
427
- async resolveApproval(id: string, decision: 'approved' | 'rejected', options?: { resolution?: string; source?: PmxAxSource }): Promise<ResolveApprovalResult> {
428
- return this.canvas.resolveApproval(id, decision, {
429
- ...(options?.resolution !== undefined ? { resolution: options.resolution } : {}),
430
- source: options?.source ?? 'mcp',
431
- });
432
- }
433
-
434
175
  async listApprovalGates(): Promise<ListApprovalGatesResult> {
435
176
  return this.canvas.listApprovalGates();
436
177
  }
437
178
 
438
- async addEvidence(input: AddEvidenceInput, options?: { source?: PmxAxSource }): Promise<AddEvidenceResult> {
439
- return this.canvas.addEvidence(input, { source: options?.source ?? 'mcp' });
440
- }
441
-
442
- async addReviewAnnotation(input: AddReviewAnnotationInput, options?: { source?: PmxAxSource }): Promise<AddReviewAnnotationResult> {
443
- return this.canvas.addReviewAnnotation(input, { source: options?.source ?? 'mcp' });
444
- }
445
-
446
- async updateReviewAnnotation(id: string, patch: UpdateReviewAnnotationPatch, options?: { source?: PmxAxSource }): Promise<UpdateReviewAnnotationResult> {
447
- return this.canvas.updateReviewAnnotation(id, patch, { source: options?.source ?? 'mcp' });
448
- }
449
-
450
179
  async listReviewAnnotations(): Promise<ListReviewAnnotationsResult> {
451
180
  return this.canvas.listReviewAnnotations();
452
181
  }
453
182
 
454
- async getHostCapability(): Promise<GetHostCapabilityResult> {
455
- return this.canvas.getHostCapability();
456
- }
457
-
458
- async reportHostCapability(input: unknown, options?: { source?: PmxAxSource }): Promise<ReportHostCapabilityResult> {
459
- return this.canvas.reportHostCapability(input, { source: options?.source ?? 'mcp' });
460
- }
461
-
462
- async clear(): Promise<void> {
463
- this.canvas.clear();
464
- }
465
-
466
- async search(query: string): Promise<SearchResult> {
467
- return this.canvas.search(query);
468
- }
469
-
470
- async undo(): Promise<UndoRedoResult> {
471
- return await this.canvas.undo();
472
- }
473
-
474
- async redo(): Promise<UndoRedoResult> {
475
- return await this.canvas.redo();
476
- }
477
-
478
183
  async getHistory(): Promise<HistoryResult> {
479
184
  return this.canvas.getHistory();
480
185
  }
481
186
 
482
- async setContextPins(nodeIds: string[], mode: 'set' | 'add' | 'remove' = 'set'): Promise<SetContextPinsResult> {
483
- return this.canvas.setContextPins(nodeIds, mode);
484
- }
485
-
486
187
  async getPinnedNodeIds(): Promise<string[]> {
487
188
  return Array.from(canvasState.contextPinnedNodeIds);
488
189
  }
@@ -491,58 +192,14 @@ class LocalCanvasAccess implements CanvasAccess {
491
192
  return await this.canvas.runBatch(operations);
492
193
  }
493
194
 
494
- async listSnapshots(options?: SnapshotListOptions): Promise<SnapshotList> {
495
- return this.canvas.listSnapshots(options);
496
- }
497
-
498
- async saveSnapshot(name: string): Promise<CanvasSnapshot | null> {
499
- return this.canvas.saveSnapshot(name);
500
- }
501
-
502
- async restoreSnapshot(id: string): Promise<{ ok: boolean }> {
503
- return await this.canvas.restoreSnapshot(id);
504
- }
505
-
506
- async deleteSnapshot(id: string): Promise<DeleteSnapshotResult> {
507
- return this.canvas.deleteSnapshot(id);
508
- }
509
-
510
- async gcSnapshots(options?: GcSnapshotsOptions): Promise<GcSnapshotsResult> {
511
- return this.canvas.gcSnapshots(options);
512
- }
513
-
514
- async diffSnapshot(idOrName: string): Promise<DiffSnapshotResult> {
515
- return this.canvas.diffSnapshot(idOrName);
516
- }
517
-
518
195
  async getCodeGraph(): Promise<CodeGraphResult> {
519
196
  return this.canvas.getCodeGraph();
520
197
  }
521
198
 
522
- async validate(): Promise<ValidationResult> {
523
- return this.canvas.validate();
524
- }
525
-
526
199
  async getAutomationWebViewStatus(): Promise<AutomationWebViewStatus> {
527
200
  return this.canvas.getAutomationWebViewStatus();
528
201
  }
529
202
 
530
- async startAutomationWebView(options: AutomationWebViewOptions = {}): Promise<AutomationWebViewStatus> {
531
- return await this.canvas.startAutomationWebView(options);
532
- }
533
-
534
- async stopAutomationWebView(): Promise<boolean> {
535
- return await this.canvas.stopAutomationWebView();
536
- }
537
-
538
- async evaluateAutomationWebView(expression: string): Promise<AutomationEvaluateResult> {
539
- return await this.canvas.evaluateAutomationWebView(expression);
540
- }
541
-
542
- async resizeAutomationWebView(width: number, height: number): Promise<AutomationWebViewStatus> {
543
- return await this.canvas.resizeAutomationWebView(width, height);
544
- }
545
-
546
203
  async screenshotAutomationWebView(options: AutomationScreenshotOptions = {}): Promise<Uint8Array> {
547
204
  return await this.canvas.screenshotAutomationWebView(options);
548
205
  }
@@ -551,11 +208,17 @@ class LocalCanvasAccess implements CanvasAccess {
551
208
  class RemoteCanvasAccess implements CanvasAccess {
552
209
  readonly remoteBaseUrl: string;
553
210
  readonly port: number;
211
+ private readonly operationInvoker: HttpOperationInvoker;
554
212
 
555
213
  constructor(baseUrl: string) {
556
214
  this.remoteBaseUrl = baseUrl.replace(/\/$/, '');
557
215
  const parsed = new URL(this.remoteBaseUrl);
558
216
  this.port = Number(parsed.port || '80');
217
+ this.operationInvoker = new HttpOperationInvoker(this.remoteBaseUrl);
218
+ }
219
+
220
+ invoker(): OperationInvoker {
221
+ return this.operationInvoker;
559
222
  }
560
223
 
561
224
  private async requestJson<T>(method: string, path: string, body?: unknown): Promise<T> {
@@ -621,61 +284,12 @@ class RemoteCanvasAccess implements CanvasAccess {
621
284
  return parsed as CanvasNodeState;
622
285
  }
623
286
 
624
- async addNode(input: AddNodeInput): Promise<string> {
625
- return await this.requestNodeId('POST', '/api/canvas/node', input);
626
- }
627
-
628
- async addWebpageNode(input: AddWebpageNodeInput): Promise<Awaited<ReturnType<PmxCanvas['addWebpageNode']>>> {
629
- return await this.requestJson<Awaited<ReturnType<PmxCanvas['addWebpageNode']>>>('POST', '/api/canvas/node', {
630
- type: 'webpage',
631
- ...input,
632
- });
633
- }
634
-
635
287
  async refreshWebpageNode(id: string, url?: string): Promise<RefreshWebpageNodeResult> {
636
288
  return await this.requestJson<RefreshWebpageNodeResult>('POST', `/api/canvas/node/${encodeURIComponent(id)}/refresh`, {
637
289
  ...(url ? { url } : {}),
638
290
  });
639
291
  }
640
292
 
641
- async openMcpApp(input: OpenMcpAppInput): Promise<OpenMcpAppResult> {
642
- return await this.requestJson<OpenMcpAppResult>('POST', '/api/canvas/mcp-app/open', input);
643
- }
644
-
645
- async addDiagram(input: AddDiagramInput): Promise<OpenMcpAppResult> {
646
- return await this.requestJson<OpenMcpAppResult>('POST', '/api/canvas/diagram', input);
647
- }
648
-
649
- async addJsonRenderNode(input: AddJsonRenderNodeInput): Promise<AddJsonRenderNodeResult> {
650
- const response = await this.requestJson<JsonRenderNodeResponse>('POST', '/api/canvas/json-render', input);
651
- const id = typeof response.id === 'string' ? response.id : response.node?.id;
652
- if (!id) throw new Error('json-render response did not include a node id.');
653
- return { id, url: response.url, spec: response.spec };
654
- }
655
-
656
- async streamJsonRenderNode(input: StreamJsonRenderNodeInput): Promise<StreamJsonRenderNodeResult> {
657
- const response = await this.requestJson<{
658
- id?: string;
659
- url?: string;
660
- applied?: number;
661
- skipped?: number;
662
- specVersion?: number;
663
- elementCount?: number;
664
- streamStatus?: 'open' | 'closed';
665
- }>('POST', '/api/canvas/json-render/stream', input);
666
- const id = typeof response.id === 'string' ? response.id : undefined;
667
- if (!id) throw new Error('json-render stream response did not include a node id.');
668
- return {
669
- id,
670
- url: response.url ?? '',
671
- applied: response.applied ?? 0,
672
- skipped: response.skipped ?? 0,
673
- specVersion: response.specVersion ?? 0,
674
- elementCount: response.elementCount ?? 0,
675
- streamStatus: response.streamStatus ?? 'open',
676
- };
677
- }
678
-
679
293
  async addHtmlNode(input: AddHtmlNodeInput): Promise<string> {
680
294
  const {
681
295
  summary,
@@ -729,98 +343,6 @@ class RemoteCanvasAccess implements CanvasAccess {
729
343
  };
730
344
  }
731
345
 
732
- async addGraphNode(input: AddGraphNodeInput): Promise<AddGraphNodeResult> {
733
- const response = await this.requestJson<GraphNodeResponse>('POST', '/api/canvas/graph', {
734
- ...input,
735
- ...(typeof input.heightPx === 'number' ? { nodeHeight: input.heightPx } : {}),
736
- });
737
- const id = typeof response.id === 'string' ? response.id : response.node?.id;
738
- if (!id) throw new Error('graph response did not include a node id.');
739
- return { id, url: response.url, spec: response.spec };
740
- }
741
-
742
- async buildWebArtifact(input: WebArtifactInput): Promise<WebArtifactResult> {
743
- return await this.requestJson<WebArtifactResult>('POST', '/api/canvas/web-artifact', input);
744
- }
745
-
746
- async updateNode(id: string, patch: UpdateNodePatch): Promise<void> {
747
- await this.requestJson<unknown>('PATCH', `/api/canvas/node/${encodeURIComponent(id)}`, patch);
748
- }
749
-
750
- async removeNode(id: string): Promise<void> {
751
- await this.requestJson<unknown>('DELETE', `/api/canvas/node/${encodeURIComponent(id)}`);
752
- }
753
-
754
- async removeAnnotation(id: string): Promise<boolean> {
755
- const response = await this.requestJson<{ ok?: boolean }>('DELETE', `/api/canvas/annotation/${encodeURIComponent(id)}`);
756
- return response.ok === true;
757
- }
758
-
759
- async addEdge(input: AddEdgeInput): Promise<string> {
760
- const response = await this.requestJson<{ id?: string }>('POST', '/api/canvas/edge', input);
761
- if (!response.id) throw new Error('Canvas edge response did not include an edge id.');
762
- return response.id;
763
- }
764
-
765
- async removeEdge(id: string): Promise<void> {
766
- await this.requestJson<unknown>('DELETE', '/api/canvas/edge', { edge_id: id });
767
- }
768
-
769
- async createGroup(input: CreateGroupInput): Promise<string> {
770
- return await this.requestNodeId('POST', '/api/canvas/group', input);
771
- }
772
-
773
- async groupNodes(groupId: string, childIds: string[], options?: GroupNodesOptions): Promise<boolean> {
774
- const response = await this.requestJson<{ ok?: boolean }>('POST', '/api/canvas/group/add', {
775
- groupId,
776
- childIds,
777
- ...(options?.childLayout ? { childLayout: options.childLayout } : {}),
778
- });
779
- return response.ok === true;
780
- }
781
-
782
- async ungroupNodes(groupId: string): Promise<boolean> {
783
- const response = await this.requestJson<{ ok?: boolean }>('POST', '/api/canvas/group/ungroup', { groupId });
784
- return response.ok === true;
785
- }
786
-
787
- async arrange(layout?: ArrangeLayout): Promise<void> {
788
- await this.requestJson<unknown>('POST', '/api/canvas/arrange', { ...(layout ? { layout } : {}) });
789
- }
790
-
791
- async focusNode(id: string, options?: { noPan?: boolean }): Promise<FocusNodeResult> {
792
- const response = await fetch(`${this.remoteBaseUrl}/api/canvas/focus`, {
793
- method: 'POST',
794
- headers: { 'Content-Type': 'application/json' },
795
- body: JSON.stringify({ id, ...(options?.noPan === true ? { noPan: true } : {}) }),
796
- });
797
- if (response.status === 404) return null;
798
- const parsed = await response.json() as { focused?: string; panned?: boolean };
799
- if (!response.ok || typeof parsed.focused !== 'string' || typeof parsed.panned !== 'boolean') return null;
800
- return { focused: parsed.focused, panned: parsed.panned };
801
- }
802
-
803
- async fitView(options?: FitViewOptions): Promise<FitViewResult> {
804
- return await this.requestJson<FitViewResult>('POST', '/api/canvas/fit', options ?? {});
805
- }
806
-
807
- async clear(): Promise<void> {
808
- await this.requestJson<unknown>('POST', '/api/canvas/clear', {});
809
- }
810
-
811
- async search(query: string): Promise<SearchResult> {
812
- const response = await this.requestJson<SearchResponse>('GET', `/api/canvas/search?q=${encodeURIComponent(query)}`);
813
- return response.results ?? [];
814
- }
815
-
816
- async undo(): Promise<UndoRedoResult> {
817
- return await this.requestJson<UndoRedoResult>('POST', '/api/canvas/undo', {});
818
- }
819
-
820
- async redo(): Promise<UndoRedoResult> {
821
- return await this.requestJson<UndoRedoResult>('POST', '/api/canvas/redo', {});
822
- }
823
-
824
346
  async getHistory(): Promise<HistoryResult> {
825
347
  return await this.requestJson<HistoryResult>('GET', '/api/canvas/history');
826
348
  }
@@ -831,35 +353,9 @@ class RemoteCanvasAccess implements CanvasAccess {
831
353
  return response.state;
832
354
  }
833
355
 
834
- async getAxContext(): Promise<AxContextResult> {
835
- return await this.requestJson<AxContextResult>('GET', '/api/canvas/ax/context');
836
- }
837
-
838
- async setAxFocus(nodeIds: string[], options?: { source?: PmxAxSource }): Promise<SetAxFocusResult> {
839
- const response = await this.requestJson<{ focus?: SetAxFocusResult }>('POST', '/api/canvas/ax/focus', {
840
- nodeIds,
841
- source: options?.source ?? 'mcp',
842
- });
843
- if (!response.focus) throw new Error('Remote canvas did not return AX focus.');
844
- return response.focus;
845
- }
846
-
847
- async recordAxEvent(input: RecordAxEventInput, options?: { source?: PmxAxSource }): Promise<RecordAxEventResult> {
848
- const response = await this.requestJson<{ event?: RecordAxEventResult }>('POST', '/api/canvas/ax/event', {
849
- ...input,
850
- source: options?.source ?? 'mcp',
851
- });
852
- if (!response.event) throw new Error('Remote canvas did not return an AX event.');
853
- return response.event;
854
- }
855
-
856
- async sendSteering(message: string, options?: { source?: PmxAxSource }): Promise<SendSteeringResult> {
857
- const response = await this.requestJson<{ steering?: SendSteeringResult }>('POST', '/api/canvas/ax/steer', {
858
- message,
859
- source: options?.source ?? 'mcp',
860
- });
861
- if (!response.steering) throw new Error('Remote canvas did not return a steering message.');
862
- return response.steering;
356
+ async getAxContext(options?: { consumer?: string }): Promise<AxContextResult> {
357
+ const qs = options?.consumer ? `?consumer=${encodeURIComponent(options.consumer)}` : '';
358
+ return await this.requestJson<AxContextResult>('GET', `/api/canvas/ax/context${qs}`);
863
359
  }
864
360
 
865
361
  async getAxTimeline(query?: GetAxTimelineQuery): Promise<GetAxTimelineResult> {
@@ -867,15 +363,6 @@ class RemoteCanvasAccess implements CanvasAccess {
867
363
  return await this.requestJson<GetAxTimelineResult>('GET', `/api/canvas/ax/timeline${qs}`);
868
364
  }
869
365
 
870
- async addWorkItem(input: AddWorkItemInput, options?: { source?: PmxAxSource }): Promise<AddWorkItemResult> {
871
- const response = await this.requestJson<{ workItem?: AddWorkItemResult }>('POST', '/api/canvas/ax/work', {
872
- ...input,
873
- source: options?.source ?? 'mcp',
874
- });
875
- if (!response.workItem) throw new Error('Remote canvas did not return a work item.');
876
- return response.workItem;
877
- }
878
-
879
366
  async submitAxInteraction(input: SubmitAxInteractionInput, options?: { source?: PmxAxSource }): Promise<SubmitAxInteractionResult> {
880
367
  // The interaction endpoint returns its structured outcome (ok/code/error) in
881
368
  // the body for both accepted and rejected interactions, so read the body
@@ -902,77 +389,21 @@ class RemoteCanvasAccess implements CanvasAccess {
902
389
  return response.pending ?? [];
903
390
  }
904
391
 
905
- async markSteeringDelivered(id: string): Promise<boolean> {
906
- const response = await this.requestJson<{ delivered?: boolean }>(
907
- 'POST',
908
- `/api/canvas/ax/delivery/${encodeURIComponent(id)}/mark`,
909
- {},
910
- );
911
- return response.delivered ?? false;
912
- }
913
-
914
392
  async listElicitations(): Promise<ListElicitationsResult> {
915
393
  const r = await this.requestJson<{ elicitations?: ListElicitationsResult }>('GET', '/api/canvas/ax/elicitation');
916
394
  return r.elicitations ?? [];
917
395
  }
918
396
 
919
- async requestElicitation(input: RequestElicitationInput, options?: { source?: PmxAxSource }): Promise<RequestElicitationResult> {
920
- const r = await this.requestJson<{ elicitation?: RequestElicitationResult }>('POST', '/api/canvas/ax/elicitation', {
921
- ...input,
922
- source: options?.source ?? 'mcp',
923
- });
924
- if (!r.elicitation) throw new Error('Remote canvas did not return an elicitation.');
925
- return r.elicitation;
926
- }
927
-
928
- async respondElicitation(id: string, response: Record<string, unknown>, options?: { source?: PmxAxSource }): Promise<RespondElicitationResult> {
929
- const res = await fetch(`${this.remoteBaseUrl}/api/canvas/ax/elicitation/${encodeURIComponent(id)}/respond`, {
930
- method: 'POST',
931
- headers: { 'Content-Type': 'application/json' },
932
- body: JSON.stringify({ response, source: options?.source ?? 'mcp' }),
933
- });
934
- if (res.status === 404) return null;
935
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
936
- return (await res.json() as { elicitation?: RespondElicitationResult }).elicitation ?? null;
937
- }
938
-
939
397
  async listModeRequests(): Promise<ListModeRequestsResult> {
940
398
  const r = await this.requestJson<{ modeRequests?: ListModeRequestsResult }>('GET', '/api/canvas/ax/mode');
941
399
  return r.modeRequests ?? [];
942
400
  }
943
401
 
944
- async requestMode(input: RequestModeInput, options?: { source?: PmxAxSource }): Promise<RequestModeResult> {
945
- const r = await this.requestJson<{ modeRequest?: RequestModeResult }>('POST', '/api/canvas/ax/mode', {
402
+ async ingestActivity(input: IngestActivityInput, options?: { source?: PmxAxSource }): Promise<IngestActivityResult> {
403
+ return await this.requestJson<IngestActivityResult>('POST', '/api/canvas/ax/activity', {
946
404
  ...input,
947
405
  source: options?.source ?? 'mcp',
948
406
  });
949
- if (!r.modeRequest) throw new Error('Remote canvas did not return a mode request.');
950
- return r.modeRequest;
951
- }
952
-
953
- async resolveModeRequest(id: string, decision: 'approved' | 'rejected', options?: { resolution?: string; source?: PmxAxSource }): Promise<ResolveModeRequestResult> {
954
- const res = await fetch(`${this.remoteBaseUrl}/api/canvas/ax/mode/${encodeURIComponent(id)}/resolve`, {
955
- method: 'POST',
956
- headers: { 'Content-Type': 'application/json' },
957
- body: JSON.stringify({ decision, ...(options?.resolution ? { resolution: options.resolution } : {}), source: options?.source ?? 'mcp' }),
958
- });
959
- if (res.status === 404) return null;
960
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
961
- return (await res.json() as { modeRequest?: ResolveModeRequestResult }).modeRequest ?? null;
962
- }
963
-
964
- async getCommandRegistry(): Promise<GetCommandRegistryResult> {
965
- const r = await this.requestJson<{ commands?: GetCommandRegistryResult }>('GET', '/api/canvas/ax/command');
966
- return r.commands ?? [];
967
- }
968
-
969
- async invokeCommand(name: string, args?: Record<string, unknown> | null, options?: { source?: PmxAxSource }): Promise<InvokeCommandResult> {
970
- const r = await this.requestJson<{ event?: InvokeCommandResult }>('POST', '/api/canvas/ax/command', {
971
- name,
972
- ...(args ? { args } : {}),
973
- source: options?.source ?? 'mcp',
974
- });
975
- return r.event ?? null;
976
407
  }
977
408
 
978
409
  async getPolicy(): Promise<GetPolicyResult> {
@@ -981,125 +412,21 @@ class RemoteCanvasAccess implements CanvasAccess {
981
412
  return r.policy;
982
413
  }
983
414
 
984
- async setPolicy(patch: SetPolicyInput, options?: { source?: PmxAxSource }): Promise<SetPolicyResult> {
985
- const r = await this.requestJson<{ policy?: SetPolicyResult }>('POST', '/api/canvas/ax/policy', {
986
- ...patch,
987
- source: options?.source ?? 'mcp',
988
- });
989
- if (!r.policy) throw new Error('Remote canvas did not return a policy.');
990
- return r.policy;
991
- }
992
-
993
- async updateWorkItem(id: string, patch: UpdateWorkItemPatch, options?: { source?: PmxAxSource }): Promise<UpdateWorkItemResult> {
994
- const response = await fetch(`${this.remoteBaseUrl}/api/canvas/ax/work/${encodeURIComponent(id)}`, {
995
- method: 'PATCH',
996
- headers: { 'Content-Type': 'application/json' },
997
- body: JSON.stringify({ ...patch, source: options?.source ?? 'mcp' }),
998
- });
999
- if (response.status === 404) return null;
1000
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
1001
- return (await response.json() as { workItem?: AddWorkItemResult }).workItem ?? null;
1002
- }
1003
-
1004
415
  async listWorkItems(): Promise<ListWorkItemsResult> {
1005
416
  const response = await this.requestJson<{ workItems?: ListWorkItemsResult }>('GET', '/api/canvas/ax/work');
1006
417
  return response.workItems ?? [];
1007
418
  }
1008
419
 
1009
- async requestApproval(input: RequestApprovalInput, options?: { source?: PmxAxSource }): Promise<RequestApprovalResult> {
1010
- const response = await this.requestJson<{ approvalGate?: RequestApprovalResult }>('POST', '/api/canvas/ax/approval', {
1011
- ...input,
1012
- source: options?.source ?? 'mcp',
1013
- });
1014
- if (!response.approvalGate) throw new Error('Remote canvas did not return an approval gate.');
1015
- return response.approvalGate;
1016
- }
1017
-
1018
- async resolveApproval(id: string, decision: 'approved' | 'rejected', options?: { resolution?: string; source?: PmxAxSource }): Promise<ResolveApprovalResult> {
1019
- const response = await fetch(`${this.remoteBaseUrl}/api/canvas/ax/approval/${encodeURIComponent(id)}/resolve`, {
1020
- method: 'POST',
1021
- headers: { 'Content-Type': 'application/json' },
1022
- body: JSON.stringify({
1023
- decision,
1024
- ...(options?.resolution !== undefined ? { resolution: options.resolution } : {}),
1025
- source: options?.source ?? 'mcp',
1026
- }),
1027
- });
1028
- if (response.status === 404) return null;
1029
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
1030
- return (await response.json() as { approvalGate?: RequestApprovalResult }).approvalGate ?? null;
1031
- }
1032
-
1033
420
  async listApprovalGates(): Promise<ListApprovalGatesResult> {
1034
421
  const response = await this.requestJson<{ approvalGates?: ListApprovalGatesResult }>('GET', '/api/canvas/ax/approval');
1035
422
  return response.approvalGates ?? [];
1036
423
  }
1037
424
 
1038
- async addEvidence(input: AddEvidenceInput, options?: { source?: PmxAxSource }): Promise<AddEvidenceResult> {
1039
- const response = await this.requestJson<{ evidence?: AddEvidenceResult }>('POST', '/api/canvas/ax/evidence', {
1040
- ...input,
1041
- source: options?.source ?? 'mcp',
1042
- });
1043
- if (!response.evidence) throw new Error('Remote canvas did not return an evidence item.');
1044
- return response.evidence;
1045
- }
1046
-
1047
- async addReviewAnnotation(input: AddReviewAnnotationInput, options?: { source?: PmxAxSource }): Promise<AddReviewAnnotationResult> {
1048
- const response = await fetch(`${this.remoteBaseUrl}/api/canvas/ax/review`, {
1049
- method: 'POST',
1050
- headers: { 'Content-Type': 'application/json' },
1051
- body: JSON.stringify({ ...input, source: options?.source ?? 'mcp' }),
1052
- });
1053
- // 400 = validation rejection (e.g. node-anchored review with an unknown
1054
- // nodeId); mirror the local path and return null rather than throwing.
1055
- if (response.status === 400) return null;
1056
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
1057
- return (await response.json() as { reviewAnnotation?: AddReviewAnnotationResult }).reviewAnnotation ?? null;
1058
- }
1059
-
1060
- async updateReviewAnnotation(id: string, patch: UpdateReviewAnnotationPatch, options?: { source?: PmxAxSource }): Promise<UpdateReviewAnnotationResult> {
1061
- const response = await fetch(`${this.remoteBaseUrl}/api/canvas/ax/review/${encodeURIComponent(id)}`, {
1062
- method: 'PATCH',
1063
- headers: { 'Content-Type': 'application/json' },
1064
- body: JSON.stringify({ ...patch, source: options?.source ?? 'mcp' }),
1065
- });
1066
- if (response.status === 404) return null;
1067
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
1068
- return (await response.json() as { reviewAnnotation?: AddReviewAnnotationResult }).reviewAnnotation ?? null;
1069
- }
1070
-
1071
425
  async listReviewAnnotations(): Promise<ListReviewAnnotationsResult> {
1072
426
  const response = await this.requestJson<{ reviewAnnotations?: ListReviewAnnotationsResult }>('GET', '/api/canvas/ax/review');
1073
427
  return response.reviewAnnotations ?? [];
1074
428
  }
1075
429
 
1076
- async getHostCapability(): Promise<GetHostCapabilityResult> {
1077
- const response = await this.requestJson<{ host?: GetHostCapabilityResult }>('GET', '/api/canvas/ax/host-capability');
1078
- return response.host ?? null;
1079
- }
1080
-
1081
- async reportHostCapability(input: unknown, options?: { source?: PmxAxSource }): Promise<ReportHostCapabilityResult> {
1082
- const body = input !== null && typeof input === 'object' && !Array.isArray(input) ? { ...input } : {};
1083
- const response = await this.requestJson<{ host?: ReportHostCapabilityResult }>('PUT', '/api/canvas/ax/host-capability', {
1084
- ...body,
1085
- source: options?.source ?? 'mcp',
1086
- });
1087
- if (!response.host) throw new Error('Remote canvas did not return host capability.');
1088
- return response.host;
1089
- }
1090
-
1091
- async setContextPins(nodeIds: string[], mode: 'set' | 'add' | 'remove' = 'set'): Promise<SetContextPinsResult> {
1092
- const existing = mode === 'set' ? [] : await this.getPinnedNodeIds();
1093
- const requested = new Set(nodeIds);
1094
- const next = mode === 'set'
1095
- ? nodeIds
1096
- : mode === 'add'
1097
- ? [...new Set([...existing, ...nodeIds])]
1098
- : existing.filter((id) => !requested.has(id));
1099
- const response = await this.requestJson<{ count?: number }>('POST', '/api/canvas/context-pins', { nodeIds: next });
1100
- return { count: response.count ?? next.length, nodeIds: next };
1101
- }
1102
-
1103
430
  async getPinnedNodeIds(): Promise<string[]> {
1104
431
  const response = await this.requestJson<{ nodeIds?: string[] }>('GET', '/api/canvas/pinned-context');
1105
432
  return Array.isArray(response.nodeIds) ? response.nodeIds : [];
@@ -1109,73 +436,15 @@ class RemoteCanvasAccess implements CanvasAccess {
1109
436
  return await this.requestJson<RunBatchResult>('POST', '/api/canvas/batch', { operations });
1110
437
  }
1111
438
 
1112
- async listSnapshots(options?: SnapshotListOptions): Promise<SnapshotList> {
1113
- const params = new URLSearchParams();
1114
- if (typeof options?.limit === 'number') params.set('limit', String(options.limit));
1115
- if (options?.query) params.set('q', options.query);
1116
- if (options?.before) params.set('before', options.before);
1117
- if (options?.after) params.set('after', options.after);
1118
- if (options?.all) params.set('all', 'true');
1119
- const query = params.size > 0 ? `?${params.toString()}` : '';
1120
- return await this.requestJson<SnapshotList>('GET', `/api/canvas/snapshots${query}`);
1121
- }
1122
-
1123
- async saveSnapshot(name: string): Promise<CanvasSnapshot | null> {
1124
- const response = await this.requestJson<SnapshotSaveResponse>('POST', '/api/canvas/snapshots', { name });
1125
- return response.snapshot ?? null;
1126
- }
1127
-
1128
- async restoreSnapshot(id: string): Promise<{ ok: boolean }> {
1129
- return await this.requestJson<{ ok: boolean }>('POST', `/api/canvas/snapshots/${encodeURIComponent(id)}`, {});
1130
- }
1131
-
1132
- async deleteSnapshot(id: string): Promise<DeleteSnapshotResult> {
1133
- return await this.requestJson<DeleteSnapshotResult>('DELETE', `/api/canvas/snapshots/${encodeURIComponent(id)}`);
1134
- }
1135
-
1136
- async gcSnapshots(options?: GcSnapshotsOptions): Promise<GcSnapshotsResult> {
1137
- return await this.requestJson<GcSnapshotsResult>('POST', '/api/canvas/snapshots/gc', options ?? {});
1138
- }
1139
-
1140
- async diffSnapshot(idOrName: string): Promise<DiffSnapshotResult> {
1141
- return await this.requestJson<DiffSnapshotResult>('GET', `/api/canvas/snapshots/${encodeURIComponent(idOrName)}/diff`);
1142
- }
1143
-
1144
439
  async getCodeGraph(): Promise<CodeGraphResult> {
1145
440
  const summary = await this.requestJson<CodeGraphResult['summary']>('GET', '/api/canvas/code-graph');
1146
441
  return { text: JSON.stringify(summary, null, 2), summary };
1147
442
  }
1148
443
 
1149
- async validate(): Promise<ValidationResult> {
1150
- return await this.requestJson<ValidationResult>('GET', '/api/canvas/validate');
1151
- }
1152
-
1153
444
  async getAutomationWebViewStatus(): Promise<AutomationWebViewStatus> {
1154
445
  return await this.requestJson<AutomationWebViewStatus>('GET', '/api/workbench/webview');
1155
446
  }
1156
447
 
1157
- async startAutomationWebView(options: AutomationWebViewOptions = {}): Promise<AutomationWebViewStatus> {
1158
- const response = await this.requestJson<WebViewEnvelope>('POST', '/api/workbench/webview/start', options);
1159
- if (!response.webview) throw new Error('WebView start response did not include status.');
1160
- return response.webview;
1161
- }
1162
-
1163
- async stopAutomationWebView(): Promise<boolean> {
1164
- const response = await this.requestJson<WebViewStopEnvelope>('DELETE', '/api/workbench/webview');
1165
- return response.stopped === true;
1166
- }
1167
-
1168
- async evaluateAutomationWebView(expression: string): Promise<AutomationEvaluateResult> {
1169
- const response = await this.requestJson<WebViewEvaluateEnvelope>('POST', '/api/workbench/webview/evaluate', { expression });
1170
- return response.value as AutomationEvaluateResult;
1171
- }
1172
-
1173
- async resizeAutomationWebView(width: number, height: number): Promise<AutomationWebViewStatus> {
1174
- const response = await this.requestJson<WebViewEnvelope>('POST', '/api/workbench/webview/resize', { width, height });
1175
- if (!response.webview) throw new Error('WebView resize response did not include status.');
1176
- return response.webview;
1177
- }
1178
-
1179
448
  async screenshotAutomationWebView(options: AutomationScreenshotOptions = {}): Promise<Uint8Array> {
1180
449
  const response = await fetch(`${this.remoteBaseUrl}/api/workbench/webview/screenshot`, {
1181
450
  method: 'POST',