pmx-canvas 0.3.0 → 0.3.2
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/CHANGELOG.md +73 -0
- package/dist/canvas/index.js +65 -65
- package/dist/types/cli/agent.d.ts +9 -1
- package/dist/types/cli/daemon.d.ts +74 -0
- package/dist/types/cli/watch.d.ts +2 -2
- package/dist/types/client/canvas/CanvasViewport.d.ts +1 -1
- package/dist/types/client/canvas/CommandPalette.d.ts +1 -1
- package/dist/types/client/canvas/Minimap.d.ts +1 -1
- package/dist/types/client/nodes/ExtAppFrame.d.ts +9 -1
- package/dist/types/client/nodes/FileNode.d.ts +1 -1
- package/dist/types/client/nodes/ImageNode.d.ts +1 -1
- package/dist/types/client/nodes/InlineFormatBar.d.ts +1 -1
- package/dist/types/client/nodes/MarkdownNode.d.ts +1 -1
- package/dist/types/client/nodes/PromptNode.d.ts +1 -1
- package/dist/types/client/nodes/ResponseNode.d.ts +1 -1
- package/dist/types/server/canvas-schema.d.ts +1 -1
- package/dist/types/server/html-primitives.d.ts +1 -1
- package/dist/types/server/index.d.ts +4 -4
- package/dist/types/server/operations/index.d.ts +1 -1
- package/dist/types/server/operations/ops/ax-read.d.ts +2 -0
- package/dist/types/server/operations/ops/canvas-wire.d.ts +2 -0
- package/dist/types/server/operations/ops/ext-app.d.ts +2 -0
- package/dist/types/server/server.d.ts +8 -0
- package/docs/cli.md +10 -1
- package/docs/http-api.md +28 -0
- package/docs/plans/plan-009-tech-debt-backlog.md +5 -5
- package/docs/screenshot.png +0 -0
- package/docs/tech-debt-assessment-2026-07.md +2 -2
- package/package.json +5 -2
- package/skills/pmx-canvas/SKILL.md +15 -10
- package/skills/pmx-canvas/references/full-reference.md +17 -3
- package/src/cli/agent.ts +1951 -1571
- package/src/cli/daemon.ts +460 -0
- package/src/cli/index.ts +80 -323
- package/src/cli/watch.ts +2 -10
- package/src/client/App.tsx +48 -46
- package/src/client/canvas/AttentionHistory.tsx +11 -1
- package/src/client/canvas/CanvasNode.tsx +41 -29
- package/src/client/canvas/CanvasViewport.tsx +101 -66
- package/src/client/canvas/CommandPalette.tsx +61 -27
- package/src/client/canvas/ContextMenu.tsx +13 -20
- package/src/client/canvas/ContextPinBar.tsx +1 -5
- package/src/client/canvas/ContextPinHud.tsx +1 -6
- package/src/client/canvas/DockedNode.tsx +4 -4
- package/src/client/canvas/EdgeLayer.tsx +37 -36
- package/src/client/canvas/ExpandedNodeOverlay.tsx +69 -45
- package/src/client/canvas/FocusFieldLayer.tsx +20 -22
- package/src/client/canvas/IntentLayer.tsx +31 -14
- package/src/client/canvas/Minimap.tsx +11 -16
- package/src/client/canvas/SelectionBar.tsx +4 -11
- package/src/client/canvas/ShortcutOverlay.tsx +3 -1
- package/src/client/canvas/SnapshotPanel.tsx +77 -95
- package/src/client/canvas/auto-fit.ts +15 -14
- package/src/client/canvas/snap-guides.ts +12 -12
- package/src/client/canvas/use-node-resize.ts +1 -5
- package/src/client/canvas/use-pan-zoom.ts +25 -26
- package/src/client/ext-app/bridge.ts +3 -12
- package/src/client/icons.tsx +63 -20
- package/src/client/nodes/ContextNode.tsx +14 -25
- package/src/client/nodes/ExtAppFrame.tsx +194 -80
- package/src/client/nodes/FileNode.tsx +74 -62
- package/src/client/nodes/GroupNode.tsx +4 -6
- package/src/client/nodes/HtmlNode.tsx +76 -46
- package/src/client/nodes/ImageNode.tsx +18 -27
- package/src/client/nodes/InlineFormatBar.tsx +4 -21
- package/src/client/nodes/InlineMarkdownEditor.tsx +0 -1
- package/src/client/nodes/LedgerNode.tsx +10 -4
- package/src/client/nodes/MarkdownNode.tsx +3 -10
- package/src/client/nodes/McpAppNode.tsx +26 -22
- package/src/client/nodes/MdFormatBar.tsx +10 -7
- package/src/client/nodes/PromptNode.tsx +23 -51
- package/src/client/nodes/ResponseNode.tsx +3 -13
- package/src/client/nodes/StatusNode.tsx +5 -9
- package/src/client/nodes/StatusSummary.tsx +2 -8
- package/src/client/nodes/WebpageNode.tsx +20 -14
- package/src/client/nodes/iframe-document-url.ts +25 -16
- package/src/client/nodes/image-warnings.ts +1 -7
- package/src/client/nodes/md-format.ts +20 -5
- package/src/client/state/attention-bridge.ts +4 -9
- package/src/client/state/attention-store.ts +1 -7
- package/src/client/state/canvas-store.ts +52 -36
- package/src/client/state/intent-bridge.ts +176 -112
- package/src/client/state/intent-store.ts +4 -1
- package/src/client/state/sse-bridge.ts +53 -70
- package/src/json-render/catalog.ts +12 -16
- package/src/json-render/charts/components.tsx +16 -20
- package/src/json-render/charts/extra-components.tsx +8 -16
- package/src/json-render/charts/extra-definitions.ts +1 -2
- package/src/json-render/charts/tufte-components.tsx +37 -20
- package/src/json-render/charts/tufte-definitions.ts +8 -2
- package/src/json-render/renderer/index.tsx +42 -22
- package/src/json-render/schema.ts +6 -3
- package/src/json-render/server.ts +33 -39
- package/src/mcp/canvas-access.ts +35 -21
- package/src/mcp/server.ts +132 -70
- package/src/server/agent-context.ts +63 -36
- package/src/server/ax-context.ts +7 -5
- package/src/server/ax-interaction.ts +176 -43
- package/src/server/ax-state-manager.ts +182 -39
- package/src/server/ax-state.ts +142 -47
- package/src/server/canvas-db.ts +213 -95
- package/src/server/canvas-operations.ts +180 -123
- package/src/server/canvas-provenance.ts +1 -4
- package/src/server/canvas-schema.ts +454 -73
- package/src/server/canvas-serialization.ts +27 -35
- package/src/server/canvas-state.ts +150 -58
- package/src/server/chart-template.ts +4 -5
- package/src/server/code-graph.ts +19 -6
- package/src/server/diagram-presets.ts +28 -29
- package/src/server/ext-app-lookup.ts +3 -12
- package/src/server/html-node-summary.ts +19 -10
- package/src/server/html-primitives.ts +326 -97
- package/src/server/html-surface.ts +6 -9
- package/src/server/image-source.ts +6 -4
- package/src/server/index.ts +320 -217
- package/src/server/intent-registry.ts +2 -5
- package/src/server/mcp-app-candidate.ts +5 -10
- package/src/server/mcp-app-host.ts +14 -38
- package/src/server/mcp-app-runtime.ts +12 -20
- package/src/server/mutation-history.ts +15 -5
- package/src/server/operations/composites.ts +1 -3
- package/src/server/operations/http.ts +2 -3
- package/src/server/operations/index.ts +7 -1
- package/src/server/operations/invoker.ts +4 -3
- package/src/server/operations/mcp.ts +22 -30
- package/src/server/operations/ops/annotation.ts +122 -10
- package/src/server/operations/ops/app.ts +98 -73
- package/src/server/operations/ops/ax-await.ts +17 -10
- package/src/server/operations/ops/ax-read.ts +347 -0
- package/src/server/operations/ops/ax-shared.ts +2 -7
- package/src/server/operations/ops/ax-state.ts +32 -14
- package/src/server/operations/ops/ax-timeline.ts +32 -19
- package/src/server/operations/ops/ax-work.ts +54 -37
- package/src/server/operations/ops/batch.ts +41 -15
- package/src/server/operations/ops/canvas-wire.ts +91 -0
- package/src/server/operations/ops/edges.ts +37 -25
- package/src/server/operations/ops/ext-app.ts +346 -0
- package/src/server/operations/ops/groups.ts +49 -20
- package/src/server/operations/ops/intent.ts +18 -12
- package/src/server/operations/ops/json-render.ts +239 -98
- package/src/server/operations/ops/nodes.ts +298 -109
- package/src/server/operations/ops/query.ts +46 -28
- package/src/server/operations/ops/snapshots.ts +35 -26
- package/src/server/operations/ops/validate.ts +2 -1
- package/src/server/operations/ops/viewport.ts +60 -16
- package/src/server/operations/ops/webview.ts +44 -18
- package/src/server/operations/registry.ts +2 -3
- package/src/server/operations/types.ts +7 -5
- package/src/server/operations/webview-runner.ts +1 -3
- package/src/server/placement.ts +8 -18
- package/src/server/server.ts +122 -1028
- package/src/server/spatial-analysis.ts +39 -25
- package/src/server/trace-manager.ts +3 -8
- package/src/server/web-artifacts.ts +23 -27
- package/src/server/webpage-node.ts +5 -13
- package/src/shared/auto-arrange.ts +12 -5
- package/src/shared/content-height-reporter.ts +8 -6
- package/src/shared/ext-app-tool-result.ts +2 -6
- package/src/shared/placement.ts +1 -4
- package/src/shared/semantic-attention.ts +39 -37
- package/src/shared/surface.ts +8 -4
|
@@ -85,10 +85,7 @@ export interface OpenMcpAppCoreResult {
|
|
|
85
85
|
* call it. The diagram.open op delegates here after building the Excalidraw input
|
|
86
86
|
* (the SSE pair fires ONCE — diagram.open does not re-emit).
|
|
87
87
|
*/
|
|
88
|
-
export async function openMcpAppCore(
|
|
89
|
-
input: OpenMcpAppCoreInput,
|
|
90
|
-
ctx: OperationContext,
|
|
91
|
-
): Promise<OpenMcpAppCoreResult> {
|
|
88
|
+
export async function openMcpAppCore(input: OpenMcpAppCoreInput, ctx: OperationContext): Promise<OpenMcpAppCoreResult> {
|
|
92
89
|
// The canvas node is created as a side-effect of the `ext-app-open` SSE event
|
|
93
90
|
// (syncEventToCanvasState). Inside a suppressed-emit run (canvas.batch) that
|
|
94
91
|
// event is dropped, so the node would never be created and the just-opened
|
|
@@ -155,10 +152,12 @@ export async function openMcpAppCore(
|
|
|
155
152
|
success: toolResult.isError !== true,
|
|
156
153
|
result: toolResult,
|
|
157
154
|
});
|
|
158
|
-
const nodeId =
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
155
|
+
const nodeId =
|
|
156
|
+
input.nodeId ??
|
|
157
|
+
findCanvasExtAppNodeId(toolCallId, {
|
|
158
|
+
getNode: (id) => canvasState.getNode(id),
|
|
159
|
+
listNodes: () => canvasState.getLayout().nodes,
|
|
160
|
+
});
|
|
162
161
|
return {
|
|
163
162
|
ok: true,
|
|
164
163
|
...(nodeId ? { id: nodeId } : {}),
|
|
@@ -180,15 +179,10 @@ function buildOpenMcpAppInput(body: Record<string, unknown>): OpenMcpAppCoreInpu
|
|
|
180
179
|
throw new OperationError('Missing valid transport or toolName.');
|
|
181
180
|
}
|
|
182
181
|
const toolArguments = isRecord(body.toolArguments) ? body.toolArguments : undefined;
|
|
183
|
-
const serverName =
|
|
184
|
-
? body.serverName.trim()
|
|
185
|
-
|
|
186
|
-
const
|
|
187
|
-
? body.title.trim()
|
|
188
|
-
: undefined;
|
|
189
|
-
const nodeId = typeof body.nodeId === 'string' && body.nodeId.trim().length > 0
|
|
190
|
-
? body.nodeId.trim()
|
|
191
|
-
: undefined;
|
|
182
|
+
const serverName =
|
|
183
|
+
typeof body.serverName === 'string' && body.serverName.trim().length > 0 ? body.serverName.trim() : undefined;
|
|
184
|
+
const title = typeof body.title === 'string' && body.title.trim().length > 0 ? body.title.trim() : undefined;
|
|
185
|
+
const nodeId = typeof body.nodeId === 'string' && body.nodeId.trim().length > 0 ? body.nodeId.trim() : undefined;
|
|
192
186
|
return {
|
|
193
187
|
transport,
|
|
194
188
|
toolName,
|
|
@@ -216,21 +210,26 @@ const openMcpAppShape = {
|
|
|
216
210
|
y: z.number().optional().describe('Y position (auto-placed if omitted)'),
|
|
217
211
|
width: z.number().optional().describe('Width in pixels (default: 720)'),
|
|
218
212
|
height: z.number().optional().describe('Height in pixels (default: 500)'),
|
|
219
|
-
timeoutMs: z
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
213
|
+
timeoutMs: z
|
|
214
|
+
.number()
|
|
215
|
+
.optional()
|
|
216
|
+
.describe('Optional MCP request timeout in milliseconds for cold external app servers'),
|
|
217
|
+
transport: z
|
|
218
|
+
.union([
|
|
219
|
+
z.object({
|
|
220
|
+
type: z.literal('stdio'),
|
|
221
|
+
command: z.string().describe('Executable used to start the external MCP server'),
|
|
222
|
+
args: z.array(z.string()).optional().describe('Arguments for the executable'),
|
|
223
|
+
cwd: z.string().optional().describe('Optional working directory'),
|
|
224
|
+
env: z.record(z.string(), z.string()).optional().describe('Optional environment overrides'),
|
|
225
|
+
}),
|
|
226
|
+
z.object({
|
|
227
|
+
type: z.literal('http'),
|
|
228
|
+
url: z.string().describe('Streamable HTTP MCP endpoint URL'),
|
|
229
|
+
headers: z.record(z.string(), z.string()).optional().describe('Optional HTTP headers'),
|
|
230
|
+
}),
|
|
231
|
+
])
|
|
232
|
+
.describe('How PMX Canvas should connect to the external MCP server'),
|
|
234
233
|
};
|
|
235
234
|
const openMcpAppSchema = z.looseObject(openMcpAppShape);
|
|
236
235
|
|
|
@@ -257,17 +256,29 @@ const openMcpAppOperation = defineOperation<z.infer<typeof openMcpAppSchema>, Op
|
|
|
257
256
|
// ── diagram.open (Excalidraw preset → mcpapp.open core) ───────
|
|
258
257
|
|
|
259
258
|
const diagramShape = {
|
|
260
|
-
elements: z
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
259
|
+
elements: z
|
|
260
|
+
.union([
|
|
261
|
+
z.string().describe('JSON array string of Excalidraw elements'),
|
|
262
|
+
z.array(z.record(z.string(), z.unknown())).describe('Array of Excalidraw elements'),
|
|
263
|
+
])
|
|
264
|
+
.describe(
|
|
265
|
+
'Excalidraw elements to render. See https://github.com/excalidraw/excalidraw-mcp for the element format.',
|
|
266
|
+
),
|
|
267
|
+
nodeId: z
|
|
268
|
+
.string()
|
|
269
|
+
.optional()
|
|
270
|
+
.describe('Existing Excalidraw mcp-app node ID to update in place instead of creating a new node.'),
|
|
265
271
|
title: z.string().optional().describe('Optional canvas node title override'),
|
|
266
272
|
x: z.number().optional().describe('X position (auto-placed if omitted)'),
|
|
267
273
|
y: z.number().optional().describe('Y position (auto-placed if omitted)'),
|
|
268
274
|
width: z.number().optional().describe('Width in pixels (default: 720)'),
|
|
269
275
|
height: z.number().optional().describe('Height in pixels (default: 500)'),
|
|
270
|
-
timeoutMs: z
|
|
276
|
+
timeoutMs: z
|
|
277
|
+
.number()
|
|
278
|
+
.optional()
|
|
279
|
+
.describe(
|
|
280
|
+
'Optional MCP request timeout in milliseconds for Excalidraw cold starts. Client-side MCP hosts may still enforce their own total request timeout.',
|
|
281
|
+
),
|
|
271
282
|
};
|
|
272
283
|
const diagramSchema = z.looseObject(diagramShape);
|
|
273
284
|
|
|
@@ -289,7 +300,7 @@ const diagramOperation = defineOperation<z.infer<typeof diagramSchema>, OpenMcpA
|
|
|
289
300
|
}),
|
|
290
301
|
},
|
|
291
302
|
handler: (input, ctx) => {
|
|
292
|
-
let built
|
|
303
|
+
let built: ReturnType<typeof buildExcalidrawOpenMcpAppInput>;
|
|
293
304
|
try {
|
|
294
305
|
built = buildExcalidrawOpenMcpAppInput(input);
|
|
295
306
|
} catch (error) {
|
|
@@ -297,19 +308,22 @@ const diagramOperation = defineOperation<z.infer<typeof diagramSchema>, OpenMcpA
|
|
|
297
308
|
}
|
|
298
309
|
// Delegate to the shared open core (the SSE pair fires once — diagram.open
|
|
299
310
|
// does not re-emit).
|
|
300
|
-
return openMcpAppCore(
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
311
|
+
return openMcpAppCore(
|
|
312
|
+
{
|
|
313
|
+
transport: built.transport,
|
|
314
|
+
toolName: built.toolName,
|
|
315
|
+
toolArguments: built.toolArguments,
|
|
316
|
+
serverName: built.serverName,
|
|
317
|
+
...(built.nodeId ? { nodeId: built.nodeId } : {}),
|
|
318
|
+
...(built.title ? { title: built.title } : {}),
|
|
319
|
+
...(typeof built.x === 'number' ? { x: built.x } : {}),
|
|
320
|
+
...(typeof built.y === 'number' ? { y: built.y } : {}),
|
|
321
|
+
...(typeof built.width === 'number' ? { width: built.width } : {}),
|
|
322
|
+
...(typeof built.height === 'number' ? { height: built.height } : {}),
|
|
323
|
+
...(typeof built.timeoutMs === 'number' ? { timeoutMs: built.timeoutMs } : {}),
|
|
324
|
+
},
|
|
325
|
+
ctx,
|
|
326
|
+
);
|
|
313
327
|
},
|
|
314
328
|
});
|
|
315
329
|
|
|
@@ -321,23 +335,42 @@ const webArtifactShape = {
|
|
|
321
335
|
indexCss: z.string().optional().describe('Optional contents for src/index.css'),
|
|
322
336
|
mainTsx: z.string().optional().describe('Optional contents for src/main.tsx'),
|
|
323
337
|
indexHtml: z.string().optional().describe('Optional contents for index.html'),
|
|
324
|
-
files: z
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
338
|
+
files: z
|
|
339
|
+
.record(z.string(), z.string())
|
|
340
|
+
.optional()
|
|
341
|
+
.describe('Optional map of additional project-relative file paths to file contents'),
|
|
342
|
+
deps: z
|
|
343
|
+
.array(z.string())
|
|
344
|
+
.optional()
|
|
345
|
+
.describe(
|
|
346
|
+
'Optional npm dependencies to install before bundling (e.g. ["recharts", "framer-motion@^11"]). Validated against npm-name format; flags and shell metacharacters are rejected.',
|
|
347
|
+
),
|
|
348
|
+
projectPath: z
|
|
349
|
+
.string()
|
|
350
|
+
.optional()
|
|
351
|
+
.describe(
|
|
352
|
+
'Optional workspace-relative reusable project path. Defaults to .pmx-canvas/artifacts/.web-artifacts/<slug>',
|
|
353
|
+
),
|
|
354
|
+
outputPath: z
|
|
355
|
+
.string()
|
|
356
|
+
.optional()
|
|
357
|
+
.describe('Optional workspace-relative HTML output path. Defaults to .pmx-canvas/artifacts/<slug>.html'),
|
|
328
358
|
openInCanvas: z.boolean().optional().describe('Open the generated artifact in canvas after build (default true)'),
|
|
329
359
|
includeLogs: z.boolean().optional().describe('Include raw build stdout/stderr in the response (default false)'),
|
|
330
|
-
initScriptPath: z
|
|
331
|
-
|
|
360
|
+
initScriptPath: z
|
|
361
|
+
.string()
|
|
362
|
+
.optional()
|
|
363
|
+
.describe('Optional script path override for tests/debugging. Must resolve inside the workspace.'),
|
|
364
|
+
bundleScriptPath: z
|
|
365
|
+
.string()
|
|
366
|
+
.optional()
|
|
367
|
+
.describe('Optional script path override for tests/debugging. Must resolve inside the workspace.'),
|
|
332
368
|
timeoutMs: z.number().optional().describe('Optional timeout in milliseconds for init and bundle commands'),
|
|
333
369
|
};
|
|
334
370
|
const webArtifactSchema = z.looseObject(webArtifactShape);
|
|
335
371
|
|
|
336
372
|
/** Shape the byte-identical web-artifact response envelope from the build result. */
|
|
337
|
-
function webArtifactEnvelope(
|
|
338
|
-
result: WebArtifactCanvasBuildResult,
|
|
339
|
-
includeLogs: boolean,
|
|
340
|
-
): Record<string, unknown> {
|
|
373
|
+
function webArtifactEnvelope(result: WebArtifactCanvasBuildResult, includeLogs: boolean): Record<string, unknown> {
|
|
341
374
|
return {
|
|
342
375
|
ok: true,
|
|
343
376
|
path: result.filePath,
|
|
@@ -415,12 +448,8 @@ const webArtifactOperation = defineOperation<z.infer<typeof webArtifactSchema>,
|
|
|
415
448
|
// legacy HTTP handler used resolveWorkspacePath; the MCP tool used its
|
|
416
449
|
// own safeWorkspacePath — both enforce containment). resolveWorkspacePath
|
|
417
450
|
// resolves against the active canvas workspace root.
|
|
418
|
-
...(typeof input.projectPath === 'string'
|
|
419
|
-
|
|
420
|
-
: {}),
|
|
421
|
-
...(typeof input.outputPath === 'string'
|
|
422
|
-
? { outputPath: resolveWorkspacePath(input.outputPath) }
|
|
423
|
-
: {}),
|
|
451
|
+
...(typeof input.projectPath === 'string' ? { projectPath: resolveWorkspacePath(input.projectPath) } : {}),
|
|
452
|
+
...(typeof input.outputPath === 'string' ? { outputPath: resolveWorkspacePath(input.outputPath) } : {}),
|
|
424
453
|
// Script-path overrides are honored only when contained inside the
|
|
425
454
|
// workspace (enforced by resolveTrustedScriptPath in
|
|
426
455
|
// executeWebArtifactBuild), so they cannot point at an arbitrary host
|
|
@@ -440,8 +469,4 @@ const webArtifactOperation = defineOperation<z.infer<typeof webArtifactSchema>,
|
|
|
440
469
|
},
|
|
441
470
|
});
|
|
442
471
|
|
|
443
|
-
export const appOperations: Operation[] = [
|
|
444
|
-
openMcpAppOperation,
|
|
445
|
-
diagramOperation,
|
|
446
|
-
webArtifactOperation,
|
|
447
|
-
];
|
|
472
|
+
export const appOperations: Operation[] = [openMcpAppOperation, diagramOperation, webArtifactOperation];
|
|
@@ -92,7 +92,13 @@ const awaitInputSchema = z.looseObject(awaitInputShape);
|
|
|
92
92
|
|
|
93
93
|
const awaitMcpExtraShape = {
|
|
94
94
|
id: z.string(),
|
|
95
|
-
timeoutMs: z
|
|
95
|
+
timeoutMs: z
|
|
96
|
+
.number()
|
|
97
|
+
.int()
|
|
98
|
+
.min(0)
|
|
99
|
+
.max(120000)
|
|
100
|
+
.optional()
|
|
101
|
+
.describe('Max ms to block (default 30000; 0 = immediate read; capped at 120000).'),
|
|
96
102
|
};
|
|
97
103
|
|
|
98
104
|
// ── ax.approval.get (canvas_await_approval) ───────────────────
|
|
@@ -110,7 +116,8 @@ const axApprovalGetOperation = defineOperation<z.infer<typeof awaitInputSchema>,
|
|
|
110
116
|
},
|
|
111
117
|
mcp: {
|
|
112
118
|
toolName: 'canvas_await_approval',
|
|
113
|
-
description:
|
|
119
|
+
description:
|
|
120
|
+
'Block until an approval gate resolves (the human approves/rejects it in the browser) or the timeout elapses — primitive D, gates that actually gate. timeoutMs 0 = read immediately without waiting. Returns { approvalGate, pending } (pending=true → still unresolved after the wait).',
|
|
114
121
|
extraShape: awaitMcpExtraShape,
|
|
115
122
|
buildInput: buildAwaitInput,
|
|
116
123
|
formatResult: (result) => {
|
|
@@ -118,7 +125,9 @@ const axApprovalGetOperation = defineOperation<z.infer<typeof awaitInputSchema>,
|
|
|
118
125
|
const approvalGate = (body.approvalGate ?? null) as PmxAxApprovalGate | null;
|
|
119
126
|
const pending = body.pending === true;
|
|
120
127
|
return {
|
|
121
|
-
content: [
|
|
128
|
+
content: [
|
|
129
|
+
{ type: 'text' as const, text: JSON.stringify({ ok: approvalGate !== null, approvalGate, pending }) },
|
|
130
|
+
],
|
|
122
131
|
};
|
|
123
132
|
},
|
|
124
133
|
},
|
|
@@ -148,7 +157,8 @@ const axElicitationGetOperation = defineOperation<z.infer<typeof awaitInputSchem
|
|
|
148
157
|
},
|
|
149
158
|
mcp: {
|
|
150
159
|
toolName: 'canvas_await_elicitation',
|
|
151
|
-
description:
|
|
160
|
+
description:
|
|
161
|
+
'Block until an elicitation is answered (the human responds in the browser) or the timeout elapses — primitive D. timeoutMs 0 = read immediately. Returns { elicitation, pending }.',
|
|
152
162
|
extraShape: awaitMcpExtraShape,
|
|
153
163
|
buildInput: buildAwaitInput,
|
|
154
164
|
formatResult: (result) => {
|
|
@@ -186,7 +196,8 @@ const axModeGetOperation = defineOperation<z.infer<typeof awaitInputSchema>, Rec
|
|
|
186
196
|
},
|
|
187
197
|
mcp: {
|
|
188
198
|
toolName: 'canvas_await_mode',
|
|
189
|
-
description:
|
|
199
|
+
description:
|
|
200
|
+
'Block until a mode request resolves (approved/rejected in the browser) or the timeout elapses — primitive D. timeoutMs 0 = read immediately. Returns { modeRequest, pending }.',
|
|
190
201
|
extraShape: awaitMcpExtraShape,
|
|
191
202
|
buildInput: buildAwaitInput,
|
|
192
203
|
formatResult: (result) => {
|
|
@@ -209,8 +220,4 @@ const axModeGetOperation = defineOperation<z.infer<typeof awaitInputSchema>, Rec
|
|
|
209
220
|
},
|
|
210
221
|
});
|
|
211
222
|
|
|
212
|
-
export const axAwaitOperations: Operation[] = [
|
|
213
|
-
axApprovalGetOperation,
|
|
214
|
-
axElicitationGetOperation,
|
|
215
|
-
axModeGetOperation,
|
|
216
|
-
];
|
|
223
|
+
export const axAwaitOperations: Operation[] = [axApprovalGetOperation, axElicitationGetOperation, axModeGetOperation];
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AX read + wire-compat operations (plan-009 C1 slice 2): the GET read
|
|
3
|
+
* surface (`ax/work`, `ax/approval`, `ax/review`, `ax/elicitation`,
|
|
4
|
+
* `ax/mode`, `ax/command`, `ax/policy`, `ax/host-capability`, `ax/context`,
|
|
5
|
+
* `ax/surface-snapshot`, `pinned-context`, `code-graph`) plus the three
|
|
6
|
+
* remaining AX writes (`ax/activity` ingest, `ax/interaction` submit, and
|
|
7
|
+
* the legacy `PATCH /api/canvas/ax` focus shape). Wire envelopes are
|
|
8
|
+
* byte-identical to the legacy server.ts handlers they replace. HTTP-only:
|
|
9
|
+
* the MCP read surface stays the composites/resources — no new tools, the
|
|
10
|
+
* frozen 27-tool surface is untouched.
|
|
11
|
+
*
|
|
12
|
+
* This module must never import server.ts or index.ts.
|
|
13
|
+
*/
|
|
14
|
+
import { z } from 'zod';
|
|
15
|
+
import { buildAgentContextPreamble, serializeNodeForAgentContext } from '../../agent-context.js';
|
|
16
|
+
import { buildCanvasAxContext, buildCanvasAxSurfaceSnapshot } from '../../ax-context.js';
|
|
17
|
+
import { applyAxInteraction } from '../../ax-interaction.js';
|
|
18
|
+
import {
|
|
19
|
+
isAxActivityKind,
|
|
20
|
+
isAxEvidenceKind,
|
|
21
|
+
type PmxAxEvidenceKind,
|
|
22
|
+
type PmxAxReviewAnchorType,
|
|
23
|
+
type PmxAxReviewKind,
|
|
24
|
+
type PmxAxReviewSeverity,
|
|
25
|
+
type PmxAxWorkItemStatus,
|
|
26
|
+
} from '../../ax-state.js';
|
|
27
|
+
import { canvasState, type CanvasNodeState } from '../../canvas-state.js';
|
|
28
|
+
import { buildCodeGraphSummary } from '../../code-graph.js';
|
|
29
|
+
import { defineOperation, OperationError, type Operation, type OperationContext } from '../types.js';
|
|
30
|
+
import { normalizeAxNodeIds, normalizeAxSource } from './ax-shared.js';
|
|
31
|
+
import { isRecord } from './nodes.js';
|
|
32
|
+
|
|
33
|
+
// ── Activity-reaction validation (moved from server.ts) ───────
|
|
34
|
+
|
|
35
|
+
const AX_WORK_STATUSES = new Set(['todo', 'in-progress', 'blocked', 'done', 'cancelled']);
|
|
36
|
+
|
|
37
|
+
function normalizeAxWorkItemStatus(value: unknown): PmxAxWorkItemStatus | undefined {
|
|
38
|
+
return typeof value === 'string' && AX_WORK_STATUSES.has(value) ? (value as PmxAxWorkItemStatus) : undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isReviewSeverity(v: unknown): v is PmxAxReviewSeverity {
|
|
42
|
+
return v === 'info' || v === 'warning' || v === 'error';
|
|
43
|
+
}
|
|
44
|
+
function isReviewKind(v: unknown): v is PmxAxReviewKind {
|
|
45
|
+
return v === 'comment' || v === 'finding';
|
|
46
|
+
}
|
|
47
|
+
function isReviewAnchor(v: unknown): v is PmxAxReviewAnchorType {
|
|
48
|
+
return v === 'node' || v === 'file' || v === 'region';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Validate untrusted activity `reactions` from an HTTP body into the typed override
|
|
52
|
+
// shape ingestActivity expects. `false` suppresses a default reaction; an object
|
|
53
|
+
// overrides its fields (invalid fields are dropped, not stored raw).
|
|
54
|
+
function normalizeActivityReactions(input: Record<string, unknown>): {
|
|
55
|
+
workItem?: false | { status?: PmxAxWorkItemStatus; detail?: string | null };
|
|
56
|
+
evidence?: false | { kind?: PmxAxEvidenceKind; body?: string | null };
|
|
57
|
+
review?:
|
|
58
|
+
| false
|
|
59
|
+
| {
|
|
60
|
+
severity?: PmxAxReviewSeverity;
|
|
61
|
+
kind?: PmxAxReviewKind;
|
|
62
|
+
anchorType?: PmxAxReviewAnchorType;
|
|
63
|
+
nodeId?: string | null;
|
|
64
|
+
};
|
|
65
|
+
} {
|
|
66
|
+
const out: ReturnType<typeof normalizeActivityReactions> = {};
|
|
67
|
+
if (input.workItem === false) out.workItem = false;
|
|
68
|
+
else if (isRecord(input.workItem)) {
|
|
69
|
+
const status = normalizeAxWorkItemStatus(input.workItem.status);
|
|
70
|
+
out.workItem = {
|
|
71
|
+
...(status ? { status } : {}),
|
|
72
|
+
...(typeof input.workItem.detail === 'string' ? { detail: input.workItem.detail } : {}),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (input.evidence === false) out.evidence = false;
|
|
76
|
+
else if (isRecord(input.evidence)) {
|
|
77
|
+
out.evidence = {
|
|
78
|
+
...(isAxEvidenceKind(input.evidence.kind) ? { kind: input.evidence.kind } : {}),
|
|
79
|
+
...(typeof input.evidence.body === 'string' ? { body: input.evidence.body } : {}),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (input.review === false) out.review = false;
|
|
83
|
+
else if (isRecord(input.review)) {
|
|
84
|
+
out.review = {
|
|
85
|
+
...(isReviewSeverity(input.review.severity) ? { severity: input.review.severity } : {}),
|
|
86
|
+
...(isReviewKind(input.review.kind) ? { kind: input.review.kind } : {}),
|
|
87
|
+
...(isReviewAnchor(input.review.anchorType) ? { anchorType: input.review.anchorType } : {}),
|
|
88
|
+
...(typeof input.review.nodeId === 'string' ? { nodeId: input.review.nodeId } : {}),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ── GET list/read factory ─────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
const emptyShape = {};
|
|
97
|
+
const emptySchema = z.looseObject(emptyShape);
|
|
98
|
+
|
|
99
|
+
/** A no-input GET whose body is `{ ok: true, ...read() }` (legacy list shape). */
|
|
100
|
+
function defineAxListOperation(name: string, path: string, read: () => Record<string, unknown>): Operation {
|
|
101
|
+
return defineOperation<z.infer<typeof emptySchema>, Record<string, unknown>>({
|
|
102
|
+
name,
|
|
103
|
+
mutates: false,
|
|
104
|
+
input: emptySchema,
|
|
105
|
+
inputShape: emptyShape,
|
|
106
|
+
http: {
|
|
107
|
+
method: 'GET',
|
|
108
|
+
path,
|
|
109
|
+
},
|
|
110
|
+
handler: () => ({ ok: true, ...read() }),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ── ax.context.get / ax.surface-snapshot.get ──────────────────
|
|
115
|
+
|
|
116
|
+
const axContextShape = {
|
|
117
|
+
consumer: z.unknown().optional().describe('Optional consumer label filtering the pending-delivery lead block'),
|
|
118
|
+
};
|
|
119
|
+
const axContextSchema = z.looseObject(axContextShape);
|
|
120
|
+
|
|
121
|
+
const axContextGetOperation = defineOperation<z.infer<typeof axContextSchema>, Record<string, unknown>>({
|
|
122
|
+
name: 'ax.context.get',
|
|
123
|
+
mutates: false,
|
|
124
|
+
input: axContextSchema,
|
|
125
|
+
inputShape: axContextShape,
|
|
126
|
+
http: {
|
|
127
|
+
method: 'GET',
|
|
128
|
+
path: '/api/canvas/ax/context',
|
|
129
|
+
},
|
|
130
|
+
// Optional ?consumer= filters the compact `delivery` lead block (loop-safe — a
|
|
131
|
+
// consumer never sees steering/activity it originated), so a host adapter can
|
|
132
|
+
// inject its own un-truncated pending block per turn (report #54 hardening).
|
|
133
|
+
handler: (input) => {
|
|
134
|
+
const consumer = typeof input.consumer === 'string' ? input.consumer : undefined;
|
|
135
|
+
return buildCanvasAxContext(consumer) as unknown as Record<string, unknown>;
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const axSurfaceSnapshotOperation = defineOperation<z.infer<typeof emptySchema>, Record<string, unknown>>({
|
|
140
|
+
name: 'ax.surface-snapshot.get',
|
|
141
|
+
mutates: false,
|
|
142
|
+
input: emptySchema,
|
|
143
|
+
inputShape: emptyShape,
|
|
144
|
+
http: {
|
|
145
|
+
method: 'GET',
|
|
146
|
+
path: '/api/canvas/ax/surface-snapshot',
|
|
147
|
+
},
|
|
148
|
+
// Compact AX state for surfaces (the same shape seeded into AX-enabled iframes).
|
|
149
|
+
// The client fetches this and pushes it to surfaces over the ax-update channel.
|
|
150
|
+
handler: () => buildCanvasAxSurfaceSnapshot() as unknown as Record<string, unknown>,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// ── pinned-context.get ────────────────────────────────────────
|
|
154
|
+
|
|
155
|
+
const pinnedContextOperation = defineOperation<z.infer<typeof emptySchema>, Record<string, unknown>>({
|
|
156
|
+
name: 'pinned-context.get',
|
|
157
|
+
mutates: false,
|
|
158
|
+
input: emptySchema,
|
|
159
|
+
inputShape: emptyShape,
|
|
160
|
+
http: {
|
|
161
|
+
method: 'GET',
|
|
162
|
+
path: '/api/canvas/pinned-context',
|
|
163
|
+
},
|
|
164
|
+
handler: () => {
|
|
165
|
+
const pinnedIds = Array.from(canvasState.contextPinnedNodeIds);
|
|
166
|
+
const nodes = pinnedIds
|
|
167
|
+
.map((id) => canvasState.getNode(id))
|
|
168
|
+
.filter((node): node is CanvasNodeState => node !== undefined);
|
|
169
|
+
const preamble =
|
|
170
|
+
pinnedIds.length > 0 ? buildAgentContextPreamble(nodes, { defaultTextLength: 700, webpageTextLength: 1600 }) : '';
|
|
171
|
+
const serialized = nodes.map((node) =>
|
|
172
|
+
serializeNodeForAgentContext(node, {
|
|
173
|
+
defaultTextLength: 700,
|
|
174
|
+
webpageTextLength: 1600,
|
|
175
|
+
includePosition: true,
|
|
176
|
+
}),
|
|
177
|
+
);
|
|
178
|
+
return { preamble, nodeIds: pinnedIds, count: pinnedIds.length, nodes: serialized };
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// ── code-graph.get ────────────────────────────────────────────
|
|
183
|
+
|
|
184
|
+
const codeGraphOperation = defineOperation<z.infer<typeof emptySchema>, Record<string, unknown>>({
|
|
185
|
+
name: 'code-graph.get',
|
|
186
|
+
mutates: false,
|
|
187
|
+
input: emptySchema,
|
|
188
|
+
inputShape: emptyShape,
|
|
189
|
+
http: {
|
|
190
|
+
method: 'GET',
|
|
191
|
+
path: '/api/canvas/code-graph',
|
|
192
|
+
},
|
|
193
|
+
handler: () => buildCodeGraphSummary() as unknown as Record<string, unknown>,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// ── ax.activity.ingest ────────────────────────────────────────
|
|
197
|
+
|
|
198
|
+
const activityShape = {
|
|
199
|
+
kind: z
|
|
200
|
+
.unknown()
|
|
201
|
+
.optional()
|
|
202
|
+
.describe('Activity kind: tool-start, tool-result, failure, error, session-start, session-end, command, note'),
|
|
203
|
+
title: z.unknown().optional().describe('Activity title'),
|
|
204
|
+
summary: z.unknown().optional(),
|
|
205
|
+
outcome: z.unknown().optional().describe('success or failure'),
|
|
206
|
+
ref: z.unknown().optional().describe('File path, URL, or commit the activity refers to'),
|
|
207
|
+
nodeIds: z.unknown().optional(),
|
|
208
|
+
data: z.unknown().optional(),
|
|
209
|
+
reactions: z.unknown().optional().describe('Override or suppress the kind-driven default reactions'),
|
|
210
|
+
source: z.unknown().optional(),
|
|
211
|
+
};
|
|
212
|
+
const activitySchema = z.looseObject(activityShape);
|
|
213
|
+
|
|
214
|
+
// Report primitive A: ingest a harness-forwarded agent activity; the board auto-reacts.
|
|
215
|
+
const activityIngestOperation = defineOperation<z.infer<typeof activitySchema>, Record<string, unknown>>({
|
|
216
|
+
name: 'ax.activity.ingest',
|
|
217
|
+
mutates: false,
|
|
218
|
+
input: activitySchema,
|
|
219
|
+
inputShape: activityShape,
|
|
220
|
+
http: {
|
|
221
|
+
method: 'POST',
|
|
222
|
+
path: '/api/canvas/ax/activity',
|
|
223
|
+
},
|
|
224
|
+
handler: (input, ctx: OperationContext) => {
|
|
225
|
+
const body: Record<string, unknown> = input;
|
|
226
|
+
if (!isAxActivityKind(body.kind)) {
|
|
227
|
+
throw new OperationError(
|
|
228
|
+
"activity requires a valid 'kind': one of tool-start, tool-result, failure, error, session-start, session-end, command, note.",
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
if (typeof body.title !== 'string' || !body.title.trim()) {
|
|
232
|
+
throw new OperationError('activity requires a title.');
|
|
233
|
+
}
|
|
234
|
+
const result = canvasState.ingestActivity(
|
|
235
|
+
{
|
|
236
|
+
kind: body.kind,
|
|
237
|
+
title: body.title,
|
|
238
|
+
...(typeof body.summary === 'string' ? { summary: body.summary } : {}),
|
|
239
|
+
...(body.outcome === 'success' || body.outcome === 'failure' ? { outcome: body.outcome } : {}),
|
|
240
|
+
...(typeof body.ref === 'string' ? { ref: body.ref } : {}),
|
|
241
|
+
...(Array.isArray(body.nodeIds) ? { nodeIds: normalizeAxNodeIds(body.nodeIds) } : {}),
|
|
242
|
+
...(isRecord(body.data) ? { data: body.data } : {}),
|
|
243
|
+
...(isRecord(body.reactions) ? { reactions: normalizeActivityReactions(body.reactions) } : {}),
|
|
244
|
+
},
|
|
245
|
+
{ source: normalizeAxSource(body.source, 'api') },
|
|
246
|
+
);
|
|
247
|
+
ctx.emit('ax-event-created', { event: result.event });
|
|
248
|
+
if (result.workItem) ctx.emit('ax-state-changed', { workItem: result.workItem });
|
|
249
|
+
if (result.evidence) ctx.emit('ax-event-created', { evidence: result.evidence });
|
|
250
|
+
if (result.review) ctx.emit('ax-state-changed', { reviewAnnotation: result.review });
|
|
251
|
+
return { ok: true, ...result };
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// ── ax.interaction.submit ─────────────────────────────────────
|
|
256
|
+
|
|
257
|
+
const interactionShape = {
|
|
258
|
+
type: z.unknown().optional().describe('Interaction type, e.g. ax.work.create'),
|
|
259
|
+
sourceNodeId: z.unknown().optional(),
|
|
260
|
+
payload: z.unknown().optional(),
|
|
261
|
+
sourceSurface: z.unknown().optional(),
|
|
262
|
+
correlationId: z.unknown().optional(),
|
|
263
|
+
source: z.unknown().optional(),
|
|
264
|
+
};
|
|
265
|
+
const interactionSchema = z.looseObject(interactionShape);
|
|
266
|
+
|
|
267
|
+
const interactionOperation = defineOperation<z.infer<typeof interactionSchema>, Record<string, unknown>>({
|
|
268
|
+
name: 'ax.interaction.submit',
|
|
269
|
+
mutates: false,
|
|
270
|
+
input: interactionSchema,
|
|
271
|
+
inputShape: interactionShape,
|
|
272
|
+
http: {
|
|
273
|
+
method: 'POST',
|
|
274
|
+
path: '/api/canvas/ax/interaction',
|
|
275
|
+
// Legacy wire: 200 when accepted, the envelope's own status when refused.
|
|
276
|
+
status: (result) =>
|
|
277
|
+
isRecord(result) && result.ok !== true && typeof result.status === 'number' ? result.status : 200,
|
|
278
|
+
},
|
|
279
|
+
handler: (input, ctx: OperationContext) => {
|
|
280
|
+
const body: Record<string, unknown> = input;
|
|
281
|
+
const { result, events } = applyAxInteraction(canvasState, body, normalizeAxSource(body.source, 'api'));
|
|
282
|
+
for (const e of events) {
|
|
283
|
+
ctx.emit(e.event, e.payload);
|
|
284
|
+
}
|
|
285
|
+
return result as unknown as Record<string, unknown>;
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// ── ax.state.patch (legacy PATCH /api/canvas/ax focus shape) ──
|
|
290
|
+
|
|
291
|
+
const statePatchShape = {
|
|
292
|
+
focus: z.unknown().optional().describe('Focus object: { nodeIds, source? }'),
|
|
293
|
+
};
|
|
294
|
+
const statePatchSchema = z.looseObject(statePatchShape);
|
|
295
|
+
|
|
296
|
+
const statePatchOperation = defineOperation<z.infer<typeof statePatchSchema>, Record<string, unknown>>({
|
|
297
|
+
name: 'ax.state.patch',
|
|
298
|
+
mutates: false,
|
|
299
|
+
input: statePatchSchema,
|
|
300
|
+
inputShape: statePatchShape,
|
|
301
|
+
http: {
|
|
302
|
+
method: 'PATCH',
|
|
303
|
+
path: '/api/canvas/ax',
|
|
304
|
+
},
|
|
305
|
+
handler: (input, ctx: OperationContext) => {
|
|
306
|
+
const body: Record<string, unknown> = input;
|
|
307
|
+
if (!body.focus || typeof body.focus !== 'object' || Array.isArray(body.focus)) {
|
|
308
|
+
throw new OperationError('PATCH /api/canvas/ax currently requires a focus object.');
|
|
309
|
+
}
|
|
310
|
+
const focusInput = body.focus as Record<string, unknown>;
|
|
311
|
+
const focus = canvasState.setAxFocus(normalizeAxNodeIds(focusInput.nodeIds), {
|
|
312
|
+
source: normalizeAxSource(focusInput.source, 'api'),
|
|
313
|
+
});
|
|
314
|
+
ctx.emit('ax-state-changed', { focus });
|
|
315
|
+
return { ok: true, state: canvasState.getAxState() };
|
|
316
|
+
},
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
export const axReadOperations: Operation[] = [
|
|
320
|
+
defineAxListOperation('ax.work.list', '/api/canvas/ax/work', () => ({ workItems: canvasState.getWorkItems() })),
|
|
321
|
+
defineAxListOperation('ax.approval.list', '/api/canvas/ax/approval', () => ({
|
|
322
|
+
approvalGates: canvasState.getApprovalGates(),
|
|
323
|
+
})),
|
|
324
|
+
defineAxListOperation('ax.review.list', '/api/canvas/ax/review', () => ({
|
|
325
|
+
reviewAnnotations: canvasState.getReviewAnnotations(),
|
|
326
|
+
})),
|
|
327
|
+
defineAxListOperation('ax.elicitation.list', '/api/canvas/ax/elicitation', () => ({
|
|
328
|
+
elicitations: canvasState.getElicitations(),
|
|
329
|
+
})),
|
|
330
|
+
defineAxListOperation('ax.mode.list', '/api/canvas/ax/mode', () => ({
|
|
331
|
+
modeRequests: canvasState.getModeRequests(),
|
|
332
|
+
})),
|
|
333
|
+
defineAxListOperation('ax.command.list', '/api/canvas/ax/command', () => ({
|
|
334
|
+
commands: canvasState.getCommandRegistry(),
|
|
335
|
+
})),
|
|
336
|
+
defineAxListOperation('ax.policy.get', '/api/canvas/ax/policy', () => ({ policy: canvasState.getPolicy() })),
|
|
337
|
+
defineAxListOperation('ax.host-capability.get', '/api/canvas/ax/host-capability', () => ({
|
|
338
|
+
host: canvasState.getHostCapability(),
|
|
339
|
+
})),
|
|
340
|
+
axContextGetOperation,
|
|
341
|
+
axSurfaceSnapshotOperation,
|
|
342
|
+
pinnedContextOperation,
|
|
343
|
+
codeGraphOperation,
|
|
344
|
+
activityIngestOperation,
|
|
345
|
+
interactionOperation,
|
|
346
|
+
statePatchOperation,
|
|
347
|
+
];
|