@nimblebrain/synapse 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -369,22 +369,18 @@ npm run ci # Run full CI pipeline locally (lint → typecheck → build
369
369
 
370
370
  ## Publishing
371
371
 
372
- Requires npm login with access to the `@nimblebrain` org.
372
+ Publishing uses npm trusted publishing via GitHub Actions. No `npm login` needed.
373
373
 
374
374
  ```bash
375
- # First time: log in to npm
376
- npm login
377
-
378
- # Bump version (updates package.json and creates a git tag)
375
+ # 1. Bump version in package.json
379
376
  npm version patch # or minor / major
380
377
 
381
- # Publish (build runs automatically via prepublishOnly)
382
- npm publish --access public
383
-
384
- # Push the version tag
378
+ # 2. Push commit + tag — CI verifies (lint, typecheck, build, test) then publishes
385
379
  git push origin main --tags
386
380
  ```
387
381
 
382
+ The `publish.yml` workflow triggers on `v*` tags. It runs the full CI suite, verifies the tag matches `package.json`, then publishes with `--provenance`.
383
+
388
384
  ## License
389
385
 
390
386
  [MIT](LICENSE)
@@ -1,5 +1,9 @@
1
1
  'use strict';
2
2
 
3
+ var extApps = require('@modelcontextprotocol/ext-apps');
4
+
5
+ // src/connect.ts
6
+
3
7
  // src/content-parser.ts
4
8
  function parseToolResultParams(params) {
5
9
  const raw = params ?? {};
@@ -31,15 +35,13 @@ function parseToolResultParams(params) {
31
35
  }
32
36
  return { content: rawContent ?? null, structuredContent: null, raw };
33
37
  }
34
-
35
- // src/event-map.ts
36
38
  var EVENT_MAP = {
37
- "tool-result": "ui/notifications/tool-result",
38
- "tool-input": "ui/notifications/tool-input",
39
- "tool-input-partial": "ui/notifications/tool-input-partial",
40
- "tool-cancelled": "ui/notifications/tool-cancelled",
41
- "theme-changed": "ui/notifications/host-context-changed",
42
- teardown: "ui/resource-teardown"
39
+ "tool-result": extApps.TOOL_RESULT_METHOD,
40
+ "tool-input": extApps.TOOL_INPUT_METHOD,
41
+ "tool-input-partial": extApps.TOOL_INPUT_PARTIAL_METHOD,
42
+ "tool-cancelled": extApps.TOOL_CANCELLED_METHOD,
43
+ "theme-changed": extApps.HOST_CONTEXT_CHANGED_METHOD,
44
+ teardown: extApps.RESOURCE_TEARDOWN_METHOD
43
45
  };
44
46
  function resolveEventMethod(name) {
45
47
  return EVENT_MAP[name] ?? name;
@@ -230,55 +232,53 @@ async function connect(options) {
230
232
  const handlers = /* @__PURE__ */ new Map();
231
233
  const resizer = createResizer((method, params) => transport.send(method, params), autoResize);
232
234
  resizer.measureAndSend();
233
- const result = await transport.request("ui/initialize", {
234
- protocolVersion: "2026-01-26",
235
- clientInfo: { name, version },
236
- capabilities: {}
237
- });
238
- const resp = result ?? {};
239
- const serverInfo = safeObj(resp.serverInfo);
240
- hostInfo = {
241
- name: typeof serverInfo?.name === "string" ? serverInfo.name : "unknown",
242
- version: typeof serverInfo?.version === "string" ? serverInfo.version : "unknown"
235
+ const initParams = {
236
+ protocolVersion: extApps.LATEST_PROTOCOL_VERSION,
237
+ appInfo: { name, version },
238
+ appCapabilities: {}
243
239
  };
244
- const hostContext = safeObj(resp.hostContext);
245
- if (hostContext) {
246
- const themeRaw = safeObj(hostContext.theme);
247
- if (themeRaw) {
240
+ const result = await transport.request(
241
+ extApps.INITIALIZE_METHOD,
242
+ initParams
243
+ );
244
+ if (result) {
245
+ hostInfo = {
246
+ name: result.hostInfo?.name ?? "unknown",
247
+ version: result.hostInfo?.version ?? "unknown"
248
+ };
249
+ const ctx = result.hostContext;
250
+ if (ctx) {
248
251
  currentTheme = {
249
- mode: themeRaw.mode === "dark" ? "dark" : "light",
250
- tokens: themeRaw.tokens && typeof themeRaw.tokens === "object" && !Array.isArray(themeRaw.tokens) ? themeRaw.tokens : {}
252
+ mode: ctx.theme === "dark" ? "dark" : "light",
253
+ tokens: ctx.styles?.variables && typeof ctx.styles.variables === "object" ? ctx.styles.variables : {}
251
254
  };
255
+ if (ctx.toolInfo && typeof ctx.toolInfo === "object") {
256
+ toolInfo = { tool: ctx.toolInfo.tool ?? {} };
257
+ }
258
+ if (ctx.containerDimensions && typeof ctx.containerDimensions === "object") {
259
+ containerDimensions = ctx.containerDimensions;
260
+ }
261
+ injectCssVariables(ctx.styles?.variables);
252
262
  }
253
- if (hostContext.toolInfo && typeof hostContext.toolInfo === "object") {
254
- const ti = hostContext.toolInfo;
255
- toolInfo = { tool: ti.tool ?? ti };
256
- }
257
- if (hostContext.containerDimensions && typeof hostContext.containerDimensions === "object") {
258
- containerDimensions = hostContext.containerDimensions;
259
- }
260
- const styles = safeObj(hostContext.styles);
261
- injectCssVariables(styles?.variables);
262
263
  }
263
- transport.send("ui/notifications/initialized", {});
264
- const themeMethod = resolveEventMethod("theme-changed");
265
- transport.onMessage(themeMethod, (params) => {
264
+ transport.onMessage(extApps.HOST_CONTEXT_CHANGED_METHOD, (params) => {
266
265
  if (destroyed || !params) return;
267
- const mode = params.theme === "dark" ? "dark" : "light";
268
- const tokens = params.tokens && typeof params.tokens === "object" && !Array.isArray(params.tokens) ? params.tokens : currentTheme.tokens;
266
+ const ctx = params;
267
+ const mode = ctx.theme === "dark" ? "dark" : "light";
268
+ const variables = ctx.styles?.variables;
269
+ const tokens = variables && typeof variables === "object" ? variables : currentTheme.tokens;
269
270
  currentTheme = { mode, tokens };
270
271
  injectCssVariables(tokens);
271
- const set = handlers.get(themeMethod);
272
+ const set = handlers.get(extApps.HOST_CONTEXT_CHANGED_METHOD);
272
273
  if (set) {
273
274
  for (const handler of set) handler(currentTheme);
274
275
  }
275
276
  });
276
- const subscribedMethods = /* @__PURE__ */ new Set([themeMethod]);
277
+ const subscribedMethods = /* @__PURE__ */ new Set([extApps.HOST_CONTEXT_CHANGED_METHOD]);
277
278
  function ensureTransportSub(method) {
278
279
  if (subscribedMethods.has(method)) return;
279
280
  subscribedMethods.add(method);
280
- const toolResultMethod = resolveEventMethod("tool-result");
281
- const isToolResult = method === toolResultMethod;
281
+ const isToolResult = method === extApps.TOOL_RESULT_METHOD;
282
282
  transport.onMessage(method, (params) => {
283
283
  if (destroyed) return;
284
284
  const set = handlers.get(method);
@@ -292,6 +292,17 @@ async function connect(options) {
292
292
  }
293
293
  });
294
294
  }
295
+ if (options.on) {
296
+ for (const [event, handler] of Object.entries(options.on)) {
297
+ if (typeof handler === "function") {
298
+ const method = resolveEventMethod(event);
299
+ if (!handlers.has(method)) handlers.set(method, /* @__PURE__ */ new Set());
300
+ handlers.get(method)?.add(handler);
301
+ ensureTransportSub(method);
302
+ }
303
+ }
304
+ }
305
+ transport.send(extApps.INITIALIZED_METHOD, {});
295
306
  const app = {
296
307
  get theme() {
297
308
  return { ...currentTheme };
@@ -325,34 +336,43 @@ async function connect(options) {
325
336
  },
326
337
  openLink(url) {
327
338
  if (destroyed) return;
328
- transport.send("ui/open-link", { url });
339
+ const params = { url };
340
+ transport.request(extApps.OPEN_LINK_METHOD, params).catch(() => {
341
+ });
329
342
  },
330
343
  updateModelContext(state, summary) {
331
344
  if (destroyed) return;
332
- transport.send("ui/update-model-context", {
345
+ const params = {
333
346
  structuredContent: state,
334
347
  ...summary !== void 0 && {
335
348
  content: [{ type: "text", text: summary }]
336
349
  }
337
- });
350
+ };
351
+ transport.send("ui/update-model-context", params);
338
352
  },
339
353
  async callTool(toolName, args) {
340
- const raw = await transport.request("tools/call", {
354
+ const params = {
341
355
  name: toolName,
342
356
  arguments: args ?? {}
343
- });
357
+ };
358
+ const raw = await transport.request(
359
+ "tools/call",
360
+ params
361
+ );
344
362
  return parseToolResult(raw);
345
363
  },
346
364
  sendMessage(text, context) {
347
365
  if (destroyed) return;
348
- const textBlock = { type: "text", text };
349
- if (context) {
350
- textBlock._meta = { context };
351
- }
352
- transport.send("ui/message", {
366
+ const textBlock = {
367
+ type: "text",
368
+ text,
369
+ ...context && { _meta: { context } }
370
+ };
371
+ const params = {
353
372
  role: "user",
354
373
  content: [textBlock]
355
- });
374
+ };
375
+ transport.send(extApps.MESSAGE_METHOD, params);
356
376
  },
357
377
  destroy() {
358
378
  if (destroyed) return;
@@ -364,12 +384,6 @@ async function connect(options) {
364
384
  };
365
385
  return app;
366
386
  }
367
- function safeObj(value) {
368
- if (value !== null && typeof value === "object" && !Array.isArray(value)) {
369
- return value;
370
- }
371
- return void 0;
372
- }
373
387
  function injectCssVariables(vars) {
374
388
  if (!vars || typeof vars !== "object") return;
375
389
  for (const [k, v] of Object.entries(vars)) {
@@ -387,31 +401,23 @@ var DEFAULT_THEME = {
387
401
  };
388
402
  function detectHost(initResponse) {
389
403
  const resp = initResponse;
390
- const serverInfo = safeObj2(resp?.serverInfo);
391
- const serverName = typeof serverInfo?.name === "string" ? serverInfo.name : "unknown";
392
- const protocolVersion = typeof resp?.protocolVersion === "string" ? resp.protocolVersion : "unknown";
393
- const hostContext = safeObj2(resp?.hostContext);
394
- const theme = extractTheme(hostContext?.theme);
404
+ const hostName = resp?.hostInfo?.name ?? "unknown";
405
+ const protocolVersion = resp?.protocolVersion ?? "unknown";
406
+ const ctx = resp?.hostContext;
407
+ const theme = extractTheme(ctx);
395
408
  return {
396
- isNimbleBrain: serverName === "nimblebrain",
397
- serverName,
409
+ isNimbleBrain: hostName === "nimblebrain",
410
+ serverName: hostName,
398
411
  protocolVersion,
399
412
  theme
400
413
  };
401
414
  }
402
- function extractTheme(raw) {
403
- const obj = safeObj2(raw);
404
- if (!obj) return { ...DEFAULT_THEME };
405
- const mode = obj.mode === "light" || obj.mode === "dark" ? obj.mode : DEFAULT_THEME.mode;
406
- const primaryColor = typeof obj.primaryColor === "string" ? obj.primaryColor : DEFAULT_THEME.primaryColor;
407
- const tokens = obj.tokens !== null && typeof obj.tokens === "object" && !Array.isArray(obj.tokens) ? obj.tokens : {};
408
- return { mode, primaryColor, tokens };
409
- }
410
- function safeObj2(value) {
411
- if (value !== null && typeof value === "object" && !Array.isArray(value)) {
412
- return value;
413
- }
414
- return void 0;
415
+ function extractTheme(ctx) {
416
+ if (!ctx) return { ...DEFAULT_THEME };
417
+ const mode = ctx.theme === "light" || ctx.theme === "dark" ? ctx.theme : DEFAULT_THEME.mode;
418
+ const variables = ctx.styles?.variables;
419
+ const tokens = variables && typeof variables === "object" && !Array.isArray(variables) ? variables : {};
420
+ return { mode, primaryColor: DEFAULT_THEME.primaryColor, tokens };
415
421
  }
416
422
 
417
423
  // src/keyboard.ts
@@ -470,27 +476,23 @@ function createSynapse(options) {
470
476
  let destroyed = false;
471
477
  let stateTimer = null;
472
478
  let keyboard = null;
473
- const ready = transport.request("ui/initialize", {
474
- protocolVersion: "2026-01-26",
475
- clientInfo: { name, version },
476
- capabilities: {}
477
- }).then((result) => {
479
+ const initParams = {
480
+ protocolVersion: extApps.LATEST_PROTOCOL_VERSION,
481
+ appInfo: { name, version },
482
+ appCapabilities: {}
483
+ };
484
+ const ready = transport.request(extApps.INITIALIZE_METHOD, initParams).then((result) => {
478
485
  hostInfo = detectHost(result);
479
486
  currentTheme = hostInfo.theme;
480
- transport.send("ui/notifications/initialized", {});
487
+ transport.send(extApps.INITIALIZED_METHOD, {});
481
488
  keyboard = new KeyboardForwarder(transport, forwardKeys);
482
489
  });
483
- const unsubTheme = transport.onMessage("ui/notifications/host-context-changed", (params) => {
490
+ const unsubTheme = transport.onMessage(extApps.HOST_CONTEXT_CHANGED_METHOD, (params) => {
484
491
  if (!params) return;
485
492
  const mode = params.theme === "dark" ? "dark" : "light";
486
- const tokens = params.tokens && typeof params.tokens === "object" ? params.tokens : currentTheme.tokens;
487
- currentTheme = { mode, primaryColor: currentTheme.primaryColor, tokens };
488
- for (const cb of themeCallbacks) cb(currentTheme);
489
- });
490
- const unsubNbTheme = transport.onMessage("synapse/theme-changed", (params) => {
491
- if (!params) return;
492
- const mode = params.mode === "dark" || params.mode === "light" ? params.mode : currentTheme.mode;
493
- const tokens = params.tokens && typeof params.tokens === "object" ? params.tokens : currentTheme.tokens;
493
+ const styles = params.styles;
494
+ const variables = styles?.variables;
495
+ const tokens = variables && typeof variables === "object" ? variables : currentTheme.tokens;
494
496
  currentTheme = { mode, primaryColor: currentTheme.primaryColor, tokens };
495
497
  for (const cb of themeCallbacks) cb(currentTheme);
496
498
  });
@@ -564,24 +566,27 @@ function createSynapse(options) {
564
566
  transport.send("synapse/action", { action, ...params });
565
567
  },
566
568
  chat(message, context) {
567
- const textBlock = { type: "text", text: message };
568
- if (isNB() && context) {
569
- textBlock._meta = { context };
570
- }
571
- transport.send("ui/message", {
569
+ const textBlock = {
570
+ type: "text",
571
+ text: message,
572
+ ...isNB() && context && { _meta: { context } }
573
+ };
574
+ const params = {
572
575
  role: "user",
573
576
  content: [textBlock]
574
- });
577
+ };
578
+ transport.send(extApps.MESSAGE_METHOD, params);
575
579
  },
576
580
  setVisibleState(state, summary) {
577
581
  if (stateTimer) clearTimeout(stateTimer);
578
582
  stateTimer = setTimeout(() => {
579
- transport.send("ui/update-model-context", {
583
+ const params = {
580
584
  structuredContent: state,
581
585
  ...summary !== void 0 && {
582
586
  content: [{ type: "text", text: summary }]
583
587
  }
584
- });
588
+ };
589
+ transport.send("ui/update-model-context", params);
585
590
  stateTimer = null;
586
591
  }, 250);
587
592
  },
@@ -594,10 +599,10 @@ function createSynapse(options) {
594
599
  });
595
600
  },
596
601
  openLink(url) {
597
- transport.send("ui/open-link", { url });
598
- if (!isNB()) {
602
+ const params = { url };
603
+ transport.request(extApps.OPEN_LINK_METHOD, params).catch(() => {
599
604
  window.open(url, "_blank", "noopener");
600
- }
605
+ });
601
606
  },
602
607
  async pickFile(options2) {
603
608
  if (!isNB()) {
@@ -634,7 +639,6 @@ function createSynapse(options) {
634
639
  if (stateTimer) clearTimeout(stateTimer);
635
640
  keyboard?.destroy();
636
641
  unsubTheme();
637
- unsubNbTheme();
638
642
  unsubData();
639
643
  unsubAction();
640
644
  themeCallbacks.clear();
@@ -648,5 +652,5 @@ function createSynapse(options) {
648
652
 
649
653
  exports.connect = connect;
650
654
  exports.createSynapse = createSynapse;
651
- //# sourceMappingURL=chunk-B3T6NB32.cjs.map
652
- //# sourceMappingURL=chunk-B3T6NB32.cjs.map
655
+ //# sourceMappingURL=chunk-54YIV4ZL.cjs.map
656
+ //# sourceMappingURL=chunk-54YIV4ZL.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/content-parser.ts","../src/event-map.ts","../src/resize.ts","../src/result-parser.ts","../src/transport.ts","../src/connect.ts","../src/detection.ts","../src/keyboard.ts","../src/core.ts"],"names":["TOOL_RESULT_METHOD","TOOL_INPUT_METHOD","TOOL_INPUT_PARTIAL_METHOD","TOOL_CANCELLED_METHOD","HOST_CONTEXT_CHANGED_METHOD","RESOURCE_TEARDOWN_METHOD","LATEST_PROTOCOL_VERSION","INITIALIZE_METHOD","INITIALIZED_METHOD","OPEN_LINK_METHOD","MESSAGE_METHOD","options"],"mappings":"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","file":"chunk-54YIV4ZL.cjs","sourcesContent":["import type { ToolResultData } from \"./types.js\";\n\nexport type { ToolResultData };\n\n/**\n * Parse inbound `ui/notifications/tool-result` notification params into\n * a consistent `ToolResultData` shape.\n *\n * Implements the 5-step parsing algorithm from the RFC:\n * 1. If `params.structuredContent` exists → use it as content.\n * 2. Else if `params.content` is an array of `{type:\"text\", text}` blocks →\n * join the text values and try JSON.parse.\n * 3. Else if `params.content` is a string → try JSON.parse.\n * 4. If JSON.parse fails in steps 2 or 3 → deliver the raw string.\n * 5. Return `{ content, structuredContent, raw }`.\n */\nexport function parseToolResultParams(params: Record<string, unknown> | undefined): ToolResultData {\n const raw = params ?? {};\n const structuredContent = raw.structuredContent ?? null;\n\n // Step 1: If structuredContent exists, use it as content\n if (structuredContent != null) {\n return { content: structuredContent, structuredContent, raw };\n }\n\n const rawContent = raw.content;\n\n // Step 2: If content is array of {type:\"text\", text} blocks, join texts and try JSON.parse\n if (Array.isArray(rawContent)) {\n const texts = rawContent\n .filter(\n (block: unknown) =>\n block != null &&\n typeof block === \"object\" &&\n (block as Record<string, unknown>).type === \"text\" &&\n typeof (block as Record<string, unknown>).text === \"string\",\n )\n .map((block: unknown) => (block as Record<string, unknown>).text as string);\n\n if (texts.length > 0) {\n const joined = texts.join(\"\");\n try {\n return { content: JSON.parse(joined), structuredContent: null, raw };\n } catch {\n return { content: joined, structuredContent: null, raw };\n }\n }\n\n // No text blocks — return raw content array\n return { content: rawContent, structuredContent: null, raw };\n }\n\n // Step 3: If content is string, try JSON.parse\n if (typeof rawContent === \"string\") {\n try {\n return { content: JSON.parse(rawContent), structuredContent: null, raw };\n } catch {\n return { content: rawContent, structuredContent: null, raw };\n }\n }\n\n // Fallback: return content as-is (could be null, object, etc.)\n return { content: rawContent ?? null, structuredContent: null, raw };\n}\n","import {\n HOST_CONTEXT_CHANGED_METHOD,\n RESOURCE_TEARDOWN_METHOD,\n TOOL_CANCELLED_METHOD,\n TOOL_INPUT_METHOD,\n TOOL_INPUT_PARTIAL_METHOD,\n TOOL_RESULT_METHOD,\n} from \"@modelcontextprotocol/ext-apps\";\n\n/**\n * Maps short event names used in App.on() to full MCP method names.\n * Uses canonical constants from @modelcontextprotocol/ext-apps to stay\n * in sync with the spec — if the spec changes a method name, this breaks\n * at compile time, not silently at runtime.\n */\nconst EVENT_MAP: Record<string, string> = {\n \"tool-result\": TOOL_RESULT_METHOD,\n \"tool-input\": TOOL_INPUT_METHOD,\n \"tool-input-partial\": TOOL_INPUT_PARTIAL_METHOD,\n \"tool-cancelled\": TOOL_CANCELLED_METHOD,\n \"theme-changed\": HOST_CONTEXT_CHANGED_METHOD,\n teardown: RESOURCE_TEARDOWN_METHOD,\n};\n\n/**\n * Resolve a short event name to a full MCP method name.\n * Returns the mapped method if found, otherwise passes through as-is.\n */\nexport function resolveEventMethod(name: string): string {\n return EVENT_MAP[name] ?? name;\n}\n","type SendFn = (method: string, params: Record<string, unknown>) => void;\n\ninterface Resizer {\n resize(width?: number, height?: number): void;\n measureAndSend(): void;\n destroy(): void;\n}\n\nexport function createResizer(send: SendFn, autoResize: boolean): Resizer {\n let destroyed = false;\n let observer: ResizeObserver | null = null;\n let rafId: number | null = null;\n\n function measureAndSend(): void {\n if (destroyed) return;\n const width = document.body.scrollWidth;\n const height = document.body.scrollHeight;\n send(\"ui/notifications/size-changed\", { width, height });\n }\n\n function resize(width?: number, height?: number): void {\n if (destroyed) return;\n if (width !== undefined && height !== undefined) {\n send(\"ui/notifications/size-changed\", { width, height });\n } else {\n measureAndSend();\n }\n }\n\n // Auto mode: attach ResizeObserver, debounced at 16ms via requestAnimationFrame\n if (autoResize && typeof ResizeObserver !== \"undefined\") {\n observer = new ResizeObserver(() => {\n if (destroyed) return;\n if (rafId !== null) cancelAnimationFrame(rafId);\n rafId = requestAnimationFrame(() => {\n rafId = null;\n measureAndSend();\n });\n });\n observer.observe(document.body);\n }\n\n function destroy(): void {\n if (destroyed) return;\n destroyed = true;\n if (rafId !== null) cancelAnimationFrame(rafId);\n observer?.disconnect();\n observer = null;\n }\n\n return { resize, measureAndSend, destroy };\n}\n","import type { ToolCallResult } from \"./types.js\";\n\n/**\n * Normalize a raw tool call response into a consistent `ToolCallResult`.\n *\n * Handles three shapes:\n * 1. MCP `CallToolResult` — has a `content` array with typed blocks.\n * 2. Raw JSON object (NimbleBrain bridge) — used as-is.\n * 3. Null / undefined — returns `{ data: null, isError: false }`.\n */\nexport function parseToolResult(raw: unknown): ToolCallResult {\n if (raw == null) {\n return { data: null, isError: false };\n }\n\n if (isCallToolResult(raw)) {\n return parseCallToolResult(raw);\n }\n\n // Raw JSON object — pass through.\n return { data: raw, isError: false };\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\ninterface McpTextBlock {\n type: \"text\";\n text: string;\n}\n\ninterface McpCallToolResult {\n content: unknown[];\n isError?: boolean;\n}\n\nfunction isCallToolResult(value: unknown): value is McpCallToolResult {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n return false;\n }\n return Array.isArray((value as Record<string, unknown>).content);\n}\n\nfunction isTextBlock(block: unknown): block is McpTextBlock {\n if (block === null || typeof block !== \"object\" || Array.isArray(block)) {\n return false;\n }\n const obj = block as Record<string, unknown>;\n return obj.type === \"text\" && typeof obj.text === \"string\";\n}\n\nfunction parseCallToolResult(result: McpCallToolResult): ToolCallResult {\n const isError = result.isError === true;\n const content = result.content;\n\n if (content.length === 0) {\n return { data: null, isError };\n }\n\n const firstText = content.find(isTextBlock);\n\n if (!firstText) {\n // No text blocks — return the full content array so callers can inspect it.\n return { data: content, isError };\n }\n\n // Try to parse JSON from the text block.\n try {\n return { data: JSON.parse(firstText.text), isError };\n } catch {\n // Invalid JSON — return the raw string.\n return { data: firstText.text, isError };\n }\n}\n","import type {\n JsonRpcMessage,\n JsonRpcNotification,\n JsonRpcRequest,\n JsonRpcResponse,\n} from \"./types.js\";\n\ntype PendingEntry = {\n resolve: (value: unknown) => void;\n reject: (reason: unknown) => void;\n};\n\ntype MessageHandler = (params: Record<string, unknown> | undefined) => void;\n\nexport class SynapseTransport {\n private counter = 0;\n private destroyed = false;\n private pending = new Map<string, PendingEntry>();\n private handlers = new Map<string, Set<MessageHandler>>();\n private listener: (event: MessageEvent) => void;\n\n constructor() {\n this.listener = (event: MessageEvent) => this.handleMessage(event);\n window.addEventListener(\"message\", this.listener);\n }\n\n send(method: string, params?: Record<string, unknown>): void {\n if (this.destroyed) return;\n\n const msg: JsonRpcNotification = {\n jsonrpc: \"2.0\",\n method,\n ...(params !== undefined && { params }),\n };\n window.parent.postMessage(msg, \"*\");\n }\n\n request(method: string, params?: Record<string, unknown>): Promise<unknown> {\n if (this.destroyed) {\n return Promise.reject(new Error(\"Transport destroyed\"));\n }\n\n const id = `syn-${++this.counter}`;\n const msg: JsonRpcRequest = {\n jsonrpc: \"2.0\",\n method,\n id,\n ...(params !== undefined && { params }),\n };\n\n return new Promise<unknown>((resolve, reject) => {\n this.pending.set(id, { resolve, reject });\n window.parent.postMessage(msg, \"*\");\n });\n }\n\n onMessage(method: string, callback: MessageHandler): () => void {\n if (!this.handlers.has(method)) {\n this.handlers.set(method, new Set());\n }\n this.handlers.get(method)?.add(callback);\n\n return () => {\n const set = this.handlers.get(method);\n if (set) {\n set.delete(callback);\n if (set.size === 0) {\n this.handlers.delete(method);\n }\n }\n };\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n\n window.removeEventListener(\"message\", this.listener);\n\n const error = new Error(\"Transport destroyed\");\n for (const entry of this.pending.values()) {\n entry.reject(error);\n }\n this.pending.clear();\n this.handlers.clear();\n }\n\n private handleMessage(event: MessageEvent): void {\n if (this.destroyed) return;\n\n const data = event.data as JsonRpcMessage;\n if (!data || data.jsonrpc !== \"2.0\") return;\n\n // Response to a pending request\n if (\"id\" in data && data.id && !(\"method\" in data)) {\n const response = data as JsonRpcResponse;\n const entry = this.pending.get(response.id);\n if (!entry) return;\n this.pending.delete(response.id);\n\n if (response.error) {\n const err = new Error(response.error.message);\n (err as any).code = response.error.code;\n (err as any).data = response.error.data;\n entry.reject(err);\n } else {\n entry.resolve(response.result);\n }\n return;\n }\n\n // Incoming notification\n if (\"method\" in data && !(\"id\" in data && data.id)) {\n const notification = data as JsonRpcNotification;\n const set = this.handlers.get(notification.method);\n if (set) {\n for (const handler of set) {\n handler(notification.params);\n }\n }\n }\n }\n}\n","import type {\n McpUiHostContext,\n McpUiHostContextChangedNotification,\n McpUiInitializedNotification,\n McpUiInitializeRequest,\n McpUiInitializeResult,\n McpUiMessageRequest,\n McpUiOpenLinkRequest,\n McpUiSizeChangedNotification,\n McpUiToolResultNotification,\n McpUiUpdateModelContextRequest,\n} from \"@modelcontextprotocol/ext-apps\";\nimport {\n HOST_CONTEXT_CHANGED_METHOD,\n INITIALIZE_METHOD,\n INITIALIZED_METHOD,\n LATEST_PROTOCOL_VERSION,\n MESSAGE_METHOD,\n OPEN_LINK_METHOD,\n SIZE_CHANGED_METHOD,\n TOOL_CANCELLED_METHOD,\n TOOL_INPUT_METHOD,\n TOOL_INPUT_PARTIAL_METHOD,\n TOOL_RESULT_METHOD,\n} from \"@modelcontextprotocol/ext-apps\";\nimport type { CallToolRequest, TextContent } from \"@modelcontextprotocol/sdk/types.js\";\n\nimport { parseToolResultParams } from \"./content-parser.js\";\nimport { resolveEventMethod } from \"./event-map.js\";\nimport { createResizer } from \"./resize.js\";\nimport { parseToolResult } from \"./result-parser.js\";\nimport { SynapseTransport } from \"./transport.js\";\nimport type { App, ConnectOptions, Dimensions, Theme, ToolCallResult } from \"./types.js\";\n\n/**\n * Connect to a MCP Apps host.\n *\n * Owns the full ext-apps handshake, content parsing, resize management,\n * and event routing. Returns a ready-to-use `App` object.\n */\nexport async function connect(options: ConnectOptions): Promise<App> {\n const { name, version, autoResize = false } = options;\n\n const transport = new SynapseTransport();\n let destroyed = false;\n\n // --- Mutable state ---\n let currentTheme: Theme = { mode: \"light\", tokens: {} };\n let hostInfo: { name: string; version: string } = { name: \"unknown\", version: \"unknown\" };\n let toolInfo: { tool: Record<string, unknown> } | null = null;\n let containerDimensions: Dimensions | null = null;\n\n // --- Event handlers ---\n const handlers = new Map<string, Set<(params: unknown) => void>>();\n\n // --- Step 1: Set up message listener (handled by SynapseTransport constructor) ---\n\n // --- Step 2: Send initial size ---\n const resizer = createResizer((method, params) => transport.send(method, params), autoResize);\n resizer.measureAndSend();\n\n // --- Steps 3-4: Send ui/initialize and wait for response ---\n const initParams: McpUiInitializeRequest[\"params\"] = {\n protocolVersion: LATEST_PROTOCOL_VERSION,\n appInfo: { name, version },\n appCapabilities: {},\n };\n\n const result = (await transport.request(\n INITIALIZE_METHOD,\n initParams as unknown as Record<string, unknown>,\n )) as McpUiInitializeResult | null;\n\n // --- Step 5: Extract theme, hostInfo, toolInfo, containerDimensions ---\n if (result) {\n hostInfo = {\n name: result.hostInfo?.name ?? \"unknown\",\n version: result.hostInfo?.version ?? \"unknown\",\n };\n\n const ctx: McpUiHostContext | undefined = result.hostContext;\n if (ctx) {\n currentTheme = {\n mode: ctx.theme === \"dark\" ? \"dark\" : \"light\",\n tokens:\n ctx.styles?.variables && typeof ctx.styles.variables === \"object\"\n ? (ctx.styles.variables as Record<string, string>)\n : {},\n };\n\n if (ctx.toolInfo && typeof ctx.toolInfo === \"object\") {\n toolInfo = { tool: (ctx.toolInfo.tool as unknown as Record<string, unknown>) ?? {} };\n }\n if (ctx.containerDimensions && typeof ctx.containerDimensions === \"object\") {\n containerDimensions = ctx.containerDimensions as Dimensions;\n }\n\n // Inject host CSS variables into the DOM\n injectCssVariables(ctx.styles?.variables as Record<string, string> | undefined);\n }\n }\n\n // --- Route incoming notifications to registered handlers ---\n\n // Special handling for host-context-changed: update internal theme state\n transport.onMessage(HOST_CONTEXT_CHANGED_METHOD, (params) => {\n if (destroyed || !params) return;\n const ctx = params as Partial<McpUiHostContextChangedNotification[\"params\"]>;\n const mode = ctx.theme === \"dark\" ? \"dark\" : \"light\";\n const variables = ctx.styles?.variables;\n const tokens =\n variables && typeof variables === \"object\"\n ? (variables as Record<string, string>)\n : currentTheme.tokens;\n currentTheme = { mode, tokens };\n injectCssVariables(tokens);\n const set = handlers.get(HOST_CONTEXT_CHANGED_METHOD);\n if (set) {\n for (const handler of set) handler(currentTheme);\n }\n });\n\n // Helper to ensure transport subscription exists for a method\n const subscribedMethods = new Set<string>([HOST_CONTEXT_CHANGED_METHOD]);\n\n function ensureTransportSub(method: string): void {\n if (subscribedMethods.has(method)) return;\n subscribedMethods.add(method);\n\n const isToolResult = method === TOOL_RESULT_METHOD;\n\n transport.onMessage(method, (params) => {\n if (destroyed) return;\n const set = handlers.get(method);\n if (!set) return;\n for (const handler of set) {\n if (isToolResult) {\n handler(parseToolResultParams(params));\n } else {\n handler(params);\n }\n }\n });\n }\n\n // --- Step 6: Pre-register handlers from options.on, then send initialized ---\n if (options.on) {\n for (const [event, handler] of Object.entries(options.on)) {\n if (typeof handler === \"function\") {\n const method = resolveEventMethod(event);\n if (!handlers.has(method)) handlers.set(method, new Set());\n handlers.get(method)?.add(handler);\n ensureTransportSub(method);\n }\n }\n }\n transport.send(INITIALIZED_METHOD, {});\n\n // --- Step 7: Build and return the App object ---\n const app: App = {\n get theme() {\n return { ...currentTheme };\n },\n get hostInfo() {\n return { ...hostInfo };\n },\n get toolInfo() {\n return toolInfo;\n },\n get containerDimensions() {\n return containerDimensions;\n },\n\n on(event: string, handler: (params: any) => void): () => void {\n const method = resolveEventMethod(event);\n\n if (!handlers.has(method)) {\n handlers.set(method, new Set());\n }\n handlers.get(method)?.add(handler);\n ensureTransportSub(method);\n\n return () => {\n const set = handlers.get(method);\n if (set) {\n set.delete(handler);\n if (set.size === 0) handlers.delete(method);\n }\n };\n },\n\n resize(width?: number, height?: number): void {\n resizer.resize(width, height);\n },\n\n openLink(url: string): void {\n if (destroyed) return;\n const params: McpUiOpenLinkRequest[\"params\"] = { url };\n // Spec: ui/open-link is a request (expects a response), not a notification\n transport\n .request(OPEN_LINK_METHOD, params as unknown as Record<string, unknown>)\n .catch(() => {});\n },\n\n updateModelContext(state: Record<string, unknown>, summary?: string): void {\n if (destroyed) return;\n const params: McpUiUpdateModelContextRequest[\"params\"] = {\n structuredContent: state,\n ...(summary !== undefined && {\n content: [{ type: \"text\", text: summary } satisfies TextContent],\n }),\n };\n transport.send(\"ui/update-model-context\", params as unknown as Record<string, unknown>);\n },\n\n async callTool(toolName: string, args?: Record<string, unknown>): Promise<ToolCallResult> {\n const params: CallToolRequest[\"params\"] = {\n name: toolName,\n arguments: args ?? {},\n };\n const raw = await transport.request(\n \"tools/call\",\n params as unknown as Record<string, unknown>,\n );\n return parseToolResult(raw);\n },\n\n sendMessage(text: string, context?: { action?: string; entity?: string }): void {\n if (destroyed) return;\n const textBlock: TextContent = {\n type: \"text\",\n text,\n ...(context && { _meta: { context } }),\n };\n const params: McpUiMessageRequest[\"params\"] = {\n role: \"user\",\n content: [textBlock],\n };\n transport.send(MESSAGE_METHOD, params as unknown as Record<string, unknown>);\n },\n\n destroy(): void {\n if (destroyed) return;\n destroyed = true;\n resizer.destroy();\n handlers.clear();\n transport.destroy();\n },\n };\n\n return app;\n}\n\n// --- Helpers ---\n\n/** Inject CSS custom properties onto :root so widgets inherit host theming. */\nfunction injectCssVariables(vars: Record<string, string> | undefined | null): void {\n if (!vars || typeof vars !== \"object\") return;\n for (const [k, v] of Object.entries(vars)) {\n if (typeof k === \"string\" && typeof v === \"string\") {\n document.documentElement.style.setProperty(k, v);\n }\n }\n}\n","import type { McpUiHostContext, McpUiInitializeResult } from \"@modelcontextprotocol/ext-apps\";\nimport type { HostInfo, SynapseTheme } from \"./types\";\n\nconst DEFAULT_THEME: SynapseTheme = {\n mode: \"light\",\n primaryColor: \"#6366f1\",\n tokens: {},\n};\n\n/**\n * Detect the host environment from the ext-apps `ui/initialize` response.\n *\n * Uses spec field names (hostInfo, hostContext.theme as string, styles.variables).\n * Handles missing or malformed fields gracefully — never throws.\n */\nexport function detectHost(initResponse: unknown): HostInfo {\n const resp = initResponse as Partial<McpUiInitializeResult> | null | undefined;\n\n const hostName = resp?.hostInfo?.name ?? \"unknown\";\n const protocolVersion = resp?.protocolVersion ?? \"unknown\";\n const ctx = resp?.hostContext as Partial<McpUiHostContext> | undefined;\n const theme = extractTheme(ctx);\n\n return {\n isNimbleBrain: hostName === \"nimblebrain\",\n serverName: hostName,\n protocolVersion,\n theme,\n };\n}\n\nfunction extractTheme(ctx: Partial<McpUiHostContext> | undefined): SynapseTheme {\n if (!ctx) return { ...DEFAULT_THEME };\n\n // Spec: theme is a string (\"light\" | \"dark\")\n const mode = ctx.theme === \"light\" || ctx.theme === \"dark\" ? ctx.theme : DEFAULT_THEME.mode;\n\n // Spec: tokens live under styles.variables\n const variables = ctx.styles?.variables;\n const tokens =\n variables && typeof variables === \"object\" && !Array.isArray(variables)\n ? (variables as Record<string, string>)\n : {};\n\n return { mode, primaryColor: DEFAULT_THEME.primaryColor, tokens };\n}\n","import type { SynapseTransport } from \"./transport.js\";\nimport type { KeyForwardConfig } from \"./types.js\";\n\n/**\n * Forward keyboard shortcuts from the iframe document to the host.\n *\n * By default, forwards all Ctrl/Cmd+key combos and Escape.\n * Apps can customize via `forwardKeys` config.\n */\nexport class KeyboardForwarder {\n private listener: (event: KeyboardEvent) => void;\n private destroyed = false;\n\n constructor(transport: SynapseTransport, customKeys?: KeyForwardConfig[]) {\n const config = customKeys ?? null; // null = default behavior\n\n this.listener = (event: KeyboardEvent) => {\n if (this.destroyed) return;\n if (this.shouldForward(event, config)) {\n event.preventDefault();\n transport.send(\"synapse/keydown\", {\n key: event.key,\n ctrlKey: event.ctrlKey,\n metaKey: event.metaKey,\n shiftKey: event.shiftKey,\n altKey: event.altKey,\n });\n }\n };\n\n document.addEventListener(\"keydown\", this.listener);\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n document.removeEventListener(\"keydown\", this.listener);\n }\n\n private shouldForward(event: KeyboardEvent, config: KeyForwardConfig[] | null): boolean {\n // Empty array = forwarding disabled\n if (config && config.length === 0) return false;\n\n // Custom config: match exactly\n if (config) {\n return config.some(\n (k) =>\n event.key.toLowerCase() === k.key.toLowerCase() &&\n (k.ctrl === undefined || event.ctrlKey === k.ctrl) &&\n (k.meta === undefined || event.metaKey === k.meta) &&\n (k.shift === undefined || event.shiftKey === k.shift) &&\n (k.alt === undefined || event.altKey === k.alt),\n );\n }\n\n // Default: forward all Ctrl/Cmd combos + Escape,\n // EXCEPT clipboard shortcuts (c, v, x, a) which the browser must handle natively.\n if (event.key === \"Escape\") return true;\n if (event.ctrlKey || event.metaKey) {\n const key = event.key.toLowerCase();\n if (key === \"c\" || key === \"v\" || key === \"x\" || key === \"a\") return false;\n return true;\n }\n return false;\n }\n}\n","import type {\n McpUiHostContextChangedNotification,\n McpUiInitializeRequest,\n McpUiMessageRequest,\n McpUiOpenLinkRequest,\n McpUiUpdateModelContextRequest,\n} from \"@modelcontextprotocol/ext-apps\";\nimport {\n HOST_CONTEXT_CHANGED_METHOD,\n INITIALIZE_METHOD,\n INITIALIZED_METHOD,\n LATEST_PROTOCOL_VERSION,\n MESSAGE_METHOD,\n OPEN_LINK_METHOD,\n} from \"@modelcontextprotocol/ext-apps\";\nimport type { TextContent } from \"@modelcontextprotocol/sdk/types.js\";\n\nimport { detectHost } from \"./detection.js\";\nimport { KeyboardForwarder } from \"./keyboard.js\";\nimport { parseToolResult } from \"./result-parser.js\";\nimport { SynapseTransport } from \"./transport.js\";\nimport type {\n AgentAction,\n DataChangedEvent,\n FileResult,\n HostInfo,\n RequestFileOptions,\n Synapse,\n SynapseOptions,\n SynapseTheme,\n ToolCallResult,\n} from \"./types.js\";\n\n/**\n * Create a Synapse instance.\n *\n * Wraps the ext-apps protocol handshake via `SynapseTransport` and provides\n * a typed, framework-agnostic API for calling tools, reacting to data changes,\n * dispatching actions, and pushing LLM-visible state.\n *\n * In non-NimbleBrain hosts, NB-specific methods degrade to no-ops.\n */\nexport function createSynapse(options: SynapseOptions): Synapse {\n const { name, version, internal = false, forwardKeys } = options;\n\n const transport = new SynapseTransport();\n let hostInfo: HostInfo | null = null;\n let currentTheme: SynapseTheme = {\n mode: \"light\",\n primaryColor: \"#6366f1\",\n tokens: {},\n };\n let destroyed = false;\n\n // --- Debounce for setVisibleState ---\n let stateTimer: ReturnType<typeof setTimeout> | null = null;\n\n // --- Keyboard forwarding ---\n let keyboard: KeyboardForwarder | null = null;\n\n // --- ext-apps handshake ---\n const initParams: McpUiInitializeRequest[\"params\"] = {\n protocolVersion: LATEST_PROTOCOL_VERSION,\n appInfo: { name, version },\n appCapabilities: {},\n };\n\n const ready = transport\n .request(INITIALIZE_METHOD, initParams as unknown as Record<string, unknown>)\n .then((result) => {\n hostInfo = detectHost(result);\n currentTheme = hostInfo.theme;\n\n transport.send(INITIALIZED_METHOD, {});\n\n keyboard = new KeyboardForwarder(transport, forwardKeys);\n });\n\n // Listen for theme changes from the host (ext-apps spec)\n const unsubTheme = transport.onMessage(HOST_CONTEXT_CHANGED_METHOD, (params) => {\n if (!params) return;\n const mode = params.theme === \"dark\" ? \"dark\" : \"light\";\n // Spec: tokens are nested under styles.variables\n const styles = params.styles as Record<string, unknown> | undefined;\n const variables = styles?.variables as Record<string, string> | undefined;\n const tokens = variables && typeof variables === \"object\" ? variables : currentTheme.tokens;\n currentTheme = { mode, primaryColor: currentTheme.primaryColor, tokens };\n for (const cb of themeCallbacks) cb(currentTheme);\n });\n\n const themeCallbacks = new Set<(theme: SynapseTheme) => void>();\n const dataCallbacks = new Set<(event: DataChangedEvent) => void>();\n const actionCallbacks = new Set<(action: AgentAction) => void>();\n\n // Listen for data change events\n const unsubData = transport.onMessage(\"synapse/data-changed\", (params) => {\n if (!params) return;\n const event: DataChangedEvent = {\n source: \"agent\",\n server: (params.server as string) ?? \"\",\n tool: (params.tool as string) ?? \"\",\n };\n for (const cb of dataCallbacks) cb(event);\n });\n\n // Listen for agent actions (typed, declarative commands from the server)\n const unsubAction = transport.onMessage(\"synapse/action\", (params) => {\n if (!params || typeof params.type !== \"string\") return;\n const action: AgentAction = {\n type: params.type as string,\n payload: (params.payload as Record<string, unknown>) ?? {},\n requiresConfirmation: params.requiresConfirmation === true,\n label: typeof params.label === \"string\" ? params.label : undefined,\n };\n for (const cb of actionCallbacks) cb(action);\n });\n\n const isNB = () => hostInfo?.isNimbleBrain === true;\n\n const synapse: Synapse = {\n get ready() {\n return ready;\n },\n\n get isNimbleBrainHost() {\n return isNB();\n },\n\n get destroyed() {\n return destroyed;\n },\n\n async callTool<TInput = Record<string, unknown>, TOutput = unknown>(\n toolName: string,\n args?: TInput,\n ): Promise<ToolCallResult<TOutput>> {\n const params: Record<string, unknown> = {\n name: toolName,\n arguments: args ?? {},\n };\n // Internal apps can cross-call\n if (internal) {\n params.server = name;\n }\n const raw = await transport.request(\"tools/call\", params);\n return parseToolResult(raw) as ToolCallResult<TOutput>;\n },\n\n onDataChanged(callback: (event: DataChangedEvent) => void): () => void {\n dataCallbacks.add(callback);\n return () => {\n dataCallbacks.delete(callback);\n };\n },\n\n onAction(callback: (action: AgentAction) => void): () => void {\n actionCallbacks.add(callback);\n return () => {\n actionCallbacks.delete(callback);\n };\n },\n\n getTheme(): SynapseTheme {\n return { ...currentTheme };\n },\n\n onThemeChanged(callback: (theme: SynapseTheme) => void): () => void {\n themeCallbacks.add(callback);\n return () => {\n themeCallbacks.delete(callback);\n };\n },\n\n action(action: string, params?: Record<string, unknown>): void {\n if (!isNB()) return;\n transport.send(\"synapse/action\", { action, ...params });\n },\n\n chat(message: string, context?: { action?: string; entity?: string }): void {\n const textBlock: TextContent = {\n type: \"text\",\n text: message,\n ...(isNB() && context && { _meta: { context } }),\n };\n const params: McpUiMessageRequest[\"params\"] = {\n role: \"user\",\n content: [textBlock],\n };\n transport.send(MESSAGE_METHOD, params as unknown as Record<string, unknown>);\n },\n\n setVisibleState(state: Record<string, unknown>, summary?: string): void {\n // Debounce: 250ms\n if (stateTimer) clearTimeout(stateTimer);\n stateTimer = setTimeout(() => {\n const params: McpUiUpdateModelContextRequest[\"params\"] = {\n structuredContent: state,\n ...(summary !== undefined && {\n content: [{ type: \"text\", text: summary } satisfies TextContent],\n }),\n };\n transport.send(\"ui/update-model-context\", params as unknown as Record<string, unknown>);\n stateTimer = null;\n }, 250);\n },\n\n saveFile(filename: string, content: string | Blob, mimeType?: string): void {\n // Always send — the bridge handles this for any host that supports it.\n // Removing the isNB() guard fixes silent failures when host detection\n // hasn't completed yet or when the handshake response is delayed.\n const data = typeof content === \"string\" ? content : \"[Blob content not serializable]\";\n transport.send(\"synapse/save-file\", {\n data,\n filename,\n mimeType: mimeType ?? \"application/octet-stream\",\n });\n },\n\n openLink(url: string): void {\n const params: McpUiOpenLinkRequest[\"params\"] = { url };\n // Spec: ui/open-link is a request (expects a response), not a notification\n transport\n .request(OPEN_LINK_METHOD, params as unknown as Record<string, unknown>)\n .catch(() => {\n // Fallback: if host doesn't respond, open directly\n window.open(url, \"_blank\", \"noopener\");\n });\n },\n\n async pickFile(options?: RequestFileOptions): Promise<FileResult | null> {\n if (!isNB()) {\n throw new Error(\"pickFile is not supported in this host\");\n }\n const result = await transport.request(\"synapse/pick-file\", {\n accept: options?.accept,\n maxSize: options?.maxSize ?? 26_214_400,\n multiple: false,\n });\n return (result as FileResult) ?? null;\n },\n\n async pickFiles(options?: RequestFileOptions): Promise<FileResult[]> {\n if (!isNB()) {\n throw new Error(\"pickFiles is not supported in this host\");\n }\n const result = await transport.request(\"synapse/pick-file\", {\n accept: options?.accept,\n maxSize: options?.maxSize ?? 26_214_400,\n multiple: true,\n });\n if (!result) return [];\n return Array.isArray(result) ? (result as FileResult[]) : [result as FileResult];\n },\n\n _onMessage(\n method: string,\n callback: (params: Record<string, unknown> | undefined) => void,\n ): () => void {\n return transport.onMessage(method, callback);\n },\n\n _request(method: string, params?: Record<string, unknown>): Promise<unknown> {\n return transport.request(method, params);\n },\n\n destroy(): void {\n if (destroyed) return;\n destroyed = true;\n\n if (stateTimer) clearTimeout(stateTimer);\n keyboard?.destroy();\n unsubTheme();\n unsubData();\n unsubAction();\n themeCallbacks.clear();\n dataCallbacks.clear();\n actionCallbacks.clear();\n transport.destroy();\n },\n };\n\n return synapse;\n}\n"]}