tracelattice 1.3.0 → 1.3.2
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 +2 -0
- package/dist/ServerConfig.d.ts +10 -11
- package/dist/ServerConfig.d.ts.map +1 -1
- package/dist/ServerConfig.js +7 -7
- package/dist/ServerConfig.js.map +1 -1
- package/dist/__tests__/core/retraction.test.d.ts +2 -0
- package/dist/__tests__/core/retraction.test.d.ts.map +1 -0
- package/dist/__tests__/helpers/factories.d.ts +2 -0
- package/dist/__tests__/helpers/factories.d.ts.map +1 -1
- package/dist/cli.js +6 -6
- package/dist/core/HistoryManager.d.ts +45 -523
- package/dist/core/HistoryManager.d.ts.map +1 -1
- package/dist/core/HistoryManager.js +101 -249
- package/dist/core/HistoryManager.js.map +1 -1
- package/dist/core/IHistoryManager.d.ts +17 -0
- package/dist/core/IHistoryManager.d.ts.map +1 -1
- package/dist/core/PersistenceBuffer.d.ts +110 -0
- package/dist/core/PersistenceBuffer.d.ts.map +1 -0
- package/dist/core/PersistenceBuffer.js +141 -0
- package/dist/core/PersistenceBuffer.js.map +1 -0
- package/dist/core/SessionManager.d.ts +58 -0
- package/dist/core/SessionManager.d.ts.map +1 -0
- package/dist/core/SessionManager.js +65 -0
- package/dist/core/SessionManager.js.map +1 -0
- package/dist/core/ThoughtEvaluator.d.ts.map +1 -1
- package/dist/core/ThoughtEvaluator.js +16 -4
- package/dist/core/ThoughtEvaluator.js.map +1 -1
- package/dist/core/ThoughtFormatter.d.ts.map +1 -1
- package/dist/core/ThoughtFormatter.js +2 -1
- package/dist/core/ThoughtFormatter.js.map +1 -1
- package/dist/core/ThoughtProcessor.d.ts +18 -0
- package/dist/core/ThoughtProcessor.d.ts.map +1 -1
- package/dist/core/ThoughtProcessor.js +47 -16
- package/dist/core/ThoughtProcessor.js.map +1 -1
- package/dist/core/evaluator/Aggregator.d.ts.map +1 -1
- package/dist/core/evaluator/Aggregator.js +6 -2
- package/dist/core/evaluator/Aggregator.js.map +1 -1
- package/dist/core/evaluator/PatternDetector.js +2 -2
- package/dist/core/evaluator/PatternDetector.js.map +1 -1
- package/dist/core/evaluator/SignalComputer.d.ts +57 -5
- package/dist/core/evaluator/SignalComputer.d.ts.map +1 -1
- package/dist/core/evaluator/SignalComputer.js +52 -10
- package/dist/core/evaluator/SignalComputer.js.map +1 -1
- package/dist/core/graph/EdgeEmitter.d.ts +64 -0
- package/dist/core/graph/EdgeEmitter.d.ts.map +1 -0
- package/dist/core/graph/EdgeEmitter.js +99 -0
- package/dist/core/graph/EdgeEmitter.js.map +1 -0
- package/dist/core/reasoning.d.ts +17 -2
- package/dist/core/reasoning.d.ts.map +1 -1
- package/dist/core/thought.d.ts +7 -0
- package/dist/core/thought.d.ts.map +1 -1
- package/dist/core/tools/InMemorySuspensionStore.js +1 -1
- package/dist/core/tools/InMemorySuspensionStore.js.map +1 -1
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +11 -0
- package/dist/lib.js.map +1 -1
- package/dist/persistence/FilePersistence.d.ts +6 -0
- package/dist/persistence/FilePersistence.d.ts.map +1 -1
- package/dist/persistence/FilePersistence.js +8 -0
- package/dist/persistence/FilePersistence.js.map +1 -1
- package/dist/persistence/MemoryPersistence.d.ts +6 -0
- package/dist/persistence/MemoryPersistence.d.ts.map +1 -1
- package/dist/persistence/MemoryPersistence.js +3 -0
- package/dist/persistence/MemoryPersistence.js.map +1 -1
- package/dist/persistence/PersistenceBackend.d.ts +6 -0
- package/dist/persistence/PersistenceBackend.d.ts.map +1 -1
- package/dist/persistence/SqlitePersistence.d.ts +6 -0
- package/dist/persistence/SqlitePersistence.d.ts.map +1 -1
- package/dist/persistence/SqlitePersistence.js +4 -0
- package/dist/persistence/SqlitePersistence.js.map +1 -1
- package/dist/schema.d.ts +3 -2
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +8 -7
- package/dist/schema.js.map +1 -1
- package/package.json +2 -2
package/dist/lib.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lib.js","sources":["../src/lib.ts"],"sourcesContent":["// Library exports for tracelattice\n// This module contains all public API exports with NO CLI side effects.\n// For the CLI entry point, see cli.ts.\n\nimport { EventEmitter } from 'node:events';\nimport * as v from 'valibot';\nimport { ThoughtData } from './core/thought.js';\nimport { SEQUENTIAL_THINKING_TOOL, SequentialThinkingSchema } from './schema.js';\nimport { IDisposable } from './types/disposable.js';\nimport { getErrorMessage } from './errors.js';\n\n// New component imports\nimport { DiscoveryCache } from './cache/DiscoveryCache.js';\nimport type { ConfigFileOptions } from './config/ConfigLoader.js';\nimport { ConfigLoader } from './config/ConfigLoader.js';\nimport { HistoryManager } from './core/HistoryManager.js';\nimport { EdgeStore } from './core/graph/EdgeStore.js';\nimport { InMemorySummaryStore } from './core/compression/InMemorySummaryStore.js';\nimport { CompressionService } from './core/compression/CompressionService.js';\nimport type { ISummaryStore } from './contracts/summary.js';\nimport { InMemorySuspensionStore } from './core/tools/InMemorySuspensionStore.js';\nimport type { ISuspensionStore } from './contracts/suspension.js';\nimport { ThoughtEvaluator } from './core/ThoughtEvaluator.js';\nimport { Calibrator } from './core/evaluator/Calibrator.js';\nimport { OutcomeRecorder } from './core/reasoning/OutcomeRecorder.js';\nimport { createReasoningStrategy } from './core/reasoning/strategies/StrategyFactory.js';\nimport { ThoughtFormatter } from './core/ThoughtFormatter.js';\nimport { ThoughtProcessor } from './core/ThoughtProcessor.js';\nimport { Container } from './di/Container.js';\nimport { StructuredLogger } from './logger/StructuredLogger.js';\nimport { Metrics } from './metrics/metrics.impl.js';\nimport type { PersistenceBackend } from './persistence/PersistenceBackend.js';\nimport { createPersistenceBackend } from './persistence/PersistenceFactory.js';\nimport { SkillRegistry } from './registry/SkillRegistry.js';\nimport { ToolRegistry } from './registry/ToolRegistry.js';\nimport { ServerConfig } from './ServerConfig.js';\nimport type { SseTransportOptions } from './transport/SseTransport.js';\nimport { SkillWatcher } from './watchers/SkillWatcher.js';\nimport { ToolWatcher } from './watchers/ToolWatcher.js';\nimport type { IReasoningStrategy } from './contracts/strategy.js';\n\nexport interface ServerOptions {\n\tmaxHistorySize?: number;\n\tmaxBranches?: number;\n\tmaxBranchSize?: number;\n\tlogger?: StructuredLogger;\n\tenableWatcher?: boolean;\n\tconfig?: ServerConfig;\n\tfileConfig?: ConfigFileOptions;\n\tcontainer?: Container;\n\t/**\n\t * Enable automatic skill discovery on server startup\n\t * @default true\n\t */\n\tautoDiscover?: boolean;\n\t/**\n\t * Enable lazy discovery (discover on first access instead of startup)\n\t * @default false\n\t */\n\tlazyDiscovery?: boolean;\n\t/**\n\t * Load history from persistence on initialization\n\t * @default true\n\t */\n\tloadFromPersistence?: boolean;\n\t/**\n\t * Transport type to use\n\t * @default 'stdio'\n\t */\n\ttransport?: 'stdio' | 'sse';\n\t/**\n\t * SSE transport options (used when transport: 'sse')\n\t */\n\tsseTransportOptions?: SseTransportOptions;\n}\n\n/**\n * Server error events for event-driven error handling\n */\ninterface ServerEvents {\n\tpersistenceError: { operation: string; error: Error };\n\tdiscoveryError: { directory: string; error: Error };\n\ttransportError: { transport: string; error: Error };\n\tthoughtProcessed: { thoughtNumber: number; duration: number };\n}\n\nexport class ToolAwareSequentialThinkingServer extends EventEmitter implements IDisposable {\n\t/**\n\t * Factory method to create a new server instance with async initialization.\n\t * This is the recommended way to create server instances.\n\t *\n\t * @param options - Server configuration options\n\t * @returns A Promise that resolves to a configured server instance\n\t */\n\tstatic async create(options: ServerOptions = {}): Promise<ToolAwareSequentialThinkingServer> {\n\t\t// Create the async container first\n\t\tconst container = await ToolAwareSequentialThinkingServer._createContainerAsyncStatic(options);\n\n\t\t// Create a minimal server with the container\n\t\tconst server = new ToolAwareSequentialThinkingServer({\n\t\t\t...options,\n\t\t\tcontainer,\n\t\t});\n\n\t\t// Load from persistence if enabled (default: true)\n\t\tif (options.loadFromPersistence !== false) {\n\t\t\tawait server.history.loadFromPersistence();\n\t\t}\n\n\t\t// Perform async discovery if enabled (default: true)\n\t\tif (options.autoDiscover !== false) {\n\t\t\tawait server.discoverSkillsAsync();\n\t\t}\n\n\t\treturn server;\n\t}\n\n\t// Type-safe event emission\n\toverride emit<K extends keyof ServerEvents>(event: K, payload: ServerEvents[K]): boolean {\n\t\treturn super.emit(event, payload);\n\t}\n\n\toverride on<K extends keyof ServerEvents>(\n\t\tevent: K,\n\t\tlistener: (payload: ServerEvents[K]) => void\n\t): this {\n\t\treturn super.on(event, listener);\n\t}\n\n\t// DI Container for managing dependencies\n\tprivate _container: Container;\n\n\t// Component instances (private)\n\tprivate _logger: StructuredLogger;\n\tprivate _historyManager: HistoryManager;\n\tprivate _thoughtProcessor: ThoughtProcessor;\n\tprivate _metrics: Metrics;\n\tprivate _skillWatcher: SkillWatcher | null = null;\n\tprivate _toolWatcher: ToolWatcher | null = null;\n\tprivate _config: ServerConfig;\n\n\t// Public manager properties (recommended API)\n\t/**\n\t * Direct access to the history manager\n\t * @example\n\t * ```typescript\n\t * server.history.getHistory();\n\t * server.history.clear();\n\t * ```\n\t */\n\tpublic readonly history: HistoryManager;\n\n\t/**\n\t * Direct access to the tool registry\n\t * @example\n\t * ```typescript\n\t * server.tools.addTool(tool);\n\t * server.tools.getTool('my-tool');\n\t * ```\n\t */\n\tpublic readonly tools: ToolRegistry;\n\n\t/**\n\t * Direct access to the skill registry\n\t * @example\n\t * ```typescript\n\t * server.skills.addSkill(skill);\n\t * server.skills.getSkill('my-skill');\n\t * ```\n\t */\n\tpublic readonly skills: SkillRegistry;\n\n\t/**\n\t * Server configuration\n\t * @example\n\t * ```typescript\n\t * console.log(server.config.maxHistorySize);\n\t * ```\n\t */\n\tpublic readonly config: ServerConfig;\n\n\tconstructor(options: ServerOptions = {}) {\n\t\t// Use provided container or create a new one\n\t\tsuper();\n\t\tif (!options.container) {\n\t\t\tthrow new Error('Container is required. Use createServer() or provide a container.');\n\t\t}\n\t\tthis._container = options.container;\n\n\t\t// Resolve dependencies from container\n\t\tthis._logger = this._container.resolve<StructuredLogger>('Logger');\n\t\tthis._historyManager = this._container.resolve<HistoryManager>('HistoryManager');\n\t\tthis._thoughtProcessor = this._container.resolve<ThoughtProcessor>('ThoughtProcessor');\n\t\tthis._metrics = this._container.resolve<Metrics>('Metrics');\n\t\tthis._config = this._container.resolve<ServerConfig>('Config');\n\n\t\t// Expose managers as public properties (recommended API)\n\t\tthis.history = this._historyManager;\n\n\t\t// Wire up persistence error event emitter\n\t\tthis._historyManager.setEventEmitter(this);\n\t\tthis.tools = this._container.resolve<ToolRegistry>('ToolRegistry');\n\t\tthis.skills = this._container.resolve<SkillRegistry>('SkillRegistry');\n\t\tthis.config = this._config;\n\n\t\t// Always include the sequential thinking tool\n\t\tthis.tools.addTool(SEQUENTIAL_THINKING_TOOL);\n\n\n\t\t// Initialize watchers if enabled\n\t\tif (options.enableWatcher) {\n\t\t\tthis._skillWatcher = new SkillWatcher(this.skills);\n\t\t\tthis._toolWatcher = new ToolWatcher(this.tools);\n\t\t}\n\t}\n\n\t/**\n\t * Shared core logic for container creation.\n\t * This method contains all common initialization logic between sync and async paths.\n\t */\n\tprivate static _createContainerCore(\n\t\toptions: ServerOptions,\n\t\tfileConfig: ConfigFileOptions | null,\n\t\tpersistence: PersistenceBackend | null\n\t): Container {\n\t\tconst container = new Container();\n\t\tconst metrics = new Metrics({\n\t\t\tprefix: 'sequentialthinking',\n\t\t});\n\n\t\t// Initialize config with file defaults overridden by constructor options\n\t\tconst config = new ServerConfig({\n\t\t\tmaxHistorySize: options.maxHistorySize ?? fileConfig?.maxHistorySize,\n\t\t\tmaxBranches: options.maxBranches ?? fileConfig?.maxBranches,\n\t\t\tmaxBranchSize: options.maxBranchSize ?? fileConfig?.maxBranchSize,\n\t\t\tskillDirs: fileConfig?.skillDirs,\n\t\t\tdiscoveryCache: fileConfig?.discoveryCache,\n\t\t\tpersistence: fileConfig?.persistence,\n\t\t});\n\n\t\t// Initialize logger\n\t\tconst logger =\n\t\t\toptions.logger ??\n\t\t\tnew StructuredLogger({\n\t\t\t\tlevel: fileConfig?.logLevel ?? 'info',\n\t\t\t\tcontext: 'SequentialThinking',\n\t\t\t\tpretty: fileConfig?.prettyLog ?? true,\n\t\t\t});\n\n\t\t// Register all services in the container\n\t\tcontainer.registerInstance('Logger', logger);\n\t\tcontainer.registerInstance('Config', config);\n\t\tcontainer.registerInstance('FileConfig', fileConfig || {});\n\t\tcontainer.registerInstance('Persistence', persistence);\n\t\tcontainer.registerInstance('Metrics', metrics);\n\t\tcontainer.register(\n\t\t\t'ToolRegistry',\n\t\t\t() =>\n\t\t\t\tnew ToolRegistry({\n\t\t\t\t\tlogger,\n\t\t\t\t\tcache: config.discoveryCache\n\t\t\t\t\t\t? new DiscoveryCache({ ...config.discoveryCache, metrics })\n\t\t\t\t\t\t: undefined,\n\t\t\t\t})\n\t\t);\n\t\tcontainer.register(\n\t\t\t'SkillRegistry',\n\t\t\t() =>\n\t\t\t\tnew SkillRegistry({\n\t\t\t\t\tlogger,\n\t\t\t\t\tcache: config.discoveryCache\n\t\t\t\t\t\t? new DiscoveryCache({ ...config.discoveryCache, metrics })\n\t\t\t\t\t\t: undefined,\n\t\t\t\t\tskillDirs: config.skillDirs,\n\t\t\t\t\tlazyDiscovery: options.lazyDiscovery,\n\t\t\t\t})\n\t\t);\n\n\t\t// Register EdgeStore as a lazy singleton (always registered; flag gates writes)\n\t\tcontainer.register('EdgeStore', () => new EdgeStore());\n\n\t\t// Register SummaryStore as a lazy singleton (always registered; flag gates writes)\n\t\tcontainer.register('summaryStore', () => new InMemorySummaryStore());\n\n\t\t// Register SuspensionStore as a lazy singleton (only when toolInterleave flag is on)\n\t\tif (config.features.toolInterleave) {\n\t\t\tcontainer.register('suspensionStore', () => {\n\t\t\t\tconst store = new InMemorySuspensionStore({\n\t\t\t\t\tttlMs: config.toolInterleaveTtlMs,\n\t\t\t\t\tsweepIntervalMs: config.toolInterleaveSweepMs,\n\t\t\t\t\tlogger,\n\t\t\t\t});\n\t\t\t\tstore.start();\n\t\t\t\treturn store;\n\t\t\t});\n\t\t}\n\n\t\t// Register CompressionService as a lazy singleton (always registered; flag gates invocation)\n\t\tcontainer.register('compressionService', () => {\n\t\t\tconst historyManager = container.resolve<HistoryManager>('HistoryManager');\n\t\t\tconst edgeStore = container.resolve<EdgeStore>('EdgeStore');\n\t\t\tconst summaryStore = container.resolve<ISummaryStore>('summaryStore');\n\t\t\tconst log = container.resolve<StructuredLogger>('Logger');\n\t\t\treturn new CompressionService({ historyManager, edgeStore, summaryStore, logger: log });\n\t\t});\n\n\t\t// Register ReasoningStrategy as a lazy singleton (selected via feature flag)\n\t\tcontainer.register('reasoningStrategy', () =>\n\t\t\tcreateReasoningStrategy(config.features.reasoningStrategy),\n\t\t);\n\n\t\t// Register HistoryManager with lazy initialization\n\t\tcontainer.register('HistoryManager', () => {\n\t\t\tconst cfg = container.resolve<ServerConfig>('Config');\n\t\t\tconst log = container.resolve<StructuredLogger>('Logger');\n\t\t\tconst pers = container.resolve('Persistence') as PersistenceBackend | null;\n\t\t\tconst componentMetrics = container.resolve<Metrics>('Metrics');\n\t\t\tconst edgeStore = container.resolve<EdgeStore>('EdgeStore');\n\t\t\treturn new HistoryManager({\n\t\t\t\tmaxHistorySize: cfg.maxHistorySize,\n\t\t\t\tmaxBranches: cfg.maxBranches,\n\t\t\t\tmaxBranchSize: cfg.maxBranchSize,\n\t\t\t\tlogger: log,\n\t\t\t\tpersistence: pers,\n\t\t\t\tmetrics: componentMetrics,\n\t\t\t\tpersistenceBufferSize: cfg.persistenceBufferSize,\n\t\t\t\tpersistenceFlushInterval: cfg.persistenceFlushInterval,\n\t\t\t\tpersistenceMaxRetries: cfg.persistenceMaxRetries,\n\t\t\t\tedgeStore,\n\t\t\t});\n\t\t});\n\n\t\t// Register ThoughtFormatter (can be transient)\n\t\tcontainer.registerFactory('ThoughtFormatter', () => new ThoughtFormatter());\n\n\t\t// Register OutcomeRecorder as a lazy singleton (gated by feature flag)\n\t\tcontainer.register(\n\t\t\t'outcomeRecorder',\n\t\t\t() => new OutcomeRecorder({ enabled: config.features.outcomeRecording ?? false }),\n\t\t);\n\n\t\t// Register Calibrator as a lazy singleton (gated by feature flag)\n\t\tcontainer.register(\n\t\t\t'calibrator',\n\t\t\t() =>\n\t\t\t\tnew Calibrator(\n\t\t\t\t\tcontainer.resolve('outcomeRecorder'),\n\t\t\t\t\tconfig.features.calibration ?? false,\n\t\t\t\t),\n\t\t);\n\n\t\t// Register ThoughtEvaluator (stateless, transient) with injected calibrator\n\t\tcontainer.registerFactory(\n\t\t\t'ThoughtEvaluator',\n\t\t\t() => new ThoughtEvaluator(container.resolve('calibrator')),\n\t\t);\n\n\t\t// Register ThoughtProcessor\n\t\tcontainer.register('ThoughtProcessor', () => {\n\t\t\tconst history = container.resolve<HistoryManager>('HistoryManager');\n\t\t\tconst formatter = container.resolve<ThoughtFormatter>('ThoughtFormatter');\n\t\t\tconst evaluator = container.resolve<ThoughtEvaluator>('ThoughtEvaluator');\n\t\t\tconst log = container.resolve<StructuredLogger>('Logger');\n\t\t\tconst strategy = container.resolve<IReasoningStrategy>('reasoningStrategy');\n\t\t\tconst compressionService = config.features.compression\n\t\t\t\t? container.resolve<CompressionService>('compressionService')\n\t\t\t\t: undefined;\n\t\t\tconst suspensionStore = config.features.toolInterleave\n\t\t\t\t? container.resolve<ISuspensionStore>('suspensionStore')\n\t\t\t\t: undefined;\n\t\t\treturn new ThoughtProcessor(\n\t\t\t\thistory,\n\t\t\t\tformatter,\n\t\t\t\tevaluator,\n\t\t\t\tlog,\n\t\t\t\tstrategy,\n\t\t\t\tcompressionService,\n\t\t\t\tsuspensionStore,\n\t\t\t\tconfig.features,\n\t\t\t);\n\t\t});\n\n\t\treturn container;\n\t}\n\n\n\t/**\n\t * Create and configure the DI container with async persistence initialization.\n\t * This is used internally by the static create() factory.\n\t */\n\tprivate static async _createContainerAsyncStatic(options: ServerOptions): Promise<Container> {\n\t\tconst configLoader = new ConfigLoader();\n\t\tconst fileConfig = configLoader.load();\n\n\t\t// Initialize persistence backend (async)\n\t\tconst persistence = await createPersistenceBackend(\n\t\t\tfileConfig?.persistence ?? { enabled: false }\n\t\t);\n\n\t\treturn ToolAwareSequentialThinkingServer._createContainerCore(options, fileConfig, persistence);\n\t}\n\n\t/**\n\t * Get the DI container used by this server\n\t * Useful for testing and advanced customizations\n\t */\n\tpublic getContainer(): Container {\n\t\treturn this._container;\n\t}\n\n\t/**\n\t * Discover skills asynchronously without blocking server startup.\n\t * This is the recommended method for skill discovery.\n\t * @returns Promise<number> - The number of skills discovered\n\t */\n\tpublic async discoverSkillsAsync(): Promise<number> {\n\t\tconst discovered = await this.skills.discoverAsync();\n\t\treturn discovered;\n\t}\n\n\t/**\n\t * Get all branches from the history manager\n\t * @returns Record<string, ThoughtData[]> - Map of branch IDs to thought arrays\n\t */\n\tpublic getBranches(): Record<string, ThoughtData[]> {\n\t\treturn this._historyManager.getBranches();\n\t}\n\n\t// Main processing method - delegate to ThoughtProcessor\n\tpublic async processThought(input: v.InferInput<typeof SequentialThinkingSchema>) {\n\t\tconst startTime = Date.now();\n\t\tconst thoughtInput = input as ThoughtData;\n\t\tconst result = await this._thoughtProcessor.process(thoughtInput);\n\t\tconst durationSeconds = (Date.now() - startTime) / 1000;\n\t\tthis._metrics.histogram('thought_processing_duration_seconds', durationSeconds, {});\n\t\treturn result;\n\t}\n\n\tpublic getMetricsSnapshot(): string {\n\t\treturn this._metrics.export();\n\t}\n\n\t/**\n\t * Stop the server and clean up watchers.\n\t * Closes persistence backend gracefully to ensure data is flushed.\n\t */\n\tpublic async stop(): Promise<void> {\n\t\tthis._skillWatcher?.stop();\n\t\tthis._toolWatcher?.stop();\n\n\t\t// Stop suspension store sweeper if registered\n\t\tif (this._config.features.toolInterleave && this._container.has('suspensionStore')) {\n\t\t\ttry {\n\t\t\t\tconst suspensionStore = this._container.resolve<ISuspensionStore>('suspensionStore');\n\t\t\t\tsuspensionStore.stop();\n\t\t\t} catch (error) {\n\t\t\t\tthis._logger.error('Error stopping suspension store', {\n\t\t\t\t\terror: getErrorMessage(error),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t// Flush any buffered writes before closing persistence\n\t\ttry {\n\t\t\tawait this._historyManager.shutdown();\n\t\t} catch (error) {\n\t\t\tthis._logger.error('Error flushing write buffer during shutdown', {\n\t\t\t\terror: getErrorMessage(error),\n\t\t\t});\n\t\t}\n\n\t\t// Close persistence backend if available\n\t\tconst persistence = this._container.resolve<PersistenceBackend | null>('Persistence');\n\t\tif (persistence) {\n\t\t\ttry {\n\t\t\t\tawait persistence.close();\n\t\t\t\tthis._logger.info('Persistence backend closed');\n\t\t\t} catch (error) {\n\t\t\t\tthis._logger.error('Error closing persistence backend', {\n\t\t\t\t\terror: getErrorMessage(error),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tthis._logger.info('Server stopped, watchers cleaned up');\n\t}\n\n\t/**\n\t * Clear all server state (history, tools, skills)\n\t * Useful for testing to reset state between tests\n\t */\n\tpublic clear(): void {\n\t\tthis._historyManager.clear();\n\t\tthis._logger.info('Server state cleared');\n\t}\n\n\t/**\n\t * Dispose of the server and all container services.\n\t * Implements the IDisposable interface.\n\t * Calls stop() for existing cleanup, then disposes the DI container.\n\t */\n\tpublic async dispose(): Promise<void> {\n\t\tawait this.stop();\n\t\tawait this._container.dispose();\n\t\tthis._logger.info('Server disposed, all resources released');\n\t}\n}\n\n/**\n * Factory function to create a new server instance with async initialization.\n *\n * This is the recommended way to create server instances, especially for testing,\n * as it allows for proper async initialization, dependency injection, and persistence.\n *\n * @param options - Server configuration options\n * @returns A Promise that resolves to a configured server instance\n *\n * @example\n * ```typescript\n * // Basic usage (with async discovery and persistence)\n * const server = await createServer();\n *\n * // With custom options\n * const server = await createServer({\n * autoDiscover: false,\n * lazyDiscovery: true,\n * maxHistorySize: 500,\n * loadFromPersistence: true\n * });\n *\n * // With custom container for testing\n * const mockContainer = new Container();\n * mockContainer.registerInstance('Logger', mockLogger);\n * const server = await createServer({ container: mockContainer });\n * ```\n */\nexport async function createServer(\n\toptions: ServerOptions = {}\n): Promise<ToolAwareSequentialThinkingServer> {\n\treturn ToolAwareSequentialThinkingServer.create(options);\n}\n\n// Initialize server\nexport async function initializeServer(): Promise<ToolAwareSequentialThinkingServer> {\n\t// Create logger for initialization\n\tconst configLoader = new ConfigLoader();\n\tconst fileConfig = configLoader.load();\n\n\tconst logger = new StructuredLogger({\n\t\tlevel: fileConfig?.logLevel ?? 'info',\n\t\tcontext: 'SequentialThinking',\n\t\tpretty: fileConfig?.prettyLog ?? true,\n\t});\n\n\t// Create server instance\n\tconst thinkingServer = await createServer({\n\t\tlogger,\n\t\tenableWatcher: true,\n\t});\n\n\tlogger.info('Server initialized successfully');\n\treturn thinkingServer;\n}\n"],"names":["ToolAwareSequentialThinkingServer","EventEmitter","options","container","server","event","payload","listener","Error","SEQUENTIAL_THINKING_TOOL","SkillWatcher","ToolWatcher","fileConfig","persistence","Container","metrics","Metrics","config","ServerConfig","logger","StructuredLogger","ToolRegistry","DiscoveryCache","undefined","SkillRegistry","EdgeStore","InMemorySummaryStore","store","InMemorySuspensionStore","historyManager","edgeStore","summaryStore","log","CompressionService","createReasoningStrategy","cfg","pers","componentMetrics","HistoryManager","ThoughtFormatter","OutcomeRecorder","Calibrator","ThoughtEvaluator","history","formatter","evaluator","strategy","compressionService","suspensionStore","ThoughtProcessor","configLoader","ConfigLoader","createPersistenceBackend","discovered","input","startTime","Date","thoughtInput","result","durationSeconds","error","getErrorMessage","createServer","initializeServer","thinkingServer"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lib.js","sources":["../src/lib.ts"],"sourcesContent":["// Library exports for tracelattice\n// This module contains all public API exports with NO CLI side effects.\n// For the CLI entry point, see cli.ts.\n\nimport { EventEmitter } from 'node:events';\nimport * as v from 'valibot';\nimport { ThoughtData } from './core/thought.js';\nimport { SEQUENTIAL_THINKING_TOOL, SequentialThinkingSchema } from './schema.js';\nimport { IDisposable } from './types/disposable.js';\nimport { getErrorMessage } from './errors.js';\n\n// New component imports\nimport { DiscoveryCache } from './cache/DiscoveryCache.js';\nimport type { ConfigFileOptions } from './config/ConfigLoader.js';\nimport { ConfigLoader } from './config/ConfigLoader.js';\nimport { HistoryManager } from './core/HistoryManager.js';\nimport { EdgeStore } from './core/graph/EdgeStore.js';\nimport { InMemorySummaryStore } from './core/compression/InMemorySummaryStore.js';\nimport { CompressionService } from './core/compression/CompressionService.js';\nimport type { ISummaryStore } from './contracts/summary.js';\nimport { InMemorySuspensionStore } from './core/tools/InMemorySuspensionStore.js';\nimport type { ISuspensionStore } from './contracts/suspension.js';\nimport { ThoughtEvaluator } from './core/ThoughtEvaluator.js';\nimport { Calibrator } from './core/evaluator/Calibrator.js';\nimport { OutcomeRecorder } from './core/reasoning/OutcomeRecorder.js';\nimport { createReasoningStrategy } from './core/reasoning/strategies/StrategyFactory.js';\nimport { ThoughtFormatter } from './core/ThoughtFormatter.js';\nimport { ThoughtProcessor } from './core/ThoughtProcessor.js';\nimport { Container } from './di/Container.js';\nimport { StructuredLogger } from './logger/StructuredLogger.js';\nimport { Metrics } from './metrics/metrics.impl.js';\nimport type { PersistenceBackend } from './persistence/PersistenceBackend.js';\nimport { createPersistenceBackend } from './persistence/PersistenceFactory.js';\nimport { SkillRegistry } from './registry/SkillRegistry.js';\nimport { ToolRegistry } from './registry/ToolRegistry.js';\nimport { ServerConfig } from './ServerConfig.js';\nimport type { SseTransportOptions } from './transport/SseTransport.js';\nimport { SkillWatcher } from './watchers/SkillWatcher.js';\nimport { ToolWatcher } from './watchers/ToolWatcher.js';\nimport type { IReasoningStrategy } from './contracts/strategy.js';\n\nexport interface ServerOptions {\n\tmaxHistorySize?: number;\n\tmaxBranches?: number;\n\tmaxBranchSize?: number;\n\tlogger?: StructuredLogger;\n\tenableWatcher?: boolean;\n\tconfig?: ServerConfig;\n\tfileConfig?: ConfigFileOptions;\n\tcontainer?: Container;\n\t/**\n\t * Enable automatic skill discovery on server startup\n\t * @default true\n\t */\n\tautoDiscover?: boolean;\n\t/**\n\t * Enable lazy discovery (discover on first access instead of startup)\n\t * @default false\n\t */\n\tlazyDiscovery?: boolean;\n\t/**\n\t * Load history from persistence on initialization\n\t * @default true\n\t */\n\tloadFromPersistence?: boolean;\n\t/**\n\t * Transport type to use\n\t * @default 'stdio'\n\t */\n\ttransport?: 'stdio' | 'sse';\n\t/**\n\t * SSE transport options (used when transport: 'sse')\n\t */\n\tsseTransportOptions?: SseTransportOptions;\n}\n\n/**\n * Server error events for event-driven error handling\n */\ninterface ServerEvents {\n\tpersistenceError: { operation: string; error: Error };\n\tdiscoveryError: { directory: string; error: Error };\n\ttransportError: { transport: string; error: Error };\n\tthoughtProcessed: { thoughtNumber: number; duration: number };\n}\n\nexport class ToolAwareSequentialThinkingServer extends EventEmitter implements IDisposable {\n\t/**\n\t * Factory method to create a new server instance with async initialization.\n\t * This is the recommended way to create server instances.\n\t *\n\t * @param options - Server configuration options\n\t * @returns A Promise that resolves to a configured server instance\n\t */\n\tstatic async create(options: ServerOptions = {}): Promise<ToolAwareSequentialThinkingServer> {\n\t\t// Create the async container first\n\t\tconst container = await ToolAwareSequentialThinkingServer._createContainerAsyncStatic(options);\n\n\t\t// Create a minimal server with the container\n\t\tconst server = new ToolAwareSequentialThinkingServer({\n\t\t\t...options,\n\t\t\tcontainer,\n\t\t});\n\n\t\t// Load from persistence if enabled (default: true)\n\t\tif (options.loadFromPersistence !== false) {\n\t\t\tawait server.history.loadFromPersistence();\n\t\t}\n\n\t\t// Perform async discovery if enabled (default: true)\n\t\tif (options.autoDiscover !== false) {\n\t\t\tawait server.discoverSkillsAsync();\n\t\t}\n\n\t\treturn server;\n\t}\n\n\t// Type-safe event emission\n\toverride emit<K extends keyof ServerEvents>(event: K, payload: ServerEvents[K]): boolean {\n\t\treturn super.emit(event, payload);\n\t}\n\n\toverride on<K extends keyof ServerEvents>(\n\t\tevent: K,\n\t\tlistener: (payload: ServerEvents[K]) => void\n\t): this {\n\t\treturn super.on(event, listener);\n\t}\n\n\t// DI Container for managing dependencies\n\tprivate _container: Container;\n\n\t// Component instances (private)\n\tprivate _logger: StructuredLogger;\n\tprivate _historyManager: HistoryManager;\n\tprivate _thoughtProcessor: ThoughtProcessor;\n\tprivate _metrics: Metrics;\n\tprivate _skillWatcher: SkillWatcher | null = null;\n\tprivate _toolWatcher: ToolWatcher | null = null;\n\tprivate _config: ServerConfig;\n\n\t// Public manager properties (recommended API)\n\t/**\n\t * Direct access to the history manager\n\t * @example\n\t * ```typescript\n\t * server.history.getHistory();\n\t * server.history.clear();\n\t * ```\n\t */\n\tpublic readonly history: HistoryManager;\n\n\t/**\n\t * Direct access to the tool registry\n\t * @example\n\t * ```typescript\n\t * server.tools.addTool(tool);\n\t * server.tools.getTool('my-tool');\n\t * ```\n\t */\n\tpublic readonly tools: ToolRegistry;\n\n\t/**\n\t * Direct access to the skill registry\n\t * @example\n\t * ```typescript\n\t * server.skills.addSkill(skill);\n\t * server.skills.getSkill('my-skill');\n\t * ```\n\t */\n\tpublic readonly skills: SkillRegistry;\n\n\t/**\n\t * Server configuration\n\t * @example\n\t * ```typescript\n\t * console.log(server.config.maxHistorySize);\n\t * ```\n\t */\n\tpublic readonly config: ServerConfig;\n\n\tconstructor(options: ServerOptions = {}) {\n\t\t// Use provided container or create a new one\n\t\tsuper();\n\t\tif (!options.container) {\n\t\t\tthrow new Error('Container is required. Use createServer() or provide a container.');\n\t\t}\n\t\tthis._container = options.container;\n\n\t\t// Resolve dependencies from container\n\t\tthis._logger = this._container.resolve<StructuredLogger>('Logger');\n\t\tthis._historyManager = this._container.resolve<HistoryManager>('HistoryManager');\n\t\tthis._thoughtProcessor = this._container.resolve<ThoughtProcessor>('ThoughtProcessor');\n\t\tthis._metrics = this._container.resolve<Metrics>('Metrics');\n\t\tthis._config = this._container.resolve<ServerConfig>('Config');\n\n\t\t// Expose managers as public properties (recommended API)\n\t\tthis.history = this._historyManager;\n\n\t\t// Wire up persistence error event emitter\n\t\tthis._historyManager.setEventEmitter(this);\n\t\tthis.tools = this._container.resolve<ToolRegistry>('ToolRegistry');\n\t\tthis.skills = this._container.resolve<SkillRegistry>('SkillRegistry');\n\t\tthis.config = this._config;\n\n\t\t// Always include the sequential thinking tool\n\t\tthis.tools.addTool(SEQUENTIAL_THINKING_TOOL);\n\n\n\t\t// Initialize watchers if enabled\n\t\tif (options.enableWatcher) {\n\t\t\tthis._skillWatcher = new SkillWatcher(this.skills);\n\t\t\tthis._toolWatcher = new ToolWatcher(this.tools);\n\t\t}\n\t}\n\n\t/**\n\t * Shared core logic for container creation.\n\t * This method contains all common initialization logic between sync and async paths.\n\t */\n\tprivate static _createContainerCore(\n\t\toptions: ServerOptions,\n\t\tfileConfig: ConfigFileOptions | null,\n\t\tpersistence: PersistenceBackend | null\n\t): Container {\n\t\tconst container = new Container();\n\t\tconst metrics = new Metrics({\n\t\t\tprefix: 'sequentialthinking',\n\t\t});\n\n\t\t// Initialize config with file defaults overridden by constructor options\n\t\tconst config = new ServerConfig({\n\t\t\tmaxHistorySize: options.maxHistorySize ?? fileConfig?.maxHistorySize,\n\t\t\tmaxBranches: options.maxBranches ?? fileConfig?.maxBranches,\n\t\t\tmaxBranchSize: options.maxBranchSize ?? fileConfig?.maxBranchSize,\n\t\t\tskillDirs: fileConfig?.skillDirs,\n\t\t\tdiscoveryCache: fileConfig?.discoveryCache,\n\t\t\tpersistence: fileConfig?.persistence,\n\t\t});\n\n\t\t// Initialize logger\n\t\tconst logger =\n\t\t\toptions.logger ??\n\t\t\tnew StructuredLogger({\n\t\t\t\tlevel: fileConfig?.logLevel ?? 'info',\n\t\t\t\tcontext: 'SequentialThinking',\n\t\t\t\tpretty: fileConfig?.prettyLog ?? true,\n\t\t\t});\n\n\t\t// Register all services in the container\n\t\tcontainer.registerInstance('Logger', logger);\n\t\tcontainer.registerInstance('Config', config);\n\t\tcontainer.registerInstance('FileConfig', fileConfig || {});\n\t\tcontainer.registerInstance('Persistence', persistence);\n\t\tcontainer.registerInstance('Metrics', metrics);\n\t\tcontainer.register(\n\t\t\t'ToolRegistry',\n\t\t\t() =>\n\t\t\t\tnew ToolRegistry({\n\t\t\t\t\tlogger,\n\t\t\t\t\tcache: config.discoveryCache\n\t\t\t\t\t\t? new DiscoveryCache({ ...config.discoveryCache, metrics })\n\t\t\t\t\t\t: undefined,\n\t\t\t\t})\n\t\t);\n\t\tcontainer.register(\n\t\t\t'SkillRegistry',\n\t\t\t() =>\n\t\t\t\tnew SkillRegistry({\n\t\t\t\t\tlogger,\n\t\t\t\t\tcache: config.discoveryCache\n\t\t\t\t\t\t? new DiscoveryCache({ ...config.discoveryCache, metrics })\n\t\t\t\t\t\t: undefined,\n\t\t\t\t\tskillDirs: config.skillDirs,\n\t\t\t\t\tlazyDiscovery: options.lazyDiscovery,\n\t\t\t\t})\n\t\t);\n\n\t\t// Register EdgeStore as a lazy singleton (always registered; flag gates writes)\n\t\tcontainer.register('EdgeStore', () => new EdgeStore());\n\n\t\t// Register SummaryStore as a lazy singleton (always registered; flag gates writes)\n\t\tcontainer.register('summaryStore', () => new InMemorySummaryStore());\n\n\t\t// Register SuspensionStore as a lazy singleton (only when toolInterleave flag is on)\n\t\tif (config.features.toolInterleave) {\n\t\t\tcontainer.register('suspensionStore', () => {\n\t\t\t\tconst store = new InMemorySuspensionStore({\n\t\t\t\t\tttlMs: config.toolInterleaveTtlMs,\n\t\t\t\t\tsweepIntervalMs: config.toolInterleaveSweepMs,\n\t\t\t\t\tlogger,\n\t\t\t\t});\n\t\t\t\tstore.start();\n\t\t\t\treturn store;\n\t\t\t});\n\t\t}\n\n\t\t// Register CompressionService as a lazy singleton (always registered; flag gates invocation)\n\t\tcontainer.register('compressionService', () => {\n\t\t\tconst historyManager = container.resolve<HistoryManager>('HistoryManager');\n\t\t\tconst edgeStore = container.resolve<EdgeStore>('EdgeStore');\n\t\t\tconst summaryStore = container.resolve<ISummaryStore>('summaryStore');\n\t\t\tconst log = container.resolve<StructuredLogger>('Logger');\n\t\t\treturn new CompressionService({ historyManager, edgeStore, summaryStore, logger: log });\n\t\t});\n\n\t\t// Register ReasoningStrategy as a lazy singleton (selected via feature flag)\n\t\tcontainer.register('reasoningStrategy', () =>\n\t\t\tcreateReasoningStrategy(config.features.reasoningStrategy),\n\t\t);\n\n\t\t// Register HistoryManager with lazy initialization\n\t\tcontainer.register('HistoryManager', () => {\n\t\t\tconst cfg = container.resolve<ServerConfig>('Config');\n\t\t\tconst log = container.resolve<StructuredLogger>('Logger');\n\t\t\tconst pers = container.resolve('Persistence') as PersistenceBackend | null;\n\t\t\tconst componentMetrics = container.resolve<Metrics>('Metrics');\n\t\t\tconst edgeStore = container.resolve<EdgeStore>('EdgeStore');\n\t\t\treturn new HistoryManager({\n\t\t\t\tmaxHistorySize: cfg.maxHistorySize,\n\t\t\t\tmaxBranches: cfg.maxBranches,\n\t\t\t\tmaxBranchSize: cfg.maxBranchSize,\n\t\t\t\tlogger: log,\n\t\t\t\tpersistence: pers,\n\t\t\t\tmetrics: componentMetrics,\n\t\t\t\tpersistenceBufferSize: cfg.persistenceBufferSize,\n\t\t\t\tpersistenceFlushInterval: cfg.persistenceFlushInterval,\n\t\t\t\tpersistenceMaxRetries: cfg.persistenceMaxRetries,\n\t\t\t\tedgeStore,\n\t\t\t});\n\t\t});\n\n\t\t// Register ThoughtFormatter (can be transient)\n\t\tcontainer.registerFactory('ThoughtFormatter', () => new ThoughtFormatter());\n\n\t\t// Register OutcomeRecorder as a lazy singleton (gated by feature flag)\n\t\tcontainer.register(\n\t\t\t'outcomeRecorder',\n\t\t\t() => new OutcomeRecorder({ enabled: config.features.outcomeRecording ?? false }),\n\t\t);\n\n\t\t// Register Calibrator as a lazy singleton (gated by feature flag)\n\t\tcontainer.register(\n\t\t\t'calibrator',\n\t\t\t() =>\n\t\t\t\tnew Calibrator(\n\t\t\t\t\tcontainer.resolve('outcomeRecorder'),\n\t\t\t\t\tconfig.features.calibration ?? false,\n\t\t\t\t),\n\t\t);\n\n\t\t// Register ThoughtEvaluator (stateless, transient) with injected calibrator\n\t\tcontainer.registerFactory(\n\t\t\t'ThoughtEvaluator',\n\t\t\t() => new ThoughtEvaluator(container.resolve('calibrator')),\n\t\t);\n\n\t\t// Register ThoughtProcessor\n\t\tcontainer.register('ThoughtProcessor', () => {\n\t\t\tconst history = container.resolve<HistoryManager>('HistoryManager');\n\t\t\tconst formatter = container.resolve<ThoughtFormatter>('ThoughtFormatter');\n\t\t\tconst evaluator = container.resolve<ThoughtEvaluator>('ThoughtEvaluator');\n\t\t\tconst log = container.resolve<StructuredLogger>('Logger');\n\t\t\tconst strategy = container.resolve<IReasoningStrategy>('reasoningStrategy');\n\t\t\tconst compressionService = config.features.compression\n\t\t\t\t? container.resolve<CompressionService>('compressionService')\n\t\t\t\t: undefined;\n\t\t\tconst suspensionStore = config.features.toolInterleave\n\t\t\t\t? container.resolve<ISuspensionStore>('suspensionStore')\n\t\t\t\t: undefined;\n\t\t\treturn new ThoughtProcessor(\n\t\t\t\thistory,\n\t\t\t\tformatter,\n\t\t\t\tevaluator,\n\t\t\t\tlog,\n\t\t\t\tstrategy,\n\t\t\t\tcompressionService,\n\t\t\t\tsuspensionStore,\n\t\t\t\tconfig.features,\n\t\t\t);\n\t\t});\n\n\t\treturn container;\n\t}\n\n\n\t/**\n\t * Create and configure the DI container with async persistence initialization.\n\t * This is used internally by the static create() factory.\n\t */\n\tprivate static async _createContainerAsyncStatic(options: ServerOptions): Promise<Container> {\n\t\tconst configLoader = new ConfigLoader();\n\t\tconst fileConfig = configLoader.load();\n\n\t\t// Initialize persistence backend (async)\n\t\tconst persistence = await createPersistenceBackend(\n\t\t\tfileConfig?.persistence ?? { enabled: false }\n\t\t);\n\n\t\treturn ToolAwareSequentialThinkingServer._createContainerCore(options, fileConfig, persistence);\n\t}\n\n\t/**\n\t * Get the DI container used by this server\n\t * Useful for testing and advanced customizations\n\t */\n\tpublic getContainer(): Container {\n\t\treturn this._container;\n\t}\n\n\t/**\n\t * Discover skills asynchronously without blocking server startup.\n\t * This is the recommended method for skill discovery.\n\t * @returns Promise<number> - The number of skills discovered\n\t */\n\tpublic async discoverSkillsAsync(): Promise<number> {\n\t\tconst discovered = await this.skills.discoverAsync();\n\t\treturn discovered;\n\t}\n\n\t/**\n\t * Get all branches from the history manager\n\t * @returns Record<string, ThoughtData[]> - Map of branch IDs to thought arrays\n\t */\n\tpublic getBranches(): Record<string, ThoughtData[]> {\n\t\treturn this._historyManager.getBranches();\n\t}\n\n\t// Main processing method - delegate to ThoughtProcessor\n\tpublic async processThought(input: v.InferInput<typeof SequentialThinkingSchema>) {\n\t\tconst startTime = Date.now();\n\t\tconst thoughtInput = input as ThoughtData & { register_branch_id?: string };\n\t\tif (typeof thoughtInput.register_branch_id === 'string' && thoughtInput.register_branch_id.length > 0) {\n\t\t\ttry {\n\t\t\t\tthis._historyManager.registerBranch(\n\t\t\t\t\tthoughtInput.session_id,\n\t\t\t\t\tthoughtInput.register_branch_id\n\t\t\t\t);\n\t\t\t} catch (err) {\n\t\t\t\tthis._logger.warn('registerBranch skipped', {\n\t\t\t\t\tbranch_id: thoughtInput.register_branch_id,\n\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t});\n\t\t\t}\n\t\t\tdelete thoughtInput.register_branch_id;\n\t\t}\n\t\tconst result = await this._thoughtProcessor.process(thoughtInput);\n\t\tconst durationSeconds = (Date.now() - startTime) / 1000;\n\t\tthis._metrics.histogram('thought_processing_duration_seconds', durationSeconds, {});\n\t\treturn result;\n\t}\n\n\tpublic getMetricsSnapshot(): string {\n\t\treturn this._metrics.export();\n\t}\n\n\t/**\n\t * Stop the server and clean up watchers.\n\t * Closes persistence backend gracefully to ensure data is flushed.\n\t */\n\tpublic async stop(): Promise<void> {\n\t\tthis._skillWatcher?.stop();\n\t\tthis._toolWatcher?.stop();\n\n\t\t// Stop suspension store sweeper if registered\n\t\tif (this._config.features.toolInterleave && this._container.has('suspensionStore')) {\n\t\t\ttry {\n\t\t\t\tconst suspensionStore = this._container.resolve<ISuspensionStore>('suspensionStore');\n\t\t\t\tsuspensionStore.stop();\n\t\t\t} catch (error) {\n\t\t\t\tthis._logger.error('Error stopping suspension store', {\n\t\t\t\t\terror: getErrorMessage(error),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t// Flush any buffered writes before closing persistence\n\t\ttry {\n\t\t\tawait this._historyManager.shutdown();\n\t\t} catch (error) {\n\t\t\tthis._logger.error('Error flushing write buffer during shutdown', {\n\t\t\t\terror: getErrorMessage(error),\n\t\t\t});\n\t\t}\n\n\t\t// Close persistence backend if available\n\t\tconst persistence = this._container.resolve<PersistenceBackend | null>('Persistence');\n\t\tif (persistence) {\n\t\t\ttry {\n\t\t\t\tawait persistence.close();\n\t\t\t\tthis._logger.info('Persistence backend closed');\n\t\t\t} catch (error) {\n\t\t\t\tthis._logger.error('Error closing persistence backend', {\n\t\t\t\t\terror: getErrorMessage(error),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tthis._logger.info('Server stopped, watchers cleaned up');\n\t}\n\n\t/**\n\t * Clear all server state (history, tools, skills)\n\t * Useful for testing to reset state between tests\n\t */\n\tpublic clear(): void {\n\t\tthis._historyManager.clear();\n\t\tthis._logger.info('Server state cleared');\n\t}\n\n\t/**\n\t * Dispose of the server and all container services.\n\t * Implements the IDisposable interface.\n\t * Calls stop() for existing cleanup, then disposes the DI container.\n\t */\n\tpublic async dispose(): Promise<void> {\n\t\tawait this.stop();\n\t\tawait this._container.dispose();\n\t\tthis._logger.info('Server disposed, all resources released');\n\t}\n}\n\n/**\n * Factory function to create a new server instance with async initialization.\n *\n * This is the recommended way to create server instances, especially for testing,\n * as it allows for proper async initialization, dependency injection, and persistence.\n *\n * @param options - Server configuration options\n * @returns A Promise that resolves to a configured server instance\n *\n * @example\n * ```typescript\n * // Basic usage (with async discovery and persistence)\n * const server = await createServer();\n *\n * // With custom options\n * const server = await createServer({\n * autoDiscover: false,\n * lazyDiscovery: true,\n * maxHistorySize: 500,\n * loadFromPersistence: true\n * });\n *\n * // With custom container for testing\n * const mockContainer = new Container();\n * mockContainer.registerInstance('Logger', mockLogger);\n * const server = await createServer({ container: mockContainer });\n * ```\n */\nexport async function createServer(\n\toptions: ServerOptions = {}\n): Promise<ToolAwareSequentialThinkingServer> {\n\treturn ToolAwareSequentialThinkingServer.create(options);\n}\n\n// Initialize server\nexport async function initializeServer(): Promise<ToolAwareSequentialThinkingServer> {\n\t// Create logger for initialization\n\tconst configLoader = new ConfigLoader();\n\tconst fileConfig = configLoader.load();\n\n\tconst logger = new StructuredLogger({\n\t\tlevel: fileConfig?.logLevel ?? 'info',\n\t\tcontext: 'SequentialThinking',\n\t\tpretty: fileConfig?.prettyLog ?? true,\n\t});\n\n\t// Create server instance\n\tconst thinkingServer = await createServer({\n\t\tlogger,\n\t\tenableWatcher: true,\n\t});\n\n\tlogger.info('Server initialized successfully');\n\treturn thinkingServer;\n}\n"],"names":["ToolAwareSequentialThinkingServer","EventEmitter","options","container","server","event","payload","listener","Error","SEQUENTIAL_THINKING_TOOL","SkillWatcher","ToolWatcher","fileConfig","persistence","Container","metrics","Metrics","config","ServerConfig","logger","StructuredLogger","ToolRegistry","DiscoveryCache","undefined","SkillRegistry","EdgeStore","InMemorySummaryStore","store","InMemorySuspensionStore","historyManager","edgeStore","summaryStore","log","CompressionService","createReasoningStrategy","cfg","pers","componentMetrics","HistoryManager","ThoughtFormatter","OutcomeRecorder","Calibrator","ThoughtEvaluator","history","formatter","evaluator","strategy","compressionService","suspensionStore","ThoughtProcessor","configLoader","ConfigLoader","createPersistenceBackend","discovered","input","startTime","Date","thoughtInput","err","String","result","durationSeconds","error","getErrorMessage","createServer","initializeServer","thinkingServer"],"mappings":"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"}
|
|
@@ -88,6 +88,12 @@ export declare class FilePersistence implements PersistenceBackend {
|
|
|
88
88
|
* @returns Edges array (empty if file is missing or corrupted)
|
|
89
89
|
*/
|
|
90
90
|
loadEdges(sessionId: string): Promise<Edge[]>;
|
|
91
|
+
/**
|
|
92
|
+
* List all session IDs that have persisted edge files.
|
|
93
|
+
*
|
|
94
|
+
* @returns Array of session identifiers (filenames without .json extension)
|
|
95
|
+
*/
|
|
96
|
+
listEdgeSessions(): Promise<string[]>;
|
|
91
97
|
/**
|
|
92
98
|
* Validates session ID format and resolves the summary file path safely.
|
|
93
99
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FilePersistence.d.ts","sourceRoot":"","sources":["../../src/persistence/FilePersistence.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAErF;;;;;;;;;;;;;GAaG;AACH,qBAAa,eAAgB,YAAW,kBAAkB;IACzD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,gBAAgB,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,CAAW;gBAEhB,OAAO,CAAC,EAAE,iBAAiB,CAAC,SAAS,CAAC,GAAG;QAAE,OAAO,CAAC,EAAE,QAAQ,CAAA;KAAE;IAe3E,OAAO,CAAC,wBAAwB;IAKhC;;OAEG;YACW,kBAAkB;IAehC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,eAAe;IAoBV,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBhD,WAAW,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAoBrC,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBpE,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,GAAG,SAAS,CAAC;IA0BhE,YAAY,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAIjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAyCtB,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IASxC;;OAEG;IACI,UAAU,IAAI,MAAM;IAI3B;;OAEG;IACU,YAAY,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAa9C;;;OAGG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAInC;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;IAkBrB;;;;;OAKG;IACU,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAyBhF;;;;;OAKG;IACU,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAqB1D;;;;;;OAMG;IACH,OAAO,CAAC,gBAAgB;IAkBxB;;;;;;OAMG;IACU,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA2B3F;;;;;OAKG;IACU,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;CAqBjE"}
|
|
1
|
+
{"version":3,"file":"FilePersistence.d.ts","sourceRoot":"","sources":["../../src/persistence/FilePersistence.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAErF;;;;;;;;;;;;;GAaG;AACH,qBAAa,eAAgB,YAAW,kBAAkB;IACzD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,gBAAgB,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,CAAW;gBAEhB,OAAO,CAAC,EAAE,iBAAiB,CAAC,SAAS,CAAC,GAAG;QAAE,OAAO,CAAC,EAAE,QAAQ,CAAA;KAAE;IAe3E,OAAO,CAAC,wBAAwB;IAKhC;;OAEG;YACW,kBAAkB;IAehC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,eAAe;IAoBV,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBhD,WAAW,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAoBrC,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBpE,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,GAAG,SAAS,CAAC;IA0BhE,YAAY,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAIjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAyCtB,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IASxC;;OAEG;IACI,UAAU,IAAI,MAAM;IAI3B;;OAEG;IACU,YAAY,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAa9C;;;OAGG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAInC;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;IAkBrB;;;;;OAKG;IACU,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAyBhF;;;;;OAKG;IACU,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAqB1D;;;;OAIG;IACU,gBAAgB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IASlD;;;;;;OAMG;IACH,OAAO,CAAC,gBAAgB;IAkBxB;;;;;;OAMG;IACU,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA2B3F;;;;;OAKG;IACU,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;CAqBjE"}
|
|
@@ -188,6 +188,14 @@ class FilePersistence {
|
|
|
188
188
|
this._recordOperationDuration('load_edges', startTime);
|
|
189
189
|
}
|
|
190
190
|
}
|
|
191
|
+
async listEdgeSessions() {
|
|
192
|
+
try {
|
|
193
|
+
const files = await readdir(this._edgesDir);
|
|
194
|
+
return files.filter((f)=>f.endsWith('.json')).map((f)=>f.slice(0, -5));
|
|
195
|
+
} catch {
|
|
196
|
+
return [];
|
|
197
|
+
}
|
|
198
|
+
}
|
|
191
199
|
_safeSummaryPath(sessionId) {
|
|
192
200
|
const validSessionIdPattern = /^[a-zA-Z0-9_-]{1,100}$/;
|
|
193
201
|
if (!validSessionIdPattern.test(sessionId)) throw new Error("Invalid session ID for summaries: must be 1-100 alphanumeric characters, hyphens, or underscores only");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persistence/FilePersistence.js","sources":["../../src/persistence/FilePersistence.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { mkdir, readFile, readdir, rename, unlink, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join, resolve, sep } from 'node:path';\nimport type { IMetrics } from '../contracts/index.js';\nimport type { ThoughtData } from '../core/thought.js';\nimport type { Edge } from '../core/graph/Edge.js';\nimport type { Summary } from '../core/compression/Summary.js';\nimport type { PersistenceBackend, PersistenceConfig } from './PersistenceBackend.js';\n\n/**\n * File-based persistence backend using JSON files.\n *\n * Stores thoughts and branches as JSON files in a configured directory.\n * Simple and reliable, with no external dependencies.\n *\n * File structure:\n * ```\n * dataDir/\n * history.json # Main thought history\n * branches/\n * <branch-id>.json # Individual branch files\n * ```\n */\nexport class FilePersistence implements PersistenceBackend {\n\tprivate _dataDir: string;\n\tprivate _historyPath: string;\n\tprivate _branchesDir: string;\n\tprivate _edgesDir: string;\n\tprivate _summariesDir: string;\n\tprivate _maxHistorySize: number;\n\tprivate _persistBranches: boolean;\n\tprivate _metrics?: IMetrics;\n\n\tconstructor(options?: PersistenceConfig['options'] & { metrics?: IMetrics }) {\n\t\t// Default to .claude/data in current directory or home directory\n\t\tconst defaultDataDir = existsSync('.claude/data')\n\t\t\t? '.claude/data'\n\t\t\t: join(homedir(), '.claude/data');\n\t\tthis._dataDir = options?.dataDir ?? defaultDataDir;\n\t\tthis._historyPath = join(this._dataDir, 'history.json');\n\t\tthis._branchesDir = join(this._dataDir, 'branches');\n\t\tthis._edgesDir = join(this._dataDir, 'edges');\n\t\tthis._summariesDir = join(this._dataDir, 'summaries');\n\t\tthis._maxHistorySize = options?.maxHistorySize ?? 10000;\n\t\tthis._persistBranches = options?.persistBranches ?? true;\n\t\tthis._metrics = options?.metrics;\n\t}\n\n\tprivate _recordOperationDuration(operation: string, startTime: number): void {\n\t\tconst durationSeconds = (Date.now() - startTime) / 1000;\n\t\tthis._metrics?.histogram('persistence_op_duration_seconds', durationSeconds, { operation });\n\t}\n\n\t/**\n\t * Initialize the persistence directory structure.\n\t */\n\tprivate async _ensureDirectories(): Promise<void> {\n\t\tif (!existsSync(this._dataDir)) {\n\t\t\tawait mkdir(this._dataDir, { recursive: true });\n\t\t}\n\t\tif (this._persistBranches && !existsSync(this._branchesDir)) {\n\t\t\tawait mkdir(this._branchesDir, { recursive: true });\n\t\t}\n\t\tif (!existsSync(this._edgesDir)) {\n\t\t\tawait mkdir(this._edgesDir, { recursive: true });\n\t\t}\n\t\tif (!existsSync(this._summariesDir)) {\n\t\t\tawait mkdir(this._summariesDir, { recursive: true });\n\t\t}\n\t}\n\n\t/**\n\t * Validates branch ID format and resolves the path safely.\n\t *\n\t * This method provides defense-in-depth security by:\n\t * 1. Validating the branch ID format (alphanumeric, hyphens, underscores only)\n\t * 2. Preventing path traversal attacks\n\t *\n\t * @param branchId - The branch ID to validate and resolve\n\t * @returns The safe, resolved branch file path\n\t * @throws Error if branch ID is invalid or path traversal is detected\n\t */\n\tprivate _safeBranchPath(branchId: string): string {\n\t\t// Validate format first (must be alphanumeric with hyphens/underscores, 1-64 chars)\n\t\tconst validBranchIdPattern = /^[a-zA-Z0-9_-]{1,64}$/;\n\t\tif (!validBranchIdPattern.test(branchId)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid branch ID: must be 1-64 alphanumeric characters, hyphens, or underscores only`\n\t\t\t);\n\t\t}\n\n\t\tconst resolved = resolve(this._branchesDir, `${branchId}.json`);\n\t\tconst normalizedBranchesDir = resolve(this._branchesDir);\n\n\t\t// Ensure the resolved path is still within branches directory\n\t\tif (!resolved.startsWith(normalizedBranchesDir + sep)) {\n\t\t\tthrow new Error(`Invalid branch ID: path traversal detected`);\n\t\t}\n\n\t\treturn resolved;\n\t}\n\n\tpublic async saveThought(thought: ThoughtData): Promise<void> {\n\t\tconst startTime = Date.now();\n\t\ttry {\n\t\t\tawait this._ensureDirectories();\n\n\t\t\tconst history = await this.loadHistory();\n\t\t\thistory.push(thought);\n\n\t\t\tif (history.length > this._maxHistorySize) {\n\t\t\t\thistory.splice(0, history.length - this._maxHistorySize);\n\t\t\t}\n\n\t\t\tawait writeFile(this._historyPath, JSON.stringify(history, null, 2), 'utf-8');\n\t\t} finally {\n\t\t\tthis._recordOperationDuration('save_thought', startTime);\n\t\t}\n\t}\n\n\tpublic async loadHistory(): Promise<ThoughtData[]> {\n\t\tconst startTime = Date.now();\n\t\ttry {\n\t\t\tif (!existsSync(this._historyPath)) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst content = await readFile(this._historyPath, 'utf-8');\n\t\t\tconst data = JSON.parse(content) as ThoughtData[];\n\n\t\t\t// Validate and filter\n\t\t\treturn Array.isArray(data) ? data : [];\n\t\t} catch {\n\t\t\t// If file is corrupted, start fresh\n\t\t\treturn [];\n\t\t} finally {\n\t\t\tthis._recordOperationDuration('load_history', startTime);\n\t\t}\n\t}\n\n\tpublic async saveBranch(branchId: string, thoughts: ThoughtData[]): Promise<void> {\n\t\tconst startTime = Date.now();\n\t\ttry {\n\t\t\tif (!this._persistBranches) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tawait this._ensureDirectories();\n\n\t\t\tconst branchPath = this._safeBranchPath(branchId);\n\t\t\tawait writeFile(branchPath, JSON.stringify(thoughts, null, 2), 'utf-8');\n\t\t} finally {\n\t\t\tthis._recordOperationDuration('save_branch', startTime);\n\t\t}\n\t}\n\n\tpublic async loadBranch(branchId: string): Promise<ThoughtData[] | undefined> {\n\t\tconst startTime = Date.now();\n\t\ttry {\n\t\t\tif (!this._persistBranches) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tconst branchPath = this._safeBranchPath(branchId);\n\n\t\t\ttry {\n\t\t\t\tif (!existsSync(branchPath)) {\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\n\t\t\t\tconst content = await readFile(branchPath, 'utf-8');\n\t\t\t\tconst data = JSON.parse(content) as ThoughtData[];\n\n\t\t\t\treturn Array.isArray(data) ? data : undefined;\n\t\t\t} catch {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t} finally {\n\t\t\tthis._recordOperationDuration('load_branch', startTime);\n\t\t}\n\t}\n\n\tpublic async listBranches(): Promise<string[]> {\n\t\treturn this.getBranchIds();\n\t}\n\n\tpublic async clear(): Promise<void> {\n\t\ttry {\n\t\t\t// Clear history\n\t\t\tif (existsSync(this._historyPath)) {\n\t\t\t\tawait unlink(this._historyPath);\n\t\t\t}\n\n\t\t\t// Clear all branches\n\t\t\tif (this._persistBranches && existsSync(this._branchesDir)) {\n\t\t\t\tconst files = await readdir(this._branchesDir);\n\t\t\t\tfor (const file of files) {\n\t\t\t\t\tif (file.endsWith('.json')) {\n\t\t\t\t\t\tawait unlink(join(this._branchesDir, file));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Clear all edges\n\t\t\tif (existsSync(this._edgesDir)) {\n\t\t\t\tconst edgeFiles = await readdir(this._edgesDir);\n\t\t\t\tfor (const file of edgeFiles) {\n\t\t\t\t\tif (file.endsWith('.json')) {\n\t\t\t\t\t\tawait unlink(join(this._edgesDir, file));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Clear all summaries\n\t\t\tif (existsSync(this._summariesDir)) {\n\t\t\t\tconst summaryFiles = await readdir(this._summariesDir);\n\t\t\t\tfor (const file of summaryFiles) {\n\t\t\t\t\tif (file.endsWith('.json')) {\n\t\t\t\t\t\tawait unlink(join(this._summariesDir, file));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore errors during clear\n\t\t}\n\t}\n\n\tpublic async healthy(): Promise<boolean> {\n\t\ttry {\n\t\t\tawait this._ensureDirectories();\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Get the data directory path.\n\t */\n\tpublic getDataDir(): string {\n\t\treturn this._dataDir;\n\t}\n\n\t/**\n\t * Get all branch IDs that are persisted.\n\t */\n\tpublic async getBranchIds(): Promise<string[]> {\n\t\tif (!this._persistBranches || !existsSync(this._branchesDir)) {\n\t\t\treturn [];\n\t\t}\n\n\t\ttry {\n\t\t\tconst files = await readdir(this._branchesDir);\n\t\t\treturn files.filter((f) => f.endsWith('.json')).map((f) => f.replace('.json', ''));\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Close the backend and release resources.\n\t * No resources to release for file backend.\n\t */\n\tpublic async close(): Promise<void> {\n\t\t// No-op for file backend (files are already flushed on write)\n\t}\n\n\t/**\n\t * Validates session ID format and resolves the edge file path safely.\n\t *\n\t * @param sessionId - The session ID to validate and resolve\n\t * @returns The safe, resolved edge file path\n\t * @throws Error if session ID is invalid or path traversal is detected\n\t */\n\tprivate _safeEdgePath(sessionId: string): string {\n\t\tconst validSessionIdPattern = /^[a-zA-Z0-9_-]{1,100}$/;\n\t\tif (!validSessionIdPattern.test(sessionId)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid session ID for edges: must be 1-100 alphanumeric characters, hyphens, or underscores only`\n\t\t\t);\n\t\t}\n\n\t\tconst resolved = resolve(this._edgesDir, `${sessionId}.json`);\n\t\tconst normalizedEdgesDir = resolve(this._edgesDir);\n\n\t\tif (!resolved.startsWith(normalizedEdgesDir + sep)) {\n\t\t\tthrow new Error(`Invalid session ID: path traversal detected`);\n\t\t}\n\n\t\treturn resolved;\n\t}\n\n\t/**\n\t * Save edges for a session to a JSON file.\n\t *\n\t * @param sessionId - The session ID\n\t * @param edges - Edges to persist (sorted by createdAt before write)\n\t */\n\tpublic async saveEdges(sessionId: string, edges: readonly Edge[]): Promise<void> {\n\t\tconst startTime = Date.now();\n\t\ttry {\n\t\t\tawait this._ensureDirectories();\n\n\t\t\tif (!existsSync(this._edgesDir)) {\n\t\t\t\tawait mkdir(this._edgesDir, { recursive: true });\n\t\t\t}\n\n\t\t\tconst edgePath = this._safeEdgePath(sessionId);\n\n\t\t\tif (edges.length === 0) {\n\t\t\t\tif (existsSync(edgePath)) {\n\t\t\t\t\tawait unlink(edgePath);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst sorted = [...edges].sort((a, b) => a.createdAt - b.createdAt);\n\t\t\tawait writeFile(edgePath, JSON.stringify(sorted, null, 2), 'utf-8');\n\t\t} finally {\n\t\t\tthis._recordOperationDuration('save_edges', startTime);\n\t\t}\n\t}\n\n\t/**\n\t * Load edges for a session from a JSON file.\n\t *\n\t * @param sessionId - The session ID\n\t * @returns Edges array (empty if file is missing or corrupted)\n\t */\n\tpublic async loadEdges(sessionId: string): Promise<Edge[]> {\n\t\tconst startTime = Date.now();\n\t\ttry {\n\t\t\tconst edgePath = this._safeEdgePath(sessionId);\n\n\t\t\tif (!existsSync(edgePath)) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst content = await readFile(edgePath, 'utf-8');\n\t\t\t\tconst data = JSON.parse(content) as Edge[];\n\t\t\t\treturn Array.isArray(data) ? data : [];\n\t\t\t} catch {\n\t\t\t\treturn [];\n\t\t\t}\n\t\t} finally {\n\t\t\tthis._recordOperationDuration('load_edges', startTime);\n\t\t}\n\t}\n\n\t/**\n\t * Validates session ID format and resolves the summary file path safely.\n\t *\n\t * @param sessionId - The session ID to validate and resolve\n\t * @returns The safe, resolved summary file path\n\t * @throws Error if session ID is invalid or path traversal is detected\n\t */\n\tprivate _safeSummaryPath(sessionId: string): string {\n\t\tconst validSessionIdPattern = /^[a-zA-Z0-9_-]{1,100}$/;\n\t\tif (!validSessionIdPattern.test(sessionId)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid session ID for summaries: must be 1-100 alphanumeric characters, hyphens, or underscores only`\n\t\t\t);\n\t\t}\n\n\t\tconst resolved = resolve(this._summariesDir, `${sessionId}.json`);\n\t\tconst normalizedSummariesDir = resolve(this._summariesDir);\n\n\t\tif (!resolved.startsWith(normalizedSummariesDir + sep)) {\n\t\t\tthrow new Error(`Invalid session ID: path traversal detected`);\n\t\t}\n\n\t\treturn resolved;\n\t}\n\n\t/**\n\t * Save summaries for a session to a JSON file using an atomic\n\t * write (tmp file + rename) to prevent partial-write corruption.\n\t *\n\t * @param sessionId - The session ID\n\t * @param summaries - Summaries to persist (sorted by createdAt before write)\n\t */\n\tpublic async saveSummaries(sessionId: string, summaries: readonly Summary[]): Promise<void> {\n\t\tconst startTime = Date.now();\n\t\ttry {\n\t\t\tawait this._ensureDirectories();\n\n\t\t\tif (!existsSync(this._summariesDir)) {\n\t\t\t\tawait mkdir(this._summariesDir, { recursive: true });\n\t\t\t}\n\n\t\t\tconst summaryPath = this._safeSummaryPath(sessionId);\n\n\t\t\tif (summaries.length === 0) {\n\t\t\t\tif (existsSync(summaryPath)) {\n\t\t\t\t\tawait unlink(summaryPath);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst sorted = [...summaries].sort((a, b) => a.createdAt - b.createdAt);\n\t\t\tconst tmpPath = `${summaryPath}.tmp`;\n\t\t\tawait writeFile(tmpPath, JSON.stringify(sorted, null, 2), 'utf-8');\n\t\t\tawait rename(tmpPath, summaryPath);\n\t\t} finally {\n\t\t\tthis._recordOperationDuration('save_summaries', startTime);\n\t\t}\n\t}\n\n\t/**\n\t * Load summaries for a session from a JSON file.\n\t *\n\t * @param sessionId - The session ID\n\t * @returns Summaries array (empty if file is missing or corrupted)\n\t */\n\tpublic async loadSummaries(sessionId: string): Promise<Summary[]> {\n\t\tconst startTime = Date.now();\n\t\ttry {\n\t\t\tconst summaryPath = this._safeSummaryPath(sessionId);\n\n\t\t\tif (!existsSync(summaryPath)) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst content = await readFile(summaryPath, 'utf-8');\n\t\t\t\tconst data = JSON.parse(content) as Summary[];\n\t\t\t\tif (!Array.isArray(data)) return [];\n\t\t\t\treturn [...data].sort((a, b) => a.createdAt - b.createdAt);\n\t\t\t} catch {\n\t\t\t\treturn [];\n\t\t\t}\n\t\t} finally {\n\t\t\tthis._recordOperationDuration('load_summaries', startTime);\n\t\t}\n\t}\n}\n"],"names":["FilePersistence","options","defaultDataDir","existsSync","join","homedir","operation","startTime","durationSeconds","Date","mkdir","branchId","validBranchIdPattern","Error","resolved","resolve","normalizedBranchesDir","sep","thought","history","writeFile","JSON","content","readFile","data","Array","thoughts","branchPath","undefined","unlink","files","readdir","file","edgeFiles","summaryFiles","f","sessionId","validSessionIdPattern","normalizedEdgesDir","edges","edgePath","sorted","a","b","normalizedSummariesDir","summaries","summaryPath","tmpPath","rename"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"persistence/FilePersistence.js","sources":["../../src/persistence/FilePersistence.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { mkdir, readFile, readdir, rename, unlink, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join, resolve, sep } from 'node:path';\nimport type { IMetrics } from '../contracts/index.js';\nimport type { ThoughtData } from '../core/thought.js';\nimport type { Edge } from '../core/graph/Edge.js';\nimport type { Summary } from '../core/compression/Summary.js';\nimport type { PersistenceBackend, PersistenceConfig } from './PersistenceBackend.js';\n\n/**\n * File-based persistence backend using JSON files.\n *\n * Stores thoughts and branches as JSON files in a configured directory.\n * Simple and reliable, with no external dependencies.\n *\n * File structure:\n * ```\n * dataDir/\n * history.json # Main thought history\n * branches/\n * <branch-id>.json # Individual branch files\n * ```\n */\nexport class FilePersistence implements PersistenceBackend {\n\tprivate _dataDir: string;\n\tprivate _historyPath: string;\n\tprivate _branchesDir: string;\n\tprivate _edgesDir: string;\n\tprivate _summariesDir: string;\n\tprivate _maxHistorySize: number;\n\tprivate _persistBranches: boolean;\n\tprivate _metrics?: IMetrics;\n\n\tconstructor(options?: PersistenceConfig['options'] & { metrics?: IMetrics }) {\n\t\t// Default to .claude/data in current directory or home directory\n\t\tconst defaultDataDir = existsSync('.claude/data')\n\t\t\t? '.claude/data'\n\t\t\t: join(homedir(), '.claude/data');\n\t\tthis._dataDir = options?.dataDir ?? defaultDataDir;\n\t\tthis._historyPath = join(this._dataDir, 'history.json');\n\t\tthis._branchesDir = join(this._dataDir, 'branches');\n\t\tthis._edgesDir = join(this._dataDir, 'edges');\n\t\tthis._summariesDir = join(this._dataDir, 'summaries');\n\t\tthis._maxHistorySize = options?.maxHistorySize ?? 10000;\n\t\tthis._persistBranches = options?.persistBranches ?? true;\n\t\tthis._metrics = options?.metrics;\n\t}\n\n\tprivate _recordOperationDuration(operation: string, startTime: number): void {\n\t\tconst durationSeconds = (Date.now() - startTime) / 1000;\n\t\tthis._metrics?.histogram('persistence_op_duration_seconds', durationSeconds, { operation });\n\t}\n\n\t/**\n\t * Initialize the persistence directory structure.\n\t */\n\tprivate async _ensureDirectories(): Promise<void> {\n\t\tif (!existsSync(this._dataDir)) {\n\t\t\tawait mkdir(this._dataDir, { recursive: true });\n\t\t}\n\t\tif (this._persistBranches && !existsSync(this._branchesDir)) {\n\t\t\tawait mkdir(this._branchesDir, { recursive: true });\n\t\t}\n\t\tif (!existsSync(this._edgesDir)) {\n\t\t\tawait mkdir(this._edgesDir, { recursive: true });\n\t\t}\n\t\tif (!existsSync(this._summariesDir)) {\n\t\t\tawait mkdir(this._summariesDir, { recursive: true });\n\t\t}\n\t}\n\n\t/**\n\t * Validates branch ID format and resolves the path safely.\n\t *\n\t * This method provides defense-in-depth security by:\n\t * 1. Validating the branch ID format (alphanumeric, hyphens, underscores only)\n\t * 2. Preventing path traversal attacks\n\t *\n\t * @param branchId - The branch ID to validate and resolve\n\t * @returns The safe, resolved branch file path\n\t * @throws Error if branch ID is invalid or path traversal is detected\n\t */\n\tprivate _safeBranchPath(branchId: string): string {\n\t\t// Validate format first (must be alphanumeric with hyphens/underscores, 1-64 chars)\n\t\tconst validBranchIdPattern = /^[a-zA-Z0-9_-]{1,64}$/;\n\t\tif (!validBranchIdPattern.test(branchId)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid branch ID: must be 1-64 alphanumeric characters, hyphens, or underscores only`\n\t\t\t);\n\t\t}\n\n\t\tconst resolved = resolve(this._branchesDir, `${branchId}.json`);\n\t\tconst normalizedBranchesDir = resolve(this._branchesDir);\n\n\t\t// Ensure the resolved path is still within branches directory\n\t\tif (!resolved.startsWith(normalizedBranchesDir + sep)) {\n\t\t\tthrow new Error(`Invalid branch ID: path traversal detected`);\n\t\t}\n\n\t\treturn resolved;\n\t}\n\n\tpublic async saveThought(thought: ThoughtData): Promise<void> {\n\t\tconst startTime = Date.now();\n\t\ttry {\n\t\t\tawait this._ensureDirectories();\n\n\t\t\tconst history = await this.loadHistory();\n\t\t\thistory.push(thought);\n\n\t\t\tif (history.length > this._maxHistorySize) {\n\t\t\t\thistory.splice(0, history.length - this._maxHistorySize);\n\t\t\t}\n\n\t\t\tawait writeFile(this._historyPath, JSON.stringify(history, null, 2), 'utf-8');\n\t\t} finally {\n\t\t\tthis._recordOperationDuration('save_thought', startTime);\n\t\t}\n\t}\n\n\tpublic async loadHistory(): Promise<ThoughtData[]> {\n\t\tconst startTime = Date.now();\n\t\ttry {\n\t\t\tif (!existsSync(this._historyPath)) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\tconst content = await readFile(this._historyPath, 'utf-8');\n\t\t\tconst data = JSON.parse(content) as ThoughtData[];\n\n\t\t\t// Validate and filter\n\t\t\treturn Array.isArray(data) ? data : [];\n\t\t} catch {\n\t\t\t// If file is corrupted, start fresh\n\t\t\treturn [];\n\t\t} finally {\n\t\t\tthis._recordOperationDuration('load_history', startTime);\n\t\t}\n\t}\n\n\tpublic async saveBranch(branchId: string, thoughts: ThoughtData[]): Promise<void> {\n\t\tconst startTime = Date.now();\n\t\ttry {\n\t\t\tif (!this._persistBranches) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tawait this._ensureDirectories();\n\n\t\t\tconst branchPath = this._safeBranchPath(branchId);\n\t\t\tawait writeFile(branchPath, JSON.stringify(thoughts, null, 2), 'utf-8');\n\t\t} finally {\n\t\t\tthis._recordOperationDuration('save_branch', startTime);\n\t\t}\n\t}\n\n\tpublic async loadBranch(branchId: string): Promise<ThoughtData[] | undefined> {\n\t\tconst startTime = Date.now();\n\t\ttry {\n\t\t\tif (!this._persistBranches) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tconst branchPath = this._safeBranchPath(branchId);\n\n\t\t\ttry {\n\t\t\t\tif (!existsSync(branchPath)) {\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\n\t\t\t\tconst content = await readFile(branchPath, 'utf-8');\n\t\t\t\tconst data = JSON.parse(content) as ThoughtData[];\n\n\t\t\t\treturn Array.isArray(data) ? data : undefined;\n\t\t\t} catch {\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t} finally {\n\t\t\tthis._recordOperationDuration('load_branch', startTime);\n\t\t}\n\t}\n\n\tpublic async listBranches(): Promise<string[]> {\n\t\treturn this.getBranchIds();\n\t}\n\n\tpublic async clear(): Promise<void> {\n\t\ttry {\n\t\t\t// Clear history\n\t\t\tif (existsSync(this._historyPath)) {\n\t\t\t\tawait unlink(this._historyPath);\n\t\t\t}\n\n\t\t\t// Clear all branches\n\t\t\tif (this._persistBranches && existsSync(this._branchesDir)) {\n\t\t\t\tconst files = await readdir(this._branchesDir);\n\t\t\t\tfor (const file of files) {\n\t\t\t\t\tif (file.endsWith('.json')) {\n\t\t\t\t\t\tawait unlink(join(this._branchesDir, file));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Clear all edges\n\t\t\tif (existsSync(this._edgesDir)) {\n\t\t\t\tconst edgeFiles = await readdir(this._edgesDir);\n\t\t\t\tfor (const file of edgeFiles) {\n\t\t\t\t\tif (file.endsWith('.json')) {\n\t\t\t\t\t\tawait unlink(join(this._edgesDir, file));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Clear all summaries\n\t\t\tif (existsSync(this._summariesDir)) {\n\t\t\t\tconst summaryFiles = await readdir(this._summariesDir);\n\t\t\t\tfor (const file of summaryFiles) {\n\t\t\t\t\tif (file.endsWith('.json')) {\n\t\t\t\t\t\tawait unlink(join(this._summariesDir, file));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore errors during clear\n\t\t}\n\t}\n\n\tpublic async healthy(): Promise<boolean> {\n\t\ttry {\n\t\t\tawait this._ensureDirectories();\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Get the data directory path.\n\t */\n\tpublic getDataDir(): string {\n\t\treturn this._dataDir;\n\t}\n\n\t/**\n\t * Get all branch IDs that are persisted.\n\t */\n\tpublic async getBranchIds(): Promise<string[]> {\n\t\tif (!this._persistBranches || !existsSync(this._branchesDir)) {\n\t\t\treturn [];\n\t\t}\n\n\t\ttry {\n\t\t\tconst files = await readdir(this._branchesDir);\n\t\t\treturn files.filter((f) => f.endsWith('.json')).map((f) => f.replace('.json', ''));\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Close the backend and release resources.\n\t * No resources to release for file backend.\n\t */\n\tpublic async close(): Promise<void> {\n\t\t// No-op for file backend (files are already flushed on write)\n\t}\n\n\t/**\n\t * Validates session ID format and resolves the edge file path safely.\n\t *\n\t * @param sessionId - The session ID to validate and resolve\n\t * @returns The safe, resolved edge file path\n\t * @throws Error if session ID is invalid or path traversal is detected\n\t */\n\tprivate _safeEdgePath(sessionId: string): string {\n\t\tconst validSessionIdPattern = /^[a-zA-Z0-9_-]{1,100}$/;\n\t\tif (!validSessionIdPattern.test(sessionId)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid session ID for edges: must be 1-100 alphanumeric characters, hyphens, or underscores only`\n\t\t\t);\n\t\t}\n\n\t\tconst resolved = resolve(this._edgesDir, `${sessionId}.json`);\n\t\tconst normalizedEdgesDir = resolve(this._edgesDir);\n\n\t\tif (!resolved.startsWith(normalizedEdgesDir + sep)) {\n\t\t\tthrow new Error(`Invalid session ID: path traversal detected`);\n\t\t}\n\n\t\treturn resolved;\n\t}\n\n\t/**\n\t * Save edges for a session to a JSON file.\n\t *\n\t * @param sessionId - The session ID\n\t * @param edges - Edges to persist (sorted by createdAt before write)\n\t */\n\tpublic async saveEdges(sessionId: string, edges: readonly Edge[]): Promise<void> {\n\t\tconst startTime = Date.now();\n\t\ttry {\n\t\t\tawait this._ensureDirectories();\n\n\t\t\tif (!existsSync(this._edgesDir)) {\n\t\t\t\tawait mkdir(this._edgesDir, { recursive: true });\n\t\t\t}\n\n\t\t\tconst edgePath = this._safeEdgePath(sessionId);\n\n\t\t\tif (edges.length === 0) {\n\t\t\t\tif (existsSync(edgePath)) {\n\t\t\t\t\tawait unlink(edgePath);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst sorted = [...edges].sort((a, b) => a.createdAt - b.createdAt);\n\t\t\tawait writeFile(edgePath, JSON.stringify(sorted, null, 2), 'utf-8');\n\t\t} finally {\n\t\t\tthis._recordOperationDuration('save_edges', startTime);\n\t\t}\n\t}\n\n\t/**\n\t * Load edges for a session from a JSON file.\n\t *\n\t * @param sessionId - The session ID\n\t * @returns Edges array (empty if file is missing or corrupted)\n\t */\n\tpublic async loadEdges(sessionId: string): Promise<Edge[]> {\n\t\tconst startTime = Date.now();\n\t\ttry {\n\t\t\tconst edgePath = this._safeEdgePath(sessionId);\n\n\t\t\tif (!existsSync(edgePath)) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst content = await readFile(edgePath, 'utf-8');\n\t\t\t\tconst data = JSON.parse(content) as Edge[];\n\t\t\t\treturn Array.isArray(data) ? data : [];\n\t\t\t} catch {\n\t\t\t\treturn [];\n\t\t\t}\n\t\t} finally {\n\t\t\tthis._recordOperationDuration('load_edges', startTime);\n\t\t}\n\t}\n\n\t/**\n\t * List all session IDs that have persisted edge files.\n\t *\n\t * @returns Array of session identifiers (filenames without .json extension)\n\t */\n\tpublic async listEdgeSessions(): Promise<string[]> {\n\t\ttry {\n\t\t\tconst files = await readdir(this._edgesDir);\n\t\t\treturn files.filter((f) => f.endsWith('.json')).map((f) => f.slice(0, -5));\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Validates session ID format and resolves the summary file path safely.\n\t *\n\t * @param sessionId - The session ID to validate and resolve\n\t * @returns The safe, resolved summary file path\n\t * @throws Error if session ID is invalid or path traversal is detected\n\t */\n\tprivate _safeSummaryPath(sessionId: string): string {\n\t\tconst validSessionIdPattern = /^[a-zA-Z0-9_-]{1,100}$/;\n\t\tif (!validSessionIdPattern.test(sessionId)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid session ID for summaries: must be 1-100 alphanumeric characters, hyphens, or underscores only`\n\t\t\t);\n\t\t}\n\n\t\tconst resolved = resolve(this._summariesDir, `${sessionId}.json`);\n\t\tconst normalizedSummariesDir = resolve(this._summariesDir);\n\n\t\tif (!resolved.startsWith(normalizedSummariesDir + sep)) {\n\t\t\tthrow new Error(`Invalid session ID: path traversal detected`);\n\t\t}\n\n\t\treturn resolved;\n\t}\n\n\t/**\n\t * Save summaries for a session to a JSON file using an atomic\n\t * write (tmp file + rename) to prevent partial-write corruption.\n\t *\n\t * @param sessionId - The session ID\n\t * @param summaries - Summaries to persist (sorted by createdAt before write)\n\t */\n\tpublic async saveSummaries(sessionId: string, summaries: readonly Summary[]): Promise<void> {\n\t\tconst startTime = Date.now();\n\t\ttry {\n\t\t\tawait this._ensureDirectories();\n\n\t\t\tif (!existsSync(this._summariesDir)) {\n\t\t\t\tawait mkdir(this._summariesDir, { recursive: true });\n\t\t\t}\n\n\t\t\tconst summaryPath = this._safeSummaryPath(sessionId);\n\n\t\t\tif (summaries.length === 0) {\n\t\t\t\tif (existsSync(summaryPath)) {\n\t\t\t\t\tawait unlink(summaryPath);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst sorted = [...summaries].sort((a, b) => a.createdAt - b.createdAt);\n\t\t\tconst tmpPath = `${summaryPath}.tmp`;\n\t\t\tawait writeFile(tmpPath, JSON.stringify(sorted, null, 2), 'utf-8');\n\t\t\tawait rename(tmpPath, summaryPath);\n\t\t} finally {\n\t\t\tthis._recordOperationDuration('save_summaries', startTime);\n\t\t}\n\t}\n\n\t/**\n\t * Load summaries for a session from a JSON file.\n\t *\n\t * @param sessionId - The session ID\n\t * @returns Summaries array (empty if file is missing or corrupted)\n\t */\n\tpublic async loadSummaries(sessionId: string): Promise<Summary[]> {\n\t\tconst startTime = Date.now();\n\t\ttry {\n\t\t\tconst summaryPath = this._safeSummaryPath(sessionId);\n\n\t\t\tif (!existsSync(summaryPath)) {\n\t\t\t\treturn [];\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst content = await readFile(summaryPath, 'utf-8');\n\t\t\t\tconst data = JSON.parse(content) as Summary[];\n\t\t\t\tif (!Array.isArray(data)) return [];\n\t\t\t\treturn [...data].sort((a, b) => a.createdAt - b.createdAt);\n\t\t\t} catch {\n\t\t\t\treturn [];\n\t\t\t}\n\t\t} finally {\n\t\t\tthis._recordOperationDuration('load_summaries', startTime);\n\t\t}\n\t}\n}\n"],"names":["FilePersistence","options","defaultDataDir","existsSync","join","homedir","operation","startTime","durationSeconds","Date","mkdir","branchId","validBranchIdPattern","Error","resolved","resolve","normalizedBranchesDir","sep","thought","history","writeFile","JSON","content","readFile","data","Array","thoughts","branchPath","undefined","unlink","files","readdir","file","edgeFiles","summaryFiles","f","sessionId","validSessionIdPattern","normalizedEdgesDir","edges","edgePath","sorted","a","b","normalizedSummariesDir","summaries","summaryPath","tmpPath","rename"],"mappings":";;;;AAwBO,MAAMA;IACJ,SAAiB;IACjB,aAAqB;IACrB,aAAqB;IACrB,UAAkB;IAClB,cAAsB;IACtB,gBAAwB;IACxB,iBAA0B;IAC1B,SAAoB;IAE5B,YAAYC,OAA+D,CAAE;QAE5E,MAAMC,iBAAiBC,WAAW,kBAC/B,iBACAC,KAAKC,WAAW;QACnB,IAAI,CAAC,QAAQ,GAAGJ,SAAS,WAAWC;QACpC,IAAI,CAAC,YAAY,GAAGE,KAAK,IAAI,CAAC,QAAQ,EAAE;QACxC,IAAI,CAAC,YAAY,GAAGA,KAAK,IAAI,CAAC,QAAQ,EAAE;QACxC,IAAI,CAAC,SAAS,GAAGA,KAAK,IAAI,CAAC,QAAQ,EAAE;QACrC,IAAI,CAAC,aAAa,GAAGA,KAAK,IAAI,CAAC,QAAQ,EAAE;QACzC,IAAI,CAAC,eAAe,GAAGH,SAAS,kBAAkB;QAClD,IAAI,CAAC,gBAAgB,GAAGA,SAAS,mBAAmB;QACpD,IAAI,CAAC,QAAQ,GAAGA,SAAS;IAC1B;IAEQ,yBAAyBK,SAAiB,EAAEC,SAAiB,EAAQ;QAC5E,MAAMC,kBAAmBC,AAAAA,CAAAA,KAAK,GAAG,KAAKF,SAAQ,IAAK;QACnD,IAAI,CAAC,QAAQ,EAAE,UAAU,mCAAmCC,iBAAiB;YAAEF;QAAU;IAC1F;IAKA,MAAc,qBAAoC;QACjD,IAAI,CAACH,WAAW,IAAI,CAAC,QAAQ,GAC5B,MAAMO,MAAM,IAAI,CAAC,QAAQ,EAAE;YAAE,WAAW;QAAK;QAE9C,IAAI,IAAI,CAAC,gBAAgB,IAAI,CAACP,WAAW,IAAI,CAAC,YAAY,GACzD,MAAMO,MAAM,IAAI,CAAC,YAAY,EAAE;YAAE,WAAW;QAAK;QAElD,IAAI,CAACP,WAAW,IAAI,CAAC,SAAS,GAC7B,MAAMO,MAAM,IAAI,CAAC,SAAS,EAAE;YAAE,WAAW;QAAK;QAE/C,IAAI,CAACP,WAAW,IAAI,CAAC,aAAa,GACjC,MAAMO,MAAM,IAAI,CAAC,aAAa,EAAE;YAAE,WAAW;QAAK;IAEpD;IAaQ,gBAAgBC,QAAgB,EAAU;QAEjD,MAAMC,uBAAuB;QAC7B,IAAI,CAACA,qBAAqB,IAAI,CAACD,WAC9B,MAAM,IAAIE,MACT;QAIF,MAAMC,WAAWC,QAAQ,IAAI,CAAC,YAAY,EAAE,GAAGJ,SAAS,KAAK,CAAC;QAC9D,MAAMK,wBAAwBD,QAAQ,IAAI,CAAC,YAAY;QAGvD,IAAI,CAACD,SAAS,UAAU,CAACE,wBAAwBC,MAChD,MAAM,IAAIJ,MAAM;QAGjB,OAAOC;IACR;IAEA,MAAa,YAAYI,OAAoB,EAAiB;QAC7D,MAAMX,YAAYE,KAAK,GAAG;QAC1B,IAAI;YACH,MAAM,IAAI,CAAC,kBAAkB;YAE7B,MAAMU,UAAU,MAAM,IAAI,CAAC,WAAW;YACtCA,QAAQ,IAAI,CAACD;YAEb,IAAIC,QAAQ,MAAM,GAAG,IAAI,CAAC,eAAe,EACxCA,QAAQ,MAAM,CAAC,GAAGA,QAAQ,MAAM,GAAG,IAAI,CAAC,eAAe;YAGxD,MAAMC,UAAU,IAAI,CAAC,YAAY,EAAEC,KAAK,SAAS,CAACF,SAAS,MAAM,IAAI;QACtE,SAAU;YACT,IAAI,CAAC,wBAAwB,CAAC,gBAAgBZ;QAC/C;IACD;IAEA,MAAa,cAAsC;QAClD,MAAMA,YAAYE,KAAK,GAAG;QAC1B,IAAI;YACH,IAAI,CAACN,WAAW,IAAI,CAAC,YAAY,GAChC,OAAO,EAAE;YAGV,MAAMmB,UAAU,MAAMC,SAAS,IAAI,CAAC,YAAY,EAAE;YAClD,MAAMC,OAAOH,KAAK,KAAK,CAACC;YAGxB,OAAOG,MAAM,OAAO,CAACD,QAAQA,OAAO,EAAE;QACvC,EAAE,OAAM;YAEP,OAAO,EAAE;QACV,SAAU;YACT,IAAI,CAAC,wBAAwB,CAAC,gBAAgBjB;QAC/C;IACD;IAEA,MAAa,WAAWI,QAAgB,EAAEe,QAAuB,EAAiB;QACjF,MAAMnB,YAAYE,KAAK,GAAG;QAC1B,IAAI;YACH,IAAI,CAAC,IAAI,CAAC,gBAAgB,EACzB;YAGD,MAAM,IAAI,CAAC,kBAAkB;YAE7B,MAAMkB,aAAa,IAAI,CAAC,eAAe,CAAChB;YACxC,MAAMS,UAAUO,YAAYN,KAAK,SAAS,CAACK,UAAU,MAAM,IAAI;QAChE,SAAU;YACT,IAAI,CAAC,wBAAwB,CAAC,eAAenB;QAC9C;IACD;IAEA,MAAa,WAAWI,QAAgB,EAAsC;QAC7E,MAAMJ,YAAYE,KAAK,GAAG;QAC1B,IAAI;YACH,IAAI,CAAC,IAAI,CAAC,gBAAgB,EACzB;YAGD,MAAMkB,aAAa,IAAI,CAAC,eAAe,CAAChB;YAExC,IAAI;gBACH,IAAI,CAACR,WAAWwB,aACf;gBAGD,MAAML,UAAU,MAAMC,SAASI,YAAY;gBAC3C,MAAMH,OAAOH,KAAK,KAAK,CAACC;gBAExB,OAAOG,MAAM,OAAO,CAACD,QAAQA,OAAOI;YACrC,EAAE,OAAM;gBACP;YACD;QACD,SAAU;YACT,IAAI,CAAC,wBAAwB,CAAC,eAAerB;QAC9C;IACD;IAEA,MAAa,eAAkC;QAC9C,OAAO,IAAI,CAAC,YAAY;IACzB;IAEA,MAAa,QAAuB;QACnC,IAAI;YAEH,IAAIJ,WAAW,IAAI,CAAC,YAAY,GAC/B,MAAM0B,OAAO,IAAI,CAAC,YAAY;YAI/B,IAAI,IAAI,CAAC,gBAAgB,IAAI1B,WAAW,IAAI,CAAC,YAAY,GAAG;gBAC3D,MAAM2B,QAAQ,MAAMC,QAAQ,IAAI,CAAC,YAAY;gBAC7C,KAAK,MAAMC,QAAQF,MAClB,IAAIE,KAAK,QAAQ,CAAC,UACjB,MAAMH,OAAOzB,KAAK,IAAI,CAAC,YAAY,EAAE4B;YAGxC;YAGA,IAAI7B,WAAW,IAAI,CAAC,SAAS,GAAG;gBAC/B,MAAM8B,YAAY,MAAMF,QAAQ,IAAI,CAAC,SAAS;gBAC9C,KAAK,MAAMC,QAAQC,UAClB,IAAID,KAAK,QAAQ,CAAC,UACjB,MAAMH,OAAOzB,KAAK,IAAI,CAAC,SAAS,EAAE4B;YAGrC;YAGA,IAAI7B,WAAW,IAAI,CAAC,aAAa,GAAG;gBACnC,MAAM+B,eAAe,MAAMH,QAAQ,IAAI,CAAC,aAAa;gBACrD,KAAK,MAAMC,QAAQE,aAClB,IAAIF,KAAK,QAAQ,CAAC,UACjB,MAAMH,OAAOzB,KAAK,IAAI,CAAC,aAAa,EAAE4B;YAGzC;QACD,EAAE,OAAM,CAER;IACD;IAEA,MAAa,UAA4B;QACxC,IAAI;YACH,MAAM,IAAI,CAAC,kBAAkB;YAC7B,OAAO;QACR,EAAE,OAAM;YACP,OAAO;QACR;IACD;IAKO,aAAqB;QAC3B,OAAO,IAAI,CAAC,QAAQ;IACrB;IAKA,MAAa,eAAkC;QAC9C,IAAI,CAAC,IAAI,CAAC,gBAAgB,IAAI,CAAC7B,WAAW,IAAI,CAAC,YAAY,GAC1D,OAAO,EAAE;QAGV,IAAI;YACH,MAAM2B,QAAQ,MAAMC,QAAQ,IAAI,CAAC,YAAY;YAC7C,OAAOD,MAAM,MAAM,CAAC,CAACK,IAAMA,EAAE,QAAQ,CAAC,UAAU,GAAG,CAAC,CAACA,IAAMA,EAAE,OAAO,CAAC,SAAS;QAC/E,EAAE,OAAM;YACP,OAAO,EAAE;QACV;IACD;IAMA,MAAa,QAAuB,CAEpC;IASQ,cAAcC,SAAiB,EAAU;QAChD,MAAMC,wBAAwB;QAC9B,IAAI,CAACA,sBAAsB,IAAI,CAACD,YAC/B,MAAM,IAAIvB,MACT;QAIF,MAAMC,WAAWC,QAAQ,IAAI,CAAC,SAAS,EAAE,GAAGqB,UAAU,KAAK,CAAC;QAC5D,MAAME,qBAAqBvB,QAAQ,IAAI,CAAC,SAAS;QAEjD,IAAI,CAACD,SAAS,UAAU,CAACwB,qBAAqBrB,MAC7C,MAAM,IAAIJ,MAAM;QAGjB,OAAOC;IACR;IAQA,MAAa,UAAUsB,SAAiB,EAAEG,KAAsB,EAAiB;QAChF,MAAMhC,YAAYE,KAAK,GAAG;QAC1B,IAAI;YACH,MAAM,IAAI,CAAC,kBAAkB;YAE7B,IAAI,CAACN,WAAW,IAAI,CAAC,SAAS,GAC7B,MAAMO,MAAM,IAAI,CAAC,SAAS,EAAE;gBAAE,WAAW;YAAK;YAG/C,MAAM8B,WAAW,IAAI,CAAC,aAAa,CAACJ;YAEpC,IAAIG,AAAiB,MAAjBA,MAAM,MAAM,EAAQ;gBACvB,IAAIpC,WAAWqC,WACd,MAAMX,OAAOW;gBAEd;YACD;YAEA,MAAMC,SAAS;mBAAIF;aAAM,CAAC,IAAI,CAAC,CAACG,GAAGC,IAAMD,EAAE,SAAS,GAAGC,EAAE,SAAS;YAClE,MAAMvB,UAAUoB,UAAUnB,KAAK,SAAS,CAACoB,QAAQ,MAAM,IAAI;QAC5D,SAAU;YACT,IAAI,CAAC,wBAAwB,CAAC,cAAclC;QAC7C;IACD;IAQA,MAAa,UAAU6B,SAAiB,EAAmB;QAC1D,MAAM7B,YAAYE,KAAK,GAAG;QAC1B,IAAI;YACH,MAAM+B,WAAW,IAAI,CAAC,aAAa,CAACJ;YAEpC,IAAI,CAACjC,WAAWqC,WACf,OAAO,EAAE;YAGV,IAAI;gBACH,MAAMlB,UAAU,MAAMC,SAASiB,UAAU;gBACzC,MAAMhB,OAAOH,KAAK,KAAK,CAACC;gBACxB,OAAOG,MAAM,OAAO,CAACD,QAAQA,OAAO,EAAE;YACvC,EAAE,OAAM;gBACP,OAAO,EAAE;YACV;QACD,SAAU;YACT,IAAI,CAAC,wBAAwB,CAAC,cAAcjB;QAC7C;IACD;IAOA,MAAa,mBAAsC;QAClD,IAAI;YACH,MAAMuB,QAAQ,MAAMC,QAAQ,IAAI,CAAC,SAAS;YAC1C,OAAOD,MAAM,MAAM,CAAC,CAACK,IAAMA,EAAE,QAAQ,CAAC,UAAU,GAAG,CAAC,CAACA,IAAMA,EAAE,KAAK,CAAC,GAAG;QACvE,EAAE,OAAM;YACP,OAAO,EAAE;QACV;IACD;IASQ,iBAAiBC,SAAiB,EAAU;QACnD,MAAMC,wBAAwB;QAC9B,IAAI,CAACA,sBAAsB,IAAI,CAACD,YAC/B,MAAM,IAAIvB,MACT;QAIF,MAAMC,WAAWC,QAAQ,IAAI,CAAC,aAAa,EAAE,GAAGqB,UAAU,KAAK,CAAC;QAChE,MAAMQ,yBAAyB7B,QAAQ,IAAI,CAAC,aAAa;QAEzD,IAAI,CAACD,SAAS,UAAU,CAAC8B,yBAAyB3B,MACjD,MAAM,IAAIJ,MAAM;QAGjB,OAAOC;IACR;IASA,MAAa,cAAcsB,SAAiB,EAAES,SAA6B,EAAiB;QAC3F,MAAMtC,YAAYE,KAAK,GAAG;QAC1B,IAAI;YACH,MAAM,IAAI,CAAC,kBAAkB;YAE7B,IAAI,CAACN,WAAW,IAAI,CAAC,aAAa,GACjC,MAAMO,MAAM,IAAI,CAAC,aAAa,EAAE;gBAAE,WAAW;YAAK;YAGnD,MAAMoC,cAAc,IAAI,CAAC,gBAAgB,CAACV;YAE1C,IAAIS,AAAqB,MAArBA,UAAU,MAAM,EAAQ;gBAC3B,IAAI1C,WAAW2C,cACd,MAAMjB,OAAOiB;gBAEd;YACD;YAEA,MAAML,SAAS;mBAAII;aAAU,CAAC,IAAI,CAAC,CAACH,GAAGC,IAAMD,EAAE,SAAS,GAAGC,EAAE,SAAS;YACtE,MAAMI,UAAU,GAAGD,YAAY,IAAI,CAAC;YACpC,MAAM1B,UAAU2B,SAAS1B,KAAK,SAAS,CAACoB,QAAQ,MAAM,IAAI;YAC1D,MAAMO,OAAOD,SAASD;QACvB,SAAU;YACT,IAAI,CAAC,wBAAwB,CAAC,kBAAkBvC;QACjD;IACD;IAQA,MAAa,cAAc6B,SAAiB,EAAsB;QACjE,MAAM7B,YAAYE,KAAK,GAAG;QAC1B,IAAI;YACH,MAAMqC,cAAc,IAAI,CAAC,gBAAgB,CAACV;YAE1C,IAAI,CAACjC,WAAW2C,cACf,OAAO,EAAE;YAGV,IAAI;gBACH,MAAMxB,UAAU,MAAMC,SAASuB,aAAa;gBAC5C,MAAMtB,OAAOH,KAAK,KAAK,CAACC;gBACxB,IAAI,CAACG,MAAM,OAAO,CAACD,OAAO,OAAO,EAAE;gBACnC,OAAO;uBAAIA;iBAAK,CAAC,IAAI,CAAC,CAACkB,GAAGC,IAAMD,EAAE,SAAS,GAAGC,EAAE,SAAS;YAC1D,EAAE,OAAM;gBACP,OAAO,EAAE;YACV;QACD,SAAU;YACT,IAAI,CAAC,wBAAwB,CAAC,kBAAkBpC;QACjD;IACD;AACD"}
|
|
@@ -71,6 +71,12 @@ export declare class MemoryPersistence implements PersistenceBackend {
|
|
|
71
71
|
* @returns Array of persisted edges, sorted by createdAt
|
|
72
72
|
*/
|
|
73
73
|
loadEdges(sessionId: string): Promise<Edge[]>;
|
|
74
|
+
/**
|
|
75
|
+
* List all session IDs that have persisted edges in memory.
|
|
76
|
+
*
|
|
77
|
+
* @returns Array of session identifiers with persisted edges
|
|
78
|
+
*/
|
|
79
|
+
listEdgeSessions(): Promise<string[]>;
|
|
74
80
|
/**
|
|
75
81
|
* Save summaries for a session, replacing any previously saved summaries.
|
|
76
82
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MemoryPersistence.d.ts","sourceRoot":"","sources":["../../src/persistence/MemoryPersistence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAElE;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACxC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,iBAAkB,YAAW,kBAAkB;IAC3D,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,SAAS,CAAyC;IAC1D,OAAO,CAAC,QAAQ,CAAC,CAAS;IAC1B,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,UAAU,CAAqC;gBAE3C,OAAO,GAAE,wBAA6B;IAIrC,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAShD,WAAW,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAIrC,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpE,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,GAAG,SAAS,CAAC;IAKhE,YAAY,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAI9C;;OAEG;IACU,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IAIxC;;OAEG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAOnC;;OAEG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAInC;;;;;OAKG;IACU,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAQhF;;;;;;OAMG;IACU,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAM1D;;;;;OAKG;IACU,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ3F;;;;;;OAMG;IACU,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAMjE;;OAEG;IACI,cAAc,IAAI,MAAM;IAI/B;;OAEG;IACI,cAAc,IAAI,MAAM;IAI/B;;OAEG;IACI,YAAY,IAAI,MAAM,EAAE;CAG/B"}
|
|
1
|
+
{"version":3,"file":"MemoryPersistence.d.ts","sourceRoot":"","sources":["../../src/persistence/MemoryPersistence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAElE;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACxC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,iBAAkB,YAAW,kBAAkB;IAC3D,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,SAAS,CAAyC;IAC1D,OAAO,CAAC,QAAQ,CAAC,CAAS;IAC1B,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,UAAU,CAAqC;gBAE3C,OAAO,GAAE,wBAA6B;IAIrC,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAShD,WAAW,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAIrC,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpE,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,GAAG,SAAS,CAAC;IAKhE,YAAY,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAI9C;;OAEG;IACU,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IAIxC;;OAEG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAOnC;;OAEG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAInC;;;;;OAKG;IACU,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAQhF;;;;;;OAMG;IACU,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAM1D;;;;OAIG;IACU,gBAAgB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAIlD;;;;;OAKG;IACU,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ3F;;;;;;OAMG;IACU,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAMjE;;OAEG;IACI,cAAc,IAAI,MAAM;IAI/B;;OAEG;IACI,cAAc,IAAI,MAAM;IAI/B;;OAEG;IACI,YAAY,IAAI,MAAM,EAAE;CAG/B"}
|
|
@@ -53,6 +53,9 @@ class MemoryPersistence {
|
|
|
53
53
|
...edges
|
|
54
54
|
].sort((a, b)=>a.createdAt - b.createdAt);
|
|
55
55
|
}
|
|
56
|
+
async listEdgeSessions() {
|
|
57
|
+
return Array.from(this._edges.keys());
|
|
58
|
+
}
|
|
56
59
|
async saveSummaries(sessionId, summaries) {
|
|
57
60
|
if (0 === summaries.length) this._summaries.delete(sessionId);
|
|
58
61
|
else this._summaries.set(sessionId, [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persistence/MemoryPersistence.js","sources":["../../src/persistence/MemoryPersistence.ts"],"sourcesContent":["import type { ThoughtData } from '../core/thought.js';\nimport type { Edge } from '../core/graph/Edge.js';\nimport type { Summary } from '../core/compression/Summary.js';\nimport type { PersistenceBackend } from './PersistenceBackend.js';\n\n/**\n * Configuration options for MemoryPersistence.\n */\nexport interface MemoryPersistenceOptions {\n\t/**\n\t * Maximum number of thoughts to keep in memory.\n\t * Older thoughts are trimmed when limit is exceeded.\n\t * Set to 0 or undefined for unlimited.\n\t * @default undefined (unlimited)\n\t */\n\tmaxSize?: number;\n}\n\n/**\n * In-memory persistence backend for testing purposes.\n *\n * This backend stores all data in memory and provides no durability.\n * It's useful for testing and development where persistence is not needed.\n *\n * @example\n * ```typescript\n * // Unlimited history\n * const backend = new MemoryPersistence();\n *\n * // Limited to 1000 thoughts\n * const backend = new MemoryPersistence({ maxSize: 1000 });\n *\n * await backend.saveThought(thought);\n * const history = await backend.loadHistory();\n * ```\n */\nexport class MemoryPersistence implements PersistenceBackend {\n\tprivate _history: ThoughtData[] = [];\n\tprivate _branches: Map<string, ThoughtData[]> = new Map();\n\tprivate _maxSize?: number;\n\tprivate _edges: Map<string, Edge[]> = new Map();\n\tprivate _summaries: Map<string, Summary[]> = new Map();\n\n\tconstructor(options: MemoryPersistenceOptions = {}) {\n\t\tthis._maxSize = options.maxSize && options.maxSize > 0 ? options.maxSize : undefined;\n\t}\n\n\tpublic async saveThought(thought: ThoughtData): Promise<void> {\n\t\tthis._history.push(thought);\n\n\t\t// Trim if maxSize is set and exceeded\n\t\tif (this._maxSize !== undefined && this._history.length > this._maxSize) {\n\t\t\tthis._history = this._history.slice(-this._maxSize);\n\t\t}\n\t}\n\n\tpublic async loadHistory(): Promise<ThoughtData[]> {\n\t\treturn [...this._history];\n\t}\n\n\tpublic async saveBranch(branchId: string, thoughts: ThoughtData[]): Promise<void> {\n\t\tthis._branches.set(branchId, [...thoughts]);\n\t}\n\n\tpublic async loadBranch(branchId: string): Promise<ThoughtData[] | undefined> {\n\t\tconst branch = this._branches.get(branchId);\n\t\treturn branch ? [...branch] : undefined;\n\t}\n\n\tpublic async listBranches(): Promise<string[]> {\n\t\treturn this.getBranchIds();\n\t}\n\n\t/**\n\t * In-memory backend is always healthy.\n\t */\n\tpublic async healthy(): Promise<boolean> {\n\t\treturn true;\n\t}\n\n\t/**\n\t * Clear all data from memory.\n\t */\n\tpublic async clear(): Promise<void> {\n\t\tthis._history = [];\n\t\tthis._branches.clear();\n\t\tthis._edges.clear();\n\t\tthis._summaries.clear();\n\t}\n\n\t/**\n\t * No resources to release for in-memory backend.\n\t */\n\tpublic async close(): Promise<void> {\n\t\t// No-op for in-memory backend\n\t}\n\n\t/**\n\t * Save edges for a session, replacing any previously saved edges.\n\t *\n\t * @param sessionId - The session whose edges to persist\n\t * @param edges - Array of edges to save\n\t */\n\tpublic async saveEdges(sessionId: string, edges: readonly Edge[]): Promise<void> {\n\t\tif (edges.length === 0) {\n\t\t\tthis._edges.delete(sessionId);\n\t\t} else {\n\t\t\tthis._edges.set(sessionId, [...edges]);\n\t\t}\n\t}\n\n\t/**\n\t * Load edges for a session from memory.\n\t * Returns edges sorted by createdAt ascending.\n\t *\n\t * @param sessionId - The session whose edges to load\n\t * @returns Array of persisted edges, sorted by createdAt\n\t */\n\tpublic async loadEdges(sessionId: string): Promise<Edge[]> {\n\t\tconst edges = this._edges.get(sessionId);\n\t\tif (!edges) return [];\n\t\treturn [...edges].sort((a, b) => a.createdAt - b.createdAt);\n\t}\n\n\t/**\n\t * Save summaries for a session, replacing any previously saved summaries.\n\t *\n\t * @param sessionId - The session whose summaries to persist\n\t * @param summaries - Array of summaries to save\n\t */\n\tpublic async saveSummaries(sessionId: string, summaries: readonly Summary[]): Promise<void> {\n\t\tif (summaries.length === 0) {\n\t\t\tthis._summaries.delete(sessionId);\n\t\t} else {\n\t\t\tthis._summaries.set(sessionId, [...summaries]);\n\t\t}\n\t}\n\n\t/**\n\t * Load summaries for a session from memory.\n\t * Returns summaries sorted by createdAt ascending.\n\t *\n\t * @param sessionId - The session whose summaries to load\n\t * @returns Array of persisted summaries, sorted by createdAt\n\t */\n\tpublic async loadSummaries(sessionId: string): Promise<Summary[]> {\n\t\tconst summaries = this._summaries.get(sessionId);\n\t\tif (!summaries) return [];\n\t\treturn [...summaries].sort((a, b) => a.createdAt - b.createdAt);\n\t}\n\n\t/**\n\t * Get the current number of thoughts in memory.\n\t */\n\tpublic getHistorySize(): number {\n\t\treturn this._history.length;\n\t}\n\n\t/**\n\t * Get the current number of branches in memory.\n\t */\n\tpublic getBranchCount(): number {\n\t\treturn this._branches.size;\n\t}\n\n\t/**\n\t * Get all branch IDs.\n\t */\n\tpublic getBranchIds(): string[] {\n\t\treturn Array.from(this._branches.keys());\n\t}\n}\n"],"names":["MemoryPersistence","Map","options","undefined","thought","branchId","thoughts","branch","sessionId","edges","a","b","
|
|
1
|
+
{"version":3,"file":"persistence/MemoryPersistence.js","sources":["../../src/persistence/MemoryPersistence.ts"],"sourcesContent":["import type { ThoughtData } from '../core/thought.js';\nimport type { Edge } from '../core/graph/Edge.js';\nimport type { Summary } from '../core/compression/Summary.js';\nimport type { PersistenceBackend } from './PersistenceBackend.js';\n\n/**\n * Configuration options for MemoryPersistence.\n */\nexport interface MemoryPersistenceOptions {\n\t/**\n\t * Maximum number of thoughts to keep in memory.\n\t * Older thoughts are trimmed when limit is exceeded.\n\t * Set to 0 or undefined for unlimited.\n\t * @default undefined (unlimited)\n\t */\n\tmaxSize?: number;\n}\n\n/**\n * In-memory persistence backend for testing purposes.\n *\n * This backend stores all data in memory and provides no durability.\n * It's useful for testing and development where persistence is not needed.\n *\n * @example\n * ```typescript\n * // Unlimited history\n * const backend = new MemoryPersistence();\n *\n * // Limited to 1000 thoughts\n * const backend = new MemoryPersistence({ maxSize: 1000 });\n *\n * await backend.saveThought(thought);\n * const history = await backend.loadHistory();\n * ```\n */\nexport class MemoryPersistence implements PersistenceBackend {\n\tprivate _history: ThoughtData[] = [];\n\tprivate _branches: Map<string, ThoughtData[]> = new Map();\n\tprivate _maxSize?: number;\n\tprivate _edges: Map<string, Edge[]> = new Map();\n\tprivate _summaries: Map<string, Summary[]> = new Map();\n\n\tconstructor(options: MemoryPersistenceOptions = {}) {\n\t\tthis._maxSize = options.maxSize && options.maxSize > 0 ? options.maxSize : undefined;\n\t}\n\n\tpublic async saveThought(thought: ThoughtData): Promise<void> {\n\t\tthis._history.push(thought);\n\n\t\t// Trim if maxSize is set and exceeded\n\t\tif (this._maxSize !== undefined && this._history.length > this._maxSize) {\n\t\t\tthis._history = this._history.slice(-this._maxSize);\n\t\t}\n\t}\n\n\tpublic async loadHistory(): Promise<ThoughtData[]> {\n\t\treturn [...this._history];\n\t}\n\n\tpublic async saveBranch(branchId: string, thoughts: ThoughtData[]): Promise<void> {\n\t\tthis._branches.set(branchId, [...thoughts]);\n\t}\n\n\tpublic async loadBranch(branchId: string): Promise<ThoughtData[] | undefined> {\n\t\tconst branch = this._branches.get(branchId);\n\t\treturn branch ? [...branch] : undefined;\n\t}\n\n\tpublic async listBranches(): Promise<string[]> {\n\t\treturn this.getBranchIds();\n\t}\n\n\t/**\n\t * In-memory backend is always healthy.\n\t */\n\tpublic async healthy(): Promise<boolean> {\n\t\treturn true;\n\t}\n\n\t/**\n\t * Clear all data from memory.\n\t */\n\tpublic async clear(): Promise<void> {\n\t\tthis._history = [];\n\t\tthis._branches.clear();\n\t\tthis._edges.clear();\n\t\tthis._summaries.clear();\n\t}\n\n\t/**\n\t * No resources to release for in-memory backend.\n\t */\n\tpublic async close(): Promise<void> {\n\t\t// No-op for in-memory backend\n\t}\n\n\t/**\n\t * Save edges for a session, replacing any previously saved edges.\n\t *\n\t * @param sessionId - The session whose edges to persist\n\t * @param edges - Array of edges to save\n\t */\n\tpublic async saveEdges(sessionId: string, edges: readonly Edge[]): Promise<void> {\n\t\tif (edges.length === 0) {\n\t\t\tthis._edges.delete(sessionId);\n\t\t} else {\n\t\t\tthis._edges.set(sessionId, [...edges]);\n\t\t}\n\t}\n\n\t/**\n\t * Load edges for a session from memory.\n\t * Returns edges sorted by createdAt ascending.\n\t *\n\t * @param sessionId - The session whose edges to load\n\t * @returns Array of persisted edges, sorted by createdAt\n\t */\n\tpublic async loadEdges(sessionId: string): Promise<Edge[]> {\n\t\tconst edges = this._edges.get(sessionId);\n\t\tif (!edges) return [];\n\t\treturn [...edges].sort((a, b) => a.createdAt - b.createdAt);\n\t}\n\n\t/**\n\t * List all session IDs that have persisted edges in memory.\n\t *\n\t * @returns Array of session identifiers with persisted edges\n\t */\n\tpublic async listEdgeSessions(): Promise<string[]> {\n\t\treturn Array.from(this._edges.keys());\n\t}\n\n\t/**\n\t * Save summaries for a session, replacing any previously saved summaries.\n\t *\n\t * @param sessionId - The session whose summaries to persist\n\t * @param summaries - Array of summaries to save\n\t */\n\tpublic async saveSummaries(sessionId: string, summaries: readonly Summary[]): Promise<void> {\n\t\tif (summaries.length === 0) {\n\t\t\tthis._summaries.delete(sessionId);\n\t\t} else {\n\t\t\tthis._summaries.set(sessionId, [...summaries]);\n\t\t}\n\t}\n\n\t/**\n\t * Load summaries for a session from memory.\n\t * Returns summaries sorted by createdAt ascending.\n\t *\n\t * @param sessionId - The session whose summaries to load\n\t * @returns Array of persisted summaries, sorted by createdAt\n\t */\n\tpublic async loadSummaries(sessionId: string): Promise<Summary[]> {\n\t\tconst summaries = this._summaries.get(sessionId);\n\t\tif (!summaries) return [];\n\t\treturn [...summaries].sort((a, b) => a.createdAt - b.createdAt);\n\t}\n\n\t/**\n\t * Get the current number of thoughts in memory.\n\t */\n\tpublic getHistorySize(): number {\n\t\treturn this._history.length;\n\t}\n\n\t/**\n\t * Get the current number of branches in memory.\n\t */\n\tpublic getBranchCount(): number {\n\t\treturn this._branches.size;\n\t}\n\n\t/**\n\t * Get all branch IDs.\n\t */\n\tpublic getBranchIds(): string[] {\n\t\treturn Array.from(this._branches.keys());\n\t}\n}\n"],"names":["MemoryPersistence","Map","options","undefined","thought","branchId","thoughts","branch","sessionId","edges","a","b","Array","summaries"],"mappings":"AAoCO,MAAMA;IACJ,WAA0B,EAAE,CAAC;IAC7B,YAAwC,IAAIC,MAAM;IAClD,SAAkB;IAClB,SAA8B,IAAIA,MAAM;IACxC,aAAqC,IAAIA,MAAM;IAEvD,YAAYC,UAAoC,CAAC,CAAC,CAAE;QACnD,IAAI,CAAC,QAAQ,GAAGA,QAAQ,OAAO,IAAIA,QAAQ,OAAO,GAAG,IAAIA,QAAQ,OAAO,GAAGC;IAC5E;IAEA,MAAa,YAAYC,OAAoB,EAAiB;QAC7D,IAAI,CAAC,QAAQ,CAAC,IAAI,CAACA;QAGnB,IAAI,AAAkBD,WAAlB,IAAI,CAAC,QAAQ,IAAkB,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAC,QAAQ,EACtE,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,QAAQ;IAEpD;IAEA,MAAa,cAAsC;QAClD,OAAO;eAAI,IAAI,CAAC,QAAQ;SAAC;IAC1B;IAEA,MAAa,WAAWE,QAAgB,EAAEC,QAAuB,EAAiB;QACjF,IAAI,CAAC,SAAS,CAAC,GAAG,CAACD,UAAU;eAAIC;SAAS;IAC3C;IAEA,MAAa,WAAWD,QAAgB,EAAsC;QAC7E,MAAME,SAAS,IAAI,CAAC,SAAS,CAAC,GAAG,CAACF;QAClC,OAAOE,SAAS;eAAIA;SAAO,GAAGJ;IAC/B;IAEA,MAAa,eAAkC;QAC9C,OAAO,IAAI,CAAC,YAAY;IACzB;IAKA,MAAa,UAA4B;QACxC,OAAO;IACR;IAKA,MAAa,QAAuB;QACnC,IAAI,CAAC,QAAQ,GAAG,EAAE;QAClB,IAAI,CAAC,SAAS,CAAC,KAAK;QACpB,IAAI,CAAC,MAAM,CAAC,KAAK;QACjB,IAAI,CAAC,UAAU,CAAC,KAAK;IACtB;IAKA,MAAa,QAAuB,CAEpC;IAQA,MAAa,UAAUK,SAAiB,EAAEC,KAAsB,EAAiB;QAChF,IAAIA,AAAiB,MAAjBA,MAAM,MAAM,EACf,IAAI,CAAC,MAAM,CAAC,MAAM,CAACD;aAEnB,IAAI,CAAC,MAAM,CAAC,GAAG,CAACA,WAAW;eAAIC;SAAM;IAEvC;IASA,MAAa,UAAUD,SAAiB,EAAmB;QAC1D,MAAMC,QAAQ,IAAI,CAAC,MAAM,CAAC,GAAG,CAACD;QAC9B,IAAI,CAACC,OAAO,OAAO,EAAE;QACrB,OAAO;eAAIA;SAAM,CAAC,IAAI,CAAC,CAACC,GAAGC,IAAMD,EAAE,SAAS,GAAGC,EAAE,SAAS;IAC3D;IAOA,MAAa,mBAAsC;QAClD,OAAOC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI;IACnC;IAQA,MAAa,cAAcJ,SAAiB,EAAEK,SAA6B,EAAiB;QAC3F,IAAIA,AAAqB,MAArBA,UAAU,MAAM,EACnB,IAAI,CAAC,UAAU,CAAC,MAAM,CAACL;aAEvB,IAAI,CAAC,UAAU,CAAC,GAAG,CAACA,WAAW;eAAIK;SAAU;IAE/C;IASA,MAAa,cAAcL,SAAiB,EAAsB;QACjE,MAAMK,YAAY,IAAI,CAAC,UAAU,CAAC,GAAG,CAACL;QACtC,IAAI,CAACK,WAAW,OAAO,EAAE;QACzB,OAAO;eAAIA;SAAU,CAAC,IAAI,CAAC,CAACH,GAAGC,IAAMD,EAAE,SAAS,GAAGC,EAAE,SAAS;IAC/D;IAKO,iBAAyB;QAC/B,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM;IAC5B;IAKO,iBAAyB;QAC/B,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI;IAC3B;IAKO,eAAyB;QAC/B,OAAOC,MAAM,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI;IACtC;AACD"}
|
|
@@ -72,6 +72,12 @@ export interface PersistenceBackend {
|
|
|
72
72
|
* @returns Array of persisted edges, sorted by createdAt
|
|
73
73
|
*/
|
|
74
74
|
loadEdges(sessionId: string): Promise<Edge[]>;
|
|
75
|
+
/**
|
|
76
|
+
* List all session IDs that have persisted edge data.
|
|
77
|
+
*
|
|
78
|
+
* @returns Array of session identifiers with persisted edges
|
|
79
|
+
*/
|
|
80
|
+
listEdgeSessions(): Promise<string[]>;
|
|
75
81
|
/**
|
|
76
82
|
* Save summaries for a session, replacing any previously saved summaries.
|
|
77
83
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PersistenceBackend.d.ts","sourceRoot":"","sources":["../../src/persistence/PersistenceBackend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAE9D;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IAClC;;;;OAIG;IACH,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjD;;;;;OAKG;IACH,WAAW,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAEtC;;;;;OAKG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAErE;;;;;OAKG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,GAAG,SAAS,CAAC,CAAC;IAEjE;;;;OAIG;IACH,YAAY,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAElC;;;OAGG;IACH,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAE5B;;;OAGG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB;;;OAGG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB;;;;;OAKG;IACH,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpE;;;;;;;OAOG;IACH,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAE9C;;;;;OAKG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/E;;;;;;;OAOG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,iBAAiB;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACvC,OAAO,CAAC,EAAE;QACT,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,eAAe,CAAC,EAAE,OAAO,CAAC;KAC1B,CAAC;CACF"}
|
|
1
|
+
{"version":3,"file":"PersistenceBackend.d.ts","sourceRoot":"","sources":["../../src/persistence/PersistenceBackend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAE9D;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IAClC;;;;OAIG;IACH,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjD;;;;;OAKG;IACH,WAAW,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAEtC;;;;;OAKG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAErE;;;;;OAKG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,GAAG,SAAS,CAAC,CAAC;IAEjE;;;;OAIG;IACH,YAAY,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAElC;;;OAGG;IACH,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAE5B;;;OAGG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB;;;OAGG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB;;;;;OAKG;IACH,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpE;;;;;;;OAOG;IACH,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAE9C;;;;OAIG;IACH,gBAAgB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAEtC;;;;;OAKG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/E;;;;;;;OAOG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,iBAAiB;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACvC,OAAO,CAAC,EAAE;QACT,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,eAAe,CAAC,EAAE,OAAO,CAAC;KAC1B,CAAC;CACF"}
|
|
@@ -66,6 +66,12 @@ export declare class SqlitePersistence implements PersistenceBackend {
|
|
|
66
66
|
* @returns A Promise that resolves to the session's edges in chronological order
|
|
67
67
|
*/
|
|
68
68
|
loadEdges(sessionId: string): Promise<Edge[]>;
|
|
69
|
+
/**
|
|
70
|
+
* List all session IDs that have persisted edges in the database.
|
|
71
|
+
*
|
|
72
|
+
* @returns Array of distinct session identifiers from the edges table
|
|
73
|
+
*/
|
|
74
|
+
listEdgeSessions(): Promise<string[]>;
|
|
69
75
|
/**
|
|
70
76
|
* Persist summaries for a session using replace semantics: deletes any
|
|
71
77
|
* existing summaries for the session, then inserts the provided summaries
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SqlitePersistence.d.ts","sourceRoot":"","sources":["../../src/persistence/SqlitePersistence.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,IAAI,EAAY,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAwBrF;;;;;;;;;;;;;GAaG;AACH,qBAAa,iBAAkB,YAAW,kBAAkB;IAC3D,OAAO,CAAC,GAAG,CAAW;IACtB,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,gBAAgB,CAAU;IAElC,OAAO;IAOP;;;;;;;;;;;;;;OAcG;WACU,MAAM,CAAC,OAAO,CAAC,EAAE,iBAAiB,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAmCvF,OAAO,CAAC,iBAAiB;IAwEZ,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBhD,WAAW,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAerC,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAWpE,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,GAAG,SAAS,CAAC;IAoBhE,YAAY,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAUjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAStB,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IAUxC;;;OAGG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAYnC;;;;;;;OAOG;IACU,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BhF;;;;;OAKG;IACU,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAyB1D;;;;;;;;OAQG;IACU,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA+B3F;;;;;OAKG;IACU,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAgCjE;;OAEG;IACI,QAAQ,IAAI;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;KACf;CAiBD"}
|
|
1
|
+
{"version":3,"file":"SqlitePersistence.d.ts","sourceRoot":"","sources":["../../src/persistence/SqlitePersistence.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,IAAI,EAAY,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAwBrF;;;;;;;;;;;;;GAaG;AACH,qBAAa,iBAAkB,YAAW,kBAAkB;IAC3D,OAAO,CAAC,GAAG,CAAW;IACtB,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,gBAAgB,CAAU;IAElC,OAAO;IAOP;;;;;;;;;;;;;;OAcG;WACU,MAAM,CAAC,OAAO,CAAC,EAAE,iBAAiB,CAAC,SAAS,CAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAmCvF,OAAO,CAAC,iBAAiB;IAwEZ,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBhD,WAAW,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAerC,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAWpE,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,GAAG,SAAS,CAAC;IAoBhE,YAAY,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAUjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAStB,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IAUxC;;;OAGG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAYnC;;;;;;;OAOG;IACU,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BhF;;;;;OAKG;IACU,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAyB1D;;;;OAIG;IACU,gBAAgB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAOlD;;;;;;;;OAQG;IACU,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA+B3F;;;;;OAKG;IACU,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAgCjE;;OAEG;IACI,QAAQ,IAAI;QAClB,YAAY,EAAE,MAAM,CAAC;QACrB,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;KACf;CAiBD"}
|
|
@@ -180,6 +180,10 @@ class SqlitePersistence {
|
|
|
180
180
|
} : {}
|
|
181
181
|
}));
|
|
182
182
|
}
|
|
183
|
+
async listEdgeSessions() {
|
|
184
|
+
const rows = this._db.prepare('SELECT DISTINCT session_id FROM edges').all();
|
|
185
|
+
return rows.map((r)=>r.session_id);
|
|
186
|
+
}
|
|
183
187
|
async saveSummaries(sessionId, summaries) {
|
|
184
188
|
const deleteStmt = this._db.prepare('DELETE FROM summaries WHERE session_id = ?');
|
|
185
189
|
const insertStmt = this._db.prepare('INSERT OR REPLACE INTO summaries (id, session_id, branch_id, root_thought_id, covered_ids, covered_range_start, covered_range_end, topics, aggregate_confidence, created_at, meta) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persistence/SqlitePersistence.js","sources":["../../src/persistence/SqlitePersistence.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport type { ThoughtData } from '../core/thought.js';\nimport type { Edge, EdgeKind } from '../core/graph/Edge.js';\nimport type { Summary } from '../core/compression/Summary.js';\nimport type { PersistenceBackend, PersistenceConfig } from './PersistenceBackend.js';\n\n/**\n * Type definition for the better-sqlite3 Database interface.\n * This allows us to use the library without importing it directly.\n */\ninterface Database {\n\texec(sql: string): void;\n\tprepare(sql: string): Statement;\n\tclose(): void;\n\tpragma(pragma: string): unknown;\n}\n\ninterface Statement {\n\trun(...params: unknown[]): RunResult;\n\tget(...params: unknown[]): unknown;\n\tall(...params: unknown[]): unknown[];\n}\n\ninterface RunResult {\n\tchanges: number;\n\tlastInsertRowid: number;\n}\n\n/**\n * SQLite-based persistence backend.\n *\n * Provides efficient, transactional persistence using SQLite.\n * Requires the 'better-sqlite3' package to be installed.\n *\n * @example\n * ```typescript\n * const backend = await SqlitePersistence.create({\n * dbPath: './data/history.db',\n * enableWAL: true\n * });\n * ```\n */\nexport class SqlitePersistence implements PersistenceBackend {\n\tprivate _db: Database;\n\tprivate _maxHistorySize: number;\n\tprivate _persistBranches: boolean;\n\n\tprivate constructor(db: Database, options: PersistenceConfig['options']) {\n\t\tthis._db = db;\n\t\tthis._maxHistorySize = options?.maxHistorySize ?? 10000;\n\t\tthis._persistBranches = options?.persistBranches ?? true;\n\t\tthis._initializeSchema();\n\t}\n\n\t/**\n\t * Creates a new SqlitePersistence instance with dynamic import of better-sqlite3.\n\t *\n\t * @param options - Configuration options\n\t * @returns A Promise that resolves to a SqlitePersistence instance\n\t * @throws Error if better-sqlite3 is not installed\n\t *\n\t * @example\n\t * ```typescript\n\t * const backend = await SqlitePersistence.create({\n\t * dbPath: './data/history.db',\n\t * enableWAL: true\n\t * });\n\t * ```\n\t */\n\tstatic async create(options?: PersistenceConfig['options']): Promise<SqlitePersistence> {\n\t\t// Default to .claude/data in current directory or home directory\n\t\tconst defaultDataDir = existsSync('.claude/data')\n\t\t\t? '.claude/data'\n\t\t\t: join(homedir(), '.claude/data');\n\t\tconst dbPath = options?.dbPath ?? join(defaultDataDir, 'history.db');\n\n\t\t// Load better-sqlite3 dynamically (optional dependency)\n\t\tlet Database: new (path: string) => Database;\n\t\ttry {\n\t\t\tconst module = await import('better-sqlite3');\n\t\t\tDatabase = module.default;\n\t\t} catch {\n\t\t\tthrow new Error(\n\t\t\t\t`SQLite persistence requires 'better-sqlite3' package. Install it with: npm install better-sqlite3`\n\t\t\t);\n\t\t}\n\n\t\tconst db = new Database(dbPath);\n\n\t\t// Enable WAL mode for better concurrency if specified\n\t\tif (options?.enableWAL !== false) {\n\t\t\tdb.pragma('journal_mode = WAL');\n\t\t}\n\n\t\t// Performance and safety PRAGMAs\n\t\tdb.pragma('synchronous = NORMAL');\n\t\tdb.pragma('foreign_keys = ON');\n\t\tdb.pragma('busy_timeout = 5000');\n\t\tdb.pragma('cache_size = -64000'); // 64MB\n\t\tdb.pragma('temp_store = MEMORY');\n\n\t\treturn new SqlitePersistence(db, options);\n\t}\n\n\tprivate _initializeSchema(): void {\n\t\t// Create thoughts table\n\t\tthis._db.exec(`\n CREATE TABLE IF NOT EXISTS thoughts (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n data TEXT NOT NULL,\n created_at INTEGER DEFAULT (strftime('%s', 'now'))\n )\n `);\n\n\t\t// Create branches table\n\t\tif (this._persistBranches) {\n\t\t\tthis._db.exec(`\n CREATE TABLE IF NOT EXISTS branches (\n branch_id TEXT PRIMARY KEY,\n data TEXT NOT NULL,\n updated_at INTEGER DEFAULT (strftime('%s', 'now'))\n )\n `);\n\t\t}\n\n\t\t// Create indexes for better performance\n\t\tthis._db.exec(`\n CREATE INDEX IF NOT EXISTS idx_thoughts_created_at ON thoughts(created_at)\n `);\n\n\t\t// Create edges table for DAG edge storage\n\t\tthis._db.exec(`\n CREATE TABLE IF NOT EXISTS edges (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL,\n from_id TEXT NOT NULL,\n to_id TEXT NOT NULL,\n kind TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n metadata TEXT\n )\n `);\n\n\t\t// Indexes for edge queries\n\t\tthis._db.exec(`\n CREATE INDEX IF NOT EXISTS idx_edges_session ON edges(session_id)\n `);\n\t\tthis._db.exec(`\n CREATE INDEX IF NOT EXISTS idx_edges_from ON edges(session_id, from_id)\n `);\n\t\tthis._db.exec(`\n CREATE INDEX IF NOT EXISTS idx_edges_to ON edges(session_id, to_id)\n `);\n\n\t\t// Create summaries table for compression subsystem\n\t\tthis._db.exec(`\n CREATE TABLE IF NOT EXISTS summaries (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL,\n branch_id TEXT,\n root_thought_id TEXT NOT NULL,\n covered_ids TEXT NOT NULL,\n covered_range_start INTEGER NOT NULL,\n covered_range_end INTEGER NOT NULL,\n topics TEXT NOT NULL,\n aggregate_confidence REAL NOT NULL,\n created_at INTEGER NOT NULL,\n meta TEXT\n )\n `);\n\n\t\tthis._db.exec(`\n CREATE INDEX IF NOT EXISTS idx_summaries_session ON summaries(session_id)\n `);\n\t}\n\n\tpublic async saveThought(thought: ThoughtData): Promise<void> {\n\t\tconst stmt = this._db.prepare('INSERT INTO thoughts (data) VALUES (?)');\n\t\tstmt.run(JSON.stringify(thought));\n\n\t\t// Trim old thoughts if over limit\n\t\tconst countStmt = this._db.prepare('SELECT COUNT(*) as count FROM thoughts');\n\t\tconst { count } = countStmt.get() as { count: number };\n\n\t\tif (count > this._maxHistorySize) {\n\t\t\tconst deleteStmt = this._db.prepare(\n\t\t\t\t`DELETE FROM thoughts WHERE id IN (\n SELECT id FROM thoughts ORDER BY id ASC LIMIT ?\n )`\n\t\t\t);\n\t\t\tdeleteStmt.run(count - this._maxHistorySize);\n\t\t}\n\t}\n\n\tpublic async loadHistory(): Promise<ThoughtData[]> {\n\t\tconst stmt = this._db.prepare('SELECT data FROM thoughts ORDER BY id ASC');\n\t\tconst rows = stmt.all() as { data: string }[];\n\n\t\treturn rows\n\t\t\t.map((row) => {\n\t\t\t\ttry {\n\t\t\t\t\treturn JSON.parse(row.data) as ThoughtData;\n\t\t\t\t} catch {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t})\n\t\t\t.filter((t): t is ThoughtData => t !== null);\n\t}\n\n\tpublic async saveBranch(branchId: string, thoughts: ThoughtData[]): Promise<void> {\n\t\tif (!this._persistBranches) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst stmt = this._db.prepare(\n\t\t\t'INSERT OR REPLACE INTO branches (branch_id, data, updated_at) VALUES (?, ?, strftime(\"%s\", \"now\"))'\n\t\t);\n\t\tstmt.run(branchId, JSON.stringify(thoughts));\n\t}\n\n\tpublic async loadBranch(branchId: string): Promise<ThoughtData[] | undefined> {\n\t\tif (!this._persistBranches) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst stmt = this._db.prepare('SELECT data FROM branches WHERE branch_id = ?');\n\t\tconst row = stmt.get(branchId) as { data: string } | undefined;\n\n\t\tif (!row) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\ttry {\n\t\t\tconst data = JSON.parse(row.data) as ThoughtData[];\n\t\t\treturn Array.isArray(data) ? data : undefined;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tpublic async listBranches(): Promise<string[]> {\n\t\tif (!this._persistBranches) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst stmt = this._db.prepare('SELECT branch_id FROM branches ORDER BY branch_id ASC');\n\t\tconst rows = stmt.all() as { branch_id: string }[];\n\t\treturn rows.map((row) => row.branch_id);\n\t}\n\n\tpublic async clear(): Promise<void> {\n\t\tthis._db.exec('DELETE FROM thoughts');\n\t\tif (this._persistBranches) {\n\t\t\tthis._db.exec('DELETE FROM branches');\n\t\t}\n\t\tthis._db.exec('DELETE FROM edges');\n\t\tthis._db.exec('DELETE FROM summaries');\n\t}\n\n\tpublic async healthy(): Promise<boolean> {\n\t\ttry {\n\t\t\t// Simple health check - try to execute a query\n\t\t\tthis._db.prepare('SELECT 1').get();\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Close the database connection with proper cleanup.\n\t * Runs WAL checkpoint before closing to ensure all data is persisted.\n\t */\n\tpublic async close(): Promise<void> {\n\t\tif (this._db) {\n\t\t\ttry {\n\t\t\t\t// Run WAL checkpoint to ensure all data is persisted\n\t\t\t\tthis._db.pragma('wal_checkpoint(TRUNCATE)');\n\t\t\t} catch {\n\t\t\t\t// Ignore checkpoint errors - still try to close\n\t\t\t}\n\t\t\tthis._db.close();\n\t\t}\n\t}\n\n\t/**\n\t * Persist edges for a session using replace semantics: deletes any existing\n\t * edges for the session, then inserts the provided edges within a transaction.\n\t *\n\t * @param sessionId - Session identifier whose edges are being replaced\n\t * @param edges - The edges to persist for the session\n\t * @returns A Promise that resolves when the edges are persisted\n\t */\n\tpublic async saveEdges(sessionId: string, edges: readonly Edge[]): Promise<void> {\n\t\tconst deleteStmt = this._db.prepare('DELETE FROM edges WHERE session_id = ?');\n\t\tconst insertStmt = this._db.prepare(\n\t\t\t'INSERT INTO edges (id, session_id, from_id, to_id, kind, created_at, metadata) VALUES (?, ?, ?, ?, ?, ?, ?)'\n\t\t);\n\n\t\tconst dbWithTx = this._db as Database & {\n\t\t\ttransaction: (fn: () => void) => () => void;\n\t\t};\n\t\tconst transaction = dbWithTx.transaction(() => {\n\t\t\tdeleteStmt.run(sessionId);\n\t\t\tfor (const edge of edges) {\n\t\t\t\tinsertStmt.run(\n\t\t\t\t\tedge.id,\n\t\t\t\t\tedge.sessionId,\n\t\t\t\t\tedge.from,\n\t\t\t\t\tedge.to,\n\t\t\t\t\tedge.kind,\n\t\t\t\t\tedge.createdAt,\n\t\t\t\t\tedge.metadata ? JSON.stringify(edge.metadata) : null\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\n\t\ttransaction();\n\t}\n\n\t/**\n\t * Load all edges for a session, ordered by `created_at` ascending.\n\t *\n\t * @param sessionId - Session identifier to load edges for\n\t * @returns A Promise that resolves to the session's edges in chronological order\n\t */\n\tpublic async loadEdges(sessionId: string): Promise<Edge[]> {\n\t\tconst stmt = this._db.prepare(\n\t\t\t'SELECT id, session_id, from_id, to_id, kind, created_at, metadata FROM edges WHERE session_id = ? ORDER BY created_at ASC'\n\t\t);\n\t\tconst rows = stmt.all(sessionId) as Array<{\n\t\t\tid: string;\n\t\t\tsession_id: string;\n\t\t\tfrom_id: string;\n\t\t\tto_id: string;\n\t\t\tkind: string;\n\t\t\tcreated_at: number;\n\t\t\tmetadata: string | null;\n\t\t}>;\n\n\t\treturn rows.map((row) => ({\n\t\t\tid: row.id,\n\t\t\tsessionId: row.session_id,\n\t\t\tfrom: row.from_id,\n\t\t\tto: row.to_id,\n\t\t\tkind: row.kind as EdgeKind,\n\t\t\tcreatedAt: row.created_at,\n\t\t\t...(row.metadata ? { metadata: JSON.parse(row.metadata) as Record<string, unknown> } : {}),\n\t\t}));\n\t}\n\n\t/**\n\t * Persist summaries for a session using replace semantics: deletes any\n\t * existing summaries for the session, then inserts the provided summaries\n\t * within a transaction.\n\t *\n\t * @param sessionId - Session identifier whose summaries are being replaced\n\t * @param summaries - The summaries to persist for the session\n\t * @returns A Promise that resolves when the summaries are persisted\n\t */\n\tpublic async saveSummaries(sessionId: string, summaries: readonly Summary[]): Promise<void> {\n\t\tconst deleteStmt = this._db.prepare('DELETE FROM summaries WHERE session_id = ?');\n\t\tconst insertStmt = this._db.prepare(\n\t\t\t'INSERT OR REPLACE INTO summaries (id, session_id, branch_id, root_thought_id, covered_ids, covered_range_start, covered_range_end, topics, aggregate_confidence, created_at, meta) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'\n\t\t);\n\n\t\tconst dbWithTx = this._db as Database & {\n\t\t\ttransaction: (fn: () => void) => () => void;\n\t\t};\n\t\tconst transaction = dbWithTx.transaction(() => {\n\t\t\tdeleteStmt.run(sessionId);\n\t\t\tfor (const summary of summaries) {\n\t\t\t\tinsertStmt.run(\n\t\t\t\t\tsummary.id,\n\t\t\t\t\tsummary.sessionId,\n\t\t\t\t\tsummary.branchId ?? null,\n\t\t\t\t\tsummary.rootThoughtId,\n\t\t\t\t\tJSON.stringify(summary.coveredIds),\n\t\t\t\t\tsummary.coveredRange[0],\n\t\t\t\t\tsummary.coveredRange[1],\n\t\t\t\t\tJSON.stringify(summary.topics),\n\t\t\t\t\tsummary.aggregateConfidence,\n\t\t\t\t\tsummary.createdAt,\n\t\t\t\t\tsummary.meta ? JSON.stringify(summary.meta) : null\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\n\t\ttransaction();\n\t}\n\n\t/**\n\t * Load all summaries for a session, ordered by `created_at` ascending.\n\t *\n\t * @param sessionId - Session identifier to load summaries for\n\t * @returns A Promise that resolves to the session's summaries in chronological order\n\t */\n\tpublic async loadSummaries(sessionId: string): Promise<Summary[]> {\n\t\tconst stmt = this._db.prepare(\n\t\t\t'SELECT id, session_id, branch_id, root_thought_id, covered_ids, covered_range_start, covered_range_end, topics, aggregate_confidence, created_at, meta FROM summaries WHERE session_id = ? ORDER BY created_at ASC'\n\t\t);\n\t\tconst rows = stmt.all(sessionId) as Array<{\n\t\t\tid: string;\n\t\t\tsession_id: string;\n\t\t\tbranch_id: string | null;\n\t\t\troot_thought_id: string;\n\t\t\tcovered_ids: string;\n\t\t\tcovered_range_start: number;\n\t\t\tcovered_range_end: number;\n\t\t\ttopics: string;\n\t\t\taggregate_confidence: number;\n\t\t\tcreated_at: number;\n\t\t\tmeta: string | null;\n\t\t}>;\n\n\t\treturn rows.map((row) => ({\n\t\t\tid: row.id,\n\t\t\tsessionId: row.session_id,\n\t\t\t...(row.branch_id !== null ? { branchId: row.branch_id } : {}),\n\t\t\trootThoughtId: row.root_thought_id,\n\t\t\tcoveredIds: JSON.parse(row.covered_ids) as string[],\n\t\t\tcoveredRange: [row.covered_range_start, row.covered_range_end] as [number, number],\n\t\t\ttopics: JSON.parse(row.topics) as string[],\n\t\t\taggregateConfidence: row.aggregate_confidence,\n\t\t\tcreatedAt: row.created_at,\n\t\t\t...(row.meta ? { meta: JSON.parse(row.meta) as Record<string, unknown> } : {}),\n\t\t}));\n\t}\n\n\t/**\n\t * Get statistics about the persisted data.\n\t */\n\tpublic getStats(): {\n\t\tthoughtCount: number;\n\t\tbranchCount: number;\n\t\tdbSize: number;\n\t} {\n\t\tconst thoughtStmt = this._db.prepare('SELECT COUNT(*) as count FROM thoughts');\n\t\tconst { count: thoughtCount } = thoughtStmt.get() as { count: number };\n\n\t\tlet branchCount = 0;\n\t\tif (this._persistBranches) {\n\t\t\tconst branchStmt = this._db.prepare('SELECT COUNT(*) as count FROM branches');\n\t\t\tconst result = branchStmt.get() as { count: number } | undefined;\n\t\t\tbranchCount = result?.count ?? 0;\n\t\t}\n\n\t\treturn {\n\t\t\tthoughtCount,\n\t\t\tbranchCount,\n\t\t\tdbSize: 0, // Would need to check file size\n\t\t};\n\t}\n}\n"],"names":["SqlitePersistence","db","options","defaultDataDir","existsSync","join","homedir","dbPath","Database","module","Error","thought","stmt","JSON","countStmt","count","deleteStmt","rows","row","t","branchId","thoughts","data","Array","undefined","sessionId","edges","insertStmt","dbWithTx","transaction","edge","summaries","summary","thoughtStmt","thoughtCount","branchCount","branchStmt","result"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"persistence/SqlitePersistence.js","sources":["../../src/persistence/SqlitePersistence.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport type { ThoughtData } from '../core/thought.js';\nimport type { Edge, EdgeKind } from '../core/graph/Edge.js';\nimport type { Summary } from '../core/compression/Summary.js';\nimport type { PersistenceBackend, PersistenceConfig } from './PersistenceBackend.js';\n\n/**\n * Type definition for the better-sqlite3 Database interface.\n * This allows us to use the library without importing it directly.\n */\ninterface Database {\n\texec(sql: string): void;\n\tprepare(sql: string): Statement;\n\tclose(): void;\n\tpragma(pragma: string): unknown;\n}\n\ninterface Statement {\n\trun(...params: unknown[]): RunResult;\n\tget(...params: unknown[]): unknown;\n\tall(...params: unknown[]): unknown[];\n}\n\ninterface RunResult {\n\tchanges: number;\n\tlastInsertRowid: number;\n}\n\n/**\n * SQLite-based persistence backend.\n *\n * Provides efficient, transactional persistence using SQLite.\n * Requires the 'better-sqlite3' package to be installed.\n *\n * @example\n * ```typescript\n * const backend = await SqlitePersistence.create({\n * dbPath: './data/history.db',\n * enableWAL: true\n * });\n * ```\n */\nexport class SqlitePersistence implements PersistenceBackend {\n\tprivate _db: Database;\n\tprivate _maxHistorySize: number;\n\tprivate _persistBranches: boolean;\n\n\tprivate constructor(db: Database, options: PersistenceConfig['options']) {\n\t\tthis._db = db;\n\t\tthis._maxHistorySize = options?.maxHistorySize ?? 10000;\n\t\tthis._persistBranches = options?.persistBranches ?? true;\n\t\tthis._initializeSchema();\n\t}\n\n\t/**\n\t * Creates a new SqlitePersistence instance with dynamic import of better-sqlite3.\n\t *\n\t * @param options - Configuration options\n\t * @returns A Promise that resolves to a SqlitePersistence instance\n\t * @throws Error if better-sqlite3 is not installed\n\t *\n\t * @example\n\t * ```typescript\n\t * const backend = await SqlitePersistence.create({\n\t * dbPath: './data/history.db',\n\t * enableWAL: true\n\t * });\n\t * ```\n\t */\n\tstatic async create(options?: PersistenceConfig['options']): Promise<SqlitePersistence> {\n\t\t// Default to .claude/data in current directory or home directory\n\t\tconst defaultDataDir = existsSync('.claude/data')\n\t\t\t? '.claude/data'\n\t\t\t: join(homedir(), '.claude/data');\n\t\tconst dbPath = options?.dbPath ?? join(defaultDataDir, 'history.db');\n\n\t\t// Load better-sqlite3 dynamically (optional dependency)\n\t\tlet Database: new (path: string) => Database;\n\t\ttry {\n\t\t\tconst module = await import('better-sqlite3');\n\t\t\tDatabase = module.default;\n\t\t} catch {\n\t\t\tthrow new Error(\n\t\t\t\t`SQLite persistence requires 'better-sqlite3' package. Install it with: npm install better-sqlite3`\n\t\t\t);\n\t\t}\n\n\t\tconst db = new Database(dbPath);\n\n\t\t// Enable WAL mode for better concurrency if specified\n\t\tif (options?.enableWAL !== false) {\n\t\t\tdb.pragma('journal_mode = WAL');\n\t\t}\n\n\t\t// Performance and safety PRAGMAs\n\t\tdb.pragma('synchronous = NORMAL');\n\t\tdb.pragma('foreign_keys = ON');\n\t\tdb.pragma('busy_timeout = 5000');\n\t\tdb.pragma('cache_size = -64000'); // 64MB\n\t\tdb.pragma('temp_store = MEMORY');\n\n\t\treturn new SqlitePersistence(db, options);\n\t}\n\n\tprivate _initializeSchema(): void {\n\t\t// Create thoughts table\n\t\tthis._db.exec(`\n CREATE TABLE IF NOT EXISTS thoughts (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n data TEXT NOT NULL,\n created_at INTEGER DEFAULT (strftime('%s', 'now'))\n )\n `);\n\n\t\t// Create branches table\n\t\tif (this._persistBranches) {\n\t\t\tthis._db.exec(`\n CREATE TABLE IF NOT EXISTS branches (\n branch_id TEXT PRIMARY KEY,\n data TEXT NOT NULL,\n updated_at INTEGER DEFAULT (strftime('%s', 'now'))\n )\n `);\n\t\t}\n\n\t\t// Create indexes for better performance\n\t\tthis._db.exec(`\n CREATE INDEX IF NOT EXISTS idx_thoughts_created_at ON thoughts(created_at)\n `);\n\n\t\t// Create edges table for DAG edge storage\n\t\tthis._db.exec(`\n CREATE TABLE IF NOT EXISTS edges (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL,\n from_id TEXT NOT NULL,\n to_id TEXT NOT NULL,\n kind TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n metadata TEXT\n )\n `);\n\n\t\t// Indexes for edge queries\n\t\tthis._db.exec(`\n CREATE INDEX IF NOT EXISTS idx_edges_session ON edges(session_id)\n `);\n\t\tthis._db.exec(`\n CREATE INDEX IF NOT EXISTS idx_edges_from ON edges(session_id, from_id)\n `);\n\t\tthis._db.exec(`\n CREATE INDEX IF NOT EXISTS idx_edges_to ON edges(session_id, to_id)\n `);\n\n\t\t// Create summaries table for compression subsystem\n\t\tthis._db.exec(`\n CREATE TABLE IF NOT EXISTS summaries (\n id TEXT PRIMARY KEY,\n session_id TEXT NOT NULL,\n branch_id TEXT,\n root_thought_id TEXT NOT NULL,\n covered_ids TEXT NOT NULL,\n covered_range_start INTEGER NOT NULL,\n covered_range_end INTEGER NOT NULL,\n topics TEXT NOT NULL,\n aggregate_confidence REAL NOT NULL,\n created_at INTEGER NOT NULL,\n meta TEXT\n )\n `);\n\n\t\tthis._db.exec(`\n CREATE INDEX IF NOT EXISTS idx_summaries_session ON summaries(session_id)\n `);\n\t}\n\n\tpublic async saveThought(thought: ThoughtData): Promise<void> {\n\t\tconst stmt = this._db.prepare('INSERT INTO thoughts (data) VALUES (?)');\n\t\tstmt.run(JSON.stringify(thought));\n\n\t\t// Trim old thoughts if over limit\n\t\tconst countStmt = this._db.prepare('SELECT COUNT(*) as count FROM thoughts');\n\t\tconst { count } = countStmt.get() as { count: number };\n\n\t\tif (count > this._maxHistorySize) {\n\t\t\tconst deleteStmt = this._db.prepare(\n\t\t\t\t`DELETE FROM thoughts WHERE id IN (\n SELECT id FROM thoughts ORDER BY id ASC LIMIT ?\n )`\n\t\t\t);\n\t\t\tdeleteStmt.run(count - this._maxHistorySize);\n\t\t}\n\t}\n\n\tpublic async loadHistory(): Promise<ThoughtData[]> {\n\t\tconst stmt = this._db.prepare('SELECT data FROM thoughts ORDER BY id ASC');\n\t\tconst rows = stmt.all() as { data: string }[];\n\n\t\treturn rows\n\t\t\t.map((row) => {\n\t\t\t\ttry {\n\t\t\t\t\treturn JSON.parse(row.data) as ThoughtData;\n\t\t\t\t} catch {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t})\n\t\t\t.filter((t): t is ThoughtData => t !== null);\n\t}\n\n\tpublic async saveBranch(branchId: string, thoughts: ThoughtData[]): Promise<void> {\n\t\tif (!this._persistBranches) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst stmt = this._db.prepare(\n\t\t\t'INSERT OR REPLACE INTO branches (branch_id, data, updated_at) VALUES (?, ?, strftime(\"%s\", \"now\"))'\n\t\t);\n\t\tstmt.run(branchId, JSON.stringify(thoughts));\n\t}\n\n\tpublic async loadBranch(branchId: string): Promise<ThoughtData[] | undefined> {\n\t\tif (!this._persistBranches) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst stmt = this._db.prepare('SELECT data FROM branches WHERE branch_id = ?');\n\t\tconst row = stmt.get(branchId) as { data: string } | undefined;\n\n\t\tif (!row) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\ttry {\n\t\t\tconst data = JSON.parse(row.data) as ThoughtData[];\n\t\t\treturn Array.isArray(data) ? data : undefined;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tpublic async listBranches(): Promise<string[]> {\n\t\tif (!this._persistBranches) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst stmt = this._db.prepare('SELECT branch_id FROM branches ORDER BY branch_id ASC');\n\t\tconst rows = stmt.all() as { branch_id: string }[];\n\t\treturn rows.map((row) => row.branch_id);\n\t}\n\n\tpublic async clear(): Promise<void> {\n\t\tthis._db.exec('DELETE FROM thoughts');\n\t\tif (this._persistBranches) {\n\t\t\tthis._db.exec('DELETE FROM branches');\n\t\t}\n\t\tthis._db.exec('DELETE FROM edges');\n\t\tthis._db.exec('DELETE FROM summaries');\n\t}\n\n\tpublic async healthy(): Promise<boolean> {\n\t\ttry {\n\t\t\t// Simple health check - try to execute a query\n\t\t\tthis._db.prepare('SELECT 1').get();\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Close the database connection with proper cleanup.\n\t * Runs WAL checkpoint before closing to ensure all data is persisted.\n\t */\n\tpublic async close(): Promise<void> {\n\t\tif (this._db) {\n\t\t\ttry {\n\t\t\t\t// Run WAL checkpoint to ensure all data is persisted\n\t\t\t\tthis._db.pragma('wal_checkpoint(TRUNCATE)');\n\t\t\t} catch {\n\t\t\t\t// Ignore checkpoint errors - still try to close\n\t\t\t}\n\t\t\tthis._db.close();\n\t\t}\n\t}\n\n\t/**\n\t * Persist edges for a session using replace semantics: deletes any existing\n\t * edges for the session, then inserts the provided edges within a transaction.\n\t *\n\t * @param sessionId - Session identifier whose edges are being replaced\n\t * @param edges - The edges to persist for the session\n\t * @returns A Promise that resolves when the edges are persisted\n\t */\n\tpublic async saveEdges(sessionId: string, edges: readonly Edge[]): Promise<void> {\n\t\tconst deleteStmt = this._db.prepare('DELETE FROM edges WHERE session_id = ?');\n\t\tconst insertStmt = this._db.prepare(\n\t\t\t'INSERT INTO edges (id, session_id, from_id, to_id, kind, created_at, metadata) VALUES (?, ?, ?, ?, ?, ?, ?)'\n\t\t);\n\n\t\tconst dbWithTx = this._db as Database & {\n\t\t\ttransaction: (fn: () => void) => () => void;\n\t\t};\n\t\tconst transaction = dbWithTx.transaction(() => {\n\t\t\tdeleteStmt.run(sessionId);\n\t\t\tfor (const edge of edges) {\n\t\t\t\tinsertStmt.run(\n\t\t\t\t\tedge.id,\n\t\t\t\t\tedge.sessionId,\n\t\t\t\t\tedge.from,\n\t\t\t\t\tedge.to,\n\t\t\t\t\tedge.kind,\n\t\t\t\t\tedge.createdAt,\n\t\t\t\t\tedge.metadata ? JSON.stringify(edge.metadata) : null\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\n\t\ttransaction();\n\t}\n\n\t/**\n\t * Load all edges for a session, ordered by `created_at` ascending.\n\t *\n\t * @param sessionId - Session identifier to load edges for\n\t * @returns A Promise that resolves to the session's edges in chronological order\n\t */\n\tpublic async loadEdges(sessionId: string): Promise<Edge[]> {\n\t\tconst stmt = this._db.prepare(\n\t\t\t'SELECT id, session_id, from_id, to_id, kind, created_at, metadata FROM edges WHERE session_id = ? ORDER BY created_at ASC'\n\t\t);\n\t\tconst rows = stmt.all(sessionId) as Array<{\n\t\t\tid: string;\n\t\t\tsession_id: string;\n\t\t\tfrom_id: string;\n\t\t\tto_id: string;\n\t\t\tkind: string;\n\t\t\tcreated_at: number;\n\t\t\tmetadata: string | null;\n\t\t}>;\n\n\t\treturn rows.map((row) => ({\n\t\t\tid: row.id,\n\t\t\tsessionId: row.session_id,\n\t\t\tfrom: row.from_id,\n\t\t\tto: row.to_id,\n\t\t\tkind: row.kind as EdgeKind,\n\t\t\tcreatedAt: row.created_at,\n\t\t\t...(row.metadata ? { metadata: JSON.parse(row.metadata) as Record<string, unknown> } : {}),\n\t\t}));\n\t}\n\n\t/**\n\t * List all session IDs that have persisted edges in the database.\n\t *\n\t * @returns Array of distinct session identifiers from the edges table\n\t */\n\tpublic async listEdgeSessions(): Promise<string[]> {\n\t\tconst rows = this._db\n\t\t\t.prepare('SELECT DISTINCT session_id FROM edges')\n\t\t\t.all() as { session_id: string }[];\n\t\treturn rows.map((r) => r.session_id);\n\t}\n\n\t/**\n\t * Persist summaries for a session using replace semantics: deletes any\n\t * existing summaries for the session, then inserts the provided summaries\n\t * within a transaction.\n\t *\n\t * @param sessionId - Session identifier whose summaries are being replaced\n\t * @param summaries - The summaries to persist for the session\n\t * @returns A Promise that resolves when the summaries are persisted\n\t */\n\tpublic async saveSummaries(sessionId: string, summaries: readonly Summary[]): Promise<void> {\n\t\tconst deleteStmt = this._db.prepare('DELETE FROM summaries WHERE session_id = ?');\n\t\tconst insertStmt = this._db.prepare(\n\t\t\t'INSERT OR REPLACE INTO summaries (id, session_id, branch_id, root_thought_id, covered_ids, covered_range_start, covered_range_end, topics, aggregate_confidence, created_at, meta) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'\n\t\t);\n\n\t\tconst dbWithTx = this._db as Database & {\n\t\t\ttransaction: (fn: () => void) => () => void;\n\t\t};\n\t\tconst transaction = dbWithTx.transaction(() => {\n\t\t\tdeleteStmt.run(sessionId);\n\t\t\tfor (const summary of summaries) {\n\t\t\t\tinsertStmt.run(\n\t\t\t\t\tsummary.id,\n\t\t\t\t\tsummary.sessionId,\n\t\t\t\t\tsummary.branchId ?? null,\n\t\t\t\t\tsummary.rootThoughtId,\n\t\t\t\t\tJSON.stringify(summary.coveredIds),\n\t\t\t\t\tsummary.coveredRange[0],\n\t\t\t\t\tsummary.coveredRange[1],\n\t\t\t\t\tJSON.stringify(summary.topics),\n\t\t\t\t\tsummary.aggregateConfidence,\n\t\t\t\t\tsummary.createdAt,\n\t\t\t\t\tsummary.meta ? JSON.stringify(summary.meta) : null\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\n\t\ttransaction();\n\t}\n\n\t/**\n\t * Load all summaries for a session, ordered by `created_at` ascending.\n\t *\n\t * @param sessionId - Session identifier to load summaries for\n\t * @returns A Promise that resolves to the session's summaries in chronological order\n\t */\n\tpublic async loadSummaries(sessionId: string): Promise<Summary[]> {\n\t\tconst stmt = this._db.prepare(\n\t\t\t'SELECT id, session_id, branch_id, root_thought_id, covered_ids, covered_range_start, covered_range_end, topics, aggregate_confidence, created_at, meta FROM summaries WHERE session_id = ? ORDER BY created_at ASC'\n\t\t);\n\t\tconst rows = stmt.all(sessionId) as Array<{\n\t\t\tid: string;\n\t\t\tsession_id: string;\n\t\t\tbranch_id: string | null;\n\t\t\troot_thought_id: string;\n\t\t\tcovered_ids: string;\n\t\t\tcovered_range_start: number;\n\t\t\tcovered_range_end: number;\n\t\t\ttopics: string;\n\t\t\taggregate_confidence: number;\n\t\t\tcreated_at: number;\n\t\t\tmeta: string | null;\n\t\t}>;\n\n\t\treturn rows.map((row) => ({\n\t\t\tid: row.id,\n\t\t\tsessionId: row.session_id,\n\t\t\t...(row.branch_id !== null ? { branchId: row.branch_id } : {}),\n\t\t\trootThoughtId: row.root_thought_id,\n\t\t\tcoveredIds: JSON.parse(row.covered_ids) as string[],\n\t\t\tcoveredRange: [row.covered_range_start, row.covered_range_end] as [number, number],\n\t\t\ttopics: JSON.parse(row.topics) as string[],\n\t\t\taggregateConfidence: row.aggregate_confidence,\n\t\t\tcreatedAt: row.created_at,\n\t\t\t...(row.meta ? { meta: JSON.parse(row.meta) as Record<string, unknown> } : {}),\n\t\t}));\n\t}\n\n\t/**\n\t * Get statistics about the persisted data.\n\t */\n\tpublic getStats(): {\n\t\tthoughtCount: number;\n\t\tbranchCount: number;\n\t\tdbSize: number;\n\t} {\n\t\tconst thoughtStmt = this._db.prepare('SELECT COUNT(*) as count FROM thoughts');\n\t\tconst { count: thoughtCount } = thoughtStmt.get() as { count: number };\n\n\t\tlet branchCount = 0;\n\t\tif (this._persistBranches) {\n\t\t\tconst branchStmt = this._db.prepare('SELECT COUNT(*) as count FROM branches');\n\t\t\tconst result = branchStmt.get() as { count: number } | undefined;\n\t\t\tbranchCount = result?.count ?? 0;\n\t\t}\n\n\t\treturn {\n\t\t\tthoughtCount,\n\t\t\tbranchCount,\n\t\t\tdbSize: 0, // Would need to check file size\n\t\t};\n\t}\n}\n"],"names":["SqlitePersistence","db","options","defaultDataDir","existsSync","join","homedir","dbPath","Database","module","Error","thought","stmt","JSON","countStmt","count","deleteStmt","rows","row","t","branchId","thoughts","data","Array","undefined","sessionId","edges","insertStmt","dbWithTx","transaction","edge","r","summaries","summary","thoughtStmt","thoughtCount","branchCount","branchStmt","result"],"mappings":"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"}
|
package/dist/schema.d.ts
CHANGED
|
@@ -468,7 +468,7 @@ export declare const SequentialThinkingSchema: v.ObjectSchema<{
|
|
|
468
468
|
next_step_conditions?: string[] | undefined;
|
|
469
469
|
}[], "Steps already recommended (lenient schema - allows partial data with defaults)">]>, undefined>;
|
|
470
470
|
readonly remaining_steps: v.OptionalSchema<v.SchemaWithPipe<readonly [v.ArraySchema<v.StringSchema<undefined>, undefined>, v.DescriptionAction<string[], "High-level descriptions of upcoming steps">]>, undefined>;
|
|
471
|
-
readonly thought_type: v.OptionalSchema<v.SchemaWithPipe<readonly [v.PicklistSchema<["regular", "hypothesis", "verification", "critique", "synthesis", "meta", "tool_call", "tool_observation", "assumption", "decomposition", "backtrack"], undefined>, v.DescriptionAction<"regular" | "hypothesis" | "verification" | "critique" | "synthesis" | "meta" | "tool_call" | "tool_observation" | "assumption" | "decomposition" | "backtrack", "Classified purpose: regular, hypothesis, verification, critique, synthesis, meta">]>, undefined>;
|
|
471
|
+
readonly thought_type: v.OptionalSchema<v.SchemaWithPipe<readonly [v.PicklistSchema<["regular", "hypothesis", "verification", "critique", "synthesis", "meta", "tool_call", "tool_observation", "assumption", "decomposition", "backtrack"], undefined>, v.DescriptionAction<"regular" | "hypothesis" | "verification" | "critique" | "synthesis" | "meta" | "tool_call" | "tool_observation" | "assumption" | "decomposition" | "backtrack", "Classified purpose: regular (default), hypothesis, verification, critique, synthesis, meta, tool_call (requires toolInterleave), tool_observation (requires toolInterleave), assumption (requires newThoughtTypes), decomposition (requires newThoughtTypes), backtrack (requires newThoughtTypes)">]>, undefined>;
|
|
472
472
|
readonly quality_score: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.MinValueAction<number, 0, undefined>, v.MaxValueAction<number, 1, undefined>, v.DescriptionAction<number, "Self-assessed quality score (0-1)">]>, undefined>;
|
|
473
473
|
readonly confidence: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.MinValueAction<number, 0, undefined>, v.MaxValueAction<number, 1, undefined>, v.DescriptionAction<number, "Explicit confidence in correctness (0-1)">]>, undefined>;
|
|
474
474
|
readonly hypothesis_id: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "Hypothesis ID must contain only letters, numbers, hyphens, and underscores">, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 50, undefined>, v.DescriptionAction<string, "Identifier linking hypothesis to verification thoughts">]>, undefined>;
|
|
@@ -487,7 +487,8 @@ export declare const SequentialThinkingSchema: v.ObjectSchema<{
|
|
|
487
487
|
readonly tool_result: v.OptionalSchema<v.SchemaWithPipe<readonly [v.UnknownSchema, v.DescriptionAction<unknown, "Result returned by the tool (for tool_observation thoughts)">]>, undefined>;
|
|
488
488
|
readonly continuation_token: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>, v.DescriptionAction<string, "Token for resuming long-running tool invocations">]>, undefined>;
|
|
489
489
|
readonly decomposition_children: v.OptionalSchema<v.SchemaWithPipe<readonly [v.ArraySchema<v.StringSchema<undefined>, undefined>, v.DescriptionAction<string[], "Child thought IDs produced by decomposition">]>, undefined>;
|
|
490
|
-
readonly backtrack_target: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 1, undefined>, v.DescriptionAction<number, "Thought number to backtrack to">]>, undefined>;
|
|
490
|
+
readonly backtrack_target: v.OptionalSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 1, undefined>, v.DescriptionAction<number, "Thought number to backtrack to. When the parent thought has thought_type=backtrack, this thought is logically retracted: it remains in history but is excluded from quality signals and reasoning stats.">]>, undefined>;
|
|
491
|
+
readonly register_branch_id: v.OptionalSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "register_branch_id must contain only letters, numbers, hyphens, and underscores">, v.MinLengthAction<string, 1, undefined>, v.MaxLengthAction<string, 50, undefined>, v.DescriptionAction<string, "Pre-declares a branch ID for this session before any thoughts reference it. Useful so that subsequent thoughts using merge_branch_ids can target a branch that has not yet received any thoughts.">]>, undefined>;
|
|
491
492
|
}, undefined>;
|
|
492
493
|
/**
|
|
493
494
|
* The sequential thinking tool definition for MCP registration.
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,CAAC,MAAM,SAAS,CAAC;AAC7B,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAiH5C;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;aAkBnC,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;aA4BpC,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAanC,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,eAAO,MAAM,+BAA+B;;;;;;;;;aAsB1C,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAe1C,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,eAAO,MAAM,wBAAwB
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,CAAC,MAAM,SAAS,CAAC;AAC7B,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAiH5C;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;aAkBnC,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;aA4BpC,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAanC,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,eAAO,MAAM,+BAA+B;;;;;;;;;aAsB1C,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAe1C,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aA0LnC,CAAC;AAEH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,wBAAwB,EAAE,IAItC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,oBAAoB;;;;;aAmB/B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,cAAc,kWASW,CAAC;AAEvC;;GAEG;AACH,eAAO,MAAM,UAAU;;;;;;;;aAQW,CAAC"}
|