@nimblebrain/synapse 0.18.0 → 0.20.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 +32 -37
- package/dist/check-JEM5PN64.cjs +435 -0
- package/dist/check-JEM5PN64.cjs.map +1 -0
- package/dist/check-YDZUQT6A.js +429 -0
- package/dist/check-YDZUQT6A.js.map +1 -0
- package/dist/{chunk-VDQZQDU3.cjs → chunk-BNF352SA.cjs} +56 -145
- package/dist/chunk-BNF352SA.cjs.map +1 -0
- package/dist/{chunk-HKRTDGXN.js → chunk-DJS2FKBN.js} +21 -89
- package/dist/chunk-DJS2FKBN.js.map +1 -0
- package/dist/chunk-H3INUKA3.js +32 -0
- package/dist/chunk-H3INUKA3.js.map +1 -0
- package/dist/{chunk-FB7GPBEM.cjs → chunk-HQLSVUOR.cjs} +193 -314
- package/dist/chunk-HQLSVUOR.cjs.map +1 -0
- package/dist/chunk-ISV6HFQE.cjs +40 -0
- package/dist/chunk-ISV6HFQE.cjs.map +1 -0
- package/dist/{chunk-3YSXPBEQ.js → chunk-TR2YNWZ5.js} +190 -313
- package/dist/chunk-TR2YNWZ5.js.map +1 -0
- package/dist/{chunk-ZLASWV4N.js → chunk-UKP4W6DI.js} +56 -145
- package/dist/chunk-UKP4W6DI.js.map +1 -0
- package/dist/{chunk-LPWEQCZV.cjs → chunk-YSL7KJNU.cjs} +21 -92
- package/dist/chunk-YSL7KJNU.cjs.map +1 -0
- package/dist/codegen/cli.cjs +38 -1
- package/dist/codegen/cli.cjs.map +1 -1
- package/dist/codegen/cli.js +38 -1
- package/dist/codegen/cli.js.map +1 -1
- package/dist/codegen/index.d.cts +1 -1
- package/dist/codegen/index.d.ts +1 -1
- package/dist/connect.iife.global.js +66 -43
- package/dist/{detect-BHYg26_d.d.cts → detect-DVGaL2bH.d.cts} +39 -33
- package/dist/{detect-Bf8Q_0dK.d.ts → detect-DVGaL2bH.d.ts} +39 -33
- package/dist/host/index.cjs +17 -17
- package/dist/host/index.d.cts +11 -27
- package/dist/host/index.d.ts +11 -27
- package/dist/host/index.js +2 -2
- package/dist/index.cjs +52 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +104 -31
- package/dist/index.d.ts +104 -31
- package/dist/index.js +34 -3
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +11 -10
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +49 -12
- package/dist/react/index.d.ts +49 -12
- package/dist/react/index.js +6 -5
- package/dist/react/index.js.map +1 -1
- package/dist/{server-QIYIJHD5.cjs → server-BR5MNFOB.cjs} +40 -29
- package/dist/server-BR5MNFOB.cjs.map +1 -0
- package/dist/{server-5N76YCWC.js → server-OOEEWAEG.js} +40 -30
- package/dist/server-OOEEWAEG.js.map +1 -0
- package/dist/synapse-runtime.iife.global.js +66 -43
- package/dist/synapse-ui.iife.global.js +4 -4
- package/dist/{types-uEO4VFJ2.d.cts → types-BxPfGHKO.d.cts} +48 -125
- package/dist/{types-uEO4VFJ2.d.ts → types-BxPfGHKO.d.ts} +48 -125
- package/dist/vite/index.cjs +77 -33
- package/dist/vite/index.cjs.map +1 -1
- package/dist/vite/index.d.cts +5 -1
- package/dist/vite/index.d.ts +5 -1
- package/dist/vite/index.js +77 -33
- package/dist/vite/index.js.map +1 -1
- package/package.json +12 -6
- package/dist/chunk-3YSXPBEQ.js.map +0 -1
- package/dist/chunk-FB7GPBEM.cjs.map +0 -1
- package/dist/chunk-HKRTDGXN.js.map +0 -1
- package/dist/chunk-LPWEQCZV.cjs.map +0 -1
- package/dist/chunk-VDQZQDU3.cjs.map +0 -1
- package/dist/chunk-ZLASWV4N.js.map +0 -1
- package/dist/server-5N76YCWC.js.map +0 -1
- package/dist/server-QIYIJHD5.cjs.map +0 -1
|
@@ -1,5 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { extractHostFontCss, applyTheme, extractTheme } from './chunk-DJS2FKBN.js';
|
|
2
|
+
import { TOOLS_CALL_METHOD, NIMBLEBRAIN_EXTENSIONS, resolveEventMethod, KEYDOWN_METHOD } from './chunk-H3INUKA3.js';
|
|
3
|
+
import { App, TOOL_INPUT_METHOD, TOOL_INPUT_PARTIAL_METHOD, TOOL_RESULT_METHOD, TOOL_CANCELLED_METHOD, applyHostFonts, HOST_CONTEXT_CHANGED_METHOD, RESOURCE_TEARDOWN_METHOD, SIZE_CHANGED_METHOD } from '@modelcontextprotocol/ext-apps';
|
|
4
|
+
import { CallToolResultSchema, ResultSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
+
|
|
6
|
+
// src/errors.ts
|
|
7
|
+
var HostCapabilityError = class extends Error {
|
|
8
|
+
capability;
|
|
9
|
+
constructor(feature, capability) {
|
|
10
|
+
super(`${feature} is not supported in this host: it did not declare ${capability}`);
|
|
11
|
+
this.name = "HostCapabilityError";
|
|
12
|
+
this.capability = capability;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
3
15
|
|
|
4
16
|
// src/internals.ts
|
|
5
17
|
var INTERNALS = /* @__PURE__ */ new WeakMap();
|
|
@@ -63,14 +75,18 @@ function parseCallToolResult(result) {
|
|
|
63
75
|
}
|
|
64
76
|
|
|
65
77
|
// src/task-handle.ts
|
|
66
|
-
var
|
|
78
|
+
var TASKS_EXTENSION_ID = "io.modelcontextprotocol/tasks";
|
|
79
|
+
function readHostTasksCapability(capabilities) {
|
|
80
|
+
const entry = capabilities?.experimental?.[TASKS_EXTENSION_ID];
|
|
81
|
+
return entry && typeof entry === "object" && !Array.isArray(entry) ? entry : void 0;
|
|
82
|
+
}
|
|
67
83
|
var TASKS_GET_METHOD = "tasks/get";
|
|
68
84
|
var TASKS_RESULT_METHOD = "tasks/result";
|
|
69
85
|
var TASKS_CANCEL_METHOD = "tasks/cancel";
|
|
70
86
|
var TASKS_STATUS_NOTIFICATION_METHOD = "notifications/tasks/status";
|
|
71
|
-
function createTaskStatusRouter(
|
|
87
|
+
function createTaskStatusRouter(subscribe) {
|
|
72
88
|
const listeners = /* @__PURE__ */ new Map();
|
|
73
|
-
const unsub =
|
|
89
|
+
const unsub = subscribe(TASKS_STATUS_NOTIFICATION_METHOD, (rawParams) => {
|
|
74
90
|
if (!rawParams) return;
|
|
75
91
|
const params = rawParams;
|
|
76
92
|
const taskId = params.taskId;
|
|
@@ -109,18 +125,17 @@ async function callToolAsTask(app, toolName, args, options) {
|
|
|
109
125
|
const deps = internalsFor(app);
|
|
110
126
|
const hostTasks = deps.hostTasksCapability;
|
|
111
127
|
if (!hostTasks?.requests?.tools?.call) {
|
|
112
|
-
throw new
|
|
113
|
-
"callToolAsTask
|
|
128
|
+
throw new HostCapabilityError(
|
|
129
|
+
"callToolAsTask",
|
|
130
|
+
`${TASKS_EXTENSION_ID} with requests.tools.call`
|
|
114
131
|
);
|
|
115
132
|
}
|
|
116
133
|
const taskParam = {};
|
|
117
134
|
if (options?.ttl !== void 0) taskParam.ttl = options.ttl;
|
|
118
|
-
const crossServer = options?.internal ?? deps.internalApp;
|
|
119
135
|
const callParams = {
|
|
120
136
|
name: toolName,
|
|
121
137
|
arguments: args ?? {},
|
|
122
|
-
task: taskParam
|
|
123
|
-
...crossServer ? { server: deps.appName } : {}
|
|
138
|
+
task: taskParam
|
|
124
139
|
};
|
|
125
140
|
const raw = await deps.request(
|
|
126
141
|
TOOLS_CALL_METHOD,
|
|
@@ -232,32 +247,18 @@ function parseToolResultParams(params) {
|
|
|
232
247
|
}
|
|
233
248
|
return { content: rawContent ?? null, structuredContent: null, raw };
|
|
234
249
|
}
|
|
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
|
|
|
250
251
|
// src/keyboard.ts
|
|
251
252
|
var KeyboardForwarder = class {
|
|
252
253
|
listener;
|
|
253
254
|
destroyed = false;
|
|
254
|
-
constructor(
|
|
255
|
+
constructor(send, customKeys) {
|
|
255
256
|
const config = customKeys ?? null;
|
|
256
257
|
this.listener = (event) => {
|
|
257
258
|
if (this.destroyed) return;
|
|
258
259
|
if (this.shouldForward(event, config)) {
|
|
259
260
|
event.preventDefault();
|
|
260
|
-
|
|
261
|
+
send(KEYDOWN_METHOD, {
|
|
261
262
|
key: event.key,
|
|
262
263
|
ctrlKey: event.ctrlKey,
|
|
263
264
|
metaKey: event.metaKey,
|
|
@@ -289,271 +290,125 @@ var KeyboardForwarder = class {
|
|
|
289
290
|
return false;
|
|
290
291
|
}
|
|
291
292
|
};
|
|
292
|
-
|
|
293
|
-
// src/resize.ts
|
|
294
293
|
function createResizer(send, autoResize) {
|
|
295
294
|
let destroyed = false;
|
|
296
295
|
let observer = null;
|
|
297
|
-
let rafId = null;
|
|
298
296
|
function measureAndSend() {
|
|
299
297
|
if (destroyed) return;
|
|
300
298
|
const width = document.body.scrollWidth;
|
|
301
299
|
const height = document.body.scrollHeight;
|
|
302
|
-
send(
|
|
300
|
+
send(SIZE_CHANGED_METHOD, { width, height });
|
|
303
301
|
}
|
|
304
302
|
function resize(width, height) {
|
|
305
303
|
if (destroyed) return;
|
|
306
304
|
if (width !== void 0 && height !== void 0) {
|
|
307
|
-
send(
|
|
305
|
+
send(SIZE_CHANGED_METHOD, { width, height });
|
|
308
306
|
} else {
|
|
309
307
|
measureAndSend();
|
|
310
308
|
}
|
|
311
309
|
}
|
|
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
310
|
function destroy() {
|
|
324
311
|
if (destroyed) return;
|
|
325
312
|
destroyed = true;
|
|
326
|
-
if (rafId !== null) cancelAnimationFrame(rafId);
|
|
327
313
|
observer?.disconnect();
|
|
328
314
|
observer = null;
|
|
329
315
|
}
|
|
330
316
|
return { resize, measureAndSend, destroy };
|
|
331
317
|
}
|
|
332
318
|
|
|
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
319
|
// src/connect.ts
|
|
427
|
-
var
|
|
428
|
-
var
|
|
320
|
+
var NIMBLEBRAIN_HOST = "nimblebrain";
|
|
321
|
+
var CHAT_CONTEXT_META_KEY = "ai.nimblebrain/context";
|
|
322
|
+
var NO_DEADLINE = { timeout: 2147483647 };
|
|
323
|
+
var MAPPED_EVENTS = [
|
|
324
|
+
["toolinput", TOOL_INPUT_METHOD],
|
|
325
|
+
["toolinputpartial", TOOL_INPUT_PARTIAL_METHOD],
|
|
326
|
+
["toolresult", TOOL_RESULT_METHOD],
|
|
327
|
+
["toolcancelled", TOOL_CANCELLED_METHOD]
|
|
328
|
+
];
|
|
429
329
|
async function connect(options) {
|
|
430
|
-
const { name, version, autoResize = false,
|
|
431
|
-
const transport = new SynapseTransport();
|
|
330
|
+
const { name, version, autoResize = false, forwardKeys } = options;
|
|
432
331
|
let destroyed = false;
|
|
433
|
-
let hostContext = {};
|
|
434
332
|
let hostInfo = { name: "unknown", version: "unknown" };
|
|
435
333
|
let isNimbleBrainHost = false;
|
|
436
334
|
let toolInfo = null;
|
|
437
335
|
let containerDimensions = null;
|
|
336
|
+
let hostCapabilities = {};
|
|
438
337
|
let hostTasksCapability;
|
|
439
338
|
let keyboard = null;
|
|
440
|
-
let
|
|
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();
|
|
339
|
+
let appliedTheme = { mode: "light", tokens: {} };
|
|
452
340
|
const appCapabilities = {
|
|
453
341
|
tasks: {
|
|
454
342
|
cancel: {},
|
|
455
343
|
requests: { tools: { call: {} } }
|
|
456
344
|
}
|
|
457
345
|
};
|
|
458
|
-
const
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
};
|
|
463
|
-
const result = await transport.request(
|
|
464
|
-
INITIALIZE_METHOD,
|
|
465
|
-
initParams
|
|
346
|
+
const client = new App(
|
|
347
|
+
{ name, version },
|
|
348
|
+
appCapabilities,
|
|
349
|
+
{ autoResize }
|
|
466
350
|
);
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
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
|
-
}
|
|
351
|
+
function currentContext() {
|
|
352
|
+
return client.getHostContext() ?? {};
|
|
353
|
+
}
|
|
354
|
+
function resolveTheme() {
|
|
355
|
+
return extractTheme(currentContext());
|
|
489
356
|
}
|
|
490
|
-
|
|
491
|
-
|
|
357
|
+
function applyFontsFrom(ctx) {
|
|
358
|
+
const css = extractHostFontCss(ctx);
|
|
359
|
+
if (css !== void 0 && typeof document !== "undefined") applyHostFonts(css);
|
|
492
360
|
}
|
|
493
|
-
function
|
|
361
|
+
function applyResolvedTheme() {
|
|
362
|
+
const theme = resolveTheme();
|
|
363
|
+
applyTheme(theme.mode, theme.tokens);
|
|
364
|
+
appliedTheme = theme;
|
|
365
|
+
return theme;
|
|
366
|
+
}
|
|
367
|
+
const handlers = /* @__PURE__ */ new Map();
|
|
368
|
+
const themeCallbacks = /* @__PURE__ */ new Set();
|
|
369
|
+
const hostContextCallbacks = /* @__PURE__ */ new Set();
|
|
370
|
+
function fanOut(method, payload) {
|
|
494
371
|
const set = handlers.get(method);
|
|
495
372
|
if (!set) return;
|
|
496
|
-
for (const handler of set) handler(
|
|
373
|
+
for (const handler of set) handler(payload);
|
|
374
|
+
}
|
|
375
|
+
function sendRaw(method, params) {
|
|
376
|
+
if (destroyed) return;
|
|
377
|
+
void client.notification({ method, params }).catch(() => {
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
function requestRaw(method, params) {
|
|
381
|
+
return client.request({ method, params }, ResultSchema, NO_DEADLINE);
|
|
382
|
+
}
|
|
383
|
+
for (const [event, method] of MAPPED_EVENTS) {
|
|
384
|
+
client.addEventListener(event, (params) => {
|
|
385
|
+
if (destroyed) return;
|
|
386
|
+
fanOut(
|
|
387
|
+
method,
|
|
388
|
+
method === TOOL_RESULT_METHOD ? parseToolResultParams(params) : params
|
|
389
|
+
);
|
|
390
|
+
});
|
|
497
391
|
}
|
|
498
|
-
|
|
392
|
+
client.addEventListener("hostcontextchanged", (params) => {
|
|
499
393
|
if (destroyed) return;
|
|
500
|
-
const
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
const
|
|
505
|
-
|
|
506
|
-
for (const cb of hostContextCallbacks) cb(hostContext);
|
|
507
|
-
fanOutRaw(HOST_CONTEXT_CHANGED_METHOD, params);
|
|
394
|
+
const prevTheme = appliedTheme;
|
|
395
|
+
applyFontsFrom(params);
|
|
396
|
+
const nextTheme = applyResolvedTheme();
|
|
397
|
+
const merged = currentContext();
|
|
398
|
+
for (const cb of hostContextCallbacks) cb(merged);
|
|
399
|
+
fanOut(HOST_CONTEXT_CHANGED_METHOD, params);
|
|
508
400
|
if (!themesEqual(prevTheme, nextTheme)) {
|
|
509
401
|
for (const cb of themeCallbacks) cb(nextTheme);
|
|
510
402
|
}
|
|
511
403
|
});
|
|
512
|
-
|
|
513
|
-
if (destroyed)
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
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) => {
|
|
404
|
+
client.onteardown = (params) => {
|
|
405
|
+
if (!destroyed) fanOut(RESOURCE_TEARDOWN_METHOD, params);
|
|
406
|
+
return {};
|
|
407
|
+
};
|
|
408
|
+
client.fallbackNotificationHandler = async (notification) => {
|
|
524
409
|
if (destroyed) return;
|
|
525
|
-
|
|
526
|
-
|
|
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
|
-
}
|
|
410
|
+
fanOut(notification.method, notification.params);
|
|
411
|
+
};
|
|
557
412
|
function subscribe(event, handler) {
|
|
558
413
|
switch (event) {
|
|
559
414
|
case "theme-changed":
|
|
@@ -566,21 +421,10 @@ async function connect(options) {
|
|
|
566
421
|
return () => {
|
|
567
422
|
hostContextCallbacks.delete(handler);
|
|
568
423
|
};
|
|
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
424
|
default: {
|
|
580
425
|
const method = resolveEventMethod(event);
|
|
581
426
|
if (!handlers.has(method)) handlers.set(method, /* @__PURE__ */ new Set());
|
|
582
427
|
handlers.get(method)?.add(handler);
|
|
583
|
-
ensureTransportSub(method);
|
|
584
428
|
return () => {
|
|
585
429
|
const set = handlers.get(method);
|
|
586
430
|
if (set) {
|
|
@@ -596,8 +440,36 @@ async function connect(options) {
|
|
|
596
440
|
if (typeof handler === "function") subscribe(event, handler);
|
|
597
441
|
}
|
|
598
442
|
}
|
|
599
|
-
|
|
600
|
-
const
|
|
443
|
+
await client.connect();
|
|
444
|
+
const hostVersion = client.getHostVersion();
|
|
445
|
+
hostInfo = {
|
|
446
|
+
name: hostVersion?.name ?? "unknown",
|
|
447
|
+
version: hostVersion?.version ?? "unknown"
|
|
448
|
+
};
|
|
449
|
+
isNimbleBrainHost = hostInfo.name === NIMBLEBRAIN_HOST;
|
|
450
|
+
hostCapabilities = client.getHostCapabilities() ?? {};
|
|
451
|
+
hostTasksCapability = readHostTasksCapability(hostCapabilities);
|
|
452
|
+
const initialContext = client.getHostContext();
|
|
453
|
+
if (initialContext) {
|
|
454
|
+
if (initialContext.toolInfo && typeof initialContext.toolInfo === "object") {
|
|
455
|
+
toolInfo = {
|
|
456
|
+
tool: initialContext.toolInfo.tool ?? {}
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
if (initialContext.containerDimensions && typeof initialContext.containerDimensions === "object") {
|
|
460
|
+
containerDimensions = initialContext.containerDimensions;
|
|
461
|
+
}
|
|
462
|
+
applyFontsFrom(initialContext);
|
|
463
|
+
applyResolvedTheme();
|
|
464
|
+
}
|
|
465
|
+
if (forwardKeys && hostCapabilities.experimental?.[NIMBLEBRAIN_EXTENSIONS.keydown.capability] !== void 0) {
|
|
466
|
+
keyboard = new KeyboardForwarder(sendRaw, forwardKeys === true ? void 0 : forwardKeys);
|
|
467
|
+
}
|
|
468
|
+
const resizer = createResizer(sendRaw);
|
|
469
|
+
if (!autoResize) resizer.measureAndSend();
|
|
470
|
+
const taskRouter = createTaskStatusRouter(
|
|
471
|
+
(method, handler) => subscribe(method, handler)
|
|
472
|
+
);
|
|
601
473
|
const app = {
|
|
602
474
|
get theme() {
|
|
603
475
|
return resolveTheme();
|
|
@@ -606,7 +478,10 @@ async function connect(options) {
|
|
|
606
478
|
return { ...hostInfo };
|
|
607
479
|
},
|
|
608
480
|
get hostContext() {
|
|
609
|
-
return
|
|
481
|
+
return currentContext();
|
|
482
|
+
},
|
|
483
|
+
get hostCapabilities() {
|
|
484
|
+
return hostCapabilities;
|
|
610
485
|
},
|
|
611
486
|
get isNimbleBrainHost() {
|
|
612
487
|
return isNimbleBrainHost;
|
|
@@ -631,52 +506,62 @@ async function connect(options) {
|
|
|
631
506
|
},
|
|
632
507
|
openLink(url) {
|
|
633
508
|
if (destroyed) return;
|
|
509
|
+
if (!hostCapabilities.openLinks) {
|
|
510
|
+
window.open(url, "_blank", "noopener");
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
634
513
|
const params = { url };
|
|
635
|
-
|
|
514
|
+
client.openLink(params, NO_DEADLINE).catch(() => {
|
|
636
515
|
window.open(url, "_blank", "noopener");
|
|
637
516
|
});
|
|
638
517
|
},
|
|
639
518
|
updateModelContext(state, summary) {
|
|
640
|
-
if (destroyed) return;
|
|
519
|
+
if (destroyed || !hostCapabilities.updateModelContext) return;
|
|
641
520
|
const params = {
|
|
642
521
|
structuredContent: state,
|
|
643
522
|
...summary !== void 0 && {
|
|
644
523
|
content: [{ type: "text", text: summary }]
|
|
645
524
|
}
|
|
646
525
|
};
|
|
647
|
-
|
|
526
|
+
client.updateModelContext(params, NO_DEADLINE).catch(() => {
|
|
527
|
+
});
|
|
648
528
|
},
|
|
649
|
-
async callTool(toolName, args
|
|
650
|
-
|
|
529
|
+
async callTool(toolName, args) {
|
|
530
|
+
if (!hostCapabilities.serverTools) {
|
|
531
|
+
throw new HostCapabilityError("callTool", "serverTools");
|
|
532
|
+
}
|
|
651
533
|
const params = {
|
|
652
534
|
name: toolName,
|
|
653
|
-
arguments: args ?? {}
|
|
654
|
-
...server !== void 0 && { server }
|
|
535
|
+
arguments: args ?? {}
|
|
655
536
|
};
|
|
656
|
-
const raw = await
|
|
537
|
+
const raw = await client.request(
|
|
538
|
+
{ method: TOOLS_CALL_METHOD, params },
|
|
539
|
+
CallToolResultSchema,
|
|
540
|
+
NO_DEADLINE
|
|
541
|
+
);
|
|
657
542
|
return parseToolResult(raw);
|
|
658
543
|
},
|
|
659
544
|
async readServerResource(params) {
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
);
|
|
664
|
-
return raw;
|
|
545
|
+
if (!hostCapabilities.serverResources) {
|
|
546
|
+
throw new HostCapabilityError("readServerResource", "serverResources");
|
|
547
|
+
}
|
|
548
|
+
return await client.readServerResource(params, NO_DEADLINE);
|
|
665
549
|
},
|
|
666
550
|
sendMessage(text, context) {
|
|
667
|
-
if (destroyed) return;
|
|
551
|
+
if (destroyed || !hostCapabilities.message) return;
|
|
668
552
|
const textBlock = {
|
|
669
553
|
type: "text",
|
|
670
554
|
text,
|
|
671
|
-
//
|
|
555
|
+
// The chat context is a NimbleBrain host field; other hosts ignore it,
|
|
672
556
|
// but there is no reason to spend the bytes off one.
|
|
673
|
-
...isNimbleBrainHost && context && { _meta: { context } }
|
|
557
|
+
...isNimbleBrainHost && context && { _meta: { [CHAT_CONTEXT_META_KEY]: context } }
|
|
674
558
|
};
|
|
675
559
|
const params = {
|
|
676
560
|
role: "user",
|
|
677
561
|
content: [textBlock]
|
|
678
562
|
};
|
|
679
|
-
|
|
563
|
+
client.sendMessage(params, NO_DEADLINE).catch(() => {
|
|
564
|
+
});
|
|
680
565
|
},
|
|
681
566
|
destroy() {
|
|
682
567
|
if (destroyed) return;
|
|
@@ -687,34 +572,28 @@ async function connect(options) {
|
|
|
687
572
|
handlers.clear();
|
|
688
573
|
themeCallbacks.clear();
|
|
689
574
|
hostContextCallbacks.clear();
|
|
690
|
-
|
|
691
|
-
actionCallbacks.clear();
|
|
692
|
-
transport.destroy();
|
|
575
|
+
void client.close();
|
|
693
576
|
}
|
|
694
577
|
};
|
|
695
578
|
registerInternals(app, {
|
|
696
579
|
send(method, params) {
|
|
697
|
-
|
|
698
|
-
transport.send(method, params);
|
|
580
|
+
sendRaw(method, params);
|
|
699
581
|
},
|
|
700
582
|
request(method, params) {
|
|
701
|
-
return
|
|
583
|
+
return requestRaw(method, params);
|
|
702
584
|
},
|
|
703
585
|
onMessage(method, handler) {
|
|
704
|
-
return
|
|
586
|
+
return subscribe(method, handler);
|
|
705
587
|
},
|
|
706
588
|
taskRouter,
|
|
707
589
|
get hostTasksCapability() {
|
|
708
590
|
return hostTasksCapability;
|
|
709
|
-
}
|
|
710
|
-
appName: name,
|
|
711
|
-
internalApp: internal
|
|
591
|
+
}
|
|
712
592
|
});
|
|
713
593
|
return app;
|
|
714
594
|
}
|
|
715
595
|
function themesEqual(a, b) {
|
|
716
596
|
if (a.mode !== b.mode) return false;
|
|
717
|
-
if (fontFacesKey(a.fontFaces) !== fontFacesKey(b.fontFaces)) return false;
|
|
718
597
|
const aKeys = Object.keys(a.tokens);
|
|
719
598
|
const bKeys = Object.keys(b.tokens);
|
|
720
599
|
if (aKeys.length !== bKeys.length) return false;
|
|
@@ -725,57 +604,55 @@ function themesEqual(a, b) {
|
|
|
725
604
|
}
|
|
726
605
|
|
|
727
606
|
// src/extensions.ts
|
|
607
|
+
function hostSupports(app, extension) {
|
|
608
|
+
const { capability } = NIMBLEBRAIN_EXTENSIONS[extension];
|
|
609
|
+
return app.hostCapabilities.experimental?.[capability] !== void 0;
|
|
610
|
+
}
|
|
728
611
|
var DEFAULT_MAX_FILE_SIZE = 26214400;
|
|
729
612
|
function action(app, name, params) {
|
|
730
|
-
if (!app
|
|
731
|
-
internalsFor(app).send(
|
|
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
|
-
});
|
|
613
|
+
if (!hostSupports(app, "action")) return;
|
|
614
|
+
internalsFor(app).send(NIMBLEBRAIN_EXTENSIONS.action.method, { action: name, ...params });
|
|
741
615
|
}
|
|
742
616
|
async function pickFile(app, options) {
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
const result = await requestFile(app, options, false);
|
|
747
|
-
if (result === null) return null;
|
|
748
|
-
return Array.isArray(result) ? result[0] ?? null : result;
|
|
617
|
+
requireRequestFile("pickFile", app);
|
|
618
|
+
const files = await requestFile(app, options, false);
|
|
619
|
+
return files[0] ?? null;
|
|
749
620
|
}
|
|
750
621
|
async function pickFiles(app, options) {
|
|
751
|
-
|
|
752
|
-
|
|
622
|
+
requireRequestFile("pickFiles", app);
|
|
623
|
+
return await requestFile(app, options, true);
|
|
624
|
+
}
|
|
625
|
+
function requireRequestFile(feature, app) {
|
|
626
|
+
if (!hostSupports(app, "requestFile")) {
|
|
627
|
+
throw new HostCapabilityError(feature, NIMBLEBRAIN_EXTENSIONS.requestFile.capability);
|
|
753
628
|
}
|
|
754
|
-
const result = await requestFile(app, options, true);
|
|
755
|
-
if (result === null) return [];
|
|
756
|
-
return Array.isArray(result) ? result : [result];
|
|
757
629
|
}
|
|
758
630
|
async function requestFile(app, options, multiple) {
|
|
759
|
-
const result = await internalsFor(app).request(
|
|
631
|
+
const result = await internalsFor(app).request(NIMBLEBRAIN_EXTENSIONS.requestFile.method, {
|
|
760
632
|
accept: options?.accept,
|
|
761
633
|
maxSize: options?.maxSize ?? DEFAULT_MAX_FILE_SIZE,
|
|
762
634
|
multiple
|
|
763
635
|
});
|
|
764
|
-
|
|
765
|
-
if (
|
|
766
|
-
|
|
636
|
+
const files = result?.files;
|
|
637
|
+
if (files === void 0) {
|
|
638
|
+
throw new Error(
|
|
639
|
+
"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."
|
|
640
|
+
);
|
|
641
|
+
}
|
|
642
|
+
if (!Array.isArray(files)) {
|
|
643
|
+
throw new Error("ai.nimblebrain/request-file returned a `files` field that is not an array.");
|
|
767
644
|
}
|
|
768
|
-
return validateFileResult
|
|
645
|
+
return files.map(validateFileResult);
|
|
769
646
|
}
|
|
770
647
|
function validateFileResult(value) {
|
|
771
648
|
if (typeof value !== "object" || value === null || typeof value.id !== "string") {
|
|
772
649
|
throw new Error(
|
|
773
|
-
"
|
|
650
|
+
"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)."
|
|
774
651
|
);
|
|
775
652
|
}
|
|
776
653
|
return value;
|
|
777
654
|
}
|
|
778
655
|
|
|
779
|
-
export { action, callToolAsTask, connect,
|
|
780
|
-
//# sourceMappingURL=chunk-
|
|
781
|
-
//# sourceMappingURL=chunk-
|
|
656
|
+
export { HostCapabilityError, action, callToolAsTask, connect, hostSupports, internalsFor, pickFile, pickFiles };
|
|
657
|
+
//# sourceMappingURL=chunk-TR2YNWZ5.js.map
|
|
658
|
+
//# sourceMappingURL=chunk-TR2YNWZ5.js.map
|