@iworkforces/tracelattice 2.0.3

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 (422) hide show
  1. package/LICENSE +24 -0
  2. package/README.md +260 -0
  3. package/dist/CliLifecycle.d.ts +31 -0
  4. package/dist/CliLifecycle.d.ts.map +1 -0
  5. package/dist/CliLifecycle.js +90 -0
  6. package/dist/CliLifecycle.js.map +1 -0
  7. package/dist/ServerConfig.d.ts +290 -0
  8. package/dist/ServerConfig.d.ts.map +1 -0
  9. package/dist/ServerConfig.js +175 -0
  10. package/dist/ServerConfig.js.map +1 -0
  11. package/dist/cache/DiscoveryCache.d.ts +269 -0
  12. package/dist/cache/DiscoveryCache.d.ts.map +1 -0
  13. package/dist/cache/DiscoveryCache.js +104 -0
  14. package/dist/cache/DiscoveryCache.js.map +1 -0
  15. package/dist/cli.d.ts +3 -0
  16. package/dist/cli.d.ts.map +1 -0
  17. package/dist/cli.js +5177 -0
  18. package/dist/cli.js.map +1 -0
  19. package/dist/config/ConfigLoader.d.ts +270 -0
  20. package/dist/config/ConfigLoader.d.ts.map +1 -0
  21. package/dist/config/ConfigLoader.js +186 -0
  22. package/dist/config/ConfigLoader.js.map +1 -0
  23. package/dist/context/RequestContext.d.ts +43 -0
  24. package/dist/context/RequestContext.d.ts.map +1 -0
  25. package/dist/context/RequestContext.js +14 -0
  26. package/dist/context/RequestContext.js.map +1 -0
  27. package/dist/contracts/PersistenceBackend.d.ts +134 -0
  28. package/dist/contracts/PersistenceBackend.d.ts.map +1 -0
  29. package/dist/contracts/PersistenceBackend.js +6 -0
  30. package/dist/contracts/PersistenceBackend.js.map +1 -0
  31. package/dist/contracts/calibrator.d.ts +97 -0
  32. package/dist/contracts/calibrator.d.ts.map +1 -0
  33. package/dist/contracts/calibrator.js +1 -0
  34. package/dist/contracts/features.d.ts +35 -0
  35. package/dist/contracts/features.d.ts.map +1 -0
  36. package/dist/contracts/features.js +12 -0
  37. package/dist/contracts/features.js.map +1 -0
  38. package/dist/contracts/ids.d.ts +68 -0
  39. package/dist/contracts/ids.d.ts.map +1 -0
  40. package/dist/contracts/ids.js +40 -0
  41. package/dist/contracts/ids.js.map +1 -0
  42. package/dist/contracts/interfaces.d.ts +254 -0
  43. package/dist/contracts/interfaces.d.ts.map +1 -0
  44. package/dist/contracts/interfaces.js +1 -0
  45. package/dist/contracts/persistence-work.d.ts +96 -0
  46. package/dist/contracts/persistence-work.d.ts.map +1 -0
  47. package/dist/contracts/persistence-work.js +1 -0
  48. package/dist/contracts/reasoning-types.d.ts +21 -0
  49. package/dist/contracts/reasoning-types.d.ts.map +1 -0
  50. package/dist/contracts/reasoning-types.js +1 -0
  51. package/dist/contracts/strategy.d.ts +107 -0
  52. package/dist/contracts/strategy.d.ts.map +1 -0
  53. package/dist/contracts/strategy.js +1 -0
  54. package/dist/contracts/summary.d.ts +110 -0
  55. package/dist/contracts/summary.d.ts.map +1 -0
  56. package/dist/contracts/summary.js +1 -0
  57. package/dist/contracts/suspension.d.ts +215 -0
  58. package/dist/contracts/suspension.d.ts.map +1 -0
  59. package/dist/contracts/suspension.js +1 -0
  60. package/dist/contracts/transport.d.ts +26 -0
  61. package/dist/contracts/transport.d.ts.map +1 -0
  62. package/dist/contracts/transport.js +1 -0
  63. package/dist/core/CrossReferenceValidator.d.ts +18 -0
  64. package/dist/core/CrossReferenceValidator.d.ts.map +1 -0
  65. package/dist/core/CrossReferenceValidator.js +125 -0
  66. package/dist/core/CrossReferenceValidator.js.map +1 -0
  67. package/dist/core/HistoryManager.d.ts +215 -0
  68. package/dist/core/HistoryManager.d.ts.map +1 -0
  69. package/dist/core/HistoryManager.js +594 -0
  70. package/dist/core/HistoryManager.js.map +1 -0
  71. package/dist/core/IHistoryManager.d.ts +163 -0
  72. package/dist/core/IHistoryManager.d.ts.map +1 -0
  73. package/dist/core/IHistoryManager.js +1 -0
  74. package/dist/core/IThoughtFormatter.d.ts +51 -0
  75. package/dist/core/IThoughtFormatter.d.ts.map +1 -0
  76. package/dist/core/IThoughtFormatter.js +1 -0
  77. package/dist/core/InputNormalizer.d.ts +140 -0
  78. package/dist/core/InputNormalizer.d.ts.map +1 -0
  79. package/dist/core/InputNormalizer.js +95 -0
  80. package/dist/core/InputNormalizer.js.map +1 -0
  81. package/dist/core/PersistenceBuffer.d.ts +204 -0
  82. package/dist/core/PersistenceBuffer.d.ts.map +1 -0
  83. package/dist/core/PersistenceBuffer.js +382 -0
  84. package/dist/core/PersistenceBuffer.js.map +1 -0
  85. package/dist/core/PersistenceBufferErrors.d.ts +22 -0
  86. package/dist/core/PersistenceBufferErrors.d.ts.map +1 -0
  87. package/dist/core/PersistenceBufferErrors.js +30 -0
  88. package/dist/core/PersistenceBufferErrors.js.map +1 -0
  89. package/dist/core/PersistenceRestore.d.ts +21 -0
  90. package/dist/core/PersistenceRestore.d.ts.map +1 -0
  91. package/dist/core/PersistenceRestore.js +87 -0
  92. package/dist/core/PersistenceRestore.js.map +1 -0
  93. package/dist/core/PersistenceWorkQueue.d.ts +155 -0
  94. package/dist/core/PersistenceWorkQueue.d.ts.map +1 -0
  95. package/dist/core/PersistenceWorkQueue.js +298 -0
  96. package/dist/core/PersistenceWorkQueue.js.map +1 -0
  97. package/dist/core/PersistenceWriter.d.ts +55 -0
  98. package/dist/core/PersistenceWriter.d.ts.map +1 -0
  99. package/dist/core/PersistenceWriter.js +112 -0
  100. package/dist/core/PersistenceWriter.js.map +1 -0
  101. package/dist/core/SessionErrors.d.ts +37 -0
  102. package/dist/core/SessionErrors.d.ts.map +1 -0
  103. package/dist/core/SessionErrors.js +50 -0
  104. package/dist/core/SessionErrors.js.map +1 -0
  105. package/dist/core/SessionLifecycleCoordinator.d.ts +74 -0
  106. package/dist/core/SessionLifecycleCoordinator.d.ts.map +1 -0
  107. package/dist/core/SessionLifecycleCoordinator.js +222 -0
  108. package/dist/core/SessionLifecycleCoordinator.js.map +1 -0
  109. package/dist/core/SessionLock.d.ts +59 -0
  110. package/dist/core/SessionLock.d.ts.map +1 -0
  111. package/dist/core/SessionLock.js +39 -0
  112. package/dist/core/SessionLock.js.map +1 -0
  113. package/dist/core/SessionManager.d.ts +62 -0
  114. package/dist/core/SessionManager.d.ts.map +1 -0
  115. package/dist/core/SessionManager.js +75 -0
  116. package/dist/core/SessionManager.js.map +1 -0
  117. package/dist/core/SessionResetCoordinator.d.ts +37 -0
  118. package/dist/core/SessionResetCoordinator.d.ts.map +1 -0
  119. package/dist/core/SessionResetCoordinator.js +80 -0
  120. package/dist/core/SessionResetCoordinator.js.map +1 -0
  121. package/dist/core/ThoughtEvaluator.d.ts +48 -0
  122. package/dist/core/ThoughtEvaluator.d.ts.map +1 -0
  123. package/dist/core/ThoughtEvaluator.js +85 -0
  124. package/dist/core/ThoughtEvaluator.js.map +1 -0
  125. package/dist/core/ThoughtFormatter.d.ts +134 -0
  126. package/dist/core/ThoughtFormatter.d.ts.map +1 -0
  127. package/dist/core/ThoughtFormatter.js +118 -0
  128. package/dist/core/ThoughtFormatter.js.map +1 -0
  129. package/dist/core/ThoughtProcessor.d.ts +259 -0
  130. package/dist/core/ThoughtProcessor.d.ts.map +1 -0
  131. package/dist/core/ThoughtProcessor.js +451 -0
  132. package/dist/core/ThoughtProcessor.js.map +1 -0
  133. package/dist/core/ThoughtReferenceIndex.d.ts +23 -0
  134. package/dist/core/ThoughtReferenceIndex.d.ts.map +1 -0
  135. package/dist/core/ThoughtReferenceIndex.js +66 -0
  136. package/dist/core/ThoughtReferenceIndex.js.map +1 -0
  137. package/dist/core/VerificationOutcomeAdmission.d.ts +15 -0
  138. package/dist/core/VerificationOutcomeAdmission.d.ts.map +1 -0
  139. package/dist/core/VerificationOutcomeAdmission.js +28 -0
  140. package/dist/core/VerificationOutcomeAdmission.js.map +1 -0
  141. package/dist/core/compression/CompressionService.d.ts +76 -0
  142. package/dist/core/compression/CompressionService.d.ts.map +1 -0
  143. package/dist/core/compression/CompressionService.js +160 -0
  144. package/dist/core/compression/CompressionService.js.map +1 -0
  145. package/dist/core/compression/DehydrationPolicy.d.ts +75 -0
  146. package/dist/core/compression/DehydrationPolicy.d.ts.map +1 -0
  147. package/dist/core/compression/DehydrationPolicy.js +45 -0
  148. package/dist/core/compression/DehydrationPolicy.js.map +1 -0
  149. package/dist/core/compression/InMemorySummaryStore.d.ts +132 -0
  150. package/dist/core/compression/InMemorySummaryStore.d.ts.map +1 -0
  151. package/dist/core/compression/InMemorySummaryStore.js +69 -0
  152. package/dist/core/compression/InMemorySummaryStore.js.map +1 -0
  153. package/dist/core/compression/Summary.d.ts +74 -0
  154. package/dist/core/compression/Summary.d.ts.map +1 -0
  155. package/dist/core/compression/Summary.js +16 -0
  156. package/dist/core/compression/Summary.js.map +1 -0
  157. package/dist/core/evaluator/Aggregator.d.ts +40 -0
  158. package/dist/core/evaluator/Aggregator.d.ts.map +1 -0
  159. package/dist/core/evaluator/Aggregator.js +36 -0
  160. package/dist/core/evaluator/Aggregator.js.map +1 -0
  161. package/dist/core/evaluator/Calibrator.d.ts +52 -0
  162. package/dist/core/evaluator/Calibrator.d.ts.map +1 -0
  163. package/dist/core/evaluator/Calibrator.js +135 -0
  164. package/dist/core/evaluator/Calibrator.js.map +1 -0
  165. package/dist/core/evaluator/PatternDetector.d.ts +69 -0
  166. package/dist/core/evaluator/PatternDetector.d.ts.map +1 -0
  167. package/dist/core/evaluator/PatternDetector.js +192 -0
  168. package/dist/core/evaluator/PatternDetector.js.map +1 -0
  169. package/dist/core/evaluator/SignalComputer.d.ts +116 -0
  170. package/dist/core/evaluator/SignalComputer.d.ts.map +1 -0
  171. package/dist/core/evaluator/SignalComputer.js +110 -0
  172. package/dist/core/evaluator/SignalComputer.js.map +1 -0
  173. package/dist/core/evaluator/calibration-math.d.ts +39 -0
  174. package/dist/core/evaluator/calibration-math.d.ts.map +1 -0
  175. package/dist/core/evaluator/calibration-math.js +42 -0
  176. package/dist/core/evaluator/calibration-math.js.map +1 -0
  177. package/dist/core/evaluator/internals.d.ts +36 -0
  178. package/dist/core/evaluator/internals.d.ts.map +1 -0
  179. package/dist/core/evaluator/internals.js +43 -0
  180. package/dist/core/evaluator/internals.js.map +1 -0
  181. package/dist/core/graph/Edge.d.ts +58 -0
  182. package/dist/core/graph/Edge.d.ts.map +1 -0
  183. package/dist/core/graph/Edge.js +1 -0
  184. package/dist/core/graph/EdgeEmitter.d.ts +68 -0
  185. package/dist/core/graph/EdgeEmitter.d.ts.map +1 -0
  186. package/dist/core/graph/EdgeEmitter.js +118 -0
  187. package/dist/core/graph/EdgeEmitter.js.map +1 -0
  188. package/dist/core/graph/EdgeStore.d.ts +116 -0
  189. package/dist/core/graph/EdgeStore.d.ts.map +1 -0
  190. package/dist/core/graph/EdgeStore.js +107 -0
  191. package/dist/core/graph/EdgeStore.js.map +1 -0
  192. package/dist/core/graph/GraphView.d.ts +157 -0
  193. package/dist/core/graph/GraphView.d.ts.map +1 -0
  194. package/dist/core/graph/GraphView.js +149 -0
  195. package/dist/core/graph/GraphView.js.map +1 -0
  196. package/dist/core/ids.d.ts +33 -0
  197. package/dist/core/ids.d.ts.map +1 -0
  198. package/dist/core/ids.js +11 -0
  199. package/dist/core/ids.js.map +1 -0
  200. package/dist/core/reasoning/OutcomeRecorder.d.ts +88 -0
  201. package/dist/core/reasoning/OutcomeRecorder.d.ts.map +1 -0
  202. package/dist/core/reasoning/OutcomeRecorder.js +51 -0
  203. package/dist/core/reasoning/OutcomeRecorder.js.map +1 -0
  204. package/dist/core/reasoning/strategies/SequentialStrategy.d.ts +54 -0
  205. package/dist/core/reasoning/strategies/SequentialStrategy.d.ts.map +1 -0
  206. package/dist/core/reasoning/strategies/SequentialStrategy.js +22 -0
  207. package/dist/core/reasoning/strategies/SequentialStrategy.js.map +1 -0
  208. package/dist/core/reasoning/strategies/StrategyFactory.d.ts +25 -0
  209. package/dist/core/reasoning/strategies/StrategyFactory.d.ts.map +1 -0
  210. package/dist/core/reasoning/strategies/StrategyFactory.js +16 -0
  211. package/dist/core/reasoning/strategies/StrategyFactory.js.map +1 -0
  212. package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.d.ts +58 -0
  213. package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.d.ts.map +1 -0
  214. package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.js +103 -0
  215. package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.js.map +1 -0
  216. package/dist/core/reasoning/strategies/plateau.d.ts +18 -0
  217. package/dist/core/reasoning/strategies/plateau.d.ts.map +1 -0
  218. package/dist/core/reasoning/strategies/plateau.js +12 -0
  219. package/dist/core/reasoning/strategies/plateau.js.map +1 -0
  220. package/dist/core/reasoning/strategies/totScoring.d.ts +91 -0
  221. package/dist/core/reasoning/strategies/totScoring.d.ts.map +1 -0
  222. package/dist/core/reasoning/strategies/totScoring.js +88 -0
  223. package/dist/core/reasoning/strategies/totScoring.js.map +1 -0
  224. package/dist/core/reasoning.d.ts +179 -0
  225. package/dist/core/reasoning.d.ts.map +1 -0
  226. package/dist/core/reasoning.js +1 -0
  227. package/dist/core/step.d.ts +50 -0
  228. package/dist/core/step.d.ts.map +1 -0
  229. package/dist/core/step.js +1 -0
  230. package/dist/core/thought.d.ts +113 -0
  231. package/dist/core/thought.d.ts.map +1 -0
  232. package/dist/core/thought.js +1 -0
  233. package/dist/core/tools/InMemorySuspensionStore.d.ts +66 -0
  234. package/dist/core/tools/InMemorySuspensionStore.d.ts.map +1 -0
  235. package/dist/core/tools/InMemorySuspensionStore.js +121 -0
  236. package/dist/core/tools/InMemorySuspensionStore.js.map +1 -0
  237. package/dist/di/Container.d.ts +253 -0
  238. package/dist/di/Container.d.ts.map +1 -0
  239. package/dist/di/Container.js +111 -0
  240. package/dist/di/Container.js.map +1 -0
  241. package/dist/di/ServiceRegistry.d.ts +50 -0
  242. package/dist/di/ServiceRegistry.d.ts.map +1 -0
  243. package/dist/di/ServiceRegistry.js +1 -0
  244. package/dist/errors.d.ts +663 -0
  245. package/dist/errors.d.ts.map +1 -0
  246. package/dist/errors.js +282 -0
  247. package/dist/errors.js.map +1 -0
  248. package/dist/health/HealthChecker.d.ts +73 -0
  249. package/dist/health/HealthChecker.d.ts.map +1 -0
  250. package/dist/health/HealthChecker.js +70 -0
  251. package/dist/health/HealthChecker.js.map +1 -0
  252. package/dist/lib.d.ts +273 -0
  253. package/dist/lib.d.ts.map +1 -0
  254. package/dist/lib.js +409 -0
  255. package/dist/lib.js.map +1 -0
  256. package/dist/logger/NullLogger.d.ts +154 -0
  257. package/dist/logger/NullLogger.d.ts.map +1 -0
  258. package/dist/logger/NullLogger.js +24 -0
  259. package/dist/logger/NullLogger.js.map +1 -0
  260. package/dist/logger/StructuredLogger.d.ts +327 -0
  261. package/dist/logger/StructuredLogger.d.ts.map +1 -0
  262. package/dist/logger/StructuredLogger.js +72 -0
  263. package/dist/logger/StructuredLogger.js.map +1 -0
  264. package/dist/metrics/metrics.impl.d.ts +252 -0
  265. package/dist/metrics/metrics.impl.d.ts.map +1 -0
  266. package/dist/metrics/metrics.impl.js +197 -0
  267. package/dist/metrics/metrics.impl.js.map +1 -0
  268. package/dist/persistence/FileLegacyImport.d.ts +2 -0
  269. package/dist/persistence/FileLegacyImport.d.ts.map +1 -0
  270. package/dist/persistence/FileLegacyImport.js +183 -0
  271. package/dist/persistence/FileLegacyImport.js.map +1 -0
  272. package/dist/persistence/FilePersistence.d.ts +53 -0
  273. package/dist/persistence/FilePersistence.d.ts.map +1 -0
  274. package/dist/persistence/FilePersistence.js +312 -0
  275. package/dist/persistence/FilePersistence.js.map +1 -0
  276. package/dist/persistence/FileSnapshotTypes.d.ts +29 -0
  277. package/dist/persistence/FileSnapshotTypes.d.ts.map +1 -0
  278. package/dist/persistence/FileSnapshotTypes.js +1 -0
  279. package/dist/persistence/FileSnapshotV2.d.ts +377 -0
  280. package/dist/persistence/FileSnapshotV2.d.ts.map +1 -0
  281. package/dist/persistence/FileSnapshotV2.js +124 -0
  282. package/dist/persistence/FileSnapshotV2.js.map +1 -0
  283. package/dist/persistence/FileSnapshotValidation.d.ts +15 -0
  284. package/dist/persistence/FileSnapshotValidation.d.ts.map +1 -0
  285. package/dist/persistence/FileSnapshotValidation.js +64 -0
  286. package/dist/persistence/FileSnapshotValidation.js.map +1 -0
  287. package/dist/persistence/FileWriter.d.ts +30 -0
  288. package/dist/persistence/FileWriter.d.ts.map +1 -0
  289. package/dist/persistence/FileWriter.js +155 -0
  290. package/dist/persistence/FileWriter.js.map +1 -0
  291. package/dist/persistence/MemoryPersistence.d.ts +45 -0
  292. package/dist/persistence/MemoryPersistence.d.ts.map +1 -0
  293. package/dist/persistence/MemoryPersistence.js +180 -0
  294. package/dist/persistence/MemoryPersistence.js.map +1 -0
  295. package/dist/persistence/PersistenceCodec.d.ts +7 -0
  296. package/dist/persistence/PersistenceCodec.d.ts.map +1 -0
  297. package/dist/persistence/PersistenceCodec.js +97 -0
  298. package/dist/persistence/PersistenceCodec.js.map +1 -0
  299. package/dist/persistence/PersistenceErrors.d.ts +23 -0
  300. package/dist/persistence/PersistenceErrors.d.ts.map +1 -0
  301. package/dist/persistence/PersistenceErrors.js +26 -0
  302. package/dist/persistence/PersistenceErrors.js.map +1 -0
  303. package/dist/persistence/PersistenceFactory.d.ts +21 -0
  304. package/dist/persistence/PersistenceFactory.d.ts.map +1 -0
  305. package/dist/persistence/PersistenceFactory.js +28 -0
  306. package/dist/persistence/PersistenceFactory.js.map +1 -0
  307. package/dist/persistence/PersistenceScope.d.ts +42 -0
  308. package/dist/persistence/PersistenceScope.d.ts.map +1 -0
  309. package/dist/persistence/PersistenceScope.js +93 -0
  310. package/dist/persistence/PersistenceScope.js.map +1 -0
  311. package/dist/persistence/SessionScopedPersistence.d.ts +4 -0
  312. package/dist/persistence/SessionScopedPersistence.d.ts.map +1 -0
  313. package/dist/persistence/SessionScopedPersistence.js +9 -0
  314. package/dist/persistence/SessionScopedPersistence.js.map +1 -0
  315. package/dist/persistence/SqliteDriver.d.ts +22 -0
  316. package/dist/persistence/SqliteDriver.d.ts.map +1 -0
  317. package/dist/persistence/SqliteDriver.js +29 -0
  318. package/dist/persistence/SqliteDriver.js.map +1 -0
  319. package/dist/persistence/SqliteLegacyImport.d.ts +14 -0
  320. package/dist/persistence/SqliteLegacyImport.d.ts.map +1 -0
  321. package/dist/persistence/SqliteLegacyImport.js +136 -0
  322. package/dist/persistence/SqliteLegacyImport.js.map +1 -0
  323. package/dist/persistence/SqliteLegacySchema.d.ts +15 -0
  324. package/dist/persistence/SqliteLegacySchema.d.ts.map +1 -0
  325. package/dist/persistence/SqliteLegacySchema.js +72 -0
  326. package/dist/persistence/SqliteLegacySchema.js.map +1 -0
  327. package/dist/persistence/SqlitePayload.d.ts +11 -0
  328. package/dist/persistence/SqlitePayload.d.ts.map +1 -0
  329. package/dist/persistence/SqlitePayload.js +86 -0
  330. package/dist/persistence/SqlitePayload.js.map +1 -0
  331. package/dist/persistence/SqlitePersistence.d.ts +49 -0
  332. package/dist/persistence/SqlitePersistence.d.ts.map +1 -0
  333. package/dist/persistence/SqlitePersistence.js +248 -0
  334. package/dist/persistence/SqlitePersistence.js.map +1 -0
  335. package/dist/persistence/SqliteSchemaV2.d.ts +14 -0
  336. package/dist/persistence/SqliteSchemaV2.d.ts.map +1 -0
  337. package/dist/persistence/SqliteSchemaV2.js +129 -0
  338. package/dist/persistence/SqliteSchemaV2.js.map +1 -0
  339. package/dist/persistence/SqliteSnapshotWriter.d.ts +17 -0
  340. package/dist/persistence/SqliteSnapshotWriter.d.ts.map +1 -0
  341. package/dist/persistence/SqliteSnapshotWriter.js +30 -0
  342. package/dist/persistence/SqliteSnapshotWriter.js.map +1 -0
  343. package/dist/pool/ConnectionPool.d.ts +203 -0
  344. package/dist/pool/ConnectionPool.d.ts.map +1 -0
  345. package/dist/pool/ConnectionPool.js +303 -0
  346. package/dist/pool/ConnectionPool.js.map +1 -0
  347. package/dist/pool/IConnectionPool.d.ts +143 -0
  348. package/dist/pool/IConnectionPool.d.ts.map +1 -0
  349. package/dist/pool/IConnectionPool.js +1 -0
  350. package/dist/pool/PoolErrors.d.ts +11 -0
  351. package/dist/pool/PoolErrors.d.ts.map +1 -0
  352. package/dist/pool/PoolErrors.js +16 -0
  353. package/dist/pool/PoolErrors.js.map +1 -0
  354. package/dist/registry/BaseRegistry.d.ts +219 -0
  355. package/dist/registry/BaseRegistry.d.ts.map +1 -0
  356. package/dist/registry/BaseRegistry.js +215 -0
  357. package/dist/registry/BaseRegistry.js.map +1 -0
  358. package/dist/registry/SkillRegistry.d.ts +67 -0
  359. package/dist/registry/SkillRegistry.d.ts.map +1 -0
  360. package/dist/registry/SkillRegistry.js +87 -0
  361. package/dist/registry/SkillRegistry.js.map +1 -0
  362. package/dist/registry/ToolRegistry.d.ts +70 -0
  363. package/dist/registry/ToolRegistry.d.ts.map +1 -0
  364. package/dist/registry/ToolRegistry.js +96 -0
  365. package/dist/registry/ToolRegistry.js.map +1 -0
  366. package/dist/sanitize.d.ts +164 -0
  367. package/dist/sanitize.d.ts.map +1 -0
  368. package/dist/sanitize.js +100 -0
  369. package/dist/sanitize.js.map +1 -0
  370. package/dist/schema.d.ts +562 -0
  371. package/dist/schema.d.ts.map +1 -0
  372. package/dist/schema.js +251 -0
  373. package/dist/schema.js.map +1 -0
  374. package/dist/transport/BaseTransport.d.ts +166 -0
  375. package/dist/transport/BaseTransport.d.ts.map +1 -0
  376. package/dist/transport/BaseTransport.js +199 -0
  377. package/dist/transport/BaseTransport.js.map +1 -0
  378. package/dist/transport/HttpHelpers.d.ts +53 -0
  379. package/dist/transport/HttpHelpers.d.ts.map +1 -0
  380. package/dist/transport/HttpHelpers.js +44 -0
  381. package/dist/transport/HttpHelpers.js.map +1 -0
  382. package/dist/transport/HttpRequestLifecycle.d.ts +37 -0
  383. package/dist/transport/HttpRequestLifecycle.d.ts.map +1 -0
  384. package/dist/transport/HttpRequestLifecycle.js +87 -0
  385. package/dist/transport/HttpRequestLifecycle.js.map +1 -0
  386. package/dist/transport/HttpTransport.d.ts +140 -0
  387. package/dist/transport/HttpTransport.d.ts.map +1 -0
  388. package/dist/transport/HttpTransport.js +245 -0
  389. package/dist/transport/HttpTransport.js.map +1 -0
  390. package/dist/transport/SseTransport.d.ts +149 -0
  391. package/dist/transport/SseTransport.d.ts.map +1 -0
  392. package/dist/transport/SseTransport.js +470 -0
  393. package/dist/transport/SseTransport.js.map +1 -0
  394. package/dist/transport/StreamableHttpTransport.d.ts +225 -0
  395. package/dist/transport/StreamableHttpTransport.d.ts.map +1 -0
  396. package/dist/transport/StreamableHttpTransport.js +449 -0
  397. package/dist/transport/StreamableHttpTransport.js.map +1 -0
  398. package/dist/types/disposable.d.ts +22 -0
  399. package/dist/types/disposable.d.ts.map +1 -0
  400. package/dist/types/disposable.js +1 -0
  401. package/dist/types/server-config.d.ts +32 -0
  402. package/dist/types/server-config.d.ts.map +1 -0
  403. package/dist/types/server-config.js +1 -0
  404. package/dist/types/skill.d.ts +74 -0
  405. package/dist/types/skill.d.ts.map +1 -0
  406. package/dist/types/skill.js +1 -0
  407. package/dist/types/tool.d.ts +78 -0
  408. package/dist/types/tool.d.ts.map +1 -0
  409. package/dist/types/tool.js +1 -0
  410. package/dist/utils.d.ts +27 -0
  411. package/dist/utils.d.ts.map +1 -0
  412. package/dist/utils.js +6 -0
  413. package/dist/utils.js.map +1 -0
  414. package/dist/watchers/SkillWatcher.d.ts +25 -0
  415. package/dist/watchers/SkillWatcher.d.ts.map +1 -0
  416. package/dist/watchers/SkillWatcher.js +96 -0
  417. package/dist/watchers/SkillWatcher.js.map +1 -0
  418. package/dist/watchers/ToolWatcher.d.ts +24 -0
  419. package/dist/watchers/ToolWatcher.d.ts.map +1 -0
  420. package/dist/watchers/ToolWatcher.js +92 -0
  421. package/dist/watchers/ToolWatcher.js.map +1 -0
  422. package/package.json +106 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core/SessionErrors.js","sources":["../../src/core/SessionErrors.ts"],"sourcesContent":["import type { SessionId } from '../contracts/ids.js';\nimport { ERROR_CODES, SequentialThinkingError } from '../errors.js';\n\n/** Lifecycle phase that can close ordinary session admission. */\nexport type SessionLifecycleClosedPhase =\n\t| 'resetting'\n\t| 'reset_failed'\n\t| 'evicting'\n\t| 'eviction_failed'\n\t| 'shutting_down'\n\t| 'stopped'\n\t| 'shutdown_failed';\n\n/** Error thrown when an operation arrives after lifecycle admission has closed. */\nexport class SessionLifecycleClosedError extends SequentialThinkingError {\n\t/** Session whose admission is closed, or `undefined` for a global exclusive request. */\n\tpublic readonly sessionId: SessionId | undefined;\n\t/** Lifecycle phase that rejected admission. */\n\tpublic readonly phase: SessionLifecycleClosedPhase;\n\n\tpublic constructor(sessionId: SessionId | undefined, phase: SessionLifecycleClosedPhase) {\n\t\tconst scope = sessionId === undefined ? 'Global' : `Session '${sessionId}'`;\n\t\tsuper(\n\t\t\t`${scope} lifecycle admission is closed in phase '${phase}'`,\n\t\t\tERROR_CODES.SESSION_LIFECYCLE_CLOSED\n\t\t);\n\t\tthis.name = 'SessionLifecycleClosedError';\n\t\tthis.sessionId = sessionId;\n\t\tthis.phase = phase;\n\t}\n}\n\n/** Error thrown when a per-session async lock cannot be acquired in time. */\nexport class LockTimeoutError extends SequentialThinkingError {\n\tpublic readonly sessionId: SessionId;\n\tpublic readonly timeoutMs: number;\n\n\tconstructor(sessionId: SessionId, timeoutMs: number) {\n\t\tsuper(`Lock timeout for session '${sessionId}' after ${timeoutMs}ms`, ERROR_CODES.LOCK_TIMEOUT);\n\t\tthis.name = 'LockTimeoutError';\n\t\tthis.sessionId = sessionId;\n\t\tthis.timeoutMs = timeoutMs;\n\t}\n}\n\n/** Error thrown when a session is accessed by a non-owner. */\nexport class SessionAccessDeniedError extends SequentialThinkingError {\n\tpublic readonly sessionId: SessionId;\n\tpublic readonly expectedOwner: string;\n\tpublic readonly actualOwner: string | undefined;\n\n\tconstructor(sessionId: SessionId, expectedOwner: string, actualOwner?: string) {\n\t\tsuper(\n\t\t\t`Access denied to session '${sessionId}': owned by '${expectedOwner}', accessed by '${actualOwner ?? 'anonymous'}'`,\n\t\t\tERROR_CODES.SESSION_ACCESS_DENIED\n\t\t);\n\t\tthis.name = 'SessionAccessDeniedError';\n\t\tthis.sessionId = sessionId;\n\t\tthis.expectedOwner = expectedOwner;\n\t\tthis.actualOwner = actualOwner;\n\t}\n}\n\n/** Scope requested through the legacy synchronous clear API. */\nexport type AsyncResetScope = 'session' | 'all';\n\n/** Condition that makes the legacy synchronous clear API unsafe. */\nexport type AsyncResetReason = 'persistent' | 'active';\n\n/** Error raised when state requires the awaitable reset API. */\nexport class AsyncResetRequiredError extends SequentialThinkingError {\n\tpublic readonly scope: AsyncResetScope;\n\tpublic readonly sessionId: SessionId | undefined;\n\tpublic readonly reason: AsyncResetReason;\n\n\tconstructor(\n\t\tscope: AsyncResetScope,\n\t\tsessionId?: SessionId,\n\t\treason: AsyncResetReason = 'persistent'\n\t) {\n\t\tconst target = scope === 'session' ? `session '${sessionId}'` : 'all sessions';\n\t\tsuper(\n\t\t\t`Synchronous clear cannot reset ${reason} ${target}; use the awaitable ${scope === 'session' ? 'resetSession()' : 'resetAll()'} API`,\n\t\t\tERROR_CODES.ASYNC_RESET_REQUIRED\n\t\t);\n\t\tthis.name = 'AsyncResetRequiredError';\n\t\tthis.scope = scope;\n\t\tthis.sessionId = sessionId;\n\t\tthis.reason = reason;\n\t}\n}\n"],"names":["SessionLifecycleClosedError","SequentialThinkingError","sessionId","phase","scope","undefined","ERROR_CODES","LockTimeoutError","timeoutMs","SessionAccessDeniedError","expectedOwner","actualOwner","AsyncResetRequiredError","reason","target"],"mappings":";AAcO,MAAMA,oCAAoCC;IAEhC,UAAiC;IAEjC,MAAmC;IAEnD,YAAmBC,SAAgC,EAAEC,KAAkC,CAAE;QACxF,MAAMC,QAAQF,AAAcG,WAAdH,YAA0B,WAAW,CAAC,SAAS,EAAEA,UAAU,CAAC,CAAC;QAC3E,KAAK,CACJ,GAAGE,MAAM,yCAAyC,EAAED,MAAM,CAAC,CAAC,EAC5DG,YAAY,wBAAwB;QAErC,IAAI,CAAC,IAAI,GAAG;QACZ,IAAI,CAAC,SAAS,GAAGJ;QACjB,IAAI,CAAC,KAAK,GAAGC;IACd;AACD;AAGO,MAAMI,yBAAyBN;IACrB,UAAqB;IACrB,UAAkB;IAElC,YAAYC,SAAoB,EAAEM,SAAiB,CAAE;QACpD,KAAK,CAAC,CAAC,0BAA0B,EAAEN,UAAU,QAAQ,EAAEM,UAAU,EAAE,CAAC,EAAEF,YAAY,YAAY;QAC9F,IAAI,CAAC,IAAI,GAAG;QACZ,IAAI,CAAC,SAAS,GAAGJ;QACjB,IAAI,CAAC,SAAS,GAAGM;IAClB;AACD;AAGO,MAAMC,iCAAiCR;IAC7B,UAAqB;IACrB,cAAsB;IACtB,YAAgC;IAEhD,YAAYC,SAAoB,EAAEQ,aAAqB,EAAEC,WAAoB,CAAE;QAC9E,KAAK,CACJ,CAAC,0BAA0B,EAAET,UAAU,aAAa,EAAEQ,cAAc,gBAAgB,EAAEC,eAAe,YAAY,CAAC,CAAC,EACnHL,YAAY,qBAAqB;QAElC,IAAI,CAAC,IAAI,GAAG;QACZ,IAAI,CAAC,SAAS,GAAGJ;QACjB,IAAI,CAAC,aAAa,GAAGQ;QACrB,IAAI,CAAC,WAAW,GAAGC;IACpB;AACD;AASO,MAAMC,gCAAgCX;IAC5B,MAAuB;IACvB,UAAiC;IACjC,OAAyB;IAEzC,YACCG,KAAsB,EACtBF,SAAqB,EACrBW,SAA2B,YAAY,CACtC;QACD,MAAMC,SAASV,AAAU,cAAVA,QAAsB,CAAC,SAAS,EAAEF,UAAU,CAAC,CAAC,GAAG;QAChE,KAAK,CACJ,CAAC,+BAA+B,EAAEW,OAAO,CAAC,EAAEC,OAAO,oBAAoB,EAAEV,AAAU,cAAVA,QAAsB,mBAAmB,aAAa,IAAI,CAAC,EACpIE,YAAY,oBAAoB;QAEjC,IAAI,CAAC,IAAI,GAAG;QACZ,IAAI,CAAC,KAAK,GAAGF;QACb,IAAI,CAAC,SAAS,GAAGF;QACjB,IAAI,CAAC,MAAM,GAAGW;IACf;AACD"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Core-local admission and exclusive lifecycle coordination.
3
+ *
4
+ * @module SessionLifecycleCoordinator
5
+ */
6
+ import type { SessionId } from '../contracts/ids.js';
7
+ /** Global lifecycle phases owned by the coordinator. */
8
+ export type GlobalLifecyclePhase = 'open' | 'resetting' | 'reset_failed' | 'shutting_down' | 'stopped' | 'shutdown_failed';
9
+ /** Per-session lifecycle phases owned by the coordinator. */
10
+ export type SessionLifecyclePhase = 'open' | 'resetting' | 'reset_failed' | 'evicting' | 'eviction_failed';
11
+ /** Options for retrying a failed session eviction. */
12
+ export interface SessionEvictionOptions {
13
+ readonly retryFailed: boolean;
14
+ }
15
+ /**
16
+ * Linearizes operation admission before external session locking and owns lifecycle exclusives.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * const lifecycle = new SessionLifecycleCoordinator();
21
+ * await lifecycle.runOperation(sessionId, () => sessionLock.withLock(sessionId, operation));
22
+ * ```
23
+ */
24
+ export declare class SessionLifecycleCoordinator {
25
+ private readonly _sessions;
26
+ private readonly _context;
27
+ private readonly _sessionIdleWaiters;
28
+ private readonly _globalIdleWaiters;
29
+ private _globalPhase;
30
+ private _activeOperations;
31
+ private _maintenanceOwners;
32
+ private _shutdownPromise;
33
+ /** @returns The current global lifecycle phase. */
34
+ get globalPhase(): GlobalLifecyclePhase;
35
+ /** @returns Whether any session lifecycle exclusive is active. */
36
+ get hasMaintenance(): boolean;
37
+ /** @returns The current phase for a session, defaulting to `open`. */
38
+ phaseFor(sessionId: SessionId): SessionLifecyclePhase;
39
+ /** @returns Whether a session is open and has no admitted operation. */
40
+ isIdle(sessionId: SessionId): boolean;
41
+ /** Admits an asynchronous operation synchronously and releases it in `finally`. */
42
+ runOperation<T>(sessionId: SessionId, operation: () => Promise<T>): Promise<T>;
43
+ /** Guards direct synchronous mutation, reusing a valid owning lifecycle context. */
44
+ runMutation<T>(sessionId: SessionId, mutation: () => T): T;
45
+ /** Closes one session for reset and permits retry only from `reset_failed`. */
46
+ withSessionReset<T>(sessionId: SessionId, operation: () => Promise<T>): Promise<T>;
47
+ /** Closes one session for eviction, optionally retrying `eviction_failed`. */
48
+ withSessionEviction<T>(sessionId: SessionId, operation: () => Promise<T>, options?: SessionEvictionOptions): Promise<T>;
49
+ /** Closes global admission for a reset and permits retry from `reset_failed`. */
50
+ withGlobalReset<T>(operation: () => Promise<T>): Promise<T>;
51
+ /** Closes admission once and returns the same shutdown settlement to every caller. */
52
+ shutdown(operation: () => Promise<void>): Promise<void>;
53
+ /**
54
+ * Atomically claims and removes a complete already-idle candidate set.
55
+ *
56
+ * @returns `false` without invoking cleanup when any candidate is not idle.
57
+ */
58
+ tryEvictIdleSessions(sessionIds: readonly SessionId[], operation: (sessionIds: readonly SessionId[]) => void): boolean;
59
+ private _startSessionExclusive;
60
+ private _runSessionExclusive;
61
+ private _runGlobalReset;
62
+ private _runShutdown;
63
+ private _state;
64
+ private _admissionError;
65
+ private _admit;
66
+ private _release;
67
+ private _waitSessionIdle;
68
+ private _waitGlobalIdle;
69
+ private _notifyGlobalIdle;
70
+ private _contextOwnsSession;
71
+ private _upgradeError;
72
+ private _globalUpgradeError;
73
+ }
74
+ //# sourceMappingURL=SessionLifecycleCoordinator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionLifecycleCoordinator.d.ts","sourceRoot":"","sources":["../../src/core/SessionLifecycleCoordinator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGrD,wDAAwD;AACxD,MAAM,MAAM,oBAAoB,GAC/B,MAAM,GAAG,WAAW,GAAG,cAAc,GAAG,eAAe,GAAG,SAAS,GAAG,iBAAiB,CAAC;AAEzF,6DAA6D;AAC7D,MAAM,MAAM,qBAAqB,GAChC,MAAM,GAAG,WAAW,GAAG,cAAc,GAAG,UAAU,GAAG,iBAAiB,CAAC;AAExE,sDAAsD;AACtD,MAAM,WAAW,sBAAsB;IACtC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CAC9B;AAoBD;;;;;;;;GAQG;AACH,qBAAa,2BAA2B;IACvC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsC;IAChE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6C;IACtE,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAyC;IAC7E,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAyB;IAC5D,OAAO,CAAC,YAAY,CAAgC;IACpD,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,gBAAgB,CAA8B;IAEtD,mDAAmD;IACnD,IAAW,WAAW,IAAI,oBAAoB,CAE7C;IAED,kEAAkE;IAClE,IAAW,cAAc,IAAI,OAAO,CAEnC;IAED,sEAAsE;IAC/D,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,qBAAqB;IAI5D,wEAAwE;IACjE,MAAM,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO;IAK5C,mFAAmF;IAC5E,YAAY,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAarF,oFAAoF;IAC7E,WAAW,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC;IAYjE,+EAA+E;IACxE,gBAAgB,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAIzF,8EAA8E;IACvE,mBAAmB,CAAC,CAAC,EAC3B,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAC3B,OAAO,GAAE,sBAA+C,GACtD,OAAO,CAAC,CAAC,CAAC;IASb,iFAAiF;IAC1E,eAAe,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAUlE,sFAAsF;IAC/E,QAAQ,CAAC,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAa9D;;;;OAIG;IACI,oBAAoB,CAC1B,UAAU,EAAE,SAAS,SAAS,EAAE,EAChC,SAAS,EAAE,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,KAAK,IAAI,GACnD,OAAO;IAoBV,OAAO,CAAC,sBAAsB;YAuChB,oBAAoB;YAmBpB,eAAe;YAaf,YAAY;IAW1B,OAAO,CAAC,MAAM;IAMd,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,MAAM;IAKd,OAAO,CAAC,QAAQ;IAYhB,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,mBAAmB;IAK3B,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,mBAAmB;CAG3B"}
@@ -0,0 +1,222 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ import { SessionLifecycleClosedError } from "./SessionErrors.js";
3
+ const RESET_EXCLUSIVE = {
4
+ active: 'resetting',
5
+ failed: 'reset_failed'
6
+ };
7
+ const EVICTION_EXCLUSIVE = {
8
+ active: 'evicting',
9
+ failed: 'eviction_failed'
10
+ };
11
+ class SessionLifecycleCoordinator {
12
+ _sessions = new Map();
13
+ _context = new AsyncLocalStorage();
14
+ _sessionIdleWaiters = new Map();
15
+ _globalIdleWaiters = new Set();
16
+ _globalPhase = 'open';
17
+ _activeOperations = 0;
18
+ _maintenanceOwners = 0;
19
+ _shutdownPromise = null;
20
+ get globalPhase() {
21
+ return this._globalPhase;
22
+ }
23
+ get hasMaintenance() {
24
+ return this._maintenanceOwners > 0;
25
+ }
26
+ phaseFor(sessionId) {
27
+ return this._sessions.get(sessionId)?.phase ?? 'open';
28
+ }
29
+ isIdle(sessionId) {
30
+ const state = this._sessions.get(sessionId);
31
+ return void 0 === state || 'open' === state.phase && 0 === state.activeOperations;
32
+ }
33
+ runOperation(sessionId, operation) {
34
+ const admissionError = this._admissionError(sessionId);
35
+ if (void 0 !== admissionError) return Promise.reject(admissionError);
36
+ this._admit(sessionId);
37
+ return this._context.run({
38
+ kind: 'operation',
39
+ sessionId
40
+ }, async ()=>{
41
+ try {
42
+ return await operation();
43
+ } finally{
44
+ this._release(sessionId);
45
+ }
46
+ });
47
+ }
48
+ runMutation(sessionId, mutation) {
49
+ if (this._contextOwnsSession(sessionId)) return mutation();
50
+ const admissionError = this._admissionError(sessionId);
51
+ if (void 0 !== admissionError) throw admissionError;
52
+ this._admit(sessionId);
53
+ try {
54
+ return this._context.run({
55
+ kind: 'operation',
56
+ sessionId
57
+ }, mutation);
58
+ } finally{
59
+ this._release(sessionId);
60
+ }
61
+ }
62
+ withSessionReset(sessionId, operation) {
63
+ return this._startSessionExclusive(sessionId, RESET_EXCLUSIVE, operation, true);
64
+ }
65
+ withSessionEviction(sessionId, operation, options = {
66
+ retryFailed: false
67
+ }) {
68
+ return this._startSessionExclusive(sessionId, EVICTION_EXCLUSIVE, operation, options.retryFailed);
69
+ }
70
+ withGlobalReset(operation) {
71
+ const context = this._context.getStore();
72
+ if (void 0 !== context) return Promise.reject(this._globalUpgradeError('global reset'));
73
+ if ('open' !== this._globalPhase && 'reset_failed' !== this._globalPhase) return Promise.reject(new SessionLifecycleClosedError(void 0, this._globalPhase));
74
+ this._globalPhase = 'resetting';
75
+ return this._runGlobalReset(operation);
76
+ }
77
+ shutdown(operation) {
78
+ if (null !== this._shutdownPromise) return this._shutdownPromise;
79
+ const context = this._context.getStore();
80
+ if (void 0 !== context) return Promise.reject(this._globalUpgradeError('shutdown'));
81
+ if ('open' !== this._globalPhase && 'reset_failed' !== this._globalPhase) return Promise.reject(new SessionLifecycleClosedError(void 0, this._globalPhase));
82
+ this._globalPhase = 'shutting_down';
83
+ const settlement = this._runShutdown(operation);
84
+ this._shutdownPromise = settlement;
85
+ return settlement;
86
+ }
87
+ tryEvictIdleSessions(sessionIds, operation) {
88
+ if ('open' !== this._globalPhase || sessionIds.some((sessionId)=>!this.isIdle(sessionId))) return false;
89
+ const uniqueIds = [
90
+ ...new Set(sessionIds)
91
+ ];
92
+ for (const sessionId of uniqueIds)this._state(sessionId).phase = 'evicting';
93
+ this._maintenanceOwners += 1;
94
+ try {
95
+ operation(uniqueIds);
96
+ for (const sessionId of uniqueIds)this._sessions.delete(sessionId);
97
+ return true;
98
+ } catch (error) {
99
+ for (const sessionId of uniqueIds)this._state(sessionId).phase = 'eviction_failed';
100
+ throw error;
101
+ } finally{
102
+ this._maintenanceOwners -= 1;
103
+ this._notifyGlobalIdle();
104
+ }
105
+ }
106
+ _startSessionExclusive(sessionId, exclusive, operation, allowFailedRetry) {
107
+ const context = this._context.getStore();
108
+ if (context?.kind === 'session_exclusive' && context.sessionId === sessionId && this.phaseFor(sessionId) === exclusive.active) return operation();
109
+ if (context?.kind === 'operation') return Promise.reject(this._upgradeError('resetting' === exclusive.active ? 'reset' : 'eviction', sessionId));
110
+ if (void 0 !== context && !this._contextOwnsSession(sessionId)) return Promise.reject(this._globalUpgradeError(`session ${'resetting' === exclusive.active ? 'reset' : 'eviction'}`));
111
+ const state = this._state(sessionId);
112
+ const retrying = state.phase === exclusive.failed && allowFailedRetry;
113
+ if ('open' !== this._globalPhase) return Promise.reject(new SessionLifecycleClosedError(sessionId, this._globalPhase));
114
+ if ('open' !== state.phase && !retrying) return Promise.reject(new SessionLifecycleClosedError(sessionId, state.phase));
115
+ state.phase = exclusive.active;
116
+ this._maintenanceOwners += 1;
117
+ return this._runSessionExclusive(sessionId, exclusive, operation);
118
+ }
119
+ async _runSessionExclusive(sessionId, exclusive, operation) {
120
+ try {
121
+ await this._waitSessionIdle(sessionId);
122
+ const result = await this._context.run({
123
+ kind: 'session_exclusive',
124
+ sessionId
125
+ }, operation);
126
+ this._sessions.delete(sessionId);
127
+ return result;
128
+ } catch (error) {
129
+ this._state(sessionId).phase = exclusive.failed;
130
+ throw error;
131
+ } finally{
132
+ this._maintenanceOwners -= 1;
133
+ this._notifyGlobalIdle();
134
+ }
135
+ }
136
+ async _runGlobalReset(operation) {
137
+ try {
138
+ await this._waitGlobalIdle();
139
+ const result = await this._context.run({
140
+ kind: 'global_exclusive'
141
+ }, operation);
142
+ this._sessions.clear();
143
+ this._globalPhase = 'open';
144
+ return result;
145
+ } catch (error) {
146
+ this._globalPhase = 'reset_failed';
147
+ throw error;
148
+ }
149
+ }
150
+ async _runShutdown(operation) {
151
+ try {
152
+ await this._waitGlobalIdle();
153
+ await this._context.run({
154
+ kind: 'global_exclusive'
155
+ }, operation);
156
+ this._globalPhase = 'stopped';
157
+ } catch (error) {
158
+ this._globalPhase = 'shutdown_failed';
159
+ throw error;
160
+ }
161
+ }
162
+ _state(sessionId) {
163
+ const state = this._sessions.get(sessionId) ?? {
164
+ phase: 'open',
165
+ activeOperations: 0
166
+ };
167
+ this._sessions.set(sessionId, state);
168
+ return state;
169
+ }
170
+ _admissionError(sessionId) {
171
+ if ('open' !== this._globalPhase) return new SessionLifecycleClosedError(sessionId, this._globalPhase);
172
+ const phase = this.phaseFor(sessionId);
173
+ return 'open' === phase ? void 0 : new SessionLifecycleClosedError(sessionId, phase);
174
+ }
175
+ _admit(sessionId) {
176
+ this._state(sessionId).activeOperations += 1;
177
+ this._activeOperations += 1;
178
+ }
179
+ _release(sessionId) {
180
+ const state = this._state(sessionId);
181
+ state.activeOperations -= 1;
182
+ this._activeOperations -= 1;
183
+ if (0 === state.activeOperations) {
184
+ const waiters = this._sessionIdleWaiters.get(sessionId);
185
+ this._sessionIdleWaiters.delete(sessionId);
186
+ for (const resolve of waiters ?? [])resolve();
187
+ }
188
+ this._notifyGlobalIdle();
189
+ }
190
+ _waitSessionIdle(sessionId) {
191
+ if (0 === this._state(sessionId).activeOperations) return Promise.resolve();
192
+ const result = Promise.withResolvers();
193
+ const waiters = this._sessionIdleWaiters.get(sessionId) ?? new Set();
194
+ waiters.add(result.resolve);
195
+ this._sessionIdleWaiters.set(sessionId, waiters);
196
+ return result.promise;
197
+ }
198
+ _waitGlobalIdle() {
199
+ if (0 === this._activeOperations && 0 === this._maintenanceOwners) return Promise.resolve();
200
+ const result = Promise.withResolvers();
201
+ this._globalIdleWaiters.add(result.resolve);
202
+ return result.promise;
203
+ }
204
+ _notifyGlobalIdle() {
205
+ if (0 !== this._activeOperations || 0 !== this._maintenanceOwners) return;
206
+ for (const resolve of this._globalIdleWaiters)resolve();
207
+ this._globalIdleWaiters.clear();
208
+ }
209
+ _contextOwnsSession(sessionId) {
210
+ const context = this._context.getStore();
211
+ return context?.kind === 'global_exclusive' || context?.sessionId === sessionId;
212
+ }
213
+ _upgradeError(mode, sessionId) {
214
+ return new TypeError(`Cannot upgrade an admitted operation for session '${sessionId}' to ${mode}`);
215
+ }
216
+ _globalUpgradeError(mode) {
217
+ return new TypeError(`Cannot upgrade an active lifecycle context to ${mode}`);
218
+ }
219
+ }
220
+ export { SessionLifecycleCoordinator };
221
+
222
+ //# sourceMappingURL=SessionLifecycleCoordinator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core/SessionLifecycleCoordinator.js","sources":["../../src/core/SessionLifecycleCoordinator.ts"],"sourcesContent":["/**\n * Core-local admission and exclusive lifecycle coordination.\n *\n * @module SessionLifecycleCoordinator\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\n\nimport type { SessionId } from '../contracts/ids.js';\nimport { SessionLifecycleClosedError } from './SessionErrors.js';\n\n/** Global lifecycle phases owned by the coordinator. */\nexport type GlobalLifecyclePhase =\n\t'open' | 'resetting' | 'reset_failed' | 'shutting_down' | 'stopped' | 'shutdown_failed';\n\n/** Per-session lifecycle phases owned by the coordinator. */\nexport type SessionLifecyclePhase =\n\t'open' | 'resetting' | 'reset_failed' | 'evicting' | 'eviction_failed';\n\n/** Options for retrying a failed session eviction. */\nexport interface SessionEvictionOptions {\n\treadonly retryFailed: boolean;\n}\n\ntype SessionState = {\n\tphase: SessionLifecyclePhase;\n\tactiveOperations: number;\n};\n\ntype LifecycleContext =\n\t| { readonly kind: 'operation'; readonly sessionId: SessionId }\n\t| { readonly kind: 'session_exclusive'; readonly sessionId: SessionId }\n\t| { readonly kind: 'global_exclusive' };\n\ntype SessionExclusive = {\n\treadonly active: 'resetting' | 'evicting';\n\treadonly failed: 'reset_failed' | 'eviction_failed';\n};\n\nconst RESET_EXCLUSIVE: SessionExclusive = { active: 'resetting', failed: 'reset_failed' };\nconst EVICTION_EXCLUSIVE: SessionExclusive = { active: 'evicting', failed: 'eviction_failed' };\n\n/**\n * Linearizes operation admission before external session locking and owns lifecycle exclusives.\n *\n * @example\n * ```ts\n * const lifecycle = new SessionLifecycleCoordinator();\n * await lifecycle.runOperation(sessionId, () => sessionLock.withLock(sessionId, operation));\n * ```\n */\nexport class SessionLifecycleCoordinator {\n\tprivate readonly _sessions = new Map<SessionId, SessionState>();\n\tprivate readonly _context = new AsyncLocalStorage<LifecycleContext>();\n\tprivate readonly _sessionIdleWaiters = new Map<SessionId, Set<() => void>>();\n\tprivate readonly _globalIdleWaiters = new Set<() => void>();\n\tprivate _globalPhase: GlobalLifecyclePhase = 'open';\n\tprivate _activeOperations = 0;\n\tprivate _maintenanceOwners = 0;\n\tprivate _shutdownPromise: Promise<void> | null = null;\n\n\t/** @returns The current global lifecycle phase. */\n\tpublic get globalPhase(): GlobalLifecyclePhase {\n\t\treturn this._globalPhase;\n\t}\n\n\t/** @returns Whether any session lifecycle exclusive is active. */\n\tpublic get hasMaintenance(): boolean {\n\t\treturn this._maintenanceOwners > 0;\n\t}\n\n\t/** @returns The current phase for a session, defaulting to `open`. */\n\tpublic phaseFor(sessionId: SessionId): SessionLifecyclePhase {\n\t\treturn this._sessions.get(sessionId)?.phase ?? 'open';\n\t}\n\n\t/** @returns Whether a session is open and has no admitted operation. */\n\tpublic isIdle(sessionId: SessionId): boolean {\n\t\tconst state = this._sessions.get(sessionId);\n\t\treturn state === undefined || (state.phase === 'open' && state.activeOperations === 0);\n\t}\n\n\t/** Admits an asynchronous operation synchronously and releases it in `finally`. */\n\tpublic runOperation<T>(sessionId: SessionId, operation: () => Promise<T>): Promise<T> {\n\t\tconst admissionError = this._admissionError(sessionId);\n\t\tif (admissionError !== undefined) return Promise.reject(admissionError);\n\t\tthis._admit(sessionId);\n\t\treturn this._context.run({ kind: 'operation', sessionId }, async () => {\n\t\t\ttry {\n\t\t\t\treturn await operation();\n\t\t\t} finally {\n\t\t\t\tthis._release(sessionId);\n\t\t\t}\n\t\t});\n\t}\n\n\t/** Guards direct synchronous mutation, reusing a valid owning lifecycle context. */\n\tpublic runMutation<T>(sessionId: SessionId, mutation: () => T): T {\n\t\tif (this._contextOwnsSession(sessionId)) return mutation();\n\t\tconst admissionError = this._admissionError(sessionId);\n\t\tif (admissionError !== undefined) throw admissionError;\n\t\tthis._admit(sessionId);\n\t\ttry {\n\t\t\treturn this._context.run({ kind: 'operation', sessionId }, mutation);\n\t\t} finally {\n\t\t\tthis._release(sessionId);\n\t\t}\n\t}\n\n\t/** Closes one session for reset and permits retry only from `reset_failed`. */\n\tpublic withSessionReset<T>(sessionId: SessionId, operation: () => Promise<T>): Promise<T> {\n\t\treturn this._startSessionExclusive(sessionId, RESET_EXCLUSIVE, operation, true);\n\t}\n\n\t/** Closes one session for eviction, optionally retrying `eviction_failed`. */\n\tpublic withSessionEviction<T>(\n\t\tsessionId: SessionId,\n\t\toperation: () => Promise<T>,\n\t\toptions: SessionEvictionOptions = { retryFailed: false }\n\t): Promise<T> {\n\t\treturn this._startSessionExclusive(\n\t\t\tsessionId,\n\t\t\tEVICTION_EXCLUSIVE,\n\t\t\toperation,\n\t\t\toptions.retryFailed\n\t\t);\n\t}\n\n\t/** Closes global admission for a reset and permits retry from `reset_failed`. */\n\tpublic withGlobalReset<T>(operation: () => Promise<T>): Promise<T> {\n\t\tconst context = this._context.getStore();\n\t\tif (context !== undefined) return Promise.reject(this._globalUpgradeError('global reset'));\n\t\tif (this._globalPhase !== 'open' && this._globalPhase !== 'reset_failed') {\n\t\t\treturn Promise.reject(new SessionLifecycleClosedError(undefined, this._globalPhase));\n\t\t}\n\t\tthis._globalPhase = 'resetting';\n\t\treturn this._runGlobalReset(operation);\n\t}\n\n\t/** Closes admission once and returns the same shutdown settlement to every caller. */\n\tpublic shutdown(operation: () => Promise<void>): Promise<void> {\n\t\tif (this._shutdownPromise !== null) return this._shutdownPromise;\n\t\tconst context = this._context.getStore();\n\t\tif (context !== undefined) return Promise.reject(this._globalUpgradeError('shutdown'));\n\t\tif (this._globalPhase !== 'open' && this._globalPhase !== 'reset_failed') {\n\t\t\treturn Promise.reject(new SessionLifecycleClosedError(undefined, this._globalPhase));\n\t\t}\n\t\tthis._globalPhase = 'shutting_down';\n\t\tconst settlement = this._runShutdown(operation);\n\t\tthis._shutdownPromise = settlement;\n\t\treturn settlement;\n\t}\n\n\t/**\n\t * Atomically claims and removes a complete already-idle candidate set.\n\t *\n\t * @returns `false` without invoking cleanup when any candidate is not idle.\n\t */\n\tpublic tryEvictIdleSessions(\n\t\tsessionIds: readonly SessionId[],\n\t\toperation: (sessionIds: readonly SessionId[]) => void\n\t): boolean {\n\t\tif (this._globalPhase !== 'open' || sessionIds.some((sessionId) => !this.isIdle(sessionId))) {\n\t\t\treturn false;\n\t\t}\n\t\tconst uniqueIds = [...new Set(sessionIds)];\n\t\tfor (const sessionId of uniqueIds) this._state(sessionId).phase = 'evicting';\n\t\tthis._maintenanceOwners += 1;\n\t\ttry {\n\t\t\toperation(uniqueIds);\n\t\t\tfor (const sessionId of uniqueIds) this._sessions.delete(sessionId);\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tfor (const sessionId of uniqueIds) this._state(sessionId).phase = 'eviction_failed';\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tthis._maintenanceOwners -= 1;\n\t\t\tthis._notifyGlobalIdle();\n\t\t}\n\t}\n\n\tprivate _startSessionExclusive<T>(\n\t\tsessionId: SessionId,\n\t\texclusive: SessionExclusive,\n\t\toperation: () => Promise<T>,\n\t\tallowFailedRetry: boolean\n\t): Promise<T> {\n\t\tconst context = this._context.getStore();\n\t\tif (\n\t\t\tcontext?.kind === 'session_exclusive' &&\n\t\t\tcontext.sessionId === sessionId &&\n\t\t\tthis.phaseFor(sessionId) === exclusive.active\n\t\t) {\n\t\t\treturn operation();\n\t\t}\n\t\tif (context?.kind === 'operation') {\n\t\t\treturn Promise.reject(\n\t\t\t\tthis._upgradeError(exclusive.active === 'resetting' ? 'reset' : 'eviction', sessionId)\n\t\t\t);\n\t\t}\n\t\tif (context !== undefined && !this._contextOwnsSession(sessionId)) {\n\t\t\treturn Promise.reject(\n\t\t\t\tthis._globalUpgradeError(\n\t\t\t\t\t`session ${exclusive.active === 'resetting' ? 'reset' : 'eviction'}`\n\t\t\t\t)\n\t\t\t);\n\t\t}\n\t\tconst state = this._state(sessionId);\n\t\tconst retrying = state.phase === exclusive.failed && allowFailedRetry;\n\t\tif (this._globalPhase !== 'open') {\n\t\t\treturn Promise.reject(new SessionLifecycleClosedError(sessionId, this._globalPhase));\n\t\t}\n\t\tif (state.phase !== 'open' && !retrying) {\n\t\t\treturn Promise.reject(new SessionLifecycleClosedError(sessionId, state.phase));\n\t\t}\n\t\tstate.phase = exclusive.active;\n\t\tthis._maintenanceOwners += 1;\n\t\treturn this._runSessionExclusive(sessionId, exclusive, operation);\n\t}\n\n\tprivate async _runSessionExclusive<T>(\n\t\tsessionId: SessionId,\n\t\texclusive: SessionExclusive,\n\t\toperation: () => Promise<T>\n\t): Promise<T> {\n\t\ttry {\n\t\t\tawait this._waitSessionIdle(sessionId);\n\t\t\tconst result = await this._context.run({ kind: 'session_exclusive', sessionId }, operation);\n\t\t\tthis._sessions.delete(sessionId);\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tthis._state(sessionId).phase = exclusive.failed;\n\t\t\tthrow error;\n\t\t} finally {\n\t\t\tthis._maintenanceOwners -= 1;\n\t\t\tthis._notifyGlobalIdle();\n\t\t}\n\t}\n\n\tprivate async _runGlobalReset<T>(operation: () => Promise<T>): Promise<T> {\n\t\ttry {\n\t\t\tawait this._waitGlobalIdle();\n\t\t\tconst result = await this._context.run({ kind: 'global_exclusive' }, operation);\n\t\t\tthis._sessions.clear();\n\t\t\tthis._globalPhase = 'open';\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tthis._globalPhase = 'reset_failed';\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprivate async _runShutdown(operation: () => Promise<void>): Promise<void> {\n\t\ttry {\n\t\t\tawait this._waitGlobalIdle();\n\t\t\tawait this._context.run({ kind: 'global_exclusive' }, operation);\n\t\t\tthis._globalPhase = 'stopped';\n\t\t} catch (error) {\n\t\t\tthis._globalPhase = 'shutdown_failed';\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tprivate _state(sessionId: SessionId): SessionState {\n\t\tconst state = this._sessions.get(sessionId) ?? { phase: 'open', activeOperations: 0 };\n\t\tthis._sessions.set(sessionId, state);\n\t\treturn state;\n\t}\n\n\tprivate _admissionError(sessionId: SessionId): SessionLifecycleClosedError | undefined {\n\t\tif (this._globalPhase !== 'open') {\n\t\t\treturn new SessionLifecycleClosedError(sessionId, this._globalPhase);\n\t\t}\n\t\tconst phase = this.phaseFor(sessionId);\n\t\treturn phase === 'open' ? undefined : new SessionLifecycleClosedError(sessionId, phase);\n\t}\n\n\tprivate _admit(sessionId: SessionId): void {\n\t\tthis._state(sessionId).activeOperations += 1;\n\t\tthis._activeOperations += 1;\n\t}\n\n\tprivate _release(sessionId: SessionId): void {\n\t\tconst state = this._state(sessionId);\n\t\tstate.activeOperations -= 1;\n\t\tthis._activeOperations -= 1;\n\t\tif (state.activeOperations === 0) {\n\t\t\tconst waiters = this._sessionIdleWaiters.get(sessionId);\n\t\t\tthis._sessionIdleWaiters.delete(sessionId);\n\t\t\tfor (const resolve of waiters ?? []) resolve();\n\t\t}\n\t\tthis._notifyGlobalIdle();\n\t}\n\n\tprivate _waitSessionIdle(sessionId: SessionId): Promise<void> {\n\t\tif (this._state(sessionId).activeOperations === 0) return Promise.resolve();\n\t\tconst result = Promise.withResolvers<void>();\n\t\tconst waiters = this._sessionIdleWaiters.get(sessionId) ?? new Set<() => void>();\n\t\twaiters.add(result.resolve);\n\t\tthis._sessionIdleWaiters.set(sessionId, waiters);\n\t\treturn result.promise;\n\t}\n\n\tprivate _waitGlobalIdle(): Promise<void> {\n\t\tif (this._activeOperations === 0 && this._maintenanceOwners === 0) return Promise.resolve();\n\t\tconst result = Promise.withResolvers<void>();\n\t\tthis._globalIdleWaiters.add(result.resolve);\n\t\treturn result.promise;\n\t}\n\n\tprivate _notifyGlobalIdle(): void {\n\t\tif (this._activeOperations !== 0 || this._maintenanceOwners !== 0) return;\n\t\tfor (const resolve of this._globalIdleWaiters) resolve();\n\t\tthis._globalIdleWaiters.clear();\n\t}\n\n\tprivate _contextOwnsSession(sessionId: SessionId): boolean {\n\t\tconst context = this._context.getStore();\n\t\treturn context?.kind === 'global_exclusive' || context?.sessionId === sessionId;\n\t}\n\n\tprivate _upgradeError(mode: string, sessionId: SessionId): TypeError {\n\t\treturn new TypeError(\n\t\t\t`Cannot upgrade an admitted operation for session '${sessionId}' to ${mode}`\n\t\t);\n\t}\n\n\tprivate _globalUpgradeError(mode: string): TypeError {\n\t\treturn new TypeError(`Cannot upgrade an active lifecycle context to ${mode}`);\n\t}\n}\n"],"names":["RESET_EXCLUSIVE","EVICTION_EXCLUSIVE","SessionLifecycleCoordinator","Map","AsyncLocalStorage","Set","sessionId","state","undefined","operation","admissionError","Promise","mutation","options","context","SessionLifecycleClosedError","settlement","sessionIds","uniqueIds","error","exclusive","allowFailedRetry","retrying","result","phase","waiters","resolve","mode","TypeError"],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Per-session async lock to prevent concurrent state mutations.
3
+ *
4
+ * Uses chained promises: each lock acquisition waits for the previous
5
+ * holder's promise to resolve. The lock map entry is purged after release
6
+ * (when no waiters chained on top), preventing unbounded memory growth.
7
+ *
8
+ * Different sessions are fully independent: locks for distinct session
9
+ * ids never block each other. `undefined`/empty session ids share a
10
+ * single global slot.
11
+ *
12
+ * @module session-lock
13
+ */
14
+ import type { ISessionLock } from '../contracts/interfaces.js';
15
+ import type { SessionId } from '../contracts/ids.js';
16
+ /**
17
+ * In-memory implementation of {@link ISessionLock}.
18
+ *
19
+ * Maintains a `Map<string, Promise<void>>` where each value is the tail
20
+ * of the lock chain for that session. New acquisitions chain onto the
21
+ * tail and replace it; once they release, the entry is purged unless a
22
+ * later acquisition chained on top.
23
+ *
24
+ * Lock chain integrity is preserved across timeouts and `fn` errors: a
25
+ * waiter's slot is only released after the previous holder actually
26
+ * finishes, even if the waiter aborted via {@link LockTimeoutError}.
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * const lock = new SessionLock();
31
+ * await lock.withLock('session-a', async () => {
32
+ * // critical section — concurrent calls for 'session-a' wait
33
+ * });
34
+ * ```
35
+ */
36
+ export declare class SessionLock implements ISessionLock {
37
+ private readonly _locks;
38
+ /**
39
+ * Number of currently held lock chains. Useful for diagnostics
40
+ * and leak assertions in tests.
41
+ */
42
+ get size(): number;
43
+ /** Returns whether a session currently has a holder or queued operation. */
44
+ isActive(sessionId: SessionId | undefined): boolean;
45
+ /**
46
+ * Execute `fn` while holding the lock for the given session.
47
+ *
48
+ * Concurrent calls for the same session are serialized. Calls for
49
+ * different sessions run in parallel. The lock is always released
50
+ * (via `finally`) even if `fn` throws.
51
+ *
52
+ * @param sessionId - Session to lock. Falsy values share a global slot.
53
+ * @param fn - Critical section to run while holding the lock.
54
+ * @param timeoutMs - Maximum time to wait for the lock (default 5000ms).
55
+ * @throws {LockTimeoutError} When the lock cannot be acquired within `timeoutMs`.
56
+ */
57
+ withLock<T>(sessionId: SessionId | undefined, fn: () => Promise<T>, timeoutMs?: number): Promise<T>;
58
+ }
59
+ //# sourceMappingURL=SessionLock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionLock.d.ts","sourceRoot":"","sources":["../../src/core/SessionLock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAgBrD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBAAa,WAAY,YAAW,YAAY;IAC/C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuC;IAE9D;;;OAGG;IACH,IAAW,IAAI,IAAI,MAAM,CAExB;IAED,4EAA4E;IACrE,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,SAAS,GAAG,OAAO;IAI1D;;;;;;;;;;;OAWG;IACU,QAAQ,CAAC,CAAC,EACtB,SAAS,EAAE,SAAS,GAAG,SAAS,EAChC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,SAAS,GAAE,MAAgC,GACzC,OAAO,CAAC,CAAC,CAAC;CAqCb"}
@@ -0,0 +1,39 @@
1
+ import { GLOBAL_SESSION_ID } from "../contracts/ids.js";
2
+ import { LockTimeoutError } from "./SessionErrors.js";
3
+ const DEFAULT_LOCK_TIMEOUT_MS = 5000;
4
+ function lockKey(sessionId) {
5
+ return sessionId && sessionId.length > 0 ? sessionId : GLOBAL_SESSION_ID;
6
+ }
7
+ class SessionLock {
8
+ _locks = new Map();
9
+ get size() {
10
+ return this._locks.size;
11
+ }
12
+ isActive(sessionId) {
13
+ return this._locks.has(lockKey(sessionId));
14
+ }
15
+ async withLock(sessionId, fn, timeoutMs = DEFAULT_LOCK_TIMEOUT_MS) {
16
+ const key = lockKey(sessionId);
17
+ const previous = this._locks.get(key) ?? Promise.resolve();
18
+ const release = Promise.withResolvers();
19
+ const next = previous.then(()=>release.promise, ()=>release.promise);
20
+ this._locks.set(key, next);
21
+ next.then(()=>{
22
+ if (this._locks.get(key) === next) this._locks.delete(key);
23
+ });
24
+ let timeoutId;
25
+ try {
26
+ await new Promise((resolve, reject)=>{
27
+ timeoutId = setTimeout(()=>reject(new LockTimeoutError(key, timeoutMs)), timeoutMs);
28
+ previous.then(()=>resolve(), ()=>resolve());
29
+ });
30
+ return await fn();
31
+ } finally{
32
+ if (void 0 !== timeoutId) clearTimeout(timeoutId);
33
+ release.resolve();
34
+ }
35
+ }
36
+ }
37
+ export { SessionLock };
38
+
39
+ //# sourceMappingURL=SessionLock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core/SessionLock.js","sources":["../../src/core/SessionLock.ts"],"sourcesContent":["/**\n * Per-session async lock to prevent concurrent state mutations.\n *\n * Uses chained promises: each lock acquisition waits for the previous\n * holder's promise to resolve. The lock map entry is purged after release\n * (when no waiters chained on top), preventing unbounded memory growth.\n *\n * Different sessions are fully independent: locks for distinct session\n * ids never block each other. `undefined`/empty session ids share a\n * single global slot.\n *\n * @module session-lock\n */\n\nimport type { ISessionLock } from '../contracts/interfaces.js';\nimport type { SessionId } from '../contracts/ids.js';\nimport { GLOBAL_SESSION_ID } from '../contracts/ids.js';\nimport { LockTimeoutError } from './SessionErrors.js';\n\nconst DEFAULT_LOCK_TIMEOUT_MS = 5000;\n\n/**\n * Normalize a session id for keying the internal lock map.\n * Treats `undefined`, `null`, and empty strings as the global session.\n *\n * @internal\n */\nfunction lockKey(sessionId: SessionId | undefined): SessionId {\n\treturn sessionId && sessionId.length > 0 ? sessionId : GLOBAL_SESSION_ID;\n}\n\n/**\n * In-memory implementation of {@link ISessionLock}.\n *\n * Maintains a `Map<string, Promise<void>>` where each value is the tail\n * of the lock chain for that session. New acquisitions chain onto the\n * tail and replace it; once they release, the entry is purged unless a\n * later acquisition chained on top.\n *\n * Lock chain integrity is preserved across timeouts and `fn` errors: a\n * waiter's slot is only released after the previous holder actually\n * finishes, even if the waiter aborted via {@link LockTimeoutError}.\n *\n * @example\n * ```typescript\n * const lock = new SessionLock();\n * await lock.withLock('session-a', async () => {\n * // critical section — concurrent calls for 'session-a' wait\n * });\n * ```\n */\nexport class SessionLock implements ISessionLock {\n\tprivate readonly _locks = new Map<SessionId, Promise<void>>();\n\n\t/**\n\t * Number of currently held lock chains. Useful for diagnostics\n\t * and leak assertions in tests.\n\t */\n\tpublic get size(): number {\n\t\treturn this._locks.size;\n\t}\n\n\t/** Returns whether a session currently has a holder or queued operation. */\n\tpublic isActive(sessionId: SessionId | undefined): boolean {\n\t\treturn this._locks.has(lockKey(sessionId));\n\t}\n\n\t/**\n\t * Execute `fn` while holding the lock for the given session.\n\t *\n\t * Concurrent calls for the same session are serialized. Calls for\n\t * different sessions run in parallel. The lock is always released\n\t * (via `finally`) even if `fn` throws.\n\t *\n\t * @param sessionId - Session to lock. Falsy values share a global slot.\n\t * @param fn - Critical section to run while holding the lock.\n\t * @param timeoutMs - Maximum time to wait for the lock (default 5000ms).\n\t * @throws {LockTimeoutError} When the lock cannot be acquired within `timeoutMs`.\n\t */\n\tpublic async withLock<T>(\n\t\tsessionId: SessionId | undefined,\n\t\tfn: () => Promise<T>,\n\t\ttimeoutMs: number = DEFAULT_LOCK_TIMEOUT_MS\n\t): Promise<T> {\n\t\tconst key = lockKey(sessionId);\n\t\tconst previous = this._locks.get(key) ?? Promise.resolve();\n\n\t\t// `next` is the tail this acquirer publishes to the chain. It only\n\t\t// resolves after `previous` settles, guaranteeing serialization even\n\t\t// when the current acquirer times out before holding the lock.\n\t\tconst release = Promise.withResolvers<void>();\n\t\tconst next = previous.then(\n\t\t\t() => release.promise,\n\t\t\t() => release.promise\n\t\t);\n\t\tthis._locks.set(key, next);\n\t\t// Identity protects a later acquisition from an earlier tail's cleanup.\n\t\tvoid next.then(() => {\n\t\t\tif (this._locks.get(key) === next) {\n\t\t\t\tthis._locks.delete(key);\n\t\t\t}\n\t\t});\n\n\t\tlet timeoutId: ReturnType<typeof setTimeout> | undefined;\n\t\ttry {\n\t\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\t\ttimeoutId = setTimeout(() => reject(new LockTimeoutError(key, timeoutMs)), timeoutMs);\n\t\t\t\tprevious.then(\n\t\t\t\t\t() => resolve(),\n\t\t\t\t\t() => resolve() // previous holder's failure must not poison the chain\n\t\t\t\t);\n\t\t\t});\n\t\t\treturn await fn();\n\t\t} finally {\n\t\t\tif (timeoutId !== undefined) {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t}\n\t\t\trelease.resolve();\n\t\t}\n\t}\n}\n"],"names":["DEFAULT_LOCK_TIMEOUT_MS","lockKey","sessionId","GLOBAL_SESSION_ID","SessionLock","Map","fn","timeoutMs","key","previous","Promise","release","next","timeoutId","resolve","reject","setTimeout","LockTimeoutError","undefined","clearTimeout"],"mappings":";;AAmBA,MAAMA,0BAA0B;AAQhC,SAASC,QAAQC,SAAgC;IAChD,OAAOA,aAAaA,UAAU,MAAM,GAAG,IAAIA,YAAYC;AACxD;AAsBO,MAAMC;IACK,SAAS,IAAIC,MAAgC;IAM9D,IAAW,OAAe;QACzB,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI;IACxB;IAGO,SAASH,SAAgC,EAAW;QAC1D,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAACD,QAAQC;IAChC;IAcA,MAAa,SACZA,SAAgC,EAChCI,EAAoB,EACpBC,YAAoBP,uBAAuB,EAC9B;QACb,MAAMQ,MAAMP,QAAQC;QACpB,MAAMO,WAAW,IAAI,CAAC,MAAM,CAAC,GAAG,CAACD,QAAQE,QAAQ,OAAO;QAKxD,MAAMC,UAAUD,QAAQ,aAAa;QACrC,MAAME,OAAOH,SAAS,IAAI,CACzB,IAAME,QAAQ,OAAO,EACrB,IAAMA,QAAQ,OAAO;QAEtB,IAAI,CAAC,MAAM,CAAC,GAAG,CAACH,KAAKI;QAEhBA,KAAK,IAAI,CAAC;YACd,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAACJ,SAASI,MAC5B,IAAI,CAAC,MAAM,CAAC,MAAM,CAACJ;QAErB;QAEA,IAAIK;QACJ,IAAI;YACH,MAAM,IAAIH,QAAc,CAACI,SAASC;gBACjCF,YAAYG,WAAW,IAAMD,OAAO,IAAIE,iBAAiBT,KAAKD,aAAaA;gBAC3EE,SAAS,IAAI,CACZ,IAAMK,WACN,IAAMA;YAER;YACA,OAAO,MAAMR;QACd,SAAU;YACT,IAAIO,AAAcK,WAAdL,WACHM,aAAaN;YAEdF,QAAQ,OAAO;QAChB;IACD;AACD"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Deterministic session cleanup candidate policy and periodic timer.
3
+ *
4
+ * @module SessionManager
5
+ */
6
+ import type { SessionId } from '../contracts/ids.js';
7
+ import type { Logger } from '../logger/StructuredLogger.js';
8
+ /** Minimal session facts needed by capacity and TTL policy. */
9
+ export interface SessionLike {
10
+ readonly lastAccessedAt: number;
11
+ readonly owner?: string;
12
+ readonly provenance?: 'restored';
13
+ }
14
+ /** Determines whether a candidate can be removed at this instant. */
15
+ export type SessionEligibility<S extends SessionLike> = (sessionId: SessionId, session: S) => boolean;
16
+ /** Configuration options for {@link SessionManager}. */
17
+ export interface SessionManagerConfig {
18
+ readonly defaultSessionId: SessionId;
19
+ readonly sessionTtlMs: number;
20
+ readonly cleanupIntervalMs: number;
21
+ readonly getMaxSessions: () => number;
22
+ readonly maxSessionsPerOwner?: number;
23
+ readonly logger?: Logger;
24
+ }
25
+ /**
26
+ * Produces complete candidate sets but never mutates the caller-owned session map.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * const victims = manager.planProspectiveAdmission(sessions, owner, lifecycle.isIdle);
31
+ * ```
32
+ */
33
+ export declare class SessionManager<S extends SessionLike> {
34
+ private readonly _defaultSessionId;
35
+ private readonly _sessionTtlMs;
36
+ private readonly _cleanupIntervalMs;
37
+ private readonly _getMaxSessions;
38
+ private readonly _maxSessionsPerOwner;
39
+ private readonly _logger;
40
+ private _cleanupTimer;
41
+ constructor(config: SessionManagerConfig);
42
+ /** @returns The periodic cleanup timer, when active. */
43
+ get timer(): ReturnType<typeof setInterval> | null;
44
+ /**
45
+ * Starts periodic TTL candidate cleanup and observes every asynchronous rejection.
46
+ */
47
+ startCleanupTimer(sessions: Map<SessionId, S>, isEligible: SessionEligibility<S>, cleanup: (sessionIds: readonly SessionId[]) => Promise<void>): void;
48
+ /** Stops the periodic cleanup timer. */
49
+ stopCleanupTimer(): void;
50
+ /**
51
+ * Plans the complete victim union required before admitting one prospective session.
52
+ *
53
+ * Owner quota need is selected first. Those victims also satisfy global need, then
54
+ * oldest globally eligible alternatives fill any remainder. `undefined` means no
55
+ * complete eligible set exists; the input map is never modified.
56
+ */
57
+ planProspectiveAdmission(sessions: ReadonlyMap<SessionId, S>, owner: string | undefined, isEligible: SessionEligibility<S>): readonly SessionId[] | undefined;
58
+ /** Returns stable oldest-first TTL candidates under the same eligibility rules. */
59
+ staleSessionCandidates(sessions: ReadonlyMap<SessionId, S>, isEligible: SessionEligibility<S>, now?: number): readonly SessionId[];
60
+ private _orderedLiveCandidates;
61
+ }
62
+ //# sourceMappingURL=SessionManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionManager.d.ts","sourceRoot":"","sources":["../../src/core/SessionManager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AAE5D,+DAA+D;AAC/D,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC;CACjC;AAED,qEAAqE;AACrE,MAAM,MAAM,kBAAkB,CAAC,CAAC,SAAS,WAAW,IAAI,CACvD,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,CAAC,KACN,OAAO,CAAC;AAEb,wDAAwD;AACxD,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,gBAAgB,EAAE,SAAS,CAAC;IACrC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,cAAc,EAAE,MAAM,MAAM,CAAC;IACtC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CACzB;AAQD;;;;;;;GAOG;AACH,qBAAa,cAAc,CAAC,CAAC,SAAS,WAAW;IAChD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAY;IAC9C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAC5C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAe;IAC/C,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAS;IAC9C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,aAAa,CAA+C;gBAEjD,MAAM,EAAE,oBAAoB;IAS/C,wDAAwD;IACxD,IAAW,KAAK,IAAI,UAAU,CAAC,OAAO,WAAW,CAAC,GAAG,IAAI,CAExD;IAED;;OAEG;IACI,iBAAiB,CACvB,QAAQ,EAAE,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,EAC3B,UAAU,EAAE,kBAAkB,CAAC,CAAC,CAAC,EACjC,OAAO,EAAE,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,GAC1D,IAAI;IAgBP,wCAAwC;IACjC,gBAAgB,IAAI,IAAI;IAM/B;;;;;;OAMG;IACI,wBAAwB,CAC9B,QAAQ,EAAE,WAAW,CAAC,SAAS,EAAE,CAAC,CAAC,EACnC,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,UAAU,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAC/B,SAAS,SAAS,EAAE,GAAG,SAAS;IAiCnC,mFAAmF;IAC5E,sBAAsB,CAC5B,QAAQ,EAAE,WAAW,CAAC,SAAS,EAAE,CAAC,CAAC,EACnC,UAAU,EAAE,kBAAkB,CAAC,CAAC,CAAC,EACjC,GAAG,GAAE,MAAmB,GACtB,SAAS,SAAS,EAAE;IAWvB,OAAO,CAAC,sBAAsB;CAe9B"}
@@ -0,0 +1,75 @@
1
+ import { NullLogger } from "../logger/NullLogger.js";
2
+ class SessionManager {
3
+ _defaultSessionId;
4
+ _sessionTtlMs;
5
+ _cleanupIntervalMs;
6
+ _getMaxSessions;
7
+ _maxSessionsPerOwner;
8
+ _logger;
9
+ _cleanupTimer = null;
10
+ constructor(config){
11
+ this._defaultSessionId = config.defaultSessionId;
12
+ this._sessionTtlMs = config.sessionTtlMs;
13
+ this._cleanupIntervalMs = config.cleanupIntervalMs;
14
+ this._getMaxSessions = config.getMaxSessions;
15
+ this._maxSessionsPerOwner = config.maxSessionsPerOwner ?? 50;
16
+ this._logger = config.logger ?? new NullLogger();
17
+ }
18
+ get timer() {
19
+ return this._cleanupTimer;
20
+ }
21
+ startCleanupTimer(sessions, isEligible, cleanup) {
22
+ if (null !== this._cleanupTimer) return;
23
+ this._cleanupTimer = setInterval(()=>{
24
+ const candidates = this.staleSessionCandidates(sessions, isEligible);
25
+ if (0 === candidates.length) return;
26
+ Promise.resolve().then(()=>cleanup(candidates)).catch((error)=>{
27
+ this._logger.error('Session cleanup failed', {
28
+ error
29
+ });
30
+ });
31
+ }, this._cleanupIntervalMs);
32
+ if ('object' == typeof this._cleanupTimer && 'unref' in this._cleanupTimer) this._cleanupTimer.unref();
33
+ }
34
+ stopCleanupTimer() {
35
+ if (null === this._cleanupTimer) return;
36
+ clearInterval(this._cleanupTimer);
37
+ this._cleanupTimer = null;
38
+ }
39
+ planProspectiveAdmission(sessions, owner, isEligible) {
40
+ const live = this._orderedLiveCandidates(sessions);
41
+ const selected = [];
42
+ if (void 0 !== owner) {
43
+ const ownerCount = live.filter((candidate)=>candidate.session.owner === owner).length;
44
+ const ownerNeed = Math.max(0, ownerCount + 1 - this._maxSessionsPerOwner);
45
+ selected.push(...live.filter((candidate)=>candidate.session.owner === owner && candidate.sessionId !== this._defaultSessionId && isEligible(candidate.sessionId, candidate.session)).slice(0, ownerNeed));
46
+ if (selected.length < ownerNeed) return;
47
+ }
48
+ const globalNeed = Math.max(0, live.length + 1 - this._getMaxSessions());
49
+ const remainingNeed = Math.max(0, globalNeed - selected.length);
50
+ const selectedIds = new Set(selected.map((candidate)=>candidate.sessionId));
51
+ const alternatives = live.filter((candidate)=>candidate.sessionId !== this._defaultSessionId && !selectedIds.has(candidate.sessionId) && isEligible(candidate.sessionId, candidate.session));
52
+ if (alternatives.length < remainingNeed) return;
53
+ selected.push(...alternatives.slice(0, remainingNeed));
54
+ return selected.map((candidate)=>candidate.sessionId);
55
+ }
56
+ staleSessionCandidates(sessions, isEligible, now = Date.now()) {
57
+ return this._orderedLiveCandidates(sessions).filter((candidate)=>candidate.sessionId !== this._defaultSessionId && now - candidate.session.lastAccessedAt > this._sessionTtlMs && isEligible(candidate.sessionId, candidate.session)).map((candidate)=>candidate.sessionId);
58
+ }
59
+ _orderedLiveCandidates(sessions) {
60
+ const candidates = [];
61
+ let insertionOrder = 0;
62
+ for (const [sessionId, session] of sessions){
63
+ if ('restored' !== session.provenance) candidates.push({
64
+ sessionId,
65
+ session,
66
+ insertionOrder
67
+ });
68
+ insertionOrder += 1;
69
+ }
70
+ return candidates.sort((left, right)=>left.session.lastAccessedAt - right.session.lastAccessedAt || left.insertionOrder - right.insertionOrder);
71
+ }
72
+ }
73
+ export { SessionManager };
74
+
75
+ //# sourceMappingURL=SessionManager.js.map