@nimblebrain/synapse 0.17.1 → 0.19.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 (95) hide show
  1. package/README.md +20 -18
  2. package/dist/chunk-54W6IAHM.js +24 -0
  3. package/dist/chunk-54W6IAHM.js.map +1 -0
  4. package/dist/{chunk-UPDBDXQB.js → chunk-DRLVEHCD.js} +6 -13
  5. package/dist/chunk-DRLVEHCD.js.map +1 -0
  6. package/dist/chunk-NP7ROSH2.cjs +33 -0
  7. package/dist/chunk-NP7ROSH2.cjs.map +1 -0
  8. package/dist/{chunk-SRQNQ26I.js → chunk-OYLZ5OCK.js} +4 -4
  9. package/dist/chunk-OYLZ5OCK.js.map +1 -0
  10. package/dist/chunk-P5DBJFSQ.js +8459 -0
  11. package/dist/chunk-P5DBJFSQ.js.map +1 -0
  12. package/dist/chunk-PZ5AY32C.js +9 -0
  13. package/dist/chunk-PZ5AY32C.js.map +1 -0
  14. package/dist/chunk-Q7SFCCGT.cjs +11 -0
  15. package/dist/chunk-Q7SFCCGT.cjs.map +1 -0
  16. package/dist/chunk-QXDCA7QW.cjs +8466 -0
  17. package/dist/chunk-QXDCA7QW.cjs.map +1 -0
  18. package/dist/{chunk-EHLQWCYV.cjs → chunk-RU64F5RS.cjs} +7 -7
  19. package/dist/chunk-RU64F5RS.cjs.map +1 -0
  20. package/dist/{chunk-TXJMGQYF.cjs → chunk-SZWHFTXV.cjs} +5 -13
  21. package/dist/chunk-SZWHFTXV.cjs.map +1 -0
  22. package/dist/codegen/cli.cjs +4 -4
  23. package/dist/codegen/cli.js +4 -4
  24. package/dist/codegen/index.cjs +1 -0
  25. package/dist/codegen/index.d.cts +1 -1
  26. package/dist/codegen/index.d.ts +1 -1
  27. package/dist/codegen/index.js +1 -0
  28. package/dist/connect.iife.global.js +66 -43
  29. package/dist/{detect-Bf8Q_0dK.d.ts → detect-DBwc8a21.d.ts} +2 -2
  30. package/dist/{detect-BHYg26_d.d.cts → detect-DZMJsyvZ.d.cts} +2 -2
  31. package/dist/host/index.cjs +15 -14
  32. package/dist/host/index.d.cts +3 -3
  33. package/dist/host/index.d.ts +3 -3
  34. package/dist/host/index.js +3 -2
  35. package/dist/index.cjs +42 -18
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.d.cts +40 -13
  38. package/dist/index.d.ts +40 -13
  39. package/dist/index.js +35 -3
  40. package/dist/index.js.map +1 -1
  41. package/dist/react/index.cjs +12 -10
  42. package/dist/react/index.cjs.map +1 -1
  43. package/dist/react/index.d.cts +14 -5
  44. package/dist/react/index.d.ts +14 -5
  45. package/dist/react/index.js +7 -5
  46. package/dist/react/index.js.map +1 -1
  47. package/dist/schema-reader-GM4A4L44.js +4 -0
  48. package/dist/{schema-reader-776246MJ.js.map → schema-reader-GM4A4L44.js.map} +1 -1
  49. package/dist/{schema-reader-B6LTGBMJ.cjs → schema-reader-MWBRQ2TQ.cjs} +3 -2
  50. package/dist/{schema-reader-B6LTGBMJ.cjs.map → schema-reader-MWBRQ2TQ.cjs.map} +1 -1
  51. package/dist/{server-QIYIJHD5.cjs → server-LR6T6C44.cjs} +32 -20
  52. package/dist/server-LR6T6C44.cjs.map +1 -0
  53. package/dist/{server-5N76YCWC.js → server-UHPEE55X.js} +32 -21
  54. package/dist/server-UHPEE55X.js.map +1 -0
  55. package/dist/synapse-runtime.iife.global.js +66 -43
  56. package/dist/synapse-ui.iife.global.js +2 -2
  57. package/dist/{type-generator-UA3L4OIA.cjs → type-generator-YTPFWLVG.cjs} +3 -2
  58. package/dist/{type-generator-UA3L4OIA.cjs.map → type-generator-YTPFWLVG.cjs.map} +1 -1
  59. package/dist/type-generator-ZYSVFK3X.js +4 -0
  60. package/dist/{type-generator-MZ4X4DE5.js.map → type-generator-ZYSVFK3X.js.map} +1 -1
  61. package/dist/{types-uEO4VFJ2.d.ts → types-CG08aO82.d.cts} +14 -83
  62. package/dist/{types-uEO4VFJ2.d.cts → types-CG08aO82.d.ts} +14 -83
  63. package/dist/ui/base.cjs +1 -0
  64. package/dist/ui/base.cjs.map +1 -1
  65. package/dist/ui/base.js +1 -0
  66. package/dist/ui/base.js.map +1 -1
  67. package/dist/ui/index.cjs +314 -87
  68. package/dist/ui/index.cjs.map +1 -1
  69. package/dist/ui/index.d.cts +29 -2
  70. package/dist/ui/index.d.ts +29 -2
  71. package/dist/ui/index.js +315 -89
  72. package/dist/ui/index.js.map +1 -1
  73. package/dist/vite/index.cjs +74 -30
  74. package/dist/vite/index.cjs.map +1 -1
  75. package/dist/vite/index.d.cts +5 -1
  76. package/dist/vite/index.d.ts +5 -1
  77. package/dist/vite/index.js +74 -30
  78. package/dist/vite/index.js.map +1 -1
  79. package/dist/{writer-NDK4U6C5.cjs → writer-HDRCQSX2.cjs} +3 -3
  80. package/dist/{writer-NDK4U6C5.cjs.map → writer-HDRCQSX2.cjs.map} +1 -1
  81. package/dist/{writer-ZNBXYUYC.js → writer-QNDPMEYD.js} +3 -3
  82. package/dist/{writer-ZNBXYUYC.js.map → writer-QNDPMEYD.js.map} +1 -1
  83. package/package.json +8 -5
  84. package/dist/chunk-EHLQWCYV.cjs.map +0 -1
  85. package/dist/chunk-FDMRJZKT.cjs +0 -788
  86. package/dist/chunk-FDMRJZKT.cjs.map +0 -1
  87. package/dist/chunk-SRQNQ26I.js.map +0 -1
  88. package/dist/chunk-TXJMGQYF.cjs.map +0 -1
  89. package/dist/chunk-UPDBDXQB.js.map +0 -1
  90. package/dist/chunk-VYR6V47N.js +0 -781
  91. package/dist/chunk-VYR6V47N.js.map +0 -1
  92. package/dist/schema-reader-776246MJ.js +0 -3
  93. package/dist/server-5N76YCWC.js.map +0 -1
  94. package/dist/server-QIYIJHD5.cjs.map +0 -1
  95. package/dist/type-generator-MZ4X4DE5.js +0 -3
@@ -1,781 +0,0 @@
1
- import { detectHost, foldFontFaces, applyTheme, fontFacesKey, extractTheme } from './chunk-UPDBDXQB.js';
2
- import { INITIALIZE_METHOD, LATEST_PROTOCOL_VERSION, HOST_CONTEXT_CHANGED_METHOD, INITIALIZED_METHOD, MESSAGE_METHOD, OPEN_LINK_METHOD, RESOURCE_TEARDOWN_METHOD, TOOL_CANCELLED_METHOD, TOOL_INPUT_PARTIAL_METHOD, TOOL_INPUT_METHOD, TOOL_RESULT_METHOD } from '@modelcontextprotocol/ext-apps';
3
-
4
- // src/internals.ts
5
- var INTERNALS = /* @__PURE__ */ new WeakMap();
6
- function registerInternals(app, internals) {
7
- INTERNALS.set(app, internals);
8
- }
9
- function internalsFor(app) {
10
- const internals = INTERNALS.get(app);
11
- if (!internals) {
12
- throw new Error(
13
- "This App did not come from this copy of @nimblebrain/synapse. Two copies of the package are installed (check for a duplicate in your lockfile, or a bundler resolving the ESM and CJS builds separately)."
14
- );
15
- }
16
- return internals;
17
- }
18
-
19
- // src/result-parser.ts
20
- function parseToolResult(raw) {
21
- if (raw == null) {
22
- return { data: null, isError: false };
23
- }
24
- if (isCallToolResult(raw)) {
25
- return parseCallToolResult(raw);
26
- }
27
- return { data: raw, isError: false };
28
- }
29
- function isCallToolResult(value) {
30
- if (value === null || typeof value !== "object" || Array.isArray(value)) {
31
- return false;
32
- }
33
- return Array.isArray(value.content);
34
- }
35
- function isTextBlock(block) {
36
- if (block === null || typeof block !== "object" || Array.isArray(block)) {
37
- return false;
38
- }
39
- const obj = block;
40
- return obj.type === "text" && typeof obj.text === "string";
41
- }
42
- function extractMeta(result) {
43
- const meta = result._meta;
44
- if (!meta || typeof meta !== "object" || Array.isArray(meta)) return void 0;
45
- return { ...meta };
46
- }
47
- function parseCallToolResult(result) {
48
- const isError = result.isError === true;
49
- const content = result.content;
50
- const meta = extractMeta(result);
51
- if (content.length === 0) {
52
- return { data: null, isError, content, ...meta && { _meta: meta } };
53
- }
54
- const firstText = content.find(isTextBlock);
55
- if (!firstText) {
56
- return { data: content, isError, content, ...meta && { _meta: meta } };
57
- }
58
- try {
59
- return { data: JSON.parse(firstText.text), isError, content, ...meta && { _meta: meta } };
60
- } catch {
61
- return { data: firstText.text, isError, content, ...meta && { _meta: meta } };
62
- }
63
- }
64
-
65
- // src/task-handle.ts
66
- var TOOLS_CALL_METHOD = "tools/call";
67
- var TASKS_GET_METHOD = "tasks/get";
68
- var TASKS_RESULT_METHOD = "tasks/result";
69
- var TASKS_CANCEL_METHOD = "tasks/cancel";
70
- var TASKS_STATUS_NOTIFICATION_METHOD = "notifications/tasks/status";
71
- function createTaskStatusRouter(transport) {
72
- const listeners = /* @__PURE__ */ new Map();
73
- const unsub = transport.onMessage(TASKS_STATUS_NOTIFICATION_METHOD, (rawParams) => {
74
- if (!rawParams) return;
75
- const params = rawParams;
76
- const taskId = params.taskId;
77
- if (typeof taskId !== "string") return;
78
- const set = listeners.get(taskId);
79
- if (!set || set.size === 0) return;
80
- const update = {
81
- taskId: params.taskId,
82
- status: params.status,
83
- ...params.statusMessage !== void 0 && { statusMessage: params.statusMessage }
84
- };
85
- for (const cb of set) cb(update);
86
- });
87
- return {
88
- subscribe(taskId, cb) {
89
- let set = listeners.get(taskId);
90
- if (!set) {
91
- set = /* @__PURE__ */ new Set();
92
- listeners.set(taskId, set);
93
- }
94
- set.add(cb);
95
- return () => {
96
- const s = listeners.get(taskId);
97
- if (!s) return;
98
- s.delete(cb);
99
- if (s.size === 0) listeners.delete(taskId);
100
- };
101
- },
102
- dispose() {
103
- listeners.clear();
104
- unsub();
105
- }
106
- };
107
- }
108
- async function callToolAsTask(app, toolName, args, options) {
109
- const deps = internalsFor(app);
110
- const hostTasks = deps.hostTasksCapability;
111
- if (!hostTasks?.requests?.tools?.call) {
112
- throw new Error(
113
- "callToolAsTask: host did not advertise tasks.requests.tools.call in its capabilities. Per MCP 2025-11-25 \xA7, requestors MUST NOT task-augment a tools/call without matching receiver capability. Fall back to `app.callTool`."
114
- );
115
- }
116
- const taskParam = {};
117
- if (options?.ttl !== void 0) taskParam.ttl = options.ttl;
118
- const crossServer = options?.internal ?? deps.internalApp;
119
- const callParams = {
120
- name: toolName,
121
- arguments: args ?? {},
122
- task: taskParam,
123
- ...crossServer ? { server: deps.appName } : {}
124
- };
125
- const raw = await deps.request(
126
- TOOLS_CALL_METHOD,
127
- callParams
128
- );
129
- const createResult = raw;
130
- const initialTask = createResult?.task;
131
- if (!initialTask || typeof initialTask !== "object" || typeof initialTask.taskId !== "string") {
132
- throw new Error(
133
- "callToolAsTask: receiver returned a response without `task` per CreateTaskResult (expected shape: `{ task: { taskId, status, ... } }`). Receiver may not honor the advertised tasks capability."
134
- );
135
- }
136
- const taskId = initialTask.taskId;
137
- const localCallbacks = /* @__PURE__ */ new Map();
138
- const handle = {
139
- task: initialTask,
140
- async result() {
141
- const params = { taskId };
142
- const rawResult = await deps.request(
143
- TASKS_RESULT_METHOD,
144
- params
145
- );
146
- const typed = rawResult;
147
- return parseToolResult(typed);
148
- },
149
- async refresh() {
150
- const params = { taskId };
151
- const raw2 = await deps.request(
152
- TASKS_GET_METHOD,
153
- params
154
- );
155
- return projectTask(raw2);
156
- },
157
- async cancel() {
158
- const params = { taskId };
159
- const raw2 = await deps.request(
160
- TASKS_CANCEL_METHOD,
161
- params
162
- );
163
- return projectTask(raw2);
164
- },
165
- onStatus(cb) {
166
- const existing = localCallbacks.get(cb);
167
- if (existing) return existing;
168
- const wireUnsub = deps.taskRouter.subscribe(taskId, (update) => {
169
- const merged = {
170
- taskId: update.taskId,
171
- status: update.status,
172
- ttl: initialTask.ttl,
173
- createdAt: initialTask.createdAt,
174
- lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString(),
175
- ...initialTask.pollInterval !== void 0 && {
176
- pollInterval: initialTask.pollInterval
177
- },
178
- ...update.statusMessage !== void 0 && { statusMessage: update.statusMessage }
179
- };
180
- cb(merged);
181
- });
182
- const unsub = () => {
183
- localCallbacks.delete(cb);
184
- wireUnsub();
185
- };
186
- localCallbacks.set(cb, unsub);
187
- return unsub;
188
- }
189
- };
190
- return handle;
191
- }
192
- function projectTask(raw) {
193
- return {
194
- taskId: raw.taskId,
195
- status: raw.status,
196
- ttl: raw.ttl,
197
- createdAt: raw.createdAt,
198
- lastUpdatedAt: raw.lastUpdatedAt,
199
- ...raw.pollInterval !== void 0 && { pollInterval: raw.pollInterval },
200
- ...raw.statusMessage !== void 0 && { statusMessage: raw.statusMessage }
201
- };
202
- }
203
-
204
- // src/content-parser.ts
205
- function parseToolResultParams(params) {
206
- const raw = params ?? {};
207
- const structuredContent = raw.structuredContent ?? null;
208
- if (structuredContent != null) {
209
- return { content: structuredContent, structuredContent, raw };
210
- }
211
- const rawContent = raw.content;
212
- if (Array.isArray(rawContent)) {
213
- const texts = rawContent.filter(
214
- (block) => block != null && typeof block === "object" && block.type === "text" && typeof block.text === "string"
215
- ).map((block) => block.text);
216
- if (texts.length > 0) {
217
- const joined = texts.join("");
218
- try {
219
- return { content: JSON.parse(joined), structuredContent: null, raw };
220
- } catch {
221
- return { content: joined, structuredContent: null, raw };
222
- }
223
- }
224
- return { content: rawContent, structuredContent: null, raw };
225
- }
226
- if (typeof rawContent === "string") {
227
- try {
228
- return { content: JSON.parse(rawContent), structuredContent: null, raw };
229
- } catch {
230
- return { content: rawContent, structuredContent: null, raw };
231
- }
232
- }
233
- return { content: rawContent ?? null, structuredContent: null, raw };
234
- }
235
- var DATA_CHANGED_METHOD = "synapse/data-changed";
236
- var ACTION_METHOD = "synapse/action";
237
- var REQUEST_FILE_METHOD = "synapse/request-file";
238
- var DOWNLOAD_FILE_METHOD = "synapse/download-file";
239
- var EVENT_MAP = {
240
- "tool-result": TOOL_RESULT_METHOD,
241
- "tool-input": TOOL_INPUT_METHOD,
242
- "tool-input-partial": TOOL_INPUT_PARTIAL_METHOD,
243
- "tool-cancelled": TOOL_CANCELLED_METHOD,
244
- teardown: RESOURCE_TEARDOWN_METHOD
245
- };
246
- function resolveEventMethod(name) {
247
- return EVENT_MAP[name] ?? name;
248
- }
249
-
250
- // src/keyboard.ts
251
- var KeyboardForwarder = class {
252
- listener;
253
- destroyed = false;
254
- constructor(transport, customKeys) {
255
- const config = customKeys ?? null;
256
- this.listener = (event) => {
257
- if (this.destroyed) return;
258
- if (this.shouldForward(event, config)) {
259
- event.preventDefault();
260
- transport.send("synapse/keydown", {
261
- key: event.key,
262
- ctrlKey: event.ctrlKey,
263
- metaKey: event.metaKey,
264
- shiftKey: event.shiftKey,
265
- altKey: event.altKey
266
- });
267
- }
268
- };
269
- document.addEventListener("keydown", this.listener);
270
- }
271
- destroy() {
272
- if (this.destroyed) return;
273
- this.destroyed = true;
274
- document.removeEventListener("keydown", this.listener);
275
- }
276
- shouldForward(event, config) {
277
- if (config && config.length === 0) return false;
278
- if (config) {
279
- return config.some(
280
- (k) => event.key.toLowerCase() === k.key.toLowerCase() && (k.ctrl === void 0 || event.ctrlKey === k.ctrl) && (k.meta === void 0 || event.metaKey === k.meta) && (k.shift === void 0 || event.shiftKey === k.shift) && (k.alt === void 0 || event.altKey === k.alt)
281
- );
282
- }
283
- if (event.key === "Escape") return true;
284
- if (event.ctrlKey || event.metaKey) {
285
- const key = event.key.toLowerCase();
286
- if (key === "c" || key === "v" || key === "x" || key === "a") return false;
287
- return true;
288
- }
289
- return false;
290
- }
291
- };
292
-
293
- // src/resize.ts
294
- function createResizer(send, autoResize) {
295
- let destroyed = false;
296
- let observer = null;
297
- let rafId = null;
298
- function measureAndSend() {
299
- if (destroyed) return;
300
- const width = document.body.scrollWidth;
301
- const height = document.body.scrollHeight;
302
- send("ui/notifications/size-changed", { width, height });
303
- }
304
- function resize(width, height) {
305
- if (destroyed) return;
306
- if (width !== void 0 && height !== void 0) {
307
- send("ui/notifications/size-changed", { width, height });
308
- } else {
309
- measureAndSend();
310
- }
311
- }
312
- if (autoResize && typeof ResizeObserver !== "undefined") {
313
- observer = new ResizeObserver(() => {
314
- if (destroyed) return;
315
- if (rafId !== null) cancelAnimationFrame(rafId);
316
- rafId = requestAnimationFrame(() => {
317
- rafId = null;
318
- measureAndSend();
319
- });
320
- });
321
- observer.observe(document.body);
322
- }
323
- function destroy() {
324
- if (destroyed) return;
325
- destroyed = true;
326
- if (rafId !== null) cancelAnimationFrame(rafId);
327
- observer?.disconnect();
328
- observer = null;
329
- }
330
- return { resize, measureAndSend, destroy };
331
- }
332
-
333
- // src/transport.ts
334
- var SynapseTransport = class {
335
- counter = 0;
336
- destroyed = false;
337
- pending = /* @__PURE__ */ new Map();
338
- handlers = /* @__PURE__ */ new Map();
339
- listener;
340
- constructor() {
341
- this.listener = (event) => this.handleMessage(event);
342
- window.addEventListener("message", this.listener);
343
- }
344
- send(method, params) {
345
- if (this.destroyed) return;
346
- const msg = {
347
- jsonrpc: "2.0",
348
- method,
349
- ...params !== void 0 && { params }
350
- };
351
- window.parent.postMessage(msg, "*");
352
- }
353
- request(method, params) {
354
- if (this.destroyed) {
355
- return Promise.reject(new Error("Transport destroyed"));
356
- }
357
- const id = `syn-${++this.counter}`;
358
- const msg = {
359
- jsonrpc: "2.0",
360
- method,
361
- id,
362
- ...params !== void 0 && { params }
363
- };
364
- return new Promise((resolve, reject) => {
365
- this.pending.set(id, { resolve, reject });
366
- window.parent.postMessage(msg, "*");
367
- });
368
- }
369
- onMessage(method, callback) {
370
- if (!this.handlers.has(method)) {
371
- this.handlers.set(method, /* @__PURE__ */ new Set());
372
- }
373
- this.handlers.get(method)?.add(callback);
374
- return () => {
375
- const set = this.handlers.get(method);
376
- if (set) {
377
- set.delete(callback);
378
- if (set.size === 0) {
379
- this.handlers.delete(method);
380
- }
381
- }
382
- };
383
- }
384
- destroy() {
385
- if (this.destroyed) return;
386
- this.destroyed = true;
387
- window.removeEventListener("message", this.listener);
388
- const error = new Error("Transport destroyed");
389
- for (const entry of this.pending.values()) {
390
- entry.reject(error);
391
- }
392
- this.pending.clear();
393
- this.handlers.clear();
394
- }
395
- handleMessage(event) {
396
- if (this.destroyed) return;
397
- const data = event.data;
398
- if (!data || data.jsonrpc !== "2.0") return;
399
- if ("id" in data && data.id && !("method" in data)) {
400
- const response = data;
401
- const entry = this.pending.get(response.id);
402
- if (!entry) return;
403
- this.pending.delete(response.id);
404
- if (response.error) {
405
- const err = new Error(response.error.message);
406
- err.code = response.error.code;
407
- err.data = response.error.data;
408
- entry.reject(err);
409
- } else {
410
- entry.resolve(response.result);
411
- }
412
- return;
413
- }
414
- if ("method" in data && !("id" in data && data.id)) {
415
- const notification = data;
416
- const set = this.handlers.get(notification.method);
417
- if (set) {
418
- for (const handler of set) {
419
- handler(notification.params);
420
- }
421
- }
422
- }
423
- }
424
- };
425
-
426
- // src/connect.ts
427
- var READ_RESOURCE_METHOD = "resources/read";
428
- var UPDATE_MODEL_CONTEXT_METHOD = "ui/update-model-context";
429
- async function connect(options) {
430
- const { name, version, autoResize = false, internal = false, forwardKeys } = options;
431
- const transport = new SynapseTransport();
432
- let destroyed = false;
433
- let hostContext = {};
434
- let hostInfo = { name: "unknown", version: "unknown" };
435
- let isNimbleBrainHost = false;
436
- let toolInfo = null;
437
- let containerDimensions = null;
438
- let hostTasksCapability;
439
- let keyboard = null;
440
- let fontFaces;
441
- function resolveTheme() {
442
- const theme = extractTheme(hostContext);
443
- return fontFaces ? { ...theme, fontFaces } : theme;
444
- }
445
- const handlers = /* @__PURE__ */ new Map();
446
- const themeCallbacks = /* @__PURE__ */ new Set();
447
- const hostContextCallbacks = /* @__PURE__ */ new Set();
448
- const dataCallbacks = /* @__PURE__ */ new Set();
449
- const actionCallbacks = /* @__PURE__ */ new Set();
450
- const resizer = createResizer((method, params) => transport.send(method, params), autoResize);
451
- resizer.measureAndSend();
452
- const appCapabilities = {
453
- tasks: {
454
- cancel: {},
455
- requests: { tools: { call: {} } }
456
- }
457
- };
458
- const initParams = {
459
- protocolVersion: LATEST_PROTOCOL_VERSION,
460
- appInfo: { name, version },
461
- appCapabilities
462
- };
463
- const result = await transport.request(
464
- INITIALIZE_METHOD,
465
- initParams
466
- );
467
- if (result) {
468
- const detected = detectHost(result);
469
- isNimbleBrainHost = detected.isNimbleBrain;
470
- hostInfo = {
471
- name: result.hostInfo?.name ?? "unknown",
472
- version: result.hostInfo?.version ?? "unknown"
473
- };
474
- const rawTasks = result.hostCapabilities?.tasks;
475
- hostTasksCapability = rawTasks && typeof rawTasks === "object" && !Array.isArray(rawTasks) ? rawTasks : void 0;
476
- const ctx = result.hostContext;
477
- if (ctx) {
478
- hostContext = ctx;
479
- fontFaces = foldFontFaces(fontFaces, ctx);
480
- if (ctx.toolInfo && typeof ctx.toolInfo === "object") {
481
- toolInfo = { tool: ctx.toolInfo.tool ?? {} };
482
- }
483
- if (ctx.containerDimensions && typeof ctx.containerDimensions === "object") {
484
- containerDimensions = ctx.containerDimensions;
485
- }
486
- const theme = resolveTheme();
487
- applyTheme(theme.mode, theme.tokens, theme.fontFaces);
488
- }
489
- }
490
- if (forwardKeys && isNimbleBrainHost) {
491
- keyboard = new KeyboardForwarder(transport, forwardKeys === true ? void 0 : forwardKeys);
492
- }
493
- function fanOutRaw(method, params) {
494
- const set = handlers.get(method);
495
- if (!set) return;
496
- for (const handler of set) handler(params);
497
- }
498
- transport.onMessage(HOST_CONTEXT_CHANGED_METHOD, (params) => {
499
- if (destroyed) return;
500
- const ctx = params ?? {};
501
- const prevTheme = resolveTheme();
502
- hostContext = { ...hostContext, ...ctx };
503
- fontFaces = foldFontFaces(fontFaces, ctx);
504
- const nextTheme = resolveTheme();
505
- applyTheme(nextTheme.mode, nextTheme.tokens, nextTheme.fontFaces);
506
- for (const cb of hostContextCallbacks) cb(hostContext);
507
- fanOutRaw(HOST_CONTEXT_CHANGED_METHOD, params);
508
- if (!themesEqual(prevTheme, nextTheme)) {
509
- for (const cb of themeCallbacks) cb(nextTheme);
510
- }
511
- });
512
- transport.onMessage(DATA_CHANGED_METHOD, (params) => {
513
- if (destroyed) return;
514
- fanOutRaw(DATA_CHANGED_METHOD, params);
515
- if (!params) return;
516
- const event = {
517
- source: "agent",
518
- server: params.server ?? "",
519
- tool: params.tool ?? ""
520
- };
521
- for (const cb of dataCallbacks) cb(event);
522
- });
523
- transport.onMessage(ACTION_METHOD, (params) => {
524
- if (destroyed) return;
525
- fanOutRaw(ACTION_METHOD, params);
526
- if (!params || typeof params.type !== "string") return;
527
- const action2 = {
528
- type: params.type,
529
- payload: params.payload ?? {},
530
- requiresConfirmation: params.requiresConfirmation === true,
531
- label: typeof params.label === "string" ? params.label : void 0
532
- };
533
- for (const cb of actionCallbacks) cb(action2);
534
- });
535
- const subscribedMethods = /* @__PURE__ */ new Set([
536
- HOST_CONTEXT_CHANGED_METHOD,
537
- DATA_CHANGED_METHOD,
538
- ACTION_METHOD
539
- ]);
540
- function ensureTransportSub(method) {
541
- if (subscribedMethods.has(method)) return;
542
- subscribedMethods.add(method);
543
- const isToolResult = method === TOOL_RESULT_METHOD;
544
- transport.onMessage(method, (params) => {
545
- if (destroyed) return;
546
- const set = handlers.get(method);
547
- if (!set) return;
548
- for (const handler of set) {
549
- if (isToolResult) {
550
- handler(parseToolResultParams(params));
551
- } else {
552
- handler(params);
553
- }
554
- }
555
- });
556
- }
557
- function subscribe(event, handler) {
558
- switch (event) {
559
- case "theme-changed":
560
- themeCallbacks.add(handler);
561
- return () => {
562
- themeCallbacks.delete(handler);
563
- };
564
- case "host-context-changed":
565
- hostContextCallbacks.add(handler);
566
- return () => {
567
- hostContextCallbacks.delete(handler);
568
- };
569
- case "data-changed":
570
- dataCallbacks.add(handler);
571
- return () => {
572
- dataCallbacks.delete(handler);
573
- };
574
- case "action":
575
- actionCallbacks.add(handler);
576
- return () => {
577
- actionCallbacks.delete(handler);
578
- };
579
- default: {
580
- const method = resolveEventMethod(event);
581
- if (!handlers.has(method)) handlers.set(method, /* @__PURE__ */ new Set());
582
- handlers.get(method)?.add(handler);
583
- ensureTransportSub(method);
584
- return () => {
585
- const set = handlers.get(method);
586
- if (set) {
587
- set.delete(handler);
588
- if (set.size === 0) handlers.delete(method);
589
- }
590
- };
591
- }
592
- }
593
- }
594
- if (options.on) {
595
- for (const [event, handler] of Object.entries(options.on)) {
596
- if (typeof handler === "function") subscribe(event, handler);
597
- }
598
- }
599
- transport.send(INITIALIZED_METHOD, {});
600
- const taskRouter = createTaskStatusRouter(transport);
601
- const app = {
602
- get theme() {
603
- return resolveTheme();
604
- },
605
- get hostInfo() {
606
- return { ...hostInfo };
607
- },
608
- get hostContext() {
609
- return hostContext;
610
- },
611
- get isNimbleBrainHost() {
612
- return isNimbleBrainHost;
613
- },
614
- get toolInfo() {
615
- return toolInfo;
616
- },
617
- get containerDimensions() {
618
- return containerDimensions;
619
- },
620
- get destroyed() {
621
- return destroyed;
622
- },
623
- get supportsTasks() {
624
- return hostTasksCapability?.requests?.tools?.call !== void 0;
625
- },
626
- on(event, handler) {
627
- return subscribe(event, handler);
628
- },
629
- resize(width, height) {
630
- resizer.resize(width, height);
631
- },
632
- openLink(url) {
633
- if (destroyed) return;
634
- const params = { url };
635
- transport.request(OPEN_LINK_METHOD, params).catch(() => {
636
- window.open(url, "_blank", "noopener");
637
- });
638
- },
639
- updateModelContext(state, summary) {
640
- if (destroyed) return;
641
- const params = {
642
- structuredContent: state,
643
- ...summary !== void 0 && {
644
- content: [{ type: "text", text: summary }]
645
- }
646
- };
647
- transport.send(UPDATE_MODEL_CONTEXT_METHOD, params);
648
- },
649
- async callTool(toolName, args, callOptions) {
650
- const server = callOptions?.server ?? (internal ? name : void 0);
651
- const params = {
652
- name: toolName,
653
- arguments: args ?? {},
654
- ...server !== void 0 && { server }
655
- };
656
- const raw = await transport.request(TOOLS_CALL_METHOD, params);
657
- return parseToolResult(raw);
658
- },
659
- async readServerResource(params) {
660
- const raw = await transport.request(
661
- READ_RESOURCE_METHOD,
662
- params
663
- );
664
- return raw;
665
- },
666
- sendMessage(text, context) {
667
- if (destroyed) return;
668
- const textBlock = {
669
- type: "text",
670
- text,
671
- // `_meta.context` is a NimbleBrain convention; other hosts ignore it,
672
- // but there is no reason to spend the bytes off one.
673
- ...isNimbleBrainHost && context && { _meta: { context } }
674
- };
675
- const params = {
676
- role: "user",
677
- content: [textBlock]
678
- };
679
- transport.send(MESSAGE_METHOD, params);
680
- },
681
- destroy() {
682
- if (destroyed) return;
683
- destroyed = true;
684
- keyboard?.destroy();
685
- resizer.destroy();
686
- taskRouter.dispose();
687
- handlers.clear();
688
- themeCallbacks.clear();
689
- hostContextCallbacks.clear();
690
- dataCallbacks.clear();
691
- actionCallbacks.clear();
692
- transport.destroy();
693
- }
694
- };
695
- registerInternals(app, {
696
- send(method, params) {
697
- if (destroyed) return;
698
- transport.send(method, params);
699
- },
700
- request(method, params) {
701
- return transport.request(method, params);
702
- },
703
- onMessage(method, handler) {
704
- return transport.onMessage(method, handler);
705
- },
706
- taskRouter,
707
- get hostTasksCapability() {
708
- return hostTasksCapability;
709
- },
710
- appName: name,
711
- internalApp: internal
712
- });
713
- return app;
714
- }
715
- function themesEqual(a, b) {
716
- if (a.mode !== b.mode) return false;
717
- if (fontFacesKey(a.fontFaces) !== fontFacesKey(b.fontFaces)) return false;
718
- const aKeys = Object.keys(a.tokens);
719
- const bKeys = Object.keys(b.tokens);
720
- if (aKeys.length !== bKeys.length) return false;
721
- for (const k of aKeys) {
722
- if (a.tokens[k] !== b.tokens[k]) return false;
723
- }
724
- return true;
725
- }
726
-
727
- // src/extensions.ts
728
- var DEFAULT_MAX_FILE_SIZE = 26214400;
729
- function action(app, name, params) {
730
- if (!app.isNimbleBrainHost) return;
731
- internalsFor(app).send(ACTION_METHOD, { action: name, ...params });
732
- }
733
- function downloadFile(app, filename, content, mimeType) {
734
- const resolvedMime = mimeType || (content instanceof Blob ? content.type : "") || "application/octet-stream";
735
- const blob = content instanceof Blob ? content : new Blob([content], { type: resolvedMime });
736
- internalsFor(app).send(DOWNLOAD_FILE_METHOD, {
737
- data: blob,
738
- filename,
739
- mimeType: resolvedMime
740
- });
741
- }
742
- async function pickFile(app, options) {
743
- if (!app.isNimbleBrainHost) {
744
- throw new Error("pickFile is not supported in this host");
745
- }
746
- const result = await requestFile(app, options, false);
747
- if (result === null) return null;
748
- return Array.isArray(result) ? result[0] ?? null : result;
749
- }
750
- async function pickFiles(app, options) {
751
- if (!app.isNimbleBrainHost) {
752
- throw new Error("pickFiles is not supported in this host");
753
- }
754
- const result = await requestFile(app, options, true);
755
- if (result === null) return [];
756
- return Array.isArray(result) ? result : [result];
757
- }
758
- async function requestFile(app, options, multiple) {
759
- const result = await internalsFor(app).request(REQUEST_FILE_METHOD, {
760
- accept: options?.accept,
761
- maxSize: options?.maxSize ?? DEFAULT_MAX_FILE_SIZE,
762
- multiple
763
- });
764
- if (result == null) return null;
765
- if (Array.isArray(result)) {
766
- return result.map(validateFileResult);
767
- }
768
- return validateFileResult(result);
769
- }
770
- function validateFileResult(value) {
771
- if (typeof value !== "object" || value === null || typeof value.id !== "string") {
772
- throw new Error(
773
- "synapse/request-file returned a result without a string `id`. The host appears to be on a version older than this SDK targets \u2014 @nimblebrain/synapse 0.8.0+ requires a host with POST /v1/resources (NimbleBrain \u2265 the version that ships PR #93)."
774
- );
775
- }
776
- return value;
777
- }
778
-
779
- export { action, callToolAsTask, connect, downloadFile, pickFile, pickFiles };
780
- //# sourceMappingURL=chunk-VYR6V47N.js.map
781
- //# sourceMappingURL=chunk-VYR6V47N.js.map