pmx-canvas 0.1.13 → 0.1.14
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 +69 -0
- package/dist/canvas/index.js +34 -34
- package/dist/types/client/nodes/ExtAppFrame.d.ts +2 -3
- package/dist/types/client/nodes/McpAppNode.d.ts +2 -1
- package/dist/types/server/canvas-operations.d.ts +8 -0
- package/dist/types/server/diagram-presets.d.ts +4 -0
- package/dist/types/server/index.d.ts +8 -0
- package/dist/types/server/mcp-app-runtime.d.ts +1 -0
- package/package.json +1 -1
- package/src/cli/agent.ts +15 -1
- package/src/client/canvas/ExpandedNodeOverlay.tsx +3 -3
- package/src/client/nodes/ExtAppFrame.tsx +10 -35
- package/src/client/nodes/McpAppNode.tsx +2 -2
- package/src/client/state/sse-bridge.ts +1 -0
- package/src/mcp/server.ts +21 -1
- package/src/server/canvas-operations.ts +35 -0
- package/src/server/diagram-presets.ts +45 -25
- package/src/server/index.ts +31 -4
- package/src/server/mcp-app-runtime.ts +15 -5
- package/src/server/server.ts +95 -50
package/src/server/index.ts
CHANGED
|
@@ -33,6 +33,8 @@ import {
|
|
|
33
33
|
ungroupCanvasNodes,
|
|
34
34
|
validateCanvasNodePatch,
|
|
35
35
|
hasStructuredNodeUpdateFields,
|
|
36
|
+
hasTraceNodeDataFields,
|
|
37
|
+
mergeTraceNodeDataFields,
|
|
36
38
|
} from './canvas-operations.js';
|
|
37
39
|
import { validateCanvasLayout } from './canvas-validation.js';
|
|
38
40
|
import { describeCanvasSchema, validateStructuredCanvasPayload } from './canvas-schema.js';
|
|
@@ -158,6 +160,12 @@ export class PmxCanvas extends EventEmitter {
|
|
|
158
160
|
type: CanvasNodeState['type'];
|
|
159
161
|
title?: string;
|
|
160
162
|
content?: string;
|
|
163
|
+
toolName?: string;
|
|
164
|
+
category?: string;
|
|
165
|
+
status?: string;
|
|
166
|
+
duration?: string;
|
|
167
|
+
resultSummary?: string;
|
|
168
|
+
error?: string;
|
|
161
169
|
x?: number;
|
|
162
170
|
y?: number;
|
|
163
171
|
width?: number;
|
|
@@ -243,9 +251,10 @@ export class PmxCanvas extends EventEmitter {
|
|
|
243
251
|
patch.title !== undefined ||
|
|
244
252
|
patch.content !== undefined ||
|
|
245
253
|
typeof patch.arrangeLocked === 'boolean' ||
|
|
246
|
-
typeof patch.strictSize === 'boolean'
|
|
254
|
+
typeof patch.strictSize === 'boolean' ||
|
|
255
|
+
(existing.type === 'trace' && hasTraceNodeDataFields(patch))
|
|
247
256
|
) {
|
|
248
|
-
|
|
257
|
+
const nextData = {
|
|
249
258
|
...existing.data,
|
|
250
259
|
...(patch.data && typeof patch.data === 'object' && !Array.isArray(patch.data) ? patch.data : {}),
|
|
251
260
|
...(typeof patch.title === 'string' ? { title: patch.title } : {}),
|
|
@@ -253,6 +262,9 @@ export class PmxCanvas extends EventEmitter {
|
|
|
253
262
|
...(typeof patch.arrangeLocked === 'boolean' ? { arrangeLocked: patch.arrangeLocked } : {}),
|
|
254
263
|
...(typeof patch.strictSize === 'boolean' ? { strictSize: patch.strictSize } : {}),
|
|
255
264
|
};
|
|
265
|
+
resolvedPatch.data = existing.type === 'trace'
|
|
266
|
+
? mergeTraceNodeDataFields(nextData, patch)
|
|
267
|
+
: nextData;
|
|
256
268
|
}
|
|
257
269
|
|
|
258
270
|
const error = validateCanvasNodePatch({
|
|
@@ -520,21 +532,36 @@ export class PmxCanvas extends EventEmitter {
|
|
|
520
532
|
transport: ExternalMcpTransportConfig;
|
|
521
533
|
toolName: string;
|
|
522
534
|
toolArguments?: Record<string, unknown>;
|
|
535
|
+
nodeId?: string;
|
|
523
536
|
serverName?: string;
|
|
524
537
|
title?: string;
|
|
525
538
|
x?: number;
|
|
526
539
|
y?: number;
|
|
527
540
|
width?: number;
|
|
528
541
|
height?: number;
|
|
542
|
+
timeoutMs?: number;
|
|
529
543
|
}): Promise<{ ok: true; id?: string; nodeId: string | null; toolCallId: string; sessionId: string; resourceUri: string }> {
|
|
544
|
+
const targetNode = input.nodeId ? canvasState.getNode(input.nodeId) : undefined;
|
|
545
|
+
if (input.nodeId && !targetNode) {
|
|
546
|
+
throw new Error(`Node "${input.nodeId}" not found.`);
|
|
547
|
+
}
|
|
548
|
+
if (targetNode && (targetNode.type !== 'mcp-app' || targetNode.data.mode !== 'ext-app')) {
|
|
549
|
+
throw new Error(`Node "${input.nodeId}" is not an external app node.`);
|
|
550
|
+
}
|
|
551
|
+
|
|
530
552
|
const opened = await openExternalMcpApp({
|
|
531
553
|
transport: input.transport,
|
|
532
554
|
toolName: input.toolName,
|
|
533
555
|
...(input.toolArguments ? { toolArguments: input.toolArguments } : {}),
|
|
534
556
|
...(input.serverName ? { serverName: input.serverName } : {}),
|
|
557
|
+
...(typeof input.timeoutMs === 'number' ? { timeoutMs: input.timeoutMs } : {}),
|
|
535
558
|
});
|
|
536
559
|
const toolCallId = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
537
|
-
const
|
|
560
|
+
const previousSessionId = targetNode?.data.appSessionId;
|
|
561
|
+
if (typeof previousSessionId === 'string' && previousSessionId.trim().length > 0) {
|
|
562
|
+
closeMcpAppSession(previousSessionId);
|
|
563
|
+
}
|
|
564
|
+
const nodeIdSeed = input.nodeId ?? `ext-app-${toolCallId}`;
|
|
538
565
|
const toolResult = isExcalidrawCreateView(opened.serverName, opened.toolName)
|
|
539
566
|
? ensureExcalidrawCheckpointId(opened.toolResult, nodeIdSeed)
|
|
540
567
|
: opened.toolResult;
|
|
@@ -566,7 +593,7 @@ export class PmxCanvas extends EventEmitter {
|
|
|
566
593
|
success: toolResult.isError !== true,
|
|
567
594
|
result: toolResult,
|
|
568
595
|
});
|
|
569
|
-
const nodeId = this.findCanvasExtAppNodeId(toolCallId);
|
|
596
|
+
const nodeId = input.nodeId ?? this.findCanvasExtAppNodeId(toolCallId);
|
|
570
597
|
return {
|
|
571
598
|
ok: true,
|
|
572
599
|
...(nodeId ? { id: nodeId } : {}),
|
|
@@ -12,6 +12,7 @@ import type {
|
|
|
12
12
|
TextResourceContents,
|
|
13
13
|
Tool,
|
|
14
14
|
} from '@modelcontextprotocol/sdk/types.js';
|
|
15
|
+
import type { RequestOptions } from '@modelcontextprotocol/sdk/shared/protocol.js';
|
|
15
16
|
import {
|
|
16
17
|
EXTENSION_ID,
|
|
17
18
|
RESOURCE_MIME_TYPE,
|
|
@@ -47,6 +48,7 @@ export interface OpenMcpAppInput {
|
|
|
47
48
|
toolName: string;
|
|
48
49
|
toolArguments?: Record<string, unknown>;
|
|
49
50
|
serverName?: string;
|
|
51
|
+
timeoutMs?: number;
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
export interface OpenMcpAppResult {
|
|
@@ -184,6 +186,12 @@ function normalizeServerName(raw: string | undefined, transport: ExternalMcpTran
|
|
|
184
186
|
return trimmed.length > 0 ? trimmed : defaultServerName(transport);
|
|
185
187
|
}
|
|
186
188
|
|
|
189
|
+
function requestOptions(timeoutMs: number | undefined): RequestOptions | undefined {
|
|
190
|
+
return typeof timeoutMs === 'number' && Number.isFinite(timeoutMs) && timeoutMs > 0
|
|
191
|
+
? { timeout: timeoutMs }
|
|
192
|
+
: undefined;
|
|
193
|
+
}
|
|
194
|
+
|
|
187
195
|
function buildTransport(config: ExternalMcpTransportConfig): RuntimeTransport {
|
|
188
196
|
if (config.type === 'http') {
|
|
189
197
|
return new StreamableHTTPClientTransport(new URL(config.url), {
|
|
@@ -209,15 +217,16 @@ function buildTransport(config: ExternalMcpTransportConfig): RuntimeTransport {
|
|
|
209
217
|
async function createSession(
|
|
210
218
|
transportConfig: ExternalMcpTransportConfig,
|
|
211
219
|
serverName?: string,
|
|
220
|
+
timeoutMs?: number,
|
|
212
221
|
): Promise<McpAppSession> {
|
|
213
222
|
const transport = buildTransport(transportConfig);
|
|
214
223
|
const client = new Client(
|
|
215
224
|
{ name: 'pmx-canvas-app-host', version: '0.1.0' },
|
|
216
225
|
{ capabilities: clientCapabilities },
|
|
217
226
|
);
|
|
218
|
-
await client.connect(transport);
|
|
227
|
+
await client.connect(transport, requestOptions(timeoutMs));
|
|
219
228
|
|
|
220
|
-
const toolList = await client.listTools();
|
|
229
|
+
const toolList = await client.listTools(undefined, requestOptions(timeoutMs));
|
|
221
230
|
const session: McpAppSession = {
|
|
222
231
|
id: randomId('mcp-app-session'),
|
|
223
232
|
serverName: normalizeServerName(serverName, transportConfig),
|
|
@@ -350,7 +359,8 @@ function prepareResourceHtml(html: string, meta: McpUiResourceMeta | undefined):
|
|
|
350
359
|
}
|
|
351
360
|
|
|
352
361
|
export async function openMcpApp(input: OpenMcpAppInput): Promise<OpenMcpAppResult> {
|
|
353
|
-
const
|
|
362
|
+
const options = requestOptions(input.timeoutMs);
|
|
363
|
+
const session = await createSession(input.transport, input.serverName, input.timeoutMs);
|
|
354
364
|
try {
|
|
355
365
|
const tool = await findTool(session, input.toolName);
|
|
356
366
|
const resourceUri = getToolUiResourceUri(tool);
|
|
@@ -362,9 +372,9 @@ export async function openMcpApp(input: OpenMcpAppInput): Promise<OpenMcpAppResu
|
|
|
362
372
|
const rawToolResult = await session.client.callTool({
|
|
363
373
|
name: tool.name,
|
|
364
374
|
arguments: toolInput,
|
|
365
|
-
});
|
|
375
|
+
}, undefined, options);
|
|
366
376
|
const toolResult = normalizeExtAppToolResult({ result: rawToolResult });
|
|
367
|
-
const readResult = await session.client.readResource({ uri: resourceUri });
|
|
377
|
+
const readResult = await session.client.readResource({ uri: resourceUri }, options);
|
|
368
378
|
const resourceMeta = resourceMetaFromReadResult(readResult);
|
|
369
379
|
const html = prepareResourceHtml(htmlContentFromReadResult(readResult, resourceUri), resourceMeta);
|
|
370
380
|
|
package/src/server/server.ts
CHANGED
|
@@ -96,6 +96,8 @@ import {
|
|
|
96
96
|
ungroupCanvasNodes,
|
|
97
97
|
validateCanvasNodePatch,
|
|
98
98
|
hasStructuredNodeUpdateFields,
|
|
99
|
+
hasTraceNodeDataFields,
|
|
100
|
+
mergeTraceNodeDataFields,
|
|
99
101
|
} from './canvas-operations.js';
|
|
100
102
|
import { validateCanvasLayout } from './canvas-validation.js';
|
|
101
103
|
import { describeCanvasSchema, validateStructuredCanvasPayload } from './canvas-schema.js';
|
|
@@ -1331,6 +1333,12 @@ async function handleCanvasAddNode(req: Request): Promise<Response> {
|
|
|
1331
1333
|
...(typeof body.title === 'string' ? { title: body.title } : {}),
|
|
1332
1334
|
...(typeof content === 'string' ? { content } : {}),
|
|
1333
1335
|
...(extraData ? { data: extraData } : {}),
|
|
1336
|
+
...(type === 'trace' && typeof body.toolName === 'string' ? { toolName: body.toolName } : {}),
|
|
1337
|
+
...(type === 'trace' && typeof body.category === 'string' ? { category: body.category } : {}),
|
|
1338
|
+
...(type === 'trace' && typeof body.status === 'string' ? { status: body.status } : {}),
|
|
1339
|
+
...(type === 'trace' && typeof body.duration === 'string' ? { duration: body.duration } : {}),
|
|
1340
|
+
...(type === 'trace' && typeof body.resultSummary === 'string' ? { resultSummary: body.resultSummary } : {}),
|
|
1341
|
+
...(type === 'trace' && typeof body.error === 'string' ? { error: body.error } : {}),
|
|
1334
1342
|
...(body.strictSize === true ? { strictSize: true } : {}),
|
|
1335
1343
|
...geometry,
|
|
1336
1344
|
defaultWidth: 360,
|
|
@@ -1495,7 +1503,14 @@ async function handleCanvasUpdateNode(nodeId: string, req: Request): Promise<Res
|
|
|
1495
1503
|
} catch (error) {
|
|
1496
1504
|
return responseJson({ ok: false, error: error instanceof Error ? error.message : String(error) }, 400);
|
|
1497
1505
|
}
|
|
1498
|
-
} else if (
|
|
1506
|
+
} else if (
|
|
1507
|
+
body.title !== undefined ||
|
|
1508
|
+
body.content !== undefined ||
|
|
1509
|
+
body.data ||
|
|
1510
|
+
typeof body.arrangeLocked === 'boolean' ||
|
|
1511
|
+
typeof body.strictSize === 'boolean' ||
|
|
1512
|
+
(existing.type === 'trace' && hasTraceNodeDataFields(body))
|
|
1513
|
+
) {
|
|
1499
1514
|
const data = { ...existing.data };
|
|
1500
1515
|
if (body.title !== undefined) {
|
|
1501
1516
|
data.title = String(body.title);
|
|
@@ -1524,7 +1539,9 @@ async function handleCanvasUpdateNode(nodeId: string, req: Request): Promise<Res
|
|
|
1524
1539
|
}
|
|
1525
1540
|
}
|
|
1526
1541
|
}
|
|
1527
|
-
patch.data =
|
|
1542
|
+
patch.data = existing.type === 'trace'
|
|
1543
|
+
? mergeTraceNodeDataFields(data, body)
|
|
1544
|
+
: data;
|
|
1528
1545
|
}
|
|
1529
1546
|
const error = validateCanvasNodePatch({
|
|
1530
1547
|
...(patch.position ? { position: patch.position as { x: number; y: number } } : {}),
|
|
@@ -2111,29 +2128,44 @@ interface RunAndEmitOpenMcpAppParams {
|
|
|
2111
2128
|
transport: ExternalMcpTransportConfig;
|
|
2112
2129
|
toolName: string;
|
|
2113
2130
|
toolArguments?: Record<string, unknown>;
|
|
2131
|
+
nodeId?: string;
|
|
2114
2132
|
serverName?: string;
|
|
2115
2133
|
title?: string;
|
|
2116
2134
|
x?: number;
|
|
2117
2135
|
y?: number;
|
|
2118
2136
|
width?: number;
|
|
2119
2137
|
height?: number;
|
|
2138
|
+
timeoutMs?: number;
|
|
2120
2139
|
}
|
|
2121
2140
|
|
|
2122
2141
|
async function runAndEmitOpenMcpApp(params: RunAndEmitOpenMcpAppParams): Promise<Response> {
|
|
2123
2142
|
try {
|
|
2143
|
+
const targetNode = params.nodeId ? canvasState.getNode(params.nodeId) : undefined;
|
|
2144
|
+
if (params.nodeId && !targetNode) {
|
|
2145
|
+
return responseJson({ ok: false, error: `Node "${params.nodeId}" not found.` }, 404);
|
|
2146
|
+
}
|
|
2147
|
+
if (targetNode && (targetNode.type !== 'mcp-app' || targetNode.data.mode !== 'ext-app')) {
|
|
2148
|
+
return responseJson({ ok: false, error: `Node "${params.nodeId}" is not an external app node.` }, 400);
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2124
2151
|
const opened = await openMcpApp({
|
|
2125
2152
|
transport: params.transport,
|
|
2126
2153
|
toolName: params.toolName,
|
|
2127
2154
|
...(params.toolArguments ? { toolArguments: params.toolArguments } : {}),
|
|
2128
2155
|
...(params.serverName ? { serverName: params.serverName } : {}),
|
|
2156
|
+
...(typeof params.timeoutMs === 'number' ? { timeoutMs: params.timeoutMs } : {}),
|
|
2129
2157
|
});
|
|
2130
2158
|
|
|
2131
2159
|
const toolCallId = randomExtAppToolCallId();
|
|
2132
|
-
|
|
2160
|
+
if (params.nodeId) closeNodeAppSession(targetNode);
|
|
2161
|
+
const nodeIdSeed = params.nodeId ?? (toolCallId.startsWith('ext-app-') ? toolCallId : `ext-app-${toolCallId}`);
|
|
2133
2162
|
const toolResult = isExcalidrawCreateView(opened.serverName, opened.toolName)
|
|
2134
2163
|
? ensureExcalidrawCheckpointId(opened.toolResult, nodeIdSeed)
|
|
2135
2164
|
: opened.toolResult;
|
|
2136
|
-
const nodeTitle = params.title
|
|
2165
|
+
const nodeTitle = params.title
|
|
2166
|
+
?? (typeof targetNode?.data.title === 'string' ? targetNode.data.title : undefined)
|
|
2167
|
+
?? opened.tool.title
|
|
2168
|
+
?? opened.tool.name;
|
|
2137
2169
|
|
|
2138
2170
|
emitPrimaryWorkbenchEvent('ext-app-open', {
|
|
2139
2171
|
toolCallId,
|
|
@@ -2163,7 +2195,7 @@ async function runAndEmitOpenMcpApp(params: RunAndEmitOpenMcpAppParams): Promise
|
|
|
2163
2195
|
success: toolResult.isError !== true,
|
|
2164
2196
|
result: toolResult,
|
|
2165
2197
|
});
|
|
2166
|
-
const nodeId = findCanvasExtAppNodeId(toolCallId);
|
|
2198
|
+
const nodeId = params.nodeId ?? findCanvasExtAppNodeId(toolCallId);
|
|
2167
2199
|
|
|
2168
2200
|
return responseJson({
|
|
2169
2201
|
ok: true,
|
|
@@ -2202,17 +2234,22 @@ async function handleCanvasOpenMcpApp(req: Request): Promise<Response> {
|
|
|
2202
2234
|
const requestedServerName = typeof body.serverName === 'string' && body.serverName.trim().length > 0
|
|
2203
2235
|
? body.serverName.trim()
|
|
2204
2236
|
: undefined;
|
|
2237
|
+
const requestedNodeId = typeof body.nodeId === 'string' && body.nodeId.trim().length > 0
|
|
2238
|
+
? body.nodeId.trim()
|
|
2239
|
+
: undefined;
|
|
2205
2240
|
|
|
2206
2241
|
return runAndEmitOpenMcpApp({
|
|
2207
2242
|
transport,
|
|
2208
2243
|
toolName,
|
|
2209
2244
|
...(toolArguments ? { toolArguments } : {}),
|
|
2245
|
+
...(requestedNodeId ? { nodeId: requestedNodeId } : {}),
|
|
2210
2246
|
...(requestedServerName ? { serverName: requestedServerName } : {}),
|
|
2211
2247
|
...(requestedTitle ? { title: requestedTitle } : {}),
|
|
2212
2248
|
...(typeof body.x === 'number' ? { x: body.x } : {}),
|
|
2213
2249
|
...(typeof body.y === 'number' ? { y: body.y } : {}),
|
|
2214
2250
|
...(typeof body.width === 'number' ? { width: body.width } : {}),
|
|
2215
2251
|
...(typeof body.height === 'number' ? { height: body.height } : {}),
|
|
2252
|
+
...(typeof body.timeoutMs === 'number' ? { timeoutMs: body.timeoutMs } : {}),
|
|
2216
2253
|
});
|
|
2217
2254
|
}
|
|
2218
2255
|
|
|
@@ -2222,11 +2259,13 @@ async function handleCanvasAddDiagram(req: Request): Promise<Response> {
|
|
|
2222
2259
|
try {
|
|
2223
2260
|
built = buildExcalidrawOpenMcpAppInput({
|
|
2224
2261
|
elements: body.elements,
|
|
2262
|
+
...(typeof body.nodeId === 'string' ? { nodeId: body.nodeId } : {}),
|
|
2225
2263
|
...(typeof body.title === 'string' ? { title: body.title } : {}),
|
|
2226
2264
|
...(typeof body.x === 'number' ? { x: body.x } : {}),
|
|
2227
2265
|
...(typeof body.y === 'number' ? { y: body.y } : {}),
|
|
2228
2266
|
...(typeof body.width === 'number' ? { width: body.width } : {}),
|
|
2229
2267
|
...(typeof body.height === 'number' ? { height: body.height } : {}),
|
|
2268
|
+
...(typeof body.timeoutMs === 'number' ? { timeoutMs: body.timeoutMs } : {}),
|
|
2230
2269
|
});
|
|
2231
2270
|
} catch (error) {
|
|
2232
2271
|
return responseJson({
|
|
@@ -2239,11 +2278,13 @@ async function handleCanvasAddDiagram(req: Request): Promise<Response> {
|
|
|
2239
2278
|
toolName: built.toolName,
|
|
2240
2279
|
toolArguments: built.toolArguments,
|
|
2241
2280
|
serverName: built.serverName,
|
|
2281
|
+
...(built.nodeId ? { nodeId: built.nodeId } : {}),
|
|
2242
2282
|
...(built.title ? { title: built.title } : {}),
|
|
2243
2283
|
...(typeof built.x === 'number' ? { x: built.x } : {}),
|
|
2244
2284
|
...(typeof built.y === 'number' ? { y: built.y } : {}),
|
|
2245
2285
|
...(typeof built.width === 'number' ? { width: built.width } : {}),
|
|
2246
2286
|
...(typeof built.height === 'number' ? { height: built.height } : {}),
|
|
2287
|
+
...(typeof built.timeoutMs === 'number' ? { timeoutMs: built.timeoutMs } : {}),
|
|
2247
2288
|
});
|
|
2248
2289
|
}
|
|
2249
2290
|
|
|
@@ -3408,52 +3449,56 @@ function syncEventToCanvasState(event: string, payload: PrimaryWorkbenchEventPay
|
|
|
3408
3449
|
});
|
|
3409
3450
|
}
|
|
3410
3451
|
} else if (event === 'ext-app-update') {
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3452
|
+
canvasState.withSuppressedRecording(() => {
|
|
3453
|
+
const toolCallId = payload.toolCallId as string;
|
|
3454
|
+
if (!toolCallId) return;
|
|
3455
|
+
const payloadNodeId = typeof payload.nodeId === 'string' ? payload.nodeId : '';
|
|
3456
|
+
const id =
|
|
3457
|
+
(payloadNodeId && canvasState.getNode(payloadNodeId) ? payloadNodeId : null) ||
|
|
3458
|
+
findCanvasExtAppNodeId(toolCallId) ||
|
|
3459
|
+
(typeof payload.serverName === 'string' && typeof payload.toolName === 'string'
|
|
3460
|
+
? findOnlyPendingCanvasExtAppNodeId(payload.serverName, payload.toolName)
|
|
3461
|
+
: null);
|
|
3462
|
+
if (!id) return;
|
|
3463
|
+
const existing = canvasState.getNode(id);
|
|
3464
|
+
if (existing) {
|
|
3465
|
+
canvasState.updateNode(id, { data: { ...existing.data, html: payload.html } });
|
|
3466
|
+
}
|
|
3467
|
+
});
|
|
3425
3468
|
} else if (event === 'ext-app-result') {
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3469
|
+
canvasState.withSuppressedRecording(() => {
|
|
3470
|
+
const toolCallId = payload.toolCallId as string;
|
|
3471
|
+
if (!toolCallId) return;
|
|
3472
|
+
const payloadNodeId = typeof payload.nodeId === 'string' ? payload.nodeId : '';
|
|
3473
|
+
const id =
|
|
3474
|
+
(payloadNodeId && canvasState.getNode(payloadNodeId) ? payloadNodeId : null) ||
|
|
3475
|
+
findCanvasExtAppNodeId(toolCallId) ||
|
|
3476
|
+
(typeof payload.serverName === 'string' && typeof payload.toolName === 'string'
|
|
3477
|
+
? findOnlyPendingCanvasExtAppNodeId(payload.serverName, payload.toolName)
|
|
3478
|
+
: null);
|
|
3479
|
+
if (!id) return;
|
|
3480
|
+
if (payload.success === false) {
|
|
3481
|
+
closeNodeAppSession(canvasState.getNode(id));
|
|
3482
|
+
canvasState.removeNode(id);
|
|
3483
|
+
return;
|
|
3484
|
+
}
|
|
3485
|
+
const existing = canvasState.getNode(id);
|
|
3486
|
+
if (existing) {
|
|
3487
|
+
canvasState.updateNode(id, {
|
|
3488
|
+
data: {
|
|
3489
|
+
...existing.data,
|
|
3490
|
+
toolResult: normalizeExtAppToolResult({
|
|
3491
|
+
result: payload.result,
|
|
3492
|
+
success: typeof payload.success === 'boolean' ? payload.success : undefined,
|
|
3493
|
+
error: typeof payload.error === 'string' ? payload.error : undefined,
|
|
3494
|
+
content: typeof payload.content === 'string' ? payload.content : undefined,
|
|
3495
|
+
detailedContent:
|
|
3496
|
+
typeof payload.detailedContent === 'string' ? payload.detailedContent : undefined,
|
|
3497
|
+
}),
|
|
3498
|
+
},
|
|
3499
|
+
});
|
|
3500
|
+
}
|
|
3501
|
+
});
|
|
3457
3502
|
} else if (event === 'context-cards') {
|
|
3458
3503
|
syncContextNodeToCanvasState(
|
|
3459
3504
|
{ cards: Array.isArray(payload.cards) ? payload.cards : [] },
|