@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.
- package/README.md +4 -1
- package/dist/aiProviderStore-HY5G4QAK.mjs +8 -0
- package/dist/chat-RCMZYACB.mjs +15 -0
- package/dist/chat-provider.js +186 -111
- package/dist/chat-provider.js.map +1 -1
- package/dist/chat-provider.mjs +7 -8
- package/dist/{chunk-J6SWNO7Z.mjs → chunk-33OSALGS.mjs} +25 -7
- package/dist/{chunk-J6SWNO7Z.mjs.map → chunk-33OSALGS.mjs.map} +1 -1
- package/dist/{chunk-5KBTOCQZ.mjs → chunk-6KRAPGXE.mjs} +13 -85
- package/dist/chunk-6KRAPGXE.mjs.map +1 -0
- package/dist/{chunk-VBHI4TJC.mjs → chunk-75OBTJPN.mjs} +6 -6
- package/dist/{chunk-URZSIL7X.mjs → chunk-A6W6XIDD.mjs} +7 -7
- package/dist/{chunk-CQJGUYRW.mjs → chunk-GRPTCHFN.mjs} +150 -42
- package/dist/chunk-GRPTCHFN.mjs.map +1 -0
- package/dist/{chunk-OCVZVBQQ.mjs → chunk-HNXPDIWC.mjs} +12 -3
- package/dist/chunk-HNXPDIWC.mjs.map +1 -0
- package/dist/{chunk-IZX2J65Q.mjs → chunk-IB2LPSQQ.mjs} +2 -2
- package/dist/{chunk-PNQRS5EN.mjs → chunk-M4DWPNZZ.mjs} +11 -11
- package/dist/{chunk-O6P37PFR.mjs → chunk-MM3NRLVR.mjs} +4 -4
- package/dist/{chunk-WLNZL6SP.mjs → chunk-Q2DINRIU.mjs} +6 -6
- package/dist/{chunk-WLNZL6SP.mjs.map → chunk-Q2DINRIU.mjs.map} +1 -1
- package/dist/{chunk-JYBQAKSZ.mjs → chunk-WSZE3DDJ.mjs} +8 -8
- package/dist/{chunk-TY5VL3SG.mjs → chunk-YH5YWK4K.mjs} +3 -3
- package/dist/index.d.mts +97 -4
- package/dist/index.d.ts +97 -4
- package/dist/index.js +200 -133
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +22 -21
- package/dist/index.mjs.map +1 -1
- package/dist/management/management.js +171 -123
- package/dist/management/management.js.map +1 -1
- package/dist/management/management.mjs +10 -11
- package/dist/{memoryUtils-2FVXDSS6.mjs → memoryUtils-HGCYEUCD.mjs} +1 -3
- package/dist/{memoryUtils-2FVXDSS6.mjs.map → memoryUtils-HGCYEUCD.mjs.map} +1 -1
- package/dist/modals/chat-modal/chat-modal.js +161 -33
- package/dist/modals/chat-modal/chat-modal.js.map +1 -1
- package/dist/modals/chat-modal/chat-modal.mjs +6 -7
- package/dist/modelStore-F6JQUXGG.mjs +8 -0
- package/dist/shared/custom-element.mjs +0 -1
- package/docs/03_provider_integration.md +5 -2
- package/package.json +1 -1
- package/dist/aiProviderStore-PEGSX4PK.mjs +0 -9
- package/dist/chat-OSAVW6L2.mjs +0 -16
- package/dist/chunk-5KBTOCQZ.mjs.map +0 -1
- package/dist/chunk-BJTO5JO5.mjs +0 -11
- package/dist/chunk-CQJGUYRW.mjs.map +0 -1
- package/dist/chunk-OCVZVBQQ.mjs.map +0 -1
- package/dist/modelStore-7XYYDCNC.mjs +0 -9
- package/dist/modelStore-7XYYDCNC.mjs.map +0 -1
- /package/dist/{aiProviderStore-PEGSX4PK.mjs.map → aiProviderStore-HY5G4QAK.mjs.map} +0 -0
- /package/dist/{chat-OSAVW6L2.mjs.map → chat-RCMZYACB.mjs.map} +0 -0
- /package/dist/{chunk-VBHI4TJC.mjs.map → chunk-75OBTJPN.mjs.map} +0 -0
- /package/dist/{chunk-URZSIL7X.mjs.map → chunk-A6W6XIDD.mjs.map} +0 -0
- /package/dist/{chunk-IZX2J65Q.mjs.map → chunk-IB2LPSQQ.mjs.map} +0 -0
- /package/dist/{chunk-PNQRS5EN.mjs.map → chunk-M4DWPNZZ.mjs.map} +0 -0
- /package/dist/{chunk-O6P37PFR.mjs.map → chunk-MM3NRLVR.mjs.map} +0 -0
- /package/dist/{chunk-JYBQAKSZ.mjs.map → chunk-WSZE3DDJ.mjs.map} +0 -0
- /package/dist/{chunk-TY5VL3SG.mjs.map → chunk-YH5YWK4K.mjs.map} +0 -0
- /package/dist/{chunk-BJTO5JO5.mjs.map → modelStore-F6JQUXGG.mjs.map} +0 -0
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
management_default
|
|
3
|
-
} from "../chunk-
|
|
4
|
-
import "../chunk-
|
|
5
|
-
import "../chunk-
|
|
6
|
-
import "../chunk-
|
|
7
|
-
import "../chunk-
|
|
8
|
-
import "../chunk-
|
|
9
|
-
import "../chunk-
|
|
10
|
-
import "../chunk-
|
|
11
|
-
import "../chunk-
|
|
12
|
-
import "../chunk-
|
|
13
|
-
import "../chunk-BJTO5JO5.mjs";
|
|
3
|
+
} from "../chunk-M4DWPNZZ.mjs";
|
|
4
|
+
import "../chunk-IB2LPSQQ.mjs";
|
|
5
|
+
import "../chunk-6KRAPGXE.mjs";
|
|
6
|
+
import "../chunk-MM3NRLVR.mjs";
|
|
7
|
+
import "../chunk-75OBTJPN.mjs";
|
|
8
|
+
import "../chunk-Q2DINRIU.mjs";
|
|
9
|
+
import "../chunk-33OSALGS.mjs";
|
|
10
|
+
import "../chunk-YH5YWK4K.mjs";
|
|
11
|
+
import "../chunk-GRPTCHFN.mjs";
|
|
12
|
+
import "../chunk-HNXPDIWC.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-
|
|
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":[]}
|
|
@@ -72,11 +72,20 @@ var init_debugLogger = __esm({
|
|
|
72
72
|
*/
|
|
73
73
|
debug(message, context) {
|
|
74
74
|
}
|
|
75
|
+
verboseEnabled() {
|
|
76
|
+
if (this.isDevelopment) return true;
|
|
77
|
+
try {
|
|
78
|
+
return typeof localStorage !== "undefined" && localStorage.getItem("bandit:debug") === "1";
|
|
79
|
+
} catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
75
83
|
/**
|
|
76
|
-
* Info logs - shown in
|
|
77
|
-
*
|
|
84
|
+
* Info logs - shown in development, or in production when localStorage
|
|
85
|
+
* `bandit:debug` is "1"
|
|
78
86
|
*/
|
|
79
87
|
info(message, context) {
|
|
88
|
+
if (!this.verboseEnabled()) return;
|
|
80
89
|
const prefix = this.isDevelopment ? "\u2139\uFE0F [INFO]" : "[INFO]";
|
|
81
90
|
if (context) {
|
|
82
91
|
console.info(`${prefix} ${message}`, context);
|
|
@@ -1441,31 +1450,42 @@ var init_anthropic_provider = __esm({
|
|
|
1441
1450
|
});
|
|
1442
1451
|
|
|
1443
1452
|
// src/store/authenticationStore.ts
|
|
1444
|
-
var import_zustand2, TOKEN_KEY, validToken, user, rawToken, useAuthenticationStore;
|
|
1453
|
+
var import_zustand2, TOKEN_KEY, decodeClaims, validOrNull, readLegacyStorage, validToken, user, rawToken, useAuthenticationStore;
|
|
1445
1454
|
var init_authenticationStore = __esm({
|
|
1446
1455
|
"src/store/authenticationStore.ts"() {
|
|
1447
1456
|
"use strict";
|
|
1448
1457
|
import_zustand2 = require("zustand");
|
|
1449
1458
|
TOKEN_KEY = "authToken";
|
|
1450
|
-
|
|
1451
|
-
user = null;
|
|
1452
|
-
rawToken = localStorage.getItem(TOKEN_KEY);
|
|
1453
|
-
if (rawToken) {
|
|
1459
|
+
decodeClaims = (token) => {
|
|
1454
1460
|
try {
|
|
1455
|
-
const base64Url =
|
|
1461
|
+
const base64Url = token.split(".")[1];
|
|
1456
1462
|
const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
|
|
1457
1463
|
const jsonPayload = decodeURIComponent(
|
|
1458
1464
|
atob(base64).split("").map((c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)).join("")
|
|
1459
1465
|
);
|
|
1460
|
-
|
|
1461
|
-
if (decoded.exp * 1e3 > Date.now()) {
|
|
1462
|
-
validToken = rawToken;
|
|
1463
|
-
user = decoded;
|
|
1464
|
-
}
|
|
1466
|
+
return JSON.parse(jsonPayload);
|
|
1465
1467
|
} catch {
|
|
1466
|
-
|
|
1467
|
-
|
|
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);
|
|
1479
|
+
} catch {
|
|
1480
|
+
return null;
|
|
1468
1481
|
}
|
|
1482
|
+
};
|
|
1483
|
+
validToken = null;
|
|
1484
|
+
user = null;
|
|
1485
|
+
rawToken = readLegacyStorage();
|
|
1486
|
+
if (rawToken) {
|
|
1487
|
+
validToken = validOrNull(rawToken);
|
|
1488
|
+
user = validToken ? decodeClaims(validToken) : null;
|
|
1469
1489
|
}
|
|
1470
1490
|
useAuthenticationStore = (0, import_zustand2.create)((set) => ({
|
|
1471
1491
|
token: validToken,
|
|
@@ -1473,16 +1493,10 @@ var init_authenticationStore = __esm({
|
|
|
1473
1493
|
authError: null,
|
|
1474
1494
|
setToken: (token) => {
|
|
1475
1495
|
if (token) {
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
atob(base64).split("").map((c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2)).join("")
|
|
1481
|
-
);
|
|
1482
|
-
const user2 = JSON.parse(jsonPayload);
|
|
1483
|
-
localStorage.setItem(TOKEN_KEY, token);
|
|
1484
|
-
set({ token, authError: null, user: user2 });
|
|
1485
|
-
} catch {
|
|
1496
|
+
const decoded = decodeClaims(token);
|
|
1497
|
+
if (decoded) {
|
|
1498
|
+
set({ token, authError: null, user: decoded });
|
|
1499
|
+
} else {
|
|
1486
1500
|
set({ token: null, authError: "Invalid token", user: null });
|
|
1487
1501
|
}
|
|
1488
1502
|
} else {
|
|
@@ -1490,7 +1504,6 @@ var init_authenticationStore = __esm({
|
|
|
1490
1504
|
}
|
|
1491
1505
|
},
|
|
1492
1506
|
clearToken: () => {
|
|
1493
|
-
localStorage.removeItem(TOKEN_KEY);
|
|
1494
1507
|
set({ token: null, authError: null, user: null });
|
|
1495
1508
|
}
|
|
1496
1509
|
}));
|
|
@@ -1506,7 +1519,7 @@ function emitAuthTokenChanged(token) {
|
|
|
1506
1519
|
detail: { token }
|
|
1507
1520
|
}));
|
|
1508
1521
|
}
|
|
1509
|
-
var TOKEN_KEY2, AUTH_TOKEN_CHANGED_EVENT, AuthenticationService, authenticationService;
|
|
1522
|
+
var TOKEN_KEY2, AUTH_TOKEN_CHANGED_EVENT, readLocalStorage, writeLocalStorage, AuthenticationService, authenticationService;
|
|
1510
1523
|
var init_authenticationService = __esm({
|
|
1511
1524
|
"src/services/auth/authenticationService.ts"() {
|
|
1512
1525
|
"use strict";
|
|
@@ -1514,21 +1527,103 @@ var init_authenticationService = __esm({
|
|
|
1514
1527
|
init_debugLogger();
|
|
1515
1528
|
TOKEN_KEY2 = "authToken";
|
|
1516
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
|
+
};
|
|
1517
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
|
+
}
|
|
1518
1569
|
getToken() {
|
|
1519
|
-
|
|
1520
|
-
|
|
1570
|
+
if (this.storageMode === "memory") {
|
|
1571
|
+
return useAuthenticationStore.getState().token;
|
|
1572
|
+
}
|
|
1573
|
+
return readLocalStorage();
|
|
1521
1574
|
}
|
|
1522
1575
|
setToken(token) {
|
|
1523
|
-
|
|
1576
|
+
this.revision += 1;
|
|
1577
|
+
if (this.storageMode === "localStorage") {
|
|
1578
|
+
writeLocalStorage(token);
|
|
1579
|
+
} else {
|
|
1580
|
+
writeLocalStorage(null);
|
|
1581
|
+
}
|
|
1524
1582
|
useAuthenticationStore.getState().setToken(token);
|
|
1583
|
+
this.mirror(() => this.persistence?.write(token));
|
|
1525
1584
|
emitAuthTokenChanged(token);
|
|
1526
1585
|
}
|
|
1527
1586
|
clearToken() {
|
|
1528
|
-
|
|
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);
|
|
1529
1595
|
useAuthenticationStore.getState().clearToken();
|
|
1596
|
+
this.mirror(() => this.persistence?.clear());
|
|
1530
1597
|
emitAuthTokenChanged(null);
|
|
1531
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
|
+
}
|
|
1532
1627
|
isAuthenticated() {
|
|
1533
1628
|
const token = useAuthenticationStore.getState().token;
|
|
1534
1629
|
return !!token && !this.isTokenExpired(token);
|
|
@@ -1558,6 +1653,16 @@ var init_authenticationService = __esm({
|
|
|
1558
1653
|
return null;
|
|
1559
1654
|
}
|
|
1560
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
|
+
}
|
|
1561
1666
|
};
|
|
1562
1667
|
authenticationService = new AuthenticationService();
|
|
1563
1668
|
}
|
|
@@ -1613,13 +1718,35 @@ var init_gateway_service = __esm({
|
|
|
1613
1718
|
});
|
|
1614
1719
|
instance.interceptors.response.use(
|
|
1615
1720
|
(response) => response,
|
|
1616
|
-
(error) => {
|
|
1721
|
+
async (error) => {
|
|
1722
|
+
const retried = await this._retryAfterRefresh(instance, error);
|
|
1723
|
+
if (retried) return retried;
|
|
1617
1724
|
this._handleAuthFailure(error);
|
|
1618
1725
|
return Promise.reject(this._normalizeAxiosError(error));
|
|
1619
1726
|
}
|
|
1620
1727
|
);
|
|
1621
1728
|
return instance;
|
|
1622
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
|
+
}
|
|
1623
1750
|
_normalizeAxiosError(error) {
|
|
1624
1751
|
if (error.response) {
|
|
1625
1752
|
return this._createHttpError(
|
|
@@ -7150,6 +7277,7 @@ var import_react5 = require("react");
|
|
|
7150
7277
|
// src/contexts/FeatureFlagContext.tsx
|
|
7151
7278
|
var import_react4 = require("react");
|
|
7152
7279
|
init_debugLogger();
|
|
7280
|
+
init_authenticationService();
|
|
7153
7281
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
7154
7282
|
var FeatureFlagContext = (0, import_react4.createContext)(void 0);
|
|
7155
7283
|
|
|
@@ -11546,7 +11674,7 @@ var VoiceService = class _VoiceService {
|
|
|
11546
11674
|
}
|
|
11547
11675
|
const token = authenticationService.getToken();
|
|
11548
11676
|
if (!token) {
|
|
11549
|
-
debugLogger.
|
|
11677
|
+
debugLogger.info("No JWT token available - skipping voice models fetch");
|
|
11550
11678
|
const emptyModels = {
|
|
11551
11679
|
models: [],
|
|
11552
11680
|
defaultModel: "",
|