@burtson-labs/bandit-engine 2.0.165 → 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 +175 -109
- package/dist/chat-provider.js.map +1 -1
- package/dist/chat-provider.mjs +6 -7
- package/dist/{chunk-FEIDGXVR.mjs → chunk-33OSALGS.mjs} +24 -6
- package/dist/{chunk-FEIDGXVR.mjs.map → chunk-33OSALGS.mjs.map} +1 -1
- package/dist/{chunk-LKGBO5OP.mjs → chunk-6KRAPGXE.mjs} +12 -84
- package/dist/chunk-6KRAPGXE.mjs.map +1 -0
- package/dist/{chunk-6VDL4SLX.mjs → chunk-75OBTJPN.mjs} +5 -5
- package/dist/{chunk-QHGGV4KJ.mjs → chunk-A6W6XIDD.mjs} +6 -6
- package/dist/{chunk-J6LSSK3G.mjs → chunk-GRPTCHFN.mjs} +149 -41
- package/dist/chunk-GRPTCHFN.mjs.map +1 -0
- package/dist/{chunk-ASWUEAWM.mjs → chunk-IB2LPSQQ.mjs} +2 -2
- package/dist/{chunk-RNCASZ2Y.mjs → chunk-M4DWPNZZ.mjs} +10 -10
- package/dist/{chunk-NGGTQVGX.mjs → chunk-MM3NRLVR.mjs} +3 -3
- package/dist/{chunk-RLW4RBIR.mjs → chunk-Q2DINRIU.mjs} +4 -4
- package/dist/{chunk-WSUUW7T2.mjs → chunk-WSZE3DDJ.mjs} +7 -7
- package/dist/{chunk-2EM4ATKI.mjs → chunk-YH5YWK4K.mjs} +2 -2
- package/dist/index.d.mts +92 -1
- package/dist/index.d.ts +92 -1
- package/dist/index.js +188 -130
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +21 -20
- package/dist/index.mjs.map +1 -1
- package/dist/management/management.js +159 -120
- package/dist/management/management.js.map +1 -1
- package/dist/management/management.mjs +9 -10
- 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 +149 -30
- package/dist/modals/chat-modal/chat-modal.js.map +1 -1
- package/dist/modals/chat-modal/chat-modal.mjs +5 -6
- 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-M4BVEKUB.mjs +0 -9
- package/dist/chat-EMKHB23B.mjs +0 -16
- package/dist/chunk-BJTO5JO5.mjs +0 -11
- package/dist/chunk-J6LSSK3G.mjs.map +0 -1
- package/dist/chunk-LKGBO5OP.mjs.map +0 -1
- package/dist/modelStore-7YYELPF6.mjs +0 -9
- package/dist/modelStore-7YYELPF6.mjs.map +0 -1
- /package/dist/{aiProviderStore-M4BVEKUB.mjs.map → aiProviderStore-HY5G4QAK.mjs.map} +0 -0
- /package/dist/{chat-EMKHB23B.mjs.map → chat-RCMZYACB.mjs.map} +0 -0
- /package/dist/{chunk-6VDL4SLX.mjs.map → chunk-75OBTJPN.mjs.map} +0 -0
- /package/dist/{chunk-QHGGV4KJ.mjs.map → chunk-A6W6XIDD.mjs.map} +0 -0
- /package/dist/{chunk-ASWUEAWM.mjs.map → chunk-IB2LPSQQ.mjs.map} +0 -0
- /package/dist/{chunk-RNCASZ2Y.mjs.map → chunk-M4DWPNZZ.mjs.map} +0 -0
- /package/dist/{chunk-NGGTQVGX.mjs.map → chunk-MM3NRLVR.mjs.map} +0 -0
- /package/dist/{chunk-RLW4RBIR.mjs.map → chunk-Q2DINRIU.mjs.map} +0 -0
- /package/dist/{chunk-WSUUW7T2.mjs.map → chunk-WSZE3DDJ.mjs.map} +0 -0
- /package/dist/{chunk-2EM4ATKI.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-
|
|
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
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-
|
|
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
|
-
|
|
1460
|
-
user = null;
|
|
1461
|
-
rawToken = localStorage.getItem(TOKEN_KEY);
|
|
1462
|
-
if (rawToken) {
|
|
1459
|
+
decodeClaims = (token) => {
|
|
1463
1460
|
try {
|
|
1464
|
-
const base64Url =
|
|
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
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
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
|
-
|
|
1529
|
-
|
|
1570
|
+
if (this.storageMode === "memory") {
|
|
1571
|
+
return useAuthenticationStore.getState().token;
|
|
1572
|
+
}
|
|
1573
|
+
return readLocalStorage();
|
|
1530
1574
|
}
|
|
1531
1575
|
setToken(token) {
|
|
1532
|
-
|
|
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
|
-
|
|
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
|
|