pmx-canvas 0.1.26 → 0.1.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/extensions/pmx-canvas/extension.mjs +191 -0
- package/CHANGELOG.md +110 -0
- package/Readme.md +74 -27
- package/dist/canvas/index.js +82 -82
- package/dist/json-render/index.css +1 -1
- package/dist/json-render/index.js +944 -164
- package/dist/types/json-render/catalog.d.ts +195 -20
- package/dist/types/json-render/charts/components.d.ts +17 -0
- package/dist/types/json-render/charts/definitions.d.ts +13 -1
- package/dist/types/json-render/charts/tufte-components.d.ts +65 -0
- package/dist/types/json-render/charts/tufte-definitions.d.ts +164 -0
- package/dist/types/json-render/directives.d.ts +33 -0
- package/dist/types/json-render/renderer/index.d.ts +1 -0
- package/dist/types/json-render/server.d.ts +32 -1
- package/dist/types/mcp/canvas-access.d.ts +62 -0
- package/dist/types/server/ax-state.d.ts +170 -0
- package/dist/types/server/canvas-db.d.ts +17 -1
- package/dist/types/server/canvas-operations.d.ts +53 -0
- package/dist/types/server/canvas-schema.d.ts +5 -1
- package/dist/types/server/canvas-state.d.ts +95 -4
- package/dist/types/server/index.d.ts +120 -3
- package/dist/types/server/mutation-history.d.ts +1 -1
- package/docs/cli.md +42 -0
- package/docs/http-api.md +64 -0
- package/docs/mcp.md +23 -5
- package/docs/node-types.md +1 -1
- package/docs/screenshots/codex-app.png +0 -0
- package/docs/screenshots/github-copilot-app.png +0 -0
- package/docs/sdk.md +23 -5
- package/package.json +10 -7
- package/skills/control-session-orchestrator/SKILL.md +359 -0
- package/skills/control-session-orchestrator/evals/evals.json +75 -0
- package/skills/data-analysis/SKILL.md +6 -0
- package/skills/pmx-canvas/SKILL.md +50 -4
- package/skills/pmx-canvas/references/github-copilot-app-adapter.md +6 -0
- package/skills/tufte-viz/SKILL.md +157 -0
- package/skills/tufte-viz/references/analytical-design.md +217 -0
- package/skills/tufte-viz/references/tufte-principles.md +147 -0
- package/src/cli/agent.ts +302 -3
- package/src/cli/index.ts +2 -1
- package/src/client/nodes/ExtAppFrame.tsx +48 -1
- package/src/client/nodes/McpAppNode.tsx +6 -2
- package/src/json-render/catalog.ts +22 -1
- package/src/json-render/charts/components.tsx +127 -15
- package/src/json-render/charts/definitions.ts +19 -2
- package/src/json-render/charts/extra-components.tsx +5 -4
- package/src/json-render/charts/tufte-components.tsx +395 -0
- package/src/json-render/charts/tufte-definitions.ts +128 -0
- package/src/json-render/directives.ts +64 -0
- package/src/json-render/renderer/index.css +107 -1
- package/src/json-render/renderer/index.tsx +33 -0
- package/src/json-render/server.ts +275 -5
- package/src/mcp/canvas-access.ts +264 -1
- package/src/mcp/server.ts +498 -9
- package/src/server/ax-context.ts +8 -3
- package/src/server/ax-state.ts +447 -0
- package/src/server/canvas-db.ts +184 -1
- package/src/server/canvas-operations.ts +123 -2
- package/src/server/canvas-schema.ts +27 -3
- package/src/server/canvas-state.ts +349 -2
- package/src/server/index.ts +259 -7
- package/src/server/mutation-history.ts +6 -0
- package/src/server/server.ts +442 -5
- package/src/server/web-artifacts.ts +31 -5
package/src/mcp/canvas-access.ts
CHANGED
|
@@ -19,6 +19,8 @@ type OpenMcpAppResult = Awaited<ReturnType<PmxCanvas['openMcpApp']>>;
|
|
|
19
19
|
type AddDiagramInput = Parameters<PmxCanvas['addDiagram']>[0];
|
|
20
20
|
type AddJsonRenderNodeInput = Parameters<PmxCanvas['addJsonRenderNode']>[0];
|
|
21
21
|
type AddJsonRenderNodeResult = ReturnType<PmxCanvas['addJsonRenderNode']>;
|
|
22
|
+
type StreamJsonRenderNodeInput = Parameters<PmxCanvas['streamJsonRenderNode']>[0];
|
|
23
|
+
type StreamJsonRenderNodeResult = ReturnType<PmxCanvas['streamJsonRenderNode']>;
|
|
22
24
|
type AddHtmlNodeInput = Parameters<PmxCanvas['addHtmlNode']>[0];
|
|
23
25
|
type AddHtmlPrimitiveInput = Parameters<PmxCanvas['addHtmlPrimitive']>[0];
|
|
24
26
|
type AddHtmlPrimitiveResult = ReturnType<PmxCanvas['addHtmlPrimitive']>;
|
|
@@ -35,6 +37,29 @@ type FitViewResult = ReturnType<PmxCanvas['fitView']>;
|
|
|
35
37
|
type AxStateResult = ReturnType<PmxCanvas['getAxState']>;
|
|
36
38
|
type AxContextResult = ReturnType<PmxCanvas['getAxContext']>;
|
|
37
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
|
+
type GetAxTimelineQuery = Parameters<PmxCanvas['getAxTimeline']>[0];
|
|
44
|
+
type GetAxTimelineResult = ReturnType<PmxCanvas['getAxTimeline']>;
|
|
45
|
+
type AddWorkItemInput = Parameters<PmxCanvas['addWorkItem']>[0];
|
|
46
|
+
type AddWorkItemResult = ReturnType<PmxCanvas['addWorkItem']>;
|
|
47
|
+
type UpdateWorkItemPatch = Parameters<PmxCanvas['updateWorkItem']>[1];
|
|
48
|
+
type UpdateWorkItemResult = ReturnType<PmxCanvas['updateWorkItem']>;
|
|
49
|
+
type ListWorkItemsResult = ReturnType<PmxCanvas['listWorkItems']>;
|
|
50
|
+
type RequestApprovalInput = Parameters<PmxCanvas['requestApproval']>[0];
|
|
51
|
+
type RequestApprovalResult = ReturnType<PmxCanvas['requestApproval']>;
|
|
52
|
+
type ResolveApprovalResult = ReturnType<PmxCanvas['resolveApproval']>;
|
|
53
|
+
type ListApprovalGatesResult = ReturnType<PmxCanvas['listApprovalGates']>;
|
|
54
|
+
type AddEvidenceInput = Parameters<PmxCanvas['addEvidence']>[0];
|
|
55
|
+
type AddEvidenceResult = ReturnType<PmxCanvas['addEvidence']>;
|
|
56
|
+
type AddReviewAnnotationInput = Parameters<PmxCanvas['addReviewAnnotation']>[0];
|
|
57
|
+
type AddReviewAnnotationResult = ReturnType<PmxCanvas['addReviewAnnotation']>;
|
|
58
|
+
type UpdateReviewAnnotationPatch = Parameters<PmxCanvas['updateReviewAnnotation']>[1];
|
|
59
|
+
type UpdateReviewAnnotationResult = ReturnType<PmxCanvas['updateReviewAnnotation']>;
|
|
60
|
+
type ListReviewAnnotationsResult = ReturnType<PmxCanvas['listReviewAnnotations']>;
|
|
61
|
+
type GetHostCapabilityResult = ReturnType<PmxCanvas['getHostCapability']>;
|
|
62
|
+
type ReportHostCapabilityResult = ReturnType<PmxCanvas['reportHostCapability']>;
|
|
38
63
|
type SearchResult = ReturnType<PmxCanvas['search']>;
|
|
39
64
|
type UndoRedoResult = Awaited<ReturnType<PmxCanvas['undo']>>;
|
|
40
65
|
type HistoryResult = ReturnType<PmxCanvas['getHistory']>;
|
|
@@ -107,6 +132,7 @@ export interface CanvasAccess {
|
|
|
107
132
|
openMcpApp(input: OpenMcpAppInput): Promise<OpenMcpAppResult>;
|
|
108
133
|
addDiagram(input: AddDiagramInput): Promise<OpenMcpAppResult>;
|
|
109
134
|
addJsonRenderNode(input: AddJsonRenderNodeInput): Promise<AddJsonRenderNodeResult>;
|
|
135
|
+
streamJsonRenderNode(input: StreamJsonRenderNodeInput): Promise<StreamJsonRenderNodeResult>;
|
|
110
136
|
addHtmlNode(input: AddHtmlNodeInput): Promise<string>;
|
|
111
137
|
addHtmlPrimitive(input: AddHtmlPrimitiveInput): Promise<AddHtmlPrimitiveResult>;
|
|
112
138
|
addGraphNode(input: AddGraphNodeInput): Promise<AddGraphNodeResult>;
|
|
@@ -125,6 +151,21 @@ export interface CanvasAccess {
|
|
|
125
151
|
getAxState(): Promise<AxStateResult>;
|
|
126
152
|
getAxContext(): Promise<AxContextResult>;
|
|
127
153
|
setAxFocus(nodeIds: string[], options?: { source?: PmxAxSource }): Promise<SetAxFocusResult>;
|
|
154
|
+
recordAxEvent(input: RecordAxEventInput, options?: { source?: PmxAxSource }): Promise<RecordAxEventResult>;
|
|
155
|
+
sendSteering(message: string, options?: { source?: PmxAxSource }): Promise<SendSteeringResult>;
|
|
156
|
+
getAxTimeline(query?: GetAxTimelineQuery): Promise<GetAxTimelineResult>;
|
|
157
|
+
addWorkItem(input: AddWorkItemInput, options?: { source?: PmxAxSource }): Promise<AddWorkItemResult>;
|
|
158
|
+
updateWorkItem(id: string, patch: UpdateWorkItemPatch, options?: { source?: PmxAxSource }): Promise<UpdateWorkItemResult>;
|
|
159
|
+
listWorkItems(): Promise<ListWorkItemsResult>;
|
|
160
|
+
requestApproval(input: RequestApprovalInput, options?: { source?: PmxAxSource }): Promise<RequestApprovalResult>;
|
|
161
|
+
resolveApproval(id: string, decision: 'approved' | 'rejected', options?: { resolution?: string; source?: PmxAxSource }): Promise<ResolveApprovalResult>;
|
|
162
|
+
listApprovalGates(): Promise<ListApprovalGatesResult>;
|
|
163
|
+
addEvidence(input: AddEvidenceInput, options?: { source?: PmxAxSource }): Promise<AddEvidenceResult>;
|
|
164
|
+
addReviewAnnotation(input: AddReviewAnnotationInput, options?: { source?: PmxAxSource }): Promise<AddReviewAnnotationResult>;
|
|
165
|
+
updateReviewAnnotation(id: string, patch: UpdateReviewAnnotationPatch, options?: { source?: PmxAxSource }): Promise<UpdateReviewAnnotationResult>;
|
|
166
|
+
listReviewAnnotations(): Promise<ListReviewAnnotationsResult>;
|
|
167
|
+
getHostCapability(): Promise<GetHostCapabilityResult>;
|
|
168
|
+
reportHostCapability(input: unknown, options?: { source?: PmxAxSource }): Promise<ReportHostCapabilityResult>;
|
|
128
169
|
clear(): Promise<void>;
|
|
129
170
|
search(query: string): Promise<SearchResult>;
|
|
130
171
|
undo(): Promise<UndoRedoResult>;
|
|
@@ -171,7 +212,9 @@ class LocalCanvasAccess implements CanvasAccess {
|
|
|
171
212
|
}
|
|
172
213
|
|
|
173
214
|
async addNode(input: AddNodeInput): Promise<string> {
|
|
174
|
-
|
|
215
|
+
// PmxCanvas.addNode returns the created node; the CanvasAccess contract
|
|
216
|
+
// (shared with the remote proxy + MCP) stays id-only.
|
|
217
|
+
return this.canvas.addNode(input).id;
|
|
175
218
|
}
|
|
176
219
|
|
|
177
220
|
async addWebpageNode(input: AddWebpageNodeInput): Promise<Awaited<ReturnType<PmxCanvas['addWebpageNode']>>> {
|
|
@@ -194,6 +237,10 @@ class LocalCanvasAccess implements CanvasAccess {
|
|
|
194
237
|
return this.canvas.addJsonRenderNode(input);
|
|
195
238
|
}
|
|
196
239
|
|
|
240
|
+
async streamJsonRenderNode(input: StreamJsonRenderNodeInput): Promise<StreamJsonRenderNodeResult> {
|
|
241
|
+
return this.canvas.streamJsonRenderNode(input);
|
|
242
|
+
}
|
|
243
|
+
|
|
197
244
|
async addHtmlNode(input: AddHtmlNodeInput): Promise<string> {
|
|
198
245
|
return this.canvas.addHtmlNode(input);
|
|
199
246
|
}
|
|
@@ -266,6 +313,69 @@ class LocalCanvasAccess implements CanvasAccess {
|
|
|
266
313
|
return this.canvas.setAxFocus(nodeIds, { source: options?.source ?? 'mcp' });
|
|
267
314
|
}
|
|
268
315
|
|
|
316
|
+
async recordAxEvent(input: RecordAxEventInput, options?: { source?: PmxAxSource }): Promise<RecordAxEventResult> {
|
|
317
|
+
return this.canvas.recordAxEvent(input, { source: options?.source ?? 'mcp' });
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
async sendSteering(message: string, options?: { source?: PmxAxSource }): Promise<SendSteeringResult> {
|
|
321
|
+
return this.canvas.sendSteering(message, { source: options?.source ?? 'mcp' });
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
async getAxTimeline(query?: GetAxTimelineQuery): Promise<GetAxTimelineResult> {
|
|
325
|
+
return this.canvas.getAxTimeline(query);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
async addWorkItem(input: AddWorkItemInput, options?: { source?: PmxAxSource }): Promise<AddWorkItemResult> {
|
|
329
|
+
return this.canvas.addWorkItem(input, { source: options?.source ?? 'mcp' });
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
async updateWorkItem(id: string, patch: UpdateWorkItemPatch, options?: { source?: PmxAxSource }): Promise<UpdateWorkItemResult> {
|
|
333
|
+
return this.canvas.updateWorkItem(id, patch, { source: options?.source ?? 'mcp' });
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
async listWorkItems(): Promise<ListWorkItemsResult> {
|
|
337
|
+
return this.canvas.listWorkItems();
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
async requestApproval(input: RequestApprovalInput, options?: { source?: PmxAxSource }): Promise<RequestApprovalResult> {
|
|
341
|
+
return this.canvas.requestApproval(input, { source: options?.source ?? 'mcp' });
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
async resolveApproval(id: string, decision: 'approved' | 'rejected', options?: { resolution?: string; source?: PmxAxSource }): Promise<ResolveApprovalResult> {
|
|
345
|
+
return this.canvas.resolveApproval(id, decision, {
|
|
346
|
+
...(options?.resolution !== undefined ? { resolution: options.resolution } : {}),
|
|
347
|
+
source: options?.source ?? 'mcp',
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
async listApprovalGates(): Promise<ListApprovalGatesResult> {
|
|
352
|
+
return this.canvas.listApprovalGates();
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
async addEvidence(input: AddEvidenceInput, options?: { source?: PmxAxSource }): Promise<AddEvidenceResult> {
|
|
356
|
+
return this.canvas.addEvidence(input, { source: options?.source ?? 'mcp' });
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
async addReviewAnnotation(input: AddReviewAnnotationInput, options?: { source?: PmxAxSource }): Promise<AddReviewAnnotationResult> {
|
|
360
|
+
return this.canvas.addReviewAnnotation(input, { source: options?.source ?? 'mcp' });
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async updateReviewAnnotation(id: string, patch: UpdateReviewAnnotationPatch, options?: { source?: PmxAxSource }): Promise<UpdateReviewAnnotationResult> {
|
|
364
|
+
return this.canvas.updateReviewAnnotation(id, patch, { source: options?.source ?? 'mcp' });
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
async listReviewAnnotations(): Promise<ListReviewAnnotationsResult> {
|
|
368
|
+
return this.canvas.listReviewAnnotations();
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
async getHostCapability(): Promise<GetHostCapabilityResult> {
|
|
372
|
+
return this.canvas.getHostCapability();
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
async reportHostCapability(input: unknown, options?: { source?: PmxAxSource }): Promise<ReportHostCapabilityResult> {
|
|
376
|
+
return this.canvas.reportHostCapability(input, { source: options?.source ?? 'mcp' });
|
|
377
|
+
}
|
|
378
|
+
|
|
269
379
|
async clear(): Promise<void> {
|
|
270
380
|
this.canvas.clear();
|
|
271
381
|
}
|
|
@@ -460,6 +570,29 @@ class RemoteCanvasAccess implements CanvasAccess {
|
|
|
460
570
|
return { id, url: response.url, spec: response.spec };
|
|
461
571
|
}
|
|
462
572
|
|
|
573
|
+
async streamJsonRenderNode(input: StreamJsonRenderNodeInput): Promise<StreamJsonRenderNodeResult> {
|
|
574
|
+
const response = await this.requestJson<{
|
|
575
|
+
id?: string;
|
|
576
|
+
url?: string;
|
|
577
|
+
applied?: number;
|
|
578
|
+
skipped?: number;
|
|
579
|
+
specVersion?: number;
|
|
580
|
+
elementCount?: number;
|
|
581
|
+
streamStatus?: 'open' | 'closed';
|
|
582
|
+
}>('POST', '/api/canvas/json-render/stream', input);
|
|
583
|
+
const id = typeof response.id === 'string' ? response.id : undefined;
|
|
584
|
+
if (!id) throw new Error('json-render stream response did not include a node id.');
|
|
585
|
+
return {
|
|
586
|
+
id,
|
|
587
|
+
url: response.url ?? '',
|
|
588
|
+
applied: response.applied ?? 0,
|
|
589
|
+
skipped: response.skipped ?? 0,
|
|
590
|
+
specVersion: response.specVersion ?? 0,
|
|
591
|
+
elementCount: response.elementCount ?? 0,
|
|
592
|
+
streamStatus: response.streamStatus ?? 'open',
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
|
|
463
596
|
async addHtmlNode(input: AddHtmlNodeInput): Promise<string> {
|
|
464
597
|
const {
|
|
465
598
|
summary,
|
|
@@ -625,6 +758,136 @@ class RemoteCanvasAccess implements CanvasAccess {
|
|
|
625
758
|
return response.focus;
|
|
626
759
|
}
|
|
627
760
|
|
|
761
|
+
async recordAxEvent(input: RecordAxEventInput, options?: { source?: PmxAxSource }): Promise<RecordAxEventResult> {
|
|
762
|
+
const response = await this.requestJson<{ event?: RecordAxEventResult }>('POST', '/api/canvas/ax/event', {
|
|
763
|
+
...input,
|
|
764
|
+
source: options?.source ?? 'mcp',
|
|
765
|
+
});
|
|
766
|
+
if (!response.event) throw new Error('Remote canvas did not return an AX event.');
|
|
767
|
+
return response.event;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
async sendSteering(message: string, options?: { source?: PmxAxSource }): Promise<SendSteeringResult> {
|
|
771
|
+
const response = await this.requestJson<{ steering?: SendSteeringResult }>('POST', '/api/canvas/ax/steer', {
|
|
772
|
+
message,
|
|
773
|
+
source: options?.source ?? 'mcp',
|
|
774
|
+
});
|
|
775
|
+
if (!response.steering) throw new Error('Remote canvas did not return a steering message.');
|
|
776
|
+
return response.steering;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
async getAxTimeline(query?: GetAxTimelineQuery): Promise<GetAxTimelineResult> {
|
|
780
|
+
const qs = query?.limit ? `?limit=${query.limit}` : '';
|
|
781
|
+
return await this.requestJson<GetAxTimelineResult>('GET', `/api/canvas/ax/timeline${qs}`);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
async addWorkItem(input: AddWorkItemInput, options?: { source?: PmxAxSource }): Promise<AddWorkItemResult> {
|
|
785
|
+
const response = await this.requestJson<{ workItem?: AddWorkItemResult }>('POST', '/api/canvas/ax/work', {
|
|
786
|
+
...input,
|
|
787
|
+
source: options?.source ?? 'mcp',
|
|
788
|
+
});
|
|
789
|
+
if (!response.workItem) throw new Error('Remote canvas did not return a work item.');
|
|
790
|
+
return response.workItem;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
async updateWorkItem(id: string, patch: UpdateWorkItemPatch, options?: { source?: PmxAxSource }): Promise<UpdateWorkItemResult> {
|
|
794
|
+
const response = await fetch(`${this.remoteBaseUrl}/api/canvas/ax/work/${encodeURIComponent(id)}`, {
|
|
795
|
+
method: 'PATCH',
|
|
796
|
+
headers: { 'Content-Type': 'application/json' },
|
|
797
|
+
body: JSON.stringify({ ...patch, source: options?.source ?? 'mcp' }),
|
|
798
|
+
});
|
|
799
|
+
if (response.status === 404) return null;
|
|
800
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
801
|
+
return (await response.json() as { workItem?: AddWorkItemResult }).workItem ?? null;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
async listWorkItems(): Promise<ListWorkItemsResult> {
|
|
805
|
+
const response = await this.requestJson<{ workItems?: ListWorkItemsResult }>('GET', '/api/canvas/ax/work');
|
|
806
|
+
return response.workItems ?? [];
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
async requestApproval(input: RequestApprovalInput, options?: { source?: PmxAxSource }): Promise<RequestApprovalResult> {
|
|
810
|
+
const response = await this.requestJson<{ approvalGate?: RequestApprovalResult }>('POST', '/api/canvas/ax/approval', {
|
|
811
|
+
...input,
|
|
812
|
+
source: options?.source ?? 'mcp',
|
|
813
|
+
});
|
|
814
|
+
if (!response.approvalGate) throw new Error('Remote canvas did not return an approval gate.');
|
|
815
|
+
return response.approvalGate;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
async resolveApproval(id: string, decision: 'approved' | 'rejected', options?: { resolution?: string; source?: PmxAxSource }): Promise<ResolveApprovalResult> {
|
|
819
|
+
const response = await fetch(`${this.remoteBaseUrl}/api/canvas/ax/approval/${encodeURIComponent(id)}/resolve`, {
|
|
820
|
+
method: 'POST',
|
|
821
|
+
headers: { 'Content-Type': 'application/json' },
|
|
822
|
+
body: JSON.stringify({
|
|
823
|
+
decision,
|
|
824
|
+
...(options?.resolution !== undefined ? { resolution: options.resolution } : {}),
|
|
825
|
+
source: options?.source ?? 'mcp',
|
|
826
|
+
}),
|
|
827
|
+
});
|
|
828
|
+
if (response.status === 404) return null;
|
|
829
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
830
|
+
return (await response.json() as { approvalGate?: RequestApprovalResult }).approvalGate ?? null;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
async listApprovalGates(): Promise<ListApprovalGatesResult> {
|
|
834
|
+
const response = await this.requestJson<{ approvalGates?: ListApprovalGatesResult }>('GET', '/api/canvas/ax/approval');
|
|
835
|
+
return response.approvalGates ?? [];
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
async addEvidence(input: AddEvidenceInput, options?: { source?: PmxAxSource }): Promise<AddEvidenceResult> {
|
|
839
|
+
const response = await this.requestJson<{ evidence?: AddEvidenceResult }>('POST', '/api/canvas/ax/evidence', {
|
|
840
|
+
...input,
|
|
841
|
+
source: options?.source ?? 'mcp',
|
|
842
|
+
});
|
|
843
|
+
if (!response.evidence) throw new Error('Remote canvas did not return an evidence item.');
|
|
844
|
+
return response.evidence;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
async addReviewAnnotation(input: AddReviewAnnotationInput, options?: { source?: PmxAxSource }): Promise<AddReviewAnnotationResult> {
|
|
848
|
+
const response = await fetch(`${this.remoteBaseUrl}/api/canvas/ax/review`, {
|
|
849
|
+
method: 'POST',
|
|
850
|
+
headers: { 'Content-Type': 'application/json' },
|
|
851
|
+
body: JSON.stringify({ ...input, source: options?.source ?? 'mcp' }),
|
|
852
|
+
});
|
|
853
|
+
// 400 = validation rejection (e.g. node-anchored review with an unknown
|
|
854
|
+
// nodeId); mirror the local path and return null rather than throwing.
|
|
855
|
+
if (response.status === 400) return null;
|
|
856
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
857
|
+
return (await response.json() as { reviewAnnotation?: AddReviewAnnotationResult }).reviewAnnotation ?? null;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
async updateReviewAnnotation(id: string, patch: UpdateReviewAnnotationPatch, options?: { source?: PmxAxSource }): Promise<UpdateReviewAnnotationResult> {
|
|
861
|
+
const response = await fetch(`${this.remoteBaseUrl}/api/canvas/ax/review/${encodeURIComponent(id)}`, {
|
|
862
|
+
method: 'PATCH',
|
|
863
|
+
headers: { 'Content-Type': 'application/json' },
|
|
864
|
+
body: JSON.stringify({ ...patch, source: options?.source ?? 'mcp' }),
|
|
865
|
+
});
|
|
866
|
+
if (response.status === 404) return null;
|
|
867
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
868
|
+
return (await response.json() as { reviewAnnotation?: AddReviewAnnotationResult }).reviewAnnotation ?? null;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
async listReviewAnnotations(): Promise<ListReviewAnnotationsResult> {
|
|
872
|
+
const response = await this.requestJson<{ reviewAnnotations?: ListReviewAnnotationsResult }>('GET', '/api/canvas/ax/review');
|
|
873
|
+
return response.reviewAnnotations ?? [];
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
async getHostCapability(): Promise<GetHostCapabilityResult> {
|
|
877
|
+
const response = await this.requestJson<{ host?: GetHostCapabilityResult }>('GET', '/api/canvas/ax/host-capability');
|
|
878
|
+
return response.host ?? null;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
async reportHostCapability(input: unknown, options?: { source?: PmxAxSource }): Promise<ReportHostCapabilityResult> {
|
|
882
|
+
const body = input !== null && typeof input === 'object' && !Array.isArray(input) ? { ...input } : {};
|
|
883
|
+
const response = await this.requestJson<{ host?: ReportHostCapabilityResult }>('PUT', '/api/canvas/ax/host-capability', {
|
|
884
|
+
...body,
|
|
885
|
+
source: options?.source ?? 'mcp',
|
|
886
|
+
});
|
|
887
|
+
if (!response.host) throw new Error('Remote canvas did not return host capability.');
|
|
888
|
+
return response.host;
|
|
889
|
+
}
|
|
890
|
+
|
|
628
891
|
async setContextPins(nodeIds: string[], mode: 'set' | 'add' | 'remove' = 'set'): Promise<SetContextPinsResult> {
|
|
629
892
|
const existing = mode === 'set' ? [] : await this.getPinnedNodeIds();
|
|
630
893
|
const requested = new Set(nodeIds);
|