pmx-canvas 0.1.26 → 0.1.27
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 +74 -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 +7 -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 +23 -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 +45 -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 +114 -2
- 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 +19 -1
- 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 +280 -2
- package/src/cli/index.ts +2 -1
- package/src/client/nodes/ExtAppFrame.tsx +23 -1
- package/src/client/nodes/McpAppNode.tsx +6 -2
- package/src/json-render/catalog.ts +22 -1
- package/src/json-render/charts/components.tsx +97 -10
- 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 +383 -0
- package/src/json-render/charts/tufte-definitions.ts +128 -0
- package/src/json-render/directives.ts +29 -0
- package/src/json-render/renderer/index.css +101 -0
- package/src/json-render/renderer/index.tsx +33 -0
- package/src/json-render/server.ts +257 -5
- package/src/mcp/canvas-access.ts +261 -0
- package/src/mcp/server.ts +496 -7
- 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 +107 -0
- package/src/server/canvas-schema.ts +26 -3
- package/src/server/canvas-state.ts +349 -2
- package/src/server/index.ts +234 -2
- package/src/server/mutation-history.ts +6 -0
- package/src/server/server.ts +419 -2
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>;
|
|
@@ -194,6 +235,10 @@ class LocalCanvasAccess implements CanvasAccess {
|
|
|
194
235
|
return this.canvas.addJsonRenderNode(input);
|
|
195
236
|
}
|
|
196
237
|
|
|
238
|
+
async streamJsonRenderNode(input: StreamJsonRenderNodeInput): Promise<StreamJsonRenderNodeResult> {
|
|
239
|
+
return this.canvas.streamJsonRenderNode(input);
|
|
240
|
+
}
|
|
241
|
+
|
|
197
242
|
async addHtmlNode(input: AddHtmlNodeInput): Promise<string> {
|
|
198
243
|
return this.canvas.addHtmlNode(input);
|
|
199
244
|
}
|
|
@@ -266,6 +311,69 @@ class LocalCanvasAccess implements CanvasAccess {
|
|
|
266
311
|
return this.canvas.setAxFocus(nodeIds, { source: options?.source ?? 'mcp' });
|
|
267
312
|
}
|
|
268
313
|
|
|
314
|
+
async recordAxEvent(input: RecordAxEventInput, options?: { source?: PmxAxSource }): Promise<RecordAxEventResult> {
|
|
315
|
+
return this.canvas.recordAxEvent(input, { source: options?.source ?? 'mcp' });
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
async sendSteering(message: string, options?: { source?: PmxAxSource }): Promise<SendSteeringResult> {
|
|
319
|
+
return this.canvas.sendSteering(message, { source: options?.source ?? 'mcp' });
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
async getAxTimeline(query?: GetAxTimelineQuery): Promise<GetAxTimelineResult> {
|
|
323
|
+
return this.canvas.getAxTimeline(query);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
async addWorkItem(input: AddWorkItemInput, options?: { source?: PmxAxSource }): Promise<AddWorkItemResult> {
|
|
327
|
+
return this.canvas.addWorkItem(input, { source: options?.source ?? 'mcp' });
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
async updateWorkItem(id: string, patch: UpdateWorkItemPatch, options?: { source?: PmxAxSource }): Promise<UpdateWorkItemResult> {
|
|
331
|
+
return this.canvas.updateWorkItem(id, patch, { source: options?.source ?? 'mcp' });
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
async listWorkItems(): Promise<ListWorkItemsResult> {
|
|
335
|
+
return this.canvas.listWorkItems();
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
async requestApproval(input: RequestApprovalInput, options?: { source?: PmxAxSource }): Promise<RequestApprovalResult> {
|
|
339
|
+
return this.canvas.requestApproval(input, { source: options?.source ?? 'mcp' });
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
async resolveApproval(id: string, decision: 'approved' | 'rejected', options?: { resolution?: string; source?: PmxAxSource }): Promise<ResolveApprovalResult> {
|
|
343
|
+
return this.canvas.resolveApproval(id, decision, {
|
|
344
|
+
...(options?.resolution !== undefined ? { resolution: options.resolution } : {}),
|
|
345
|
+
source: options?.source ?? 'mcp',
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
async listApprovalGates(): Promise<ListApprovalGatesResult> {
|
|
350
|
+
return this.canvas.listApprovalGates();
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
async addEvidence(input: AddEvidenceInput, options?: { source?: PmxAxSource }): Promise<AddEvidenceResult> {
|
|
354
|
+
return this.canvas.addEvidence(input, { source: options?.source ?? 'mcp' });
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
async addReviewAnnotation(input: AddReviewAnnotationInput, options?: { source?: PmxAxSource }): Promise<AddReviewAnnotationResult> {
|
|
358
|
+
return this.canvas.addReviewAnnotation(input, { source: options?.source ?? 'mcp' });
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
async updateReviewAnnotation(id: string, patch: UpdateReviewAnnotationPatch, options?: { source?: PmxAxSource }): Promise<UpdateReviewAnnotationResult> {
|
|
362
|
+
return this.canvas.updateReviewAnnotation(id, patch, { source: options?.source ?? 'mcp' });
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
async listReviewAnnotations(): Promise<ListReviewAnnotationsResult> {
|
|
366
|
+
return this.canvas.listReviewAnnotations();
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
async getHostCapability(): Promise<GetHostCapabilityResult> {
|
|
370
|
+
return this.canvas.getHostCapability();
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
async reportHostCapability(input: unknown, options?: { source?: PmxAxSource }): Promise<ReportHostCapabilityResult> {
|
|
374
|
+
return this.canvas.reportHostCapability(input, { source: options?.source ?? 'mcp' });
|
|
375
|
+
}
|
|
376
|
+
|
|
269
377
|
async clear(): Promise<void> {
|
|
270
378
|
this.canvas.clear();
|
|
271
379
|
}
|
|
@@ -460,6 +568,29 @@ class RemoteCanvasAccess implements CanvasAccess {
|
|
|
460
568
|
return { id, url: response.url, spec: response.spec };
|
|
461
569
|
}
|
|
462
570
|
|
|
571
|
+
async streamJsonRenderNode(input: StreamJsonRenderNodeInput): Promise<StreamJsonRenderNodeResult> {
|
|
572
|
+
const response = await this.requestJson<{
|
|
573
|
+
id?: string;
|
|
574
|
+
url?: string;
|
|
575
|
+
applied?: number;
|
|
576
|
+
skipped?: number;
|
|
577
|
+
specVersion?: number;
|
|
578
|
+
elementCount?: number;
|
|
579
|
+
streamStatus?: 'open' | 'closed';
|
|
580
|
+
}>('POST', '/api/canvas/json-render/stream', input);
|
|
581
|
+
const id = typeof response.id === 'string' ? response.id : undefined;
|
|
582
|
+
if (!id) throw new Error('json-render stream response did not include a node id.');
|
|
583
|
+
return {
|
|
584
|
+
id,
|
|
585
|
+
url: response.url ?? '',
|
|
586
|
+
applied: response.applied ?? 0,
|
|
587
|
+
skipped: response.skipped ?? 0,
|
|
588
|
+
specVersion: response.specVersion ?? 0,
|
|
589
|
+
elementCount: response.elementCount ?? 0,
|
|
590
|
+
streamStatus: response.streamStatus ?? 'open',
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
|
|
463
594
|
async addHtmlNode(input: AddHtmlNodeInput): Promise<string> {
|
|
464
595
|
const {
|
|
465
596
|
summary,
|
|
@@ -625,6 +756,136 @@ class RemoteCanvasAccess implements CanvasAccess {
|
|
|
625
756
|
return response.focus;
|
|
626
757
|
}
|
|
627
758
|
|
|
759
|
+
async recordAxEvent(input: RecordAxEventInput, options?: { source?: PmxAxSource }): Promise<RecordAxEventResult> {
|
|
760
|
+
const response = await this.requestJson<{ event?: RecordAxEventResult }>('POST', '/api/canvas/ax/event', {
|
|
761
|
+
...input,
|
|
762
|
+
source: options?.source ?? 'mcp',
|
|
763
|
+
});
|
|
764
|
+
if (!response.event) throw new Error('Remote canvas did not return an AX event.');
|
|
765
|
+
return response.event;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
async sendSteering(message: string, options?: { source?: PmxAxSource }): Promise<SendSteeringResult> {
|
|
769
|
+
const response = await this.requestJson<{ steering?: SendSteeringResult }>('POST', '/api/canvas/ax/steer', {
|
|
770
|
+
message,
|
|
771
|
+
source: options?.source ?? 'mcp',
|
|
772
|
+
});
|
|
773
|
+
if (!response.steering) throw new Error('Remote canvas did not return a steering message.');
|
|
774
|
+
return response.steering;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
async getAxTimeline(query?: GetAxTimelineQuery): Promise<GetAxTimelineResult> {
|
|
778
|
+
const qs = query?.limit ? `?limit=${query.limit}` : '';
|
|
779
|
+
return await this.requestJson<GetAxTimelineResult>('GET', `/api/canvas/ax/timeline${qs}`);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
async addWorkItem(input: AddWorkItemInput, options?: { source?: PmxAxSource }): Promise<AddWorkItemResult> {
|
|
783
|
+
const response = await this.requestJson<{ workItem?: AddWorkItemResult }>('POST', '/api/canvas/ax/work', {
|
|
784
|
+
...input,
|
|
785
|
+
source: options?.source ?? 'mcp',
|
|
786
|
+
});
|
|
787
|
+
if (!response.workItem) throw new Error('Remote canvas did not return a work item.');
|
|
788
|
+
return response.workItem;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
async updateWorkItem(id: string, patch: UpdateWorkItemPatch, options?: { source?: PmxAxSource }): Promise<UpdateWorkItemResult> {
|
|
792
|
+
const response = await fetch(`${this.remoteBaseUrl}/api/canvas/ax/work/${encodeURIComponent(id)}`, {
|
|
793
|
+
method: 'PATCH',
|
|
794
|
+
headers: { 'Content-Type': 'application/json' },
|
|
795
|
+
body: JSON.stringify({ ...patch, source: options?.source ?? 'mcp' }),
|
|
796
|
+
});
|
|
797
|
+
if (response.status === 404) return null;
|
|
798
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
799
|
+
return (await response.json() as { workItem?: AddWorkItemResult }).workItem ?? null;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
async listWorkItems(): Promise<ListWorkItemsResult> {
|
|
803
|
+
const response = await this.requestJson<{ workItems?: ListWorkItemsResult }>('GET', '/api/canvas/ax/work');
|
|
804
|
+
return response.workItems ?? [];
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
async requestApproval(input: RequestApprovalInput, options?: { source?: PmxAxSource }): Promise<RequestApprovalResult> {
|
|
808
|
+
const response = await this.requestJson<{ approvalGate?: RequestApprovalResult }>('POST', '/api/canvas/ax/approval', {
|
|
809
|
+
...input,
|
|
810
|
+
source: options?.source ?? 'mcp',
|
|
811
|
+
});
|
|
812
|
+
if (!response.approvalGate) throw new Error('Remote canvas did not return an approval gate.');
|
|
813
|
+
return response.approvalGate;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
async resolveApproval(id: string, decision: 'approved' | 'rejected', options?: { resolution?: string; source?: PmxAxSource }): Promise<ResolveApprovalResult> {
|
|
817
|
+
const response = await fetch(`${this.remoteBaseUrl}/api/canvas/ax/approval/${encodeURIComponent(id)}/resolve`, {
|
|
818
|
+
method: 'POST',
|
|
819
|
+
headers: { 'Content-Type': 'application/json' },
|
|
820
|
+
body: JSON.stringify({
|
|
821
|
+
decision,
|
|
822
|
+
...(options?.resolution !== undefined ? { resolution: options.resolution } : {}),
|
|
823
|
+
source: options?.source ?? 'mcp',
|
|
824
|
+
}),
|
|
825
|
+
});
|
|
826
|
+
if (response.status === 404) return null;
|
|
827
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
828
|
+
return (await response.json() as { approvalGate?: RequestApprovalResult }).approvalGate ?? null;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
async listApprovalGates(): Promise<ListApprovalGatesResult> {
|
|
832
|
+
const response = await this.requestJson<{ approvalGates?: ListApprovalGatesResult }>('GET', '/api/canvas/ax/approval');
|
|
833
|
+
return response.approvalGates ?? [];
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
async addEvidence(input: AddEvidenceInput, options?: { source?: PmxAxSource }): Promise<AddEvidenceResult> {
|
|
837
|
+
const response = await this.requestJson<{ evidence?: AddEvidenceResult }>('POST', '/api/canvas/ax/evidence', {
|
|
838
|
+
...input,
|
|
839
|
+
source: options?.source ?? 'mcp',
|
|
840
|
+
});
|
|
841
|
+
if (!response.evidence) throw new Error('Remote canvas did not return an evidence item.');
|
|
842
|
+
return response.evidence;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
async addReviewAnnotation(input: AddReviewAnnotationInput, options?: { source?: PmxAxSource }): Promise<AddReviewAnnotationResult> {
|
|
846
|
+
const response = await fetch(`${this.remoteBaseUrl}/api/canvas/ax/review`, {
|
|
847
|
+
method: 'POST',
|
|
848
|
+
headers: { 'Content-Type': 'application/json' },
|
|
849
|
+
body: JSON.stringify({ ...input, source: options?.source ?? 'mcp' }),
|
|
850
|
+
});
|
|
851
|
+
// 400 = validation rejection (e.g. node-anchored review with an unknown
|
|
852
|
+
// nodeId); mirror the local path and return null rather than throwing.
|
|
853
|
+
if (response.status === 400) return null;
|
|
854
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
855
|
+
return (await response.json() as { reviewAnnotation?: AddReviewAnnotationResult }).reviewAnnotation ?? null;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
async updateReviewAnnotation(id: string, patch: UpdateReviewAnnotationPatch, options?: { source?: PmxAxSource }): Promise<UpdateReviewAnnotationResult> {
|
|
859
|
+
const response = await fetch(`${this.remoteBaseUrl}/api/canvas/ax/review/${encodeURIComponent(id)}`, {
|
|
860
|
+
method: 'PATCH',
|
|
861
|
+
headers: { 'Content-Type': 'application/json' },
|
|
862
|
+
body: JSON.stringify({ ...patch, source: options?.source ?? 'mcp' }),
|
|
863
|
+
});
|
|
864
|
+
if (response.status === 404) return null;
|
|
865
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
866
|
+
return (await response.json() as { reviewAnnotation?: AddReviewAnnotationResult }).reviewAnnotation ?? null;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
async listReviewAnnotations(): Promise<ListReviewAnnotationsResult> {
|
|
870
|
+
const response = await this.requestJson<{ reviewAnnotations?: ListReviewAnnotationsResult }>('GET', '/api/canvas/ax/review');
|
|
871
|
+
return response.reviewAnnotations ?? [];
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
async getHostCapability(): Promise<GetHostCapabilityResult> {
|
|
875
|
+
const response = await this.requestJson<{ host?: GetHostCapabilityResult }>('GET', '/api/canvas/ax/host-capability');
|
|
876
|
+
return response.host ?? null;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
async reportHostCapability(input: unknown, options?: { source?: PmxAxSource }): Promise<ReportHostCapabilityResult> {
|
|
880
|
+
const body = input !== null && typeof input === 'object' && !Array.isArray(input) ? { ...input } : {};
|
|
881
|
+
const response = await this.requestJson<{ host?: ReportHostCapabilityResult }>('PUT', '/api/canvas/ax/host-capability', {
|
|
882
|
+
...body,
|
|
883
|
+
source: options?.source ?? 'mcp',
|
|
884
|
+
});
|
|
885
|
+
if (!response.host) throw new Error('Remote canvas did not return host capability.');
|
|
886
|
+
return response.host;
|
|
887
|
+
}
|
|
888
|
+
|
|
628
889
|
async setContextPins(nodeIds: string[], mode: 'set' | 'add' | 'remove' = 'set'): Promise<SetContextPinsResult> {
|
|
629
890
|
const existing = mode === 'set' ? [] : await this.getPinnedNodeIds();
|
|
630
891
|
const requested = new Set(nodeIds);
|