@burtson-labs/bandit-engine 2.0.39 → 2.0.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +14 -11
  2. package/dist/{aiProviderStore-XN7GCBHJ.mjs → aiProviderStore-UQI33C5E.mjs} +2 -2
  3. package/dist/{chat-5QJNWB7I.mjs → chat-T5ANWWYQ.mjs} +5 -5
  4. package/dist/chat-provider.js +571 -122
  5. package/dist/chat-provider.js.map +1 -1
  6. package/dist/chat-provider.mjs +4 -4
  7. package/dist/{chunk-3A2527TE.mjs → chunk-22EY3ZDC.mjs} +3 -3
  8. package/dist/{chunk-ECRNIAG6.mjs → chunk-3E57HLDV.mjs} +4 -4
  9. package/dist/{chunk-QU5S5QQP.mjs → chunk-54ZQ3FSN.mjs} +481 -77
  10. package/dist/chunk-54ZQ3FSN.mjs.map +1 -0
  11. package/dist/{chunk-JRCDANLN.mjs → chunk-A6OBEF72.mjs} +75 -12
  12. package/dist/{chunk-JRCDANLN.mjs.map → chunk-A6OBEF72.mjs.map} +1 -1
  13. package/dist/{chunk-CDQYBO3Q.mjs → chunk-CX3INLYJ.mjs} +27 -5
  14. package/dist/chunk-CX3INLYJ.mjs.map +1 -0
  15. package/dist/{chunk-QYH2T4L5.mjs → chunk-LYWVYBKU.mjs} +3 -3
  16. package/dist/{chunk-WO5KFNNW.mjs → chunk-QFNEHSY4.mjs} +62 -24
  17. package/dist/chunk-QFNEHSY4.mjs.map +1 -0
  18. package/dist/{chunk-EOKIE5HZ.mjs → chunk-WPWWWUD7.mjs} +51 -46
  19. package/dist/chunk-WPWWWUD7.mjs.map +1 -0
  20. package/dist/{cli/cli.js → cli.js} +423 -10
  21. package/dist/cli.js.map +1 -0
  22. package/dist/{gateway-B0LJ3-jT.d.ts → gateway-5yt_3QDP.d.mts} +4 -4
  23. package/dist/{gateway-B0LJ3-jT.d.mts → gateway-5yt_3QDP.d.ts} +4 -4
  24. package/dist/index.d.mts +2 -2
  25. package/dist/index.d.ts +2 -2
  26. package/dist/index.js +756 -206
  27. package/dist/index.js.map +1 -1
  28. package/dist/index.mjs +8 -8
  29. package/dist/management/management.js +754 -204
  30. package/dist/management/management.js.map +1 -1
  31. package/dist/management/management.mjs +6 -6
  32. package/dist/modals/chat-modal/chat-modal.js +532 -88
  33. package/dist/modals/chat-modal/chat-modal.js.map +1 -1
  34. package/dist/modals/chat-modal/chat-modal.mjs +4 -4
  35. package/dist/public-types.d.mts +1 -1
  36. package/dist/public-types.d.ts +1 -1
  37. package/docs/01_quickstart.md +10 -4
  38. package/docs/02_gateway_api.md +19 -3
  39. package/docs/03_provider_integration.md +5 -4
  40. package/docs/api_reference/media/02_gateway_api.md +19 -3
  41. package/docs/api_reference/media/README.md +3 -1
  42. package/package.json +1 -1
  43. package/dist/chunk-CDQYBO3Q.mjs.map +0 -1
  44. package/dist/chunk-EOKIE5HZ.mjs.map +0 -1
  45. package/dist/chunk-QU5S5QQP.mjs.map +0 -1
  46. package/dist/chunk-WO5KFNNW.mjs.map +0 -1
  47. package/dist/cli/cli.js.map +0 -1
  48. /package/dist/{aiProviderStore-XN7GCBHJ.mjs.map → aiProviderStore-UQI33C5E.mjs.map} +0 -0
  49. /package/dist/{chat-5QJNWB7I.mjs.map → chat-T5ANWWYQ.mjs.map} +0 -0
  50. /package/dist/{chunk-3A2527TE.mjs.map → chunk-22EY3ZDC.mjs.map} +0 -0
  51. /package/dist/{chunk-ECRNIAG6.mjs.map → chunk-3E57HLDV.mjs.map} +0 -0
  52. /package/dist/{chunk-QYH2T4L5.mjs.map → chunk-LYWVYBKU.mjs.map} +0 -0
package/dist/index.js CHANGED
@@ -12,11 +12,11 @@ var __export = (target, all) => {
12
12
  for (var name in all)
13
13
  __defProp(target, name, { get: all[name], enumerable: true });
14
14
  };
15
- var __copyProps = (to, from9, except, desc) => {
16
- if (from9 && typeof from9 === "object" || typeof from9 === "function") {
17
- for (let key of __getOwnPropNames(from9))
15
+ var __copyProps = (to, from10, except, desc) => {
16
+ if (from10 && typeof from10 === "object" || typeof from10 === "function") {
17
+ for (let key of __getOwnPropNames(from10))
18
18
  if (!__hasOwnProp.call(to, key) && key !== except)
19
- __defProp(to, key, { get: () => from9[key], enumerable: !(desc = __getOwnPropDesc(from9, key)) || desc.enumerable });
19
+ __defProp(to, key, { get: () => from10[key], enumerable: !(desc = __getOwnPropDesc(from10, key)) || desc.enumerable });
20
20
  }
21
21
  return to;
22
22
  };
@@ -1567,13 +1567,17 @@ var init_conversationStore = __esm({
1567
1567
  const updatedConversations = conversations.map((c) => {
1568
1568
  if (c.id === currentId && c.history.length > 0) {
1569
1569
  const updatedHistory = [...c.history];
1570
+ const existingImages = updatedHistory[updatedHistory.length - 1].images;
1571
+ const nextImages = Array.isArray(images) && images.length > 0 ? [...images] : Array.isArray(existingImages) && existingImages.length > 0 ? [...existingImages] : existingImages;
1570
1572
  updatedHistory[updatedHistory.length - 1] = {
1571
1573
  ...updatedHistory[updatedHistory.length - 1],
1572
1574
  answer,
1573
1575
  memoryUpdated,
1574
- images: images ?? updatedHistory[updatedHistory.length - 1].images,
1576
+ images: nextImages,
1575
1577
  sourceFiles: sourceFiles ?? updatedHistory[updatedHistory.length - 1].sourceFiles,
1576
- cancelled: cancelled ?? updatedHistory[updatedHistory.length - 1].cancelled
1578
+ cancelled: cancelled ?? updatedHistory[updatedHistory.length - 1].cancelled,
1579
+ placeholder: false,
1580
+ rawQuestion: void 0
1577
1581
  };
1578
1582
  return normalizeConversation({ ...c, history: updatedHistory, updatedAt: /* @__PURE__ */ new Date() });
1579
1583
  }
@@ -1654,6 +1658,24 @@ var init_conversationStore = __esm({
1654
1658
  });
1655
1659
  continue;
1656
1660
  }
1661
+ if (Array.isArray(existing.history) && Array.isArray(conversation.history)) {
1662
+ const mergedHistory = conversation.history.map((incomingEntry, index) => {
1663
+ const existingEntry = existing.history[index];
1664
+ if (!existingEntry) {
1665
+ return incomingEntry;
1666
+ }
1667
+ const mergedImagesSource = Array.isArray(incomingEntry.images) && incomingEntry.images.length > 0 ? incomingEntry.images : existingEntry.images;
1668
+ const mergedImages = Array.isArray(mergedImagesSource) && mergedImagesSource.length > 0 ? [...mergedImagesSource] : mergedImagesSource;
1669
+ return {
1670
+ ...existingEntry,
1671
+ ...incomingEntry,
1672
+ images: mergedImages,
1673
+ placeholder: incomingEntry.placeholder ?? existingEntry.placeholder,
1674
+ rawQuestion: incomingEntry.rawQuestion ?? existingEntry.rawQuestion
1675
+ };
1676
+ });
1677
+ conversation.history = mergedHistory;
1678
+ }
1657
1679
  }
1658
1680
  next.set(conversation.id, conversation);
1659
1681
  toPersist.push(conversation);
@@ -4446,57 +4468,15 @@ var init_gateway_service = __esm({
4446
4468
  */
4447
4469
  chat(request) {
4448
4470
  const endpoint = request.provider === "ollama" ? `/api/${request.provider}/chat` : request.provider ? `/api/${request.provider}/chat/completions` : "/api/chat/completions";
4449
- const url = `${this._baseUrl}${endpoint}`;
4450
- debugLogger.debug(`Gateway chat request to ${url} with provider: ${request.provider || "default"}`, {
4451
- model: request.model,
4452
- messageCount: request.messages.length,
4453
- hasImages: !!(request.images && request.images.length > 0),
4454
- imageCount: request.images?.length || 0
4455
- });
4456
- const requestBody = { ...request, stream: request.stream !== false };
4471
+ const fallbackEndpoint = request.provider === "bandit" ? "/completions" : null;
4472
+ const normalizedModel = request.provider === "bandit" ? (() => {
4473
+ const trimmed = (request.model ?? "").replace(/^bandit:/, "").trim();
4474
+ return trimmed !== "" ? trimmed : "bandit-core-1";
4475
+ })() : request.model;
4476
+ const requestBody = { ...request, model: normalizedModel, stream: request.stream !== false };
4457
4477
  return new import_rxjs6.Observable((observer) => {
4458
4478
  const controller = new AbortController();
4459
- const task = fetch(url, {
4460
- method: "POST",
4461
- headers: this._getHeaders(),
4462
- body: JSON.stringify(requestBody),
4463
- signal: controller.signal
4464
- });
4465
- task.then(async (response) => {
4466
- debugLogger.debug(`Gateway chat response status: ${response.status} for provider: ${request.provider || "default"}`);
4467
- if (!response.ok) {
4468
- let errorText = "";
4469
- let errorData = null;
4470
- try {
4471
- errorText = await response.text();
4472
- debugLogger.error("GatewayService chat error response body", {
4473
- status: response.status,
4474
- statusText: response.statusText,
4475
- url: response.url,
4476
- body: errorText
4477
- });
4478
- } catch (readError) {
4479
- debugLogger.error("GatewayService chat failed to read error response body", { error: readError });
4480
- errorText = `Request failed with status ${response.status}`;
4481
- }
4482
- try {
4483
- errorData = JSON.parse(errorText);
4484
- debugLogger.error("GatewayService chat parsed error payload", errorData);
4485
- } catch (parseError) {
4486
- debugLogger.error("GatewayService chat error payload was not valid JSON");
4487
- errorData = { message: errorText };
4488
- }
4489
- const error = this._createHttpError(
4490
- `POST ${url} failed: ${response.status} ${response.statusText ?? ""}`,
4491
- {
4492
- status: response.status,
4493
- statusText: response.statusText ?? "",
4494
- data: errorData,
4495
- url
4496
- }
4497
- );
4498
- throw error;
4499
- }
4479
+ const handleStreamingResponse = async (response) => {
4500
4480
  const reader = response.body?.getReader();
4501
4481
  const decoder = new TextDecoder();
4502
4482
  let buffer = "";
@@ -4571,14 +4551,75 @@ var init_gateway_service = __esm({
4571
4551
  }).catch((err) => observer.error(err));
4572
4552
  };
4573
4553
  read();
4574
- }).catch((err) => {
4575
- debugLogger.error("GatewayService chat fetch error", {
4576
- error: err,
4577
- url,
4578
- provider: request.provider
4554
+ };
4555
+ const sendRequest = (targetEndpoint, allowFallback) => {
4556
+ const url = `${this._baseUrl}${targetEndpoint}`;
4557
+ debugLogger.debug(`Gateway chat request to ${url} with provider: ${request.provider || "default"}`, {
4558
+ model: normalizedModel,
4559
+ messageCount: request.messages.length,
4560
+ hasImages: !!(request.images && request.images.length > 0),
4561
+ imageCount: request.images?.length || 0
4579
4562
  });
4580
- observer.error(err);
4581
- });
4563
+ fetch(url, {
4564
+ method: "POST",
4565
+ headers: this._getHeaders(),
4566
+ body: JSON.stringify(requestBody),
4567
+ signal: controller.signal
4568
+ }).then(async (response) => {
4569
+ debugLogger.debug(`Gateway chat response status: ${response.status} for provider: ${request.provider || "default"}`);
4570
+ if (response.status === 404 && allowFallback && fallbackEndpoint) {
4571
+ debugLogger.warn("GatewayService chat endpoint returned 404, attempting fallback route", {
4572
+ provider: request.provider,
4573
+ attemptedEndpoint: targetEndpoint,
4574
+ fallbackEndpoint
4575
+ });
4576
+ sendRequest(fallbackEndpoint, false);
4577
+ return;
4578
+ }
4579
+ if (!response.ok) {
4580
+ let errorText = "";
4581
+ let errorData = null;
4582
+ try {
4583
+ errorText = await response.text();
4584
+ debugLogger.error("GatewayService chat error response body", {
4585
+ status: response.status,
4586
+ statusText: response.statusText,
4587
+ url: response.url,
4588
+ body: errorText
4589
+ });
4590
+ } catch (readError) {
4591
+ debugLogger.error("GatewayService chat failed to read error response body", { error: readError });
4592
+ errorText = `Request failed with status ${response.status}`;
4593
+ }
4594
+ try {
4595
+ errorData = JSON.parse(errorText);
4596
+ debugLogger.error("GatewayService chat parsed error payload", errorData);
4597
+ } catch (parseError) {
4598
+ debugLogger.error("GatewayService chat error payload was not valid JSON");
4599
+ errorData = { message: errorText };
4600
+ }
4601
+ const error = this._createHttpError(
4602
+ `POST ${url} failed: ${response.status} ${response.statusText ?? ""}`,
4603
+ {
4604
+ status: response.status,
4605
+ statusText: response.statusText ?? "",
4606
+ data: errorData,
4607
+ url
4608
+ }
4609
+ );
4610
+ throw error;
4611
+ }
4612
+ await handleStreamingResponse(response);
4613
+ }).catch((err) => {
4614
+ debugLogger.error("GatewayService chat fetch error", {
4615
+ error: err,
4616
+ url,
4617
+ provider: request.provider
4618
+ });
4619
+ observer.error(err);
4620
+ });
4621
+ };
4622
+ sendRequest(endpoint, true);
4582
4623
  return () => {
4583
4624
  try {
4584
4625
  controller.abort();
@@ -4593,12 +4634,18 @@ var init_gateway_service = __esm({
4593
4634
  generate(request) {
4594
4635
  const endpoint = request.provider ? `/api/${request.provider}/generate` : "/api/generate";
4595
4636
  const url = `${this._baseUrl}${endpoint}`;
4596
- debugLogger.debug(`Gateway generate request to ${url} with provider: ${request.provider || "default"}`);
4637
+ const normalizedModel = request.provider === "bandit" ? (() => {
4638
+ const trimmed = (request.model ?? "").replace(/^bandit:/, "").trim();
4639
+ return trimmed !== "" ? trimmed : "bandit-core-1";
4640
+ })() : request.model;
4641
+ debugLogger.debug(`Gateway generate request to ${url} with provider: ${request.provider || "default"}`, {
4642
+ model: normalizedModel
4643
+ });
4597
4644
  return new import_rxjs6.Observable((observer) => {
4598
4645
  const task = fetch(url, {
4599
4646
  method: "POST",
4600
4647
  headers: this._getHeaders(),
4601
- body: JSON.stringify({ ...request, stream: request.stream !== false })
4648
+ body: JSON.stringify({ ...request, model: normalizedModel, stream: request.stream !== false })
4602
4649
  });
4603
4650
  task.then(async (response) => {
4604
4651
  if (!response.ok) {
@@ -4705,18 +4752,46 @@ var init_gateway_service = __esm({
4705
4752
  );
4706
4753
  }
4707
4754
  _getHeaders() {
4708
- const token = this._tokenFactory();
4755
+ const rawToken2 = this._tokenFactory();
4709
4756
  const headers = {
4710
4757
  "Content-Type": "application/json"
4711
4758
  };
4712
- if (token && token.trim() !== "") {
4713
- headers["Authorization"] = `Bearer ${token}`;
4714
- debugLogger.debug("Authorization header set with token");
4715
- } else {
4759
+ if (!rawToken2) {
4716
4760
  debugLogger.warn("GatewayService: No token found, skipping Authorization header");
4761
+ return headers;
4762
+ }
4763
+ const token = rawToken2.trim();
4764
+ if (token === "") {
4765
+ debugLogger.warn("GatewayService: Token factory returned empty string");
4766
+ return headers;
4767
+ }
4768
+ if (/^(Bearer|ApiKey)\s+/i.test(token)) {
4769
+ headers["Authorization"] = token;
4770
+ debugLogger.debug("GatewayService: Authorization header set with explicit scheme");
4771
+ return headers;
4772
+ }
4773
+ if (this._isLikelyBanditApiKey(token)) {
4774
+ headers["Authorization"] = `ApiKey ${token}`;
4775
+ headers["X-Burtson-Api-Key"] = token;
4776
+ debugLogger.debug("GatewayService: Authorization header set using API key");
4777
+ return headers;
4717
4778
  }
4779
+ if (this._isLikelyJwt(token)) {
4780
+ headers["Authorization"] = `Bearer ${token}`;
4781
+ debugLogger.debug("GatewayService: Authorization header set using bearer token");
4782
+ return headers;
4783
+ }
4784
+ headers["Authorization"] = `Bearer ${token}`;
4785
+ debugLogger.debug("GatewayService: Authorization header defaulted to bearer scheme");
4718
4786
  return headers;
4719
4787
  }
4788
+ _isLikelyJwt(token) {
4789
+ const segments = token.split(".");
4790
+ return segments.length === 3 && segments.every((segment) => segment.length > 0);
4791
+ }
4792
+ _isLikelyBanditApiKey(value) {
4793
+ return /^bai_[a-z0-9]{10,}$/i.test(value);
4794
+ }
4720
4795
  /**
4721
4796
  * Submit feedback to the gateway API
4722
4797
  */
@@ -5196,6 +5271,112 @@ var init_ollama_gateway_service = __esm({
5196
5271
  }
5197
5272
  });
5198
5273
 
5274
+ // src/services/gateway/bandit-gateway.service.ts
5275
+ var import_operators5, normalizeBanditModel, isGatewayMessageContent, normalizeBanditMessages, BanditAIGatewayService;
5276
+ var init_bandit_gateway_service = __esm({
5277
+ "src/services/gateway/bandit-gateway.service.ts"() {
5278
+ "use strict";
5279
+ init_gateway_service();
5280
+ import_operators5 = require("rxjs/operators");
5281
+ init_debugLogger();
5282
+ normalizeBanditModel = (model) => {
5283
+ if (typeof model !== "string" || model.trim() === "") {
5284
+ return "bandit-core-1";
5285
+ }
5286
+ const normalized = model.replace(/^bandit:/, "").trim();
5287
+ return normalized === "" ? "bandit-core-1" : normalized;
5288
+ };
5289
+ isGatewayMessageContent = (value) => {
5290
+ if (!value || typeof value !== "object") return false;
5291
+ const candidate = value;
5292
+ if (candidate.type !== "text" && candidate.type !== "image_url") {
5293
+ return false;
5294
+ }
5295
+ if (candidate.type === "text") {
5296
+ return typeof candidate.text === "string";
5297
+ }
5298
+ if (candidate.type === "image_url") {
5299
+ return !!candidate.image_url && typeof candidate.image_url.url === "string";
5300
+ }
5301
+ return false;
5302
+ };
5303
+ normalizeBanditMessages = (messages) => messages.map((message) => {
5304
+ const content = message.content;
5305
+ if (typeof content === "string") {
5306
+ return { role: message.role, content };
5307
+ }
5308
+ if (Array.isArray(content)) {
5309
+ const filtered = content.filter(isGatewayMessageContent);
5310
+ if (filtered.length === 0) {
5311
+ return { role: message.role, content: JSON.stringify(content) };
5312
+ }
5313
+ return {
5314
+ role: message.role,
5315
+ content: filtered
5316
+ };
5317
+ }
5318
+ return { role: message.role, content: content != null ? String(content) : "" };
5319
+ });
5320
+ BanditAIGatewayService = class {
5321
+ _gatewayService;
5322
+ constructor(gatewayUrl, tokenFactory) {
5323
+ this._gatewayService = new GatewayService(gatewayUrl, tokenFactory);
5324
+ debugLogger.info("BanditAIGatewayService initialized", { gatewayUrl });
5325
+ }
5326
+ async validateServiceAvailability(args) {
5327
+ return this._gatewayService.validateServiceAvailability(args);
5328
+ }
5329
+ chat(request) {
5330
+ const model = normalizeBanditModel(request.model);
5331
+ const messages = normalizeBanditMessages(request.messages);
5332
+ const gatewayRequest = {
5333
+ ...request,
5334
+ messages,
5335
+ model,
5336
+ provider: "bandit",
5337
+ stream: request.stream
5338
+ };
5339
+ debugLogger.debug("Bandit Gateway chat request", {
5340
+ model,
5341
+ messageCount: request.messages.length,
5342
+ stream: request.stream
5343
+ });
5344
+ return this._gatewayService.chat(gatewayRequest);
5345
+ }
5346
+ complete(prompt, options) {
5347
+ const model = normalizeBanditModel(options.model);
5348
+ const gatewayRequest = {
5349
+ model,
5350
+ prompt,
5351
+ temperature: options.temperature,
5352
+ max_tokens: options.max_tokens,
5353
+ stream: options.stream,
5354
+ stop: options.stop,
5355
+ provider: "bandit"
5356
+ };
5357
+ debugLogger.debug("Bandit Gateway generate request", {
5358
+ model,
5359
+ promptLength: prompt.length,
5360
+ stream: options.stream
5361
+ });
5362
+ return this._gatewayService.generate(gatewayRequest);
5363
+ }
5364
+ listModels() {
5365
+ debugLogger.debug("Fetching Bandit models through gateway");
5366
+ return this._gatewayService.listModelsByProvider("bandit");
5367
+ }
5368
+ getHealth() {
5369
+ return this._gatewayService.getHealth().pipe(
5370
+ (0, import_operators5.map)((health) => ({
5371
+ ...health,
5372
+ bandit_status: health.providers.find((p) => p.name === "bandit")?.status || "unavailable"
5373
+ }))
5374
+ );
5375
+ }
5376
+ };
5377
+ }
5378
+ });
5379
+
5199
5380
  // src/services/ai-provider/providers/gateway.provider.ts
5200
5381
  var import_rxjs7, GatewayProvider;
5201
5382
  var init_gateway_provider = __esm({
@@ -5209,6 +5390,7 @@ var init_gateway_provider = __esm({
5209
5390
  init_azure_openai_gateway_service();
5210
5391
  init_anthropic_gateway_service();
5211
5392
  init_ollama_gateway_service();
5393
+ init_bandit_gateway_service();
5212
5394
  GatewayProvider = class {
5213
5395
  config;
5214
5396
  gatewayService;
@@ -5250,6 +5432,9 @@ var init_gateway_provider = __esm({
5250
5432
  case "anthropic":
5251
5433
  this.providerSpecificService = new AnthropicGatewayService(gatewayUrl, tokenFactory);
5252
5434
  break;
5435
+ case "bandit":
5436
+ this.providerSpecificService = new BanditAIGatewayService(gatewayUrl, tokenFactory);
5437
+ break;
5253
5438
  case "ollama":
5254
5439
  this.providerSpecificService = new OllamaGatewayService(gatewayUrl, tokenFactory);
5255
5440
  break;
@@ -5264,6 +5449,16 @@ var init_gateway_provider = __esm({
5264
5449
  role: msg.role,
5265
5450
  content: msg.content
5266
5451
  }));
5452
+ const normalizeImageUrl2 = (value) => {
5453
+ if (!value) {
5454
+ return value;
5455
+ }
5456
+ const trimmed = value.trim();
5457
+ if (/^data:/i.test(trimmed) || /^https?:\/\//i.test(trimmed)) {
5458
+ return trimmed;
5459
+ }
5460
+ return `data:image/jpeg;base64,${trimmed}`;
5461
+ };
5267
5462
  if (request.images && request.images.length > 0) {
5268
5463
  const lastUserMessageIndex = messages.map((m) => m.role).lastIndexOf("user");
5269
5464
  if (this.config.provider === "ollama") {
@@ -5273,7 +5468,7 @@ var init_gateway_provider = __esm({
5273
5468
  images: request.images
5274
5469
  };
5275
5470
  }
5276
- } else if (["openai", "azure-openai", "anthropic"].includes(this.config.provider || "")) {
5471
+ } else if (["openai", "azure-openai", "anthropic", "bandit"].includes(this.config.provider || "")) {
5277
5472
  if (lastUserMessageIndex !== -1) {
5278
5473
  const currentMessage = messages[lastUserMessageIndex];
5279
5474
  const contentArray = [
@@ -5282,11 +5477,11 @@ var init_gateway_provider = __esm({
5282
5477
  text: currentMessage.content
5283
5478
  }
5284
5479
  ];
5285
- request.images.forEach((base64Image) => {
5480
+ request.images.forEach((imageRef) => {
5286
5481
  contentArray.push({
5287
5482
  type: "image_url",
5288
5483
  image_url: {
5289
- url: base64Image.startsWith("data:") ? base64Image : `data:image/jpeg;base64,${base64Image}`,
5484
+ url: normalizeImageUrl2(imageRef),
5290
5485
  detail: "auto"
5291
5486
  }
5292
5487
  });
@@ -5295,6 +5490,11 @@ var init_gateway_provider = __esm({
5295
5490
  ...messages[lastUserMessageIndex],
5296
5491
  content: contentArray
5297
5492
  };
5493
+ debugLogger.debug("Gateway provider injected image attachments", {
5494
+ provider: this.config.provider,
5495
+ imageCount: request.images.length,
5496
+ messageIndex: lastUserMessageIndex
5497
+ });
5298
5498
  }
5299
5499
  }
5300
5500
  }
@@ -5803,6 +6003,244 @@ var init_xai_provider = __esm({
5803
6003
  }
5804
6004
  });
5805
6005
 
6006
+ // src/services/ai-provider/providers/bandit-ai.provider.ts
6007
+ var import_rxjs10, DEFAULT_BANDIT_BASE, normalizeImageUrl, injectImagesIntoMessages, BanditAIProvider;
6008
+ var init_bandit_ai_provider = __esm({
6009
+ "src/services/ai-provider/providers/bandit-ai.provider.ts"() {
6010
+ "use strict";
6011
+ import_rxjs10 = require("rxjs");
6012
+ init_common_types();
6013
+ init_debugLogger();
6014
+ DEFAULT_BANDIT_BASE = "https://api.burtson.ai";
6015
+ normalizeImageUrl = (value) => {
6016
+ if (!value) {
6017
+ return null;
6018
+ }
6019
+ const trimmed = value.trim();
6020
+ if (!trimmed) {
6021
+ return null;
6022
+ }
6023
+ if (/^data:/i.test(trimmed) || /^https?:\/\//i.test(trimmed)) {
6024
+ return trimmed;
6025
+ }
6026
+ return `data:image/jpeg;base64,${trimmed}`;
6027
+ };
6028
+ injectImagesIntoMessages = (messages, images) => {
6029
+ const normalized = messages.map((message) => ({
6030
+ role: message.role,
6031
+ content: message.content
6032
+ }));
6033
+ if (!images || images.length === 0) {
6034
+ return normalized;
6035
+ }
6036
+ const normalizedImages = images.map(normalizeImageUrl).filter((url) => Boolean(url));
6037
+ if (normalizedImages.length === 0) {
6038
+ return normalized;
6039
+ }
6040
+ const lastUserIndex = normalized.map((msg) => msg.role).lastIndexOf("user");
6041
+ if (lastUserIndex === -1) {
6042
+ return normalized;
6043
+ }
6044
+ const target = normalized[lastUserIndex];
6045
+ const baseContent = typeof target.content === "string" && target.content.trim().length > 0 ? [
6046
+ {
6047
+ type: "text",
6048
+ text: target.content
6049
+ }
6050
+ ] : [];
6051
+ const imageContent = normalizedImages.map((url) => ({
6052
+ type: "image_url",
6053
+ image_url: { url, detail: "auto" }
6054
+ }));
6055
+ normalized[lastUserIndex] = {
6056
+ role: target.role,
6057
+ content: [...baseContent, ...imageContent]
6058
+ };
6059
+ return normalized;
6060
+ };
6061
+ BanditAIProvider = class {
6062
+ config;
6063
+ baseUrl;
6064
+ constructor(config) {
6065
+ this.config = config;
6066
+ this.baseUrl = (config.baseUrl || DEFAULT_BANDIT_BASE).replace(/\/$/, "");
6067
+ }
6068
+ chat(request) {
6069
+ const url = `${this.baseUrl}/chat/completions`;
6070
+ const messages = injectImagesIntoMessages(request.messages, request.images);
6071
+ const payload = {
6072
+ model: request.model,
6073
+ messages,
6074
+ stream: Boolean(request.stream),
6075
+ temperature: request.temperature,
6076
+ max_tokens: request.maxTokens
6077
+ };
6078
+ if (request.stream) {
6079
+ return this.streamChatRequest(url, payload);
6080
+ }
6081
+ return this.nonStreamChatRequest(url, payload);
6082
+ }
6083
+ generate(request) {
6084
+ const chatRequest = {
6085
+ model: request.model,
6086
+ messages: [{ role: "user", content: request.prompt }],
6087
+ stream: request.stream,
6088
+ options: request.options
6089
+ };
6090
+ return this.chat(chatRequest).pipe(
6091
+ (0, import_rxjs10.map)((response) => ({
6092
+ response: response.message.content,
6093
+ done: response.done
6094
+ }))
6095
+ );
6096
+ }
6097
+ listModels() {
6098
+ const url = `${this.baseUrl}/models`;
6099
+ return (0, import_rxjs10.from)(fetch(url, { headers: this.getHeaders() })).pipe(
6100
+ (0, import_rxjs10.switchMap)((response) => {
6101
+ if (!response.ok) {
6102
+ debugLogger.error("BanditAI listModels failed", { status: response.status, url });
6103
+ return (0, import_rxjs10.throwError)(() => new Error(`Failed to list Bandit models: ${response.status}`));
6104
+ }
6105
+ return (0, import_rxjs10.from)(response.json());
6106
+ }),
6107
+ (0, import_rxjs10.map)(
6108
+ (data) => data.data.map((model) => ({
6109
+ name: model.id,
6110
+ details: {
6111
+ format: "bandit",
6112
+ family: model.object
6113
+ }
6114
+ }))
6115
+ )
6116
+ );
6117
+ }
6118
+ async validateServiceAvailability(args) {
6119
+ const attempt = async (url) => {
6120
+ try {
6121
+ const controller = new AbortController();
6122
+ const timeoutId = setTimeout(() => controller.abort(), args.timeoutMs);
6123
+ const response = await fetch(`${url}/models`, {
6124
+ headers: this.getHeaders(),
6125
+ signal: controller.signal
6126
+ });
6127
+ clearTimeout(timeoutId);
6128
+ return response.ok;
6129
+ } catch (error) {
6130
+ debugLogger.warn("BanditAI availability check failed", { url, error });
6131
+ return false;
6132
+ }
6133
+ };
6134
+ const primary = await attempt(this.baseUrl);
6135
+ if (primary) {
6136
+ return { url: this.baseUrl, isAvailable: true };
6137
+ }
6138
+ if (args.fallbackUrl) {
6139
+ const fallback = args.fallbackUrl.replace(/\/$/, "");
6140
+ if (await attempt(fallback)) {
6141
+ this.baseUrl = fallback;
6142
+ return { url: fallback, isAvailable: true };
6143
+ }
6144
+ }
6145
+ return { url: this.baseUrl, isAvailable: false };
6146
+ }
6147
+ getProviderType() {
6148
+ return "bandit" /* BANDIT */;
6149
+ }
6150
+ getConfig() {
6151
+ return this.config;
6152
+ }
6153
+ streamChatRequest(url, payload) {
6154
+ return new import_rxjs10.Observable((observer) => {
6155
+ const task = fetch(url, {
6156
+ method: "POST",
6157
+ headers: {
6158
+ ...this.getHeaders(),
6159
+ "Content-Type": "application/json"
6160
+ },
6161
+ body: JSON.stringify(payload)
6162
+ });
6163
+ task.then((response) => {
6164
+ if (!response.ok) {
6165
+ observer.error(new Error(`BanditAI request failed: ${response.status}`));
6166
+ return;
6167
+ }
6168
+ const reader = response.body?.getReader();
6169
+ const decoder = new TextDecoder();
6170
+ let buffer = "";
6171
+ const read = () => {
6172
+ reader?.read().then(({ done, value }) => {
6173
+ if (done) {
6174
+ observer.next({ message: { content: "", role: "assistant" }, done: true });
6175
+ observer.complete();
6176
+ return;
6177
+ }
6178
+ buffer += decoder.decode(value, { stream: true });
6179
+ const lines = buffer.split("\n");
6180
+ buffer = lines.pop() ?? "";
6181
+ for (const rawLine of lines) {
6182
+ const line = rawLine.trim();
6183
+ if (!line.startsWith("data: ")) {
6184
+ continue;
6185
+ }
6186
+ const data = line.slice(6).trim();
6187
+ if (data === "[DONE]") {
6188
+ observer.next({ message: { content: "", role: "assistant" }, done: true });
6189
+ observer.complete();
6190
+ return;
6191
+ }
6192
+ try {
6193
+ const parsed = JSON.parse(data);
6194
+ const content = parsed.choices?.[0]?.delta?.content ?? "";
6195
+ if (content) {
6196
+ observer.next({ message: { content, role: "assistant" }, done: false });
6197
+ }
6198
+ } catch (error) {
6199
+ debugLogger.error("BanditAI stream chunk parse failure", { data, error });
6200
+ }
6201
+ }
6202
+ read();
6203
+ }).catch((err) => observer.error(err));
6204
+ };
6205
+ read();
6206
+ }).catch((err) => observer.error(err));
6207
+ });
6208
+ }
6209
+ nonStreamChatRequest(url, payload) {
6210
+ return (0, import_rxjs10.from)(fetch(url, {
6211
+ method: "POST",
6212
+ headers: {
6213
+ ...this.getHeaders(),
6214
+ "Content-Type": "application/json"
6215
+ },
6216
+ body: JSON.stringify(payload)
6217
+ })).pipe(
6218
+ (0, import_rxjs10.switchMap)((response) => {
6219
+ if (!response.ok) {
6220
+ return (0, import_rxjs10.throwError)(() => new Error(`BanditAI request failed: ${response.status}`));
6221
+ }
6222
+ return (0, import_rxjs10.from)(response.json());
6223
+ }),
6224
+ (0, import_rxjs10.map)((data) => ({
6225
+ message: {
6226
+ content: data.choices?.[0]?.message?.content ?? "",
6227
+ role: "assistant"
6228
+ },
6229
+ done: true
6230
+ }))
6231
+ );
6232
+ }
6233
+ getHeaders() {
6234
+ const headers = {};
6235
+ if (this.config.apiKey) {
6236
+ headers["Authorization"] = `Bearer ${this.config.apiKey}`;
6237
+ }
6238
+ return headers;
6239
+ }
6240
+ };
6241
+ }
6242
+ });
6243
+
5806
6244
  // src/services/ai-provider/ai-provider.factory.ts
5807
6245
  var AIProviderFactory;
5808
6246
  var init_ai_provider_factory = __esm({
@@ -5816,6 +6254,7 @@ var init_ai_provider_factory = __esm({
5816
6254
  init_gateway_provider();
5817
6255
  init_playground_provider();
5818
6256
  init_xai_provider();
6257
+ init_bandit_ai_provider();
5819
6258
  AIProviderFactory = class {
5820
6259
  static createProvider(config) {
5821
6260
  switch (config.type) {
@@ -5829,6 +6268,8 @@ var init_ai_provider_factory = __esm({
5829
6268
  return new AnthropicProvider(config);
5830
6269
  case "xai" /* XAI */:
5831
6270
  return new XAIProvider(config);
6271
+ case "bandit" /* BANDIT */:
6272
+ return new BanditAIProvider(config);
5832
6273
  case "gateway" /* GATEWAY */:
5833
6274
  return new GatewayProvider(config);
5834
6275
  case "playground" /* PLAYGROUND */:
@@ -5843,6 +6284,7 @@ var init_ai_provider_factory = __esm({
5843
6284
  "openai" /* OPENAI */,
5844
6285
  "azure-openai" /* AZURE_OPENAI */,
5845
6286
  "xai" /* XAI */,
6287
+ "bandit" /* BANDIT */,
5846
6288
  "gateway" /* GATEWAY */,
5847
6289
  "playground" /* PLAYGROUND */
5848
6290
  ];
@@ -5860,6 +6302,8 @@ var init_ai_provider_factory = __esm({
5860
6302
  return !!config.apiKey;
5861
6303
  case "xai" /* XAI */:
5862
6304
  return !!config.apiKey;
6305
+ case "bandit" /* BANDIT */:
6306
+ return !!config.apiKey;
5863
6307
  case "gateway" /* GATEWAY */:
5864
6308
  return !!(config.gatewayUrl && config.provider);
5865
6309
  case "playground" /* PLAYGROUND */:
@@ -6220,11 +6664,11 @@ var init_notificationService = __esm({
6220
6664
  });
6221
6665
 
6222
6666
  // src/services/prompts/conversationStarters.ts
6223
- var import_rxjs10, generateConversationStarters;
6667
+ var import_rxjs11, generateConversationStarters;
6224
6668
  var init_conversationStarters = __esm({
6225
6669
  "src/services/prompts/conversationStarters.ts"() {
6226
6670
  "use strict";
6227
- import_rxjs10 = require("rxjs");
6671
+ import_rxjs11 = require("rxjs");
6228
6672
  init_aiProviderStore();
6229
6673
  init_packageSettingsStore();
6230
6674
  init_getStableQuestionPrompt();
@@ -6245,7 +6689,7 @@ var init_conversationStarters = __esm({
6245
6689
  stream: false,
6246
6690
  options: { temperature: 1.5, num_predict: 250 }
6247
6691
  });
6248
- const questions$ = data$.pipe((0, import_rxjs10.map)((d) => {
6692
+ const questions$ = data$.pipe((0, import_rxjs11.map)((d) => {
6249
6693
  const sanitizeLine = (line) => {
6250
6694
  const withoutNumbering = line.replace(/^[0-9]+[.)\-\s:]+/, "").replace(/^[•*+-]\s+/, "");
6251
6695
  const withoutQuotes = withoutNumbering.replace(/^[“"']+/, "").replace(/[”"']+$/, "");
@@ -6267,7 +6711,7 @@ var init_conversationStarters = __esm({
6267
6711
  });
6268
6712
  return unique;
6269
6713
  }));
6270
- const starters = await (0, import_rxjs10.lastValueFrom)(questions$);
6714
+ const starters = await (0, import_rxjs11.lastValueFrom)(questions$);
6271
6715
  if (starters.length === 0) {
6272
6716
  debugLogger.warn("No meaningful conversation starters generated");
6273
6717
  return [];
@@ -6283,11 +6727,11 @@ var init_conversationStarters = __esm({
6283
6727
  });
6284
6728
 
6285
6729
  // src/services/prompts/moodDetection.ts
6286
- var import_rxjs11, detectMessageMood;
6730
+ var import_rxjs12, detectMessageMood;
6287
6731
  var init_moodDetection = __esm({
6288
6732
  "src/services/prompts/moodDetection.ts"() {
6289
6733
  "use strict";
6290
- import_rxjs11 = require("rxjs");
6734
+ import_rxjs12 = require("rxjs");
6291
6735
  init_aiProviderStore();
6292
6736
  init_packageSettingsStore();
6293
6737
  init_debugLogger();
@@ -6318,8 +6762,8 @@ Response:`;
6318
6762
  options: { temperature: 0.3, num_predict: 10 }
6319
6763
  });
6320
6764
  const chunks$ = response$.pipe(
6321
- (0, import_rxjs11.map)((chunk) => chunk.response.trim().toLowerCase()),
6322
- (0, import_rxjs11.toArray)()
6765
+ (0, import_rxjs12.map)((chunk) => chunk.response.trim().toLowerCase()),
6766
+ (0, import_rxjs12.toArray)()
6323
6767
  );
6324
6768
  const result = await chunks$.toPromise();
6325
6769
  const finalResult = (result || []).join("").trim();
@@ -6336,11 +6780,11 @@ Response:`;
6336
6780
  });
6337
6781
 
6338
6782
  // src/services/prompts/detectUserInterestAndExcitement.ts
6339
- var import_rxjs12, detectUserInterestAndExcitement;
6783
+ var import_rxjs13, detectUserInterestAndExcitement;
6340
6784
  var init_detectUserInterestAndExcitement = __esm({
6341
6785
  "src/services/prompts/detectUserInterestAndExcitement.ts"() {
6342
6786
  "use strict";
6343
- import_rxjs12 = require("rxjs");
6787
+ import_rxjs13 = require("rxjs");
6344
6788
  init_aiProviderStore();
6345
6789
  init_packageSettingsStore();
6346
6790
  init_debugLogger();
@@ -6387,10 +6831,10 @@ var init_detectUserInterestAndExcitement = __esm({
6387
6831
  options: { temperature: 0.1, num_predict: 5 }
6388
6832
  });
6389
6833
  const chunks$ = response$.pipe(
6390
- (0, import_rxjs12.map)((chunk) => chunk.response.trim().toUpperCase()),
6391
- (0, import_rxjs12.toArray)()
6834
+ (0, import_rxjs13.map)((chunk) => chunk.response.trim().toUpperCase()),
6835
+ (0, import_rxjs13.toArray)()
6392
6836
  );
6393
- const result = await (0, import_rxjs12.lastValueFrom)(chunks$);
6837
+ const result = await (0, import_rxjs13.lastValueFrom)(chunks$);
6394
6838
  const decision = result.join("").trim();
6395
6839
  debugLogger.llmDebug("detectUserInterestAndExcitement result", { decision });
6396
6840
  return decision.includes("YES");
@@ -6403,11 +6847,11 @@ var init_detectUserInterestAndExcitement = __esm({
6403
6847
  });
6404
6848
 
6405
6849
  // src/services/prompts/documentSummarization.ts
6406
- var import_rxjs13, summarizeDocument;
6850
+ var import_rxjs14, summarizeDocument;
6407
6851
  var init_documentSummarization = __esm({
6408
6852
  "src/services/prompts/documentSummarization.ts"() {
6409
6853
  "use strict";
6410
- import_rxjs13 = require("rxjs");
6854
+ import_rxjs14 = require("rxjs");
6411
6855
  init_aiProviderStore();
6412
6856
  init_packageSettingsStore();
6413
6857
  init_debugLogger();
@@ -6435,8 +6879,8 @@ ${content.slice(0, 4e3)}
6435
6879
  stream: false,
6436
6880
  options: { temperature: 0.3, num_predict: 100 }
6437
6881
  });
6438
- const summary$ = data$.pipe((0, import_rxjs13.map)((d) => d.response.trim()));
6439
- const summary = await (0, import_rxjs13.lastValueFrom)(summary$);
6882
+ const summary$ = data$.pipe((0, import_rxjs14.map)((d) => d.response.trim()));
6883
+ const summary = await (0, import_rxjs14.lastValueFrom)(summary$);
6440
6884
  debugLogger.ragDebug("summarizeDocument result", { name, summary });
6441
6885
  return summary || `Document summary for ${name}`;
6442
6886
  } catch (error) {
@@ -6448,11 +6892,11 @@ ${content.slice(0, 4e3)}
6448
6892
  });
6449
6893
 
6450
6894
  // src/services/prompts/documentRelevance.ts
6451
- var import_rxjs14, determineRelevantDocuments;
6895
+ var import_rxjs15, determineRelevantDocuments;
6452
6896
  var init_documentRelevance = __esm({
6453
6897
  "src/services/prompts/documentRelevance.ts"() {
6454
6898
  "use strict";
6455
- import_rxjs14 = require("rxjs");
6899
+ import_rxjs15 = require("rxjs");
6456
6900
  init_aiProviderStore();
6457
6901
  init_packageSettingsStore();
6458
6902
  init_debugLogger();
@@ -6493,10 +6937,10 @@ Response:`;
6493
6937
  options: { temperature: 0.2, num_predict: 50 }
6494
6938
  });
6495
6939
  const chunks$ = response$.pipe(
6496
- (0, import_rxjs14.map)((chunk) => chunk.response.trim()),
6497
- (0, import_rxjs14.toArray)()
6940
+ (0, import_rxjs15.map)((chunk) => chunk.response.trim()),
6941
+ (0, import_rxjs15.toArray)()
6498
6942
  );
6499
- const result = await (0, import_rxjs14.lastValueFrom)(chunks$);
6943
+ const result = await (0, import_rxjs15.lastValueFrom)(chunks$);
6500
6944
  const vetResult = result.join("").trim().toLowerCase();
6501
6945
  debugLogger.ragDebug("determineRelevantDocuments result", { vetResult });
6502
6946
  if (vetResult.includes("none") || !vetResult) {
@@ -10180,6 +10624,8 @@ var init_chat_scroll_to_bottom_button = __esm({
10180
10624
  drawerOpen = false,
10181
10625
  isMobile = false
10182
10626
  }) => {
10627
+ const verticalBuffer = isMobile ? 36 : 56;
10628
+ const bottomOffset = Math.max(inputHeight + verticalBuffer, verticalBuffer + 72);
10183
10629
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
10184
10630
  import_material3.IconButton,
10185
10631
  {
@@ -10188,12 +10634,12 @@ var init_chat_scroll_to_bottom_button = __esm({
10188
10634
  position: "fixed",
10189
10635
  left: drawerOpen && !isMobile ? "calc(50% + 170px)" : "50%",
10190
10636
  transform: "translateX(-50%)",
10191
- bottom: inputHeight + 10,
10637
+ bottom: bottomOffset,
10192
10638
  bgcolor: (theme) => theme.palette.background.paper,
10193
10639
  color: (theme) => theme.palette.text.primary,
10194
10640
  border: "1px solid",
10195
10641
  borderColor: (theme) => theme.palette.divider,
10196
- zIndex: 999,
10642
+ zIndex: (theme) => Math.max(theme.zIndex.modal + 1, 1400),
10197
10643
  boxShadow: 3,
10198
10644
  transition: "bottom 0.3s ease, left 0.3s ease-in-out",
10199
10645
  "&:hover": {
@@ -11884,11 +12330,11 @@ var init_ttsSanitizer = __esm({
11884
12330
  });
11885
12331
 
11886
12332
  // src/services/tts/tts-client.ts
11887
- var import_rxjs15, getOrAppendAuthHeader;
12333
+ var import_rxjs16, getOrAppendAuthHeader;
11888
12334
  var init_tts_client = __esm({
11889
12335
  "src/services/tts/tts-client.ts"() {
11890
12336
  "use strict";
11891
- import_rxjs15 = require("rxjs");
12337
+ import_rxjs16 = require("rxjs");
11892
12338
  init_authenticationService();
11893
12339
  init_voiceStore();
11894
12340
  init_ttsSanitizer();
@@ -11908,11 +12354,11 @@ var init_tts_client = __esm({
11908
12354
  });
11909
12355
 
11910
12356
  // src/services/tts/streaming-tts.ts
11911
- var import_rxjs16, TTSState, StreamingTTSClient, getStreamingTTSClient, speakStream, stopTTS, pauseTTS, resumeTTS, getTTSState;
12357
+ var import_rxjs17, TTSState, StreamingTTSClient, getStreamingTTSClient, speakStream, stopTTS, pauseTTS, resumeTTS, getTTSState;
11912
12358
  var init_streaming_tts = __esm({
11913
12359
  "src/services/tts/streaming-tts.ts"() {
11914
12360
  "use strict";
11915
- import_rxjs16 = require("rxjs");
12361
+ import_rxjs17 = require("rxjs");
11916
12362
  init_debugLogger();
11917
12363
  init_packageSettingsStore();
11918
12364
  init_tts_client();
@@ -11932,8 +12378,8 @@ var init_streaming_tts = __esm({
11932
12378
  // Store event handler references for proper cleanup
11933
12379
  audioHandlers = /* @__PURE__ */ new Map();
11934
12380
  // State management
11935
- stateSubject = new import_rxjs16.BehaviorSubject("IDLE" /* IDLE */);
11936
- progressSubject = new import_rxjs16.Subject();
12381
+ stateSubject = new import_rxjs17.BehaviorSubject("IDLE" /* IDLE */);
12382
+ progressSubject = new import_rxjs17.Subject();
11937
12383
  constructor() {
11938
12384
  }
11939
12385
  static getInstance() {
@@ -11964,7 +12410,7 @@ var init_streaming_tts = __esm({
11964
12410
  * Speak text with simple streaming
11965
12411
  */
11966
12412
  speakStream(text, voice, options = {}) {
11967
- return new import_rxjs16.Observable((subscriber) => {
12413
+ return new import_rxjs17.Observable((subscriber) => {
11968
12414
  this.performSimpleStreaming(text, voice, options, subscriber);
11969
12415
  });
11970
12416
  }
@@ -14493,28 +14939,28 @@ var init_create_audio_blob = __esm({
14493
14939
  });
14494
14940
 
14495
14941
  // src/services/stt/sound-recorder.service.ts
14496
- var import_rxjs17, SoundRecorderService;
14942
+ var import_rxjs18, SoundRecorderService;
14497
14943
  var init_sound_recorder_service = __esm({
14498
14944
  "src/services/stt/sound-recorder.service.ts"() {
14499
14945
  "use strict";
14500
- import_rxjs17 = require("rxjs");
14946
+ import_rxjs18 = require("rxjs");
14501
14947
  init_create_audio_blob();
14502
14948
  SoundRecorderService = class {
14503
14949
  _mediaRecorder;
14504
14950
  start() {
14505
- const mediaStream = (0, import_rxjs17.from)(navigator.mediaDevices.getUserMedia({ audio: true }));
14506
- this._mediaRecorder = mediaStream.pipe((0, import_rxjs17.map)((stream) => {
14951
+ const mediaStream = (0, import_rxjs18.from)(navigator.mediaDevices.getUserMedia({ audio: true }));
14952
+ this._mediaRecorder = mediaStream.pipe((0, import_rxjs18.map)((stream) => {
14507
14953
  const rec = new MediaRecorder(stream);
14508
14954
  rec.start();
14509
14955
  return rec;
14510
- }), (0, import_rxjs17.shareReplay)(1));
14956
+ }), (0, import_rxjs18.shareReplay)(1));
14511
14957
  const dataAvailableEvent = this._mediaRecorder.pipe(
14512
- (0, import_rxjs17.switchMap)((recorder) => (0, import_rxjs17.fromEvent)(recorder, "dataavailable"))
14958
+ (0, import_rxjs18.switchMap)((recorder) => (0, import_rxjs18.fromEvent)(recorder, "dataavailable"))
14513
14959
  );
14514
14960
  const blob = dataAvailableEvent.pipe(
14515
- (0, import_rxjs17.first)(),
14516
- (0, import_rxjs17.map)((event) => createAudioBlob(event.data)),
14517
- (0, import_rxjs17.shareReplay)(1)
14961
+ (0, import_rxjs18.first)(),
14962
+ (0, import_rxjs18.map)((event) => createAudioBlob(event.data)),
14963
+ (0, import_rxjs18.shareReplay)(1)
14518
14964
  );
14519
14965
  return blob;
14520
14966
  }
@@ -14522,7 +14968,7 @@ var init_sound_recorder_service = __esm({
14522
14968
  if (!this._mediaRecorder) {
14523
14969
  return;
14524
14970
  }
14525
- this._mediaRecorder.pipe((0, import_rxjs17.first)()).subscribe((recorder) => {
14971
+ this._mediaRecorder.pipe((0, import_rxjs18.first)()).subscribe((recorder) => {
14526
14972
  recorder.stop();
14527
14973
  });
14528
14974
  }
@@ -14642,7 +15088,7 @@ var init_stt_client = __esm({
14642
15088
  });
14643
15089
 
14644
15090
  // src/services/stt/transcriber.tsx
14645
- var import_react16, import_Mic, import_Check4, import_Close3, import_material10, import_rxjs18, import_jsx_runtime13, initialButtonStyles, Transcriber, transcriber_default;
15091
+ var import_react16, import_Mic, import_Check4, import_Close3, import_material10, import_rxjs19, import_jsx_runtime13, initialButtonStyles, Transcriber, transcriber_default;
14646
15092
  var init_transcriber = __esm({
14647
15093
  "src/services/stt/transcriber.tsx"() {
14648
15094
  "use strict";
@@ -14653,7 +15099,7 @@ var init_transcriber = __esm({
14653
15099
  init_sound_recorder_service();
14654
15100
  init_stt_client();
14655
15101
  import_material10 = require("@mui/material");
14656
- import_rxjs18 = require("rxjs");
15102
+ import_rxjs19 = require("rxjs");
14657
15103
  init_debugLogger();
14658
15104
  import_jsx_runtime13 = require("react/jsx-runtime");
14659
15105
  initialButtonStyles = (badgeBackground, fileText, hoverBadgeBackground) => ({
@@ -14672,14 +15118,14 @@ var init_transcriber = __esm({
14672
15118
  const [status, setStatus] = (0, import_react16.useState)("IDLE");
14673
15119
  const recorderRef = (0, import_react16.useRef)(new SoundRecorderService());
14674
15120
  const [iconButtonStyles] = (0, import_react16.useState)(() => initialButtonStyles(badgeBackground, fileText, hoverBadgeBackground));
14675
- const [recordingSub, setRecordingSub] = (0, import_react16.useState)(() => new import_rxjs18.Subscription());
15121
+ const [recordingSub, setRecordingSub] = (0, import_react16.useState)(() => new import_rxjs19.Subscription());
14676
15122
  const start = () => {
14677
15123
  recordingSub.unsubscribe();
14678
15124
  const recording = recorderRef.current.start();
14679
15125
  const text = recording.pipe(
14680
- (0, import_rxjs18.switchMap)((blob) => {
15126
+ (0, import_rxjs19.switchMap)((blob) => {
14681
15127
  debugLogger.debug("Processing audio blob for transcription");
14682
- return (0, import_rxjs18.from)(STTClient.transcribe(blob));
15128
+ return (0, import_rxjs19.from)(STTClient.transcribe(blob));
14683
15129
  })
14684
15130
  );
14685
15131
  const sub = text.subscribe({
@@ -18293,11 +18739,11 @@ ${sanitize(
18293
18739
  });
18294
18740
 
18295
18741
  // src/chat/hooks/useMemoryEnhancer.tsx
18296
- var import_rxjs19, MEMORY_LIMIT, MIN_MEMORY_WORDS, MERGE_THRESHOLD, REJECT_ECHO_THRESHOLD, REJECT_DUPLICATE_THRESHOLD, CONTEXTUAL_DIVERGENCE_THRESHOLD, normalizeText, isStructurallyDuplicate, isAboutBandit, hasEngagementValue, isMemoryTooShortOrGeneric, isPersonalText, mergeMemory, isVoiceShifted, sanitizeMemory, sanitizeMemoryText, shouldAcceptMemory, isContextuallyDivergent, useMemoryEnhancer;
18742
+ var import_rxjs20, MEMORY_LIMIT, MIN_MEMORY_WORDS, MERGE_THRESHOLD, REJECT_ECHO_THRESHOLD, REJECT_DUPLICATE_THRESHOLD, CONTEXTUAL_DIVERGENCE_THRESHOLD, normalizeText, isStructurallyDuplicate, isAboutBandit, hasEngagementValue, isMemoryTooShortOrGeneric, isPersonalText, mergeMemory, isVoiceShifted, sanitizeMemory, sanitizeMemoryText, shouldAcceptMemory, isContextuallyDivergent, useMemoryEnhancer;
18297
18743
  var init_useMemoryEnhancer = __esm({
18298
18744
  "src/chat/hooks/useMemoryEnhancer.tsx"() {
18299
18745
  "use strict";
18300
- import_rxjs19 = require("rxjs");
18746
+ import_rxjs20 = require("rxjs");
18301
18747
  init_memoryStore();
18302
18748
  init_aiProviderStore();
18303
18749
  init_packageSettingsStore();
@@ -18689,8 +19135,8 @@ var init_useMemoryEnhancer = __esm({
18689
19135
  stream: false,
18690
19136
  options: { temperature: 0.1, num_predict: 150 }
18691
19137
  });
18692
- const suggestion = await (0, import_rxjs19.lastValueFrom)(
18693
- result$.pipe((0, import_rxjs19.map)((chunk) => chunk.response))
19138
+ const suggestion = await (0, import_rxjs20.lastValueFrom)(
19139
+ result$.pipe((0, import_rxjs20.map)((chunk) => chunk.response))
18694
19140
  );
18695
19141
  debugLogger.memoryDebug(`LLM memory suggestion received (${attempt})`, {
18696
19142
  suggestion: typeof suggestion === "string" ? suggestion.slice(0, 200) : suggestion,
@@ -19381,10 +19827,22 @@ var init_useAIProvider = __esm({
19381
19827
  setResponse("");
19382
19828
  setStreamBuffer("");
19383
19829
  clearFlushTimer();
19384
- setPendingMessage({ question, images });
19830
+ const imageList = Array.isArray(images) ? [...images] : [];
19831
+ const conversationStoreState = useConversationStore.getState();
19832
+ const { addToCurrent, replaceLastAnswer, conversations, currentId } = conversationStoreState;
19833
+ const currentConv = conversations.find((c) => c.id === currentId);
19834
+ const lastEntry = currentConv?.history.at(-1);
19835
+ const lastWasPlaceholder = !!lastEntry && lastEntry.answer === "..." && (lastEntry.placeholder === true || lastEntry.rawQuestion === question || lastEntry.question === question);
19836
+ const pendingQuestion = lastWasPlaceholder ? lastEntry?.question ?? question : question;
19837
+ const pendingImagesRaw = lastWasPlaceholder && Array.isArray(lastEntry?.images) && lastEntry.images.length > 0 ? lastEntry.images : imageList;
19838
+ const pendingImages = Array.isArray(pendingImagesRaw) && pendingImagesRaw.length > 0 ? [...pendingImagesRaw] : void 0;
19839
+ setPendingMessage({
19840
+ question: pendingQuestion,
19841
+ images: pendingImages
19842
+ });
19385
19843
  const modelName = usePackageSettingsStore.getState().settings?.defaultModel || "bandit-core:4b-it-qat";
19386
19844
  const CONFIG = modelConfigs[modelName] ?? modelConfigs["bandit-core:4b-it-qat"];
19387
- const base64Images = images.map((img) => img.split(",")[1]);
19845
+ const base64Images = imageList.map((img) => img.split(",")[1]);
19388
19846
  const latestEntries = history.slice(-CONFIG.historyMessages);
19389
19847
  const contextMessages = latestEntries.flatMap((entry) => [
19390
19848
  { role: "user", content: entry.question },
@@ -19770,11 +20228,8 @@ ${protocol}`;
19770
20228
  }, delay);
19771
20229
  };
19772
20230
  const stream = provider.chat(request);
19773
- const { addToCurrent, conversations, currentId, replaceLastAnswer } = useConversationStore.getState();
19774
- const currentConv = conversations.find((c) => c.id === currentId);
19775
- const lastEntry = currentConv?.history.at(-1);
19776
- const isPlaceholder = lastEntry?.question === question && lastEntry?.answer === "...";
19777
- lastPartialRef.current = { text: "", images, usedDocs, question };
20231
+ const initialPlaceholderQuestion = lastEntry?.question;
20232
+ lastPartialRef.current = { text: "", images: [...imageList], usedDocs, question };
19778
20233
  if (currentSubRef.current) {
19779
20234
  try {
19780
20235
  currentSubRef.current.unsubscribe();
@@ -20033,14 +20488,24 @@ ${items.map((item, index) => {
20033
20488
  } else {
20034
20489
  debugLogger.info("Memory scan skipped - disabled in preferences");
20035
20490
  }
20036
- const { addToCurrent: addToCurrent2, replaceLastAnswer: replaceLastAnswer2 } = useConversationStore.getState();
20037
- const current = useConversationStore.getState();
20038
- const conv = current.conversations.find((c) => c.id === current.currentId);
20491
+ const currentState = useConversationStore.getState();
20492
+ const conv = currentState.conversations.find((c) => c.id === currentState.currentId);
20039
20493
  const last = conv?.history.at(-1);
20040
- if (!last || last.answer !== "..." || last.question !== question) {
20041
- addToCurrent2({ question, answer: enhancedMessage, images, memoryUpdated, sourceFiles: usedDocs });
20494
+ const lastIsPlaceholder = !!last && last.answer === "..." && last.placeholder !== false;
20495
+ const preservedImagesSource = imageList.length > 0 ? imageList : lastPartialRef.current.images.length > 0 ? lastPartialRef.current.images : last?.images;
20496
+ const preservedImages = Array.isArray(preservedImagesSource) && preservedImagesSource.length > 0 ? [...preservedImagesSource] : void 0;
20497
+ if (lastIsPlaceholder) {
20498
+ replaceLastAnswer(enhancedMessage, preservedImages, memoryUpdated, usedDocs);
20042
20499
  } else {
20043
- replaceLastAnswer2(enhancedMessage, images, memoryUpdated, usedDocs);
20500
+ const historyQuestion = last && last.answer === "..." && last.question || initialPlaceholderQuestion || question;
20501
+ addToCurrent({
20502
+ question: historyQuestion,
20503
+ answer: enhancedMessage,
20504
+ images: preservedImages,
20505
+ memoryUpdated,
20506
+ sourceFiles: usedDocs,
20507
+ rawQuestion: question
20508
+ });
20044
20509
  }
20045
20510
  setInputValue("");
20046
20511
  setPastedImages([]);
@@ -24283,15 +24748,15 @@ var init_chat_app_bar = __esm({
24283
24748
  });
24284
24749
 
24285
24750
  // src/chat/hooks/useConversationNameGenerator.tsx
24286
- var import_rxjs20, import_operators5, useConversationNameGenerator;
24751
+ var import_rxjs21, import_operators6, useConversationNameGenerator;
24287
24752
  var init_useConversationNameGenerator = __esm({
24288
24753
  "src/chat/hooks/useConversationNameGenerator.tsx"() {
24289
24754
  "use strict";
24290
24755
  init_aiProviderStore();
24291
24756
  init_packageSettingsStore();
24292
24757
  init_debugLogger();
24293
- import_rxjs20 = require("rxjs");
24294
- import_operators5 = require("rxjs/operators");
24758
+ import_rxjs21 = require("rxjs");
24759
+ import_operators6 = require("rxjs/operators");
24295
24760
  init_conversationStore();
24296
24761
  useConversationNameGenerator = () => {
24297
24762
  const generateName = async (userMessage) => {
@@ -24326,8 +24791,8 @@ Respond with just the title and nothing else.
24326
24791
  num_predict: 20
24327
24792
  }
24328
24793
  });
24329
- const title = await (0, import_rxjs20.lastValueFrom)(
24330
- result$.pipe((0, import_operators5.map)((d) => d.response?.trim().replace(/["']/g, "")))
24794
+ const title = await (0, import_rxjs21.lastValueFrom)(
24795
+ result$.pipe((0, import_operators6.map)((d) => d.response?.trim().replace(/["']/g, "")))
24331
24796
  );
24332
24797
  if (title && title.length > 0) {
24333
24798
  const sanitizedTitle = sanitizeConversationName(title, 60);
@@ -28236,12 +28701,20 @@ var init_chat2 = __esm({
28236
28701
  (question, images, displayQuestion) => {
28237
28702
  const requestStartTime = trackRequestStart();
28238
28703
  const questionForDisplay = displayQuestion || question;
28239
- setPendingMessage({ question: questionForDisplay, images });
28704
+ const pendingImages = images.length > 0 ? [...images] : void 0;
28705
+ setPendingMessage({ question: questionForDisplay, images: pendingImages });
28240
28706
  setIsStreaming(true);
28241
28707
  setResponseStarted(true);
28242
28708
  setStreamBuffer("");
28243
28709
  const { addToCurrent } = useConversationStore.getState();
28244
- addToCurrent({ question: questionForDisplay, answer: "...", images });
28710
+ const placeholderImages = pendingImages ? [...pendingImages] : void 0;
28711
+ addToCurrent({
28712
+ question: questionForDisplay,
28713
+ answer: "...",
28714
+ images: placeholderImages,
28715
+ placeholder: true,
28716
+ rawQuestion: question
28717
+ });
28245
28718
  const getCurrentModel = useModelStore.getState().getCurrentModel;
28246
28719
  const systemPrompt = getCurrentModel()?.systemPrompt ?? "You are a helpful assistant.";
28247
28720
  const { currentId: currentId2, conversations: conversations2, createConversation, renameConversation } = useConversationStore.getState();
@@ -28255,8 +28728,16 @@ var init_chat2 = __esm({
28255
28728
  if (!newCurrentId) return;
28256
28729
  setResponse("");
28257
28730
  const { addToCurrent: addToNew } = useConversationStore.getState();
28258
- addToNew({ question: questionForDisplay, answer: "...", images });
28259
- aiProvider(systemPrompt, question, images);
28731
+ const newPlaceholderImages = pendingImages ? [...pendingImages] : void 0;
28732
+ addToNew({
28733
+ question: questionForDisplay,
28734
+ answer: "...",
28735
+ images: newPlaceholderImages,
28736
+ placeholder: true,
28737
+ rawQuestion: question
28738
+ });
28739
+ const providerImages2 = pendingImages ? [...pendingImages] : [];
28740
+ aiProvider(systemPrompt, question, providerImages2);
28260
28741
  }, 0);
28261
28742
  });
28262
28743
  return;
@@ -28306,7 +28787,8 @@ var init_chat2 = __esm({
28306
28787
  }, 50);
28307
28788
  }
28308
28789
  setResponse("");
28309
- aiProvider(systemPrompt, question, images);
28790
+ const providerImages = pendingImages ? [...pendingImages] : [];
28791
+ aiProvider(systemPrompt, question, providerImages);
28310
28792
  },
28311
28793
  [
28312
28794
  aiProvider,
@@ -28829,26 +29311,7 @@ var AIProviderInitService = class _AIProviderInitService {
28829
29311
  if (providerConfig.type === "anthropic" /* ANTHROPIC */) {
28830
29312
  providerConfig = this.convertAnthropicConfig(providerConfig, settings?.gatewayApiUrl);
28831
29313
  }
28832
- if ((providerConfig.type === "ollama" /* OLLAMA */ || providerConfig.type === "gateway" /* GATEWAY */) && !providerConfig.tokenFactory) {
28833
- providerConfig.tokenFactory = () => {
28834
- let token = authenticationService.getToken();
28835
- if (!token) {
28836
- token = localStorage.getItem("authToken");
28837
- }
28838
- if (!token) {
28839
- try {
28840
- const { useAuthenticationStore: useAuthenticationStore2 } = require("../../store/authenticationStore");
28841
- const authStore = useAuthenticationStore2.getState();
28842
- token = authStore.token;
28843
- } catch (e) {
28844
- }
28845
- }
28846
- debugLogger.info("AI Provider Init: IndexedDB config token factory", {
28847
- hasToken: !!token
28848
- });
28849
- return token;
28850
- };
28851
- }
29314
+ providerConfig = this.ensureTokenFactory(providerConfig);
28852
29315
  try {
28853
29316
  const { createProvider } = useAIProviderStore.getState();
28854
29317
  createProvider(providerConfig);
@@ -28876,27 +29339,7 @@ var AIProviderInitService = class _AIProviderInitService {
28876
29339
  if (providerConfig.type === "anthropic" /* ANTHROPIC */) {
28877
29340
  providerConfig = this.convertAnthropicConfig(providerConfig, settings.gatewayApiUrl);
28878
29341
  }
28879
- if (providerConfig.type === "ollama" /* OLLAMA */ && !providerConfig.tokenFactory) {
28880
- providerConfig.tokenFactory = () => {
28881
- let token = authenticationService.getToken();
28882
- if (!token) {
28883
- token = localStorage.getItem("authToken");
28884
- }
28885
- if (!token) {
28886
- try {
28887
- const { useAuthenticationStore: useAuthenticationStore2 } = require("../../store/authenticationStore");
28888
- const authStore = useAuthenticationStore2.getState();
28889
- token = authStore.token;
28890
- } catch (e) {
28891
- }
28892
- }
28893
- debugLogger.info("AIProviderInit: Explicit config tokenFactory", {
28894
- hasToken: !!token,
28895
- localStorage: !!localStorage.getItem("authToken")
28896
- });
28897
- return token;
28898
- };
28899
- }
29342
+ providerConfig = this.ensureTokenFactory(providerConfig);
28900
29343
  debugLogger.info("Using explicit AI provider config", providerConfig);
28901
29344
  } else {
28902
29345
  providerConfig = {
@@ -29008,9 +29451,10 @@ var AIProviderInitService = class _AIProviderInitService {
29008
29451
  */
29009
29452
  switchProvider(config) {
29010
29453
  try {
29454
+ const normalizedConfig = this.ensureTokenFactory({ ...config });
29011
29455
  const { switchProvider } = useAIProviderStore.getState();
29012
- switchProvider(config);
29013
- debugLogger.info(`Switched to AI provider: ${config.type}`);
29456
+ switchProvider(normalizedConfig);
29457
+ debugLogger.info(`Switched to AI provider: ${normalizedConfig.type}`);
29014
29458
  } catch (error) {
29015
29459
  debugLogger.error("Failed to switch AI provider:", { error });
29016
29460
  throw error;
@@ -29044,6 +29488,49 @@ var AIProviderInitService = class _AIProviderInitService {
29044
29488
  debugLogger.info("AI Provider Init: Converted direct Anthropic provider to gateway configuration");
29045
29489
  return normalized;
29046
29490
  }
29491
+ /**
29492
+ * Ensure providers that require auth have a token factory configured.
29493
+ * Handles both UI auth tokens and API key scenarios.
29494
+ */
29495
+ ensureTokenFactory(config) {
29496
+ if (config.type === "ollama" /* OLLAMA */ || config.type === "gateway" /* GATEWAY */) {
29497
+ const existingFactory = config.tokenFactory;
29498
+ if (existingFactory) {
29499
+ return config;
29500
+ }
29501
+ if (typeof config.apiKey === "string" && config.apiKey.trim() !== "") {
29502
+ const key = config.apiKey.trim();
29503
+ config.tokenFactory = () => key;
29504
+ debugLogger.info("AIProviderInit: Using API key for token factory", {
29505
+ type: config.type,
29506
+ hasKey: true
29507
+ });
29508
+ return config;
29509
+ }
29510
+ config.tokenFactory = () => {
29511
+ let token = authenticationService.getToken();
29512
+ if (!token && typeof localStorage !== "undefined") {
29513
+ try {
29514
+ token = localStorage.getItem("authToken");
29515
+ } catch {
29516
+ }
29517
+ }
29518
+ if (!token) {
29519
+ try {
29520
+ const { useAuthenticationStore: useAuthenticationStore2 } = require("../../store/authenticationStore");
29521
+ const authStore = useAuthenticationStore2.getState();
29522
+ token = authStore.token;
29523
+ } catch {
29524
+ }
29525
+ }
29526
+ debugLogger.info("AIProviderInit: Token factory resolved auth token", {
29527
+ hasToken: !!token
29528
+ });
29529
+ return token;
29530
+ };
29531
+ }
29532
+ return config;
29533
+ }
29047
29534
  };
29048
29535
  var aiProviderInitService = AIProviderInitService.getInstance();
29049
29536
 
@@ -29620,7 +30107,7 @@ init_modelStore();
29620
30107
  init_ai_response_text_field();
29621
30108
  init_memory_modal();
29622
30109
  init_streaming_tts();
29623
- var import_rxjs21 = require("rxjs");
30110
+ var import_rxjs22 = require("rxjs");
29624
30111
  init_debugLogger();
29625
30112
  init_util();
29626
30113
  var import_jsx_runtime32 = require("react/jsx-runtime");
@@ -29649,7 +30136,7 @@ var AIQueriesDrawer = ({ drawerOpen, onClose, onClearComplete, onNavigateToMain
29649
30136
  const [memoryModalOpen, setMemoryModalOpen] = (0, import_react39.useState)(false);
29650
30137
  const [contextMode, setContextMode] = (0, import_react39.useState)("local");
29651
30138
  const [expandedSections, setExpandedSections] = (0, import_react39.useState)(/* @__PURE__ */ new Set(["history", "voice"]));
29652
- const [audioSub, setAudioSub] = (0, import_react39.useState)(new import_rxjs21.Subscription());
30139
+ const [audioSub, setAudioSub] = (0, import_react39.useState)(new import_rxjs22.Subscription());
29653
30140
  const [isContextSwitching, setIsContextSwitching] = (0, import_react39.useState)(false);
29654
30141
  const [isDrawerLoading, setIsDrawerLoading] = (0, import_react39.useState)(false);
29655
30142
  (0, import_react39.useEffect)(() => {
@@ -31312,7 +31799,7 @@ init_debugLogger();
31312
31799
  init_banditTheme();
31313
31800
  init_themeMap();
31314
31801
  init_useTTS();
31315
- var import_rxjs22 = require("rxjs");
31802
+ var import_rxjs23 = require("rxjs");
31316
31803
  var import_jsx_runtime34 = require("react/jsx-runtime");
31317
31804
  var FULL_SCREEN_THRESHOLD = 100;
31318
31805
  var CDN_BASE2 = "https://cdn.burtson.ai/";
@@ -31379,7 +31866,7 @@ var ChatModal = ({
31379
31866
  const [modalLogo, setModalLogo] = (0, import_react42.useState)("https://cdn.burtson.ai/logos/bandit-ai-logo.png");
31380
31867
  const [modelAnchorEl, setModelAnchorEl] = (0, import_react42.useState)(null);
31381
31868
  const [voiceAnchorEl, setVoiceAnchorEl] = (0, import_react42.useState)(null);
31382
- const [audioSub, setAudioSub] = (0, import_react42.useState)(new import_rxjs22.Subscription());
31869
+ const [audioSub, setAudioSub] = (0, import_react42.useState)(new import_rxjs23.Subscription());
31383
31870
  const [selectedTheme, setSelectedTheme] = (0, import_react42.useState)(null);
31384
31871
  const [themeLoading, setThemeLoading] = (0, import_react42.useState)(true);
31385
31872
  const [autoFullscreenTriggered, setAutoFullscreenTriggered] = (0, import_react42.useState)(false);
@@ -33920,7 +34407,7 @@ init_conversationSyncStore();
33920
34407
 
33921
34408
  // src/hooks/useGatewayQueries.ts
33922
34409
  var import_react45 = require("react");
33923
- var import_rxjs23 = require("rxjs");
34410
+ var import_rxjs24 = require("rxjs");
33924
34411
  var import_react_query2 = require("@tanstack/react-query");
33925
34412
  init_packageSettingsStore();
33926
34413
  init_gateway_service();
@@ -33951,7 +34438,7 @@ var useGatewayHealth = (options) => {
33951
34438
  if (!service) {
33952
34439
  throw new Error("Gateway service is not configured");
33953
34440
  }
33954
- return (0, import_rxjs23.lastValueFrom)(service.getHealth());
34441
+ return (0, import_rxjs24.lastValueFrom)(service.getHealth());
33955
34442
  },
33956
34443
  enabled
33957
34444
  });
@@ -33966,7 +34453,7 @@ var useGatewayModels = (options) => {
33966
34453
  if (!service) {
33967
34454
  throw new Error("Gateway service is not configured");
33968
34455
  }
33969
- return (0, import_rxjs23.lastValueFrom)(service.listModels());
34456
+ return (0, import_rxjs24.lastValueFrom)(service.listModels());
33970
34457
  },
33971
34458
  enabled
33972
34459
  });
@@ -33981,7 +34468,7 @@ var useGatewayMemory = (options) => {
33981
34468
  if (!service) {
33982
34469
  throw new Error("Gateway service is not configured");
33983
34470
  }
33984
- return (0, import_rxjs23.lastValueFrom)(service.getMemory());
34471
+ return (0, import_rxjs24.lastValueFrom)(service.getMemory());
33985
34472
  },
33986
34473
  enabled
33987
34474
  });
@@ -38715,6 +39202,8 @@ var ProviderTab = () => {
38715
39202
  return "gpt-4o-mini";
38716
39203
  case "xai" /* XAI */:
38717
39204
  return "grok-beta";
39205
+ case "bandit" /* BANDIT */:
39206
+ return "bandit-core-1";
38718
39207
  default:
38719
39208
  return "";
38720
39209
  }
@@ -38722,7 +39211,7 @@ var ProviderTab = () => {
38722
39211
  const applyDefaultModel = (0, import_react53.useCallback)((config) => {
38723
39212
  const normalized = { ...config };
38724
39213
  const trimmed = typeof normalized.defaultModel === "string" ? normalized.defaultModel.trim() : void 0;
38725
- const requiresModel = normalized.type === "openai" /* OPENAI */ || normalized.type === "xai" /* XAI */;
39214
+ const requiresModel = normalized.type === "openai" /* OPENAI */ || normalized.type === "xai" /* XAI */ || normalized.type === "bandit" /* BANDIT */;
38726
39215
  if (trimmed) {
38727
39216
  normalized.defaultModel = trimmed;
38728
39217
  return normalized;
@@ -38745,11 +39234,16 @@ var ProviderTab = () => {
38745
39234
  const trimmed = sanitized.defaultModel.trim();
38746
39235
  sanitized.defaultModel = trimmed || void 0;
38747
39236
  }
39237
+ if (typeof sanitized.apiKey === "string") {
39238
+ const trimmedKey = sanitized.apiKey.trim();
39239
+ sanitized.apiKey = trimmedKey || void 0;
39240
+ }
38748
39241
  return sanitized;
38749
39242
  }, []);
38750
39243
  const [providerConfig, setProviderConfig] = (0, import_react53.useState)({
38751
- type: "ollama",
38752
- baseUrl: "http://localhost:11434"
39244
+ type: "gateway",
39245
+ gatewayUrl: packageSettings?.gatewayApiUrl || "",
39246
+ provider: "bandit"
38753
39247
  });
38754
39248
  const [isProviderConfigOpen, setIsProviderConfigOpen] = (0, import_react53.useState)(false);
38755
39249
  const [snackbarMessage, setSnackbarMessage] = (0, import_react53.useState)("");
@@ -38873,11 +39367,19 @@ var ProviderTab = () => {
38873
39367
  apiKey: ""
38874
39368
  }));
38875
39369
  break;
39370
+ case "bandit" /* BANDIT */:
39371
+ setProviderConfig(applyDefaultModel({
39372
+ ...baseConfig,
39373
+ baseUrl: "https://api.burtson.ai",
39374
+ apiKey: "",
39375
+ defaultModel: "bandit-core-1"
39376
+ }));
39377
+ break;
38876
39378
  case "gateway" /* GATEWAY */:
38877
39379
  setProviderConfig(applyDefaultModel({
38878
39380
  ...baseConfig,
38879
39381
  gatewayUrl: packageSettings?.gatewayApiUrl || "",
38880
- provider: "openai"
39382
+ provider: "bandit"
38881
39383
  }));
38882
39384
  break;
38883
39385
  case "playground" /* PLAYGROUND */:
@@ -38891,7 +39393,7 @@ var ProviderTab = () => {
38891
39393
  try {
38892
39394
  const normalizedConfigIntermediate = sanitizeConfigForSave(providerConfig);
38893
39395
  const normalizedConfig = convertAnthropicConfig(normalizedConfigIntermediate) || normalizedConfigIntermediate;
38894
- const requiresModel = normalizedConfig.type === "openai" /* OPENAI */ || normalizedConfig.type === "xai" /* XAI */;
39396
+ const requiresModel = normalizedConfig.type === "openai" /* OPENAI */ || normalizedConfig.type === "xai" /* XAI */ || normalizedConfig.type === "bandit" /* BANDIT */;
38895
39397
  if (requiresModel && !normalizedConfig.defaultModel) {
38896
39398
  showMessage("Please provide a default model ID for the selected provider.", "error");
38897
39399
  return;
@@ -39016,6 +39518,7 @@ var ProviderTab = () => {
39016
39518
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_material43.MenuItem, { value: "ollama", children: "Ollama" }),
39017
39519
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_material43.MenuItem, { value: "openai", children: "OpenAI" }),
39018
39520
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_material43.MenuItem, { value: "azure-openai", children: "Azure OpenAI" }),
39521
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_material43.MenuItem, { value: "bandit", children: "Bandit AI" }),
39019
39522
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_material43.MenuItem, { value: "xai", children: "xAI" }),
39020
39523
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_material43.MenuItem, { value: "playground", children: "Playground (Mock Demo)" })
39021
39524
  ]
@@ -39050,6 +39553,7 @@ var ProviderTab = () => {
39050
39553
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_material43.MenuItem, { value: "azure-openai", children: "Azure OpenAI" }),
39051
39554
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_material43.MenuItem, { value: "anthropic", children: "Anthropic" }),
39052
39555
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_material43.MenuItem, { value: "ollama", children: "Ollama" }),
39556
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_material43.MenuItem, { value: "bandit", children: "Bandit AI" }),
39053
39557
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_material43.MenuItem, { value: "xai", children: "xAI" })
39054
39558
  ]
39055
39559
  }
@@ -39065,6 +39569,52 @@ var ProviderTab = () => {
39065
39569
  placeholder: "http://localhost:11434"
39066
39570
  }
39067
39571
  ),
39572
+ providerConfig.type === "bandit" && /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_material43.Box, { children: [
39573
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
39574
+ import_material43.TextField,
39575
+ {
39576
+ label: "API Base URL",
39577
+ value: providerConfig.baseUrl || "",
39578
+ onChange: (e) => setProviderConfig((prev) => ({
39579
+ ...prev,
39580
+ baseUrl: e.target.value
39581
+ })),
39582
+ fullWidth: true,
39583
+ sx: { mb: 2 },
39584
+ placeholder: "https://api.burtson.ai",
39585
+ helperText: "Defaults to https://api.burtson.ai"
39586
+ }
39587
+ ),
39588
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
39589
+ import_material43.TextField,
39590
+ {
39591
+ label: "API Key",
39592
+ type: "password",
39593
+ value: providerConfig.apiKey || "",
39594
+ onChange: (e) => setProviderConfig((prev) => ({
39595
+ ...prev,
39596
+ apiKey: e.target.value
39597
+ })),
39598
+ fullWidth: true,
39599
+ sx: { mb: 2 },
39600
+ placeholder: "bai_..."
39601
+ }
39602
+ ),
39603
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
39604
+ import_material43.TextField,
39605
+ {
39606
+ label: "Default Model ID",
39607
+ value: providerConfig.defaultModel || "",
39608
+ onChange: (e) => setProviderConfig((prev) => ({
39609
+ ...prev,
39610
+ defaultModel: e.target.value
39611
+ })),
39612
+ fullWidth: true,
39613
+ placeholder: "bandit-core-1",
39614
+ helperText: "Example: bandit-core-1 (Bandit Core canonical alias)."
39615
+ }
39616
+ )
39617
+ ] }),
39068
39618
  providerConfig.type === "openai" && /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_material43.Box, { children: [
39069
39619
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
39070
39620
  import_material43.TextField,
@@ -39405,20 +39955,20 @@ var MCPToolsTabV2 = () => {
39405
39955
  }
39406
39956
  }, [isLoaded]);
39407
39957
  const localEnabledMap = (0, import_react54.useMemo)(() => {
39408
- const map21 = /* @__PURE__ */ new Map();
39958
+ const map23 = /* @__PURE__ */ new Map();
39409
39959
  const sortedTools = [...localTools].sort((a, b) => {
39410
39960
  if (a.isBuiltIn && !b.isBuiltIn) return -1;
39411
39961
  if (!a.isBuiltIn && b.isBuiltIn) return 1;
39412
39962
  return a.id.length - b.id.length;
39413
39963
  });
39414
39964
  sortedTools.forEach((t) => {
39415
- map21.set(t.function.name, t.enabled);
39416
- map21.set(t.id, t.enabled);
39965
+ map23.set(t.function.name, t.enabled);
39966
+ map23.set(t.id, t.enabled);
39417
39967
  if (t.name) {
39418
- map21.set(t.name, t.enabled);
39968
+ map23.set(t.name, t.enabled);
39419
39969
  }
39420
39970
  });
39421
- return map21;
39971
+ return map23;
39422
39972
  }, [localTools]);
39423
39973
  return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_material44.Box, { children: [
39424
39974
  /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_material44.Box, { sx: { display: "flex", alignItems: "center", justifyContent: "space-between", mb: 2 }, children: [