@iworkforces/tracelattice 2.1.1 → 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.
- package/README.md +20 -3
- package/dist/contracts/PersistenceBackend.d.ts +10 -1
- package/dist/contracts/PersistenceBackend.d.ts.map +1 -1
- package/dist/contracts/calibrator.d.ts +9 -6
- package/dist/contracts/calibrator.d.ts.map +1 -1
- package/dist/contracts/interfaces.d.ts +6 -0
- package/dist/contracts/interfaces.d.ts.map +1 -1
- package/dist/contracts/persistence-work.d.ts +13 -1
- package/dist/contracts/persistence-work.d.ts.map +1 -1
- package/dist/contracts/strategy.d.ts +14 -9
- package/dist/contracts/strategy.d.ts.map +1 -1
- package/dist/core/DurableThoughtIdentityIndex.d.ts +35 -0
- package/dist/core/DurableThoughtIdentityIndex.d.ts.map +1 -0
- package/dist/core/DurableThoughtIdentityIndex.js +94 -0
- package/dist/core/DurableThoughtIdentityIndex.js.map +1 -0
- package/dist/core/HistoryManager.d.ts +5 -0
- package/dist/core/HistoryManager.d.ts.map +1 -1
- package/dist/core/HistoryManager.js +37 -4
- package/dist/core/HistoryManager.js.map +1 -1
- package/dist/core/IHistoryManager.d.ts +2 -0
- package/dist/core/IHistoryManager.d.ts.map +1 -1
- package/dist/core/PersistenceBuffer.d.ts +7 -1
- package/dist/core/PersistenceBuffer.d.ts.map +1 -1
- package/dist/core/PersistenceBuffer.js +13 -1
- package/dist/core/PersistenceBuffer.js.map +1 -1
- package/dist/core/PersistenceRestore.d.ts.map +1 -1
- package/dist/core/PersistenceRestore.js +11 -3
- package/dist/core/PersistenceRestore.js.map +1 -1
- package/dist/core/PersistenceWorkQueue.d.ts +13 -1
- package/dist/core/PersistenceWorkQueue.d.ts.map +1 -1
- package/dist/core/PersistenceWorkQueue.js +86 -11
- package/dist/core/PersistenceWorkQueue.js.map +1 -1
- package/dist/core/PersistenceWriter.d.ts.map +1 -1
- package/dist/core/PersistenceWriter.js +3 -0
- package/dist/core/PersistenceWriter.js.map +1 -1
- package/dist/core/ThoughtEvaluator.d.ts +7 -4
- package/dist/core/ThoughtEvaluator.d.ts.map +1 -1
- package/dist/core/ThoughtEvaluator.js +15 -7
- package/dist/core/ThoughtEvaluator.js.map +1 -1
- package/dist/core/ThoughtProcessor.d.ts.map +1 -1
- package/dist/core/ThoughtProcessor.js +19 -7
- package/dist/core/ThoughtProcessor.js.map +1 -1
- package/dist/core/ThoughtReferenceIndex.d.ts +4 -0
- package/dist/core/ThoughtReferenceIndex.d.ts.map +1 -1
- package/dist/core/ThoughtReferenceIndex.js +28 -1
- package/dist/core/ThoughtReferenceIndex.js.map +1 -1
- package/dist/core/VerificationOutcomeAdmission.d.ts +8 -1
- package/dist/core/VerificationOutcomeAdmission.d.ts.map +1 -1
- package/dist/core/VerificationOutcomeAdmission.js.map +1 -1
- package/dist/core/VerificationTargetRestore.d.ts +5 -0
- package/dist/core/VerificationTargetRestore.d.ts.map +1 -0
- package/dist/core/VerificationTargetRestore.js +61 -0
- package/dist/core/VerificationTargetRestore.js.map +1 -0
- package/dist/core/evaluator/Aggregator.d.ts +2 -1
- package/dist/core/evaluator/Aggregator.d.ts.map +1 -1
- package/dist/core/evaluator/Aggregator.js +4 -12
- package/dist/core/evaluator/Aggregator.js.map +1 -1
- package/dist/core/evaluator/Calibrator.d.ts +13 -9
- package/dist/core/evaluator/Calibrator.d.ts.map +1 -1
- package/dist/core/evaluator/Calibrator.js +25 -11
- package/dist/core/evaluator/Calibrator.js.map +1 -1
- package/dist/core/evaluator/PatternDetector.d.ts +2 -1
- package/dist/core/evaluator/PatternDetector.d.ts.map +1 -1
- package/dist/core/evaluator/PatternDetector.js +13 -12
- package/dist/core/evaluator/PatternDetector.js.map +1 -1
- package/dist/core/evaluator/SignalComputer.d.ts +3 -2
- package/dist/core/evaluator/SignalComputer.d.ts.map +1 -1
- package/dist/core/evaluator/SignalComputer.js +4 -9
- package/dist/core/evaluator/SignalComputer.js.map +1 -1
- package/dist/core/evaluator/VerificationLinks.d.ts +19 -0
- package/dist/core/evaluator/VerificationLinks.d.ts.map +1 -0
- package/dist/core/evaluator/VerificationLinks.js +113 -0
- package/dist/core/evaluator/VerificationLinks.js.map +1 -0
- package/dist/core/evaluator/calibration-math.d.ts +12 -2
- package/dist/core/evaluator/calibration-math.d.ts.map +1 -1
- package/dist/core/evaluator/calibration-math.js +11 -2
- package/dist/core/evaluator/calibration-math.js.map +1 -1
- package/dist/core/graph/EdgeEmitter.d.ts.map +1 -1
- package/dist/core/graph/EdgeEmitter.js +2 -1
- package/dist/core/graph/EdgeEmitter.js.map +1 -1
- package/dist/core/graph/GraphView.d.ts +5 -5
- package/dist/core/graph/GraphView.d.ts.map +1 -1
- package/dist/core/graph/GraphView.js +11 -11
- package/dist/core/graph/GraphView.js.map +1 -1
- package/dist/core/reasoning/ActiveEvidenceProjection.d.ts +20 -0
- package/dist/core/reasoning/ActiveEvidenceProjection.d.ts.map +1 -0
- package/dist/core/reasoning/ActiveEvidenceProjection.js +100 -0
- package/dist/core/reasoning/ActiveEvidenceProjection.js.map +1 -0
- package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.d.ts +5 -4
- package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.d.ts.map +1 -1
- package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.js +24 -14
- package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.js.map +1 -1
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +3 -1
- package/dist/lib.js.map +1 -1
- package/dist/persistence/BacktrackPersistence.d.ts +13 -0
- package/dist/persistence/BacktrackPersistence.d.ts.map +1 -0
- package/dist/persistence/BacktrackPersistence.js +90 -0
- package/dist/persistence/BacktrackPersistence.js.map +1 -0
- package/dist/persistence/FilePersistence.d.ts +2 -1
- package/dist/persistence/FilePersistence.d.ts.map +1 -1
- package/dist/persistence/FilePersistence.js +28 -0
- package/dist/persistence/FilePersistence.js.map +1 -1
- package/dist/persistence/FileSnapshotV2.d.ts +2 -0
- package/dist/persistence/FileSnapshotV2.d.ts.map +1 -1
- package/dist/persistence/FileSnapshotV2.js +6 -2
- package/dist/persistence/FileSnapshotV2.js.map +1 -1
- package/dist/persistence/MemoryPersistence.d.ts +2 -1
- package/dist/persistence/MemoryPersistence.d.ts.map +1 -1
- package/dist/persistence/MemoryPersistence.js +21 -1
- package/dist/persistence/MemoryPersistence.js.map +1 -1
- package/dist/persistence/PersistenceCodec.d.ts.map +1 -1
- package/dist/persistence/PersistenceCodec.js +5 -0
- package/dist/persistence/PersistenceCodec.js.map +1 -1
- package/dist/persistence/PersistenceErrors.d.ts +1 -1
- package/dist/persistence/PersistenceErrors.d.ts.map +1 -1
- package/dist/persistence/PersistenceErrors.js.map +1 -1
- package/dist/persistence/PersistenceScope.d.ts +1 -1
- package/dist/persistence/PersistenceScope.d.ts.map +1 -1
- package/dist/persistence/PersistenceScope.js.map +1 -1
- package/dist/persistence/SqlitePersistence.d.ts +2 -1
- package/dist/persistence/SqlitePersistence.d.ts.map +1 -1
- package/dist/persistence/SqlitePersistence.js +18 -0
- package/dist/persistence/SqlitePersistence.js.map +1 -1
- package/dist/registry/BaseRegistry.d.ts +10 -2
- package/dist/registry/BaseRegistry.d.ts.map +1 -1
- package/dist/registry/BaseRegistry.js +128 -81
- package/dist/registry/BaseRegistry.js.map +1 -1
- 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;
|
|
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
|
-
|
|
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(
|
|
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\
|
|
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":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EACX,2BAA2B,EAC3B,eAAe,EACf,sBAAsB,EACtB,oBAAoB,EACpB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;
|
|
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:
|
|
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:
|
|
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:
|
|
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)
|
|
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;
|