@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.
Files changed (129) hide show
  1. package/README.md +20 -3
  2. package/dist/contracts/PersistenceBackend.d.ts +10 -1
  3. package/dist/contracts/PersistenceBackend.d.ts.map +1 -1
  4. package/dist/contracts/calibrator.d.ts +9 -6
  5. package/dist/contracts/calibrator.d.ts.map +1 -1
  6. package/dist/contracts/interfaces.d.ts +6 -0
  7. package/dist/contracts/interfaces.d.ts.map +1 -1
  8. package/dist/contracts/persistence-work.d.ts +13 -1
  9. package/dist/contracts/persistence-work.d.ts.map +1 -1
  10. package/dist/contracts/strategy.d.ts +14 -9
  11. package/dist/contracts/strategy.d.ts.map +1 -1
  12. package/dist/core/DurableThoughtIdentityIndex.d.ts +35 -0
  13. package/dist/core/DurableThoughtIdentityIndex.d.ts.map +1 -0
  14. package/dist/core/DurableThoughtIdentityIndex.js +94 -0
  15. package/dist/core/DurableThoughtIdentityIndex.js.map +1 -0
  16. package/dist/core/HistoryManager.d.ts +5 -0
  17. package/dist/core/HistoryManager.d.ts.map +1 -1
  18. package/dist/core/HistoryManager.js +37 -4
  19. package/dist/core/HistoryManager.js.map +1 -1
  20. package/dist/core/IHistoryManager.d.ts +2 -0
  21. package/dist/core/IHistoryManager.d.ts.map +1 -1
  22. package/dist/core/PersistenceBuffer.d.ts +7 -1
  23. package/dist/core/PersistenceBuffer.d.ts.map +1 -1
  24. package/dist/core/PersistenceBuffer.js +13 -1
  25. package/dist/core/PersistenceBuffer.js.map +1 -1
  26. package/dist/core/PersistenceRestore.d.ts.map +1 -1
  27. package/dist/core/PersistenceRestore.js +11 -3
  28. package/dist/core/PersistenceRestore.js.map +1 -1
  29. package/dist/core/PersistenceWorkQueue.d.ts +13 -1
  30. package/dist/core/PersistenceWorkQueue.d.ts.map +1 -1
  31. package/dist/core/PersistenceWorkQueue.js +86 -11
  32. package/dist/core/PersistenceWorkQueue.js.map +1 -1
  33. package/dist/core/PersistenceWriter.d.ts.map +1 -1
  34. package/dist/core/PersistenceWriter.js +3 -0
  35. package/dist/core/PersistenceWriter.js.map +1 -1
  36. package/dist/core/ThoughtEvaluator.d.ts +7 -4
  37. package/dist/core/ThoughtEvaluator.d.ts.map +1 -1
  38. package/dist/core/ThoughtEvaluator.js +15 -7
  39. package/dist/core/ThoughtEvaluator.js.map +1 -1
  40. package/dist/core/ThoughtProcessor.d.ts.map +1 -1
  41. package/dist/core/ThoughtProcessor.js +19 -7
  42. package/dist/core/ThoughtProcessor.js.map +1 -1
  43. package/dist/core/ThoughtReferenceIndex.d.ts +4 -0
  44. package/dist/core/ThoughtReferenceIndex.d.ts.map +1 -1
  45. package/dist/core/ThoughtReferenceIndex.js +28 -1
  46. package/dist/core/ThoughtReferenceIndex.js.map +1 -1
  47. package/dist/core/VerificationOutcomeAdmission.d.ts +8 -1
  48. package/dist/core/VerificationOutcomeAdmission.d.ts.map +1 -1
  49. package/dist/core/VerificationOutcomeAdmission.js.map +1 -1
  50. package/dist/core/VerificationTargetRestore.d.ts +5 -0
  51. package/dist/core/VerificationTargetRestore.d.ts.map +1 -0
  52. package/dist/core/VerificationTargetRestore.js +61 -0
  53. package/dist/core/VerificationTargetRestore.js.map +1 -0
  54. package/dist/core/evaluator/Aggregator.d.ts +2 -1
  55. package/dist/core/evaluator/Aggregator.d.ts.map +1 -1
  56. package/dist/core/evaluator/Aggregator.js +4 -12
  57. package/dist/core/evaluator/Aggregator.js.map +1 -1
  58. package/dist/core/evaluator/Calibrator.d.ts +13 -9
  59. package/dist/core/evaluator/Calibrator.d.ts.map +1 -1
  60. package/dist/core/evaluator/Calibrator.js +25 -11
  61. package/dist/core/evaluator/Calibrator.js.map +1 -1
  62. package/dist/core/evaluator/PatternDetector.d.ts +2 -1
  63. package/dist/core/evaluator/PatternDetector.d.ts.map +1 -1
  64. package/dist/core/evaluator/PatternDetector.js +13 -12
  65. package/dist/core/evaluator/PatternDetector.js.map +1 -1
  66. package/dist/core/evaluator/SignalComputer.d.ts +3 -2
  67. package/dist/core/evaluator/SignalComputer.d.ts.map +1 -1
  68. package/dist/core/evaluator/SignalComputer.js +4 -9
  69. package/dist/core/evaluator/SignalComputer.js.map +1 -1
  70. package/dist/core/evaluator/VerificationLinks.d.ts +19 -0
  71. package/dist/core/evaluator/VerificationLinks.d.ts.map +1 -0
  72. package/dist/core/evaluator/VerificationLinks.js +113 -0
  73. package/dist/core/evaluator/VerificationLinks.js.map +1 -0
  74. package/dist/core/evaluator/calibration-math.d.ts +12 -2
  75. package/dist/core/evaluator/calibration-math.d.ts.map +1 -1
  76. package/dist/core/evaluator/calibration-math.js +11 -2
  77. package/dist/core/evaluator/calibration-math.js.map +1 -1
  78. package/dist/core/graph/EdgeEmitter.d.ts.map +1 -1
  79. package/dist/core/graph/EdgeEmitter.js +2 -1
  80. package/dist/core/graph/EdgeEmitter.js.map +1 -1
  81. package/dist/core/graph/GraphView.d.ts +5 -5
  82. package/dist/core/graph/GraphView.d.ts.map +1 -1
  83. package/dist/core/graph/GraphView.js +11 -11
  84. package/dist/core/graph/GraphView.js.map +1 -1
  85. package/dist/core/reasoning/ActiveEvidenceProjection.d.ts +20 -0
  86. package/dist/core/reasoning/ActiveEvidenceProjection.d.ts.map +1 -0
  87. package/dist/core/reasoning/ActiveEvidenceProjection.js +100 -0
  88. package/dist/core/reasoning/ActiveEvidenceProjection.js.map +1 -0
  89. package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.d.ts +5 -4
  90. package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.d.ts.map +1 -1
  91. package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.js +24 -14
  92. package/dist/core/reasoning/strategies/TreeOfThoughtStrategy.js.map +1 -1
  93. package/dist/lib.d.ts.map +1 -1
  94. package/dist/lib.js +3 -1
  95. package/dist/lib.js.map +1 -1
  96. package/dist/persistence/BacktrackPersistence.d.ts +13 -0
  97. package/dist/persistence/BacktrackPersistence.d.ts.map +1 -0
  98. package/dist/persistence/BacktrackPersistence.js +90 -0
  99. package/dist/persistence/BacktrackPersistence.js.map +1 -0
  100. package/dist/persistence/FilePersistence.d.ts +2 -1
  101. package/dist/persistence/FilePersistence.d.ts.map +1 -1
  102. package/dist/persistence/FilePersistence.js +28 -0
  103. package/dist/persistence/FilePersistence.js.map +1 -1
  104. package/dist/persistence/FileSnapshotV2.d.ts +2 -0
  105. package/dist/persistence/FileSnapshotV2.d.ts.map +1 -1
  106. package/dist/persistence/FileSnapshotV2.js +6 -2
  107. package/dist/persistence/FileSnapshotV2.js.map +1 -1
  108. package/dist/persistence/MemoryPersistence.d.ts +2 -1
  109. package/dist/persistence/MemoryPersistence.d.ts.map +1 -1
  110. package/dist/persistence/MemoryPersistence.js +21 -1
  111. package/dist/persistence/MemoryPersistence.js.map +1 -1
  112. package/dist/persistence/PersistenceCodec.d.ts.map +1 -1
  113. package/dist/persistence/PersistenceCodec.js +5 -0
  114. package/dist/persistence/PersistenceCodec.js.map +1 -1
  115. package/dist/persistence/PersistenceErrors.d.ts +1 -1
  116. package/dist/persistence/PersistenceErrors.d.ts.map +1 -1
  117. package/dist/persistence/PersistenceErrors.js.map +1 -1
  118. package/dist/persistence/PersistenceScope.d.ts +1 -1
  119. package/dist/persistence/PersistenceScope.d.ts.map +1 -1
  120. package/dist/persistence/PersistenceScope.js.map +1 -1
  121. package/dist/persistence/SqlitePersistence.d.ts +2 -1
  122. package/dist/persistence/SqlitePersistence.d.ts.map +1 -1
  123. package/dist/persistence/SqlitePersistence.js +18 -0
  124. package/dist/persistence/SqlitePersistence.js.map +1 -1
  125. package/dist/registry/BaseRegistry.d.ts +10 -2
  126. package/dist/registry/BaseRegistry.d.ts.map +1 -1
  127. package/dist/registry/BaseRegistry.js +128 -81
  128. package/dist/registry/BaseRegistry.js.map +1 -1
  129. package/package.json +4 -3
@@ -6,7 +6,7 @@ export type PersistenceScope = {
6
6
  readonly branchId?: BranchId;
7
7
  };
8
8
  /** Operations whose payload ownership is validated before mutation. */
9
- export type PersistenceWriteOperation = 'saveThought' | 'saveThoughtForSession' | 'saveBranch' | 'saveBranchForSession' | 'saveEdges' | 'saveSummaries';
9
+ export type PersistenceWriteOperation = 'saveThought' | 'saveThoughtForSession' | 'saveBacktrackForSession' | 'saveBranch' | 'saveBranchForSession' | 'saveEdges' | 'saveSummaries';
10
10
  /** Error raised before a write whose payload belongs to another namespace. */
11
11
  export declare class PersistenceScopeMismatchError extends SequentialThinkingError {
12
12
  readonly operation: PersistenceWriteOperation;
@@ -1 +1 @@
1
- {"version":3,"file":"PersistenceErrors.d.ts","sourceRoot":"","sources":["../../src/persistence/PersistenceErrors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAe,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAEpE,6DAA6D;AAC7D,MAAM,MAAM,gBAAgB,GAAG;IAC9B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;CAC7B,CAAC;AAEF,uEAAuE;AACvE,MAAM,MAAM,yBAAyB,GAClC,aAAa,GACb,uBAAuB,GACvB,YAAY,GACZ,sBAAsB,GACtB,WAAW,GACX,eAAe,CAAC;AAEnB,8EAA8E;AAC9E,qBAAa,6BAA8B,SAAQ,uBAAuB;IACzE,SAAgB,SAAS,EAAE,yBAAyB,CAAC;IACrD,SAAgB,aAAa,EAAE,gBAAgB,CAAC;IAChD,SAAgB,YAAY,EAAE,SAAS,gBAAgB,EAAE,CAAC;gBAGzD,SAAS,EAAE,yBAAyB,EACpC,aAAa,EAAE,gBAAgB,EAC/B,YAAY,EAAE,SAAS,gBAAgB,EAAE;CAW1C"}
1
+ {"version":3,"file":"PersistenceErrors.d.ts","sourceRoot":"","sources":["../../src/persistence/PersistenceErrors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAe,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAEpE,6DAA6D;AAC7D,MAAM,MAAM,gBAAgB,GAAG;IAC9B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;CAC7B,CAAC;AAEF,uEAAuE;AACvE,MAAM,MAAM,yBAAyB,GAClC,aAAa,GACb,uBAAuB,GACvB,yBAAyB,GACzB,YAAY,GACZ,sBAAsB,GACtB,WAAW,GACX,eAAe,CAAC;AAEnB,8EAA8E;AAC9E,qBAAa,6BAA8B,SAAQ,uBAAuB;IACzE,SAAgB,SAAS,EAAE,yBAAyB,CAAC;IACrD,SAAgB,aAAa,EAAE,gBAAgB,CAAC;IAChD,SAAgB,YAAY,EAAE,SAAS,gBAAgB,EAAE,CAAC;gBAGzD,SAAS,EAAE,yBAAyB,EACpC,aAAa,EAAE,gBAAgB,EAC/B,YAAY,EAAE,SAAS,gBAAgB,EAAE;CAW1C"}
@@ -1 +1 @@
1
- {"version":3,"file":"persistence/PersistenceErrors.js","sources":["../../src/persistence/PersistenceErrors.ts"],"sourcesContent":["import type { BranchId, SessionId } from '../contracts/ids.js';\nimport { ERROR_CODES, SequentialThinkingError } from '../errors.js';\n\n/** Durable namespace associated with a persistence write. */\nexport type PersistenceScope = {\n\treadonly sessionId: SessionId;\n\treadonly branchId?: BranchId;\n};\n\n/** Operations whose payload ownership is validated before mutation. */\nexport type PersistenceWriteOperation =\n\t| 'saveThought'\n\t| 'saveThoughtForSession'\n\t| 'saveBranch'\n\t| 'saveBranchForSession'\n\t| 'saveEdges'\n\t| 'saveSummaries';\n\n/** Error raised before a write whose payload belongs to another namespace. */\nexport class PersistenceScopeMismatchError extends SequentialThinkingError {\n\tpublic readonly operation: PersistenceWriteOperation;\n\tpublic readonly expectedScope: PersistenceScope;\n\tpublic readonly actualScopes: readonly PersistenceScope[];\n\n\tconstructor(\n\t\toperation: PersistenceWriteOperation,\n\t\texpectedScope: PersistenceScope,\n\t\tactualScopes: readonly PersistenceScope[]\n\t) {\n\t\tsuper(\n\t\t\t`Persistence payload scope does not match '${expectedScope.sessionId}' for ${operation}`,\n\t\t\tERROR_CODES.PERSISTENCE_SCOPE_MISMATCH\n\t\t);\n\t\tthis.name = 'PersistenceScopeMismatchError';\n\t\tthis.operation = operation;\n\t\tthis.expectedScope = expectedScope;\n\t\tthis.actualScopes = [...actualScopes];\n\t}\n}\n"],"names":["PersistenceScopeMismatchError","SequentialThinkingError","operation","expectedScope","actualScopes","ERROR_CODES"],"mappings":";AAmBO,MAAMA,sCAAsCC;IAClC,UAAqC;IACrC,cAAgC;IAChC,aAA0C;IAE1D,YACCC,SAAoC,EACpCC,aAA+B,EAC/BC,YAAyC,CACxC;QACD,KAAK,CACJ,CAAC,0CAA0C,EAAED,cAAc,SAAS,CAAC,MAAM,EAAED,WAAW,EACxFG,YAAY,0BAA0B;QAEvC,IAAI,CAAC,IAAI,GAAG;QACZ,IAAI,CAAC,SAAS,GAAGH;QACjB,IAAI,CAAC,aAAa,GAAGC;QACrB,IAAI,CAAC,YAAY,GAAG;eAAIC;SAAa;IACtC;AACD"}
1
+ {"version":3,"file":"persistence/PersistenceErrors.js","sources":["../../src/persistence/PersistenceErrors.ts"],"sourcesContent":["import type { BranchId, SessionId } from '../contracts/ids.js';\nimport { ERROR_CODES, SequentialThinkingError } from '../errors.js';\n\n/** Durable namespace associated with a persistence write. */\nexport type PersistenceScope = {\n\treadonly sessionId: SessionId;\n\treadonly branchId?: BranchId;\n};\n\n/** Operations whose payload ownership is validated before mutation. */\nexport type PersistenceWriteOperation =\n\t| 'saveThought'\n\t| 'saveThoughtForSession'\n\t| 'saveBacktrackForSession'\n\t| 'saveBranch'\n\t| 'saveBranchForSession'\n\t| 'saveEdges'\n\t| 'saveSummaries';\n\n/** Error raised before a write whose payload belongs to another namespace. */\nexport class PersistenceScopeMismatchError extends SequentialThinkingError {\n\tpublic readonly operation: PersistenceWriteOperation;\n\tpublic readonly expectedScope: PersistenceScope;\n\tpublic readonly actualScopes: readonly PersistenceScope[];\n\n\tconstructor(\n\t\toperation: PersistenceWriteOperation,\n\t\texpectedScope: PersistenceScope,\n\t\tactualScopes: readonly PersistenceScope[]\n\t) {\n\t\tsuper(\n\t\t\t`Persistence payload scope does not match '${expectedScope.sessionId}' for ${operation}`,\n\t\t\tERROR_CODES.PERSISTENCE_SCOPE_MISMATCH\n\t\t);\n\t\tthis.name = 'PersistenceScopeMismatchError';\n\t\tthis.operation = operation;\n\t\tthis.expectedScope = expectedScope;\n\t\tthis.actualScopes = [...actualScopes];\n\t}\n}\n"],"names":["PersistenceScopeMismatchError","SequentialThinkingError","operation","expectedScope","actualScopes","ERROR_CODES"],"mappings":";AAoBO,MAAMA,sCAAsCC;IAClC,UAAqC;IACrC,cAAgC;IAChC,aAA0C;IAE1D,YACCC,SAAoC,EACpCC,aAA+B,EAC/BC,YAAyC,CACxC;QACD,KAAK,CACJ,CAAC,0CAA0C,EAAED,cAAc,SAAS,CAAC,MAAM,EAAED,WAAW,EACxFG,YAAY,0BAA0B;QAEvC,IAAI,CAAC,IAAI,GAAG;QACZ,IAAI,CAAC,SAAS,GAAGH;QACjB,IAAI,CAAC,aAAa,GAAGC;QACrB,IAAI,CAAC,YAAY,GAAG;eAAIC;SAAa;IACtC;AACD"}
@@ -23,7 +23,7 @@ export declare function assertPersistableThoughts(thoughts: readonly ThoughtData
23
23
  * @returns Nothing when all collection identities are compatible.
24
24
  */
25
25
  export declare function assertPersistableThoughtCollections(collections: readonly PersistedThoughtCollection[], sourcePath: string): void;
26
- export declare function assertThoughtScope(operation: Extract<PersistenceWriteOperation, 'saveThought' | 'saveThoughtForSession'>, sessionId: SessionId, thought: ThoughtData): void;
26
+ export declare function assertThoughtScope(operation: Extract<PersistenceWriteOperation, 'saveThought' | 'saveThoughtForSession' | 'saveBacktrackForSession'>, sessionId: SessionId, thought: ThoughtData): void;
27
27
  export declare function assertBranchScope(operation: Extract<PersistenceWriteOperation, 'saveBranch' | 'saveBranchForSession'>, sessionId: SessionId, branchId: BranchId, thoughts: readonly ThoughtData[]): void;
28
28
  export declare function assertEdgeScopes(sessionId: SessionId, edges: readonly Edge[]): void;
29
29
  export declare function assertSummaryScopes(sessionId: SessionId, summaries: readonly Summary[]): void;
@@ -1 +1 @@
1
- {"version":3,"file":"PersistenceScope.d.ts","sourceRoot":"","sources":["../../src/persistence/PersistenceScope.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EAA2B,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAE7F,OAAO,EAGN,KAAK,yBAAyB,EAC9B,MAAM,wBAAwB,CAAC;AAIhC,KAAK,oBAAoB,GAAG,WAAW,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAElE,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;CAC1C;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,QAAQ,CAKpF;AAED,wBAAgB,yBAAyB,CACxC,QAAQ,EAAE,SAAS,WAAW,EAAE,EAChC,UAAU,EAAE,MAAM,GAChB,OAAO,CAAC,QAAQ,IAAI,SAAS,oBAAoB,EAAE,CAWrD;AAED;;;;;;;;;GASG;AACH,wBAAgB,mCAAmC,CAClD,WAAW,EAAE,SAAS,0BAA0B,EAAE,EAClD,UAAU,EAAE,MAAM,GAChB,IAAI,CAqBN;AASD,wBAAgB,kBAAkB,CACjC,SAAS,EAAE,OAAO,CAAC,yBAAyB,EAAE,aAAa,GAAG,uBAAuB,CAAC,EACtF,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,WAAW,GAClB,IAAI,CAKN;AAED,wBAAgB,iBAAiB,CAChC,SAAS,EAAE,OAAO,CAAC,yBAAyB,EAAE,YAAY,GAAG,sBAAsB,CAAC,EACpF,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,SAAS,WAAW,EAAE,GAC9B,IAAI,CAYN;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,IAAI,EAAE,GAAG,IAAI,CAOnF;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,OAAO,EAAE,GAAG,IAAI,CAU7F;AAED,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,SAAS;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,EAAE,EAC3C,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GAAG,SAAS,GAC5B,IAAI,CAWN;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED,wBAAgB,oBAAoB,CACnC,IAAI,EAAE;IAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,EACzD,KAAK,EAAE;IAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GACxD,MAAM,CAER"}
1
+ {"version":3,"file":"PersistenceScope.d.ts","sourceRoot":"","sources":["../../src/persistence/PersistenceScope.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EAA2B,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAE7F,OAAO,EAGN,KAAK,yBAAyB,EAC9B,MAAM,wBAAwB,CAAC;AAIhC,KAAK,oBAAoB,GAAG,WAAW,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAElE,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;CAC1C;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,QAAQ,CAKpF;AAED,wBAAgB,yBAAyB,CACxC,QAAQ,EAAE,SAAS,WAAW,EAAE,EAChC,UAAU,EAAE,MAAM,GAChB,OAAO,CAAC,QAAQ,IAAI,SAAS,oBAAoB,EAAE,CAWrD;AAED;;;;;;;;;GASG;AACH,wBAAgB,mCAAmC,CAClD,WAAW,EAAE,SAAS,0BAA0B,EAAE,EAClD,UAAU,EAAE,MAAM,GAChB,IAAI,CAqBN;AASD,wBAAgB,kBAAkB,CACjC,SAAS,EAAE,OAAO,CACjB,yBAAyB,EACzB,aAAa,GAAG,uBAAuB,GAAG,yBAAyB,CACnE,EACD,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,WAAW,GAClB,IAAI,CAKN;AAED,wBAAgB,iBAAiB,CAChC,SAAS,EAAE,OAAO,CAAC,yBAAyB,EAAE,YAAY,GAAG,sBAAsB,CAAC,EACpF,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,SAAS,WAAW,EAAE,GAC9B,IAAI,CAYN;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,IAAI,EAAE,GAAG,IAAI,CAOnF;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,OAAO,EAAE,GAAG,IAAI,CAU7F;AAED,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,SAAS;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,EAAE,EAC3C,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GAAG,SAAS,GAC5B,IAAI,CAWN;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED,wBAAgB,oBAAoB,CACnC,IAAI,EAAE;IAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,EACzD,KAAK,EAAE;IAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GACxD,MAAM,CAER"}
@@ -1 +1 @@
1
- {"version":3,"file":"persistence/PersistenceScope.js","sources":["../../src/persistence/PersistenceScope.ts"],"sourcesContent":["import { isDeepStrictEqual } from 'node:util';\nimport type { ThoughtData } from '../core/thought.js';\nimport type { Edge } from '../core/graph/Edge.js';\nimport type { Summary } from '../core/compression/Summary.js';\nimport { asBranchId, asSessionId, type BranchId, type SessionId } from '../contracts/ids.js';\nimport { PersistenceCompatibilityError } from '../errors.js';\nimport {\n\tPersistenceScopeMismatchError,\n\ttype PersistenceScope,\n\ttype PersistenceWriteOperation,\n} from './PersistenceErrors.js';\n\nconst BRANCH_ID_PATTERN = /^[A-Za-z0-9_-]{1,50}$/;\n\ntype PersistedThoughtData = ThoughtData & { readonly id: string };\n\nexport interface PersistedThoughtCollection {\n\treadonly sessionId: SessionId;\n\treadonly thoughts: readonly ThoughtData[];\n}\n\nexport function parsePersistenceBranchId(value: string, sourcePath: string): BranchId {\n\tif (!BRANCH_ID_PATTERN.test(value)) {\n\t\tthrow new PersistenceCompatibilityError(sourcePath, `invalid branch id '${value}'`);\n\t}\n\treturn asBranchId(value);\n}\n\nexport function assertPersistableThoughts(\n\tthoughts: readonly ThoughtData[],\n\tsourcePath: string\n): asserts thoughts is readonly PersistedThoughtData[] {\n\tconst identifiers = new Set<string>();\n\tfor (const thought of thoughts) {\n\t\tif (thought.id === undefined || thought.id.length === 0) {\n\t\t\tthrow new PersistenceCompatibilityError(sourcePath, 'persisted thoughts require an id');\n\t\t}\n\t\tif (identifiers.has(thought.id)) {\n\t\t\tthrow new PersistenceCompatibilityError(sourcePath, `duplicate thought id '${thought.id}'`);\n\t\t}\n\t\tidentifiers.add(thought.id);\n\t}\n}\n\n/**\n * Enforces thought identity across session-tagged persisted arrays.\n *\n * Each array must contain unique IDs. Reuse between arrays is valid only for deeply equal\n * payloads in the same session; equal identifiers in different sessions are independent.\n *\n * @param collections - History and branch arrays tagged with their owning session.\n * @param sourcePath - Persistence location included in compatibility errors.\n * @returns Nothing when all collection identities are compatible.\n */\nexport function assertPersistableThoughtCollections(\n\tcollections: readonly PersistedThoughtCollection[],\n\tsourcePath: string\n): void {\n\tconst payloadsBySession = new Map<SessionId, Map<string, ThoughtData>>();\n\tfor (const collection of collections) {\n\t\tassertPersistableThoughts(collection.thoughts, sourcePath);\n\t\tlet sessionPayloads = payloadsBySession.get(collection.sessionId);\n\t\tif (sessionPayloads === undefined) {\n\t\t\tsessionPayloads = new Map<string, ThoughtData>();\n\t\t\tpayloadsBySession.set(collection.sessionId, sessionPayloads);\n\t\t}\n\t\tfor (const thought of collection.thoughts) {\n\t\t\tconst identifier = thought.id;\n\t\t\tconst existing = sessionPayloads.get(identifier);\n\t\t\tif (existing !== undefined && !isDeepStrictEqual(existing, thought)) {\n\t\t\t\tthrow new PersistenceCompatibilityError(\n\t\t\t\t\tsourcePath,\n\t\t\t\t\t`conflicting thought id '${identifier}'`\n\t\t\t\t);\n\t\t\t}\n\t\t\tsessionPayloads.set(identifier, thought);\n\t\t}\n\t}\n}\n\nfunction thoughtScope(thought: ThoughtData): PersistenceScope {\n\treturn {\n\t\tsessionId: asSessionId(thought.session_id),\n\t\t...(thought.branch_id === undefined ? {} : { branchId: thought.branch_id }),\n\t};\n}\n\nexport function assertThoughtScope(\n\toperation: Extract<PersistenceWriteOperation, 'saveThought' | 'saveThoughtForSession'>,\n\tsessionId: SessionId,\n\tthought: ThoughtData\n): void {\n\tconst actualScope = thoughtScope(thought);\n\tif (actualScope.sessionId !== sessionId) {\n\t\tthrow new PersistenceScopeMismatchError(operation, { sessionId }, [actualScope]);\n\t}\n}\n\nexport function assertBranchScope(\n\toperation: Extract<PersistenceWriteOperation, 'saveBranch' | 'saveBranchForSession'>,\n\tsessionId: SessionId,\n\tbranchId: BranchId,\n\tthoughts: readonly ThoughtData[]\n): void {\n\tconst expectedScope = { sessionId, branchId } satisfies PersistenceScope;\n\tconst mismatches = thoughts\n\t\t.map(thoughtScope)\n\t\t.filter(\n\t\t\t(scope) =>\n\t\t\t\tscope.sessionId !== expectedScope.sessionId ||\n\t\t\t\t(scope.branchId !== undefined && scope.branchId !== expectedScope.branchId)\n\t\t);\n\tif (mismatches.length > 0) {\n\t\tthrow new PersistenceScopeMismatchError(operation, expectedScope, mismatches);\n\t}\n}\n\nexport function assertEdgeScopes(sessionId: SessionId, edges: readonly Edge[]): void {\n\tconst actualScopes = edges\n\t\t.filter((edge) => edge.sessionId !== sessionId)\n\t\t.map((edge) => ({ sessionId: edge.sessionId }));\n\tif (actualScopes.length > 0) {\n\t\tthrow new PersistenceScopeMismatchError('saveEdges', { sessionId }, actualScopes);\n\t}\n}\n\nexport function assertSummaryScopes(sessionId: SessionId, summaries: readonly Summary[]): void {\n\tconst actualScopes = summaries\n\t\t.filter((summary) => summary.sessionId !== sessionId)\n\t\t.map((summary) => ({\n\t\t\tsessionId: summary.sessionId,\n\t\t\t...(summary.branchId === undefined ? {} : { branchId: summary.branchId }),\n\t\t}));\n\tif (actualScopes.length > 0) {\n\t\tthrow new PersistenceScopeMismatchError('saveSummaries', { sessionId }, actualScopes);\n\t}\n}\n\nexport function assertUniqueRecordIds(\n\trecords: readonly { readonly id: string }[],\n\tsourcePath: string,\n\trecordKind: 'edge' | 'summary'\n): void {\n\tconst identifiers = new Set<string>();\n\tfor (const record of records) {\n\t\tif (identifiers.has(record.id)) {\n\t\t\tthrow new PersistenceCompatibilityError(\n\t\t\t\tsourcePath,\n\t\t\t\t`duplicate ${recordKind} id '${record.id}'`\n\t\t\t);\n\t\t}\n\t\tidentifiers.add(record.id);\n\t}\n}\n\nexport function compareCodePoint(left: string, right: string): number {\n\treturn left < right ? -1 : left > right ? 1 : 0;\n}\n\nexport function compareCreatedThenId(\n\tleft: { readonly createdAt: number; readonly id: string },\n\tright: { readonly createdAt: number; readonly id: string }\n): number {\n\treturn left.createdAt - right.createdAt || compareCodePoint(left.id, right.id);\n}\n"],"names":["BRANCH_ID_PATTERN","parsePersistenceBranchId","value","sourcePath","PersistenceCompatibilityError","asBranchId","assertPersistableThoughts","thoughts","identifiers","Set","thought","undefined","assertPersistableThoughtCollections","collections","payloadsBySession","Map","collection","sessionPayloads","identifier","existing","isDeepStrictEqual","thoughtScope","asSessionId","assertThoughtScope","operation","sessionId","actualScope","PersistenceScopeMismatchError","assertBranchScope","branchId","expectedScope","mismatches","scope","assertEdgeScopes","edges","actualScopes","edge","assertSummaryScopes","summaries","summary","assertUniqueRecordIds","records","recordKind","record","compareCodePoint","left","right","compareCreatedThenId"],"mappings":"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"}
1
+ {"version":3,"file":"persistence/PersistenceScope.js","sources":["../../src/persistence/PersistenceScope.ts"],"sourcesContent":["import { isDeepStrictEqual } from 'node:util';\nimport type { ThoughtData } from '../core/thought.js';\nimport type { Edge } from '../core/graph/Edge.js';\nimport type { Summary } from '../core/compression/Summary.js';\nimport { asBranchId, asSessionId, type BranchId, type SessionId } from '../contracts/ids.js';\nimport { PersistenceCompatibilityError } from '../errors.js';\nimport {\n\tPersistenceScopeMismatchError,\n\ttype PersistenceScope,\n\ttype PersistenceWriteOperation,\n} from './PersistenceErrors.js';\n\nconst BRANCH_ID_PATTERN = /^[A-Za-z0-9_-]{1,50}$/;\n\ntype PersistedThoughtData = ThoughtData & { readonly id: string };\n\nexport interface PersistedThoughtCollection {\n\treadonly sessionId: SessionId;\n\treadonly thoughts: readonly ThoughtData[];\n}\n\nexport function parsePersistenceBranchId(value: string, sourcePath: string): BranchId {\n\tif (!BRANCH_ID_PATTERN.test(value)) {\n\t\tthrow new PersistenceCompatibilityError(sourcePath, `invalid branch id '${value}'`);\n\t}\n\treturn asBranchId(value);\n}\n\nexport function assertPersistableThoughts(\n\tthoughts: readonly ThoughtData[],\n\tsourcePath: string\n): asserts thoughts is readonly PersistedThoughtData[] {\n\tconst identifiers = new Set<string>();\n\tfor (const thought of thoughts) {\n\t\tif (thought.id === undefined || thought.id.length === 0) {\n\t\t\tthrow new PersistenceCompatibilityError(sourcePath, 'persisted thoughts require an id');\n\t\t}\n\t\tif (identifiers.has(thought.id)) {\n\t\t\tthrow new PersistenceCompatibilityError(sourcePath, `duplicate thought id '${thought.id}'`);\n\t\t}\n\t\tidentifiers.add(thought.id);\n\t}\n}\n\n/**\n * Enforces thought identity across session-tagged persisted arrays.\n *\n * Each array must contain unique IDs. Reuse between arrays is valid only for deeply equal\n * payloads in the same session; equal identifiers in different sessions are independent.\n *\n * @param collections - History and branch arrays tagged with their owning session.\n * @param sourcePath - Persistence location included in compatibility errors.\n * @returns Nothing when all collection identities are compatible.\n */\nexport function assertPersistableThoughtCollections(\n\tcollections: readonly PersistedThoughtCollection[],\n\tsourcePath: string\n): void {\n\tconst payloadsBySession = new Map<SessionId, Map<string, ThoughtData>>();\n\tfor (const collection of collections) {\n\t\tassertPersistableThoughts(collection.thoughts, sourcePath);\n\t\tlet sessionPayloads = payloadsBySession.get(collection.sessionId);\n\t\tif (sessionPayloads === undefined) {\n\t\t\tsessionPayloads = new Map<string, ThoughtData>();\n\t\t\tpayloadsBySession.set(collection.sessionId, sessionPayloads);\n\t\t}\n\t\tfor (const thought of collection.thoughts) {\n\t\t\tconst identifier = thought.id;\n\t\t\tconst existing = sessionPayloads.get(identifier);\n\t\t\tif (existing !== undefined && !isDeepStrictEqual(existing, thought)) {\n\t\t\t\tthrow new PersistenceCompatibilityError(\n\t\t\t\t\tsourcePath,\n\t\t\t\t\t`conflicting thought id '${identifier}'`\n\t\t\t\t);\n\t\t\t}\n\t\t\tsessionPayloads.set(identifier, thought);\n\t\t}\n\t}\n}\n\nfunction thoughtScope(thought: ThoughtData): PersistenceScope {\n\treturn {\n\t\tsessionId: asSessionId(thought.session_id),\n\t\t...(thought.branch_id === undefined ? {} : { branchId: thought.branch_id }),\n\t};\n}\n\nexport function assertThoughtScope(\n\toperation: Extract<\n\t\tPersistenceWriteOperation,\n\t\t'saveThought' | 'saveThoughtForSession' | 'saveBacktrackForSession'\n\t>,\n\tsessionId: SessionId,\n\tthought: ThoughtData\n): void {\n\tconst actualScope = thoughtScope(thought);\n\tif (actualScope.sessionId !== sessionId) {\n\t\tthrow new PersistenceScopeMismatchError(operation, { sessionId }, [actualScope]);\n\t}\n}\n\nexport function assertBranchScope(\n\toperation: Extract<PersistenceWriteOperation, 'saveBranch' | 'saveBranchForSession'>,\n\tsessionId: SessionId,\n\tbranchId: BranchId,\n\tthoughts: readonly ThoughtData[]\n): void {\n\tconst expectedScope = { sessionId, branchId } satisfies PersistenceScope;\n\tconst mismatches = thoughts\n\t\t.map(thoughtScope)\n\t\t.filter(\n\t\t\t(scope) =>\n\t\t\t\tscope.sessionId !== expectedScope.sessionId ||\n\t\t\t\t(scope.branchId !== undefined && scope.branchId !== expectedScope.branchId)\n\t\t);\n\tif (mismatches.length > 0) {\n\t\tthrow new PersistenceScopeMismatchError(operation, expectedScope, mismatches);\n\t}\n}\n\nexport function assertEdgeScopes(sessionId: SessionId, edges: readonly Edge[]): void {\n\tconst actualScopes = edges\n\t\t.filter((edge) => edge.sessionId !== sessionId)\n\t\t.map((edge) => ({ sessionId: edge.sessionId }));\n\tif (actualScopes.length > 0) {\n\t\tthrow new PersistenceScopeMismatchError('saveEdges', { sessionId }, actualScopes);\n\t}\n}\n\nexport function assertSummaryScopes(sessionId: SessionId, summaries: readonly Summary[]): void {\n\tconst actualScopes = summaries\n\t\t.filter((summary) => summary.sessionId !== sessionId)\n\t\t.map((summary) => ({\n\t\t\tsessionId: summary.sessionId,\n\t\t\t...(summary.branchId === undefined ? {} : { branchId: summary.branchId }),\n\t\t}));\n\tif (actualScopes.length > 0) {\n\t\tthrow new PersistenceScopeMismatchError('saveSummaries', { sessionId }, actualScopes);\n\t}\n}\n\nexport function assertUniqueRecordIds(\n\trecords: readonly { readonly id: string }[],\n\tsourcePath: string,\n\trecordKind: 'edge' | 'summary'\n): void {\n\tconst identifiers = new Set<string>();\n\tfor (const record of records) {\n\t\tif (identifiers.has(record.id)) {\n\t\t\tthrow new PersistenceCompatibilityError(\n\t\t\t\tsourcePath,\n\t\t\t\t`duplicate ${recordKind} id '${record.id}'`\n\t\t\t);\n\t\t}\n\t\tidentifiers.add(record.id);\n\t}\n}\n\nexport function compareCodePoint(left: string, right: string): number {\n\treturn left < right ? -1 : left > right ? 1 : 0;\n}\n\nexport function compareCreatedThenId(\n\tleft: { readonly createdAt: number; readonly id: string },\n\tright: { readonly createdAt: number; readonly id: string }\n): number {\n\treturn left.createdAt - right.createdAt || compareCodePoint(left.id, right.id);\n}\n"],"names":["BRANCH_ID_PATTERN","parsePersistenceBranchId","value","sourcePath","PersistenceCompatibilityError","asBranchId","assertPersistableThoughts","thoughts","identifiers","Set","thought","undefined","assertPersistableThoughtCollections","collections","payloadsBySession","Map","collection","sessionPayloads","identifier","existing","isDeepStrictEqual","thoughtScope","asSessionId","assertThoughtScope","operation","sessionId","actualScope","PersistenceScopeMismatchError","assertBranchScope","branchId","expectedScope","mismatches","scope","assertEdgeScopes","edges","actualScopes","edge","assertSummaryScopes","summaries","summary","assertUniqueRecordIds","records","recordKind","record","compareCodePoint","left","right","compareCreatedThenId"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import type { PersistenceConfig, PersistenceBackend } from '../contracts/PersistenceBackend.js';
2
- import { type BranchId, type SessionId } from '../contracts/ids.js';
2
+ import { type BranchId, type SessionId, type ThoughtId } from '../contracts/ids.js';
3
3
  import type { Summary } from '../core/compression/Summary.js';
4
4
  import type { Edge } from '../core/graph/Edge.js';
5
5
  import type { ThoughtData } from '../core/thought.js';
@@ -14,6 +14,7 @@ export declare class SqlitePersistence implements PersistenceBackend {
14
14
  static createWithDatabase(database: SqliteDatabase, options?: SqliteOptions, sourcePath?: string): SqlitePersistence;
15
15
  static create(options?: SqliteOptions): Promise<SqlitePersistence>;
16
16
  saveThoughtForSession(sessionId: SessionId, thought: ThoughtData): Promise<void>;
17
+ saveBacktrackForSession(sessionId: SessionId, thought: ThoughtData, targetThoughtId: ThoughtId): Promise<void>;
17
18
  private _saveThought;
18
19
  loadHistoryForSession(sessionId: SessionId): Promise<ThoughtData[]>;
19
20
  saveBranchForSession(sessionId: SessionId, branchId: BranchId, thoughts: readonly ThoughtData[]): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"SqlitePersistence.d.ts","sourceRoot":"","sources":["../../src/persistence/SqlitePersistence.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACX,iBAAiB,EACjB,kBAAkB,EAClB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAe,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,KAAK,EAAE,cAAc,EAA6B,MAAM,mBAAmB,CAAC;AAsBnF,KAAK,aAAa,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;AAOlD,qBAAa,iBAAkB,YAAW,kBAAkB;IAE1D,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IAJlC,OAAO;WAOO,kBAAkB,CAC/B,QAAQ,EAAE,cAAc,EACxB,OAAO,GAAE,aAAkB,EAC3B,UAAU,SAAwB,GAChC,iBAAiB;WAUA,MAAM,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAgClE,qBAAqB,CAAC,SAAS,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAM7F,OAAO,CAAC,YAAY;IAwBP,qBAAqB,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAQnE,oBAAoB,CAChC,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,SAAS,WAAW,EAAE,GAC9B,OAAO,CAAC,IAAI,CAAC;IAOhB,OAAO,CAAC,WAAW;IAoBN,sBAAsB,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAW/E,oBAAoB,CAChC,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,QAAQ,GAChB,OAAO,CAAC,WAAW,EAAE,GAAG,SAAS,CAAC;IASxB,sBAAsB,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAUjE,YAAY,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAUpC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAOzB,YAAY,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IASjD,SAAS,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,IAAI,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBtE,SAAS,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAUhD,aAAa,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAyBjF,aAAa,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAUvD,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IAU3B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B,QAAQ,IAAI;QAClB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;QAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;KACxB;IAcD,OAAO,CAAC,0BAA0B;IAoDlC,OAAO,CAAC,eAAe;CAMvB"}
1
+ {"version":3,"file":"SqlitePersistence.d.ts","sourceRoot":"","sources":["../../src/persistence/SqlitePersistence.ts"],"names":[],"mappings":"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"}
@@ -7,6 +7,7 @@ import { runSqliteReadTransaction, runSqliteTransaction } from "./SqliteDriver.j
7
7
  import { decodeEdgeRow, decodeSummaryRow, decodeThoughtRow, numberField, stringField } from "./SqlitePayload.js";
8
8
  import { assertBranchScope, assertEdgeScopes, assertPersistableThoughtCollections, assertPersistableThoughts, assertSummaryScopes, assertThoughtScope, compareCodePoint, parsePersistenceBranchId } from "./PersistenceScope.js";
9
9
  import { initializeOrValidateSqliteV2 } from "./SqliteSchemaV2.js";
10
+ import { stageBacktrackPersistence } from "./BacktrackPersistence.js";
10
11
  class SqlitePersistence {
11
12
  _db;
12
13
  _sourcePath;
@@ -52,6 +53,23 @@ class SqlitePersistence {
52
53
  assertThoughtScope('saveThoughtForSession', validatedSessionId, thought);
53
54
  this._saveThought(validatedSessionId, thought);
54
55
  }
56
+ async saveBacktrackForSession(sessionId, thought, targetThoughtId) {
57
+ const validatedSessionId = asSessionId(sessionId);
58
+ runSqliteTransaction(this._db, ()=>{
59
+ const current = this._loadValidatedThoughtState(validatedSessionId);
60
+ const staged = stageBacktrackPersistence(validatedSessionId, current.history, [
61
+ ...current.branches
62
+ ].map(([branchId, thoughts])=>({
63
+ branchId,
64
+ thoughts
65
+ })), thought, targetThoughtId, this._maxHistorySize);
66
+ this._db.prepare('DELETE FROM thoughts WHERE session_id = ?').run(validatedSessionId);
67
+ const insertThought = this._db.prepare('INSERT INTO thoughts (session_id, data) VALUES (?, ?)');
68
+ for (const retained of staged.history)insertThought.run(validatedSessionId, JSON.stringify(retained));
69
+ const replaceBranch = this._db.prepare('INSERT OR REPLACE INTO branches (session_id, branch_id, data) VALUES (?, ?, ?)');
70
+ for (const branch of staged.branches)replaceBranch.run(validatedSessionId, branch.branchId, JSON.stringify(branch.thoughts));
71
+ });
72
+ }
55
73
  _saveThought(sessionId, thought) {
56
74
  runSqliteTransaction(this._db, ()=>{
57
75
  this._loadValidatedThoughtState(sessionId, [
@@ -1 +1 @@
1
- {"version":3,"file":"persistence/SqlitePersistence.js","sources":["../../src/persistence/SqlitePersistence.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport type {\n\tPersistenceConfig,\n\tPersistenceBackend,\n} from '../contracts/PersistenceBackend.js';\nimport { asSessionId, type BranchId, type SessionId } from '../contracts/ids.js';\nimport type { Summary } from '../core/compression/Summary.js';\nimport type { Edge } from '../core/graph/Edge.js';\nimport type { ThoughtData } from '../core/thought.js';\nimport { PersistenceCompatibilityError, PersistenceCorruptionError } from '../errors.js';\nimport type { SqliteDatabase, SqliteDatabaseConstructor } from './SqliteDriver.js';\nimport { runSqliteReadTransaction, runSqliteTransaction } from './SqliteDriver.js';\nimport {\n\tdecodeEdgeRow,\n\tdecodeSummaryRow,\n\tdecodeThoughtRow,\n\tnumberField,\n\tstringField,\n} from './SqlitePayload.js';\nimport {\n\tassertBranchScope,\n\tassertEdgeScopes,\n\tassertPersistableThoughtCollections,\n\tassertPersistableThoughts,\n\tassertSummaryScopes,\n\tassertThoughtScope,\n\tcompareCodePoint,\n\tparsePersistenceBranchId,\n\ttype PersistedThoughtCollection,\n} from './PersistenceScope.js';\nimport { initializeOrValidateSqliteV2 } from './SqliteSchemaV2.js';\n\ntype SqliteOptions = PersistenceConfig['options'];\n\ntype ValidatedThoughtState = {\n\treadonly history: ThoughtData[];\n\treadonly branches: ReadonlyMap<BranchId, ThoughtData[]>;\n};\n\nexport class SqlitePersistence implements PersistenceBackend {\n\tprivate constructor(\n\t\tprivate readonly _db: SqliteDatabase,\n\t\tprivate readonly _sourcePath: string,\n\t\tprivate readonly _maxHistorySize: number,\n\t\tprivate readonly _persistBranches: boolean\n\t) {}\n\n\tpublic static createWithDatabase(\n\t\tdatabase: SqliteDatabase,\n\t\toptions: SqliteOptions = {},\n\t\tsourcePath = '<structural-sqlite>'\n\t): SqlitePersistence {\n\t\tinitializeOrValidateSqliteV2(database, sourcePath);\n\t\treturn new SqlitePersistence(\n\t\t\tdatabase,\n\t\t\tsourcePath,\n\t\t\toptions?.maxHistorySize ?? 10000,\n\t\t\toptions?.persistBranches ?? true\n\t\t);\n\t}\n\n\tpublic static async create(options?: SqliteOptions): Promise<SqlitePersistence> {\n\t\tconst defaultDataDir = existsSync('.claude/data')\n\t\t\t? '.claude/data'\n\t\t\t: join(homedir(), '.claude/data');\n\t\tconst dbPath = options?.dbPath ?? join(defaultDataDir, 'history.db');\n\t\tlet Database: SqliteDatabaseConstructor;\n\t\ttry {\n\t\t\tconst module = await import('better-sqlite3');\n\t\t\tDatabase = module.default;\n\t\t} catch (error) {\n\t\t\tthrow new PersistenceCompatibilityError(\n\t\t\t\tdbPath,\n\t\t\t\t\"SQLite persistence requires the optional 'better-sqlite3' package\",\n\t\t\t\terror\n\t\t\t);\n\t\t}\n\t\tconst database = new Database(dbPath);\n\t\ttry {\n\t\t\tconst persistence = SqlitePersistence.createWithDatabase(database, options, dbPath);\n\t\t\tif (options?.enableWAL !== false) database.pragma('journal_mode = WAL');\n\t\t\tdatabase.pragma('synchronous = NORMAL');\n\t\t\tdatabase.pragma('foreign_keys = ON');\n\t\t\tdatabase.pragma('busy_timeout = 5000');\n\t\t\tdatabase.pragma('cache_size = -64000');\n\t\t\tdatabase.pragma('temp_store = MEMORY');\n\t\t\treturn persistence;\n\t\t} catch (error) {\n\t\t\tdatabase.close();\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tpublic async saveThoughtForSession(sessionId: SessionId, thought: ThoughtData): Promise<void> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\tassertThoughtScope('saveThoughtForSession', validatedSessionId, thought);\n\t\tthis._saveThought(validatedSessionId, thought);\n\t}\n\n\tprivate _saveThought(sessionId: SessionId, thought: ThoughtData): void {\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tthis._loadValidatedThoughtState(sessionId, [thought]);\n\t\t\tthis._db\n\t\t\t\t.prepare('INSERT INTO thoughts (session_id, data) VALUES (?, ?)')\n\t\t\t\t.run(sessionId, JSON.stringify(thought));\n\t\t\tif (this._maxHistorySize > 0) {\n\t\t\t\tconst countRow = this._db\n\t\t\t\t\t.prepare('SELECT COUNT(*) AS count FROM thoughts WHERE session_id = ?')\n\t\t\t\t\t.get(sessionId);\n\t\t\t\tconst excess =\n\t\t\t\t\tnumberField(countRow, 'count', `${this._sourcePath}:thought-count`) -\n\t\t\t\t\tthis._maxHistorySize;\n\t\t\t\tif (excess > 0) {\n\t\t\t\t\tthis._db\n\t\t\t\t\t\t.prepare(\n\t\t\t\t\t\t\t'DELETE FROM thoughts WHERE id IN (SELECT id FROM thoughts WHERE session_id = ? ORDER BY id ASC LIMIT ?)'\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.run(sessionId, excess);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic async loadHistoryForSession(sessionId: SessionId): Promise<ThoughtData[]> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\treturn runSqliteReadTransaction(\n\t\t\tthis._db,\n\t\t\t() => this._loadValidatedThoughtState(validatedSessionId).history\n\t\t);\n\t}\n\n\tpublic async saveBranchForSession(\n\t\tsessionId: SessionId,\n\t\tbranchId: BranchId,\n\t\tthoughts: readonly ThoughtData[]\n\t): Promise<void> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\tconst validatedBranchId = parsePersistenceBranchId(branchId, `${this._sourcePath}:branches`);\n\t\tassertBranchScope('saveBranchForSession', validatedSessionId, validatedBranchId, thoughts);\n\t\tthis._saveBranch(validatedSessionId, validatedBranchId, thoughts);\n\t}\n\n\tprivate _saveBranch(\n\t\tsessionId: SessionId,\n\t\tbranchId: BranchId,\n\t\tthoughts: readonly ThoughtData[]\n\t): void {\n\t\tassertPersistableThoughts(thoughts, `${this._sourcePath}:branch:${branchId}`);\n\t\tif (!this._persistBranches) {\n\t\t\trunSqliteReadTransaction(this._db, () =>\n\t\t\t\tthis._loadValidatedThoughtState(sessionId, thoughts, branchId)\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tthis._loadValidatedThoughtState(sessionId, thoughts, branchId);\n\t\t\tthis._db\n\t\t\t\t.prepare('INSERT OR REPLACE INTO branches (session_id, branch_id, data) VALUES (?, ?, ?)')\n\t\t\t\t.run(sessionId, branchId, JSON.stringify(thoughts));\n\t\t});\n\t}\n\n\tpublic async deleteBranchForSession(sessionId: SessionId, branchId: BranchId): Promise<void> {\n\t\tif (!this._persistBranches) return;\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\tconst validatedBranchId = parsePersistenceBranchId(branchId, `${this._sourcePath}:branches`);\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tthis._db\n\t\t\t\t.prepare('DELETE FROM branches WHERE session_id = ? AND branch_id = ?')\n\t\t\t\t.run(validatedSessionId, validatedBranchId);\n\t\t});\n\t}\n\n\tpublic async loadBranchForSession(\n\t\tsessionId: SessionId,\n\t\tbranchId: BranchId\n\t): Promise<ThoughtData[] | undefined> {\n\t\tif (!this._persistBranches) return undefined;\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\tconst validatedBranchId = parsePersistenceBranchId(branchId, `${this._sourcePath}:branches`);\n\t\treturn runSqliteReadTransaction(this._db, () =>\n\t\t\tthis._loadValidatedThoughtState(validatedSessionId).branches.get(validatedBranchId)\n\t\t);\n\t}\n\n\tpublic async listBranchesForSession(sessionId: SessionId): Promise<BranchId[]> {\n\t\tif (!this._persistBranches) return [];\n\t\treturn this._db\n\t\t\t.prepare('SELECT branch_id FROM branches WHERE session_id = ? ORDER BY branch_id ASC')\n\t\t\t.all(asSessionId(sessionId))\n\t\t\t.map((row) =>\n\t\t\t\tparsePersistenceBranchId(stringField(row, 'branch_id', this._sourcePath), this._sourcePath)\n\t\t\t);\n\t}\n\n\tpublic async listSessions(): Promise<SessionId[]> {\n\t\treturn this._db\n\t\t\t.prepare(\n\t\t\t\t'SELECT session_id FROM thoughts UNION SELECT session_id FROM branches UNION SELECT session_id FROM edges UNION SELECT session_id FROM summaries'\n\t\t\t)\n\t\t\t.all()\n\t\t\t.map((row) => asSessionId(stringField(row, 'session_id', this._sourcePath)))\n\t\t\t.sort(compareCodePoint);\n\t}\n\n\tpublic async clearAll(): Promise<void> {\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tfor (const table of ['thoughts', 'branches', 'edges', 'summaries'])\n\t\t\t\tthis._db.exec(`DELETE FROM ${table}`);\n\t\t});\n\t}\n\n\tpublic async clearSession(sessionId: SessionId): Promise<void> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tfor (const table of ['thoughts', 'branches', 'edges', 'summaries']) {\n\t\t\t\tthis._db.prepare(`DELETE FROM ${table} WHERE session_id = ?`).run(validatedSessionId);\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic async saveEdges(sessionId: SessionId, edges: readonly Edge[]): Promise<void> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\tassertEdgeScopes(validatedSessionId, edges);\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tthis._db.prepare('DELETE FROM edges WHERE session_id = ?').run(validatedSessionId);\n\t\t\tconst insert = this._db.prepare(\n\t\t\t\t'INSERT INTO edges (id, session_id, from_id, to_id, kind, created_at, metadata) VALUES (?, ?, ?, ?, ?, ?, ?)'\n\t\t\t);\n\t\t\tfor (const edge of edges)\n\t\t\t\tinsert.run(\n\t\t\t\t\tedge.id,\n\t\t\t\t\tedge.sessionId,\n\t\t\t\t\tedge.from,\n\t\t\t\t\tedge.to,\n\t\t\t\t\tedge.kind,\n\t\t\t\t\tedge.createdAt,\n\t\t\t\t\tedge.metadata === undefined ? null : JSON.stringify(edge.metadata)\n\t\t\t\t);\n\t\t});\n\t}\n\n\tpublic async loadEdges(sessionId: SessionId): Promise<Edge[]> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\treturn this._db\n\t\t\t.prepare(\n\t\t\t\t'SELECT id, session_id, from_id, to_id, kind, created_at, metadata FROM edges WHERE session_id = ? ORDER BY created_at ASC, id ASC'\n\t\t\t)\n\t\t\t.all(validatedSessionId)\n\t\t\t.map((row, index) => decodeEdgeRow(row, `${this._sourcePath}:edges:${index}`));\n\t}\n\n\tpublic async saveSummaries(sessionId: SessionId, summaries: readonly Summary[]): Promise<void> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\tassertSummaryScopes(validatedSessionId, summaries);\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tthis._db.prepare('DELETE FROM summaries WHERE session_id = ?').run(validatedSessionId);\n\t\t\tconst insert = this._db.prepare(\n\t\t\t\t'INSERT INTO summaries (id, session_id, branch_id, root_thought_id, covered_ids, covered_range_start, covered_range_end, topics, aggregate_confidence, created_at, meta) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'\n\t\t\t);\n\t\t\tfor (const summary of summaries)\n\t\t\t\tinsert.run(\n\t\t\t\t\tsummary.id,\n\t\t\t\t\tsummary.sessionId,\n\t\t\t\t\tsummary.branchId ?? null,\n\t\t\t\t\tsummary.rootThoughtId,\n\t\t\t\t\tJSON.stringify(summary.coveredIds),\n\t\t\t\t\tsummary.coveredRange[0],\n\t\t\t\t\tsummary.coveredRange[1],\n\t\t\t\t\tJSON.stringify(summary.topics),\n\t\t\t\t\tsummary.aggregateConfidence,\n\t\t\t\t\tsummary.createdAt,\n\t\t\t\t\tsummary.meta === undefined ? null : JSON.stringify(summary.meta)\n\t\t\t\t);\n\t\t});\n\t}\n\n\tpublic async loadSummaries(sessionId: SessionId): Promise<Summary[]> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\treturn this._db\n\t\t\t.prepare(\n\t\t\t\t'SELECT id, session_id, branch_id, root_thought_id, covered_ids, covered_range_start, covered_range_end, topics, aggregate_confidence, created_at, meta FROM summaries WHERE session_id = ? ORDER BY created_at ASC, id ASC'\n\t\t\t)\n\t\t\t.all(validatedSessionId)\n\t\t\t.map((row, index) => decodeSummaryRow(row, `${this._sourcePath}:summaries:${index}`));\n\t}\n\n\tpublic async healthy(): Promise<boolean> {\n\t\ttry {\n\t\t\tthis._db.prepare('SELECT 1').get();\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error) return false;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tpublic async close(): Promise<void> {\n\t\tthis._db.close();\n\t}\n\n\tpublic getStats(): {\n\t\treadonly thoughtCount: number;\n\t\treadonly branchCount: number;\n\t\treadonly dbSize: number;\n\t} {\n\t\tconst thoughtCount = numberField(\n\t\t\tthis._db.prepare('SELECT COUNT(*) AS count FROM thoughts').get(),\n\t\t\t'count',\n\t\t\tthis._sourcePath\n\t\t);\n\t\tconst branchCount = numberField(\n\t\t\tthis._db.prepare('SELECT COUNT(*) AS count FROM branches').get(),\n\t\t\t'count',\n\t\t\tthis._sourcePath\n\t\t);\n\t\treturn { thoughtCount, branchCount: this._persistBranches ? branchCount : 0, dbSize: 0 };\n\t}\n\n\tprivate _loadValidatedThoughtState(\n\t\tsessionId: SessionId,\n\t\tcandidates: readonly ThoughtData[] = [],\n\t\treplacingBranch?: BranchId\n\t): ValidatedThoughtState {\n\t\tconst history = this.loadHistoryRows(sessionId);\n\t\tfor (const thought of history) {\n\t\t\tassertThoughtScope('saveThoughtForSession', sessionId, thought);\n\t\t}\n\t\tconst collections: PersistedThoughtCollection[] = [\n\t\t\t{\n\t\t\t\tsessionId,\n\t\t\t\tthoughts: replacingBranch === undefined ? [...history, ...candidates] : history,\n\t\t\t},\n\t\t];\n\t\tconst branches = new Map<BranchId, ThoughtData[]>();\n\t\tfor (const row of this._db\n\t\t\t.prepare('SELECT branch_id, data FROM branches WHERE session_id = ?')\n\t\t\t.all(sessionId)) {\n\t\t\tconst branchId = parsePersistenceBranchId(\n\t\t\t\tstringField(row, 'branch_id', this._sourcePath),\n\t\t\t\tthis._sourcePath\n\t\t\t);\n\t\t\tconst data = stringField(row, 'data', this._sourcePath);\n\t\t\tlet values: unknown;\n\t\t\ttry {\n\t\t\t\tvalues = JSON.parse(data);\n\t\t\t} catch (error) {\n\t\t\t\tthrow new PersistenceCorruptionError(this._sourcePath, error);\n\t\t\t}\n\t\t\tif (!Array.isArray(values))\n\t\t\t\tthrow new PersistenceCorruptionError(\n\t\t\t\t\tthis._sourcePath,\n\t\t\t\t\tnew TypeError('branch data is not an array')\n\t\t\t\t);\n\t\t\tconst thoughts = values.map((value, index) =>\n\t\t\t\tdecodeThoughtRow(\n\t\t\t\t\t{ data: JSON.stringify(value) },\n\t\t\t\t\t`${this._sourcePath}:branch:${sessionId}:${branchId}:${index}`\n\t\t\t\t)\n\t\t\t);\n\t\t\tassertBranchScope('saveBranchForSession', sessionId, branchId, thoughts);\n\t\t\tbranches.set(branchId, thoughts);\n\t\t\tif (branchId !== replacingBranch) collections.push({ sessionId, thoughts });\n\t\t}\n\t\tif (replacingBranch !== undefined) {\n\t\t\tcollections.push({ sessionId, thoughts: candidates });\n\t\t}\n\t\tassertPersistableThoughtCollections(collections, this._sourcePath);\n\t\treturn { history, branches };\n\t}\n\n\tprivate loadHistoryRows(sessionId: SessionId): ThoughtData[] {\n\t\treturn this._db\n\t\t\t.prepare('SELECT data FROM thoughts WHERE session_id = ? ORDER BY id ASC')\n\t\t\t.all(sessionId)\n\t\t\t.map((row) => decodeThoughtRow(row, this._sourcePath));\n\t}\n}\n"],"names":["SqlitePersistence","_db","_sourcePath","_maxHistorySize","_persistBranches","database","options","sourcePath","initializeOrValidateSqliteV2","defaultDataDir","existsSync","join","homedir","dbPath","Database","module","error","PersistenceCompatibilityError","persistence","sessionId","thought","validatedSessionId","asSessionId","assertThoughtScope","runSqliteTransaction","JSON","countRow","excess","numberField","runSqliteReadTransaction","branchId","thoughts","validatedBranchId","parsePersistenceBranchId","assertBranchScope","assertPersistableThoughts","row","stringField","compareCodePoint","table","edges","assertEdgeScopes","insert","edge","undefined","index","decodeEdgeRow","summaries","assertSummaryScopes","summary","decodeSummaryRow","Error","thoughtCount","branchCount","candidates","replacingBranch","history","collections","branches","Map","data","values","PersistenceCorruptionError","Array","TypeError","value","decodeThoughtRow","assertPersistableThoughtCollections"],"mappings":"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"}
1
+ {"version":3,"file":"persistence/SqlitePersistence.js","sources":["../../src/persistence/SqlitePersistence.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport type { PersistenceConfig, PersistenceBackend } from '../contracts/PersistenceBackend.js';\nimport { asSessionId, type BranchId, type SessionId, type ThoughtId } from '../contracts/ids.js';\nimport type { Summary } from '../core/compression/Summary.js';\nimport type { Edge } from '../core/graph/Edge.js';\nimport type { ThoughtData } from '../core/thought.js';\nimport { PersistenceCompatibilityError, PersistenceCorruptionError } from '../errors.js';\nimport type { SqliteDatabase, SqliteDatabaseConstructor } from './SqliteDriver.js';\nimport { runSqliteReadTransaction, runSqliteTransaction } from './SqliteDriver.js';\nimport {\n\tdecodeEdgeRow,\n\tdecodeSummaryRow,\n\tdecodeThoughtRow,\n\tnumberField,\n\tstringField,\n} from './SqlitePayload.js';\nimport {\n\tassertBranchScope,\n\tassertEdgeScopes,\n\tassertPersistableThoughtCollections,\n\tassertPersistableThoughts,\n\tassertSummaryScopes,\n\tassertThoughtScope,\n\tcompareCodePoint,\n\tparsePersistenceBranchId,\n\ttype PersistedThoughtCollection,\n} from './PersistenceScope.js';\nimport { initializeOrValidateSqliteV2 } from './SqliteSchemaV2.js';\nimport { stageBacktrackPersistence } from './BacktrackPersistence.js';\n\ntype SqliteOptions = PersistenceConfig['options'];\n\ntype ValidatedThoughtState = {\n\treadonly history: ThoughtData[];\n\treadonly branches: ReadonlyMap<BranchId, ThoughtData[]>;\n};\n\nexport class SqlitePersistence implements PersistenceBackend {\n\tprivate constructor(\n\t\tprivate readonly _db: SqliteDatabase,\n\t\tprivate readonly _sourcePath: string,\n\t\tprivate readonly _maxHistorySize: number,\n\t\tprivate readonly _persistBranches: boolean\n\t) {}\n\n\tpublic static createWithDatabase(\n\t\tdatabase: SqliteDatabase,\n\t\toptions: SqliteOptions = {},\n\t\tsourcePath = '<structural-sqlite>'\n\t): SqlitePersistence {\n\t\tinitializeOrValidateSqliteV2(database, sourcePath);\n\t\treturn new SqlitePersistence(\n\t\t\tdatabase,\n\t\t\tsourcePath,\n\t\t\toptions?.maxHistorySize ?? 10000,\n\t\t\toptions?.persistBranches ?? true\n\t\t);\n\t}\n\n\tpublic static async create(options?: SqliteOptions): Promise<SqlitePersistence> {\n\t\tconst defaultDataDir = existsSync('.claude/data')\n\t\t\t? '.claude/data'\n\t\t\t: join(homedir(), '.claude/data');\n\t\tconst dbPath = options?.dbPath ?? join(defaultDataDir, 'history.db');\n\t\tlet Database: SqliteDatabaseConstructor;\n\t\ttry {\n\t\t\tconst module = await import('better-sqlite3');\n\t\t\tDatabase = module.default;\n\t\t} catch (error) {\n\t\t\tthrow new PersistenceCompatibilityError(\n\t\t\t\tdbPath,\n\t\t\t\t\"SQLite persistence requires the optional 'better-sqlite3' package\",\n\t\t\t\terror\n\t\t\t);\n\t\t}\n\t\tconst database = new Database(dbPath);\n\t\ttry {\n\t\t\tconst persistence = SqlitePersistence.createWithDatabase(database, options, dbPath);\n\t\t\tif (options?.enableWAL !== false) database.pragma('journal_mode = WAL');\n\t\t\tdatabase.pragma('synchronous = NORMAL');\n\t\t\tdatabase.pragma('foreign_keys = ON');\n\t\t\tdatabase.pragma('busy_timeout = 5000');\n\t\t\tdatabase.pragma('cache_size = -64000');\n\t\t\tdatabase.pragma('temp_store = MEMORY');\n\t\t\treturn persistence;\n\t\t} catch (error) {\n\t\t\tdatabase.close();\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tpublic async saveThoughtForSession(sessionId: SessionId, thought: ThoughtData): Promise<void> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\tassertThoughtScope('saveThoughtForSession', validatedSessionId, thought);\n\t\tthis._saveThought(validatedSessionId, thought);\n\t}\n\n\tpublic async saveBacktrackForSession(\n\t\tsessionId: SessionId,\n\t\tthought: ThoughtData,\n\t\ttargetThoughtId: ThoughtId\n\t): Promise<void> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tconst current = this._loadValidatedThoughtState(validatedSessionId);\n\t\t\tconst staged = stageBacktrackPersistence(\n\t\t\t\tvalidatedSessionId,\n\t\t\t\tcurrent.history,\n\t\t\t\t[...current.branches].map(([branchId, thoughts]) => ({ branchId, thoughts })),\n\t\t\t\tthought,\n\t\t\t\ttargetThoughtId,\n\t\t\t\tthis._maxHistorySize\n\t\t\t);\n\t\t\tthis._db.prepare('DELETE FROM thoughts WHERE session_id = ?').run(validatedSessionId);\n\t\t\tconst insertThought = this._db.prepare(\n\t\t\t\t'INSERT INTO thoughts (session_id, data) VALUES (?, ?)'\n\t\t\t);\n\t\t\tfor (const retained of staged.history) {\n\t\t\t\tinsertThought.run(validatedSessionId, JSON.stringify(retained));\n\t\t\t}\n\t\t\tconst replaceBranch = this._db.prepare(\n\t\t\t\t'INSERT OR REPLACE INTO branches (session_id, branch_id, data) VALUES (?, ?, ?)'\n\t\t\t);\n\t\t\tfor (const branch of staged.branches) {\n\t\t\t\treplaceBranch.run(validatedSessionId, branch.branchId, JSON.stringify(branch.thoughts));\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate _saveThought(sessionId: SessionId, thought: ThoughtData): void {\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tthis._loadValidatedThoughtState(sessionId, [thought]);\n\t\t\tthis._db\n\t\t\t\t.prepare('INSERT INTO thoughts (session_id, data) VALUES (?, ?)')\n\t\t\t\t.run(sessionId, JSON.stringify(thought));\n\t\t\tif (this._maxHistorySize > 0) {\n\t\t\t\tconst countRow = this._db\n\t\t\t\t\t.prepare('SELECT COUNT(*) AS count FROM thoughts WHERE session_id = ?')\n\t\t\t\t\t.get(sessionId);\n\t\t\t\tconst excess =\n\t\t\t\t\tnumberField(countRow, 'count', `${this._sourcePath}:thought-count`) -\n\t\t\t\t\tthis._maxHistorySize;\n\t\t\t\tif (excess > 0) {\n\t\t\t\t\tthis._db\n\t\t\t\t\t\t.prepare(\n\t\t\t\t\t\t\t'DELETE FROM thoughts WHERE id IN (SELECT id FROM thoughts WHERE session_id = ? ORDER BY id ASC LIMIT ?)'\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.run(sessionId, excess);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic async loadHistoryForSession(sessionId: SessionId): Promise<ThoughtData[]> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\treturn runSqliteReadTransaction(\n\t\t\tthis._db,\n\t\t\t() => this._loadValidatedThoughtState(validatedSessionId).history\n\t\t);\n\t}\n\n\tpublic async saveBranchForSession(\n\t\tsessionId: SessionId,\n\t\tbranchId: BranchId,\n\t\tthoughts: readonly ThoughtData[]\n\t): Promise<void> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\tconst validatedBranchId = parsePersistenceBranchId(branchId, `${this._sourcePath}:branches`);\n\t\tassertBranchScope('saveBranchForSession', validatedSessionId, validatedBranchId, thoughts);\n\t\tthis._saveBranch(validatedSessionId, validatedBranchId, thoughts);\n\t}\n\n\tprivate _saveBranch(\n\t\tsessionId: SessionId,\n\t\tbranchId: BranchId,\n\t\tthoughts: readonly ThoughtData[]\n\t): void {\n\t\tassertPersistableThoughts(thoughts, `${this._sourcePath}:branch:${branchId}`);\n\t\tif (!this._persistBranches) {\n\t\t\trunSqliteReadTransaction(this._db, () =>\n\t\t\t\tthis._loadValidatedThoughtState(sessionId, thoughts, branchId)\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tthis._loadValidatedThoughtState(sessionId, thoughts, branchId);\n\t\t\tthis._db\n\t\t\t\t.prepare('INSERT OR REPLACE INTO branches (session_id, branch_id, data) VALUES (?, ?, ?)')\n\t\t\t\t.run(sessionId, branchId, JSON.stringify(thoughts));\n\t\t});\n\t}\n\n\tpublic async deleteBranchForSession(sessionId: SessionId, branchId: BranchId): Promise<void> {\n\t\tif (!this._persistBranches) return;\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\tconst validatedBranchId = parsePersistenceBranchId(branchId, `${this._sourcePath}:branches`);\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tthis._db\n\t\t\t\t.prepare('DELETE FROM branches WHERE session_id = ? AND branch_id = ?')\n\t\t\t\t.run(validatedSessionId, validatedBranchId);\n\t\t});\n\t}\n\n\tpublic async loadBranchForSession(\n\t\tsessionId: SessionId,\n\t\tbranchId: BranchId\n\t): Promise<ThoughtData[] | undefined> {\n\t\tif (!this._persistBranches) return undefined;\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\tconst validatedBranchId = parsePersistenceBranchId(branchId, `${this._sourcePath}:branches`);\n\t\treturn runSqliteReadTransaction(this._db, () =>\n\t\t\tthis._loadValidatedThoughtState(validatedSessionId).branches.get(validatedBranchId)\n\t\t);\n\t}\n\n\tpublic async listBranchesForSession(sessionId: SessionId): Promise<BranchId[]> {\n\t\tif (!this._persistBranches) return [];\n\t\treturn this._db\n\t\t\t.prepare('SELECT branch_id FROM branches WHERE session_id = ? ORDER BY branch_id ASC')\n\t\t\t.all(asSessionId(sessionId))\n\t\t\t.map((row) =>\n\t\t\t\tparsePersistenceBranchId(stringField(row, 'branch_id', this._sourcePath), this._sourcePath)\n\t\t\t);\n\t}\n\n\tpublic async listSessions(): Promise<SessionId[]> {\n\t\treturn this._db\n\t\t\t.prepare(\n\t\t\t\t'SELECT session_id FROM thoughts UNION SELECT session_id FROM branches UNION SELECT session_id FROM edges UNION SELECT session_id FROM summaries'\n\t\t\t)\n\t\t\t.all()\n\t\t\t.map((row) => asSessionId(stringField(row, 'session_id', this._sourcePath)))\n\t\t\t.sort(compareCodePoint);\n\t}\n\n\tpublic async clearAll(): Promise<void> {\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tfor (const table of ['thoughts', 'branches', 'edges', 'summaries'])\n\t\t\t\tthis._db.exec(`DELETE FROM ${table}`);\n\t\t});\n\t}\n\n\tpublic async clearSession(sessionId: SessionId): Promise<void> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tfor (const table of ['thoughts', 'branches', 'edges', 'summaries']) {\n\t\t\t\tthis._db.prepare(`DELETE FROM ${table} WHERE session_id = ?`).run(validatedSessionId);\n\t\t\t}\n\t\t});\n\t}\n\n\tpublic async saveEdges(sessionId: SessionId, edges: readonly Edge[]): Promise<void> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\tassertEdgeScopes(validatedSessionId, edges);\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tthis._db.prepare('DELETE FROM edges WHERE session_id = ?').run(validatedSessionId);\n\t\t\tconst insert = this._db.prepare(\n\t\t\t\t'INSERT INTO edges (id, session_id, from_id, to_id, kind, created_at, metadata) VALUES (?, ?, ?, ?, ?, ?, ?)'\n\t\t\t);\n\t\t\tfor (const edge of edges)\n\t\t\t\tinsert.run(\n\t\t\t\t\tedge.id,\n\t\t\t\t\tedge.sessionId,\n\t\t\t\t\tedge.from,\n\t\t\t\t\tedge.to,\n\t\t\t\t\tedge.kind,\n\t\t\t\t\tedge.createdAt,\n\t\t\t\t\tedge.metadata === undefined ? null : JSON.stringify(edge.metadata)\n\t\t\t\t);\n\t\t});\n\t}\n\n\tpublic async loadEdges(sessionId: SessionId): Promise<Edge[]> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\treturn this._db\n\t\t\t.prepare(\n\t\t\t\t'SELECT id, session_id, from_id, to_id, kind, created_at, metadata FROM edges WHERE session_id = ? ORDER BY created_at ASC, id ASC'\n\t\t\t)\n\t\t\t.all(validatedSessionId)\n\t\t\t.map((row, index) => decodeEdgeRow(row, `${this._sourcePath}:edges:${index}`));\n\t}\n\n\tpublic async saveSummaries(sessionId: SessionId, summaries: readonly Summary[]): Promise<void> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\tassertSummaryScopes(validatedSessionId, summaries);\n\t\trunSqliteTransaction(this._db, () => {\n\t\t\tthis._db.prepare('DELETE FROM summaries WHERE session_id = ?').run(validatedSessionId);\n\t\t\tconst insert = this._db.prepare(\n\t\t\t\t'INSERT INTO summaries (id, session_id, branch_id, root_thought_id, covered_ids, covered_range_start, covered_range_end, topics, aggregate_confidence, created_at, meta) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'\n\t\t\t);\n\t\t\tfor (const summary of summaries)\n\t\t\t\tinsert.run(\n\t\t\t\t\tsummary.id,\n\t\t\t\t\tsummary.sessionId,\n\t\t\t\t\tsummary.branchId ?? null,\n\t\t\t\t\tsummary.rootThoughtId,\n\t\t\t\t\tJSON.stringify(summary.coveredIds),\n\t\t\t\t\tsummary.coveredRange[0],\n\t\t\t\t\tsummary.coveredRange[1],\n\t\t\t\t\tJSON.stringify(summary.topics),\n\t\t\t\t\tsummary.aggregateConfidence,\n\t\t\t\t\tsummary.createdAt,\n\t\t\t\t\tsummary.meta === undefined ? null : JSON.stringify(summary.meta)\n\t\t\t\t);\n\t\t});\n\t}\n\n\tpublic async loadSummaries(sessionId: SessionId): Promise<Summary[]> {\n\t\tconst validatedSessionId = asSessionId(sessionId);\n\t\treturn this._db\n\t\t\t.prepare(\n\t\t\t\t'SELECT id, session_id, branch_id, root_thought_id, covered_ids, covered_range_start, covered_range_end, topics, aggregate_confidence, created_at, meta FROM summaries WHERE session_id = ? ORDER BY created_at ASC, id ASC'\n\t\t\t)\n\t\t\t.all(validatedSessionId)\n\t\t\t.map((row, index) => decodeSummaryRow(row, `${this._sourcePath}:summaries:${index}`));\n\t}\n\n\tpublic async healthy(): Promise<boolean> {\n\t\ttry {\n\t\t\tthis._db.prepare('SELECT 1').get();\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tif (error instanceof Error) return false;\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tpublic async close(): Promise<void> {\n\t\tthis._db.close();\n\t}\n\n\tpublic getStats(): {\n\t\treadonly thoughtCount: number;\n\t\treadonly branchCount: number;\n\t\treadonly dbSize: number;\n\t} {\n\t\tconst thoughtCount = numberField(\n\t\t\tthis._db.prepare('SELECT COUNT(*) AS count FROM thoughts').get(),\n\t\t\t'count',\n\t\t\tthis._sourcePath\n\t\t);\n\t\tconst branchCount = numberField(\n\t\t\tthis._db.prepare('SELECT COUNT(*) AS count FROM branches').get(),\n\t\t\t'count',\n\t\t\tthis._sourcePath\n\t\t);\n\t\treturn { thoughtCount, branchCount: this._persistBranches ? branchCount : 0, dbSize: 0 };\n\t}\n\n\tprivate _loadValidatedThoughtState(\n\t\tsessionId: SessionId,\n\t\tcandidates: readonly ThoughtData[] = [],\n\t\treplacingBranch?: BranchId\n\t): ValidatedThoughtState {\n\t\tconst history = this.loadHistoryRows(sessionId);\n\t\tfor (const thought of history) {\n\t\t\tassertThoughtScope('saveThoughtForSession', sessionId, thought);\n\t\t}\n\t\tconst collections: PersistedThoughtCollection[] = [\n\t\t\t{\n\t\t\t\tsessionId,\n\t\t\t\tthoughts: replacingBranch === undefined ? [...history, ...candidates] : history,\n\t\t\t},\n\t\t];\n\t\tconst branches = new Map<BranchId, ThoughtData[]>();\n\t\tfor (const row of this._db\n\t\t\t.prepare('SELECT branch_id, data FROM branches WHERE session_id = ?')\n\t\t\t.all(sessionId)) {\n\t\t\tconst branchId = parsePersistenceBranchId(\n\t\t\t\tstringField(row, 'branch_id', this._sourcePath),\n\t\t\t\tthis._sourcePath\n\t\t\t);\n\t\t\tconst data = stringField(row, 'data', this._sourcePath);\n\t\t\tlet values: unknown;\n\t\t\ttry {\n\t\t\t\tvalues = JSON.parse(data);\n\t\t\t} catch (error) {\n\t\t\t\tthrow new PersistenceCorruptionError(this._sourcePath, error);\n\t\t\t}\n\t\t\tif (!Array.isArray(values))\n\t\t\t\tthrow new PersistenceCorruptionError(\n\t\t\t\t\tthis._sourcePath,\n\t\t\t\t\tnew TypeError('branch data is not an array')\n\t\t\t\t);\n\t\t\tconst thoughts = values.map((value, index) =>\n\t\t\t\tdecodeThoughtRow(\n\t\t\t\t\t{ data: JSON.stringify(value) },\n\t\t\t\t\t`${this._sourcePath}:branch:${sessionId}:${branchId}:${index}`\n\t\t\t\t)\n\t\t\t);\n\t\t\tassertBranchScope('saveBranchForSession', sessionId, branchId, thoughts);\n\t\t\tbranches.set(branchId, thoughts);\n\t\t\tif (branchId !== replacingBranch) collections.push({ sessionId, thoughts });\n\t\t}\n\t\tif (replacingBranch !== undefined) {\n\t\t\tcollections.push({ sessionId, thoughts: candidates });\n\t\t}\n\t\tassertPersistableThoughtCollections(collections, this._sourcePath);\n\t\treturn { history, branches };\n\t}\n\n\tprivate loadHistoryRows(sessionId: SessionId): ThoughtData[] {\n\t\treturn this._db\n\t\t\t.prepare('SELECT data FROM thoughts WHERE session_id = ? ORDER BY id ASC')\n\t\t\t.all(sessionId)\n\t\t\t.map((row) => decodeThoughtRow(row, this._sourcePath));\n\t}\n}\n"],"names":["SqlitePersistence","_db","_sourcePath","_maxHistorySize","_persistBranches","database","options","sourcePath","initializeOrValidateSqliteV2","defaultDataDir","existsSync","join","homedir","dbPath","Database","module","error","PersistenceCompatibilityError","persistence","sessionId","thought","validatedSessionId","asSessionId","assertThoughtScope","targetThoughtId","runSqliteTransaction","current","staged","stageBacktrackPersistence","branchId","thoughts","insertThought","retained","JSON","replaceBranch","branch","countRow","excess","numberField","runSqliteReadTransaction","validatedBranchId","parsePersistenceBranchId","assertBranchScope","assertPersistableThoughts","row","stringField","compareCodePoint","table","edges","assertEdgeScopes","insert","edge","undefined","index","decodeEdgeRow","summaries","assertSummaryScopes","summary","decodeSummaryRow","Error","thoughtCount","branchCount","candidates","replacingBranch","history","collections","branches","Map","data","values","PersistenceCorruptionError","Array","TypeError","value","decodeThoughtRow","assertPersistableThoughtCollections"],"mappings":"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"}
@@ -56,7 +56,10 @@ export declare abstract class BaseRegistry<T extends {
56
56
  protected _discovered: boolean;
57
57
  /** Promise for in-progress discovery (null if not in progress). */
58
58
  protected _discoveryPromise: Promise<number> | null;
59
- private _refreshRequested;
59
+ private _activeDiscoveryEpoch;
60
+ private _queuedRefreshEpoch;
61
+ private _replacementEpoch;
62
+ private _discoveredCount;
60
63
  /** File extensions to match during discovery. */
61
64
  protected abstract readonly _fileExtensions: string[];
62
65
  /**
@@ -195,9 +198,14 @@ export declare abstract class BaseRegistry<T extends {
195
198
  *
196
199
  * @returns A Promise resolving to the number of items discovered
197
200
  */
198
- protected _performDiscovery(): Promise<number>;
201
+ protected _performDiscovery(epoch?: number): Promise<number>;
202
+ private _scanDirectory;
203
+ private _scanFile;
199
204
  private _retainLastKnownGood;
200
205
  private _retainDirectory;
206
+ private _publishDiscovery;
207
+ private _rebuildVisibleItems;
208
+ private _beginReplacement;
201
209
  /**
202
210
  * Parses YAML frontmatter from file content.
203
211
  *
@@ -1 +1 @@
1
- {"version":3,"file":"BaseRegistry.d.ts","sourceRoot":"","sources":["../../src/registry/BaseRegistry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"BaseRegistry.d.ts","sourceRoot":"","sources":["../../src/registry/BaseRegistry.ts"],"names":[],"mappings":"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"}
@@ -14,7 +14,10 @@ class BaseRegistry {
14
14
  _searchDirs;
15
15
  _discovered = false;
16
16
  _discoveryPromise = null;
17
- _refreshRequested = false;
17
+ _activeDiscoveryEpoch = null;
18
+ _queuedRefreshEpoch = null;
19
+ _replacementEpoch = 0;
20
+ _discoveredCount = 0;
18
21
  constructor(options){
19
22
  this._items = new Map();
20
23
  this._manualItems = new Map();
@@ -41,9 +44,11 @@ class BaseRegistry {
41
44
  }
42
45
  remove(name) {
43
46
  if (!this._items.has(name)) throw this._createNotFoundError(name, 'remove');
44
- this._items.delete(name);
45
- this._manualItems.delete(name);
46
- for (const [filePath, item] of this._discoveredItemsByPath)if (item.name === name) this._discoveredItemsByPath.delete(filePath);
47
+ const removedManualItem = this._manualItems.delete(name);
48
+ if (!removedManualItem) {
49
+ for (const [filePath, item] of this._discoveredItemsByPath)if (item.name === name) this._discoveredItemsByPath.delete(filePath);
50
+ }
51
+ this._rebuildVisibleItems();
47
52
  this.log(`Removed ${this._entityName}: ${name}`, {
48
53
  [`${this._entityName}Name`]: name
49
54
  });
@@ -52,14 +57,16 @@ class BaseRegistry {
52
57
  }
53
58
  update(name, updates) {
54
59
  if (!this._items.has(name)) throw this._createNotFoundError(name, 'update');
60
+ const isManualItem = this._manualItems.has(name);
55
61
  const existing = this._items.get(name);
56
62
  const updated = {
57
63
  ...existing,
58
- ...updates
64
+ ...updates,
65
+ name
59
66
  };
60
67
  this._items.set(name, updated);
61
- if (this._manualItems.has(name)) this._manualItems.set(name, updated);
62
- for (const [filePath, item] of this._discoveredItemsByPath)if (item.name === name) this._discoveredItemsByPath.set(filePath, updated);
68
+ if (isManualItem) this._manualItems.set(name, updated);
69
+ else for (const [filePath, item] of this._discoveredItemsByPath)if (item.name === name) this._discoveredItemsByPath.set(filePath, updated);
63
70
  this.log(`Updated ${this._entityName}: ${name}`, {
64
71
  [`${this._entityName}Name`]: name
65
72
  });
@@ -85,110 +92,150 @@ class BaseRegistry {
85
92
  return Array.from(this._items.keys());
86
93
  }
87
94
  clear() {
88
- this._items.clear();
89
- this._manualItems.clear();
90
- this._discoveredItemsByPath.clear();
95
+ this._beginReplacement();
91
96
  this.log(`Cleared all ${this._entityName}s`);
92
- this._cache?.clear();
93
97
  }
94
98
  size() {
95
99
  return this._items.size;
96
100
  }
97
101
  async discoverAsync() {
98
- if (this._discoveryPromise) return this._discoveryPromise;
99
- if (this._discovered) {
100
- const cached = this._cache.get('all');
101
- return cached?.length ?? 0;
102
+ if (this._discoveryPromise) {
103
+ if (!this._discovered && this._activeDiscoveryEpoch !== this._replacementEpoch) this._queuedRefreshEpoch = this._replacementEpoch;
104
+ return this._discoveryPromise;
102
105
  }
106
+ if (this._discovered) return this._discoveredCount;
103
107
  return this._beginDiscovery();
104
108
  }
105
109
  async refreshAsync() {
106
- this._refreshRequested = true;
107
- return this._discoveryPromise ?? this._beginDiscovery();
110
+ if (this._discoveryPromise) {
111
+ this._queuedRefreshEpoch = this._replacementEpoch;
112
+ return this._discoveryPromise;
113
+ }
114
+ return this._beginDiscovery();
108
115
  }
109
116
  _beginDiscovery() {
110
117
  const operation = this._runDiscoveryLoop();
111
118
  this._discoveryPromise = operation;
112
- const clearOperation = ()=>{
113
- if (this._discoveryPromise === operation) this._discoveryPromise = null;
114
- };
115
- operation.then(clearOperation, clearOperation);
116
119
  return operation;
117
120
  }
118
121
  async _runDiscoveryLoop() {
119
- this._refreshRequested = false;
120
- let count = await this._performDiscovery();
121
- while(this._refreshRequested){
122
- this._refreshRequested = false;
123
- count = await this._performDiscovery();
122
+ let passEpoch = this._replacementEpoch;
123
+ try {
124
+ while(true){
125
+ this._activeDiscoveryEpoch = passEpoch;
126
+ let count;
127
+ try {
128
+ count = await this._performDiscovery(passEpoch);
129
+ } finally{
130
+ this._activeDiscoveryEpoch = null;
131
+ }
132
+ const queuedEpoch = this._queuedRefreshEpoch;
133
+ this._queuedRefreshEpoch = null;
134
+ if (null === queuedEpoch) return count;
135
+ passEpoch = queuedEpoch;
136
+ }
137
+ } finally{
138
+ this._discoveryPromise = null;
124
139
  }
125
- return count;
126
- }
127
- async _performDiscovery() {
128
- const discoveredItems = new Map();
129
- const claimedNames = new Set(this._manualItems.keys());
130
- for (const dir of this._searchDirs)try {
131
- if (!existsSync(dir)) continue;
132
- const entries = await readdir(dir, {
140
+ }
141
+ async _performDiscovery(epoch = this._replacementEpoch) {
142
+ const scan = {
143
+ itemsByPath: new Map(),
144
+ claimedNames: new Set(),
145
+ previousItemsByPath: new Map(this._discoveredItemsByPath),
146
+ warnings: []
147
+ };
148
+ for (const dir of this._searchDirs)await this._scanDirectory(dir, scan);
149
+ return this._publishDiscovery(epoch, scan);
150
+ }
151
+ async _scanDirectory(directory, scan) {
152
+ try {
153
+ if (!existsSync(directory)) return;
154
+ const entries = await readdir(directory, {
133
155
  withFileTypes: true
134
156
  });
135
- for (const entry of entries)if (!this._shouldSkipFile(entry.name)) {
136
- if (entry.isFile() && this._fileExtensions.some((ext)=>entry.name.endsWith(ext))) {
137
- const filePath = join(dir, entry.name);
138
- try {
139
- const content = await readFile(filePath, 'utf-8');
140
- const parsed = this._parseFrontmatter(content);
141
- if (parsed._error) {
142
- this._retainLastKnownGood(filePath, parsed._error, discoveredItems, claimedNames);
143
- continue;
144
- }
145
- if (parsed.name) {
146
- const item = this._buildItem(parsed);
147
- if (item && !claimedNames.has(item.name)) {
148
- discoveredItems.set(filePath, item);
149
- claimedNames.add(item.name);
150
- }
151
- }
152
- } catch (readError) {
153
- this._retainLastKnownGood(filePath, getErrorMessage(readError), discoveredItems, claimedNames);
154
- }
155
- }
157
+ for (const entry of entries)if (!this._shouldSkipFile(entry.name) && entry.isFile()) {
158
+ if (this._fileExtensions.some((extension)=>entry.name.endsWith(extension))) await this._scanFile(join(directory, entry.name), scan);
156
159
  }
157
160
  } catch (error) {
158
- this._logger.warn(`Failed to scan ${this._entityName} directory`, {
159
- directory: dir,
160
- error: getErrorMessage(error)
161
+ scan.warnings.push({
162
+ message: `Failed to scan ${this._entityName} directory`,
163
+ meta: {
164
+ directory,
165
+ error: getErrorMessage(error)
166
+ }
161
167
  });
162
- this._retainDirectory(dir, discoveredItems, claimedNames);
168
+ this._retainDirectory(directory, scan);
163
169
  }
164
- this._discoveredItemsByPath = discoveredItems;
165
- this._items = new Map(this._manualItems);
166
- for (const item of discoveredItems.values())if (!this._items.has(item.name)) this._items.set(item.name, item);
170
+ }
171
+ async _scanFile(filePath, scan) {
172
+ try {
173
+ const content = await readFile(filePath, 'utf-8');
174
+ const parsed = this._parseFrontmatter(content);
175
+ if (parsed._error) return void this._retainLastKnownGood(filePath, parsed._error, scan);
176
+ if (!parsed.name) return;
177
+ const item = this._buildItem(parsed);
178
+ if (item && !scan.claimedNames.has(item.name)) {
179
+ scan.itemsByPath.set(filePath, item);
180
+ scan.claimedNames.add(item.name);
181
+ }
182
+ } catch (error) {
183
+ this._retainLastKnownGood(filePath, getErrorMessage(error), scan);
184
+ }
185
+ }
186
+ _retainLastKnownGood(filePath, reason, scan) {
187
+ const previous = scan.previousItemsByPath.get(filePath);
188
+ if (previous && !scan.claimedNames.has(previous.name)) {
189
+ scan.itemsByPath.set(filePath, previous);
190
+ scan.claimedNames.add(previous.name);
191
+ }
192
+ scan.warnings.push({
193
+ message: `Invalid ${this._entityName} discovery file`,
194
+ meta: {
195
+ filePath,
196
+ reason,
197
+ retainedLastKnownGood: void 0 !== previous
198
+ }
199
+ });
200
+ }
201
+ _retainDirectory(directory, scan) {
202
+ for (const [filePath, item] of scan.previousItemsByPath)if (dirname(filePath) === directory && !scan.claimedNames.has(item.name)) {
203
+ scan.itemsByPath.set(filePath, item);
204
+ scan.claimedNames.add(item.name);
205
+ }
206
+ }
207
+ _publishDiscovery(epoch, scan) {
208
+ if (epoch !== this._replacementEpoch) return this._discoveredCount;
209
+ this._discoveredItemsByPath = scan.itemsByPath;
210
+ this._rebuildVisibleItems();
167
211
  this._discovered = true;
168
212
  this._cache.clear();
169
213
  this._cache.set('all', Array.from(this._items.values()));
170
- this.log(`Discovery complete: found ${discoveredItems.size} ${this._entityName}s`, {
171
- discoveredCount: discoveredItems.size
214
+ for (const warning of scan.warnings)this._logger.warn(warning.message, warning.meta);
215
+ this.log(`Discovery complete: found ${this._discoveredCount} ${this._entityName}s`, {
216
+ discoveredCount: this._discoveredCount
172
217
  });
173
- return discoveredItems.size;
218
+ return this._discoveredCount;
174
219
  }
175
- _retainLastKnownGood(filePath, reason, discoveredItems, claimedNames) {
176
- const previous = this._discoveredItemsByPath.get(filePath);
177
- if (previous && !claimedNames.has(previous.name)) {
178
- discoveredItems.set(filePath, previous);
179
- claimedNames.add(previous.name);
220
+ _rebuildVisibleItems() {
221
+ const visibleItems = new Map(this._manualItems);
222
+ let visibleDiscoveredCount = 0;
223
+ for (const item of this._discoveredItemsByPath.values())if (!visibleItems.has(item.name)) {
224
+ visibleItems.set(item.name, item);
225
+ visibleDiscoveredCount++;
180
226
  }
181
- this._logger.warn(`Invalid ${this._entityName} discovery file`, {
182
- filePath,
183
- reason,
184
- retainedLastKnownGood: void 0 !== previous
185
- });
227
+ this._items = visibleItems;
228
+ this._discoveredCount = visibleDiscoveredCount;
186
229
  }
187
- _retainDirectory(directory, discoveredItems, claimedNames) {
188
- for (const [filePath, item] of this._discoveredItemsByPath)if (dirname(filePath) === directory && !claimedNames.has(item.name)) {
189
- discoveredItems.set(filePath, item);
190
- claimedNames.add(item.name);
191
- }
230
+ _beginReplacement() {
231
+ this._replacementEpoch++;
232
+ this._queuedRefreshEpoch = null;
233
+ this._items.clear();
234
+ this._manualItems.clear();
235
+ this._discoveredItemsByPath.clear();
236
+ this._discovered = false;
237
+ this._discoveredCount = 0;
238
+ this._cache.clear();
192
239
  }
193
240
  _extractFrontmatter(content) {
194
241
  const match = content.match(/^---\n([\s\S]+?)\n---/);
@@ -196,7 +243,7 @@ class BaseRegistry {
196
243
  return parse(match[1]);
197
244
  }
198
245
  setAll(items) {
199
- this.clear();
246
+ this._beginReplacement();
200
247
  for (const item of items)try {
201
248
  this.add(item);
202
249
  } catch (error) {