pmx-canvas 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/dist/canvas/index.js +2 -2
- 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/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/docs/http-api.md +28 -0
- package/docs/plans/plan-009-tech-debt-backlog.md +5 -5
- package/docs/tech-debt-assessment-2026-07.md +2 -2
- package/package.json +5 -2
- package/skills/pmx-canvas/SKILL.md +3 -1
- package/skills/pmx-canvas/references/full-reference.md +10 -3
- package/src/cli/agent.ts +1861 -1548
- package/src/cli/daemon.ts +460 -0
- package/src/cli/index.ts +63 -326
- 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 +60 -39
- 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 +177 -120
- 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 +39 -14
- 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 +108 -1027
- 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
package/src/mcp/server.ts
CHANGED
|
@@ -197,12 +197,19 @@ function compactNodePayload(node: Awaited<ReturnType<CanvasAccess['getNode']>>):
|
|
|
197
197
|
};
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
-
function compactLayoutPayload(
|
|
200
|
+
function compactLayoutPayload(
|
|
201
|
+
layout: Awaited<ReturnType<CanvasAccess['getLayout']>>,
|
|
202
|
+
pinnedIds: string[],
|
|
203
|
+
): Record<string, unknown> {
|
|
201
204
|
return {
|
|
202
205
|
summary: buildSummaryFromLayout(layout, pinnedIds),
|
|
203
206
|
viewport: layout.viewport,
|
|
204
|
-
annotations: (layout.annotations ?? []).map((annotation) =>
|
|
205
|
-
|
|
207
|
+
annotations: (layout.annotations ?? []).map((annotation) =>
|
|
208
|
+
summarizeCanvasAnnotationForContext(annotation, layout.nodes),
|
|
209
|
+
),
|
|
210
|
+
nodes: layout.nodes
|
|
211
|
+
.map((node) => compactNodePayload(node))
|
|
212
|
+
.filter((node): node is Record<string, unknown> => node !== null),
|
|
206
213
|
edges: layout.edges.map((edge) => ({
|
|
207
214
|
id: edge.id,
|
|
208
215
|
from: edge.from,
|
|
@@ -218,11 +225,16 @@ function compactLayoutPayload(layout: Awaited<ReturnType<CanvasAccess['getLayout
|
|
|
218
225
|
function agentSafeFullLayoutPayload(layout: Awaited<ReturnType<CanvasAccess['getLayout']>>): Record<string, unknown> {
|
|
219
226
|
return {
|
|
220
227
|
...serializeCanvasLayoutForAgent(layout),
|
|
221
|
-
annotations: (layout.annotations ?? []).map((annotation) =>
|
|
228
|
+
annotations: (layout.annotations ?? []).map((annotation) =>
|
|
229
|
+
summarizeCanvasAnnotationForContext(annotation, layout.nodes),
|
|
230
|
+
),
|
|
222
231
|
};
|
|
223
232
|
}
|
|
224
233
|
|
|
225
|
-
function buildSummaryFromLayout(
|
|
234
|
+
function buildSummaryFromLayout(
|
|
235
|
+
layout: Awaited<ReturnType<CanvasAccess['getLayout']>>,
|
|
236
|
+
pinnedIds: string[],
|
|
237
|
+
): Record<string, unknown> {
|
|
226
238
|
const pinned = new Set(pinnedIds);
|
|
227
239
|
const nodesByType: Record<string, number> = {};
|
|
228
240
|
const pinnedTitles: string[] = [];
|
|
@@ -235,7 +247,9 @@ function buildSummaryFromLayout(layout: Awaited<ReturnType<CanvasAccess['getLayo
|
|
|
235
247
|
totalNodes: layout.nodes.length,
|
|
236
248
|
totalEdges: layout.edges.length,
|
|
237
249
|
totalAnnotations: (layout.annotations ?? []).length,
|
|
238
|
-
annotations: (layout.annotations ?? []).map((annotation) =>
|
|
250
|
+
annotations: (layout.annotations ?? []).map((annotation) =>
|
|
251
|
+
summarizeCanvasAnnotationForContext(annotation, layout.nodes),
|
|
252
|
+
),
|
|
239
253
|
nodesByType,
|
|
240
254
|
pinnedCount: pinned.size,
|
|
241
255
|
pinnedTitles,
|
|
@@ -276,12 +290,18 @@ export async function startMcpServer(): Promise<void> {
|
|
|
276
290
|
'canvas_ax_interaction',
|
|
277
291
|
'Submit a node-originated AX interaction: a capability-gated, validated event from an eligible node that maps onto an AX operation (work item, evidence, approval, review, focus, steering, event). Returns { ok: false, code } if the node type/metadata does not allow the interaction type or the payload is invalid.',
|
|
278
292
|
{
|
|
279
|
-
type: z
|
|
293
|
+
type: z
|
|
294
|
+
.enum(AX_INTERACTION_TYPES)
|
|
295
|
+
.describe('Interaction type, e.g. ax.work.create, ax.evidence.add, ax.focus.set.'),
|
|
280
296
|
sourceNodeId: z.string().describe('The node emitting the interaction.'),
|
|
281
|
-
payload: z
|
|
297
|
+
payload: z
|
|
298
|
+
.record(z.string(), z.unknown())
|
|
299
|
+
.optional()
|
|
300
|
+
.describe('Type-specific payload, e.g. {"title":"..."} for ax.work.create.'),
|
|
282
301
|
sourceSurface: z.enum(['native-node', 'json-render', 'html-node', 'mcp-app', 'adapter']).optional(),
|
|
283
302
|
correlationId: z.string().optional(),
|
|
284
|
-
source: z
|
|
303
|
+
source: z
|
|
304
|
+
.enum(['agent', 'api', 'browser', 'cli', 'codex', 'copilot', 'mcp', 'sdk', 'system'])
|
|
285
305
|
.optional()
|
|
286
306
|
.describe('Optional host/source label. Defaults to mcp.'),
|
|
287
307
|
},
|
|
@@ -319,29 +339,59 @@ export async function startMcpServer(): Promise<void> {
|
|
|
319
339
|
'canvas_ingest_activity',
|
|
320
340
|
'Ingest a normalized agent activity (a tool/session event your harness forwards) so the board reacts automatically — primitive A, makes AX bidirectional. Always records a timeline event; kind-driven default reactions (overridable per call via `reactions`): failure/error → work item (blocked) + review finding + evidence (logs); tool-result + outcome:"success" → evidence (tool-result); everything else (tool-start, session-*, command, note) → event only. Set any reaction to false to suppress it, or to an object to override its fields. Returns { event, workItem, evidence, review }.',
|
|
321
341
|
{
|
|
322
|
-
kind: z.enum([
|
|
342
|
+
kind: z.enum([
|
|
343
|
+
'tool-start',
|
|
344
|
+
'tool-result',
|
|
345
|
+
'failure',
|
|
346
|
+
'error',
|
|
347
|
+
'session-start',
|
|
348
|
+
'session-end',
|
|
349
|
+
'command',
|
|
350
|
+
'note',
|
|
351
|
+
]),
|
|
323
352
|
title: z.string(),
|
|
324
353
|
summary: z.string().optional(),
|
|
325
354
|
outcome: z.enum(['success', 'failure']).optional(),
|
|
326
|
-
ref: z
|
|
355
|
+
ref: z
|
|
356
|
+
.string()
|
|
357
|
+
.optional()
|
|
358
|
+
.describe('A file path, URL, or commit the activity refers to (used as the review file anchor for failures).'),
|
|
327
359
|
nodeIds: z.array(z.string()).optional(),
|
|
328
360
|
data: z.record(z.string(), z.unknown()).optional(),
|
|
329
|
-
reactions: z
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
361
|
+
reactions: z
|
|
362
|
+
.object({
|
|
363
|
+
workItem: z
|
|
364
|
+
.union([
|
|
365
|
+
z.literal(false),
|
|
366
|
+
z.object({
|
|
367
|
+
status: z.enum(['todo', 'in-progress', 'blocked', 'done', 'cancelled']).optional(),
|
|
368
|
+
detail: z.string().nullable().optional(),
|
|
369
|
+
}),
|
|
370
|
+
])
|
|
371
|
+
.optional(),
|
|
372
|
+
evidence: z
|
|
373
|
+
.union([
|
|
374
|
+
z.literal(false),
|
|
375
|
+
z.object({
|
|
376
|
+
kind: z.enum(['logs', 'tool-result', 'screenshot', 'file', 'diff', 'test-output']).optional(),
|
|
377
|
+
body: z.string().nullable().optional(),
|
|
378
|
+
}),
|
|
379
|
+
])
|
|
380
|
+
.optional(),
|
|
381
|
+
review: z
|
|
382
|
+
.union([
|
|
383
|
+
z.literal(false),
|
|
384
|
+
z.object({
|
|
385
|
+
severity: z.enum(['info', 'warning', 'error']).optional(),
|
|
386
|
+
kind: z.enum(['comment', 'finding']).optional(),
|
|
387
|
+
anchorType: z.enum(['node', 'file', 'region']).optional(),
|
|
388
|
+
nodeId: z.string().nullable().optional(),
|
|
389
|
+
}),
|
|
390
|
+
])
|
|
391
|
+
.optional(),
|
|
392
|
+
})
|
|
393
|
+
.optional()
|
|
394
|
+
.describe('Override or suppress the kind-driven default reactions.'),
|
|
345
395
|
source: z.enum(['agent', 'api', 'browser', 'cli', 'codex', 'copilot', 'mcp', 'sdk', 'system']).optional(),
|
|
346
396
|
},
|
|
347
397
|
async ({ kind, title, summary, outcome, ref, nodeIds, data, reactions, source }) => {
|
|
@@ -391,7 +441,10 @@ export async function startMcpServer(): Promise<void> {
|
|
|
391
441
|
'canvas_screenshot',
|
|
392
442
|
'Capture a screenshot from the active Bun.WebView automation session. Returns both an MCP image payload and JSON metadata. Requires an active automation session started via canvas_webview with action "start".',
|
|
393
443
|
{
|
|
394
|
-
format: z
|
|
444
|
+
format: z
|
|
445
|
+
.enum(['png', 'jpeg', 'webp'])
|
|
446
|
+
.optional()
|
|
447
|
+
.describe('Screenshot format (default depends on Bun; png recommended)'),
|
|
395
448
|
quality: z.number().optional().describe('Optional quality for lossy formats'),
|
|
396
449
|
},
|
|
397
450
|
async ({ format, quality }) => {
|
|
@@ -407,27 +460,21 @@ export async function startMcpServer(): Promise<void> {
|
|
|
407
460
|
{
|
|
408
461
|
type: 'image',
|
|
409
462
|
data: encodeBase64(bytes),
|
|
410
|
-
mimeType:
|
|
411
|
-
format === 'jpeg'
|
|
412
|
-
? 'image/jpeg'
|
|
413
|
-
: format === 'webp'
|
|
414
|
-
? 'image/webp'
|
|
415
|
-
: 'image/png',
|
|
463
|
+
mimeType: format === 'jpeg' ? 'image/jpeg' : format === 'webp' ? 'image/webp' : 'image/png',
|
|
416
464
|
},
|
|
417
465
|
{
|
|
418
466
|
type: 'text',
|
|
419
|
-
text: JSON.stringify(
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
format === 'jpeg'
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
}, null, 2),
|
|
467
|
+
text: JSON.stringify(
|
|
468
|
+
{
|
|
469
|
+
bytes: bytes.byteLength,
|
|
470
|
+
backend: status.backend,
|
|
471
|
+
width: status.width,
|
|
472
|
+
height: status.height,
|
|
473
|
+
mimeType: format === 'jpeg' ? 'image/jpeg' : format === 'webp' ? 'image/webp' : 'image/png',
|
|
474
|
+
},
|
|
475
|
+
null,
|
|
476
|
+
2,
|
|
477
|
+
),
|
|
431
478
|
},
|
|
432
479
|
],
|
|
433
480
|
};
|
|
@@ -449,8 +496,7 @@ export async function startMcpServer(): Promise<void> {
|
|
|
449
496
|
'schema',
|
|
450
497
|
'canvas://schema',
|
|
451
498
|
{
|
|
452
|
-
description:
|
|
453
|
-
`Machine-readable create schemas, canonical examples, json-render catalog details, and MCP node-type routing from the running PMX Canvas server version. Routing: ${structuredSchemaDescription()}.`,
|
|
499
|
+
description: `Machine-readable create schemas, canonical examples, json-render catalog details, and MCP node-type routing from the running PMX Canvas server version. Routing: ${structuredSchemaDescription()}.`,
|
|
454
500
|
mimeType: 'application/json',
|
|
455
501
|
},
|
|
456
502
|
async () => ({
|
|
@@ -480,20 +526,20 @@ export async function startMcpServer(): Promise<void> {
|
|
|
480
526
|
const layout = await c.getLayout();
|
|
481
527
|
|
|
482
528
|
const pinnedNodes = layout.nodes.filter((n) => pinnedIds.has(n.id));
|
|
483
|
-
const pinnedEdges = layout.edges.filter(
|
|
484
|
-
(e) => pinnedIds.has(e.from) && pinnedIds.has(e.to),
|
|
485
|
-
);
|
|
529
|
+
const pinnedEdges = layout.edges.filter((e) => pinnedIds.has(e.from) && pinnedIds.has(e.to));
|
|
486
530
|
|
|
487
531
|
// Compute neighborhoods: for each pinned node, find nearby unpinned nodes
|
|
488
532
|
const neighborhoods = findNeighborhoods(layout.nodes, pinnedIds);
|
|
489
533
|
|
|
490
534
|
const context = {
|
|
491
535
|
pinnedCount: pinnedNodes.length,
|
|
492
|
-
nodes: pinnedNodes.map((n) =>
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
536
|
+
nodes: pinnedNodes.map((n) =>
|
|
537
|
+
serializeNodeForAgentContext(n, {
|
|
538
|
+
defaultTextLength: 700,
|
|
539
|
+
webpageTextLength: 1600,
|
|
540
|
+
includePosition: true,
|
|
541
|
+
}),
|
|
542
|
+
),
|
|
497
543
|
edges: pinnedEdges.map((e) => ({
|
|
498
544
|
id: e.id,
|
|
499
545
|
from: e.from,
|
|
@@ -547,8 +593,7 @@ export async function startMcpServer(): Promise<void> {
|
|
|
547
593
|
'ax-context',
|
|
548
594
|
'canvas://ax-context',
|
|
549
595
|
{
|
|
550
|
-
description:
|
|
551
|
-
'Agent-ready PMX AX context combining pinned context, focus, and surface metadata.',
|
|
596
|
+
description: 'Agent-ready PMX AX context combining pinned context, focus, and surface metadata.',
|
|
552
597
|
mimeType: 'application/json',
|
|
553
598
|
},
|
|
554
599
|
async () => {
|
|
@@ -606,14 +651,18 @@ export async function startMcpServer(): Promise<void> {
|
|
|
606
651
|
{
|
|
607
652
|
uri: 'canvas://ax-work',
|
|
608
653
|
mimeType: 'application/json',
|
|
609
|
-
text: JSON.stringify(
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
654
|
+
text: JSON.stringify(
|
|
655
|
+
{
|
|
656
|
+
workItems,
|
|
657
|
+
approvalGates,
|
|
658
|
+
reviewAnnotations: state.reviewAnnotations,
|
|
659
|
+
elicitations: state.elicitations,
|
|
660
|
+
modeRequests: state.modeRequests,
|
|
661
|
+
policy: state.policy,
|
|
662
|
+
},
|
|
663
|
+
null,
|
|
664
|
+
2,
|
|
665
|
+
),
|
|
617
666
|
},
|
|
618
667
|
],
|
|
619
668
|
};
|
|
@@ -634,7 +683,11 @@ export async function startMcpServer(): Promise<void> {
|
|
|
634
683
|
const pendingActivity = buildPendingAxActivity(state);
|
|
635
684
|
return {
|
|
636
685
|
contents: [
|
|
637
|
-
{
|
|
686
|
+
{
|
|
687
|
+
uri: 'canvas://ax-pending-steering',
|
|
688
|
+
mimeType: 'application/json',
|
|
689
|
+
text: JSON.stringify({ pending, pendingActivity }, null, 2),
|
|
690
|
+
},
|
|
638
691
|
],
|
|
639
692
|
};
|
|
640
693
|
},
|
|
@@ -653,7 +706,11 @@ export async function startMcpServer(): Promise<void> {
|
|
|
653
706
|
const timeline = await c.getAxTimeline();
|
|
654
707
|
return {
|
|
655
708
|
contents: [
|
|
656
|
-
{
|
|
709
|
+
{
|
|
710
|
+
uri: 'canvas://ax-delivery',
|
|
711
|
+
mimeType: 'application/json',
|
|
712
|
+
text: JSON.stringify({ steering: timeline.steering }, null, 2),
|
|
713
|
+
},
|
|
657
714
|
],
|
|
658
715
|
};
|
|
659
716
|
},
|
|
@@ -732,13 +789,18 @@ export async function startMcpServer(): Promise<void> {
|
|
|
732
789
|
'grouped together), provides reading order (top-left to bottom-right), and shows ' +
|
|
733
790
|
'neighborhoods around pinned nodes (nearby unpinned nodes the human implicitly associated). ' +
|
|
734
791
|
'This makes "spatial arrangement is communication" real — read this to understand the ' +
|
|
735
|
-
|
|
792
|
+
"human's spatial intent, not just which nodes are pinned.",
|
|
736
793
|
mimeType: 'application/json',
|
|
737
794
|
},
|
|
738
795
|
async () => {
|
|
739
796
|
const c = await ensureCanvas();
|
|
740
797
|
const layout = await c.getLayout();
|
|
741
|
-
const spatial = buildSpatialContext(
|
|
798
|
+
const spatial = buildSpatialContext(
|
|
799
|
+
layout.nodes,
|
|
800
|
+
layout.edges,
|
|
801
|
+
new Set(await c.getPinnedNodeIds()),
|
|
802
|
+
layout.annotations ?? [],
|
|
803
|
+
);
|
|
742
804
|
return {
|
|
743
805
|
contents: [
|
|
744
806
|
{
|
|
@@ -56,11 +56,8 @@ function summarizeWebpageData(data: Record<string, unknown>, maxLength: number):
|
|
|
56
56
|
const status = typeof data.status === 'string' ? data.status : '';
|
|
57
57
|
const statusCode = typeof data.statusCode === 'number' ? data.statusCode : null;
|
|
58
58
|
const error = typeof data.error === 'string' ? data.error : '';
|
|
59
|
-
const content =
|
|
60
|
-
? data.content
|
|
61
|
-
: typeof data.excerpt === 'string'
|
|
62
|
-
? data.excerpt
|
|
63
|
-
: '';
|
|
59
|
+
const content =
|
|
60
|
+
typeof data.content === 'string' ? data.content : typeof data.excerpt === 'string' ? data.excerpt : '';
|
|
64
61
|
|
|
65
62
|
if (url) parts.push(`URL: ${url}`);
|
|
66
63
|
if (pageTitle) parts.push(`Title: ${pageTitle}`);
|
|
@@ -132,9 +129,7 @@ function summarizeWebArtifactData(data: Record<string, unknown>, maxLength: numb
|
|
|
132
129
|
const sourceFiles = Array.isArray(data.sourceFiles)
|
|
133
130
|
? data.sourceFiles.filter((file): file is string => typeof file === 'string')
|
|
134
131
|
: [];
|
|
135
|
-
const deps = Array.isArray(data.deps)
|
|
136
|
-
? data.deps.filter((dep): dep is string => typeof dep === 'string')
|
|
137
|
-
: [];
|
|
132
|
+
const deps = Array.isArray(data.deps) ? data.deps.filter((dep): dep is string => typeof dep === 'string') : [];
|
|
138
133
|
|
|
139
134
|
if (content) parts.push(content);
|
|
140
135
|
if (!content && title) parts.push(`Web artifact: ${title}`);
|
|
@@ -170,7 +165,16 @@ function metadataForNode(node: CanvasNodeState): Record<string, unknown> | undef
|
|
|
170
165
|
switch (node.type) {
|
|
171
166
|
case 'webpage': {
|
|
172
167
|
const metadata: Record<string, unknown> = {};
|
|
173
|
-
for (const key of [
|
|
168
|
+
for (const key of [
|
|
169
|
+
'url',
|
|
170
|
+
'pageTitle',
|
|
171
|
+
'description',
|
|
172
|
+
'imageUrl',
|
|
173
|
+
'fetchedAt',
|
|
174
|
+
'status',
|
|
175
|
+
'statusCode',
|
|
176
|
+
'contentType',
|
|
177
|
+
]) {
|
|
174
178
|
const value = node.data[key];
|
|
175
179
|
if (value !== undefined && value !== null && value !== '') metadata[key] = value;
|
|
176
180
|
}
|
|
@@ -194,7 +198,19 @@ function metadataForNode(node: CanvasNodeState): Record<string, unknown> | undef
|
|
|
194
198
|
}
|
|
195
199
|
case 'html': {
|
|
196
200
|
const metadata: Record<string, unknown> = {};
|
|
197
|
-
for (const key of [
|
|
201
|
+
for (const key of [
|
|
202
|
+
'summary',
|
|
203
|
+
'description',
|
|
204
|
+
'agentSummary',
|
|
205
|
+
'contentSummary',
|
|
206
|
+
'htmlPrimitive',
|
|
207
|
+
'presentation',
|
|
208
|
+
'slideCount',
|
|
209
|
+
'slideTitles',
|
|
210
|
+
'speakerNotes',
|
|
211
|
+
'embeddedNodeIds',
|
|
212
|
+
'embeddedUrls',
|
|
213
|
+
]) {
|
|
198
214
|
const value = node.data[key];
|
|
199
215
|
if (Array.isArray(value)) {
|
|
200
216
|
if (value.length > 0) metadata[key] = value;
|
|
@@ -206,7 +222,22 @@ function metadataForNode(node: CanvasNodeState): Record<string, unknown> | undef
|
|
|
206
222
|
}
|
|
207
223
|
case 'mcp-app': {
|
|
208
224
|
const metadata: Record<string, unknown> = {};
|
|
209
|
-
for (const key of [
|
|
225
|
+
for (const key of [
|
|
226
|
+
'url',
|
|
227
|
+
'path',
|
|
228
|
+
'mode',
|
|
229
|
+
'hostMode',
|
|
230
|
+
'viewerType',
|
|
231
|
+
'serverName',
|
|
232
|
+
'toolName',
|
|
233
|
+
'resourceUri',
|
|
234
|
+
'sessionStatus',
|
|
235
|
+
'projectPath',
|
|
236
|
+
'artifactBytes',
|
|
237
|
+
'sourceFiles',
|
|
238
|
+
'sourceFileCount',
|
|
239
|
+
'deps',
|
|
240
|
+
]) {
|
|
210
241
|
const value = node.data[key];
|
|
211
242
|
if (Array.isArray(value)) {
|
|
212
243
|
if (value.length > 0) metadata[key] = value;
|
|
@@ -221,10 +252,7 @@ function metadataForNode(node: CanvasNodeState): Record<string, unknown> | undef
|
|
|
221
252
|
}
|
|
222
253
|
}
|
|
223
254
|
|
|
224
|
-
export function summarizeNodeForAgentContext(
|
|
225
|
-
node: CanvasNodeState,
|
|
226
|
-
options: AgentContextOptions = {},
|
|
227
|
-
): string {
|
|
255
|
+
export function summarizeNodeForAgentContext(node: CanvasNodeState, options: AgentContextOptions = {}): string {
|
|
228
256
|
const defaultTextLength = options.defaultTextLength ?? DEFAULT_CONTEXT_TEXT_LENGTH;
|
|
229
257
|
const webpageTextLength = options.webpageTextLength ?? DEFAULT_WEBPAGE_CONTEXT_TEXT_LENGTH;
|
|
230
258
|
|
|
@@ -241,9 +269,7 @@ export function summarizeNodeForAgentContext(
|
|
|
241
269
|
if (chartCfg) {
|
|
242
270
|
const chartTitle = (chartCfg.title as string) || 'Untitled chart';
|
|
243
271
|
const chartType = (chartCfg.type as string) || 'unknown';
|
|
244
|
-
const labels = Array.isArray(chartCfg.labels)
|
|
245
|
-
? (chartCfg.labels as string[]).join(', ')
|
|
246
|
-
: '';
|
|
272
|
+
const labels = Array.isArray(chartCfg.labels) ? (chartCfg.labels as string[]).join(', ') : '';
|
|
247
273
|
return truncateContextText(`Chart: ${chartTitle} (${chartType}). Labels: ${labels}`, defaultTextLength);
|
|
248
274
|
}
|
|
249
275
|
return summarizeMcpAppData(node.data, defaultTextLength);
|
|
@@ -263,14 +289,17 @@ export function summarizeNodeForAgentContext(
|
|
|
263
289
|
if (typeof node.data.htmlPrimitive === 'string') {
|
|
264
290
|
return summarizeHtmlPrimitiveData(node.data, defaultTextLength);
|
|
265
291
|
}
|
|
266
|
-
return stringifyContextValue(
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
292
|
+
return stringifyContextValue(
|
|
293
|
+
{
|
|
294
|
+
title: node.data.title,
|
|
295
|
+
description: node.data.description,
|
|
296
|
+
summary: node.data.summary,
|
|
297
|
+
contentSummary: node.data.contentSummary,
|
|
298
|
+
embeddedNodeIds: node.data.embeddedNodeIds,
|
|
299
|
+
embeddedUrls: node.data.embeddedUrls,
|
|
300
|
+
},
|
|
301
|
+
defaultTextLength,
|
|
302
|
+
);
|
|
274
303
|
}
|
|
275
304
|
case 'prompt':
|
|
276
305
|
case 'response': {
|
|
@@ -279,11 +308,12 @@ export function summarizeNodeForAgentContext(
|
|
|
279
308
|
}
|
|
280
309
|
case 'file': {
|
|
281
310
|
const path = typeof node.data.path === 'string' ? node.data.path : '';
|
|
282
|
-
const fileContent =
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
311
|
+
const fileContent =
|
|
312
|
+
typeof node.data.fileContent === 'string'
|
|
313
|
+
? node.data.fileContent
|
|
314
|
+
: typeof node.data.content === 'string'
|
|
315
|
+
? node.data.content
|
|
316
|
+
: '';
|
|
287
317
|
const prefix = path ? `Path: ${path}\n\n` : '';
|
|
288
318
|
const remaining = Math.max(0, defaultTextLength - prefix.length);
|
|
289
319
|
return `${prefix}${truncateContextText(fileContent, remaining)}`.trim();
|
|
@@ -309,13 +339,10 @@ export function serializeNodeForAgentContext(
|
|
|
309
339
|
};
|
|
310
340
|
}
|
|
311
341
|
|
|
312
|
-
export function buildAgentContextPreamble(
|
|
313
|
-
nodes: CanvasNodeState[],
|
|
314
|
-
options: AgentContextOptions = {},
|
|
315
|
-
): string {
|
|
342
|
+
export function buildAgentContextPreamble(nodes: CanvasNodeState[], options: AgentContextOptions = {}): string {
|
|
316
343
|
const sections = nodes
|
|
317
344
|
.map((node) => {
|
|
318
|
-
const title =
|
|
345
|
+
const title = typeof node.data.title === 'string' && node.data.title ? node.data.title : node.id;
|
|
319
346
|
const content = summarizeNodeForAgentContext(node, options);
|
|
320
347
|
if (!content) return '';
|
|
321
348
|
const kind = getCanvasNodeKind(node);
|
package/src/server/ax-context.ts
CHANGED
|
@@ -53,11 +53,13 @@ export function buildCanvasAxSurfaceSnapshot(): PmxAxSurfaceSnapshot {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
function serializeNodes(nodes: CanvasNodeState[]) {
|
|
56
|
-
return nodes.map((node) =>
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
return nodes.map((node) =>
|
|
57
|
+
serializeNodeForAgentContext(node, {
|
|
58
|
+
defaultTextLength: 700,
|
|
59
|
+
webpageTextLength: 1600,
|
|
60
|
+
includePosition: true,
|
|
61
|
+
}),
|
|
62
|
+
);
|
|
61
63
|
}
|
|
62
64
|
|
|
63
65
|
export function buildCanvasAxPinnedContext(): PmxAxPinnedContext {
|