tracelattice 1.3.1 → 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/core/HistoryManager.d.ts +2 -2
- package/dist/core/HistoryManager.d.ts.map +1 -1
- package/dist/core/HistoryManager.js +2 -2
- package/dist/core/HistoryManager.js.map +1 -1
- package/dist/core/ThoughtProcessor.js +6 -6
- 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.map +1 -1
- package/dist/core/evaluator/SignalComputer.js +5 -1
- package/dist/core/evaluator/SignalComputer.js.map +1 -1
- package/dist/core/graph/EdgeEmitter.js +1 -1
- package/dist/core/graph/EdgeEmitter.js.map +1 -1
- package/dist/core/tools/InMemorySuspensionStore.js +1 -1
- package/dist/core/tools/InMemorySuspensionStore.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# TraceLattice
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/tracelattice)
|
|
4
|
+
|
|
3
5
|
An MCP (Model Context Protocol) server providing structured sequential thinking with tool/skill recommendations, DAG-based thought relationships, configurable reasoning strategies, and confidence calibration for AI agents.
|
|
4
6
|
|
|
5
7
|
## Features
|
package/dist/ServerConfig.d.ts
CHANGED
|
@@ -24,26 +24,25 @@ import type { PersistenceConfig } from './persistence/PersistenceBackend.js';
|
|
|
24
24
|
* ```
|
|
25
25
|
*/
|
|
26
26
|
/**
|
|
27
|
-
* Feature flags for opt-in TraceLattice capabilities.
|
|
27
|
+
* Feature flags for opt-in/out TraceLattice capabilities.
|
|
28
28
|
*
|
|
29
|
-
* All flags default to
|
|
30
|
-
*
|
|
31
|
-
* mappings predictable (TRACELATTICE_FEATURES_*).
|
|
29
|
+
* All flags default to ON. Users can disable any flag via
|
|
30
|
+
* TRACELATTICE_FEATURES_*=false environment variables.
|
|
32
31
|
*/
|
|
33
32
|
export interface FeatureFlags {
|
|
34
|
-
/** Enable DAG edges between thoughts (Item #1). @default
|
|
33
|
+
/** Enable DAG edges between thoughts (Item #1). @default true */
|
|
35
34
|
dagEdges: boolean;
|
|
36
35
|
/** Reasoning strategy selector (Item #2). @default 'sequential' */
|
|
37
36
|
reasoningStrategy: 'sequential' | 'tot';
|
|
38
|
-
/** Enable confidence calibration (Item #3). @default
|
|
37
|
+
/** Enable confidence calibration (Item #3). @default true */
|
|
39
38
|
calibration: boolean;
|
|
40
|
-
/** Enable thought compression (Item #5). @default
|
|
39
|
+
/** Enable thought compression (Item #5). @default true */
|
|
41
40
|
compression: boolean;
|
|
42
|
-
/** Enable tool interleaving (Item #6). @default
|
|
41
|
+
/** Enable tool interleaving (Item #6). @default true */
|
|
43
42
|
toolInterleave: boolean;
|
|
44
|
-
/** Enable new thought types (Item #8). @default
|
|
43
|
+
/** Enable new thought types (Item #8). @default true */
|
|
45
44
|
newThoughtTypes: boolean;
|
|
46
|
-
/** Enable outcome recording (prereq for Item #10). @default
|
|
45
|
+
/** Enable outcome recording (prereq for Item #10). @default true */
|
|
47
46
|
outcomeRecording: boolean;
|
|
48
47
|
}
|
|
49
48
|
export interface ServerConfigOptions {
|
|
@@ -133,7 +132,7 @@ export interface ServerConfigOptions {
|
|
|
133
132
|
* ```typescript
|
|
134
133
|
* // Using defaults
|
|
135
134
|
* const config1 = new ServerConfig();
|
|
136
|
-
* console.log(config1.maxHistorySize); //
|
|
135
|
+
* console.log(config1.maxHistorySize); // 10000
|
|
137
136
|
*
|
|
138
137
|
* // With custom options
|
|
139
138
|
* const config2 = new ServerConfig({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ServerConfig.d.ts","sourceRoot":"","sources":["../src/ServerConfig.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AAE7E;;;;;;;;;;;;;;GAcG;AAEH
|
|
1
|
+
{"version":3,"file":"ServerConfig.d.ts","sourceRoot":"","sources":["../src/ServerConfig.ts"],"names":[],"mappings":"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"}
|
package/dist/ServerConfig.js
CHANGED
|
@@ -29,7 +29,7 @@ class ServerConfig {
|
|
|
29
29
|
this.toolInterleaveSweepMs = this.validatePositiveMs(options.toolInterleaveSweepMs, 60000, 'toolInterleaveSweepMs');
|
|
30
30
|
}
|
|
31
31
|
validateMaxHistorySize(value) {
|
|
32
|
-
const defaultValue =
|
|
32
|
+
const defaultValue = 10000;
|
|
33
33
|
if (null == value) return defaultValue;
|
|
34
34
|
if ('number' != typeof value || !Number.isFinite(value)) throw new ConfigurationError(`maxHistorySize must be a finite number, got ${value}`);
|
|
35
35
|
if (value < 1) throw new ConfigurationError(`maxHistorySize must be at least 1, got ${value}`);
|
|
@@ -110,13 +110,13 @@ class ServerConfig {
|
|
|
110
110
|
}
|
|
111
111
|
validateFeatures(value) {
|
|
112
112
|
return {
|
|
113
|
-
dagEdges: value?.dagEdges ??
|
|
113
|
+
dagEdges: value?.dagEdges ?? true,
|
|
114
114
|
reasoningStrategy: value?.reasoningStrategy ?? 'sequential',
|
|
115
|
-
calibration: value?.calibration ??
|
|
116
|
-
compression: value?.compression ??
|
|
117
|
-
toolInterleave: value?.toolInterleave ??
|
|
118
|
-
newThoughtTypes: value?.newThoughtTypes ??
|
|
119
|
-
outcomeRecording: value?.outcomeRecording ??
|
|
115
|
+
calibration: value?.calibration ?? true,
|
|
116
|
+
compression: value?.compression ?? true,
|
|
117
|
+
toolInterleave: value?.toolInterleave ?? true,
|
|
118
|
+
newThoughtTypes: value?.newThoughtTypes ?? true,
|
|
119
|
+
outcomeRecording: value?.outcomeRecording ?? true
|
|
120
120
|
};
|
|
121
121
|
}
|
|
122
122
|
validatePositiveMs(value, defaultValue, fieldName) {
|
package/dist/ServerConfig.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ServerConfig.js","sources":["../src/ServerConfig.ts"],"sourcesContent":["/**\n * Server configuration management with validation.\n *\n * This module provides the `ServerConfig` class which handles all server configuration\n * with built-in validation and sensible defaults. Configuration values are validated\n * on construction and warnings are emitted for out-of-range values.\n *\n * @module ServerConfig\n */\n\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { ConfigurationError } from './errors.js';\nimport type { PersistenceConfig } from './persistence/PersistenceBackend.js';\n\n/**\n * Configuration options for creating a `ServerConfig` instance.\n *\n * All properties are optional with sensible defaults applied during validation.\n *\n * @example\n * ```typescript\n * const options: ServerConfigOptions = {\n * maxHistorySize: 500,\n * maxBranches: 25,\n * skillDirs: ['./custom-skills'],\n * persistence: { enabled: true, backend: 'sqlite' }\n * };\n * ```\n */\n\n/**\n * Feature flags for opt-in TraceLattice capabilities.\n *\n * All flags default to OFF so new features can be merged behind a flag\n * without altering existing behavior. Flags are flat to keep env var\n * mappings predictable (TRACELATTICE_FEATURES_*).\n */\nexport interface FeatureFlags {\n\t/** Enable DAG edges between thoughts (Item #1). @default false */\n\tdagEdges: boolean;\n\t/** Reasoning strategy selector (Item #2). @default 'sequential' */\n\treasoningStrategy: 'sequential' | 'tot';\n\t/** Enable confidence calibration (Item #3). @default false */\n\tcalibration: boolean;\n\t/** Enable thought compression (Item #5). @default false */\n\tcompression: boolean;\n\t/** Enable tool interleaving (Item #6). @default false */\n\ttoolInterleave: boolean;\n\t/** Enable new thought types (Item #8). @default false */\n\tnewThoughtTypes: boolean;\n\t/** Enable outcome recording (prereq for Item #10). @default false */\n\toutcomeRecording: boolean;\n}\nexport interface ServerConfigOptions {\n\t/**\n\t * Maximum number of thoughts to keep in history.\n\t * @default 1000\n\t */\n\tmaxHistorySize?: number;\n\n\t/**\n\t * Maximum number of branches to maintain.\n\t * @default 50\n\t */\n\tmaxBranches?: number;\n\n\t/**\n\t * Maximum size of each branch.\n\t * @default 100\n\t */\n\tmaxBranchSize?: number;\n\n\t/**\n\t * Directory paths to search for skills.\n\t * @default ['.claude/skills', '~/.claude/skills']\n\t */\n\tskillDirs?: string[];\n\n\t/**\n\t * Discovery cache configuration.\n\t */\n\tdiscoveryCache?: {\n\t\t/**\n\t\t * Time-to-live for cache entries in milliseconds.\n\t\t * @default 300000 (5 minutes)\n\t\t */\n\t\tttl?: number;\n\t\t/**\n\t\t * Maximum number of entries in the cache.\n\t\t * @default 100\n\t\t */\n\t\tmaxSize?: number;\n\t};\n\n\t/**\n\t * Persistence configuration for storing history and state.\n\t */\n\tpersistence?: PersistenceConfig;\n\n\t/**\n\t * Maximum number of thoughts to buffer before flushing to persistence.\n\t * @default 100\n\t */\n\tpersistenceBufferSize?: number;\n\n\t/**\n\t * Interval in milliseconds between periodic persistence flushes.\n\t * @default 1000\n\t */\n\tpersistenceFlushInterval?: number;\n\n\t/**\n\t * Maximum number of retries for failed persistence flushes.\n\t * @default 3\n\t */\n\tpersistenceMaxRetries?: number;\n\n\t/**\n\t * Feature flag overrides. Missing fields are filled with defaults (all OFF,\n\t * reasoningStrategy='sequential').\n\t */\n\tfeatures?: Partial<FeatureFlags>;\n\n\t/**\n\t * TTL in milliseconds for suspended tool-interleave entries in SuspensionStore.\n\t * @default 60000\n\t */\n\ttoolInterleaveTtlMs?: number;\n\n\t/**\n\t * Sweep interval in milliseconds for SuspensionStore expiration cleanup.\n\t * @default 60000\n\t */\n\ttoolInterleaveSweepMs?: number;\n}\n\n/**\n * Server configuration with validation and defaults.\n *\n * This class manages all server configuration including history limits,\n * branch limits, skill directories, discovery cache settings, and persistence.\n * All values are validated on construction with appropriate defaults applied.\n *\n * @remarks\n * - Values outside recommended ranges trigger warnings but are still applied\n * - Environment variables override file-based configuration\n * - The `toJSON()` method provides a plain object representation\n *\n * @example\n * ```typescript\n * // Using defaults\n * const config1 = new ServerConfig();\n * console.log(config1.maxHistorySize); // 1000\n *\n * // With custom options\n * const config2 = new ServerConfig({\n * maxHistorySize: 500,\n * persistence: { enabled: true, backend: 'file', options: { dataDir: './data' } }\n * });\n *\n * // Export as plain object\n * const json = config2.toJSON();\n * ```\n */\nexport class ServerConfig {\n\t/** Maximum number of thoughts to keep in history. */\n\tpublic maxHistorySize: number;\n\n\t/** Maximum number of branches to maintain. */\n\tpublic maxBranches: number;\n\n\t/** Maximum size of each branch. */\n\tpublic maxBranchSize: number;\n\n\t/** Directory paths to search for skills. */\n\tpublic skillDirs: string[];\n\n\t/** Discovery cache configuration. */\n\tpublic discoveryCache: { ttl: number; maxSize: number };\n\n\t/** Persistence configuration. */\n\tpublic persistence: PersistenceConfig;\n\n\t/** Maximum number of thoughts to buffer before flushing to persistence. */\n\tpublic persistenceBufferSize: number;\n\n\t/** Interval in milliseconds between periodic persistence flushes. */\n\tpublic persistenceFlushInterval: number;\n\n\t/** Maximum number of retries for failed persistence flushes. */\n\tpublic persistenceMaxRetries: number;\n\n\t/** Feature flag toggles. */\n\tpublic features: FeatureFlags;\n\n\t/** TTL in milliseconds for suspended tool-interleave entries. */\n\tpublic toolInterleaveTtlMs: number;\n\n\t/** Sweep interval in milliseconds for SuspensionStore expiration cleanup. */\n\tpublic toolInterleaveSweepMs: number;\n\n\t/**\n\t * Creates a new ServerConfig instance with validation.\n\t *\n\t * All values are validated and defaults are applied for undefined options.\n\t * Warnings are emitted to console for values outside recommended ranges.\n\t *\n\t * @param options - Optional configuration overrides\n\t *\n\t * @example\n\t * ```typescript\n\t * const config = new ServerConfig({\n\t * maxHistorySize: 500,\n\t * skillDirs: ['./my-skills']\n\t * });\n\t * ```\n\t */\n\tconstructor(options: ServerConfigOptions = {}) {\n\t\tthis.maxHistorySize = this.validateMaxHistorySize(options.maxHistorySize);\n\t\tthis.maxBranches = this.validateMaxBranches(options.maxBranches);\n\t\tthis.maxBranchSize = this.validateMaxBranchSize(options.maxBranchSize);\n\t\tthis.skillDirs = this.validateSkillDirs(options.skillDirs);\n\t\tthis.discoveryCache = this.validateDiscoveryCache(options.discoveryCache);\n\t\tthis.persistence = this.validatePersistence(options.persistence);\n\t\tthis.persistenceBufferSize = this.validatePersistenceBufferSize(options.persistenceBufferSize);\n\t\tthis.persistenceFlushInterval = this.validatePersistenceFlushInterval(\n\t\t\toptions.persistenceFlushInterval\n\t\t);\n\t\tthis.persistenceMaxRetries = this.validatePersistenceMaxRetries(options.persistenceMaxRetries);\n\t\tthis.features = this.validateFeatures(options.features);\n\t\tthis.toolInterleaveTtlMs = this.validatePositiveMs(\n\t\t\toptions.toolInterleaveTtlMs,\n\t\t\t60000,\n\t\t\t'toolInterleaveTtlMs'\n\t\t);\n\t\tthis.toolInterleaveSweepMs = this.validatePositiveMs(\n\t\t\toptions.toolInterleaveSweepMs,\n\t\t\t60000,\n\t\t\t'toolInterleaveSweepMs'\n\t\t);\n\t}\n\n\t/**\n\t * Validates the max history size value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default (1000)\n\t * @private\n\t */\n\tprivate validateMaxHistorySize(value?: number): number {\n\t\tconst defaultValue = 1000;\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(`maxHistorySize must be a finite number, got ${value}`);\n\t\t}\n\t\tif (value < 1) {\n\t\t\tthrow new ConfigurationError(`maxHistorySize must be at least 1, got ${value}`);\n\t\t}\n\t\tif (value > 10000) {\n\t\t\tthrow new ConfigurationError(`maxHistorySize must not exceed 10000, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates the max branches value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default (50)\n\t * @private\n\t */\n\tprivate validateMaxBranches(value?: number): number {\n\t\tconst defaultValue = 50;\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(`maxBranches must be a finite number, got ${value}`);\n\t\t}\n\t\tif (value < 0) {\n\t\t\tthrow new ConfigurationError(`maxBranches must be non-negative, got ${value}`);\n\t\t}\n\t\tif (value > 1000) {\n\t\t\tthrow new ConfigurationError(`maxBranches must not exceed 1000, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates the max branch size value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default (100)\n\t * @private\n\t */\n\tprivate validateMaxBranchSize(value?: number): number {\n\t\tconst defaultValue = 100;\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(`maxBranchSize must be a finite number, got ${value}`);\n\t\t}\n\t\tif (value < 1) {\n\t\t\tthrow new ConfigurationError(`maxBranchSize must be at least 1, got ${value}`);\n\t\t}\n\t\tif (value > 1000) {\n\t\t\tthrow new ConfigurationError(`maxBranchSize must not exceed 1000, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates the skill directories value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default ['.claude/skills', '~/.claude/skills']\n\t * @private\n\t */\n\tprivate validateSkillDirs(value?: string[]): string[] {\n\t\tconst defaultValue = ['.claude/skills', join(homedir(), '.claude/skills')];\n\t\tif (!value) return defaultValue;\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates the discovery cache configuration.\n\t * @param value - The value to validate\n\t * @returns The validated value with defaults applied\n\t * @private\n\t */\n\tprivate validateDiscoveryCache(value?: { ttl?: number; maxSize?: number }): {\n\t\tttl: number;\n\t\tmaxSize: number;\n\t} {\n\t\treturn {\n\t\t\tttl: value?.ttl ?? 300000,\n\t\t\tmaxSize: value?.maxSize ?? 100,\n\t\t};\n\t}\n\n\t/**\n\t * Validates the persistence configuration.\n\t * @param value - The value to validate\n\t * @returns The validated value with defaults applied\n\t * @private\n\t */\n\tprivate validatePersistence(value?: PersistenceConfig): PersistenceConfig {\n\t\tif (!value) {\n\t\t\t// Default to in-memory (no actual persistence, just consistency)\n\t\t\treturn {\n\t\t\t\tenabled: false,\n\t\t\t\tbackend: 'memory',\n\t\t\t};\n\t\t}\n\n\t\t// Validate backend type\n\t\tconst validBackends = ['file', 'sqlite', 'memory'];\n\t\tconst backend = value.backend ?? 'memory';\n\t\tif (!validBackends.includes(backend)) {\n\t\t\tthrow new ConfigurationError(\n\t\t\t\t`persistence.backend must be one of ${validBackends.join(', ')}, got ${backend}`\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\tenabled: value.enabled ?? false,\n\t\t\tbackend,\n\t\t\toptions: value.options ?? {},\n\t\t};\n\t}\n\n\t/**\n\t * Validates the persistence buffer size value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default (100)\n\t * @private\n\t */\n\tprivate validatePersistenceBufferSize(value?: number): number {\n\t\tconst defaultValue = 100;\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(`persistenceBufferSize must be a finite number, got ${value}`);\n\t\t}\n\t\tif (value < 1) {\n\t\t\tthrow new ConfigurationError(`persistenceBufferSize must be at least 1, got ${value}`);\n\t\t}\n\t\tif (value > 10000) {\n\t\t\tthrow new ConfigurationError(`persistenceBufferSize must not exceed 10000, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates the persistence flush interval value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default (1000)\n\t * @private\n\t */\n\tprivate validatePersistenceFlushInterval(value?: number): number {\n\t\tconst defaultValue = 1000;\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(\n\t\t\t\t`persistenceFlushInterval must be a finite number, got ${value}`\n\t\t\t);\n\t\t}\n\t\tif (value < 100) {\n\t\t\tthrow new ConfigurationError(`persistenceFlushInterval must be at least 100, got ${value}`);\n\t\t}\n\t\tif (value > 60000) {\n\t\t\tthrow new ConfigurationError(`persistenceFlushInterval must not exceed 60000, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates the persistence max retries value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default (3)\n\t * @private\n\t */\n\tprivate validatePersistenceMaxRetries(value?: number): number {\n\t\tconst defaultValue = 3;\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(`persistenceMaxRetries must be a finite number, got ${value}`);\n\t\t}\n\t\tif (value < 0) {\n\t\t\tthrow new ConfigurationError(`persistenceMaxRetries must be non-negative, got ${value}`);\n\t\t}\n\t\tif (value > 10) {\n\t\t\tthrow new ConfigurationError(`persistenceMaxRetries must not exceed 10, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates feature flags and fills defaults for missing fields.\n\t * @param value - Partial feature flag overrides\n\t * @returns Fully populated FeatureFlags with defaults applied\n\t * @private\n\t */\n\tprivate validateFeatures(value?: Partial<FeatureFlags>): FeatureFlags {\n\t\treturn {\n\t\t\tdagEdges: value?.dagEdges ?? false,\n\t\t\treasoningStrategy: value?.reasoningStrategy ?? 'sequential',\n\t\t\tcalibration: value?.calibration ?? false,\n\t\t\tcompression: value?.compression ?? false,\n\t\t\ttoolInterleave: value?.toolInterleave ?? false,\n\t\t\tnewThoughtTypes: value?.newThoughtTypes ?? false,\n\t\t\toutcomeRecording: value?.outcomeRecording ?? false,\n\t\t};\n\t}\n\n\t/**\n\t * Validates a positive millisecond value with a default fallback.\n\t * @param value - The value to validate\n\t * @param defaultValue - Default applied if value is undefined/null\n\t * @param fieldName - Field name for error messages\n\t * @returns The validated millisecond value\n\t * @private\n\t */\n\tprivate validatePositiveMs(\n\t\tvalue: number | undefined,\n\t\tdefaultValue: number,\n\t\tfieldName: string\n\t): number {\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(`${fieldName} must be a finite number, got ${value}`);\n\t\t}\n\t\tif (value < 1) {\n\t\t\tthrow new ConfigurationError(`${fieldName} must be at least 1, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Converts the configuration to a plain object.\n\t *\n\t * Useful for serialization, logging, or when a plain object representation\n\t * is preferred over the ServerConfig instance.\n\t *\n\t * @returns A plain object representation of the configuration\n\t *\n\t * @example\n\t * ```typescript\n\t * const config = new ServerConfig({ maxHistorySize: 500 });\n\t * const json = config.toJSON();\n\t * console.log(JSON.stringify(json, null, 2));\n\t * ```\n\t */\n\tpublic toJSON(): ServerConfigOptions {\n\t\treturn {\n\t\t\tmaxHistorySize: this.maxHistorySize,\n\t\t\tmaxBranches: this.maxBranches,\n\t\t\tmaxBranchSize: this.maxBranchSize,\n\t\t\tskillDirs: this.skillDirs,\n\t\t\tdiscoveryCache: this.discoveryCache,\n\t\t\tpersistence: this.persistence,\n\t\t\tpersistenceBufferSize: this.persistenceBufferSize,\n\t\t\tpersistenceFlushInterval: this.persistenceFlushInterval,\n\t\t\tpersistenceMaxRetries: this.persistenceMaxRetries,\n\t\t\tfeatures: this.features,\n\t\t\ttoolInterleaveTtlMs: this.toolInterleaveTtlMs,\n\t\t\ttoolInterleaveSweepMs: this.toolInterleaveSweepMs,\n\t\t};\n\t}\n}\n"],"names":["ServerConfig","options","value","defaultValue","Number","ConfigurationError","join","homedir","validBackends","backend","fieldName"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ServerConfig.js","sources":["../src/ServerConfig.ts"],"sourcesContent":["/**\n * Server configuration management with validation.\n *\n * This module provides the `ServerConfig` class which handles all server configuration\n * with built-in validation and sensible defaults. Configuration values are validated\n * on construction and warnings are emitted for out-of-range values.\n *\n * @module ServerConfig\n */\n\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { ConfigurationError } from './errors.js';\nimport type { PersistenceConfig } from './persistence/PersistenceBackend.js';\n\n/**\n * Configuration options for creating a `ServerConfig` instance.\n *\n * All properties are optional with sensible defaults applied during validation.\n *\n * @example\n * ```typescript\n * const options: ServerConfigOptions = {\n * maxHistorySize: 500,\n * maxBranches: 25,\n * skillDirs: ['./custom-skills'],\n * persistence: { enabled: true, backend: 'sqlite' }\n * };\n * ```\n */\n\n/**\n * Feature flags for opt-in/out TraceLattice capabilities.\n *\n * All flags default to ON. Users can disable any flag via\n * TRACELATTICE_FEATURES_*=false environment variables.\n */\nexport interface FeatureFlags {\n\t/** Enable DAG edges between thoughts (Item #1). @default true */\n\tdagEdges: boolean;\n\t/** Reasoning strategy selector (Item #2). @default 'sequential' */\n\treasoningStrategy: 'sequential' | 'tot';\n\t/** Enable confidence calibration (Item #3). @default true */\n\tcalibration: boolean;\n\t/** Enable thought compression (Item #5). @default true */\n\tcompression: boolean;\n\t/** Enable tool interleaving (Item #6). @default true */\n\ttoolInterleave: boolean;\n\t/** Enable new thought types (Item #8). @default true */\n\tnewThoughtTypes: boolean;\n\t/** Enable outcome recording (prereq for Item #10). @default true */\n\toutcomeRecording: boolean;\n}\nexport interface ServerConfigOptions {\n\t/**\n\t * Maximum number of thoughts to keep in history.\n\t * @default 1000\n\t */\n\tmaxHistorySize?: number;\n\n\t/**\n\t * Maximum number of branches to maintain.\n\t * @default 50\n\t */\n\tmaxBranches?: number;\n\n\t/**\n\t * Maximum size of each branch.\n\t * @default 100\n\t */\n\tmaxBranchSize?: number;\n\n\t/**\n\t * Directory paths to search for skills.\n\t * @default ['.claude/skills', '~/.claude/skills']\n\t */\n\tskillDirs?: string[];\n\n\t/**\n\t * Discovery cache configuration.\n\t */\n\tdiscoveryCache?: {\n\t\t/**\n\t\t * Time-to-live for cache entries in milliseconds.\n\t\t * @default 300000 (5 minutes)\n\t\t */\n\t\tttl?: number;\n\t\t/**\n\t\t * Maximum number of entries in the cache.\n\t\t * @default 100\n\t\t */\n\t\tmaxSize?: number;\n\t};\n\n\t/**\n\t * Persistence configuration for storing history and state.\n\t */\n\tpersistence?: PersistenceConfig;\n\n\t/**\n\t * Maximum number of thoughts to buffer before flushing to persistence.\n\t * @default 100\n\t */\n\tpersistenceBufferSize?: number;\n\n\t/**\n\t * Interval in milliseconds between periodic persistence flushes.\n\t * @default 1000\n\t */\n\tpersistenceFlushInterval?: number;\n\n\t/**\n\t * Maximum number of retries for failed persistence flushes.\n\t * @default 3\n\t */\n\tpersistenceMaxRetries?: number;\n\n\t/**\n\t * Feature flag overrides. Missing fields are filled with defaults (all OFF,\n\t * reasoningStrategy='sequential').\n\t */\n\tfeatures?: Partial<FeatureFlags>;\n\n\t/**\n\t * TTL in milliseconds for suspended tool-interleave entries in SuspensionStore.\n\t * @default 60000\n\t */\n\ttoolInterleaveTtlMs?: number;\n\n\t/**\n\t * Sweep interval in milliseconds for SuspensionStore expiration cleanup.\n\t * @default 60000\n\t */\n\ttoolInterleaveSweepMs?: number;\n}\n\n/**\n * Server configuration with validation and defaults.\n *\n * This class manages all server configuration including history limits,\n * branch limits, skill directories, discovery cache settings, and persistence.\n * All values are validated on construction with appropriate defaults applied.\n *\n * @remarks\n * - Values outside recommended ranges trigger warnings but are still applied\n * - Environment variables override file-based configuration\n * - The `toJSON()` method provides a plain object representation\n *\n * @example\n * ```typescript\n * // Using defaults\n * const config1 = new ServerConfig();\n * console.log(config1.maxHistorySize); // 10000\n *\n * // With custom options\n * const config2 = new ServerConfig({\n * maxHistorySize: 500,\n * persistence: { enabled: true, backend: 'file', options: { dataDir: './data' } }\n * });\n *\n * // Export as plain object\n * const json = config2.toJSON();\n * ```\n */\nexport class ServerConfig {\n\t/** Maximum number of thoughts to keep in history. */\n\tpublic maxHistorySize: number;\n\n\t/** Maximum number of branches to maintain. */\n\tpublic maxBranches: number;\n\n\t/** Maximum size of each branch. */\n\tpublic maxBranchSize: number;\n\n\t/** Directory paths to search for skills. */\n\tpublic skillDirs: string[];\n\n\t/** Discovery cache configuration. */\n\tpublic discoveryCache: { ttl: number; maxSize: number };\n\n\t/** Persistence configuration. */\n\tpublic persistence: PersistenceConfig;\n\n\t/** Maximum number of thoughts to buffer before flushing to persistence. */\n\tpublic persistenceBufferSize: number;\n\n\t/** Interval in milliseconds between periodic persistence flushes. */\n\tpublic persistenceFlushInterval: number;\n\n\t/** Maximum number of retries for failed persistence flushes. */\n\tpublic persistenceMaxRetries: number;\n\n\t/** Feature flag toggles. */\n\tpublic features: FeatureFlags;\n\n\t/** TTL in milliseconds for suspended tool-interleave entries. */\n\tpublic toolInterleaveTtlMs: number;\n\n\t/** Sweep interval in milliseconds for SuspensionStore expiration cleanup. */\n\tpublic toolInterleaveSweepMs: number;\n\n\t/**\n\t * Creates a new ServerConfig instance with validation.\n\t *\n\t * All values are validated and defaults are applied for undefined options.\n\t * Warnings are emitted to console for values outside recommended ranges.\n\t *\n\t * @param options - Optional configuration overrides\n\t *\n\t * @example\n\t * ```typescript\n\t * const config = new ServerConfig({\n\t * maxHistorySize: 500,\n\t * skillDirs: ['./my-skills']\n\t * });\n\t * ```\n\t */\n\tconstructor(options: ServerConfigOptions = {}) {\n\t\tthis.maxHistorySize = this.validateMaxHistorySize(options.maxHistorySize);\n\t\tthis.maxBranches = this.validateMaxBranches(options.maxBranches);\n\t\tthis.maxBranchSize = this.validateMaxBranchSize(options.maxBranchSize);\n\t\tthis.skillDirs = this.validateSkillDirs(options.skillDirs);\n\t\tthis.discoveryCache = this.validateDiscoveryCache(options.discoveryCache);\n\t\tthis.persistence = this.validatePersistence(options.persistence);\n\t\tthis.persistenceBufferSize = this.validatePersistenceBufferSize(options.persistenceBufferSize);\n\t\tthis.persistenceFlushInterval = this.validatePersistenceFlushInterval(\n\t\t\toptions.persistenceFlushInterval\n\t\t);\n\t\tthis.persistenceMaxRetries = this.validatePersistenceMaxRetries(options.persistenceMaxRetries);\n\t\tthis.features = this.validateFeatures(options.features);\n\t\tthis.toolInterleaveTtlMs = this.validatePositiveMs(\n\t\t\toptions.toolInterleaveTtlMs,\n\t\t\t60000,\n\t\t\t'toolInterleaveTtlMs'\n\t\t);\n\t\tthis.toolInterleaveSweepMs = this.validatePositiveMs(\n\t\t\toptions.toolInterleaveSweepMs,\n\t\t\t60000,\n\t\t\t'toolInterleaveSweepMs'\n\t\t);\n\t}\n\n\t/**\n\t * Validates the max history size value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default (1000)\n\t * @private\n\t */\n\tprivate validateMaxHistorySize(value?: number): number {\n\t\tconst defaultValue = 10000;\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(`maxHistorySize must be a finite number, got ${value}`);\n\t\t}\n\t\tif (value < 1) {\n\t\t\tthrow new ConfigurationError(`maxHistorySize must be at least 1, got ${value}`);\n\t\t}\n\t\tif (value > 10000) {\n\t\t\tthrow new ConfigurationError(`maxHistorySize must not exceed 10000, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates the max branches value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default (50)\n\t * @private\n\t */\n\tprivate validateMaxBranches(value?: number): number {\n\t\tconst defaultValue = 50;\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(`maxBranches must be a finite number, got ${value}`);\n\t\t}\n\t\tif (value < 0) {\n\t\t\tthrow new ConfigurationError(`maxBranches must be non-negative, got ${value}`);\n\t\t}\n\t\tif (value > 1000) {\n\t\t\tthrow new ConfigurationError(`maxBranches must not exceed 1000, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates the max branch size value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default (100)\n\t * @private\n\t */\n\tprivate validateMaxBranchSize(value?: number): number {\n\t\tconst defaultValue = 100;\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(`maxBranchSize must be a finite number, got ${value}`);\n\t\t}\n\t\tif (value < 1) {\n\t\t\tthrow new ConfigurationError(`maxBranchSize must be at least 1, got ${value}`);\n\t\t}\n\t\tif (value > 1000) {\n\t\t\tthrow new ConfigurationError(`maxBranchSize must not exceed 1000, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates the skill directories value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default ['.claude/skills', '~/.claude/skills']\n\t * @private\n\t */\n\tprivate validateSkillDirs(value?: string[]): string[] {\n\t\tconst defaultValue = ['.claude/skills', join(homedir(), '.claude/skills')];\n\t\tif (!value) return defaultValue;\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates the discovery cache configuration.\n\t * @param value - The value to validate\n\t * @returns The validated value with defaults applied\n\t * @private\n\t */\n\tprivate validateDiscoveryCache(value?: { ttl?: number; maxSize?: number }): {\n\t\tttl: number;\n\t\tmaxSize: number;\n\t} {\n\t\treturn {\n\t\t\tttl: value?.ttl ?? 300000,\n\t\t\tmaxSize: value?.maxSize ?? 100,\n\t\t};\n\t}\n\n\t/**\n\t * Validates the persistence configuration.\n\t * @param value - The value to validate\n\t * @returns The validated value with defaults applied\n\t * @private\n\t */\n\tprivate validatePersistence(value?: PersistenceConfig): PersistenceConfig {\n\t\tif (!value) {\n\t\t\t// Default to in-memory (no actual persistence, just consistency)\n\t\t\treturn {\n\t\t\t\tenabled: false,\n\t\t\t\tbackend: 'memory',\n\t\t\t};\n\t\t}\n\n\t\t// Validate backend type\n\t\tconst validBackends = ['file', 'sqlite', 'memory'];\n\t\tconst backend = value.backend ?? 'memory';\n\t\tif (!validBackends.includes(backend)) {\n\t\t\tthrow new ConfigurationError(\n\t\t\t\t`persistence.backend must be one of ${validBackends.join(', ')}, got ${backend}`\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\tenabled: value.enabled ?? false,\n\t\t\tbackend,\n\t\t\toptions: value.options ?? {},\n\t\t};\n\t}\n\n\t/**\n\t * Validates the persistence buffer size value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default (100)\n\t * @private\n\t */\n\tprivate validatePersistenceBufferSize(value?: number): number {\n\t\tconst defaultValue = 100;\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(`persistenceBufferSize must be a finite number, got ${value}`);\n\t\t}\n\t\tif (value < 1) {\n\t\t\tthrow new ConfigurationError(`persistenceBufferSize must be at least 1, got ${value}`);\n\t\t}\n\t\tif (value > 10000) {\n\t\t\tthrow new ConfigurationError(`persistenceBufferSize must not exceed 10000, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates the persistence flush interval value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default (1000)\n\t * @private\n\t */\n\tprivate validatePersistenceFlushInterval(value?: number): number {\n\t\tconst defaultValue = 1000;\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(\n\t\t\t\t`persistenceFlushInterval must be a finite number, got ${value}`\n\t\t\t);\n\t\t}\n\t\tif (value < 100) {\n\t\t\tthrow new ConfigurationError(`persistenceFlushInterval must be at least 100, got ${value}`);\n\t\t}\n\t\tif (value > 60000) {\n\t\t\tthrow new ConfigurationError(`persistenceFlushInterval must not exceed 60000, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates the persistence max retries value.\n\t * @param value - The value to validate\n\t * @returns The validated value or default (3)\n\t * @private\n\t */\n\tprivate validatePersistenceMaxRetries(value?: number): number {\n\t\tconst defaultValue = 3;\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(`persistenceMaxRetries must be a finite number, got ${value}`);\n\t\t}\n\t\tif (value < 0) {\n\t\t\tthrow new ConfigurationError(`persistenceMaxRetries must be non-negative, got ${value}`);\n\t\t}\n\t\tif (value > 10) {\n\t\t\tthrow new ConfigurationError(`persistenceMaxRetries must not exceed 10, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Validates feature flags and fills defaults for missing fields.\n\t * @param value - Partial feature flag overrides\n\t * @returns Fully populated FeatureFlags with defaults applied\n\t * @private\n\t */\n\tprivate validateFeatures(value?: Partial<FeatureFlags>): FeatureFlags {\n\t\treturn {\n\t\t\tdagEdges: value?.dagEdges ?? true,\n\t\t\treasoningStrategy: value?.reasoningStrategy ?? 'sequential',\n\t\t\tcalibration: value?.calibration ?? true,\n\t\t\tcompression: value?.compression ?? true,\n\t\t\ttoolInterleave: value?.toolInterleave ?? true,\n\t\t\tnewThoughtTypes: value?.newThoughtTypes ?? true,\n\t\t\toutcomeRecording: value?.outcomeRecording ?? true,\n\t\t};\n\t}\n\n\t/**\n\t * Validates a positive millisecond value with a default fallback.\n\t * @param value - The value to validate\n\t * @param defaultValue - Default applied if value is undefined/null\n\t * @param fieldName - Field name for error messages\n\t * @returns The validated millisecond value\n\t * @private\n\t */\n\tprivate validatePositiveMs(\n\t\tvalue: number | undefined,\n\t\tdefaultValue: number,\n\t\tfieldName: string\n\t): number {\n\t\tif (value === undefined || value === null) return defaultValue;\n\t\tif (typeof value !== 'number' || !Number.isFinite(value)) {\n\t\t\tthrow new ConfigurationError(`${fieldName} must be a finite number, got ${value}`);\n\t\t}\n\t\tif (value < 1) {\n\t\t\tthrow new ConfigurationError(`${fieldName} must be at least 1, got ${value}`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Converts the configuration to a plain object.\n\t *\n\t * Useful for serialization, logging, or when a plain object representation\n\t * is preferred over the ServerConfig instance.\n\t *\n\t * @returns A plain object representation of the configuration\n\t *\n\t * @example\n\t * ```typescript\n\t * const config = new ServerConfig({ maxHistorySize: 500 });\n\t * const json = config.toJSON();\n\t * console.log(JSON.stringify(json, null, 2));\n\t * ```\n\t */\n\tpublic toJSON(): ServerConfigOptions {\n\t\treturn {\n\t\t\tmaxHistorySize: this.maxHistorySize,\n\t\t\tmaxBranches: this.maxBranches,\n\t\t\tmaxBranchSize: this.maxBranchSize,\n\t\t\tskillDirs: this.skillDirs,\n\t\t\tdiscoveryCache: this.discoveryCache,\n\t\t\tpersistence: this.persistence,\n\t\t\tpersistenceBufferSize: this.persistenceBufferSize,\n\t\t\tpersistenceFlushInterval: this.persistenceFlushInterval,\n\t\t\tpersistenceMaxRetries: this.persistenceMaxRetries,\n\t\t\tfeatures: this.features,\n\t\t\ttoolInterleaveTtlMs: this.toolInterleaveTtlMs,\n\t\t\ttoolInterleaveSweepMs: this.toolInterleaveSweepMs,\n\t\t};\n\t}\n}\n"],"names":["ServerConfig","options","value","defaultValue","Number","ConfigurationError","join","homedir","validBackends","backend","fieldName"],"mappings":"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"}
|
|
@@ -16,10 +16,10 @@ import type { IEdgeStore } from '../contracts/interfaces.js';
|
|
|
16
16
|
import type { ISummaryStore } from '../contracts/summary.js';
|
|
17
17
|
import type { Logger } from '../logger/StructuredLogger.js';
|
|
18
18
|
import type { PersistenceBackend } from '../persistence/PersistenceBackend.js';
|
|
19
|
-
import type { IHistoryManager } from './IHistoryManager.js';
|
|
20
|
-
import type { ThoughtData } from './thought.js';
|
|
21
19
|
import { type DehydrationOptions, type HydratedEntry } from './compression/DehydrationPolicy.js';
|
|
20
|
+
import type { IHistoryManager } from './IHistoryManager.js';
|
|
22
21
|
import { type PersistenceEventEmitter } from './PersistenceBuffer.js';
|
|
22
|
+
import type { ThoughtData } from './thought.js';
|
|
23
23
|
export type { PersistenceEventEmitter } from './PersistenceBuffer.js';
|
|
24
24
|
/** Absolute maximum history size (~20MB at 2KB/thought). Cannot be overridden. */
|
|
25
25
|
export declare const ABSOLUTE_MAX_HISTORY_SIZE = 10000;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HistoryManager.d.ts","sourceRoot":"","sources":["../../src/core/HistoryManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"HistoryManager.d.ts","sourceRoot":"","sources":["../../src/core/HistoryManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -26,7 +26,7 @@ class HistoryManager {
|
|
|
26
26
|
_sessionManager;
|
|
27
27
|
constructor(config = {}){
|
|
28
28
|
this._logger = config.logger ?? new NullLogger();
|
|
29
|
-
const requestedMaxSize = config.maxHistorySize ??
|
|
29
|
+
const requestedMaxSize = config.maxHistorySize ?? 10000;
|
|
30
30
|
this._maxHistorySize = Math.min(requestedMaxSize, ABSOLUTE_MAX_HISTORY_SIZE);
|
|
31
31
|
if (requestedMaxSize > ABSOLUTE_MAX_HISTORY_SIZE) this._logger.warn('maxHistorySize exceeds absolute maximum, capped', {
|
|
32
32
|
requested: requestedMaxSize,
|
|
@@ -40,7 +40,7 @@ class HistoryManager {
|
|
|
40
40
|
this._eventEmitter = config.eventEmitter ?? null;
|
|
41
41
|
this._edgeStore = config.edgeStore;
|
|
42
42
|
this._summaryStore = config.summaryStore;
|
|
43
|
-
this._dagEdges = config.dagEdges ??
|
|
43
|
+
this._dagEdges = config.dagEdges ?? true;
|
|
44
44
|
this._edgeEmitter = new EdgeEmitter({
|
|
45
45
|
edgeStore: this._edgeStore,
|
|
46
46
|
dagEdges: this._dagEdges,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core/HistoryManager.js","sources":["../../src/core/HistoryManager.ts"],"sourcesContent":["/**\n * History and branch management for sequential thinking.\n *\n * This module provides the `HistoryManager` class which manages thought history,\n * branching, and optional persistence with per-session state isolation.\n *\n * Internally delegates to three focused collaborators:\n * - `EdgeEmitter` — DAG edge emission\n * - `PersistenceBuffer` — buffered persistence + retry/backoff\n * - `SessionManager` — session lifecycle (TTL/LRU eviction)\n *\n * @module HistoryManager\n */\n\nimport type { IMetrics } from '../contracts/index.js';\nimport { ValidationError, getErrorMessage } from '../errors.js';\nimport type { IEdgeStore } from '../contracts/interfaces.js';\nimport type { ISummaryStore } from '../contracts/summary.js';\nimport { NullLogger } from '../logger/NullLogger.js';\nimport type { Logger } from '../logger/StructuredLogger.js';\nimport type { PersistenceBackend } from '../persistence/PersistenceBackend.js';\nimport type { IHistoryManager } from './IHistoryManager.js';\nimport type { ThoughtData } from './thought.js';\nimport {\n\tDehydrationPolicy,\n\ttype DehydrationOptions,\n\ttype HydratedEntry,\n} from './compression/DehydrationPolicy.js';\nimport { EdgeEmitter } from './graph/EdgeEmitter.js';\nimport { PersistenceBuffer, type PersistenceEventEmitter } from './PersistenceBuffer.js';\nimport { SessionManager } from './SessionManager.js';\n\nexport type { PersistenceEventEmitter } from './PersistenceBuffer.js';\n\n/** Absolute maximum history size (~20MB at 2KB/thought). Cannot be overridden. */\nexport const ABSOLUTE_MAX_HISTORY_SIZE = 10_000;\n\ninterface SessionState {\n\tthought_history: ThoughtData[];\n\tbranches: Record<string, ThoughtData[]>;\n\tavailableMcpTools: string[] | undefined;\n\tavailableSkills: string[] | undefined;\n\twriteBuffer: ThoughtData[];\n\tlastAccessedAt: number;\n\tregisteredBranches: Set<string>;\n}\n\nexport interface HistoryManagerConfig {\n\t/** Maximum number of thoughts to keep in main history. @default 1000 */\n\tmaxHistorySize?: number;\n\t/** Maximum number of branches to maintain. @default 50 */\n\tmaxBranches?: number;\n\t/** Maximum size of each branch. @default 100 */\n\tmaxBranchSize?: number;\n\tlogger?: Logger;\n\tpersistence?: PersistenceBackend | null;\n\tmetrics?: IMetrics;\n\t/** Maximum number of thoughts to buffer before flushing. @default 100 */\n\tpersistenceBufferSize?: number;\n\t/** Periodic flush interval in ms. @default 1000 */\n\tpersistenceFlushInterval?: number;\n\t/** Max retries for failed persistence flushes. @default 3 */\n\tpersistenceMaxRetries?: number;\n\teventEmitter?: PersistenceEventEmitter;\n\tedgeStore?: IEdgeStore;\n\tsummaryStore?: ISummaryStore;\n\t/** Whether to emit DAG edges (gated independently of edgeStore). @default false */\n\tdagEdges?: boolean;\n}\n\n/**\n * Manages thought history and branching for sequential thinking.\n *\n * Owns the per-session `Map<string, SessionState>`. Delegates DAG edge emission,\n * buffered persistence, and session TTL/LRU eviction to focused collaborators while\n * preserving test-coupled private member names (`_flushTimer`, `_startFlushTimer`,\n * `_flushBuffer`, `_sessions`).\n */\nexport class HistoryManager implements IHistoryManager {\n\tprivate static readonly DEFAULT_SESSION = '__global__';\n\tprivate static readonly SESSION_TTL_MS = 30 * 60 * 1000;\n\tprivate static readonly MAX_SESSIONS = 100;\n\tprivate _sessions: Map<string, SessionState> = new Map();\n\tprivate _maxHistorySize: number;\n\tprivate _maxBranches: number;\n\tprivate _maxBranchSize: number;\n\tprivate _logger: Logger;\n\tprivate _persistence: PersistenceBackend | null;\n\tprivate _persistenceEnabled: boolean;\n\tprivate _metrics?: IMetrics;\n\n\tprivate _edgeStore?: IEdgeStore;\n\tprivate _summaryStore?: ISummaryStore;\n\tprivate _dagEdges: boolean;\n\n\tprivate _eventEmitter: PersistenceEventEmitter | null;\n\n\tprivate readonly _edgeEmitter: EdgeEmitter;\n\tprivate _persistenceBuffer: PersistenceBuffer<SessionState> | null;\n\tprivate readonly _sessionManager: SessionManager<SessionState>;\n\n\tconstructor(config: HistoryManagerConfig = {}) {\n\t\tthis._logger = config.logger ?? new NullLogger();\n\t\tconst requestedMaxSize = config.maxHistorySize ?? 1000;\n\t\tthis._maxHistorySize = Math.min(requestedMaxSize, ABSOLUTE_MAX_HISTORY_SIZE);\n\t\tif (requestedMaxSize > ABSOLUTE_MAX_HISTORY_SIZE) {\n\t\t\tthis._logger.warn('maxHistorySize exceeds absolute maximum, capped', {\n\t\t\t\trequested: requestedMaxSize,\n\t\t\t\tapplied: ABSOLUTE_MAX_HISTORY_SIZE,\n\t\t\t});\n\t\t}\n\t\tthis._maxBranches = config.maxBranches || 50;\n\t\tthis._maxBranchSize = config.maxBranchSize || 100;\n\t\tthis._persistence = config.persistence ?? null;\n\t\tthis._persistenceEnabled = this._persistence !== null;\n\t\tthis._metrics = config.metrics;\n\t\tthis._eventEmitter = config.eventEmitter ?? null;\n\t\tthis._edgeStore = config.edgeStore;\n\t\tthis._summaryStore = config.summaryStore;\n\t\tthis._dagEdges = config.dagEdges ?? false;\n\n\t\t// Wire delegates\n\t\tthis._edgeEmitter = new EdgeEmitter({\n\t\t\tedgeStore: this._edgeStore,\n\t\t\tdagEdges: this._dagEdges,\n\t\t\tdefaultSessionId: HistoryManager.DEFAULT_SESSION,\n\t\t\tlogger: this._logger,\n\t\t});\n\n\t\tthis._sessionManager = new SessionManager<SessionState>({\n\t\t\tdefaultSessionId: HistoryManager.DEFAULT_SESSION,\n\t\t\tsessionTtlMs: HistoryManager.SESSION_TTL_MS,\n\t\t\tcleanupIntervalMs: 5 * 60 * 1000,\n\t\t\tgetMaxSessions: () => HistoryManager.MAX_SESSIONS,\n\t\t\tlogger: this._logger,\n\t\t});\n\n\t\tthis._persistenceBuffer = null;\n\t\tif (this._persistenceEnabled && this._persistence) {\n\t\t\tthis._persistenceBuffer = new PersistenceBuffer<SessionState>({\n\t\t\t\tpersistence: this._persistence,\n\t\t\t\tbufferSize: config.persistenceBufferSize ?? 100,\n\t\t\t\tflushInterval: config.persistenceFlushInterval ?? 1000,\n\t\t\t\tmaxRetries: config.persistenceMaxRetries ?? 3,\n\t\t\t\tdefaultSessionId: HistoryManager.DEFAULT_SESSION,\n\t\t\t\tgetSessions: () => this._sessions,\n\t\t\t\tgetDefaultSession: () => this._getSession(),\n\t\t\t\tedgeStore: this._edgeStore,\n\t\t\t\teventEmitter: this._eventEmitter,\n\t\t\t\tlogger: this._logger,\n\t\t\t});\n\t\t\tthis._startFlushTimer();\n\t\t}\n\n\t\tthis._sessionManager.startCleanupTimer(this._sessions);\n\t}\n\n\t// Test-coupled accessors: these private member names must remain reachable\n\t// via `manager as unknown as { _flushTimer; _startFlushTimer }`.\n\tprivate get _flushTimer(): ReturnType<typeof setInterval> | null {\n\t\treturn this._persistenceBuffer?.timer ?? null;\n\t}\n\n\tprivate _startFlushTimer(): void {\n\t\tthis._persistenceBuffer?.startFlushTimer();\n\t}\n\n\tprivate _stopFlushTimer(): void {\n\t\tif (this._flushTimer === null) return;\n\t\tthis._persistenceBuffer?.stopFlushTimer();\n\t}\n\n\t/** @internal Public for backward-compatible test coupling. */\n\tpublic async _flushBuffer(): Promise<void> {\n\t\tawait this._persistenceBuffer?.flush();\n\t}\n\n\t/** EdgeStore instance, if configured. Used by ThoughtProcessor for StrategyContext. */\n\tpublic getEdgeStore(): IEdgeStore | undefined {\n\t\treturn this._edgeStore;\n\t}\n\n\tprivate log(message: string, meta?: Record<string, unknown>): void {\n\t\tthis._logger.info(message, meta);\n\t}\n\n\t/** Gets or creates session state; updates lastAccessedAt. */\n\tprivate _getSession(sessionId?: string): SessionState {\n\t\tconst key = sessionId ?? HistoryManager.DEFAULT_SESSION;\n\t\tlet session = this._sessions.get(key);\n\t\tif (!session) {\n\t\t\tsession = {\n\t\t\t\tthought_history: [],\n\t\t\t\tbranches: {},\n\t\t\t\tavailableMcpTools: undefined,\n\t\t\t\tavailableSkills: undefined,\n\t\t\t\twriteBuffer: [],\n\t\t\t\tlastAccessedAt: Date.now(),\n\t\t\t\tregisteredBranches: new Set<string>(),\n\t\t\t};\n\t\t\tthis._sessions.set(key, session);\n\t\t\tthis._sessionManager.evictExcessSessions(this._sessions);\n\t\t}\n\t\tsession.lastAccessedAt = Date.now();\n\t\treturn session;\n\t}\n\n\t/**\n\t * Adds a thought to the history. Routes per-session, applies retraction for backtrack,\n\t * caches tools/skills, trims, branches, emits DAG edges, and buffers for persistence.\n\t */\n\tpublic addThought(thought: ThoughtData): void {\n\t\tconst session = this._getSession(thought.session_id);\n\t\tthis._metrics?.counter(\n\t\t\t'thought_requests_total',\n\t\t\t1,\n\t\t\t{},\n\t\t\t'Total thought requests added to history'\n\t\t);\n\n\t\tsession.thought_history.push(thought);\n\n\t\t// Logical retraction: when a backtrack thought is added, mark its target\n\t\t// as retracted (append-only — target remains in history).\n\t\tif (thought.thought_type === 'backtrack' && thought.backtrack_target !== undefined) {\n\t\t\tthis._applyRetraction(session, thought.backtrack_target);\n\t\t}\n\n\t\t// Cache available_mcp_tools/available_skills for cross-call persistence\n\t\tif (thought.available_mcp_tools) {\n\t\t\tsession.availableMcpTools = thought.available_mcp_tools;\n\t\t}\n\t\tif (thought.available_skills) {\n\t\t\tsession.availableSkills = thought.available_skills;\n\t\t}\n\n\t\tif (session.thought_history.length > this._maxHistorySize) {\n\t\t\tsession.thought_history = session.thought_history.slice(-this._maxHistorySize);\n\t\t\tthis.log(`History trimmed to ${this._maxHistorySize} items`, {\n\t\t\t\tmaxSize: this._maxHistorySize,\n\t\t\t});\n\t\t}\n\n\t\tif (thought.branch_from_thought && thought.branch_id) {\n\t\t\tthis._addToSessionBranch(session, thought.branch_id, thought);\n\t\t}\n\n\t\t// Track merge operations for analytics\n\t\tif (thought.merge_from_thoughts?.length || thought.merge_branch_ids?.length) {\n\t\t\tthis._metrics?.counter(\n\t\t\t\t'thought_merge_operations_total',\n\t\t\t\t1,\n\t\t\t\t{},\n\t\t\t\t'Total merge operations (graph topology)'\n\t\t\t);\n\t\t}\n\n\t\t// Emit DAG edges (no-op unless edgeStore + dagEdges flag both enabled)\n\t\tthis._edgeEmitter.emitEdgesForThought(session, thought);\n\n\t\t// Buffer thought for persistence (no-op when persistence disabled)\n\t\tif (this._persistenceBuffer) {\n\t\t\tthis._persistenceBuffer.bufferThought(session, thought);\n\t\t}\n\t}\n\n\t/** Marks the thought as retracted within the session (append-only). */\n\tprivate _applyRetraction(session: SessionState, targetNumber: number): void {\n\t\tfor (const t of session.thought_history) {\n\t\t\tif (t.thought_number === targetNumber) {\n\t\t\t\tt.retracted = true;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tfor (const branchThoughts of Object.values(session.branches)) {\n\t\t\tfor (const t of branchThoughts) {\n\t\t\t\tif (t.thought_number === targetNumber) {\n\t\t\t\t\tt.retracted = true;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate _addToSessionBranch(session: SessionState, branchId: string, thought: ThoughtData): void {\n\t\tif (!session.branches[branchId]) {\n\t\t\tsession.branches[branchId] = [];\n\t\t}\n\t\tthis._trimSessionBranchSize(session, branchId);\n\t\tsession.branches[branchId].push(thought);\n\n\t\tif (Object.keys(session.branches).length > this._maxBranches) {\n\t\t\tthis._cleanupSessionBranches(session);\n\t\t}\n\n\t\t// Persist branch to backend if enabled\n\t\tif (this._persistenceEnabled && this._persistence) {\n\t\t\tthis._persistence.saveBranch(branchId, session.branches[branchId]).catch((err) => {\n\t\t\t\tthis.log('Failed to persist branch', {\n\t\t\t\t\tbranchId,\n\t\t\t\t\terror: getErrorMessage(err),\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate _cleanupSessionBranches(session: SessionState): void {\n\t\tconst branchCount = Object.keys(session.branches).length;\n\t\tif (branchCount > this._maxBranches) {\n\t\t\tconst branchesToRemove = Object.keys(session.branches).slice(\n\t\t\t\t0,\n\t\t\t\tbranchCount - this._maxBranches\n\t\t\t);\n\t\t\tfor (const branchId of branchesToRemove) {\n\t\t\t\tdelete session.branches[branchId];\n\t\t\t\tthis.log(`Removed old branch: ${branchId}`, { branchId });\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate _trimSessionBranchSize(session: SessionState, branchId: string): void {\n\t\tif ((session.branches[branchId] ?? []).length > this._maxBranchSize) {\n\t\t\tconst removed = session.branches[branchId]!.length - this._maxBranchSize;\n\t\t\tsession.branches[branchId] = session.branches[branchId]!.slice(-this._maxBranchSize);\n\t\t\tthis.log(`Trimmed branch '${branchId}': removed ${removed} old thoughts`, {\n\t\t\t\tbranchId,\n\t\t\t\tremoved,\n\t\t\t});\n\t\t}\n\t}\n\n\tpublic getHistory(sessionId?: string): ThoughtData[] {\n\t\treturn this._getSession(sessionId).thought_history;\n\t}\n\n\t/**\n\t * Returns history with optional sliding-window dehydration. Non-mutating: when\n\t * `dagEdges` is off OR no `ISummaryStore` is configured, returns same as getHistory.\n\t */\n\tpublic getHistoryHydrated(\n\t\tsessionId?: string,\n\t\topts?: DehydrationOptions\n\t): HydratedEntry[] {\n\t\tconst history = this.getHistory(sessionId);\n\t\tif (!this._dagEdges || !this._summaryStore) {\n\t\t\treturn history.slice();\n\t\t}\n\t\tconst sid = sessionId ?? HistoryManager.DEFAULT_SESSION;\n\t\tconst policy = new DehydrationPolicy(this._summaryStore);\n\t\treturn policy.apply(history, sid, opts);\n\t}\n\n\tpublic getHistoryLength(sessionId?: string): number {\n\t\treturn this._getSession(sessionId).thought_history.length;\n\t}\n\n\tpublic getBranches(sessionId?: string): Record<string, ThoughtData[]> {\n\t\treturn this._getSession(sessionId).branches;\n\t}\n\n\tpublic getBranchIds(sessionId?: string): string[] {\n\t\tconst session = this._getSession(sessionId);\n\t\tconst ids = new Set<string>(Object.keys(session.branches));\n\t\tfor (const id of session.registeredBranches) ids.add(id);\n\t\treturn Array.from(ids);\n\t}\n\n\t/** @throws {ValidationError} If branchId is empty or already exists. */\n\tpublic registerBranch(sessionId: string | undefined, branchId: string): void {\n\t\tif (typeof branchId !== 'string' || branchId.length === 0) {\n\t\t\tthrow new ValidationError('branch_id', 'branch_id must be a non-empty string');\n\t\t}\n\t\tconst session = this._getSession(sessionId);\n\t\tif (branchId in session.branches || session.registeredBranches.has(branchId)) {\n\t\t\tthrow new ValidationError('branch_id', `Branch already exists: ${branchId}`);\n\t\t}\n\t\tsession.registeredBranches.add(branchId);\n\t\tthis.log('Registered branch', { branchId, sessionId: sessionId ?? null });\n\t}\n\n\tpublic branchExists(sessionId: string | undefined, branchId: string): boolean {\n\t\tconst session = this._getSession(sessionId);\n\t\treturn branchId in session.branches || session.registeredBranches.has(branchId);\n\t}\n\n\tpublic getAvailableMcpTools(sessionId?: string): string[] | undefined {\n\t\treturn this._getSession(sessionId).availableMcpTools;\n\t}\n\n\tpublic getAvailableSkills(sessionId?: string): string[] | undefined {\n\t\treturn this._getSession(sessionId).availableSkills;\n\t}\n\n\tpublic getBranch(branchId: string, sessionId?: string): ThoughtData[] | undefined {\n\t\treturn this._getSession(sessionId).branches[branchId];\n\t}\n\n\t/** Clears history and branches. If sessionId provided, clears only that session. */\n\tpublic clear(sessionId?: string): void {\n\t\t// Clear edges from EdgeStore (before session map mutation so keys are still available)\n\t\tif (this._edgeStore) {\n\t\t\tif (sessionId !== undefined) {\n\t\t\t\tthis._edgeStore.clearSession(sessionId);\n\t\t\t} else {\n\t\t\t\tfor (const sid of this._sessions.keys()) {\n\t\t\t\t\tthis._edgeStore.clearSession(sid);\n\t\t\t\t}\n\t\t\t\t// Also clear the default session in case no session entries exist yet\n\t\t\t\tthis._edgeStore.clearSession(HistoryManager.DEFAULT_SESSION);\n\t\t\t}\n\t\t}\n\n\t\tif (sessionId !== undefined) {\n\t\t\tthis._sessions.delete(sessionId);\n\t\t\tthis.log('Session cleared', { sessionId });\n\t\t} else {\n\t\t\tthis._sessions.clear();\n\t\t\tthis.log('History cleared (all sessions)');\n\t\t}\n\n\t\t// Clear persisted data if enabled\n\t\tif (this._persistenceEnabled && this._persistence) {\n\t\t\tthis._persistence.clear().catch((err) => {\n\t\t\t\tthis.log('Failed to clear persisted data', {\n\t\t\t\t\terror: getErrorMessage(err),\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\t}\n\n\tpublic clearSession(sessionId: string): void {\n\t\tthis.clear(sessionId);\n\t}\n\n\tpublic getSessionIds(): string[] {\n\t\treturn Array.from(this._sessions.keys());\n\t}\n\n\tpublic getSessionCount(): number {\n\t\treturn this._sessions.size;\n\t}\n\n\t/** Loads history from persistence into the global session. Call at init. */\n\tpublic async loadFromPersistence(): Promise<void> {\n\t\tif (!this._persistenceEnabled || !this._persistence) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst isHealthy = await this._persistence.healthy();\n\t\t\tif (!isHealthy) {\n\t\t\t\tthis.log('Persistence backend not healthy, skipping load');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst globalSession = this._getSession();\n\n\t\t\tconst history = await this._persistence.loadHistory();\n\t\t\tif (history.length > 0) {\n\t\t\t\tglobalSession.thought_history = history.slice(-this._maxHistorySize);\n\t\t\t\tthis.log(`Loaded ${globalSession.thought_history.length} thoughts from persistence`);\n\t\t\t}\n\n\t\t\tconst branchIds = await this._persistence.listBranches();\n\t\t\tfor (const branchId of branchIds) {\n\t\t\t\tconst branchData = await this._persistence.loadBranch(branchId);\n\t\t\t\tif (branchData) {\n\t\t\t\t\tglobalSession.branches[branchId] = branchData.slice(-this._maxBranchSize);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.log(`Loaded ${Object.keys(globalSession.branches).length} branches from persistence`);\n\n\t\t\t// Load edges if EdgeStore is configured — restore for ALL persisted sessions\n\t\t\tif (this._edgeStore) {\n\t\t\t\ttry {\n\t\t\t\t\tconst edgeSessions = await this._persistence.listEdgeSessions();\n\t\t\t\t\tlet totalEdges = 0;\n\t\t\t\t\tfor (const sessionId of edgeSessions) {\n\t\t\t\t\t\tconst edges = await this._persistence.loadEdges(sessionId);\n\t\t\t\t\t\tfor (const edge of edges) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tthis._edgeStore.addEdge(edge);\n\t\t\t\t\t\t\t\ttotalEdges++;\n\t\t\t\t\t\t\t} catch (edgeErr) {\n\t\t\t\t\t\t\t\tthis.log('Failed to restore edge', {\n\t\t\t\t\t\t\t\t\tedgeId: edge.id,\n\t\t\t\t\t\t\t\t\tsessionId,\n\t\t\t\t\t\t\t\t\terror: getErrorMessage(edgeErr),\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tthis.log(\n\t\t\t\t\t\t`Loaded ${totalEdges} edges across ${edgeSessions.length} sessions from persistence`,\n\t\t\t\t\t);\n\t\t\t\t} catch (edgeError) {\n\t\t\t\t\tthis.log('Failed to load edges from persistence', {\n\t\t\t\t\t\terror: getErrorMessage(edgeError),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthis.log('Failed to load from persistence', {\n\t\t\t\terror: getErrorMessage(error),\n\t\t\t});\n\t\t}\n\t}\n\n\tpublic isPersistenceEnabled(): boolean {\n\t\treturn this._persistenceEnabled;\n\t}\n\n\tpublic getPersistenceBackend(): PersistenceBackend | null {\n\t\treturn this._persistence;\n\t}\n\n\t/** Sets the event emitter for persistence error events (post-construction wiring). */\n\tpublic setEventEmitter(emitter: PersistenceEventEmitter): void {\n\t\tthis._eventEmitter = emitter;\n\t\tthis._persistenceBuffer?.setEventEmitter(emitter);\n\t}\n\n\t/** Stops timers and flushes any remaining buffered writes. */\n\tpublic async shutdown(): Promise<void> {\n\t\tthis._stopFlushTimer();\n\t\tthis._sessionManager.stopCleanupTimer();\n\t\tawait this._flushBuffer();\n\t}\n\n\t/** Total write buffer length across all sessions. */\n\tpublic getWriteBufferLength(): number {\n\t\tlet total = 0;\n\t\tfor (const session of this._sessions.values()) {\n\t\t\ttotal += session.writeBuffer.length;\n\t\t}\n\t\treturn total;\n\t}\n}\n"],"names":["ABSOLUTE_MAX_HISTORY_SIZE","HistoryManager","Map","config","NullLogger","requestedMaxSize","Math","EdgeEmitter","SessionManager","PersistenceBuffer","message","meta","sessionId","key","session","undefined","Date","Set","thought","targetNumber","t","branchThoughts","Object","branchId","err","getErrorMessage","branchCount","branchesToRemove","removed","opts","history","sid","policy","DehydrationPolicy","ids","id","Array","ValidationError","isHealthy","globalSession","branchIds","branchData","edgeSessions","totalEdges","edges","edge","edgeErr","edgeError","error","emitter","total"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"core/HistoryManager.js","sources":["../../src/core/HistoryManager.ts"],"sourcesContent":["/**\n * History and branch management for sequential thinking.\n *\n * This module provides the `HistoryManager` class which manages thought history,\n * branching, and optional persistence with per-session state isolation.\n *\n * Internally delegates to three focused collaborators:\n * - `EdgeEmitter` — DAG edge emission\n * - `PersistenceBuffer` — buffered persistence + retry/backoff\n * - `SessionManager` — session lifecycle (TTL/LRU eviction)\n *\n * @module HistoryManager\n */\n\nimport type { IMetrics } from '../contracts/index.js';\nimport type { IEdgeStore } from '../contracts/interfaces.js';\nimport type { ISummaryStore } from '../contracts/summary.js';\nimport { ValidationError, getErrorMessage } from '../errors.js';\nimport { NullLogger } from '../logger/NullLogger.js';\nimport type { Logger } from '../logger/StructuredLogger.js';\nimport type { PersistenceBackend } from '../persistence/PersistenceBackend.js';\nimport {\n\tDehydrationPolicy,\n\ttype DehydrationOptions,\n\ttype HydratedEntry,\n} from './compression/DehydrationPolicy.js';\nimport { EdgeEmitter } from './graph/EdgeEmitter.js';\nimport type { IHistoryManager } from './IHistoryManager.js';\nimport { PersistenceBuffer, type PersistenceEventEmitter } from './PersistenceBuffer.js';\nimport { SessionManager } from './SessionManager.js';\nimport type { ThoughtData } from './thought.js';\n\nexport type { PersistenceEventEmitter } from './PersistenceBuffer.js';\n\n/** Absolute maximum history size (~20MB at 2KB/thought). Cannot be overridden. */\nexport const ABSOLUTE_MAX_HISTORY_SIZE = 10_000;\n\ninterface SessionState {\n\tthought_history: ThoughtData[];\n\tbranches: Record<string, ThoughtData[]>;\n\tavailableMcpTools: string[] | undefined;\n\tavailableSkills: string[] | undefined;\n\twriteBuffer: ThoughtData[];\n\tlastAccessedAt: number;\n\tregisteredBranches: Set<string>;\n}\n\nexport interface HistoryManagerConfig {\n\t/** Maximum number of thoughts to keep in main history. @default 1000 */\n\tmaxHistorySize?: number;\n\t/** Maximum number of branches to maintain. @default 50 */\n\tmaxBranches?: number;\n\t/** Maximum size of each branch. @default 100 */\n\tmaxBranchSize?: number;\n\tlogger?: Logger;\n\tpersistence?: PersistenceBackend | null;\n\tmetrics?: IMetrics;\n\t/** Maximum number of thoughts to buffer before flushing. @default 100 */\n\tpersistenceBufferSize?: number;\n\t/** Periodic flush interval in ms. @default 1000 */\n\tpersistenceFlushInterval?: number;\n\t/** Max retries for failed persistence flushes. @default 3 */\n\tpersistenceMaxRetries?: number;\n\teventEmitter?: PersistenceEventEmitter;\n\tedgeStore?: IEdgeStore;\n\tsummaryStore?: ISummaryStore;\n\t/** Whether to emit DAG edges (gated independently of edgeStore). @default false */\n\tdagEdges?: boolean;\n}\n\n/**\n * Manages thought history and branching for sequential thinking.\n *\n * Owns the per-session `Map<string, SessionState>`. Delegates DAG edge emission,\n * buffered persistence, and session TTL/LRU eviction to focused collaborators while\n * preserving test-coupled private member names (`_flushTimer`, `_startFlushTimer`,\n * `_flushBuffer`, `_sessions`).\n */\nexport class HistoryManager implements IHistoryManager {\n\tprivate static readonly DEFAULT_SESSION = '__global__';\n\tprivate static readonly SESSION_TTL_MS = 30 * 60 * 1000;\n\tprivate static readonly MAX_SESSIONS = 100;\n\tprivate _sessions: Map<string, SessionState> = new Map();\n\tprivate _maxHistorySize: number;\n\tprivate _maxBranches: number;\n\tprivate _maxBranchSize: number;\n\tprivate _logger: Logger;\n\tprivate _persistence: PersistenceBackend | null;\n\tprivate _persistenceEnabled: boolean;\n\tprivate _metrics?: IMetrics;\n\n\tprivate _edgeStore?: IEdgeStore;\n\tprivate _summaryStore?: ISummaryStore;\n\tprivate _dagEdges: boolean;\n\n\tprivate _eventEmitter: PersistenceEventEmitter | null;\n\n\tprivate readonly _edgeEmitter: EdgeEmitter;\n\tprivate _persistenceBuffer: PersistenceBuffer<SessionState> | null;\n\tprivate readonly _sessionManager: SessionManager<SessionState>;\n\n\tconstructor(config: HistoryManagerConfig = {}) {\n\t\tthis._logger = config.logger ?? new NullLogger();\n\t\tconst requestedMaxSize = config.maxHistorySize ?? 10000;\n\t\tthis._maxHistorySize = Math.min(requestedMaxSize, ABSOLUTE_MAX_HISTORY_SIZE);\n\t\tif (requestedMaxSize > ABSOLUTE_MAX_HISTORY_SIZE) {\n\t\t\tthis._logger.warn('maxHistorySize exceeds absolute maximum, capped', {\n\t\t\t\trequested: requestedMaxSize,\n\t\t\t\tapplied: ABSOLUTE_MAX_HISTORY_SIZE,\n\t\t\t});\n\t\t}\n\t\tthis._maxBranches = config.maxBranches || 50;\n\t\tthis._maxBranchSize = config.maxBranchSize || 100;\n\t\tthis._persistence = config.persistence ?? null;\n\t\tthis._persistenceEnabled = this._persistence !== null;\n\t\tthis._metrics = config.metrics;\n\t\tthis._eventEmitter = config.eventEmitter ?? null;\n\t\tthis._edgeStore = config.edgeStore;\n\t\tthis._summaryStore = config.summaryStore;\n\t\tthis._dagEdges = config.dagEdges ?? true;\n\n\t\t// Wire delegates\n\t\tthis._edgeEmitter = new EdgeEmitter({\n\t\t\tedgeStore: this._edgeStore,\n\t\t\tdagEdges: this._dagEdges,\n\t\t\tdefaultSessionId: HistoryManager.DEFAULT_SESSION,\n\t\t\tlogger: this._logger,\n\t\t});\n\n\t\tthis._sessionManager = new SessionManager<SessionState>({\n\t\t\tdefaultSessionId: HistoryManager.DEFAULT_SESSION,\n\t\t\tsessionTtlMs: HistoryManager.SESSION_TTL_MS,\n\t\t\tcleanupIntervalMs: 5 * 60 * 1000,\n\t\t\tgetMaxSessions: () => HistoryManager.MAX_SESSIONS,\n\t\t\tlogger: this._logger,\n\t\t});\n\n\t\tthis._persistenceBuffer = null;\n\t\tif (this._persistenceEnabled && this._persistence) {\n\t\t\tthis._persistenceBuffer = new PersistenceBuffer<SessionState>({\n\t\t\t\tpersistence: this._persistence,\n\t\t\t\tbufferSize: config.persistenceBufferSize ?? 100,\n\t\t\t\tflushInterval: config.persistenceFlushInterval ?? 1000,\n\t\t\t\tmaxRetries: config.persistenceMaxRetries ?? 3,\n\t\t\t\tdefaultSessionId: HistoryManager.DEFAULT_SESSION,\n\t\t\t\tgetSessions: () => this._sessions,\n\t\t\t\tgetDefaultSession: () => this._getSession(),\n\t\t\t\tedgeStore: this._edgeStore,\n\t\t\t\teventEmitter: this._eventEmitter,\n\t\t\t\tlogger: this._logger,\n\t\t\t});\n\t\t\tthis._startFlushTimer();\n\t\t}\n\n\t\tthis._sessionManager.startCleanupTimer(this._sessions);\n\t}\n\n\t// Test-coupled accessors: these private member names must remain reachable\n\t// via `manager as unknown as { _flushTimer; _startFlushTimer }`.\n\tprivate get _flushTimer(): ReturnType<typeof setInterval> | null {\n\t\treturn this._persistenceBuffer?.timer ?? null;\n\t}\n\n\tprivate _startFlushTimer(): void {\n\t\tthis._persistenceBuffer?.startFlushTimer();\n\t}\n\n\tprivate _stopFlushTimer(): void {\n\t\tif (this._flushTimer === null) return;\n\t\tthis._persistenceBuffer?.stopFlushTimer();\n\t}\n\n\t/** @internal Public for backward-compatible test coupling. */\n\tpublic async _flushBuffer(): Promise<void> {\n\t\tawait this._persistenceBuffer?.flush();\n\t}\n\n\t/** EdgeStore instance, if configured. Used by ThoughtProcessor for StrategyContext. */\n\tpublic getEdgeStore(): IEdgeStore | undefined {\n\t\treturn this._edgeStore;\n\t}\n\n\tprivate log(message: string, meta?: Record<string, unknown>): void {\n\t\tthis._logger.info(message, meta);\n\t}\n\n\t/** Gets or creates session state; updates lastAccessedAt. */\n\tprivate _getSession(sessionId?: string): SessionState {\n\t\tconst key = sessionId ?? HistoryManager.DEFAULT_SESSION;\n\t\tlet session = this._sessions.get(key);\n\t\tif (!session) {\n\t\t\tsession = {\n\t\t\t\tthought_history: [],\n\t\t\t\tbranches: {},\n\t\t\t\tavailableMcpTools: undefined,\n\t\t\t\tavailableSkills: undefined,\n\t\t\t\twriteBuffer: [],\n\t\t\t\tlastAccessedAt: Date.now(),\n\t\t\t\tregisteredBranches: new Set<string>(),\n\t\t\t};\n\t\t\tthis._sessions.set(key, session);\n\t\t\tthis._sessionManager.evictExcessSessions(this._sessions);\n\t\t}\n\t\tsession.lastAccessedAt = Date.now();\n\t\treturn session;\n\t}\n\n\t/**\n\t * Adds a thought to the history. Routes per-session, applies retraction for backtrack,\n\t * caches tools/skills, trims, branches, emits DAG edges, and buffers for persistence.\n\t */\n\tpublic addThought(thought: ThoughtData): void {\n\t\tconst session = this._getSession(thought.session_id);\n\t\tthis._metrics?.counter(\n\t\t\t'thought_requests_total',\n\t\t\t1,\n\t\t\t{},\n\t\t\t'Total thought requests added to history'\n\t\t);\n\n\t\tsession.thought_history.push(thought);\n\n\t\t// Logical retraction: when a backtrack thought is added, mark its target\n\t\t// as retracted (append-only — target remains in history).\n\t\tif (thought.thought_type === 'backtrack' && thought.backtrack_target !== undefined) {\n\t\t\tthis._applyRetraction(session, thought.backtrack_target);\n\t\t}\n\n\t\t// Cache available_mcp_tools/available_skills for cross-call persistence\n\t\tif (thought.available_mcp_tools) {\n\t\t\tsession.availableMcpTools = thought.available_mcp_tools;\n\t\t}\n\t\tif (thought.available_skills) {\n\t\t\tsession.availableSkills = thought.available_skills;\n\t\t}\n\n\t\tif (session.thought_history.length > this._maxHistorySize) {\n\t\t\tsession.thought_history = session.thought_history.slice(-this._maxHistorySize);\n\t\t\tthis.log(`History trimmed to ${this._maxHistorySize} items`, {\n\t\t\t\tmaxSize: this._maxHistorySize,\n\t\t\t});\n\t\t}\n\n\t\tif (thought.branch_from_thought && thought.branch_id) {\n\t\t\tthis._addToSessionBranch(session, thought.branch_id, thought);\n\t\t}\n\n\t\t// Track merge operations for analytics\n\t\tif (thought.merge_from_thoughts?.length || thought.merge_branch_ids?.length) {\n\t\t\tthis._metrics?.counter(\n\t\t\t\t'thought_merge_operations_total',\n\t\t\t\t1,\n\t\t\t\t{},\n\t\t\t\t'Total merge operations (graph topology)'\n\t\t\t);\n\t\t}\n\n\t\t// Emit DAG edges (no-op unless edgeStore + dagEdges flag both enabled)\n\t\tthis._edgeEmitter.emitEdgesForThought(session, thought);\n\n\t\t// Buffer thought for persistence (no-op when persistence disabled)\n\t\tif (this._persistenceBuffer) {\n\t\t\tthis._persistenceBuffer.bufferThought(session, thought);\n\t\t}\n\t}\n\n\t/** Marks the thought as retracted within the session (append-only). */\n\tprivate _applyRetraction(session: SessionState, targetNumber: number): void {\n\t\tfor (const t of session.thought_history) {\n\t\t\tif (t.thought_number === targetNumber) {\n\t\t\t\tt.retracted = true;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tfor (const branchThoughts of Object.values(session.branches)) {\n\t\t\tfor (const t of branchThoughts) {\n\t\t\t\tif (t.thought_number === targetNumber) {\n\t\t\t\t\tt.retracted = true;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate _addToSessionBranch(session: SessionState, branchId: string, thought: ThoughtData): void {\n\t\tif (!session.branches[branchId]) {\n\t\t\tsession.branches[branchId] = [];\n\t\t}\n\t\tthis._trimSessionBranchSize(session, branchId);\n\t\tsession.branches[branchId].push(thought);\n\n\t\tif (Object.keys(session.branches).length > this._maxBranches) {\n\t\t\tthis._cleanupSessionBranches(session);\n\t\t}\n\n\t\t// Persist branch to backend if enabled\n\t\tif (this._persistenceEnabled && this._persistence) {\n\t\t\tthis._persistence.saveBranch(branchId, session.branches[branchId]).catch((err) => {\n\t\t\t\tthis.log('Failed to persist branch', {\n\t\t\t\t\tbranchId,\n\t\t\t\t\terror: getErrorMessage(err),\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate _cleanupSessionBranches(session: SessionState): void {\n\t\tconst branchCount = Object.keys(session.branches).length;\n\t\tif (branchCount > this._maxBranches) {\n\t\t\tconst branchesToRemove = Object.keys(session.branches).slice(\n\t\t\t\t0,\n\t\t\t\tbranchCount - this._maxBranches\n\t\t\t);\n\t\t\tfor (const branchId of branchesToRemove) {\n\t\t\t\tdelete session.branches[branchId];\n\t\t\t\tthis.log(`Removed old branch: ${branchId}`, { branchId });\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate _trimSessionBranchSize(session: SessionState, branchId: string): void {\n\t\tif ((session.branches[branchId] ?? []).length > this._maxBranchSize) {\n\t\t\tconst removed = session.branches[branchId]!.length - this._maxBranchSize;\n\t\t\tsession.branches[branchId] = session.branches[branchId]!.slice(-this._maxBranchSize);\n\t\t\tthis.log(`Trimmed branch '${branchId}': removed ${removed} old thoughts`, {\n\t\t\t\tbranchId,\n\t\t\t\tremoved,\n\t\t\t});\n\t\t}\n\t}\n\n\tpublic getHistory(sessionId?: string): ThoughtData[] {\n\t\treturn this._getSession(sessionId).thought_history;\n\t}\n\n\t/**\n\t * Returns history with optional sliding-window dehydration. Non-mutating: when\n\t * `dagEdges` is off OR no `ISummaryStore` is configured, returns same as getHistory.\n\t */\n\tpublic getHistoryHydrated(\n\t\tsessionId?: string,\n\t\topts?: DehydrationOptions\n\t): HydratedEntry[] {\n\t\tconst history = this.getHistory(sessionId);\n\t\tif (!this._dagEdges || !this._summaryStore) {\n\t\t\treturn history.slice();\n\t\t}\n\t\tconst sid = sessionId ?? HistoryManager.DEFAULT_SESSION;\n\t\tconst policy = new DehydrationPolicy(this._summaryStore);\n\t\treturn policy.apply(history, sid, opts);\n\t}\n\n\tpublic getHistoryLength(sessionId?: string): number {\n\t\treturn this._getSession(sessionId).thought_history.length;\n\t}\n\n\tpublic getBranches(sessionId?: string): Record<string, ThoughtData[]> {\n\t\treturn this._getSession(sessionId).branches;\n\t}\n\n\tpublic getBranchIds(sessionId?: string): string[] {\n\t\tconst session = this._getSession(sessionId);\n\t\tconst ids = new Set<string>(Object.keys(session.branches));\n\t\tfor (const id of session.registeredBranches) ids.add(id);\n\t\treturn Array.from(ids);\n\t}\n\n\t/** @throws {ValidationError} If branchId is empty or already exists. */\n\tpublic registerBranch(sessionId: string | undefined, branchId: string): void {\n\t\tif (typeof branchId !== 'string' || branchId.length === 0) {\n\t\t\tthrow new ValidationError('branch_id', 'branch_id must be a non-empty string');\n\t\t}\n\t\tconst session = this._getSession(sessionId);\n\t\tif (branchId in session.branches || session.registeredBranches.has(branchId)) {\n\t\t\tthrow new ValidationError('branch_id', `Branch already exists: ${branchId}`);\n\t\t}\n\t\tsession.registeredBranches.add(branchId);\n\t\tthis.log('Registered branch', { branchId, sessionId: sessionId ?? null });\n\t}\n\n\tpublic branchExists(sessionId: string | undefined, branchId: string): boolean {\n\t\tconst session = this._getSession(sessionId);\n\t\treturn branchId in session.branches || session.registeredBranches.has(branchId);\n\t}\n\n\tpublic getAvailableMcpTools(sessionId?: string): string[] | undefined {\n\t\treturn this._getSession(sessionId).availableMcpTools;\n\t}\n\n\tpublic getAvailableSkills(sessionId?: string): string[] | undefined {\n\t\treturn this._getSession(sessionId).availableSkills;\n\t}\n\n\tpublic getBranch(branchId: string, sessionId?: string): ThoughtData[] | undefined {\n\t\treturn this._getSession(sessionId).branches[branchId];\n\t}\n\n\t/** Clears history and branches. If sessionId provided, clears only that session. */\n\tpublic clear(sessionId?: string): void {\n\t\t// Clear edges from EdgeStore (before session map mutation so keys are still available)\n\t\tif (this._edgeStore) {\n\t\t\tif (sessionId !== undefined) {\n\t\t\t\tthis._edgeStore.clearSession(sessionId);\n\t\t\t} else {\n\t\t\t\tfor (const sid of this._sessions.keys()) {\n\t\t\t\t\tthis._edgeStore.clearSession(sid);\n\t\t\t\t}\n\t\t\t\t// Also clear the default session in case no session entries exist yet\n\t\t\t\tthis._edgeStore.clearSession(HistoryManager.DEFAULT_SESSION);\n\t\t\t}\n\t\t}\n\n\t\tif (sessionId !== undefined) {\n\t\t\tthis._sessions.delete(sessionId);\n\t\t\tthis.log('Session cleared', { sessionId });\n\t\t} else {\n\t\t\tthis._sessions.clear();\n\t\t\tthis.log('History cleared (all sessions)');\n\t\t}\n\n\t\t// Clear persisted data if enabled\n\t\tif (this._persistenceEnabled && this._persistence) {\n\t\t\tthis._persistence.clear().catch((err) => {\n\t\t\t\tthis.log('Failed to clear persisted data', {\n\t\t\t\t\terror: getErrorMessage(err),\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\t}\n\n\tpublic clearSession(sessionId: string): void {\n\t\tthis.clear(sessionId);\n\t}\n\n\tpublic getSessionIds(): string[] {\n\t\treturn Array.from(this._sessions.keys());\n\t}\n\n\tpublic getSessionCount(): number {\n\t\treturn this._sessions.size;\n\t}\n\n\t/** Loads history from persistence into the global session. Call at init. */\n\tpublic async loadFromPersistence(): Promise<void> {\n\t\tif (!this._persistenceEnabled || !this._persistence) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst isHealthy = await this._persistence.healthy();\n\t\t\tif (!isHealthy) {\n\t\t\t\tthis.log('Persistence backend not healthy, skipping load');\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst globalSession = this._getSession();\n\n\t\t\tconst history = await this._persistence.loadHistory();\n\t\t\tif (history.length > 0) {\n\t\t\t\tglobalSession.thought_history = history.slice(-this._maxHistorySize);\n\t\t\t\tthis.log(`Loaded ${globalSession.thought_history.length} thoughts from persistence`);\n\t\t\t}\n\n\t\t\tconst branchIds = await this._persistence.listBranches();\n\t\t\tfor (const branchId of branchIds) {\n\t\t\t\tconst branchData = await this._persistence.loadBranch(branchId);\n\t\t\t\tif (branchData) {\n\t\t\t\t\tglobalSession.branches[branchId] = branchData.slice(-this._maxBranchSize);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.log(`Loaded ${Object.keys(globalSession.branches).length} branches from persistence`);\n\n\t\t\t// Load edges if EdgeStore is configured — restore for ALL persisted sessions\n\t\t\tif (this._edgeStore) {\n\t\t\t\ttry {\n\t\t\t\t\tconst edgeSessions = await this._persistence.listEdgeSessions();\n\t\t\t\t\tlet totalEdges = 0;\n\t\t\t\t\tfor (const sessionId of edgeSessions) {\n\t\t\t\t\t\tconst edges = await this._persistence.loadEdges(sessionId);\n\t\t\t\t\t\tfor (const edge of edges) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tthis._edgeStore.addEdge(edge);\n\t\t\t\t\t\t\t\ttotalEdges++;\n\t\t\t\t\t\t\t} catch (edgeErr) {\n\t\t\t\t\t\t\t\tthis.log('Failed to restore edge', {\n\t\t\t\t\t\t\t\t\tedgeId: edge.id,\n\t\t\t\t\t\t\t\t\tsessionId,\n\t\t\t\t\t\t\t\t\terror: getErrorMessage(edgeErr),\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tthis.log(\n\t\t\t\t\t\t`Loaded ${totalEdges} edges across ${edgeSessions.length} sessions from persistence`,\n\t\t\t\t\t);\n\t\t\t\t} catch (edgeError) {\n\t\t\t\t\tthis.log('Failed to load edges from persistence', {\n\t\t\t\t\t\terror: getErrorMessage(edgeError),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthis.log('Failed to load from persistence', {\n\t\t\t\terror: getErrorMessage(error),\n\t\t\t});\n\t\t}\n\t}\n\n\tpublic isPersistenceEnabled(): boolean {\n\t\treturn this._persistenceEnabled;\n\t}\n\n\tpublic getPersistenceBackend(): PersistenceBackend | null {\n\t\treturn this._persistence;\n\t}\n\n\t/** Sets the event emitter for persistence error events (post-construction wiring). */\n\tpublic setEventEmitter(emitter: PersistenceEventEmitter): void {\n\t\tthis._eventEmitter = emitter;\n\t\tthis._persistenceBuffer?.setEventEmitter(emitter);\n\t}\n\n\t/** Stops timers and flushes any remaining buffered writes. */\n\tpublic async shutdown(): Promise<void> {\n\t\tthis._stopFlushTimer();\n\t\tthis._sessionManager.stopCleanupTimer();\n\t\tawait this._flushBuffer();\n\t}\n\n\t/** Total write buffer length across all sessions. */\n\tpublic getWriteBufferLength(): number {\n\t\tlet total = 0;\n\t\tfor (const session of this._sessions.values()) {\n\t\t\ttotal += session.writeBuffer.length;\n\t\t}\n\t\treturn total;\n\t}\n}\n"],"names":["ABSOLUTE_MAX_HISTORY_SIZE","HistoryManager","Map","config","NullLogger","requestedMaxSize","Math","EdgeEmitter","SessionManager","PersistenceBuffer","message","meta","sessionId","key","session","undefined","Date","Set","thought","targetNumber","t","branchThoughts","Object","branchId","err","getErrorMessage","branchCount","branchesToRemove","removed","opts","history","sid","policy","DehydrationPolicy","ids","id","Array","ValidationError","isHealthy","globalSession","branchIds","branchData","edgeSessions","totalEdges","edges","edge","edgeErr","edgeError","error","emitter","total"],"mappings":"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"}
|
|
@@ -4,13 +4,13 @@ import { GraphView } from "./graph/GraphView.js";
|
|
|
4
4
|
import { normalizeInput } from "./InputNormalizer.js";
|
|
5
5
|
import { SequentialStrategy } from "./reasoning/strategies/SequentialStrategy.js";
|
|
6
6
|
const DEFAULT_FEATURES = {
|
|
7
|
-
dagEdges:
|
|
7
|
+
dagEdges: true,
|
|
8
8
|
reasoningStrategy: 'sequential',
|
|
9
|
-
calibration:
|
|
10
|
-
compression:
|
|
11
|
-
toolInterleave:
|
|
12
|
-
newThoughtTypes:
|
|
13
|
-
outcomeRecording:
|
|
9
|
+
calibration: true,
|
|
10
|
+
compression: true,
|
|
11
|
+
toolInterleave: true,
|
|
12
|
+
newThoughtTypes: true,
|
|
13
|
+
outcomeRecording: true
|
|
14
14
|
};
|
|
15
15
|
class ThoughtProcessor {
|
|
16
16
|
historyManager;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core/ThoughtProcessor.js","sources":["../../src/core/ThoughtProcessor.ts"],"sourcesContent":["/**\n * Core thought processing logic and validation.\n *\n * This module provides the `ThoughtProcessor` class which handles the main\n * sequential thinking request processing pipeline, including input validation,\n * history management, and response formatting.\n *\n * @module processor\n */\n\nimport { NullLogger } from '../logger/NullLogger.js';\nimport type { Logger } from '../logger/StructuredLogger.js';\nimport type { IEdgeStore } from '../contracts/interfaces.js';\nimport type { ISuspensionStore, SuspensionRecord } from '../contracts/suspension.js';\nimport type { IReasoningStrategy, StrategyDecision } from '../contracts/strategy.js';\nimport type { FeatureFlags } from '../ServerConfig.js';\nimport {\n\tInvalidBacktrackError,\n\tInvalidToolCallError,\n\tSuspensionExpiredError,\n\tSuspensionNotFoundError,\n\tValidationError,\n} from '../errors.js';\nimport { getErrorMessage } from '../errors.js';\nimport { GraphView } from './graph/GraphView.js';\nimport type { IHistoryManager } from './IHistoryManager.js';\nimport { normalizeInput } from './InputNormalizer.js';\nimport type { ThoughtData } from './thought.js';\nimport type { ThoughtEvaluator } from './ThoughtEvaluator.js';\nimport { ThoughtFormatter } from './ThoughtFormatter.js';\nimport type { PatternSignal } from './reasoning.js';\nimport { SequentialStrategy } from './reasoning/strategies/SequentialStrategy.js';\nimport type { CompressionService } from './compression/CompressionService.js';\n\n/**\n * Internal extension to ThoughtData carrying resume metadata.\n * Attached by `_handleToolObservation` so downstream consumers (e.g. edge\n * emission) can correlate the observation with the suspended `tool_call`.\n */\ntype ResumableThought = ThoughtData & { _resumedFrom?: number };\n\n/** Default feature flags used when none are supplied to ThoughtProcessor. */\nconst DEFAULT_FEATURES: FeatureFlags = {\n\tdagEdges: false,\n\treasoningStrategy: 'sequential',\n\tcalibration: false,\n\tcompression: false,\n\ttoolInterleave: false,\n\tnewThoughtTypes: false,\n\toutcomeRecording: false,\n};\n\n/**\n * The return type expected by MCP tool invocations.\n *\n * This structure matches the MCP protocol for tool results,\n * supporting both success and error responses.\n *\n * @example\n * ```typescript\n * const successResult: CallToolResult = {\n * content: [{ type: 'text', text: '{\"status\":\"success\"}' }]\n * };\n *\n * const errorResult: CallToolResult = {\n * content: [{ type: 'text', text: '{\"error\":\"Something went wrong\"}' }],\n * isError: true\n * };\n * ```\n */\nexport interface CallToolResult {\n\t/** Array of content blocks (typically text) to return to the client. */\n\tcontent: Array<{\n\t\ttype: 'text';\n\t\ttext: string;\n\t}>;\n\n\t/** Whether this result represents an error condition. */\n\tisError?: boolean;\n}\n\n/**\n * Core processor for sequential thinking requests.\n *\n * Pipeline: validate → normalize → add to history → format → evaluate signals\n * → run reasoning strategy → return structured response.\n *\n * Auto-adjusts `total_thoughts` if `thought_number` exceeds it. All errors are\n * caught and returned as formatted error responses with `isError: true`.\n *\n * @example\n * ```typescript\n * const processor = new ThoughtProcessor(historyManager, formatter, new ThoughtEvaluator());\n * const result = await processor.process({ thought: '...', thought_number: 1, total_thoughts: 5, next_thought_needed: true });\n * ```\n */\nexport class ThoughtProcessor {\n\t/** Logger for debugging and monitoring. */\n\tprivate _logger: Logger;\n\n\t/** Evaluator for quality signal computation. */\n\tprivate readonly _thoughtEvaluator: ThoughtEvaluator;\n\n\t/**\n\t * Per-session cooldown tracker: session_id → pattern → last_fired_thought_number.\n\t * Prevents re-firing the same pattern hint within 3 thoughts.\n\t */\n\tprivate _hintCooldowns = new Map<string, Map<string, number>>();\n\n\t/**\n\t * Creates a new ThoughtProcessor instance.\n\t *\n\t * @param historyManager - History manager for storing thoughts\n\t * @param thoughtFormatter - Formatter for output formatting\n\t * @param thoughtEvaluator - Evaluator for quality signal computation\n\t * @param logger - Optional logger for diagnostics (defaults to NullLogger)\n\t * @param strategy - Reasoning strategy controlling next-action decisions (defaults to SequentialStrategy)\n\t * @param compressionService - Optional compression service for auto-compression on terminate\n\t */\n\tconstructor(\n\t\tprivate historyManager: IHistoryManager,\n\t\tprivate thoughtFormatter: ThoughtFormatter,\n\t\tthoughtEvaluator: ThoughtEvaluator,\n\t\tlogger?: Logger,\n\t\tprivate readonly strategy: IReasoningStrategy = new SequentialStrategy(),\n\t\tprivate readonly _compressionService?: CompressionService,\n\t\tprivate readonly _suspensionStore?: ISuspensionStore,\n\t\tprivate readonly _features: FeatureFlags = DEFAULT_FEATURES\n\t) {\n\t\tthis._thoughtEvaluator = thoughtEvaluator;\n\t\tthis._logger = logger ?? new NullLogger();\n\t}\n\n\t/**\n\t * Internal logging method.\n\t * @param message - The message to log\n\t * @param meta - Optional metadata\n\t * @private\n\t */\n\tprivate log(message: string, meta?: Record<string, unknown>): void {\n\t\tthis._logger.info(message, meta);\n\t}\n\n\t/**\n\t * Priority ordering for warning patterns (lower = higher priority).\n\t * Ensures the most actionable patterns fill the hint cap first.\n\t */\n\tprivate static readonly _HINT_PRIORITY: Readonly<Record<string, number>> = {\n\t\tconfidence_drift: 1, // Most actionable — degrading confidence\n\t\tunverified_hypothesis: 2, // Important for quality\n\t\tno_alternatives_explored: 3, // Breadth gap\n\t\tconsecutive_without_verification: 4, // Routine pattern\n\t};\n\n\t/**\n\t * Generate actionable hints from pattern signals.\n\t * Rules: max 3 hints, warning-severity only, cooldown of 3 thoughts per pattern per session.\n\t *\n\t * Warning patterns are sorted by priority before selection (see _HINT_PRIORITY).\n\t * Higher-priority patterns (lower number) fill the hint cap first.\n\t *\n\t * @param patterns - Detected pattern signals\n\t * @param currentThoughtNumber - The current thought number being processed\n\t * @param sessionId - Session identifier for cooldown scoping\n\t * @returns Array of hint strings (max 3), empty if no warnings\n\t */\n\tprivate _generateHints(\n\t\tpatterns: PatternSignal[],\n\t\tcurrentThoughtNumber: number,\n\t\tsessionId?: string\n\t): string[] {\n\t\tconst warnings = patterns.filter((p) => p.severity === 'warning');\n\t\tif (warnings.length === 0) return [];\n\n\t\t// Sort by priority (lower number = higher priority)\n\t\twarnings.sort((a, b) => {\n\t\t\tconst pa = ThoughtProcessor._HINT_PRIORITY[a.pattern] ?? 99;\n\t\t\tconst pb = ThoughtProcessor._HINT_PRIORITY[b.pattern] ?? 99;\n\t\t\treturn pa - pb;\n\t\t});\n\n\t\tconst sessionKey = sessionId ?? '__global__';\n\t\tif (!this._hintCooldowns.has(sessionKey)) {\n\t\t\tthis._hintCooldowns.set(sessionKey, new Map());\n\t\t}\n\t\tconst cooldowns = this._hintCooldowns.get(sessionKey)!;\n\n\t\tconst hints: string[] = [];\n\t\tfor (const warning of warnings) {\n\t\t\tif (hints.length >= 3) break;\n\n\t\t\tconst lastFired = cooldowns.get(warning.pattern);\n\t\t\tif (lastFired !== undefined && currentThoughtNumber - lastFired < 3) {\n\t\t\t\tcontinue; // Still in cooldown\n\t\t\t}\n\n\t\t\thints.push(warning.message);\n\t\t\tcooldowns.set(warning.pattern, currentThoughtNumber);\n\t\t}\n\n\t\treturn hints;\n\t}\n\n\t/**\n\t * Processes a thought through the sequential thinking pipeline.\n\t *\n\t * This method validates the input, adds it to history, formats the output,\n\t * computes quality signals via the ThoughtEvaluator, and returns\n\t * a structured response with metadata about the current state.\n\t *\n\t * @param input - The thought data to process\n\t * @returns A Promise resolving to the formatted tool result containing:\n\t * - `thought_number` — Current thought index\n\t * - `total_thoughts` — Estimated total thoughts\n\t * - `next_thought_needed` — Whether to continue\n\t * - `branches` — Active branch IDs\n\t * - `thought_history_length` — Number of thoughts in history\n\t * - `available_mcp_tools` — MCP tools available for recommendation\n\t * - `available_skills` — Skills available for recommendation\n\t * - `current_step` — Current step recommendation\n\t * - `previous_steps` — Previously recommended steps\n\t * - `remaining_steps` — Upcoming step descriptions\n\t * - `thought_type` — Classification of thought purpose (optional)\n\t * - `quality_score` — Self-assessed quality score 0-1 (optional)\n\t * - `confidence` — Self-assessed confidence 0-1 (optional)\n\t * - `hypothesis_id` — Hypothesis link for verification chains (optional)\n * - `confidence_signals` — Computed reasoning quality signals (includes structural_quality and quality_components)\n * - `reasoning_stats` — Aggregated reasoning analytics\n * - `reasoning_hints` — (Conditional) Actionable hints from pattern analysis, max 3, warning-severity only (optional)\n\t *\n\t * @example\n\t * ```typescript\n\t * const result = await processor.process({\n\t * thought: 'I should read the README file',\n\t * thought_number: 1,\n\t * total_thoughts: 3,\n\t * next_thought_needed: true\n\t * });\n\t *\n\t * console.log(result.content[0].text);\n\t * // Output includes: thought_number, total_thoughts, next_thought_needed,\n\t * // branches, thought_history_length, and any recommendations\n\t * ```\n\t */\n\tpublic async process(input: ThoughtData): Promise<CallToolResult> {\n\t\ttry {\n\t\t\t// Normalize input to handle common LLM field name mistakes\n\t\t\tconst normalizedInput = normalizeInput(input);\n\t\t\tconst sessionId = normalizedInput.session_id;\n\n\t\t\t// Handle reset_state: clear session before processing\n\t\t\tif (normalizedInput.reset_state) {\n\t\t\t\tthis.historyManager.clear(sessionId);\n\t\t\t\tthis.log('State reset for session', { sessionId: sessionId ?? '__global__' });\n\t\t\t}\n\n\t\t\t// Persist available_mcp_tools/available_skills across calls within a session.\n\t\t\t// If the caller omits these, reuse the last-seen values from the session.\n\t\t\tif (!normalizedInput.available_mcp_tools) {\n\t\t\t\tnormalizedInput.available_mcp_tools = this.historyManager.getAvailableMcpTools(sessionId);\n\t\t\t}\n\t\t\tif (!normalizedInput.available_skills) {\n\t\t\t\tnormalizedInput.available_skills = this.historyManager.getAvailableSkills(sessionId);\n\t\t\t}\n\n\t\t\tconst { result: validatedInput, warnings: validateWarnings } =\n\t\t\t\tthis.validateInput(normalizedInput);\n\t\t\tconst { result: checkedInput, warnings: refWarnings } =\n\t\t\t\tthis._validateCrossReferences(validatedInput, sessionId);\n\t\t\tconst allWarnings = [...validateWarnings, ...refWarnings];\n\n\t\t\t// Validate new thought types and tool-interleave invariants.\n\t\t\tthis._validateNewTypes(checkedInput, sessionId);\n\n\t\t\t// Tool-interleave suspend path: persist the tool_call thought, then return\n\t\t\t// a `suspended` envelope without running strategy/evaluator.\n\t\t\tif (checkedInput.thought_type === 'tool_call' && this._suspensionStore) {\n\t\t\t\treturn this._handleToolCall(checkedInput, sessionId);\n\t\t\t}\n\n\t\t\t// Tool-interleave resume path: consume the suspension and continue the\n\t\t\t// normal pipeline (addThought → format → evaluate → strategy).\n\t\t\tif (checkedInput.thought_type === 'tool_observation' && this._suspensionStore) {\n\t\t\t\tthis._handleToolObservation(checkedInput, sessionId);\n\t\t\t}\n\n\t\t\tthis.historyManager.addThought(checkedInput);\n\n\t\t\tconst formattedThought = this.thoughtFormatter.formatThought(checkedInput);\n\t\t\tthis.log(formattedThought, { sessionId: sessionId ?? '__global__' });\n\n\t\t\t// Compute quality signals — fetch history/branches once\n\t\t\tconst history = this.historyManager.getHistory(sessionId);\n\t\t\tconst branches = this.historyManager.getBranches(sessionId);\n\n\t\t\tconst confidenceSignals = this._thoughtEvaluator.computeConfidenceSignals(\n\t\t\t\thistory,\n\t\t\t\tbranches\n\t\t\t);\n\t\t\tconst reasoningStats = this._thoughtEvaluator.computeReasoningStats(\n\t\t\t\thistory,\n\t\t\t\tbranches\n\t\t\t);\n\n\t\t\t// Detect reasoning patterns and generate hints\n\t\t\tconst patternSignals = this._thoughtEvaluator.computePatternSignals(\n\t\t\t\thistory,\n\t\t\t\tbranches\n\t\t\t);\n\t\t\tconst reasoningHints = this._generateHints(\n\t\t\t\tpatternSignals,\n\t\t\t\tcheckedInput.thought_number,\n\t\t\t\tsessionId\n\t\t\t);\n\n\t\t\t// Strategy decision — pluggable reasoning policy hook.\n\t\t\t// Built after history/stats so strategies see the latest state.\n\t\t\tconst decision = this._runStrategy(checkedInput, history, reasoningStats, sessionId);\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthought_number: checkedInput.thought_number,\n\t\t\t\t\t\t\ttotal_thoughts: checkedInput.total_thoughts,\n\t\t\t\t\t\t\tnext_thought_needed: checkedInput.next_thought_needed ?? true,\n\t\t\t\t\t\t\tbranches: this.historyManager.getBranchIds(sessionId),\n\t\t\t\t\t\t\tthought_history_length: this.historyManager.getHistoryLength(sessionId),\n\t\t\t\t\t\t\tavailable_mcp_tools: checkedInput.available_mcp_tools,\n\t\t\t\t\t\t\tavailable_skills: checkedInput.available_skills,\n\t\t\t\t\t\t\tcurrent_step: checkedInput.current_step,\n\t\t\t\t\t\t\tprevious_steps: checkedInput.previous_steps,\n\t\t\t\t\t\t\tremaining_steps: checkedInput.remaining_steps,\n\t\t\t\t\t\t\t// Reasoning enrichment fields\n\t\t\t\t\t\t\tthought_type: checkedInput.thought_type,\n\t\t\t\t\t\t\tquality_score: checkedInput.quality_score,\n\t\t\t\t\t\t\tconfidence: checkedInput.confidence,\n\t\t\t\t\t\t\thypothesis_id: checkedInput.hypothesis_id,\n\t\t\t\t\t\t\tconfidence_signals: confidenceSignals,\n\t\t\t\t\t\t\treasoning_stats: reasoningStats,\n\t\t\t\t\t\t\t...(reasoningHints.length > 0 && { reasoning_hints: reasoningHints }),\n\t\t\t\t\t\t\t...(decision.action !== 'continue' && { strategy_hint: decision }),\n\t\t\t\t\t\t\t...(allWarnings.length > 0 && { warnings: allWarnings.slice(0, 3) }),\n\t\t\t\t\t\t\t...(sessionId ? { session_id: sessionId } : {}),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tnull,\n\t\t\t\t\t\t2\n\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\terror: getErrorMessage(error),\n\t\t\t\t\t\t\t\tstatus: 'failed',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t2\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tisError: true,\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Run the configured reasoning strategy and return its decision.\n\t * Strategy errors degrade to `{ action: 'continue' }`.\n\t * @private\n\t */\n\tprivate _runStrategy(\n\t\tcurrentThought: ThoughtData,\n\t\thistory: ThoughtData[],\n\t\tstats: ReturnType<ThoughtEvaluator['computeReasoningStats']>,\n\t\tsessionId?: string\n\t): StrategyDecision {\n\t\tlet decision: StrategyDecision;\n\t\ttry {\n\t\t\tconst edgeStore = this._getEdgeStore();\n\t\t\tconst graph = edgeStore ? new GraphView(edgeStore) : (undefined as unknown as GraphView);\n\t\t\tdecision = this.strategy.decide({\n\t\t\t\tsessionId: sessionId ?? '__global__',\n\t\t\t\thistory,\n\t\t\t\tgraph,\n\t\t\t\tstats,\n\t\t\t\tcurrentThought,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthis._logger.warn('Reasoning strategy threw — defaulting to continue', {\n\t\t\t\tstrategy: this.strategy.name,\n\t\t\t\terror: getErrorMessage(error),\n\t\t\t});\n\t\t\tdecision = { action: 'continue' };\n\t\t}\n\n\t\t// Auto-compression trigger: when strategy terminates a branch and\n\t\t// compression is enabled, summarize the branch subtree. Compression\n\t\t// failures must NEVER break the thought pipeline.\n\t\tif (\n\t\t\tdecision.action === 'terminate' &&\n\t\t\tthis._compressionService &&\n\t\t\tcurrentThought.branch_id\n\t\t) {\n\t\t\ttry {\n\t\t\t\tconst sid = sessionId ?? '__global__';\n\t\t\t\tconst branchRoot = this._findBranchRoot(sid, currentThought.branch_id);\n\t\t\t\tif (branchRoot) {\n\t\t\t\t\tthis._compressionService.compressBranch(sid, currentThought.branch_id, branchRoot);\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tthis._logger.debug('Compression auto-trigger failed', {\n\t\t\t\t\terror: getErrorMessage(err),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn decision;\n\t}\n\n\t/**\n\t * Locate the root thought id for a branch.\n\t * Prefers GraphView.branchThoughts() when an EdgeStore is available;\n\t * falls back to historyManager.getBranches()[branchId][0].id.\n\t * @private\n\t */\n\tprivate _findBranchRoot(sessionId: string, branchId: string): string | undefined {\n\t\tconst edgeStore = this._getEdgeStore();\n\t\tconst branches = this.historyManager.getBranches(sessionId);\n\t\tconst branchList = branches[branchId];\n\t\tconst firstId = branchList?.[0]?.id;\n\t\tif (edgeStore && firstId) {\n\t\t\tconst graph = new GraphView(edgeStore);\n\t\t\tconst ids = graph.branchThoughts(sessionId, firstId);\n\t\t\tif (ids.length > 0) return ids[0];\n\t\t}\n\t\treturn firstId;\n\t}\n\n\t/** Best-effort access to the EdgeStore via duck typing. @private */\n\tprivate _getEdgeStore(): IEdgeStore | undefined {\n\t\tconst hm = this.historyManager as { getEdgeStore?: () => IEdgeStore | undefined };\n\t\treturn typeof hm.getEdgeStore === 'function' ? hm.getEdgeStore() : undefined;\n\t}\n\n\t/**\n\t * Validates and normalizes thought input.\n\t *\n\t * Ensures that thought numbers are consistent and within valid ranges.\n\t * If `thought_number` exceeds `total_thoughts`, `total_thoughts` is\n\t * automatically adjusted to match and a warning is emitted.\n\t *\n\t * @param input - The input to validate\n\t * @returns Object with validated input and any warnings generated\n\t * @private\n\t *\n\t * @example\n\t * ```typescript\n\t * // Auto-adjusts total_thoughts when thought_number exceeds it\n\t * const { result, warnings } = this.validateInput(input);\n\t * // result.total_thoughts === 10 (auto-adjusted from 5)\n\t * // warnings === ['Auto-adjusted total_thoughts from 5 to 10 to match thought_number']\n\t * ```\n\t */\n\tprivate validateInput(input: ThoughtData): {\n\t\tresult: ThoughtData;\n\t\twarnings: string[];\n\t} {\n\t\tconst warnings: string[] = [];\n\t\tif (input.thought_number > input.total_thoughts) {\n\t\t\tconst originalTotal = input.total_thoughts;\n\t\t\twarnings.push(\n\t\t\t\t`Auto-adjusted total_thoughts from ${originalTotal} to ${input.thought_number} to match thought_number`\n\t\t\t);\n\t\t\tthis._logger.warn('Auto-adjusted total_thoughts to match thought_number', {\n\t\t\t\tthought_number: input.thought_number,\n\t\t\t\toriginal_total_thoughts: originalTotal,\n\t\t\t\tadjusted_total_thoughts: input.thought_number,\n\t\t\t});\n\t\t\tinput.total_thoughts = input.thought_number;\n\t\t}\n\t\treturn { result: input, warnings };\n\t}\n\n\t/**\n\t * Validates cross-field references against actual thought history.\n\t * Drops invalid references with a warning log — never rejects.\n\t * LLMs frequently send optimistic references to thoughts that don't exist yet.\n\t *\n\t * @param input - The thought data to validate\n\t * @returns Object with cleaned input and any warnings generated\n\t * @private\n\t *\n\t * @example\n\t * ```typescript\n\t * // verification_target=999 with only 3 thoughts in history\n\t * const { result, warnings } = this._validateCrossReferences(input);\n\t * // result.verification_target === undefined\n\t * // warnings === ['Dropped dangling verification_target: 999 (history has 3 thoughts)']\n\t * ```\n\t */\n\tprivate _validateCrossReferences(input: ThoughtData, sessionId?: string): {\n\t\tresult: ThoughtData;\n\t\twarnings: string[];\n\t} {\n\t\tconst warnings: string[] = [];\n\t\tconst historyLength = this.historyManager.getHistoryLength(sessionId);\n\n\t\t// verification_target: must reference existing thought\n\t\tif (input.verification_target !== undefined && input.verification_target > historyLength) {\n\t\t\twarnings.push(\n\t\t\t\t`Dropped dangling verification_target: ${input.verification_target} (history has ${historyLength} thoughts)`\n\t\t\t);\n\t\t\tthis._logger.warn('Dropped dangling verification_target', {\n\t\t\t\tverification_target: input.verification_target,\n\t\t\t\thistoryLength,\n\t\t\t});\n\t\t\tinput.verification_target = undefined;\n\t\t}\n\n\t\t// revises_thought: must reference existing thought\n\t\tif (input.revises_thought !== undefined && input.revises_thought > historyLength) {\n\t\t\twarnings.push(\n\t\t\t\t`Dropped dangling revises_thought: ${input.revises_thought} (history has ${historyLength} thoughts)`\n\t\t\t);\n\t\t\tthis._logger.warn('Dropped dangling revises_thought', {\n\t\t\t\trevises_thought: input.revises_thought,\n\t\t\t\thistoryLength,\n\t\t\t});\n\t\t\tinput.revises_thought = undefined;\n\t\t}\n\n\t\t// branch_from_thought: must reference existing thought\n\t\tif (input.branch_from_thought !== undefined && input.branch_from_thought > historyLength) {\n\t\t\twarnings.push(\n\t\t\t\t`Dropped dangling branch_from_thought: ${input.branch_from_thought} (history has ${historyLength} thoughts)`\n\t\t\t);\n\t\t\tthis._logger.warn('Dropped dangling branch_from_thought', {\n\t\t\t\tbranch_from_thought: input.branch_from_thought,\n\t\t\t\thistoryLength,\n\t\t\t});\n\t\t\tinput.branch_from_thought = undefined;\n\t\t}\n\n\t\t// synthesis_sources: filter to existing thoughts only\n\t\tif (input.synthesis_sources?.length) {\n\t\t\tconst valid = input.synthesis_sources.filter((n: number) => n <= historyLength);\n\t\t\tif (valid.length < input.synthesis_sources.length) {\n\t\t\t\tconst dropped = input.synthesis_sources.filter((n: number) => n > historyLength);\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Filtered dangling synthesis_sources: [${dropped.join(', ')}] (history has ${historyLength} thoughts)`\n\t\t\t\t);\n\t\t\t\tthis._logger.warn('Filtered dangling synthesis_sources', {\n\t\t\t\t\toriginal: input.synthesis_sources,\n\t\t\t\t\tfiltered: valid,\n\t\t\t\t\thistoryLength,\n\t\t\t\t});\n\t\t\t}\n\t\t\tinput.synthesis_sources = valid.length > 0 ? valid : undefined;\n\t\t}\n\n\t\t// merge_from_thoughts: filter to existing thoughts only\n\t\tif (input.merge_from_thoughts?.length) {\n\t\t\tconst valid = input.merge_from_thoughts.filter((n: number) => n <= historyLength);\n\t\t\tif (valid.length < input.merge_from_thoughts.length) {\n\t\t\t\tconst dropped = input.merge_from_thoughts.filter(\n\t\t\t\t\t(n: number) => n > historyLength\n\t\t\t\t);\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Filtered dangling merge_from_thoughts: [${dropped.join(', ')}] (history has ${historyLength} thoughts)`\n\t\t\t\t);\n\t\t\t\tthis._logger.warn('Filtered dangling merge_from_thoughts', {\n\t\t\t\t\toriginal: input.merge_from_thoughts,\n\t\t\t\t\tfiltered: valid,\n\t\t\t\t\thistoryLength,\n\t\t\t\t});\n\t\t\t}\n\t\t\tinput.merge_from_thoughts = valid.length > 0 ? valid : undefined;\n\t\t}\n\n\t\t// merge_branch_ids: filter to existing branches only (includes pre-registered)\n\t\tif (input.merge_branch_ids?.length) {\n\t\t\tconst valid = input.merge_branch_ids.filter((id: string) =>\n\t\t\t\tthis.historyManager.branchExists(sessionId, id)\n\t\t\t);\n\t\t\tif (valid.length < input.merge_branch_ids.length) {\n\t\t\t\tconst dropped = input.merge_branch_ids.filter(\n\t\t\t\t\t(id: string) => !this.historyManager.branchExists(sessionId, id)\n\t\t\t\t);\n\t\t\t\twarnings.push(`Filtered dangling merge_branch_ids: [${dropped.join(', ')}]`);\n\t\t\t\tthis._logger.warn('Filtered dangling merge_branch_ids', {\n\t\t\t\t\toriginal: input.merge_branch_ids,\n\t\t\t\t\tfiltered: valid,\n\t\t\t\t\texistingBranches: this.historyManager.getBranchIds(sessionId),\n\t\t\t\t});\n\t\t\t}\n\t\t\tinput.merge_branch_ids = valid.length > 0 ? valid : undefined;\n\t\t}\n\n\t\treturn { result: input, warnings };\n\t}\n\n\t/**\n\t * Validate new thought-type invariants behind feature flags.\n\t * @private\n\t */\n\tprivate _validateNewTypes(input: ThoughtData, sessionId?: string): void {\n\t\tconst t = input.thought_type;\n\t\tif ((t === 'tool_call' || t === 'tool_observation') && !this._features.toolInterleave) {\n\t\t\tthrow new ValidationError(\n\t\t\t\t'thought_type',\n\t\t\t\t`Type '${t}' requires the toolInterleave feature flag. Set TRACELATTICE_FEATURES_TOOL_INTERLEAVE=true to enable it.`\n\t\t\t);\n\t\t}\n\t\tif (\n\t\t\t(t === 'assumption' || t === 'decomposition' || t === 'backtrack') &&\n\t\t\t!this._features.newThoughtTypes\n\t\t) {\n\t\t\tthrow new ValidationError(\n\t\t\t\t'thought_type',\n\t\t\t\t`Type '${t}' requires the newThoughtTypes feature flag. Set TRACELATTICE_FEATURES_NEW_THOUGHT_TYPES=true to enable it, or use '${ThoughtProcessor._getWorkaroundType(t)}' type as a workaround.`\n\t\t\t);\n\t\t}\n\t\tif (t === 'tool_call' && !input.tool_name) {\n\t\t\tthrow new InvalidToolCallError(\n\t\t\t\t'tool_call thought ' + input.thought_number + ' missing required tool_name'\n\t\t\t);\n\t\t}\n\t\tif (t === 'tool_observation' && !input.continuation_token) {\n\t\t\tthrow new ValidationError(\n\t\t\t\t'continuation_token',\n\t\t\t\t'tool_observation thought ' + input.thought_number + ' missing continuation_token'\n\t\t\t);\n\t\t}\n\t\tif (t === 'backtrack') {\n\t\t\tif (input.backtrack_target === undefined) {\n\t\t\t\tthrow new ValidationError(\n\t\t\t\t\t'backtrack_target',\n\t\t\t\t\t'backtrack thought ' + input.thought_number + ' requires backtrack_target'\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (input.backtrack_target > input.thought_number) {\n\t\t\t\tthrow new InvalidBacktrackError(\n\t\t\t\t\t'backtrack_target ' + input.backtrack_target + ' must be <= thought_number ' + input.thought_number\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!this._thoughtNumberExists(input.backtrack_target, sessionId)) {\n\t\t\t\tthrow new InvalidBacktrackError(\n\t\t\t\t\t'backtrack_target ' + input.backtrack_target + ' does not exist in session history'\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Checks whether a given thought_number exists in the session history or any branch.\n\t * @private\n\t */\n\tprivate _thoughtNumberExists(thoughtNumber: number, sessionId?: string): boolean {\n\t\tconst history = this.historyManager.getHistory(sessionId);\n\t\tfor (const t of history) {\n\t\t\tif (t.thought_number === thoughtNumber) return true;\n\t\t}\n\t\tconst branches = this.historyManager.getBranches(sessionId);\n\t\tfor (const branchThoughts of Object.values(branches)) {\n\t\t\tfor (const t of branchThoughts) {\n\t\t\t\tif (t.thought_number === thoughtNumber) return true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Returns a workaround thought type for a feature-flagged type.\n\t * @private\n\t */\n\tprivate static _getWorkaroundType(t: 'assumption' | 'decomposition' | 'backtrack'): string {\n\t\tswitch (t) {\n\t\t\tcase 'assumption':\n\t\t\t\treturn 'regular';\n\t\t\tcase 'decomposition':\n\t\t\t\treturn 'hypothesis';\n\t\t\tcase 'backtrack':\n\t\t\t\treturn 'regular';\n\t\t}\n\t}\n\n\t/**\n\t * Persist a tool_call thought and return a `suspended` envelope.\n\t * Strategy/evaluator are intentionally skipped.\n\t * @private\n\t */\n\tprivate _handleToolCall(input: ThoughtData, sessionId?: string): CallToolResult {\n\t\tthis.historyManager.addThought(input);\n\t\tconst record: SuspensionRecord = this._suspensionStore!.suspend({\n\t\t\tsessionId: sessionId ?? '__global__',\n\t\t\ttoolCallThoughtNumber: input.thought_number,\n\t\t\ttoolName: input.tool_name!,\n\t\t\ttoolArguments: input.tool_arguments ?? {},\n\t\t\tttlMs: 5 * 60_000,\n\t\t\texpiresAt: 0,\n\t\t});\n\t\treturn {\n\t\t\tcontent: [\n\t\t\t\t{\n\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\ttext: JSON.stringify(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstatus: 'suspended',\n\t\t\t\t\t\t\tcontinuation_token: record.token,\n\t\t\t\t\t\t\ttool_name: record.toolName,\n\t\t\t\t\t\t\ttool_arguments: record.toolArguments,\n\t\t\t\t\t\t\texpires_at: record.expiresAt,\n\t\t\t\t\t\t\tthought_number: input.thought_number,\n\t\t\t\t\t\t\ttotal_thoughts: input.total_thoughts,\n\t\t\t\t\t\t\t...(sessionId ? { session_id: sessionId } : {}),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tnull,\n\t\t\t\t\t\t2\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t}\n\n\t/**\n\t * Resume from a tool_observation, consuming the suspension record.\n\t * Distinguishes missing vs expired via peek().\n\t * @private\n\t */\n\tprivate _handleToolObservation(input: ThoughtData, _sessionId?: string): void {\n\t\tconst token = input.continuation_token!;\n\t\tconst peeked = this._suspensionStore!.peek(token);\n\t\tif (peeked && peeked.expiresAt <= Date.now()) {\n\t\t\tthrow new SuspensionExpiredError('Suspension token expired: ' + token);\n\t\t}\n\t\tconst record = this._suspensionStore!.resume(token);\n\t\tif (!record) {\n\t\t\tthrow new SuspensionNotFoundError('Suspension token not found: ' + token);\n\t\t}\n\t\t(input as ResumableThought)._resumedFrom = record.toolCallThoughtNumber;\n\t}\n}\n"],"names":["DEFAULT_FEATURES","ThoughtProcessor","Map","historyManager","thoughtFormatter","thoughtEvaluator","logger","strategy","SequentialStrategy","_compressionService","_suspensionStore","_features","NullLogger","message","meta","patterns","currentThoughtNumber","sessionId","warnings","p","a","b","pa","pb","sessionKey","cooldowns","hints","warning","lastFired","undefined","input","normalizedInput","normalizeInput","validatedInput","validateWarnings","checkedInput","refWarnings","allWarnings","formattedThought","history","branches","confidenceSignals","reasoningStats","patternSignals","reasoningHints","decision","JSON","error","getErrorMessage","currentThought","stats","edgeStore","graph","GraphView","sid","branchRoot","err","branchId","branchList","firstId","ids","hm","originalTotal","historyLength","valid","n","dropped","id","t","ValidationError","InvalidToolCallError","InvalidBacktrackError","thoughtNumber","branchThoughts","Object","record","_sessionId","token","peeked","Date","SuspensionExpiredError","SuspensionNotFoundError"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"core/ThoughtProcessor.js","sources":["../../src/core/ThoughtProcessor.ts"],"sourcesContent":["/**\n * Core thought processing logic and validation.\n *\n * This module provides the `ThoughtProcessor` class which handles the main\n * sequential thinking request processing pipeline, including input validation,\n * history management, and response formatting.\n *\n * @module processor\n */\n\nimport { NullLogger } from '../logger/NullLogger.js';\nimport type { Logger } from '../logger/StructuredLogger.js';\nimport type { IEdgeStore } from '../contracts/interfaces.js';\nimport type { ISuspensionStore, SuspensionRecord } from '../contracts/suspension.js';\nimport type { IReasoningStrategy, StrategyDecision } from '../contracts/strategy.js';\nimport type { FeatureFlags } from '../ServerConfig.js';\nimport {\n\tInvalidBacktrackError,\n\tInvalidToolCallError,\n\tSuspensionExpiredError,\n\tSuspensionNotFoundError,\n\tValidationError,\n} from '../errors.js';\nimport { getErrorMessage } from '../errors.js';\nimport { GraphView } from './graph/GraphView.js';\nimport type { IHistoryManager } from './IHistoryManager.js';\nimport { normalizeInput } from './InputNormalizer.js';\nimport type { ThoughtData } from './thought.js';\nimport type { ThoughtEvaluator } from './ThoughtEvaluator.js';\nimport { ThoughtFormatter } from './ThoughtFormatter.js';\nimport type { PatternSignal } from './reasoning.js';\nimport { SequentialStrategy } from './reasoning/strategies/SequentialStrategy.js';\nimport type { CompressionService } from './compression/CompressionService.js';\n\n/**\n * Internal extension to ThoughtData carrying resume metadata.\n * Attached by `_handleToolObservation` so downstream consumers (e.g. edge\n * emission) can correlate the observation with the suspended `tool_call`.\n */\ntype ResumableThought = ThoughtData & { _resumedFrom?: number };\n\n/** Default feature flags used when none are supplied to ThoughtProcessor. */\nconst DEFAULT_FEATURES: FeatureFlags = {\n\tdagEdges: true,\n\treasoningStrategy: 'sequential',\n\tcalibration: true,\n\tcompression: true,\n\ttoolInterleave: true,\n\tnewThoughtTypes: true,\n\toutcomeRecording: true,\n};\n\n/**\n * The return type expected by MCP tool invocations.\n *\n * This structure matches the MCP protocol for tool results,\n * supporting both success and error responses.\n *\n * @example\n * ```typescript\n * const successResult: CallToolResult = {\n * content: [{ type: 'text', text: '{\"status\":\"success\"}' }]\n * };\n *\n * const errorResult: CallToolResult = {\n * content: [{ type: 'text', text: '{\"error\":\"Something went wrong\"}' }],\n * isError: true\n * };\n * ```\n */\nexport interface CallToolResult {\n\t/** Array of content blocks (typically text) to return to the client. */\n\tcontent: Array<{\n\t\ttype: 'text';\n\t\ttext: string;\n\t}>;\n\n\t/** Whether this result represents an error condition. */\n\tisError?: boolean;\n}\n\n/**\n * Core processor for sequential thinking requests.\n *\n * Pipeline: validate → normalize → add to history → format → evaluate signals\n * → run reasoning strategy → return structured response.\n *\n * Auto-adjusts `total_thoughts` if `thought_number` exceeds it. All errors are\n * caught and returned as formatted error responses with `isError: true`.\n *\n * @example\n * ```typescript\n * const processor = new ThoughtProcessor(historyManager, formatter, new ThoughtEvaluator());\n * const result = await processor.process({ thought: '...', thought_number: 1, total_thoughts: 5, next_thought_needed: true });\n * ```\n */\nexport class ThoughtProcessor {\n\t/** Logger for debugging and monitoring. */\n\tprivate _logger: Logger;\n\n\t/** Evaluator for quality signal computation. */\n\tprivate readonly _thoughtEvaluator: ThoughtEvaluator;\n\n\t/**\n\t * Per-session cooldown tracker: session_id → pattern → last_fired_thought_number.\n\t * Prevents re-firing the same pattern hint within 3 thoughts.\n\t */\n\tprivate _hintCooldowns = new Map<string, Map<string, number>>();\n\n\t/**\n\t * Creates a new ThoughtProcessor instance.\n\t *\n\t * @param historyManager - History manager for storing thoughts\n\t * @param thoughtFormatter - Formatter for output formatting\n\t * @param thoughtEvaluator - Evaluator for quality signal computation\n\t * @param logger - Optional logger for diagnostics (defaults to NullLogger)\n\t * @param strategy - Reasoning strategy controlling next-action decisions (defaults to SequentialStrategy)\n\t * @param compressionService - Optional compression service for auto-compression on terminate\n\t */\n\tconstructor(\n\t\tprivate historyManager: IHistoryManager,\n\t\tprivate thoughtFormatter: ThoughtFormatter,\n\t\tthoughtEvaluator: ThoughtEvaluator,\n\t\tlogger?: Logger,\n\t\tprivate readonly strategy: IReasoningStrategy = new SequentialStrategy(),\n\t\tprivate readonly _compressionService?: CompressionService,\n\t\tprivate readonly _suspensionStore?: ISuspensionStore,\n\t\tprivate readonly _features: FeatureFlags = DEFAULT_FEATURES\n\t) {\n\t\tthis._thoughtEvaluator = thoughtEvaluator;\n\t\tthis._logger = logger ?? new NullLogger();\n\t}\n\n\t/**\n\t * Internal logging method.\n\t * @param message - The message to log\n\t * @param meta - Optional metadata\n\t * @private\n\t */\n\tprivate log(message: string, meta?: Record<string, unknown>): void {\n\t\tthis._logger.info(message, meta);\n\t}\n\n\t/**\n\t * Priority ordering for warning patterns (lower = higher priority).\n\t * Ensures the most actionable patterns fill the hint cap first.\n\t */\n\tprivate static readonly _HINT_PRIORITY: Readonly<Record<string, number>> = {\n\t\tconfidence_drift: 1, // Most actionable — degrading confidence\n\t\tunverified_hypothesis: 2, // Important for quality\n\t\tno_alternatives_explored: 3, // Breadth gap\n\t\tconsecutive_without_verification: 4, // Routine pattern\n\t};\n\n\t/**\n\t * Generate actionable hints from pattern signals.\n\t * Rules: max 3 hints, warning-severity only, cooldown of 3 thoughts per pattern per session.\n\t *\n\t * Warning patterns are sorted by priority before selection (see _HINT_PRIORITY).\n\t * Higher-priority patterns (lower number) fill the hint cap first.\n\t *\n\t * @param patterns - Detected pattern signals\n\t * @param currentThoughtNumber - The current thought number being processed\n\t * @param sessionId - Session identifier for cooldown scoping\n\t * @returns Array of hint strings (max 3), empty if no warnings\n\t */\n\tprivate _generateHints(\n\t\tpatterns: PatternSignal[],\n\t\tcurrentThoughtNumber: number,\n\t\tsessionId?: string\n\t): string[] {\n\t\tconst warnings = patterns.filter((p) => p.severity === 'warning');\n\t\tif (warnings.length === 0) return [];\n\n\t\t// Sort by priority (lower number = higher priority)\n\t\twarnings.sort((a, b) => {\n\t\t\tconst pa = ThoughtProcessor._HINT_PRIORITY[a.pattern] ?? 99;\n\t\t\tconst pb = ThoughtProcessor._HINT_PRIORITY[b.pattern] ?? 99;\n\t\t\treturn pa - pb;\n\t\t});\n\n\t\tconst sessionKey = sessionId ?? '__global__';\n\t\tif (!this._hintCooldowns.has(sessionKey)) {\n\t\t\tthis._hintCooldowns.set(sessionKey, new Map());\n\t\t}\n\t\tconst cooldowns = this._hintCooldowns.get(sessionKey)!;\n\n\t\tconst hints: string[] = [];\n\t\tfor (const warning of warnings) {\n\t\t\tif (hints.length >= 3) break;\n\n\t\t\tconst lastFired = cooldowns.get(warning.pattern);\n\t\t\tif (lastFired !== undefined && currentThoughtNumber - lastFired < 3) {\n\t\t\t\tcontinue; // Still in cooldown\n\t\t\t}\n\n\t\t\thints.push(warning.message);\n\t\t\tcooldowns.set(warning.pattern, currentThoughtNumber);\n\t\t}\n\n\t\treturn hints;\n\t}\n\n\t/**\n\t * Processes a thought through the sequential thinking pipeline.\n\t *\n\t * This method validates the input, adds it to history, formats the output,\n\t * computes quality signals via the ThoughtEvaluator, and returns\n\t * a structured response with metadata about the current state.\n\t *\n\t * @param input - The thought data to process\n\t * @returns A Promise resolving to the formatted tool result containing:\n\t * - `thought_number` — Current thought index\n\t * - `total_thoughts` — Estimated total thoughts\n\t * - `next_thought_needed` — Whether to continue\n\t * - `branches` — Active branch IDs\n\t * - `thought_history_length` — Number of thoughts in history\n\t * - `available_mcp_tools` — MCP tools available for recommendation\n\t * - `available_skills` — Skills available for recommendation\n\t * - `current_step` — Current step recommendation\n\t * - `previous_steps` — Previously recommended steps\n\t * - `remaining_steps` — Upcoming step descriptions\n\t * - `thought_type` — Classification of thought purpose (optional)\n\t * - `quality_score` — Self-assessed quality score 0-1 (optional)\n\t * - `confidence` — Self-assessed confidence 0-1 (optional)\n\t * - `hypothesis_id` — Hypothesis link for verification chains (optional)\n * - `confidence_signals` — Computed reasoning quality signals (includes structural_quality and quality_components)\n * - `reasoning_stats` — Aggregated reasoning analytics\n * - `reasoning_hints` — (Conditional) Actionable hints from pattern analysis, max 3, warning-severity only (optional)\n\t *\n\t * @example\n\t * ```typescript\n\t * const result = await processor.process({\n\t * thought: 'I should read the README file',\n\t * thought_number: 1,\n\t * total_thoughts: 3,\n\t * next_thought_needed: true\n\t * });\n\t *\n\t * console.log(result.content[0].text);\n\t * // Output includes: thought_number, total_thoughts, next_thought_needed,\n\t * // branches, thought_history_length, and any recommendations\n\t * ```\n\t */\n\tpublic async process(input: ThoughtData): Promise<CallToolResult> {\n\t\ttry {\n\t\t\t// Normalize input to handle common LLM field name mistakes\n\t\t\tconst normalizedInput = normalizeInput(input);\n\t\t\tconst sessionId = normalizedInput.session_id;\n\n\t\t\t// Handle reset_state: clear session before processing\n\t\t\tif (normalizedInput.reset_state) {\n\t\t\t\tthis.historyManager.clear(sessionId);\n\t\t\t\tthis.log('State reset for session', { sessionId: sessionId ?? '__global__' });\n\t\t\t}\n\n\t\t\t// Persist available_mcp_tools/available_skills across calls within a session.\n\t\t\t// If the caller omits these, reuse the last-seen values from the session.\n\t\t\tif (!normalizedInput.available_mcp_tools) {\n\t\t\t\tnormalizedInput.available_mcp_tools = this.historyManager.getAvailableMcpTools(sessionId);\n\t\t\t}\n\t\t\tif (!normalizedInput.available_skills) {\n\t\t\t\tnormalizedInput.available_skills = this.historyManager.getAvailableSkills(sessionId);\n\t\t\t}\n\n\t\t\tconst { result: validatedInput, warnings: validateWarnings } =\n\t\t\t\tthis.validateInput(normalizedInput);\n\t\t\tconst { result: checkedInput, warnings: refWarnings } =\n\t\t\t\tthis._validateCrossReferences(validatedInput, sessionId);\n\t\t\tconst allWarnings = [...validateWarnings, ...refWarnings];\n\n\t\t\t// Validate new thought types and tool-interleave invariants.\n\t\t\tthis._validateNewTypes(checkedInput, sessionId);\n\n\t\t\t// Tool-interleave suspend path: persist the tool_call thought, then return\n\t\t\t// a `suspended` envelope without running strategy/evaluator.\n\t\t\tif (checkedInput.thought_type === 'tool_call' && this._suspensionStore) {\n\t\t\t\treturn this._handleToolCall(checkedInput, sessionId);\n\t\t\t}\n\n\t\t\t// Tool-interleave resume path: consume the suspension and continue the\n\t\t\t// normal pipeline (addThought → format → evaluate → strategy).\n\t\t\tif (checkedInput.thought_type === 'tool_observation' && this._suspensionStore) {\n\t\t\t\tthis._handleToolObservation(checkedInput, sessionId);\n\t\t\t}\n\n\t\t\tthis.historyManager.addThought(checkedInput);\n\n\t\t\tconst formattedThought = this.thoughtFormatter.formatThought(checkedInput);\n\t\t\tthis.log(formattedThought, { sessionId: sessionId ?? '__global__' });\n\n\t\t\t// Compute quality signals — fetch history/branches once\n\t\t\tconst history = this.historyManager.getHistory(sessionId);\n\t\t\tconst branches = this.historyManager.getBranches(sessionId);\n\n\t\t\tconst confidenceSignals = this._thoughtEvaluator.computeConfidenceSignals(\n\t\t\t\thistory,\n\t\t\t\tbranches\n\t\t\t);\n\t\t\tconst reasoningStats = this._thoughtEvaluator.computeReasoningStats(\n\t\t\t\thistory,\n\t\t\t\tbranches\n\t\t\t);\n\n\t\t\t// Detect reasoning patterns and generate hints\n\t\t\tconst patternSignals = this._thoughtEvaluator.computePatternSignals(\n\t\t\t\thistory,\n\t\t\t\tbranches\n\t\t\t);\n\t\t\tconst reasoningHints = this._generateHints(\n\t\t\t\tpatternSignals,\n\t\t\t\tcheckedInput.thought_number,\n\t\t\t\tsessionId\n\t\t\t);\n\n\t\t\t// Strategy decision — pluggable reasoning policy hook.\n\t\t\t// Built after history/stats so strategies see the latest state.\n\t\t\tconst decision = this._runStrategy(checkedInput, history, reasoningStats, sessionId);\n\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\tthought_number: checkedInput.thought_number,\n\t\t\t\t\t\t\ttotal_thoughts: checkedInput.total_thoughts,\n\t\t\t\t\t\t\tnext_thought_needed: checkedInput.next_thought_needed ?? true,\n\t\t\t\t\t\t\tbranches: this.historyManager.getBranchIds(sessionId),\n\t\t\t\t\t\t\tthought_history_length: this.historyManager.getHistoryLength(sessionId),\n\t\t\t\t\t\t\tavailable_mcp_tools: checkedInput.available_mcp_tools,\n\t\t\t\t\t\t\tavailable_skills: checkedInput.available_skills,\n\t\t\t\t\t\t\tcurrent_step: checkedInput.current_step,\n\t\t\t\t\t\t\tprevious_steps: checkedInput.previous_steps,\n\t\t\t\t\t\t\tremaining_steps: checkedInput.remaining_steps,\n\t\t\t\t\t\t\t// Reasoning enrichment fields\n\t\t\t\t\t\t\tthought_type: checkedInput.thought_type,\n\t\t\t\t\t\t\tquality_score: checkedInput.quality_score,\n\t\t\t\t\t\t\tconfidence: checkedInput.confidence,\n\t\t\t\t\t\t\thypothesis_id: checkedInput.hypothesis_id,\n\t\t\t\t\t\t\tconfidence_signals: confidenceSignals,\n\t\t\t\t\t\t\treasoning_stats: reasoningStats,\n\t\t\t\t\t\t\t...(reasoningHints.length > 0 && { reasoning_hints: reasoningHints }),\n\t\t\t\t\t\t\t...(decision.action !== 'continue' && { strategy_hint: decision }),\n\t\t\t\t\t\t\t...(allWarnings.length > 0 && { warnings: allWarnings.slice(0, 3) }),\n\t\t\t\t\t\t\t...(sessionId ? { session_id: sessionId } : {}),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tnull,\n\t\t\t\t\t\t2\n\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t};\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\ttext: JSON.stringify(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\terror: getErrorMessage(error),\n\t\t\t\t\t\t\t\tstatus: 'failed',\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t2\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tisError: true,\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Run the configured reasoning strategy and return its decision.\n\t * Strategy errors degrade to `{ action: 'continue' }`.\n\t * @private\n\t */\n\tprivate _runStrategy(\n\t\tcurrentThought: ThoughtData,\n\t\thistory: ThoughtData[],\n\t\tstats: ReturnType<ThoughtEvaluator['computeReasoningStats']>,\n\t\tsessionId?: string\n\t): StrategyDecision {\n\t\tlet decision: StrategyDecision;\n\t\ttry {\n\t\t\tconst edgeStore = this._getEdgeStore();\n\t\t\tconst graph = edgeStore ? new GraphView(edgeStore) : (undefined as unknown as GraphView);\n\t\t\tdecision = this.strategy.decide({\n\t\t\t\tsessionId: sessionId ?? '__global__',\n\t\t\t\thistory,\n\t\t\t\tgraph,\n\t\t\t\tstats,\n\t\t\t\tcurrentThought,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthis._logger.warn('Reasoning strategy threw — defaulting to continue', {\n\t\t\t\tstrategy: this.strategy.name,\n\t\t\t\terror: getErrorMessage(error),\n\t\t\t});\n\t\t\tdecision = { action: 'continue' };\n\t\t}\n\n\t\t// Auto-compression trigger: when strategy terminates a branch and\n\t\t// compression is enabled, summarize the branch subtree. Compression\n\t\t// failures must NEVER break the thought pipeline.\n\t\tif (\n\t\t\tdecision.action === 'terminate' &&\n\t\t\tthis._compressionService &&\n\t\t\tcurrentThought.branch_id\n\t\t) {\n\t\t\ttry {\n\t\t\t\tconst sid = sessionId ?? '__global__';\n\t\t\t\tconst branchRoot = this._findBranchRoot(sid, currentThought.branch_id);\n\t\t\t\tif (branchRoot) {\n\t\t\t\t\tthis._compressionService.compressBranch(sid, currentThought.branch_id, branchRoot);\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tthis._logger.debug('Compression auto-trigger failed', {\n\t\t\t\t\terror: getErrorMessage(err),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn decision;\n\t}\n\n\t/**\n\t * Locate the root thought id for a branch.\n\t * Prefers GraphView.branchThoughts() when an EdgeStore is available;\n\t * falls back to historyManager.getBranches()[branchId][0].id.\n\t * @private\n\t */\n\tprivate _findBranchRoot(sessionId: string, branchId: string): string | undefined {\n\t\tconst edgeStore = this._getEdgeStore();\n\t\tconst branches = this.historyManager.getBranches(sessionId);\n\t\tconst branchList = branches[branchId];\n\t\tconst firstId = branchList?.[0]?.id;\n\t\tif (edgeStore && firstId) {\n\t\t\tconst graph = new GraphView(edgeStore);\n\t\t\tconst ids = graph.branchThoughts(sessionId, firstId);\n\t\t\tif (ids.length > 0) return ids[0];\n\t\t}\n\t\treturn firstId;\n\t}\n\n\t/** Best-effort access to the EdgeStore via duck typing. @private */\n\tprivate _getEdgeStore(): IEdgeStore | undefined {\n\t\tconst hm = this.historyManager as { getEdgeStore?: () => IEdgeStore | undefined };\n\t\treturn typeof hm.getEdgeStore === 'function' ? hm.getEdgeStore() : undefined;\n\t}\n\n\t/**\n\t * Validates and normalizes thought input.\n\t *\n\t * Ensures that thought numbers are consistent and within valid ranges.\n\t * If `thought_number` exceeds `total_thoughts`, `total_thoughts` is\n\t * automatically adjusted to match and a warning is emitted.\n\t *\n\t * @param input - The input to validate\n\t * @returns Object with validated input and any warnings generated\n\t * @private\n\t *\n\t * @example\n\t * ```typescript\n\t * // Auto-adjusts total_thoughts when thought_number exceeds it\n\t * const { result, warnings } = this.validateInput(input);\n\t * // result.total_thoughts === 10 (auto-adjusted from 5)\n\t * // warnings === ['Auto-adjusted total_thoughts from 5 to 10 to match thought_number']\n\t * ```\n\t */\n\tprivate validateInput(input: ThoughtData): {\n\t\tresult: ThoughtData;\n\t\twarnings: string[];\n\t} {\n\t\tconst warnings: string[] = [];\n\t\tif (input.thought_number > input.total_thoughts) {\n\t\t\tconst originalTotal = input.total_thoughts;\n\t\t\twarnings.push(\n\t\t\t\t`Auto-adjusted total_thoughts from ${originalTotal} to ${input.thought_number} to match thought_number`\n\t\t\t);\n\t\t\tthis._logger.warn('Auto-adjusted total_thoughts to match thought_number', {\n\t\t\t\tthought_number: input.thought_number,\n\t\t\t\toriginal_total_thoughts: originalTotal,\n\t\t\t\tadjusted_total_thoughts: input.thought_number,\n\t\t\t});\n\t\t\tinput.total_thoughts = input.thought_number;\n\t\t}\n\t\treturn { result: input, warnings };\n\t}\n\n\t/**\n\t * Validates cross-field references against actual thought history.\n\t * Drops invalid references with a warning log — never rejects.\n\t * LLMs frequently send optimistic references to thoughts that don't exist yet.\n\t *\n\t * @param input - The thought data to validate\n\t * @returns Object with cleaned input and any warnings generated\n\t * @private\n\t *\n\t * @example\n\t * ```typescript\n\t * // verification_target=999 with only 3 thoughts in history\n\t * const { result, warnings } = this._validateCrossReferences(input);\n\t * // result.verification_target === undefined\n\t * // warnings === ['Dropped dangling verification_target: 999 (history has 3 thoughts)']\n\t * ```\n\t */\n\tprivate _validateCrossReferences(input: ThoughtData, sessionId?: string): {\n\t\tresult: ThoughtData;\n\t\twarnings: string[];\n\t} {\n\t\tconst warnings: string[] = [];\n\t\tconst historyLength = this.historyManager.getHistoryLength(sessionId);\n\n\t\t// verification_target: must reference existing thought\n\t\tif (input.verification_target !== undefined && input.verification_target > historyLength) {\n\t\t\twarnings.push(\n\t\t\t\t`Dropped dangling verification_target: ${input.verification_target} (history has ${historyLength} thoughts)`\n\t\t\t);\n\t\t\tthis._logger.warn('Dropped dangling verification_target', {\n\t\t\t\tverification_target: input.verification_target,\n\t\t\t\thistoryLength,\n\t\t\t});\n\t\t\tinput.verification_target = undefined;\n\t\t}\n\n\t\t// revises_thought: must reference existing thought\n\t\tif (input.revises_thought !== undefined && input.revises_thought > historyLength) {\n\t\t\twarnings.push(\n\t\t\t\t`Dropped dangling revises_thought: ${input.revises_thought} (history has ${historyLength} thoughts)`\n\t\t\t);\n\t\t\tthis._logger.warn('Dropped dangling revises_thought', {\n\t\t\t\trevises_thought: input.revises_thought,\n\t\t\t\thistoryLength,\n\t\t\t});\n\t\t\tinput.revises_thought = undefined;\n\t\t}\n\n\t\t// branch_from_thought: must reference existing thought\n\t\tif (input.branch_from_thought !== undefined && input.branch_from_thought > historyLength) {\n\t\t\twarnings.push(\n\t\t\t\t`Dropped dangling branch_from_thought: ${input.branch_from_thought} (history has ${historyLength} thoughts)`\n\t\t\t);\n\t\t\tthis._logger.warn('Dropped dangling branch_from_thought', {\n\t\t\t\tbranch_from_thought: input.branch_from_thought,\n\t\t\t\thistoryLength,\n\t\t\t});\n\t\t\tinput.branch_from_thought = undefined;\n\t\t}\n\n\t\t// synthesis_sources: filter to existing thoughts only\n\t\tif (input.synthesis_sources?.length) {\n\t\t\tconst valid = input.synthesis_sources.filter((n: number) => n <= historyLength);\n\t\t\tif (valid.length < input.synthesis_sources.length) {\n\t\t\t\tconst dropped = input.synthesis_sources.filter((n: number) => n > historyLength);\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Filtered dangling synthesis_sources: [${dropped.join(', ')}] (history has ${historyLength} thoughts)`\n\t\t\t\t);\n\t\t\t\tthis._logger.warn('Filtered dangling synthesis_sources', {\n\t\t\t\t\toriginal: input.synthesis_sources,\n\t\t\t\t\tfiltered: valid,\n\t\t\t\t\thistoryLength,\n\t\t\t\t});\n\t\t\t}\n\t\t\tinput.synthesis_sources = valid.length > 0 ? valid : undefined;\n\t\t}\n\n\t\t// merge_from_thoughts: filter to existing thoughts only\n\t\tif (input.merge_from_thoughts?.length) {\n\t\t\tconst valid = input.merge_from_thoughts.filter((n: number) => n <= historyLength);\n\t\t\tif (valid.length < input.merge_from_thoughts.length) {\n\t\t\t\tconst dropped = input.merge_from_thoughts.filter(\n\t\t\t\t\t(n: number) => n > historyLength\n\t\t\t\t);\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Filtered dangling merge_from_thoughts: [${dropped.join(', ')}] (history has ${historyLength} thoughts)`\n\t\t\t\t);\n\t\t\t\tthis._logger.warn('Filtered dangling merge_from_thoughts', {\n\t\t\t\t\toriginal: input.merge_from_thoughts,\n\t\t\t\t\tfiltered: valid,\n\t\t\t\t\thistoryLength,\n\t\t\t\t});\n\t\t\t}\n\t\t\tinput.merge_from_thoughts = valid.length > 0 ? valid : undefined;\n\t\t}\n\n\t\t// merge_branch_ids: filter to existing branches only (includes pre-registered)\n\t\tif (input.merge_branch_ids?.length) {\n\t\t\tconst valid = input.merge_branch_ids.filter((id: string) =>\n\t\t\t\tthis.historyManager.branchExists(sessionId, id)\n\t\t\t);\n\t\t\tif (valid.length < input.merge_branch_ids.length) {\n\t\t\t\tconst dropped = input.merge_branch_ids.filter(\n\t\t\t\t\t(id: string) => !this.historyManager.branchExists(sessionId, id)\n\t\t\t\t);\n\t\t\t\twarnings.push(`Filtered dangling merge_branch_ids: [${dropped.join(', ')}]`);\n\t\t\t\tthis._logger.warn('Filtered dangling merge_branch_ids', {\n\t\t\t\t\toriginal: input.merge_branch_ids,\n\t\t\t\t\tfiltered: valid,\n\t\t\t\t\texistingBranches: this.historyManager.getBranchIds(sessionId),\n\t\t\t\t});\n\t\t\t}\n\t\t\tinput.merge_branch_ids = valid.length > 0 ? valid : undefined;\n\t\t}\n\n\t\treturn { result: input, warnings };\n\t}\n\n\t/**\n\t * Validate new thought-type invariants behind feature flags.\n\t * @private\n\t */\n\tprivate _validateNewTypes(input: ThoughtData, sessionId?: string): void {\n\t\tconst t = input.thought_type;\n\t\tif ((t === 'tool_call' || t === 'tool_observation') && !this._features.toolInterleave) {\n\t\t\tthrow new ValidationError(\n\t\t\t\t'thought_type',\n\t\t\t\t`Type '${t}' requires the toolInterleave feature flag. Set TRACELATTICE_FEATURES_TOOL_INTERLEAVE=true to enable it.`\n\t\t\t);\n\t\t}\n\t\tif (\n\t\t\t(t === 'assumption' || t === 'decomposition' || t === 'backtrack') &&\n\t\t\t!this._features.newThoughtTypes\n\t\t) {\n\t\t\tthrow new ValidationError(\n\t\t\t\t'thought_type',\n\t\t\t\t`Type '${t}' requires the newThoughtTypes feature flag. Set TRACELATTICE_FEATURES_NEW_THOUGHT_TYPES=true to enable it, or use '${ThoughtProcessor._getWorkaroundType(t)}' type as a workaround.`\n\t\t\t);\n\t\t}\n\t\tif (t === 'tool_call' && !input.tool_name) {\n\t\t\tthrow new InvalidToolCallError(\n\t\t\t\t'tool_call thought ' + input.thought_number + ' missing required tool_name'\n\t\t\t);\n\t\t}\n\t\tif (t === 'tool_observation' && !input.continuation_token) {\n\t\t\tthrow new ValidationError(\n\t\t\t\t'continuation_token',\n\t\t\t\t'tool_observation thought ' + input.thought_number + ' missing continuation_token'\n\t\t\t);\n\t\t}\n\t\tif (t === 'backtrack') {\n\t\t\tif (input.backtrack_target === undefined) {\n\t\t\t\tthrow new ValidationError(\n\t\t\t\t\t'backtrack_target',\n\t\t\t\t\t'backtrack thought ' + input.thought_number + ' requires backtrack_target'\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (input.backtrack_target > input.thought_number) {\n\t\t\t\tthrow new InvalidBacktrackError(\n\t\t\t\t\t'backtrack_target ' + input.backtrack_target + ' must be <= thought_number ' + input.thought_number\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (!this._thoughtNumberExists(input.backtrack_target, sessionId)) {\n\t\t\t\tthrow new InvalidBacktrackError(\n\t\t\t\t\t'backtrack_target ' + input.backtrack_target + ' does not exist in session history'\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Checks whether a given thought_number exists in the session history or any branch.\n\t * @private\n\t */\n\tprivate _thoughtNumberExists(thoughtNumber: number, sessionId?: string): boolean {\n\t\tconst history = this.historyManager.getHistory(sessionId);\n\t\tfor (const t of history) {\n\t\t\tif (t.thought_number === thoughtNumber) return true;\n\t\t}\n\t\tconst branches = this.historyManager.getBranches(sessionId);\n\t\tfor (const branchThoughts of Object.values(branches)) {\n\t\t\tfor (const t of branchThoughts) {\n\t\t\t\tif (t.thought_number === thoughtNumber) return true;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\t/**\n\t * Returns a workaround thought type for a feature-flagged type.\n\t * @private\n\t */\n\tprivate static _getWorkaroundType(t: 'assumption' | 'decomposition' | 'backtrack'): string {\n\t\tswitch (t) {\n\t\t\tcase 'assumption':\n\t\t\t\treturn 'regular';\n\t\t\tcase 'decomposition':\n\t\t\t\treturn 'hypothesis';\n\t\t\tcase 'backtrack':\n\t\t\t\treturn 'regular';\n\t\t}\n\t}\n\n\t/**\n\t * Persist a tool_call thought and return a `suspended` envelope.\n\t * Strategy/evaluator are intentionally skipped.\n\t * @private\n\t */\n\tprivate _handleToolCall(input: ThoughtData, sessionId?: string): CallToolResult {\n\t\tthis.historyManager.addThought(input);\n\t\tconst record: SuspensionRecord = this._suspensionStore!.suspend({\n\t\t\tsessionId: sessionId ?? '__global__',\n\t\t\ttoolCallThoughtNumber: input.thought_number,\n\t\t\ttoolName: input.tool_name!,\n\t\t\ttoolArguments: input.tool_arguments ?? {},\n\t\t\tttlMs: 5 * 60_000,\n\t\t\texpiresAt: 0,\n\t\t});\n\t\treturn {\n\t\t\tcontent: [\n\t\t\t\t{\n\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\ttext: JSON.stringify(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tstatus: 'suspended',\n\t\t\t\t\t\t\tcontinuation_token: record.token,\n\t\t\t\t\t\t\ttool_name: record.toolName,\n\t\t\t\t\t\t\ttool_arguments: record.toolArguments,\n\t\t\t\t\t\t\texpires_at: record.expiresAt,\n\t\t\t\t\t\t\tthought_number: input.thought_number,\n\t\t\t\t\t\t\ttotal_thoughts: input.total_thoughts,\n\t\t\t\t\t\t\t...(sessionId ? { session_id: sessionId } : {}),\n\t\t\t\t\t\t},\n\t\t\t\t\t\tnull,\n\t\t\t\t\t\t2\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t}\n\n\t/**\n\t * Resume from a tool_observation, consuming the suspension record.\n\t * Distinguishes missing vs expired via peek().\n\t * @private\n\t */\n\tprivate _handleToolObservation(input: ThoughtData, _sessionId?: string): void {\n\t\tconst token = input.continuation_token!;\n\t\tconst peeked = this._suspensionStore!.peek(token);\n\t\tif (peeked && peeked.expiresAt <= Date.now()) {\n\t\t\tthrow new SuspensionExpiredError('Suspension token expired: ' + token);\n\t\t}\n\t\tconst record = this._suspensionStore!.resume(token);\n\t\tif (!record) {\n\t\t\tthrow new SuspensionNotFoundError('Suspension token not found: ' + token);\n\t\t}\n\t\t(input as ResumableThought)._resumedFrom = record.toolCallThoughtNumber;\n\t}\n}\n"],"names":["DEFAULT_FEATURES","ThoughtProcessor","Map","historyManager","thoughtFormatter","thoughtEvaluator","logger","strategy","SequentialStrategy","_compressionService","_suspensionStore","_features","NullLogger","message","meta","patterns","currentThoughtNumber","sessionId","warnings","p","a","b","pa","pb","sessionKey","cooldowns","hints","warning","lastFired","undefined","input","normalizedInput","normalizeInput","validatedInput","validateWarnings","checkedInput","refWarnings","allWarnings","formattedThought","history","branches","confidenceSignals","reasoningStats","patternSignals","reasoningHints","decision","JSON","error","getErrorMessage","currentThought","stats","edgeStore","graph","GraphView","sid","branchRoot","err","branchId","branchList","firstId","ids","hm","originalTotal","historyLength","valid","n","dropped","id","t","ValidationError","InvalidToolCallError","InvalidBacktrackError","thoughtNumber","branchThoughts","Object","record","_sessionId","token","peeked","Date","SuspensionExpiredError","SuspensionNotFoundError"],"mappings":";;;;;AA0CA,MAAMA,mBAAiC;IACtC,UAAU;IACV,mBAAmB;IACnB,aAAa;IACb,aAAa;IACb,gBAAgB;IAChB,iBAAiB;IACjB,kBAAkB;AACnB;AA8CO,MAAMC;;;;;;;IAEJ,QAAgB;IAGP,kBAAoC;IAM7C,iBAAiB,IAAIC,MAAmC;IAYhE,YACSC,cAA+B,EAC/BC,gBAAkC,EAC1CC,gBAAkC,EAClCC,MAAe,EACEC,WAA+B,IAAIC,oBAAoB,EACvDC,mBAAwC,EACxCC,gBAAmC,EACnCC,YAA0BX,gBAAgB,CAC1D;aAROG,cAAc,GAAdA;aACAC,gBAAgB,GAAhBA;aAGSG,QAAQ,GAARA;aACAE,mBAAmB,GAAnBA;aACAC,gBAAgB,GAAhBA;aACAC,SAAS,GAATA;QAEjB,IAAI,CAAC,iBAAiB,GAAGN;QACzB,IAAI,CAAC,OAAO,GAAGC,UAAU,IAAIM;IAC9B;IAQQ,IAAIC,OAAe,EAAEC,IAA8B,EAAQ;QAClE,IAAI,CAAC,OAAO,CAAC,IAAI,CAACD,SAASC;IAC5B;IAMA,OAAwB,iBAAmD;QAC1E,kBAAkB;QAClB,uBAAuB;QACvB,0BAA0B;QAC1B,kCAAkC;IACnC,EAAE;IAcM,eACPC,QAAyB,EACzBC,oBAA4B,EAC5BC,SAAkB,EACP;QACX,MAAMC,WAAWH,SAAS,MAAM,CAAC,CAACI,IAAMA,AAAe,cAAfA,EAAE,QAAQ;QAClD,IAAID,AAAoB,MAApBA,SAAS,MAAM,EAAQ,OAAO,EAAE;QAGpCA,SAAS,IAAI,CAAC,CAACE,GAAGC;YACjB,MAAMC,KAAKrB,iBAAiB,cAAc,CAACmB,EAAE,OAAO,CAAC,IAAI;YACzD,MAAMG,KAAKtB,iBAAiB,cAAc,CAACoB,EAAE,OAAO,CAAC,IAAI;YACzD,OAAOC,KAAKC;QACb;QAEA,MAAMC,aAAaP,aAAa;QAChC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAACO,aAC5B,IAAI,CAAC,cAAc,CAAC,GAAG,CAACA,YAAY,IAAItB;QAEzC,MAAMuB,YAAY,IAAI,CAAC,cAAc,CAAC,GAAG,CAACD;QAE1C,MAAME,QAAkB,EAAE;QAC1B,KAAK,MAAMC,WAAWT,SAAU;YAC/B,IAAIQ,MAAM,MAAM,IAAI,GAAG;YAEvB,MAAME,YAAYH,UAAU,GAAG,CAACE,QAAQ,OAAO;YAC/C,IAAIC,AAAcC,WAAdD,cAA2BZ,CAAAA,uBAAuBY,YAAY;gBAIlEF,MAAM,IAAI,CAACC,QAAQ,OAAO;gBAC1BF,UAAU,GAAG,CAACE,QAAQ,OAAO,EAAEX;;QAChC;QAEA,OAAOU;IACR;IA2CA,MAAa,QAAQI,KAAkB,EAA2B;QACjE,IAAI;YAEH,MAAMC,kBAAkBC,eAAeF;YACvC,MAAMb,YAAYc,gBAAgB,UAAU;YAG5C,IAAIA,gBAAgB,WAAW,EAAE;gBAChC,IAAI,CAAC,cAAc,CAAC,KAAK,CAACd;gBAC1B,IAAI,CAAC,GAAG,CAAC,2BAA2B;oBAAE,WAAWA,aAAa;gBAAa;YAC5E;YAIA,IAAI,CAACc,gBAAgB,mBAAmB,EACvCA,gBAAgB,mBAAmB,GAAG,IAAI,CAAC,cAAc,CAAC,oBAAoB,CAACd;YAEhF,IAAI,CAACc,gBAAgB,gBAAgB,EACpCA,gBAAgB,gBAAgB,GAAG,IAAI,CAAC,cAAc,CAAC,kBAAkB,CAACd;YAG3E,MAAM,EAAE,QAAQgB,cAAc,EAAE,UAAUC,gBAAgB,EAAE,GAC3D,IAAI,CAAC,aAAa,CAACH;YACpB,MAAM,EAAE,QAAQI,YAAY,EAAE,UAAUC,WAAW,EAAE,GACpD,IAAI,CAAC,wBAAwB,CAACH,gBAAgBhB;YAC/C,MAAMoB,cAAc;mBAAIH;mBAAqBE;aAAY;YAGzD,IAAI,CAAC,iBAAiB,CAACD,cAAclB;YAIrC,IAAIkB,AAA8B,gBAA9BA,aAAa,YAAY,IAAoB,IAAI,CAAC,gBAAgB,EACrE,OAAO,IAAI,CAAC,eAAe,CAACA,cAAclB;YAK3C,IAAIkB,AAA8B,uBAA9BA,aAAa,YAAY,IAA2B,IAAI,CAAC,gBAAgB,EAC5E,IAAI,CAAC,sBAAsB,CAACA,cAAclB;YAG3C,IAAI,CAAC,cAAc,CAAC,UAAU,CAACkB;YAE/B,MAAMG,mBAAmB,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAACH;YAC7D,IAAI,CAAC,GAAG,CAACG,kBAAkB;gBAAE,WAAWrB,aAAa;YAAa;YAGlE,MAAMsB,UAAU,IAAI,CAAC,cAAc,CAAC,UAAU,CAACtB;YAC/C,MAAMuB,WAAW,IAAI,CAAC,cAAc,CAAC,WAAW,CAACvB;YAEjD,MAAMwB,oBAAoB,IAAI,CAAC,iBAAiB,CAAC,wBAAwB,CACxEF,SACAC;YAED,MAAME,iBAAiB,IAAI,CAAC,iBAAiB,CAAC,qBAAqB,CAClEH,SACAC;YAID,MAAMG,iBAAiB,IAAI,CAAC,iBAAiB,CAAC,qBAAqB,CAClEJ,SACAC;YAED,MAAMI,iBAAiB,IAAI,CAAC,cAAc,CACzCD,gBACAR,aAAa,cAAc,EAC3BlB;YAKD,MAAM4B,WAAW,IAAI,CAAC,YAAY,CAACV,cAAcI,SAASG,gBAAgBzB;YAE1E,OAAO;gBACN,SAAS;oBACR;wBACC,MAAM;wBACN,MAAM6B,KAAK,SAAS,CACnB;4BACA,gBAAgBX,aAAa,cAAc;4BAC3C,gBAAgBA,aAAa,cAAc;4BAC3C,qBAAqBA,aAAa,mBAAmB,IAAI;4BACzD,UAAU,IAAI,CAAC,cAAc,CAAC,YAAY,CAAClB;4BAC3C,wBAAwB,IAAI,CAAC,cAAc,CAAC,gBAAgB,CAACA;4BAC7D,qBAAqBkB,aAAa,mBAAmB;4BACrD,kBAAkBA,aAAa,gBAAgB;4BAC/C,cAAcA,aAAa,YAAY;4BACvC,gBAAgBA,aAAa,cAAc;4BAC3C,iBAAiBA,aAAa,eAAe;4BAE7C,cAAcA,aAAa,YAAY;4BACvC,eAAeA,aAAa,aAAa;4BACzC,YAAYA,aAAa,UAAU;4BACnC,eAAeA,aAAa,aAAa;4BACzC,oBAAoBM;4BACpB,iBAAiBC;4BACjB,GAAIE,eAAe,MAAM,GAAG,KAAK;gCAAE,iBAAiBA;4BAAe,CAAC;4BACpE,GAAIC,AAAoB,eAApBA,SAAS,MAAM,IAAmB;gCAAE,eAAeA;4BAAS,CAAC;4BACjE,GAAIR,YAAY,MAAM,GAAG,KAAK;gCAAE,UAAUA,YAAY,KAAK,CAAC,GAAG;4BAAG,CAAC;4BACnE,GAAIpB,YAAY;gCAAE,YAAYA;4BAAU,IAAI,CAAC,CAAC;wBAC/C,GACA,MACA;oBAED;iBACA;YACF;QACD,EAAE,OAAO8B,OAAO;YACf,OAAO;gBACN,SAAS;oBACR;wBACC,MAAM;wBACN,MAAMD,KAAK,SAAS,CACnB;4BACC,OAAOE,gBAAgBD;4BACvB,QAAQ;wBACT,GACA,MACA;oBAEF;iBACA;gBACD,SAAS;YACV;QACD;IACD;IAOQ,aACPE,cAA2B,EAC3BV,OAAsB,EACtBW,KAA4D,EAC5DjC,SAAkB,EACC;QACnB,IAAI4B;QACJ,IAAI;YACH,MAAMM,YAAY,IAAI,CAAC,aAAa;YACpC,MAAMC,QAAQD,YAAY,IAAIE,UAAUF,aAActB;YACtDgB,WAAW,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAC/B,WAAW5B,aAAa;gBACxBsB;gBACAa;gBACAF;gBACAD;YACD;QACD,EAAE,OAAOF,OAAO;YACf,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,qDAAqD;gBACtE,UAAU,IAAI,CAAC,QAAQ,CAAC,IAAI;gBAC5B,OAAOC,gBAAgBD;YACxB;YACAF,WAAW;gBAAE,QAAQ;YAAW;QACjC;QAKA,IACCA,AAAoB,gBAApBA,SAAS,MAAM,IACf,IAAI,CAAC,mBAAmB,IACxBI,eAAe,SAAS,EAExB,IAAI;YACH,MAAMK,MAAMrC,aAAa;YACzB,MAAMsC,aAAa,IAAI,CAAC,eAAe,CAACD,KAAKL,eAAe,SAAS;YACrE,IAAIM,YACH,IAAI,CAAC,mBAAmB,CAAC,cAAc,CAACD,KAAKL,eAAe,SAAS,EAAEM;QAEzE,EAAE,OAAOC,KAAK;YACb,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,mCAAmC;gBACrD,OAAOR,gBAAgBQ;YACxB;QACD;QAGD,OAAOX;IACR;IAQQ,gBAAgB5B,SAAiB,EAAEwC,QAAgB,EAAsB;QAChF,MAAMN,YAAY,IAAI,CAAC,aAAa;QACpC,MAAMX,WAAW,IAAI,CAAC,cAAc,CAAC,WAAW,CAACvB;QACjD,MAAMyC,aAAalB,QAAQ,CAACiB,SAAS;QACrC,MAAME,UAAUD,YAAY,CAAC,EAAE,EAAE;QACjC,IAAIP,aAAaQ,SAAS;YACzB,MAAMP,QAAQ,IAAIC,UAAUF;YAC5B,MAAMS,MAAMR,MAAM,cAAc,CAACnC,WAAW0C;YAC5C,IAAIC,IAAI,MAAM,GAAG,GAAG,OAAOA,GAAG,CAAC,EAAE;QAClC;QACA,OAAOD;IACR;IAGQ,gBAAwC;QAC/C,MAAME,KAAK,IAAI,CAAC,cAAc;QAC9B,OAAO,AAA2B,cAA3B,OAAOA,GAAG,YAAY,GAAkBA,GAAG,YAAY,KAAKhC;IACpE;IAqBQ,cAAcC,KAAkB,EAGtC;QACD,MAAMZ,WAAqB,EAAE;QAC7B,IAAIY,MAAM,cAAc,GAAGA,MAAM,cAAc,EAAE;YAChD,MAAMgC,gBAAgBhC,MAAM,cAAc;YAC1CZ,SAAS,IAAI,CACZ,CAAC,kCAAkC,EAAE4C,cAAc,IAAI,EAAEhC,MAAM,cAAc,CAAC,wBAAwB,CAAC;YAExG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,wDAAwD;gBACzE,gBAAgBA,MAAM,cAAc;gBACpC,yBAAyBgC;gBACzB,yBAAyBhC,MAAM,cAAc;YAC9C;YACAA,MAAM,cAAc,GAAGA,MAAM,cAAc;QAC5C;QACA,OAAO;YAAE,QAAQA;YAAOZ;QAAS;IAClC;IAmBQ,yBAAyBY,KAAkB,EAAEb,SAAkB,EAGrE;QACD,MAAMC,WAAqB,EAAE;QAC7B,MAAM6C,gBAAgB,IAAI,CAAC,cAAc,CAAC,gBAAgB,CAAC9C;QAG3D,IAAIa,AAA8BD,WAA9BC,MAAM,mBAAmB,IAAkBA,MAAM,mBAAmB,GAAGiC,eAAe;YACzF7C,SAAS,IAAI,CACZ,CAAC,sCAAsC,EAAEY,MAAM,mBAAmB,CAAC,cAAc,EAAEiC,cAAc,UAAU,CAAC;YAE7G,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,wCAAwC;gBACzD,qBAAqBjC,MAAM,mBAAmB;gBAC9CiC;YACD;YACAjC,MAAM,mBAAmB,GAAGD;QAC7B;QAGA,IAAIC,AAA0BD,WAA1BC,MAAM,eAAe,IAAkBA,MAAM,eAAe,GAAGiC,eAAe;YACjF7C,SAAS,IAAI,CACZ,CAAC,kCAAkC,EAAEY,MAAM,eAAe,CAAC,cAAc,EAAEiC,cAAc,UAAU,CAAC;YAErG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,oCAAoC;gBACrD,iBAAiBjC,MAAM,eAAe;gBACtCiC;YACD;YACAjC,MAAM,eAAe,GAAGD;QACzB;QAGA,IAAIC,AAA8BD,WAA9BC,MAAM,mBAAmB,IAAkBA,MAAM,mBAAmB,GAAGiC,eAAe;YACzF7C,SAAS,IAAI,CACZ,CAAC,sCAAsC,EAAEY,MAAM,mBAAmB,CAAC,cAAc,EAAEiC,cAAc,UAAU,CAAC;YAE7G,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,wCAAwC;gBACzD,qBAAqBjC,MAAM,mBAAmB;gBAC9CiC;YACD;YACAjC,MAAM,mBAAmB,GAAGD;QAC7B;QAGA,IAAIC,MAAM,iBAAiB,EAAE,QAAQ;YACpC,MAAMkC,QAAQlC,MAAM,iBAAiB,CAAC,MAAM,CAAC,CAACmC,IAAcA,KAAKF;YACjE,IAAIC,MAAM,MAAM,GAAGlC,MAAM,iBAAiB,CAAC,MAAM,EAAE;gBAClD,MAAMoC,UAAUpC,MAAM,iBAAiB,CAAC,MAAM,CAAC,CAACmC,IAAcA,IAAIF;gBAClE7C,SAAS,IAAI,CACZ,CAAC,sCAAsC,EAAEgD,QAAQ,IAAI,CAAC,MAAM,eAAe,EAAEH,cAAc,UAAU,CAAC;gBAEvG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,uCAAuC;oBACxD,UAAUjC,MAAM,iBAAiB;oBACjC,UAAUkC;oBACVD;gBACD;YACD;YACAjC,MAAM,iBAAiB,GAAGkC,MAAM,MAAM,GAAG,IAAIA,QAAQnC;QACtD;QAGA,IAAIC,MAAM,mBAAmB,EAAE,QAAQ;YACtC,MAAMkC,QAAQlC,MAAM,mBAAmB,CAAC,MAAM,CAAC,CAACmC,IAAcA,KAAKF;YACnE,IAAIC,MAAM,MAAM,GAAGlC,MAAM,mBAAmB,CAAC,MAAM,EAAE;gBACpD,MAAMoC,UAAUpC,MAAM,mBAAmB,CAAC,MAAM,CAC/C,CAACmC,IAAcA,IAAIF;gBAEpB7C,SAAS,IAAI,CACZ,CAAC,wCAAwC,EAAEgD,QAAQ,IAAI,CAAC,MAAM,eAAe,EAAEH,cAAc,UAAU,CAAC;gBAEzG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,yCAAyC;oBAC1D,UAAUjC,MAAM,mBAAmB;oBACnC,UAAUkC;oBACVD;gBACD;YACD;YACAjC,MAAM,mBAAmB,GAAGkC,MAAM,MAAM,GAAG,IAAIA,QAAQnC;QACxD;QAGA,IAAIC,MAAM,gBAAgB,EAAE,QAAQ;YACnC,MAAMkC,QAAQlC,MAAM,gBAAgB,CAAC,MAAM,CAAC,CAACqC,KAC5C,IAAI,CAAC,cAAc,CAAC,YAAY,CAAClD,WAAWkD;YAE7C,IAAIH,MAAM,MAAM,GAAGlC,MAAM,gBAAgB,CAAC,MAAM,EAAE;gBACjD,MAAMoC,UAAUpC,MAAM,gBAAgB,CAAC,MAAM,CAC5C,CAACqC,KAAe,CAAC,IAAI,CAAC,cAAc,CAAC,YAAY,CAAClD,WAAWkD;gBAE9DjD,SAAS,IAAI,CAAC,CAAC,qCAAqC,EAAEgD,QAAQ,IAAI,CAAC,MAAM,CAAC,CAAC;gBAC3E,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,sCAAsC;oBACvD,UAAUpC,MAAM,gBAAgB;oBAChC,UAAUkC;oBACV,kBAAkB,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC/C;gBACpD;YACD;YACAa,MAAM,gBAAgB,GAAGkC,MAAM,MAAM,GAAG,IAAIA,QAAQnC;QACrD;QAEA,OAAO;YAAE,QAAQC;YAAOZ;QAAS;IAClC;IAMQ,kBAAkBY,KAAkB,EAAEb,SAAkB,EAAQ;QACvE,MAAMmD,IAAItC,MAAM,YAAY;QAC5B,IAAKsC,AAAAA,CAAAA,AAAM,gBAANA,KAAqBA,AAAM,uBAANA,CAAuB,KAAM,CAAC,IAAI,CAAC,SAAS,CAAC,cAAc,EACpF,MAAM,IAAIC,gBACT,gBACA,CAAC,MAAM,EAAED,EAAE,wGAAwG,CAAC;QAGtH,IACEA,AAAAA,CAAAA,AAAM,iBAANA,KAAsBA,AAAM,oBAANA,KAAyBA,AAAM,gBAANA,CAAgB,KAChE,CAAC,IAAI,CAAC,SAAS,CAAC,eAAe,EAE/B,MAAM,IAAIC,gBACT,gBACA,CAAC,MAAM,EAAED,EAAE,oHAAoH,EAAEnE,iBAAiB,kBAAkB,CAACmE,GAAG,uBAAuB,CAAC;QAGlM,IAAIA,AAAM,gBAANA,KAAqB,CAACtC,MAAM,SAAS,EACxC,MAAM,IAAIwC,qBACT,uBAAuBxC,MAAM,cAAc,GAAG;QAGhD,IAAIsC,AAAM,uBAANA,KAA4B,CAACtC,MAAM,kBAAkB,EACxD,MAAM,IAAIuC,gBACT,sBACA,8BAA8BvC,MAAM,cAAc,GAAG;QAGvD,IAAIsC,AAAM,gBAANA,GAAmB;YACtB,IAAItC,AAA2BD,WAA3BC,MAAM,gBAAgB,EACzB,MAAM,IAAIuC,gBACT,oBACA,uBAAuBvC,MAAM,cAAc,GAAG;YAGhD,IAAIA,MAAM,gBAAgB,GAAGA,MAAM,cAAc,EAChD,MAAM,IAAIyC,sBACT,sBAAsBzC,MAAM,gBAAgB,GAAG,gCAAgCA,MAAM,cAAc;YAGrG,IAAI,CAAC,IAAI,CAAC,oBAAoB,CAACA,MAAM,gBAAgB,EAAEb,YACtD,MAAM,IAAIsD,sBACT,sBAAsBzC,MAAM,gBAAgB,GAAG;QAGlD;IACD;IAMQ,qBAAqB0C,aAAqB,EAAEvD,SAAkB,EAAW;QAChF,MAAMsB,UAAU,IAAI,CAAC,cAAc,CAAC,UAAU,CAACtB;QAC/C,KAAK,MAAMmD,KAAK7B,QACf,IAAI6B,EAAE,cAAc,KAAKI,eAAe,OAAO;QAEhD,MAAMhC,WAAW,IAAI,CAAC,cAAc,CAAC,WAAW,CAACvB;QACjD,KAAK,MAAMwD,kBAAkBC,OAAO,MAAM,CAAClC,UAC1C,KAAK,MAAM4B,KAAKK,eACf,IAAIL,EAAE,cAAc,KAAKI,eAAe,OAAO;QAGjD,OAAO;IACR;IAMA,OAAe,mBAAmBJ,CAA+C,EAAU;QAC1F,OAAQA;YACP,KAAK;gBACJ,OAAO;YACR,KAAK;gBACJ,OAAO;YACR,KAAK;gBACJ,OAAO;QACT;IACD;IAOQ,gBAAgBtC,KAAkB,EAAEb,SAAkB,EAAkB;QAC/E,IAAI,CAAC,cAAc,CAAC,UAAU,CAACa;QAC/B,MAAM6C,SAA2B,IAAI,CAAC,gBAAgB,CAAE,OAAO,CAAC;YAC/D,WAAW1D,aAAa;YACxB,uBAAuBa,MAAM,cAAc;YAC3C,UAAUA,MAAM,SAAS;YACzB,eAAeA,MAAM,cAAc,IAAI,CAAC;YACxC,OAAO;YACP,WAAW;QACZ;QACA,OAAO;YACN,SAAS;gBACR;oBACC,MAAM;oBACN,MAAMgB,KAAK,SAAS,CACnB;wBACC,QAAQ;wBACR,oBAAoB6B,OAAO,KAAK;wBAChC,WAAWA,OAAO,QAAQ;wBAC1B,gBAAgBA,OAAO,aAAa;wBACpC,YAAYA,OAAO,SAAS;wBAC5B,gBAAgB7C,MAAM,cAAc;wBACpC,gBAAgBA,MAAM,cAAc;wBACpC,GAAIb,YAAY;4BAAE,YAAYA;wBAAU,IAAI,CAAC,CAAC;oBAC/C,GACA,MACA;gBAEF;aACA;QACF;IACD;IAOQ,uBAAuBa,KAAkB,EAAE8C,UAAmB,EAAQ;QAC7E,MAAMC,QAAQ/C,MAAM,kBAAkB;QACtC,MAAMgD,SAAS,IAAI,CAAC,gBAAgB,CAAE,IAAI,CAACD;QAC3C,IAAIC,UAAUA,OAAO,SAAS,IAAIC,KAAK,GAAG,IACzC,MAAM,IAAIC,uBAAuB,+BAA+BH;QAEjE,MAAMF,SAAS,IAAI,CAAC,gBAAgB,CAAE,MAAM,CAACE;QAC7C,IAAI,CAACF,QACJ,MAAM,IAAIM,wBAAwB,iCAAiCJ;QAEnE/C,MAA2B,YAAY,GAAG6C,OAAO,qBAAqB;IACxE;AACD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Aggregator.d.ts","sourceRoot":"","sources":["../../../src/core/evaluator/Aggregator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"Aggregator.d.ts","sourceRoot":"","sources":["../../../src/core/evaluator/Aggregator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAajD;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,UAAU;IACtB;;;;;;;OAOG;IACI,qBAAqB,CAC3B,OAAO,EAAE,WAAW,EAAE,EACtB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,GACrC,cAAc;CAwCjB"}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { _computeChainDepth, _countByType } from "./internals.js";
|
|
2
|
+
function roundToPrecision(value, decimals = 10) {
|
|
3
|
+
const factor = Math.pow(10, decimals);
|
|
4
|
+
return Math.round(value * factor) / factor;
|
|
5
|
+
}
|
|
2
6
|
class Aggregator {
|
|
3
7
|
computeReasoningStats(history, branches) {
|
|
4
8
|
const typeCounts = _countByType(history);
|
|
@@ -22,8 +26,8 @@ class Aggregator {
|
|
|
22
26
|
...hypothesisIds
|
|
23
27
|
].filter((id)=>verifiedIds.has(id)).length,
|
|
24
28
|
unresolved_hypothesis_count: unresolvedCount,
|
|
25
|
-
average_quality_score: allScores.length > 0 ? allScores.reduce((a, b)=>a + b, 0) / allScores.length : null,
|
|
26
|
-
average_confidence: allConfidences.length > 0 ? allConfidences.reduce((a, b)=>a + b, 0) / allConfidences.length : null
|
|
29
|
+
average_quality_score: allScores.length > 0 ? roundToPrecision(allScores.reduce((a, b)=>a + b, 0) / allScores.length) : null,
|
|
30
|
+
average_confidence: allConfidences.length > 0 ? roundToPrecision(allConfidences.reduce((a, b)=>a + b, 0) / allConfidences.length) : null
|
|
27
31
|
};
|
|
28
32
|
}
|
|
29
33
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core/evaluator/Aggregator.js","sources":["../../../src/core/evaluator/Aggregator.ts"],"sourcesContent":["/**\n * Reasoning analytics aggregation.\n *\n * Provides the {@link Aggregator} class — a stateless service that computes\n * aggregated {@link ReasoningStats} from thought history and branch data.\n * Extracted from {@link ThoughtEvaluator} to isolate the stats-aggregation\n * concern.\n *\n * @module core/evaluator/Aggregator\n */\n\nimport type { ReasoningStats } from '../reasoning.js';\nimport type { ThoughtData } from '../thought.js';\nimport { _computeChainDepth, _countByType } from './internals.js';\n\n/**\n * Stateless service that aggregates reasoning analytics from thought\n * history and branch data.\n *\n * @remarks\n * All methods are pure computations — no side effects, no I/O, no\n * internal state. Designed to be registered as transient in the DI\n * container.\n *\n * @example\n * ```typescript\n * const aggregator = new Aggregator();\n * const stats = aggregator.computeReasoningStats(history, branches);\n * console.log(stats.total_thoughts); // 12\n * ```\n */\nexport class Aggregator {\n\t/**\n\t * Compute aggregated reasoning analytics.\n\t * Pure computation from history + branches.\n\t *\n\t * @param history - All thoughts in the current session\n\t * @param branches - Map of branch IDs to their thought arrays\n\t * @returns Aggregated reasoning statistics for the session\n\t */\n\tpublic computeReasoningStats(\n\t\thistory: ThoughtData[],\n\t\tbranches: Record<string, ThoughtData[]>\n\t): ReasoningStats {\n\t\tconst typeCounts = _countByType(history);\n\t\tconst allScores = history\n\t\t\t.map((t) => t.quality_score)\n\t\t\t.filter((s): s is number => s !== undefined);\n\t\tconst allConfidences = history\n\t\t\t.map((t) => t.confidence)\n\t\t\t.filter((c): c is number => c !== undefined);\n\n\t\tconst hypotheses = history.filter((t) => t.thought_type === 'hypothesis');\n\t\tconst hypothesisIds = new Set(hypotheses.map((t) => t.hypothesis_id).filter(Boolean));\n\t\tconst verifiedIds = new Set(\n\t\t\thistory\n\t\t\t\t.filter((t) => t.thought_type === 'verification' && t.hypothesis_id)\n\t\t\t\t.map((t) => t.hypothesis_id)\n\t\t);\n\t\tconst unresolvedCount = [...hypothesisIds].filter((id) => !verifiedIds.has(id)).length;\n\n\t\treturn {\n\t\t\ttotal_thoughts: history.length,\n\t\t\ttotal_branches: Object.keys(branches).length,\n\t\t\ttotal_revisions: history.filter((t) => t.is_revision).length,\n\t\t\ttotal_merges: history.filter(\n\t\t\t\t(t) => (t.merge_from_thoughts?.length ?? 0) > 0 || (t.merge_branch_ids?.length ?? 0) > 0\n\t\t\t).length,\n\t\t\tchain_depth: _computeChainDepth(history),\n\t\t\tthought_type_counts: typeCounts,\n\t\t\thypothesis_count: hypothesisIds.size,\n\t\t\tverified_hypothesis_count: [...hypothesisIds].filter((id) => verifiedIds.has(id)).length,\n\t\t\tunresolved_hypothesis_count: unresolvedCount,\n\t\t\taverage_quality_score:\n\t\t\t\tallScores.length > 0
|
|
1
|
+
{"version":3,"file":"core/evaluator/Aggregator.js","sources":["../../../src/core/evaluator/Aggregator.ts"],"sourcesContent":["/**\n * Reasoning analytics aggregation.\n *\n * Provides the {@link Aggregator} class — a stateless service that computes\n * aggregated {@link ReasoningStats} from thought history and branch data.\n * Extracted from {@link ThoughtEvaluator} to isolate the stats-aggregation\n * concern.\n *\n * @module core/evaluator/Aggregator\n */\n\nimport type { ReasoningStats } from '../reasoning.js';\nimport type { ThoughtData } from '../thought.js';\nimport { _computeChainDepth, _countByType } from './internals.js';\n\n/**\n * Round a numeric value to a fixed number of decimal places to mitigate\n * IEEE 754 floating-point accumulation errors (e.g. 0.9 + 0.8 averaging to\n * 0.8500000000000001 instead of 0.85).\n */\nfunction roundToPrecision(value: number, decimals: number = 10): number {\n\tconst factor = Math.pow(10, decimals);\n\treturn Math.round(value * factor) / factor;\n}\n\n/**\n * Stateless service that aggregates reasoning analytics from thought\n * history and branch data.\n *\n * @remarks\n * All methods are pure computations — no side effects, no I/O, no\n * internal state. Designed to be registered as transient in the DI\n * container.\n *\n * @example\n * ```typescript\n * const aggregator = new Aggregator();\n * const stats = aggregator.computeReasoningStats(history, branches);\n * console.log(stats.total_thoughts); // 12\n * ```\n */\nexport class Aggregator {\n\t/**\n\t * Compute aggregated reasoning analytics.\n\t * Pure computation from history + branches.\n\t *\n\t * @param history - All thoughts in the current session\n\t * @param branches - Map of branch IDs to their thought arrays\n\t * @returns Aggregated reasoning statistics for the session\n\t */\n\tpublic computeReasoningStats(\n\t\thistory: ThoughtData[],\n\t\tbranches: Record<string, ThoughtData[]>\n\t): ReasoningStats {\n\t\tconst typeCounts = _countByType(history);\n\t\tconst allScores = history\n\t\t\t.map((t) => t.quality_score)\n\t\t\t.filter((s): s is number => s !== undefined);\n\t\tconst allConfidences = history\n\t\t\t.map((t) => t.confidence)\n\t\t\t.filter((c): c is number => c !== undefined);\n\n\t\tconst hypotheses = history.filter((t) => t.thought_type === 'hypothesis');\n\t\tconst hypothesisIds = new Set(hypotheses.map((t) => t.hypothesis_id).filter(Boolean));\n\t\tconst verifiedIds = new Set(\n\t\t\thistory\n\t\t\t\t.filter((t) => t.thought_type === 'verification' && t.hypothesis_id)\n\t\t\t\t.map((t) => t.hypothesis_id)\n\t\t);\n\t\tconst unresolvedCount = [...hypothesisIds].filter((id) => !verifiedIds.has(id)).length;\n\n\t\treturn {\n\t\t\ttotal_thoughts: history.length,\n\t\t\ttotal_branches: Object.keys(branches).length,\n\t\t\ttotal_revisions: history.filter((t) => t.is_revision).length,\n\t\t\ttotal_merges: history.filter(\n\t\t\t\t(t) => (t.merge_from_thoughts?.length ?? 0) > 0 || (t.merge_branch_ids?.length ?? 0) > 0\n\t\t\t).length,\n\t\t\tchain_depth: _computeChainDepth(history),\n\t\t\tthought_type_counts: typeCounts,\n\t\t\thypothesis_count: hypothesisIds.size,\n\t\t\tverified_hypothesis_count: [...hypothesisIds].filter((id) => verifiedIds.has(id)).length,\n\t\t\tunresolved_hypothesis_count: unresolvedCount,\n\t\t\taverage_quality_score:\n\t\t\t\tallScores.length > 0\n\t\t\t\t\t? roundToPrecision(allScores.reduce((a, b) => a + b, 0) / allScores.length)\n\t\t\t\t\t: null,\n\t\t\taverage_confidence:\n\t\t\t\tallConfidences.length > 0\n\t\t\t\t\t? roundToPrecision(allConfidences.reduce((a, b) => a + b, 0) / allConfidences.length)\n\t\t\t\t\t: null,\n\t\t};\n\t}\n}\n"],"names":["roundToPrecision","value","decimals","factor","Math","Aggregator","history","branches","typeCounts","_countByType","allScores","t","s","undefined","allConfidences","c","hypotheses","hypothesisIds","Set","Boolean","verifiedIds","unresolvedCount","id","Object","_computeChainDepth","a","b"],"mappings":";AAoBA,SAASA,iBAAiBC,KAAa,EAAEC,WAAmB,EAAE;IAC7D,MAAMC,SAASC,KAAK,GAAG,CAAC,IAAIF;IAC5B,OAAOE,KAAK,KAAK,CAACH,QAAQE,UAAUA;AACrC;AAkBO,MAAME;IASL,sBACNC,OAAsB,EACtBC,QAAuC,EACtB;QACjB,MAAMC,aAAaC,aAAaH;QAChC,MAAMI,YAAYJ,QAChB,GAAG,CAAC,CAACK,IAAMA,EAAE,aAAa,EAC1B,MAAM,CAAC,CAACC,IAAmBA,AAAMC,WAAND;QAC7B,MAAME,iBAAiBR,QACrB,GAAG,CAAC,CAACK,IAAMA,EAAE,UAAU,EACvB,MAAM,CAAC,CAACI,IAAmBA,AAAMF,WAANE;QAE7B,MAAMC,aAAaV,QAAQ,MAAM,CAAC,CAACK,IAAMA,AAAmB,iBAAnBA,EAAE,YAAY;QACvD,MAAMM,gBAAgB,IAAIC,IAAIF,WAAW,GAAG,CAAC,CAACL,IAAMA,EAAE,aAAa,EAAE,MAAM,CAACQ;QAC5E,MAAMC,cAAc,IAAIF,IACvBZ,QACE,MAAM,CAAC,CAACK,IAAMA,AAAmB,mBAAnBA,EAAE,YAAY,IAAuBA,EAAE,aAAa,EAClE,GAAG,CAAC,CAACA,IAAMA,EAAE,aAAa;QAE7B,MAAMU,kBAAkB;eAAIJ;SAAc,CAAC,MAAM,CAAC,CAACK,KAAO,CAACF,YAAY,GAAG,CAACE,KAAK,MAAM;QAEtF,OAAO;YACN,gBAAgBhB,QAAQ,MAAM;YAC9B,gBAAgBiB,OAAO,IAAI,CAAChB,UAAU,MAAM;YAC5C,iBAAiBD,QAAQ,MAAM,CAAC,CAACK,IAAMA,EAAE,WAAW,EAAE,MAAM;YAC5D,cAAcL,QAAQ,MAAM,CAC3B,CAACK,IAAOA,AAAAA,CAAAA,EAAE,mBAAmB,EAAE,UAAU,KAAK,KAAMA,AAAAA,CAAAA,EAAE,gBAAgB,EAAE,UAAU,KAAK,GACtF,MAAM;YACR,aAAaa,mBAAmBlB;YAChC,qBAAqBE;YACrB,kBAAkBS,cAAc,IAAI;YACpC,2BAA2B;mBAAIA;aAAc,CAAC,MAAM,CAAC,CAACK,KAAOF,YAAY,GAAG,CAACE,KAAK,MAAM;YACxF,6BAA6BD;YAC7B,uBACCX,UAAU,MAAM,GAAG,IAChBV,iBAAiBU,UAAU,MAAM,CAAC,CAACe,GAAGC,IAAMD,IAAIC,GAAG,KAAKhB,UAAU,MAAM,IACxE;YACJ,oBACCI,eAAe,MAAM,GAAG,IACrBd,iBAAiBc,eAAe,MAAM,CAAC,CAACW,GAAGC,IAAMD,IAAIC,GAAG,KAAKZ,eAAe,MAAM,IAClF;QACL;IACD;AACD"}
|
|
@@ -68,7 +68,7 @@ class PatternDetector {
|
|
|
68
68
|
return [
|
|
69
69
|
{
|
|
70
70
|
pattern: 'no_alternatives_explored',
|
|
71
|
-
severity: '
|
|
71
|
+
severity: 'warning',
|
|
72
72
|
message: '5+ thoughts with no critique or branching — consider exploring alternatives',
|
|
73
73
|
thought_range: [
|
|
74
74
|
start,
|
|
@@ -104,7 +104,7 @@ class PatternDetector {
|
|
|
104
104
|
const end = history[runStart + runLength - 1].thought_number ?? runStart + runLength;
|
|
105
105
|
signals.push({
|
|
106
106
|
pattern: 'monotonic_type',
|
|
107
|
-
severity: '
|
|
107
|
+
severity: 'warning',
|
|
108
108
|
message: `4+ consecutive '${runType}' thoughts (${start}-${end}) — consider varying approach`,
|
|
109
109
|
thought_range: [
|
|
110
110
|
start,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core/evaluator/PatternDetector.js","sources":["../../../src/core/evaluator/PatternDetector.ts"],"sourcesContent":["/**\n * Pattern detection for sequential thinking reasoning analytics.\n *\n * Provides the {@link PatternDetector} class — a stateless service that detects\n * reasoning patterns (anti-patterns and positive signals) from thought history.\n * Extracted from {@link ThoughtEvaluator} to keep concerns isolated and reduce\n * cyclomatic complexity per function.\n *\n * Detected patterns:\n * - `consecutive_without_verification` (warning) — 3+ consecutive regular thoughts\n * without a verification step\n * - `unverified_hypothesis` (warning) — hypothesis not verified within 3 subsequent\n * thoughts\n * - `no_alternatives_explored` (info) — 5+ thoughts with no critique and no branches\n * - `monotonic_type` (info) — 4+ consecutive thoughts with the same thought_type\n * (requires ≥1 explicit type and ≥5 thoughts)\n * - `confidence_drift` (warning) — 3+ consecutive thoughts with strictly decreasing\n * confidence\n * - `healthy_verification` (info) — hypothesis verified within 3 subsequent thoughts\n *\n * @module core/evaluator/PatternDetector\n */\n\nimport type { PatternSignal } from '../reasoning.js';\nimport type { ThoughtData } from '../thought.js';\n\n/**\n * Stateless service that detects reasoning patterns from thought history.\n *\n * @remarks\n * All methods are pure computations — no side effects, no I/O, no internal state.\n * Designed to be registered as transient in the DI container or composed directly\n * into {@link ThoughtEvaluator}.\n *\n * @example\n * ```typescript\n * const detector = new PatternDetector();\n * const signals = detector.computePatternSignals(history, branches);\n * const warnings = signals.filter((s) => s.severity === 'warning');\n * ```\n */\nexport class PatternDetector {\n\t/**\n\t * Detect all reasoning patterns from history and branches.\n\t *\n\t * @param history - All thoughts in the current session\n\t * @param branches - Map of branch IDs to their thought arrays\n\t * @returns Array of detected pattern signals (possibly empty)\n\t */\n\tpublic computePatternSignals(\n\t\thistory: ThoughtData[],\n\t\tbranches: Record<string, ThoughtData[]>\n\t): PatternSignal[] {\n\t\tif (history.length === 0) return [];\n\n\t\tconst signals: PatternSignal[] = [];\n\t\tsignals.push(...this._detectConsecutiveWithoutVerification(history));\n\t\tsignals.push(...this._detectUnverifiedHypothesis(history));\n\t\tsignals.push(...this._detectNoAlternativesExplored(history, branches));\n\t\tsignals.push(...this._detectMonotonicType(history));\n\t\tsignals.push(...this._detectConfidenceDrift(history));\n\t\tsignals.push(...this._detectHealthyVerification(history));\n\t\treturn signals;\n\t}\n\n\t/** Detect runs of 3+ consecutive thoughts without verification. */\n\tprivate _detectConsecutiveWithoutVerification(history: ThoughtData[]): PatternSignal[] {\n\t\tconst signals: PatternSignal[] = [];\n\t\tlet runStart = 0;\n\t\tfor (let i = 0; i < history.length; i++) {\n\t\t\tconst type = history[i]!.thought_type ?? 'regular';\n\t\t\tif (type === 'verification') {\n\t\t\t\trunStart = i + 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (i - runStart + 1 >= 3) {\n\t\t\t\tconst start = history[runStart]!.thought_number ?? runStart + 1;\n\t\t\t\tconst end = history[i]!.thought_number ?? i + 1;\n\t\t\t\tsignals.push({\n\t\t\t\t\tpattern: 'consecutive_without_verification',\n\t\t\t\t\tseverity: 'warning',\n\t\t\t\t\tmessage: `3+ consecutive thoughts (${start}-${end}) without verification`,\n\t\t\t\t\tthought_range: [start, end],\n\t\t\t\t});\n\t\t\t\trunStart = i + 1;\n\t\t\t}\n\t\t}\n\t\treturn signals;\n\t}\n\n\t/** Detect hypothesis thoughts not verified within 3 subsequent thoughts. */\n\tprivate _detectUnverifiedHypothesis(history: ThoughtData[]): PatternSignal[] {\n\t\tconst signals: PatternSignal[] = [];\n\t\tfor (let i = 0; i < history.length; i++) {\n\t\t\tif (history[i]!.thought_type !== 'hypothesis') continue;\n\t\t\tconst remaining = history.length - i - 1;\n\t\t\tif (remaining < 3) continue;\n\t\t\tconst lookahead = history.slice(i + 1, i + 4);\n\t\t\tconst hasVerification = lookahead.some((t) => t.thought_type === 'verification');\n\t\t\tif (!hasVerification) {\n\t\t\t\tconst n = history[i]!.thought_number ?? i + 1;\n\t\t\t\tsignals.push({\n\t\t\t\t\tpattern: 'unverified_hypothesis',\n\t\t\t\t\tseverity: 'warning',\n\t\t\t\t\tmessage: `Hypothesis at thought ${n} has not been verified within 3 thoughts`,\n\t\t\t\t\tthought_range: [n, n],\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn signals;\n\t}\n\n\t/** Detect 5+ thoughts with no critique and no branches. */\n\tprivate _detectNoAlternativesExplored(\n\t\thistory: ThoughtData[],\n\t\tbranches: Record<string, ThoughtData[]>\n\t): PatternSignal[] {\n\t\tif (history.length < 5) return [];\n\t\tif (history.some((t) => t.thought_type === 'critique')) return [];\n\t\tif (Object.keys(branches).length > 0) return [];\n\t\tconst start = history[0]!.thought_number ?? 1;\n\t\tconst end = history[history.length - 1]!.thought_number ?? history.length;\n\t\treturn [\n\t\t\t{\n\t\t\t\tpattern: 'no_alternatives_explored',\n\t\t\t\tseverity: 'info',\n\t\t\t\tmessage: '5+ thoughts with no critique or branching — consider exploring alternatives',\n\t\t\t\tthought_range: [start, end],\n\t\t\t},\n\t\t];\n\t}\n\n\t/**\n\t * Detect runs of 4+ consecutive thoughts with the same thought_type.\n\t * Only fires when history has ≥5 thoughts and at least one explicitly set thought_type.\n\t */\n\tprivate _detectMonotonicType(history: ThoughtData[]): PatternSignal[] {\n\t\tif (history.length < 5) return [];\n\t\tconst hasExplicitType = history.some((t) => t.thought_type !== undefined);\n\t\tif (!hasExplicitType) return [];\n\n\t\tconst signals: PatternSignal[] = [];\n\t\tlet runType = history[0]!.thought_type ?? 'regular';\n\t\tlet runStart = 0;\n\t\tlet runLength = 1;\n\n\t\tfor (let i = 1; i < history.length; i++) {\n\t\t\tconst type = history[i]!.thought_type ?? 'regular';\n\t\t\tif (type === runType) {\n\t\t\t\trunLength++;\n\t\t\t} else {\n\t\t\t\tthis._flushMonotonicRun(history, runStart, runLength, runType, signals);\n\t\t\t\trunType = type;\n\t\t\t\trunStart = i;\n\t\t\t\trunLength = 1;\n\t\t\t}\n\t\t}\n\t\tthis._flushMonotonicRun(history, runStart, runLength, runType, signals);\n\t\treturn signals;\n\t}\n\n\t/** Emit a monotonic_type signal if the run length qualifies. */\n\tprivate _flushMonotonicRun(\n\t\thistory: ThoughtData[],\n\t\trunStart: number,\n\t\trunLength: number,\n\t\trunType: string,\n\t\tsignals: PatternSignal[]\n\t): void {\n\t\tif (runLength < 4) return;\n\t\tconst start = history[runStart]!.thought_number ?? runStart + 1;\n\t\tconst end = history[runStart + runLength - 1]!.thought_number ?? runStart + runLength;\n\t\tsignals.push({\n\t\t\tpattern: 'monotonic_type',\n\t\t\tseverity: 'info',\n\t\t\tmessage: `4+ consecutive '${runType}' thoughts (${start}-${end}) — consider varying approach`,\n\t\t\tthought_range: [start, end],\n\t\t});\n\t}\n\n\t/** Detect runs of 3+ consecutive thoughts with strictly decreasing confidence. */\n\tprivate _detectConfidenceDrift(history: ThoughtData[]): PatternSignal[] {\n\t\tconst signals: PatternSignal[] = [];\n\t\tlet runStart = -1;\n\t\tlet runLength = 0;\n\t\tlet prevConf = -1;\n\n\t\tfor (let i = 0; i < history.length; i++) {\n\t\t\tconst conf = history[i]!.confidence;\n\t\t\tif (conf === undefined) {\n\t\t\t\tthis._flushDriftRun(history, runStart, runLength, signals);\n\t\t\t\trunStart = -1;\n\t\t\t\trunLength = 0;\n\t\t\t\tprevConf = -1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (runLength === 0) {\n\t\t\t\trunStart = i;\n\t\t\t\trunLength = 1;\n\t\t\t\tprevConf = conf;\n\t\t\t} else if (conf < prevConf) {\n\t\t\t\trunLength++;\n\t\t\t\tprevConf = conf;\n\t\t\t} else {\n\t\t\t\tthis._flushDriftRun(history, runStart, runLength, signals);\n\t\t\t\trunStart = i;\n\t\t\t\trunLength = 1;\n\t\t\t\tprevConf = conf;\n\t\t\t}\n\t\t}\n\t\tthis._flushDriftRun(history, runStart, runLength, signals);\n\t\treturn signals;\n\t}\n\n\t/** Emit a confidence_drift signal if the run length qualifies. */\n\tprivate _flushDriftRun(\n\t\thistory: ThoughtData[],\n\t\trunStart: number,\n\t\trunLength: number,\n\t\tsignals: PatternSignal[]\n\t): void {\n\t\tif (runLength < 3) return;\n\t\tconst start = history[runStart]!.thought_number ?? runStart + 1;\n\t\tconst end = history[runStart + runLength - 1]!.thought_number ?? runStart + runLength;\n\t\tconst firstConf = history[runStart]!.confidence!;\n\t\tconst lastConf = history[runStart + runLength - 1]!.confidence!;\n\t\tsignals.push({\n\t\t\tpattern: 'confidence_drift',\n\t\t\tseverity: 'warning',\n\t\t\tmessage: `Confidence decreasing across thoughts ${start}-${end} (${firstConf} → ${lastConf})`,\n\t\t\tthought_range: [start, end],\n\t\t});\n\t}\n\n\t/** Detect hypothesis verified within 3 subsequent thoughts — positive signal. */\n\tprivate _detectHealthyVerification(history: ThoughtData[]): PatternSignal[] {\n\t\tconst signals: PatternSignal[] = [];\n\t\tfor (let i = 0; i < history.length; i++) {\n\t\t\tif (history[i]!.thought_type !== 'hypothesis') continue;\n\t\t\tconst hypId = history[i]!.hypothesis_id;\n\t\t\tconst lookahead = history.slice(i + 1, i + 4);\n\t\t\tconst verifier = lookahead.find(\n\t\t\t\t(t) =>\n\t\t\t\t\tt.thought_type === 'verification' &&\n\t\t\t\t\t(t.hypothesis_id === hypId ||\n\t\t\t\t\t\tt.verification_target === (history[i]!.thought_number ?? i + 1))\n\t\t\t);\n\t\t\tif (verifier) {\n\t\t\t\tconst n = history[i]!.thought_number ?? i + 1;\n\t\t\t\tconst m = verifier.thought_number ?? history.indexOf(verifier) + 1;\n\t\t\t\tsignals.push({\n\t\t\t\t\tpattern: 'healthy_verification',\n\t\t\t\t\tseverity: 'info',\n\t\t\t\t\tmessage: `Hypothesis at thought ${n} verified at thought ${m} — good practice`,\n\t\t\t\t\tthought_range: [n, m],\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn signals;\n\t}\n}\n"],"names":["PatternDetector","history","branches","signals","runStart","i","type","start","end","remaining","lookahead","hasVerification","t","n","Object","hasExplicitType","undefined","runType","runLength","prevConf","conf","firstConf","lastConf","hypId","verifier","m"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"core/evaluator/PatternDetector.js","sources":["../../../src/core/evaluator/PatternDetector.ts"],"sourcesContent":["/**\n * Pattern detection for sequential thinking reasoning analytics.\n *\n * Provides the {@link PatternDetector} class — a stateless service that detects\n * reasoning patterns (anti-patterns and positive signals) from thought history.\n * Extracted from {@link ThoughtEvaluator} to keep concerns isolated and reduce\n * cyclomatic complexity per function.\n *\n * Detected patterns:\n * - `consecutive_without_verification` (warning) — 3+ consecutive regular thoughts\n * without a verification step\n * - `unverified_hypothesis` (warning) — hypothesis not verified within 3 subsequent\n * thoughts\n * - `no_alternatives_explored` (info) — 5+ thoughts with no critique and no branches\n * - `monotonic_type` (info) — 4+ consecutive thoughts with the same thought_type\n * (requires ≥1 explicit type and ≥5 thoughts)\n * - `confidence_drift` (warning) — 3+ consecutive thoughts with strictly decreasing\n * confidence\n * - `healthy_verification` (info) — hypothesis verified within 3 subsequent thoughts\n *\n * @module core/evaluator/PatternDetector\n */\n\nimport type { PatternSignal } from '../reasoning.js';\nimport type { ThoughtData } from '../thought.js';\n\n/**\n * Stateless service that detects reasoning patterns from thought history.\n *\n * @remarks\n * All methods are pure computations — no side effects, no I/O, no internal state.\n * Designed to be registered as transient in the DI container or composed directly\n * into {@link ThoughtEvaluator}.\n *\n * @example\n * ```typescript\n * const detector = new PatternDetector();\n * const signals = detector.computePatternSignals(history, branches);\n * const warnings = signals.filter((s) => s.severity === 'warning');\n * ```\n */\nexport class PatternDetector {\n\t/**\n\t * Detect all reasoning patterns from history and branches.\n\t *\n\t * @param history - All thoughts in the current session\n\t * @param branches - Map of branch IDs to their thought arrays\n\t * @returns Array of detected pattern signals (possibly empty)\n\t */\n\tpublic computePatternSignals(\n\t\thistory: ThoughtData[],\n\t\tbranches: Record<string, ThoughtData[]>\n\t): PatternSignal[] {\n\t\tif (history.length === 0) return [];\n\n\t\tconst signals: PatternSignal[] = [];\n\t\tsignals.push(...this._detectConsecutiveWithoutVerification(history));\n\t\tsignals.push(...this._detectUnverifiedHypothesis(history));\n\t\tsignals.push(...this._detectNoAlternativesExplored(history, branches));\n\t\tsignals.push(...this._detectMonotonicType(history));\n\t\tsignals.push(...this._detectConfidenceDrift(history));\n\t\tsignals.push(...this._detectHealthyVerification(history));\n\t\treturn signals;\n\t}\n\n\t/** Detect runs of 3+ consecutive thoughts without verification. */\n\tprivate _detectConsecutiveWithoutVerification(history: ThoughtData[]): PatternSignal[] {\n\t\tconst signals: PatternSignal[] = [];\n\t\tlet runStart = 0;\n\t\tfor (let i = 0; i < history.length; i++) {\n\t\t\tconst type = history[i]!.thought_type ?? 'regular';\n\t\t\tif (type === 'verification') {\n\t\t\t\trunStart = i + 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (i - runStart + 1 >= 3) {\n\t\t\t\tconst start = history[runStart]!.thought_number ?? runStart + 1;\n\t\t\t\tconst end = history[i]!.thought_number ?? i + 1;\n\t\t\t\tsignals.push({\n\t\t\t\t\tpattern: 'consecutive_without_verification',\n\t\t\t\t\tseverity: 'warning',\n\t\t\t\t\tmessage: `3+ consecutive thoughts (${start}-${end}) without verification`,\n\t\t\t\t\tthought_range: [start, end],\n\t\t\t\t});\n\t\t\t\trunStart = i + 1;\n\t\t\t}\n\t\t}\n\t\treturn signals;\n\t}\n\n\t/** Detect hypothesis thoughts not verified within 3 subsequent thoughts. */\n\tprivate _detectUnverifiedHypothesis(history: ThoughtData[]): PatternSignal[] {\n\t\tconst signals: PatternSignal[] = [];\n\t\tfor (let i = 0; i < history.length; i++) {\n\t\t\tif (history[i]!.thought_type !== 'hypothesis') continue;\n\t\t\tconst remaining = history.length - i - 1;\n\t\t\tif (remaining < 3) continue;\n\t\t\tconst lookahead = history.slice(i + 1, i + 4);\n\t\t\tconst hasVerification = lookahead.some((t) => t.thought_type === 'verification');\n\t\t\tif (!hasVerification) {\n\t\t\t\tconst n = history[i]!.thought_number ?? i + 1;\n\t\t\t\tsignals.push({\n\t\t\t\t\tpattern: 'unverified_hypothesis',\n\t\t\t\t\tseverity: 'warning',\n\t\t\t\t\tmessage: `Hypothesis at thought ${n} has not been verified within 3 thoughts`,\n\t\t\t\t\tthought_range: [n, n],\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn signals;\n\t}\n\n\t/** Detect 5+ thoughts with no critique and no branches. */\n\tprivate _detectNoAlternativesExplored(\n\t\thistory: ThoughtData[],\n\t\tbranches: Record<string, ThoughtData[]>\n\t): PatternSignal[] {\n\t\tif (history.length < 5) return [];\n\t\tif (history.some((t) => t.thought_type === 'critique')) return [];\n\t\tif (Object.keys(branches).length > 0) return [];\n\t\tconst start = history[0]!.thought_number ?? 1;\n\t\tconst end = history[history.length - 1]!.thought_number ?? history.length;\n\t\treturn [\n\t\t\t{\n\t\t\t\tpattern: 'no_alternatives_explored',\n\t\t\t\tseverity: 'warning',\n\t\t\t\tmessage: '5+ thoughts with no critique or branching — consider exploring alternatives',\n\t\t\t\tthought_range: [start, end],\n\t\t\t},\n\t\t];\n\t}\n\n\t/**\n\t * Detect runs of 4+ consecutive thoughts with the same thought_type.\n\t * Only fires when history has ≥5 thoughts and at least one explicitly set thought_type.\n\t */\n\tprivate _detectMonotonicType(history: ThoughtData[]): PatternSignal[] {\n\t\tif (history.length < 5) return [];\n\t\tconst hasExplicitType = history.some((t) => t.thought_type !== undefined);\n\t\tif (!hasExplicitType) return [];\n\n\t\tconst signals: PatternSignal[] = [];\n\t\tlet runType = history[0]!.thought_type ?? 'regular';\n\t\tlet runStart = 0;\n\t\tlet runLength = 1;\n\n\t\tfor (let i = 1; i < history.length; i++) {\n\t\t\tconst type = history[i]!.thought_type ?? 'regular';\n\t\t\tif (type === runType) {\n\t\t\t\trunLength++;\n\t\t\t} else {\n\t\t\t\tthis._flushMonotonicRun(history, runStart, runLength, runType, signals);\n\t\t\t\trunType = type;\n\t\t\t\trunStart = i;\n\t\t\t\trunLength = 1;\n\t\t\t}\n\t\t}\n\t\tthis._flushMonotonicRun(history, runStart, runLength, runType, signals);\n\t\treturn signals;\n\t}\n\n\t/** Emit a monotonic_type signal if the run length qualifies. */\n\tprivate _flushMonotonicRun(\n\t\thistory: ThoughtData[],\n\t\trunStart: number,\n\t\trunLength: number,\n\t\trunType: string,\n\t\tsignals: PatternSignal[]\n\t): void {\n\t\tif (runLength < 4) return;\n\t\tconst start = history[runStart]!.thought_number ?? runStart + 1;\n\t\tconst end = history[runStart + runLength - 1]!.thought_number ?? runStart + runLength;\n\t\tsignals.push({\n\t\t\tpattern: 'monotonic_type',\n\t\t\tseverity: 'warning',\n\t\t\tmessage: `4+ consecutive '${runType}' thoughts (${start}-${end}) — consider varying approach`,\n\t\t\tthought_range: [start, end],\n\t\t});\n\t}\n\n\t/** Detect runs of 3+ consecutive thoughts with strictly decreasing confidence. */\n\tprivate _detectConfidenceDrift(history: ThoughtData[]): PatternSignal[] {\n\t\tconst signals: PatternSignal[] = [];\n\t\tlet runStart = -1;\n\t\tlet runLength = 0;\n\t\tlet prevConf = -1;\n\n\t\tfor (let i = 0; i < history.length; i++) {\n\t\t\tconst conf = history[i]!.confidence;\n\t\t\tif (conf === undefined) {\n\t\t\t\tthis._flushDriftRun(history, runStart, runLength, signals);\n\t\t\t\trunStart = -1;\n\t\t\t\trunLength = 0;\n\t\t\t\tprevConf = -1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (runLength === 0) {\n\t\t\t\trunStart = i;\n\t\t\t\trunLength = 1;\n\t\t\t\tprevConf = conf;\n\t\t\t} else if (conf < prevConf) {\n\t\t\t\trunLength++;\n\t\t\t\tprevConf = conf;\n\t\t\t} else {\n\t\t\t\tthis._flushDriftRun(history, runStart, runLength, signals);\n\t\t\t\trunStart = i;\n\t\t\t\trunLength = 1;\n\t\t\t\tprevConf = conf;\n\t\t\t}\n\t\t}\n\t\tthis._flushDriftRun(history, runStart, runLength, signals);\n\t\treturn signals;\n\t}\n\n\t/** Emit a confidence_drift signal if the run length qualifies. */\n\tprivate _flushDriftRun(\n\t\thistory: ThoughtData[],\n\t\trunStart: number,\n\t\trunLength: number,\n\t\tsignals: PatternSignal[]\n\t): void {\n\t\tif (runLength < 3) return;\n\t\tconst start = history[runStart]!.thought_number ?? runStart + 1;\n\t\tconst end = history[runStart + runLength - 1]!.thought_number ?? runStart + runLength;\n\t\tconst firstConf = history[runStart]!.confidence!;\n\t\tconst lastConf = history[runStart + runLength - 1]!.confidence!;\n\t\tsignals.push({\n\t\t\tpattern: 'confidence_drift',\n\t\t\tseverity: 'warning',\n\t\t\tmessage: `Confidence decreasing across thoughts ${start}-${end} (${firstConf} → ${lastConf})`,\n\t\t\tthought_range: [start, end],\n\t\t});\n\t}\n\n\t/** Detect hypothesis verified within 3 subsequent thoughts — positive signal. */\n\tprivate _detectHealthyVerification(history: ThoughtData[]): PatternSignal[] {\n\t\tconst signals: PatternSignal[] = [];\n\t\tfor (let i = 0; i < history.length; i++) {\n\t\t\tif (history[i]!.thought_type !== 'hypothesis') continue;\n\t\t\tconst hypId = history[i]!.hypothesis_id;\n\t\t\tconst lookahead = history.slice(i + 1, i + 4);\n\t\t\tconst verifier = lookahead.find(\n\t\t\t\t(t) =>\n\t\t\t\t\tt.thought_type === 'verification' &&\n\t\t\t\t\t(t.hypothesis_id === hypId ||\n\t\t\t\t\t\tt.verification_target === (history[i]!.thought_number ?? i + 1))\n\t\t\t);\n\t\t\tif (verifier) {\n\t\t\t\tconst n = history[i]!.thought_number ?? i + 1;\n\t\t\t\tconst m = verifier.thought_number ?? history.indexOf(verifier) + 1;\n\t\t\t\tsignals.push({\n\t\t\t\t\tpattern: 'healthy_verification',\n\t\t\t\t\tseverity: 'info',\n\t\t\t\t\tmessage: `Hypothesis at thought ${n} verified at thought ${m} — good practice`,\n\t\t\t\t\tthought_range: [n, m],\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn signals;\n\t}\n}\n"],"names":["PatternDetector","history","branches","signals","runStart","i","type","start","end","remaining","lookahead","hasVerification","t","n","Object","hasExplicitType","undefined","runType","runLength","prevConf","conf","firstConf","lastConf","hypId","verifier","m"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SignalComputer.d.ts","sourceRoot":"","sources":["../../../src/core/evaluator/SignalComputer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"SignalComputer.d.ts","sourceRoot":"","sources":["../../../src/core/evaluator/SignalComputer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAgBjD,iEAAiE;AACjE,UAAU,uBAAuB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE;QACX,cAAc,EAAE,MAAM,CAAC;QACvB,qBAAqB,EAAE,MAAM,CAAC;QAC9B,gBAAgB,EAAE,MAAM,CAAC;QACzB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;KACpC,CAAC;IACF,cAAc,EAAE;QACf,cAAc,EAAE,MAAM,CAAC;QACvB,qBAAqB,EAAE,MAAM,CAAC;QAC9B,gBAAgB,EAAE,MAAM,CAAC;QACzB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;KACpC,CAAC;CACF;AAED;;;;;;GAMG;AACH,qBAAa,cAAc;IAC1B;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAK5B;IAEX;;;OAGG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAIlC;IAEX;;;;;;OAMG;IACI,wBAAwB,CAC9B,OAAO,EAAE,WAAW,EAAE,EACtB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,GACrC,iBAAiB;IAiCpB;;;;;;;;;;;;;;;OAeG;IACI,wBAAwB,CAC9B,OAAO,EAAE,WAAW,EAAE,EACtB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,EACvC,gBAAgB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,EAC7C,WAAW,EAAE,MAAM,EAAE,GACnB,uBAAuB,GAAG,IAAI;IA+EjC;;;;;;OAMG;IACI,0BAA0B,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI;IAUvE;;;;;;OAMG;IACI,6BAA6B,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI;CAS1E"}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { ALL_THOUGHT_TYPES, _computeChainDepth, _countByType } from "./internals.js";
|
|
2
2
|
const FLOOR = 0.01;
|
|
3
|
+
function roundToPrecision(value, decimals = 10) {
|
|
4
|
+
const factor = Math.pow(10, decimals);
|
|
5
|
+
return Math.round(value * factor) / factor;
|
|
6
|
+
}
|
|
3
7
|
class SignalComputer {
|
|
4
8
|
static QUALITY_WEIGHTS = {
|
|
5
9
|
typeDiversity: 0.3,
|
|
@@ -23,7 +27,7 @@ class SignalComputer {
|
|
|
23
27
|
thought_type_distribution: typeDistribution,
|
|
24
28
|
has_hypothesis: history.some((t)=>'hypothesis' === t.thought_type),
|
|
25
29
|
has_verification: history.some((t)=>'verification' === t.thought_type),
|
|
26
|
-
average_confidence: allConfidences.length > 0 ? allConfidences.reduce((a, b)=>a + b, 0) / allConfidences.length : null,
|
|
30
|
+
average_confidence: allConfidences.length > 0 ? roundToPrecision(allConfidences.reduce((a, b)=>a + b, 0) / allConfidences.length) : null,
|
|
27
31
|
...null !== structuralResult && {
|
|
28
32
|
structural_quality: structuralResult.score,
|
|
29
33
|
quality_components: structuralResult.components,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core/evaluator/SignalComputer.js","sources":["../../../src/core/evaluator/SignalComputer.ts"],"sourcesContent":["/**\n * Confidence signal computation for sequential thinking.\n *\n * Provides {@link SignalComputer} — a pure stateless service that computes\n * {@link ConfidenceSignals} (including the composite `structural_quality`\n * score and its components) from thought history and branch data.\n *\n * Extracted from `ThoughtEvaluator` as part of the evaluator decomposition.\n * All methods are pure: no side effects, no I/O, no internal state.\n *\n * @module core/evaluator/SignalComputer\n */\n\nimport type { ConfidenceSignals, ThoughtType } from '../reasoning.js';\nimport type { ThoughtData } from '../thought.js';\nimport { ALL_THOUGHT_TYPES, _computeChainDepth, _countByType } from './internals.js';\n\n/** Floor value applied to each quality component to prevent geometric mean collapse. */\nconst FLOOR = 0.01;\n\n/** Result of {@link SignalComputer.computeStructuralQuality}. */\ninterface StructuralQualityResult {\n\tscore: number;\n\tcomponents: {\n\t\ttype_diversity: number;\n\t\tverification_coverage: number;\n\t\tdepth_efficiency: number;\n\t\tconfidence_stability: number | null;\n\t};\n\traw_components: {\n\t\ttype_diversity: number;\n\t\tverification_coverage: number;\n\t\tdepth_efficiency: number;\n\t\tconfidence_stability: number | null;\n\t};\n}\n\n/**\n * Stateless service that computes {@link ConfidenceSignals} from thought history.\n *\n * @remarks\n * Pure computation — no side effects, no I/O, no internal state.\n * Safe to register as singleton or transient in DI.\n */\nexport class SignalComputer {\n\t/**\n\t * Structural quality weight configuration.\n\t *\n\t * Weights reflect the relative importance of each quality dimension:\n\t * - type_diversity (0.3): Shannon entropy of thought types — rewards reasoning that uses\n\t * multiple cognitive modes (hypothesis, verification, critique, synthesis) rather than\n\t * monotonic sequences. Based on information theory: higher entropy = more information.\n\t *\n\t * - verification_coverage (0.3): Ratio of verified to total hypotheses — rewards scientific\n\t * rigor. Hypotheses without verification are speculation; verification grounds reasoning.\n\t * Equal weight with diversity because unverified reasoning is a common failure mode.\n\t *\n\t * - depth_efficiency (0.2): Ratio of structural depth to total thoughts — rewards efficient\n\t * reasoning. Deep chains (many revisions/branches) are good; but only if proportionally\n\t * dense. Padding with low-value thoughts penalizes this metric.\n\t *\n\t * - confidence_stability (0.2): Low variance in self-assessed confidence — rewards calibrated\n\t * reasoning. Wildly fluctuating confidence suggests uncertainty about the approach.\n\t * Lower weight because confidence values are LLM self-reports (inherently noisy).\n\t *\n\t * Design rationale:\n\t * - Diversity + Verification (0.6) > Depth + Stability (0.4): Structural properties of the\n\t * reasoning DAG are more important than behavioral signals.\n\t * - All weights are positive (no dimension is penalizing).\n\t * - Weights sum to 1.0 for normalized scoring.\n\t * - When confidence_stability is unavailable (n<2), remaining weights redistribute\n\t * proportionally: td→0.375, vc→0.375, de→0.25 (preserving relative ratios).\n\t */\n\tprivate static readonly QUALITY_WEIGHTS = {\n\t\ttypeDiversity: 0.3,\n\t\tverificationCoverage: 0.3,\n\t\tdepthEfficiency: 0.2,\n\t\tconfidenceStability: 0.2,\n\t} as const;\n\n\t/**\n\t * Redistributed weights when confidence_stability is excluded (n<2).\n\t * Preserves relative ratios of remaining components (sum = 1.0).\n\t */\n\tprivate static readonly QUALITY_WEIGHTS_NO_CS = {\n\t\ttypeDiversity: 0.375,\n\t\tverificationCoverage: 0.375,\n\t\tdepthEfficiency: 0.25,\n\t} as const;\n\n\t/**\n\t * Compute confidence signals from history context.\n\t *\n\t * @param history - All thoughts in the current session\n\t * @param branches - Map of branch IDs to their thought arrays\n\t * @returns Computed confidence signals reflecting reasoning quality\n\t */\n\tpublic computeConfidenceSignals(\n\t\thistory: ThoughtData[],\n\t\tbranches: Record<string, ThoughtData[]>\n\t): ConfidenceSignals {\n\t\tconst typeDistribution = _countByType(history);\n\t\tconst allConfidences = history\n\t\t\t.map((t) => t.confidence)\n\t\t\t.filter((c): c is number => c !== undefined);\n\n\t\t// Compute structural quality components\n\t\tconst structuralResult = this.computeStructuralQuality(\n\t\t\thistory,\n\t\t\tbranches,\n\t\t\ttypeDistribution,\n\t\t\tallConfidences\n\t\t);\n\n\t\treturn {\n\t\t\treasoning_depth: history.length,\n\t\t\trevision_count: history.filter((t) => t.is_revision).length,\n\t\t\tbranch_count: Object.keys(branches).length,\n\t\t\tthought_type_distribution: typeDistribution,\n\t\t\thas_hypothesis: history.some((t) => t.thought_type === 'hypothesis'),\n\t\t\thas_verification: history.some((t) => t.thought_type === 'verification'),\n\t\t\taverage_confidence:\n\t\t\t\tallConfidences.length > 0\n\t\t\t\t\t? allConfidences.reduce((a, b) => a + b, 0) / allConfidences.length\n\t\t\t\t\t: null,\n\t\t\t...(structuralResult !== null && {\n\t\t\t\tstructural_quality: structuralResult.score,\n\t\t\t\tquality_components: structuralResult.components,\n\t\t\t\tquality_components_raw: structuralResult.raw_components,\n\t\t\t}),\n\t\t};\n\t}\n\n\t/**\n\t * Compute the composite structural quality score and its components.\n\t *\n\t * Algorithm (all components floored at {@link FLOOR} = 0.01):\n\t * - `type_diversity` = Shannon entropy of thought_type distribution / log2(6)\n\t * - `verification_coverage` = verified_hypotheses / max(total_hypotheses, 1) (1.0 if none)\n\t * - `depth_efficiency` = max(chain_depth, branch_count + 1) / total_thoughts, clamped to 1.0\n\t * - `confidence_stability` = 1 - stddev(confidences), default 0.5 when empty, null when single value\n\t * - `structural_quality` = td^0.3 * vc^0.3 * de^0.2 * cs^0.2 (weighted geometric mean)\n\t *\n\t * Note: When confidence_stability is null (fewer than 2 confidence values), it is\n\t * excluded from the geometric mean and the remaining weights are redistributed\n\t * proportionally (td→0.375, vc→0.375, de→0.25).\n\t *\n\t * @returns Score + components, or `null` when history is empty\n\t */\n\tpublic computeStructuralQuality(\n\t\thistory: ThoughtData[],\n\t\tbranches: Record<string, ThoughtData[]>,\n\t\ttypeDistribution: Record<ThoughtType, number>,\n\t\tconfidences: number[]\n\t): StructuralQualityResult | null {\n\t\tif (history.length === 0) return null;\n\n\t\t// 1. type_diversity: Shannon entropy / log2(6)\n\t\tconst total = history.length;\n\t\tlet entropy = 0;\n\t\tfor (const type of ALL_THOUGHT_TYPES) {\n\t\t\tconst count = typeDistribution[type];\n\t\t\tif (count > 0) {\n\t\t\t\tconst pk = count / total;\n\t\t\t\tentropy -= pk * Math.log2(pk);\n\t\t\t}\n\t\t}\n\t\tconst rawTypeDiversity = entropy / Math.log2(6);\n\t\tconst typeDiversity = Math.max(rawTypeDiversity, FLOOR);\n\n\t\t// 2. verification_coverage: verified / total hypotheses (1.0 if none)\n\t\tconst hypotheses = history.filter((t) => t.thought_type === 'hypothesis');\n\t\tconst hypothesisIds = new Set(hypotheses.map((t) => t.hypothesis_id).filter(Boolean));\n\t\tconst verifiedIds = new Set(\n\t\t\thistory\n\t\t\t\t.filter((t) => t.thought_type === 'verification' && t.hypothesis_id)\n\t\t\t\t.map((t) => t.hypothesis_id)\n\t\t);\n\t\tconst rawVerificationCoverage =\n\t\t\thypothesisIds.size === 0\n\t\t\t\t? 1.0\n\t\t\t\t: [...hypothesisIds].filter((id) => verifiedIds.has(id)).length / hypothesisIds.size;\n\t\tconst verificationCoverage = Math.max(rawVerificationCoverage, FLOOR);\n\n\t\t// 3. depth_efficiency: max(chain_depth, branch_count + 1) / total_thoughts, clamped to 1.0\n\t\t// NOTE (Metis H3): Branching is desirable — treat branches as depth-equivalent.\n\t\tconst chainDepth = _computeChainDepth(history);\n\t\tconst branchCount = Object.keys(branches).length;\n\t\tconst effectiveDepth = Math.max(chainDepth, branchCount + 1);\n\t\tconst rawDepthEfficiency = Math.min(effectiveDepth / total, 1.0);\n\t\tconst depthEfficiency = Math.max(rawDepthEfficiency, FLOOR);\n\n\t\t// 4. confidence_stability: 1 - stddev(confidences), default 0.5\n\t\tconst confidenceStability = this.computeConfidenceStability(confidences);\n\t\tconst rawConfidenceStability = this.computeRawConfidenceStability(confidences);\n\n\t\tconst components: StructuralQualityResult['components'] = {\n\t\t\ttype_diversity: typeDiversity,\n\t\t\tverification_coverage: verificationCoverage,\n\t\t\tdepth_efficiency: depthEfficiency,\n\t\t\tconfidence_stability: confidenceStability,\n\t\t};\n\n\t\tconst rawComponents: StructuralQualityResult['raw_components'] = {\n\t\t\ttype_diversity: rawTypeDiversity,\n\t\t\tverification_coverage: rawVerificationCoverage,\n\t\t\tdepth_efficiency: rawDepthEfficiency,\n\t\t\tconfidence_stability: rawConfidenceStability,\n\t\t};\n\n\t\t// Weighted geometric mean: td^0.3 * vc^0.3 * de^0.2 * cs^0.2\n\t\t// When confidence_stability is null (n<2), exclude it from the geomean\n\t\t// and redistribute its weight proportionally to remaining components\n\t\t// (normalized td=0.375, vc=0.375, de=0.25)\n\t\tlet score: number;\n\t\tif (confidenceStability !== null) {\n\t\t\tconst w = SignalComputer.QUALITY_WEIGHTS;\n\t\t\tscore =\n\t\t\t\tMath.pow(typeDiversity, w.typeDiversity) *\n\t\t\t\tMath.pow(verificationCoverage, w.verificationCoverage) *\n\t\t\t\tMath.pow(depthEfficiency, w.depthEfficiency) *\n\t\t\t\tMath.pow(confidenceStability, w.confidenceStability);\n\t\t} else {\n\t\t\tconst w3 = SignalComputer.QUALITY_WEIGHTS_NO_CS;\n\t\t\tscore =\n\t\t\t\tMath.pow(typeDiversity, w3.typeDiversity) *\n\t\t\t\tMath.pow(verificationCoverage, w3.verificationCoverage) *\n\t\t\t\tMath.pow(depthEfficiency, w3.depthEfficiency);\n\t\t}\n\n\t\treturn { score, components, raw_components: rawComponents };\n\t}\n\n\t/**\n\t * Compute confidence stability: 1 - stddev(confidences).\n\t *\n\t * - Empty input → 0.5 (neutral default)\n\t * - Single value → null (insufficient data, excluded from structural quality)\n\t * - Otherwise → max(1 - stddev, FLOOR)\n\t */\n\tpublic computeConfidenceStability(confidences: number[]): number | null {\n\t\tif (confidences.length === 0) return 0.5;\n\t\tif (confidences.length === 1) return null;\n\n\t\tconst mean = confidences.reduce((a, b) => a + b, 0) / confidences.length;\n\t\tconst variance = confidences.reduce((sum, c) => sum + (c - mean) ** 2, 0) / confidences.length;\n\t\tconst stddev = Math.sqrt(variance);\n\t\treturn Math.max(1 - stddev, FLOOR);\n\t}\n\n\t/**\n\t * Compute raw (unfloored) confidence stability: 1 - stddev(confidences).\n\t *\n\t * - Empty input → 0.5 (neutral default)\n\t * - Single value → null (insufficient data)\n\t * - Otherwise → 1 - stddev (may be below FLOOR)\n\t */\n\tpublic computeRawConfidenceStability(confidences: number[]): number | null {\n\t\tif (confidences.length === 0) return 0.5;\n\t\tif (confidences.length === 1) return null;\n\n\t\tconst mean = confidences.reduce((a, b) => a + b, 0) / confidences.length;\n\t\tconst variance = confidences.reduce((sum, c) => sum + (c - mean) ** 2, 0) / confidences.length;\n\t\tconst stddev = Math.sqrt(variance);\n\t\treturn 1 - stddev;\n\t}\n}\n"],"names":["FLOOR","SignalComputer","history","branches","typeDistribution","_countByType","allConfidences","t","c","undefined","structuralResult","Object","a","b","confidences","total","entropy","type","ALL_THOUGHT_TYPES","count","pk","Math","rawTypeDiversity","typeDiversity","hypotheses","hypothesisIds","Set","Boolean","verifiedIds","rawVerificationCoverage","id","verificationCoverage","chainDepth","_computeChainDepth","branchCount","effectiveDepth","rawDepthEfficiency","depthEfficiency","confidenceStability","rawConfidenceStability","components","rawComponents","score","w","w3","mean","variance","sum","stddev"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"core/evaluator/SignalComputer.js","sources":["../../../src/core/evaluator/SignalComputer.ts"],"sourcesContent":["/**\n * Confidence signal computation for sequential thinking.\n *\n * Provides {@link SignalComputer} — a pure stateless service that computes\n * {@link ConfidenceSignals} (including the composite `structural_quality`\n * score and its components) from thought history and branch data.\n *\n * Extracted from `ThoughtEvaluator` as part of the evaluator decomposition.\n * All methods are pure: no side effects, no I/O, no internal state.\n *\n * @module core/evaluator/SignalComputer\n */\n\nimport type { ConfidenceSignals, ThoughtType } from '../reasoning.js';\nimport type { ThoughtData } from '../thought.js';\nimport { ALL_THOUGHT_TYPES, _computeChainDepth, _countByType } from './internals.js';\n\n/** Floor value applied to each quality component to prevent geometric mean collapse. */\nconst FLOOR = 0.01;\n\n/**\n * Round a numeric value to a fixed number of decimal places to mitigate\n * IEEE 754 floating-point accumulation errors (e.g. 0.9 + 0.8 averaging to\n * 0.8500000000000001 instead of 0.85).\n */\nfunction roundToPrecision(value: number, decimals: number = 10): number {\n\tconst factor = Math.pow(10, decimals);\n\treturn Math.round(value * factor) / factor;\n}\n\n/** Result of {@link SignalComputer.computeStructuralQuality}. */\ninterface StructuralQualityResult {\n\tscore: number;\n\tcomponents: {\n\t\ttype_diversity: number;\n\t\tverification_coverage: number;\n\t\tdepth_efficiency: number;\n\t\tconfidence_stability: number | null;\n\t};\n\traw_components: {\n\t\ttype_diversity: number;\n\t\tverification_coverage: number;\n\t\tdepth_efficiency: number;\n\t\tconfidence_stability: number | null;\n\t};\n}\n\n/**\n * Stateless service that computes {@link ConfidenceSignals} from thought history.\n *\n * @remarks\n * Pure computation — no side effects, no I/O, no internal state.\n * Safe to register as singleton or transient in DI.\n */\nexport class SignalComputer {\n\t/**\n\t * Structural quality weight configuration.\n\t *\n\t * Weights reflect the relative importance of each quality dimension:\n\t * - type_diversity (0.3): Shannon entropy of thought types — rewards reasoning that uses\n\t * multiple cognitive modes (hypothesis, verification, critique, synthesis) rather than\n\t * monotonic sequences. Based on information theory: higher entropy = more information.\n\t *\n\t * - verification_coverage (0.3): Ratio of verified to total hypotheses — rewards scientific\n\t * rigor. Hypotheses without verification are speculation; verification grounds reasoning.\n\t * Equal weight with diversity because unverified reasoning is a common failure mode.\n\t *\n\t * - depth_efficiency (0.2): Ratio of structural depth to total thoughts — rewards efficient\n\t * reasoning. Deep chains (many revisions/branches) are good; but only if proportionally\n\t * dense. Padding with low-value thoughts penalizes this metric.\n\t *\n\t * - confidence_stability (0.2): Low variance in self-assessed confidence — rewards calibrated\n\t * reasoning. Wildly fluctuating confidence suggests uncertainty about the approach.\n\t * Lower weight because confidence values are LLM self-reports (inherently noisy).\n\t *\n\t * Design rationale:\n\t * - Diversity + Verification (0.6) > Depth + Stability (0.4): Structural properties of the\n\t * reasoning DAG are more important than behavioral signals.\n\t * - All weights are positive (no dimension is penalizing).\n\t * - Weights sum to 1.0 for normalized scoring.\n\t * - When confidence_stability is unavailable (n<2), remaining weights redistribute\n\t * proportionally: td→0.375, vc→0.375, de→0.25 (preserving relative ratios).\n\t */\n\tprivate static readonly QUALITY_WEIGHTS = {\n\t\ttypeDiversity: 0.3,\n\t\tverificationCoverage: 0.3,\n\t\tdepthEfficiency: 0.2,\n\t\tconfidenceStability: 0.2,\n\t} as const;\n\n\t/**\n\t * Redistributed weights when confidence_stability is excluded (n<2).\n\t * Preserves relative ratios of remaining components (sum = 1.0).\n\t */\n\tprivate static readonly QUALITY_WEIGHTS_NO_CS = {\n\t\ttypeDiversity: 0.375,\n\t\tverificationCoverage: 0.375,\n\t\tdepthEfficiency: 0.25,\n\t} as const;\n\n\t/**\n\t * Compute confidence signals from history context.\n\t *\n\t * @param history - All thoughts in the current session\n\t * @param branches - Map of branch IDs to their thought arrays\n\t * @returns Computed confidence signals reflecting reasoning quality\n\t */\n\tpublic computeConfidenceSignals(\n\t\thistory: ThoughtData[],\n\t\tbranches: Record<string, ThoughtData[]>\n\t): ConfidenceSignals {\n\t\tconst typeDistribution = _countByType(history);\n\t\tconst allConfidences = history\n\t\t\t.map((t) => t.confidence)\n\t\t\t.filter((c): c is number => c !== undefined);\n\n\t\t// Compute structural quality components\n\t\tconst structuralResult = this.computeStructuralQuality(\n\t\t\thistory,\n\t\t\tbranches,\n\t\t\ttypeDistribution,\n\t\t\tallConfidences\n\t\t);\n\n\t\treturn {\n\t\t\treasoning_depth: history.length,\n\t\t\trevision_count: history.filter((t) => t.is_revision).length,\n\t\t\tbranch_count: Object.keys(branches).length,\n\t\t\tthought_type_distribution: typeDistribution,\n\t\t\thas_hypothesis: history.some((t) => t.thought_type === 'hypothesis'),\n\t\t\thas_verification: history.some((t) => t.thought_type === 'verification'),\n\t\t\taverage_confidence:\n\t\t\t\tallConfidences.length > 0\n\t\t\t\t\t? roundToPrecision(allConfidences.reduce((a, b) => a + b, 0) / allConfidences.length)\n\t\t\t\t\t: null,\n\t\t\t...(structuralResult !== null && {\n\t\t\t\tstructural_quality: structuralResult.score,\n\t\t\t\tquality_components: structuralResult.components,\n\t\t\t\tquality_components_raw: structuralResult.raw_components,\n\t\t\t}),\n\t\t};\n\t}\n\n\t/**\n\t * Compute the composite structural quality score and its components.\n\t *\n\t * Algorithm (all components floored at {@link FLOOR} = 0.01):\n\t * - `type_diversity` = Shannon entropy of thought_type distribution / log2(6)\n\t * - `verification_coverage` = verified_hypotheses / max(total_hypotheses, 1) (1.0 if none)\n\t * - `depth_efficiency` = max(chain_depth, branch_count + 1) / total_thoughts, clamped to 1.0\n\t * - `confidence_stability` = 1 - stddev(confidences), default 0.5 when empty, null when single value\n\t * - `structural_quality` = td^0.3 * vc^0.3 * de^0.2 * cs^0.2 (weighted geometric mean)\n\t *\n\t * Note: When confidence_stability is null (fewer than 2 confidence values), it is\n\t * excluded from the geometric mean and the remaining weights are redistributed\n\t * proportionally (td→0.375, vc→0.375, de→0.25).\n\t *\n\t * @returns Score + components, or `null` when history is empty\n\t */\n\tpublic computeStructuralQuality(\n\t\thistory: ThoughtData[],\n\t\tbranches: Record<string, ThoughtData[]>,\n\t\ttypeDistribution: Record<ThoughtType, number>,\n\t\tconfidences: number[]\n\t): StructuralQualityResult | null {\n\t\tif (history.length === 0) return null;\n\n\t\t// 1. type_diversity: Shannon entropy / log2(6)\n\t\tconst total = history.length;\n\t\tlet entropy = 0;\n\t\tfor (const type of ALL_THOUGHT_TYPES) {\n\t\t\tconst count = typeDistribution[type];\n\t\t\tif (count > 0) {\n\t\t\t\tconst pk = count / total;\n\t\t\t\tentropy -= pk * Math.log2(pk);\n\t\t\t}\n\t\t}\n\t\tconst rawTypeDiversity = entropy / Math.log2(6);\n\t\tconst typeDiversity = Math.max(rawTypeDiversity, FLOOR);\n\n\t\t// 2. verification_coverage: verified / total hypotheses (1.0 if none)\n\t\tconst hypotheses = history.filter((t) => t.thought_type === 'hypothesis');\n\t\tconst hypothesisIds = new Set(hypotheses.map((t) => t.hypothesis_id).filter(Boolean));\n\t\tconst verifiedIds = new Set(\n\t\t\thistory\n\t\t\t\t.filter((t) => t.thought_type === 'verification' && t.hypothesis_id)\n\t\t\t\t.map((t) => t.hypothesis_id)\n\t\t);\n\t\tconst rawVerificationCoverage =\n\t\t\thypothesisIds.size === 0\n\t\t\t\t? 1.0\n\t\t\t\t: [...hypothesisIds].filter((id) => verifiedIds.has(id)).length / hypothesisIds.size;\n\t\tconst verificationCoverage = Math.max(rawVerificationCoverage, FLOOR);\n\n\t\t// 3. depth_efficiency: max(chain_depth, branch_count + 1) / total_thoughts, clamped to 1.0\n\t\t// NOTE (Metis H3): Branching is desirable — treat branches as depth-equivalent.\n\t\tconst chainDepth = _computeChainDepth(history);\n\t\tconst branchCount = Object.keys(branches).length;\n\t\tconst effectiveDepth = Math.max(chainDepth, branchCount + 1);\n\t\tconst rawDepthEfficiency = Math.min(effectiveDepth / total, 1.0);\n\t\tconst depthEfficiency = Math.max(rawDepthEfficiency, FLOOR);\n\n\t\t// 4. confidence_stability: 1 - stddev(confidences), default 0.5\n\t\tconst confidenceStability = this.computeConfidenceStability(confidences);\n\t\tconst rawConfidenceStability = this.computeRawConfidenceStability(confidences);\n\n\t\tconst components: StructuralQualityResult['components'] = {\n\t\t\ttype_diversity: typeDiversity,\n\t\t\tverification_coverage: verificationCoverage,\n\t\t\tdepth_efficiency: depthEfficiency,\n\t\t\tconfidence_stability: confidenceStability,\n\t\t};\n\n\t\tconst rawComponents: StructuralQualityResult['raw_components'] = {\n\t\t\ttype_diversity: rawTypeDiversity,\n\t\t\tverification_coverage: rawVerificationCoverage,\n\t\t\tdepth_efficiency: rawDepthEfficiency,\n\t\t\tconfidence_stability: rawConfidenceStability,\n\t\t};\n\n\t\t// Weighted geometric mean: td^0.3 * vc^0.3 * de^0.2 * cs^0.2\n\t\t// When confidence_stability is null (n<2), exclude it from the geomean\n\t\t// and redistribute its weight proportionally to remaining components\n\t\t// (normalized td=0.375, vc=0.375, de=0.25)\n\t\tlet score: number;\n\t\tif (confidenceStability !== null) {\n\t\t\tconst w = SignalComputer.QUALITY_WEIGHTS;\n\t\t\tscore =\n\t\t\t\tMath.pow(typeDiversity, w.typeDiversity) *\n\t\t\t\tMath.pow(verificationCoverage, w.verificationCoverage) *\n\t\t\t\tMath.pow(depthEfficiency, w.depthEfficiency) *\n\t\t\t\tMath.pow(confidenceStability, w.confidenceStability);\n\t\t} else {\n\t\t\tconst w3 = SignalComputer.QUALITY_WEIGHTS_NO_CS;\n\t\t\tscore =\n\t\t\t\tMath.pow(typeDiversity, w3.typeDiversity) *\n\t\t\t\tMath.pow(verificationCoverage, w3.verificationCoverage) *\n\t\t\t\tMath.pow(depthEfficiency, w3.depthEfficiency);\n\t\t}\n\n\t\treturn { score, components, raw_components: rawComponents };\n\t}\n\n\t/**\n\t * Compute confidence stability: 1 - stddev(confidences).\n\t *\n\t * - Empty input → 0.5 (neutral default)\n\t * - Single value → null (insufficient data, excluded from structural quality)\n\t * - Otherwise → max(1 - stddev, FLOOR)\n\t */\n\tpublic computeConfidenceStability(confidences: number[]): number | null {\n\t\tif (confidences.length === 0) return 0.5;\n\t\tif (confidences.length === 1) return null;\n\n\t\tconst mean = confidences.reduce((a, b) => a + b, 0) / confidences.length;\n\t\tconst variance = confidences.reduce((sum, c) => sum + (c - mean) ** 2, 0) / confidences.length;\n\t\tconst stddev = Math.sqrt(variance);\n\t\treturn Math.max(1 - stddev, FLOOR);\n\t}\n\n\t/**\n\t * Compute raw (unfloored) confidence stability: 1 - stddev(confidences).\n\t *\n\t * - Empty input → 0.5 (neutral default)\n\t * - Single value → null (insufficient data)\n\t * - Otherwise → 1 - stddev (may be below FLOOR)\n\t */\n\tpublic computeRawConfidenceStability(confidences: number[]): number | null {\n\t\tif (confidences.length === 0) return 0.5;\n\t\tif (confidences.length === 1) return null;\n\n\t\tconst mean = confidences.reduce((a, b) => a + b, 0) / confidences.length;\n\t\tconst variance = confidences.reduce((sum, c) => sum + (c - mean) ** 2, 0) / confidences.length;\n\t\tconst stddev = Math.sqrt(variance);\n\t\treturn 1 - stddev;\n\t}\n}\n"],"names":["FLOOR","roundToPrecision","value","decimals","factor","Math","SignalComputer","history","branches","typeDistribution","_countByType","allConfidences","t","c","undefined","structuralResult","Object","a","b","confidences","total","entropy","type","ALL_THOUGHT_TYPES","count","pk","rawTypeDiversity","typeDiversity","hypotheses","hypothesisIds","Set","Boolean","verifiedIds","rawVerificationCoverage","id","verificationCoverage","chainDepth","_computeChainDepth","branchCount","effectiveDepth","rawDepthEfficiency","depthEfficiency","confidenceStability","rawConfidenceStability","components","rawComponents","score","w","w3","mean","variance","sum","stddev"],"mappings":";AAkBA,MAAMA,QAAQ;AAOd,SAASC,iBAAiBC,KAAa,EAAEC,WAAmB,EAAE;IAC7D,MAAMC,SAASC,KAAK,GAAG,CAAC,IAAIF;IAC5B,OAAOE,KAAK,KAAK,CAACH,QAAQE,UAAUA;AACrC;AA0BO,MAAME;IA6BZ,OAAwB,kBAAkB;QACzC,eAAe;QACf,sBAAsB;QACtB,iBAAiB;QACjB,qBAAqB;IACtB,EAAW;IAMX,OAAwB,wBAAwB;QAC/C,eAAe;QACf,sBAAsB;QACtB,iBAAiB;IAClB,EAAW;IASJ,yBACNC,OAAsB,EACtBC,QAAuC,EACnB;QACpB,MAAMC,mBAAmBC,aAAaH;QACtC,MAAMI,iBAAiBJ,QACrB,GAAG,CAAC,CAACK,IAAMA,EAAE,UAAU,EACvB,MAAM,CAAC,CAACC,IAAmBA,AAAMC,WAAND;QAG7B,MAAME,mBAAmB,IAAI,CAAC,wBAAwB,CACrDR,SACAC,UACAC,kBACAE;QAGD,OAAO;YACN,iBAAiBJ,QAAQ,MAAM;YAC/B,gBAAgBA,QAAQ,MAAM,CAAC,CAACK,IAAMA,EAAE,WAAW,EAAE,MAAM;YAC3D,cAAcI,OAAO,IAAI,CAACR,UAAU,MAAM;YAC1C,2BAA2BC;YAC3B,gBAAgBF,QAAQ,IAAI,CAAC,CAACK,IAAMA,AAAmB,iBAAnBA,EAAE,YAAY;YAClD,kBAAkBL,QAAQ,IAAI,CAAC,CAACK,IAAMA,AAAmB,mBAAnBA,EAAE,YAAY;YACpD,oBACCD,eAAe,MAAM,GAAG,IACrBV,iBAAiBU,eAAe,MAAM,CAAC,CAACM,GAAGC,IAAMD,IAAIC,GAAG,KAAKP,eAAe,MAAM,IAClF;YACJ,GAAII,AAAqB,SAArBA,oBAA6B;gBAChC,oBAAoBA,iBAAiB,KAAK;gBAC1C,oBAAoBA,iBAAiB,UAAU;gBAC/C,wBAAwBA,iBAAiB,cAAc;YACxD,CAAC;QACF;IACD;IAkBO,yBACNR,OAAsB,EACtBC,QAAuC,EACvCC,gBAA6C,EAC7CU,WAAqB,EACY;QACjC,IAAIZ,AAAmB,MAAnBA,QAAQ,MAAM,EAAQ,OAAO;QAGjC,MAAMa,QAAQb,QAAQ,MAAM;QAC5B,IAAIc,UAAU;QACd,KAAK,MAAMC,QAAQC,kBAAmB;YACrC,MAAMC,QAAQf,gBAAgB,CAACa,KAAK;YACpC,IAAIE,QAAQ,GAAG;gBACd,MAAMC,KAAKD,QAAQJ;gBACnBC,WAAWI,KAAKpB,KAAK,IAAI,CAACoB;YAC3B;QACD;QACA,MAAMC,mBAAmBL,UAAUhB,KAAK,IAAI,CAAC;QAC7C,MAAMsB,gBAAgBtB,KAAK,GAAG,CAACqB,kBAAkB1B;QAGjD,MAAM4B,aAAarB,QAAQ,MAAM,CAAC,CAACK,IAAMA,AAAmB,iBAAnBA,EAAE,YAAY;QACvD,MAAMiB,gBAAgB,IAAIC,IAAIF,WAAW,GAAG,CAAC,CAAChB,IAAMA,EAAE,aAAa,EAAE,MAAM,CAACmB;QAC5E,MAAMC,cAAc,IAAIF,IACvBvB,QACE,MAAM,CAAC,CAACK,IAAMA,AAAmB,mBAAnBA,EAAE,YAAY,IAAuBA,EAAE,aAAa,EAClE,GAAG,CAAC,CAACA,IAAMA,EAAE,aAAa;QAE7B,MAAMqB,0BACLJ,AAAuB,MAAvBA,cAAc,IAAI,GACf,MACA;eAAIA;SAAc,CAAC,MAAM,CAAC,CAACK,KAAOF,YAAY,GAAG,CAACE,KAAK,MAAM,GAAGL,cAAc,IAAI;QACtF,MAAMM,uBAAuB9B,KAAK,GAAG,CAAC4B,yBAAyBjC;QAI/D,MAAMoC,aAAaC,mBAAmB9B;QACtC,MAAM+B,cAActB,OAAO,IAAI,CAACR,UAAU,MAAM;QAChD,MAAM+B,iBAAiBlC,KAAK,GAAG,CAAC+B,YAAYE,cAAc;QAC1D,MAAME,qBAAqBnC,KAAK,GAAG,CAACkC,iBAAiBnB,OAAO;QAC5D,MAAMqB,kBAAkBpC,KAAK,GAAG,CAACmC,oBAAoBxC;QAGrD,MAAM0C,sBAAsB,IAAI,CAAC,0BAA0B,CAACvB;QAC5D,MAAMwB,yBAAyB,IAAI,CAAC,6BAA6B,CAACxB;QAElE,MAAMyB,aAAoD;YACzD,gBAAgBjB;YAChB,uBAAuBQ;YACvB,kBAAkBM;YAClB,sBAAsBC;QACvB;QAEA,MAAMG,gBAA2D;YAChE,gBAAgBnB;YAChB,uBAAuBO;YACvB,kBAAkBO;YAClB,sBAAsBG;QACvB;QAMA,IAAIG;QACJ,IAAIJ,AAAwB,SAAxBA,qBAA8B;YACjC,MAAMK,IAAIzC,eAAe,eAAe;YACxCwC,QACCzC,KAAK,GAAG,CAACsB,eAAeoB,EAAE,aAAa,IACvC1C,KAAK,GAAG,CAAC8B,sBAAsBY,EAAE,oBAAoB,IACrD1C,KAAK,GAAG,CAACoC,iBAAiBM,EAAE,eAAe,IAC3C1C,KAAK,GAAG,CAACqC,qBAAqBK,EAAE,mBAAmB;QACrD,OAAO;YACN,MAAMC,KAAK1C,eAAe,qBAAqB;YAC/CwC,QACCzC,KAAK,GAAG,CAACsB,eAAeqB,GAAG,aAAa,IACxC3C,KAAK,GAAG,CAAC8B,sBAAsBa,GAAG,oBAAoB,IACtD3C,KAAK,GAAG,CAACoC,iBAAiBO,GAAG,eAAe;QAC9C;QAEA,OAAO;YAAEF;YAAOF;YAAY,gBAAgBC;QAAc;IAC3D;IASO,2BAA2B1B,WAAqB,EAAiB;QACvE,IAAIA,AAAuB,MAAvBA,YAAY,MAAM,EAAQ,OAAO;QACrC,IAAIA,AAAuB,MAAvBA,YAAY,MAAM,EAAQ,OAAO;QAErC,MAAM8B,OAAO9B,YAAY,MAAM,CAAC,CAACF,GAAGC,IAAMD,IAAIC,GAAG,KAAKC,YAAY,MAAM;QACxE,MAAM+B,WAAW/B,YAAY,MAAM,CAAC,CAACgC,KAAKtC,IAAMsC,MAAOtC,AAAAA,CAAAA,IAAIoC,IAAG,KAAM,GAAG,KAAK9B,YAAY,MAAM;QAC9F,MAAMiC,SAAS/C,KAAK,IAAI,CAAC6C;QACzB,OAAO7C,KAAK,GAAG,CAAC,IAAI+C,QAAQpD;IAC7B;IASO,8BAA8BmB,WAAqB,EAAiB;QAC1E,IAAIA,AAAuB,MAAvBA,YAAY,MAAM,EAAQ,OAAO;QACrC,IAAIA,AAAuB,MAAvBA,YAAY,MAAM,EAAQ,OAAO;QAErC,MAAM8B,OAAO9B,YAAY,MAAM,CAAC,CAACF,GAAGC,IAAMD,IAAIC,GAAG,KAAKC,YAAY,MAAM;QACxE,MAAM+B,WAAW/B,YAAY,MAAM,CAAC,CAACgC,KAAKtC,IAAMsC,MAAOtC,AAAAA,CAAAA,IAAIoC,IAAG,KAAM,GAAG,KAAK9B,YAAY,MAAM;QAC9F,MAAMiC,SAAS/C,KAAK,IAAI,CAAC6C;QACzB,OAAO,IAAIE;IACZ;AACD"}
|
|
@@ -8,7 +8,7 @@ class EdgeEmitter {
|
|
|
8
8
|
_logger;
|
|
9
9
|
constructor(config){
|
|
10
10
|
this._edgeStore = config.edgeStore;
|
|
11
|
-
this._dagEdges = config.dagEdges ??
|
|
11
|
+
this._dagEdges = config.dagEdges ?? true;
|
|
12
12
|
this._defaultSessionId = config.defaultSessionId;
|
|
13
13
|
this._logger = config.logger ?? new NullLogger();
|
|
14
14
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core/graph/EdgeEmitter.js","sources":["../../../src/core/graph/EdgeEmitter.ts"],"sourcesContent":["/**\n * EdgeEmitter — emits DAG edges for thoughts based on their metadata.\n *\n * Stateless helper extracted from HistoryManager. Holds a reference to an\n * optional `IEdgeStore` and a feature flag (`dagEdges`) gating writes.\n *\n * @module EdgeEmitter\n */\n\nimport type { IEdgeStore } from '../../contracts/interfaces.js';\nimport { getErrorMessage } from '../../errors.js';\nimport type { Logger } from '../../logger/StructuredLogger.js';\nimport { NullLogger } from '../../logger/NullLogger.js';\nimport type { ThoughtData } from '../thought.js';\nimport type { Edge, EdgeKind } from './Edge.js';\nimport { generateUlid } from '../ids.js';\n\n/** Minimal session view needed for edge emission. */\nexport interface EdgeEmissionSession {\n\tthought_history: ThoughtData[];\n\tbranches: Record<string, ThoughtData[]>;\n}\n\n/** Configuration options for EdgeEmitter. */\nexport interface EdgeEmitterConfig {\n\tedgeStore?: IEdgeStore;\n\tdagEdges?: boolean;\n\tdefaultSessionId: string;\n\tlogger?: Logger;\n}\n\n/**\n * Emits DAG edges for thought relationships when an `IEdgeStore` is configured\n * and the `dagEdges` feature flag is enabled. No-ops otherwise.\n */\nexport class EdgeEmitter {\n\tprivate readonly _edgeStore?: IEdgeStore;\n\tprivate readonly _dagEdges: boolean;\n\tprivate readonly _defaultSessionId: string;\n\tprivate readonly _logger: Logger;\n\n\tconstructor(config: EdgeEmitterConfig) {\n\t\tthis._edgeStore = config.edgeStore;\n\t\tthis._dagEdges = config.dagEdges ?? false;\n\t\tthis._defaultSessionId = config.defaultSessionId;\n\t\tthis._logger = config.logger ?? new NullLogger();\n\t}\n\n\t/** Returns true when edge emission is active (store + flag both set). */\n\tpublic isEnabled(): boolean {\n\t\treturn this._edgeStore !== undefined && this._dagEdges;\n\t}\n\n\t/**\n\t * Emits DAG edges for a thought based on its metadata fields.\n\t *\n\t * Edge kinds (in priority order):\n\t * - branch: branch_from_thought + branch_id → parent.id → current.id\n\t * - merge: merge_from_thoughts → source.id → current.id (per source)\n\t * - verifies: verification_target + thought_type=verification → current.id → target.id\n\t * - critiques: verification_target + thought_type=critique → current.id → target.id\n\t * - derives_from: synthesis_sources → source.id → current.id (per source)\n\t * - revises: revises_thought → current.id → target.id\n\t * - tool_invocation: tool_observation with _resumedFrom → tool_call.id → current.id\n\t * - sequence: default chronological link from previous thought (if none of the above)\n\t */\n\tpublic emitEdgesForThought(session: EdgeEmissionSession, thought: ThoughtData): void {\n\t\tif (!this._edgeStore || !this._dagEdges) return;\n\t\tif (!thought.id) return;\n\n\t\tconst sessionId = thought.session_id ?? this._defaultSessionId;\n\t\tlet emittedRelational = false;\n\n\t\tif (thought.branch_from_thought !== undefined && thought.branch_id) {\n\t\t\tconst parentId = this.resolveThoughtId(session, thought.branch_from_thought);\n\t\t\tif (this._addEdgeIfValid(parentId, thought.id, 'branch', sessionId)) {\n\t\t\t\temittedRelational = true;\n\t\t\t}\n\t\t}\n\n\t\tif (thought.merge_from_thoughts?.length) {\n\t\t\tfor (const src of thought.merge_from_thoughts) {\n\t\t\t\tconst srcId = this.resolveThoughtId(session, src);\n\t\t\t\tif (this._addEdgeIfValid(srcId, thought.id, 'merge', sessionId)) {\n\t\t\t\t\temittedRelational = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (thought.verification_target !== undefined && thought.thought_type === 'verification') {\n\t\t\tconst targetId = this.resolveThoughtId(session, thought.verification_target);\n\t\t\tif (this._addEdgeIfValid(thought.id, targetId, 'verifies', sessionId)) {\n\t\t\t\temittedRelational = true;\n\t\t\t}\n\t\t}\n\n\t\tif (thought.verification_target !== undefined && thought.thought_type === 'critique') {\n\t\t\tconst targetId = this.resolveThoughtId(session, thought.verification_target);\n\t\t\tif (this._addEdgeIfValid(thought.id, targetId, 'critiques', sessionId)) {\n\t\t\t\temittedRelational = true;\n\t\t\t}\n\t\t}\n\n\t\tif (thought.synthesis_sources?.length) {\n\t\t\tfor (const src of thought.synthesis_sources) {\n\t\t\t\tconst srcId = this.resolveThoughtId(session, src);\n\t\t\t\tif (this._addEdgeIfValid(srcId, thought.id, 'derives_from', sessionId)) {\n\t\t\t\t\temittedRelational = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (thought.revises_thought !== undefined) {\n\t\t\tconst targetId = this.resolveThoughtId(session, thought.revises_thought);\n\t\t\tif (this._addEdgeIfValid(thought.id, targetId, 'revises', sessionId)) {\n\t\t\t\temittedRelational = true;\n\t\t\t}\n\t\t}\n\n\t\t// tool_invocation edge: tool_call → tool_observation\n\t\tif (thought.thought_type === 'tool_observation' && thought._resumedFrom !== undefined) {\n\t\t\tconst toolCallId = this.resolveThoughtId(session, thought._resumedFrom);\n\t\t\tconst meta: Record<string, unknown> = {};\n\t\t\tif (thought.tool_name !== undefined) meta.tool_name = thought.tool_name;\n\t\t\tif (\n\t\t\t\tthis._addEdgeIfValid(\n\t\t\t\t\ttoolCallId,\n\t\t\t\t\tthought.id,\n\t\t\t\t\t'tool_invocation',\n\t\t\t\t\tsessionId,\n\t\t\t\t\tObject.keys(meta).length > 0 ? meta : undefined\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\temittedRelational = true;\n\t\t\t}\n\t\t}\n\n\t\tif (!emittedRelational) {\n\t\t\t// Default: chronological sequence from previous thought (the one before current).\n\t\t\t// current was just pushed, so prev is at length - 2.\n\t\t\tconst history = session.thought_history;\n\t\t\tif (history.length >= 2) {\n\t\t\t\tconst prev = history[history.length - 2]!;\n\t\t\t\tif (prev.id) {\n\t\t\t\t\tthis._addEdgeIfValid(prev.id, thought.id, 'sequence', sessionId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Resolves a thought_number to its stable id within the given session.\n\t * Searches main history first, then branches.\n\t *\n\t * @returns The thought's id if found and non-empty, undefined otherwise\n\t */\n\tpublic resolveThoughtId(\n\t\tsession: EdgeEmissionSession,\n\t\tthoughtNumber: number\n\t): string | undefined {\n\t\tfor (const t of session.thought_history) {\n\t\t\tif (t.thought_number === thoughtNumber && typeof t.id === 'string' && t.id.length > 0) {\n\t\t\t\treturn t.id;\n\t\t\t}\n\t\t}\n\t\tfor (const branchThoughts of Object.values(session.branches)) {\n\t\t\tfor (const t of branchThoughts) {\n\t\t\t\tif (t.thought_number === thoughtNumber && typeof t.id === 'string' && t.id.length > 0) {\n\t\t\t\t\treturn t.id;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Adds an edge to the edge store if both endpoints are non-empty strings.\n\t * Returns true if added, false if skipped (missing endpoint).\n\t * Failures (e.g. self-edge) are caught and logged.\n\t */\n\tprivate _addEdgeIfValid(\n\t\tfrom: string | undefined,\n\t\tto: string | undefined,\n\t\tkind: EdgeKind,\n\t\tsessionId: string,\n\t\tmetadata?: Record<string, unknown>\n\t): boolean {\n\t\tif (!from || !to) {\n\t\t\tthis._logger.debug('Skipping edge: unresolved endpoint', {\n\t\t\t\tkind,\n\t\t\t\tfrom: from ?? null,\n\t\t\t\tto: to ?? null,\n\t\t\t});\n\t\t\treturn false;\n\t\t}\n\t\tconst edge: Edge = {\n\t\t\tid: generateUlid(),\n\t\t\tfrom,\n\t\t\tto,\n\t\t\tkind,\n\t\t\tsessionId,\n\t\t\tcreatedAt: Date.now(),\n\t\t\t...(metadata !== undefined ? { metadata } : {}),\n\t\t};\n\t\ttry {\n\t\t\tthis._edgeStore!.addEdge(edge);\n\t\t\treturn true;\n\t\t} catch (err) {\n\t\t\tthis._logger.info('Failed to add DAG edge', {\n\t\t\t\tkind,\n\t\t\t\terror: getErrorMessage(err),\n\t\t\t});\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"],"names":["EdgeEmitter","config","NullLogger","undefined","session","thought","sessionId","emittedRelational","parentId","src","srcId","targetId","toolCallId","meta","Object","history","prev","thoughtNumber","t","branchThoughts","from","to","kind","metadata","edge","generateUlid","Date","err","getErrorMessage"],"mappings":";;;AAmCO,MAAMA;IACK,WAAwB;IACxB,UAAmB;IACnB,kBAA0B;IAC1B,QAAgB;IAEjC,YAAYC,MAAyB,CAAE;QACtC,IAAI,CAAC,UAAU,GAAGA,OAAO,SAAS;QAClC,IAAI,CAAC,SAAS,GAAGA,OAAO,QAAQ,IAAI;QACpC,IAAI,CAAC,iBAAiB,GAAGA,OAAO,gBAAgB;QAChD,IAAI,CAAC,OAAO,GAAGA,OAAO,MAAM,IAAI,IAAIC;IACrC;IAGO,YAAqB;QAC3B,OAAO,AAAoBC,WAApB,IAAI,CAAC,UAAU,IAAkB,IAAI,CAAC,SAAS;IACvD;IAeO,oBAAoBC,OAA4B,EAAEC,OAAoB,EAAQ;QACpF,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;QACzC,IAAI,CAACA,QAAQ,EAAE,EAAE;QAEjB,MAAMC,YAAYD,QAAQ,UAAU,IAAI,IAAI,CAAC,iBAAiB;QAC9D,IAAIE,oBAAoB;QAExB,IAAIF,AAAgCF,WAAhCE,QAAQ,mBAAmB,IAAkBA,QAAQ,SAAS,EAAE;YACnE,MAAMG,WAAW,IAAI,CAAC,gBAAgB,CAACJ,SAASC,QAAQ,mBAAmB;YAC3E,IAAI,IAAI,CAAC,eAAe,CAACG,UAAUH,QAAQ,EAAE,EAAE,UAAUC,YACxDC,oBAAoB;QAEtB;QAEA,IAAIF,QAAQ,mBAAmB,EAAE,QAChC,KAAK,MAAMI,OAAOJ,QAAQ,mBAAmB,CAAE;YAC9C,MAAMK,QAAQ,IAAI,CAAC,gBAAgB,CAACN,SAASK;YAC7C,IAAI,IAAI,CAAC,eAAe,CAACC,OAAOL,QAAQ,EAAE,EAAE,SAASC,YACpDC,oBAAoB;QAEtB;QAGD,IAAIF,AAAgCF,WAAhCE,QAAQ,mBAAmB,IAAkBA,AAAyB,mBAAzBA,QAAQ,YAAY,EAAqB;YACzF,MAAMM,WAAW,IAAI,CAAC,gBAAgB,CAACP,SAASC,QAAQ,mBAAmB;YAC3E,IAAI,IAAI,CAAC,eAAe,CAACA,QAAQ,EAAE,EAAEM,UAAU,YAAYL,YAC1DC,oBAAoB;QAEtB;QAEA,IAAIF,AAAgCF,WAAhCE,QAAQ,mBAAmB,IAAkBA,AAAyB,eAAzBA,QAAQ,YAAY,EAAiB;YACrF,MAAMM,WAAW,IAAI,CAAC,gBAAgB,CAACP,SAASC,QAAQ,mBAAmB;YAC3E,IAAI,IAAI,CAAC,eAAe,CAACA,QAAQ,EAAE,EAAEM,UAAU,aAAaL,YAC3DC,oBAAoB;QAEtB;QAEA,IAAIF,QAAQ,iBAAiB,EAAE,QAC9B,KAAK,MAAMI,OAAOJ,QAAQ,iBAAiB,CAAE;YAC5C,MAAMK,QAAQ,IAAI,CAAC,gBAAgB,CAACN,SAASK;YAC7C,IAAI,IAAI,CAAC,eAAe,CAACC,OAAOL,QAAQ,EAAE,EAAE,gBAAgBC,YAC3DC,oBAAoB;QAEtB;QAGD,IAAIF,AAA4BF,WAA5BE,QAAQ,eAAe,EAAgB;YAC1C,MAAMM,WAAW,IAAI,CAAC,gBAAgB,CAACP,SAASC,QAAQ,eAAe;YACvE,IAAI,IAAI,CAAC,eAAe,CAACA,QAAQ,EAAE,EAAEM,UAAU,WAAWL,YACzDC,oBAAoB;QAEtB;QAGA,IAAIF,AAAyB,uBAAzBA,QAAQ,YAAY,IAA2BA,AAAyBF,WAAzBE,QAAQ,YAAY,EAAgB;YACtF,MAAMO,aAAa,IAAI,CAAC,gBAAgB,CAACR,SAASC,QAAQ,YAAY;YACtE,MAAMQ,OAAgC,CAAC;YACvC,IAAIR,AAAsBF,WAAtBE,QAAQ,SAAS,EAAgBQ,KAAK,SAAS,GAAGR,QAAQ,SAAS;YACvE,IACC,IAAI,CAAC,eAAe,CACnBO,YACAP,QAAQ,EAAE,EACV,mBACAC,WACAQ,OAAO,IAAI,CAACD,MAAM,MAAM,GAAG,IAAIA,OAAOV,SAGvCI,oBAAoB;QAEtB;QAEA,IAAI,CAACA,mBAAmB;YAGvB,MAAMQ,UAAUX,QAAQ,eAAe;YACvC,IAAIW,QAAQ,MAAM,IAAI,GAAG;gBACxB,MAAMC,OAAOD,OAAO,CAACA,QAAQ,MAAM,GAAG,EAAE;gBACxC,IAAIC,KAAK,EAAE,EACV,IAAI,CAAC,eAAe,CAACA,KAAK,EAAE,EAAEX,QAAQ,EAAE,EAAE,YAAYC;YAExD;QACD;IACD;IAQO,iBACNF,OAA4B,EAC5Ba,aAAqB,EACA;QACrB,KAAK,MAAMC,KAAKd,QAAQ,eAAe,CACtC,IAAIc,EAAE,cAAc,KAAKD,iBAAiB,AAAgB,YAAhB,OAAOC,EAAE,EAAE,IAAiBA,EAAE,EAAE,CAAC,MAAM,GAAG,GACnF,OAAOA,EAAE,EAAE;QAGb,KAAK,MAAMC,kBAAkBL,OAAO,MAAM,CAACV,QAAQ,QAAQ,EAC1D,KAAK,MAAMc,KAAKC,eACf,IAAID,EAAE,cAAc,KAAKD,iBAAiB,AAAgB,YAAhB,OAAOC,EAAE,EAAE,IAAiBA,EAAE,EAAE,CAAC,MAAM,GAAG,GACnF,OAAOA,EAAE,EAAE;IAKf;IAOQ,gBACPE,IAAwB,EACxBC,EAAsB,EACtBC,IAAc,EACdhB,SAAiB,EACjBiB,QAAkC,EACxB;QACV,IAAI,CAACH,QAAQ,CAACC,IAAI;YACjB,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,sCAAsC;gBACxDC;gBACA,MAAMF,QAAQ;gBACd,IAAIC,MAAM;YACX;YACA,OAAO;QACR;QACA,MAAMG,OAAa;YAClB,IAAIC;YACJL;YACAC;YACAC;YACAhB;YACA,WAAWoB,KAAK,GAAG;YACnB,GAAIH,AAAapB,WAAboB,WAAyB;gBAAEA;YAAS,IAAI,CAAC,CAAC;QAC/C;QACA,IAAI;YACH,IAAI,CAAC,UAAU,CAAE,OAAO,CAACC;YACzB,OAAO;QACR,EAAE,OAAOG,KAAK;YACb,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,0BAA0B;gBAC3CL;gBACA,OAAOM,gBAAgBD;YACxB;YACA,OAAO;QACR;IACD;AACD"}
|
|
1
|
+
{"version":3,"file":"core/graph/EdgeEmitter.js","sources":["../../../src/core/graph/EdgeEmitter.ts"],"sourcesContent":["/**\n * EdgeEmitter — emits DAG edges for thoughts based on their metadata.\n *\n * Stateless helper extracted from HistoryManager. Holds a reference to an\n * optional `IEdgeStore` and a feature flag (`dagEdges`) gating writes.\n *\n * @module EdgeEmitter\n */\n\nimport type { IEdgeStore } from '../../contracts/interfaces.js';\nimport { getErrorMessage } from '../../errors.js';\nimport type { Logger } from '../../logger/StructuredLogger.js';\nimport { NullLogger } from '../../logger/NullLogger.js';\nimport type { ThoughtData } from '../thought.js';\nimport type { Edge, EdgeKind } from './Edge.js';\nimport { generateUlid } from '../ids.js';\n\n/** Minimal session view needed for edge emission. */\nexport interface EdgeEmissionSession {\n\tthought_history: ThoughtData[];\n\tbranches: Record<string, ThoughtData[]>;\n}\n\n/** Configuration options for EdgeEmitter. */\nexport interface EdgeEmitterConfig {\n\tedgeStore?: IEdgeStore;\n\tdagEdges?: boolean;\n\tdefaultSessionId: string;\n\tlogger?: Logger;\n}\n\n/**\n * Emits DAG edges for thought relationships when an `IEdgeStore` is configured\n * and the `dagEdges` feature flag is enabled. No-ops otherwise.\n */\nexport class EdgeEmitter {\n\tprivate readonly _edgeStore?: IEdgeStore;\n\tprivate readonly _dagEdges: boolean;\n\tprivate readonly _defaultSessionId: string;\n\tprivate readonly _logger: Logger;\n\n\tconstructor(config: EdgeEmitterConfig) {\n\t\tthis._edgeStore = config.edgeStore;\n\t\tthis._dagEdges = config.dagEdges ?? true;\n\t\tthis._defaultSessionId = config.defaultSessionId;\n\t\tthis._logger = config.logger ?? new NullLogger();\n\t}\n\n\t/** Returns true when edge emission is active (store + flag both set). */\n\tpublic isEnabled(): boolean {\n\t\treturn this._edgeStore !== undefined && this._dagEdges;\n\t}\n\n\t/**\n\t * Emits DAG edges for a thought based on its metadata fields.\n\t *\n\t * Edge kinds (in priority order):\n\t * - branch: branch_from_thought + branch_id → parent.id → current.id\n\t * - merge: merge_from_thoughts → source.id → current.id (per source)\n\t * - verifies: verification_target + thought_type=verification → current.id → target.id\n\t * - critiques: verification_target + thought_type=critique → current.id → target.id\n\t * - derives_from: synthesis_sources → source.id → current.id (per source)\n\t * - revises: revises_thought → current.id → target.id\n\t * - tool_invocation: tool_observation with _resumedFrom → tool_call.id → current.id\n\t * - sequence: default chronological link from previous thought (if none of the above)\n\t */\n\tpublic emitEdgesForThought(session: EdgeEmissionSession, thought: ThoughtData): void {\n\t\tif (!this._edgeStore || !this._dagEdges) return;\n\t\tif (!thought.id) return;\n\n\t\tconst sessionId = thought.session_id ?? this._defaultSessionId;\n\t\tlet emittedRelational = false;\n\n\t\tif (thought.branch_from_thought !== undefined && thought.branch_id) {\n\t\t\tconst parentId = this.resolveThoughtId(session, thought.branch_from_thought);\n\t\t\tif (this._addEdgeIfValid(parentId, thought.id, 'branch', sessionId)) {\n\t\t\t\temittedRelational = true;\n\t\t\t}\n\t\t}\n\n\t\tif (thought.merge_from_thoughts?.length) {\n\t\t\tfor (const src of thought.merge_from_thoughts) {\n\t\t\t\tconst srcId = this.resolveThoughtId(session, src);\n\t\t\t\tif (this._addEdgeIfValid(srcId, thought.id, 'merge', sessionId)) {\n\t\t\t\t\temittedRelational = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (thought.verification_target !== undefined && thought.thought_type === 'verification') {\n\t\t\tconst targetId = this.resolveThoughtId(session, thought.verification_target);\n\t\t\tif (this._addEdgeIfValid(thought.id, targetId, 'verifies', sessionId)) {\n\t\t\t\temittedRelational = true;\n\t\t\t}\n\t\t}\n\n\t\tif (thought.verification_target !== undefined && thought.thought_type === 'critique') {\n\t\t\tconst targetId = this.resolveThoughtId(session, thought.verification_target);\n\t\t\tif (this._addEdgeIfValid(thought.id, targetId, 'critiques', sessionId)) {\n\t\t\t\temittedRelational = true;\n\t\t\t}\n\t\t}\n\n\t\tif (thought.synthesis_sources?.length) {\n\t\t\tfor (const src of thought.synthesis_sources) {\n\t\t\t\tconst srcId = this.resolveThoughtId(session, src);\n\t\t\t\tif (this._addEdgeIfValid(srcId, thought.id, 'derives_from', sessionId)) {\n\t\t\t\t\temittedRelational = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (thought.revises_thought !== undefined) {\n\t\t\tconst targetId = this.resolveThoughtId(session, thought.revises_thought);\n\t\t\tif (this._addEdgeIfValid(thought.id, targetId, 'revises', sessionId)) {\n\t\t\t\temittedRelational = true;\n\t\t\t}\n\t\t}\n\n\t\t// tool_invocation edge: tool_call → tool_observation\n\t\tif (thought.thought_type === 'tool_observation' && thought._resumedFrom !== undefined) {\n\t\t\tconst toolCallId = this.resolveThoughtId(session, thought._resumedFrom);\n\t\t\tconst meta: Record<string, unknown> = {};\n\t\t\tif (thought.tool_name !== undefined) meta.tool_name = thought.tool_name;\n\t\t\tif (\n\t\t\t\tthis._addEdgeIfValid(\n\t\t\t\t\ttoolCallId,\n\t\t\t\t\tthought.id,\n\t\t\t\t\t'tool_invocation',\n\t\t\t\t\tsessionId,\n\t\t\t\t\tObject.keys(meta).length > 0 ? meta : undefined\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\temittedRelational = true;\n\t\t\t}\n\t\t}\n\n\t\tif (!emittedRelational) {\n\t\t\t// Default: chronological sequence from previous thought (the one before current).\n\t\t\t// current was just pushed, so prev is at length - 2.\n\t\t\tconst history = session.thought_history;\n\t\t\tif (history.length >= 2) {\n\t\t\t\tconst prev = history[history.length - 2]!;\n\t\t\t\tif (prev.id) {\n\t\t\t\t\tthis._addEdgeIfValid(prev.id, thought.id, 'sequence', sessionId);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Resolves a thought_number to its stable id within the given session.\n\t * Searches main history first, then branches.\n\t *\n\t * @returns The thought's id if found and non-empty, undefined otherwise\n\t */\n\tpublic resolveThoughtId(\n\t\tsession: EdgeEmissionSession,\n\t\tthoughtNumber: number\n\t): string | undefined {\n\t\tfor (const t of session.thought_history) {\n\t\t\tif (t.thought_number === thoughtNumber && typeof t.id === 'string' && t.id.length > 0) {\n\t\t\t\treturn t.id;\n\t\t\t}\n\t\t}\n\t\tfor (const branchThoughts of Object.values(session.branches)) {\n\t\t\tfor (const t of branchThoughts) {\n\t\t\t\tif (t.thought_number === thoughtNumber && typeof t.id === 'string' && t.id.length > 0) {\n\t\t\t\t\treturn t.id;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Adds an edge to the edge store if both endpoints are non-empty strings.\n\t * Returns true if added, false if skipped (missing endpoint).\n\t * Failures (e.g. self-edge) are caught and logged.\n\t */\n\tprivate _addEdgeIfValid(\n\t\tfrom: string | undefined,\n\t\tto: string | undefined,\n\t\tkind: EdgeKind,\n\t\tsessionId: string,\n\t\tmetadata?: Record<string, unknown>\n\t): boolean {\n\t\tif (!from || !to) {\n\t\t\tthis._logger.debug('Skipping edge: unresolved endpoint', {\n\t\t\t\tkind,\n\t\t\t\tfrom: from ?? null,\n\t\t\t\tto: to ?? null,\n\t\t\t});\n\t\t\treturn false;\n\t\t}\n\t\tconst edge: Edge = {\n\t\t\tid: generateUlid(),\n\t\t\tfrom,\n\t\t\tto,\n\t\t\tkind,\n\t\t\tsessionId,\n\t\t\tcreatedAt: Date.now(),\n\t\t\t...(metadata !== undefined ? { metadata } : {}),\n\t\t};\n\t\ttry {\n\t\t\tthis._edgeStore!.addEdge(edge);\n\t\t\treturn true;\n\t\t} catch (err) {\n\t\t\tthis._logger.info('Failed to add DAG edge', {\n\t\t\t\tkind,\n\t\t\t\terror: getErrorMessage(err),\n\t\t\t});\n\t\t\treturn false;\n\t\t}\n\t}\n}\n"],"names":["EdgeEmitter","config","NullLogger","undefined","session","thought","sessionId","emittedRelational","parentId","src","srcId","targetId","toolCallId","meta","Object","history","prev","thoughtNumber","t","branchThoughts","from","to","kind","metadata","edge","generateUlid","Date","err","getErrorMessage"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core/tools/InMemorySuspensionStore.js","sources":["../../../src/core/tools/InMemorySuspensionStore.ts"],"sourcesContent":["/**\n * In-memory implementation of {@link ISuspensionStore}.\n *\n * Stores suspension records in process memory with optional background\n * sweeping of expired entries. Suitable for single-process deployments;\n * cluster setups should provide a shared backend.\n *\n * @module core/tools/InMemorySuspensionStore\n */\n\nimport type { ISuspensionStore, SuspensionRecord } from '../../contracts/suspension.js';\nimport { generateUlid } from '../ids.js';\n\n/**\n * Configuration for {@link InMemorySuspensionStore}.\n */\nexport interface InMemorySuspensionStoreConfig {\n\t/** Default TTL applied to records that omit `ttlMs`. Defaults to 60_000ms. */\n\tttlMs?: number;\n\t/** Sweep interval for the background expiry timer. Defaults to 60_000ms. */\n\tsweepIntervalMs?: number;\n\t/** Optional logger; reserved for future diagnostic output. */\n\tlogger?: unknown;\n}\n\nconst DEFAULT_TTL_MS =
|
|
1
|
+
{"version":3,"file":"core/tools/InMemorySuspensionStore.js","sources":["../../../src/core/tools/InMemorySuspensionStore.ts"],"sourcesContent":["/**\n * In-memory implementation of {@link ISuspensionStore}.\n *\n * Stores suspension records in process memory with optional background\n * sweeping of expired entries. Suitable for single-process deployments;\n * cluster setups should provide a shared backend.\n *\n * @module core/tools/InMemorySuspensionStore\n */\n\nimport type { ISuspensionStore, SuspensionRecord } from '../../contracts/suspension.js';\nimport { generateUlid } from '../ids.js';\n\n/**\n * Configuration for {@link InMemorySuspensionStore}.\n */\nexport interface InMemorySuspensionStoreConfig {\n\t/** Default TTL applied to records that omit `ttlMs`. Defaults to 60_000ms. */\n\tttlMs?: number;\n\t/** Sweep interval for the background expiry timer. Defaults to 60_000ms. */\n\tsweepIntervalMs?: number;\n\t/** Optional logger; reserved for future diagnostic output. */\n\tlogger?: unknown;\n}\n\nconst DEFAULT_TTL_MS = 300_000;\nconst DEFAULT_SWEEP_INTERVAL_MS = 60_000;\n\n/**\n * In-memory suspension store with TTL expiry and background sweep.\n *\n * @example\n * ```typescript\n * const store = new InMemorySuspensionStore({ ttlMs: 30_000 });\n * store.start();\n * const rec = store.suspend({\n * sessionId: 's1',\n * toolCallThoughtNumber: 3,\n * toolName: 'search',\n * toolArguments: { q: 'foo' },\n * expiresAt: 0,\n * });\n * const resumed = store.resume(rec.token);\n * store.stop();\n * ```\n */\nexport class InMemorySuspensionStore implements ISuspensionStore {\n\tprivate readonly _byToken: Map<string, SuspensionRecord> = new Map();\n\tprivate readonly _bySession: Map<string, Set<string>> = new Map();\n\tprivate readonly _ttlMs: number;\n\tprivate readonly _sweepIntervalMs: number;\n\tprivate _timer: ReturnType<typeof setInterval> | null = null;\n\n\tconstructor(config: InMemorySuspensionStoreConfig = {}) {\n\t\tthis._ttlMs = config.ttlMs ?? DEFAULT_TTL_MS;\n\t\tthis._sweepIntervalMs = config.sweepIntervalMs ?? DEFAULT_SWEEP_INTERVAL_MS;\n\t}\n\n\tsuspend(\n\t\trecord: Omit<SuspensionRecord, 'token' | 'createdAt'> & { ttlMs?: number },\n\t): SuspensionRecord {\n\t\tconst token = generateUlid();\n\t\tconst createdAt = Date.now();\n\t\tconst ttlMs = record.ttlMs ?? this._ttlMs;\n\t\tconst expiresAt = createdAt + ttlMs;\n\n\t\tconst full: SuspensionRecord = {\n\t\t\ttoken,\n\t\t\tsessionId: record.sessionId,\n\t\t\ttoolCallThoughtNumber: record.toolCallThoughtNumber,\n\t\t\ttoolName: record.toolName,\n\t\t\ttoolArguments: record.toolArguments,\n\t\t\tcreatedAt,\n\t\t\texpiresAt,\n\t\t};\n\n\t\tthis._byToken.set(token, full);\n\t\tlet bucket = this._bySession.get(full.sessionId);\n\t\tif (!bucket) {\n\t\t\tbucket = new Set();\n\t\t\tthis._bySession.set(full.sessionId, bucket);\n\t\t}\n\t\tbucket.add(token);\n\n\t\treturn full;\n\t}\n\n\tresume(token: string): SuspensionRecord | null {\n\t\tconst rec = this._byToken.get(token);\n\t\tif (!rec) return null;\n\t\tif (rec.expiresAt <= Date.now()) {\n\t\t\tthis._delete(token, rec.sessionId);\n\t\t\treturn null;\n\t\t}\n\t\tthis._delete(token, rec.sessionId);\n\t\treturn rec;\n\t}\n\n\tpeek(token: string): SuspensionRecord | null {\n\t\treturn this._byToken.get(token) ?? null;\n\t}\n\n\texpireOlderThan(now: number): number {\n\t\tlet removed = 0;\n\t\tfor (const [token, rec] of this._byToken) {\n\t\t\tif (rec.expiresAt <= now) {\n\t\t\t\tthis._delete(token, rec.sessionId);\n\t\t\t\tremoved++;\n\t\t\t}\n\t\t}\n\t\treturn removed;\n\t}\n\n\tclearSession(sessionId: string): void {\n\t\tconst bucket = this._bySession.get(sessionId);\n\t\tif (!bucket) return;\n\t\tfor (const token of bucket) {\n\t\t\tthis._byToken.delete(token);\n\t\t}\n\t\tthis._bySession.delete(sessionId);\n\t}\n\n\tsize(sessionId?: string): number {\n\t\tif (sessionId === undefined) return this._byToken.size;\n\t\treturn this._bySession.get(sessionId)?.size ?? 0;\n\t}\n\n\tstart(): void {\n\t\tif (this._timer !== null) return;\n\t\tthis._timer = setInterval(() => {\n\t\t\tthis.expireOlderThan(Date.now());\n\t\t}, this._sweepIntervalMs);\n\t\tthis._timer.unref?.();\n\t}\n\n\tstop(): void {\n\t\tif (this._timer === null) return;\n\t\tclearInterval(this._timer);\n\t\tthis._timer = null;\n\t}\n\n\tprivate _delete(token: string, sessionId: string): void {\n\t\tthis._byToken.delete(token);\n\t\tconst bucket = this._bySession.get(sessionId);\n\t\tif (bucket) {\n\t\t\tbucket.delete(token);\n\t\t\tif (bucket.size === 0) this._bySession.delete(sessionId);\n\t\t}\n\t}\n}\n"],"names":["DEFAULT_TTL_MS","DEFAULT_SWEEP_INTERVAL_MS","InMemorySuspensionStore","Map","config","record","token","generateUlid","createdAt","Date","ttlMs","expiresAt","full","bucket","Set","rec","now","removed","sessionId","undefined","setInterval","clearInterval"],"mappings":"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"}
|