@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.
Files changed (132) hide show
  1. package/README.md +20 -0
  2. package/dist/bridges/charts.d.cts +39 -0
  3. package/dist/bridges/charts.d.ts +39 -0
  4. package/dist/bridges/code.d.cts +47 -0
  5. package/dist/bridges/code.d.ts +47 -0
  6. package/dist/bridges/flow.d.cts +4 -3
  7. package/dist/bridges/flow.d.ts +4 -3
  8. package/dist/bridges/forms.d.cts +76 -0
  9. package/dist/bridges/forms.d.ts +76 -0
  10. package/dist/bridges/scene.d.cts +54 -0
  11. package/dist/bridges/scene.d.ts +54 -0
  12. package/dist/bridges/screens.d.cts +78 -0
  13. package/dist/bridges/screens.d.ts +78 -0
  14. package/dist/bridges/sheets.d.cts +62 -0
  15. package/dist/bridges/sheets.d.ts +62 -0
  16. package/dist/bridges/whiteboard.d.cts +4 -3
  17. package/dist/bridges/whiteboard.d.ts +4 -3
  18. package/dist/bridges-charts.cjs +167 -0
  19. package/dist/bridges-charts.cjs.map +1 -0
  20. package/dist/bridges-charts.js +6 -0
  21. package/dist/bridges-charts.js.map +1 -0
  22. package/dist/bridges-code.cjs +219 -0
  23. package/dist/bridges-code.cjs.map +1 -0
  24. package/dist/bridges-code.js +6 -0
  25. package/dist/bridges-code.js.map +1 -0
  26. package/dist/bridges-flow.cjs +78 -19
  27. package/dist/bridges-flow.cjs.map +1 -1
  28. package/dist/bridges-flow.js +4 -2
  29. package/dist/bridges-forms.cjs +205 -0
  30. package/dist/bridges-forms.cjs.map +1 -0
  31. package/dist/bridges-forms.js +6 -0
  32. package/dist/bridges-forms.js.map +1 -0
  33. package/dist/bridges-scene.cjs +250 -0
  34. package/dist/bridges-scene.cjs.map +1 -0
  35. package/dist/bridges-scene.js +6 -0
  36. package/dist/bridges-scene.js.map +1 -0
  37. package/dist/bridges-screens.cjs +227 -0
  38. package/dist/bridges-screens.cjs.map +1 -0
  39. package/dist/bridges-screens.js +6 -0
  40. package/dist/bridges-screens.js.map +1 -0
  41. package/dist/bridges-sheets.cjs +327 -0
  42. package/dist/bridges-sheets.cjs.map +1 -0
  43. package/dist/bridges-sheets.js +6 -0
  44. package/dist/bridges-sheets.js.map +1 -0
  45. package/dist/bridges-whiteboard.cjs +226 -40
  46. package/dist/bridges-whiteboard.cjs.map +1 -1
  47. package/dist/bridges-whiteboard.js +5 -2
  48. package/dist/{chunk-5ZUHNNLR.js → chunk-3KSZNGNW.js} +81 -43
  49. package/dist/chunk-3KSZNGNW.js.map +1 -0
  50. package/dist/chunk-4BL5M3U3.js +158 -0
  51. package/dist/chunk-4BL5M3U3.js.map +1 -0
  52. package/dist/{chunk-QGCF7YKW.js → chunk-4KAIV6OD.js} +40 -12
  53. package/dist/chunk-4KAIV6OD.js.map +1 -0
  54. package/dist/chunk-52S7XYZK.js +38 -0
  55. package/dist/chunk-52S7XYZK.js.map +1 -0
  56. package/dist/chunk-57ZDHD53.js +180 -0
  57. package/dist/chunk-57ZDHD53.js.map +1 -0
  58. package/dist/chunk-E4AICMFZ.js +83 -0
  59. package/dist/chunk-E4AICMFZ.js.map +1 -0
  60. package/dist/chunk-GQ7XXK7G.js +124 -0
  61. package/dist/chunk-GQ7XXK7G.js.map +1 -0
  62. package/dist/chunk-HSTW7ZNO.js +172 -0
  63. package/dist/chunk-HSTW7ZNO.js.map +1 -0
  64. package/dist/chunk-IANI25IT.js +280 -0
  65. package/dist/chunk-IANI25IT.js.map +1 -0
  66. package/dist/{chunk-FLEOQUKF.js → chunk-JMYPUAFH.js} +17 -2
  67. package/dist/chunk-JMYPUAFH.js.map +1 -0
  68. package/dist/chunk-JU2N4KK6.js +34 -0
  69. package/dist/chunk-JU2N4KK6.js.map +1 -0
  70. package/dist/{chunk-2VOQJKSU.js → chunk-N3H4DXY5.js} +44 -22
  71. package/dist/chunk-N3H4DXY5.js.map +1 -0
  72. package/dist/chunk-NTDZWGYB.js +120 -0
  73. package/dist/chunk-NTDZWGYB.js.map +1 -0
  74. package/dist/chunk-RGO42EQ6.js +25 -0
  75. package/dist/chunk-RGO42EQ6.js.map +1 -0
  76. package/dist/chunk-X66JWQBB.js +37 -0
  77. package/dist/chunk-X66JWQBB.js.map +1 -0
  78. package/dist/chunk-XRAJSOPS.js +203 -0
  79. package/dist/chunk-XRAJSOPS.js.map +1 -0
  80. package/dist/index.cjs +1766 -127
  81. package/dist/index.cjs.map +1 -1
  82. package/dist/index.d.cts +99 -3
  83. package/dist/index.d.ts +99 -3
  84. package/dist/index.js +115 -9
  85. package/dist/index.js.map +1 -1
  86. package/dist/mcp/index.d.cts +5 -2
  87. package/dist/mcp/index.d.ts +5 -2
  88. package/dist/mcp.cjs +37 -9
  89. package/dist/mcp.cjs.map +1 -1
  90. package/dist/mcp.js +1 -1
  91. package/dist/presence/index.d.cts +136 -0
  92. package/dist/presence/index.d.ts +136 -0
  93. package/dist/presence.cjs +107 -0
  94. package/dist/presence.cjs.map +1 -0
  95. package/dist/presence.js +5 -0
  96. package/dist/presence.js.map +1 -0
  97. package/dist/registry-2DRURS6U.js +3 -0
  98. package/dist/registry-2DRURS6U.js.map +1 -0
  99. package/dist/server-BsSwfemr.d.cts +63 -0
  100. package/dist/server-Du3-IGqM.d.ts +63 -0
  101. package/dist/sharing/index.d.cts +3 -1
  102. package/dist/sharing/index.d.ts +3 -1
  103. package/dist/sharing.cjs +68 -0
  104. package/dist/sharing.cjs.map +1 -1
  105. package/dist/sharing.js +1 -1
  106. package/dist/sheets-adapter.cjs +96 -0
  107. package/dist/sheets-adapter.cjs.map +1 -0
  108. package/dist/sheets-adapter.d.cts +115 -0
  109. package/dist/sheets-adapter.d.ts +115 -0
  110. package/dist/sheets-adapter.js +4 -0
  111. package/dist/sheets-adapter.js.map +1 -0
  112. package/dist/styles.css +57 -0
  113. package/dist/styles.css.map +1 -1
  114. package/dist/tool-host-BQuUygLF.d.cts +60 -0
  115. package/dist/tool-host-C8JMMGYq.d.ts +60 -0
  116. package/dist/{types-CRPA_D0z.d.ts → types-CCSBGW9T.d.cts} +2 -2
  117. package/dist/{types-DR5AS6Rd.d.cts → types-DIVNcIQO.d.ts} +2 -2
  118. package/dist/types-aOQLTW0E.d.cts +112 -0
  119. package/dist/types-aOQLTW0E.d.ts +112 -0
  120. package/dist/undo/index.d.cts +69 -0
  121. package/dist/undo/index.d.ts +69 -0
  122. package/dist/undo.cjs +163 -0
  123. package/dist/undo.cjs.map +1 -0
  124. package/dist/undo.js +5 -0
  125. package/dist/undo.js.map +1 -0
  126. package/package.json +1 -1
  127. package/dist/chunk-2VOQJKSU.js.map +0 -1
  128. package/dist/chunk-5ZUHNNLR.js.map +0 -1
  129. package/dist/chunk-FLEOQUKF.js.map +0 -1
  130. package/dist/chunk-QGCF7YKW.js.map +0 -1
  131. package/dist/server-Bv985us3.d.cts +0 -173
  132. 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
- this.tools = /* @__PURE__ */ new Map();
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
- listTools() {
43
- return Array.from(this.tools.values()).map((t) => t.definition);
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(server, options) {
438
+ function registerWhiteboardBridge(host, options) {
205
439
  const { adapter } = options;
206
440
  const agent = { ...DEFAULT_AGENT, ...options.agent ?? {} };
207
441
  const disposers = [];
208
- const reg = (name, description, inputProperties, required, handler) => {
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
- server.registerTool(
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
- async (args) => {
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
- let removed = false;
515
- adapter.setNotes((all) => {
516
- const next = all.filter((x) => x.id !== id);
517
- if (next.length !== all.length) removed = true;
518
- return next;
519
- });
520
- adapter.setShapes((all) => {
521
- const next = all.filter((x) => x.id !== id);
522
- if (next.length !== all.length) removed = true;
523
- return next;
524
- });
525
- adapter.setConnectors((all) => {
526
- const next = all.filter((x) => x.id !== id);
527
- if (next.length !== all.length) removed = true;
528
- return next;
529
- });
530
- adapter.setStrokes((all) => {
531
- const next = all.filter((x) => x.id !== id);
532
- if (next.length !== all.length) removed = true;
533
- return next;
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 removed ? textResult(`Deleted ${id}`) : errorResult(`No item with id ${id}`);
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(server, options) {
865
+ function registerFlowBridge(host, options) {
593
866
  const { adapter } = options;
594
- ({ ...options.agent ?? {} });
867
+ const agent = { ...DEFAULT_AGENT2, ...options.agent ?? {} };
595
868
  const disposers = [];
596
- const reg = (name, description, properties, required, handler) => {
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
- server.registerTool(
888
+ host.registerTool(
599
889
  {
600
890
  name,
601
891
  description,
602
892
  inputSchema: { type: "object", properties, required, additionalProperties: false }
603
893
  },
604
- async (args) => {
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
- function AgentPanel({ agent, activity, onSubmit, busy, actions, className, style }) {
904
- const scrollRef = react.useRef(null);
905
- const inputRef = react.useRef(null);
906
- react.useEffect(() => {
907
- const el = scrollRef.current;
908
- if (!el) return;
909
- el.scrollTop = el.scrollHeight;
910
- }, [activity.length]);
911
- const handleSubmit = (e) => {
912
- e.preventDefault();
913
- const value = inputRef.current?.value.trim();
914
- if (!value || !onSubmit) return;
915
- onSubmit(value);
916
- if (inputRef.current) inputRef.current.value = "";
917
- };
918
- const color = agent?.color ?? "#a855f7";
919
- const name = agent?.name ?? "Agent";
920
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: ["fai-panel", className ?? ""].filter(Boolean).join(" "), style, children: [
921
- /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "fai-panel__header", children: [
922
- /* @__PURE__ */ jsxRuntime.jsx(
923
- "div",
924
- {
925
- className: "fai-panel__avatar",
926
- style: { background: color },
927
- "aria-hidden": true,
928
- children: name.slice(0, 1)
929
- }
930
- ),
931
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fai-panel__title", children: [
932
- /* @__PURE__ */ jsxRuntime.jsx("strong", { children: name }),
933
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "fai-panel__subtitle", children: busy ? "Working\u2026" : `${activity.length} event${activity.length === 1 ? "" : "s"}` })
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
- ref: inputRef,
943
- className: "fai-panel__input",
944
- placeholder: busy ? "Working\u2026" : "Ask the agent\u2026",
945
- disabled: busy,
946
- rows: 2,
947
- onKeyDown: (e) => {
948
- if (e.key === "Enter" && !e.shiftKey) {
949
- e.preventDefault();
950
- handleSubmit(e);
951
- }
952
- }
953
- }
954
- ),
955
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", className: "fai-panel__send", disabled: busy, children: "Send" })
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
- function ActivityRow({ item }) {
960
- const time = formatTime(item.at);
961
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `fai-row fai-row--${item.kind}`, children: [
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 DEFAULT_AGENT3 = { id: "agent", name: "Agent", color: "#a855f7" };
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 = DEFAULT_AGENT3,
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