@remnic/core 9.48.0 → 9.49.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 (240) hide show
  1. package/dist/access-admin-ops-surface.d.ts +6 -5
  2. package/dist/access-admin-ops-surface.js +27 -26
  3. package/dist/access-audit.js +2 -2
  4. package/dist/access-authorization-probe.d.ts +6 -5
  5. package/dist/access-authorization-probe.js +28 -27
  6. package/dist/access-boundary.d.ts +6 -5
  7. package/dist/access-boundary.js +27 -26
  8. package/dist/access-cli.js +64 -63
  9. package/dist/access-cli.js.map +1 -1
  10. package/dist/access-extraction-force-flush.d.ts +6 -5
  11. package/dist/access-extraction-force-flush.js +27 -26
  12. package/dist/access-http-lcm-compaction.d.ts +6 -5
  13. package/dist/access-http-lifecycle-flush.d.ts +6 -5
  14. package/dist/access-http-offline-stream.d.ts +6 -5
  15. package/dist/access-http-query.js +27 -26
  16. package/dist/access-http.d.ts +6 -5
  17. package/dist/access-http.js +35 -34
  18. package/dist/access-identity-continuity-surface.d.ts +5 -4
  19. package/dist/access-identity-continuity-surface.js +27 -26
  20. package/dist/access-lcm-surface.d.ts +6 -5
  21. package/dist/access-lcm-surface.js +27 -26
  22. package/dist/access-mcp.d.ts +6 -5
  23. package/dist/access-mcp.js +33 -32
  24. package/dist/access-namespace-preflight.js +27 -26
  25. package/dist/access-observe-write-surface.d.ts +6 -5
  26. package/dist/access-observe-write-surface.js +27 -26
  27. package/dist/access-offline-manifest.d.ts +5 -4
  28. package/dist/access-operations-batch.js +29 -28
  29. package/dist/access-operations.d.ts +6 -5
  30. package/dist/access-operations.js +32 -31
  31. package/dist/access-recall-concurrency.d.ts +6 -5
  32. package/dist/access-recall-concurrency.js +27 -26
  33. package/dist/access-recall-response.d.ts +6 -5
  34. package/dist/access-recall-response.js +27 -26
  35. package/dist/access-recall-surface.d.ts +6 -5
  36. package/dist/access-recall-surface.js +27 -26
  37. package/dist/access-schema.js +3 -3
  38. package/dist/{access-service-andNjwfD.d.ts → access-service-DvXOzCrM.d.ts} +3 -3
  39. package/dist/access-service-helpers.d.ts +6 -5
  40. package/dist/access-service.d.ts +6 -5
  41. package/dist/access-service.js +27 -26
  42. package/dist/access-surface-catalog.d.ts +6 -5
  43. package/dist/access-wearables-meetings-surface.d.ts +3 -2
  44. package/dist/bootstrap.d.ts +5 -4
  45. package/dist/briefing.d.ts +2 -1
  46. package/dist/briefing.js +7 -6
  47. package/dist/buffer.d.ts +2 -1
  48. package/dist/{capsule-crypto-7FJQINUR.js → capsule-crypto-YO5QJ6L3.js} +2 -2
  49. package/dist/{capsule-merge-Q6AR3XL7.js → capsule-merge-6GFHWECG.js} +4 -4
  50. package/dist/causal-consolidation.js +8 -7
  51. package/dist/causal-consolidation.js.map +1 -1
  52. package/dist/{chunk-FMCHFSZF.js → chunk-2RIWJUSQ.js} +6 -6
  53. package/dist/{chunk-S55OAS5P.js → chunk-2VHLLPTX.js} +4 -4
  54. package/dist/{chunk-GWHPQOUC.js → chunk-3R6D33UF.js} +2 -2
  55. package/dist/{chunk-PELT6ADC.js → chunk-5AGR7SAI.js} +3 -3
  56. package/dist/chunk-5GINB5LC.js +13 -0
  57. package/dist/chunk-5GINB5LC.js.map +1 -0
  58. package/dist/{chunk-FJ2ND6CZ.js → chunk-5JRC6BYS.js} +10 -10
  59. package/dist/{chunk-A2TFK5PG.js → chunk-ABR2HEJO.js} +2 -2
  60. package/dist/{chunk-NXUJRXSF.js → chunk-B6KGGRUL.js} +2 -2
  61. package/dist/{chunk-PRVJH7CY.js → chunk-BMCHFWGX.js} +2 -2
  62. package/dist/{chunk-2NLLXCJG.js → chunk-BXLOS5AJ.js} +2 -2
  63. package/dist/{chunk-ANCC3JJH.js → chunk-C3KVFJH5.js} +2 -2
  64. package/dist/{chunk-JYPO7L3G.js → chunk-CNQQMB67.js} +2 -2
  65. package/dist/{chunk-X7Y7WX73.js → chunk-DQEMWVMT.js} +1 -1
  66. package/dist/{chunk-2EJNXE53.js → chunk-ENEA47UC.js} +2 -2
  67. package/dist/{chunk-PLCZD3LG.js → chunk-EY64JLOM.js} +66 -31
  68. package/dist/chunk-EY64JLOM.js.map +1 -0
  69. package/dist/{chunk-4WBGZYZZ.js → chunk-EYAIZBLM.js} +4 -4
  70. package/dist/chunk-EYAIZBLM.js.map +1 -0
  71. package/dist/{chunk-YNDLCWXS.js → chunk-EZ25VE3G.js} +4 -4
  72. package/dist/{chunk-HAYSYUAB.js → chunk-FMXJ555A.js} +2 -2
  73. package/dist/{chunk-VANZKHTN.js → chunk-G5OB4XZX.js} +4 -4
  74. package/dist/{chunk-DUEEQCTT.js → chunk-I3YHVVT4.js} +2 -2
  75. package/dist/{chunk-3JUYWPFJ.js → chunk-I4TQST3L.js} +2 -2
  76. package/dist/{chunk-HIDEBEAN.js → chunk-IPBNC77A.js} +8 -8
  77. package/dist/{chunk-45OTV7FZ.js → chunk-IXPKESVP.js} +11 -11
  78. package/dist/{chunk-KSGEGHYA.js → chunk-IZANPXMV.js} +34 -34
  79. package/dist/{chunk-IP3DHDU2.js → chunk-JFLOOTXU.js} +3 -3
  80. package/dist/{chunk-XL3TATMJ.js → chunk-JHMAYABJ.js} +4 -4
  81. package/dist/{chunk-HRMEYQRO.js → chunk-JXZ7NLYH.js} +2 -2
  82. package/dist/{chunk-53AJILXF.js → chunk-K7VKAHFE.js} +4 -4
  83. package/dist/{chunk-IJ7HK452.js → chunk-KMAP7SNQ.js} +2 -2
  84. package/dist/{chunk-D5NGH2EW.js → chunk-L6DCVUCA.js} +2 -2
  85. package/dist/{chunk-M4DMO67I.js → chunk-LQL7ARIC.js} +3 -3
  86. package/dist/{chunk-3NDN5P55.js → chunk-MXPB6PJQ.js} +2 -2
  87. package/dist/{chunk-RSPSHRZA.js → chunk-MZZR3J6M.js} +2 -2
  88. package/dist/{chunk-YLHM5BQQ.js → chunk-OLX2HJFW.js} +4 -4
  89. package/dist/{chunk-NIS256NW.js → chunk-QKD65RGL.js} +2 -2
  90. package/dist/{chunk-A4JMGKCD.js → chunk-R5IGA2NH.js} +4 -4
  91. package/dist/chunk-RBOJCG4T.js +77 -0
  92. package/dist/chunk-RBOJCG4T.js.map +1 -0
  93. package/dist/{chunk-7YKVM5H3.js → chunk-RHTO4HTL.js} +2 -2
  94. package/dist/{chunk-GD7UPIBG.js → chunk-RJV6Q2FT.js} +2 -2
  95. package/dist/{chunk-5L7PTX2Q.js → chunk-SHDZNSK4.js} +2 -2
  96. package/dist/{chunk-WOEVJKKQ.js → chunk-SMPVHGHG.js} +3 -3
  97. package/dist/{chunk-7K5Q6COX.js → chunk-TVVEYCNW.js} +4 -4
  98. package/dist/{chunk-52I3YZZ6.js → chunk-ULWHRQEQ.js} +2 -2
  99. package/dist/{chunk-KQAFEZQX.js → chunk-VDX2J7OX.js} +2 -2
  100. package/dist/{chunk-2P3GQVM2.js → chunk-YQIVWS7Y.js} +50 -41
  101. package/dist/chunk-YQIVWS7Y.js.map +1 -0
  102. package/dist/{chunk-G2RBWVFQ.js → chunk-Z4U6U3M3.js} +2 -2
  103. package/dist/{chunk-EZNXW27Q.js → chunk-Z5UC3OKB.js} +6 -6
  104. package/dist/{cli-p_WmVo2h.d.ts → cli-PyAUaozE.d.ts} +3 -3
  105. package/dist/cli.d.ts +7 -6
  106. package/dist/cli.js +52 -51
  107. package/dist/compounding/engine.d.ts +2 -1
  108. package/dist/compounding/engine.js +7 -6
  109. package/dist/connectors/codex-materialize-runner.js +7 -6
  110. package/dist/connectors/index.js +7 -6
  111. package/dist/consolidation-provenance-check.d.ts +2 -1
  112. package/dist/consolidation-provenance-check.js +4 -4
  113. package/dist/consolidation-undo.d.ts +2 -1
  114. package/dist/consolidation-undo.js +3 -3
  115. package/dist/contradiction/index.d.ts +2 -1
  116. package/dist/convergence-refresh.d.ts +2 -1
  117. package/dist/convergence-refresh.js +9 -8
  118. package/dist/corpus-read-cancellation.d.ts +38 -0
  119. package/dist/corpus-read-cancellation.js +9 -0
  120. package/dist/corpus-read-cancellation.js.map +1 -0
  121. package/dist/corpus-watermark.js +16 -15
  122. package/dist/dashboard-runtime.js +2 -2
  123. package/dist/entity-retrieval.d.ts +2 -1
  124. package/dist/entity-retrieval.js +7 -6
  125. package/dist/explicit-capture.d.ts +5 -4
  126. package/dist/external-wiki-access.d.ts +6 -5
  127. package/dist/external-wiki-access.js +28 -27
  128. package/dist/external-wiki-mcp-tools.d.ts +6 -5
  129. package/dist/extraction.js +8 -7
  130. package/dist/in-flight-reads.d.ts +46 -2
  131. package/dist/in-flight-reads.js +5 -3
  132. package/dist/index.d.ts +11 -10
  133. package/dist/index.js +68 -67
  134. package/dist/lcm/index.js +3 -3
  135. package/dist/maintenance/memory-governance.js +7 -6
  136. package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -1
  137. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +7 -6
  138. package/dist/maintenance/rebuild-memory-projection.d.ts +2 -1
  139. package/dist/maintenance/rebuild-memory-projection.js +8 -7
  140. package/dist/{maintenance-BfcuwfJS.d.ts → maintenance-j7sfNQdK.d.ts} +1 -1
  141. package/dist/mcp-memory-inspector-app.d.ts +6 -5
  142. package/dist/memory-corpus-version.js +2 -2
  143. package/dist/memory-worth-outcomes.d.ts +2 -1
  144. package/dist/namespaces/migrate.d.ts +2 -1
  145. package/dist/namespaces/migrate.js +15 -14
  146. package/dist/namespaces/search.js +7 -7
  147. package/dist/namespaces/storage.d.ts +2 -1
  148. package/dist/namespaces/storage.js +7 -6
  149. package/dist/operator-doctor-corpus.js +17 -16
  150. package/dist/operator-toolkit.d.ts +2 -1
  151. package/dist/operator-toolkit.js +22 -21
  152. package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -1
  153. package/dist/orchestration/maintenance.d.ts +3 -2
  154. package/dist/orchestration/maintenance.js +12 -11
  155. package/dist/{orchestrator-B7nLo4z1.d.ts → orchestrator-Haa9xVPV.d.ts} +3 -3
  156. package/dist/orchestrator.d.ts +5 -4
  157. package/dist/orchestrator.js +64 -63
  158. package/dist/page-versioning.js +3 -3
  159. package/dist/search/factory.js +6 -6
  160. package/dist/search/index.js +11 -11
  161. package/dist/search/lancedb-backend.js +2 -2
  162. package/dist/search/meilisearch-backend.js +2 -2
  163. package/dist/search/orama-backend.js +2 -2
  164. package/dist/semantic-consolidation.js +8 -7
  165. package/dist/semantic-rule-promotion.js +7 -6
  166. package/dist/semantic-rule-verifier.js +7 -6
  167. package/dist/{service-B5W5IYCx.d.ts → service-Da_BI4S8.d.ts} +1 -1
  168. package/dist/source-agent-qualifier.js +7 -6
  169. package/dist/{storage-COL907QF.d.ts → storage-D1apCiCS.d.ts} +4 -3
  170. package/dist/storage.d.ts +2 -1
  171. package/dist/storage.js +6 -5
  172. package/dist/temporal-supersession.d.ts +2 -1
  173. package/dist/tier-migration.d.ts +2 -1
  174. package/dist/transfer/backup.js +2 -2
  175. package/dist/transfer/capsule-export.js +2 -2
  176. package/dist/transfer/capsule-import.js +5 -5
  177. package/dist/verified-recall.js +7 -6
  178. package/package.json +3 -3
  179. package/src/corpus-read-cancellation.ts +42 -0
  180. package/src/entity-retrieval.ts +4 -4
  181. package/src/in-flight-reads.ts +119 -6
  182. package/src/orchestration/namespace-read-fanout.ts +13 -2
  183. package/src/orchestration/recall-internal-deps.ts +2 -0
  184. package/src/orchestration/recall-internal.ts +4 -1
  185. package/src/orchestration/recall-search-pipeline.ts +2 -0
  186. package/src/orchestration/recall-search-prefilter.ts +5 -1
  187. package/src/orchestrator.ts +5 -5
  188. package/src/storage/entity-store.ts +9 -2
  189. package/src/storage/memory-read-store.ts +33 -4
  190. package/src/storage-contract/corpus-read-cancellation.test.ts +408 -0
  191. package/src/storage.ts +61 -19
  192. package/dist/chunk-2P3GQVM2.js.map +0 -1
  193. package/dist/chunk-4WBGZYZZ.js.map +0 -1
  194. package/dist/chunk-PDJTHRAW.js +0 -36
  195. package/dist/chunk-PDJTHRAW.js.map +0 -1
  196. package/dist/chunk-PLCZD3LG.js.map +0 -1
  197. /package/dist/{capsule-crypto-7FJQINUR.js.map → capsule-crypto-YO5QJ6L3.js.map} +0 -0
  198. /package/dist/{capsule-merge-Q6AR3XL7.js.map → capsule-merge-6GFHWECG.js.map} +0 -0
  199. /package/dist/{chunk-FMCHFSZF.js.map → chunk-2RIWJUSQ.js.map} +0 -0
  200. /package/dist/{chunk-S55OAS5P.js.map → chunk-2VHLLPTX.js.map} +0 -0
  201. /package/dist/{chunk-GWHPQOUC.js.map → chunk-3R6D33UF.js.map} +0 -0
  202. /package/dist/{chunk-PELT6ADC.js.map → chunk-5AGR7SAI.js.map} +0 -0
  203. /package/dist/{chunk-FJ2ND6CZ.js.map → chunk-5JRC6BYS.js.map} +0 -0
  204. /package/dist/{chunk-A2TFK5PG.js.map → chunk-ABR2HEJO.js.map} +0 -0
  205. /package/dist/{chunk-NXUJRXSF.js.map → chunk-B6KGGRUL.js.map} +0 -0
  206. /package/dist/{chunk-PRVJH7CY.js.map → chunk-BMCHFWGX.js.map} +0 -0
  207. /package/dist/{chunk-2NLLXCJG.js.map → chunk-BXLOS5AJ.js.map} +0 -0
  208. /package/dist/{chunk-ANCC3JJH.js.map → chunk-C3KVFJH5.js.map} +0 -0
  209. /package/dist/{chunk-JYPO7L3G.js.map → chunk-CNQQMB67.js.map} +0 -0
  210. /package/dist/{chunk-X7Y7WX73.js.map → chunk-DQEMWVMT.js.map} +0 -0
  211. /package/dist/{chunk-2EJNXE53.js.map → chunk-ENEA47UC.js.map} +0 -0
  212. /package/dist/{chunk-YNDLCWXS.js.map → chunk-EZ25VE3G.js.map} +0 -0
  213. /package/dist/{chunk-HAYSYUAB.js.map → chunk-FMXJ555A.js.map} +0 -0
  214. /package/dist/{chunk-VANZKHTN.js.map → chunk-G5OB4XZX.js.map} +0 -0
  215. /package/dist/{chunk-DUEEQCTT.js.map → chunk-I3YHVVT4.js.map} +0 -0
  216. /package/dist/{chunk-3JUYWPFJ.js.map → chunk-I4TQST3L.js.map} +0 -0
  217. /package/dist/{chunk-HIDEBEAN.js.map → chunk-IPBNC77A.js.map} +0 -0
  218. /package/dist/{chunk-45OTV7FZ.js.map → chunk-IXPKESVP.js.map} +0 -0
  219. /package/dist/{chunk-KSGEGHYA.js.map → chunk-IZANPXMV.js.map} +0 -0
  220. /package/dist/{chunk-IP3DHDU2.js.map → chunk-JFLOOTXU.js.map} +0 -0
  221. /package/dist/{chunk-XL3TATMJ.js.map → chunk-JHMAYABJ.js.map} +0 -0
  222. /package/dist/{chunk-HRMEYQRO.js.map → chunk-JXZ7NLYH.js.map} +0 -0
  223. /package/dist/{chunk-53AJILXF.js.map → chunk-K7VKAHFE.js.map} +0 -0
  224. /package/dist/{chunk-IJ7HK452.js.map → chunk-KMAP7SNQ.js.map} +0 -0
  225. /package/dist/{chunk-D5NGH2EW.js.map → chunk-L6DCVUCA.js.map} +0 -0
  226. /package/dist/{chunk-M4DMO67I.js.map → chunk-LQL7ARIC.js.map} +0 -0
  227. /package/dist/{chunk-3NDN5P55.js.map → chunk-MXPB6PJQ.js.map} +0 -0
  228. /package/dist/{chunk-RSPSHRZA.js.map → chunk-MZZR3J6M.js.map} +0 -0
  229. /package/dist/{chunk-YLHM5BQQ.js.map → chunk-OLX2HJFW.js.map} +0 -0
  230. /package/dist/{chunk-NIS256NW.js.map → chunk-QKD65RGL.js.map} +0 -0
  231. /package/dist/{chunk-A4JMGKCD.js.map → chunk-R5IGA2NH.js.map} +0 -0
  232. /package/dist/{chunk-7YKVM5H3.js.map → chunk-RHTO4HTL.js.map} +0 -0
  233. /package/dist/{chunk-GD7UPIBG.js.map → chunk-RJV6Q2FT.js.map} +0 -0
  234. /package/dist/{chunk-5L7PTX2Q.js.map → chunk-SHDZNSK4.js.map} +0 -0
  235. /package/dist/{chunk-WOEVJKKQ.js.map → chunk-SMPVHGHG.js.map} +0 -0
  236. /package/dist/{chunk-7K5Q6COX.js.map → chunk-TVVEYCNW.js.map} +0 -0
  237. /package/dist/{chunk-52I3YZZ6.js.map → chunk-ULWHRQEQ.js.map} +0 -0
  238. /package/dist/{chunk-KQAFEZQX.js.map → chunk-VDX2J7OX.js.map} +0 -0
  239. /package/dist/{chunk-G2RBWVFQ.js.map → chunk-Z4U6U3M3.js.map} +0 -0
  240. /package/dist/{chunk-EZNXW27Q.js.map → chunk-Z5UC3OKB.js.map} +0 -0
package/src/storage.ts CHANGED
@@ -16,6 +16,8 @@ import {
16
16
  import { appendFileSync, createReadStream, mkdirSync, readFileSync, statSync, type Dirent } from "node:fs";
17
17
  import { createHash, randomUUID } from "node:crypto";
18
18
  import { normalizeContent, computeContentHash } from "./content-hash.js";
19
+ import { raceAbort } from "./abort-error.js";
20
+ import { checkCorpusReadAbort, type CorpusReadOptions } from "./corpus-read-cancellation.js";
19
21
  import { selectArtifactMatches, type ArtifactSearchOptions } from "./artifact-search.js";
20
22
  import path from "node:path";
21
23
  import { log } from "./logger.js";
@@ -71,8 +73,8 @@ import {
71
73
  updateCacheOnWrite,
72
74
  } from "./memory-cache.js";
73
75
  import {
74
- getInFlightRead,
75
- setInFlightRead,
76
+ attachInFlightReader,
77
+ beginCoalescedScan,
76
78
  deleteInFlightRead,
77
79
  deleteInFlightReadsForDir,
78
80
  clearInFlightReads,
@@ -4347,8 +4349,8 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
4347
4349
  return id;
4348
4350
  }
4349
4351
 
4350
- private async readAllArtifactsCached(): Promise<MemoryFile[]> {
4351
- return this.memoryReadStore.readAllArtifactsCached();
4352
+ private async readAllArtifactsCached(options?: CorpusReadOptions): Promise<MemoryFile[]> {
4353
+ return this.memoryReadStore.readAllArtifactsCached(options);
4352
4354
  }
4353
4355
 
4354
4356
  async searchArtifacts(
@@ -4357,7 +4359,7 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
4357
4359
  options: ArtifactSearchOptions = {},
4358
4360
  ): Promise<MemoryFile[]> {
4359
4361
  return selectArtifactMatches(
4360
- () => this.readAllArtifactsCached(),
4362
+ () => this.readAllArtifactsCached({ abortSignal: options.abortSignal }),
4361
4363
  query,
4362
4364
  maxResults,
4363
4365
  options,
@@ -4474,7 +4476,8 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
4474
4476
  return memories;
4475
4477
  }
4476
4478
 
4477
- async readAllMemories(): Promise<MemoryFile[]> {
4479
+ async readAllMemories(options?: CorpusReadOptions): Promise<MemoryFile[]> {
4480
+ checkCorpusReadAbort(options);
4478
4481
  if (this.hotMemoriesCacheEnabled) {
4479
4482
  const cached = getCachedMemories(
4480
4483
  this.baseDir,
@@ -4492,25 +4495,51 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
4492
4495
  // The in-flight slot get/set/delete also key on this snapshot so a mid-scan
4493
4496
  // key change can't orphan the slot registered under the old identity.
4494
4497
  const keyId = this.hotCacheKeyId();
4495
- const inFlight = getInFlightRead(this.baseDir, keyId);
4496
- if (inFlight) return this.rememberMemorySnapshots(await inFlight);
4498
+ const inFlight = attachInFlightReader(this.baseDir, keyId, options?.abortSignal);
4499
+ if (inFlight) {
4500
+ // A joiner never cancels the shared scan on its own, but it does stop waiting
4501
+ // on it, and its departure counts: if it was the last waiter the scan is
4502
+ // cancelled (issue #2307 review).
4503
+ try {
4504
+ return this.rememberMemorySnapshots(
4505
+ await raceAbort(inFlight.read, options?.abortSignal, "corpus read aborted"),
4506
+ );
4507
+ } finally {
4508
+ inFlight.waiter.leave();
4509
+ }
4510
+ }
4497
4511
 
4512
+ // Sole-waiter cancellation lives with the registry that decides whether this
4513
+ // scan is still cancellable at all — see `beginCoalescedScan` (issue #2307).
4514
+ const scan = beginCoalescedScan(this.baseDir, keyId, options?.abortSignal);
4498
4515
  const readPromise = (async (): Promise<MemoryFile[]> => {
4499
4516
  const version = this.getMemoryCorpusVersion();
4500
- const memories = await this._readAllMemoriesFromDisk();
4517
+ const memories = await this._readAllMemoriesFromDisk({ abortSignal: scan.scanSignal });
4501
4518
  // Publish only if neither the corpus version NOR the key identity changed
4502
4519
  // during the scan. The version guard prevents clobbering a newer patched +
4503
4520
  // re-keyed entry; the keyId guard prevents publishing this key's decrypted
4504
- // corpus under a since-changed identity (Codex Medium, #1902).
4521
+ // corpus under a since-changed identity (Codex Medium, #1902). A cancelled
4522
+ // scan never arrives here — the checkpoints throw — so a partial corpus is
4523
+ // never published.
4505
4524
  if (this.hotMemoriesCacheEnabled && this.getMemoryCorpusVersion() === version && this.hotCacheKeyId() === keyId) {
4506
4525
  setCachedMemories(this.baseDir, memories, version, keyId, this.hotCacheTtlMs());
4507
4526
  }
4508
4527
  return memories;
4509
4528
  })();
4510
- setInFlightRead(this.baseDir, keyId, readPromise);
4529
+ scan.arm(readPromise);
4511
4530
  try {
4512
- return this.rememberMemorySnapshots(await readPromise);
4531
+ // The starter races its own signal too. Refcounting means a sole waiter's
4532
+ // abort rejects the scan itself, but while another reader keeps the scan
4533
+ // alive the starter would otherwise sit here and be handed the full corpus
4534
+ // after cancelling — the joiner path's contract, applied symmetrically
4535
+ // (issue #2307 review). The scan is NOT aborted here: it belongs to the
4536
+ // readers still waiting, and a completed scan is still worth caching for
4537
+ // them.
4538
+ return this.rememberMemorySnapshots(
4539
+ await raceAbort(readPromise, options?.abortSignal, "corpus read aborted"),
4540
+ );
4513
4541
  } finally {
4542
+ scan.leave();
4514
4543
  deleteInFlightRead(this.baseDir, keyId, readPromise);
4515
4544
  }
4516
4545
  }
@@ -4769,7 +4798,9 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
4769
4798
  * active memory file paths without parsing frontmatter — safe on a 100k+
4770
4799
  * corpus, unlike readAllMemories().
4771
4800
  */
4772
- async collectActiveMemoryPaths(options?: { propagateReadErrors?: boolean }): Promise<string[]> {
4801
+ async collectActiveMemoryPaths(
4802
+ options?: { propagateReadErrors?: boolean } & CorpusReadOptions,
4803
+ ): Promise<string[]> {
4773
4804
  return this.memoryReadStore.collectActiveMemoryPaths(options);
4774
4805
  }
4775
4806
 
@@ -4793,12 +4824,19 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
4793
4824
  return `${this.getMemoryCorpusVersion()}:${this.readColdWriteVersion()}`;
4794
4825
  }
4795
4826
 
4796
- private async readParsedMemoriesFromPaths(filePaths: string[], batchSize?: number): Promise<MemoryFile[]> {
4827
+ private async readParsedMemoriesFromPaths(
4828
+ filePaths: string[],
4829
+ batchSize?: number,
4830
+ options?: CorpusReadOptions,
4831
+ ): Promise<MemoryFile[]> {
4797
4832
  if (filePaths.length === 0) return [];
4798
4833
 
4799
4834
  const normalizedBatchSize = this.normalizeMemoryReadBatchSize(batchSize);
4800
4835
  const memories: MemoryFile[] = [];
4801
4836
  for (let i = 0; i < filePaths.length; i += normalizedBatchSize) {
4837
+ // Per batch: the parse phase is as long as the walk on a large corpus, and
4838
+ // an abandoned caller must not keep paying for it (issue #2307).
4839
+ checkCorpusReadAbort(options);
4802
4840
  const batch = filePaths.slice(i, i + normalizedBatchSize);
4803
4841
  const results = await Promise.all(
4804
4842
  batch.map(async (fullPath) => {
@@ -4831,6 +4869,10 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
4831
4869
  if (memory !== null) memories.push(memory);
4832
4870
  }
4833
4871
  }
4872
+ // Once more after the loop: a signal that fired during the LAST batch's await
4873
+ // would otherwise let this return successfully, publishing to the cache and
4874
+ // handing results to a caller that had already given up (issue #2307 review).
4875
+ checkCorpusReadAbort(options);
4834
4876
  return memories;
4835
4877
  }
4836
4878
 
@@ -4939,9 +4981,9 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
4939
4981
  return this.memoryReadStore.readMemoriesWindow(options);
4940
4982
  }
4941
4983
 
4942
- private async _readAllMemoriesFromDisk(): Promise<MemoryFile[]> {
4943
- const filePaths = await this.collectActiveMemoryPaths();
4944
- return this.readParsedMemoriesFromPaths(filePaths, 50);
4984
+ private async _readAllMemoriesFromDisk(options?: CorpusReadOptions): Promise<MemoryFile[]> {
4985
+ const filePaths = await this.collectActiveMemoryPaths(options);
4986
+ return this.readParsedMemoriesFromPaths(filePaths, 50, options);
4945
4987
  }
4946
4988
 
4947
4989
  async readAllColdMemories(): Promise<MemoryFile[]> {
@@ -6430,8 +6472,8 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
6430
6472
  // Scoring + Knowledge Index (Knowledge Graph v7.0)
6431
6473
  // ---------------------------------------------------------------------------
6432
6474
 
6433
- async readAllEntityFiles(): Promise<EntityFile[]> {
6434
- return this.entityStore.readAllEntityFiles();
6475
+ async readAllEntityFiles(options?: CorpusReadOptions): Promise<EntityFile[]> {
6476
+ return this.entityStore.readAllEntityFiles(options);
6435
6477
  }
6436
6478
 
6437
6479
  /**