@nimblebrain/synapse 0.19.0 → 0.21.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 (100) hide show
  1. package/README.md +20 -25
  2. package/dist/check-QMTOCTRS.js +474 -0
  3. package/dist/check-QMTOCTRS.js.map +1 -0
  4. package/dist/check-VK4SOAUU.cjs +480 -0
  5. package/dist/check-VK4SOAUU.cjs.map +1 -0
  6. package/dist/{chunk-RU64F5RS.cjs → chunk-BNF352SA.cjs} +57 -146
  7. package/dist/chunk-BNF352SA.cjs.map +1 -0
  8. package/dist/{chunk-DRLVEHCD.js → chunk-DJS2FKBN.js} +21 -79
  9. package/dist/chunk-DJS2FKBN.js.map +1 -0
  10. package/dist/chunk-H3INUKA3.js +32 -0
  11. package/dist/chunk-H3INUKA3.js.map +1 -0
  12. package/dist/chunk-HQLSVUOR.cjs +667 -0
  13. package/dist/chunk-HQLSVUOR.cjs.map +1 -0
  14. package/dist/{chunk-NP7ROSH2.cjs → chunk-ISV6HFQE.cjs} +14 -7
  15. package/dist/chunk-ISV6HFQE.cjs.map +1 -0
  16. package/dist/chunk-TR2YNWZ5.js +658 -0
  17. package/dist/chunk-TR2YNWZ5.js.map +1 -0
  18. package/dist/{chunk-OYLZ5OCK.js → chunk-UKP4W6DI.js} +57 -146
  19. package/dist/chunk-UKP4W6DI.js.map +1 -0
  20. package/dist/{chunk-SZWHFTXV.cjs → chunk-YSL7KJNU.cjs} +21 -81
  21. package/dist/chunk-YSL7KJNU.cjs.map +1 -0
  22. package/dist/codegen/cli.cjs +41 -4
  23. package/dist/codegen/cli.cjs.map +1 -1
  24. package/dist/codegen/cli.js +41 -4
  25. package/dist/codegen/cli.js.map +1 -1
  26. package/dist/codegen/index.cjs +0 -1
  27. package/dist/codegen/index.d.cts +1 -1
  28. package/dist/codegen/index.d.ts +1 -1
  29. package/dist/codegen/index.js +0 -1
  30. package/dist/connect.iife.global.js +34 -34
  31. package/dist/{detect-DZMJsyvZ.d.cts → detect-DVGaL2bH.d.cts} +39 -33
  32. package/dist/{detect-DBwc8a21.d.ts → detect-DVGaL2bH.d.ts} +39 -33
  33. package/dist/host/index.cjs +17 -18
  34. package/dist/host/index.d.cts +11 -27
  35. package/dist/host/index.d.ts +11 -27
  36. package/dist/host/index.js +2 -3
  37. package/dist/index.cjs +30 -19
  38. package/dist/index.cjs.map +1 -1
  39. package/dist/index.d.cts +72 -26
  40. package/dist/index.d.ts +72 -26
  41. package/dist/index.js +8 -9
  42. package/dist/index.js.map +1 -1
  43. package/dist/react/index.cjs +9 -10
  44. package/dist/react/index.cjs.map +1 -1
  45. package/dist/react/index.d.cts +36 -8
  46. package/dist/react/index.d.ts +36 -8
  47. package/dist/react/index.js +3 -4
  48. package/dist/react/index.js.map +1 -1
  49. package/dist/schema-reader-776246MJ.js +3 -0
  50. package/dist/{schema-reader-GM4A4L44.js.map → schema-reader-776246MJ.js.map} +1 -1
  51. package/dist/{schema-reader-MWBRQ2TQ.cjs → schema-reader-B6LTGBMJ.cjs} +2 -3
  52. package/dist/{schema-reader-MWBRQ2TQ.cjs.map → schema-reader-B6LTGBMJ.cjs.map} +1 -1
  53. package/dist/{server-LR6T6C44.cjs → server-BR5MNFOB.cjs} +13 -14
  54. package/dist/server-BR5MNFOB.cjs.map +1 -0
  55. package/dist/{server-UHPEE55X.js → server-OOEEWAEG.js} +13 -14
  56. package/dist/server-OOEEWAEG.js.map +1 -0
  57. package/dist/synapse-runtime.iife.global.js +34 -34
  58. package/dist/synapse-ui.iife.global.js +4 -4
  59. package/dist/type-generator-MZ4X4DE5.js +3 -0
  60. package/dist/{type-generator-ZYSVFK3X.js.map → type-generator-MZ4X4DE5.js.map} +1 -1
  61. package/dist/{type-generator-YTPFWLVG.cjs → type-generator-UA3L4OIA.cjs} +2 -3
  62. package/dist/{type-generator-YTPFWLVG.cjs.map → type-generator-UA3L4OIA.cjs.map} +1 -1
  63. package/dist/{types-CG08aO82.d.cts → types-BxPfGHKO.d.cts} +35 -43
  64. package/dist/{types-CG08aO82.d.ts → types-BxPfGHKO.d.ts} +35 -43
  65. package/dist/ui/base.cjs +0 -1
  66. package/dist/ui/base.cjs.map +1 -1
  67. package/dist/ui/base.js +0 -1
  68. package/dist/ui/base.js.map +1 -1
  69. package/dist/ui/index.cjs +0 -1
  70. package/dist/ui/index.cjs.map +1 -1
  71. package/dist/ui/index.js +0 -1
  72. package/dist/ui/index.js.map +1 -1
  73. package/dist/vite/index.cjs +10 -10
  74. package/dist/vite/index.cjs.map +1 -1
  75. package/dist/vite/index.js +6 -6
  76. package/dist/vite/index.js.map +1 -1
  77. package/dist/{writer-HDRCQSX2.cjs → writer-NDK4U6C5.cjs} +3 -3
  78. package/dist/{writer-HDRCQSX2.cjs.map → writer-NDK4U6C5.cjs.map} +1 -1
  79. package/dist/{writer-QNDPMEYD.js → writer-ZNBXYUYC.js} +3 -3
  80. package/dist/{writer-QNDPMEYD.js.map → writer-ZNBXYUYC.js.map} +1 -1
  81. package/package.json +6 -3
  82. package/dist/chunk-54W6IAHM.js +0 -24
  83. package/dist/chunk-54W6IAHM.js.map +0 -1
  84. package/dist/chunk-DRLVEHCD.js.map +0 -1
  85. package/dist/chunk-NP7ROSH2.cjs.map +0 -1
  86. package/dist/chunk-OYLZ5OCK.js.map +0 -1
  87. package/dist/chunk-P5DBJFSQ.js +0 -8459
  88. package/dist/chunk-P5DBJFSQ.js.map +0 -1
  89. package/dist/chunk-PZ5AY32C.js +0 -9
  90. package/dist/chunk-PZ5AY32C.js.map +0 -1
  91. package/dist/chunk-Q7SFCCGT.cjs +0 -11
  92. package/dist/chunk-Q7SFCCGT.cjs.map +0 -1
  93. package/dist/chunk-QXDCA7QW.cjs +0 -8466
  94. package/dist/chunk-QXDCA7QW.cjs.map +0 -1
  95. package/dist/chunk-RU64F5RS.cjs.map +0 -1
  96. package/dist/chunk-SZWHFTXV.cjs.map +0 -1
  97. package/dist/schema-reader-GM4A4L44.js +0 -4
  98. package/dist/server-LR6T6C44.cjs.map +0 -1
  99. package/dist/server-UHPEE55X.js.map +0 -1
  100. package/dist/type-generator-ZYSVFK3X.js +0 -4
@@ -0,0 +1,667 @@
1
+ 'use strict';
2
+
3
+ var chunkYSL7KJNU_cjs = require('./chunk-YSL7KJNU.cjs');
4
+ var chunkISV6HFQE_cjs = require('./chunk-ISV6HFQE.cjs');
5
+ var extApps = require('@modelcontextprotocol/ext-apps');
6
+ var types_js = require('@modelcontextprotocol/sdk/types.js');
7
+
8
+ // src/errors.ts
9
+ var HostCapabilityError = class extends Error {
10
+ capability;
11
+ constructor(feature, capability) {
12
+ super(`${feature} is not supported in this host: it did not declare ${capability}`);
13
+ this.name = "HostCapabilityError";
14
+ this.capability = capability;
15
+ }
16
+ };
17
+
18
+ // src/internals.ts
19
+ var INTERNALS = /* @__PURE__ */ new WeakMap();
20
+ function registerInternals(app, internals) {
21
+ INTERNALS.set(app, internals);
22
+ }
23
+ function internalsFor(app) {
24
+ const internals = INTERNALS.get(app);
25
+ if (!internals) {
26
+ throw new Error(
27
+ "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)."
28
+ );
29
+ }
30
+ return internals;
31
+ }
32
+
33
+ // src/result-parser.ts
34
+ function parseToolResult(raw) {
35
+ if (raw == null) {
36
+ return { data: null, isError: false };
37
+ }
38
+ if (isCallToolResult(raw)) {
39
+ return parseCallToolResult(raw);
40
+ }
41
+ return { data: raw, isError: false };
42
+ }
43
+ function isCallToolResult(value) {
44
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
45
+ return false;
46
+ }
47
+ return Array.isArray(value.content);
48
+ }
49
+ function isTextBlock(block) {
50
+ if (block === null || typeof block !== "object" || Array.isArray(block)) {
51
+ return false;
52
+ }
53
+ const obj = block;
54
+ return obj.type === "text" && typeof obj.text === "string";
55
+ }
56
+ function extractMeta(result) {
57
+ const meta = result._meta;
58
+ if (!meta || typeof meta !== "object" || Array.isArray(meta)) return void 0;
59
+ return { ...meta };
60
+ }
61
+ function parseCallToolResult(result) {
62
+ const isError = result.isError === true;
63
+ const content = result.content;
64
+ const meta = extractMeta(result);
65
+ if (content.length === 0) {
66
+ return { data: null, isError, content, ...meta && { _meta: meta } };
67
+ }
68
+ const firstText = content.find(isTextBlock);
69
+ if (!firstText) {
70
+ return { data: content, isError, content, ...meta && { _meta: meta } };
71
+ }
72
+ try {
73
+ return { data: JSON.parse(firstText.text), isError, content, ...meta && { _meta: meta } };
74
+ } catch {
75
+ return { data: firstText.text, isError, content, ...meta && { _meta: meta } };
76
+ }
77
+ }
78
+
79
+ // src/task-handle.ts
80
+ var TASKS_EXTENSION_ID = "io.modelcontextprotocol/tasks";
81
+ function readHostTasksCapability(capabilities) {
82
+ const entry = capabilities?.experimental?.[TASKS_EXTENSION_ID];
83
+ return entry && typeof entry === "object" && !Array.isArray(entry) ? entry : void 0;
84
+ }
85
+ var TASKS_GET_METHOD = "tasks/get";
86
+ var TASKS_RESULT_METHOD = "tasks/result";
87
+ var TASKS_CANCEL_METHOD = "tasks/cancel";
88
+ var TASKS_STATUS_NOTIFICATION_METHOD = "notifications/tasks/status";
89
+ function createTaskStatusRouter(subscribe) {
90
+ const listeners = /* @__PURE__ */ new Map();
91
+ const unsub = subscribe(TASKS_STATUS_NOTIFICATION_METHOD, (rawParams) => {
92
+ if (!rawParams) return;
93
+ const params = rawParams;
94
+ const taskId = params.taskId;
95
+ if (typeof taskId !== "string") return;
96
+ const set = listeners.get(taskId);
97
+ if (!set || set.size === 0) return;
98
+ const update = {
99
+ taskId: params.taskId,
100
+ status: params.status,
101
+ ...params.statusMessage !== void 0 && { statusMessage: params.statusMessage }
102
+ };
103
+ for (const cb of set) cb(update);
104
+ });
105
+ return {
106
+ subscribe(taskId, cb) {
107
+ let set = listeners.get(taskId);
108
+ if (!set) {
109
+ set = /* @__PURE__ */ new Set();
110
+ listeners.set(taskId, set);
111
+ }
112
+ set.add(cb);
113
+ return () => {
114
+ const s = listeners.get(taskId);
115
+ if (!s) return;
116
+ s.delete(cb);
117
+ if (s.size === 0) listeners.delete(taskId);
118
+ };
119
+ },
120
+ dispose() {
121
+ listeners.clear();
122
+ unsub();
123
+ }
124
+ };
125
+ }
126
+ async function callToolAsTask(app, toolName, args, options) {
127
+ const deps = internalsFor(app);
128
+ const hostTasks = deps.hostTasksCapability;
129
+ if (!hostTasks?.requests?.tools?.call) {
130
+ throw new HostCapabilityError(
131
+ "callToolAsTask",
132
+ `${TASKS_EXTENSION_ID} with requests.tools.call`
133
+ );
134
+ }
135
+ const taskParam = {};
136
+ if (options?.ttl !== void 0) taskParam.ttl = options.ttl;
137
+ const callParams = {
138
+ name: toolName,
139
+ arguments: args ?? {},
140
+ task: taskParam
141
+ };
142
+ const raw = await deps.request(
143
+ chunkISV6HFQE_cjs.TOOLS_CALL_METHOD,
144
+ callParams
145
+ );
146
+ const createResult = raw;
147
+ const initialTask = createResult?.task;
148
+ if (!initialTask || typeof initialTask !== "object" || typeof initialTask.taskId !== "string") {
149
+ throw new Error(
150
+ "callToolAsTask: receiver returned a response without `task` per CreateTaskResult (expected shape: `{ task: { taskId, status, ... } }`). Receiver may not honor the advertised tasks capability."
151
+ );
152
+ }
153
+ const taskId = initialTask.taskId;
154
+ const localCallbacks = /* @__PURE__ */ new Map();
155
+ const handle = {
156
+ task: initialTask,
157
+ async result() {
158
+ const params = { taskId };
159
+ const rawResult = await deps.request(
160
+ TASKS_RESULT_METHOD,
161
+ params
162
+ );
163
+ const typed = rawResult;
164
+ return parseToolResult(typed);
165
+ },
166
+ async refresh() {
167
+ const params = { taskId };
168
+ const raw2 = await deps.request(
169
+ TASKS_GET_METHOD,
170
+ params
171
+ );
172
+ return projectTask(raw2);
173
+ },
174
+ async cancel() {
175
+ const params = { taskId };
176
+ const raw2 = await deps.request(
177
+ TASKS_CANCEL_METHOD,
178
+ params
179
+ );
180
+ return projectTask(raw2);
181
+ },
182
+ onStatus(cb) {
183
+ const existing = localCallbacks.get(cb);
184
+ if (existing) return existing;
185
+ const wireUnsub = deps.taskRouter.subscribe(taskId, (update) => {
186
+ const merged = {
187
+ taskId: update.taskId,
188
+ status: update.status,
189
+ ttl: initialTask.ttl,
190
+ createdAt: initialTask.createdAt,
191
+ lastUpdatedAt: (/* @__PURE__ */ new Date()).toISOString(),
192
+ ...initialTask.pollInterval !== void 0 && {
193
+ pollInterval: initialTask.pollInterval
194
+ },
195
+ ...update.statusMessage !== void 0 && { statusMessage: update.statusMessage }
196
+ };
197
+ cb(merged);
198
+ });
199
+ const unsub = () => {
200
+ localCallbacks.delete(cb);
201
+ wireUnsub();
202
+ };
203
+ localCallbacks.set(cb, unsub);
204
+ return unsub;
205
+ }
206
+ };
207
+ return handle;
208
+ }
209
+ function projectTask(raw) {
210
+ return {
211
+ taskId: raw.taskId,
212
+ status: raw.status,
213
+ ttl: raw.ttl,
214
+ createdAt: raw.createdAt,
215
+ lastUpdatedAt: raw.lastUpdatedAt,
216
+ ...raw.pollInterval !== void 0 && { pollInterval: raw.pollInterval },
217
+ ...raw.statusMessage !== void 0 && { statusMessage: raw.statusMessage }
218
+ };
219
+ }
220
+
221
+ // src/content-parser.ts
222
+ function parseToolResultParams(params) {
223
+ const raw = params ?? {};
224
+ const structuredContent = raw.structuredContent ?? null;
225
+ if (structuredContent != null) {
226
+ return { content: structuredContent, structuredContent, raw };
227
+ }
228
+ const rawContent = raw.content;
229
+ if (Array.isArray(rawContent)) {
230
+ const texts = rawContent.filter(
231
+ (block) => block != null && typeof block === "object" && block.type === "text" && typeof block.text === "string"
232
+ ).map((block) => block.text);
233
+ if (texts.length > 0) {
234
+ const joined = texts.join("");
235
+ try {
236
+ return { content: JSON.parse(joined), structuredContent: null, raw };
237
+ } catch {
238
+ return { content: joined, structuredContent: null, raw };
239
+ }
240
+ }
241
+ return { content: rawContent, structuredContent: null, raw };
242
+ }
243
+ if (typeof rawContent === "string") {
244
+ try {
245
+ return { content: JSON.parse(rawContent), structuredContent: null, raw };
246
+ } catch {
247
+ return { content: rawContent, structuredContent: null, raw };
248
+ }
249
+ }
250
+ return { content: rawContent ?? null, structuredContent: null, raw };
251
+ }
252
+
253
+ // src/keyboard.ts
254
+ var KeyboardForwarder = class {
255
+ listener;
256
+ destroyed = false;
257
+ constructor(send, customKeys) {
258
+ const config = customKeys ?? null;
259
+ this.listener = (event) => {
260
+ if (this.destroyed) return;
261
+ if (this.shouldForward(event, config)) {
262
+ event.preventDefault();
263
+ send(chunkISV6HFQE_cjs.KEYDOWN_METHOD, {
264
+ key: event.key,
265
+ ctrlKey: event.ctrlKey,
266
+ metaKey: event.metaKey,
267
+ shiftKey: event.shiftKey,
268
+ altKey: event.altKey
269
+ });
270
+ }
271
+ };
272
+ document.addEventListener("keydown", this.listener);
273
+ }
274
+ destroy() {
275
+ if (this.destroyed) return;
276
+ this.destroyed = true;
277
+ document.removeEventListener("keydown", this.listener);
278
+ }
279
+ shouldForward(event, config) {
280
+ if (config && config.length === 0) return false;
281
+ if (config) {
282
+ return config.some(
283
+ (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)
284
+ );
285
+ }
286
+ if (event.key === "Escape") return true;
287
+ if (event.ctrlKey || event.metaKey) {
288
+ const key = event.key.toLowerCase();
289
+ if (key === "c" || key === "v" || key === "x" || key === "a") return false;
290
+ return true;
291
+ }
292
+ return false;
293
+ }
294
+ };
295
+ function createResizer(send, autoResize) {
296
+ let destroyed = false;
297
+ let observer = null;
298
+ function measureAndSend() {
299
+ if (destroyed) return;
300
+ const width = document.body.scrollWidth;
301
+ const height = document.body.scrollHeight;
302
+ send(extApps.SIZE_CHANGED_METHOD, { width, height });
303
+ }
304
+ function resize(width, height) {
305
+ if (destroyed) return;
306
+ if (width !== void 0 && height !== void 0) {
307
+ send(extApps.SIZE_CHANGED_METHOD, { width, height });
308
+ } else {
309
+ measureAndSend();
310
+ }
311
+ }
312
+ function destroy() {
313
+ if (destroyed) return;
314
+ destroyed = true;
315
+ observer?.disconnect();
316
+ observer = null;
317
+ }
318
+ return { resize, measureAndSend, destroy };
319
+ }
320
+
321
+ // src/connect.ts
322
+ var NIMBLEBRAIN_HOST = "nimblebrain";
323
+ var CHAT_CONTEXT_META_KEY = "ai.nimblebrain/context";
324
+ var NO_DEADLINE = { timeout: 2147483647 };
325
+ var MAPPED_EVENTS = [
326
+ ["toolinput", extApps.TOOL_INPUT_METHOD],
327
+ ["toolinputpartial", extApps.TOOL_INPUT_PARTIAL_METHOD],
328
+ ["toolresult", extApps.TOOL_RESULT_METHOD],
329
+ ["toolcancelled", extApps.TOOL_CANCELLED_METHOD]
330
+ ];
331
+ async function connect(options) {
332
+ const { name, version, autoResize = false, forwardKeys } = options;
333
+ let destroyed = false;
334
+ let hostInfo = { name: "unknown", version: "unknown" };
335
+ let isNimbleBrainHost = false;
336
+ let toolInfo = null;
337
+ let containerDimensions = null;
338
+ let hostCapabilities = {};
339
+ let hostTasksCapability;
340
+ let keyboard = null;
341
+ let appliedTheme = { mode: "light", tokens: {} };
342
+ const appCapabilities = {
343
+ tasks: {
344
+ cancel: {},
345
+ requests: { tools: { call: {} } }
346
+ }
347
+ };
348
+ const client = new extApps.App(
349
+ { name, version },
350
+ appCapabilities,
351
+ { autoResize }
352
+ );
353
+ function currentContext() {
354
+ return client.getHostContext() ?? {};
355
+ }
356
+ function resolveTheme() {
357
+ return chunkYSL7KJNU_cjs.extractTheme(currentContext());
358
+ }
359
+ function applyFontsFrom(ctx) {
360
+ const css = chunkYSL7KJNU_cjs.extractHostFontCss(ctx);
361
+ if (css !== void 0 && typeof document !== "undefined") extApps.applyHostFonts(css);
362
+ }
363
+ function applyResolvedTheme() {
364
+ const theme = resolveTheme();
365
+ chunkYSL7KJNU_cjs.applyTheme(theme.mode, theme.tokens);
366
+ appliedTheme = theme;
367
+ return theme;
368
+ }
369
+ const handlers = /* @__PURE__ */ new Map();
370
+ const themeCallbacks = /* @__PURE__ */ new Set();
371
+ const hostContextCallbacks = /* @__PURE__ */ new Set();
372
+ function fanOut(method, payload) {
373
+ const set = handlers.get(method);
374
+ if (!set) return;
375
+ for (const handler of set) handler(payload);
376
+ }
377
+ function sendRaw(method, params) {
378
+ if (destroyed) return;
379
+ void client.notification({ method, params }).catch(() => {
380
+ });
381
+ }
382
+ function requestRaw(method, params) {
383
+ return client.request({ method, params }, types_js.ResultSchema, NO_DEADLINE);
384
+ }
385
+ for (const [event, method] of MAPPED_EVENTS) {
386
+ client.addEventListener(event, (params) => {
387
+ if (destroyed) return;
388
+ fanOut(
389
+ method,
390
+ method === extApps.TOOL_RESULT_METHOD ? parseToolResultParams(params) : params
391
+ );
392
+ });
393
+ }
394
+ client.addEventListener("hostcontextchanged", (params) => {
395
+ if (destroyed) return;
396
+ const prevTheme = appliedTheme;
397
+ applyFontsFrom(params);
398
+ const nextTheme = applyResolvedTheme();
399
+ const merged = currentContext();
400
+ for (const cb of hostContextCallbacks) cb(merged);
401
+ fanOut(extApps.HOST_CONTEXT_CHANGED_METHOD, params);
402
+ if (!themesEqual(prevTheme, nextTheme)) {
403
+ for (const cb of themeCallbacks) cb(nextTheme);
404
+ }
405
+ });
406
+ client.onteardown = (params) => {
407
+ if (!destroyed) fanOut(extApps.RESOURCE_TEARDOWN_METHOD, params);
408
+ return {};
409
+ };
410
+ client.fallbackNotificationHandler = async (notification) => {
411
+ if (destroyed) return;
412
+ fanOut(notification.method, notification.params);
413
+ };
414
+ function subscribe(event, handler) {
415
+ switch (event) {
416
+ case "theme-changed":
417
+ themeCallbacks.add(handler);
418
+ return () => {
419
+ themeCallbacks.delete(handler);
420
+ };
421
+ case "host-context-changed":
422
+ hostContextCallbacks.add(handler);
423
+ return () => {
424
+ hostContextCallbacks.delete(handler);
425
+ };
426
+ default: {
427
+ const method = chunkISV6HFQE_cjs.resolveEventMethod(event);
428
+ if (!handlers.has(method)) handlers.set(method, /* @__PURE__ */ new Set());
429
+ handlers.get(method)?.add(handler);
430
+ return () => {
431
+ const set = handlers.get(method);
432
+ if (set) {
433
+ set.delete(handler);
434
+ if (set.size === 0) handlers.delete(method);
435
+ }
436
+ };
437
+ }
438
+ }
439
+ }
440
+ if (options.on) {
441
+ for (const [event, handler] of Object.entries(options.on)) {
442
+ if (typeof handler === "function") subscribe(event, handler);
443
+ }
444
+ }
445
+ await client.connect();
446
+ const hostVersion = client.getHostVersion();
447
+ hostInfo = {
448
+ name: hostVersion?.name ?? "unknown",
449
+ version: hostVersion?.version ?? "unknown"
450
+ };
451
+ isNimbleBrainHost = hostInfo.name === NIMBLEBRAIN_HOST;
452
+ hostCapabilities = client.getHostCapabilities() ?? {};
453
+ hostTasksCapability = readHostTasksCapability(hostCapabilities);
454
+ const initialContext = client.getHostContext();
455
+ if (initialContext) {
456
+ if (initialContext.toolInfo && typeof initialContext.toolInfo === "object") {
457
+ toolInfo = {
458
+ tool: initialContext.toolInfo.tool ?? {}
459
+ };
460
+ }
461
+ if (initialContext.containerDimensions && typeof initialContext.containerDimensions === "object") {
462
+ containerDimensions = initialContext.containerDimensions;
463
+ }
464
+ applyFontsFrom(initialContext);
465
+ applyResolvedTheme();
466
+ }
467
+ if (forwardKeys && hostCapabilities.experimental?.[chunkISV6HFQE_cjs.NIMBLEBRAIN_EXTENSIONS.keydown.capability] !== void 0) {
468
+ keyboard = new KeyboardForwarder(sendRaw, forwardKeys === true ? void 0 : forwardKeys);
469
+ }
470
+ const resizer = createResizer(sendRaw);
471
+ if (!autoResize) resizer.measureAndSend();
472
+ const taskRouter = createTaskStatusRouter(
473
+ (method, handler) => subscribe(method, handler)
474
+ );
475
+ const app = {
476
+ get theme() {
477
+ return resolveTheme();
478
+ },
479
+ get hostInfo() {
480
+ return { ...hostInfo };
481
+ },
482
+ get hostContext() {
483
+ return currentContext();
484
+ },
485
+ get hostCapabilities() {
486
+ return hostCapabilities;
487
+ },
488
+ get isNimbleBrainHost() {
489
+ return isNimbleBrainHost;
490
+ },
491
+ get toolInfo() {
492
+ return toolInfo;
493
+ },
494
+ get containerDimensions() {
495
+ return containerDimensions;
496
+ },
497
+ get destroyed() {
498
+ return destroyed;
499
+ },
500
+ get supportsTasks() {
501
+ return hostTasksCapability?.requests?.tools?.call !== void 0;
502
+ },
503
+ on(event, handler) {
504
+ return subscribe(event, handler);
505
+ },
506
+ resize(width, height) {
507
+ resizer.resize(width, height);
508
+ },
509
+ openLink(url) {
510
+ if (destroyed) return;
511
+ if (!hostCapabilities.openLinks) {
512
+ window.open(url, "_blank", "noopener");
513
+ return;
514
+ }
515
+ const params = { url };
516
+ client.openLink(params, NO_DEADLINE).catch(() => {
517
+ window.open(url, "_blank", "noopener");
518
+ });
519
+ },
520
+ updateModelContext(state, summary) {
521
+ if (destroyed || !hostCapabilities.updateModelContext) return;
522
+ const params = {
523
+ structuredContent: state,
524
+ ...summary !== void 0 && {
525
+ content: [{ type: "text", text: summary }]
526
+ }
527
+ };
528
+ client.updateModelContext(params, NO_DEADLINE).catch(() => {
529
+ });
530
+ },
531
+ async callTool(toolName, args) {
532
+ if (!hostCapabilities.serverTools) {
533
+ throw new HostCapabilityError("callTool", "serverTools");
534
+ }
535
+ const params = {
536
+ name: toolName,
537
+ arguments: args ?? {}
538
+ };
539
+ const raw = await client.request(
540
+ { method: chunkISV6HFQE_cjs.TOOLS_CALL_METHOD, params },
541
+ types_js.CallToolResultSchema,
542
+ NO_DEADLINE
543
+ );
544
+ return parseToolResult(raw);
545
+ },
546
+ async readServerResource(params) {
547
+ if (!hostCapabilities.serverResources) {
548
+ throw new HostCapabilityError("readServerResource", "serverResources");
549
+ }
550
+ return await client.readServerResource(params, NO_DEADLINE);
551
+ },
552
+ sendMessage(text, context) {
553
+ if (destroyed || !hostCapabilities.message) return;
554
+ const textBlock = {
555
+ type: "text",
556
+ text,
557
+ // The chat context is a NimbleBrain host field; other hosts ignore it,
558
+ // but there is no reason to spend the bytes off one.
559
+ ...isNimbleBrainHost && context && { _meta: { [CHAT_CONTEXT_META_KEY]: context } }
560
+ };
561
+ const params = {
562
+ role: "user",
563
+ content: [textBlock]
564
+ };
565
+ client.sendMessage(params, NO_DEADLINE).catch(() => {
566
+ });
567
+ },
568
+ destroy() {
569
+ if (destroyed) return;
570
+ destroyed = true;
571
+ keyboard?.destroy();
572
+ resizer.destroy();
573
+ taskRouter.dispose();
574
+ handlers.clear();
575
+ themeCallbacks.clear();
576
+ hostContextCallbacks.clear();
577
+ void client.close();
578
+ }
579
+ };
580
+ registerInternals(app, {
581
+ send(method, params) {
582
+ sendRaw(method, params);
583
+ },
584
+ request(method, params) {
585
+ return requestRaw(method, params);
586
+ },
587
+ onMessage(method, handler) {
588
+ return subscribe(method, handler);
589
+ },
590
+ taskRouter,
591
+ get hostTasksCapability() {
592
+ return hostTasksCapability;
593
+ }
594
+ });
595
+ return app;
596
+ }
597
+ function themesEqual(a, b) {
598
+ if (a.mode !== b.mode) return false;
599
+ const aKeys = Object.keys(a.tokens);
600
+ const bKeys = Object.keys(b.tokens);
601
+ if (aKeys.length !== bKeys.length) return false;
602
+ for (const k of aKeys) {
603
+ if (a.tokens[k] !== b.tokens[k]) return false;
604
+ }
605
+ return true;
606
+ }
607
+
608
+ // src/extensions.ts
609
+ function hostSupports(app, extension) {
610
+ const { capability } = chunkISV6HFQE_cjs.NIMBLEBRAIN_EXTENSIONS[extension];
611
+ return app.hostCapabilities.experimental?.[capability] !== void 0;
612
+ }
613
+ var DEFAULT_MAX_FILE_SIZE = 26214400;
614
+ function action(app, name, params) {
615
+ if (!hostSupports(app, "action")) return;
616
+ internalsFor(app).send(chunkISV6HFQE_cjs.NIMBLEBRAIN_EXTENSIONS.action.method, { action: name, ...params });
617
+ }
618
+ async function pickFile(app, options) {
619
+ requireRequestFile("pickFile", app);
620
+ const files = await requestFile(app, options, false);
621
+ return files[0] ?? null;
622
+ }
623
+ async function pickFiles(app, options) {
624
+ requireRequestFile("pickFiles", app);
625
+ return await requestFile(app, options, true);
626
+ }
627
+ function requireRequestFile(feature, app) {
628
+ if (!hostSupports(app, "requestFile")) {
629
+ throw new HostCapabilityError(feature, chunkISV6HFQE_cjs.NIMBLEBRAIN_EXTENSIONS.requestFile.capability);
630
+ }
631
+ }
632
+ async function requestFile(app, options, multiple) {
633
+ const result = await internalsFor(app).request(chunkISV6HFQE_cjs.NIMBLEBRAIN_EXTENSIONS.requestFile.method, {
634
+ accept: options?.accept,
635
+ maxSize: options?.maxSize ?? DEFAULT_MAX_FILE_SIZE,
636
+ multiple
637
+ });
638
+ const files = result?.files;
639
+ if (files === void 0) {
640
+ throw new Error(
641
+ "ai.nimblebrain/request-file returned no `files`. The host is older than this SDK targets: it answers the picker with a bare array or `null`, which a spec-compliant client cannot parse."
642
+ );
643
+ }
644
+ if (!Array.isArray(files)) {
645
+ throw new Error("ai.nimblebrain/request-file returned a `files` field that is not an array.");
646
+ }
647
+ return files.map(validateFileResult);
648
+ }
649
+ function validateFileResult(value) {
650
+ if (typeof value !== "object" || value === null || typeof value.id !== "string") {
651
+ throw new Error(
652
+ "ai.nimblebrain/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)."
653
+ );
654
+ }
655
+ return value;
656
+ }
657
+
658
+ exports.HostCapabilityError = HostCapabilityError;
659
+ exports.action = action;
660
+ exports.callToolAsTask = callToolAsTask;
661
+ exports.connect = connect;
662
+ exports.hostSupports = hostSupports;
663
+ exports.internalsFor = internalsFor;
664
+ exports.pickFile = pickFile;
665
+ exports.pickFiles = pickFiles;
666
+ //# sourceMappingURL=chunk-HQLSVUOR.cjs.map
667
+ //# sourceMappingURL=chunk-HQLSVUOR.cjs.map