@burtson-labs/bandit-engine 2.0.164 → 2.0.166

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 (59) hide show
  1. package/README.md +4 -1
  2. package/dist/aiProviderStore-HY5G4QAK.mjs +8 -0
  3. package/dist/chat-RCMZYACB.mjs +15 -0
  4. package/dist/chat-provider.js +186 -111
  5. package/dist/chat-provider.js.map +1 -1
  6. package/dist/chat-provider.mjs +7 -8
  7. package/dist/{chunk-J6SWNO7Z.mjs → chunk-33OSALGS.mjs} +25 -7
  8. package/dist/{chunk-J6SWNO7Z.mjs.map → chunk-33OSALGS.mjs.map} +1 -1
  9. package/dist/{chunk-5KBTOCQZ.mjs → chunk-6KRAPGXE.mjs} +13 -85
  10. package/dist/chunk-6KRAPGXE.mjs.map +1 -0
  11. package/dist/{chunk-VBHI4TJC.mjs → chunk-75OBTJPN.mjs} +6 -6
  12. package/dist/{chunk-URZSIL7X.mjs → chunk-A6W6XIDD.mjs} +7 -7
  13. package/dist/{chunk-CQJGUYRW.mjs → chunk-GRPTCHFN.mjs} +150 -42
  14. package/dist/chunk-GRPTCHFN.mjs.map +1 -0
  15. package/dist/{chunk-OCVZVBQQ.mjs → chunk-HNXPDIWC.mjs} +12 -3
  16. package/dist/chunk-HNXPDIWC.mjs.map +1 -0
  17. package/dist/{chunk-IZX2J65Q.mjs → chunk-IB2LPSQQ.mjs} +2 -2
  18. package/dist/{chunk-PNQRS5EN.mjs → chunk-M4DWPNZZ.mjs} +11 -11
  19. package/dist/{chunk-O6P37PFR.mjs → chunk-MM3NRLVR.mjs} +4 -4
  20. package/dist/{chunk-WLNZL6SP.mjs → chunk-Q2DINRIU.mjs} +6 -6
  21. package/dist/{chunk-WLNZL6SP.mjs.map → chunk-Q2DINRIU.mjs.map} +1 -1
  22. package/dist/{chunk-JYBQAKSZ.mjs → chunk-WSZE3DDJ.mjs} +8 -8
  23. package/dist/{chunk-TY5VL3SG.mjs → chunk-YH5YWK4K.mjs} +3 -3
  24. package/dist/index.d.mts +97 -4
  25. package/dist/index.d.ts +97 -4
  26. package/dist/index.js +200 -133
  27. package/dist/index.js.map +1 -1
  28. package/dist/index.mjs +22 -21
  29. package/dist/index.mjs.map +1 -1
  30. package/dist/management/management.js +171 -123
  31. package/dist/management/management.js.map +1 -1
  32. package/dist/management/management.mjs +10 -11
  33. package/dist/{memoryUtils-2FVXDSS6.mjs → memoryUtils-HGCYEUCD.mjs} +1 -3
  34. package/dist/{memoryUtils-2FVXDSS6.mjs.map → memoryUtils-HGCYEUCD.mjs.map} +1 -1
  35. package/dist/modals/chat-modal/chat-modal.js +161 -33
  36. package/dist/modals/chat-modal/chat-modal.js.map +1 -1
  37. package/dist/modals/chat-modal/chat-modal.mjs +6 -7
  38. package/dist/modelStore-F6JQUXGG.mjs +8 -0
  39. package/dist/shared/custom-element.mjs +0 -1
  40. package/docs/03_provider_integration.md +5 -2
  41. package/package.json +1 -1
  42. package/dist/aiProviderStore-PEGSX4PK.mjs +0 -9
  43. package/dist/chat-OSAVW6L2.mjs +0 -16
  44. package/dist/chunk-5KBTOCQZ.mjs.map +0 -1
  45. package/dist/chunk-BJTO5JO5.mjs +0 -11
  46. package/dist/chunk-CQJGUYRW.mjs.map +0 -1
  47. package/dist/chunk-OCVZVBQQ.mjs.map +0 -1
  48. package/dist/modelStore-7XYYDCNC.mjs +0 -9
  49. package/dist/modelStore-7XYYDCNC.mjs.map +0 -1
  50. /package/dist/{aiProviderStore-PEGSX4PK.mjs.map → aiProviderStore-HY5G4QAK.mjs.map} +0 -0
  51. /package/dist/{chat-OSAVW6L2.mjs.map → chat-RCMZYACB.mjs.map} +0 -0
  52. /package/dist/{chunk-VBHI4TJC.mjs.map → chunk-75OBTJPN.mjs.map} +0 -0
  53. /package/dist/{chunk-URZSIL7X.mjs.map → chunk-A6W6XIDD.mjs.map} +0 -0
  54. /package/dist/{chunk-IZX2J65Q.mjs.map → chunk-IB2LPSQQ.mjs.map} +0 -0
  55. /package/dist/{chunk-PNQRS5EN.mjs.map → chunk-M4DWPNZZ.mjs.map} +0 -0
  56. /package/dist/{chunk-O6P37PFR.mjs.map → chunk-MM3NRLVR.mjs.map} +0 -0
  57. /package/dist/{chunk-JYBQAKSZ.mjs.map → chunk-WSZE3DDJ.mjs.map} +0 -0
  58. /package/dist/{chunk-TY5VL3SG.mjs.map → chunk-YH5YWK4K.mjs.map} +0 -0
  59. /package/dist/{chunk-BJTO5JO5.mjs.map → modelStore-F6JQUXGG.mjs.map} +0 -0
@@ -270,11 +270,20 @@ var init_debugLogger = __esm({
270
270
  */
271
271
  debug(message, context) {
272
272
  }
273
+ verboseEnabled() {
274
+ if (this.isDevelopment) return true;
275
+ try {
276
+ return typeof localStorage !== "undefined" && localStorage.getItem("bandit:debug") === "1";
277
+ } catch {
278
+ return false;
279
+ }
280
+ }
273
281
  /**
274
- * Info logs - shown in both development and production
275
- * Used for important user-facing information
282
+ * Info logs - shown in development, or in production when localStorage
283
+ * `bandit:debug` is "1"
276
284
  */
277
285
  info(message, context) {
286
+ if (!this.verboseEnabled()) return;
278
287
  const prefix = this.isDevelopment ? "\u2139\uFE0F [INFO]" : "[INFO]";
279
288
  if (context) {
280
289
  console.info(`${prefix} ${message}`, context);
@@ -1547,31 +1556,42 @@ var init_anthropic_provider = __esm({
1547
1556
  });
1548
1557
 
1549
1558
  // src/store/authenticationStore.ts
1550
- var import_zustand2, TOKEN_KEY, validToken, user, rawToken, useAuthenticationStore, readPersistedToken;
1559
+ var import_zustand2, TOKEN_KEY, decodeClaims, validOrNull, readLegacyStorage, validToken, user, rawToken, useAuthenticationStore, readPersistedToken;
1551
1560
  var init_authenticationStore = __esm({
1552
1561
  "src/store/authenticationStore.ts"() {
1553
1562
  "use strict";
1554
1563
  import_zustand2 = require("zustand");
1555
1564
  TOKEN_KEY = "authToken";
1556
- validToken = null;
1557
- user = null;
1558
- rawToken = localStorage.getItem(TOKEN_KEY);
1559
- if (rawToken) {
1565
+ decodeClaims = (token) => {
1560
1566
  try {
1561
- const base64Url = rawToken.split(".")[1];
1567
+ const base64Url = token.split(".")[1];
1562
1568
  const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
1563
1569
  const jsonPayload = decodeURIComponent(
1564
1570
  atob(base64).split("").map((c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)).join("")
1565
1571
  );
1566
- const decoded = JSON.parse(jsonPayload);
1567
- if (decoded.exp * 1e3 > Date.now()) {
1568
- validToken = rawToken;
1569
- user = decoded;
1570
- }
1572
+ return JSON.parse(jsonPayload);
1571
1573
  } catch {
1572
- validToken = null;
1573
- user = null;
1574
+ return null;
1574
1575
  }
1576
+ };
1577
+ validOrNull = (raw) => {
1578
+ if (!raw) return null;
1579
+ const decoded = decodeClaims(raw);
1580
+ return decoded && decoded.exp * 1e3 > Date.now() ? raw : null;
1581
+ };
1582
+ readLegacyStorage = () => {
1583
+ try {
1584
+ return typeof localStorage === "undefined" ? null : localStorage.getItem(TOKEN_KEY);
1585
+ } catch {
1586
+ return null;
1587
+ }
1588
+ };
1589
+ validToken = null;
1590
+ user = null;
1591
+ rawToken = readLegacyStorage();
1592
+ if (rawToken) {
1593
+ validToken = validOrNull(rawToken);
1594
+ user = validToken ? decodeClaims(validToken) : null;
1575
1595
  }
1576
1596
  useAuthenticationStore = (0, import_zustand2.create)((set) => ({
1577
1597
  token: validToken,
@@ -1579,16 +1599,10 @@ var init_authenticationStore = __esm({
1579
1599
  authError: null,
1580
1600
  setToken: (token) => {
1581
1601
  if (token) {
1582
- try {
1583
- const base64Url = token.split(".")[1];
1584
- const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
1585
- const jsonPayload = decodeURIComponent(
1586
- atob(base64).split("").map((c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)).join("")
1587
- );
1588
- const user2 = JSON.parse(jsonPayload);
1589
- localStorage.setItem(TOKEN_KEY, token);
1590
- set({ token, authError: null, user: user2 });
1591
- } catch {
1602
+ const decoded = decodeClaims(token);
1603
+ if (decoded) {
1604
+ set({ token, authError: null, user: decoded });
1605
+ } else {
1592
1606
  set({ token: null, authError: "Invalid token", user: null });
1593
1607
  }
1594
1608
  } else {
@@ -1596,23 +1610,11 @@ var init_authenticationStore = __esm({
1596
1610
  }
1597
1611
  },
1598
1612
  clearToken: () => {
1599
- localStorage.removeItem(TOKEN_KEY);
1600
1613
  set({ token: null, authError: null, user: null });
1601
1614
  }
1602
1615
  }));
1603
1616
  readPersistedToken = () => {
1604
- try {
1605
- const raw = localStorage.getItem(TOKEN_KEY);
1606
- if (!raw) return null;
1607
- const base64 = raw.split(".")[1].replace(/-/g, "+").replace(/_/g, "/");
1608
- const json2 = decodeURIComponent(
1609
- atob(base64).split("").map((c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)).join("")
1610
- );
1611
- const decoded = JSON.parse(json2);
1612
- return decoded.exp * 1e3 > Date.now() ? raw : null;
1613
- } catch {
1614
- return null;
1615
- }
1617
+ return validOrNull(readLegacyStorage()) ?? validOrNull(useAuthenticationStore.getState().token);
1616
1618
  };
1617
1619
  }
1618
1620
  });
@@ -1626,7 +1628,7 @@ function emitAuthTokenChanged(token) {
1626
1628
  detail: { token }
1627
1629
  }));
1628
1630
  }
1629
- var TOKEN_KEY2, AUTH_TOKEN_CHANGED_EVENT, AuthenticationService, authenticationService;
1631
+ var TOKEN_KEY2, AUTH_TOKEN_CHANGED_EVENT, readLocalStorage, writeLocalStorage, AuthenticationService, authenticationService;
1630
1632
  var init_authenticationService = __esm({
1631
1633
  "src/services/auth/authenticationService.ts"() {
1632
1634
  "use strict";
@@ -1634,21 +1636,103 @@ var init_authenticationService = __esm({
1634
1636
  init_debugLogger();
1635
1637
  TOKEN_KEY2 = "authToken";
1636
1638
  AUTH_TOKEN_CHANGED_EVENT = "bandit:auth-token-changed";
1639
+ readLocalStorage = () => {
1640
+ try {
1641
+ return typeof localStorage === "undefined" ? null : localStorage.getItem(TOKEN_KEY2);
1642
+ } catch {
1643
+ return null;
1644
+ }
1645
+ };
1646
+ writeLocalStorage = (token) => {
1647
+ try {
1648
+ if (typeof localStorage === "undefined") return;
1649
+ if (token) localStorage.setItem(TOKEN_KEY2, token);
1650
+ else localStorage.removeItem(TOKEN_KEY2);
1651
+ } catch {
1652
+ }
1653
+ };
1637
1654
  AuthenticationService = class {
1655
+ storageMode = "localStorage";
1656
+ persistence = null;
1657
+ handlers = {};
1658
+ /** Bumped on every setToken/clearToken; async flows compare it to detect a superseded session. */
1659
+ revision = 0;
1660
+ refreshInFlight = null;
1661
+ lastPersist = Promise.resolve();
1662
+ /**
1663
+ * Pick the storage contract for this host. Call once at boot, before the
1664
+ * first `setToken`. Switching modes never moves a token by itself: the
1665
+ * host migrates (e.g. reads the legacy localStorage value, then clears it).
1666
+ */
1667
+ configureSession(config) {
1668
+ this.storageMode = config.storage;
1669
+ this.persistence = config.persistence ?? null;
1670
+ this.handlers = config.handlers ?? {};
1671
+ }
1672
+ getStorageMode() {
1673
+ return this.storageMode;
1674
+ }
1675
+ getSessionRevision() {
1676
+ return this.revision;
1677
+ }
1638
1678
  getToken() {
1639
- const token = localStorage.getItem(TOKEN_KEY2);
1640
- return token;
1679
+ if (this.storageMode === "memory") {
1680
+ return useAuthenticationStore.getState().token;
1681
+ }
1682
+ return readLocalStorage();
1641
1683
  }
1642
1684
  setToken(token) {
1643
- localStorage.setItem(TOKEN_KEY2, token);
1685
+ this.revision += 1;
1686
+ if (this.storageMode === "localStorage") {
1687
+ writeLocalStorage(token);
1688
+ } else {
1689
+ writeLocalStorage(null);
1690
+ }
1644
1691
  useAuthenticationStore.getState().setToken(token);
1692
+ this.mirror(() => this.persistence?.write(token));
1645
1693
  emitAuthTokenChanged(token);
1646
1694
  }
1647
1695
  clearToken() {
1648
- localStorage.removeItem(TOKEN_KEY2);
1696
+ this.revision += 1;
1697
+ const current = this.getToken() ?? useAuthenticationStore.getState().token;
1698
+ try {
1699
+ this.handlers.beforeClear?.(current);
1700
+ } catch (error) {
1701
+ debugLogger.warn("Auth beforeClear handler failed", { error });
1702
+ }
1703
+ writeLocalStorage(null);
1649
1704
  useAuthenticationStore.getState().clearToken();
1705
+ this.mirror(() => this.persistence?.clear());
1650
1706
  emitAuthTokenChanged(null);
1651
1707
  }
1708
+ /** True when the host registered a silent refresh path. */
1709
+ canRefresh() {
1710
+ return typeof this.handlers.refresh === "function";
1711
+ }
1712
+ /**
1713
+ * Ask the host for a fresh token. Concurrent callers share one attempt.
1714
+ * Resolves false when no refresh path exists or it did not produce a token.
1715
+ */
1716
+ tryRefresh() {
1717
+ const refresh = this.handlers.refresh;
1718
+ if (!refresh) return Promise.resolve(false);
1719
+ if (this.refreshInFlight) return this.refreshInFlight;
1720
+ this.refreshInFlight = (async () => {
1721
+ try {
1722
+ return await refresh();
1723
+ } catch (error) {
1724
+ debugLogger.warn("Auth refresh handler failed", { error });
1725
+ return false;
1726
+ } finally {
1727
+ this.refreshInFlight = null;
1728
+ }
1729
+ })();
1730
+ return this.refreshInFlight;
1731
+ }
1732
+ /** Resolves once the most recent persistence write/clear has settled. */
1733
+ whenPersisted() {
1734
+ return this.lastPersist;
1735
+ }
1652
1736
  isAuthenticated() {
1653
1737
  const token = useAuthenticationStore.getState().token;
1654
1738
  return !!token && !this.isTokenExpired(token);
@@ -1678,6 +1762,16 @@ var init_authenticationService = __esm({
1678
1762
  return null;
1679
1763
  }
1680
1764
  }
1765
+ mirror(run) {
1766
+ const attempt = (async () => {
1767
+ try {
1768
+ await run();
1769
+ } catch (error) {
1770
+ debugLogger.warn("Auth token persistence failed", { error });
1771
+ }
1772
+ })();
1773
+ this.lastPersist = attempt;
1774
+ }
1681
1775
  };
1682
1776
  authenticationService = new AuthenticationService();
1683
1777
  }
@@ -1733,13 +1827,35 @@ var init_gateway_service = __esm({
1733
1827
  });
1734
1828
  instance.interceptors.response.use(
1735
1829
  (response) => response,
1736
- (error) => {
1830
+ async (error) => {
1831
+ const retried = await this._retryAfterRefresh(instance, error);
1832
+ if (retried) return retried;
1737
1833
  this._handleAuthFailure(error);
1738
1834
  return Promise.reject(this._normalizeAxiosError(error));
1739
1835
  }
1740
1836
  );
1741
1837
  return instance;
1742
1838
  }
1839
+ /**
1840
+ * A bare 401 on a host with a silent refresh path (HttpOnly refresh cookie,
1841
+ * native device token) usually means the access token aged out mid-session.
1842
+ * Refresh once and replay the request with the new token instead of
1843
+ * signing the user out; a failed refresh falls through to the sign-out path.
1844
+ */
1845
+ async _retryAfterRefresh(instance, error) {
1846
+ const config = error.config;
1847
+ if (!config || config._banditAuthRetried || error.response?.status !== 401) return null;
1848
+ if (!authenticationService.canRefresh()) return null;
1849
+ const data = error.response?.data;
1850
+ if (data?.code && data.code !== "TOKEN_EXPIRED") return null;
1851
+ try {
1852
+ if (!await authenticationService.tryRefresh()) return null;
1853
+ } catch {
1854
+ return null;
1855
+ }
1856
+ config._banditAuthRetried = true;
1857
+ return instance.request(config);
1858
+ }
1743
1859
  _normalizeAxiosError(error) {
1744
1860
  if (error.response) {
1745
1861
  return this._createHttpError(
@@ -6428,6 +6544,7 @@ var init_FeatureFlagContext = __esm({
6428
6544
  import_react = require("react");
6429
6545
  init_featureFlags();
6430
6546
  init_debugLogger();
6547
+ init_authenticationService();
6431
6548
  import_jsx_runtime = require("react/jsx-runtime");
6432
6549
  FeatureFlagContext = (0, import_react.createContext)(void 0);
6433
6550
  useFeatureFlag = () => {
@@ -11762,7 +11879,7 @@ var init_voiceService = __esm({
11762
11879
  }
11763
11880
  const token = authenticationService.getToken();
11764
11881
  if (!token) {
11765
- debugLogger.warn("No JWT token available - skipping voice models fetch");
11882
+ debugLogger.info("No JWT token available - skipping voice models fetch");
11766
11883
  const emptyModels = {
11767
11884
  models: [],
11768
11885
  defaultModel: "",
@@ -40932,6 +41049,11 @@ init_common_types();
40932
41049
  init_authenticationService();
40933
41050
  init_debugLogger();
40934
41051
  init_indexedDBService();
41052
+ var sessionTokenFactory = (label) => () => {
41053
+ const token = authenticationService.getToken();
41054
+ debugLogger.info(`AIProviderInit: ${label}`, { hasToken: !!token });
41055
+ return token;
41056
+ };
40935
41057
  var AIProviderInitService = class _AIProviderInitService {
40936
41058
  static instance;
40937
41059
  constructor() {
@@ -41011,25 +41133,7 @@ var AIProviderInitService = class _AIProviderInitService {
41011
41133
  providerConfig = {
41012
41134
  type: "ollama" /* OLLAMA */,
41013
41135
  baseUrl: settings.ollamaUrl,
41014
- tokenFactory: () => {
41015
- let token = authenticationService.getToken();
41016
- if (!token) {
41017
- token = localStorage.getItem("authToken");
41018
- }
41019
- if (!token) {
41020
- try {
41021
- const { useAuthenticationStore: useAuthenticationStore2 } = require("../../store/authenticationStore");
41022
- const authStore = useAuthenticationStore2.getState();
41023
- token = authStore.token;
41024
- } catch (e) {
41025
- }
41026
- }
41027
- debugLogger.info("AIProviderInit: Token factory called", {
41028
- hasToken: !!token,
41029
- localStorage: !!localStorage.getItem("authToken")
41030
- });
41031
- return token;
41032
- }
41136
+ tokenFactory: sessionTokenFactory("Token factory called")
41033
41137
  };
41034
41138
  debugLogger.info("Using legacy Ollama config", providerConfig);
41035
41139
  }
@@ -41052,25 +41156,7 @@ var AIProviderInitService = class _AIProviderInitService {
41052
41156
  const fallbackConfig = {
41053
41157
  type: "ollama" /* OLLAMA */,
41054
41158
  baseUrl: settings.ollamaUrl || "http://localhost:11434",
41055
- tokenFactory: () => {
41056
- let token = authenticationService.getToken();
41057
- if (!token) {
41058
- token = localStorage.getItem("authToken");
41059
- }
41060
- if (!token) {
41061
- try {
41062
- const { useAuthenticationStore: useAuthenticationStore2 } = require("../../store/authenticationStore");
41063
- const authStore = useAuthenticationStore2.getState();
41064
- token = authStore.token;
41065
- } catch (e) {
41066
- }
41067
- }
41068
- debugLogger.info("AIProviderInit: Fallback tokenFactory", {
41069
- hasToken: !!token,
41070
- localStorage: !!localStorage.getItem("authToken")
41071
- });
41072
- return token;
41073
- }
41159
+ tokenFactory: sessionTokenFactory("Fallback tokenFactory")
41074
41160
  };
41075
41161
  const { createProvider } = useAIProviderStore.getState();
41076
41162
  createProvider(fallbackConfig);
@@ -41083,25 +41169,7 @@ var AIProviderInitService = class _AIProviderInitService {
41083
41169
  const localFallbackConfig = {
41084
41170
  type: "ollama" /* OLLAMA */,
41085
41171
  baseUrl: "http://localhost:11434",
41086
- tokenFactory: () => {
41087
- let token = authenticationService.getToken();
41088
- if (!token) {
41089
- token = localStorage.getItem("authToken");
41090
- }
41091
- if (!token) {
41092
- try {
41093
- const { useAuthenticationStore: useAuthenticationStore2 } = require("../../store/authenticationStore");
41094
- const authStore = useAuthenticationStore2.getState();
41095
- token = authStore.token;
41096
- } catch (e) {
41097
- }
41098
- }
41099
- debugLogger.info("AIProviderInit: Local fallback tokenFactory", {
41100
- hasToken: !!token,
41101
- localStorage: !!localStorage.getItem("authToken")
41102
- });
41103
- return token;
41104
- }
41172
+ tokenFactory: sessionTokenFactory("Local fallback tokenFactory")
41105
41173
  };
41106
41174
  const { createProvider } = useAIProviderStore.getState();
41107
41175
  createProvider(localFallbackConfig);
@@ -41173,27 +41241,7 @@ var AIProviderInitService = class _AIProviderInitService {
41173
41241
  });
41174
41242
  return config;
41175
41243
  }
41176
- config.tokenFactory = () => {
41177
- let token = authenticationService.getToken();
41178
- if (!token && typeof localStorage !== "undefined") {
41179
- try {
41180
- token = localStorage.getItem("authToken");
41181
- } catch {
41182
- }
41183
- }
41184
- if (!token) {
41185
- try {
41186
- const { useAuthenticationStore: useAuthenticationStore2 } = require("../../store/authenticationStore");
41187
- const authStore = useAuthenticationStore2.getState();
41188
- token = authStore.token;
41189
- } catch {
41190
- }
41191
- }
41192
- debugLogger.info("AIProviderInit: Token factory resolved auth token", {
41193
- hasToken: !!token
41194
- });
41195
- return token;
41196
- };
41244
+ config.tokenFactory = sessionTokenFactory("Token factory resolved auth token");
41197
41245
  }
41198
41246
  return config;
41199
41247
  }