@particle-academy/agent-integrations 0.2.4 → 0.4.0
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/README.md +20 -0
- 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 +4 -3
- package/dist/bridges/flow.d.ts +4 -3
- 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/screens.d.cts +78 -0
- package/dist/bridges/screens.d.ts +78 -0
- package/dist/bridges/sheets.d.cts +62 -0
- package/dist/bridges/sheets.d.ts +62 -0
- package/dist/bridges/whiteboard.d.cts +4 -3
- package/dist/bridges/whiteboard.d.ts +4 -3
- 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 +78 -19
- package/dist/bridges-flow.cjs.map +1 -1
- package/dist/bridges-flow.js +4 -2
- 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-screens.cjs +227 -0
- package/dist/bridges-screens.cjs.map +1 -0
- package/dist/bridges-screens.js +6 -0
- package/dist/bridges-screens.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 +226 -40
- package/dist/bridges-whiteboard.cjs.map +1 -1
- package/dist/bridges-whiteboard.js +5 -2
- package/dist/{chunk-5ZUHNNLR.js → chunk-3KSZNGNW.js} +81 -43
- package/dist/chunk-3KSZNGNW.js.map +1 -0
- package/dist/chunk-4BL5M3U3.js +158 -0
- package/dist/chunk-4BL5M3U3.js.map +1 -0
- package/dist/{chunk-QGCF7YKW.js → chunk-4KAIV6OD.js} +40 -12
- package/dist/chunk-4KAIV6OD.js.map +1 -0
- package/dist/chunk-52S7XYZK.js +38 -0
- package/dist/chunk-52S7XYZK.js.map +1 -0
- package/dist/chunk-57ZDHD53.js +180 -0
- package/dist/chunk-57ZDHD53.js.map +1 -0
- package/dist/chunk-E4AICMFZ.js +83 -0
- package/dist/chunk-E4AICMFZ.js.map +1 -0
- package/dist/chunk-GQ7XXK7G.js +124 -0
- package/dist/chunk-GQ7XXK7G.js.map +1 -0
- package/dist/chunk-HSTW7ZNO.js +172 -0
- package/dist/chunk-HSTW7ZNO.js.map +1 -0
- package/dist/chunk-IANI25IT.js +280 -0
- package/dist/chunk-IANI25IT.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-2VOQJKSU.js → chunk-N3H4DXY5.js} +44 -22
- package/dist/chunk-N3H4DXY5.js.map +1 -0
- package/dist/chunk-NTDZWGYB.js +120 -0
- package/dist/chunk-NTDZWGYB.js.map +1 -0
- package/dist/chunk-RGO42EQ6.js +25 -0
- package/dist/chunk-RGO42EQ6.js.map +1 -0
- package/dist/chunk-X66JWQBB.js +37 -0
- package/dist/chunk-X66JWQBB.js.map +1 -0
- package/dist/chunk-XRAJSOPS.js +203 -0
- package/dist/chunk-XRAJSOPS.js.map +1 -0
- package/dist/index.cjs +1766 -127
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +99 -3
- package/dist/index.d.ts +99 -3
- package/dist/index.js +115 -9
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.d.cts +5 -2
- package/dist/mcp/index.d.ts +5 -2
- package/dist/mcp.cjs +37 -9
- package/dist/mcp.cjs.map +1 -1
- package/dist/mcp.js +1 -1
- 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-BsSwfemr.d.cts +63 -0
- package/dist/server-Du3-IGqM.d.ts +63 -0
- package/dist/sharing/index.d.cts +3 -1
- package/dist/sharing/index.d.ts +3 -1
- package/dist/sharing.cjs +68 -0
- package/dist/sharing.cjs.map +1 -1
- package/dist/sharing.js +1 -1
- package/dist/sheets-adapter.cjs +96 -0
- package/dist/sheets-adapter.cjs.map +1 -0
- package/dist/sheets-adapter.d.cts +115 -0
- package/dist/sheets-adapter.d.ts +115 -0
- package/dist/sheets-adapter.js +4 -0
- package/dist/sheets-adapter.js.map +1 -0
- package/dist/styles.css +57 -0
- package/dist/styles.css.map +1 -1
- package/dist/tool-host-BQuUygLF.d.cts +60 -0
- package/dist/tool-host-C8JMMGYq.d.ts +60 -0
- package/dist/{types-CRPA_D0z.d.ts → types-CCSBGW9T.d.cts} +2 -2
- package/dist/{types-DR5AS6Rd.d.cts → types-DIVNcIQO.d.ts} +2 -2
- package/dist/types-aOQLTW0E.d.cts +112 -0
- package/dist/types-aOQLTW0E.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/chunk-QGCF7YKW.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,16 +4,102 @@ 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;
|
|
10
63
|
var JSONRPC_INTERNAL_ERROR = -32603;
|
|
11
64
|
var MCP_PROTOCOL_VERSION = "2025-06-18";
|
|
12
65
|
|
|
66
|
+
// src/mcp/tool-host.ts
|
|
67
|
+
var ToolRegistry = class {
|
|
68
|
+
constructor() {
|
|
69
|
+
this.tools = /* @__PURE__ */ new Map();
|
|
70
|
+
}
|
|
71
|
+
registerTool(definition, handler) {
|
|
72
|
+
this.tools.set(definition.name, { definition, handler });
|
|
73
|
+
this.onToolsChanged();
|
|
74
|
+
return () => {
|
|
75
|
+
if (this.tools.delete(definition.name)) this.onToolsChanged();
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
getTool(name) {
|
|
79
|
+
return this.tools.get(name) ?? null;
|
|
80
|
+
}
|
|
81
|
+
listTools() {
|
|
82
|
+
return Array.from(this.tools.values()).map((t) => t.definition);
|
|
83
|
+
}
|
|
84
|
+
async callTool(name, args = {}) {
|
|
85
|
+
const tool = this.tools.get(name);
|
|
86
|
+
if (!tool) {
|
|
87
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
88
|
+
}
|
|
89
|
+
return tool.handler(args);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Hook for subclasses (e.g. MicroMcpServer) to notify subscribers
|
|
93
|
+
* when the tool catalog changes. Default no-op.
|
|
94
|
+
*/
|
|
95
|
+
onToolsChanged() {
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
13
99
|
// src/mcp/server.ts
|
|
14
|
-
var MicroMcpServer = class {
|
|
100
|
+
var MicroMcpServer = class extends ToolRegistry {
|
|
15
101
|
constructor(options) {
|
|
16
|
-
|
|
102
|
+
super();
|
|
17
103
|
this.transports = /* @__PURE__ */ new Set();
|
|
18
104
|
this.notifyListChangedScheduled = false;
|
|
19
105
|
this.info = options.info;
|
|
@@ -29,18 +115,13 @@ var MicroMcpServer = class {
|
|
|
29
115
|
transport.close?.();
|
|
30
116
|
}
|
|
31
117
|
}
|
|
32
|
-
registerTool(definition, handler) {
|
|
33
|
-
this.tools.set(definition.name, { definition, handler });
|
|
34
|
-
this.scheduleListChangedNotification();
|
|
35
|
-
return () => this.unregisterTool(definition.name);
|
|
36
|
-
}
|
|
37
118
|
unregisterTool(name) {
|
|
38
119
|
if (this.tools.delete(name)) {
|
|
39
120
|
this.scheduleListChangedNotification();
|
|
40
121
|
}
|
|
41
122
|
}
|
|
42
|
-
|
|
43
|
-
|
|
123
|
+
onToolsChanged() {
|
|
124
|
+
this.scheduleListChangedNotification();
|
|
44
125
|
}
|
|
45
126
|
/**
|
|
46
127
|
* Receive a JSON-RPC frame from a client (called by the transport).
|
|
@@ -194,6 +275,159 @@ function attachRelay(server, channel) {
|
|
|
194
275
|
return transport;
|
|
195
276
|
}
|
|
196
277
|
|
|
278
|
+
// src/presence/wrap-tool-with-activity.ts
|
|
279
|
+
init_registry();
|
|
280
|
+
function wrapToolWithActivity(handler, options) {
|
|
281
|
+
return async (args) => {
|
|
282
|
+
const result = await handler(args);
|
|
283
|
+
if (result.isError) return result;
|
|
284
|
+
let target;
|
|
285
|
+
if (options.resolveTarget) {
|
|
286
|
+
target = options.resolveTarget({ toolName: options.toolName, args, result });
|
|
287
|
+
} else {
|
|
288
|
+
target = { kind: options.kind, screenId: options.screenId };
|
|
289
|
+
}
|
|
290
|
+
if (!target) return result;
|
|
291
|
+
emitActivity({
|
|
292
|
+
agentId: options.agent.id,
|
|
293
|
+
agentName: options.agent.name,
|
|
294
|
+
agentColor: options.agent.color,
|
|
295
|
+
target: { ...target, kind: target.kind ?? options.kind, screenId: target.screenId ?? options.screenId },
|
|
296
|
+
action: options.toolName,
|
|
297
|
+
timestamp: Date.now(),
|
|
298
|
+
meta: extractMeta(result),
|
|
299
|
+
ttlMs: options.ttlMs
|
|
300
|
+
});
|
|
301
|
+
return result;
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
function extractMeta(result) {
|
|
305
|
+
const sc = result.structuredContent;
|
|
306
|
+
if (sc && typeof sc === "object" && !Array.isArray(sc)) {
|
|
307
|
+
return sc;
|
|
308
|
+
}
|
|
309
|
+
return void 0;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// src/undo/undo-stack.ts
|
|
313
|
+
var stacks = /* @__PURE__ */ new Map();
|
|
314
|
+
var CAP = 200;
|
|
315
|
+
function getStack(agentId) {
|
|
316
|
+
let s = stacks.get(agentId);
|
|
317
|
+
if (!s) {
|
|
318
|
+
s = { past: [], future: [] };
|
|
319
|
+
stacks.set(agentId, s);
|
|
320
|
+
}
|
|
321
|
+
return s;
|
|
322
|
+
}
|
|
323
|
+
function pushUndoEntry(agentId, entry) {
|
|
324
|
+
const s = getStack(agentId);
|
|
325
|
+
s.past.push(entry);
|
|
326
|
+
if (s.past.length > CAP) s.past.splice(0, s.past.length - CAP);
|
|
327
|
+
s.future.length = 0;
|
|
328
|
+
}
|
|
329
|
+
async function undoOne(agentId) {
|
|
330
|
+
const s = getStack(agentId);
|
|
331
|
+
const entry = s.past.pop();
|
|
332
|
+
if (!entry) return null;
|
|
333
|
+
await entry.undo();
|
|
334
|
+
s.future.push(entry);
|
|
335
|
+
return entry;
|
|
336
|
+
}
|
|
337
|
+
async function redoOne(agentId) {
|
|
338
|
+
const s = getStack(agentId);
|
|
339
|
+
const entry = s.future.pop();
|
|
340
|
+
if (!entry) return null;
|
|
341
|
+
await entry.redo();
|
|
342
|
+
s.past.push(entry);
|
|
343
|
+
return entry;
|
|
344
|
+
}
|
|
345
|
+
function readHistory(agentId) {
|
|
346
|
+
return getStack(agentId).past.slice();
|
|
347
|
+
}
|
|
348
|
+
function clearStack(agentId) {
|
|
349
|
+
stacks.delete(agentId);
|
|
350
|
+
}
|
|
351
|
+
function resetAllUndoStacks() {
|
|
352
|
+
stacks.clear();
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// src/undo/undo-tools.ts
|
|
356
|
+
var installedHosts = /* @__PURE__ */ new WeakSet();
|
|
357
|
+
function ensureUndoToolsRegistered(host, options = {}) {
|
|
358
|
+
if (installedHosts.has(host)) return;
|
|
359
|
+
installedHosts.add(host);
|
|
360
|
+
registerUndoTools(host, options);
|
|
361
|
+
}
|
|
362
|
+
function registerUndoTools(host, options = {}) {
|
|
363
|
+
const defaultAgent = options.defaultAgentId ?? "agent";
|
|
364
|
+
const disposers = [];
|
|
365
|
+
const agentOf = (args) => typeof args?.agentId === "string" ? args.agentId : defaultAgent;
|
|
366
|
+
disposers.push(
|
|
367
|
+
host.registerTool(
|
|
368
|
+
{
|
|
369
|
+
name: "agent_undo",
|
|
370
|
+
description: "Undo the most recent action on the agent's stack. Optional agentId targets a specific agent.",
|
|
371
|
+
inputSchema: {
|
|
372
|
+
type: "object",
|
|
373
|
+
properties: { agentId: { type: "string" } },
|
|
374
|
+
additionalProperties: false
|
|
375
|
+
}
|
|
376
|
+
},
|
|
377
|
+
async (args) => {
|
|
378
|
+
const entry = await undoOne(agentOf(args));
|
|
379
|
+
if (!entry) return errorResult("Nothing to undo.");
|
|
380
|
+
return textResult(`Undid: ${entry.label}`, { entry: serialize(entry) });
|
|
381
|
+
}
|
|
382
|
+
)
|
|
383
|
+
);
|
|
384
|
+
disposers.push(
|
|
385
|
+
host.registerTool(
|
|
386
|
+
{
|
|
387
|
+
name: "agent_redo",
|
|
388
|
+
description: "Redo the most recently undone action.",
|
|
389
|
+
inputSchema: {
|
|
390
|
+
type: "object",
|
|
391
|
+
properties: { agentId: { type: "string" } },
|
|
392
|
+
additionalProperties: false
|
|
393
|
+
}
|
|
394
|
+
},
|
|
395
|
+
async (args) => {
|
|
396
|
+
const entry = await redoOne(agentOf(args));
|
|
397
|
+
if (!entry) return errorResult("Nothing to redo.");
|
|
398
|
+
return textResult(`Redid: ${entry.label}`, { entry: serialize(entry) });
|
|
399
|
+
}
|
|
400
|
+
)
|
|
401
|
+
);
|
|
402
|
+
disposers.push(
|
|
403
|
+
host.registerTool(
|
|
404
|
+
{
|
|
405
|
+
name: "agent_history",
|
|
406
|
+
description: "List the agent's undo stack (oldest first). Useful for understanding what's reversible.",
|
|
407
|
+
inputSchema: {
|
|
408
|
+
type: "object",
|
|
409
|
+
properties: { agentId: { type: "string" } },
|
|
410
|
+
additionalProperties: false
|
|
411
|
+
}
|
|
412
|
+
},
|
|
413
|
+
async (args) => {
|
|
414
|
+
const history2 = readHistory(agentOf(args)).map(serialize);
|
|
415
|
+
const text = history2.map((e) => `${new Date(e.timestamp).toISOString()} ${e.bridgeId} ${e.action}: ${e.label}`).join("\n");
|
|
416
|
+
return textResult(text || "(empty)", history2);
|
|
417
|
+
}
|
|
418
|
+
)
|
|
419
|
+
);
|
|
420
|
+
return () => disposers.forEach((d) => d());
|
|
421
|
+
}
|
|
422
|
+
function serialize(entry) {
|
|
423
|
+
return {
|
|
424
|
+
timestamp: entry.timestamp,
|
|
425
|
+
bridgeId: entry.bridgeId,
|
|
426
|
+
action: entry.action,
|
|
427
|
+
label: entry.label
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
197
431
|
// src/bridges/whiteboard.ts
|
|
198
432
|
var DEFAULT_AGENT = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
199
433
|
var VALID_SHAPES = ["rect", "rounded-rect", "ellipse", "diamond", "triangle", "line", "arrow", "text"];
|
|
@@ -201,13 +435,31 @@ var num = (v, fallback) => typeof v === "number" && Number.isFinite(v) ? v : fal
|
|
|
201
435
|
var str = (v, fallback = "") => typeof v === "string" ? v : fallback;
|
|
202
436
|
var bool = (v, fallback = false) => typeof v === "boolean" ? v : fallback;
|
|
203
437
|
var newId = (prefix) => `${prefix}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 7)}`;
|
|
204
|
-
function registerWhiteboardBridge(
|
|
438
|
+
function registerWhiteboardBridge(host, options) {
|
|
205
439
|
const { adapter } = options;
|
|
206
440
|
const agent = { ...DEFAULT_AGENT, ...options.agent ?? {} };
|
|
207
441
|
const disposers = [];
|
|
208
|
-
|
|
442
|
+
ensureUndoToolsRegistered(host, { defaultAgentId: agent.id });
|
|
443
|
+
const wbTarget = (args, result) => ({
|
|
444
|
+
kind: "whiteboard",
|
|
445
|
+
elementId: result?.structuredContent?.id ?? args?.id
|
|
446
|
+
});
|
|
447
|
+
const reg = (name, description, inputProperties, required, handler, resolveTarget) => {
|
|
448
|
+
const wrapped = async (args) => {
|
|
449
|
+
try {
|
|
450
|
+
return await handler(args);
|
|
451
|
+
} catch (e) {
|
|
452
|
+
return errorResult(e instanceof Error ? e.message : String(e));
|
|
453
|
+
}
|
|
454
|
+
};
|
|
455
|
+
const final = resolveTarget ? wrapToolWithActivity(wrapped, {
|
|
456
|
+
toolName: name,
|
|
457
|
+
agent: { id: agent.id, name: agent.name, color: agent.color },
|
|
458
|
+
kind: "whiteboard",
|
|
459
|
+
resolveTarget: ({ args, result }) => resolveTarget(args, result)
|
|
460
|
+
}) : wrapped;
|
|
209
461
|
disposers.push(
|
|
210
|
-
|
|
462
|
+
host.registerTool(
|
|
211
463
|
{
|
|
212
464
|
name,
|
|
213
465
|
description,
|
|
@@ -218,13 +470,7 @@ function registerWhiteboardBridge(server, options) {
|
|
|
218
470
|
additionalProperties: false
|
|
219
471
|
}
|
|
220
472
|
},
|
|
221
|
-
|
|
222
|
-
try {
|
|
223
|
-
return await handler(args);
|
|
224
|
-
} catch (e) {
|
|
225
|
-
return errorResult(e instanceof Error ? e.message : String(e));
|
|
226
|
-
}
|
|
227
|
-
}
|
|
473
|
+
final
|
|
228
474
|
)
|
|
229
475
|
);
|
|
230
476
|
};
|
|
@@ -301,8 +547,17 @@ function registerWhiteboardBridge(server, options) {
|
|
|
301
547
|
authorId: agent.id
|
|
302
548
|
};
|
|
303
549
|
adapter.setNotes((all) => [...all, note]);
|
|
550
|
+
pushUndoEntry(agent.id, {
|
|
551
|
+
timestamp: Date.now(),
|
|
552
|
+
bridgeId: "whiteboard",
|
|
553
|
+
action: "whiteboard_add_sticky",
|
|
554
|
+
label: `Added sticky ${note.id}`,
|
|
555
|
+
undo: () => adapter.setNotes((all) => all.filter((n) => n.id !== note.id)),
|
|
556
|
+
redo: () => adapter.setNotes((all) => [...all, note])
|
|
557
|
+
});
|
|
304
558
|
return textResult(`Added sticky ${note.id}`, note);
|
|
305
|
-
}
|
|
559
|
+
},
|
|
560
|
+
wbTarget
|
|
306
561
|
);
|
|
307
562
|
reg(
|
|
308
563
|
"whiteboard_stream_text",
|
|
@@ -369,7 +624,8 @@ function registerWhiteboardBridge(server, options) {
|
|
|
369
624
|
})
|
|
370
625
|
);
|
|
371
626
|
return textResult(`Updated sticky ${id}`, updated);
|
|
372
|
-
}
|
|
627
|
+
},
|
|
628
|
+
wbTarget
|
|
373
629
|
);
|
|
374
630
|
reg(
|
|
375
631
|
"whiteboard_add_shape",
|
|
@@ -410,7 +666,8 @@ function registerWhiteboardBridge(server, options) {
|
|
|
410
666
|
};
|
|
411
667
|
adapter.setShapes((all) => [...all, shape]);
|
|
412
668
|
return textResult(`Added ${kind} ${shape.id}`, shape);
|
|
413
|
-
}
|
|
669
|
+
},
|
|
670
|
+
wbTarget
|
|
414
671
|
);
|
|
415
672
|
reg(
|
|
416
673
|
"whiteboard_update_shape",
|
|
@@ -452,7 +709,8 @@ function registerWhiteboardBridge(server, options) {
|
|
|
452
709
|
})
|
|
453
710
|
);
|
|
454
711
|
return textResult(`Updated shape ${id}`, updated);
|
|
455
|
-
}
|
|
712
|
+
},
|
|
713
|
+
wbTarget
|
|
456
714
|
);
|
|
457
715
|
reg(
|
|
458
716
|
"whiteboard_add_connector",
|
|
@@ -473,7 +731,8 @@ function registerWhiteboardBridge(server, options) {
|
|
|
473
731
|
};
|
|
474
732
|
adapter.setConnectors((all) => [...all, c]);
|
|
475
733
|
return textResult(`Added connector ${c.id}`, c);
|
|
476
|
-
}
|
|
734
|
+
},
|
|
735
|
+
wbTarget
|
|
477
736
|
);
|
|
478
737
|
reg(
|
|
479
738
|
"whiteboard_add_stroke",
|
|
@@ -502,7 +761,8 @@ function registerWhiteboardBridge(server, options) {
|
|
|
502
761
|
};
|
|
503
762
|
adapter.setStrokes((all) => [...all, stroke]);
|
|
504
763
|
return textResult(`Added stroke ${stroke.id} (${points.length} points)`, stroke);
|
|
505
|
-
}
|
|
764
|
+
},
|
|
765
|
+
wbTarget
|
|
506
766
|
);
|
|
507
767
|
reg(
|
|
508
768
|
"whiteboard_delete_item",
|
|
@@ -511,29 +771,37 @@ function registerWhiteboardBridge(server, options) {
|
|
|
511
771
|
["id"],
|
|
512
772
|
(args) => {
|
|
513
773
|
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
|
-
|
|
774
|
+
const removedNotes = adapter.getNotes().filter((x) => x.id === id);
|
|
775
|
+
const removedShapes = adapter.getShapes().filter((x) => x.id === id);
|
|
776
|
+
const removedConnectors = adapter.getConnectors().filter((x) => x.id === id);
|
|
777
|
+
const removedStrokes = adapter.getStrokes().filter((x) => x.id === id);
|
|
778
|
+
const removed = removedNotes.length + removedShapes.length + removedConnectors.length + removedStrokes.length > 0;
|
|
779
|
+
if (!removed) return errorResult(`No item with id ${id}`);
|
|
780
|
+
adapter.setNotes((all) => all.filter((x) => x.id !== id));
|
|
781
|
+
adapter.setShapes((all) => all.filter((x) => x.id !== id));
|
|
782
|
+
adapter.setConnectors((all) => all.filter((x) => x.id !== id));
|
|
783
|
+
adapter.setStrokes((all) => all.filter((x) => x.id !== id));
|
|
784
|
+
pushUndoEntry(agent.id, {
|
|
785
|
+
timestamp: Date.now(),
|
|
786
|
+
bridgeId: "whiteboard",
|
|
787
|
+
action: "whiteboard_delete_item",
|
|
788
|
+
label: `Deleted ${id}`,
|
|
789
|
+
undo: () => {
|
|
790
|
+
if (removedNotes.length) adapter.setNotes((all) => [...all, ...removedNotes]);
|
|
791
|
+
if (removedShapes.length) adapter.setShapes((all) => [...all, ...removedShapes]);
|
|
792
|
+
if (removedConnectors.length) adapter.setConnectors((all) => [...all, ...removedConnectors]);
|
|
793
|
+
if (removedStrokes.length) adapter.setStrokes((all) => [...all, ...removedStrokes]);
|
|
794
|
+
},
|
|
795
|
+
redo: () => {
|
|
796
|
+
adapter.setNotes((all) => all.filter((x) => x.id !== id));
|
|
797
|
+
adapter.setShapes((all) => all.filter((x) => x.id !== id));
|
|
798
|
+
adapter.setConnectors((all) => all.filter((x) => x.id !== id));
|
|
799
|
+
adapter.setStrokes((all) => all.filter((x) => x.id !== id));
|
|
800
|
+
}
|
|
534
801
|
});
|
|
535
|
-
return
|
|
536
|
-
}
|
|
802
|
+
return textResult(`Deleted ${id}`);
|
|
803
|
+
},
|
|
804
|
+
wbTarget
|
|
537
805
|
);
|
|
538
806
|
reg(
|
|
539
807
|
"whiteboard_set_viewport",
|
|
@@ -549,7 +817,8 @@ function registerWhiteboardBridge(server, options) {
|
|
|
549
817
|
};
|
|
550
818
|
adapter.setViewport(next);
|
|
551
819
|
return textResult(`Viewport \u2192 ${JSON.stringify(next)}`, next);
|
|
552
|
-
}
|
|
820
|
+
},
|
|
821
|
+
wbTarget
|
|
553
822
|
);
|
|
554
823
|
reg(
|
|
555
824
|
"whiteboard_set_agent_cursor",
|
|
@@ -575,7 +844,8 @@ function registerWhiteboardBridge(server, options) {
|
|
|
575
844
|
};
|
|
576
845
|
adapter.setAgentCursor(cursor);
|
|
577
846
|
return textResult(`Cursor \u2192 (${cursor.x}, ${cursor.y})`, cursor);
|
|
578
|
-
}
|
|
847
|
+
},
|
|
848
|
+
wbTarget
|
|
579
849
|
);
|
|
580
850
|
return {
|
|
581
851
|
id: "whiteboard",
|
|
@@ -586,28 +856,42 @@ function registerWhiteboardBridge(server, options) {
|
|
|
586
856
|
}
|
|
587
857
|
};
|
|
588
858
|
}
|
|
859
|
+
|
|
860
|
+
// src/bridges/flow.ts
|
|
861
|
+
var DEFAULT_AGENT2 = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
589
862
|
var num2 = (v, fallback) => typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
590
863
|
var str2 = (v, fallback = "") => typeof v === "string" ? v : fallback;
|
|
591
864
|
var newId2 = (prefix) => `${prefix}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 7)}`;
|
|
592
|
-
function registerFlowBridge(
|
|
865
|
+
function registerFlowBridge(host, options) {
|
|
593
866
|
const { adapter } = options;
|
|
594
|
-
|
|
867
|
+
const agent = { ...DEFAULT_AGENT2, ...options.agent ?? {} };
|
|
595
868
|
const disposers = [];
|
|
596
|
-
const
|
|
869
|
+
const flTarget = (args, result) => ({
|
|
870
|
+
kind: "flow",
|
|
871
|
+
elementId: result?.structuredContent?.id ?? args?.id
|
|
872
|
+
});
|
|
873
|
+
const reg = (name, description, properties, required, handler, resolveTarget) => {
|
|
874
|
+
const wrapped = async (args) => {
|
|
875
|
+
try {
|
|
876
|
+
return await handler(args);
|
|
877
|
+
} catch (e) {
|
|
878
|
+
return errorResult(e instanceof Error ? e.message : String(e));
|
|
879
|
+
}
|
|
880
|
+
};
|
|
881
|
+
const final = resolveTarget ? wrapToolWithActivity(wrapped, {
|
|
882
|
+
toolName: name,
|
|
883
|
+
agent: { id: agent.id, name: agent.name, color: agent.color },
|
|
884
|
+
kind: "flow",
|
|
885
|
+
resolveTarget: ({ args, result }) => resolveTarget(args, result)
|
|
886
|
+
}) : wrapped;
|
|
597
887
|
disposers.push(
|
|
598
|
-
|
|
888
|
+
host.registerTool(
|
|
599
889
|
{
|
|
600
890
|
name,
|
|
601
891
|
description,
|
|
602
892
|
inputSchema: { type: "object", properties, required, additionalProperties: false }
|
|
603
893
|
},
|
|
604
|
-
|
|
605
|
-
try {
|
|
606
|
-
return await handler(args);
|
|
607
|
-
} catch (e) {
|
|
608
|
-
return errorResult(e instanceof Error ? e.message : String(e));
|
|
609
|
-
}
|
|
610
|
-
}
|
|
894
|
+
final
|
|
611
895
|
)
|
|
612
896
|
);
|
|
613
897
|
};
|
|
@@ -743,7 +1027,8 @@ function registerFlowBridge(server, options) {
|
|
|
743
1027
|
};
|
|
744
1028
|
adapter.setNodes((all) => [...all, node]);
|
|
745
1029
|
return textResult(`Added ${kindName} ${id} ("${str2(args.label)}")`, node);
|
|
746
|
-
}
|
|
1030
|
+
},
|
|
1031
|
+
flTarget
|
|
747
1032
|
);
|
|
748
1033
|
reg(
|
|
749
1034
|
"flow_update_node",
|
|
@@ -781,7 +1066,8 @@ function registerFlowBridge(server, options) {
|
|
|
781
1066
|
);
|
|
782
1067
|
if (!updated) return errorResult(`No node with id ${id}`);
|
|
783
1068
|
return textResult(`Updated node ${id}`, updated);
|
|
784
|
-
}
|
|
1069
|
+
},
|
|
1070
|
+
flTarget
|
|
785
1071
|
);
|
|
786
1072
|
reg(
|
|
787
1073
|
"flow_delete_node",
|
|
@@ -796,7 +1082,8 @@ function registerFlowBridge(server, options) {
|
|
|
796
1082
|
adapter.setNodes((all) => all.filter((n) => n.id !== id));
|
|
797
1083
|
adapter.setEdges((all) => all.filter((e) => e.source !== id && e.target !== id));
|
|
798
1084
|
return textResult(`Deleted node ${id}`);
|
|
799
|
-
}
|
|
1085
|
+
},
|
|
1086
|
+
flTarget
|
|
800
1087
|
);
|
|
801
1088
|
reg(
|
|
802
1089
|
"flow_connect",
|
|
@@ -825,7 +1112,8 @@ function registerFlowBridge(server, options) {
|
|
|
825
1112
|
};
|
|
826
1113
|
adapter.setEdges((existing) => [...existing, edge]);
|
|
827
1114
|
return textResult(`Connected ${source}${edge.sourceHandle ? `:${edge.sourceHandle}` : ""} \u2192 ${target}${edge.targetHandle ? `:${edge.targetHandle}` : ""}`, edge);
|
|
828
|
-
}
|
|
1115
|
+
},
|
|
1116
|
+
flTarget
|
|
829
1117
|
);
|
|
830
1118
|
reg(
|
|
831
1119
|
"flow_disconnect",
|
|
@@ -839,7 +1127,8 @@ function registerFlowBridge(server, options) {
|
|
|
839
1127
|
}
|
|
840
1128
|
adapter.setEdges((all) => all.filter((e) => e.id !== id));
|
|
841
1129
|
return textResult(`Disconnected ${id}`);
|
|
842
|
-
}
|
|
1130
|
+
},
|
|
1131
|
+
flTarget
|
|
843
1132
|
);
|
|
844
1133
|
reg(
|
|
845
1134
|
"flow_set_node_status",
|
|
@@ -868,7 +1157,8 @@ function registerFlowBridge(server, options) {
|
|
|
868
1157
|
if (!found) return errorResult(`No node with id ${id}`);
|
|
869
1158
|
}
|
|
870
1159
|
return textResult(`${id} \u2192 ${status}${text ? ` (${text})` : ""}`);
|
|
871
|
-
}
|
|
1160
|
+
},
|
|
1161
|
+
flTarget
|
|
872
1162
|
);
|
|
873
1163
|
reg(
|
|
874
1164
|
"flow_run",
|
|
@@ -879,7 +1169,8 @@ function registerFlowBridge(server, options) {
|
|
|
879
1169
|
if (!adapter.run) return errorResult("Host did not provide a run handler.");
|
|
880
1170
|
const result = await adapter.run();
|
|
881
1171
|
return textResult(result.ok ? "Run complete" : `Run failed: ${result.error ?? "unknown"}`, result);
|
|
882
|
-
}
|
|
1172
|
+
},
|
|
1173
|
+
flTarget
|
|
883
1174
|
);
|
|
884
1175
|
reg(
|
|
885
1176
|
"flow_cancel",
|
|
@@ -890,7 +1181,8 @@ function registerFlowBridge(server, options) {
|
|
|
890
1181
|
if (!adapter.cancel) return errorResult("Host did not provide a cancel handler.");
|
|
891
1182
|
adapter.cancel();
|
|
892
1183
|
return textResult("Run cancelled");
|
|
893
|
-
}
|
|
1184
|
+
},
|
|
1185
|
+
flTarget
|
|
894
1186
|
);
|
|
895
1187
|
return {
|
|
896
1188
|
id: "flow",
|
|
@@ -900,65 +1192,1221 @@ function registerFlowBridge(server, options) {
|
|
|
900
1192
|
}
|
|
901
1193
|
};
|
|
902
1194
|
}
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
const
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
)
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
] }),
|
|
935
|
-
actions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fai-panel__actions", children: actions })
|
|
936
|
-
] }),
|
|
937
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { ref: scrollRef, className: "fai-panel__stream", children: activity.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "fai-panel__empty", children: "No activity yet." }) : activity.map((a) => /* @__PURE__ */ jsxRuntime.jsx(ActivityRow, { item: a }, a.id)) }),
|
|
938
|
-
onSubmit && /* @__PURE__ */ jsxRuntime.jsxs("form", { className: "fai-panel__composer", onSubmit: handleSubmit, children: [
|
|
939
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
940
|
-
"textarea",
|
|
1195
|
+
|
|
1196
|
+
// src/bridges/forms.ts
|
|
1197
|
+
var DEFAULT_AGENT3 = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
1198
|
+
function registerFormBridge(host, options) {
|
|
1199
|
+
const { adapter } = options;
|
|
1200
|
+
const agent = { ...DEFAULT_AGENT3, ...options.agent ?? {} };
|
|
1201
|
+
const disposers = [];
|
|
1202
|
+
const formId = adapter.id;
|
|
1203
|
+
const target = (args) => ({
|
|
1204
|
+
kind: "form",
|
|
1205
|
+
screenId: adapter.screenId,
|
|
1206
|
+
elementId: typeof args?.field === "string" ? `${formId}:${args.field}` : formId,
|
|
1207
|
+
label: typeof args?.field === "string" ? `${adapter.title ?? formId} \u2192 ${args.field}` : adapter.title ?? formId
|
|
1208
|
+
});
|
|
1209
|
+
const reg = (name, description, properties, required, handler, isMutation) => {
|
|
1210
|
+
const wrapped = async (args) => {
|
|
1211
|
+
try {
|
|
1212
|
+
return await handler(args);
|
|
1213
|
+
} catch (e) {
|
|
1214
|
+
return errorResult(e instanceof Error ? e.message : String(e));
|
|
1215
|
+
}
|
|
1216
|
+
};
|
|
1217
|
+
const final = isMutation ? wrapToolWithActivity(wrapped, {
|
|
1218
|
+
toolName: name,
|
|
1219
|
+
agent: { id: agent.id, name: agent.name, color: agent.color },
|
|
1220
|
+
kind: "form",
|
|
1221
|
+
screenId: adapter.screenId,
|
|
1222
|
+
resolveTarget: ({ args }) => target(args)
|
|
1223
|
+
}) : wrapped;
|
|
1224
|
+
disposers.push(
|
|
1225
|
+
host.registerTool(
|
|
941
1226
|
{
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
1227
|
+
name,
|
|
1228
|
+
description,
|
|
1229
|
+
inputSchema: { type: "object", properties, required, additionalProperties: false }
|
|
1230
|
+
},
|
|
1231
|
+
final
|
|
1232
|
+
)
|
|
1233
|
+
);
|
|
1234
|
+
};
|
|
1235
|
+
reg(
|
|
1236
|
+
"form_describe",
|
|
1237
|
+
`Describe the form "${formId}" \u2014 fields, types, options, required flags. Call this first to know what's fillable.`,
|
|
1238
|
+
{},
|
|
1239
|
+
[],
|
|
1240
|
+
() => {
|
|
1241
|
+
const fields = adapter.getFields();
|
|
1242
|
+
const text = fields.map((f) => `${f.name}${f.required ? "*" : ""} (${f.type})${f.label ? ` \u2014 ${f.label}` : ""}`).join("\n");
|
|
1243
|
+
return textResult(text || "(no fields)", { id: formId, title: adapter.title, fields });
|
|
1244
|
+
},
|
|
1245
|
+
false
|
|
1246
|
+
);
|
|
1247
|
+
reg(
|
|
1248
|
+
"form_get_value",
|
|
1249
|
+
"Read a single field's current value.",
|
|
1250
|
+
{ field: { type: "string" } },
|
|
1251
|
+
["field"],
|
|
1252
|
+
(args) => {
|
|
1253
|
+
const name = String(args.field ?? "");
|
|
1254
|
+
if (!adapter.getFields().find((f) => f.name === name)) {
|
|
1255
|
+
return errorResult(`Unknown field: ${name}`);
|
|
1256
|
+
}
|
|
1257
|
+
const value = adapter.getValue(name);
|
|
1258
|
+
return textResult(JSON.stringify(value), { field: name, value });
|
|
1259
|
+
},
|
|
1260
|
+
false
|
|
1261
|
+
);
|
|
1262
|
+
reg(
|
|
1263
|
+
"form_get_values",
|
|
1264
|
+
"Read every field's current value as a JSON object.",
|
|
1265
|
+
{},
|
|
1266
|
+
[],
|
|
1267
|
+
() => {
|
|
1268
|
+
const values = adapter.getValues();
|
|
1269
|
+
return textResult(JSON.stringify(values, null, 2), values);
|
|
1270
|
+
},
|
|
1271
|
+
false
|
|
1272
|
+
);
|
|
1273
|
+
reg(
|
|
1274
|
+
"form_set_value",
|
|
1275
|
+
"Set one field's value. The host's controlled state updates and the human sees the field change.",
|
|
1276
|
+
{
|
|
1277
|
+
field: { type: "string" },
|
|
1278
|
+
value: { description: "Value to set. Type depends on the field's `type`." }
|
|
1279
|
+
},
|
|
1280
|
+
["field", "value"],
|
|
1281
|
+
(args) => {
|
|
1282
|
+
const name = String(args.field ?? "");
|
|
1283
|
+
const fieldDef = adapter.getFields().find((f) => f.name === name);
|
|
1284
|
+
if (!fieldDef) return errorResult(`Unknown field: ${name}`);
|
|
1285
|
+
adapter.setValue(name, args.value);
|
|
1286
|
+
return textResult(`${name} \u2190 ${JSON.stringify(args.value)}`, { field: name, value: args.value });
|
|
1287
|
+
},
|
|
1288
|
+
true
|
|
1289
|
+
);
|
|
1290
|
+
reg(
|
|
1291
|
+
"form_set_values",
|
|
1292
|
+
"Set multiple fields atomically. Pass a `values` object keyed by field name.",
|
|
1293
|
+
{ values: { type: "object" } },
|
|
1294
|
+
["values"],
|
|
1295
|
+
(args) => {
|
|
1296
|
+
const values = args.values && typeof args.values === "object" ? args.values : {};
|
|
1297
|
+
const fields = adapter.getFields();
|
|
1298
|
+
const known = new Set(fields.map((f) => f.name));
|
|
1299
|
+
const unknownKeys = Object.keys(values).filter((k) => !known.has(k));
|
|
1300
|
+
if (unknownKeys.length) return errorResult(`Unknown fields: ${unknownKeys.join(", ")}`);
|
|
1301
|
+
if (adapter.setValues) {
|
|
1302
|
+
adapter.setValues(values);
|
|
1303
|
+
} else {
|
|
1304
|
+
for (const [k, v] of Object.entries(values)) adapter.setValue(k, v);
|
|
1305
|
+
}
|
|
1306
|
+
return textResult(`Set ${Object.keys(values).length} field(s)`, { values });
|
|
1307
|
+
},
|
|
1308
|
+
true
|
|
1309
|
+
);
|
|
1310
|
+
reg(
|
|
1311
|
+
"form_focus",
|
|
1312
|
+
"Move browser focus to a field (host-implemented). Useful before streaming text into it.",
|
|
1313
|
+
{ field: { type: "string" } },
|
|
1314
|
+
["field"],
|
|
1315
|
+
(args) => {
|
|
1316
|
+
const name = String(args.field ?? "");
|
|
1317
|
+
if (!adapter.focus) return errorResult("Host did not provide a focus implementation.");
|
|
1318
|
+
if (!adapter.getFields().find((f) => f.name === name)) {
|
|
1319
|
+
return errorResult(`Unknown field: ${name}`);
|
|
1320
|
+
}
|
|
1321
|
+
adapter.focus(name);
|
|
1322
|
+
return textResult(`Focused ${name}`, { field: name });
|
|
1323
|
+
},
|
|
1324
|
+
true
|
|
1325
|
+
);
|
|
1326
|
+
reg(
|
|
1327
|
+
"form_submit",
|
|
1328
|
+
"Submit the form. Host returns ok + values (or an error).",
|
|
1329
|
+
{},
|
|
1330
|
+
[],
|
|
1331
|
+
async () => {
|
|
1332
|
+
if (!adapter.submit) return errorResult("Host did not provide a submit implementation.");
|
|
1333
|
+
const result = await adapter.submit();
|
|
1334
|
+
if (!result.ok) return errorResult(result.error ?? "Submit failed");
|
|
1335
|
+
return textResult("Submitted", { values: result.values });
|
|
1336
|
+
},
|
|
1337
|
+
true
|
|
1338
|
+
);
|
|
1339
|
+
return {
|
|
1340
|
+
id: `form:${formId}`,
|
|
1341
|
+
title: adapter.title ?? formId,
|
|
1342
|
+
dispose: () => {
|
|
1343
|
+
for (const d of disposers) d();
|
|
1344
|
+
}
|
|
1345
|
+
};
|
|
958
1346
|
}
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
1347
|
+
|
|
1348
|
+
// src/bridges/sheets.ts
|
|
1349
|
+
var DEFAULT_AGENT4 = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
1350
|
+
function registerSheetsBridge(host, options) {
|
|
1351
|
+
const { adapter } = options;
|
|
1352
|
+
const agent = { ...DEFAULT_AGENT4, ...options.agent ?? {} };
|
|
1353
|
+
const disposers = [];
|
|
1354
|
+
const target = (sheetId, address) => ({
|
|
1355
|
+
kind: "sheet",
|
|
1356
|
+
screenId: adapter.screenId,
|
|
1357
|
+
elementId: address ? `${sheetId}!${address}` : sheetId,
|
|
1358
|
+
label: address ? `${sheetId}!${address}` : sheetId
|
|
1359
|
+
});
|
|
1360
|
+
const reg = (name, description, properties, required, handler, isMutation, resolveTarget) => {
|
|
1361
|
+
const wrapped = async (args) => {
|
|
1362
|
+
try {
|
|
1363
|
+
return await handler(args);
|
|
1364
|
+
} catch (e) {
|
|
1365
|
+
return errorResult(e instanceof Error ? e.message : String(e));
|
|
1366
|
+
}
|
|
1367
|
+
};
|
|
1368
|
+
const final = isMutation ? wrapToolWithActivity(wrapped, {
|
|
1369
|
+
toolName: name,
|
|
1370
|
+
agent,
|
|
1371
|
+
kind: "sheet",
|
|
1372
|
+
screenId: adapter.screenId,
|
|
1373
|
+
resolveTarget: ({ args, result }) => resolveTarget?.(args, result) ?? target(getSheetId(args))
|
|
1374
|
+
}) : wrapped;
|
|
1375
|
+
disposers.push(
|
|
1376
|
+
host.registerTool(
|
|
1377
|
+
{
|
|
1378
|
+
name,
|
|
1379
|
+
description,
|
|
1380
|
+
inputSchema: { type: "object", properties, required, additionalProperties: false }
|
|
1381
|
+
},
|
|
1382
|
+
final
|
|
1383
|
+
)
|
|
1384
|
+
);
|
|
1385
|
+
};
|
|
1386
|
+
function activeSheetId() {
|
|
1387
|
+
return adapter.getWorkbook().activeSheetId;
|
|
1388
|
+
}
|
|
1389
|
+
function getSheetId(args) {
|
|
1390
|
+
return typeof args.sheet === "string" ? args.sheet : activeSheetId();
|
|
1391
|
+
}
|
|
1392
|
+
function getSheet(workbook, sheetId) {
|
|
1393
|
+
return workbook.sheets.find((s) => s.id === sheetId);
|
|
1394
|
+
}
|
|
1395
|
+
reg(
|
|
1396
|
+
"sheet_describe",
|
|
1397
|
+
"Describe the workbook: every sheet's id, name, dimensions, cell count, active sheet. Call before reading or writing.",
|
|
1398
|
+
{},
|
|
1399
|
+
[],
|
|
1400
|
+
() => {
|
|
1401
|
+
const wb = adapter.getWorkbook();
|
|
1402
|
+
const summary = {
|
|
1403
|
+
activeSheetId: wb.activeSheetId,
|
|
1404
|
+
sheets: wb.sheets.map((s) => ({
|
|
1405
|
+
id: s.id,
|
|
1406
|
+
name: s.name,
|
|
1407
|
+
cellCount: Object.keys(s.cells).length,
|
|
1408
|
+
columnCount: Object.keys(s.columnWidths).length,
|
|
1409
|
+
frozenRows: s.frozenRows,
|
|
1410
|
+
frozenCols: s.frozenCols
|
|
1411
|
+
}))
|
|
1412
|
+
};
|
|
1413
|
+
const text = `Active: ${summary.activeSheetId}
|
|
1414
|
+
` + summary.sheets.map((s) => `${s.id} "${s.name}" \u2014 ${s.cellCount} cells`).join("\n");
|
|
1415
|
+
return textResult(text, summary);
|
|
1416
|
+
},
|
|
1417
|
+
false
|
|
1418
|
+
);
|
|
1419
|
+
reg(
|
|
1420
|
+
"sheet_get_cell",
|
|
1421
|
+
"Read a single cell's raw + computed value.",
|
|
1422
|
+
{
|
|
1423
|
+
sheet: { type: "string", description: "Sheet id (defaults to active)." },
|
|
1424
|
+
address: { type: "string", description: 'A1-style address, e.g. "B12".' }
|
|
1425
|
+
},
|
|
1426
|
+
["address"],
|
|
1427
|
+
(args) => {
|
|
1428
|
+
const sheetId = getSheetId(args);
|
|
1429
|
+
const address = String(args.address);
|
|
1430
|
+
const sheet = getSheet(adapter.getWorkbook(), sheetId);
|
|
1431
|
+
if (!sheet) return errorResult(`No sheet ${sheetId}`);
|
|
1432
|
+
const cell = sheet.cells[address];
|
|
1433
|
+
if (!cell) {
|
|
1434
|
+
return textResult(`(empty)`, { sheet: sheetId, address, value: null });
|
|
1435
|
+
}
|
|
1436
|
+
return textResult(`${address} = ${JSON.stringify(cell.computedValue ?? cell.value)}`, { ...cell, sheet: sheetId, address });
|
|
1437
|
+
},
|
|
1438
|
+
false
|
|
1439
|
+
);
|
|
1440
|
+
reg(
|
|
1441
|
+
"sheet_get_range",
|
|
1442
|
+
"Read a rectangular range as a 2D array of values.",
|
|
1443
|
+
{
|
|
1444
|
+
sheet: { type: "string" },
|
|
1445
|
+
start: { type: "string", description: "Top-left A1 address." },
|
|
1446
|
+
end: { type: "string", description: "Bottom-right A1 address." }
|
|
1447
|
+
},
|
|
1448
|
+
["start", "end"],
|
|
1449
|
+
(args) => {
|
|
1450
|
+
const sheetId = getSheetId(args);
|
|
1451
|
+
const sheet = getSheet(adapter.getWorkbook(), sheetId);
|
|
1452
|
+
if (!sheet) return errorResult(`No sheet ${sheetId}`);
|
|
1453
|
+
const grid = readRange(sheet, String(args.start), String(args.end));
|
|
1454
|
+
return textResult(JSON.stringify(grid), { sheet: sheetId, start: args.start, end: args.end, values: grid });
|
|
1455
|
+
},
|
|
1456
|
+
false
|
|
1457
|
+
);
|
|
1458
|
+
reg(
|
|
1459
|
+
"sheet_set_cell",
|
|
1460
|
+
"Set a single cell's value. To set a formula, pass a string starting with '='.",
|
|
1461
|
+
{
|
|
1462
|
+
sheet: { type: "string" },
|
|
1463
|
+
address: { type: "string" },
|
|
1464
|
+
value: { description: "string | number | boolean | null. Strings starting with '=' are stored as formulas." }
|
|
1465
|
+
},
|
|
1466
|
+
["address", "value"],
|
|
1467
|
+
(args) => {
|
|
1468
|
+
const sheetId = getSheetId(args);
|
|
1469
|
+
const address = String(args.address);
|
|
1470
|
+
const value = args.value;
|
|
1471
|
+
const wb = adapter.getWorkbook();
|
|
1472
|
+
const sheet = getSheet(wb, sheetId);
|
|
1473
|
+
if (!sheet) return errorResult(`No sheet ${sheetId}`);
|
|
1474
|
+
const next = mergeCells(wb, sheetId, { [address]: cellOf(address, value) });
|
|
1475
|
+
adapter.setWorkbook(next);
|
|
1476
|
+
return textResult(`${sheetId}!${address} \u2190 ${JSON.stringify(value)}`, { sheet: sheetId, address, value });
|
|
1477
|
+
},
|
|
1478
|
+
true,
|
|
1479
|
+
(args) => target(getSheetId(args), String(args.address ?? ""))
|
|
1480
|
+
);
|
|
1481
|
+
reg(
|
|
1482
|
+
"sheet_set_range",
|
|
1483
|
+
'Set many cells atomically. `cells` is an object map of { "A1": value, "B2": value, ... }.',
|
|
1484
|
+
{
|
|
1485
|
+
sheet: { type: "string" },
|
|
1486
|
+
cells: { type: "object" }
|
|
1487
|
+
},
|
|
1488
|
+
["cells"],
|
|
1489
|
+
(args) => {
|
|
1490
|
+
const sheetId = getSheetId(args);
|
|
1491
|
+
const wb = adapter.getWorkbook();
|
|
1492
|
+
const sheet = getSheet(wb, sheetId);
|
|
1493
|
+
if (!sheet) return errorResult(`No sheet ${sheetId}`);
|
|
1494
|
+
const cells = args.cells && typeof args.cells === "object" ? args.cells : {};
|
|
1495
|
+
const updates = {};
|
|
1496
|
+
for (const [addr, v] of Object.entries(cells)) updates[addr] = cellOf(addr, v);
|
|
1497
|
+
const next = mergeCells(wb, sheetId, updates);
|
|
1498
|
+
adapter.setWorkbook(next);
|
|
1499
|
+
return textResult(`Set ${Object.keys(cells).length} cells in ${sheetId}`, { sheet: sheetId, count: Object.keys(cells).length });
|
|
1500
|
+
},
|
|
1501
|
+
true
|
|
1502
|
+
);
|
|
1503
|
+
reg(
|
|
1504
|
+
"sheet_add_sheet",
|
|
1505
|
+
"Add a new sheet (tab) to the workbook.",
|
|
1506
|
+
{ id: { type: "string" }, name: { type: "string" } },
|
|
1507
|
+
["id", "name"],
|
|
1508
|
+
(args) => {
|
|
1509
|
+
const wb = adapter.getWorkbook();
|
|
1510
|
+
const id = String(args.id);
|
|
1511
|
+
const name = String(args.name);
|
|
1512
|
+
if (wb.sheets.find((s) => s.id === id)) return errorResult(`Sheet ${id} already exists`);
|
|
1513
|
+
const next = {
|
|
1514
|
+
...wb,
|
|
1515
|
+
sheets: [
|
|
1516
|
+
...wb.sheets,
|
|
1517
|
+
{
|
|
1518
|
+
id,
|
|
1519
|
+
name,
|
|
1520
|
+
cells: {},
|
|
1521
|
+
columnWidths: {},
|
|
1522
|
+
mergedRegions: [],
|
|
1523
|
+
columnFilters: {},
|
|
1524
|
+
frozenRows: 0,
|
|
1525
|
+
frozenCols: 0
|
|
1526
|
+
}
|
|
1527
|
+
]
|
|
1528
|
+
};
|
|
1529
|
+
adapter.setWorkbook(next);
|
|
1530
|
+
return textResult(`Added sheet ${id} ("${name}")`, { id, name });
|
|
1531
|
+
},
|
|
1532
|
+
true,
|
|
1533
|
+
(args) => target(String(args.id ?? ""))
|
|
1534
|
+
);
|
|
1535
|
+
reg(
|
|
1536
|
+
"sheet_set_active",
|
|
1537
|
+
"Switch to a different sheet tab.",
|
|
1538
|
+
{ sheet: { type: "string" } },
|
|
1539
|
+
["sheet"],
|
|
1540
|
+
(args) => {
|
|
1541
|
+
const sheetId = String(args.sheet);
|
|
1542
|
+
const wb = adapter.getWorkbook();
|
|
1543
|
+
if (!getSheet(wb, sheetId)) return errorResult(`No sheet ${sheetId}`);
|
|
1544
|
+
adapter.setWorkbook({ ...wb, activeSheetId: sheetId });
|
|
1545
|
+
return textResult(`Active sheet \u2192 ${sheetId}`, { sheet: sheetId });
|
|
1546
|
+
},
|
|
1547
|
+
true
|
|
1548
|
+
);
|
|
1549
|
+
reg(
|
|
1550
|
+
"sheet_set_active_cell",
|
|
1551
|
+
"Move the active cell selection (host implements DOM focus + scroll).",
|
|
1552
|
+
{ sheet: { type: "string" }, address: { type: "string" } },
|
|
1553
|
+
["address"],
|
|
1554
|
+
(args) => {
|
|
1555
|
+
if (!adapter.setActiveCell) return errorResult("Host did not provide setActiveCell.");
|
|
1556
|
+
const sheetId = getSheetId(args);
|
|
1557
|
+
adapter.setActiveCell(sheetId, String(args.address));
|
|
1558
|
+
return textResult(`Active cell \u2192 ${sheetId}!${args.address}`, { sheet: sheetId, address: args.address });
|
|
1559
|
+
},
|
|
1560
|
+
true,
|
|
1561
|
+
(args) => target(getSheetId(args), String(args.address ?? ""))
|
|
1562
|
+
);
|
|
1563
|
+
return {
|
|
1564
|
+
id: "sheets",
|
|
1565
|
+
title: "Sheets",
|
|
1566
|
+
dispose: () => {
|
|
1567
|
+
for (const d of disposers) d();
|
|
1568
|
+
}
|
|
1569
|
+
};
|
|
1570
|
+
}
|
|
1571
|
+
function cellOf(address, value) {
|
|
1572
|
+
return { address, value };
|
|
1573
|
+
}
|
|
1574
|
+
function mergeCells(wb, sheetId, updates) {
|
|
1575
|
+
return {
|
|
1576
|
+
...wb,
|
|
1577
|
+
sheets: wb.sheets.map(
|
|
1578
|
+
(s) => s.id !== sheetId ? s : { ...s, cells: { ...s.cells, ...updates } }
|
|
1579
|
+
)
|
|
1580
|
+
};
|
|
1581
|
+
}
|
|
1582
|
+
function parseAddress(addr) {
|
|
1583
|
+
const m = /^([A-Za-z]+)(\d+)$/.exec(addr.trim());
|
|
1584
|
+
if (!m) throw new Error(`Bad address: ${addr}`);
|
|
1585
|
+
const letters = m[1].toUpperCase();
|
|
1586
|
+
let col = 0;
|
|
1587
|
+
for (let i = 0; i < letters.length; i++) {
|
|
1588
|
+
col = col * 26 + (letters.charCodeAt(i) - 64);
|
|
1589
|
+
}
|
|
1590
|
+
return { col: col - 1, row: parseInt(m[2], 10) - 1 };
|
|
1591
|
+
}
|
|
1592
|
+
function colToLetter(col) {
|
|
1593
|
+
let s = "";
|
|
1594
|
+
let n = col + 1;
|
|
1595
|
+
while (n > 0) {
|
|
1596
|
+
const r = (n - 1) % 26;
|
|
1597
|
+
s = String.fromCharCode(65 + r) + s;
|
|
1598
|
+
n = Math.floor((n - 1) / 26);
|
|
1599
|
+
}
|
|
1600
|
+
return s;
|
|
1601
|
+
}
|
|
1602
|
+
function readRange(sheet, startAddr, endAddr) {
|
|
1603
|
+
const start = parseAddress(startAddr);
|
|
1604
|
+
const end = parseAddress(endAddr);
|
|
1605
|
+
const r0 = Math.min(start.row, end.row);
|
|
1606
|
+
const r1 = Math.max(start.row, end.row);
|
|
1607
|
+
const c0 = Math.min(start.col, end.col);
|
|
1608
|
+
const c1 = Math.max(start.col, end.col);
|
|
1609
|
+
const grid = [];
|
|
1610
|
+
for (let r = r0; r <= r1; r++) {
|
|
1611
|
+
const row = [];
|
|
1612
|
+
for (let c = c0; c <= c1; c++) {
|
|
1613
|
+
const addr = `${colToLetter(c)}${r + 1}`;
|
|
1614
|
+
const cell = sheet.cells[addr];
|
|
1615
|
+
row.push(cell?.computedValue ?? cell?.value ?? null);
|
|
1616
|
+
}
|
|
1617
|
+
grid.push(row);
|
|
1618
|
+
}
|
|
1619
|
+
return grid;
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
// src/sheets-adapter.ts
|
|
1623
|
+
init_registry();
|
|
1624
|
+
function useSheetsAdapter(initial, options = {}) {
|
|
1625
|
+
const [workbook, setWorkbook] = react.useState(initial);
|
|
1626
|
+
const [activeCell, setActiveCellState] = react.useState(null);
|
|
1627
|
+
const workbookRef = react.useRef(workbook);
|
|
1628
|
+
workbookRef.current = workbook;
|
|
1629
|
+
const setActiveCell = react.useCallback((sheetId, address) => {
|
|
1630
|
+
setWorkbook((cur) => cur.activeSheetId === sheetId ? cur : { ...cur, activeSheetId: sheetId });
|
|
1631
|
+
setActiveCellState(address);
|
|
1632
|
+
}, []);
|
|
1633
|
+
const onActiveCellChange = react.useCallback((address) => {
|
|
1634
|
+
setActiveCellState(address);
|
|
1635
|
+
}, []);
|
|
1636
|
+
const setWorkbookRef = react.useRef(setWorkbook);
|
|
1637
|
+
setWorkbookRef.current = setWorkbook;
|
|
1638
|
+
const adapter = react.useMemo(
|
|
1639
|
+
() => ({
|
|
1640
|
+
screenId: options.screenId,
|
|
1641
|
+
getWorkbook: () => workbookRef.current,
|
|
1642
|
+
setWorkbook: (next) => setWorkbookRef.current(next),
|
|
1643
|
+
setActiveCell
|
|
1644
|
+
}),
|
|
1645
|
+
[options.screenId, setActiveCell]
|
|
1646
|
+
);
|
|
1647
|
+
return {
|
|
1648
|
+
workbook,
|
|
1649
|
+
setWorkbook,
|
|
1650
|
+
onActiveCellChange,
|
|
1651
|
+
adapter,
|
|
1652
|
+
setActiveCell,
|
|
1653
|
+
activeCell
|
|
1654
|
+
};
|
|
1655
|
+
}
|
|
1656
|
+
function useSheetsActivityHighlights(options = {}) {
|
|
1657
|
+
const ttlMs = options.ttlMs ?? 2200;
|
|
1658
|
+
const screenId = options.screenId;
|
|
1659
|
+
const [, force] = react.useState(0);
|
|
1660
|
+
const hitsRef = react.useRef(/* @__PURE__ */ new Map());
|
|
1661
|
+
react.useEffect(() => {
|
|
1662
|
+
const off = onActivity((event) => {
|
|
1663
|
+
if (event.target?.kind !== "sheet") return;
|
|
1664
|
+
if (screenId && event.target.screenId && event.target.screenId !== screenId) return;
|
|
1665
|
+
const elementId = event.target.elementId;
|
|
1666
|
+
if (!elementId || !elementId.includes("!")) return;
|
|
1667
|
+
hitsRef.current.set(elementId, { event, expiresAt: Date.now() + ttlMs });
|
|
1668
|
+
force((n) => n + 1);
|
|
1669
|
+
});
|
|
1670
|
+
return off;
|
|
1671
|
+
}, [screenId, ttlMs]);
|
|
1672
|
+
react.useEffect(() => {
|
|
1673
|
+
const t = window.setInterval(() => {
|
|
1674
|
+
const now = Date.now();
|
|
1675
|
+
let dirty = false;
|
|
1676
|
+
for (const [k, v] of hitsRef.current) {
|
|
1677
|
+
if (v.expiresAt < now) {
|
|
1678
|
+
hitsRef.current.delete(k);
|
|
1679
|
+
dirty = true;
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
if (dirty) force((n) => n + 1);
|
|
1683
|
+
}, 500);
|
|
1684
|
+
return () => window.clearInterval(t);
|
|
1685
|
+
}, []);
|
|
1686
|
+
const out = {};
|
|
1687
|
+
for (const [elementId, { event }] of hitsRef.current) {
|
|
1688
|
+
const idx = elementId.indexOf("!");
|
|
1689
|
+
const address = elementId.slice(idx + 1);
|
|
1690
|
+
if (!address) continue;
|
|
1691
|
+
const color = event.agentColor ?? "#a855f7";
|
|
1692
|
+
out[address] = {
|
|
1693
|
+
color,
|
|
1694
|
+
background: color + "33",
|
|
1695
|
+
label: event.agentName ?? event.agentId ?? "agent"
|
|
1696
|
+
};
|
|
1697
|
+
}
|
|
1698
|
+
return out;
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
// src/bridges/code.ts
|
|
1702
|
+
var DEFAULT_AGENT5 = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
1703
|
+
function registerCodeBridge(host, options) {
|
|
1704
|
+
const { adapter } = options;
|
|
1705
|
+
const agent = { ...DEFAULT_AGENT5, ...options.agent ?? {} };
|
|
1706
|
+
const disposers = [];
|
|
1707
|
+
const target = {
|
|
1708
|
+
kind: "code",
|
|
1709
|
+
screenId: adapter.screenId,
|
|
1710
|
+
elementId: adapter.id,
|
|
1711
|
+
label: adapter.title ?? adapter.id
|
|
1712
|
+
};
|
|
1713
|
+
const reg = (name, description, properties, required, handler, isMutation) => {
|
|
1714
|
+
const wrapped = async (args) => {
|
|
1715
|
+
try {
|
|
1716
|
+
return await handler(args);
|
|
1717
|
+
} catch (e) {
|
|
1718
|
+
return errorResult(e instanceof Error ? e.message : String(e));
|
|
1719
|
+
}
|
|
1720
|
+
};
|
|
1721
|
+
const final = isMutation ? wrapToolWithActivity(wrapped, {
|
|
1722
|
+
toolName: name,
|
|
1723
|
+
agent,
|
|
1724
|
+
kind: "code",
|
|
1725
|
+
screenId: adapter.screenId,
|
|
1726
|
+
resolveTarget: () => target
|
|
1727
|
+
}) : wrapped;
|
|
1728
|
+
disposers.push(
|
|
1729
|
+
host.registerTool(
|
|
1730
|
+
{
|
|
1731
|
+
name,
|
|
1732
|
+
description,
|
|
1733
|
+
inputSchema: { type: "object", properties, required, additionalProperties: false }
|
|
1734
|
+
},
|
|
1735
|
+
final
|
|
1736
|
+
)
|
|
1737
|
+
);
|
|
1738
|
+
};
|
|
1739
|
+
reg(
|
|
1740
|
+
"code_describe",
|
|
1741
|
+
`Describe the editor "${adapter.id}" \u2014 language + length + has-selection.`,
|
|
1742
|
+
{},
|
|
1743
|
+
[],
|
|
1744
|
+
() => {
|
|
1745
|
+
const value = adapter.getValue();
|
|
1746
|
+
const language = adapter.getLanguage?.() ?? "unknown";
|
|
1747
|
+
const summary = { id: adapter.id, language, length: value.length, lines: value.split("\n").length };
|
|
1748
|
+
return textResult(JSON.stringify(summary), summary);
|
|
1749
|
+
},
|
|
1750
|
+
false
|
|
1751
|
+
);
|
|
1752
|
+
reg(
|
|
1753
|
+
"code_get_value",
|
|
1754
|
+
"Read the full document text.",
|
|
1755
|
+
{},
|
|
1756
|
+
[],
|
|
1757
|
+
() => {
|
|
1758
|
+
const value = adapter.getValue();
|
|
1759
|
+
return textResult(value, { value });
|
|
1760
|
+
},
|
|
1761
|
+
false
|
|
1762
|
+
);
|
|
1763
|
+
reg(
|
|
1764
|
+
"code_get_selection",
|
|
1765
|
+
"Read the currently-selected text (empty string if no selection).",
|
|
1766
|
+
{},
|
|
1767
|
+
[],
|
|
1768
|
+
() => {
|
|
1769
|
+
if (!adapter.getSelection) return errorResult("Host did not provide getSelection.");
|
|
1770
|
+
const value = adapter.getSelection();
|
|
1771
|
+
return textResult(value, { value });
|
|
1772
|
+
},
|
|
1773
|
+
false
|
|
1774
|
+
);
|
|
1775
|
+
reg(
|
|
1776
|
+
"code_set_value",
|
|
1777
|
+
"Replace the entire document.",
|
|
1778
|
+
{ value: { type: "string" } },
|
|
1779
|
+
["value"],
|
|
1780
|
+
(args) => {
|
|
1781
|
+
const value = String(args.value ?? "");
|
|
1782
|
+
adapter.setValue(value);
|
|
1783
|
+
return textResult(`Replaced document (${value.length} chars)`, { length: value.length });
|
|
1784
|
+
},
|
|
1785
|
+
true
|
|
1786
|
+
);
|
|
1787
|
+
reg(
|
|
1788
|
+
"code_append",
|
|
1789
|
+
"Append text to the end of the document.",
|
|
1790
|
+
{ text: { type: "string" } },
|
|
1791
|
+
["text"],
|
|
1792
|
+
(args) => {
|
|
1793
|
+
const text = String(args.text ?? "");
|
|
1794
|
+
const next = adapter.getValue() + text;
|
|
1795
|
+
adapter.setValue(next);
|
|
1796
|
+
return textResult(`Appended ${text.length} chars`, { length: next.length });
|
|
1797
|
+
},
|
|
1798
|
+
true
|
|
1799
|
+
);
|
|
1800
|
+
reg(
|
|
1801
|
+
"code_stream_append",
|
|
1802
|
+
"Type characters into the document one at a time so the human can read it forming. Returns when streaming finishes.",
|
|
1803
|
+
{
|
|
1804
|
+
text: { type: "string" },
|
|
1805
|
+
cps: { type: "number", description: "Characters per second. Default 25." }
|
|
1806
|
+
},
|
|
1807
|
+
["text"],
|
|
1808
|
+
async (args) => {
|
|
1809
|
+
const text = String(args.text ?? "");
|
|
1810
|
+
const cps = Math.max(1, Number(args.cps ?? 25));
|
|
1811
|
+
const interval = Math.max(8, Math.round(1e3 / cps));
|
|
1812
|
+
const start = adapter.getValue();
|
|
1813
|
+
for (let i = 1; i <= text.length; i++) {
|
|
1814
|
+
adapter.setValue(start + text.slice(0, i));
|
|
1815
|
+
if (i < text.length) await new Promise((r) => setTimeout(r, interval));
|
|
1816
|
+
}
|
|
1817
|
+
return textResult(`Streamed ${text.length} chars`, { length: text.length });
|
|
1818
|
+
},
|
|
1819
|
+
true
|
|
1820
|
+
);
|
|
1821
|
+
reg(
|
|
1822
|
+
"code_replace_selection",
|
|
1823
|
+
"Replace the currently-selected text with the supplied text.",
|
|
1824
|
+
{ text: { type: "string" } },
|
|
1825
|
+
["text"],
|
|
1826
|
+
(args) => {
|
|
1827
|
+
if (!adapter.replaceSelection) return errorResult("Host did not provide replaceSelection.");
|
|
1828
|
+
adapter.replaceSelection(String(args.text ?? ""));
|
|
1829
|
+
return textResult("Selection replaced", {});
|
|
1830
|
+
},
|
|
1831
|
+
true
|
|
1832
|
+
);
|
|
1833
|
+
reg(
|
|
1834
|
+
"code_set_language",
|
|
1835
|
+
"Switch the active syntax highlighting / formatter.",
|
|
1836
|
+
{ language: { type: "string", description: "e.g. 'javascript', 'php', 'sql'." } },
|
|
1837
|
+
["language"],
|
|
1838
|
+
(args) => {
|
|
1839
|
+
if (!adapter.setLanguage) return errorResult("Host did not provide setLanguage.");
|
|
1840
|
+
const lang = String(args.language ?? "");
|
|
1841
|
+
adapter.setLanguage(lang);
|
|
1842
|
+
return textResult(`Language \u2192 ${lang}`, { language: lang });
|
|
1843
|
+
},
|
|
1844
|
+
true
|
|
1845
|
+
);
|
|
1846
|
+
reg(
|
|
1847
|
+
"code_focus",
|
|
1848
|
+
"Move browser focus to the editor.",
|
|
1849
|
+
{},
|
|
1850
|
+
[],
|
|
1851
|
+
() => {
|
|
1852
|
+
if (!adapter.focus) return errorResult("Host did not provide focus.");
|
|
1853
|
+
adapter.focus();
|
|
1854
|
+
return textResult("Focused", {});
|
|
1855
|
+
},
|
|
1856
|
+
true
|
|
1857
|
+
);
|
|
1858
|
+
return {
|
|
1859
|
+
id: `code:${adapter.id}`,
|
|
1860
|
+
title: adapter.title ?? adapter.id,
|
|
1861
|
+
dispose: () => {
|
|
1862
|
+
for (const d of disposers) d();
|
|
1863
|
+
}
|
|
1864
|
+
};
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
// src/bridges/charts.ts
|
|
1868
|
+
var DEFAULT_AGENT6 = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
1869
|
+
function registerChartsBridge(host, options) {
|
|
1870
|
+
const { adapter } = options;
|
|
1871
|
+
const agent = { ...DEFAULT_AGENT6, ...options.agent ?? {} };
|
|
1872
|
+
const disposers = [];
|
|
1873
|
+
const target = {
|
|
1874
|
+
kind: "chart",
|
|
1875
|
+
screenId: adapter.screenId,
|
|
1876
|
+
elementId: adapter.id,
|
|
1877
|
+
label: adapter.title ?? adapter.id
|
|
1878
|
+
};
|
|
1879
|
+
const reg = (name, description, properties, required, handler, isMutation) => {
|
|
1880
|
+
const wrapped = async (args) => {
|
|
1881
|
+
try {
|
|
1882
|
+
return await handler(args);
|
|
1883
|
+
} catch (e) {
|
|
1884
|
+
return errorResult(e instanceof Error ? e.message : String(e));
|
|
1885
|
+
}
|
|
1886
|
+
};
|
|
1887
|
+
const final = isMutation ? wrapToolWithActivity(wrapped, {
|
|
1888
|
+
toolName: name,
|
|
1889
|
+
agent,
|
|
1890
|
+
kind: "chart",
|
|
1891
|
+
screenId: adapter.screenId,
|
|
1892
|
+
resolveTarget: () => target
|
|
1893
|
+
}) : wrapped;
|
|
1894
|
+
disposers.push(
|
|
1895
|
+
host.registerTool(
|
|
1896
|
+
{ name, description, inputSchema: { type: "object", properties, required, additionalProperties: false } },
|
|
1897
|
+
final
|
|
1898
|
+
)
|
|
1899
|
+
);
|
|
1900
|
+
};
|
|
1901
|
+
reg(
|
|
1902
|
+
"chart_describe",
|
|
1903
|
+
`Describe the chart "${adapter.id}" \u2014 series count, type guesses, axis info.`,
|
|
1904
|
+
{},
|
|
1905
|
+
[],
|
|
1906
|
+
() => {
|
|
1907
|
+
const opt = adapter.getOption();
|
|
1908
|
+
const series = Array.isArray(opt.series) ? opt.series : [];
|
|
1909
|
+
const summary = {
|
|
1910
|
+
id: adapter.id,
|
|
1911
|
+
title: adapter.title,
|
|
1912
|
+
seriesCount: series.length,
|
|
1913
|
+
seriesTypes: series.map((s) => s?.type ?? "unknown"),
|
|
1914
|
+
hasXAxis: !!opt.xAxis,
|
|
1915
|
+
hasYAxis: !!opt.yAxis
|
|
1916
|
+
};
|
|
1917
|
+
return textResult(JSON.stringify(summary), summary);
|
|
1918
|
+
},
|
|
1919
|
+
false
|
|
1920
|
+
);
|
|
1921
|
+
reg(
|
|
1922
|
+
"chart_get_option",
|
|
1923
|
+
"Read the full ECharts option object the chart is rendering.",
|
|
1924
|
+
{},
|
|
1925
|
+
[],
|
|
1926
|
+
() => {
|
|
1927
|
+
const opt = adapter.getOption();
|
|
1928
|
+
return textResult(JSON.stringify(opt, null, 2), opt);
|
|
1929
|
+
},
|
|
1930
|
+
false
|
|
1931
|
+
);
|
|
1932
|
+
reg(
|
|
1933
|
+
"chart_set_option",
|
|
1934
|
+
"Replace the entire ECharts option. Use chart_update_option for partial updates.",
|
|
1935
|
+
{ option: { type: "object" } },
|
|
1936
|
+
["option"],
|
|
1937
|
+
(args) => {
|
|
1938
|
+
const opt = args.option && typeof args.option === "object" ? args.option : {};
|
|
1939
|
+
adapter.setOption(opt);
|
|
1940
|
+
return textResult("Replaced chart option", {});
|
|
1941
|
+
},
|
|
1942
|
+
true
|
|
1943
|
+
);
|
|
1944
|
+
reg(
|
|
1945
|
+
"chart_update_option",
|
|
1946
|
+
"Shallow-merge a partial option update \u2014 only the keys you provide change.",
|
|
1947
|
+
{ partial: { type: "object" } },
|
|
1948
|
+
["partial"],
|
|
1949
|
+
(args) => {
|
|
1950
|
+
const partial = args.partial && typeof args.partial === "object" ? args.partial : {};
|
|
1951
|
+
if (adapter.updateOption) {
|
|
1952
|
+
adapter.updateOption(partial);
|
|
1953
|
+
} else {
|
|
1954
|
+
adapter.setOption({ ...adapter.getOption(), ...partial });
|
|
1955
|
+
}
|
|
1956
|
+
return textResult("Merged chart option", { keys: Object.keys(partial) });
|
|
1957
|
+
},
|
|
1958
|
+
true
|
|
1959
|
+
);
|
|
1960
|
+
reg(
|
|
1961
|
+
"chart_update_data",
|
|
1962
|
+
"Update only the data (typically the `series` field). Leaves layout / axes / colors alone.",
|
|
1963
|
+
{ data: { description: "New series array (or whatever shape the host's adapter expects)." } },
|
|
1964
|
+
["data"],
|
|
1965
|
+
(args) => {
|
|
1966
|
+
if (!adapter.updateData) return errorResult("Host did not provide updateData.");
|
|
1967
|
+
adapter.updateData(args.data);
|
|
1968
|
+
return textResult("Updated chart data", {});
|
|
1969
|
+
},
|
|
1970
|
+
true
|
|
1971
|
+
);
|
|
1972
|
+
return {
|
|
1973
|
+
id: `chart:${adapter.id}`,
|
|
1974
|
+
title: adapter.title ?? adapter.id,
|
|
1975
|
+
dispose: () => {
|
|
1976
|
+
for (const d of disposers) d();
|
|
1977
|
+
}
|
|
1978
|
+
};
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
// src/bridges/scene.ts
|
|
1982
|
+
var DEFAULT_AGENT7 = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
1983
|
+
function registerSceneBridge(host, options) {
|
|
1984
|
+
const { adapter } = options;
|
|
1985
|
+
const agent = { ...DEFAULT_AGENT7, ...options.agent ?? {} };
|
|
1986
|
+
const disposers = [];
|
|
1987
|
+
const target = (objectId) => ({
|
|
1988
|
+
kind: "scene",
|
|
1989
|
+
screenId: adapter.screenId,
|
|
1990
|
+
elementId: objectId ? `${adapter.id}:${objectId}` : adapter.id,
|
|
1991
|
+
label: objectId ? `${adapter.title ?? adapter.id} \u2192 ${objectId}` : adapter.title ?? adapter.id
|
|
1992
|
+
});
|
|
1993
|
+
const reg = (name, description, properties, required, handler, isMutation, objectIdFromArgs) => {
|
|
1994
|
+
const wrapped = async (args) => {
|
|
1995
|
+
try {
|
|
1996
|
+
return await handler(args);
|
|
1997
|
+
} catch (e) {
|
|
1998
|
+
return errorResult(e instanceof Error ? e.message : String(e));
|
|
1999
|
+
}
|
|
2000
|
+
};
|
|
2001
|
+
const final = isMutation ? wrapToolWithActivity(wrapped, {
|
|
2002
|
+
toolName: name,
|
|
2003
|
+
agent,
|
|
2004
|
+
kind: "scene",
|
|
2005
|
+
screenId: adapter.screenId,
|
|
2006
|
+
resolveTarget: ({ args }) => target(objectIdFromArgs?.(args))
|
|
2007
|
+
}) : wrapped;
|
|
2008
|
+
disposers.push(
|
|
2009
|
+
host.registerTool(
|
|
2010
|
+
{ name, description, inputSchema: { type: "object", properties, required, additionalProperties: false } },
|
|
2011
|
+
final
|
|
2012
|
+
)
|
|
2013
|
+
);
|
|
2014
|
+
};
|
|
2015
|
+
const newId3 = (kind) => `${kind}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
|
|
2016
|
+
reg(
|
|
2017
|
+
"scene_describe",
|
|
2018
|
+
"Describe the scene \u2014 object count, kinds, camera position.",
|
|
2019
|
+
{},
|
|
2020
|
+
[],
|
|
2021
|
+
() => {
|
|
2022
|
+
const scene = adapter.getScene();
|
|
2023
|
+
const summary = {
|
|
2024
|
+
id: adapter.id,
|
|
2025
|
+
objectCount: scene.objects.length,
|
|
2026
|
+
kinds: scene.objects.map((o) => o.kind),
|
|
2027
|
+
camera: scene.camera,
|
|
2028
|
+
background: scene.background
|
|
2029
|
+
};
|
|
2030
|
+
return textResult(JSON.stringify(summary, null, 2), summary);
|
|
2031
|
+
},
|
|
2032
|
+
false
|
|
2033
|
+
);
|
|
2034
|
+
reg(
|
|
2035
|
+
"scene_get_state",
|
|
2036
|
+
"Read the full SceneState (objects + camera + background).",
|
|
2037
|
+
{},
|
|
2038
|
+
[],
|
|
2039
|
+
() => {
|
|
2040
|
+
const scene = adapter.getScene();
|
|
2041
|
+
return textResult(JSON.stringify(scene, null, 2), scene);
|
|
2042
|
+
},
|
|
2043
|
+
false
|
|
2044
|
+
);
|
|
2045
|
+
reg(
|
|
2046
|
+
"scene_add_object",
|
|
2047
|
+
"Add an object to the scene root. Returns the new object's id.",
|
|
2048
|
+
{
|
|
2049
|
+
kind: { type: "string", description: "box | sphere | cylinder | plane | screen | group | custom kind" },
|
|
2050
|
+
position: { type: "array", description: "[x, y, z]" },
|
|
2051
|
+
rotation: { type: "array", description: "[x, y, z] euler" },
|
|
2052
|
+
scale: { type: "array", description: "[x, y, z]" },
|
|
2053
|
+
color: { type: "string" },
|
|
2054
|
+
props: { type: "object", description: "Per-kind config." }
|
|
2055
|
+
},
|
|
2056
|
+
["kind"],
|
|
2057
|
+
(args) => {
|
|
2058
|
+
const obj = {
|
|
2059
|
+
id: newId3(String(args.kind)),
|
|
2060
|
+
kind: String(args.kind),
|
|
2061
|
+
position: parseTriple(args.position),
|
|
2062
|
+
rotation: parseTriple(args.rotation),
|
|
2063
|
+
scale: parseTriple(args.scale),
|
|
2064
|
+
color: typeof args.color === "string" ? args.color : void 0,
|
|
2065
|
+
props: args.props && typeof args.props === "object" ? args.props : void 0
|
|
2066
|
+
};
|
|
2067
|
+
const scene = adapter.getScene();
|
|
2068
|
+
adapter.setScene({ ...scene, objects: [...scene.objects, obj] });
|
|
2069
|
+
return textResult(`Added ${obj.kind} ${obj.id}`, obj);
|
|
2070
|
+
},
|
|
2071
|
+
true,
|
|
2072
|
+
(args) => void 0
|
|
2073
|
+
// id resolved from result.structuredContent.id
|
|
2074
|
+
);
|
|
2075
|
+
reg(
|
|
2076
|
+
"scene_update_object",
|
|
2077
|
+
"Update fields on an object. Only provided fields change.",
|
|
2078
|
+
{
|
|
2079
|
+
id: { type: "string" },
|
|
2080
|
+
position: { type: "array" },
|
|
2081
|
+
rotation: { type: "array" },
|
|
2082
|
+
scale: { type: "array" },
|
|
2083
|
+
color: { type: "string" },
|
|
2084
|
+
props: { type: "object" }
|
|
2085
|
+
},
|
|
2086
|
+
["id"],
|
|
2087
|
+
(args) => {
|
|
2088
|
+
const id = String(args.id);
|
|
2089
|
+
const scene = adapter.getScene();
|
|
2090
|
+
const idx = scene.objects.findIndex((o) => o.id === id);
|
|
2091
|
+
if (idx === -1) return errorResult(`No object ${id}`);
|
|
2092
|
+
const orig = scene.objects[idx];
|
|
2093
|
+
const next = {
|
|
2094
|
+
...orig,
|
|
2095
|
+
...args.position !== void 0 ? { position: parseTriple(args.position) } : {},
|
|
2096
|
+
...args.rotation !== void 0 ? { rotation: parseTriple(args.rotation) } : {},
|
|
2097
|
+
...args.scale !== void 0 ? { scale: parseTriple(args.scale) } : {},
|
|
2098
|
+
...args.color !== void 0 ? { color: String(args.color) } : {},
|
|
2099
|
+
...args.props && typeof args.props === "object" ? { props: { ...orig.props ?? {}, ...args.props } } : {}
|
|
2100
|
+
};
|
|
2101
|
+
const objects = [...scene.objects];
|
|
2102
|
+
objects[idx] = next;
|
|
2103
|
+
adapter.setScene({ ...scene, objects });
|
|
2104
|
+
return textResult(`Updated ${id}`, next);
|
|
2105
|
+
},
|
|
2106
|
+
true,
|
|
2107
|
+
(args) => String(args.id ?? "")
|
|
2108
|
+
);
|
|
2109
|
+
reg(
|
|
2110
|
+
"scene_delete_object",
|
|
2111
|
+
"Remove an object from the scene root.",
|
|
2112
|
+
{ id: { type: "string" } },
|
|
2113
|
+
["id"],
|
|
2114
|
+
(args) => {
|
|
2115
|
+
const id = String(args.id);
|
|
2116
|
+
const scene = adapter.getScene();
|
|
2117
|
+
const next = scene.objects.filter((o) => o.id !== id);
|
|
2118
|
+
if (next.length === scene.objects.length) return errorResult(`No object ${id}`);
|
|
2119
|
+
adapter.setScene({ ...scene, objects: next });
|
|
2120
|
+
return textResult(`Deleted ${id}`);
|
|
2121
|
+
},
|
|
2122
|
+
true,
|
|
2123
|
+
(args) => String(args.id ?? "")
|
|
2124
|
+
);
|
|
2125
|
+
reg(
|
|
2126
|
+
"scene_set_camera",
|
|
2127
|
+
"Move the camera. Pass any subset of position/target/fov.",
|
|
2128
|
+
{
|
|
2129
|
+
position: { type: "array", description: "[x, y, z]" },
|
|
2130
|
+
target: { type: "array", description: "[x, y, z] look-at point" },
|
|
2131
|
+
fov: { type: "number" }
|
|
2132
|
+
},
|
|
2133
|
+
[],
|
|
2134
|
+
(args) => {
|
|
2135
|
+
const scene = adapter.getScene();
|
|
2136
|
+
const next = {
|
|
2137
|
+
...scene.camera ?? {},
|
|
2138
|
+
...args.position !== void 0 ? { position: parseTriple(args.position) } : {},
|
|
2139
|
+
...args.target !== void 0 ? { target: parseTriple(args.target) } : {},
|
|
2140
|
+
...args.fov !== void 0 ? { fov: Number(args.fov) } : {}
|
|
2141
|
+
};
|
|
2142
|
+
if (adapter.setCamera) {
|
|
2143
|
+
adapter.setCamera(next);
|
|
2144
|
+
} else {
|
|
2145
|
+
adapter.setScene({ ...scene, camera: next });
|
|
2146
|
+
}
|
|
2147
|
+
return textResult(`Camera updated`, next);
|
|
2148
|
+
},
|
|
2149
|
+
true
|
|
2150
|
+
);
|
|
2151
|
+
reg(
|
|
2152
|
+
"scene_set_background",
|
|
2153
|
+
"Change the scene background color (CSS color).",
|
|
2154
|
+
{ color: { type: "string" } },
|
|
2155
|
+
["color"],
|
|
2156
|
+
(args) => {
|
|
2157
|
+
const scene = adapter.getScene();
|
|
2158
|
+
adapter.setScene({ ...scene, background: String(args.color) });
|
|
2159
|
+
return textResult(`Background \u2192 ${args.color}`, { background: args.color });
|
|
2160
|
+
},
|
|
2161
|
+
true
|
|
2162
|
+
);
|
|
2163
|
+
return {
|
|
2164
|
+
id: `scene:${adapter.id}`,
|
|
2165
|
+
title: adapter.title ?? adapter.id,
|
|
2166
|
+
dispose: () => {
|
|
2167
|
+
for (const d of disposers) d();
|
|
2168
|
+
}
|
|
2169
|
+
};
|
|
2170
|
+
}
|
|
2171
|
+
function parseTriple(v) {
|
|
2172
|
+
if (!Array.isArray(v) || v.length !== 3) return void 0;
|
|
2173
|
+
const out = v.map((x) => Number(x));
|
|
2174
|
+
if (out.some((x) => !Number.isFinite(x))) return void 0;
|
|
2175
|
+
return out;
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
// src/bridges/screens.ts
|
|
2179
|
+
var DEFAULT_AGENT8 = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
2180
|
+
function registerScreensBridge(host, options) {
|
|
2181
|
+
const { adapter } = options;
|
|
2182
|
+
const agent = { ...DEFAULT_AGENT8, ...options.agent ?? {} };
|
|
2183
|
+
const disposers = [];
|
|
2184
|
+
const target = (screenId) => ({
|
|
2185
|
+
kind: "screens",
|
|
2186
|
+
screenId,
|
|
2187
|
+
label: `Screen ${screenId}`
|
|
2188
|
+
});
|
|
2189
|
+
const reg = (name, description, properties, required, handler, isMutation, targetFromArgs) => {
|
|
2190
|
+
const wrapped = async (args) => {
|
|
2191
|
+
try {
|
|
2192
|
+
return await handler(args);
|
|
2193
|
+
} catch (e) {
|
|
2194
|
+
return errorResult(e instanceof Error ? e.message : String(e));
|
|
2195
|
+
}
|
|
2196
|
+
};
|
|
2197
|
+
const final = isMutation ? wrapToolWithActivity(wrapped, {
|
|
2198
|
+
toolName: name,
|
|
2199
|
+
agent,
|
|
2200
|
+
kind: "screens",
|
|
2201
|
+
resolveTarget: ({ args }) => targetFromArgs?.(args) ?? null
|
|
2202
|
+
}) : wrapped;
|
|
2203
|
+
disposers.push(
|
|
2204
|
+
host.registerTool(
|
|
2205
|
+
{ name, description, inputSchema: { type: "object", properties, required, additionalProperties: false } },
|
|
2206
|
+
final
|
|
2207
|
+
)
|
|
2208
|
+
);
|
|
2209
|
+
};
|
|
2210
|
+
reg(
|
|
2211
|
+
"screens_list",
|
|
2212
|
+
"List every screen the host has registered. Returns id, title, active flag, and optional kind.",
|
|
2213
|
+
{},
|
|
2214
|
+
[],
|
|
2215
|
+
() => {
|
|
2216
|
+
const screens = adapter.listScreens();
|
|
2217
|
+
const text = screens.map((s) => `${s.active ? "\u25B8" : " "} ${s.id}${s.title ? ` \u2014 ${s.title}` : ""}${s.kind ? ` [${s.kind}]` : ""}`).join("\n");
|
|
2218
|
+
return textResult(text || "(no screens)", { screens, active: adapter.getActive() });
|
|
2219
|
+
},
|
|
2220
|
+
false
|
|
2221
|
+
);
|
|
2222
|
+
reg(
|
|
2223
|
+
"screens_describe_active",
|
|
2224
|
+
"Get the currently-active screen id (or null).",
|
|
2225
|
+
{},
|
|
2226
|
+
[],
|
|
2227
|
+
() => {
|
|
2228
|
+
const active = adapter.getActive();
|
|
2229
|
+
return textResult(active ?? "(none)", { active });
|
|
2230
|
+
},
|
|
2231
|
+
false
|
|
2232
|
+
);
|
|
2233
|
+
reg(
|
|
2234
|
+
"screens_list_kinds",
|
|
2235
|
+
"List the screen kinds (templates) the host knows how to instantiate. Use this before screens_create to know what's available.",
|
|
2236
|
+
{},
|
|
2237
|
+
[],
|
|
2238
|
+
() => {
|
|
2239
|
+
if (!adapter.listKinds) return errorResult("Host did not register a kind catalog. Cannot create screens dynamically.");
|
|
2240
|
+
const kinds = adapter.listKinds();
|
|
2241
|
+
const text = kinds.map((k) => `${k.kind}${k.label ? ` \u2014 ${k.label}` : ""}${k.description ? ` (${k.description})` : ""}`).join("\n");
|
|
2242
|
+
return textResult(text || "(no kinds registered)", kinds);
|
|
2243
|
+
},
|
|
2244
|
+
false
|
|
2245
|
+
);
|
|
2246
|
+
reg(
|
|
2247
|
+
"screens_create",
|
|
2248
|
+
"Instantiate a new screen from a template kind + config. Switches the active view to the new screen.",
|
|
2249
|
+
{
|
|
2250
|
+
id: { type: "string", description: "Stable screen id. Must be unique." },
|
|
2251
|
+
title: { type: "string" },
|
|
2252
|
+
kind: { type: "string", description: "Template kind \u2014 call screens_list_kinds for the catalog." },
|
|
2253
|
+
config: { type: "object", description: "Template-specific config (e.g. { fields: [...] } for a form)." }
|
|
2254
|
+
},
|
|
2255
|
+
["id", "kind"],
|
|
2256
|
+
(args) => {
|
|
2257
|
+
if (!adapter.createScreen) return errorResult("Host did not provide createScreen.");
|
|
2258
|
+
const id = String(args.id);
|
|
2259
|
+
const kind = String(args.kind);
|
|
2260
|
+
if (adapter.listScreens().find((s) => s.id === id)) {
|
|
2261
|
+
return errorResult(`Screen ${id} already exists. Use screens_destroy first or pick a fresh id.`);
|
|
2262
|
+
}
|
|
2263
|
+
adapter.createScreen({
|
|
2264
|
+
id,
|
|
2265
|
+
title: typeof args.title === "string" ? args.title : void 0,
|
|
2266
|
+
kind,
|
|
2267
|
+
config: args.config && typeof args.config === "object" ? args.config : void 0
|
|
2268
|
+
});
|
|
2269
|
+
adapter.setActive(id);
|
|
2270
|
+
return textResult(`Created ${kind} screen "${id}"`, { id, kind });
|
|
2271
|
+
},
|
|
2272
|
+
true,
|
|
2273
|
+
(args) => target(String(args.id ?? ""))
|
|
2274
|
+
);
|
|
2275
|
+
reg(
|
|
2276
|
+
"screens_destroy",
|
|
2277
|
+
"Remove a previously-created screen. Active screen falls back to the first remaining one (or null).",
|
|
2278
|
+
{ id: { type: "string" } },
|
|
2279
|
+
["id"],
|
|
2280
|
+
(args) => {
|
|
2281
|
+
if (!adapter.destroyScreen) return errorResult("Host did not provide destroyScreen.");
|
|
2282
|
+
const id = String(args.id);
|
|
2283
|
+
if (!adapter.listScreens().find((s) => s.id === id)) {
|
|
2284
|
+
return errorResult(`No screen with id ${id}`);
|
|
2285
|
+
}
|
|
2286
|
+
adapter.destroyScreen(id);
|
|
2287
|
+
return textResult(`Destroyed screen ${id}`, { id });
|
|
2288
|
+
},
|
|
2289
|
+
true,
|
|
2290
|
+
(args) => target(String(args.id ?? ""))
|
|
2291
|
+
);
|
|
2292
|
+
reg(
|
|
2293
|
+
"screens_set_layout",
|
|
2294
|
+
"Change the layout of an existing composite screen. Layouts: 'single', 'split-h' (left/right), 'split-v' (top/bottom), 'grid-2x2', 'stack' (tabs).",
|
|
2295
|
+
{
|
|
2296
|
+
id: { type: "string" },
|
|
2297
|
+
layout: { type: "string", enum: ["single", "split-h", "split-v", "grid-2x2", "stack"] }
|
|
2298
|
+
},
|
|
2299
|
+
["id", "layout"],
|
|
2300
|
+
(args) => {
|
|
2301
|
+
if (!adapter.updateScreenContent) return errorResult("Host did not provide updateScreenContent.");
|
|
2302
|
+
adapter.updateScreenContent(String(args.id), { layout: String(args.layout) });
|
|
2303
|
+
return textResult(`Layout of ${args.id} \u2192 ${args.layout}`, { id: args.id, layout: args.layout });
|
|
2304
|
+
},
|
|
2305
|
+
true,
|
|
2306
|
+
(args) => target(String(args.id ?? ""))
|
|
2307
|
+
);
|
|
2308
|
+
reg(
|
|
2309
|
+
"screens_update_content",
|
|
2310
|
+
"Merge new config into an existing screen (e.g. add a field to a form, append a sheet column, change chart series).",
|
|
2311
|
+
{
|
|
2312
|
+
id: { type: "string" },
|
|
2313
|
+
partial: { type: "object", description: "Shallow-merged into the screen's config." }
|
|
2314
|
+
},
|
|
2315
|
+
["id", "partial"],
|
|
2316
|
+
(args) => {
|
|
2317
|
+
if (!adapter.updateScreenContent) return errorResult("Host did not provide updateScreenContent.");
|
|
2318
|
+
const id = String(args.id);
|
|
2319
|
+
const partial = args.partial && typeof args.partial === "object" ? args.partial : {};
|
|
2320
|
+
adapter.updateScreenContent(id, partial);
|
|
2321
|
+
return textResult(`Updated content of ${id}`, { id });
|
|
2322
|
+
},
|
|
2323
|
+
true,
|
|
2324
|
+
(args) => target(String(args.id ?? ""))
|
|
2325
|
+
);
|
|
2326
|
+
reg(
|
|
2327
|
+
"screens_navigate",
|
|
2328
|
+
"Switch the human's view to a different screen. The host updates its router / tab state and re-renders.",
|
|
2329
|
+
{ screen: { type: "string", description: "Screen id to activate." } },
|
|
2330
|
+
["screen"],
|
|
2331
|
+
(args) => {
|
|
2332
|
+
const screenId = String(args.screen ?? "");
|
|
2333
|
+
const screens = adapter.listScreens();
|
|
2334
|
+
if (!screens.find((s) => s.id === screenId)) {
|
|
2335
|
+
return errorResult(`No screen registered with id "${screenId}". Call screens_list first.`);
|
|
2336
|
+
}
|
|
2337
|
+
adapter.setActive(screenId);
|
|
2338
|
+
return textResult(`Navigated to ${screenId}`, { screen: screenId });
|
|
2339
|
+
},
|
|
2340
|
+
true,
|
|
2341
|
+
(args) => target(String(args.screen ?? ""))
|
|
2342
|
+
);
|
|
2343
|
+
return {
|
|
2344
|
+
id: "screens",
|
|
2345
|
+
title: "Screens",
|
|
2346
|
+
dispose: () => {
|
|
2347
|
+
for (const d of disposers) d();
|
|
2348
|
+
}
|
|
2349
|
+
};
|
|
2350
|
+
}
|
|
2351
|
+
function AgentPanel({ agent, activity, onSubmit, busy, actions, className, style }) {
|
|
2352
|
+
const scrollRef = react.useRef(null);
|
|
2353
|
+
const inputRef = react.useRef(null);
|
|
2354
|
+
react.useEffect(() => {
|
|
2355
|
+
const el = scrollRef.current;
|
|
2356
|
+
if (!el) return;
|
|
2357
|
+
el.scrollTop = el.scrollHeight;
|
|
2358
|
+
}, [activity.length]);
|
|
2359
|
+
const handleSubmit = (e) => {
|
|
2360
|
+
e.preventDefault();
|
|
2361
|
+
const value = inputRef.current?.value.trim();
|
|
2362
|
+
if (!value || !onSubmit) return;
|
|
2363
|
+
onSubmit(value);
|
|
2364
|
+
if (inputRef.current) inputRef.current.value = "";
|
|
2365
|
+
};
|
|
2366
|
+
const color = agent?.color ?? "#a855f7";
|
|
2367
|
+
const name = agent?.name ?? "Agent";
|
|
2368
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ["fai-panel", className ?? ""].filter(Boolean).join(" "), style, children: [
|
|
2369
|
+
/* @__PURE__ */ jsxRuntime.jsxs("header", { className: "fai-panel__header", children: [
|
|
2370
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2371
|
+
"div",
|
|
2372
|
+
{
|
|
2373
|
+
className: "fai-panel__avatar",
|
|
2374
|
+
style: { background: color },
|
|
2375
|
+
"aria-hidden": true,
|
|
2376
|
+
children: name.slice(0, 1)
|
|
2377
|
+
}
|
|
2378
|
+
),
|
|
2379
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fai-panel__title", children: [
|
|
2380
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: name }),
|
|
2381
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "fai-panel__subtitle", children: busy ? "Working\u2026" : `${activity.length} event${activity.length === 1 ? "" : "s"}` })
|
|
2382
|
+
] }),
|
|
2383
|
+
actions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fai-panel__actions", children: actions })
|
|
2384
|
+
] }),
|
|
2385
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { ref: scrollRef, className: "fai-panel__stream", children: activity.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "fai-panel__empty", children: "No activity yet." }) : activity.map((a) => /* @__PURE__ */ jsxRuntime.jsx(ActivityRow, { item: a }, a.id)) }),
|
|
2386
|
+
onSubmit && /* @__PURE__ */ jsxRuntime.jsxs("form", { className: "fai-panel__composer", onSubmit: handleSubmit, children: [
|
|
2387
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2388
|
+
"textarea",
|
|
2389
|
+
{
|
|
2390
|
+
ref: inputRef,
|
|
2391
|
+
className: "fai-panel__input",
|
|
2392
|
+
placeholder: busy ? "Working\u2026" : "Ask the agent\u2026",
|
|
2393
|
+
disabled: busy,
|
|
2394
|
+
rows: 2,
|
|
2395
|
+
onKeyDown: (e) => {
|
|
2396
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
2397
|
+
e.preventDefault();
|
|
2398
|
+
handleSubmit(e);
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
),
|
|
2403
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", className: "fai-panel__send", disabled: busy, children: "Send" })
|
|
2404
|
+
] })
|
|
2405
|
+
] });
|
|
2406
|
+
}
|
|
2407
|
+
function ActivityRow({ item }) {
|
|
2408
|
+
const time = formatTime(item.at);
|
|
2409
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `fai-row fai-row--${item.kind}`, children: [
|
|
962
2410
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fai-row__meta", children: [
|
|
963
2411
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "fai-row__source", children: item.source }),
|
|
964
2412
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "fai-row__time", children: time })
|
|
@@ -1063,6 +2511,101 @@ function AgentActivityHighlight({
|
|
|
1063
2511
|
}
|
|
1064
2512
|
);
|
|
1065
2513
|
}
|
|
2514
|
+
function BridgedForm({
|
|
2515
|
+
id,
|
|
2516
|
+
title,
|
|
2517
|
+
screenId,
|
|
2518
|
+
fields,
|
|
2519
|
+
values,
|
|
2520
|
+
onChange,
|
|
2521
|
+
onSubmit,
|
|
2522
|
+
server,
|
|
2523
|
+
agent,
|
|
2524
|
+
children
|
|
2525
|
+
}) {
|
|
2526
|
+
const valuesRef = react.useRef(values);
|
|
2527
|
+
const onChangeRef = react.useRef(onChange);
|
|
2528
|
+
const fieldsRef = react.useRef(fields);
|
|
2529
|
+
const submitRef = react.useRef(onSubmit);
|
|
2530
|
+
react.useEffect(() => {
|
|
2531
|
+
valuesRef.current = values;
|
|
2532
|
+
}, [values]);
|
|
2533
|
+
react.useEffect(() => {
|
|
2534
|
+
onChangeRef.current = onChange;
|
|
2535
|
+
}, [onChange]);
|
|
2536
|
+
react.useEffect(() => {
|
|
2537
|
+
fieldsRef.current = fields;
|
|
2538
|
+
}, [fields]);
|
|
2539
|
+
react.useEffect(() => {
|
|
2540
|
+
submitRef.current = onSubmit;
|
|
2541
|
+
}, [onSubmit]);
|
|
2542
|
+
const focusElement = (name) => {
|
|
2543
|
+
if (typeof document === "undefined") return;
|
|
2544
|
+
const el = document.querySelector(`[data-form-id="${id}"] [name="${name}"]`);
|
|
2545
|
+
el?.focus();
|
|
2546
|
+
};
|
|
2547
|
+
const adapter = react.useMemo(() => ({
|
|
2548
|
+
id,
|
|
2549
|
+
title,
|
|
2550
|
+
screenId,
|
|
2551
|
+
getFields: () => fieldsRef.current,
|
|
2552
|
+
getValue: (name) => valuesRef.current[name],
|
|
2553
|
+
getValues: () => ({ ...valuesRef.current }),
|
|
2554
|
+
setValue: (name, v) => onChangeRef.current({ ...valuesRef.current, [name]: v }),
|
|
2555
|
+
setValues: (next) => onChangeRef.current({ ...valuesRef.current, ...next }),
|
|
2556
|
+
focus: focusElement,
|
|
2557
|
+
submit: async () => {
|
|
2558
|
+
if (!submitRef.current) {
|
|
2559
|
+
return { ok: true, values: { ...valuesRef.current } };
|
|
2560
|
+
}
|
|
2561
|
+
return submitRef.current();
|
|
2562
|
+
}
|
|
2563
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2564
|
+
}), [id, title, screenId]);
|
|
2565
|
+
react.useEffect(() => {
|
|
2566
|
+
if (!server) return;
|
|
2567
|
+
const bridge = registerFormBridge(server, { adapter, agent });
|
|
2568
|
+
return () => bridge.dispose();
|
|
2569
|
+
}, [server, adapter, agent]);
|
|
2570
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-form-id": id, children });
|
|
2571
|
+
}
|
|
2572
|
+
|
|
2573
|
+
// src/components/ScreensActivityBridge/ScreensActivityBridge.tsx
|
|
2574
|
+
init_registry();
|
|
2575
|
+
function ScreensActivityBridge({ system, fadeMs = 1500 }) {
|
|
2576
|
+
react.useEffect(() => {
|
|
2577
|
+
const fadeTimers = /* @__PURE__ */ new Map();
|
|
2578
|
+
const off = onActivity((event) => {
|
|
2579
|
+
const screenId = event.target.screenId;
|
|
2580
|
+
if (!screenId) return;
|
|
2581
|
+
if (!system.registry.has(screenId)) return;
|
|
2582
|
+
const activity = {
|
|
2583
|
+
agentId: event.agentId,
|
|
2584
|
+
agentName: event.agentName,
|
|
2585
|
+
agentColor: event.agentColor,
|
|
2586
|
+
action: event.action,
|
|
2587
|
+
timestamp: event.timestamp,
|
|
2588
|
+
elementId: event.target.elementId,
|
|
2589
|
+
label: event.target.label
|
|
2590
|
+
};
|
|
2591
|
+
system.updateScreen(screenId, { agentActivity: activity });
|
|
2592
|
+
const prev = fadeTimers.get(screenId);
|
|
2593
|
+
if (prev) clearTimeout(prev);
|
|
2594
|
+
fadeTimers.set(
|
|
2595
|
+
screenId,
|
|
2596
|
+
setTimeout(() => {
|
|
2597
|
+
system.updateScreen(screenId, { agentActivity: null });
|
|
2598
|
+
fadeTimers.delete(screenId);
|
|
2599
|
+
}, event.ttlMs ?? fadeMs)
|
|
2600
|
+
);
|
|
2601
|
+
});
|
|
2602
|
+
return () => {
|
|
2603
|
+
off();
|
|
2604
|
+
for (const t of fadeTimers.values()) clearTimeout(t);
|
|
2605
|
+
};
|
|
2606
|
+
}, [system, fadeMs]);
|
|
2607
|
+
return null;
|
|
2608
|
+
}
|
|
1066
2609
|
|
|
1067
2610
|
// src/sharing/token.ts
|
|
1068
2611
|
var TOKEN_BYTES = 24;
|
|
@@ -1333,16 +2876,31 @@ function attachSseRelay(server, options) {
|
|
|
1333
2876
|
transport.bindServer(server);
|
|
1334
2877
|
server.attach(transport);
|
|
1335
2878
|
transport.start();
|
|
2879
|
+
Promise.resolve().then(() => (init_registry(), registry_exports)).then(({ onActivity: onActivity2 }) => {
|
|
2880
|
+
const off = onActivity2((event) => {
|
|
2881
|
+
transport.send({
|
|
2882
|
+
jsonrpc: "2.0",
|
|
2883
|
+
method: "notifications/agent_activity",
|
|
2884
|
+
params: event
|
|
2885
|
+
});
|
|
2886
|
+
});
|
|
2887
|
+
const origClose = transport.close.bind(transport);
|
|
2888
|
+
transport.close = () => {
|
|
2889
|
+
off();
|
|
2890
|
+
origClose();
|
|
2891
|
+
};
|
|
2892
|
+
}).catch(() => {
|
|
2893
|
+
});
|
|
1336
2894
|
return transport;
|
|
1337
2895
|
}
|
|
1338
|
-
var
|
|
2896
|
+
var DEFAULT_AGENT9 = { id: "agent", name: "Agent", color: "#a855f7" };
|
|
1339
2897
|
function SharedWhiteboard({
|
|
1340
2898
|
initialNotes = [],
|
|
1341
2899
|
initialShapes = [],
|
|
1342
2900
|
initialConnectors = [],
|
|
1343
2901
|
initialStrokes = [],
|
|
1344
2902
|
initialViewport = { x: 0, y: 0, zoom: 1 },
|
|
1345
|
-
agent =
|
|
2903
|
+
agent = DEFAULT_AGENT9,
|
|
1346
2904
|
shareBaseUrl = "/whiteboard-share",
|
|
1347
2905
|
onRegisterSession,
|
|
1348
2906
|
showAgentPanel = true,
|
|
@@ -1605,28 +3163,109 @@ function resolveCenter(ref, notes, shapes) {
|
|
|
1605
3163
|
return ref;
|
|
1606
3164
|
}
|
|
1607
3165
|
|
|
3166
|
+
// src/presence/index.ts
|
|
3167
|
+
init_registry();
|
|
3168
|
+
|
|
3169
|
+
// src/presence/use-agent-activity.ts
|
|
3170
|
+
init_registry();
|
|
3171
|
+
function useAgentActivity(filter, options = {}) {
|
|
3172
|
+
const cap = options.capacity ?? 50;
|
|
3173
|
+
const [events, setEvents] = react.useState(() => readActivityHistory(filter).slice(-cap));
|
|
3174
|
+
react.useEffect(() => {
|
|
3175
|
+
setEvents(readActivityHistory(filter).slice(-cap));
|
|
3176
|
+
return onActivity((event) => {
|
|
3177
|
+
setEvents((prev) => {
|
|
3178
|
+
const next = prev.length >= cap ? prev.slice(prev.length - cap + 1) : prev.slice();
|
|
3179
|
+
next.push(event);
|
|
3180
|
+
return next;
|
|
3181
|
+
});
|
|
3182
|
+
}, filter);
|
|
3183
|
+
}, [filter?.agentId, filter?.screenId, filter?.kind, cap]);
|
|
3184
|
+
return { events, latest: events.length > 0 ? events[events.length - 1] : null };
|
|
3185
|
+
}
|
|
3186
|
+
function useAgentActivityForScreen(screenId, options = {}) {
|
|
3187
|
+
const { events, latest } = useAgentActivity({ screenId }, options);
|
|
3188
|
+
const fadeAfter = latest?.ttlMs ?? 1500;
|
|
3189
|
+
const [isAgentActive, setActive] = react.useState(false);
|
|
3190
|
+
react.useEffect(() => {
|
|
3191
|
+
if (!latest) {
|
|
3192
|
+
setActive(false);
|
|
3193
|
+
return;
|
|
3194
|
+
}
|
|
3195
|
+
setActive(true);
|
|
3196
|
+
const timer = setTimeout(() => setActive(false), fadeAfter);
|
|
3197
|
+
return () => clearTimeout(timer);
|
|
3198
|
+
}, [latest, fadeAfter]);
|
|
3199
|
+
return { events, latest, isAgentActive };
|
|
3200
|
+
}
|
|
3201
|
+
function useUndoStack(agentId, intervalMs = 500) {
|
|
3202
|
+
const [history2, setHistory] = react.useState(() => readHistory(agentId));
|
|
3203
|
+
react.useEffect(() => {
|
|
3204
|
+
let cancelled = false;
|
|
3205
|
+
const tick = () => {
|
|
3206
|
+
if (cancelled) return;
|
|
3207
|
+
setHistory(readHistory(agentId));
|
|
3208
|
+
};
|
|
3209
|
+
const id = setInterval(tick, intervalMs);
|
|
3210
|
+
tick();
|
|
3211
|
+
return () => {
|
|
3212
|
+
cancelled = true;
|
|
3213
|
+
clearInterval(id);
|
|
3214
|
+
};
|
|
3215
|
+
}, [agentId, intervalMs]);
|
|
3216
|
+
const refresh = react.useCallback(() => setHistory(readHistory(agentId)), [agentId]);
|
|
3217
|
+
return { history: history2, refresh };
|
|
3218
|
+
}
|
|
3219
|
+
|
|
1608
3220
|
exports.AgentActivityHighlight = AgentActivityHighlight;
|
|
1609
3221
|
exports.AgentCursor = AgentCursor;
|
|
1610
3222
|
exports.AgentPanel = AgentPanel;
|
|
3223
|
+
exports.BridgedForm = BridgedForm;
|
|
1611
3224
|
exports.InProcessTransport = InProcessTransport;
|
|
1612
3225
|
exports.MCP_PROTOCOL_VERSION = MCP_PROTOCOL_VERSION;
|
|
1613
3226
|
exports.MicroMcpServer = MicroMcpServer;
|
|
1614
3227
|
exports.RelayTransport = RelayTransport;
|
|
3228
|
+
exports.ScreensActivityBridge = ScreensActivityBridge;
|
|
1615
3229
|
exports.ShareControls = ShareControls;
|
|
1616
3230
|
exports.SharedWhiteboard = SharedWhiteboard;
|
|
1617
3231
|
exports.SseRelayTransport = SseRelayTransport;
|
|
3232
|
+
exports.ToolRegistry = ToolRegistry;
|
|
1618
3233
|
exports.attachInProcess = attachInProcess;
|
|
1619
3234
|
exports.attachRelay = attachRelay;
|
|
1620
3235
|
exports.attachSseRelay = attachSseRelay;
|
|
1621
3236
|
exports.buildShareConfig = buildShareConfig;
|
|
1622
3237
|
exports.buildShareUrl = buildShareUrl;
|
|
3238
|
+
exports.clearUndoStack = clearStack;
|
|
1623
3239
|
exports.createSessionDescriptor = createSessionDescriptor;
|
|
1624
3240
|
exports.describeSession = describeSession;
|
|
3241
|
+
exports.emitActivity = emitActivity;
|
|
3242
|
+
exports.ensureUndoToolsRegistered = ensureUndoToolsRegistered;
|
|
1625
3243
|
exports.errorResult = errorResult;
|
|
3244
|
+
exports.onActivity = onActivity;
|
|
3245
|
+
exports.pushUndoEntry = pushUndoEntry;
|
|
3246
|
+
exports.readActivityHistory = readActivityHistory;
|
|
1626
3247
|
exports.readSessionFromUrl = readSessionFromUrl;
|
|
3248
|
+
exports.readUndoHistory = readHistory;
|
|
3249
|
+
exports.redoOne = redoOne;
|
|
3250
|
+
exports.registerChartsBridge = registerChartsBridge;
|
|
3251
|
+
exports.registerCodeBridge = registerCodeBridge;
|
|
1627
3252
|
exports.registerFlowBridge = registerFlowBridge;
|
|
3253
|
+
exports.registerFormBridge = registerFormBridge;
|
|
3254
|
+
exports.registerSceneBridge = registerSceneBridge;
|
|
3255
|
+
exports.registerScreensBridge = registerScreensBridge;
|
|
3256
|
+
exports.registerSheetsBridge = registerSheetsBridge;
|
|
3257
|
+
exports.registerUndoTools = registerUndoTools;
|
|
1628
3258
|
exports.registerWhiteboardBridge = registerWhiteboardBridge;
|
|
3259
|
+
exports.resetActivityRegistry = resetActivityRegistry;
|
|
3260
|
+
exports.resetAllUndoStacks = resetAllUndoStacks;
|
|
1629
3261
|
exports.rpcError = rpcError;
|
|
1630
3262
|
exports.textResult = textResult;
|
|
3263
|
+
exports.undoOne = undoOne;
|
|
3264
|
+
exports.useAgentActivity = useAgentActivity;
|
|
3265
|
+
exports.useAgentActivityForScreen = useAgentActivityForScreen;
|
|
3266
|
+
exports.useSheetsActivityHighlights = useSheetsActivityHighlights;
|
|
3267
|
+
exports.useSheetsAdapter = useSheetsAdapter;
|
|
3268
|
+
exports.useUndoStack = useUndoStack;
|
|
3269
|
+
exports.wrapToolWithActivity = wrapToolWithActivity;
|
|
1631
3270
|
//# sourceMappingURL=index.cjs.map
|
|
1632
3271
|
//# sourceMappingURL=index.cjs.map
|