@burtson-labs/bandit-engine 2.0.160 → 2.0.162
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 +78 -77
- package/dist/{aiProviderStore-BNZGM6QU.mjs → aiProviderStore-JOB2H4LP.mjs} +2 -2
- package/dist/chat-LB6UEEEW.mjs +16 -0
- package/dist/chat-provider.js +281 -142
- package/dist/chat-provider.js.map +1 -1
- package/dist/chat-provider.mjs +6 -6
- package/dist/{chunk-MHF76C2F.mjs → chunk-3ACT5BTW.mjs} +1343 -1222
- package/dist/chunk-3ACT5BTW.mjs.map +1 -0
- package/dist/{chunk-LFQH6M6B.mjs → chunk-47MOBOP3.mjs} +5 -5
- package/dist/chunk-47MOBOP3.mjs.map +1 -0
- package/dist/chunk-G5POQT2R.mjs +390 -0
- package/dist/chunk-G5POQT2R.mjs.map +1 -0
- package/dist/{chunk-PDQT564P.mjs → chunk-ODGVCSKV.mjs} +18 -12
- package/dist/{chunk-PDQT564P.mjs.map → chunk-ODGVCSKV.mjs.map} +1 -1
- package/dist/{chunk-TKSOJGHX.mjs → chunk-OGMXL7BF.mjs} +62 -61
- package/dist/chunk-OGMXL7BF.mjs.map +1 -0
- package/dist/{chunk-B3HAQC6F.mjs → chunk-Q6LXVYTC.mjs} +68 -25
- package/dist/chunk-Q6LXVYTC.mjs.map +1 -0
- package/dist/{chunk-LTWMMHFT.mjs → chunk-R6HMLYKQ.mjs} +1820 -3260
- package/dist/chunk-R6HMLYKQ.mjs.map +1 -0
- package/dist/{chunk-IABD4L6K.mjs → chunk-RUXYVMW2.mjs} +3841 -4457
- package/dist/chunk-RUXYVMW2.mjs.map +1 -0
- package/dist/{chunk-XTB6ZLUI.mjs → chunk-WQEBTH2R.mjs} +7 -2
- package/dist/{chunk-XTB6ZLUI.mjs.map → chunk-WQEBTH2R.mjs.map} +1 -1
- package/dist/{chunk-PXCIDXRP.mjs → chunk-YHSYHU5P.mjs} +6 -6
- package/dist/{chunk-P6SLB5RC.mjs → chunk-ZNK3ELTZ.mjs} +3 -3
- package/dist/index.d.mts +85 -2
- package/dist/index.d.ts +85 -2
- package/dist/index.js +8080 -9868
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +24 -15
- package/dist/index.mjs.map +1 -1
- package/dist/management/management.js +7681 -9478
- package/dist/management/management.js.map +1 -1
- package/dist/management/management.mjs +9 -9
- package/dist/modals/chat-modal/chat-modal.js +1714 -3302
- package/dist/modals/chat-modal/chat-modal.js.map +1 -1
- package/dist/modals/chat-modal/chat-modal.mjs +5 -5
- package/dist/{modelStore-NMCRL3FR.mjs → modelStore-FGUGOWTL.mjs} +2 -2
- package/package.json +4 -2
- package/dist/chat-4BOIV4VP.mjs +0 -16
- package/dist/chunk-B3HAQC6F.mjs.map +0 -1
- package/dist/chunk-IABD4L6K.mjs.map +0 -1
- package/dist/chunk-LFQH6M6B.mjs.map +0 -1
- package/dist/chunk-LNGF2XAZ.mjs +0 -311
- package/dist/chunk-LNGF2XAZ.mjs.map +0 -1
- package/dist/chunk-LTWMMHFT.mjs.map +0 -1
- package/dist/chunk-MHF76C2F.mjs.map +0 -1
- package/dist/chunk-TKSOJGHX.mjs.map +0 -1
- /package/dist/{aiProviderStore-BNZGM6QU.mjs.map → aiProviderStore-JOB2H4LP.mjs.map} +0 -0
- /package/dist/{chat-4BOIV4VP.mjs.map → chat-LB6UEEEW.mjs.map} +0 -0
- /package/dist/{chunk-PXCIDXRP.mjs.map → chunk-YHSYHU5P.mjs.map} +0 -0
- /package/dist/{chunk-P6SLB5RC.mjs.map → chunk-ZNK3ELTZ.mjs.map} +0 -0
- /package/dist/{modelStore-NMCRL3FR.mjs.map → modelStore-FGUGOWTL.mjs.map} +0 -0
package/dist/chat-provider.js
CHANGED
|
@@ -619,6 +619,8 @@ var init_preferencesStore = __esm({
|
|
|
619
619
|
usePreferencesStore = (0, import_zustand4.create)((set, get) => ({
|
|
620
620
|
preferences: defaultPreferences,
|
|
621
621
|
isLoaded: false,
|
|
622
|
+
saveState: "idle",
|
|
623
|
+
lastSavedAt: null,
|
|
622
624
|
setPreferences: (newPreferences) => {
|
|
623
625
|
const sanitized = sanitizePreferences(newPreferences);
|
|
624
626
|
set((state) => ({
|
|
@@ -661,6 +663,7 @@ var init_preferencesStore = __esm({
|
|
|
661
663
|
}
|
|
662
664
|
},
|
|
663
665
|
savePreferences: async () => {
|
|
666
|
+
set({ saveState: "saving" });
|
|
664
667
|
try {
|
|
665
668
|
const { preferences } = get();
|
|
666
669
|
const storeConfigs6 = [{ name: "config", keyPath: "id" }];
|
|
@@ -669,8 +672,10 @@ var init_preferencesStore = __esm({
|
|
|
669
672
|
preferences
|
|
670
673
|
}, storeConfigs6);
|
|
671
674
|
debugLogger.debug("Preferences saved to IndexedDB");
|
|
675
|
+
set({ saveState: "saved", lastSavedAt: Date.now() });
|
|
672
676
|
} catch (error) {
|
|
673
677
|
debugLogger.error("Failed to save preferences to IndexedDB", { error });
|
|
678
|
+
set({ saveState: "error" });
|
|
674
679
|
}
|
|
675
680
|
},
|
|
676
681
|
exportPreferences: () => {
|
|
@@ -1169,6 +1174,40 @@ var init_common_types = __esm({
|
|
|
1169
1174
|
}
|
|
1170
1175
|
});
|
|
1171
1176
|
|
|
1177
|
+
// src/services/stream/streamLineParser.ts
|
|
1178
|
+
var SKIP, StreamLineParser;
|
|
1179
|
+
var init_streamLineParser = __esm({
|
|
1180
|
+
"src/services/stream/streamLineParser.ts"() {
|
|
1181
|
+
"use strict";
|
|
1182
|
+
SKIP = { kind: "skip" };
|
|
1183
|
+
StreamLineParser = class {
|
|
1184
|
+
/** Set by `event: ping` until the frame's blank-line terminator. */
|
|
1185
|
+
inHeartbeatFrame = false;
|
|
1186
|
+
next(rawLine) {
|
|
1187
|
+
const line = rawLine.trim();
|
|
1188
|
+
if (!line) {
|
|
1189
|
+
this.inHeartbeatFrame = false;
|
|
1190
|
+
return SKIP;
|
|
1191
|
+
}
|
|
1192
|
+
if (line.startsWith(":")) return SKIP;
|
|
1193
|
+
if (line.startsWith("event:")) {
|
|
1194
|
+
this.inHeartbeatFrame = line.slice(6).trim().toLowerCase() === "ping";
|
|
1195
|
+
return SKIP;
|
|
1196
|
+
}
|
|
1197
|
+
if (line.startsWith("id:") || line.startsWith("retry:")) return SKIP;
|
|
1198
|
+
if (line.startsWith("data:")) {
|
|
1199
|
+
if (this.inHeartbeatFrame) return SKIP;
|
|
1200
|
+
const data = line.slice(5).trim();
|
|
1201
|
+
if (!data) return SKIP;
|
|
1202
|
+
if (data === "[DONE]") return { kind: "done" };
|
|
1203
|
+
return { kind: "json", text: data };
|
|
1204
|
+
}
|
|
1205
|
+
return { kind: "json", text: line };
|
|
1206
|
+
}
|
|
1207
|
+
};
|
|
1208
|
+
}
|
|
1209
|
+
});
|
|
1210
|
+
|
|
1172
1211
|
// src/services/ollama/ollama.service.ts
|
|
1173
1212
|
var import_rxjs, handleError, parseResponseBody, OllamaService;
|
|
1174
1213
|
var init_ollama_service = __esm({
|
|
@@ -1176,6 +1215,7 @@ var init_ollama_service = __esm({
|
|
|
1176
1215
|
"use strict";
|
|
1177
1216
|
import_rxjs = require("rxjs");
|
|
1178
1217
|
init_debugLogger();
|
|
1218
|
+
init_streamLineParser();
|
|
1179
1219
|
handleError = () => (obs) => obs.pipe(
|
|
1180
1220
|
(0, import_rxjs.switchMap)(
|
|
1181
1221
|
(response) => response.ok ? (0, import_rxjs.of)(response) : (0, import_rxjs.throwError)(() => new Error(`Request failed: ${response.status} ${response.statusText}`))
|
|
@@ -1266,13 +1306,15 @@ var init_ollama_service = __esm({
|
|
|
1266
1306
|
this._throwErrorIfNotOk(url, response);
|
|
1267
1307
|
const reader = response.body?.getReader();
|
|
1268
1308
|
const decoder = new TextDecoder();
|
|
1309
|
+
const lineParser = new StreamLineParser();
|
|
1269
1310
|
let buffer = "";
|
|
1270
1311
|
const read = () => {
|
|
1271
1312
|
reader?.read().then(({ done, value }) => {
|
|
1272
1313
|
if (done) {
|
|
1273
|
-
|
|
1314
|
+
const tail = lineParser.next(buffer);
|
|
1315
|
+
if (tail.kind === "json") {
|
|
1274
1316
|
try {
|
|
1275
|
-
observer.next(JSON.parse(
|
|
1317
|
+
observer.next(JSON.parse(tail.text));
|
|
1276
1318
|
} catch (err) {
|
|
1277
1319
|
observer.error(err);
|
|
1278
1320
|
debugLogger.error("Final chunk parsing error (generate):", { buffer });
|
|
@@ -1285,9 +1327,10 @@ var init_ollama_service = __esm({
|
|
|
1285
1327
|
const lines = buffer.split("\n");
|
|
1286
1328
|
buffer = lines.pop() ?? "";
|
|
1287
1329
|
for (const line of lines) {
|
|
1288
|
-
|
|
1330
|
+
const classified = lineParser.next(line);
|
|
1331
|
+
if (classified.kind === "json") {
|
|
1289
1332
|
try {
|
|
1290
|
-
observer.next(JSON.parse(
|
|
1333
|
+
observer.next(JSON.parse(classified.text));
|
|
1291
1334
|
} catch (err) {
|
|
1292
1335
|
observer.error(err);
|
|
1293
1336
|
debugLogger.error("Error parsing JSON line (generate):", { line });
|
|
@@ -1317,13 +1360,15 @@ var init_ollama_service = __esm({
|
|
|
1317
1360
|
this._throwErrorIfNotOk(url, response);
|
|
1318
1361
|
const reader = response.body?.getReader();
|
|
1319
1362
|
const decoder = new TextDecoder();
|
|
1363
|
+
const lineParser = new StreamLineParser();
|
|
1320
1364
|
let buffer = "";
|
|
1321
1365
|
const read = () => {
|
|
1322
1366
|
reader?.read().then(({ done, value }) => {
|
|
1323
1367
|
if (done) {
|
|
1324
|
-
|
|
1368
|
+
const tail = lineParser.next(buffer);
|
|
1369
|
+
if (tail.kind === "json") {
|
|
1325
1370
|
try {
|
|
1326
|
-
observer.next(JSON.parse(
|
|
1371
|
+
observer.next(JSON.parse(tail.text));
|
|
1327
1372
|
} catch (err) {
|
|
1328
1373
|
observer.error(err);
|
|
1329
1374
|
debugLogger.error("Final chunk parsing error (chat):", { buffer });
|
|
@@ -1336,9 +1381,10 @@ var init_ollama_service = __esm({
|
|
|
1336
1381
|
const lines = buffer.split("\n");
|
|
1337
1382
|
buffer = lines.pop() ?? "";
|
|
1338
1383
|
for (const line of lines) {
|
|
1339
|
-
|
|
1384
|
+
const classified = lineParser.next(line);
|
|
1385
|
+
if (classified.kind === "json") {
|
|
1340
1386
|
try {
|
|
1341
|
-
observer.next(JSON.parse(
|
|
1387
|
+
observer.next(JSON.parse(classified.text));
|
|
1342
1388
|
} catch (err) {
|
|
1343
1389
|
observer.error(err);
|
|
1344
1390
|
debugLogger.error("Error parsing JSON line (chat):", { line });
|
|
@@ -2232,6 +2278,7 @@ var init_gateway_service = __esm({
|
|
|
2232
2278
|
"src/services/gateway/gateway.service.ts"() {
|
|
2233
2279
|
"use strict";
|
|
2234
2280
|
import_axios = __toESM(require("axios"));
|
|
2281
|
+
init_streamLineParser();
|
|
2235
2282
|
import_rxjs6 = require("rxjs");
|
|
2236
2283
|
init_debugLogger();
|
|
2237
2284
|
init_authenticationService();
|
|
@@ -2413,13 +2460,15 @@ var init_gateway_service = __esm({
|
|
|
2413
2460
|
const handleStreamingResponse = async (response) => {
|
|
2414
2461
|
const reader = response.body?.getReader();
|
|
2415
2462
|
const decoder = new TextDecoder();
|
|
2463
|
+
const lineParser = new StreamLineParser();
|
|
2416
2464
|
let buffer = "";
|
|
2417
2465
|
const read = () => {
|
|
2418
2466
|
reader?.read().then(({ done, value }) => {
|
|
2419
2467
|
if (done) {
|
|
2420
|
-
|
|
2468
|
+
const tail = lineParser.next(buffer);
|
|
2469
|
+
if (tail.kind === "json") {
|
|
2421
2470
|
try {
|
|
2422
|
-
const finalResponse = JSON.parse(
|
|
2471
|
+
const finalResponse = JSON.parse(tail.text);
|
|
2423
2472
|
observer.next(finalResponse);
|
|
2424
2473
|
} catch (err) {
|
|
2425
2474
|
debugLogger.error("GatewayService chat final chunk parsing error", { buffer, error: err });
|
|
@@ -2433,16 +2482,14 @@ var init_gateway_service = __esm({
|
|
|
2433
2482
|
const lines = buffer.split("\n");
|
|
2434
2483
|
buffer = lines.pop() ?? "";
|
|
2435
2484
|
for (const line of lines) {
|
|
2436
|
-
const
|
|
2437
|
-
if (
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
}
|
|
2445
|
-
}
|
|
2485
|
+
const classified = lineParser.next(line);
|
|
2486
|
+
if (classified.kind === "done") {
|
|
2487
|
+
observer.complete();
|
|
2488
|
+
return;
|
|
2489
|
+
}
|
|
2490
|
+
if (classified.kind === "json") {
|
|
2491
|
+
const trimmed = line.trim();
|
|
2492
|
+
const data = classified.text;
|
|
2446
2493
|
try {
|
|
2447
2494
|
const parsed = JSON.parse(data);
|
|
2448
2495
|
if (request.provider === "ollama" && parsed.message) {
|
|
@@ -2609,13 +2656,15 @@ var init_gateway_service = __esm({
|
|
|
2609
2656
|
}
|
|
2610
2657
|
const reader = response.body?.getReader();
|
|
2611
2658
|
const decoder = new TextDecoder();
|
|
2659
|
+
const lineParser = new StreamLineParser();
|
|
2612
2660
|
let buffer = "";
|
|
2613
2661
|
const read = () => {
|
|
2614
2662
|
reader?.read().then(({ done, value }) => {
|
|
2615
2663
|
if (done) {
|
|
2616
|
-
|
|
2664
|
+
const tail = lineParser.next(buffer);
|
|
2665
|
+
if (tail.kind === "json") {
|
|
2617
2666
|
try {
|
|
2618
|
-
observer.next(JSON.parse(
|
|
2667
|
+
observer.next(JSON.parse(tail.text));
|
|
2619
2668
|
} catch (err) {
|
|
2620
2669
|
observer.error(err);
|
|
2621
2670
|
debugLogger.error("Final chunk parsing error (gateway generate):", { buffer });
|
|
@@ -2628,9 +2677,14 @@ var init_gateway_service = __esm({
|
|
|
2628
2677
|
const lines = buffer.split("\n");
|
|
2629
2678
|
buffer = lines.pop() ?? "";
|
|
2630
2679
|
for (const line of lines) {
|
|
2631
|
-
|
|
2680
|
+
const classified = lineParser.next(line);
|
|
2681
|
+
if (classified.kind === "done") {
|
|
2682
|
+
observer.complete();
|
|
2683
|
+
return;
|
|
2684
|
+
}
|
|
2685
|
+
if (classified.kind === "json") {
|
|
2632
2686
|
try {
|
|
2633
|
-
observer.next(JSON.parse(
|
|
2687
|
+
observer.next(JSON.parse(classified.text));
|
|
2634
2688
|
} catch (err) {
|
|
2635
2689
|
observer.error(err);
|
|
2636
2690
|
debugLogger.error("Error parsing JSON line (gateway generate):", { line });
|
|
@@ -6674,6 +6728,16 @@ var discoverMcpTools = async (id) => parse(await fetch(`${gatewayBase()}/mcp/ser
|
|
|
6674
6728
|
|
|
6675
6729
|
// src/store/mcpToolsStore.ts
|
|
6676
6730
|
var sanitizeMcpToolName = (raw) => raw.replace(/[^a-zA-Z0-9_]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").slice(0, 60);
|
|
6731
|
+
var STORE_CONFIGS = [{ name: "config", keyPath: "id" }];
|
|
6732
|
+
var readChoices = (value) => {
|
|
6733
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
6734
|
+
const out = {};
|
|
6735
|
+
for (const [id, on] of Object.entries(value)) {
|
|
6736
|
+
if (typeof on === "boolean") out[id] = on;
|
|
6737
|
+
}
|
|
6738
|
+
return out;
|
|
6739
|
+
};
|
|
6740
|
+
var withChoice = (tool, choices) => Object.prototype.hasOwnProperty.call(choices, tool.id) && tool.enabled !== choices[tool.id] ? { ...tool, enabled: choices[tool.id] } : tool;
|
|
6677
6741
|
var healthCheckTool = {
|
|
6678
6742
|
id: "health-check",
|
|
6679
6743
|
name: "check_gateway_health",
|
|
@@ -6787,130 +6851,199 @@ var createFileTool = {
|
|
|
6787
6851
|
isBuiltIn: true
|
|
6788
6852
|
};
|
|
6789
6853
|
var defaultTools = [healthCheckTool, webSearchTool, webFetchTool, imageGenerationTool, createFileTool];
|
|
6790
|
-
var useMCPToolsStore = (0, import_zustand11.create)((set, get) =>
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
},
|
|
6803
|
-
updateTool: (id, updates) => {
|
|
6804
|
-
set((state) => ({
|
|
6805
|
-
tools: state.tools.map(
|
|
6806
|
-
(tool) => tool.id === id ? { ...tool, ...updates } : tool
|
|
6807
|
-
)
|
|
6808
|
-
}));
|
|
6809
|
-
get().saveTools();
|
|
6810
|
-
},
|
|
6811
|
-
deleteTool: (id) => {
|
|
6812
|
-
set((state) => ({
|
|
6813
|
-
tools: state.tools.filter((tool) => tool.id !== id || tool.isBuiltIn)
|
|
6814
|
-
}));
|
|
6815
|
-
get().saveTools();
|
|
6816
|
-
},
|
|
6817
|
-
toggleTool: (id) => {
|
|
6818
|
-
set((state) => ({
|
|
6819
|
-
tools: state.tools.map(
|
|
6820
|
-
(tool) => tool.id === id ? { ...tool, enabled: !tool.enabled } : tool
|
|
6821
|
-
)
|
|
6822
|
-
}));
|
|
6823
|
-
get().saveTools();
|
|
6824
|
-
},
|
|
6825
|
-
getEnabledTools: () => {
|
|
6826
|
-
return get().tools.filter((tool) => tool.enabled);
|
|
6827
|
-
},
|
|
6828
|
-
loadTools: async () => {
|
|
6829
|
-
try {
|
|
6830
|
-
const storeConfigs6 = [{ name: "config", keyPath: "id" }];
|
|
6831
|
-
const data = await indexedDBService_default.get("banditConfig", 1, "config", "mcpTools", storeConfigs6);
|
|
6832
|
-
if (data?.tools && Array.isArray(data.tools)) {
|
|
6833
|
-
const savedTools = data.tools;
|
|
6834
|
-
const builtInIds = defaultTools.map((t) => t.id);
|
|
6835
|
-
const customTools = savedTools.filter(
|
|
6836
|
-
(tool) => !tool.isBuiltIn && !builtInIds.includes(tool.id)
|
|
6837
|
-
);
|
|
6838
|
-
set({
|
|
6839
|
-
tools: [...defaultTools, ...customTools],
|
|
6840
|
-
isLoaded: true
|
|
6841
|
-
});
|
|
6842
|
-
debugLogger.info("MCP tools loaded from IndexedDB", {
|
|
6843
|
-
totalTools: defaultTools.length + customTools.length,
|
|
6844
|
-
builtInTools: defaultTools.length,
|
|
6845
|
-
customTools: customTools.length
|
|
6846
|
-
});
|
|
6847
|
-
} else {
|
|
6848
|
-
set({ isLoaded: true });
|
|
6849
|
-
await get().saveTools();
|
|
6850
|
-
debugLogger.info("Default MCP tools initialized");
|
|
6851
|
-
}
|
|
6852
|
-
} catch (error) {
|
|
6853
|
-
debugLogger.error("Failed to load MCP tools from IndexedDB", { error });
|
|
6854
|
-
set({ isLoaded: true });
|
|
6855
|
-
}
|
|
6856
|
-
},
|
|
6857
|
-
saveTools: async () => {
|
|
6854
|
+
var useMCPToolsStore = (0, import_zustand11.create)((set, get) => {
|
|
6855
|
+
const persist = async () => {
|
|
6856
|
+
const { tools, choices } = get();
|
|
6857
|
+
const customTools = tools.filter((tool) => !tool.isBuiltIn && !tool.mcpServerId);
|
|
6858
|
+
await indexedDBService_default.put("banditConfig", 1, "config", {
|
|
6859
|
+
id: "mcpTools",
|
|
6860
|
+
tools: customTools,
|
|
6861
|
+
choices
|
|
6862
|
+
}, STORE_CONFIGS);
|
|
6863
|
+
debugLogger.debug("MCP tools saved to IndexedDB", { toolCount: customTools.length, choices: Object.keys(choices).length });
|
|
6864
|
+
};
|
|
6865
|
+
const persistQuietly = async () => {
|
|
6858
6866
|
try {
|
|
6859
|
-
|
|
6860
|
-
const storeConfigs6 = [{ name: "config", keyPath: "id" }];
|
|
6861
|
-
const customTools = tools.filter((tool) => !tool.isBuiltIn && !tool.mcpServerId);
|
|
6862
|
-
await indexedDBService_default.put("banditConfig", 1, "config", {
|
|
6863
|
-
id: "mcpTools",
|
|
6864
|
-
tools: customTools
|
|
6865
|
-
}, storeConfigs6);
|
|
6866
|
-
debugLogger.debug("MCP tools saved to IndexedDB", { toolCount: customTools.length });
|
|
6867
|
+
await persist();
|
|
6867
6868
|
} catch (error) {
|
|
6868
6869
|
debugLogger.error("Failed to save MCP tools to IndexedDB", { error });
|
|
6869
6870
|
}
|
|
6870
|
-
}
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6871
|
+
};
|
|
6872
|
+
return {
|
|
6873
|
+
tools: defaultTools,
|
|
6874
|
+
isLoaded: false,
|
|
6875
|
+
choices: {},
|
|
6876
|
+
saveState: "idle",
|
|
6877
|
+
lastSavedAt: null,
|
|
6878
|
+
addTool: (toolData) => {
|
|
6879
|
+
const newTool = {
|
|
6880
|
+
...toolData,
|
|
6881
|
+
id: `tool-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
|
|
6882
|
+
};
|
|
6883
|
+
set((state) => ({
|
|
6884
|
+
tools: [...state.tools, newTool]
|
|
6885
|
+
}));
|
|
6886
|
+
get().saveTools();
|
|
6887
|
+
},
|
|
6888
|
+
updateTool: (id, updates) => {
|
|
6889
|
+
set((state) => ({
|
|
6890
|
+
tools: state.tools.map(
|
|
6891
|
+
(tool) => tool.id === id ? { ...tool, ...updates } : tool
|
|
6892
|
+
)
|
|
6893
|
+
}));
|
|
6894
|
+
get().saveTools();
|
|
6895
|
+
},
|
|
6896
|
+
deleteTool: (id) => {
|
|
6897
|
+
set((state) => ({
|
|
6898
|
+
tools: state.tools.filter((tool) => tool.id !== id || tool.isBuiltIn)
|
|
6899
|
+
}));
|
|
6900
|
+
get().saveTools();
|
|
6901
|
+
},
|
|
6902
|
+
toggleTool: async (id) => {
|
|
6903
|
+
const tool = get().tools.find((t) => t.id === id);
|
|
6904
|
+
if (!tool) return false;
|
|
6905
|
+
const enabled = !tool.enabled;
|
|
6906
|
+
set((state) => ({
|
|
6907
|
+
tools: state.tools.map((t) => t.id === id ? { ...t, enabled } : t),
|
|
6908
|
+
choices: { ...state.choices, [id]: enabled }
|
|
6909
|
+
}));
|
|
6910
|
+
return get().saveTools();
|
|
6911
|
+
},
|
|
6912
|
+
getEnabledTools: () => {
|
|
6913
|
+
return get().tools.filter((tool) => tool.enabled);
|
|
6914
|
+
},
|
|
6915
|
+
loadTools: async () => {
|
|
6884
6916
|
try {
|
|
6885
|
-
const
|
|
6886
|
-
|
|
6887
|
-
const
|
|
6888
|
-
const
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
|
|
6917
|
+
const data = await indexedDBService_default.get("banditConfig", 1, "config", "mcpTools", STORE_CONFIGS);
|
|
6918
|
+
if (data?.tools && Array.isArray(data.tools)) {
|
|
6919
|
+
const choices = { ...readChoices(data.choices), ...get().choices };
|
|
6920
|
+
const savedTools = data.tools;
|
|
6921
|
+
const builtInIds = defaultTools.map((t) => t.id);
|
|
6922
|
+
const customTools = savedTools.filter(
|
|
6923
|
+
(tool) => !tool.isBuiltIn && !builtInIds.includes(tool.id)
|
|
6924
|
+
);
|
|
6925
|
+
const discovered = get().tools.filter((t) => t.mcpServerId);
|
|
6926
|
+
set({
|
|
6927
|
+
tools: [...defaultTools, ...customTools, ...discovered].map((t) => withChoice(t, choices)),
|
|
6928
|
+
choices,
|
|
6929
|
+
isLoaded: true
|
|
6930
|
+
});
|
|
6931
|
+
debugLogger.info("MCP tools loaded from IndexedDB", {
|
|
6932
|
+
totalTools: defaultTools.length + customTools.length,
|
|
6933
|
+
builtInTools: defaultTools.length,
|
|
6934
|
+
customTools: customTools.length,
|
|
6935
|
+
choices: Object.keys(choices).length
|
|
6936
|
+
});
|
|
6937
|
+
} else {
|
|
6938
|
+
set({ isLoaded: true });
|
|
6939
|
+
await persistQuietly();
|
|
6940
|
+
debugLogger.info("Default MCP tools initialized");
|
|
6941
|
+
}
|
|
6942
|
+
} catch (error) {
|
|
6943
|
+
debugLogger.error("Failed to load MCP tools from IndexedDB", { error });
|
|
6944
|
+
set({ isLoaded: true });
|
|
6945
|
+
}
|
|
6946
|
+
},
|
|
6947
|
+
saveTools: async () => {
|
|
6948
|
+
set({ saveState: "saving" });
|
|
6949
|
+
try {
|
|
6950
|
+
await persist();
|
|
6951
|
+
set({ saveState: "saved", lastSavedAt: Date.now() });
|
|
6952
|
+
return true;
|
|
6953
|
+
} catch (error) {
|
|
6954
|
+
debugLogger.error("Failed to save MCP tools to IndexedDB", { error });
|
|
6955
|
+
set({ saveState: "error" });
|
|
6956
|
+
return false;
|
|
6957
|
+
}
|
|
6958
|
+
},
|
|
6959
|
+
syncControllerTools: (controllerTools) => {
|
|
6960
|
+
const { choices } = get();
|
|
6961
|
+
const next = [...get().tools];
|
|
6962
|
+
let changed = false;
|
|
6963
|
+
for (const ct of controllerTools) {
|
|
6964
|
+
const functionName = (ct.id || ct.name).replace(/[^a-zA-Z0-9_]/g, "_");
|
|
6965
|
+
const index = [
|
|
6966
|
+
next.findIndex((lt) => lt.function.name === functionName),
|
|
6967
|
+
next.findIndex((lt) => lt.function.name === ct.id),
|
|
6968
|
+
next.findIndex((lt) => lt.id === ct.id)
|
|
6969
|
+
].find((i) => i >= 0) ?? -1;
|
|
6970
|
+
if (index < 0) {
|
|
6971
|
+
next.push({
|
|
6972
|
+
id: `tool-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
6973
|
+
name: functionName,
|
|
6974
|
+
description: ct.description,
|
|
6975
|
+
enabled: ct.isEnabled,
|
|
6894
6976
|
type: "function",
|
|
6895
6977
|
function: {
|
|
6896
|
-
name:
|
|
6897
|
-
|
|
6898
|
-
|
|
6978
|
+
name: ct.id,
|
|
6979
|
+
// the controller id maps back to the tool
|
|
6980
|
+
description: ct.description,
|
|
6981
|
+
parameters: { type: "object", properties: {}, required: [] }
|
|
6899
6982
|
},
|
|
6900
|
-
|
|
6901
|
-
|
|
6983
|
+
endpoint: `/mcp/${ct.id}`,
|
|
6984
|
+
method: "GET",
|
|
6985
|
+
isBuiltIn: false
|
|
6902
6986
|
});
|
|
6987
|
+
changed = true;
|
|
6988
|
+
continue;
|
|
6989
|
+
}
|
|
6990
|
+
const local = next[index];
|
|
6991
|
+
const enabled = Object.prototype.hasOwnProperty.call(choices, local.id) ? choices[local.id] : ct.isEnabled;
|
|
6992
|
+
if (local.enabled !== enabled) {
|
|
6993
|
+
next[index] = { ...local, enabled };
|
|
6994
|
+
changed = true;
|
|
6903
6995
|
}
|
|
6996
|
+
}
|
|
6997
|
+
if (!changed) return;
|
|
6998
|
+
set({ tools: next });
|
|
6999
|
+
void persistQuietly();
|
|
7000
|
+
},
|
|
7001
|
+
// Discover the user's connected MCP servers' tools from the gateway and merge
|
|
7002
|
+
// them into the tool list (so the model can call them). Replaces any prior
|
|
7003
|
+
// MCP-server tools; best-effort — a single unreachable server is skipped.
|
|
7004
|
+
loadMcpServerTools: async () => {
|
|
7005
|
+
let servers;
|
|
7006
|
+
try {
|
|
7007
|
+
servers = await listMcpServers();
|
|
6904
7008
|
} catch (error) {
|
|
6905
|
-
debugLogger.warn("
|
|
7009
|
+
debugLogger.warn("Could not list MCP servers", { error: String(error) });
|
|
7010
|
+
return;
|
|
7011
|
+
}
|
|
7012
|
+
const collected = [];
|
|
7013
|
+
for (const server of servers.filter((s) => s.enabled)) {
|
|
7014
|
+
try {
|
|
7015
|
+
const { tools } = await discoverMcpTools(server.id);
|
|
7016
|
+
for (const t of tools) {
|
|
7017
|
+
const fnName = sanitizeMcpToolName(`mcp_${server.name}_${t.name}`);
|
|
7018
|
+
const schema = t.inputSchema && typeof t.inputSchema === "object" && t.inputSchema.type === "object" ? t.inputSchema : { type: "object", properties: {}, required: [] };
|
|
7019
|
+
const id = `mcp-${server.id}-${t.name}`;
|
|
7020
|
+
const { choices } = get();
|
|
7021
|
+
collected.push({
|
|
7022
|
+
id,
|
|
7023
|
+
name: fnName,
|
|
7024
|
+
description: t.description || `${t.name} (via ${server.name})`,
|
|
7025
|
+
enabled: Object.prototype.hasOwnProperty.call(choices, id) ? choices[id] : true,
|
|
7026
|
+
type: "function",
|
|
7027
|
+
function: {
|
|
7028
|
+
name: fnName,
|
|
7029
|
+
description: t.description || `${t.name} \u2014 provided by the ${server.name} MCP server.`,
|
|
7030
|
+
parameters: schema
|
|
7031
|
+
},
|
|
7032
|
+
mcpServerId: server.id,
|
|
7033
|
+
mcpToolName: t.name
|
|
7034
|
+
});
|
|
7035
|
+
}
|
|
7036
|
+
} catch (error) {
|
|
7037
|
+
debugLogger.warn("Failed to discover tools for MCP server", { server: server.name, error: String(error) });
|
|
7038
|
+
}
|
|
6906
7039
|
}
|
|
7040
|
+
set((state) => ({
|
|
7041
|
+
tools: [...state.tools.filter((t) => !t.mcpServerId), ...collected]
|
|
7042
|
+
}));
|
|
7043
|
+
debugLogger.info("MCP server tools loaded", { count: collected.length });
|
|
6907
7044
|
}
|
|
6908
|
-
|
|
6909
|
-
|
|
6910
|
-
}));
|
|
6911
|
-
debugLogger.info("MCP server tools loaded", { count: collected.length });
|
|
6912
|
-
}
|
|
6913
|
-
}));
|
|
7045
|
+
};
|
|
7046
|
+
});
|
|
6914
7047
|
|
|
6915
7048
|
// src/chat/hooks/useKnowledgeStore.tsx
|
|
6916
7049
|
var import_react4 = require("react");
|
|
@@ -9296,9 +9429,17 @@ var useVectorStore = () => {
|
|
|
9296
9429
|
const hasAnyAdvancedFeatures = isAdvancedVectorFeaturesEnabled;
|
|
9297
9430
|
const hasVectorAccess = features.isAdmin() || features.hasAdvancedSearch() || features.hasAdvancedMemories();
|
|
9298
9431
|
const [hasCompatibleProvider, setHasCompatibleProvider] = (0, import_react3.useState)(false);
|
|
9432
|
+
const mountedRef = (0, import_react3.useRef)(true);
|
|
9433
|
+
(0, import_react3.useEffect)(() => {
|
|
9434
|
+
mountedRef.current = true;
|
|
9435
|
+
return () => {
|
|
9436
|
+
mountedRef.current = false;
|
|
9437
|
+
};
|
|
9438
|
+
}, []);
|
|
9299
9439
|
const checkProviderCompatibility = (0, import_react3.useCallback)(async () => {
|
|
9300
9440
|
try {
|
|
9301
9441
|
const { useAIProviderStore: useAIProviderStore2 } = await Promise.resolve().then(() => (init_aiProviderStore(), aiProviderStore_exports));
|
|
9442
|
+
if (!mountedRef.current) return false;
|
|
9302
9443
|
const aiProviderState = useAIProviderStore2.getState();
|
|
9303
9444
|
const provider = aiProviderState.provider;
|
|
9304
9445
|
const config = aiProviderState.config;
|
|
@@ -9315,7 +9456,7 @@ var useVectorStore = () => {
|
|
|
9315
9456
|
return isCompatible;
|
|
9316
9457
|
} catch (error) {
|
|
9317
9458
|
debugLogger.warn("Could not check AI provider for vector compatibility", { error });
|
|
9318
|
-
setHasCompatibleProvider(false);
|
|
9459
|
+
if (mountedRef.current) setHasCompatibleProvider(false);
|
|
9319
9460
|
return false;
|
|
9320
9461
|
}
|
|
9321
9462
|
}, []);
|
|
@@ -9323,12 +9464,10 @@ var useVectorStore = () => {
|
|
|
9323
9464
|
checkProviderCompatibility();
|
|
9324
9465
|
}, [checkProviderCompatibility]);
|
|
9325
9466
|
(0, import_react3.useEffect)(() => {
|
|
9326
|
-
const
|
|
9327
|
-
|
|
9328
|
-
|
|
9329
|
-
|
|
9330
|
-
};
|
|
9331
|
-
checkOnProviderChange();
|
|
9467
|
+
const timer = setTimeout(() => {
|
|
9468
|
+
void checkProviderCompatibility();
|
|
9469
|
+
}, 100);
|
|
9470
|
+
return () => clearTimeout(timer);
|
|
9332
9471
|
}, [authStore.token, checkProviderCompatibility]);
|
|
9333
9472
|
(0, import_react3.useEffect)(() => {
|
|
9334
9473
|
const interval = setInterval(() => {
|