tracelattice 1.3.0 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +2 -0
  2. package/dist/ServerConfig.d.ts +10 -11
  3. package/dist/ServerConfig.d.ts.map +1 -1
  4. package/dist/ServerConfig.js +7 -7
  5. package/dist/ServerConfig.js.map +1 -1
  6. package/dist/__tests__/core/retraction.test.d.ts +2 -0
  7. package/dist/__tests__/core/retraction.test.d.ts.map +1 -0
  8. package/dist/__tests__/helpers/factories.d.ts +2 -0
  9. package/dist/__tests__/helpers/factories.d.ts.map +1 -1
  10. package/dist/cli.js +6 -6
  11. package/dist/core/HistoryManager.d.ts +45 -523
  12. package/dist/core/HistoryManager.d.ts.map +1 -1
  13. package/dist/core/HistoryManager.js +101 -249
  14. package/dist/core/HistoryManager.js.map +1 -1
  15. package/dist/core/IHistoryManager.d.ts +17 -0
  16. package/dist/core/IHistoryManager.d.ts.map +1 -1
  17. package/dist/core/PersistenceBuffer.d.ts +110 -0
  18. package/dist/core/PersistenceBuffer.d.ts.map +1 -0
  19. package/dist/core/PersistenceBuffer.js +141 -0
  20. package/dist/core/PersistenceBuffer.js.map +1 -0
  21. package/dist/core/SessionManager.d.ts +58 -0
  22. package/dist/core/SessionManager.d.ts.map +1 -0
  23. package/dist/core/SessionManager.js +65 -0
  24. package/dist/core/SessionManager.js.map +1 -0
  25. package/dist/core/ThoughtEvaluator.d.ts.map +1 -1
  26. package/dist/core/ThoughtEvaluator.js +16 -4
  27. package/dist/core/ThoughtEvaluator.js.map +1 -1
  28. package/dist/core/ThoughtFormatter.d.ts.map +1 -1
  29. package/dist/core/ThoughtFormatter.js +2 -1
  30. package/dist/core/ThoughtFormatter.js.map +1 -1
  31. package/dist/core/ThoughtProcessor.d.ts +18 -0
  32. package/dist/core/ThoughtProcessor.d.ts.map +1 -1
  33. package/dist/core/ThoughtProcessor.js +47 -16
  34. package/dist/core/ThoughtProcessor.js.map +1 -1
  35. package/dist/core/evaluator/Aggregator.d.ts.map +1 -1
  36. package/dist/core/evaluator/Aggregator.js +6 -2
  37. package/dist/core/evaluator/Aggregator.js.map +1 -1
  38. package/dist/core/evaluator/PatternDetector.js +2 -2
  39. package/dist/core/evaluator/PatternDetector.js.map +1 -1
  40. package/dist/core/evaluator/SignalComputer.d.ts +57 -5
  41. package/dist/core/evaluator/SignalComputer.d.ts.map +1 -1
  42. package/dist/core/evaluator/SignalComputer.js +52 -10
  43. package/dist/core/evaluator/SignalComputer.js.map +1 -1
  44. package/dist/core/graph/EdgeEmitter.d.ts +64 -0
  45. package/dist/core/graph/EdgeEmitter.d.ts.map +1 -0
  46. package/dist/core/graph/EdgeEmitter.js +99 -0
  47. package/dist/core/graph/EdgeEmitter.js.map +1 -0
  48. package/dist/core/reasoning.d.ts +17 -2
  49. package/dist/core/reasoning.d.ts.map +1 -1
  50. package/dist/core/thought.d.ts +7 -0
  51. package/dist/core/thought.d.ts.map +1 -1
  52. package/dist/core/tools/InMemorySuspensionStore.js +1 -1
  53. package/dist/core/tools/InMemorySuspensionStore.js.map +1 -1
  54. package/dist/lib.d.ts.map +1 -1
  55. package/dist/lib.js +11 -0
  56. package/dist/lib.js.map +1 -1
  57. package/dist/persistence/FilePersistence.d.ts +6 -0
  58. package/dist/persistence/FilePersistence.d.ts.map +1 -1
  59. package/dist/persistence/FilePersistence.js +8 -0
  60. package/dist/persistence/FilePersistence.js.map +1 -1
  61. package/dist/persistence/MemoryPersistence.d.ts +6 -0
  62. package/dist/persistence/MemoryPersistence.d.ts.map +1 -1
  63. package/dist/persistence/MemoryPersistence.js +3 -0
  64. package/dist/persistence/MemoryPersistence.js.map +1 -1
  65. package/dist/persistence/PersistenceBackend.d.ts +6 -0
  66. package/dist/persistence/PersistenceBackend.d.ts.map +1 -1
  67. package/dist/persistence/SqlitePersistence.d.ts +6 -0
  68. package/dist/persistence/SqlitePersistence.d.ts.map +1 -1
  69. package/dist/persistence/SqlitePersistence.js +4 -0
  70. package/dist/persistence/SqlitePersistence.js.map +1 -1
  71. package/dist/schema.d.ts +3 -2
  72. package/dist/schema.d.ts.map +1 -1
  73. package/dist/schema.js +8 -7
  74. package/dist/schema.js.map +1 -1
  75. package/package.json +2 -2
@@ -1,14 +1,15 @@
1
- import { getErrorMessage } from "../errors.js";
2
- import { generateUlid } from "./ids.js";
1
+ import { ValidationError, getErrorMessage } from "../errors.js";
3
2
  import { NullLogger } from "../logger/NullLogger.js";
4
3
  import { DehydrationPolicy } from "./compression/DehydrationPolicy.js";
4
+ import { EdgeEmitter } from "./graph/EdgeEmitter.js";
5
+ import { PersistenceBuffer } from "./PersistenceBuffer.js";
6
+ import { SessionManager } from "./SessionManager.js";
5
7
  const ABSOLUTE_MAX_HISTORY_SIZE = 10000;
6
8
  class HistoryManager {
7
9
  static DEFAULT_SESSION = '__global__';
8
10
  static SESSION_TTL_MS = 1800000;
9
11
  static MAX_SESSIONS = 100;
10
12
  _sessions = new Map();
11
- _sessionCleanupTimer = null;
12
13
  _maxHistorySize;
13
14
  _maxBranches;
14
15
  _maxBranchSize;
@@ -16,19 +17,16 @@ class HistoryManager {
16
17
  _persistence;
17
18
  _persistenceEnabled;
18
19
  _metrics;
19
- _flushTimer = null;
20
- _isFlushing = false;
21
- _flushRetryCount = 0;
22
- _persistenceBufferSize;
23
- _persistenceFlushInterval;
24
- _persistenceMaxRetries;
25
- _eventEmitter;
26
20
  _edgeStore;
27
21
  _summaryStore;
28
22
  _dagEdges;
23
+ _eventEmitter;
24
+ _edgeEmitter;
25
+ _persistenceBuffer;
26
+ _sessionManager;
29
27
  constructor(config = {}){
30
28
  this._logger = config.logger ?? new NullLogger();
31
- const requestedMaxSize = config.maxHistorySize ?? 1000;
29
+ const requestedMaxSize = config.maxHistorySize ?? 10000;
32
30
  this._maxHistorySize = Math.min(requestedMaxSize, ABSOLUTE_MAX_HISTORY_SIZE);
33
31
  if (requestedMaxSize > ABSOLUTE_MAX_HISTORY_SIZE) this._logger.warn('maxHistorySize exceeds absolute maximum, capped', {
34
32
  requested: requestedMaxSize,
@@ -39,15 +37,53 @@ class HistoryManager {
39
37
  this._persistence = config.persistence ?? null;
40
38
  this._persistenceEnabled = null !== this._persistence;
41
39
  this._metrics = config.metrics;
42
- this._persistenceBufferSize = config.persistenceBufferSize ?? 100;
43
- this._persistenceFlushInterval = config.persistenceFlushInterval ?? 1000;
44
- this._persistenceMaxRetries = config.persistenceMaxRetries ?? 3;
45
40
  this._eventEmitter = config.eventEmitter ?? null;
46
41
  this._edgeStore = config.edgeStore;
47
42
  this._summaryStore = config.summaryStore;
48
- this._dagEdges = config.dagEdges ?? false;
49
- if (this._persistenceEnabled) this._startFlushTimer();
50
- this._startSessionCleanupTimer();
43
+ this._dagEdges = config.dagEdges ?? true;
44
+ this._edgeEmitter = new EdgeEmitter({
45
+ edgeStore: this._edgeStore,
46
+ dagEdges: this._dagEdges,
47
+ defaultSessionId: HistoryManager.DEFAULT_SESSION,
48
+ logger: this._logger
49
+ });
50
+ this._sessionManager = new SessionManager({
51
+ defaultSessionId: HistoryManager.DEFAULT_SESSION,
52
+ sessionTtlMs: HistoryManager.SESSION_TTL_MS,
53
+ cleanupIntervalMs: 300000,
54
+ getMaxSessions: ()=>HistoryManager.MAX_SESSIONS,
55
+ logger: this._logger
56
+ });
57
+ this._persistenceBuffer = null;
58
+ if (this._persistenceEnabled && this._persistence) {
59
+ this._persistenceBuffer = new PersistenceBuffer({
60
+ persistence: this._persistence,
61
+ bufferSize: config.persistenceBufferSize ?? 100,
62
+ flushInterval: config.persistenceFlushInterval ?? 1000,
63
+ maxRetries: config.persistenceMaxRetries ?? 3,
64
+ defaultSessionId: HistoryManager.DEFAULT_SESSION,
65
+ getSessions: ()=>this._sessions,
66
+ getDefaultSession: ()=>this._getSession(),
67
+ edgeStore: this._edgeStore,
68
+ eventEmitter: this._eventEmitter,
69
+ logger: this._logger
70
+ });
71
+ this._startFlushTimer();
72
+ }
73
+ this._sessionManager.startCleanupTimer(this._sessions);
74
+ }
75
+ get _flushTimer() {
76
+ return this._persistenceBuffer?.timer ?? null;
77
+ }
78
+ _startFlushTimer() {
79
+ this._persistenceBuffer?.startFlushTimer();
80
+ }
81
+ _stopFlushTimer() {
82
+ if (null === this._flushTimer) return;
83
+ this._persistenceBuffer?.stopFlushTimer();
84
+ }
85
+ async _flushBuffer() {
86
+ await this._persistenceBuffer?.flush();
51
87
  }
52
88
  getEdgeStore() {
53
89
  return this._edgeStore;
@@ -65,10 +101,11 @@ class HistoryManager {
65
101
  availableMcpTools: void 0,
66
102
  availableSkills: void 0,
67
103
  writeBuffer: [],
68
- lastAccessedAt: Date.now()
104
+ lastAccessedAt: Date.now(),
105
+ registeredBranches: new Set()
69
106
  };
70
107
  this._sessions.set(key, session);
71
- this._evictExcessSessions();
108
+ this._sessionManager.evictExcessSessions(this._sessions);
72
109
  }
73
110
  session.lastAccessedAt = Date.now();
74
111
  return session;
@@ -77,6 +114,7 @@ class HistoryManager {
77
114
  const session = this._getSession(thought.session_id);
78
115
  this._metrics?.counter('thought_requests_total', 1, {}, 'Total thought requests added to history');
79
116
  session.thought_history.push(thought);
117
+ if ('backtrack' === thought.thought_type && void 0 !== thought.backtrack_target) this._applyRetraction(session, thought.backtrack_target);
80
118
  if (thought.available_mcp_tools) session.availableMcpTools = thought.available_mcp_tools;
81
119
  if (thought.available_skills) session.availableSkills = thought.available_skills;
82
120
  if (session.thought_history.length > this._maxHistorySize) {
@@ -87,95 +125,19 @@ class HistoryManager {
87
125
  }
88
126
  if (thought.branch_from_thought && thought.branch_id) this._addToSessionBranch(session, thought.branch_id, thought);
89
127
  if (thought.merge_from_thoughts?.length || thought.merge_branch_ids?.length) this._metrics?.counter('thought_merge_operations_total', 1, {}, 'Total merge operations (graph topology)');
90
- this._emitEdgesForThought(session, thought);
91
- this._bufferForPersistence(session, thought);
92
- }
93
- _emitEdgesForThought(session, thought) {
94
- if (!this._edgeStore || !this._dagEdges) return;
95
- if (!thought.id) return;
96
- const sessionId = thought.session_id ?? HistoryManager.DEFAULT_SESSION;
97
- let emittedRelational = false;
98
- if (void 0 !== thought.branch_from_thought && thought.branch_id) {
99
- const parentId = this._resolveThoughtId(session, thought.branch_from_thought);
100
- if (this._addEdgeIfValid(parentId, thought.id, 'branch', sessionId)) emittedRelational = true;
101
- }
102
- if (thought.merge_from_thoughts?.length) for (const src of thought.merge_from_thoughts){
103
- const srcId = this._resolveThoughtId(session, src);
104
- if (this._addEdgeIfValid(srcId, thought.id, 'merge', sessionId)) emittedRelational = true;
105
- }
106
- if (void 0 !== thought.verification_target && 'verification' === thought.thought_type) {
107
- const targetId = this._resolveThoughtId(session, thought.verification_target);
108
- if (this._addEdgeIfValid(thought.id, targetId, 'verifies', sessionId)) emittedRelational = true;
109
- }
110
- if (void 0 !== thought.verification_target && 'critique' === thought.thought_type) {
111
- const targetId = this._resolveThoughtId(session, thought.verification_target);
112
- if (this._addEdgeIfValid(thought.id, targetId, 'critiques', sessionId)) emittedRelational = true;
113
- }
114
- if (thought.synthesis_sources?.length) for (const src of thought.synthesis_sources){
115
- const srcId = this._resolveThoughtId(session, src);
116
- if (this._addEdgeIfValid(srcId, thought.id, 'derives_from', sessionId)) emittedRelational = true;
117
- }
118
- if (void 0 !== thought.revises_thought) {
119
- const targetId = this._resolveThoughtId(session, thought.revises_thought);
120
- if (this._addEdgeIfValid(thought.id, targetId, 'revises', sessionId)) emittedRelational = true;
121
- }
122
- if ('tool_observation' === thought.thought_type && void 0 !== thought._resumedFrom) {
123
- const toolCallId = this._resolveThoughtId(session, thought._resumedFrom);
124
- const meta = {};
125
- if (void 0 !== thought.tool_name) meta.tool_name = thought.tool_name;
126
- if (this._addEdgeIfValid(toolCallId, thought.id, 'tool_invocation', sessionId, Object.keys(meta).length > 0 ? meta : void 0)) emittedRelational = true;
127
- }
128
- if (!emittedRelational) {
129
- const history = session.thought_history;
130
- if (history.length >= 2) {
131
- const prev = history[history.length - 2];
132
- if (prev.id) this._addEdgeIfValid(prev.id, thought.id, 'sequence', sessionId);
133
- }
134
- }
128
+ this._edgeEmitter.emitEdgesForThought(session, thought);
129
+ if (this._persistenceBuffer) this._persistenceBuffer.bufferThought(session, thought);
135
130
  }
136
- _resolveThoughtId(session, thoughtNumber) {
137
- for (const t of session.thought_history)if (t.thought_number === thoughtNumber && 'string' == typeof t.id && t.id.length > 0) return t.id;
138
- }
139
- _addEdgeIfValid(from, to, kind, sessionId, metadata) {
140
- if (!from || !to) {
141
- this._logger.debug('Skipping edge: unresolved endpoint', {
142
- kind,
143
- from: from ?? null,
144
- to: to ?? null
145
- });
146
- return false;
131
+ _applyRetraction(session, targetNumber) {
132
+ for (const t of session.thought_history)if (t.thought_number === targetNumber) {
133
+ t.retracted = true;
134
+ return;
147
135
  }
148
- const edge = {
149
- id: generateUlid(),
150
- from,
151
- to,
152
- kind,
153
- sessionId,
154
- createdAt: Date.now(),
155
- ...void 0 !== metadata ? {
156
- metadata
157
- } : {}
158
- };
159
- try {
160
- this._edgeStore.addEdge(edge);
161
- return true;
162
- } catch (err) {
163
- this.log('Failed to add DAG edge', {
164
- kind,
165
- error: getErrorMessage(err)
166
- });
167
- return false;
136
+ for (const branchThoughts of Object.values(session.branches))for (const t of branchThoughts)if (t.thought_number === targetNumber) {
137
+ t.retracted = true;
138
+ return;
168
139
  }
169
140
  }
170
- _bufferForPersistence(session, thought) {
171
- if (!this._persistenceEnabled || !this._persistence) return;
172
- if (session.writeBuffer.length >= this._persistenceBufferSize && this._isFlushing) this.log('Write buffer full and flush in progress, applying backpressure', {
173
- bufferSize: session.writeBuffer.length,
174
- maxSize: this._persistenceBufferSize
175
- });
176
- session.writeBuffer.push(thought);
177
- if (session.writeBuffer.length >= this._persistenceBufferSize) this._flushBuffer();
178
- }
179
141
  _addToSessionBranch(session, branchId, thought) {
180
142
  if (!session.branches[branchId]) session.branches[branchId] = [];
181
143
  this._trimSessionBranchSize(session, branchId);
@@ -227,7 +189,24 @@ class HistoryManager {
227
189
  return this._getSession(sessionId).branches;
228
190
  }
229
191
  getBranchIds(sessionId) {
230
- return Object.keys(this._getSession(sessionId).branches);
192
+ const session = this._getSession(sessionId);
193
+ const ids = new Set(Object.keys(session.branches));
194
+ for (const id of session.registeredBranches)ids.add(id);
195
+ return Array.from(ids);
196
+ }
197
+ registerBranch(sessionId, branchId) {
198
+ if ('string' != typeof branchId || 0 === branchId.length) throw new ValidationError('branch_id', 'branch_id must be a non-empty string');
199
+ const session = this._getSession(sessionId);
200
+ if (branchId in session.branches || session.registeredBranches.has(branchId)) throw new ValidationError('branch_id', `Branch already exists: ${branchId}`);
201
+ session.registeredBranches.add(branchId);
202
+ this.log('Registered branch', {
203
+ branchId,
204
+ sessionId: sessionId ?? null
205
+ });
206
+ }
207
+ branchExists(sessionId, branchId) {
208
+ const session = this._getSession(sessionId);
209
+ return branchId in session.branches || session.registeredBranches.has(branchId);
231
210
  }
232
211
  getAvailableMcpTools(sessionId) {
233
212
  return this._getSession(sessionId).availableMcpTools;
@@ -286,16 +265,22 @@ class HistoryManager {
286
265
  }
287
266
  this.log(`Loaded ${Object.keys(globalSession.branches).length} branches from persistence`);
288
267
  if (this._edgeStore) try {
289
- const globalEdges = await this._persistence.loadEdges(HistoryManager.DEFAULT_SESSION);
290
- for (const edge of globalEdges)try {
291
- this._edgeStore.addEdge(edge);
292
- } catch (edgeErr) {
293
- this.log('Failed to restore edge', {
294
- edgeId: edge.id,
295
- error: getErrorMessage(edgeErr)
296
- });
268
+ const edgeSessions = await this._persistence.listEdgeSessions();
269
+ let totalEdges = 0;
270
+ for (const sessionId of edgeSessions){
271
+ const edges = await this._persistence.loadEdges(sessionId);
272
+ for (const edge of edges)try {
273
+ this._edgeStore.addEdge(edge);
274
+ totalEdges++;
275
+ } catch (edgeErr) {
276
+ this.log('Failed to restore edge', {
277
+ edgeId: edge.id,
278
+ sessionId,
279
+ error: getErrorMessage(edgeErr)
280
+ });
281
+ }
297
282
  }
298
- this.log(`Loaded ${globalEdges.length} edges for global session from persistence`);
283
+ this.log(`Loaded ${totalEdges} edges across ${edgeSessions.length} sessions from persistence`);
299
284
  } catch (edgeError) {
300
285
  this.log('Failed to load edges from persistence', {
301
286
  error: getErrorMessage(edgeError)
@@ -315,146 +300,13 @@ class HistoryManager {
315
300
  }
316
301
  setEventEmitter(emitter) {
317
302
  this._eventEmitter = emitter;
303
+ this._persistenceBuffer?.setEventEmitter(emitter);
318
304
  }
319
305
  async shutdown() {
320
306
  this._stopFlushTimer();
321
- this._stopSessionCleanupTimer();
307
+ this._sessionManager.stopCleanupTimer();
322
308
  await this._flushBuffer();
323
309
  }
324
- _startFlushTimer() {
325
- if (null !== this._flushTimer) return;
326
- this._flushTimer = setInterval(()=>{
327
- this._flushBuffer();
328
- }, this._persistenceFlushInterval);
329
- if (this._flushTimer && 'object' == typeof this._flushTimer && 'unref' in this._flushTimer) this._flushTimer.unref();
330
- }
331
- _stopFlushTimer() {
332
- if (null !== this._flushTimer) {
333
- clearInterval(this._flushTimer);
334
- this._flushTimer = null;
335
- }
336
- }
337
- _startSessionCleanupTimer() {
338
- if (null !== this._sessionCleanupTimer) return;
339
- this._sessionCleanupTimer = setInterval(()=>{
340
- this._cleanupStaleSessions();
341
- }, 300000);
342
- if (this._sessionCleanupTimer && 'object' == typeof this._sessionCleanupTimer && 'unref' in this._sessionCleanupTimer) this._sessionCleanupTimer.unref();
343
- }
344
- _stopSessionCleanupTimer() {
345
- if (null !== this._sessionCleanupTimer) {
346
- clearInterval(this._sessionCleanupTimer);
347
- this._sessionCleanupTimer = null;
348
- }
349
- }
350
- _cleanupStaleSessions() {
351
- const now = Date.now();
352
- for (const [key, session] of this._sessions)if (key !== HistoryManager.DEFAULT_SESSION) {
353
- if (now - session.lastAccessedAt > HistoryManager.SESSION_TTL_MS) {
354
- this._sessions.delete(key);
355
- this.log('Evicted stale session', {
356
- sessionId: key
357
- });
358
- }
359
- }
360
- }
361
- _evictExcessSessions() {
362
- while(this._sessions.size > HistoryManager.MAX_SESSIONS){
363
- let oldestKey = null;
364
- let oldestTime = 1 / 0;
365
- for (const [key, session] of this._sessions)if (key !== HistoryManager.DEFAULT_SESSION) {
366
- if (session.lastAccessedAt < oldestTime) {
367
- oldestTime = session.lastAccessedAt;
368
- oldestKey = key;
369
- }
370
- }
371
- if (null !== oldestKey) {
372
- this._sessions.delete(oldestKey);
373
- this.log('Evicted oldest session (LRU)', {
374
- sessionId: oldestKey
375
- });
376
- } else break;
377
- }
378
- }
379
- async _flushBuffer() {
380
- if (this._isFlushing || !this._persistence) return;
381
- const allPending = [];
382
- for (const session of this._sessions.values())if (session.writeBuffer.length > 0) allPending.push(...session.writeBuffer.splice(0));
383
- if (0 === allPending.length) return;
384
- this._isFlushing = true;
385
- const failedItems = [];
386
- try {
387
- for (const thought of allPending){
388
- const saved = await this._flushSingleThought(thought);
389
- if (!saved) failedItems.push(thought);
390
- }
391
- this._handleFlushResult(failedItems, allPending.length);
392
- if (this._edgeStore) await this._flushEdges();
393
- } finally{
394
- this._isFlushing = false;
395
- }
396
- }
397
- async _flushEdges() {
398
- if (!this._edgeStore || !this._persistence) return;
399
- const sessionKeys = new Set(this._sessions.keys());
400
- sessionKeys.add(HistoryManager.DEFAULT_SESSION);
401
- for (const sessionId of sessionKeys){
402
- const edges = this._edgeStore.edgesForSession(sessionId);
403
- if (0 !== edges.length) try {
404
- await this._persistence.saveEdges(sessionId, edges);
405
- } catch (err) {
406
- this.log('Failed to persist edges for session', {
407
- sessionId,
408
- error: getErrorMessage(err)
409
- });
410
- }
411
- }
412
- }
413
- async _flushSingleThought(thought) {
414
- const backoffDelays = [
415
- 100,
416
- 500,
417
- 2000
418
- ];
419
- for(let attempt = 0; attempt <= this._persistenceMaxRetries; attempt++)try {
420
- await this._persistence.saveThought(thought);
421
- return true;
422
- } catch (err) {
423
- if (attempt < this._persistenceMaxRetries) {
424
- const delay = backoffDelays[attempt] ?? backoffDelays[backoffDelays.length - 1];
425
- this.log(`Persistence retry ${attempt + 1}/${this._persistenceMaxRetries}`, {
426
- thoughtNumber: thought.thought_number,
427
- delay,
428
- error: getErrorMessage(err)
429
- });
430
- await this._delay(delay);
431
- } else this.log('All persistence retries exhausted for thought', {
432
- thoughtNumber: thought.thought_number,
433
- error: getErrorMessage(err)
434
- });
435
- }
436
- return false;
437
- }
438
- _handleFlushResult(failedItems, totalCount) {
439
- if (failedItems.length > 0) {
440
- const globalSession = this._getSession();
441
- globalSession.writeBuffer.unshift(...failedItems);
442
- this._flushRetryCount++;
443
- const error = new Error(`Failed to persist ${failedItems.length} thoughts after ${this._persistenceMaxRetries} retries`);
444
- this._eventEmitter?.emit('persistenceError', {
445
- operation: 'flushBuffer',
446
- error
447
- });
448
- this.log('Flush completed with failures', {
449
- failed: failedItems.length,
450
- total: totalCount,
451
- consecutiveFailures: this._flushRetryCount
452
- });
453
- } else this._flushRetryCount = 0;
454
- }
455
- _delay(ms) {
456
- return new Promise((resolve)=>setTimeout(resolve, ms));
457
- }
458
310
  getWriteBufferLength() {
459
311
  let total = 0;
460
312
  for (const session of this._sessions.values())total += session.writeBuffer.length;
@@ -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 * @module HistoryManager\n */\n\nimport type { IMetrics } from '../contracts/index.js';\nimport { getErrorMessage } from '../errors.js';\nimport type { IEdgeStore } from '../contracts/interfaces.js';\nimport type { ISummaryStore } from '../contracts/summary.js';\nimport type { Edge, EdgeKind } from './graph/Edge.js';\nimport { generateUlid } from './ids.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';\n\n/**\n * Absolute maximum history size. Cannot be overridden by configuration.\n * Prevents unbounded memory growth from misconfiguration.\n * At ~2KB per thought, 10K thoughts ≈ ~20MB — reasonable for server-side.\n * @constant\n */\nexport const ABSOLUTE_MAX_HISTORY_SIZE = 10_000;\n\n/**\n * Interface for emitting persistence error events.\n * Compatible with EventEmitter's emit method signature.\n */\nexport interface PersistenceEventEmitter {\n\temit(event: 'persistenceError', payload: { operation: string; error: Error }): boolean;\n}\n\n/** Internal state container for a single session's data. */\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}\n\n/**\n * Configuration options for creating a `HistoryManager` instance.\n *\n * @example\n * ```typescript\n * const config: HistoryManagerConfig = {\n * maxHistorySize: 500,\n * maxBranches: 25,\n * maxBranchSize: 50,\n * logger: new StructuredLogger(),\n * persistence: filePersistence\n * };\n * ```\n */\nexport interface HistoryManagerConfig {\n\t/**\n\t * Maximum number of thoughts to keep in main 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/** Optional logger for diagnostics. */\n\tlogger?: Logger;\n\n\t/** Optional persistence backend for saving/loading history. */\n\tpersistence?: PersistenceBackend | null;\n\tmetrics?: IMetrics;\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 * Event emitter for persistence error events.\n\t * When provided, persistenceError events are emitted on persistent failures.\n\t */\n\teventEmitter?: PersistenceEventEmitter;\n\n\t/**\n\t * Optional EdgeStore for capturing thought DAG edges.\n\t * When provided, edge emission strategies can record relationships between thoughts.\n\t * @default undefined\n\t */\n\tedgeStore?: IEdgeStore;\n\n\t/**\n\t * Optional summary store for dehydrated history views.\n\t * When provided alongside the `dagEdges` flag, `getHistoryHydrated()`\n\t * uses it to replace cold thoughts with summary references.\n\t * @default undefined\n\t */\n\tsummaryStore?: ISummaryStore;\n\n\t/**\n\t * Whether to emit DAG edges when an EdgeStore is provided.\n\t * When false (default), no edges are emitted even if edgeStore is set.\n\t * Gated independently from edgeStore so that EdgeStore can be registered\n\t * unconditionally in DI while writes remain feature-flagged.\n\t * @default false\n\t */\n\tdagEdges?: boolean;\n}\n\n/**\n * Manages thought history and branching for sequential thinking.\n *\n * This class is the central component for managing the state of sequential thinking\n * operations. It handles thought storage, branch management, and optional persistence\n * for state recovery. State is isolated per session via a `Map<string, SessionState>`.\n *\n * @remarks\n * **History Management:**\n * - Thoughts are stored in a linear history array per session\n * - Auto-trimming occurs when `maxHistorySize` is exceeded\n * - Oldest thoughts are removed first (FIFO eviction)\n *\n * **Session Isolation:**\n * - Each session maintains its own thought history, branches, and cached tools/skills\n * - Sessions are identified by optional `session_id` on ThoughtData\n * - Default (undefined) session_id maps to `__global__`\n * - TTL-based cleanup prevents unbounded memory growth\n * - LRU eviction when MAX_SESSIONS exceeded\n *\n * **Branch Management:**\n * - Branches allow exploring alternative reasoning paths\n * - Each branch has its own thought array within a session\n * - Branches are created when `branch_from_thought` and `branch_id` are set\n * - Branch count and size are limited by `maxBranches` and `maxBranchSize`\n *\n * **Persistence:**\n * - Optional persistence backend for saving/loading state\n * - Persists thoughts and branches asynchronously (fire-and-forget)\n * - Does not block on persistence failures\n *\n * @example\n * ```typescript\n * const manager = new HistoryManager({\n * maxHistorySize: 500,\n * maxBranches: 25,\n * logger: new StructuredLogger({ context: 'History' })\n * });\n *\n * // Add a thought\n * manager.addThought({\n * thought: 'I need to analyze the problem',\n * thought_number: 1,\n * total_thoughts: 5,\n * next_thought_needed: true\n * });\n *\n * // Get history\n * const history = manager.getHistory();\n * console.log(`Thoughts: ${history.length}`);\n *\n * // Get branches\n * const branches = manager.getBranches();\n * console.log(`Branches: ${Object.keys(branches).length}`);\n *\n * // Clear all state\n * manager.clear();\n * ```\n */\nexport class HistoryManager implements IHistoryManager {\n\t/** Default session key for backward-compatible global state. */\n\tprivate static readonly DEFAULT_SESSION = '__global__';\n\n\t/** TTL for inactive sessions in milliseconds (default: 30 minutes). */\n\tprivate static readonly SESSION_TTL_MS = 30 * 60 * 1000;\n\n\t/** Maximum number of concurrent sessions before eviction. */\n\tprivate static readonly MAX_SESSIONS = 100;\n\n\t/** Session state storage. */\n\tprivate _sessions: Map<string, SessionState> = new Map();\n\n\t/** Timer for periodic session cleanup. */\n\tprivate _sessionCleanupTimer: ReturnType<typeof setInterval> | null = null;\n\n\t/** Maximum history size before auto-trimming. */\n\tprivate _maxHistorySize: number;\n\n\t/** Maximum number of branches before cleanup. */\n\tprivate _maxBranches: number;\n\n\t/** Maximum size of each branch. */\n\tprivate _maxBranchSize: number;\n\n\t/** Logger for diagnostics. */\n\tprivate _logger: Logger;\n\n\t/** Persistence backend for saving/loading state. */\n\tprivate _persistence: PersistenceBackend | null;\n\n\t/** Whether persistence is enabled. */\n\tprivate _persistenceEnabled: boolean;\n\n\tprivate _metrics?: IMetrics;\n\n\t/** Timer for periodic buffer flushes. */\n\tprivate _flushTimer: ReturnType<typeof setInterval> | null = null;\n\n\t/** Guard to prevent concurrent flushes. */\n\tprivate _isFlushing: boolean = false;\n\n\t/** Tracks consecutive flush failures for backoff. */\n\tprivate _flushRetryCount: number = 0;\n\n\t/** Maximum buffer size before triggering immediate flush. */\n\tprivate _persistenceBufferSize: number;\n\n\t/** Interval in milliseconds between periodic flushes. */\n\tprivate _persistenceFlushInterval: number;\n\n\t/** Maximum number of retries for failed flushes. */\n\tprivate _persistenceMaxRetries: number;\n\n\t/** Event emitter for persistence error events. */\n\tprivate _eventEmitter: PersistenceEventEmitter | null;\n\n\t/** Optional EdgeStore for capturing thought DAG edges. */\n\tprivate _edgeStore?: IEdgeStore;\n\n\t/** Optional SummaryStore used by getHistoryHydrated(). */\n\tprivate _summaryStore?: ISummaryStore;\n\n\t/** Whether to emit DAG edges (gated by features.dagEdges flag). */\n\tprivate _dagEdges: boolean;\n\n\t/**\n\t * Creates a new HistoryManager instance.\n\t *\n\t * @param config - Configuration options for the history manager\n\t *\n\t * @example\n\t * ```typescript\n\t * const manager = new HistoryManager({\n\t * maxHistorySize: 500,\n\t * maxBranches: 25,\n\t * logger: new StructuredLogger(),\n\t * persistence: filePersistence\n\t * });\n\t * ```\n\t */\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._persistenceBufferSize = config.persistenceBufferSize ?? 100;\n\t\tthis._persistenceFlushInterval = config.persistenceFlushInterval ?? 1000;\n\t\tthis._persistenceMaxRetries = config.persistenceMaxRetries ?? 3;\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// Start the periodic flush timer if persistence is enabled\n\t\tif (this._persistenceEnabled) {\n\t\t\tthis._startFlushTimer();\n\t\t}\n\n\t\t// Start the periodic session cleanup timer\n\t\tthis._startSessionCleanupTimer();\n\t}\n\n\t/**\n\t * Get the EdgeStore instance, if configured.\n\t * Returns undefined when no EdgeStore was injected.\n\t * @public Used by ThoughtProcessor to build StrategyContext for reasoning strategies.\n\t */\n\tpublic getEdgeStore(): IEdgeStore | undefined {\n\t\treturn this._edgeStore;\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 * Gets or creates the session state for a given session ID.\n\t * Creates a new SessionState if one doesn't exist.\n\t * Updates lastAccessedAt on every access.\n\t *\n\t * @param sessionId - Optional session ID (defaults to `__global__`)\n\t * @returns The session state\n\t * @private\n\t */\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};\n\t\t\tthis._sessions.set(key, session);\n\t\t\tthis._evictExcessSessions();\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.\n\t *\n\t * The thought is appended to the session's history array. If history exceeds\n\t * `maxHistorySize`, the oldest thoughts are removed. If the thought\n\t * has `branch_from_thought` and `branch_id` set, it's also added to\n\t * the appropriate branch. The thought is persisted asynchronously if\n\t * persistence is enabled.\n\t *\n\t * @param thought - The thought data to add\n\t *\n\t * @example\n\t * ```typescript\n\t * manager.addThought({\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 */\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// 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._emitEdgesForThought(session, thought);\n\n\t\t// Buffer thought for persistence instead of fire-and-forget\n\t\tthis._bufferForPersistence(session, thought);\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 * - sequence: default chronological link from previous thought (if none of the above)\n\t *\n\t * No-op when edgeStore is absent, dagEdges flag is off, or thought has no id.\n\t * @param session - The session state\n\t * @param thought - The current thought being added\n\t * @private\n\t */\n\tprivate _emitEdgesForThought(session: SessionState, 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 ?? HistoryManager.DEFAULT_SESSION;\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 * @param session - The session state to search\n\t * @param thoughtNumber - The thought_number to look up\n\t * @returns The thought's id if found and non-empty, undefined otherwise\n\t * @private\n\t */\n\tprivate _resolveThoughtId(\n\t\tsession: SessionState,\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\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 * @private\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.log('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\t/**\n\t * Buffers a thought for persistence if enabled.\n\t * @param session - The session state to buffer into\n\t * @param thought - The thought to buffer\n\t * @private\n\t */\n\tprivate _bufferForPersistence(session: SessionState, thought: ThoughtData): void {\n\t\tif (!this._persistenceEnabled || !this._persistence) return;\n\n\t\t// Backpressure: if buffer is full and flush is failing, log warning\n\t\tif (session.writeBuffer.length >= this._persistenceBufferSize && this._isFlushing) {\n\t\t\tthis.log('Write buffer full and flush in progress, applying backpressure', {\n\t\t\t\tbufferSize: session.writeBuffer.length,\n\t\t\t\tmaxSize: this._persistenceBufferSize,\n\t\t\t});\n\t\t}\n\n\t\tsession.writeBuffer.push(thought);\n\n\t\t// Trigger immediate flush if buffer is at capacity\n\t\tif (session.writeBuffer.length >= this._persistenceBufferSize) {\n\t\t\tvoid this._flushBuffer();\n\t\t}\n\t}\n\n\t/**\n\t * Adds a thought to a branch within a specific session.\n\t * @param session - The session state\n\t * @param branchId - The branch identifier\n\t * @param thought - The thought data to add\n\t * @private\n\t */\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\t/**\n\t * Removes old branches when count exceeds maxBranches within a session.\n\t * @param session - The session state\n\t * @private\n\t */\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\t/**\n\t * Trims a branch to maxBranchSize within a session.\n\t * @param session - The session state\n\t * @param branchId - The branch identifier to trim\n\t * @private\n\t */\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\t/**\n\t * Gets the complete thought history.\n\t *\n\t * @param sessionId - Optional session ID for session-scoped results\n\t * @returns An array of all thoughts in chronological order\n\t *\n\t * @example\n\t * ```typescript\n\t * const history = manager.getHistory();\n\t * history.forEach(thought => {\n\t * console.log(`${thought.thought_number}: ${thought.thought}`);\n\t * });\n\t * ```\n\t */\n\tpublic getHistory(sessionId?: string): ThoughtData[] {\n\t\treturn this._getSession(sessionId).thought_history;\n\t}\n\n\t/**\n\t * Gets the thought history with optional sliding-window dehydration applied.\n\t *\n\t * Non-mutating view: the underlying history is never modified. When the\n\t * `dagEdges` feature flag is off OR no `ISummaryStore` is configured, this\n\t * method returns the same `ThoughtData[]` as {@link getHistory}. Otherwise,\n\t * a {@link DehydrationPolicy} is applied that replaces older thoughts (cold\n\t * prefix beyond `keepLastK`) with {@link SummaryRef} placeholders pointing\n\t * at existing summaries in the store.\n\t *\n\t * @param sessionId - Optional session ID for session-scoped results\n\t * @param opts - Optional dehydration options (e.g. `keepLastK`)\n\t * @returns Mixed array of original thoughts and summary refs (or the raw\n\t * history when dehydration is disabled)\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\t/**\n\t * Gets the current length of the thought history.\n\t *\n\t * @param sessionId - Optional session ID for session-scoped results\n\t * @returns The number of thoughts in history\n\t *\n\t * @example\n\t * ```typescript\n\t * console.log(`Total thoughts: ${manager.getHistoryLength()}`);\n\t * ```\n\t */\n\tpublic getHistoryLength(sessionId?: string): number {\n\t\treturn this._getSession(sessionId).thought_history.length;\n\t}\n\n\t/**\n\t * Gets all branches.\n\t *\n\t * @param sessionId - Optional session ID for session-scoped results\n\t * @returns A record mapping branch IDs to their thought arrays\n\t *\n\t * @example\n\t * ```typescript\n\t * const branches = manager.getBranches();\n\t * for (const [branchId, thoughts] of Object.entries(branches)) {\n\t * console.log(`Branch ${branchId}: ${thoughts.length} thoughts`);\n\t * }\n\t * ```\n\t */\n\tpublic getBranches(sessionId?: string): Record<string, ThoughtData[]> {\n\t\treturn this._getSession(sessionId).branches;\n\t}\n\n\t/**\n\t * Gets all branch IDs.\n\t *\n\t * @param sessionId - Optional session ID for session-scoped results\n\t * @returns An array of branch identifiers\n\t *\n\t * @example\n\t * ```typescript\n\t * const branchIds = manager.getBranchIds();\n\t * console.log(`Active branches: ${branchIds.join(', ')}`);\n\t * ```\n\t */\n\tpublic getBranchIds(sessionId?: string): string[] {\n\t\treturn Object.keys(this._getSession(sessionId).branches);\n\t}\n\n\t/**\n\t * Gets the most recently available MCP tools from the session.\n\t *\n\t * @param sessionId - Optional session ID for session-scoped results\n\t * @returns The last-seen array of MCP tool names, or undefined if never set\n\t *\n\t * @example\n\t * ```typescript\n\t * const tools = manager.getAvailableMcpTools();\n\t * // ['Read', 'Grep', 'Glob'] or undefined\n\t * ```\n\t */\n\tpublic getAvailableMcpTools(sessionId?: string): string[] | undefined {\n\t\treturn this._getSession(sessionId).availableMcpTools;\n\t}\n\n\t/**\n\t * Gets the most recently available skills from the session.\n\t *\n\t * @param sessionId - Optional session ID for session-scoped results\n\t * @returns The last-seen array of skill names, or undefined if never set\n\t *\n\t * @example\n\t * ```typescript\n\t * const skills = manager.getAvailableSkills();\n\t * // ['commit', 'review-pr'] or undefined\n\t * ```\n\t */\n\tpublic getAvailableSkills(sessionId?: string): string[] | undefined {\n\t\treturn this._getSession(sessionId).availableSkills;\n\t}\n\n\t/**\n\t * Gets a specific branch by ID.\n\t *\n\t * @param branchId - The branch identifier\n\t * @param sessionId - Optional session ID for session-scoped results\n\t * @returns The branch's thought array, or undefined if not found\n\t *\n\t * @example\n\t * ```typescript\n\t * const branch = manager.getBranch('alternative-approach');\n\t * if (branch) {\n\t * console.log(`Branch has ${branch.length} thoughts`);\n\t * } else {\n\t * console.log('Branch not found');\n\t * }\n\t * ```\n\t */\n\tpublic getBranch(branchId: string, sessionId?: string): ThoughtData[] | undefined {\n\t\treturn this._getSession(sessionId).branches[branchId];\n\t}\n\n\t/**\n\t * Clears history and branches.\n\t * If sessionId is provided, clears only that session.\n\t * If omitted, clears all sessions.\n\t *\n\t * @param sessionId - Optional session ID to clear\n\t *\n\t * @example\n\t * ```typescript\n\t * manager.clear();\n\t * console.log('All history and branches cleared');\n\t * ```\n\t */\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\t// Clear specific session\n\t\t\tthis._sessions.delete(sessionId);\n\t\t\tthis.log('Session cleared', { sessionId });\n\t\t} else {\n\t\t\t// Clear all sessions\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\t/** Clears state for a specific session. Alias for clear(sessionId). */\n\tpublic clearSession(sessionId: string): void {\n\t\tthis.clear(sessionId);\n\t}\n\n\t/** Gets all active session IDs. */\n\tpublic getSessionIds(): string[] {\n\t\treturn Array.from(this._sessions.keys());\n\t}\n\n\t/** Gets the number of active sessions. */\n\tpublic getSessionCount(): number {\n\t\treturn this._sessions.size;\n\t}\n\n\t/**\n\t * Loads history from the persistence backend.\n\t *\n\t * This should be called during initialization to restore previous state.\n\t * Only loads if persistence is enabled and the backend is healthy.\n\t * Loads into the global session.\n\t *\n\t * @returns Promise that resolves when loading is complete\n\t *\n\t * @example\n\t * ```typescript\n\t * await manager.loadFromPersistence();\n\t * console.log(`Loaded ${manager.getHistoryLength()} thoughts`);\n\t * ```\n\t */\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\t// Check backend health\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\t// Load history\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\t// Load branches\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 (DEFAULT_SESSION only — multi-session is additive later)\n\t\t\tif (this._edgeStore) {\n\t\t\t\ttry {\n\t\t\t\t\tconst globalEdges = await this._persistence.loadEdges(HistoryManager.DEFAULT_SESSION);\n\t\t\t\t\tfor (const edge of globalEdges) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tthis._edgeStore.addEdge(edge);\n\t\t\t\t\t\t} catch (edgeErr) {\n\t\t\t\t\t\t\tthis.log('Failed to restore edge', {\n\t\t\t\t\t\t\t\tedgeId: edge.id,\n\t\t\t\t\t\t\t\terror: getErrorMessage(edgeErr),\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(`Loaded ${globalEdges.length} edges for global session from persistence`);\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\t/**\n\t * Checks if persistence is enabled.\n\t *\n\t * @returns true if persistence is enabled, false otherwise\n\t *\n\t * @example\n\t * ```typescript\n\t * if (manager.isPersistenceEnabled()) {\n\t * console.log('Persistence is active');\n\t * }\n\t * ```\n\t */\n\tpublic isPersistenceEnabled(): boolean {\n\t\treturn this._persistenceEnabled;\n\t}\n\n\t/**\n\t * Gets the persistence backend instance.\n\t *\n\t * @returns The persistence backend, or null if not configured\n\t *\n\t * @example\n\t * ```typescript\n\t * const backend = manager.getPersistenceBackend();\n\t * if (backend) {\n\t * await backend.healthy();\n\t * }\n\t * ```\n\t */\n\tpublic getPersistenceBackend(): PersistenceBackend | null {\n\t\treturn this._persistence;\n\t}\n\n\t/**\n\t * Sets the event emitter for persistence error events.\n\t * This allows wiring up the event emitter after construction\n\t * (e.g., when the server instance is the emitter).\n\t *\n\t * @param emitter - The event emitter to use for persistence error events\n\t */\n\tpublic setEventEmitter(emitter: PersistenceEventEmitter): void {\n\t\tthis._eventEmitter = emitter;\n\t}\n\n\t/**\n\t * Gracefully shuts down the write buffer and session cleanup.\n\t * Stops the periodic flush timer and session cleanup timer,\n\t * then flushes any remaining buffered writes.\n\t * Should be called during server shutdown before closing the persistence backend.\n\t */\n\tpublic async shutdown(): Promise<void> {\n\t\tthis._stopFlushTimer();\n\t\tthis._stopSessionCleanupTimer();\n\t\tawait this._flushBuffer();\n\t}\n\n\t/**\n\t * Starts the periodic flush timer for the write buffer.\n\t * @private\n\t */\n\tprivate _startFlushTimer(): void {\n\t\tif (this._flushTimer !== null) {\n\t\t\treturn;\n\t\t}\n\t\tthis._flushTimer = setInterval(() => {\n\t\t\tvoid this._flushBuffer();\n\t\t}, this._persistenceFlushInterval);\n\t\t// Allow the process to exit even if the timer is still running\n\t\tif (this._flushTimer && typeof this._flushTimer === 'object' && 'unref' in this._flushTimer) {\n\t\t\tthis._flushTimer.unref();\n\t\t}\n\t}\n\n\t/**\n\t * Stops the periodic flush timer.\n\t * @private\n\t */\n\tprivate _stopFlushTimer(): void {\n\t\tif (this._flushTimer !== null) {\n\t\t\tclearInterval(this._flushTimer);\n\t\t\tthis._flushTimer = null;\n\t\t}\n\t}\n\n\t/**\n\t * Starts the periodic session cleanup timer.\n\t * Runs every 5 minutes to evict sessions that exceeded TTL.\n\t * @private\n\t */\n\tprivate _startSessionCleanupTimer(): void {\n\t\tif (this._sessionCleanupTimer !== null) return;\n\t\tthis._sessionCleanupTimer = setInterval(\n\t\t\t() => {\n\t\t\t\tthis._cleanupStaleSessions();\n\t\t\t},\n\t\t\t5 * 60 * 1000\n\t\t);\n\t\tif (\n\t\t\tthis._sessionCleanupTimer &&\n\t\t\ttypeof this._sessionCleanupTimer === 'object' &&\n\t\t\t'unref' in this._sessionCleanupTimer\n\t\t) {\n\t\t\tthis._sessionCleanupTimer.unref();\n\t\t}\n\t}\n\n\t/**\n\t * Stops the periodic session cleanup timer.\n\t * @private\n\t */\n\tprivate _stopSessionCleanupTimer(): void {\n\t\tif (this._sessionCleanupTimer !== null) {\n\t\t\tclearInterval(this._sessionCleanupTimer);\n\t\t\tthis._sessionCleanupTimer = null;\n\t\t}\n\t}\n\n\t/**\n\t * Evicts sessions that have been inactive longer than SESSION_TTL_MS.\n\t * The global session is never evicted.\n\t * @private\n\t */\n\tprivate _cleanupStaleSessions(): void {\n\t\tconst now = Date.now();\n\t\tfor (const [key, session] of this._sessions) {\n\t\t\t// Never evict the global session\n\t\t\tif (key === HistoryManager.DEFAULT_SESSION) continue;\n\t\t\tif (now - session.lastAccessedAt > HistoryManager.SESSION_TTL_MS) {\n\t\t\t\tthis._sessions.delete(key);\n\t\t\t\tthis.log('Evicted stale session', { sessionId: key });\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Evicts oldest sessions when MAX_SESSIONS is exceeded (LRU).\n\t * The global session is never evicted.\n\t * @private\n\t */\n\tprivate _evictExcessSessions(): void {\n\t\twhile (this._sessions.size > HistoryManager.MAX_SESSIONS) {\n\t\t\tlet oldestKey: string | null = null;\n\t\t\tlet oldestTime = Infinity;\n\t\t\tfor (const [key, session] of this._sessions) {\n\t\t\t\tif (key === HistoryManager.DEFAULT_SESSION) continue;\n\t\t\t\tif (session.lastAccessedAt < oldestTime) {\n\t\t\t\t\toldestTime = session.lastAccessedAt;\n\t\t\t\t\toldestKey = key;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (oldestKey !== null) {\n\t\t\t\tthis._sessions.delete(oldestKey);\n\t\t\t\tthis.log('Evicted oldest session (LRU)', { sessionId: oldestKey });\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Flushes the write buffer to the persistence backend.\n\t *\n\t * Collects all buffered thoughts across all sessions and saves them\n\t * individually with retry logic. On persistent failure (all retries exhausted),\n\t * emits a `persistenceError` event and re-queues failed items.\n\t *\n\t * This method is safe to call concurrently — duplicate calls are skipped.\n\t * @internal\n\t */\n\tpublic async _flushBuffer(): Promise<void> {\n\t\tif (this._isFlushing || !this._persistence) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Collect all pending writes from all sessions\n\t\tconst allPending: ThoughtData[] = [];\n\t\tfor (const session of this._sessions.values()) {\n\t\t\tif (session.writeBuffer.length > 0) {\n\t\t\t\tallPending.push(...session.writeBuffer.splice(0));\n\t\t\t}\n\t\t}\n\n\t\tif (allPending.length === 0) return;\n\n\t\tthis._isFlushing = true;\n\t\tconst failedItems: ThoughtData[] = [];\n\n\t\ttry {\n\t\t\tfor (const thought of allPending) {\n\t\t\t\tconst saved = await this._flushSingleThought(thought);\n\t\t\t\tif (!saved) {\n\t\t\t\t\tfailedItems.push(thought);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis._handleFlushResult(failedItems, allPending.length);\n\n\t\t\t// Flush edges for sessions when EdgeStore is configured\n\t\t\tif (this._edgeStore) {\n\t\t\t\tawait this._flushEdges();\n\t\t\t}\n\t\t} finally {\n\t\t\tthis._isFlushing = false;\n\t\t}\n\t}\n\n\t/**\n\t * Flushes edges for all known sessions to the persistence backend.\n\t * No-op when EdgeStore or persistence is unavailable.\n\t * @private\n\t */\n\tprivate async _flushEdges(): Promise<void> {\n\t\tif (!this._edgeStore || !this._persistence) return;\n\t\tconst sessionKeys = new Set<string>(this._sessions.keys());\n\t\tsessionKeys.add(HistoryManager.DEFAULT_SESSION);\n\t\tfor (const sessionId of sessionKeys) {\n\t\t\tconst edges = this._edgeStore.edgesForSession(sessionId);\n\t\t\tif (edges.length === 0) continue;\n\t\t\ttry {\n\t\t\t\tawait this._persistence.saveEdges(sessionId, edges);\n\t\t\t} catch (err) {\n\t\t\t\tthis.log('Failed to persist edges for session', {\n\t\t\t\t\tsessionId,\n\t\t\t\t\terror: getErrorMessage(err),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Flushes a single thought to persistence with retry logic.\n\t * @param thought - The thought to flush\n\t * @returns true if saved successfully, false otherwise\n\t * @private\n\t */\n\tprivate async _flushSingleThought(thought: ThoughtData): Promise<boolean> {\n\t\tconst backoffDelays = [100, 500, 2000];\n\n\t\tfor (let attempt = 0; attempt <= this._persistenceMaxRetries; attempt++) {\n\t\t\ttry {\n\t\t\t\tawait this._persistence!.saveThought(thought);\n\t\t\t\treturn true;\n\t\t\t} catch (err) {\n\t\t\t\tif (attempt < this._persistenceMaxRetries) {\n\t\t\t\t\tconst delay = backoffDelays[attempt] ?? backoffDelays[backoffDelays.length - 1]!;\n\t\t\t\t\tthis.log(`Persistence retry ${attempt + 1}/${this._persistenceMaxRetries}`, {\n\t\t\t\t\t\tthoughtNumber: thought.thought_number,\n\t\t\t\t\t\tdelay,\n\t\t\t\t\t\terror: getErrorMessage(err),\n\t\t\t\t\t});\n\t\t\t\t\tawait this._delay(delay);\n\t\t\t\t} else {\n\t\t\t\t\tthis.log('All persistence retries exhausted for thought', {\n\t\t\t\t\t\tthoughtNumber: thought.thought_number,\n\t\t\t\t\t\terror: getErrorMessage(err),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Handles the result of a flush operation, re-queuing failures.\n\t * @param failedItems - Thoughts that failed to persist\n\t * @param totalCount - Total number of thoughts attempted\n\t * @private\n\t */\n\tprivate _handleFlushResult(failedItems: ThoughtData[], totalCount: number): void {\n\t\tif (failedItems.length > 0) {\n\t\t\t// Put failed items back in the global session's buffer\n\t\t\tconst globalSession = this._getSession();\n\t\t\tglobalSession.writeBuffer.unshift(...failedItems);\n\t\t\tthis._flushRetryCount++;\n\n\t\t\tconst error = new Error(\n\t\t\t\t`Failed to persist ${failedItems.length} thoughts after ${this._persistenceMaxRetries} retries`\n\t\t\t);\n\t\t\tthis._eventEmitter?.emit('persistenceError', {\n\t\t\t\toperation: 'flushBuffer',\n\t\t\t\terror,\n\t\t\t});\n\n\t\t\tthis.log('Flush completed with failures', {\n\t\t\t\tfailed: failedItems.length,\n\t\t\t\ttotal: totalCount,\n\t\t\t\tconsecutiveFailures: this._flushRetryCount,\n\t\t\t});\n\t\t} else {\n\t\t\t// Reset retry count on full success\n\t\t\tthis._flushRetryCount = 0;\n\t\t}\n\t}\n\n\t/**\n\t * Returns a promise that resolves after the specified delay.\n\t * @param ms - Delay in milliseconds\n\t * @private\n\t */\n\tprivate _delay(ms: number): Promise<void> {\n\t\treturn new Promise((resolve) => setTimeout(resolve, ms));\n\t}\n\n\t/**\n\t * Gets the current write buffer length across all sessions.\n\t * Useful for monitoring and testing.\n\t */\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","message","meta","sessionId","key","session","undefined","Date","thought","emittedRelational","parentId","src","srcId","targetId","toolCallId","Object","history","prev","thoughtNumber","t","from","to","kind","metadata","edge","generateUlid","err","getErrorMessage","branchId","branchCount","branchesToRemove","removed","opts","sid","policy","DehydrationPolicy","Array","isHealthy","globalSession","branchIds","branchData","globalEdges","edgeErr","edgeError","error","emitter","setInterval","clearInterval","now","oldestKey","oldestTime","Infinity","allPending","failedItems","saved","sessionKeys","Set","edges","backoffDelays","attempt","delay","totalCount","Error","ms","Promise","resolve","setTimeout","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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"}