@iworkforces/tracelattice 2.1.2 → 2.2.0

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 (125) hide show
  1. package/README.md +19 -3
  2. package/dist/contracts/PersistenceBackend.d.ts +10 -1
  3. package/dist/contracts/PersistenceBackend.d.ts.map +1 -1
  4. package/dist/contracts/calibrator.d.ts +9 -6
  5. package/dist/contracts/calibrator.d.ts.map +1 -1
  6. package/dist/contracts/interfaces.d.ts +6 -0
  7. package/dist/contracts/interfaces.d.ts.map +1 -1
  8. package/dist/contracts/persistence-work.d.ts +13 -1
  9. package/dist/contracts/persistence-work.d.ts.map +1 -1
  10. package/dist/contracts/strategy.d.ts +14 -9
  11. package/dist/contracts/strategy.d.ts.map +1 -1
  12. package/dist/core/DurableThoughtIdentityIndex.d.ts +35 -0
  13. package/dist/core/DurableThoughtIdentityIndex.d.ts.map +1 -0
  14. package/dist/core/DurableThoughtIdentityIndex.js +94 -0
  15. package/dist/core/DurableThoughtIdentityIndex.js.map +1 -0
  16. package/dist/core/HistoryManager.d.ts +5 -0
  17. package/dist/core/HistoryManager.d.ts.map +1 -1
  18. package/dist/core/HistoryManager.js +37 -4
  19. package/dist/core/HistoryManager.js.map +1 -1
  20. package/dist/core/IHistoryManager.d.ts +2 -0
  21. package/dist/core/IHistoryManager.d.ts.map +1 -1
  22. package/dist/core/PersistenceBuffer.d.ts +7 -1
  23. package/dist/core/PersistenceBuffer.d.ts.map +1 -1
  24. package/dist/core/PersistenceBuffer.js +13 -1
  25. package/dist/core/PersistenceBuffer.js.map +1 -1
  26. package/dist/core/PersistenceRestore.d.ts.map +1 -1
  27. package/dist/core/PersistenceRestore.js +11 -3
  28. package/dist/core/PersistenceRestore.js.map +1 -1
  29. package/dist/core/PersistenceWorkQueue.d.ts +13 -1
  30. package/dist/core/PersistenceWorkQueue.d.ts.map +1 -1
  31. package/dist/core/PersistenceWorkQueue.js +86 -11
  32. package/dist/core/PersistenceWorkQueue.js.map +1 -1
  33. package/dist/core/PersistenceWriter.d.ts.map +1 -1
  34. package/dist/core/PersistenceWriter.js +3 -0
  35. package/dist/core/PersistenceWriter.js.map +1 -1
  36. package/dist/core/ThoughtEvaluator.d.ts +7 -4
  37. package/dist/core/ThoughtEvaluator.d.ts.map +1 -1
  38. package/dist/core/ThoughtEvaluator.js +15 -7
  39. package/dist/core/ThoughtEvaluator.js.map +1 -1
  40. package/dist/core/ThoughtProcessor.d.ts.map +1 -1
  41. package/dist/core/ThoughtProcessor.js +19 -7
  42. package/dist/core/ThoughtProcessor.js.map +1 -1
  43. package/dist/core/ThoughtReferenceIndex.d.ts +4 -0
  44. package/dist/core/ThoughtReferenceIndex.d.ts.map +1 -1
  45. package/dist/core/ThoughtReferenceIndex.js +28 -1
  46. package/dist/core/ThoughtReferenceIndex.js.map +1 -1
  47. package/dist/core/VerificationOutcomeAdmission.d.ts +8 -1
  48. package/dist/core/VerificationOutcomeAdmission.d.ts.map +1 -1
  49. package/dist/core/VerificationOutcomeAdmission.js.map +1 -1
  50. package/dist/core/VerificationTargetRestore.d.ts +5 -0
  51. package/dist/core/VerificationTargetRestore.d.ts.map +1 -0
  52. package/dist/core/VerificationTargetRestore.js +61 -0
  53. package/dist/core/VerificationTargetRestore.js.map +1 -0
  54. package/dist/core/evaluator/Aggregator.d.ts +2 -1
  55. package/dist/core/evaluator/Aggregator.d.ts.map +1 -1
  56. package/dist/core/evaluator/Aggregator.js +4 -12
  57. package/dist/core/evaluator/Aggregator.js.map +1 -1
  58. package/dist/core/evaluator/Calibrator.d.ts +13 -9
  59. package/dist/core/evaluator/Calibrator.d.ts.map +1 -1
  60. package/dist/core/evaluator/Calibrator.js +25 -11
  61. package/dist/core/evaluator/Calibrator.js.map +1 -1
  62. package/dist/core/evaluator/PatternDetector.d.ts +2 -1
  63. package/dist/core/evaluator/PatternDetector.d.ts.map +1 -1
  64. package/dist/core/evaluator/PatternDetector.js +13 -12
  65. package/dist/core/evaluator/PatternDetector.js.map +1 -1
  66. package/dist/core/evaluator/SignalComputer.d.ts +3 -2
  67. package/dist/core/evaluator/SignalComputer.d.ts.map +1 -1
  68. package/dist/core/evaluator/SignalComputer.js +4 -9
  69. package/dist/core/evaluator/SignalComputer.js.map +1 -1
  70. package/dist/core/evaluator/VerificationLinks.d.ts +19 -0
  71. package/dist/core/evaluator/VerificationLinks.d.ts.map +1 -0
  72. package/dist/core/evaluator/VerificationLinks.js +113 -0
  73. package/dist/core/evaluator/VerificationLinks.js.map +1 -0
  74. package/dist/core/evaluator/calibration-math.d.ts +12 -2
  75. package/dist/core/evaluator/calibration-math.d.ts.map +1 -1
  76. package/dist/core/evaluator/calibration-math.js +11 -2
  77. package/dist/core/evaluator/calibration-math.js.map +1 -1
  78. package/dist/core/graph/EdgeEmitter.d.ts.map +1 -1
  79. package/dist/core/graph/EdgeEmitter.js +2 -1
  80. package/dist/core/graph/EdgeEmitter.js.map +1 -1
  81. package/dist/core/graph/GraphView.d.ts +5 -5
  82. package/dist/core/graph/GraphView.d.ts.map +1 -1
  83. package/dist/core/graph/GraphView.js +11 -11
  84. package/dist/core/graph/GraphView.js.map +1 -1
  85. package/dist/core/reasoning/ActiveEvidenceProjection.d.ts +20 -0
  86. package/dist/core/reasoning/ActiveEvidenceProjection.d.ts.map +1 -0
  87. package/dist/core/reasoning/ActiveEvidenceProjection.js +100 -0
  88. package/dist/core/reasoning/ActiveEvidenceProjection.js.map +1 -0
  89. package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.d.ts +5 -4
  90. package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.d.ts.map +1 -1
  91. package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.js +24 -14
  92. package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.js.map +1 -1
  93. package/dist/lib.d.ts.map +1 -1
  94. package/dist/lib.js +3 -1
  95. package/dist/lib.js.map +1 -1
  96. package/dist/persistence/BacktrackPersistence.d.ts +13 -0
  97. package/dist/persistence/BacktrackPersistence.d.ts.map +1 -0
  98. package/dist/persistence/BacktrackPersistence.js +90 -0
  99. package/dist/persistence/BacktrackPersistence.js.map +1 -0
  100. package/dist/persistence/FilePersistence.d.ts +2 -1
  101. package/dist/persistence/FilePersistence.d.ts.map +1 -1
  102. package/dist/persistence/FilePersistence.js +28 -0
  103. package/dist/persistence/FilePersistence.js.map +1 -1
  104. package/dist/persistence/FileSnapshotV2.d.ts +2 -0
  105. package/dist/persistence/FileSnapshotV2.d.ts.map +1 -1
  106. package/dist/persistence/FileSnapshotV2.js +6 -2
  107. package/dist/persistence/FileSnapshotV2.js.map +1 -1
  108. package/dist/persistence/MemoryPersistence.d.ts +2 -1
  109. package/dist/persistence/MemoryPersistence.d.ts.map +1 -1
  110. package/dist/persistence/MemoryPersistence.js +21 -1
  111. package/dist/persistence/MemoryPersistence.js.map +1 -1
  112. package/dist/persistence/PersistenceCodec.d.ts.map +1 -1
  113. package/dist/persistence/PersistenceCodec.js +5 -0
  114. package/dist/persistence/PersistenceCodec.js.map +1 -1
  115. package/dist/persistence/PersistenceErrors.d.ts +1 -1
  116. package/dist/persistence/PersistenceErrors.d.ts.map +1 -1
  117. package/dist/persistence/PersistenceErrors.js.map +1 -1
  118. package/dist/persistence/PersistenceScope.d.ts +1 -1
  119. package/dist/persistence/PersistenceScope.d.ts.map +1 -1
  120. package/dist/persistence/PersistenceScope.js.map +1 -1
  121. package/dist/persistence/SqlitePersistence.d.ts +2 -1
  122. package/dist/persistence/SqlitePersistence.d.ts.map +1 -1
  123. package/dist/persistence/SqlitePersistence.js +18 -0
  124. package/dist/persistence/SqlitePersistence.js.map +1 -1
  125. package/package.json +4 -3
@@ -1 +1 @@
1
- {"version":3,"file":"core/PersistenceBuffer.js","sources":["../../src/core/PersistenceBuffer.ts"],"sourcesContent":["/**\n * Joinable coordinator for attributable persistence work.\n *\n * @module PersistenceBuffer\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\n\nimport type { PersistenceBackend } from '../contracts/PersistenceBackend.js';\nimport type { BranchId, SessionId } from '../contracts/ids.js';\nimport type { PersistenceWork, PersistenceWorkToken } from '../contracts/persistence-work.js';\nimport {\n\tPersistenceDrainError,\n\tPersistenceSessionAdmissionClosedError,\n\tPersistenceSessionBarrierReentrancyError,\n} from './PersistenceBufferErrors.js';\nimport { NullLogger } from '../logger/NullLogger.js';\nimport type { Logger } from '../logger/StructuredLogger.js';\nimport { assertNever } from '../utils.js';\nimport type { Summary } from './compression/Summary.js';\nimport type { Edge } from './graph/Edge.js';\nimport { PersistenceWorkQueue, type PersistenceSelectionMode } from './PersistenceWorkQueue.js';\nimport { PersistenceWriter, type PersistenceDelay } from './PersistenceWriter.js';\nimport type { ThoughtData } from './thought.js';\n\n/** Event emitter contract for persistence error events. */\nexport interface PersistenceEventEmitter {\n\temit(event: 'persistenceError', payload: { operation: string; error: Error }): boolean;\n}\n\n/** Observable persistence state relevant to safe in-memory session eviction. */\nexport type SessionEvictionState = 'quiescent' | 'pending' | 'failed' | 'barrier';\n\n/** Configuration options for {@link PersistenceBuffer}. */\nexport interface PersistenceBufferConfig {\n\treadonly persistence: PersistenceBackend;\n\treadonly bufferSize: number;\n\treadonly flushInterval: number;\n\treadonly maxRetries: number;\n\t/** Optional emitter for `persistenceError` events. */\n\treadonly eventEmitter?: PersistenceEventEmitter | null;\n\treadonly logger?: Logger;\n\t/** Optional retry scheduler for deterministic coordination and testing. */\n\treadonly delay?: PersistenceDelay;\n}\n\ntype ActiveDrain = {\n\treadonly promise: Promise<void>;\n\treadonly resolve: () => void;\n\treadonly reject: (reason: unknown) => void;\n\treadonly selectedTokens: Set<PersistenceWorkToken>;\n\tmode: PersistenceSelectionMode;\n\tbackgroundObserved: boolean;\n\tinFlightWork: PersistenceWork | null;\n\tsettlement: DrainSettlement | null;\n};\n\ntype SessionBarrierState = {\n\ttail: Promise<void>;\n\tpendingOwners: number;\n};\n\ntype DrainTermination =\n\t{ readonly kind: 'result' } | { readonly kind: 'fault'; readonly fault: unknown };\n\ntype DrainSettlement =\n\t{ readonly kind: 'resolved' } | { readonly kind: 'rejected'; readonly reason: unknown };\n\ntype SessionQuiescence =\n\t| { readonly kind: 'pending' }\n\t| { readonly kind: 'resolved' }\n\t| { readonly kind: 'rejected'; readonly reason: unknown };\n\n/**\n * Coordinates one globally joinable persistence generation at a time.\n *\n * Accepted work is owned by a queue independent of live session state. Explicit\n * callers can join and upgrade a background generation without starting another writer.\n */\nexport class PersistenceBuffer {\n\tprivate readonly _bufferSize: number;\n\tprivate readonly _flushInterval: number;\n\tprivate readonly _queue = new PersistenceWorkQueue();\n\tprivate readonly _writer: PersistenceWriter;\n\tprivate _eventEmitter: PersistenceEventEmitter | null;\n\tprivate readonly _logger: Logger;\n\n\tprivate _flushTimer: ReturnType<typeof setInterval> | null = null;\n\tprivate _activeDrain: ActiveDrain | null = null;\n\tprivate readonly _sessionBarriers = new Map<SessionId, SessionBarrierState>();\n\tprivate readonly _sessionBarrierOwnership = new AsyncLocalStorage<ReadonlySet<SessionId>>();\n\tprivate readonly _sessionProgressWaiters = new Map<SessionId, Set<() => void>>();\n\tprivate readonly _quarantinedSessions = new Set<SessionId>();\n\tprivate readonly _evictionQuarantinedSessions = new Set<SessionId>();\n\tprivate _globalResetTail: Promise<void> = Promise.resolve();\n\tprivate _globalResetOwners = 0;\n\tprivate _globalQuarantined = false;\n\n\t/**\n\t * Creates a persistence-drain coordinator.\n\t *\n\t * @param config - Persistence dependencies, trigger thresholds, and retry policy.\n\t */\n\tpublic constructor(config: PersistenceBufferConfig) {\n\t\tthis._bufferSize = config.bufferSize;\n\t\tthis._flushInterval = config.flushInterval;\n\t\tthis._eventEmitter = config.eventEmitter ?? null;\n\t\tthis._logger = config.logger ?? new NullLogger();\n\t\tthis._writer = new PersistenceWriter({\n\t\t\tpersistence: config.persistence,\n\t\t\tmaxRetries: config.maxRetries,\n\t\t\tdelay: config.delay,\n\t\t});\n\t}\n\n\t/** @returns The underlying flush timer for lifecycle introspection. */\n\tpublic get timer(): ReturnType<typeof setInterval> | null {\n\t\treturn this._flushTimer;\n\t}\n\n\t/** @returns Whether a global drain generation is active. */\n\tpublic get isFlushing(): boolean {\n\t\treturn this._activeDrain !== null;\n\t}\n\n\t/** @returns Number of accepted thought writes not yet acknowledged successful. */\n\tpublic get pendingThoughtCount(): number {\n\t\treturn this._queue.pendingThoughtCount;\n\t}\n\n\t/**\n\t * Sets or clears the persistence error event emitter.\n\t *\n\t * @param emitter - Replacement emitter, or `null` to disable events.\n\t */\n\tpublic setEventEmitter(emitter: PersistenceEventEmitter | null): void {\n\t\tthis._eventEmitter = emitter;\n\t}\n\n\t/**\n\t * Accepts a thought with authoritative session attribution.\n\t *\n\t * @param sessionId - Session that owns the accepted thought.\n\t * @param thought - Thought to persist.\n\t */\n\tpublic bufferThought(sessionId: SessionId, thought: ThoughtData): void {\n\t\tthis._assertSessionAdmissionOpen(sessionId);\n\t\tif (this._queue.pendingThoughtCount >= this._bufferSize && this.isFlushing) {\n\t\t\tthis._logger.info('Write buffer full and flush in progress, applying backpressure', {\n\t\t\t\tbufferSize: this._queue.pendingThoughtCount,\n\t\t\t\tmaxSize: this._bufferSize,\n\t\t\t});\n\t\t}\n\n\t\tthis._queue.enqueueThought(sessionId, thought);\n\t\tif (this._queue.pendingThoughtCount >= this._bufferSize) this._triggerBackgroundDrain();\n\t}\n\n\t/**\n\t * Accepts the latest snapshot for one session-owned branch.\n\t *\n\t * @param sessionId - Session that owns the branch.\n\t * @param branchId - Stable branch coordinate.\n\t * @param thoughts - Branch snapshot copied by the queue.\n\t */\n\tpublic bufferBranch(\n\t\tsessionId: SessionId,\n\t\tbranchId: BranchId,\n\t\tthoughts: readonly ThoughtData[]\n\t): void {\n\t\tthis._assertSessionAdmissionOpen(sessionId);\n\t\tthis._queue.replaceBranch(sessionId, branchId, thoughts);\n\t}\n\n\t/**\n\t * @param sessionId - Session that owns the branch.\n\t * @param branchId - Stable branch coordinate to delete.\n\t */\n\tpublic deleteBranch(sessionId: SessionId, branchId: BranchId): void {\n\t\tthis._assertSessionAdmissionOpen(sessionId);\n\t\tthis._queue.deleteBranch(sessionId, branchId);\n\t}\n\n\t/**\n\t * Accepts the latest edge snapshot for one session.\n\t *\n\t * @param sessionId - Session that owns the edges.\n\t * @param edges - Edge snapshot copied by the queue.\n\t */\n\tpublic bufferEdges(sessionId: SessionId, edges: readonly Edge[]): void {\n\t\tthis._assertSessionAdmissionOpen(sessionId);\n\t\tthis._queue.replaceEdges(sessionId, edges);\n\t}\n\n\t/**\n\t * Accepts the latest summary snapshot for one session.\n\t *\n\t * @param sessionId - Session that owns the summaries.\n\t * @param summaries - Summary snapshot copied by the queue.\n\t */\n\tpublic bufferSummaries(sessionId: SessionId, summaries: readonly Summary[]): void {\n\t\tthis._assertSessionAdmissionOpen(sessionId);\n\t\tthis._queue.replaceSummaries(sessionId, summaries);\n\t}\n\n\t/** Rejects synchronously when lifecycle coordination has closed persistence admission. */\n\tpublic assertSessionAdmissionOpen(sessionId: SessionId): void {\n\t\tthis._assertSessionAdmissionOpen(sessionId);\n\t}\n\n\t/** Starts the periodic background-drain timer without keeping the process alive. */\n\tpublic startFlushTimer(): void {\n\t\tif (this._flushTimer !== null) return;\n\t\tthis._flushTimer = setInterval(() => this._triggerBackgroundDrain(), this._flushInterval);\n\t\tif (typeof this._flushTimer === 'object' && 'unref' in this._flushTimer) {\n\t\t\tthis._flushTimer.unref();\n\t\t}\n\t}\n\n\t/** Stops the periodic background-drain timer. */\n\tpublic stopFlushTimer(): void {\n\t\tif (this._flushTimer === null) return;\n\t\tclearInterval(this._flushTimer);\n\t\tthis._flushTimer = null;\n\t}\n\n\t/**\n\t * Starts or joins an explicit global drain generation.\n\t *\n\t * @returns The exact shared promise for the active generation.\n\t */\n\tpublic drain(): Promise<void> {\n\t\treturn this._joinOrStart('explicit').promise;\n\t}\n\n\t/** @returns The exact same promise as {@link drain} for the active generation. */\n\tpublic flush(): Promise<void> {\n\t\treturn this.drain();\n\t}\n\n\t/**\n\t * Joins the global explicit generation and projects its terminal failures to one session.\n\t * This method does not close admission; lifecycle owners must use\n\t * {@link withSessionBarrier} when work must not cross an owner callback.\n\t *\n\t * @param sessionId - Session whose accepted writes form the barrier projection.\n\t * @returns A promise that rejects only for that session's failures or an unknown fault.\n\t */\n\tpublic drainSession(sessionId: SessionId): Promise<void> {\n\t\tconst generation = this._joinOrStart('explicit');\n\t\treturn generation.promise.catch((reason: unknown) => {\n\t\t\tif (!(reason instanceof PersistenceDrainError)) throw reason;\n\t\t\tconst failures = reason.failures.filter((failure) => failure.sessionId === sessionId);\n\t\t\tif (failures.length > 0) throw new PersistenceDrainError(failures);\n\t\t});\n\t}\n\n\t/** Returns queue-derived persistence state for safe session eviction planning. */\n\tpublic sessionEvictionState(sessionId: SessionId): SessionEvictionState {\n\t\tif (this._globalResetOwners > 0 || this._sessionBarriers.has(sessionId)) return 'barrier';\n\t\tif (\n\t\t\tthis._globalQuarantined ||\n\t\t\tthis._queue.hasSessionTerminalFailure(sessionId) ||\n\t\t\tthis._quarantinedSessions.has(sessionId) ||\n\t\t\tthis._evictionQuarantinedSessions.has(sessionId)\n\t\t) {\n\t\t\treturn 'failed';\n\t\t}\n\t\treturn this._queue.hasSessionWork(sessionId) ? 'pending' : 'quiescent';\n\t}\n\n\t/**\n\t * Drains accepted session work before ordinary in-memory eviction cleanup.\n\t *\n\t * Unlike reset barriers, this operation never clears durable state or discards\n\t * accepted work. Failure retains admission quarantine until a later explicit\n\t * invocation successfully drains and completes cleanup.\n\t */\n\tpublic withSessionEvictionBarrier<T>(\n\t\tsessionId: SessionId,\n\t\toperation: () => Promise<T>\n\t): Promise<T> {\n\t\tif (\n\t\t\tthis._globalResetOwners > 0 ||\n\t\t\tthis._globalQuarantined ||\n\t\t\tthis._quarantinedSessions.has(sessionId)\n\t\t) {\n\t\t\treturn Promise.reject(new PersistenceSessionAdmissionClosedError(sessionId));\n\t\t}\n\t\tconst currentOwnership = this._sessionBarrierOwnership.getStore();\n\t\tif (currentOwnership?.has(sessionId) === true) {\n\t\t\treturn Promise.reject(new PersistenceSessionBarrierReentrancyError(sessionId));\n\t\t}\n\t\tconst operationOwnership = new Set(currentOwnership);\n\t\toperationOwnership.add(sessionId);\n\t\tconst state = this._sessionBarriers.get(sessionId) ?? {\n\t\t\ttail: Promise.resolve(),\n\t\t\tpendingOwners: 0,\n\t\t};\n\t\tthis._sessionBarriers.set(sessionId, state);\n\t\tstate.pendingOwners += 1;\n\n\t\tconst result = state.tail.then(async () => {\n\t\t\ttry {\n\t\t\t\tawait this._awaitSessionQuiescence(sessionId);\n\t\t\t\tconst value = await this._sessionBarrierOwnership.run(operationOwnership, operation);\n\t\t\t\tthis._queue.forgetQuiescentSession(sessionId);\n\t\t\t\tthis._evictionQuarantinedSessions.delete(sessionId);\n\t\t\t\treturn value;\n\t\t\t} catch (error) {\n\t\t\t\tthis._evictionQuarantinedSessions.add(sessionId);\n\t\t\t\tthrow error;\n\t\t\t} finally {\n\t\t\t\tstate.pendingOwners -= 1;\n\t\t\t\tif (state.pendingOwners === 0) this._sessionBarriers.delete(sessionId);\n\t\t\t}\n\t\t});\n\t\tstate.tail = result.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined\n\t\t);\n\t\treturn result;\n\t}\n\n\t/** Forgets queue coordinates only when the session is fully quiescent. */\n\tpublic forgetQuiescentSession(sessionId: SessionId): boolean {\n\t\treturn this.sessionEvictionState(sessionId) === 'quiescent'\n\t\t\t? this._queue.forgetQuiescentSession(sessionId)\n\t\t\t: false;\n\t}\n\n\t/**\n\t * Runs one lifecycle operation while persistence admission is closed for a session.\n\t *\n\t * Admission closes synchronously when this method is called. Already accepted work for\n\t * the session is settled first; an exhausted session failure rejects without invoking\n\t * `operation`. Concurrent calls for the same session execute in FIFO call order and keep\n\t * admission closed until the final callback settles. Other sessions remain admissible and\n\t * may be owned by a nested callback. Reacquiring the same session from the owning callback's\n\t * async call chain rejects before joining the FIFO because awaiting it would deadlock its owner.\n\t *\n\t * @example\n\t * ```ts\n\t * await buffer.withSessionBarrier(sessionId, async () => {\n\t * await persistence.clearSession(sessionId);\n\t * });\n\t * ```\n\t *\n\t * @param sessionId - Session exclusively owned for the callback duration.\n\t * @param operation - Awaited lifecycle operation run only after prior work settles.\n\t * @returns The callback result after admission has reopened when no owner remains.\n\t * @throws {@link PersistenceSessionBarrierReentrancyError} when the owning async call chain\n\t * attempts to reacquire `sessionId`.\n\t */\n\tpublic withSessionBarrier<T>(sessionId: SessionId, operation: () => Promise<T>): Promise<T> {\n\t\tconst currentOwnership = this._sessionBarrierOwnership.getStore();\n\t\tif (currentOwnership?.has(sessionId) === true) {\n\t\t\treturn Promise.reject(new PersistenceSessionBarrierReentrancyError(sessionId));\n\t\t}\n\t\tconst operationOwnership = new Set(currentOwnership);\n\t\toperationOwnership.add(sessionId);\n\n\t\tconst state = this._sessionBarriers.get(sessionId) ?? {\n\t\t\ttail: Promise.resolve(),\n\t\t\tpendingOwners: 0,\n\t\t};\n\t\tthis._sessionBarriers.set(sessionId, state);\n\t\tstate.pendingOwners += 1;\n\n\t\tconst result = state.tail.then(async () => {\n\t\t\ttry {\n\t\t\t\tawait this._awaitSessionQuiescence(sessionId);\n\t\t\t\treturn await this._sessionBarrierOwnership.run(operationOwnership, operation);\n\t\t\t} finally {\n\t\t\t\tstate.pendingOwners -= 1;\n\t\t\t\tif (state.pendingOwners === 0) this._sessionBarriers.delete(sessionId);\n\t\t\t}\n\t\t});\n\t\tstate.tail = result.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined\n\t\t);\n\t\treturn result;\n\t}\n\n\t/**\n\t * Runs a scoped reset, retaining admission quarantine on failure.\n\t * A retry may pass retained write failures because successful durable deletion\n\t * invalidates those stale queue entries before admission reopens.\n\t */\n\tpublic withSessionResetBarrier<T>(sessionId: SessionId, operation: () => Promise<T>): Promise<T> {\n\t\tif (this._globalResetOwners > 0 || this._globalQuarantined) {\n\t\t\treturn Promise.reject(new PersistenceSessionAdmissionClosedError(sessionId));\n\t\t}\n\t\tconst currentOwnership = this._sessionBarrierOwnership.getStore();\n\t\tif (currentOwnership?.has(sessionId) === true) {\n\t\t\treturn Promise.reject(new PersistenceSessionBarrierReentrancyError(sessionId));\n\t\t}\n\t\tconst operationOwnership = new Set(currentOwnership);\n\t\toperationOwnership.add(sessionId);\n\t\tconst state = this._sessionBarriers.get(sessionId) ?? {\n\t\t\ttail: Promise.resolve(),\n\t\t\tpendingOwners: 0,\n\t\t};\n\t\tthis._sessionBarriers.set(sessionId, state);\n\t\tstate.pendingOwners += 1;\n\n\t\tconst result = state.tail.then(async () => {\n\t\t\ttry {\n\t\t\t\ttry {\n\t\t\t\t\tawait this._awaitSessionQuiescence(sessionId);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!(error instanceof PersistenceDrainError)) throw error;\n\t\t\t\t}\n\t\t\t\tconst value = await this._sessionBarrierOwnership.run(operationOwnership, operation);\n\t\t\t\tthis._queue.discardSession(sessionId);\n\t\t\t\tthis._quarantinedSessions.delete(sessionId);\n\t\t\t\tthis._evictionQuarantinedSessions.delete(sessionId);\n\t\t\t\treturn value;\n\t\t\t} catch (error) {\n\t\t\t\tthis._quarantinedSessions.add(sessionId);\n\t\t\t\tthrow error;\n\t\t\t} finally {\n\t\t\t\tstate.pendingOwners -= 1;\n\t\t\t\tif (state.pendingOwners === 0) this._sessionBarriers.delete(sessionId);\n\t\t\t}\n\t\t});\n\t\tstate.tail = result.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined\n\t\t);\n\t\treturn result;\n\t}\n\n\t/** Runs a trusted global reset behind a global admission barrier. */\n\tpublic withGlobalResetBarrier<T>(operation: () => Promise<T>): Promise<T> {\n\t\tthis._globalResetOwners += 1;\n\t\tconst prior = this._globalResetTail;\n\t\tconst result = prior.then(async () => {\n\t\t\ttry {\n\t\t\t\tawait Promise.all(Array.from(this._sessionBarriers.values(), (state) => state.tail));\n\t\t\t\ttry {\n\t\t\t\t\tawait this.drain();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!(error instanceof PersistenceDrainError)) throw error;\n\t\t\t\t}\n\t\t\t\tconst value = await operation();\n\t\t\t\tthis._queue.discardAll();\n\t\t\t\tthis._quarantinedSessions.clear();\n\t\t\t\tthis._evictionQuarantinedSessions.clear();\n\t\t\t\tthis._globalQuarantined = false;\n\t\t\t\treturn value;\n\t\t\t} catch (error) {\n\t\t\t\tthis._globalQuarantined = true;\n\t\t\t\tthrow error;\n\t\t\t} finally {\n\t\t\t\tthis._globalResetOwners -= 1;\n\t\t\t}\n\t\t});\n\t\tthis._globalResetTail = result.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined\n\t\t);\n\t\treturn result;\n\t}\n\n\tprivate _joinOrStart(mode: PersistenceSelectionMode): ActiveDrain {\n\t\tconst active = this._activeDrain;\n\t\tif (active !== null) {\n\t\t\tif (mode === 'explicit') active.mode = mode;\n\t\t\treturn active;\n\t\t}\n\n\t\tlet resolveGeneration = (): void => undefined;\n\t\tlet rejectGeneration = (_reason: unknown): void => undefined;\n\t\tconst promise = new Promise<void>((resolve, reject) => {\n\t\t\tresolveGeneration = resolve;\n\t\t\trejectGeneration = reject;\n\t\t});\n\t\tconst generation: ActiveDrain = {\n\t\t\tpromise,\n\t\t\tresolve: resolveGeneration,\n\t\t\treject: rejectGeneration,\n\t\t\tselectedTokens: new Set<PersistenceWorkToken>(),\n\t\t\tmode,\n\t\t\tbackgroundObserved: false,\n\t\t\tinFlightWork: null,\n\t\t\tsettlement: null,\n\t\t};\n\t\tthis._activeDrain = generation;\n\t\tvoid this._runDrain(generation).catch((fault: unknown) => {\n\t\t\tthis._closeGeneration(generation, { kind: 'fault', fault });\n\t\t});\n\t\treturn generation;\n\t}\n\n\tprivate _triggerBackgroundDrain(): void {\n\t\tconst generation = this._joinOrStart('background');\n\t\tif (generation.backgroundObserved) return;\n\t\tgeneration.backgroundObserved = true;\n\t\tvoid generation.promise.catch(() => undefined);\n\t}\n\n\tprivate async _runDrain(generation: ActiveDrain): Promise<void> {\n\t\twhile (true) {\n\t\t\tconst work = this._queue.nextEligibleWork(generation.selectedTokens, generation.mode);\n\t\t\tif (work === undefined) {\n\t\t\t\tthis._closeGeneration(generation, { kind: 'result' });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tgeneration.inFlightWork = work;\n\t\t\tconst result = await this._writer.write(work);\n\t\t\tif (result === true) this._queue.acknowledgeSuccess(work);\n\t\t\telse this._queue.acknowledgeFailure(work, result);\n\t\t\tgeneration.inFlightWork = null;\n\t\t\tthis._notifySessionProgress();\n\t\t}\n\t}\n\n\tprivate _closeGeneration(generation: ActiveDrain, termination: DrainTermination): void {\n\t\tif (this._activeDrain !== generation) return;\n\t\tlet settlement: DrainSettlement;\n\t\tswitch (termination.kind) {\n\t\t\tcase 'result': {\n\t\t\t\tconst result = this._queue.generationResult(generation.selectedTokens);\n\t\t\t\tsettlement =\n\t\t\t\t\tresult.failures.length === 0\n\t\t\t\t\t\t? { kind: 'resolved' }\n\t\t\t\t\t\t: { kind: 'rejected', reason: new PersistenceDrainError(result.failures) };\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'fault':\n\t\t\t\tsettlement = { kind: 'rejected', reason: termination.fault };\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tassertNever(termination);\n\t\t}\n\n\t\tgeneration.inFlightWork = null;\n\t\tgeneration.settlement = settlement;\n\t\tthis._activeDrain = null;\n\t\tthis._notifySessionProgress();\n\t\tswitch (settlement.kind) {\n\t\t\tcase 'resolved':\n\t\t\t\tgeneration.resolve();\n\t\t\t\treturn;\n\t\t\tcase 'rejected':\n\t\t\t\tgeneration.reject(settlement.reason);\n\t\t\t\tif (settlement.reason instanceof PersistenceDrainError) {\n\t\t\t\t\tthis._observeFailure(generation, settlement.reason);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\tdefault:\n\t\t\t\tassertNever(settlement);\n\t\t}\n\t}\n\n\tprivate async _awaitSessionQuiescence(sessionId: SessionId): Promise<void> {\n\t\tconst generation = this._joinOrStart('explicit');\n\t\tvoid generation.promise.catch(() => undefined);\n\t\twhile (true) {\n\t\t\tconst quiescence = this._sessionQuiescence(generation, sessionId);\n\t\t\tswitch (quiescence.kind) {\n\t\t\t\tcase 'pending':\n\t\t\t\t\tawait this._waitForSessionProgress(sessionId);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'resolved':\n\t\t\t\t\treturn;\n\t\t\t\tcase 'rejected':\n\t\t\t\t\tthrow quiescence.reason;\n\t\t\t\tdefault:\n\t\t\t\t\treturn assertNever(quiescence);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate _sessionQuiescence(generation: ActiveDrain, sessionId: SessionId): SessionQuiescence {\n\t\tif (\n\t\t\tgeneration.settlement?.kind === 'rejected' &&\n\t\t\t!(generation.settlement.reason instanceof PersistenceDrainError)\n\t\t) {\n\t\t\treturn { kind: 'rejected', reason: generation.settlement.reason };\n\t\t}\n\t\tif (generation.inFlightWork?.sessionId === sessionId) return { kind: 'pending' };\n\t\tif (this._queue.hasEligibleWork(generation.selectedTokens, generation.mode, sessionId)) {\n\t\t\treturn { kind: 'pending' };\n\t\t}\n\n\t\tconst failures = this._queue.currentFailures(generation.selectedTokens, sessionId);\n\t\treturn failures.length === 0\n\t\t\t? { kind: 'resolved' }\n\t\t\t: { kind: 'rejected', reason: new PersistenceDrainError(failures) };\n\t}\n\n\tprivate _waitForSessionProgress(sessionId: SessionId): Promise<void> {\n\t\treturn new Promise((resolve) => {\n\t\t\tconst waiters = this._sessionProgressWaiters.get(sessionId) ?? new Set<() => void>();\n\t\t\twaiters.add(resolve);\n\t\t\tthis._sessionProgressWaiters.set(sessionId, waiters);\n\t\t});\n\t}\n\n\tprivate _notifySessionProgress(): void {\n\t\tfor (const [sessionId, waiters] of this._sessionProgressWaiters) {\n\t\t\tthis._sessionProgressWaiters.delete(sessionId);\n\t\t\tfor (const resolve of waiters) resolve();\n\t\t}\n\t}\n\n\tprivate _assertSessionAdmissionOpen(sessionId: SessionId): void {\n\t\tif (\n\t\t\tthis._globalResetOwners > 0 ||\n\t\t\tthis._globalQuarantined ||\n\t\t\tthis._sessionBarriers.has(sessionId) ||\n\t\t\tthis._quarantinedSessions.has(sessionId) ||\n\t\t\tthis._evictionQuarantinedSessions.has(sessionId)\n\t\t) {\n\t\t\tthrow new PersistenceSessionAdmissionClosedError(sessionId);\n\t\t}\n\t}\n\n\tprivate _observeFailure(generation: ActiveDrain, error: PersistenceDrainError): void {\n\t\tthis._logger.info('Persistence drain completed with failures', {\n\t\t\tfailed: error.failures.length,\n\t\t\tselected: generation.selectedTokens.size,\n\t\t});\n\t\tthis._eventEmitter?.emit('persistenceError', { operation: 'flushBuffer', error });\n\t}\n}\n"],"names":["PersistenceBuffer","PersistenceWorkQueue","Map","AsyncLocalStorage","Set","Promise","config","NullLogger","PersistenceWriter","emitter","sessionId","thought","branchId","thoughts","edges","summaries","setInterval","clearInterval","generation","reason","PersistenceDrainError","failures","failure","operation","PersistenceSessionAdmissionClosedError","currentOwnership","PersistenceSessionBarrierReentrancyError","operationOwnership","state","result","value","error","undefined","prior","Array","mode","active","resolveGeneration","rejectGeneration","_reason","promise","resolve","reject","fault","work","termination","settlement","assertNever","quiescence","waiters"],"mappings":"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"}
1
+ {"version":3,"file":"core/PersistenceBuffer.js","sources":["../../src/core/PersistenceBuffer.ts"],"sourcesContent":["/**\n * Joinable coordinator for attributable persistence work.\n *\n * @module PersistenceBuffer\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\n\nimport type { PersistenceBackend } from '../contracts/PersistenceBackend.js';\nimport type { BranchId, SessionId, ThoughtId } from '../contracts/ids.js';\nimport type { PersistenceWork, PersistenceWorkToken } from '../contracts/persistence-work.js';\nimport {\n\tPersistenceDrainError,\n\tPersistenceSessionAdmissionClosedError,\n\tPersistenceSessionBarrierReentrancyError,\n} from './PersistenceBufferErrors.js';\nimport { NullLogger } from '../logger/NullLogger.js';\nimport type { Logger } from '../logger/StructuredLogger.js';\nimport { assertNever } from '../utils.js';\nimport type { Summary } from './compression/Summary.js';\nimport type { DurableIdentitySession } from './DurableThoughtIdentityIndex.js';\nimport type { Edge } from './graph/Edge.js';\nimport { PersistenceWorkQueue, type PersistenceSelectionMode } from './PersistenceWorkQueue.js';\nimport { PersistenceWriter, type PersistenceDelay } from './PersistenceWriter.js';\nimport type { ThoughtData } from './thought.js';\n\n/** Event emitter contract for persistence error events. */\nexport interface PersistenceEventEmitter {\n\temit(event: 'persistenceError', payload: { operation: string; error: Error }): boolean;\n}\n\n/** Observable persistence state relevant to safe in-memory session eviction. */\nexport type SessionEvictionState = 'quiescent' | 'pending' | 'failed' | 'barrier';\n\n/** Configuration options for {@link PersistenceBuffer}. */\nexport interface PersistenceBufferConfig {\n\treadonly persistence: PersistenceBackend;\n\treadonly bufferSize: number;\n\treadonly flushInterval: number;\n\treadonly maxRetries: number;\n\t/** Optional emitter for `persistenceError` events. */\n\treadonly eventEmitter?: PersistenceEventEmitter | null;\n\treadonly logger?: Logger;\n\t/** Optional retry scheduler for deterministic coordination and testing. */\n\treadonly delay?: PersistenceDelay;\n\treadonly durableHistorySize?: number;\n\treadonly persistBranches?: boolean;\n}\n\ntype ActiveDrain = {\n\treadonly promise: Promise<void>;\n\treadonly resolve: () => void;\n\treadonly reject: (reason: unknown) => void;\n\treadonly selectedTokens: Set<PersistenceWorkToken>;\n\tmode: PersistenceSelectionMode;\n\tbackgroundObserved: boolean;\n\tinFlightWork: PersistenceWork | null;\n\tsettlement: DrainSettlement | null;\n};\n\ntype SessionBarrierState = {\n\ttail: Promise<void>;\n\tpendingOwners: number;\n};\n\ntype DrainTermination =\n\t{ readonly kind: 'result' } | { readonly kind: 'fault'; readonly fault: unknown };\n\ntype DrainSettlement =\n\t{ readonly kind: 'resolved' } | { readonly kind: 'rejected'; readonly reason: unknown };\n\ntype SessionQuiescence =\n\t| { readonly kind: 'pending' }\n\t| { readonly kind: 'resolved' }\n\t| { readonly kind: 'rejected'; readonly reason: unknown };\n\n/**\n * Coordinates one globally joinable persistence generation at a time.\n *\n * Accepted work is owned by a queue independent of live session state. Explicit\n * callers can join and upgrade a background generation without starting another writer.\n */\nexport class PersistenceBuffer {\n\tprivate readonly _bufferSize: number;\n\tprivate readonly _flushInterval: number;\n\tprivate readonly _queue: PersistenceWorkQueue;\n\tprivate readonly _writer: PersistenceWriter;\n\tprivate _eventEmitter: PersistenceEventEmitter | null;\n\tprivate readonly _logger: Logger;\n\n\tprivate _flushTimer: ReturnType<typeof setInterval> | null = null;\n\tprivate _activeDrain: ActiveDrain | null = null;\n\tprivate readonly _sessionBarriers = new Map<SessionId, SessionBarrierState>();\n\tprivate readonly _sessionBarrierOwnership = new AsyncLocalStorage<ReadonlySet<SessionId>>();\n\tprivate readonly _sessionProgressWaiters = new Map<SessionId, Set<() => void>>();\n\tprivate readonly _quarantinedSessions = new Set<SessionId>();\n\tprivate readonly _evictionQuarantinedSessions = new Set<SessionId>();\n\tprivate _globalResetTail: Promise<void> = Promise.resolve();\n\tprivate _globalResetOwners = 0;\n\tprivate _globalQuarantined = false;\n\n\t/**\n\t * Creates a persistence-drain coordinator.\n\t *\n\t * @param config - Persistence dependencies, trigger thresholds, and retry policy.\n\t */\n\tpublic constructor(config: PersistenceBufferConfig) {\n\t\tthis._queue = new PersistenceWorkQueue(config.durableHistorySize, config.persistBranches);\n\t\tthis._bufferSize = config.bufferSize;\n\t\tthis._flushInterval = config.flushInterval;\n\t\tthis._eventEmitter = config.eventEmitter ?? null;\n\t\tthis._logger = config.logger ?? new NullLogger();\n\t\tthis._writer = new PersistenceWriter({\n\t\t\tpersistence: config.persistence,\n\t\t\tmaxRetries: config.maxRetries,\n\t\t\tdelay: config.delay,\n\t\t});\n\t}\n\n\t/** @returns The underlying flush timer for lifecycle introspection. */\n\tpublic get timer(): ReturnType<typeof setInterval> | null {\n\t\treturn this._flushTimer;\n\t}\n\n\t/** @returns Whether a global drain generation is active. */\n\tpublic get isFlushing(): boolean {\n\t\treturn this._activeDrain !== null;\n\t}\n\n\t/** @returns Number of accepted thought writes not yet acknowledged successful. */\n\tpublic get pendingThoughtCount(): number {\n\t\treturn this._queue.pendingThoughtCount;\n\t}\n\n\tpublic hasThoughtIdentity(sessionId: SessionId, thoughtId: ThoughtId): boolean {\n\t\treturn this._queue.hasThoughtIdentity(sessionId, thoughtId);\n\t}\n\n\tpublic replaceDurableThoughtIdentities(sessions: readonly DurableIdentitySession[]): void {\n\t\tthis._queue.replaceDurableThoughtIdentities(sessions);\n\t}\n\n\t/**\n\t * Sets or clears the persistence error event emitter.\n\t *\n\t * @param emitter - Replacement emitter, or `null` to disable events.\n\t */\n\tpublic setEventEmitter(emitter: PersistenceEventEmitter | null): void {\n\t\tthis._eventEmitter = emitter;\n\t}\n\n\t/**\n\t * Accepts a thought with authoritative session attribution.\n\t *\n\t * @param sessionId - Session that owns the accepted thought.\n\t * @param thought - Thought to persist.\n\t */\n\tpublic bufferThought(sessionId: SessionId, thought: ThoughtData): void {\n\t\tthis._assertSessionAdmissionOpen(sessionId);\n\t\tif (this._queue.pendingThoughtCount >= this._bufferSize && this.isFlushing) {\n\t\t\tthis._logger.info('Write buffer full and flush in progress, applying backpressure', {\n\t\t\t\tbufferSize: this._queue.pendingThoughtCount,\n\t\t\t\tmaxSize: this._bufferSize,\n\t\t\t});\n\t\t}\n\n\t\tthis._queue.enqueueThought(sessionId, thought);\n\t\tif (this._queue.pendingThoughtCount >= this._bufferSize) this._triggerBackgroundDrain();\n\t}\n\n\tpublic bufferBacktrack(\n\t\tsessionId: SessionId,\n\t\tthought: ThoughtData,\n\t\ttargetThoughtId: ThoughtId\n\t): void {\n\t\tthis._assertSessionAdmissionOpen(sessionId);\n\t\tthis._queue.enqueueBacktrack(sessionId, thought, targetThoughtId);\n\t\tif (this._queue.pendingThoughtCount >= this._bufferSize) this._triggerBackgroundDrain();\n\t}\n\n\t/**\n\t * Accepts the latest snapshot for one session-owned branch.\n\t *\n\t * @param sessionId - Session that owns the branch.\n\t * @param branchId - Stable branch coordinate.\n\t * @param thoughts - Branch snapshot copied by the queue.\n\t */\n\tpublic bufferBranch(\n\t\tsessionId: SessionId,\n\t\tbranchId: BranchId,\n\t\tthoughts: readonly ThoughtData[]\n\t): void {\n\t\tthis._assertSessionAdmissionOpen(sessionId);\n\t\tthis._queue.replaceBranch(sessionId, branchId, thoughts);\n\t}\n\n\t/**\n\t * @param sessionId - Session that owns the branch.\n\t * @param branchId - Stable branch coordinate to delete.\n\t */\n\tpublic deleteBranch(sessionId: SessionId, branchId: BranchId): void {\n\t\tthis._assertSessionAdmissionOpen(sessionId);\n\t\tthis._queue.deleteBranch(sessionId, branchId);\n\t}\n\n\t/**\n\t * Accepts the latest edge snapshot for one session.\n\t *\n\t * @param sessionId - Session that owns the edges.\n\t * @param edges - Edge snapshot copied by the queue.\n\t */\n\tpublic bufferEdges(sessionId: SessionId, edges: readonly Edge[]): void {\n\t\tthis._assertSessionAdmissionOpen(sessionId);\n\t\tthis._queue.replaceEdges(sessionId, edges);\n\t}\n\n\t/**\n\t * Accepts the latest summary snapshot for one session.\n\t *\n\t * @param sessionId - Session that owns the summaries.\n\t * @param summaries - Summary snapshot copied by the queue.\n\t */\n\tpublic bufferSummaries(sessionId: SessionId, summaries: readonly Summary[]): void {\n\t\tthis._assertSessionAdmissionOpen(sessionId);\n\t\tthis._queue.replaceSummaries(sessionId, summaries);\n\t}\n\n\t/** Rejects synchronously when lifecycle coordination has closed persistence admission. */\n\tpublic assertSessionAdmissionOpen(sessionId: SessionId): void {\n\t\tthis._assertSessionAdmissionOpen(sessionId);\n\t}\n\n\t/** Starts the periodic background-drain timer without keeping the process alive. */\n\tpublic startFlushTimer(): void {\n\t\tif (this._flushTimer !== null) return;\n\t\tthis._flushTimer = setInterval(() => this._triggerBackgroundDrain(), this._flushInterval);\n\t\tif (typeof this._flushTimer === 'object' && 'unref' in this._flushTimer) {\n\t\t\tthis._flushTimer.unref();\n\t\t}\n\t}\n\n\t/** Stops the periodic background-drain timer. */\n\tpublic stopFlushTimer(): void {\n\t\tif (this._flushTimer === null) return;\n\t\tclearInterval(this._flushTimer);\n\t\tthis._flushTimer = null;\n\t}\n\n\t/**\n\t * Starts or joins an explicit global drain generation.\n\t *\n\t * @returns The exact shared promise for the active generation.\n\t */\n\tpublic drain(): Promise<void> {\n\t\treturn this._joinOrStart('explicit').promise;\n\t}\n\n\t/** @returns The exact same promise as {@link drain} for the active generation. */\n\tpublic flush(): Promise<void> {\n\t\treturn this.drain();\n\t}\n\n\t/**\n\t * Joins the global explicit generation and projects its terminal failures to one session.\n\t * This method does not close admission; lifecycle owners must use\n\t * {@link withSessionBarrier} when work must not cross an owner callback.\n\t *\n\t * @param sessionId - Session whose accepted writes form the barrier projection.\n\t * @returns A promise that rejects only for that session's failures or an unknown fault.\n\t */\n\tpublic drainSession(sessionId: SessionId): Promise<void> {\n\t\tconst generation = this._joinOrStart('explicit');\n\t\treturn generation.promise.catch((reason: unknown) => {\n\t\t\tif (!(reason instanceof PersistenceDrainError)) throw reason;\n\t\t\tconst failures = reason.failures.filter((failure) => failure.sessionId === sessionId);\n\t\t\tif (failures.length > 0) throw new PersistenceDrainError(failures);\n\t\t});\n\t}\n\n\t/** Returns queue-derived persistence state for safe session eviction planning. */\n\tpublic sessionEvictionState(sessionId: SessionId): SessionEvictionState {\n\t\tif (this._globalResetOwners > 0 || this._sessionBarriers.has(sessionId)) return 'barrier';\n\t\tif (\n\t\t\tthis._globalQuarantined ||\n\t\t\tthis._queue.hasSessionTerminalFailure(sessionId) ||\n\t\t\tthis._quarantinedSessions.has(sessionId) ||\n\t\t\tthis._evictionQuarantinedSessions.has(sessionId)\n\t\t) {\n\t\t\treturn 'failed';\n\t\t}\n\t\treturn this._queue.hasSessionWork(sessionId) ? 'pending' : 'quiescent';\n\t}\n\n\t/**\n\t * Drains accepted session work before ordinary in-memory eviction cleanup.\n\t *\n\t * Unlike reset barriers, this operation never clears durable state or discards\n\t * accepted work. Failure retains admission quarantine until a later explicit\n\t * invocation successfully drains and completes cleanup.\n\t */\n\tpublic withSessionEvictionBarrier<T>(\n\t\tsessionId: SessionId,\n\t\toperation: () => Promise<T>\n\t): Promise<T> {\n\t\tif (\n\t\t\tthis._globalResetOwners > 0 ||\n\t\t\tthis._globalQuarantined ||\n\t\t\tthis._quarantinedSessions.has(sessionId)\n\t\t) {\n\t\t\treturn Promise.reject(new PersistenceSessionAdmissionClosedError(sessionId));\n\t\t}\n\t\tconst currentOwnership = this._sessionBarrierOwnership.getStore();\n\t\tif (currentOwnership?.has(sessionId) === true) {\n\t\t\treturn Promise.reject(new PersistenceSessionBarrierReentrancyError(sessionId));\n\t\t}\n\t\tconst operationOwnership = new Set(currentOwnership);\n\t\toperationOwnership.add(sessionId);\n\t\tconst state = this._sessionBarriers.get(sessionId) ?? {\n\t\t\ttail: Promise.resolve(),\n\t\t\tpendingOwners: 0,\n\t\t};\n\t\tthis._sessionBarriers.set(sessionId, state);\n\t\tstate.pendingOwners += 1;\n\n\t\tconst result = state.tail.then(async () => {\n\t\t\ttry {\n\t\t\t\tawait this._awaitSessionQuiescence(sessionId);\n\t\t\t\tconst value = await this._sessionBarrierOwnership.run(operationOwnership, operation);\n\t\t\t\tthis._queue.forgetQuiescentSession(sessionId);\n\t\t\t\tthis._evictionQuarantinedSessions.delete(sessionId);\n\t\t\t\treturn value;\n\t\t\t} catch (error) {\n\t\t\t\tthis._evictionQuarantinedSessions.add(sessionId);\n\t\t\t\tthrow error;\n\t\t\t} finally {\n\t\t\t\tstate.pendingOwners -= 1;\n\t\t\t\tif (state.pendingOwners === 0) this._sessionBarriers.delete(sessionId);\n\t\t\t}\n\t\t});\n\t\tstate.tail = result.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined\n\t\t);\n\t\treturn result;\n\t}\n\n\t/** Forgets queue coordinates only when the session is fully quiescent. */\n\tpublic forgetQuiescentSession(sessionId: SessionId): boolean {\n\t\treturn this.sessionEvictionState(sessionId) === 'quiescent'\n\t\t\t? this._queue.forgetQuiescentSession(sessionId)\n\t\t\t: false;\n\t}\n\n\t/**\n\t * Runs one lifecycle operation while persistence admission is closed for a session.\n\t *\n\t * Admission closes synchronously when this method is called. Already accepted work for\n\t * the session is settled first; an exhausted session failure rejects without invoking\n\t * `operation`. Concurrent calls for the same session execute in FIFO call order and keep\n\t * admission closed until the final callback settles. Other sessions remain admissible and\n\t * may be owned by a nested callback. Reacquiring the same session from the owning callback's\n\t * async call chain rejects before joining the FIFO because awaiting it would deadlock its owner.\n\t *\n\t * @example\n\t * ```ts\n\t * await buffer.withSessionBarrier(sessionId, async () => {\n\t * await persistence.clearSession(sessionId);\n\t * });\n\t * ```\n\t *\n\t * @param sessionId - Session exclusively owned for the callback duration.\n\t * @param operation - Awaited lifecycle operation run only after prior work settles.\n\t * @returns The callback result after admission has reopened when no owner remains.\n\t * @throws {@link PersistenceSessionBarrierReentrancyError} when the owning async call chain\n\t * attempts to reacquire `sessionId`.\n\t */\n\tpublic withSessionBarrier<T>(sessionId: SessionId, operation: () => Promise<T>): Promise<T> {\n\t\tconst currentOwnership = this._sessionBarrierOwnership.getStore();\n\t\tif (currentOwnership?.has(sessionId) === true) {\n\t\t\treturn Promise.reject(new PersistenceSessionBarrierReentrancyError(sessionId));\n\t\t}\n\t\tconst operationOwnership = new Set(currentOwnership);\n\t\toperationOwnership.add(sessionId);\n\n\t\tconst state = this._sessionBarriers.get(sessionId) ?? {\n\t\t\ttail: Promise.resolve(),\n\t\t\tpendingOwners: 0,\n\t\t};\n\t\tthis._sessionBarriers.set(sessionId, state);\n\t\tstate.pendingOwners += 1;\n\n\t\tconst result = state.tail.then(async () => {\n\t\t\ttry {\n\t\t\t\tawait this._awaitSessionQuiescence(sessionId);\n\t\t\t\treturn await this._sessionBarrierOwnership.run(operationOwnership, operation);\n\t\t\t} finally {\n\t\t\t\tstate.pendingOwners -= 1;\n\t\t\t\tif (state.pendingOwners === 0) this._sessionBarriers.delete(sessionId);\n\t\t\t}\n\t\t});\n\t\tstate.tail = result.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined\n\t\t);\n\t\treturn result;\n\t}\n\n\t/**\n\t * Runs a scoped reset, retaining admission quarantine on failure.\n\t * A retry may pass retained write failures because successful durable deletion\n\t * invalidates those stale queue entries before admission reopens.\n\t */\n\tpublic withSessionResetBarrier<T>(sessionId: SessionId, operation: () => Promise<T>): Promise<T> {\n\t\tif (this._globalResetOwners > 0 || this._globalQuarantined) {\n\t\t\treturn Promise.reject(new PersistenceSessionAdmissionClosedError(sessionId));\n\t\t}\n\t\tconst currentOwnership = this._sessionBarrierOwnership.getStore();\n\t\tif (currentOwnership?.has(sessionId) === true) {\n\t\t\treturn Promise.reject(new PersistenceSessionBarrierReentrancyError(sessionId));\n\t\t}\n\t\tconst operationOwnership = new Set(currentOwnership);\n\t\toperationOwnership.add(sessionId);\n\t\tconst state = this._sessionBarriers.get(sessionId) ?? {\n\t\t\ttail: Promise.resolve(),\n\t\t\tpendingOwners: 0,\n\t\t};\n\t\tthis._sessionBarriers.set(sessionId, state);\n\t\tstate.pendingOwners += 1;\n\n\t\tconst result = state.tail.then(async () => {\n\t\t\ttry {\n\t\t\t\ttry {\n\t\t\t\t\tawait this._awaitSessionQuiescence(sessionId);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!(error instanceof PersistenceDrainError)) throw error;\n\t\t\t\t}\n\t\t\t\tconst value = await this._sessionBarrierOwnership.run(operationOwnership, operation);\n\t\t\t\tthis._queue.discardSession(sessionId);\n\t\t\t\tthis._quarantinedSessions.delete(sessionId);\n\t\t\t\tthis._evictionQuarantinedSessions.delete(sessionId);\n\t\t\t\treturn value;\n\t\t\t} catch (error) {\n\t\t\t\tthis._quarantinedSessions.add(sessionId);\n\t\t\t\tthrow error;\n\t\t\t} finally {\n\t\t\t\tstate.pendingOwners -= 1;\n\t\t\t\tif (state.pendingOwners === 0) this._sessionBarriers.delete(sessionId);\n\t\t\t}\n\t\t});\n\t\tstate.tail = result.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined\n\t\t);\n\t\treturn result;\n\t}\n\n\t/** Runs a trusted global reset behind a global admission barrier. */\n\tpublic withGlobalResetBarrier<T>(operation: () => Promise<T>): Promise<T> {\n\t\tthis._globalResetOwners += 1;\n\t\tconst prior = this._globalResetTail;\n\t\tconst result = prior.then(async () => {\n\t\t\ttry {\n\t\t\t\tawait Promise.all(Array.from(this._sessionBarriers.values(), (state) => state.tail));\n\t\t\t\ttry {\n\t\t\t\t\tawait this.drain();\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!(error instanceof PersistenceDrainError)) throw error;\n\t\t\t\t}\n\t\t\t\tconst value = await operation();\n\t\t\t\tthis._queue.discardAll();\n\t\t\t\tthis._quarantinedSessions.clear();\n\t\t\t\tthis._evictionQuarantinedSessions.clear();\n\t\t\t\tthis._globalQuarantined = false;\n\t\t\t\treturn value;\n\t\t\t} catch (error) {\n\t\t\t\tthis._globalQuarantined = true;\n\t\t\t\tthrow error;\n\t\t\t} finally {\n\t\t\t\tthis._globalResetOwners -= 1;\n\t\t\t}\n\t\t});\n\t\tthis._globalResetTail = result.then(\n\t\t\t() => undefined,\n\t\t\t() => undefined\n\t\t);\n\t\treturn result;\n\t}\n\n\tprivate _joinOrStart(mode: PersistenceSelectionMode): ActiveDrain {\n\t\tconst active = this._activeDrain;\n\t\tif (active !== null) {\n\t\t\tif (mode === 'explicit') active.mode = mode;\n\t\t\treturn active;\n\t\t}\n\n\t\tlet resolveGeneration = (): void => undefined;\n\t\tlet rejectGeneration = (_reason: unknown): void => undefined;\n\t\tconst promise = new Promise<void>((resolve, reject) => {\n\t\t\tresolveGeneration = resolve;\n\t\t\trejectGeneration = reject;\n\t\t});\n\t\tconst generation: ActiveDrain = {\n\t\t\tpromise,\n\t\t\tresolve: resolveGeneration,\n\t\t\treject: rejectGeneration,\n\t\t\tselectedTokens: new Set<PersistenceWorkToken>(),\n\t\t\tmode,\n\t\t\tbackgroundObserved: false,\n\t\t\tinFlightWork: null,\n\t\t\tsettlement: null,\n\t\t};\n\t\tthis._activeDrain = generation;\n\t\tvoid this._runDrain(generation).catch((fault: unknown) => {\n\t\t\tthis._closeGeneration(generation, { kind: 'fault', fault });\n\t\t});\n\t\treturn generation;\n\t}\n\n\tprivate _triggerBackgroundDrain(): void {\n\t\tconst generation = this._joinOrStart('background');\n\t\tif (generation.backgroundObserved) return;\n\t\tgeneration.backgroundObserved = true;\n\t\tvoid generation.promise.catch(() => undefined);\n\t}\n\n\tprivate async _runDrain(generation: ActiveDrain): Promise<void> {\n\t\twhile (true) {\n\t\t\tconst work = this._queue.nextEligibleWork(generation.selectedTokens, generation.mode);\n\t\t\tif (work === undefined) {\n\t\t\t\tthis._closeGeneration(generation, { kind: 'result' });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tgeneration.inFlightWork = work;\n\t\t\tconst result = await this._writer.write(work);\n\t\t\tif (result === true) this._queue.acknowledgeSuccess(work);\n\t\t\telse this._queue.acknowledgeFailure(work, result);\n\t\t\tgeneration.inFlightWork = null;\n\t\t\tthis._notifySessionProgress();\n\t\t}\n\t}\n\n\tprivate _closeGeneration(generation: ActiveDrain, termination: DrainTermination): void {\n\t\tif (this._activeDrain !== generation) return;\n\t\tlet settlement: DrainSettlement;\n\t\tswitch (termination.kind) {\n\t\t\tcase 'result': {\n\t\t\t\tconst result = this._queue.generationResult(generation.selectedTokens);\n\t\t\t\tsettlement =\n\t\t\t\t\tresult.failures.length === 0\n\t\t\t\t\t\t? { kind: 'resolved' }\n\t\t\t\t\t\t: { kind: 'rejected', reason: new PersistenceDrainError(result.failures) };\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'fault':\n\t\t\t\tsettlement = { kind: 'rejected', reason: termination.fault };\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tassertNever(termination);\n\t\t}\n\n\t\tgeneration.inFlightWork = null;\n\t\tgeneration.settlement = settlement;\n\t\tthis._activeDrain = null;\n\t\tthis._notifySessionProgress();\n\t\tswitch (settlement.kind) {\n\t\t\tcase 'resolved':\n\t\t\t\tgeneration.resolve();\n\t\t\t\treturn;\n\t\t\tcase 'rejected':\n\t\t\t\tgeneration.reject(settlement.reason);\n\t\t\t\tif (settlement.reason instanceof PersistenceDrainError) {\n\t\t\t\t\tthis._observeFailure(generation, settlement.reason);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\tdefault:\n\t\t\t\tassertNever(settlement);\n\t\t}\n\t}\n\n\tprivate async _awaitSessionQuiescence(sessionId: SessionId): Promise<void> {\n\t\tconst generation = this._joinOrStart('explicit');\n\t\tvoid generation.promise.catch(() => undefined);\n\t\twhile (true) {\n\t\t\tconst quiescence = this._sessionQuiescence(generation, sessionId);\n\t\t\tswitch (quiescence.kind) {\n\t\t\t\tcase 'pending':\n\t\t\t\t\tawait this._waitForSessionProgress(sessionId);\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'resolved':\n\t\t\t\t\treturn;\n\t\t\t\tcase 'rejected':\n\t\t\t\t\tthrow quiescence.reason;\n\t\t\t\tdefault:\n\t\t\t\t\treturn assertNever(quiescence);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate _sessionQuiescence(generation: ActiveDrain, sessionId: SessionId): SessionQuiescence {\n\t\tif (\n\t\t\tgeneration.settlement?.kind === 'rejected' &&\n\t\t\t!(generation.settlement.reason instanceof PersistenceDrainError)\n\t\t) {\n\t\t\treturn { kind: 'rejected', reason: generation.settlement.reason };\n\t\t}\n\t\tif (generation.inFlightWork?.sessionId === sessionId) return { kind: 'pending' };\n\t\tif (this._queue.hasEligibleWork(generation.selectedTokens, generation.mode, sessionId)) {\n\t\t\treturn { kind: 'pending' };\n\t\t}\n\n\t\tconst failures = this._queue.currentFailures(generation.selectedTokens, sessionId);\n\t\treturn failures.length === 0\n\t\t\t? { kind: 'resolved' }\n\t\t\t: { kind: 'rejected', reason: new PersistenceDrainError(failures) };\n\t}\n\n\tprivate _waitForSessionProgress(sessionId: SessionId): Promise<void> {\n\t\treturn new Promise((resolve) => {\n\t\t\tconst waiters = this._sessionProgressWaiters.get(sessionId) ?? new Set<() => void>();\n\t\t\twaiters.add(resolve);\n\t\t\tthis._sessionProgressWaiters.set(sessionId, waiters);\n\t\t});\n\t}\n\n\tprivate _notifySessionProgress(): void {\n\t\tfor (const [sessionId, waiters] of this._sessionProgressWaiters) {\n\t\t\tthis._sessionProgressWaiters.delete(sessionId);\n\t\t\tfor (const resolve of waiters) resolve();\n\t\t}\n\t}\n\n\tprivate _assertSessionAdmissionOpen(sessionId: SessionId): void {\n\t\tif (\n\t\t\tthis._globalResetOwners > 0 ||\n\t\t\tthis._globalQuarantined ||\n\t\t\tthis._sessionBarriers.has(sessionId) ||\n\t\t\tthis._quarantinedSessions.has(sessionId) ||\n\t\t\tthis._evictionQuarantinedSessions.has(sessionId)\n\t\t) {\n\t\t\tthrow new PersistenceSessionAdmissionClosedError(sessionId);\n\t\t}\n\t}\n\n\tprivate _observeFailure(generation: ActiveDrain, error: PersistenceDrainError): void {\n\t\tthis._logger.info('Persistence drain completed with failures', {\n\t\t\tfailed: error.failures.length,\n\t\t\tselected: generation.selectedTokens.size,\n\t\t});\n\t\tthis._eventEmitter?.emit('persistenceError', { operation: 'flushBuffer', error });\n\t}\n}\n"],"names":["PersistenceBuffer","Map","AsyncLocalStorage","Set","Promise","config","PersistenceWorkQueue","NullLogger","PersistenceWriter","sessionId","thoughtId","sessions","emitter","thought","targetThoughtId","branchId","thoughts","edges","summaries","setInterval","clearInterval","generation","reason","PersistenceDrainError","failures","failure","operation","PersistenceSessionAdmissionClosedError","currentOwnership","PersistenceSessionBarrierReentrancyError","operationOwnership","state","result","value","error","undefined","prior","Array","mode","active","resolveGeneration","rejectGeneration","_reason","promise","resolve","reject","fault","work","termination","settlement","assertNever","quiescence","waiters"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"PersistenceRestore.d.ts","sourceRoot":"","sources":["../../src/core/PersistenceRestore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,EAAe,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAYxD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,MAAM,MAAM,cAAc,GAAG;IAC5B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;CAC1C,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC7B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IACzC,QAAQ,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAC;IAC7C,QAAQ,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,SAAS,OAAO,EAAE,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACxC,QAAQ,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAC;CAC9C,CAAC;AAiEF,wBAAsB,uBAAuB,CAC5C,OAAO,EAAE,kBAAkB,GACzB,OAAO,CAAC,0BAA0B,CAAC,CAgBrC"}
1
+ {"version":3,"file":"PersistenceRestore.d.ts","sourceRoot":"","sources":["../../src/core/PersistenceRestore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,EAAe,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAYxD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,MAAM,MAAM,cAAc,GAAG;IAC5B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;CAC1C,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC7B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IACzC,QAAQ,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAC;IAC7C,QAAQ,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,SAAS,OAAO,EAAE,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACxC,QAAQ,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,CAAC;CAC9C,CAAC;AAqEF,wBAAsB,uBAAuB,CAC5C,OAAO,EAAE,kBAAkB,GACzB,OAAO,CAAC,0BAA0B,CAAC,CAgBrC"}
@@ -2,6 +2,7 @@ import { asSessionId } from "../contracts/ids.js";
2
2
  import { PersistenceCompatibilityError, PersistenceUnavailableError } from "../errors.js";
3
3
  import { assertBranchScope, assertEdgeScopes, assertPersistableThoughtCollections, assertSummaryScopes, assertThoughtScope, assertUniqueRecordIds, parsePersistenceBranchId } from "../persistence/PersistenceScope.js";
4
4
  import { EdgeStore } from "./graph/EdgeStore.js";
5
+ import { repairRetainedBacktracks } from "../persistence/BacktrackPersistence.js";
5
6
  function validateThoughts(sessionId, history, branches) {
6
7
  for (const thought of history)assertThoughtScope('saveThoughtForSession', sessionId, thought);
7
8
  for (const branch of branches)assertBranchScope('saveBranchForSession', sessionId, branch.branchId, branch.thoughts);
@@ -35,7 +36,14 @@ async function stageSession(persistence, sessionId) {
35
36
  ])
36
37
  }));
37
38
  }
38
- validateThoughts(sessionId, history, branches);
39
+ const repaired = repairRetainedBacktracks(sessionId, history, branches);
40
+ const repairedBranches = repaired.branches.map((branch)=>Object.freeze({
41
+ branchId: branch.branchId,
42
+ thoughts: Object.freeze([
43
+ ...branch.thoughts
44
+ ])
45
+ }));
46
+ validateThoughts(sessionId, repaired.history, repairedBranches);
39
47
  const edges = await persistence.loadEdges(sessionId);
40
48
  assertEdgeScopes(sessionId, edges);
41
49
  assertUniqueRecordIds(edges, `restore:${sessionId}:edges`, 'edge');
@@ -47,9 +55,9 @@ async function stageSession(persistence, sessionId) {
47
55
  return Object.freeze({
48
56
  sessionId,
49
57
  history: Object.freeze([
50
- ...history
58
+ ...repaired.history
51
59
  ]),
52
- branches: Object.freeze(branches),
60
+ branches: Object.freeze(repairedBranches),
53
61
  edges: Object.freeze([
54
62
  ...edges
55
63
  ]),
@@ -1 +1 @@
1
- {"version":3,"file":"core/PersistenceRestore.js","sources":["../../src/core/PersistenceRestore.ts"],"sourcesContent":["import type { PersistenceBackend } from '../contracts/PersistenceBackend.js';\nimport { asSessionId, type BranchId, type SessionId } from '../contracts/ids.js';\nimport type { Summary } from './compression/Summary.js';\nimport { PersistenceCompatibilityError, PersistenceUnavailableError } from '../errors.js';\nimport {\n\tassertBranchScope,\n\tassertEdgeScopes,\n\tassertPersistableThoughtCollections,\n\tassertSummaryScopes,\n\tassertThoughtScope,\n\tassertUniqueRecordIds,\n\tparsePersistenceBranchId,\n\ttype PersistedThoughtCollection,\n} from '../persistence/PersistenceScope.js';\nimport type { Edge } from './graph/Edge.js';\nimport { EdgeStore } from './graph/EdgeStore.js';\nimport type { ThoughtData } from './thought.js';\n\nexport type RestoredBranch = {\n\treadonly branchId: BranchId;\n\treadonly thoughts: readonly ThoughtData[];\n};\n\nexport type RestoredSession = {\n\treadonly sessionId: SessionId;\n\treadonly history: readonly ThoughtData[];\n\treadonly branches: readonly RestoredBranch[];\n\treadonly edges: readonly Edge[];\n\treadonly summaries: readonly Summary[];\n};\n\nexport type PersistenceRestoreSnapshot = {\n\treadonly sessions: readonly RestoredSession[];\n};\n\nfunction validateThoughts(\n\tsessionId: SessionId,\n\thistory: readonly ThoughtData[],\n\tbranches: readonly RestoredBranch[]\n): void {\n\tfor (const thought of history) assertThoughtScope('saveThoughtForSession', sessionId, thought);\n\tfor (const branch of branches) {\n\t\tassertBranchScope('saveBranchForSession', sessionId, branch.branchId, branch.thoughts);\n\t}\n\tconst collections: PersistedThoughtCollection[] = [\n\t\t{ sessionId, thoughts: history },\n\t\t...branches.map((branch) => ({ sessionId, thoughts: branch.thoughts })),\n\t];\n\tassertPersistableThoughtCollections(collections, `restore:${sessionId}`);\n}\n\nasync function stageSession(\n\tpersistence: PersistenceBackend,\n\tsessionId: SessionId\n): Promise<RestoredSession> {\n\tconst history = await persistence.loadHistoryForSession(sessionId);\n\tconst listedBranches = await persistence.listBranchesForSession(sessionId);\n\tconst branches: RestoredBranch[] = [];\n\tconst seenBranches = new Set<BranchId>();\n\tfor (const listedBranchId of listedBranches) {\n\t\tconst branchId = parsePersistenceBranchId(listedBranchId, `restore:${sessionId}:branches`);\n\t\tif (seenBranches.has(branchId)) {\n\t\t\tthrow new PersistenceCompatibilityError(\n\t\t\t\t`restore:${sessionId}:branches`,\n\t\t\t\t`duplicate branch id '${branchId}'`\n\t\t\t);\n\t\t}\n\t\tseenBranches.add(branchId);\n\t\tconst thoughts = await persistence.loadBranchForSession(sessionId, branchId);\n\t\tif (thoughts === undefined) {\n\t\t\tthrow new PersistenceCompatibilityError(\n\t\t\t\t`restore:${sessionId}:branches:${branchId}`,\n\t\t\t\t'listed branch is missing'\n\t\t\t);\n\t\t}\n\t\tbranches.push(Object.freeze({ branchId, thoughts: Object.freeze([...thoughts]) }));\n\t}\n\tvalidateThoughts(sessionId, history, branches);\n\n\tconst edges = await persistence.loadEdges(sessionId);\n\tassertEdgeScopes(sessionId, edges);\n\tassertUniqueRecordIds(edges, `restore:${sessionId}:edges`, 'edge');\n\tconst validationStore = new EdgeStore();\n\tfor (const restoredEdge of edges) validationStore.addEdge(restoredEdge);\n\n\tconst summaries = await persistence.loadSummaries(sessionId);\n\tassertSummaryScopes(sessionId, summaries);\n\tassertUniqueRecordIds(summaries, `restore:${sessionId}:summaries`, 'summary');\n\n\treturn Object.freeze({\n\t\tsessionId,\n\t\thistory: Object.freeze([...history]),\n\t\tbranches: Object.freeze(branches),\n\t\tedges: Object.freeze([...edges]),\n\t\tsummaries: Object.freeze([...summaries]),\n\t});\n}\n\nexport async function stagePersistenceRestore(\n\tbackend: PersistenceBackend\n): Promise<PersistenceRestoreSnapshot> {\n\tif (!(await backend.healthy())) throw new PersistenceUnavailableError();\n\n\tconst listedSessions = await backend.listSessions();\n\tconst sessionIds: SessionId[] = [];\n\tconst seen = new Set<SessionId>();\n\tfor (const listedSessionId of listedSessions) {\n\t\tconst sessionId = asSessionId(listedSessionId);\n\t\tif (seen.has(sessionId)) continue;\n\t\tseen.add(sessionId);\n\t\tsessionIds.push(sessionId);\n\t}\n\n\tconst sessions: RestoredSession[] = [];\n\tfor (const sessionId of sessionIds) sessions.push(await stageSession(backend, sessionId));\n\treturn Object.freeze({ sessions: Object.freeze(sessions) });\n}\n"],"names":["validateThoughts","sessionId","history","branches","thought","assertThoughtScope","branch","assertBranchScope","collections","assertPersistableThoughtCollections","stageSession","persistence","listedBranches","seenBranches","Set","listedBranchId","branchId","parsePersistenceBranchId","PersistenceCompatibilityError","thoughts","undefined","Object","edges","assertEdgeScopes","assertUniqueRecordIds","validationStore","EdgeStore","restoredEdge","summaries","assertSummaryScopes","stagePersistenceRestore","backend","PersistenceUnavailableError","listedSessions","sessionIds","seen","listedSessionId","asSessionId","sessions"],"mappings":";;;;AAmCA,SAASA,iBACRC,SAAoB,EACpBC,OAA+B,EAC/BC,QAAmC;IAEnC,KAAK,MAAMC,WAAWF,QAASG,mBAAmB,yBAAyBJ,WAAWG;IACtF,KAAK,MAAME,UAAUH,SACpBI,kBAAkB,wBAAwBN,WAAWK,OAAO,QAAQ,EAAEA,OAAO,QAAQ;IAEtF,MAAME,cAA4C;QACjD;YAAEP;YAAW,UAAUC;QAAQ;WAC5BC,SAAS,GAAG,CAAC,CAACG,SAAY;gBAAEL;gBAAW,UAAUK,OAAO,QAAQ;YAAC;KACpE;IACDG,oCAAoCD,aAAa,CAAC,QAAQ,EAAEP,WAAW;AACxE;AAEA,eAAeS,aACdC,WAA+B,EAC/BV,SAAoB;IAEpB,MAAMC,UAAU,MAAMS,YAAY,qBAAqB,CAACV;IACxD,MAAMW,iBAAiB,MAAMD,YAAY,sBAAsB,CAACV;IAChE,MAAME,WAA6B,EAAE;IACrC,MAAMU,eAAe,IAAIC;IACzB,KAAK,MAAMC,kBAAkBH,eAAgB;QAC5C,MAAMI,WAAWC,yBAAyBF,gBAAgB,CAAC,QAAQ,EAAEd,UAAU,SAAS,CAAC;QACzF,IAAIY,aAAa,GAAG,CAACG,WACpB,MAAM,IAAIE,8BACT,CAAC,QAAQ,EAAEjB,UAAU,SAAS,CAAC,EAC/B,CAAC,qBAAqB,EAAEe,SAAS,CAAC,CAAC;QAGrCH,aAAa,GAAG,CAACG;QACjB,MAAMG,WAAW,MAAMR,YAAY,oBAAoB,CAACV,WAAWe;QACnE,IAAIG,AAAaC,WAAbD,UACH,MAAM,IAAID,8BACT,CAAC,QAAQ,EAAEjB,UAAU,UAAU,EAAEe,UAAU,EAC3C;QAGFb,SAAS,IAAI,CAACkB,OAAO,MAAM,CAAC;YAAEL;YAAU,UAAUK,OAAO,MAAM,CAAC;mBAAIF;aAAS;QAAE;IAChF;IACAnB,iBAAiBC,WAAWC,SAASC;IAErC,MAAMmB,QAAQ,MAAMX,YAAY,SAAS,CAACV;IAC1CsB,iBAAiBtB,WAAWqB;IAC5BE,sBAAsBF,OAAO,CAAC,QAAQ,EAAErB,UAAU,MAAM,CAAC,EAAE;IAC3D,MAAMwB,kBAAkB,IAAIC;IAC5B,KAAK,MAAMC,gBAAgBL,MAAOG,gBAAgB,OAAO,CAACE;IAE1D,MAAMC,YAAY,MAAMjB,YAAY,aAAa,CAACV;IAClD4B,oBAAoB5B,WAAW2B;IAC/BJ,sBAAsBI,WAAW,CAAC,QAAQ,EAAE3B,UAAU,UAAU,CAAC,EAAE;IAEnE,OAAOoB,OAAO,MAAM,CAAC;QACpBpB;QACA,SAASoB,OAAO,MAAM,CAAC;eAAInB;SAAQ;QACnC,UAAUmB,OAAO,MAAM,CAAClB;QACxB,OAAOkB,OAAO,MAAM,CAAC;eAAIC;SAAM;QAC/B,WAAWD,OAAO,MAAM,CAAC;eAAIO;SAAU;IACxC;AACD;AAEO,eAAeE,wBACrBC,OAA2B;IAE3B,IAAI,CAAE,MAAMA,QAAQ,OAAO,IAAK,MAAM,IAAIC;IAE1C,MAAMC,iBAAiB,MAAMF,QAAQ,YAAY;IACjD,MAAMG,aAA0B,EAAE;IAClC,MAAMC,OAAO,IAAIrB;IACjB,KAAK,MAAMsB,mBAAmBH,eAAgB;QAC7C,MAAMhC,YAAYoC,YAAYD;QAC9B,KAAID,KAAK,GAAG,CAAClC;YACbkC,KAAK,GAAG,CAAClC;YACTiC,WAAW,IAAI,CAACjC;;IACjB;IAEA,MAAMqC,WAA8B,EAAE;IACtC,KAAK,MAAMrC,aAAaiC,WAAYI,SAAS,IAAI,CAAC,MAAM5B,aAAaqB,SAAS9B;IAC9E,OAAOoB,OAAO,MAAM,CAAC;QAAE,UAAUA,OAAO,MAAM,CAACiB;IAAU;AAC1D"}
1
+ {"version":3,"file":"core/PersistenceRestore.js","sources":["../../src/core/PersistenceRestore.ts"],"sourcesContent":["import type { PersistenceBackend } from '../contracts/PersistenceBackend.js';\nimport { asSessionId, type BranchId, type SessionId } from '../contracts/ids.js';\nimport type { Summary } from './compression/Summary.js';\nimport { PersistenceCompatibilityError, PersistenceUnavailableError } from '../errors.js';\nimport {\n\tassertBranchScope,\n\tassertEdgeScopes,\n\tassertPersistableThoughtCollections,\n\tassertSummaryScopes,\n\tassertThoughtScope,\n\tassertUniqueRecordIds,\n\tparsePersistenceBranchId,\n\ttype PersistedThoughtCollection,\n} from '../persistence/PersistenceScope.js';\nimport type { Edge } from './graph/Edge.js';\nimport { EdgeStore } from './graph/EdgeStore.js';\nimport type { ThoughtData } from './thought.js';\nimport { repairRetainedBacktracks } from '../persistence/BacktrackPersistence.js';\n\nexport type RestoredBranch = {\n\treadonly branchId: BranchId;\n\treadonly thoughts: readonly ThoughtData[];\n};\n\nexport type RestoredSession = {\n\treadonly sessionId: SessionId;\n\treadonly history: readonly ThoughtData[];\n\treadonly branches: readonly RestoredBranch[];\n\treadonly edges: readonly Edge[];\n\treadonly summaries: readonly Summary[];\n};\n\nexport type PersistenceRestoreSnapshot = {\n\treadonly sessions: readonly RestoredSession[];\n};\n\nfunction validateThoughts(\n\tsessionId: SessionId,\n\thistory: readonly ThoughtData[],\n\tbranches: readonly RestoredBranch[]\n): void {\n\tfor (const thought of history) assertThoughtScope('saveThoughtForSession', sessionId, thought);\n\tfor (const branch of branches) {\n\t\tassertBranchScope('saveBranchForSession', sessionId, branch.branchId, branch.thoughts);\n\t}\n\tconst collections: PersistedThoughtCollection[] = [\n\t\t{ sessionId, thoughts: history },\n\t\t...branches.map((branch) => ({ sessionId, thoughts: branch.thoughts })),\n\t];\n\tassertPersistableThoughtCollections(collections, `restore:${sessionId}`);\n}\n\nasync function stageSession(\n\tpersistence: PersistenceBackend,\n\tsessionId: SessionId\n): Promise<RestoredSession> {\n\tconst history = await persistence.loadHistoryForSession(sessionId);\n\tconst listedBranches = await persistence.listBranchesForSession(sessionId);\n\tconst branches: RestoredBranch[] = [];\n\tconst seenBranches = new Set<BranchId>();\n\tfor (const listedBranchId of listedBranches) {\n\t\tconst branchId = parsePersistenceBranchId(listedBranchId, `restore:${sessionId}:branches`);\n\t\tif (seenBranches.has(branchId)) {\n\t\t\tthrow new PersistenceCompatibilityError(\n\t\t\t\t`restore:${sessionId}:branches`,\n\t\t\t\t`duplicate branch id '${branchId}'`\n\t\t\t);\n\t\t}\n\t\tseenBranches.add(branchId);\n\t\tconst thoughts = await persistence.loadBranchForSession(sessionId, branchId);\n\t\tif (thoughts === undefined) {\n\t\t\tthrow new PersistenceCompatibilityError(\n\t\t\t\t`restore:${sessionId}:branches:${branchId}`,\n\t\t\t\t'listed branch is missing'\n\t\t\t);\n\t\t}\n\t\tbranches.push(Object.freeze({ branchId, thoughts: Object.freeze([...thoughts]) }));\n\t}\n\tconst repaired = repairRetainedBacktracks(sessionId, history, branches);\n\tconst repairedBranches = repaired.branches.map((branch) =>\n\t\tObject.freeze({ branchId: branch.branchId, thoughts: Object.freeze([...branch.thoughts]) })\n\t);\n\tvalidateThoughts(sessionId, repaired.history, repairedBranches);\n\n\tconst edges = await persistence.loadEdges(sessionId);\n\tassertEdgeScopes(sessionId, edges);\n\tassertUniqueRecordIds(edges, `restore:${sessionId}:edges`, 'edge');\n\tconst validationStore = new EdgeStore();\n\tfor (const restoredEdge of edges) validationStore.addEdge(restoredEdge);\n\n\tconst summaries = await persistence.loadSummaries(sessionId);\n\tassertSummaryScopes(sessionId, summaries);\n\tassertUniqueRecordIds(summaries, `restore:${sessionId}:summaries`, 'summary');\n\n\treturn Object.freeze({\n\t\tsessionId,\n\t\thistory: Object.freeze([...repaired.history]),\n\t\tbranches: Object.freeze(repairedBranches),\n\t\tedges: Object.freeze([...edges]),\n\t\tsummaries: Object.freeze([...summaries]),\n\t});\n}\n\nexport async function stagePersistenceRestore(\n\tbackend: PersistenceBackend\n): Promise<PersistenceRestoreSnapshot> {\n\tif (!(await backend.healthy())) throw new PersistenceUnavailableError();\n\n\tconst listedSessions = await backend.listSessions();\n\tconst sessionIds: SessionId[] = [];\n\tconst seen = new Set<SessionId>();\n\tfor (const listedSessionId of listedSessions) {\n\t\tconst sessionId = asSessionId(listedSessionId);\n\t\tif (seen.has(sessionId)) continue;\n\t\tseen.add(sessionId);\n\t\tsessionIds.push(sessionId);\n\t}\n\n\tconst sessions: RestoredSession[] = [];\n\tfor (const sessionId of sessionIds) sessions.push(await stageSession(backend, sessionId));\n\treturn Object.freeze({ sessions: Object.freeze(sessions) });\n}\n"],"names":["validateThoughts","sessionId","history","branches","thought","assertThoughtScope","branch","assertBranchScope","collections","assertPersistableThoughtCollections","stageSession","persistence","listedBranches","seenBranches","Set","listedBranchId","branchId","parsePersistenceBranchId","PersistenceCompatibilityError","thoughts","undefined","Object","repaired","repairRetainedBacktracks","repairedBranches","edges","assertEdgeScopes","assertUniqueRecordIds","validationStore","EdgeStore","restoredEdge","summaries","assertSummaryScopes","stagePersistenceRestore","backend","PersistenceUnavailableError","listedSessions","sessionIds","seen","listedSessionId","asSessionId","sessions"],"mappings":"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"}
@@ -4,8 +4,9 @@
4
4
  * @module core/PersistenceWorkQueue
5
5
  */
6
6
  import type { PersistenceGenerationResult, PersistenceWork, PersistenceWorkFailure, PersistenceWorkToken } from '../contracts/persistence-work.js';
7
- import type { BranchId, SessionId } from '../contracts/ids.js';
7
+ import type { BranchId, SessionId, ThoughtId } from '../contracts/ids.js';
8
8
  import type { Summary } from './compression/Summary.js';
9
+ import { type DurableIdentitySession } from './DurableThoughtIdentityIndex.js';
9
10
  import type { Edge } from './graph/Edge.js';
10
11
  import type { ThoughtData } from './thought.js';
11
12
  /** Selects whether retained terminal failures are eligible for a generation. */
@@ -32,6 +33,9 @@ type WorkOf<K extends WorkKind> = Extract<PersistenceWork, {
32
33
  */
33
34
  export declare class PersistenceWorkQueue {
34
35
  private readonly _thoughts;
36
+ private readonly _thoughtIdentities;
37
+ private readonly _durableThoughtIdentities;
38
+ private readonly _persistBranches;
35
39
  private readonly _branches;
36
40
  private readonly _edges;
37
41
  private readonly _summaries;
@@ -39,6 +43,7 @@ export declare class PersistenceWorkQueue {
39
43
  private readonly _edgeVersions;
40
44
  private readonly _summaryVersions;
41
45
  private _nextSequence;
46
+ constructor(durableHistorySize?: number, persistBranches?: boolean);
42
47
  /**
43
48
  * Accepts one thought as a distinct FIFO work item.
44
49
  *
@@ -47,6 +52,9 @@ export declare class PersistenceWorkQueue {
47
52
  * @returns The exact immutable work handle used for acknowledgement.
48
53
  */
49
54
  enqueueThought(sessionId: SessionId, thought: ThoughtData): WorkOf<'thought'>;
55
+ enqueueBacktrack(sessionId: SessionId, thought: ThoughtData, targetThoughtId: ThoughtId): WorkOf<'backtrack'>;
56
+ hasThoughtIdentity(sessionId: SessionId, thoughtId: ThoughtId): boolean;
57
+ replaceDurableThoughtIdentities(sessions: readonly DurableIdentitySession[]): void;
50
58
  /**
51
59
  * Replaces the pending snapshot for one session-owned branch.
52
60
  *
@@ -144,6 +152,10 @@ export declare class PersistenceWorkQueue {
144
152
  /** Discards all retained work after a successful durable global reset. */
145
153
  discardAll(): void;
146
154
  private _accept;
155
+ private _freezeClone;
156
+ private _deepFreeze;
157
+ private _addThoughtIdentity;
158
+ private _removeThoughtIdentity;
147
159
  private _entries;
148
160
  private _findEligible;
149
161
  private _modeAllows;
@@ -1 +1 @@
1
- {"version":3,"file":"PersistenceWorkQueue.d.ts","sourceRoot":"","sources":["../../src/core/PersistenceWorkQueue.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PersistenceWorkQueue.d.ts","sourceRoot":"","sources":["../../src/core/PersistenceWorkQueue.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,10 @@
1
1
  import { assertNever } from "../utils.js";
2
+ import { DurableThoughtIdentityIndex, normalizeDurableHistoryRetention } from "./DurableThoughtIdentityIndex.js";
2
3
  class PersistenceWorkQueue {
3
4
  _thoughts = [];
5
+ _thoughtIdentities = new Map();
6
+ _durableThoughtIdentities;
7
+ _persistBranches;
4
8
  _branches = new Map();
5
9
  _edges = new Map();
6
10
  _summaries = new Map();
@@ -8,21 +12,49 @@ class PersistenceWorkQueue {
8
12
  _edgeVersions = new Map();
9
13
  _summaryVersions = new Map();
10
14
  _nextSequence = 1;
15
+ constructor(durableHistorySize = 10000, persistBranches = true){
16
+ this._durableThoughtIdentities = new DurableThoughtIdentityIndex(normalizeDurableHistoryRetention(durableHistorySize));
17
+ this._persistBranches = persistBranches;
18
+ }
11
19
  enqueueThought(sessionId, thought) {
12
20
  const acceptance = this._accept();
13
21
  const work = Object.freeze({
14
22
  kind: 'thought',
15
23
  token: acceptance.token,
16
24
  sessionId,
17
- thought
25
+ thought: this._freezeClone(thought)
26
+ });
27
+ this._thoughts.push({
28
+ acceptedSequence: acceptance.sequence,
29
+ work,
30
+ terminalFailure: void 0
31
+ });
32
+ if (void 0 !== thought.id) this._addThoughtIdentity(sessionId, thought.id);
33
+ return work;
34
+ }
35
+ enqueueBacktrack(sessionId, thought, targetThoughtId) {
36
+ const acceptance = this._accept();
37
+ const work = Object.freeze({
38
+ kind: 'backtrack',
39
+ token: acceptance.token,
40
+ sessionId,
41
+ thought: this._freezeClone(thought),
42
+ targetThoughtId
18
43
  });
19
44
  this._thoughts.push({
20
45
  acceptedSequence: acceptance.sequence,
21
46
  work,
22
47
  terminalFailure: void 0
23
48
  });
49
+ if (void 0 !== thought.id) this._addThoughtIdentity(sessionId, thought.id);
24
50
  return work;
25
51
  }
52
+ hasThoughtIdentity(sessionId, thoughtId) {
53
+ return this._thoughtIdentities.get(sessionId)?.has(thoughtId) === true || this._durableThoughtIdentities.has(sessionId, thoughtId);
54
+ }
55
+ replaceDurableThoughtIdentities(sessions) {
56
+ this._durableThoughtIdentities.replaceAll(sessions);
57
+ }
26
58
  replaceBranch(sessionId, branchId, thoughts) {
27
59
  const versionMap = this._branchVersions.get(sessionId) ?? new Map();
28
60
  this._branchVersions.set(sessionId, versionMap);
@@ -36,9 +68,7 @@ class PersistenceWorkQueue {
36
68
  sessionId,
37
69
  key: branchId,
38
70
  version,
39
- snapshot: Object.freeze([
40
- ...thoughts
41
- ])
71
+ snapshot: this._freezeClone(thoughts)
42
72
  });
43
73
  const branchMap = this._branches.get(sessionId) ?? new Map();
44
74
  this._branches.set(sessionId, branchMap);
@@ -82,9 +112,7 @@ class PersistenceWorkQueue {
82
112
  sessionId,
83
113
  key: sessionId,
84
114
  version,
85
- snapshot: Object.freeze([
86
- ...edges
87
- ])
115
+ snapshot: this._freezeClone(edges)
88
116
  });
89
117
  this._edges.set(sessionId, {
90
118
  acceptedSequence: acceptance.sequence,
@@ -103,9 +131,7 @@ class PersistenceWorkQueue {
103
131
  sessionId,
104
132
  key: sessionId,
105
133
  version,
106
- snapshot: Object.freeze([
107
- ...summaries
108
- ])
134
+ snapshot: this._freezeClone(summaries)
109
135
  });
110
136
  this._summaries.set(sessionId, {
111
137
  acceptedSequence: acceptance.sequence,
@@ -130,11 +156,28 @@ class PersistenceWorkQueue {
130
156
  case 'thought':
131
157
  {
132
158
  const index = this._thoughts.findIndex((candidate)=>candidate === entry);
133
- if (index >= 0) this._thoughts.splice(index, 1);
159
+ if (index >= 0) {
160
+ this._thoughts.splice(index, 1);
161
+ if (void 0 !== work.thought.id) this._removeThoughtIdentity(work.sessionId, work.thought.id);
162
+ this._durableThoughtIdentities.appendThought(work.sessionId, work.thought);
163
+ }
164
+ return;
165
+ }
166
+ case 'backtrack':
167
+ {
168
+ const index = this._thoughts.findIndex((candidate)=>candidate === entry);
169
+ if (index >= 0) {
170
+ this._thoughts.splice(index, 1);
171
+ if (void 0 !== work.thought.id) this._removeThoughtIdentity(work.sessionId, work.thought.id);
172
+ this._durableThoughtIdentities.appendThought(work.sessionId, work.thought);
173
+ }
134
174
  return;
135
175
  }
136
176
  case 'branch':
137
177
  this._branches.get(work.sessionId)?.delete(work.key);
178
+ if (!this._persistBranches) return;
179
+ if ('save' === work.operation) this._durableThoughtIdentities.replaceBranch(work.sessionId, work.key, work.snapshot);
180
+ else this._durableThoughtIdentities.deleteBranch(work.sessionId, work.key);
138
181
  return;
139
182
  case 'edge':
140
183
  this._edges.delete(work.key);
@@ -188,6 +231,8 @@ class PersistenceWorkQueue {
188
231
  }
189
232
  discardSession(sessionId) {
190
233
  for(let index = this._thoughts.length - 1; index >= 0; index -= 1)if (this._thoughts[index]?.work.sessionId === sessionId) this._thoughts.splice(index, 1);
234
+ this._thoughtIdentities.delete(sessionId);
235
+ this._durableThoughtIdentities.clearSession(sessionId);
191
236
  this._branches.delete(sessionId);
192
237
  this._edges.delete(sessionId);
193
238
  this._summaries.delete(sessionId);
@@ -197,6 +242,8 @@ class PersistenceWorkQueue {
197
242
  }
198
243
  discardAll() {
199
244
  this._thoughts.length = 0;
245
+ this._thoughtIdentities.clear();
246
+ this._durableThoughtIdentities.clearAll();
200
247
  this._branches.clear();
201
248
  this._edges.clear();
202
249
  this._summaries.clear();
@@ -212,6 +259,30 @@ class PersistenceWorkQueue {
212
259
  sequence
213
260
  };
214
261
  }
262
+ _freezeClone(value) {
263
+ const clone = structuredClone(value);
264
+ return this._deepFreeze(clone);
265
+ }
266
+ _deepFreeze(value) {
267
+ if (null !== value && 'object' == typeof value) {
268
+ for (const nested of Object.values(value))this._deepFreeze(nested);
269
+ Object.freeze(value);
270
+ }
271
+ return value;
272
+ }
273
+ _addThoughtIdentity(sessionId, thoughtId) {
274
+ const identities = this._thoughtIdentities.get(sessionId) ?? new Map();
275
+ identities.set(thoughtId, (identities.get(thoughtId) ?? 0) + 1);
276
+ this._thoughtIdentities.set(sessionId, identities);
277
+ }
278
+ _removeThoughtIdentity(sessionId, thoughtId) {
279
+ const identities = this._thoughtIdentities.get(sessionId);
280
+ const count = identities?.get(thoughtId);
281
+ if (void 0 === identities || void 0 === count) return;
282
+ if (count > 1) identities.set(thoughtId, count - 1);
283
+ else identities.delete(thoughtId);
284
+ if (0 === identities.size) this._thoughtIdentities.delete(sessionId);
285
+ }
215
286
  _entries() {
216
287
  const entries = [
217
288
  ...this._thoughts
@@ -247,6 +318,7 @@ class PersistenceWorkQueue {
247
318
  let entry;
248
319
  switch(work.kind){
249
320
  case 'thought':
321
+ case 'backtrack':
250
322
  entry = this._thoughts.find((candidate)=>candidate.work.token === work.token);
251
323
  break;
252
324
  case 'branch':
@@ -268,6 +340,8 @@ class PersistenceWorkQueue {
268
340
  switch(selected.kind){
269
341
  case 'thought':
270
342
  return 'thought' === current.kind;
343
+ case 'backtrack':
344
+ return 'backtrack' === current.kind && current.targetThoughtId === selected.targetThoughtId;
271
345
  case 'branch':
272
346
  return 'branch' === current.kind && current.operation === selected.operation && current.key === selected.key && current.version === selected.version;
273
347
  case 'edge':
@@ -282,6 +356,7 @@ class PersistenceWorkQueue {
282
356
  if (work.kind !== failure.kind || work.token !== failure.token || work.sessionId !== failure.sessionId) return false;
283
357
  switch(work.kind){
284
358
  case 'thought':
359
+ case 'backtrack':
285
360
  return true;
286
361
  case 'branch':
287
362
  return 'branch' === failure.kind && work.operation === failure.operation && work.key === failure.key && work.version === failure.version;