@burtson-labs/bandit-engine 2.0.165 → 2.0.167

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 (61) hide show
  1. package/README.md +4 -1
  2. package/dist/aiProviderStore-HY5G4QAK.mjs +8 -0
  3. package/dist/chat-6N5B7A7F.mjs +15 -0
  4. package/dist/chat-provider.js +175 -109
  5. package/dist/chat-provider.js.map +1 -1
  6. package/dist/chat-provider.mjs +6 -7
  7. package/dist/{chunk-FEIDGXVR.mjs → chunk-46OGYEME.mjs} +24 -6
  8. package/dist/{chunk-FEIDGXVR.mjs.map → chunk-46OGYEME.mjs.map} +1 -1
  9. package/dist/{chunk-ASWUEAWM.mjs → chunk-5BZWJQ5O.mjs} +2 -2
  10. package/dist/{chunk-LKGBO5OP.mjs → chunk-CABOLDNR.mjs} +12 -84
  11. package/dist/chunk-CABOLDNR.mjs.map +1 -0
  12. package/dist/{chunk-NGGTQVGX.mjs → chunk-E5WMDLE3.mjs} +3 -3
  13. package/dist/{chunk-J6LSSK3G.mjs → chunk-GRPTCHFN.mjs} +149 -41
  14. package/dist/chunk-GRPTCHFN.mjs.map +1 -0
  15. package/dist/{chunk-RLW4RBIR.mjs → chunk-KDIL5WUL.mjs} +4 -4
  16. package/dist/{chunk-6VDL4SLX.mjs → chunk-N4BQ6HKL.mjs} +5 -5
  17. package/dist/{chunk-RNCASZ2Y.mjs → chunk-PK3LFBGC.mjs} +10 -10
  18. package/dist/{chunk-WSUUW7T2.mjs → chunk-SHYNXDRO.mjs} +10 -7
  19. package/dist/chunk-SHYNXDRO.mjs.map +1 -0
  20. package/dist/{chunk-2EM4ATKI.mjs → chunk-SZJB5FLZ.mjs} +2 -2
  21. package/dist/{chunk-2EM4ATKI.mjs.map → chunk-SZJB5FLZ.mjs.map} +1 -1
  22. package/dist/{chunk-QHGGV4KJ.mjs → chunk-US4TG745.mjs} +6 -6
  23. package/dist/index.d.mts +94 -3
  24. package/dist/index.d.ts +94 -3
  25. package/dist/index.js +191 -130
  26. package/dist/index.js.map +1 -1
  27. package/dist/index.mjs +21 -20
  28. package/dist/index.mjs.map +1 -1
  29. package/dist/management/management.js +162 -120
  30. package/dist/management/management.js.map +1 -1
  31. package/dist/management/management.mjs +9 -10
  32. package/dist/{memoryUtils-2FVXDSS6.mjs → memoryUtils-HGCYEUCD.mjs} +1 -3
  33. package/dist/{memoryUtils-2FVXDSS6.mjs.map → memoryUtils-HGCYEUCD.mjs.map} +1 -1
  34. package/dist/modals/chat-modal/chat-modal.js +149 -30
  35. package/dist/modals/chat-modal/chat-modal.js.map +1 -1
  36. package/dist/modals/chat-modal/chat-modal.mjs +5 -6
  37. package/dist/modelStore-TFNX4GEC.mjs +8 -0
  38. package/dist/{public-0EYmtpGD.d.mts → public-DJa4Db00.d.mts} +9 -0
  39. package/dist/{public-0EYmtpGD.d.ts → public-DJa4Db00.d.ts} +9 -0
  40. package/dist/public-types.d.mts +1 -1
  41. package/dist/public-types.d.ts +1 -1
  42. package/dist/shared/custom-element.mjs +0 -1
  43. package/docs/03_provider_integration.md +5 -2
  44. package/package.json +1 -1
  45. package/dist/aiProviderStore-M4BVEKUB.mjs +0 -9
  46. package/dist/chat-EMKHB23B.mjs +0 -16
  47. package/dist/chunk-BJTO5JO5.mjs +0 -11
  48. package/dist/chunk-J6LSSK3G.mjs.map +0 -1
  49. package/dist/chunk-LKGBO5OP.mjs.map +0 -1
  50. package/dist/chunk-WSUUW7T2.mjs.map +0 -1
  51. package/dist/modelStore-7YYELPF6.mjs +0 -9
  52. package/dist/modelStore-7YYELPF6.mjs.map +0 -1
  53. /package/dist/{aiProviderStore-M4BVEKUB.mjs.map → aiProviderStore-HY5G4QAK.mjs.map} +0 -0
  54. /package/dist/{chat-EMKHB23B.mjs.map → chat-6N5B7A7F.mjs.map} +0 -0
  55. /package/dist/{chunk-ASWUEAWM.mjs.map → chunk-5BZWJQ5O.mjs.map} +0 -0
  56. /package/dist/{chunk-NGGTQVGX.mjs.map → chunk-E5WMDLE3.mjs.map} +0 -0
  57. /package/dist/{chunk-RLW4RBIR.mjs.map → chunk-KDIL5WUL.mjs.map} +0 -0
  58. /package/dist/{chunk-6VDL4SLX.mjs.map → chunk-N4BQ6HKL.mjs.map} +0 -0
  59. /package/dist/{chunk-RNCASZ2Y.mjs.map → chunk-PK3LFBGC.mjs.map} +0 -0
  60. /package/dist/{chunk-QHGGV4KJ.mjs.map → chunk-US4TG745.mjs.map} +0 -0
  61. /package/dist/{chunk-BJTO5JO5.mjs.map → modelStore-TFNX4GEC.mjs.map} +0 -0
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { F as FeatureKey, a as FeatureEvaluation, S as SubscriptionTier, b as FeatureFlagConfig, T as TrialUsage } from './public-0EYmtpGD.js';
2
- export { A as AIChatRequest, c as AIChatResponse, d as AIGenerateRequest, e as AIGenerateResponse, f as AIMessage, g as AIModel, h as AIProviderConfig, C as ChatConfig, i as ChatModal, j as ChatModalProps, k as ChatProvider, D as DEFAULT_TIER_FEATURES, l as FeatureMatrix, O as OSS_DEFAULT_FEATURES, P as PackageSettings, V as VoiceModelsResponse, m as VoiceService, v as voiceService } from './public-0EYmtpGD.js';
1
+ import { F as FeatureKey, a as FeatureEvaluation, S as SubscriptionTier, b as FeatureFlagConfig, T as TrialUsage } from './public-DJa4Db00.js';
2
+ export { A as AIChatRequest, c as AIChatResponse, d as AIGenerateRequest, e as AIGenerateResponse, f as AIMessage, g as AIModel, h as AIProviderConfig, C as ChatConfig, i as ChatModal, j as ChatModalProps, k as ChatProvider, D as DEFAULT_TIER_FEATURES, l as FeatureMatrix, O as OSS_DEFAULT_FEATURES, P as PackageSettings, V as VoiceModelsResponse, m as VoiceService, v as voiceService } from './public-DJa4Db00.js';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
  import React, { ReactNode } from 'react';
5
5
  import { Theme } from '@mui/material/styles';
@@ -248,14 +248,105 @@ interface JwtClaims {
248
248
  * Present when user belongs to a team.
249
249
  */
250
250
  teamId?: string;
251
+ /**
252
+ * Identifiers of the teams this user OWNS (Team.createdBy == email).
253
+ * Present only when the user owns at least one team.
254
+ */
255
+ ownedTeamIds?: string[];
256
+ /**
257
+ * Host domains this token can reach — tenant workspaces appear here as
258
+ * "{slug}.banditai.ai". The tenant app gates access on this claim.
259
+ */
260
+ domains?: string[];
261
+ /**
262
+ * Current subscription tier inferred from the JWT.
263
+ */
264
+ subscriptionType?: string;
265
+ /**
266
+ * Indicates whether the subscription is active.
267
+ */
268
+ isSubscribed?: boolean;
269
+ /**
270
+ * Optional avatar reference (an S3 app-asset id) for the user's profile
271
+ * picture. Resolved to an image via the file-storage download endpoint.
272
+ */
273
+ avatarUrl?: string;
274
+ }
275
+ declare const TOKEN_KEY = "authToken";
276
+ declare const AUTH_TOKEN_CHANGED_EVENT = "bandit:auth-token-changed";
277
+ /**
278
+ * Where the live access token is kept.
279
+ *
280
+ * - `localStorage` — the historical contract: the JWT is persisted under
281
+ * `authToken` and readable by any script on the page. Default, so npm
282
+ * consumers that wire `tokenFactory: () => localStorage.getItem("authToken")`
283
+ * keep working unchanged.
284
+ * - `memory` — the JWT lives only in the auth store for the life of the page.
285
+ * The host app is responsible for restoring it at boot (an HttpOnly refresh
286
+ * cookie, secure native storage, ...) through a `TokenPersistence` and a
287
+ * `refresh` handler. Nothing token-shaped is written to Web Storage.
288
+ */
289
+ type TokenStorageMode = "localStorage" | "memory";
290
+ /**
291
+ * Optional write-through mirror of the live token. Used by the native shell
292
+ * to keep the session in the OS keychain / Android Keystore instead of the
293
+ * WebView's localStorage. Failures are the host's to handle; the in-memory
294
+ * session is already valid when these run.
295
+ */
296
+ interface TokenPersistence {
297
+ write(token: string): void | Promise<void>;
298
+ clear(): void | Promise<void>;
299
+ }
300
+ interface AuthSessionHandlers {
301
+ /**
302
+ * Obtain a fresh access token silently (refresh cookie, device token, ...).
303
+ * Resolve `true` after calling `setToken` with the new token, `false` when
304
+ * an interactive sign-in is needed. Deduplicated by the service.
305
+ */
306
+ refresh?: () => Promise<boolean>;
307
+ /**
308
+ * Runs synchronously inside `clearToken()`, before the token is dropped,
309
+ * with the token being dropped — the moment to revoke it server-side.
310
+ */
311
+ beforeClear?: (token: string | null) => void;
312
+ }
313
+ interface AuthSessionConfig {
314
+ storage: TokenStorageMode;
315
+ persistence?: TokenPersistence | null;
316
+ handlers?: AuthSessionHandlers;
251
317
  }
252
318
  declare class AuthenticationService {
319
+ private storageMode;
320
+ private persistence;
321
+ private handlers;
322
+ /** Bumped on every setToken/clearToken; async flows compare it to detect a superseded session. */
323
+ private revision;
324
+ private refreshInFlight;
325
+ private lastPersist;
326
+ /**
327
+ * Pick the storage contract for this host. Call once at boot, before the
328
+ * first `setToken`. Switching modes never moves a token by itself: the
329
+ * host migrates (e.g. reads the legacy localStorage value, then clears it).
330
+ */
331
+ configureSession(config: AuthSessionConfig): void;
332
+ getStorageMode(): TokenStorageMode;
333
+ getSessionRevision(): number;
253
334
  getToken(): string | null;
254
335
  setToken(token: string): void;
255
336
  clearToken(): void;
337
+ /** True when the host registered a silent refresh path. */
338
+ canRefresh(): boolean;
339
+ /**
340
+ * Ask the host for a fresh token. Concurrent callers share one attempt.
341
+ * Resolves false when no refresh path exists or it did not produce a token.
342
+ */
343
+ tryRefresh(): Promise<boolean>;
344
+ /** Resolves once the most recent persistence write/clear has settled. */
345
+ whenPersisted(): Promise<void>;
256
346
  isAuthenticated(): boolean;
257
347
  isTokenExpired(token: string): boolean;
258
348
  parseJwtClaims(token: string): JwtClaims | null;
349
+ private mirror;
259
350
  }
260
351
  declare const authenticationService: AuthenticationService;
261
352
 
@@ -1440,4 +1531,4 @@ declare const useVectorStore: () => {
1440
1531
  initializeVectorService: () => Promise<void>;
1441
1532
  };
1442
1533
 
1443
- export { Chat, type CreateMemoryOptions, DebugLogger, FeatureEvaluation, FeatureFlagConfig, FeatureFlagContext, type FeatureFlagContextValue, FeatureFlagProvider, type FeatureFlagProviderProps, FeatureFlagService, FeatureKey, FeedbackButton, type FeedbackButtonProps, type FeedbackCategories, FeedbackModal, type FeedbackModalProps, type FeedbackPriorities, type FeedbackRequest, type FeedbackResponse, type FileUploadResult, type GatewayQueryOptions, InterestsEditor, type LogContext, MANAGEMENT_SECTIONS, Management, type ManagementAccess, type ManagementAccessInput, type ManagementLink, type ManagementProps, type ManagementRequest, type ManagementSectionDef, type ManagementSectionId, type MemorySearchFilters, type MigrationProgress, type MigrationStatus, type NotificationConfig, type NotificationContextType, NotificationProvider, type NotificationProviderProps, NotificationService, SYSTEM_FLAGS, type SearchOptions, type SearchResult, StreamingTTSClient, SubscriptionExpiredGuard, type SubscriptionExpiredGuardProps, SubscriptionExpiredModal, type SubscriptionExpiredModalProps, SubscriptionTier, type TTSOptions, type TTSProgress, TTSState, TrialUsage, type UploadRequest, type UseTTSReturn, VectorDatabaseService, type VectorDocument, type VectorMemory, type VectorMemoryMetadata, VectorMigrationService, type VectorStoreStatus, type VoiceState, authenticationService, debugLogger, defineCustomElement, featureFlagService, getCriticalConfig, getFeatureMatrix, getStreamingTTSClient, getSystemConstants, getTTSState, handleHttpError, handleSubscriptionUpgrade, handleValidationError, initializeCoreSystem, isWorkspaceAdminRole, notificationService, pauseTTS, previewTierUpgrade, resolveManagementSections, resumeTTS, showInfoNotification, showSuccessNotification, speakStream as speakWithStreaming, stopTTS, syncSubscriptionWithAPI, updateSubscriptionTier, useFeatureFlag, useFeatureVisibility, useFeatures, useGatewayHealth, useGatewayMemory, useGatewayModels, useGlobalTTS, useManagementAccess, useNotification, useNotificationService, useTTS, useVectorStore, useVoiceStore, useVoices, validateEnvironment, validateSystemIntegrity, vectorDatabaseService, vectorMigrationService };
1534
+ export { AUTH_TOKEN_CHANGED_EVENT, type AuthSessionConfig, type AuthSessionHandlers, Chat, type CreateMemoryOptions, DebugLogger, FeatureEvaluation, FeatureFlagConfig, FeatureFlagContext, type FeatureFlagContextValue, FeatureFlagProvider, type FeatureFlagProviderProps, FeatureFlagService, FeatureKey, FeedbackButton, type FeedbackButtonProps, type FeedbackCategories, FeedbackModal, type FeedbackModalProps, type FeedbackPriorities, type FeedbackRequest, type FeedbackResponse, type FileUploadResult, type GatewayQueryOptions, InterestsEditor, type JwtClaims, type LogContext, MANAGEMENT_SECTIONS, Management, type ManagementAccess, type ManagementAccessInput, type ManagementLink, type ManagementProps, type ManagementRequest, type ManagementSectionDef, type ManagementSectionId, type MemorySearchFilters, type MigrationProgress, type MigrationStatus, type NotificationConfig, type NotificationContextType, NotificationProvider, type NotificationProviderProps, NotificationService, SYSTEM_FLAGS, type SearchOptions, type SearchResult, StreamingTTSClient, SubscriptionExpiredGuard, type SubscriptionExpiredGuardProps, SubscriptionExpiredModal, type SubscriptionExpiredModalProps, SubscriptionTier, TOKEN_KEY, type TTSOptions, type TTSProgress, TTSState, type TokenPersistence, type TokenStorageMode, TrialUsage, type UploadRequest, type UseTTSReturn, VectorDatabaseService, type VectorDocument, type VectorMemory, type VectorMemoryMetadata, VectorMigrationService, type VectorStoreStatus, type VoiceState, authenticationService, debugLogger, defineCustomElement, featureFlagService, getCriticalConfig, getFeatureMatrix, getStreamingTTSClient, getSystemConstants, getTTSState, handleHttpError, handleSubscriptionUpgrade, handleValidationError, initializeCoreSystem, isWorkspaceAdminRole, notificationService, pauseTTS, previewTierUpgrade, resolveManagementSections, resumeTTS, showInfoNotification, showSuccessNotification, speakStream as speakWithStreaming, stopTTS, syncSubscriptionWithAPI, updateSubscriptionTier, useFeatureFlag, useFeatureVisibility, useFeatures, useGatewayHealth, useGatewayMemory, useGatewayModels, useGlobalTTS, useManagementAccess, useNotification, useNotificationService, useTTS, useVectorStore, useVoiceStore, useVoices, validateEnvironment, validateSystemIntegrity, vectorDatabaseService, vectorMigrationService };
package/dist/index.js CHANGED
@@ -46,31 +46,42 @@ var init_packageSettingsStore = __esm({
46
46
  });
47
47
 
48
48
  // src/store/authenticationStore.ts
49
- var import_zustand2, TOKEN_KEY, validToken, user, rawToken, useAuthenticationStore, readPersistedToken;
49
+ var import_zustand2, TOKEN_KEY, decodeClaims, validOrNull, readLegacyStorage, validToken, user, rawToken, useAuthenticationStore, readPersistedToken;
50
50
  var init_authenticationStore = __esm({
51
51
  "src/store/authenticationStore.ts"() {
52
52
  "use strict";
53
53
  import_zustand2 = require("zustand");
54
54
  TOKEN_KEY = "authToken";
55
- validToken = null;
56
- user = null;
57
- rawToken = localStorage.getItem(TOKEN_KEY);
58
- if (rawToken) {
55
+ decodeClaims = (token) => {
59
56
  try {
60
- const base64Url = rawToken.split(".")[1];
57
+ const base64Url = token.split(".")[1];
61
58
  const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
62
59
  const jsonPayload = decodeURIComponent(
63
60
  atob(base64).split("").map((c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)).join("")
64
61
  );
65
- const decoded = JSON.parse(jsonPayload);
66
- if (decoded.exp * 1e3 > Date.now()) {
67
- validToken = rawToken;
68
- user = decoded;
69
- }
62
+ return JSON.parse(jsonPayload);
63
+ } catch {
64
+ return null;
65
+ }
66
+ };
67
+ validOrNull = (raw) => {
68
+ if (!raw) return null;
69
+ const decoded = decodeClaims(raw);
70
+ return decoded && decoded.exp * 1e3 > Date.now() ? raw : null;
71
+ };
72
+ readLegacyStorage = () => {
73
+ try {
74
+ return typeof localStorage === "undefined" ? null : localStorage.getItem(TOKEN_KEY);
70
75
  } catch {
71
- validToken = null;
72
- user = null;
76
+ return null;
73
77
  }
78
+ };
79
+ validToken = null;
80
+ user = null;
81
+ rawToken = readLegacyStorage();
82
+ if (rawToken) {
83
+ validToken = validOrNull(rawToken);
84
+ user = validToken ? decodeClaims(validToken) : null;
74
85
  }
75
86
  useAuthenticationStore = (0, import_zustand2.create)((set) => ({
76
87
  token: validToken,
@@ -78,16 +89,10 @@ var init_authenticationStore = __esm({
78
89
  authError: null,
79
90
  setToken: (token) => {
80
91
  if (token) {
81
- try {
82
- const base64Url = token.split(".")[1];
83
- const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
84
- const jsonPayload = decodeURIComponent(
85
- atob(base64).split("").map((c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)).join("")
86
- );
87
- const user2 = JSON.parse(jsonPayload);
88
- localStorage.setItem(TOKEN_KEY, token);
89
- set({ token, authError: null, user: user2 });
90
- } catch {
92
+ const decoded = decodeClaims(token);
93
+ if (decoded) {
94
+ set({ token, authError: null, user: decoded });
95
+ } else {
91
96
  set({ token: null, authError: "Invalid token", user: null });
92
97
  }
93
98
  } else {
@@ -95,23 +100,11 @@ var init_authenticationStore = __esm({
95
100
  }
96
101
  },
97
102
  clearToken: () => {
98
- localStorage.removeItem(TOKEN_KEY);
99
103
  set({ token: null, authError: null, user: null });
100
104
  }
101
105
  }));
102
106
  readPersistedToken = () => {
103
- try {
104
- const raw = localStorage.getItem(TOKEN_KEY);
105
- if (!raw) return null;
106
- const base64 = raw.split(".")[1].replace(/-/g, "+").replace(/_/g, "/");
107
- const json2 = decodeURIComponent(
108
- atob(base64).split("").map((c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)).join("")
109
- );
110
- const decoded = JSON.parse(json2);
111
- return decoded.exp * 1e3 > Date.now() ? raw : null;
112
- } catch {
113
- return null;
114
- }
107
+ return validOrNull(readLegacyStorage()) ?? validOrNull(useAuthenticationStore.getState().token);
115
108
  };
116
109
  }
117
110
  });
@@ -241,7 +234,7 @@ function emitAuthTokenChanged(token) {
241
234
  detail: { token }
242
235
  }));
243
236
  }
244
- var TOKEN_KEY2, AUTH_TOKEN_CHANGED_EVENT, AuthenticationService, authenticationService;
237
+ var TOKEN_KEY2, AUTH_TOKEN_CHANGED_EVENT, readLocalStorage, writeLocalStorage, AuthenticationService, authenticationService;
245
238
  var init_authenticationService = __esm({
246
239
  "src/services/auth/authenticationService.ts"() {
247
240
  "use strict";
@@ -249,21 +242,103 @@ var init_authenticationService = __esm({
249
242
  init_debugLogger();
250
243
  TOKEN_KEY2 = "authToken";
251
244
  AUTH_TOKEN_CHANGED_EVENT = "bandit:auth-token-changed";
245
+ readLocalStorage = () => {
246
+ try {
247
+ return typeof localStorage === "undefined" ? null : localStorage.getItem(TOKEN_KEY2);
248
+ } catch {
249
+ return null;
250
+ }
251
+ };
252
+ writeLocalStorage = (token) => {
253
+ try {
254
+ if (typeof localStorage === "undefined") return;
255
+ if (token) localStorage.setItem(TOKEN_KEY2, token);
256
+ else localStorage.removeItem(TOKEN_KEY2);
257
+ } catch {
258
+ }
259
+ };
252
260
  AuthenticationService = class {
261
+ storageMode = "localStorage";
262
+ persistence = null;
263
+ handlers = {};
264
+ /** Bumped on every setToken/clearToken; async flows compare it to detect a superseded session. */
265
+ revision = 0;
266
+ refreshInFlight = null;
267
+ lastPersist = Promise.resolve();
268
+ /**
269
+ * Pick the storage contract for this host. Call once at boot, before the
270
+ * first `setToken`. Switching modes never moves a token by itself: the
271
+ * host migrates (e.g. reads the legacy localStorage value, then clears it).
272
+ */
273
+ configureSession(config) {
274
+ this.storageMode = config.storage;
275
+ this.persistence = config.persistence ?? null;
276
+ this.handlers = config.handlers ?? {};
277
+ }
278
+ getStorageMode() {
279
+ return this.storageMode;
280
+ }
281
+ getSessionRevision() {
282
+ return this.revision;
283
+ }
253
284
  getToken() {
254
- const token = localStorage.getItem(TOKEN_KEY2);
255
- return token;
285
+ if (this.storageMode === "memory") {
286
+ return useAuthenticationStore.getState().token;
287
+ }
288
+ return readLocalStorage();
256
289
  }
257
290
  setToken(token) {
258
- localStorage.setItem(TOKEN_KEY2, token);
291
+ this.revision += 1;
292
+ if (this.storageMode === "localStorage") {
293
+ writeLocalStorage(token);
294
+ } else {
295
+ writeLocalStorage(null);
296
+ }
259
297
  useAuthenticationStore.getState().setToken(token);
298
+ this.mirror(() => this.persistence?.write(token));
260
299
  emitAuthTokenChanged(token);
261
300
  }
262
301
  clearToken() {
263
- localStorage.removeItem(TOKEN_KEY2);
302
+ this.revision += 1;
303
+ const current = this.getToken() ?? useAuthenticationStore.getState().token;
304
+ try {
305
+ this.handlers.beforeClear?.(current);
306
+ } catch (error) {
307
+ debugLogger.warn("Auth beforeClear handler failed", { error });
308
+ }
309
+ writeLocalStorage(null);
264
310
  useAuthenticationStore.getState().clearToken();
311
+ this.mirror(() => this.persistence?.clear());
265
312
  emitAuthTokenChanged(null);
266
313
  }
314
+ /** True when the host registered a silent refresh path. */
315
+ canRefresh() {
316
+ return typeof this.handlers.refresh === "function";
317
+ }
318
+ /**
319
+ * Ask the host for a fresh token. Concurrent callers share one attempt.
320
+ * Resolves false when no refresh path exists or it did not produce a token.
321
+ */
322
+ tryRefresh() {
323
+ const refresh = this.handlers.refresh;
324
+ if (!refresh) return Promise.resolve(false);
325
+ if (this.refreshInFlight) return this.refreshInFlight;
326
+ this.refreshInFlight = (async () => {
327
+ try {
328
+ return await refresh();
329
+ } catch (error) {
330
+ debugLogger.warn("Auth refresh handler failed", { error });
331
+ return false;
332
+ } finally {
333
+ this.refreshInFlight = null;
334
+ }
335
+ })();
336
+ return this.refreshInFlight;
337
+ }
338
+ /** Resolves once the most recent persistence write/clear has settled. */
339
+ whenPersisted() {
340
+ return this.lastPersist;
341
+ }
267
342
  isAuthenticated() {
268
343
  const token = useAuthenticationStore.getState().token;
269
344
  return !!token && !this.isTokenExpired(token);
@@ -293,6 +368,16 @@ var init_authenticationService = __esm({
293
368
  return null;
294
369
  }
295
370
  }
371
+ mirror(run) {
372
+ const attempt = (async () => {
373
+ try {
374
+ await run();
375
+ } catch (error) {
376
+ debugLogger.warn("Auth token persistence failed", { error });
377
+ }
378
+ })();
379
+ this.lastPersist = attempt;
380
+ }
296
381
  };
297
382
  authenticationService = new AuthenticationService();
298
383
  }
@@ -4881,13 +4966,35 @@ var init_gateway_service = __esm({
4881
4966
  });
4882
4967
  instance.interceptors.response.use(
4883
4968
  (response) => response,
4884
- (error) => {
4969
+ async (error) => {
4970
+ const retried = await this._retryAfterRefresh(instance, error);
4971
+ if (retried) return retried;
4885
4972
  this._handleAuthFailure(error);
4886
4973
  return Promise.reject(this._normalizeAxiosError(error));
4887
4974
  }
4888
4975
  );
4889
4976
  return instance;
4890
4977
  }
4978
+ /**
4979
+ * A bare 401 on a host with a silent refresh path (HttpOnly refresh cookie,
4980
+ * native device token) usually means the access token aged out mid-session.
4981
+ * Refresh once and replay the request with the new token instead of
4982
+ * signing the user out; a failed refresh falls through to the sign-out path.
4983
+ */
4984
+ async _retryAfterRefresh(instance, error) {
4985
+ const config = error.config;
4986
+ if (!config || config._banditAuthRetried || error.response?.status !== 401) return null;
4987
+ if (!authenticationService.canRefresh()) return null;
4988
+ const data = error.response?.data;
4989
+ if (data?.code && data.code !== "TOKEN_EXPIRED") return null;
4990
+ try {
4991
+ if (!await authenticationService.tryRefresh()) return null;
4992
+ } catch {
4993
+ return null;
4994
+ }
4995
+ config._banditAuthRetried = true;
4996
+ return instance.request(config);
4997
+ }
4891
4998
  _normalizeAxiosError(error) {
4892
4999
  if (error.response) {
4893
5000
  return this._createHttpError(
@@ -9607,14 +9714,28 @@ function isJWTExpired(payload) {
9607
9714
  if (!payload.exp) return false;
9608
9715
  return Date.now() >= payload.exp * 1e3;
9609
9716
  }
9610
- var import_react, import_jsx_runtime, FeatureFlagContext, coerceNumber, coerceBoolean, FeatureFlagProvider, useFeatureFlag;
9717
+ var import_react, import_jsx_runtime, readSessionToken, FeatureFlagContext, coerceNumber, coerceBoolean, FeatureFlagProvider, useFeatureFlag;
9611
9718
  var init_FeatureFlagContext = __esm({
9612
9719
  "src/contexts/FeatureFlagContext.tsx"() {
9613
9720
  "use strict";
9614
9721
  import_react = require("react");
9615
9722
  init_featureFlags();
9616
9723
  init_debugLogger();
9724
+ init_authenticationService();
9617
9725
  import_jsx_runtime = require("react/jsx-runtime");
9726
+ readSessionToken = (jwtStorageKey) => {
9727
+ const fromStorage = () => {
9728
+ try {
9729
+ return localStorage.getItem(jwtStorageKey);
9730
+ } catch {
9731
+ return null;
9732
+ }
9733
+ };
9734
+ if (jwtStorageKey && jwtStorageKey !== TOKEN_KEY2) {
9735
+ return fromStorage() ?? authenticationService.getToken();
9736
+ }
9737
+ return authenticationService.getToken() ?? fromStorage();
9738
+ };
9618
9739
  FeatureFlagContext = (0, import_react.createContext)(void 0);
9619
9740
  coerceNumber = (value, fallback) => {
9620
9741
  if (typeof value === "number" && Number.isFinite(value)) {
@@ -9664,7 +9785,7 @@ var init_FeatureFlagContext = __esm({
9664
9785
  let effectiveTier = currentTier || subscriptionType || "oss";
9665
9786
  if (typeof window !== "undefined") {
9666
9787
  try {
9667
- const token = localStorage.getItem(jwtStorageKey);
9788
+ const token = readSessionToken(jwtStorageKey);
9668
9789
  if (token) {
9669
9790
  jwtFound = true;
9670
9791
  const payload = decodeJWT(token);
@@ -9750,10 +9871,13 @@ var init_FeatureFlagContext = __esm({
9750
9871
  setCurrentTier(e.detail.tier);
9751
9872
  }
9752
9873
  };
9874
+ const handleSessionChange = () => refreshEvaluation();
9753
9875
  window.addEventListener("storage", handleStorageChange);
9876
+ window.addEventListener(AUTH_TOKEN_CHANGED_EVENT, handleSessionChange);
9754
9877
  window.addEventListener("bandit:tier-updated", handleTierUpdate);
9755
9878
  return () => {
9756
9879
  window.removeEventListener("storage", handleStorageChange);
9880
+ window.removeEventListener(AUTH_TOKEN_CHANGED_EVENT, handleSessionChange);
9757
9881
  window.removeEventListener("bandit:tier-updated", handleTierUpdate);
9758
9882
  };
9759
9883
  }, [refreshEvaluation, config.jwtStorageKey]);
@@ -27488,6 +27612,7 @@ var init_chat_app_bar = __esm({
27488
27612
  const { conversations, currentId, createNewConversation, toggleIncognito, _hasHydrated } = useConversationStore();
27489
27613
  const { preferences } = usePreferencesStore();
27490
27614
  const { settings: packageSettings } = usePackageSettingsStore();
27615
+ const HostNav = packageSettings?.components?.ProductNav;
27491
27616
  const isPlaygroundRoute = typeof window !== "undefined" && window.location.pathname.includes("/playground");
27492
27617
  const isPlaygroundMode3 = Boolean(packageSettings?.playgroundMode) || (packageSettings?.gatewayApiUrl ?? "").toLowerCase().startsWith("playground://") || typeof window !== "undefined" && window.location.pathname.startsWith("/playground");
27493
27618
  const { syncEnabled, setSyncEnabled } = useConversationSyncStore((state) => ({
@@ -27626,6 +27751,7 @@ var init_chat_app_bar = __esm({
27626
27751
  ]
27627
27752
  }
27628
27753
  ),
27754
+ HostNav && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_material30.Box, { sx: { minWidth: 0, mx: 1, display: "flex", justifyContent: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(HostNav, { compact: true }) }),
27629
27755
  /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_material30.Box, { sx: { display: "flex", alignItems: "center", gap: 1 }, children: [
27630
27756
  canShowNewConversationButton && !isIncognitoActive && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
27631
27757
  import_material30.IconButton,
@@ -27744,6 +27870,7 @@ var init_chat_app_bar = __esm({
27744
27870
  ]
27745
27871
  }
27746
27872
  ),
27873
+ !isMobile && HostNav && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_material30.Box, { sx: { minWidth: 0, mx: 2, display: "flex", justifyContent: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(HostNav, {}) }),
27747
27874
  !isMobile && /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
27748
27875
  import_material30.Box,
27749
27876
  {
@@ -31116,6 +31243,7 @@ var init_chat2 = __esm({
31116
31243
  // src/index.ts
31117
31244
  var index_exports = {};
31118
31245
  __export(index_exports, {
31246
+ AUTH_TOKEN_CHANGED_EVENT: () => AUTH_TOKEN_CHANGED_EVENT,
31119
31247
  Chat: () => chat_default,
31120
31248
  ChatModal: () => chat_modal_default,
31121
31249
  ChatProvider: () => chat_provider_default,
@@ -31136,6 +31264,7 @@ __export(index_exports, {
31136
31264
  StreamingTTSClient: () => StreamingTTSClient,
31137
31265
  SubscriptionExpiredGuard: () => SubscriptionExpiredGuard,
31138
31266
  SubscriptionExpiredModal: () => SubscriptionExpiredModal,
31267
+ TOKEN_KEY: () => TOKEN_KEY2,
31139
31268
  TTSState: () => TTSState,
31140
31269
  VectorDatabaseService: () => VectorDatabaseService,
31141
31270
  VectorMigrationService: () => VectorMigrationService,
@@ -31210,6 +31339,11 @@ init_common_types();
31210
31339
  init_authenticationService();
31211
31340
  init_debugLogger();
31212
31341
  init_indexedDBService();
31342
+ var sessionTokenFactory = (label) => () => {
31343
+ const token = authenticationService.getToken();
31344
+ debugLogger.info(`AIProviderInit: ${label}`, { hasToken: !!token });
31345
+ return token;
31346
+ };
31213
31347
  var AIProviderInitService = class _AIProviderInitService {
31214
31348
  static instance;
31215
31349
  constructor() {
@@ -31289,25 +31423,7 @@ var AIProviderInitService = class _AIProviderInitService {
31289
31423
  providerConfig = {
31290
31424
  type: "ollama" /* OLLAMA */,
31291
31425
  baseUrl: settings.ollamaUrl,
31292
- tokenFactory: () => {
31293
- let token = authenticationService.getToken();
31294
- if (!token) {
31295
- token = localStorage.getItem("authToken");
31296
- }
31297
- if (!token) {
31298
- try {
31299
- const { useAuthenticationStore: useAuthenticationStore2 } = require("../../store/authenticationStore");
31300
- const authStore = useAuthenticationStore2.getState();
31301
- token = authStore.token;
31302
- } catch (e) {
31303
- }
31304
- }
31305
- debugLogger.info("AIProviderInit: Token factory called", {
31306
- hasToken: !!token,
31307
- localStorage: !!localStorage.getItem("authToken")
31308
- });
31309
- return token;
31310
- }
31426
+ tokenFactory: sessionTokenFactory("Token factory called")
31311
31427
  };
31312
31428
  debugLogger.info("Using legacy Ollama config", providerConfig);
31313
31429
  }
@@ -31330,25 +31446,7 @@ var AIProviderInitService = class _AIProviderInitService {
31330
31446
  const fallbackConfig = {
31331
31447
  type: "ollama" /* OLLAMA */,
31332
31448
  baseUrl: settings.ollamaUrl || "http://localhost:11434",
31333
- tokenFactory: () => {
31334
- let token = authenticationService.getToken();
31335
- if (!token) {
31336
- token = localStorage.getItem("authToken");
31337
- }
31338
- if (!token) {
31339
- try {
31340
- const { useAuthenticationStore: useAuthenticationStore2 } = require("../../store/authenticationStore");
31341
- const authStore = useAuthenticationStore2.getState();
31342
- token = authStore.token;
31343
- } catch (e) {
31344
- }
31345
- }
31346
- debugLogger.info("AIProviderInit: Fallback tokenFactory", {
31347
- hasToken: !!token,
31348
- localStorage: !!localStorage.getItem("authToken")
31349
- });
31350
- return token;
31351
- }
31449
+ tokenFactory: sessionTokenFactory("Fallback tokenFactory")
31352
31450
  };
31353
31451
  const { createProvider } = useAIProviderStore.getState();
31354
31452
  createProvider(fallbackConfig);
@@ -31361,25 +31459,7 @@ var AIProviderInitService = class _AIProviderInitService {
31361
31459
  const localFallbackConfig = {
31362
31460
  type: "ollama" /* OLLAMA */,
31363
31461
  baseUrl: "http://localhost:11434",
31364
- tokenFactory: () => {
31365
- let token = authenticationService.getToken();
31366
- if (!token) {
31367
- token = localStorage.getItem("authToken");
31368
- }
31369
- if (!token) {
31370
- try {
31371
- const { useAuthenticationStore: useAuthenticationStore2 } = require("../../store/authenticationStore");
31372
- const authStore = useAuthenticationStore2.getState();
31373
- token = authStore.token;
31374
- } catch (e) {
31375
- }
31376
- }
31377
- debugLogger.info("AIProviderInit: Local fallback tokenFactory", {
31378
- hasToken: !!token,
31379
- localStorage: !!localStorage.getItem("authToken")
31380
- });
31381
- return token;
31382
- }
31462
+ tokenFactory: sessionTokenFactory("Local fallback tokenFactory")
31383
31463
  };
31384
31464
  const { createProvider } = useAIProviderStore.getState();
31385
31465
  createProvider(localFallbackConfig);
@@ -31451,27 +31531,7 @@ var AIProviderInitService = class _AIProviderInitService {
31451
31531
  });
31452
31532
  return config;
31453
31533
  }
31454
- config.tokenFactory = () => {
31455
- let token = authenticationService.getToken();
31456
- if (!token && typeof localStorage !== "undefined") {
31457
- try {
31458
- token = localStorage.getItem("authToken");
31459
- } catch {
31460
- }
31461
- }
31462
- if (!token) {
31463
- try {
31464
- const { useAuthenticationStore: useAuthenticationStore2 } = require("../../store/authenticationStore");
31465
- const authStore = useAuthenticationStore2.getState();
31466
- token = authStore.token;
31467
- } catch {
31468
- }
31469
- }
31470
- debugLogger.info("AIProviderInit: Token factory resolved auth token", {
31471
- hasToken: !!token
31472
- });
31473
- return token;
31474
- };
31534
+ config.tokenFactory = sessionTokenFactory("Token factory resolved auth token");
31475
31535
  }
31476
31536
  return config;
31477
31537
  }
@@ -45225,7 +45285,7 @@ var SubscriptionExpiredModal = ({
45225
45285
  "Account: ",
45226
45286
  /* @__PURE__ */ (0, import_jsx_runtime64.jsx)("strong", { children: userEmail })
45227
45287
  ] }),
45228
- /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(import_material62.Typography, { variant: "body1", sx: { textAlign: "center", color: "text.secondary" }, children: IS_STORE_BUILD ? "Premium features are unavailable on this account right now." : "To continue using all features, please renew your subscription or return to the main application." }),
45288
+ /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(import_material62.Typography, { variant: "body1", sx: { textAlign: "center", color: "text.secondary" }, children: IS_STORE_BUILD ? "Premium features are unavailable on this account right now. You can subscribe to Pro in the app or review your account." : "To continue using all features, please renew your subscription or return to the main application." }),
45229
45289
  /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(import_material62.Box, { sx: {
45230
45290
  bgcolor: "background.paper",
45231
45291
  border: 1,
@@ -45247,10 +45307,10 @@ var SubscriptionExpiredModal = ({
45247
45307
  startIcon: /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(import_react89.Home, {}),
45248
45308
  size: "large",
45249
45309
  sx: { minWidth: 140 },
45250
- children: "Go Home"
45310
+ children: IS_STORE_BUILD ? "My Account" : "Go Home"
45251
45311
  }
45252
45312
  ),
45253
- !IS_STORE_BUILD && /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
45313
+ (!IS_STORE_BUILD || onManageSubscription) && /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
45254
45314
  import_material62.Button,
45255
45315
  {
45256
45316
  onClick: handleManageSubscription,
@@ -45259,7 +45319,7 @@ var SubscriptionExpiredModal = ({
45259
45319
  size: "large",
45260
45320
  color: "primary",
45261
45321
  sx: { minWidth: 140 },
45262
- children: "Renew Subscription"
45322
+ children: IS_STORE_BUILD ? "View Pro Options" : "Renew Subscription"
45263
45323
  }
45264
45324
  )
45265
45325
  ] })
@@ -45271,6 +45331,7 @@ var SubscriptionExpiredModal = ({
45271
45331
  // src/guards/SubscriptionExpiredGuard.tsx
45272
45332
  var import_react90 = require("react");
45273
45333
  init_useFeatures();
45334
+ init_authenticationService();
45274
45335
  var import_jsx_runtime65 = require("react/jsx-runtime");
45275
45336
  var SubscriptionExpiredGuard = ({
45276
45337
  children,
@@ -45298,11 +45359,9 @@ var SubscriptionExpiredGuard = ({
45298
45359
  const evaluation = getFullEvaluation();
45299
45360
  const extractedEmail = userEmail || evaluation?.metadata?.jwtFound ? (() => {
45300
45361
  try {
45301
- const token = localStorage.getItem("authToken");
45362
+ const token = authenticationService.getToken();
45302
45363
  if (token) {
45303
- const parts = token.split(".");
45304
- const payload = JSON.parse(atob(parts[1]));
45305
- return payload.email;
45364
+ return authenticationService.parseJwtClaims(token)?.email;
45306
45365
  }
45307
45366
  } catch (e) {
45308
45367
  }
@@ -45484,6 +45543,7 @@ init_vectorMigrationService();
45484
45543
  init_useVectorStore();
45485
45544
  // Annotate the CommonJS export names for ESM import in node:
45486
45545
  0 && (module.exports = {
45546
+ AUTH_TOKEN_CHANGED_EVENT,
45487
45547
  Chat,
45488
45548
  ChatModal,
45489
45549
  ChatProvider,
@@ -45504,6 +45564,7 @@ init_useVectorStore();
45504
45564
  StreamingTTSClient,
45505
45565
  SubscriptionExpiredGuard,
45506
45566
  SubscriptionExpiredModal,
45567
+ TOKEN_KEY,
45507
45568
  TTSState,
45508
45569
  VectorDatabaseService,
45509
45570
  VectorMigrationService,