@particle-academy/agent-integrations 0.2.4 → 0.3.4
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/dist/bridges/charts.d.cts +39 -0
- package/dist/bridges/charts.d.ts +39 -0
- package/dist/bridges/code.d.cts +47 -0
- package/dist/bridges/code.d.ts +47 -0
- package/dist/bridges/flow.d.cts +3 -2
- package/dist/bridges/flow.d.ts +3 -2
- package/dist/bridges/forms.d.cts +76 -0
- package/dist/bridges/forms.d.ts +76 -0
- package/dist/bridges/scene.d.cts +54 -0
- package/dist/bridges/scene.d.ts +54 -0
- package/dist/bridges/sheets.d.cts +62 -0
- package/dist/bridges/sheets.d.ts +62 -0
- package/dist/bridges/whiteboard.d.cts +3 -2
- package/dist/bridges/whiteboard.d.ts +3 -2
- package/dist/bridges-charts.cjs +167 -0
- package/dist/bridges-charts.cjs.map +1 -0
- package/dist/bridges-charts.js +6 -0
- package/dist/bridges-charts.js.map +1 -0
- package/dist/bridges-code.cjs +219 -0
- package/dist/bridges-code.cjs.map +1 -0
- package/dist/bridges-code.js +6 -0
- package/dist/bridges-code.js.map +1 -0
- package/dist/bridges-flow.cjs +76 -17
- package/dist/bridges-flow.cjs.map +1 -1
- package/dist/bridges-flow.js +3 -1
- package/dist/bridges-forms.cjs +205 -0
- package/dist/bridges-forms.cjs.map +1 -0
- package/dist/bridges-forms.js +6 -0
- package/dist/bridges-forms.js.map +1 -0
- package/dist/bridges-scene.cjs +250 -0
- package/dist/bridges-scene.cjs.map +1 -0
- package/dist/bridges-scene.js +6 -0
- package/dist/bridges-scene.js.map +1 -0
- package/dist/bridges-sheets.cjs +327 -0
- package/dist/bridges-sheets.cjs.map +1 -0
- package/dist/bridges-sheets.js +6 -0
- package/dist/bridges-sheets.js.map +1 -0
- package/dist/bridges-whiteboard.cjs +224 -38
- package/dist/bridges-whiteboard.cjs.map +1 -1
- package/dist/bridges-whiteboard.js +4 -1
- package/dist/{chunk-2VOQJKSU.js → chunk-4IAVAFUV.js} +41 -19
- package/dist/chunk-4IAVAFUV.js.map +1 -0
- package/dist/chunk-52S7XYZK.js +38 -0
- package/dist/chunk-52S7XYZK.js.map +1 -0
- package/dist/chunk-ACBENYYO.js +124 -0
- package/dist/chunk-ACBENYYO.js.map +1 -0
- package/dist/chunk-DJOWMF6Q.js +25 -0
- package/dist/chunk-DJOWMF6Q.js.map +1 -0
- package/dist/{chunk-FLEOQUKF.js → chunk-JMYPUAFH.js} +17 -2
- package/dist/chunk-JMYPUAFH.js.map +1 -0
- package/dist/chunk-JU2N4KK6.js +34 -0
- package/dist/chunk-JU2N4KK6.js.map +1 -0
- package/dist/chunk-OEIULP2L.js +158 -0
- package/dist/chunk-OEIULP2L.js.map +1 -0
- package/dist/chunk-PDBF4W7E.js +280 -0
- package/dist/chunk-PDBF4W7E.js.map +1 -0
- package/dist/chunk-PHPXKSWI.js +120 -0
- package/dist/chunk-PHPXKSWI.js.map +1 -0
- package/dist/chunk-QJUTISFC.js +203 -0
- package/dist/chunk-QJUTISFC.js.map +1 -0
- package/dist/{chunk-5ZUHNNLR.js → chunk-TBEITXF4.js} +79 -41
- package/dist/chunk-TBEITXF4.js.map +1 -0
- package/dist/chunk-X66JWQBB.js +37 -0
- package/dist/chunk-X66JWQBB.js.map +1 -0
- package/dist/chunk-XYYSTJHW.js +172 -0
- package/dist/chunk-XYYSTJHW.js.map +1 -0
- package/dist/index.cjs +1411 -57
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +96 -3
- package/dist/index.d.ts +96 -3
- package/dist/index.js +111 -7
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.d.cts +4 -2
- package/dist/mcp/index.d.ts +4 -2
- package/dist/presence/index.d.cts +136 -0
- package/dist/presence/index.d.ts +136 -0
- package/dist/presence.cjs +107 -0
- package/dist/presence.cjs.map +1 -0
- package/dist/presence.js +5 -0
- package/dist/presence.js.map +1 -0
- package/dist/registry-2DRURS6U.js +3 -0
- package/dist/registry-2DRURS6U.js.map +1 -0
- package/dist/server-BJu_AMH3.d.ts +64 -0
- package/dist/server-si-VvFxI.d.cts +64 -0
- package/dist/sharing/index.d.cts +2 -1
- package/dist/sharing/index.d.ts +2 -1
- package/dist/sharing.cjs +68 -0
- package/dist/sharing.cjs.map +1 -1
- package/dist/sharing.js +1 -1
- package/dist/styles.css +57 -0
- package/dist/styles.css.map +1 -1
- package/dist/{types-DR5AS6Rd.d.cts → types-Bf1ZoGmI.d.cts} +1 -1
- package/dist/{types-CRPA_D0z.d.ts → types-DXKpLuia.d.ts} +1 -1
- package/dist/types-DksGd5Y7.d.cts +112 -0
- package/dist/types-DksGd5Y7.d.ts +112 -0
- package/dist/undo/index.d.cts +69 -0
- package/dist/undo/index.d.ts +69 -0
- package/dist/undo.cjs +163 -0
- package/dist/undo.cjs.map +1 -0
- package/dist/undo.js +5 -0
- package/dist/undo.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-2VOQJKSU.js.map +0 -1
- package/dist/chunk-5ZUHNNLR.js.map +0 -1
- package/dist/chunk-FLEOQUKF.js.map +0 -1
- package/dist/server-Bv985us3.d.cts +0 -173
- package/dist/server-Bv985us3.d.ts +0 -173
package/dist/index.cjs
CHANGED
|
@@ -4,6 +4,59 @@ var react = require('react');
|
|
|
4
4
|
var jsxRuntime = require('react/jsx-runtime');
|
|
5
5
|
var fancyWhiteboard = require('@particle-academy/fancy-whiteboard');
|
|
6
6
|
|
|
7
|
+
var __defProp = Object.defineProperty;
|
|
8
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
9
|
+
var __esm = (fn, res) => function __init() {
|
|
10
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
|
+
};
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// src/presence/registry.ts
|
|
18
|
+
var registry_exports = {};
|
|
19
|
+
__export(registry_exports, {
|
|
20
|
+
emitActivity: () => emitActivity,
|
|
21
|
+
onActivity: () => onActivity,
|
|
22
|
+
readActivityHistory: () => readActivityHistory,
|
|
23
|
+
resetActivityRegistry: () => resetActivityRegistry
|
|
24
|
+
});
|
|
25
|
+
function emitActivity(event) {
|
|
26
|
+
history.push(event);
|
|
27
|
+
if (history.length > HISTORY_CAP) history.splice(0, history.length - HISTORY_CAP);
|
|
28
|
+
for (const l of listeners) l(event);
|
|
29
|
+
}
|
|
30
|
+
function onActivity(listener, filter) {
|
|
31
|
+
const wrapped = filter ? (e) => {
|
|
32
|
+
if (matches(e, filter)) listener(e);
|
|
33
|
+
} : listener;
|
|
34
|
+
listeners.add(wrapped);
|
|
35
|
+
return () => listeners.delete(wrapped);
|
|
36
|
+
}
|
|
37
|
+
function readActivityHistory(filter) {
|
|
38
|
+
if (!filter) return history.slice();
|
|
39
|
+
return history.filter((e) => matches(e, filter));
|
|
40
|
+
}
|
|
41
|
+
function resetActivityRegistry() {
|
|
42
|
+
listeners.clear();
|
|
43
|
+
history.length = 0;
|
|
44
|
+
}
|
|
45
|
+
function matches(e, f) {
|
|
46
|
+
if (f.agentId !== void 0 && e.agentId !== f.agentId) return false;
|
|
47
|
+
if (f.screenId !== void 0 && e.target.screenId !== f.screenId) return false;
|
|
48
|
+
if (f.kind !== void 0 && e.target.kind !== f.kind) return false;
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
var HISTORY_CAP, listeners, history;
|
|
52
|
+
var init_registry = __esm({
|
|
53
|
+
"src/presence/registry.ts"() {
|
|
54
|
+
HISTORY_CAP = 200;
|
|
55
|
+
listeners = /* @__PURE__ */ new Set();
|
|
56
|
+
history = [];
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
7
60
|
// src/mcp/types.ts
|
|
8
61
|
var JSONRPC_METHOD_NOT_FOUND = -32601;
|
|
9
62
|
var JSONRPC_INVALID_PARAMS = -32602;
|
|
@@ -194,6 +247,159 @@ function attachRelay(server, channel) {
|
|
|
194
247
|
return transport;
|
|
195
248
|
}
|
|
196
249
|
|
|
250
|
+
// src/presence/wrap-tool-with-activity.ts
|
|
251
|
+
init_registry();
|
|
252
|
+
function wrapToolWithActivity(handler, options) {
|
|
253
|
+
return async (args) => {
|
|
254
|
+
const result = await handler(args);
|
|
255
|
+
if (result.isError) return result;
|
|
256
|
+
let target;
|
|
257
|
+
if (options.resolveTarget) {
|
|
258
|
+
target = options.resolveTarget({ toolName: options.toolName, args, result });
|
|
259
|
+
} else {
|
|
260
|
+
target = { kind: options.kind, screenId: options.screenId };
|
|
261
|
+
}
|
|
262
|
+
if (!target) return result;
|
|
263
|
+
emitActivity({
|
|
264
|
+
agentId: options.agent.id,
|
|
265
|
+
agentName: options.agent.name,
|
|
266
|
+
agentColor: options.agent.color,
|
|
267
|
+
target: { ...target, kind: target.kind ?? options.kind, screenId: target.screenId ?? options.screenId },
|
|
268
|
+
action: options.toolName,
|
|
269
|
+
timestamp: Date.now(),
|
|
270
|
+
meta: extractMeta(result),
|
|
271
|
+
ttlMs: options.ttlMs
|
|
272
|
+
});
|
|
273
|
+
return result;
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
function extractMeta(result) {
|
|
277
|
+
const sc = result.structuredContent;
|
|
278
|
+
if (sc && typeof sc === "object" && !Array.isArray(sc)) {
|
|
279
|
+
return sc;
|
|
280
|
+
}
|
|
281
|
+
return void 0;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// src/undo/undo-stack.ts
|
|
285
|
+
var stacks = /* @__PURE__ */ new Map();
|
|
286
|
+
var CAP = 200;
|
|
287
|
+
function getStack(agentId) {
|
|
288
|
+
let s = stacks.get(agentId);
|
|
289
|
+
if (!s) {
|
|
290
|
+
s = { past: [], future: [] };
|
|
291
|
+
stacks.set(agentId, s);
|
|
292
|
+
}
|
|
293
|
+
return s;
|
|
294
|
+
}
|
|
295
|
+
function pushUndoEntry(agentId, entry) {
|
|
296
|
+
const s = getStack(agentId);
|
|
297
|
+
s.past.push(entry);
|
|
298
|
+
if (s.past.length > CAP) s.past.splice(0, s.past.length - CAP);
|
|
299
|
+
s.future.length = 0;
|
|
300
|
+
}
|
|
301
|
+
async function undoOne(agentId) {
|
|
302
|
+
const s = getStack(agentId);
|
|
303
|
+
const entry = s.past.pop();
|
|
304
|
+
if (!entry) return null;
|
|
305
|
+
await entry.undo();
|
|
306
|
+
s.future.push(entry);
|
|
307
|
+
return entry;
|
|
308
|
+
}
|
|
309
|
+
async function redoOne(agentId) {
|
|
310
|
+
const s = getStack(agentId);
|
|
311
|
+
const entry = s.future.pop();
|
|
312
|
+
if (!entry) return null;
|
|
313
|
+
await entry.redo();
|
|
314
|
+
s.past.push(entry);
|
|
315
|
+
return entry;
|
|
316
|
+
}
|
|
317
|
+
function readHistory(agentId) {
|
|
318
|
+
return getStack(agentId).past.slice();
|
|
319
|
+
}
|
|
320
|
+
function clearStack(agentId) {
|
|
321
|
+
stacks.delete(agentId);
|
|
322
|
+
}
|
|
323
|
+
function resetAllUndoStacks() {
|
|
324
|
+
stacks.clear();
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// src/undo/undo-tools.ts
|
|
328
|
+
var installedServers = /* @__PURE__ */ new WeakSet();
|
|
329
|
+
function ensureUndoToolsRegistered(server, options = {}) {
|
|
330
|
+
if (installedServers.has(server)) return;
|
|
331
|
+
installedServers.add(server);
|
|
332
|
+
registerUndoTools(server, options);
|
|
333
|
+
}
|
|
334
|
+
function registerUndoTools(server, options = {}) {
|
|
335
|
+
const defaultAgent = options.defaultAgentId ?? "agent";
|
|
336
|
+
const disposers = [];
|
|
337
|
+
const agentOf = (args) => typeof args?.agentId === "string" ? args.agentId : defaultAgent;
|
|
338
|
+
disposers.push(
|
|
339
|
+
server.registerTool(
|
|
340
|
+
{
|
|
341
|
+
name: "agent_undo",
|
|
342
|
+
description: "Undo the most recent action on the agent's stack. Optional agentId targets a specific agent.",
|
|
343
|
+
inputSchema: {
|
|
344
|
+
type: "object",
|
|
345
|
+
properties: { agentId: { type: "string" } },
|
|
346
|
+
additionalProperties: false
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
async (args) => {
|
|
350
|
+
const entry = await undoOne(agentOf(args));
|
|
351
|
+
if (!entry) return errorResult("Nothing to undo.");
|
|
352
|
+
return textResult(`Undid: ${entry.label}`, { entry: serialize(entry) });
|
|
353
|
+
}
|
|
354
|
+
)
|
|
355
|
+
);
|
|
356
|
+
disposers.push(
|
|
357
|
+
server.registerTool(
|
|
358
|
+
{
|
|
359
|
+
name: "agent_redo",
|
|
360
|
+
description: "Redo the most recently undone action.",
|
|
361
|
+
inputSchema: {
|
|
362
|
+
type: "object",
|
|
363
|
+
properties: { agentId: { type: "string" } },
|
|
364
|
+
additionalProperties: false
|
|
365
|
+
}
|
|
366
|
+
},
|
|
367
|
+
async (args) => {
|
|
368
|
+
const entry = await redoOne(agentOf(args));
|
|
369
|
+
if (!entry) return errorResult("Nothing to redo.");
|
|
370
|
+
return textResult(`Redid: ${entry.label}`, { entry: serialize(entry) });
|
|
371
|
+
}
|
|
372
|
+
)
|
|
373
|
+
);
|
|
374
|
+
disposers.push(
|
|
375
|
+
server.registerTool(
|
|
376
|
+
{
|
|
377
|
+
name: "agent_history",
|
|
378
|
+
description: "List the agent's undo stack (oldest first). Useful for understanding what's reversible.",
|
|
379
|
+
inputSchema: {
|
|
380
|
+
type: "object",
|
|
381
|
+
properties: { agentId: { type: "string" } },
|
|
382
|
+
additionalProperties: false
|
|
383
|
+
}
|
|
384
|
+
},
|
|
385
|
+
async (args) => {
|
|
386
|
+
const history2 = readHistory(agentOf(args)).map(serialize);
|
|
387
|
+
const text = history2.map((e) => `${new Date(e.timestamp).toISOString()} ${e.bridgeId} ${e.action}: ${e.label}`).join("\n");
|
|
388
|
+
return textResult(text || "(empty)", history2);
|
|
389
|
+
}
|
|
390
|
+
)
|
|
391
|
+
);
|
|
392
|
+
return () => disposers.forEach((d) => d());
|
|
393
|
+
}
|
|
394
|
+
function serialize(entry) {
|
|
395
|
+
return {
|
|
396
|
+
timestamp: entry.timestamp,
|
|
397
|
+
bridgeId: entry.bridgeId,
|
|
398
|
+
action: entry.action,
|
|
399
|
+
label: entry.label
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
197
403
|
// src/bridges/whiteboard.ts
|
|
198
404
|
var DEFAULT_AGENT = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
199
405
|
var VALID_SHAPES = ["rect", "rounded-rect", "ellipse", "diamond", "triangle", "line", "arrow", "text"];
|
|
@@ -205,7 +411,25 @@ function registerWhiteboardBridge(server, options) {
|
|
|
205
411
|
const { adapter } = options;
|
|
206
412
|
const agent = { ...DEFAULT_AGENT, ...options.agent ?? {} };
|
|
207
413
|
const disposers = [];
|
|
208
|
-
|
|
414
|
+
ensureUndoToolsRegistered(server, { defaultAgentId: agent.id });
|
|
415
|
+
const wbTarget = (args, result) => ({
|
|
416
|
+
kind: "whiteboard",
|
|
417
|
+
elementId: result?.structuredContent?.id ?? args?.id
|
|
418
|
+
});
|
|
419
|
+
const reg = (name, description, inputProperties, required, handler, resolveTarget) => {
|
|
420
|
+
const wrapped = async (args) => {
|
|
421
|
+
try {
|
|
422
|
+
return await handler(args);
|
|
423
|
+
} catch (e) {
|
|
424
|
+
return errorResult(e instanceof Error ? e.message : String(e));
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
const final = resolveTarget ? wrapToolWithActivity(wrapped, {
|
|
428
|
+
toolName: name,
|
|
429
|
+
agent: { id: agent.id, name: agent.name, color: agent.color },
|
|
430
|
+
kind: "whiteboard",
|
|
431
|
+
resolveTarget: ({ args, result }) => resolveTarget(args, result)
|
|
432
|
+
}) : wrapped;
|
|
209
433
|
disposers.push(
|
|
210
434
|
server.registerTool(
|
|
211
435
|
{
|
|
@@ -218,13 +442,7 @@ function registerWhiteboardBridge(server, options) {
|
|
|
218
442
|
additionalProperties: false
|
|
219
443
|
}
|
|
220
444
|
},
|
|
221
|
-
|
|
222
|
-
try {
|
|
223
|
-
return await handler(args);
|
|
224
|
-
} catch (e) {
|
|
225
|
-
return errorResult(e instanceof Error ? e.message : String(e));
|
|
226
|
-
}
|
|
227
|
-
}
|
|
445
|
+
final
|
|
228
446
|
)
|
|
229
447
|
);
|
|
230
448
|
};
|
|
@@ -301,8 +519,17 @@ function registerWhiteboardBridge(server, options) {
|
|
|
301
519
|
authorId: agent.id
|
|
302
520
|
};
|
|
303
521
|
adapter.setNotes((all) => [...all, note]);
|
|
522
|
+
pushUndoEntry(agent.id, {
|
|
523
|
+
timestamp: Date.now(),
|
|
524
|
+
bridgeId: "whiteboard",
|
|
525
|
+
action: "whiteboard_add_sticky",
|
|
526
|
+
label: `Added sticky ${note.id}`,
|
|
527
|
+
undo: () => adapter.setNotes((all) => all.filter((n) => n.id !== note.id)),
|
|
528
|
+
redo: () => adapter.setNotes((all) => [...all, note])
|
|
529
|
+
});
|
|
304
530
|
return textResult(`Added sticky ${note.id}`, note);
|
|
305
|
-
}
|
|
531
|
+
},
|
|
532
|
+
wbTarget
|
|
306
533
|
);
|
|
307
534
|
reg(
|
|
308
535
|
"whiteboard_stream_text",
|
|
@@ -369,7 +596,8 @@ function registerWhiteboardBridge(server, options) {
|
|
|
369
596
|
})
|
|
370
597
|
);
|
|
371
598
|
return textResult(`Updated sticky ${id}`, updated);
|
|
372
|
-
}
|
|
599
|
+
},
|
|
600
|
+
wbTarget
|
|
373
601
|
);
|
|
374
602
|
reg(
|
|
375
603
|
"whiteboard_add_shape",
|
|
@@ -410,7 +638,8 @@ function registerWhiteboardBridge(server, options) {
|
|
|
410
638
|
};
|
|
411
639
|
adapter.setShapes((all) => [...all, shape]);
|
|
412
640
|
return textResult(`Added ${kind} ${shape.id}`, shape);
|
|
413
|
-
}
|
|
641
|
+
},
|
|
642
|
+
wbTarget
|
|
414
643
|
);
|
|
415
644
|
reg(
|
|
416
645
|
"whiteboard_update_shape",
|
|
@@ -452,7 +681,8 @@ function registerWhiteboardBridge(server, options) {
|
|
|
452
681
|
})
|
|
453
682
|
);
|
|
454
683
|
return textResult(`Updated shape ${id}`, updated);
|
|
455
|
-
}
|
|
684
|
+
},
|
|
685
|
+
wbTarget
|
|
456
686
|
);
|
|
457
687
|
reg(
|
|
458
688
|
"whiteboard_add_connector",
|
|
@@ -473,7 +703,8 @@ function registerWhiteboardBridge(server, options) {
|
|
|
473
703
|
};
|
|
474
704
|
adapter.setConnectors((all) => [...all, c]);
|
|
475
705
|
return textResult(`Added connector ${c.id}`, c);
|
|
476
|
-
}
|
|
706
|
+
},
|
|
707
|
+
wbTarget
|
|
477
708
|
);
|
|
478
709
|
reg(
|
|
479
710
|
"whiteboard_add_stroke",
|
|
@@ -502,7 +733,8 @@ function registerWhiteboardBridge(server, options) {
|
|
|
502
733
|
};
|
|
503
734
|
adapter.setStrokes((all) => [...all, stroke]);
|
|
504
735
|
return textResult(`Added stroke ${stroke.id} (${points.length} points)`, stroke);
|
|
505
|
-
}
|
|
736
|
+
},
|
|
737
|
+
wbTarget
|
|
506
738
|
);
|
|
507
739
|
reg(
|
|
508
740
|
"whiteboard_delete_item",
|
|
@@ -511,29 +743,37 @@ function registerWhiteboardBridge(server, options) {
|
|
|
511
743
|
["id"],
|
|
512
744
|
(args) => {
|
|
513
745
|
const id = str(args.id);
|
|
514
|
-
|
|
515
|
-
adapter.
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
});
|
|
520
|
-
adapter.
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
746
|
+
const removedNotes = adapter.getNotes().filter((x) => x.id === id);
|
|
747
|
+
const removedShapes = adapter.getShapes().filter((x) => x.id === id);
|
|
748
|
+
const removedConnectors = adapter.getConnectors().filter((x) => x.id === id);
|
|
749
|
+
const removedStrokes = adapter.getStrokes().filter((x) => x.id === id);
|
|
750
|
+
const removed = removedNotes.length + removedShapes.length + removedConnectors.length + removedStrokes.length > 0;
|
|
751
|
+
if (!removed) return errorResult(`No item with id ${id}`);
|
|
752
|
+
adapter.setNotes((all) => all.filter((x) => x.id !== id));
|
|
753
|
+
adapter.setShapes((all) => all.filter((x) => x.id !== id));
|
|
754
|
+
adapter.setConnectors((all) => all.filter((x) => x.id !== id));
|
|
755
|
+
adapter.setStrokes((all) => all.filter((x) => x.id !== id));
|
|
756
|
+
pushUndoEntry(agent.id, {
|
|
757
|
+
timestamp: Date.now(),
|
|
758
|
+
bridgeId: "whiteboard",
|
|
759
|
+
action: "whiteboard_delete_item",
|
|
760
|
+
label: `Deleted ${id}`,
|
|
761
|
+
undo: () => {
|
|
762
|
+
if (removedNotes.length) adapter.setNotes((all) => [...all, ...removedNotes]);
|
|
763
|
+
if (removedShapes.length) adapter.setShapes((all) => [...all, ...removedShapes]);
|
|
764
|
+
if (removedConnectors.length) adapter.setConnectors((all) => [...all, ...removedConnectors]);
|
|
765
|
+
if (removedStrokes.length) adapter.setStrokes((all) => [...all, ...removedStrokes]);
|
|
766
|
+
},
|
|
767
|
+
redo: () => {
|
|
768
|
+
adapter.setNotes((all) => all.filter((x) => x.id !== id));
|
|
769
|
+
adapter.setShapes((all) => all.filter((x) => x.id !== id));
|
|
770
|
+
adapter.setConnectors((all) => all.filter((x) => x.id !== id));
|
|
771
|
+
adapter.setStrokes((all) => all.filter((x) => x.id !== id));
|
|
772
|
+
}
|
|
534
773
|
});
|
|
535
|
-
return
|
|
536
|
-
}
|
|
774
|
+
return textResult(`Deleted ${id}`);
|
|
775
|
+
},
|
|
776
|
+
wbTarget
|
|
537
777
|
);
|
|
538
778
|
reg(
|
|
539
779
|
"whiteboard_set_viewport",
|
|
@@ -549,7 +789,8 @@ function registerWhiteboardBridge(server, options) {
|
|
|
549
789
|
};
|
|
550
790
|
adapter.setViewport(next);
|
|
551
791
|
return textResult(`Viewport \u2192 ${JSON.stringify(next)}`, next);
|
|
552
|
-
}
|
|
792
|
+
},
|
|
793
|
+
wbTarget
|
|
553
794
|
);
|
|
554
795
|
reg(
|
|
555
796
|
"whiteboard_set_agent_cursor",
|
|
@@ -575,7 +816,8 @@ function registerWhiteboardBridge(server, options) {
|
|
|
575
816
|
};
|
|
576
817
|
adapter.setAgentCursor(cursor);
|
|
577
818
|
return textResult(`Cursor \u2192 (${cursor.x}, ${cursor.y})`, cursor);
|
|
578
|
-
}
|
|
819
|
+
},
|
|
820
|
+
wbTarget
|
|
579
821
|
);
|
|
580
822
|
return {
|
|
581
823
|
id: "whiteboard",
|
|
@@ -586,14 +828,34 @@ function registerWhiteboardBridge(server, options) {
|
|
|
586
828
|
}
|
|
587
829
|
};
|
|
588
830
|
}
|
|
831
|
+
|
|
832
|
+
// src/bridges/flow.ts
|
|
833
|
+
var DEFAULT_AGENT2 = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
589
834
|
var num2 = (v, fallback) => typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
590
835
|
var str2 = (v, fallback = "") => typeof v === "string" ? v : fallback;
|
|
591
836
|
var newId2 = (prefix) => `${prefix}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 7)}`;
|
|
592
837
|
function registerFlowBridge(server, options) {
|
|
593
838
|
const { adapter } = options;
|
|
594
|
-
|
|
839
|
+
const agent = { ...DEFAULT_AGENT2, ...options.agent ?? {} };
|
|
595
840
|
const disposers = [];
|
|
596
|
-
const
|
|
841
|
+
const flTarget = (args, result) => ({
|
|
842
|
+
kind: "flow",
|
|
843
|
+
elementId: result?.structuredContent?.id ?? args?.id
|
|
844
|
+
});
|
|
845
|
+
const reg = (name, description, properties, required, handler, resolveTarget) => {
|
|
846
|
+
const wrapped = async (args) => {
|
|
847
|
+
try {
|
|
848
|
+
return await handler(args);
|
|
849
|
+
} catch (e) {
|
|
850
|
+
return errorResult(e instanceof Error ? e.message : String(e));
|
|
851
|
+
}
|
|
852
|
+
};
|
|
853
|
+
const final = resolveTarget ? wrapToolWithActivity(wrapped, {
|
|
854
|
+
toolName: name,
|
|
855
|
+
agent: { id: agent.id, name: agent.name, color: agent.color },
|
|
856
|
+
kind: "flow",
|
|
857
|
+
resolveTarget: ({ args, result }) => resolveTarget(args, result)
|
|
858
|
+
}) : wrapped;
|
|
597
859
|
disposers.push(
|
|
598
860
|
server.registerTool(
|
|
599
861
|
{
|
|
@@ -601,13 +863,7 @@ function registerFlowBridge(server, options) {
|
|
|
601
863
|
description,
|
|
602
864
|
inputSchema: { type: "object", properties, required, additionalProperties: false }
|
|
603
865
|
},
|
|
604
|
-
|
|
605
|
-
try {
|
|
606
|
-
return await handler(args);
|
|
607
|
-
} catch (e) {
|
|
608
|
-
return errorResult(e instanceof Error ? e.message : String(e));
|
|
609
|
-
}
|
|
610
|
-
}
|
|
866
|
+
final
|
|
611
867
|
)
|
|
612
868
|
);
|
|
613
869
|
};
|
|
@@ -743,7 +999,8 @@ function registerFlowBridge(server, options) {
|
|
|
743
999
|
};
|
|
744
1000
|
adapter.setNodes((all) => [...all, node]);
|
|
745
1001
|
return textResult(`Added ${kindName} ${id} ("${str2(args.label)}")`, node);
|
|
746
|
-
}
|
|
1002
|
+
},
|
|
1003
|
+
flTarget
|
|
747
1004
|
);
|
|
748
1005
|
reg(
|
|
749
1006
|
"flow_update_node",
|
|
@@ -781,7 +1038,8 @@ function registerFlowBridge(server, options) {
|
|
|
781
1038
|
);
|
|
782
1039
|
if (!updated) return errorResult(`No node with id ${id}`);
|
|
783
1040
|
return textResult(`Updated node ${id}`, updated);
|
|
784
|
-
}
|
|
1041
|
+
},
|
|
1042
|
+
flTarget
|
|
785
1043
|
);
|
|
786
1044
|
reg(
|
|
787
1045
|
"flow_delete_node",
|
|
@@ -796,7 +1054,8 @@ function registerFlowBridge(server, options) {
|
|
|
796
1054
|
adapter.setNodes((all) => all.filter((n) => n.id !== id));
|
|
797
1055
|
adapter.setEdges((all) => all.filter((e) => e.source !== id && e.target !== id));
|
|
798
1056
|
return textResult(`Deleted node ${id}`);
|
|
799
|
-
}
|
|
1057
|
+
},
|
|
1058
|
+
flTarget
|
|
800
1059
|
);
|
|
801
1060
|
reg(
|
|
802
1061
|
"flow_connect",
|
|
@@ -825,7 +1084,8 @@ function registerFlowBridge(server, options) {
|
|
|
825
1084
|
};
|
|
826
1085
|
adapter.setEdges((existing) => [...existing, edge]);
|
|
827
1086
|
return textResult(`Connected ${source}${edge.sourceHandle ? `:${edge.sourceHandle}` : ""} \u2192 ${target}${edge.targetHandle ? `:${edge.targetHandle}` : ""}`, edge);
|
|
828
|
-
}
|
|
1087
|
+
},
|
|
1088
|
+
flTarget
|
|
829
1089
|
);
|
|
830
1090
|
reg(
|
|
831
1091
|
"flow_disconnect",
|
|
@@ -839,7 +1099,8 @@ function registerFlowBridge(server, options) {
|
|
|
839
1099
|
}
|
|
840
1100
|
adapter.setEdges((all) => all.filter((e) => e.id !== id));
|
|
841
1101
|
return textResult(`Disconnected ${id}`);
|
|
842
|
-
}
|
|
1102
|
+
},
|
|
1103
|
+
flTarget
|
|
843
1104
|
);
|
|
844
1105
|
reg(
|
|
845
1106
|
"flow_set_node_status",
|
|
@@ -868,7 +1129,8 @@ function registerFlowBridge(server, options) {
|
|
|
868
1129
|
if (!found) return errorResult(`No node with id ${id}`);
|
|
869
1130
|
}
|
|
870
1131
|
return textResult(`${id} \u2192 ${status}${text ? ` (${text})` : ""}`);
|
|
871
|
-
}
|
|
1132
|
+
},
|
|
1133
|
+
flTarget
|
|
872
1134
|
);
|
|
873
1135
|
reg(
|
|
874
1136
|
"flow_run",
|
|
@@ -879,7 +1141,8 @@ function registerFlowBridge(server, options) {
|
|
|
879
1141
|
if (!adapter.run) return errorResult("Host did not provide a run handler.");
|
|
880
1142
|
const result = await adapter.run();
|
|
881
1143
|
return textResult(result.ok ? "Run complete" : `Run failed: ${result.error ?? "unknown"}`, result);
|
|
882
|
-
}
|
|
1144
|
+
},
|
|
1145
|
+
flTarget
|
|
883
1146
|
);
|
|
884
1147
|
reg(
|
|
885
1148
|
"flow_cancel",
|
|
@@ -890,7 +1153,8 @@ function registerFlowBridge(server, options) {
|
|
|
890
1153
|
if (!adapter.cancel) return errorResult("Host did not provide a cancel handler.");
|
|
891
1154
|
adapter.cancel();
|
|
892
1155
|
return textResult("Run cancelled");
|
|
893
|
-
}
|
|
1156
|
+
},
|
|
1157
|
+
flTarget
|
|
894
1158
|
);
|
|
895
1159
|
return {
|
|
896
1160
|
id: "flow",
|
|
@@ -900,6 +1164,909 @@ function registerFlowBridge(server, options) {
|
|
|
900
1164
|
}
|
|
901
1165
|
};
|
|
902
1166
|
}
|
|
1167
|
+
|
|
1168
|
+
// src/bridges/forms.ts
|
|
1169
|
+
var DEFAULT_AGENT3 = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
1170
|
+
function registerFormBridge(server, options) {
|
|
1171
|
+
const { adapter } = options;
|
|
1172
|
+
const agent = { ...DEFAULT_AGENT3, ...options.agent ?? {} };
|
|
1173
|
+
const disposers = [];
|
|
1174
|
+
const formId = adapter.id;
|
|
1175
|
+
const target = (args) => ({
|
|
1176
|
+
kind: "form",
|
|
1177
|
+
screenId: adapter.screenId,
|
|
1178
|
+
elementId: typeof args?.field === "string" ? `${formId}:${args.field}` : formId,
|
|
1179
|
+
label: typeof args?.field === "string" ? `${adapter.title ?? formId} \u2192 ${args.field}` : adapter.title ?? formId
|
|
1180
|
+
});
|
|
1181
|
+
const reg = (name, description, properties, required, handler, isMutation) => {
|
|
1182
|
+
const wrapped = async (args) => {
|
|
1183
|
+
try {
|
|
1184
|
+
return await handler(args);
|
|
1185
|
+
} catch (e) {
|
|
1186
|
+
return errorResult(e instanceof Error ? e.message : String(e));
|
|
1187
|
+
}
|
|
1188
|
+
};
|
|
1189
|
+
const final = isMutation ? wrapToolWithActivity(wrapped, {
|
|
1190
|
+
toolName: name,
|
|
1191
|
+
agent: { id: agent.id, name: agent.name, color: agent.color },
|
|
1192
|
+
kind: "form",
|
|
1193
|
+
screenId: adapter.screenId,
|
|
1194
|
+
resolveTarget: ({ args }) => target(args)
|
|
1195
|
+
}) : wrapped;
|
|
1196
|
+
disposers.push(
|
|
1197
|
+
server.registerTool(
|
|
1198
|
+
{
|
|
1199
|
+
name,
|
|
1200
|
+
description,
|
|
1201
|
+
inputSchema: { type: "object", properties, required, additionalProperties: false }
|
|
1202
|
+
},
|
|
1203
|
+
final
|
|
1204
|
+
)
|
|
1205
|
+
);
|
|
1206
|
+
};
|
|
1207
|
+
reg(
|
|
1208
|
+
"form_describe",
|
|
1209
|
+
`Describe the form "${formId}" \u2014 fields, types, options, required flags. Call this first to know what's fillable.`,
|
|
1210
|
+
{},
|
|
1211
|
+
[],
|
|
1212
|
+
() => {
|
|
1213
|
+
const fields = adapter.getFields();
|
|
1214
|
+
const text = fields.map((f) => `${f.name}${f.required ? "*" : ""} (${f.type})${f.label ? ` \u2014 ${f.label}` : ""}`).join("\n");
|
|
1215
|
+
return textResult(text || "(no fields)", { id: formId, title: adapter.title, fields });
|
|
1216
|
+
},
|
|
1217
|
+
false
|
|
1218
|
+
);
|
|
1219
|
+
reg(
|
|
1220
|
+
"form_get_value",
|
|
1221
|
+
"Read a single field's current value.",
|
|
1222
|
+
{ field: { type: "string" } },
|
|
1223
|
+
["field"],
|
|
1224
|
+
(args) => {
|
|
1225
|
+
const name = String(args.field ?? "");
|
|
1226
|
+
if (!adapter.getFields().find((f) => f.name === name)) {
|
|
1227
|
+
return errorResult(`Unknown field: ${name}`);
|
|
1228
|
+
}
|
|
1229
|
+
const value = adapter.getValue(name);
|
|
1230
|
+
return textResult(JSON.stringify(value), { field: name, value });
|
|
1231
|
+
},
|
|
1232
|
+
false
|
|
1233
|
+
);
|
|
1234
|
+
reg(
|
|
1235
|
+
"form_get_values",
|
|
1236
|
+
"Read every field's current value as a JSON object.",
|
|
1237
|
+
{},
|
|
1238
|
+
[],
|
|
1239
|
+
() => {
|
|
1240
|
+
const values = adapter.getValues();
|
|
1241
|
+
return textResult(JSON.stringify(values, null, 2), values);
|
|
1242
|
+
},
|
|
1243
|
+
false
|
|
1244
|
+
);
|
|
1245
|
+
reg(
|
|
1246
|
+
"form_set_value",
|
|
1247
|
+
"Set one field's value. The host's controlled state updates and the human sees the field change.",
|
|
1248
|
+
{
|
|
1249
|
+
field: { type: "string" },
|
|
1250
|
+
value: { description: "Value to set. Type depends on the field's `type`." }
|
|
1251
|
+
},
|
|
1252
|
+
["field", "value"],
|
|
1253
|
+
(args) => {
|
|
1254
|
+
const name = String(args.field ?? "");
|
|
1255
|
+
const fieldDef = adapter.getFields().find((f) => f.name === name);
|
|
1256
|
+
if (!fieldDef) return errorResult(`Unknown field: ${name}`);
|
|
1257
|
+
adapter.setValue(name, args.value);
|
|
1258
|
+
return textResult(`${name} \u2190 ${JSON.stringify(args.value)}`, { field: name, value: args.value });
|
|
1259
|
+
},
|
|
1260
|
+
true
|
|
1261
|
+
);
|
|
1262
|
+
reg(
|
|
1263
|
+
"form_set_values",
|
|
1264
|
+
"Set multiple fields atomically. Pass a `values` object keyed by field name.",
|
|
1265
|
+
{ values: { type: "object" } },
|
|
1266
|
+
["values"],
|
|
1267
|
+
(args) => {
|
|
1268
|
+
const values = args.values && typeof args.values === "object" ? args.values : {};
|
|
1269
|
+
const fields = adapter.getFields();
|
|
1270
|
+
const known = new Set(fields.map((f) => f.name));
|
|
1271
|
+
const unknownKeys = Object.keys(values).filter((k) => !known.has(k));
|
|
1272
|
+
if (unknownKeys.length) return errorResult(`Unknown fields: ${unknownKeys.join(", ")}`);
|
|
1273
|
+
if (adapter.setValues) {
|
|
1274
|
+
adapter.setValues(values);
|
|
1275
|
+
} else {
|
|
1276
|
+
for (const [k, v] of Object.entries(values)) adapter.setValue(k, v);
|
|
1277
|
+
}
|
|
1278
|
+
return textResult(`Set ${Object.keys(values).length} field(s)`, { values });
|
|
1279
|
+
},
|
|
1280
|
+
true
|
|
1281
|
+
);
|
|
1282
|
+
reg(
|
|
1283
|
+
"form_focus",
|
|
1284
|
+
"Move browser focus to a field (host-implemented). Useful before streaming text into it.",
|
|
1285
|
+
{ field: { type: "string" } },
|
|
1286
|
+
["field"],
|
|
1287
|
+
(args) => {
|
|
1288
|
+
const name = String(args.field ?? "");
|
|
1289
|
+
if (!adapter.focus) return errorResult("Host did not provide a focus implementation.");
|
|
1290
|
+
if (!adapter.getFields().find((f) => f.name === name)) {
|
|
1291
|
+
return errorResult(`Unknown field: ${name}`);
|
|
1292
|
+
}
|
|
1293
|
+
adapter.focus(name);
|
|
1294
|
+
return textResult(`Focused ${name}`, { field: name });
|
|
1295
|
+
},
|
|
1296
|
+
true
|
|
1297
|
+
);
|
|
1298
|
+
reg(
|
|
1299
|
+
"form_submit",
|
|
1300
|
+
"Submit the form. Host returns ok + values (or an error).",
|
|
1301
|
+
{},
|
|
1302
|
+
[],
|
|
1303
|
+
async () => {
|
|
1304
|
+
if (!adapter.submit) return errorResult("Host did not provide a submit implementation.");
|
|
1305
|
+
const result = await adapter.submit();
|
|
1306
|
+
if (!result.ok) return errorResult(result.error ?? "Submit failed");
|
|
1307
|
+
return textResult("Submitted", { values: result.values });
|
|
1308
|
+
},
|
|
1309
|
+
true
|
|
1310
|
+
);
|
|
1311
|
+
return {
|
|
1312
|
+
id: `form:${formId}`,
|
|
1313
|
+
title: adapter.title ?? formId,
|
|
1314
|
+
dispose: () => {
|
|
1315
|
+
for (const d of disposers) d();
|
|
1316
|
+
}
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
// src/bridges/sheets.ts
|
|
1321
|
+
var DEFAULT_AGENT4 = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
1322
|
+
function registerSheetsBridge(server, options) {
|
|
1323
|
+
const { adapter } = options;
|
|
1324
|
+
const agent = { ...DEFAULT_AGENT4, ...options.agent ?? {} };
|
|
1325
|
+
const disposers = [];
|
|
1326
|
+
const target = (sheetId, address) => ({
|
|
1327
|
+
kind: "sheet",
|
|
1328
|
+
screenId: adapter.screenId,
|
|
1329
|
+
elementId: address ? `${sheetId}!${address}` : sheetId,
|
|
1330
|
+
label: address ? `${sheetId}!${address}` : sheetId
|
|
1331
|
+
});
|
|
1332
|
+
const reg = (name, description, properties, required, handler, isMutation, resolveTarget) => {
|
|
1333
|
+
const wrapped = async (args) => {
|
|
1334
|
+
try {
|
|
1335
|
+
return await handler(args);
|
|
1336
|
+
} catch (e) {
|
|
1337
|
+
return errorResult(e instanceof Error ? e.message : String(e));
|
|
1338
|
+
}
|
|
1339
|
+
};
|
|
1340
|
+
const final = isMutation ? wrapToolWithActivity(wrapped, {
|
|
1341
|
+
toolName: name,
|
|
1342
|
+
agent,
|
|
1343
|
+
kind: "sheet",
|
|
1344
|
+
screenId: adapter.screenId,
|
|
1345
|
+
resolveTarget: ({ args, result }) => resolveTarget?.(args, result) ?? target(getSheetId(args))
|
|
1346
|
+
}) : wrapped;
|
|
1347
|
+
disposers.push(
|
|
1348
|
+
server.registerTool(
|
|
1349
|
+
{
|
|
1350
|
+
name,
|
|
1351
|
+
description,
|
|
1352
|
+
inputSchema: { type: "object", properties, required, additionalProperties: false }
|
|
1353
|
+
},
|
|
1354
|
+
final
|
|
1355
|
+
)
|
|
1356
|
+
);
|
|
1357
|
+
};
|
|
1358
|
+
function activeSheetId() {
|
|
1359
|
+
return adapter.getWorkbook().activeSheetId;
|
|
1360
|
+
}
|
|
1361
|
+
function getSheetId(args) {
|
|
1362
|
+
return typeof args.sheet === "string" ? args.sheet : activeSheetId();
|
|
1363
|
+
}
|
|
1364
|
+
function getSheet(workbook, sheetId) {
|
|
1365
|
+
return workbook.sheets.find((s) => s.id === sheetId);
|
|
1366
|
+
}
|
|
1367
|
+
reg(
|
|
1368
|
+
"sheet_describe",
|
|
1369
|
+
"Describe the workbook: every sheet's id, name, dimensions, cell count, active sheet. Call before reading or writing.",
|
|
1370
|
+
{},
|
|
1371
|
+
[],
|
|
1372
|
+
() => {
|
|
1373
|
+
const wb = adapter.getWorkbook();
|
|
1374
|
+
const summary = {
|
|
1375
|
+
activeSheetId: wb.activeSheetId,
|
|
1376
|
+
sheets: wb.sheets.map((s) => ({
|
|
1377
|
+
id: s.id,
|
|
1378
|
+
name: s.name,
|
|
1379
|
+
cellCount: Object.keys(s.cells).length,
|
|
1380
|
+
columnCount: Object.keys(s.columnWidths).length,
|
|
1381
|
+
frozenRows: s.frozenRows,
|
|
1382
|
+
frozenCols: s.frozenCols
|
|
1383
|
+
}))
|
|
1384
|
+
};
|
|
1385
|
+
const text = `Active: ${summary.activeSheetId}
|
|
1386
|
+
` + summary.sheets.map((s) => `${s.id} "${s.name}" \u2014 ${s.cellCount} cells`).join("\n");
|
|
1387
|
+
return textResult(text, summary);
|
|
1388
|
+
},
|
|
1389
|
+
false
|
|
1390
|
+
);
|
|
1391
|
+
reg(
|
|
1392
|
+
"sheet_get_cell",
|
|
1393
|
+
"Read a single cell's raw + computed value.",
|
|
1394
|
+
{
|
|
1395
|
+
sheet: { type: "string", description: "Sheet id (defaults to active)." },
|
|
1396
|
+
address: { type: "string", description: 'A1-style address, e.g. "B12".' }
|
|
1397
|
+
},
|
|
1398
|
+
["address"],
|
|
1399
|
+
(args) => {
|
|
1400
|
+
const sheetId = getSheetId(args);
|
|
1401
|
+
const address = String(args.address);
|
|
1402
|
+
const sheet = getSheet(adapter.getWorkbook(), sheetId);
|
|
1403
|
+
if (!sheet) return errorResult(`No sheet ${sheetId}`);
|
|
1404
|
+
const cell = sheet.cells[address];
|
|
1405
|
+
if (!cell) {
|
|
1406
|
+
return textResult(`(empty)`, { sheet: sheetId, address, value: null });
|
|
1407
|
+
}
|
|
1408
|
+
return textResult(`${address} = ${JSON.stringify(cell.computedValue ?? cell.value)}`, { ...cell, sheet: sheetId, address });
|
|
1409
|
+
},
|
|
1410
|
+
false
|
|
1411
|
+
);
|
|
1412
|
+
reg(
|
|
1413
|
+
"sheet_get_range",
|
|
1414
|
+
"Read a rectangular range as a 2D array of values.",
|
|
1415
|
+
{
|
|
1416
|
+
sheet: { type: "string" },
|
|
1417
|
+
start: { type: "string", description: "Top-left A1 address." },
|
|
1418
|
+
end: { type: "string", description: "Bottom-right A1 address." }
|
|
1419
|
+
},
|
|
1420
|
+
["start", "end"],
|
|
1421
|
+
(args) => {
|
|
1422
|
+
const sheetId = getSheetId(args);
|
|
1423
|
+
const sheet = getSheet(adapter.getWorkbook(), sheetId);
|
|
1424
|
+
if (!sheet) return errorResult(`No sheet ${sheetId}`);
|
|
1425
|
+
const grid = readRange(sheet, String(args.start), String(args.end));
|
|
1426
|
+
return textResult(JSON.stringify(grid), { sheet: sheetId, start: args.start, end: args.end, values: grid });
|
|
1427
|
+
},
|
|
1428
|
+
false
|
|
1429
|
+
);
|
|
1430
|
+
reg(
|
|
1431
|
+
"sheet_set_cell",
|
|
1432
|
+
"Set a single cell's value. To set a formula, pass a string starting with '='.",
|
|
1433
|
+
{
|
|
1434
|
+
sheet: { type: "string" },
|
|
1435
|
+
address: { type: "string" },
|
|
1436
|
+
value: { description: "string | number | boolean | null. Strings starting with '=' are stored as formulas." }
|
|
1437
|
+
},
|
|
1438
|
+
["address", "value"],
|
|
1439
|
+
(args) => {
|
|
1440
|
+
const sheetId = getSheetId(args);
|
|
1441
|
+
const address = String(args.address);
|
|
1442
|
+
const value = args.value;
|
|
1443
|
+
const wb = adapter.getWorkbook();
|
|
1444
|
+
const sheet = getSheet(wb, sheetId);
|
|
1445
|
+
if (!sheet) return errorResult(`No sheet ${sheetId}`);
|
|
1446
|
+
const next = mergeCells(wb, sheetId, { [address]: cellOf(address, value) });
|
|
1447
|
+
adapter.setWorkbook(next);
|
|
1448
|
+
return textResult(`${sheetId}!${address} \u2190 ${JSON.stringify(value)}`, { sheet: sheetId, address, value });
|
|
1449
|
+
},
|
|
1450
|
+
true,
|
|
1451
|
+
(args) => target(getSheetId(args), String(args.address ?? ""))
|
|
1452
|
+
);
|
|
1453
|
+
reg(
|
|
1454
|
+
"sheet_set_range",
|
|
1455
|
+
'Set many cells atomically. `cells` is an object map of { "A1": value, "B2": value, ... }.',
|
|
1456
|
+
{
|
|
1457
|
+
sheet: { type: "string" },
|
|
1458
|
+
cells: { type: "object" }
|
|
1459
|
+
},
|
|
1460
|
+
["cells"],
|
|
1461
|
+
(args) => {
|
|
1462
|
+
const sheetId = getSheetId(args);
|
|
1463
|
+
const wb = adapter.getWorkbook();
|
|
1464
|
+
const sheet = getSheet(wb, sheetId);
|
|
1465
|
+
if (!sheet) return errorResult(`No sheet ${sheetId}`);
|
|
1466
|
+
const cells = args.cells && typeof args.cells === "object" ? args.cells : {};
|
|
1467
|
+
const updates = {};
|
|
1468
|
+
for (const [addr, v] of Object.entries(cells)) updates[addr] = cellOf(addr, v);
|
|
1469
|
+
const next = mergeCells(wb, sheetId, updates);
|
|
1470
|
+
adapter.setWorkbook(next);
|
|
1471
|
+
return textResult(`Set ${Object.keys(cells).length} cells in ${sheetId}`, { sheet: sheetId, count: Object.keys(cells).length });
|
|
1472
|
+
},
|
|
1473
|
+
true
|
|
1474
|
+
);
|
|
1475
|
+
reg(
|
|
1476
|
+
"sheet_add_sheet",
|
|
1477
|
+
"Add a new sheet (tab) to the workbook.",
|
|
1478
|
+
{ id: { type: "string" }, name: { type: "string" } },
|
|
1479
|
+
["id", "name"],
|
|
1480
|
+
(args) => {
|
|
1481
|
+
const wb = adapter.getWorkbook();
|
|
1482
|
+
const id = String(args.id);
|
|
1483
|
+
const name = String(args.name);
|
|
1484
|
+
if (wb.sheets.find((s) => s.id === id)) return errorResult(`Sheet ${id} already exists`);
|
|
1485
|
+
const next = {
|
|
1486
|
+
...wb,
|
|
1487
|
+
sheets: [
|
|
1488
|
+
...wb.sheets,
|
|
1489
|
+
{
|
|
1490
|
+
id,
|
|
1491
|
+
name,
|
|
1492
|
+
cells: {},
|
|
1493
|
+
columnWidths: {},
|
|
1494
|
+
mergedRegions: [],
|
|
1495
|
+
columnFilters: {},
|
|
1496
|
+
frozenRows: 0,
|
|
1497
|
+
frozenCols: 0
|
|
1498
|
+
}
|
|
1499
|
+
]
|
|
1500
|
+
};
|
|
1501
|
+
adapter.setWorkbook(next);
|
|
1502
|
+
return textResult(`Added sheet ${id} ("${name}")`, { id, name });
|
|
1503
|
+
},
|
|
1504
|
+
true,
|
|
1505
|
+
(args) => target(String(args.id ?? ""))
|
|
1506
|
+
);
|
|
1507
|
+
reg(
|
|
1508
|
+
"sheet_set_active",
|
|
1509
|
+
"Switch to a different sheet tab.",
|
|
1510
|
+
{ sheet: { type: "string" } },
|
|
1511
|
+
["sheet"],
|
|
1512
|
+
(args) => {
|
|
1513
|
+
const sheetId = String(args.sheet);
|
|
1514
|
+
const wb = adapter.getWorkbook();
|
|
1515
|
+
if (!getSheet(wb, sheetId)) return errorResult(`No sheet ${sheetId}`);
|
|
1516
|
+
adapter.setWorkbook({ ...wb, activeSheetId: sheetId });
|
|
1517
|
+
return textResult(`Active sheet \u2192 ${sheetId}`, { sheet: sheetId });
|
|
1518
|
+
},
|
|
1519
|
+
true
|
|
1520
|
+
);
|
|
1521
|
+
reg(
|
|
1522
|
+
"sheet_set_active_cell",
|
|
1523
|
+
"Move the active cell selection (host implements DOM focus + scroll).",
|
|
1524
|
+
{ sheet: { type: "string" }, address: { type: "string" } },
|
|
1525
|
+
["address"],
|
|
1526
|
+
(args) => {
|
|
1527
|
+
if (!adapter.setActiveCell) return errorResult("Host did not provide setActiveCell.");
|
|
1528
|
+
const sheetId = getSheetId(args);
|
|
1529
|
+
adapter.setActiveCell(sheetId, String(args.address));
|
|
1530
|
+
return textResult(`Active cell \u2192 ${sheetId}!${args.address}`, { sheet: sheetId, address: args.address });
|
|
1531
|
+
},
|
|
1532
|
+
true,
|
|
1533
|
+
(args) => target(getSheetId(args), String(args.address ?? ""))
|
|
1534
|
+
);
|
|
1535
|
+
return {
|
|
1536
|
+
id: "sheets",
|
|
1537
|
+
title: "Sheets",
|
|
1538
|
+
dispose: () => {
|
|
1539
|
+
for (const d of disposers) d();
|
|
1540
|
+
}
|
|
1541
|
+
};
|
|
1542
|
+
}
|
|
1543
|
+
function cellOf(address, value) {
|
|
1544
|
+
return { address, value };
|
|
1545
|
+
}
|
|
1546
|
+
function mergeCells(wb, sheetId, updates) {
|
|
1547
|
+
return {
|
|
1548
|
+
...wb,
|
|
1549
|
+
sheets: wb.sheets.map(
|
|
1550
|
+
(s) => s.id !== sheetId ? s : { ...s, cells: { ...s.cells, ...updates } }
|
|
1551
|
+
)
|
|
1552
|
+
};
|
|
1553
|
+
}
|
|
1554
|
+
function parseAddress(addr) {
|
|
1555
|
+
const m = /^([A-Za-z]+)(\d+)$/.exec(addr.trim());
|
|
1556
|
+
if (!m) throw new Error(`Bad address: ${addr}`);
|
|
1557
|
+
const letters = m[1].toUpperCase();
|
|
1558
|
+
let col = 0;
|
|
1559
|
+
for (let i = 0; i < letters.length; i++) {
|
|
1560
|
+
col = col * 26 + (letters.charCodeAt(i) - 64);
|
|
1561
|
+
}
|
|
1562
|
+
return { col: col - 1, row: parseInt(m[2], 10) - 1 };
|
|
1563
|
+
}
|
|
1564
|
+
function colToLetter(col) {
|
|
1565
|
+
let s = "";
|
|
1566
|
+
let n = col + 1;
|
|
1567
|
+
while (n > 0) {
|
|
1568
|
+
const r = (n - 1) % 26;
|
|
1569
|
+
s = String.fromCharCode(65 + r) + s;
|
|
1570
|
+
n = Math.floor((n - 1) / 26);
|
|
1571
|
+
}
|
|
1572
|
+
return s;
|
|
1573
|
+
}
|
|
1574
|
+
function readRange(sheet, startAddr, endAddr) {
|
|
1575
|
+
const start = parseAddress(startAddr);
|
|
1576
|
+
const end = parseAddress(endAddr);
|
|
1577
|
+
const r0 = Math.min(start.row, end.row);
|
|
1578
|
+
const r1 = Math.max(start.row, end.row);
|
|
1579
|
+
const c0 = Math.min(start.col, end.col);
|
|
1580
|
+
const c1 = Math.max(start.col, end.col);
|
|
1581
|
+
const grid = [];
|
|
1582
|
+
for (let r = r0; r <= r1; r++) {
|
|
1583
|
+
const row = [];
|
|
1584
|
+
for (let c = c0; c <= c1; c++) {
|
|
1585
|
+
const addr = `${colToLetter(c)}${r + 1}`;
|
|
1586
|
+
const cell = sheet.cells[addr];
|
|
1587
|
+
row.push(cell?.computedValue ?? cell?.value ?? null);
|
|
1588
|
+
}
|
|
1589
|
+
grid.push(row);
|
|
1590
|
+
}
|
|
1591
|
+
return grid;
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
// src/bridges/code.ts
|
|
1595
|
+
var DEFAULT_AGENT5 = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
1596
|
+
function registerCodeBridge(server, options) {
|
|
1597
|
+
const { adapter } = options;
|
|
1598
|
+
const agent = { ...DEFAULT_AGENT5, ...options.agent ?? {} };
|
|
1599
|
+
const disposers = [];
|
|
1600
|
+
const target = {
|
|
1601
|
+
kind: "code",
|
|
1602
|
+
screenId: adapter.screenId,
|
|
1603
|
+
elementId: adapter.id,
|
|
1604
|
+
label: adapter.title ?? adapter.id
|
|
1605
|
+
};
|
|
1606
|
+
const reg = (name, description, properties, required, handler, isMutation) => {
|
|
1607
|
+
const wrapped = async (args) => {
|
|
1608
|
+
try {
|
|
1609
|
+
return await handler(args);
|
|
1610
|
+
} catch (e) {
|
|
1611
|
+
return errorResult(e instanceof Error ? e.message : String(e));
|
|
1612
|
+
}
|
|
1613
|
+
};
|
|
1614
|
+
const final = isMutation ? wrapToolWithActivity(wrapped, {
|
|
1615
|
+
toolName: name,
|
|
1616
|
+
agent,
|
|
1617
|
+
kind: "code",
|
|
1618
|
+
screenId: adapter.screenId,
|
|
1619
|
+
resolveTarget: () => target
|
|
1620
|
+
}) : wrapped;
|
|
1621
|
+
disposers.push(
|
|
1622
|
+
server.registerTool(
|
|
1623
|
+
{
|
|
1624
|
+
name,
|
|
1625
|
+
description,
|
|
1626
|
+
inputSchema: { type: "object", properties, required, additionalProperties: false }
|
|
1627
|
+
},
|
|
1628
|
+
final
|
|
1629
|
+
)
|
|
1630
|
+
);
|
|
1631
|
+
};
|
|
1632
|
+
reg(
|
|
1633
|
+
"code_describe",
|
|
1634
|
+
`Describe the editor "${adapter.id}" \u2014 language + length + has-selection.`,
|
|
1635
|
+
{},
|
|
1636
|
+
[],
|
|
1637
|
+
() => {
|
|
1638
|
+
const value = adapter.getValue();
|
|
1639
|
+
const language = adapter.getLanguage?.() ?? "unknown";
|
|
1640
|
+
const summary = { id: adapter.id, language, length: value.length, lines: value.split("\n").length };
|
|
1641
|
+
return textResult(JSON.stringify(summary), summary);
|
|
1642
|
+
},
|
|
1643
|
+
false
|
|
1644
|
+
);
|
|
1645
|
+
reg(
|
|
1646
|
+
"code_get_value",
|
|
1647
|
+
"Read the full document text.",
|
|
1648
|
+
{},
|
|
1649
|
+
[],
|
|
1650
|
+
() => {
|
|
1651
|
+
const value = adapter.getValue();
|
|
1652
|
+
return textResult(value, { value });
|
|
1653
|
+
},
|
|
1654
|
+
false
|
|
1655
|
+
);
|
|
1656
|
+
reg(
|
|
1657
|
+
"code_get_selection",
|
|
1658
|
+
"Read the currently-selected text (empty string if no selection).",
|
|
1659
|
+
{},
|
|
1660
|
+
[],
|
|
1661
|
+
() => {
|
|
1662
|
+
if (!adapter.getSelection) return errorResult("Host did not provide getSelection.");
|
|
1663
|
+
const value = adapter.getSelection();
|
|
1664
|
+
return textResult(value, { value });
|
|
1665
|
+
},
|
|
1666
|
+
false
|
|
1667
|
+
);
|
|
1668
|
+
reg(
|
|
1669
|
+
"code_set_value",
|
|
1670
|
+
"Replace the entire document.",
|
|
1671
|
+
{ value: { type: "string" } },
|
|
1672
|
+
["value"],
|
|
1673
|
+
(args) => {
|
|
1674
|
+
const value = String(args.value ?? "");
|
|
1675
|
+
adapter.setValue(value);
|
|
1676
|
+
return textResult(`Replaced document (${value.length} chars)`, { length: value.length });
|
|
1677
|
+
},
|
|
1678
|
+
true
|
|
1679
|
+
);
|
|
1680
|
+
reg(
|
|
1681
|
+
"code_append",
|
|
1682
|
+
"Append text to the end of the document.",
|
|
1683
|
+
{ text: { type: "string" } },
|
|
1684
|
+
["text"],
|
|
1685
|
+
(args) => {
|
|
1686
|
+
const text = String(args.text ?? "");
|
|
1687
|
+
const next = adapter.getValue() + text;
|
|
1688
|
+
adapter.setValue(next);
|
|
1689
|
+
return textResult(`Appended ${text.length} chars`, { length: next.length });
|
|
1690
|
+
},
|
|
1691
|
+
true
|
|
1692
|
+
);
|
|
1693
|
+
reg(
|
|
1694
|
+
"code_stream_append",
|
|
1695
|
+
"Type characters into the document one at a time so the human can read it forming. Returns when streaming finishes.",
|
|
1696
|
+
{
|
|
1697
|
+
text: { type: "string" },
|
|
1698
|
+
cps: { type: "number", description: "Characters per second. Default 25." }
|
|
1699
|
+
},
|
|
1700
|
+
["text"],
|
|
1701
|
+
async (args) => {
|
|
1702
|
+
const text = String(args.text ?? "");
|
|
1703
|
+
const cps = Math.max(1, Number(args.cps ?? 25));
|
|
1704
|
+
const interval = Math.max(8, Math.round(1e3 / cps));
|
|
1705
|
+
const start = adapter.getValue();
|
|
1706
|
+
for (let i = 1; i <= text.length; i++) {
|
|
1707
|
+
adapter.setValue(start + text.slice(0, i));
|
|
1708
|
+
if (i < text.length) await new Promise((r) => setTimeout(r, interval));
|
|
1709
|
+
}
|
|
1710
|
+
return textResult(`Streamed ${text.length} chars`, { length: text.length });
|
|
1711
|
+
},
|
|
1712
|
+
true
|
|
1713
|
+
);
|
|
1714
|
+
reg(
|
|
1715
|
+
"code_replace_selection",
|
|
1716
|
+
"Replace the currently-selected text with the supplied text.",
|
|
1717
|
+
{ text: { type: "string" } },
|
|
1718
|
+
["text"],
|
|
1719
|
+
(args) => {
|
|
1720
|
+
if (!adapter.replaceSelection) return errorResult("Host did not provide replaceSelection.");
|
|
1721
|
+
adapter.replaceSelection(String(args.text ?? ""));
|
|
1722
|
+
return textResult("Selection replaced", {});
|
|
1723
|
+
},
|
|
1724
|
+
true
|
|
1725
|
+
);
|
|
1726
|
+
reg(
|
|
1727
|
+
"code_set_language",
|
|
1728
|
+
"Switch the active syntax highlighting / formatter.",
|
|
1729
|
+
{ language: { type: "string", description: "e.g. 'javascript', 'php', 'sql'." } },
|
|
1730
|
+
["language"],
|
|
1731
|
+
(args) => {
|
|
1732
|
+
if (!adapter.setLanguage) return errorResult("Host did not provide setLanguage.");
|
|
1733
|
+
const lang = String(args.language ?? "");
|
|
1734
|
+
adapter.setLanguage(lang);
|
|
1735
|
+
return textResult(`Language \u2192 ${lang}`, { language: lang });
|
|
1736
|
+
},
|
|
1737
|
+
true
|
|
1738
|
+
);
|
|
1739
|
+
reg(
|
|
1740
|
+
"code_focus",
|
|
1741
|
+
"Move browser focus to the editor.",
|
|
1742
|
+
{},
|
|
1743
|
+
[],
|
|
1744
|
+
() => {
|
|
1745
|
+
if (!adapter.focus) return errorResult("Host did not provide focus.");
|
|
1746
|
+
adapter.focus();
|
|
1747
|
+
return textResult("Focused", {});
|
|
1748
|
+
},
|
|
1749
|
+
true
|
|
1750
|
+
);
|
|
1751
|
+
return {
|
|
1752
|
+
id: `code:${adapter.id}`,
|
|
1753
|
+
title: adapter.title ?? adapter.id,
|
|
1754
|
+
dispose: () => {
|
|
1755
|
+
for (const d of disposers) d();
|
|
1756
|
+
}
|
|
1757
|
+
};
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
// src/bridges/charts.ts
|
|
1761
|
+
var DEFAULT_AGENT6 = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
1762
|
+
function registerChartsBridge(server, options) {
|
|
1763
|
+
const { adapter } = options;
|
|
1764
|
+
const agent = { ...DEFAULT_AGENT6, ...options.agent ?? {} };
|
|
1765
|
+
const disposers = [];
|
|
1766
|
+
const target = {
|
|
1767
|
+
kind: "chart",
|
|
1768
|
+
screenId: adapter.screenId,
|
|
1769
|
+
elementId: adapter.id,
|
|
1770
|
+
label: adapter.title ?? adapter.id
|
|
1771
|
+
};
|
|
1772
|
+
const reg = (name, description, properties, required, handler, isMutation) => {
|
|
1773
|
+
const wrapped = async (args) => {
|
|
1774
|
+
try {
|
|
1775
|
+
return await handler(args);
|
|
1776
|
+
} catch (e) {
|
|
1777
|
+
return errorResult(e instanceof Error ? e.message : String(e));
|
|
1778
|
+
}
|
|
1779
|
+
};
|
|
1780
|
+
const final = isMutation ? wrapToolWithActivity(wrapped, {
|
|
1781
|
+
toolName: name,
|
|
1782
|
+
agent,
|
|
1783
|
+
kind: "chart",
|
|
1784
|
+
screenId: adapter.screenId,
|
|
1785
|
+
resolveTarget: () => target
|
|
1786
|
+
}) : wrapped;
|
|
1787
|
+
disposers.push(
|
|
1788
|
+
server.registerTool(
|
|
1789
|
+
{ name, description, inputSchema: { type: "object", properties, required, additionalProperties: false } },
|
|
1790
|
+
final
|
|
1791
|
+
)
|
|
1792
|
+
);
|
|
1793
|
+
};
|
|
1794
|
+
reg(
|
|
1795
|
+
"chart_describe",
|
|
1796
|
+
`Describe the chart "${adapter.id}" \u2014 series count, type guesses, axis info.`,
|
|
1797
|
+
{},
|
|
1798
|
+
[],
|
|
1799
|
+
() => {
|
|
1800
|
+
const opt = adapter.getOption();
|
|
1801
|
+
const series = Array.isArray(opt.series) ? opt.series : [];
|
|
1802
|
+
const summary = {
|
|
1803
|
+
id: adapter.id,
|
|
1804
|
+
title: adapter.title,
|
|
1805
|
+
seriesCount: series.length,
|
|
1806
|
+
seriesTypes: series.map((s) => s?.type ?? "unknown"),
|
|
1807
|
+
hasXAxis: !!opt.xAxis,
|
|
1808
|
+
hasYAxis: !!opt.yAxis
|
|
1809
|
+
};
|
|
1810
|
+
return textResult(JSON.stringify(summary), summary);
|
|
1811
|
+
},
|
|
1812
|
+
false
|
|
1813
|
+
);
|
|
1814
|
+
reg(
|
|
1815
|
+
"chart_get_option",
|
|
1816
|
+
"Read the full ECharts option object the chart is rendering.",
|
|
1817
|
+
{},
|
|
1818
|
+
[],
|
|
1819
|
+
() => {
|
|
1820
|
+
const opt = adapter.getOption();
|
|
1821
|
+
return textResult(JSON.stringify(opt, null, 2), opt);
|
|
1822
|
+
},
|
|
1823
|
+
false
|
|
1824
|
+
);
|
|
1825
|
+
reg(
|
|
1826
|
+
"chart_set_option",
|
|
1827
|
+
"Replace the entire ECharts option. Use chart_update_option for partial updates.",
|
|
1828
|
+
{ option: { type: "object" } },
|
|
1829
|
+
["option"],
|
|
1830
|
+
(args) => {
|
|
1831
|
+
const opt = args.option && typeof args.option === "object" ? args.option : {};
|
|
1832
|
+
adapter.setOption(opt);
|
|
1833
|
+
return textResult("Replaced chart option", {});
|
|
1834
|
+
},
|
|
1835
|
+
true
|
|
1836
|
+
);
|
|
1837
|
+
reg(
|
|
1838
|
+
"chart_update_option",
|
|
1839
|
+
"Shallow-merge a partial option update \u2014 only the keys you provide change.",
|
|
1840
|
+
{ partial: { type: "object" } },
|
|
1841
|
+
["partial"],
|
|
1842
|
+
(args) => {
|
|
1843
|
+
const partial = args.partial && typeof args.partial === "object" ? args.partial : {};
|
|
1844
|
+
if (adapter.updateOption) {
|
|
1845
|
+
adapter.updateOption(partial);
|
|
1846
|
+
} else {
|
|
1847
|
+
adapter.setOption({ ...adapter.getOption(), ...partial });
|
|
1848
|
+
}
|
|
1849
|
+
return textResult("Merged chart option", { keys: Object.keys(partial) });
|
|
1850
|
+
},
|
|
1851
|
+
true
|
|
1852
|
+
);
|
|
1853
|
+
reg(
|
|
1854
|
+
"chart_update_data",
|
|
1855
|
+
"Update only the data (typically the `series` field). Leaves layout / axes / colors alone.",
|
|
1856
|
+
{ data: { description: "New series array (or whatever shape the host's adapter expects)." } },
|
|
1857
|
+
["data"],
|
|
1858
|
+
(args) => {
|
|
1859
|
+
if (!adapter.updateData) return errorResult("Host did not provide updateData.");
|
|
1860
|
+
adapter.updateData(args.data);
|
|
1861
|
+
return textResult("Updated chart data", {});
|
|
1862
|
+
},
|
|
1863
|
+
true
|
|
1864
|
+
);
|
|
1865
|
+
return {
|
|
1866
|
+
id: `chart:${adapter.id}`,
|
|
1867
|
+
title: adapter.title ?? adapter.id,
|
|
1868
|
+
dispose: () => {
|
|
1869
|
+
for (const d of disposers) d();
|
|
1870
|
+
}
|
|
1871
|
+
};
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
// src/bridges/scene.ts
|
|
1875
|
+
var DEFAULT_AGENT7 = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
1876
|
+
function registerSceneBridge(server, options) {
|
|
1877
|
+
const { adapter } = options;
|
|
1878
|
+
const agent = { ...DEFAULT_AGENT7, ...options.agent ?? {} };
|
|
1879
|
+
const disposers = [];
|
|
1880
|
+
const target = (objectId) => ({
|
|
1881
|
+
kind: "scene",
|
|
1882
|
+
screenId: adapter.screenId,
|
|
1883
|
+
elementId: objectId ? `${adapter.id}:${objectId}` : adapter.id,
|
|
1884
|
+
label: objectId ? `${adapter.title ?? adapter.id} \u2192 ${objectId}` : adapter.title ?? adapter.id
|
|
1885
|
+
});
|
|
1886
|
+
const reg = (name, description, properties, required, handler, isMutation, objectIdFromArgs) => {
|
|
1887
|
+
const wrapped = async (args) => {
|
|
1888
|
+
try {
|
|
1889
|
+
return await handler(args);
|
|
1890
|
+
} catch (e) {
|
|
1891
|
+
return errorResult(e instanceof Error ? e.message : String(e));
|
|
1892
|
+
}
|
|
1893
|
+
};
|
|
1894
|
+
const final = isMutation ? wrapToolWithActivity(wrapped, {
|
|
1895
|
+
toolName: name,
|
|
1896
|
+
agent,
|
|
1897
|
+
kind: "scene",
|
|
1898
|
+
screenId: adapter.screenId,
|
|
1899
|
+
resolveTarget: ({ args }) => target(objectIdFromArgs?.(args))
|
|
1900
|
+
}) : wrapped;
|
|
1901
|
+
disposers.push(
|
|
1902
|
+
server.registerTool(
|
|
1903
|
+
{ name, description, inputSchema: { type: "object", properties, required, additionalProperties: false } },
|
|
1904
|
+
final
|
|
1905
|
+
)
|
|
1906
|
+
);
|
|
1907
|
+
};
|
|
1908
|
+
const newId3 = (kind) => `${kind}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
|
|
1909
|
+
reg(
|
|
1910
|
+
"scene_describe",
|
|
1911
|
+
"Describe the scene \u2014 object count, kinds, camera position.",
|
|
1912
|
+
{},
|
|
1913
|
+
[],
|
|
1914
|
+
() => {
|
|
1915
|
+
const scene = adapter.getScene();
|
|
1916
|
+
const summary = {
|
|
1917
|
+
id: adapter.id,
|
|
1918
|
+
objectCount: scene.objects.length,
|
|
1919
|
+
kinds: scene.objects.map((o) => o.kind),
|
|
1920
|
+
camera: scene.camera,
|
|
1921
|
+
background: scene.background
|
|
1922
|
+
};
|
|
1923
|
+
return textResult(JSON.stringify(summary, null, 2), summary);
|
|
1924
|
+
},
|
|
1925
|
+
false
|
|
1926
|
+
);
|
|
1927
|
+
reg(
|
|
1928
|
+
"scene_get_state",
|
|
1929
|
+
"Read the full SceneState (objects + camera + background).",
|
|
1930
|
+
{},
|
|
1931
|
+
[],
|
|
1932
|
+
() => {
|
|
1933
|
+
const scene = adapter.getScene();
|
|
1934
|
+
return textResult(JSON.stringify(scene, null, 2), scene);
|
|
1935
|
+
},
|
|
1936
|
+
false
|
|
1937
|
+
);
|
|
1938
|
+
reg(
|
|
1939
|
+
"scene_add_object",
|
|
1940
|
+
"Add an object to the scene root. Returns the new object's id.",
|
|
1941
|
+
{
|
|
1942
|
+
kind: { type: "string", description: "box | sphere | cylinder | plane | screen | group | custom kind" },
|
|
1943
|
+
position: { type: "array", description: "[x, y, z]" },
|
|
1944
|
+
rotation: { type: "array", description: "[x, y, z] euler" },
|
|
1945
|
+
scale: { type: "array", description: "[x, y, z]" },
|
|
1946
|
+
color: { type: "string" },
|
|
1947
|
+
props: { type: "object", description: "Per-kind config." }
|
|
1948
|
+
},
|
|
1949
|
+
["kind"],
|
|
1950
|
+
(args) => {
|
|
1951
|
+
const obj = {
|
|
1952
|
+
id: newId3(String(args.kind)),
|
|
1953
|
+
kind: String(args.kind),
|
|
1954
|
+
position: parseTriple(args.position),
|
|
1955
|
+
rotation: parseTriple(args.rotation),
|
|
1956
|
+
scale: parseTriple(args.scale),
|
|
1957
|
+
color: typeof args.color === "string" ? args.color : void 0,
|
|
1958
|
+
props: args.props && typeof args.props === "object" ? args.props : void 0
|
|
1959
|
+
};
|
|
1960
|
+
const scene = adapter.getScene();
|
|
1961
|
+
adapter.setScene({ ...scene, objects: [...scene.objects, obj] });
|
|
1962
|
+
return textResult(`Added ${obj.kind} ${obj.id}`, obj);
|
|
1963
|
+
},
|
|
1964
|
+
true,
|
|
1965
|
+
(args) => void 0
|
|
1966
|
+
// id resolved from result.structuredContent.id
|
|
1967
|
+
);
|
|
1968
|
+
reg(
|
|
1969
|
+
"scene_update_object",
|
|
1970
|
+
"Update fields on an object. Only provided fields change.",
|
|
1971
|
+
{
|
|
1972
|
+
id: { type: "string" },
|
|
1973
|
+
position: { type: "array" },
|
|
1974
|
+
rotation: { type: "array" },
|
|
1975
|
+
scale: { type: "array" },
|
|
1976
|
+
color: { type: "string" },
|
|
1977
|
+
props: { type: "object" }
|
|
1978
|
+
},
|
|
1979
|
+
["id"],
|
|
1980
|
+
(args) => {
|
|
1981
|
+
const id = String(args.id);
|
|
1982
|
+
const scene = adapter.getScene();
|
|
1983
|
+
const idx = scene.objects.findIndex((o) => o.id === id);
|
|
1984
|
+
if (idx === -1) return errorResult(`No object ${id}`);
|
|
1985
|
+
const orig = scene.objects[idx];
|
|
1986
|
+
const next = {
|
|
1987
|
+
...orig,
|
|
1988
|
+
...args.position !== void 0 ? { position: parseTriple(args.position) } : {},
|
|
1989
|
+
...args.rotation !== void 0 ? { rotation: parseTriple(args.rotation) } : {},
|
|
1990
|
+
...args.scale !== void 0 ? { scale: parseTriple(args.scale) } : {},
|
|
1991
|
+
...args.color !== void 0 ? { color: String(args.color) } : {},
|
|
1992
|
+
...args.props && typeof args.props === "object" ? { props: { ...orig.props ?? {}, ...args.props } } : {}
|
|
1993
|
+
};
|
|
1994
|
+
const objects = [...scene.objects];
|
|
1995
|
+
objects[idx] = next;
|
|
1996
|
+
adapter.setScene({ ...scene, objects });
|
|
1997
|
+
return textResult(`Updated ${id}`, next);
|
|
1998
|
+
},
|
|
1999
|
+
true,
|
|
2000
|
+
(args) => String(args.id ?? "")
|
|
2001
|
+
);
|
|
2002
|
+
reg(
|
|
2003
|
+
"scene_delete_object",
|
|
2004
|
+
"Remove an object from the scene root.",
|
|
2005
|
+
{ id: { type: "string" } },
|
|
2006
|
+
["id"],
|
|
2007
|
+
(args) => {
|
|
2008
|
+
const id = String(args.id);
|
|
2009
|
+
const scene = adapter.getScene();
|
|
2010
|
+
const next = scene.objects.filter((o) => o.id !== id);
|
|
2011
|
+
if (next.length === scene.objects.length) return errorResult(`No object ${id}`);
|
|
2012
|
+
adapter.setScene({ ...scene, objects: next });
|
|
2013
|
+
return textResult(`Deleted ${id}`);
|
|
2014
|
+
},
|
|
2015
|
+
true,
|
|
2016
|
+
(args) => String(args.id ?? "")
|
|
2017
|
+
);
|
|
2018
|
+
reg(
|
|
2019
|
+
"scene_set_camera",
|
|
2020
|
+
"Move the camera. Pass any subset of position/target/fov.",
|
|
2021
|
+
{
|
|
2022
|
+
position: { type: "array", description: "[x, y, z]" },
|
|
2023
|
+
target: { type: "array", description: "[x, y, z] look-at point" },
|
|
2024
|
+
fov: { type: "number" }
|
|
2025
|
+
},
|
|
2026
|
+
[],
|
|
2027
|
+
(args) => {
|
|
2028
|
+
const scene = adapter.getScene();
|
|
2029
|
+
const next = {
|
|
2030
|
+
...scene.camera ?? {},
|
|
2031
|
+
...args.position !== void 0 ? { position: parseTriple(args.position) } : {},
|
|
2032
|
+
...args.target !== void 0 ? { target: parseTriple(args.target) } : {},
|
|
2033
|
+
...args.fov !== void 0 ? { fov: Number(args.fov) } : {}
|
|
2034
|
+
};
|
|
2035
|
+
if (adapter.setCamera) {
|
|
2036
|
+
adapter.setCamera(next);
|
|
2037
|
+
} else {
|
|
2038
|
+
adapter.setScene({ ...scene, camera: next });
|
|
2039
|
+
}
|
|
2040
|
+
return textResult(`Camera updated`, next);
|
|
2041
|
+
},
|
|
2042
|
+
true
|
|
2043
|
+
);
|
|
2044
|
+
reg(
|
|
2045
|
+
"scene_set_background",
|
|
2046
|
+
"Change the scene background color (CSS color).",
|
|
2047
|
+
{ color: { type: "string" } },
|
|
2048
|
+
["color"],
|
|
2049
|
+
(args) => {
|
|
2050
|
+
const scene = adapter.getScene();
|
|
2051
|
+
adapter.setScene({ ...scene, background: String(args.color) });
|
|
2052
|
+
return textResult(`Background \u2192 ${args.color}`, { background: args.color });
|
|
2053
|
+
},
|
|
2054
|
+
true
|
|
2055
|
+
);
|
|
2056
|
+
return {
|
|
2057
|
+
id: `scene:${adapter.id}`,
|
|
2058
|
+
title: adapter.title ?? adapter.id,
|
|
2059
|
+
dispose: () => {
|
|
2060
|
+
for (const d of disposers) d();
|
|
2061
|
+
}
|
|
2062
|
+
};
|
|
2063
|
+
}
|
|
2064
|
+
function parseTriple(v) {
|
|
2065
|
+
if (!Array.isArray(v) || v.length !== 3) return void 0;
|
|
2066
|
+
const out = v.map((x) => Number(x));
|
|
2067
|
+
if (out.some((x) => !Number.isFinite(x))) return void 0;
|
|
2068
|
+
return out;
|
|
2069
|
+
}
|
|
903
2070
|
function AgentPanel({ agent, activity, onSubmit, busy, actions, className, style }) {
|
|
904
2071
|
const scrollRef = react.useRef(null);
|
|
905
2072
|
const inputRef = react.useRef(null);
|
|
@@ -1063,6 +2230,101 @@ function AgentActivityHighlight({
|
|
|
1063
2230
|
}
|
|
1064
2231
|
);
|
|
1065
2232
|
}
|
|
2233
|
+
function BridgedForm({
|
|
2234
|
+
id,
|
|
2235
|
+
title,
|
|
2236
|
+
screenId,
|
|
2237
|
+
fields,
|
|
2238
|
+
values,
|
|
2239
|
+
onChange,
|
|
2240
|
+
onSubmit,
|
|
2241
|
+
server,
|
|
2242
|
+
agent,
|
|
2243
|
+
children
|
|
2244
|
+
}) {
|
|
2245
|
+
const valuesRef = react.useRef(values);
|
|
2246
|
+
const onChangeRef = react.useRef(onChange);
|
|
2247
|
+
const fieldsRef = react.useRef(fields);
|
|
2248
|
+
const submitRef = react.useRef(onSubmit);
|
|
2249
|
+
react.useEffect(() => {
|
|
2250
|
+
valuesRef.current = values;
|
|
2251
|
+
}, [values]);
|
|
2252
|
+
react.useEffect(() => {
|
|
2253
|
+
onChangeRef.current = onChange;
|
|
2254
|
+
}, [onChange]);
|
|
2255
|
+
react.useEffect(() => {
|
|
2256
|
+
fieldsRef.current = fields;
|
|
2257
|
+
}, [fields]);
|
|
2258
|
+
react.useEffect(() => {
|
|
2259
|
+
submitRef.current = onSubmit;
|
|
2260
|
+
}, [onSubmit]);
|
|
2261
|
+
const focusElement = (name) => {
|
|
2262
|
+
if (typeof document === "undefined") return;
|
|
2263
|
+
const el = document.querySelector(`[data-form-id="${id}"] [name="${name}"]`);
|
|
2264
|
+
el?.focus();
|
|
2265
|
+
};
|
|
2266
|
+
const adapter = react.useMemo(() => ({
|
|
2267
|
+
id,
|
|
2268
|
+
title,
|
|
2269
|
+
screenId,
|
|
2270
|
+
getFields: () => fieldsRef.current,
|
|
2271
|
+
getValue: (name) => valuesRef.current[name],
|
|
2272
|
+
getValues: () => ({ ...valuesRef.current }),
|
|
2273
|
+
setValue: (name, v) => onChangeRef.current({ ...valuesRef.current, [name]: v }),
|
|
2274
|
+
setValues: (next) => onChangeRef.current({ ...valuesRef.current, ...next }),
|
|
2275
|
+
focus: focusElement,
|
|
2276
|
+
submit: async () => {
|
|
2277
|
+
if (!submitRef.current) {
|
|
2278
|
+
return { ok: true, values: { ...valuesRef.current } };
|
|
2279
|
+
}
|
|
2280
|
+
return submitRef.current();
|
|
2281
|
+
}
|
|
2282
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2283
|
+
}), [id, title, screenId]);
|
|
2284
|
+
react.useEffect(() => {
|
|
2285
|
+
if (!server) return;
|
|
2286
|
+
const bridge = registerFormBridge(server, { adapter, agent });
|
|
2287
|
+
return () => bridge.dispose();
|
|
2288
|
+
}, [server, adapter, agent]);
|
|
2289
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-form-id": id, children });
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
// src/components/ScreensActivityBridge/ScreensActivityBridge.tsx
|
|
2293
|
+
init_registry();
|
|
2294
|
+
function ScreensActivityBridge({ system, fadeMs = 1500 }) {
|
|
2295
|
+
react.useEffect(() => {
|
|
2296
|
+
const fadeTimers = /* @__PURE__ */ new Map();
|
|
2297
|
+
const off = onActivity((event) => {
|
|
2298
|
+
const screenId = event.target.screenId;
|
|
2299
|
+
if (!screenId) return;
|
|
2300
|
+
if (!system.registry.has(screenId)) return;
|
|
2301
|
+
const activity = {
|
|
2302
|
+
agentId: event.agentId,
|
|
2303
|
+
agentName: event.agentName,
|
|
2304
|
+
agentColor: event.agentColor,
|
|
2305
|
+
action: event.action,
|
|
2306
|
+
timestamp: event.timestamp,
|
|
2307
|
+
elementId: event.target.elementId,
|
|
2308
|
+
label: event.target.label
|
|
2309
|
+
};
|
|
2310
|
+
system.updateScreen(screenId, { agentActivity: activity });
|
|
2311
|
+
const prev = fadeTimers.get(screenId);
|
|
2312
|
+
if (prev) clearTimeout(prev);
|
|
2313
|
+
fadeTimers.set(
|
|
2314
|
+
screenId,
|
|
2315
|
+
setTimeout(() => {
|
|
2316
|
+
system.updateScreen(screenId, { agentActivity: null });
|
|
2317
|
+
fadeTimers.delete(screenId);
|
|
2318
|
+
}, event.ttlMs ?? fadeMs)
|
|
2319
|
+
);
|
|
2320
|
+
});
|
|
2321
|
+
return () => {
|
|
2322
|
+
off();
|
|
2323
|
+
for (const t of fadeTimers.values()) clearTimeout(t);
|
|
2324
|
+
};
|
|
2325
|
+
}, [system, fadeMs]);
|
|
2326
|
+
return null;
|
|
2327
|
+
}
|
|
1066
2328
|
|
|
1067
2329
|
// src/sharing/token.ts
|
|
1068
2330
|
var TOKEN_BYTES = 24;
|
|
@@ -1333,16 +2595,31 @@ function attachSseRelay(server, options) {
|
|
|
1333
2595
|
transport.bindServer(server);
|
|
1334
2596
|
server.attach(transport);
|
|
1335
2597
|
transport.start();
|
|
2598
|
+
Promise.resolve().then(() => (init_registry(), registry_exports)).then(({ onActivity: onActivity2 }) => {
|
|
2599
|
+
const off = onActivity2((event) => {
|
|
2600
|
+
transport.send({
|
|
2601
|
+
jsonrpc: "2.0",
|
|
2602
|
+
method: "notifications/agent_activity",
|
|
2603
|
+
params: event
|
|
2604
|
+
});
|
|
2605
|
+
});
|
|
2606
|
+
const origClose = transport.close.bind(transport);
|
|
2607
|
+
transport.close = () => {
|
|
2608
|
+
off();
|
|
2609
|
+
origClose();
|
|
2610
|
+
};
|
|
2611
|
+
}).catch(() => {
|
|
2612
|
+
});
|
|
1336
2613
|
return transport;
|
|
1337
2614
|
}
|
|
1338
|
-
var
|
|
2615
|
+
var DEFAULT_AGENT8 = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
1339
2616
|
function SharedWhiteboard({
|
|
1340
2617
|
initialNotes = [],
|
|
1341
2618
|
initialShapes = [],
|
|
1342
2619
|
initialConnectors = [],
|
|
1343
2620
|
initialStrokes = [],
|
|
1344
2621
|
initialViewport = { x: 0, y: 0, zoom: 1 },
|
|
1345
|
-
agent =
|
|
2622
|
+
agent = DEFAULT_AGENT8,
|
|
1346
2623
|
shareBaseUrl = "/whiteboard-share",
|
|
1347
2624
|
onRegisterSession,
|
|
1348
2625
|
showAgentPanel = true,
|
|
@@ -1605,13 +2882,69 @@ function resolveCenter(ref, notes, shapes) {
|
|
|
1605
2882
|
return ref;
|
|
1606
2883
|
}
|
|
1607
2884
|
|
|
2885
|
+
// src/presence/index.ts
|
|
2886
|
+
init_registry();
|
|
2887
|
+
|
|
2888
|
+
// src/presence/use-agent-activity.ts
|
|
2889
|
+
init_registry();
|
|
2890
|
+
function useAgentActivity(filter, options = {}) {
|
|
2891
|
+
const cap = options.capacity ?? 50;
|
|
2892
|
+
const [events, setEvents] = react.useState(() => readActivityHistory(filter).slice(-cap));
|
|
2893
|
+
react.useEffect(() => {
|
|
2894
|
+
setEvents(readActivityHistory(filter).slice(-cap));
|
|
2895
|
+
return onActivity((event) => {
|
|
2896
|
+
setEvents((prev) => {
|
|
2897
|
+
const next = prev.length >= cap ? prev.slice(prev.length - cap + 1) : prev.slice();
|
|
2898
|
+
next.push(event);
|
|
2899
|
+
return next;
|
|
2900
|
+
});
|
|
2901
|
+
}, filter);
|
|
2902
|
+
}, [filter?.agentId, filter?.screenId, filter?.kind, cap]);
|
|
2903
|
+
return { events, latest: events.length > 0 ? events[events.length - 1] : null };
|
|
2904
|
+
}
|
|
2905
|
+
function useAgentActivityForScreen(screenId, options = {}) {
|
|
2906
|
+
const { events, latest } = useAgentActivity({ screenId }, options);
|
|
2907
|
+
const fadeAfter = latest?.ttlMs ?? 1500;
|
|
2908
|
+
const [isAgentActive, setActive] = react.useState(false);
|
|
2909
|
+
react.useEffect(() => {
|
|
2910
|
+
if (!latest) {
|
|
2911
|
+
setActive(false);
|
|
2912
|
+
return;
|
|
2913
|
+
}
|
|
2914
|
+
setActive(true);
|
|
2915
|
+
const timer = setTimeout(() => setActive(false), fadeAfter);
|
|
2916
|
+
return () => clearTimeout(timer);
|
|
2917
|
+
}, [latest, fadeAfter]);
|
|
2918
|
+
return { events, latest, isAgentActive };
|
|
2919
|
+
}
|
|
2920
|
+
function useUndoStack(agentId, intervalMs = 500) {
|
|
2921
|
+
const [history2, setHistory] = react.useState(() => readHistory(agentId));
|
|
2922
|
+
react.useEffect(() => {
|
|
2923
|
+
let cancelled = false;
|
|
2924
|
+
const tick = () => {
|
|
2925
|
+
if (cancelled) return;
|
|
2926
|
+
setHistory(readHistory(agentId));
|
|
2927
|
+
};
|
|
2928
|
+
const id = setInterval(tick, intervalMs);
|
|
2929
|
+
tick();
|
|
2930
|
+
return () => {
|
|
2931
|
+
cancelled = true;
|
|
2932
|
+
clearInterval(id);
|
|
2933
|
+
};
|
|
2934
|
+
}, [agentId, intervalMs]);
|
|
2935
|
+
const refresh = react.useCallback(() => setHistory(readHistory(agentId)), [agentId]);
|
|
2936
|
+
return { history: history2, refresh };
|
|
2937
|
+
}
|
|
2938
|
+
|
|
1608
2939
|
exports.AgentActivityHighlight = AgentActivityHighlight;
|
|
1609
2940
|
exports.AgentCursor = AgentCursor;
|
|
1610
2941
|
exports.AgentPanel = AgentPanel;
|
|
2942
|
+
exports.BridgedForm = BridgedForm;
|
|
1611
2943
|
exports.InProcessTransport = InProcessTransport;
|
|
1612
2944
|
exports.MCP_PROTOCOL_VERSION = MCP_PROTOCOL_VERSION;
|
|
1613
2945
|
exports.MicroMcpServer = MicroMcpServer;
|
|
1614
2946
|
exports.RelayTransport = RelayTransport;
|
|
2947
|
+
exports.ScreensActivityBridge = ScreensActivityBridge;
|
|
1615
2948
|
exports.ShareControls = ShareControls;
|
|
1616
2949
|
exports.SharedWhiteboard = SharedWhiteboard;
|
|
1617
2950
|
exports.SseRelayTransport = SseRelayTransport;
|
|
@@ -1620,13 +2953,34 @@ exports.attachRelay = attachRelay;
|
|
|
1620
2953
|
exports.attachSseRelay = attachSseRelay;
|
|
1621
2954
|
exports.buildShareConfig = buildShareConfig;
|
|
1622
2955
|
exports.buildShareUrl = buildShareUrl;
|
|
2956
|
+
exports.clearUndoStack = clearStack;
|
|
1623
2957
|
exports.createSessionDescriptor = createSessionDescriptor;
|
|
1624
2958
|
exports.describeSession = describeSession;
|
|
2959
|
+
exports.emitActivity = emitActivity;
|
|
2960
|
+
exports.ensureUndoToolsRegistered = ensureUndoToolsRegistered;
|
|
1625
2961
|
exports.errorResult = errorResult;
|
|
2962
|
+
exports.onActivity = onActivity;
|
|
2963
|
+
exports.pushUndoEntry = pushUndoEntry;
|
|
2964
|
+
exports.readActivityHistory = readActivityHistory;
|
|
1626
2965
|
exports.readSessionFromUrl = readSessionFromUrl;
|
|
2966
|
+
exports.readUndoHistory = readHistory;
|
|
2967
|
+
exports.redoOne = redoOne;
|
|
2968
|
+
exports.registerChartsBridge = registerChartsBridge;
|
|
2969
|
+
exports.registerCodeBridge = registerCodeBridge;
|
|
1627
2970
|
exports.registerFlowBridge = registerFlowBridge;
|
|
2971
|
+
exports.registerFormBridge = registerFormBridge;
|
|
2972
|
+
exports.registerSceneBridge = registerSceneBridge;
|
|
2973
|
+
exports.registerSheetsBridge = registerSheetsBridge;
|
|
2974
|
+
exports.registerUndoTools = registerUndoTools;
|
|
1628
2975
|
exports.registerWhiteboardBridge = registerWhiteboardBridge;
|
|
2976
|
+
exports.resetActivityRegistry = resetActivityRegistry;
|
|
2977
|
+
exports.resetAllUndoStacks = resetAllUndoStacks;
|
|
1629
2978
|
exports.rpcError = rpcError;
|
|
1630
2979
|
exports.textResult = textResult;
|
|
2980
|
+
exports.undoOne = undoOne;
|
|
2981
|
+
exports.useAgentActivity = useAgentActivity;
|
|
2982
|
+
exports.useAgentActivityForScreen = useAgentActivityForScreen;
|
|
2983
|
+
exports.useUndoStack = useUndoStack;
|
|
2984
|
+
exports.wrapToolWithActivity = wrapToolWithActivity;
|
|
1631
2985
|
//# sourceMappingURL=index.cjs.map
|
|
1632
2986
|
//# sourceMappingURL=index.cjs.map
|