@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
@@ -1,16 +1,15 @@
1
1
  import {
2
2
  management_default
3
- } from "../chunk-RNCASZ2Y.mjs";
4
- import "../chunk-ASWUEAWM.mjs";
5
- import "../chunk-LKGBO5OP.mjs";
6
- import "../chunk-NGGTQVGX.mjs";
7
- import "../chunk-6VDL4SLX.mjs";
8
- import "../chunk-RLW4RBIR.mjs";
9
- import "../chunk-FEIDGXVR.mjs";
10
- import "../chunk-2EM4ATKI.mjs";
11
- import "../chunk-J6LSSK3G.mjs";
3
+ } from "../chunk-PK3LFBGC.mjs";
4
+ import "../chunk-5BZWJQ5O.mjs";
5
+ import "../chunk-CABOLDNR.mjs";
6
+ import "../chunk-E5WMDLE3.mjs";
7
+ import "../chunk-N4BQ6HKL.mjs";
8
+ import "../chunk-KDIL5WUL.mjs";
9
+ import "../chunk-46OGYEME.mjs";
10
+ import "../chunk-SZJB5FLZ.mjs";
11
+ import "../chunk-GRPTCHFN.mjs";
12
12
  import "../chunk-HNXPDIWC.mjs";
13
- import "../chunk-BJTO5JO5.mjs";
14
13
  export {
15
14
  management_default as default
16
15
  };
@@ -1,5 +1,3 @@
1
- import "./chunk-BJTO5JO5.mjs";
2
-
3
1
  // src/utils/memoryUtils.ts
4
2
  var isRecord = (value) => typeof value === "object" && value !== null;
5
3
  var isStringArray = (value) => Array.isArray(value) && value.every((item) => typeof item === "string");
@@ -220,4 +218,4 @@ export {
220
218
  generateProgressMessage,
221
219
  validateMemories
222
220
  };
223
- //# sourceMappingURL=memoryUtils-2FVXDSS6.mjs.map
221
+ //# sourceMappingURL=memoryUtils-HGCYEUCD.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/memoryUtils.ts"],"sourcesContent":["/*\n * Copyright (c) 2025 Burtson Labs LLC.\n * SPDX-License-Identifier: BUSL-1.1\n * See LICENSE for the applicable terms.\n */\n\n\nexport interface BulkMemoryResult {\n success: boolean;\n message: string;\n totalProcessed: number;\n successCount: number;\n failureCount: number;\n errors: string[];\n}\n\nexport interface MemoryImportOptions {\n mode: 'append' | 'replace' | 'smartDedupe';\n clearExisting?: boolean;\n includeMetadata?: boolean;\n batchSize?: number;\n}\n\ntype MemoryRecordCandidate = {\n content?: unknown;\n title?: unknown;\n tags?: unknown;\n timestamp?: unknown;\n};\n\ntype BatchOperationResult = {\n summary?: {\n totalProcessed?: number;\n successCount?: number;\n failureCount?: number;\n };\n totalMemories?: number;\n successCount?: number;\n failureCount?: number;\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null;\n\nconst isStringArray = (value: unknown): value is string[] =>\n Array.isArray(value) && value.every((item) => typeof item === 'string');\n\nconst resolveTimestampLabel = (timestamp: unknown): string => {\n if (timestamp instanceof Date && !Number.isNaN(timestamp.getTime())) {\n return timestamp.toLocaleDateString();\n }\n\n if (typeof timestamp === 'number' && Number.isFinite(timestamp)) {\n const date = new Date(timestamp);\n if (!Number.isNaN(date.getTime())) {\n return date.toLocaleDateString();\n }\n }\n\n if (typeof timestamp === 'string' && timestamp.trim()) {\n const date = new Date(timestamp);\n if (!Number.isNaN(date.getTime())) {\n return date.toLocaleDateString();\n }\n }\n\n return new Date().toLocaleDateString();\n};\n\n/**\n * Memory Utilities for Bulk Operations\n * \n * Provides helper functions for batch memory operations,\n * data formatting, and migration utilities.\n */\nexport class MemoryUtils {\n /**\n * Validate memory content before bulk operations\n */\n static validateMemories(memories: Array<{ content: string; title?: string; tags?: string[] }>): {\n valid: Array<{ content: string; title?: string; tags?: string[] }>;\n invalid: Array<{ content: string; error: string; index: number }>;\n } {\n const valid: Array<{ content: string; title?: string; tags?: string[] }> = [];\n const invalid: Array<{ content: string; error: string; index: number }> = [];\n\n memories.forEach((memory, index) => {\n // Check content length\n if (!memory.content || memory.content.trim().length === 0) {\n invalid.push({\n content: memory.content || '',\n error: 'Empty content',\n index\n });\n return;\n }\n\n // Check maximum content length (e.g., 50KB)\n if (memory.content.length > 50000) {\n invalid.push({\n content: memory.content.substring(0, 100) + '...',\n error: 'Content too long (max 50KB)',\n index\n });\n return;\n }\n\n // Check tags format\n if (memory.tags && (!Array.isArray(memory.tags) || memory.tags.some(tag => typeof tag !== 'string'))) {\n invalid.push({\n content: memory.content.substring(0, 100) + '...',\n error: 'Invalid tags format (must be string array)',\n index\n });\n return;\n }\n\n valid.push(memory);\n });\n\n return { valid, invalid };\n }\n\n /**\n * Format IndexedDB memories for vector import\n */\n static formatForVectorImport(indexedDBMemories: ReadonlyArray<unknown>): Array<{ content: string; title?: string; tags?: string[] }> {\n return indexedDBMemories.reduce<Array<{ content: string; title?: string; tags?: string[] }>>((acc, entry) => {\n if (!isRecord(entry)) {\n return acc;\n }\n\n const candidate = entry as MemoryRecordCandidate;\n if (typeof candidate.content !== 'string') {\n return acc;\n }\n\n const title = typeof candidate.title === 'string' && candidate.title.trim().length > 0\n ? candidate.title\n : `Memory from ${resolveTimestampLabel(candidate.timestamp)}`;\n\n const tags = isStringArray(candidate.tags) ? candidate.tags : [];\n\n acc.push({\n content: candidate.content,\n title,\n tags\n });\n return acc;\n }, []);\n }\n\n /**\n * Chunk large batch operations for better performance\n */\n static chunkMemories<T>(\n memories: T[], \n chunkSize: number = 100\n ): T[][] {\n const chunks: T[][] = [];\n for (let i = 0; i < memories.length; i += chunkSize) {\n chunks.push(memories.slice(i, i + chunkSize));\n }\n return chunks;\n }\n\n /**\n * Estimate batch operation time and resource usage\n */\n static estimateBatchOperation(memoryCount: number, averageContentLength: number): {\n estimatedTimeMinutes: number;\n estimatedTokens: number;\n recommendedChunkSize: number;\n warnings: string[];\n } {\n const warnings: string[] = [];\n \n // Rough estimates based on typical embedding performance\n const embeddingsPerMinute = 100; // Conservative estimate\n const tokensPerCharacter = 0.25; // Rough estimate for token calculation\n \n const estimatedTimeMinutes = Math.ceil(memoryCount / embeddingsPerMinute);\n const estimatedTokens = Math.ceil(memoryCount * averageContentLength * tokensPerCharacter);\n \n // Recommend smaller chunks for large operations\n let recommendedChunkSize = 50;\n if (memoryCount > 1000) {\n recommendedChunkSize = 25;\n warnings.push('Large batch operation - consider running during off-peak hours');\n }\n if (averageContentLength > 2000) {\n recommendedChunkSize = Math.max(10, recommendedChunkSize / 2);\n warnings.push('Large content size - reduced chunk size recommended');\n }\n if (estimatedTimeMinutes > 30) {\n warnings.push('Long operation expected - ensure stable internet connection');\n }\n\n return {\n estimatedTimeMinutes,\n estimatedTokens,\n recommendedChunkSize,\n warnings\n };\n }\n\n /**\n * Create summary report for batch operations\n */\n static createBatchSummary(\n result: BatchOperationResult,\n operationType: 'import' | 'create' | 'migrate',\n startTime: Date\n ): {\n summary: string;\n details: {\n operation: string;\n duration: string;\n totalMemories: number;\n successful: number;\n failed: number;\n successRate: string;\n averageTimePerMemory: string;\n };\n } {\n const endTime = new Date();\n const durationMs = endTime.getTime() - startTime.getTime();\n const durationMinutes = Math.floor(durationMs / 60000);\n const durationSeconds = Math.floor((durationMs % 60000) / 1000);\n \n const totalMemories = result.summary?.totalProcessed ?? result.totalMemories ?? 0;\n const successful = result.summary?.successCount ?? result.successCount ?? 0;\n const failed = result.summary?.failureCount ?? result.failureCount ?? 0;\n const successRate = totalMemories > 0 ? ((successful / totalMemories) * 100).toFixed(1) : '0';\n const avgTimePerMemory = totalMemories > 0 ? (durationMs / totalMemories).toFixed(0) : '0';\n\n const summary = `${operationType.toUpperCase()} completed: ${successful}/${totalMemories} memories processed successfully (${successRate}% success rate) in ${durationMinutes}m ${durationSeconds}s`;\n\n const details = {\n operation: operationType,\n duration: `${durationMinutes}m ${durationSeconds}s`,\n totalMemories,\n successful,\n failed,\n successRate: `${successRate}%`,\n averageTimePerMemory: `${avgTimePerMemory}ms`\n };\n\n return { summary, details };\n }\n\n /**\n * Generate progress messages for UI updates\n */\n static generateProgressMessage(\n current: number, \n total: number, \n operation: string = 'Processing'\n ): string {\n const percentage = Math.floor((current / total) * 100);\n const remaining = total - current;\n \n if (current === 0) {\n return `Starting ${operation.toLowerCase()}...`;\n } else if (current === total) {\n return `${operation} completed!`;\n } else if (current < total / 4) {\n return `${operation} memories... (${current}/${total})`;\n } else if (current < total / 2) {\n return `${percentage}% complete... (${remaining} remaining)`;\n } else if (current < total * 0.9) {\n return `Almost done... ${percentage}% complete`;\n } else {\n return `Finishing up... ${remaining} memories left`;\n }\n }\n\n /**\n * Compare memory content for deduplication\n */\n static calculateSimilarity(content1: string, content2: string): number {\n // Simple similarity check - could be enhanced with more sophisticated algorithms\n const words1 = content1.toLowerCase().split(/\\s+/);\n const words2 = content2.toLowerCase().split(/\\s+/);\n \n const set1 = new Set(words1);\n const set2 = new Set(words2);\n \n const intersection = new Set([...set1].filter(word => set2.has(word)));\n const union = new Set([...set1, ...set2]);\n \n return union.size > 0 ? intersection.size / union.size : 0;\n }\n\n /**\n * Find potential duplicates in memory list\n */\n static findPotentialDuplicates(\n memories: Array<{ content: string; title?: string; tags?: string[] }>,\n similarityThreshold: number = 0.8\n ): Array<{\n memory: { content: string; title?: string; tags?: string[] };\n duplicates: Array<{\n memory: { content: string; title?: string; tags?: string[] };\n similarity: number;\n index: number;\n }>;\n }> {\n const duplicates: Array<{\n memory: { content: string; title?: string; tags?: string[] };\n duplicates: Array<{\n memory: { content: string; title?: string; tags?: string[] };\n similarity: number;\n index: number;\n }>;\n }> = [];\n\n for (let i = 0; i < memories.length; i++) {\n const currentMemory = memories[i];\n const potentialDuplicates: Array<{\n memory: { content: string; title?: string; tags?: string[] };\n similarity: number;\n index: number;\n }> = [];\n\n for (let j = i + 1; j < memories.length; j++) {\n const compareMemory = memories[j];\n const similarity = this.calculateSimilarity(currentMemory.content, compareMemory.content);\n \n if (similarity >= similarityThreshold) {\n potentialDuplicates.push({\n memory: compareMemory,\n similarity,\n index: j\n });\n }\n }\n\n if (potentialDuplicates.length > 0) {\n duplicates.push({\n memory: currentMemory,\n duplicates: potentialDuplicates\n });\n }\n }\n\n return duplicates;\n }\n}\n\n// Export utility functions for direct use\nexport const validateMemories = MemoryUtils.validateMemories;\nexport const formatForVectorImport = MemoryUtils.formatForVectorImport;\nexport const chunkMemories = MemoryUtils.chunkMemories;\nexport const estimateBatchOperation = MemoryUtils.estimateBatchOperation;\nexport const createBatchSummary = MemoryUtils.createBatchSummary;\nexport const generateProgressMessage = MemoryUtils.generateProgressMessage;\nexport const findPotentialDuplicates = MemoryUtils.findPotentialDuplicates;\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/utils/memoryUtils.ts"],"sourcesContent":["/*\n * Copyright (c) 2025 Burtson Labs LLC.\n * SPDX-License-Identifier: BUSL-1.1\n * See LICENSE for the applicable terms.\n */\n\n\nexport interface BulkMemoryResult {\n success: boolean;\n message: string;\n totalProcessed: number;\n successCount: number;\n failureCount: number;\n errors: string[];\n}\n\nexport interface MemoryImportOptions {\n mode: 'append' | 'replace' | 'smartDedupe';\n clearExisting?: boolean;\n includeMetadata?: boolean;\n batchSize?: number;\n}\n\ntype MemoryRecordCandidate = {\n content?: unknown;\n title?: unknown;\n tags?: unknown;\n timestamp?: unknown;\n};\n\ntype BatchOperationResult = {\n summary?: {\n totalProcessed?: number;\n successCount?: number;\n failureCount?: number;\n };\n totalMemories?: number;\n successCount?: number;\n failureCount?: number;\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null;\n\nconst isStringArray = (value: unknown): value is string[] =>\n Array.isArray(value) && value.every((item) => typeof item === 'string');\n\nconst resolveTimestampLabel = (timestamp: unknown): string => {\n if (timestamp instanceof Date && !Number.isNaN(timestamp.getTime())) {\n return timestamp.toLocaleDateString();\n }\n\n if (typeof timestamp === 'number' && Number.isFinite(timestamp)) {\n const date = new Date(timestamp);\n if (!Number.isNaN(date.getTime())) {\n return date.toLocaleDateString();\n }\n }\n\n if (typeof timestamp === 'string' && timestamp.trim()) {\n const date = new Date(timestamp);\n if (!Number.isNaN(date.getTime())) {\n return date.toLocaleDateString();\n }\n }\n\n return new Date().toLocaleDateString();\n};\n\n/**\n * Memory Utilities for Bulk Operations\n * \n * Provides helper functions for batch memory operations,\n * data formatting, and migration utilities.\n */\nexport class MemoryUtils {\n /**\n * Validate memory content before bulk operations\n */\n static validateMemories(memories: Array<{ content: string; title?: string; tags?: string[] }>): {\n valid: Array<{ content: string; title?: string; tags?: string[] }>;\n invalid: Array<{ content: string; error: string; index: number }>;\n } {\n const valid: Array<{ content: string; title?: string; tags?: string[] }> = [];\n const invalid: Array<{ content: string; error: string; index: number }> = [];\n\n memories.forEach((memory, index) => {\n // Check content length\n if (!memory.content || memory.content.trim().length === 0) {\n invalid.push({\n content: memory.content || '',\n error: 'Empty content',\n index\n });\n return;\n }\n\n // Check maximum content length (e.g., 50KB)\n if (memory.content.length > 50000) {\n invalid.push({\n content: memory.content.substring(0, 100) + '...',\n error: 'Content too long (max 50KB)',\n index\n });\n return;\n }\n\n // Check tags format\n if (memory.tags && (!Array.isArray(memory.tags) || memory.tags.some(tag => typeof tag !== 'string'))) {\n invalid.push({\n content: memory.content.substring(0, 100) + '...',\n error: 'Invalid tags format (must be string array)',\n index\n });\n return;\n }\n\n valid.push(memory);\n });\n\n return { valid, invalid };\n }\n\n /**\n * Format IndexedDB memories for vector import\n */\n static formatForVectorImport(indexedDBMemories: ReadonlyArray<unknown>): Array<{ content: string; title?: string; tags?: string[] }> {\n return indexedDBMemories.reduce<Array<{ content: string; title?: string; tags?: string[] }>>((acc, entry) => {\n if (!isRecord(entry)) {\n return acc;\n }\n\n const candidate = entry as MemoryRecordCandidate;\n if (typeof candidate.content !== 'string') {\n return acc;\n }\n\n const title = typeof candidate.title === 'string' && candidate.title.trim().length > 0\n ? candidate.title\n : `Memory from ${resolveTimestampLabel(candidate.timestamp)}`;\n\n const tags = isStringArray(candidate.tags) ? candidate.tags : [];\n\n acc.push({\n content: candidate.content,\n title,\n tags\n });\n return acc;\n }, []);\n }\n\n /**\n * Chunk large batch operations for better performance\n */\n static chunkMemories<T>(\n memories: T[], \n chunkSize: number = 100\n ): T[][] {\n const chunks: T[][] = [];\n for (let i = 0; i < memories.length; i += chunkSize) {\n chunks.push(memories.slice(i, i + chunkSize));\n }\n return chunks;\n }\n\n /**\n * Estimate batch operation time and resource usage\n */\n static estimateBatchOperation(memoryCount: number, averageContentLength: number): {\n estimatedTimeMinutes: number;\n estimatedTokens: number;\n recommendedChunkSize: number;\n warnings: string[];\n } {\n const warnings: string[] = [];\n \n // Rough estimates based on typical embedding performance\n const embeddingsPerMinute = 100; // Conservative estimate\n const tokensPerCharacter = 0.25; // Rough estimate for token calculation\n \n const estimatedTimeMinutes = Math.ceil(memoryCount / embeddingsPerMinute);\n const estimatedTokens = Math.ceil(memoryCount * averageContentLength * tokensPerCharacter);\n \n // Recommend smaller chunks for large operations\n let recommendedChunkSize = 50;\n if (memoryCount > 1000) {\n recommendedChunkSize = 25;\n warnings.push('Large batch operation - consider running during off-peak hours');\n }\n if (averageContentLength > 2000) {\n recommendedChunkSize = Math.max(10, recommendedChunkSize / 2);\n warnings.push('Large content size - reduced chunk size recommended');\n }\n if (estimatedTimeMinutes > 30) {\n warnings.push('Long operation expected - ensure stable internet connection');\n }\n\n return {\n estimatedTimeMinutes,\n estimatedTokens,\n recommendedChunkSize,\n warnings\n };\n }\n\n /**\n * Create summary report for batch operations\n */\n static createBatchSummary(\n result: BatchOperationResult,\n operationType: 'import' | 'create' | 'migrate',\n startTime: Date\n ): {\n summary: string;\n details: {\n operation: string;\n duration: string;\n totalMemories: number;\n successful: number;\n failed: number;\n successRate: string;\n averageTimePerMemory: string;\n };\n } {\n const endTime = new Date();\n const durationMs = endTime.getTime() - startTime.getTime();\n const durationMinutes = Math.floor(durationMs / 60000);\n const durationSeconds = Math.floor((durationMs % 60000) / 1000);\n \n const totalMemories = result.summary?.totalProcessed ?? result.totalMemories ?? 0;\n const successful = result.summary?.successCount ?? result.successCount ?? 0;\n const failed = result.summary?.failureCount ?? result.failureCount ?? 0;\n const successRate = totalMemories > 0 ? ((successful / totalMemories) * 100).toFixed(1) : '0';\n const avgTimePerMemory = totalMemories > 0 ? (durationMs / totalMemories).toFixed(0) : '0';\n\n const summary = `${operationType.toUpperCase()} completed: ${successful}/${totalMemories} memories processed successfully (${successRate}% success rate) in ${durationMinutes}m ${durationSeconds}s`;\n\n const details = {\n operation: operationType,\n duration: `${durationMinutes}m ${durationSeconds}s`,\n totalMemories,\n successful,\n failed,\n successRate: `${successRate}%`,\n averageTimePerMemory: `${avgTimePerMemory}ms`\n };\n\n return { summary, details };\n }\n\n /**\n * Generate progress messages for UI updates\n */\n static generateProgressMessage(\n current: number, \n total: number, \n operation: string = 'Processing'\n ): string {\n const percentage = Math.floor((current / total) * 100);\n const remaining = total - current;\n \n if (current === 0) {\n return `Starting ${operation.toLowerCase()}...`;\n } else if (current === total) {\n return `${operation} completed!`;\n } else if (current < total / 4) {\n return `${operation} memories... (${current}/${total})`;\n } else if (current < total / 2) {\n return `${percentage}% complete... (${remaining} remaining)`;\n } else if (current < total * 0.9) {\n return `Almost done... ${percentage}% complete`;\n } else {\n return `Finishing up... ${remaining} memories left`;\n }\n }\n\n /**\n * Compare memory content for deduplication\n */\n static calculateSimilarity(content1: string, content2: string): number {\n // Simple similarity check - could be enhanced with more sophisticated algorithms\n const words1 = content1.toLowerCase().split(/\\s+/);\n const words2 = content2.toLowerCase().split(/\\s+/);\n \n const set1 = new Set(words1);\n const set2 = new Set(words2);\n \n const intersection = new Set([...set1].filter(word => set2.has(word)));\n const union = new Set([...set1, ...set2]);\n \n return union.size > 0 ? intersection.size / union.size : 0;\n }\n\n /**\n * Find potential duplicates in memory list\n */\n static findPotentialDuplicates(\n memories: Array<{ content: string; title?: string; tags?: string[] }>,\n similarityThreshold: number = 0.8\n ): Array<{\n memory: { content: string; title?: string; tags?: string[] };\n duplicates: Array<{\n memory: { content: string; title?: string; tags?: string[] };\n similarity: number;\n index: number;\n }>;\n }> {\n const duplicates: Array<{\n memory: { content: string; title?: string; tags?: string[] };\n duplicates: Array<{\n memory: { content: string; title?: string; tags?: string[] };\n similarity: number;\n index: number;\n }>;\n }> = [];\n\n for (let i = 0; i < memories.length; i++) {\n const currentMemory = memories[i];\n const potentialDuplicates: Array<{\n memory: { content: string; title?: string; tags?: string[] };\n similarity: number;\n index: number;\n }> = [];\n\n for (let j = i + 1; j < memories.length; j++) {\n const compareMemory = memories[j];\n const similarity = this.calculateSimilarity(currentMemory.content, compareMemory.content);\n \n if (similarity >= similarityThreshold) {\n potentialDuplicates.push({\n memory: compareMemory,\n similarity,\n index: j\n });\n }\n }\n\n if (potentialDuplicates.length > 0) {\n duplicates.push({\n memory: currentMemory,\n duplicates: potentialDuplicates\n });\n }\n }\n\n return duplicates;\n }\n}\n\n// Export utility functions for direct use\nexport const validateMemories = MemoryUtils.validateMemories;\nexport const formatForVectorImport = MemoryUtils.formatForVectorImport;\nexport const chunkMemories = MemoryUtils.chunkMemories;\nexport const estimateBatchOperation = MemoryUtils.estimateBatchOperation;\nexport const createBatchSummary = MemoryUtils.createBatchSummary;\nexport const generateProgressMessage = MemoryUtils.generateProgressMessage;\nexport const findPotentialDuplicates = MemoryUtils.findPotentialDuplicates;\n"],"mappings":"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","names":[]}
@@ -1450,31 +1450,42 @@ var init_anthropic_provider = __esm({
1450
1450
  });
1451
1451
 
1452
1452
  // src/store/authenticationStore.ts
1453
- var import_zustand2, TOKEN_KEY, validToken, user, rawToken, useAuthenticationStore;
1453
+ var import_zustand2, TOKEN_KEY, decodeClaims, validOrNull, readLegacyStorage, validToken, user, rawToken, useAuthenticationStore;
1454
1454
  var init_authenticationStore = __esm({
1455
1455
  "src/store/authenticationStore.ts"() {
1456
1456
  "use strict";
1457
1457
  import_zustand2 = require("zustand");
1458
1458
  TOKEN_KEY = "authToken";
1459
- validToken = null;
1460
- user = null;
1461
- rawToken = localStorage.getItem(TOKEN_KEY);
1462
- if (rawToken) {
1459
+ decodeClaims = (token) => {
1463
1460
  try {
1464
- const base64Url = rawToken.split(".")[1];
1461
+ const base64Url = token.split(".")[1];
1465
1462
  const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
1466
1463
  const jsonPayload = decodeURIComponent(
1467
1464
  atob(base64).split("").map((c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)).join("")
1468
1465
  );
1469
- const decoded = JSON.parse(jsonPayload);
1470
- if (decoded.exp * 1e3 > Date.now()) {
1471
- validToken = rawToken;
1472
- user = decoded;
1473
- }
1466
+ return JSON.parse(jsonPayload);
1467
+ } catch {
1468
+ return null;
1469
+ }
1470
+ };
1471
+ validOrNull = (raw) => {
1472
+ if (!raw) return null;
1473
+ const decoded = decodeClaims(raw);
1474
+ return decoded && decoded.exp * 1e3 > Date.now() ? raw : null;
1475
+ };
1476
+ readLegacyStorage = () => {
1477
+ try {
1478
+ return typeof localStorage === "undefined" ? null : localStorage.getItem(TOKEN_KEY);
1474
1479
  } catch {
1475
- validToken = null;
1476
- user = null;
1480
+ return null;
1477
1481
  }
1482
+ };
1483
+ validToken = null;
1484
+ user = null;
1485
+ rawToken = readLegacyStorage();
1486
+ if (rawToken) {
1487
+ validToken = validOrNull(rawToken);
1488
+ user = validToken ? decodeClaims(validToken) : null;
1478
1489
  }
1479
1490
  useAuthenticationStore = (0, import_zustand2.create)((set) => ({
1480
1491
  token: validToken,
@@ -1482,16 +1493,10 @@ var init_authenticationStore = __esm({
1482
1493
  authError: null,
1483
1494
  setToken: (token) => {
1484
1495
  if (token) {
1485
- try {
1486
- const base64Url = token.split(".")[1];
1487
- const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
1488
- const jsonPayload = decodeURIComponent(
1489
- atob(base64).split("").map((c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)).join("")
1490
- );
1491
- const user2 = JSON.parse(jsonPayload);
1492
- localStorage.setItem(TOKEN_KEY, token);
1493
- set({ token, authError: null, user: user2 });
1494
- } catch {
1496
+ const decoded = decodeClaims(token);
1497
+ if (decoded) {
1498
+ set({ token, authError: null, user: decoded });
1499
+ } else {
1495
1500
  set({ token: null, authError: "Invalid token", user: null });
1496
1501
  }
1497
1502
  } else {
@@ -1499,7 +1504,6 @@ var init_authenticationStore = __esm({
1499
1504
  }
1500
1505
  },
1501
1506
  clearToken: () => {
1502
- localStorage.removeItem(TOKEN_KEY);
1503
1507
  set({ token: null, authError: null, user: null });
1504
1508
  }
1505
1509
  }));
@@ -1515,7 +1519,7 @@ function emitAuthTokenChanged(token) {
1515
1519
  detail: { token }
1516
1520
  }));
1517
1521
  }
1518
- var TOKEN_KEY2, AUTH_TOKEN_CHANGED_EVENT, AuthenticationService, authenticationService;
1522
+ var TOKEN_KEY2, AUTH_TOKEN_CHANGED_EVENT, readLocalStorage, writeLocalStorage, AuthenticationService, authenticationService;
1519
1523
  var init_authenticationService = __esm({
1520
1524
  "src/services/auth/authenticationService.ts"() {
1521
1525
  "use strict";
@@ -1523,21 +1527,103 @@ var init_authenticationService = __esm({
1523
1527
  init_debugLogger();
1524
1528
  TOKEN_KEY2 = "authToken";
1525
1529
  AUTH_TOKEN_CHANGED_EVENT = "bandit:auth-token-changed";
1530
+ readLocalStorage = () => {
1531
+ try {
1532
+ return typeof localStorage === "undefined" ? null : localStorage.getItem(TOKEN_KEY2);
1533
+ } catch {
1534
+ return null;
1535
+ }
1536
+ };
1537
+ writeLocalStorage = (token) => {
1538
+ try {
1539
+ if (typeof localStorage === "undefined") return;
1540
+ if (token) localStorage.setItem(TOKEN_KEY2, token);
1541
+ else localStorage.removeItem(TOKEN_KEY2);
1542
+ } catch {
1543
+ }
1544
+ };
1526
1545
  AuthenticationService = class {
1546
+ storageMode = "localStorage";
1547
+ persistence = null;
1548
+ handlers = {};
1549
+ /** Bumped on every setToken/clearToken; async flows compare it to detect a superseded session. */
1550
+ revision = 0;
1551
+ refreshInFlight = null;
1552
+ lastPersist = Promise.resolve();
1553
+ /**
1554
+ * Pick the storage contract for this host. Call once at boot, before the
1555
+ * first `setToken`. Switching modes never moves a token by itself: the
1556
+ * host migrates (e.g. reads the legacy localStorage value, then clears it).
1557
+ */
1558
+ configureSession(config) {
1559
+ this.storageMode = config.storage;
1560
+ this.persistence = config.persistence ?? null;
1561
+ this.handlers = config.handlers ?? {};
1562
+ }
1563
+ getStorageMode() {
1564
+ return this.storageMode;
1565
+ }
1566
+ getSessionRevision() {
1567
+ return this.revision;
1568
+ }
1527
1569
  getToken() {
1528
- const token = localStorage.getItem(TOKEN_KEY2);
1529
- return token;
1570
+ if (this.storageMode === "memory") {
1571
+ return useAuthenticationStore.getState().token;
1572
+ }
1573
+ return readLocalStorage();
1530
1574
  }
1531
1575
  setToken(token) {
1532
- localStorage.setItem(TOKEN_KEY2, token);
1576
+ this.revision += 1;
1577
+ if (this.storageMode === "localStorage") {
1578
+ writeLocalStorage(token);
1579
+ } else {
1580
+ writeLocalStorage(null);
1581
+ }
1533
1582
  useAuthenticationStore.getState().setToken(token);
1583
+ this.mirror(() => this.persistence?.write(token));
1534
1584
  emitAuthTokenChanged(token);
1535
1585
  }
1536
1586
  clearToken() {
1537
- localStorage.removeItem(TOKEN_KEY2);
1587
+ this.revision += 1;
1588
+ const current = this.getToken() ?? useAuthenticationStore.getState().token;
1589
+ try {
1590
+ this.handlers.beforeClear?.(current);
1591
+ } catch (error) {
1592
+ debugLogger.warn("Auth beforeClear handler failed", { error });
1593
+ }
1594
+ writeLocalStorage(null);
1538
1595
  useAuthenticationStore.getState().clearToken();
1596
+ this.mirror(() => this.persistence?.clear());
1539
1597
  emitAuthTokenChanged(null);
1540
1598
  }
1599
+ /** True when the host registered a silent refresh path. */
1600
+ canRefresh() {
1601
+ return typeof this.handlers.refresh === "function";
1602
+ }
1603
+ /**
1604
+ * Ask the host for a fresh token. Concurrent callers share one attempt.
1605
+ * Resolves false when no refresh path exists or it did not produce a token.
1606
+ */
1607
+ tryRefresh() {
1608
+ const refresh = this.handlers.refresh;
1609
+ if (!refresh) return Promise.resolve(false);
1610
+ if (this.refreshInFlight) return this.refreshInFlight;
1611
+ this.refreshInFlight = (async () => {
1612
+ try {
1613
+ return await refresh();
1614
+ } catch (error) {
1615
+ debugLogger.warn("Auth refresh handler failed", { error });
1616
+ return false;
1617
+ } finally {
1618
+ this.refreshInFlight = null;
1619
+ }
1620
+ })();
1621
+ return this.refreshInFlight;
1622
+ }
1623
+ /** Resolves once the most recent persistence write/clear has settled. */
1624
+ whenPersisted() {
1625
+ return this.lastPersist;
1626
+ }
1541
1627
  isAuthenticated() {
1542
1628
  const token = useAuthenticationStore.getState().token;
1543
1629
  return !!token && !this.isTokenExpired(token);
@@ -1567,6 +1653,16 @@ var init_authenticationService = __esm({
1567
1653
  return null;
1568
1654
  }
1569
1655
  }
1656
+ mirror(run) {
1657
+ const attempt = (async () => {
1658
+ try {
1659
+ await run();
1660
+ } catch (error) {
1661
+ debugLogger.warn("Auth token persistence failed", { error });
1662
+ }
1663
+ })();
1664
+ this.lastPersist = attempt;
1665
+ }
1570
1666
  };
1571
1667
  authenticationService = new AuthenticationService();
1572
1668
  }
@@ -1622,13 +1718,35 @@ var init_gateway_service = __esm({
1622
1718
  });
1623
1719
  instance.interceptors.response.use(
1624
1720
  (response) => response,
1625
- (error) => {
1721
+ async (error) => {
1722
+ const retried = await this._retryAfterRefresh(instance, error);
1723
+ if (retried) return retried;
1626
1724
  this._handleAuthFailure(error);
1627
1725
  return Promise.reject(this._normalizeAxiosError(error));
1628
1726
  }
1629
1727
  );
1630
1728
  return instance;
1631
1729
  }
1730
+ /**
1731
+ * A bare 401 on a host with a silent refresh path (HttpOnly refresh cookie,
1732
+ * native device token) usually means the access token aged out mid-session.
1733
+ * Refresh once and replay the request with the new token instead of
1734
+ * signing the user out; a failed refresh falls through to the sign-out path.
1735
+ */
1736
+ async _retryAfterRefresh(instance, error) {
1737
+ const config = error.config;
1738
+ if (!config || config._banditAuthRetried || error.response?.status !== 401) return null;
1739
+ if (!authenticationService.canRefresh()) return null;
1740
+ const data = error.response?.data;
1741
+ if (data?.code && data.code !== "TOKEN_EXPIRED") return null;
1742
+ try {
1743
+ if (!await authenticationService.tryRefresh()) return null;
1744
+ } catch {
1745
+ return null;
1746
+ }
1747
+ config._banditAuthRetried = true;
1748
+ return instance.request(config);
1749
+ }
1632
1750
  _normalizeAxiosError(error) {
1633
1751
  if (error.response) {
1634
1752
  return this._createHttpError(
@@ -7159,6 +7277,7 @@ var import_react5 = require("react");
7159
7277
  // src/contexts/FeatureFlagContext.tsx
7160
7278
  var import_react4 = require("react");
7161
7279
  init_debugLogger();
7280
+ init_authenticationService();
7162
7281
  var import_jsx_runtime4 = require("react/jsx-runtime");
7163
7282
  var FeatureFlagContext = (0, import_react4.createContext)(void 0);
7164
7283