@remnic/core 9.3.650 → 9.3.652

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 (193) hide show
  1. package/dist/access-cli.js +39 -38
  2. package/dist/access-cli.js.map +1 -1
  3. package/dist/access-http.d.ts +3 -2
  4. package/dist/access-http.js +19 -19
  5. package/dist/access-mcp.d.ts +3 -2
  6. package/dist/access-mcp.js +18 -18
  7. package/dist/access-schema.js +3 -3
  8. package/dist/{access-service-DIZRHQ7Q.d.ts → access-service-CdJFd3_b.d.ts} +23 -2
  9. package/dist/access-service.d.ts +3 -2
  10. package/dist/access-service.js +16 -16
  11. package/dist/{auto-sync-54QQHOG5.js → auto-sync-5CJBJMPZ.js} +5 -5
  12. package/dist/bootstrap.d.ts +2 -1
  13. package/dist/briefing.js +3 -3
  14. package/dist/calibration.js +2 -2
  15. package/dist/{capsule-crypto-GWVG7LGC.js → capsule-crypto-7FJQINUR.js} +2 -2
  16. package/dist/causal-consolidation.js +6 -6
  17. package/dist/{chunk-QT4THOLT.js → chunk-2DGQLOOM.js} +1 -1
  18. package/dist/chunk-2DGQLOOM.js.map +1 -0
  19. package/dist/{chunk-OWHERGF2.js → chunk-2NLLXCJG.js} +2 -2
  20. package/dist/{chunk-OAZ5MFUB.js → chunk-3XGWCZ63.js} +45 -28
  21. package/dist/chunk-3XGWCZ63.js.map +1 -0
  22. package/dist/{chunk-QKE4LHNR.js → chunk-4HYSMH7D.js} +2 -2
  23. package/dist/{chunk-DDRNDPX4.js → chunk-4SKKVWLQ.js} +2 -2
  24. package/dist/chunk-5FOCXX5E.js +34 -0
  25. package/dist/chunk-5FOCXX5E.js.map +1 -0
  26. package/dist/{chunk-YAFSTKTH.js → chunk-5V3TAB7D.js} +184 -12
  27. package/dist/chunk-5V3TAB7D.js.map +1 -0
  28. package/dist/{chunk-DB5A3NHS.js → chunk-7LWRCOP7.js} +9 -2
  29. package/dist/chunk-7LWRCOP7.js.map +1 -0
  30. package/dist/{chunk-FOVPSMGI.js → chunk-7WEB3FLJ.js} +2 -2
  31. package/dist/{chunk-APJQ6UEA.js → chunk-AGNBY3VG.js} +4 -4
  32. package/dist/{chunk-4BISW7RX.js → chunk-AJE7FJVE.js} +2 -2
  33. package/dist/{chunk-ZXWAQFDE.js → chunk-CFOCZPIQ.js} +2 -2
  34. package/dist/{chunk-NT5TINK5.js → chunk-DHGSZ3UD.js} +2 -2
  35. package/dist/{chunk-OTC2KOZ2.js → chunk-EHQLDFSH.js} +2 -2
  36. package/dist/{chunk-RRRCNIPK.js → chunk-GI45G4BK.js} +4 -4
  37. package/dist/{chunk-AMACWKM4.js → chunk-IJHLC5CH.js} +2 -2
  38. package/dist/{chunk-OR7R6M5Z.js → chunk-IVYSVAC6.js} +2 -2
  39. package/dist/{chunk-76QTEJ2Q.js → chunk-JBHXMCYN.js} +2 -2
  40. package/dist/{chunk-UMKPSD35.js → chunk-JF7SFXTG.js} +2 -2
  41. package/dist/{chunk-NMIOW7XG.js → chunk-JVRPJ7D4.js} +126 -26
  42. package/dist/chunk-JVRPJ7D4.js.map +1 -0
  43. package/dist/{chunk-TQUWNX7C.js → chunk-JX2RINDR.js} +2 -2
  44. package/dist/{chunk-MCYT2RNT.js → chunk-KJDKZVF3.js} +3 -3
  45. package/dist/{chunk-BUKK5SWA.js → chunk-KQAFEZQX.js} +2 -2
  46. package/dist/{chunk-PQFUUXWK.js → chunk-KWM33SPU.js} +2 -2
  47. package/dist/{chunk-A3BS64GV.js → chunk-LCC5EZTT.js} +4 -4
  48. package/dist/{chunk-TVOPSKOK.js → chunk-MGGNV3H2.js} +4 -4
  49. package/dist/{chunk-D6WVJIS3.js → chunk-ORGWWNJG.js} +2 -2
  50. package/dist/{chunk-Z3PZRDLW.js → chunk-PRQXUSQV.js} +2 -2
  51. package/dist/{chunk-VWT3F4IV.js → chunk-PS3SYNHP.js} +12 -4
  52. package/dist/chunk-PS3SYNHP.js.map +1 -0
  53. package/dist/{chunk-I4COC5XW.js → chunk-PYWNNF2I.js} +47 -9
  54. package/dist/chunk-PYWNNF2I.js.map +1 -0
  55. package/dist/{chunk-IMWFHBG2.js → chunk-QWRC7GIO.js} +2 -2
  56. package/dist/{chunk-U3GQ33JC.js → chunk-SLTKP5WJ.js} +2 -2
  57. package/dist/{chunk-23RYLGYA.js → chunk-TCX4WLKK.js} +104 -112
  58. package/dist/chunk-TCX4WLKK.js.map +1 -0
  59. package/dist/{chunk-6NKAQ74D.js → chunk-UU6MVCJ6.js} +1 -1
  60. package/dist/chunk-UU6MVCJ6.js.map +1 -0
  61. package/dist/{chunk-WEPMT6SC.js → chunk-V25ZAOSB.js} +5 -5
  62. package/dist/{chunk-UMTG2BN2.js → chunk-V4UDXYGG.js} +2 -2
  63. package/dist/{chunk-TUMH6EDV.js → chunk-WSFNYPAT.js} +26 -26
  64. package/dist/{chunk-ZT6R3WR3.js → chunk-WTI35CVJ.js} +4 -4
  65. package/dist/{chunk-UVYI6VIX.js → chunk-X7Y7WX73.js} +1 -1
  66. package/dist/{chunk-OZKZ2TRP.js → chunk-XBIACVCO.js} +9 -2
  67. package/dist/chunk-XBIACVCO.js.map +1 -0
  68. package/dist/{chunk-ALUZN7BE.js → chunk-XMN6MMTU.js} +2 -2
  69. package/dist/{chunk-A4BTPHIN.js → chunk-Y7NWBBHV.js} +6 -6
  70. package/dist/{chunk-WPCCNSWO.js → chunk-YM3LR4LS.js} +7 -7
  71. package/dist/{chunk-3IJEQWQX.js → chunk-YOVKPOMD.js} +4 -4
  72. package/dist/{chunk-M75TBFKQ.js → chunk-Z2OXSMZK.js} +2 -2
  73. package/dist/{cli-BG4ybtJr.d.ts → cli-DDo7Qgs-.d.ts} +2 -2
  74. package/dist/cli.d.ts +4 -3
  75. package/dist/cli.js +34 -34
  76. package/dist/compounding/engine.js +3 -3
  77. package/dist/connectors/codex-materialize-runner.js +3 -3
  78. package/dist/connectors/index.js +3 -3
  79. package/dist/entity-retrieval.js +3 -3
  80. package/dist/event-order-recall.js +1 -1
  81. package/dist/explicit-capture.d.ts +2 -1
  82. package/dist/explicit-cue-recall.d.ts +7 -0
  83. package/dist/explicit-cue-recall.js +2 -1
  84. package/dist/extraction-judge.js +3 -3
  85. package/dist/extraction.js +3 -3
  86. package/dist/fallback-llm.js +2 -2
  87. package/dist/focused-list-recall.d.ts +6 -0
  88. package/dist/focused-list-recall.js +2 -1
  89. package/dist/index.d.ts +5 -4
  90. package/dist/index.js +87 -86
  91. package/dist/index.js.map +1 -1
  92. package/dist/lcm/engine.js +2 -2
  93. package/dist/lcm/index.js +5 -5
  94. package/dist/lcm-fallback-read.d.ts +71 -0
  95. package/dist/lcm-fallback-read.js +10 -0
  96. package/dist/lcm-fallback-read.js.map +1 -0
  97. package/dist/maintenance/memory-governance.js +3 -3
  98. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
  99. package/dist/maintenance/rebuild-memory-projection.js +4 -4
  100. package/dist/mcp-memory-inspector-app.d.ts +3 -2
  101. package/dist/namespaces/migrate.js +11 -11
  102. package/dist/namespaces/search.d.ts +18 -1
  103. package/dist/namespaces/search.js +7 -7
  104. package/dist/namespaces/storage.js +3 -3
  105. package/dist/operator-toolkit.js +13 -13
  106. package/dist/{orchestrator-CX-oqwJq.d.ts → orchestrator-8fTZsa0y.d.ts} +2 -0
  107. package/dist/orchestrator.d.ts +2 -1
  108. package/dist/orchestrator.js +33 -32
  109. package/dist/qmd.d.ts +2 -1
  110. package/dist/qmd.js +2 -2
  111. package/dist/recall-planner-llm.js +2 -2
  112. package/dist/response-guidance-recall.d.ts +6 -0
  113. package/dist/response-guidance-recall.js +2 -1
  114. package/dist/search/factory.js +6 -6
  115. package/dist/search/index.js +8 -8
  116. package/dist/search/lancedb-backend.js +2 -2
  117. package/dist/search/meilisearch-backend.js +2 -2
  118. package/dist/search/orama-backend.js +2 -2
  119. package/dist/search/port.d.ts +6 -0
  120. package/dist/search/port.js +1 -1
  121. package/dist/semantic-consolidation.js +4 -4
  122. package/dist/semantic-rule-promotion.js +3 -3
  123. package/dist/semantic-rule-verifier.js +3 -3
  124. package/dist/storage.js +2 -2
  125. package/dist/summarizer.js +3 -3
  126. package/dist/targeted-fact-recall.d.ts +6 -0
  127. package/dist/targeted-fact-recall.js +2 -1
  128. package/dist/transfer/backup.js +2 -2
  129. package/dist/transfer/capsule-export.js +2 -2
  130. package/dist/transfer/capsule-import.js +2 -2
  131. package/dist/transfer/import-sqlite.js +2 -2
  132. package/dist/verified-recall.js +3 -3
  133. package/package.json +1 -1
  134. package/src/access-service-health.test.ts +402 -0
  135. package/src/access-service.ts +274 -2
  136. package/src/event-order-recall.ts +8 -0
  137. package/src/explicit-cue-recall.ts +70 -29
  138. package/src/focused-list-recall.ts +23 -1
  139. package/src/lcm-fallback-read.ts +113 -0
  140. package/src/namespaces/search.test.ts +258 -3
  141. package/src/namespaces/search.ts +184 -30
  142. package/src/orchestrator.ts +179 -122
  143. package/src/qmd.test.ts +102 -0
  144. package/src/qmd.ts +54 -7
  145. package/src/response-guidance-recall.ts +21 -1
  146. package/src/search/port.ts +6 -0
  147. package/src/targeted-fact-recall.ts +24 -3
  148. package/dist/chunk-23RYLGYA.js.map +0 -1
  149. package/dist/chunk-6NKAQ74D.js.map +0 -1
  150. package/dist/chunk-DB5A3NHS.js.map +0 -1
  151. package/dist/chunk-I4COC5XW.js.map +0 -1
  152. package/dist/chunk-NMIOW7XG.js.map +0 -1
  153. package/dist/chunk-OAZ5MFUB.js.map +0 -1
  154. package/dist/chunk-OZKZ2TRP.js.map +0 -1
  155. package/dist/chunk-QT4THOLT.js.map +0 -1
  156. package/dist/chunk-VWT3F4IV.js.map +0 -1
  157. package/dist/chunk-YAFSTKTH.js.map +0 -1
  158. /package/dist/{auto-sync-54QQHOG5.js.map → auto-sync-5CJBJMPZ.js.map} +0 -0
  159. /package/dist/{capsule-crypto-GWVG7LGC.js.map → capsule-crypto-7FJQINUR.js.map} +0 -0
  160. /package/dist/{chunk-OWHERGF2.js.map → chunk-2NLLXCJG.js.map} +0 -0
  161. /package/dist/{chunk-QKE4LHNR.js.map → chunk-4HYSMH7D.js.map} +0 -0
  162. /package/dist/{chunk-DDRNDPX4.js.map → chunk-4SKKVWLQ.js.map} +0 -0
  163. /package/dist/{chunk-FOVPSMGI.js.map → chunk-7WEB3FLJ.js.map} +0 -0
  164. /package/dist/{chunk-APJQ6UEA.js.map → chunk-AGNBY3VG.js.map} +0 -0
  165. /package/dist/{chunk-4BISW7RX.js.map → chunk-AJE7FJVE.js.map} +0 -0
  166. /package/dist/{chunk-ZXWAQFDE.js.map → chunk-CFOCZPIQ.js.map} +0 -0
  167. /package/dist/{chunk-NT5TINK5.js.map → chunk-DHGSZ3UD.js.map} +0 -0
  168. /package/dist/{chunk-OTC2KOZ2.js.map → chunk-EHQLDFSH.js.map} +0 -0
  169. /package/dist/{chunk-RRRCNIPK.js.map → chunk-GI45G4BK.js.map} +0 -0
  170. /package/dist/{chunk-AMACWKM4.js.map → chunk-IJHLC5CH.js.map} +0 -0
  171. /package/dist/{chunk-OR7R6M5Z.js.map → chunk-IVYSVAC6.js.map} +0 -0
  172. /package/dist/{chunk-76QTEJ2Q.js.map → chunk-JBHXMCYN.js.map} +0 -0
  173. /package/dist/{chunk-UMKPSD35.js.map → chunk-JF7SFXTG.js.map} +0 -0
  174. /package/dist/{chunk-TQUWNX7C.js.map → chunk-JX2RINDR.js.map} +0 -0
  175. /package/dist/{chunk-MCYT2RNT.js.map → chunk-KJDKZVF3.js.map} +0 -0
  176. /package/dist/{chunk-BUKK5SWA.js.map → chunk-KQAFEZQX.js.map} +0 -0
  177. /package/dist/{chunk-PQFUUXWK.js.map → chunk-KWM33SPU.js.map} +0 -0
  178. /package/dist/{chunk-A3BS64GV.js.map → chunk-LCC5EZTT.js.map} +0 -0
  179. /package/dist/{chunk-TVOPSKOK.js.map → chunk-MGGNV3H2.js.map} +0 -0
  180. /package/dist/{chunk-D6WVJIS3.js.map → chunk-ORGWWNJG.js.map} +0 -0
  181. /package/dist/{chunk-Z3PZRDLW.js.map → chunk-PRQXUSQV.js.map} +0 -0
  182. /package/dist/{chunk-IMWFHBG2.js.map → chunk-QWRC7GIO.js.map} +0 -0
  183. /package/dist/{chunk-U3GQ33JC.js.map → chunk-SLTKP5WJ.js.map} +0 -0
  184. /package/dist/{chunk-WEPMT6SC.js.map → chunk-V25ZAOSB.js.map} +0 -0
  185. /package/dist/{chunk-UMTG2BN2.js.map → chunk-V4UDXYGG.js.map} +0 -0
  186. /package/dist/{chunk-TUMH6EDV.js.map → chunk-WSFNYPAT.js.map} +0 -0
  187. /package/dist/{chunk-ZT6R3WR3.js.map → chunk-WTI35CVJ.js.map} +0 -0
  188. /package/dist/{chunk-UVYI6VIX.js.map → chunk-X7Y7WX73.js.map} +0 -0
  189. /package/dist/{chunk-ALUZN7BE.js.map → chunk-XMN6MMTU.js.map} +0 -0
  190. /package/dist/{chunk-A4BTPHIN.js.map → chunk-Y7NWBBHV.js.map} +0 -0
  191. /package/dist/{chunk-WPCCNSWO.js.map → chunk-YM3LR4LS.js.map} +0 -0
  192. /package/dist/{chunk-3IJEQWQX.js.map → chunk-YOVKPOMD.js.map} +0 -0
  193. /package/dist/{chunk-M75TBFKQ.js.map → chunk-Z2OXSMZK.js.map} +0 -0
package/src/qmd.test.ts CHANGED
@@ -183,6 +183,108 @@ test("QmdClient preserves configured qmdPath diagnostics when all probes fail",
183
183
  }
184
184
  });
185
185
 
186
+ test("QmdClient read-only availability failures preserve operational state", async () => {
187
+ const { QmdClient } = await import("./qmd.js");
188
+ const originalPath = process.env.PATH;
189
+ const originalWindowsPath = process.env.Path;
190
+ const missingQmdPath = path.join(
191
+ os.tmpdir(),
192
+ `remnic-missing-readonly-qmd-${process.pid}-${Date.now()}`,
193
+ "qmd.cmd",
194
+ );
195
+
196
+ process.env.PATH = "";
197
+ process.env.Path = "";
198
+ try {
199
+ const client = new QmdClient("test-collection", 10, {
200
+ qmdPath: missingQmdPath,
201
+ qmdFallbackPaths: [],
202
+ });
203
+ (client as any).available = true;
204
+ (client as any).qmdPath = "qmd";
205
+ (client as any).qmdPathSource = "auto-path";
206
+ (client as any).cliVersion = "qmd 2.5.3";
207
+ (client as any).qmdCapabilities = resolveQmdCapabilities("qmd 2.5.3");
208
+ (client as any).lastCliProbeError = null;
209
+
210
+ assert.equal(await client.checkAvailability(), false);
211
+
212
+ assert.equal(client.isAvailable(), true);
213
+ assert.equal((client as any).qmdPath, "qmd");
214
+ assert.equal((client as any).qmdPathSource, "auto-path");
215
+ assert.equal((client as any).cliVersion, "qmd 2.5.3");
216
+ assert.equal((client as any).lastCliProbeError, null);
217
+ } finally {
218
+ if (originalPath === undefined) delete process.env.PATH;
219
+ else process.env.PATH = originalPath;
220
+ if (originalWindowsPath === undefined) delete process.env.Path;
221
+ else process.env.Path = originalWindowsPath;
222
+ }
223
+ });
224
+
225
+ test("QmdClient read-only availability preserves live daemon availability", async () => {
226
+ const { QmdClient } = await import("./qmd.js");
227
+ const originalPath = process.env.PATH;
228
+ const originalWindowsPath = process.env.Path;
229
+ const missingQmdPath = path.join(
230
+ os.tmpdir(),
231
+ `remnic-missing-daemon-qmd-${process.pid}-${Date.now()}`,
232
+ "qmd.cmd",
233
+ );
234
+
235
+ process.env.PATH = "";
236
+ process.env.Path = "";
237
+ try {
238
+ const client = new QmdClient("test-collection", 10, {
239
+ qmdPath: missingQmdPath,
240
+ qmdFallbackPaths: [],
241
+ });
242
+ (client as any).daemonAvailable = true;
243
+ (client as any).daemonSession = { isActive: () => true };
244
+
245
+ assert.equal(await client.checkAvailability(), true);
246
+ assert.equal(client.isAvailable(), true);
247
+ } finally {
248
+ if (originalPath === undefined) delete process.env.PATH;
249
+ else process.env.PATH = originalPath;
250
+ if (originalWindowsPath === undefined) delete process.env.Path;
251
+ else process.env.Path = originalWindowsPath;
252
+ }
253
+ });
254
+
255
+ test("QmdClient read-only availability clears stale daemon availability", async () => {
256
+ const { QmdClient } = await import("./qmd.js");
257
+ const originalPath = process.env.PATH;
258
+ const originalWindowsPath = process.env.Path;
259
+ const missingQmdPath = path.join(
260
+ os.tmpdir(),
261
+ `remnic-missing-stale-daemon-qmd-${process.pid}-${Date.now()}`,
262
+ "qmd.cmd",
263
+ );
264
+
265
+ process.env.PATH = "";
266
+ process.env.Path = "";
267
+ try {
268
+ const client = new QmdClient("test-collection", 10, {
269
+ qmdPath: missingQmdPath,
270
+ qmdFallbackPaths: [],
271
+ });
272
+ (client as any).daemonAvailable = true;
273
+ (client as any).daemonSession = { isActive: () => false };
274
+ (client as any).lastDaemonCheckAtMs = Date.now();
275
+
276
+ assert.equal(await client.checkAvailability(), false);
277
+ assert.equal(client.isAvailable(), false);
278
+ assert.equal((client as any).daemonAvailable, false);
279
+ assert.equal((client as any).lastDaemonCheckAtMs, 0);
280
+ } finally {
281
+ if (originalPath === undefined) delete process.env.PATH;
282
+ else process.env.PATH = originalPath;
283
+ if (originalWindowsPath === undefined) delete process.env.Path;
284
+ else process.env.Path = originalWindowsPath;
285
+ }
286
+ });
287
+
186
288
  test("QmdClient applies chunk strategy to normal and forced embed args", async () => {
187
289
  const { QmdClient } = await import("./qmd.js");
188
290
  const client = new QmdClient("test", 5, {
package/src/qmd.ts CHANGED
@@ -1362,6 +1362,22 @@ export class QmdClient implements SearchBackend {
1362
1362
  return cliOk || this.daemonAvailable;
1363
1363
  }
1364
1364
 
1365
+ async checkAvailability(execution?: SearchExecutionOptions): Promise<boolean> {
1366
+ const cliAvailable = await this.probeCli({
1367
+ allowAutoUpgrade: false,
1368
+ preserveStateOnFailure: true,
1369
+ signal: execution?.signal,
1370
+ });
1371
+ if (this.daemonAvailable && this.daemonSession?.isActive()) {
1372
+ return true;
1373
+ }
1374
+ if (this.daemonAvailable) {
1375
+ this.daemonAvailable = false;
1376
+ this.lastDaemonCheckAtMs = 0;
1377
+ }
1378
+ return cliAvailable;
1379
+ }
1380
+
1365
1381
  private async probeDaemon(): Promise<boolean> {
1366
1382
  this.lastDaemonCheckAtMs = Date.now();
1367
1383
  const normalizedPath = this.qmdPath.trim() || "qmd";
@@ -1409,7 +1425,32 @@ export class QmdClient implements SearchBackend {
1409
1425
  }
1410
1426
  }
1411
1427
 
1412
- private async probeCli(): Promise<boolean> {
1428
+ private async probeCli(
1429
+ options: {
1430
+ allowAutoUpgrade?: boolean;
1431
+ preserveStateOnFailure?: boolean;
1432
+ signal?: AbortSignal;
1433
+ } = {},
1434
+ ): Promise<boolean> {
1435
+ const priorState = options.preserveStateOnFailure === true
1436
+ ? {
1437
+ available: this.available,
1438
+ qmdPath: this.qmdPath,
1439
+ qmdPathSource: this.qmdPathSource,
1440
+ cliVersion: this.cliVersion,
1441
+ lastCliProbeError: this.lastCliProbeError,
1442
+ qmdCapabilities: this.qmdCapabilities,
1443
+ }
1444
+ : null;
1445
+ const restorePriorState = (): void => {
1446
+ if (!priorState) return;
1447
+ this.available = priorState.available;
1448
+ this.qmdPath = priorState.qmdPath;
1449
+ this.qmdPathSource = priorState.qmdPathSource;
1450
+ this.cliVersion = priorState.cliVersion;
1451
+ this.lastCliProbeError = priorState.lastCliProbeError;
1452
+ this.qmdCapabilities = priorState.qmdCapabilities;
1453
+ };
1413
1454
  let configuredProbeFailure: string | null = null;
1414
1455
  const markProbeFailure = (err: unknown): void => {
1415
1456
  this.lastCliProbeError = err instanceof Error ? err.message : String(err);
@@ -1431,12 +1472,14 @@ export class QmdClient implements SearchBackend {
1431
1472
  this.cliVersion = parseQmdVersionOutput(result.stdout, result.stderr);
1432
1473
  this.qmdCapabilities = resolveQmdCapabilities(this.cliVersion);
1433
1474
  this.lastCliProbeError = null;
1434
- await this.maybeAutoUpgradeQmd();
1475
+ if (options.allowAutoUpgrade !== false) {
1476
+ await this.maybeAutoUpgradeQmd();
1477
+ }
1435
1478
  };
1436
1479
 
1437
1480
  if (this.configuredQmdPath) {
1438
1481
  try {
1439
- const result = await runQmd(["--version"], QMD_PROBE_TIMEOUT_MS, this.configuredQmdPath, undefined, this.qmdRuntimeEnv);
1482
+ const result = await runQmd(["--version"], QMD_PROBE_TIMEOUT_MS, this.configuredQmdPath, options.signal, this.qmdRuntimeEnv);
1440
1483
  await recordProbeSuccess(result, this.configuredQmdPath, "configured");
1441
1484
  return true;
1442
1485
  } catch (err) {
@@ -1452,7 +1495,7 @@ export class QmdClient implements SearchBackend {
1452
1495
 
1453
1496
  // Try PATH first
1454
1497
  try {
1455
- const result = await runQmd(["--version"], QMD_PROBE_TIMEOUT_MS, "qmd", undefined, this.qmdRuntimeEnv);
1498
+ const result = await runQmd(["--version"], QMD_PROBE_TIMEOUT_MS, "qmd", options.signal, this.qmdRuntimeEnv);
1456
1499
  await recordProbeSuccess(result, "qmd", "auto-path");
1457
1500
  return true;
1458
1501
  } catch (err) {
@@ -1460,7 +1503,7 @@ export class QmdClient implements SearchBackend {
1460
1503
  // Try fallback paths
1461
1504
  for (const fallbackPath of this.qmdFallbackPaths) {
1462
1505
  try {
1463
- const result = await runQmd(["--version"], QMD_PROBE_TIMEOUT_MS, fallbackPath, undefined, this.qmdRuntimeEnv);
1506
+ const result = await runQmd(["--version"], QMD_PROBE_TIMEOUT_MS, fallbackPath, options.signal, this.qmdRuntimeEnv);
1464
1507
  await recordProbeSuccess(result, fallbackPath, "auto-fallback");
1465
1508
  log.info(`QMD: found at ${fallbackPath}`);
1466
1509
  return true;
@@ -1469,8 +1512,12 @@ export class QmdClient implements SearchBackend {
1469
1512
  // Continue to next fallback
1470
1513
  }
1471
1514
  }
1472
- this.available = false;
1473
- restoreConfiguredProbeFailure();
1515
+ if (priorState) {
1516
+ restorePriorState();
1517
+ } else {
1518
+ this.available = false;
1519
+ restoreConfiguredProbeFailure();
1520
+ }
1474
1521
  return false;
1475
1522
  }
1476
1523
  }
@@ -1,9 +1,19 @@
1
1
  import { buildEvidencePack, type EvidencePackItem } from "./evidence-pack.js";
2
2
  import type { ExplicitCueRecallEngine } from "./explicit-cue-recall.js";
3
+ import {
4
+ gatherAcrossReadSessions,
5
+ resolveLcmReadSessionIds,
6
+ } from "./lcm-fallback-read.js";
3
7
 
4
8
  export interface ResponseGuidanceRecallOptions {
5
9
  engine: ExplicitCueRecallEngine | null | undefined;
6
10
  sessionId?: string;
11
+ /**
12
+ * Ordered, read-authorized LCM read key set (primary overlay → project/root
13
+ * fallbacks). When present, evidence is gathered across EVERY key and merged
14
+ * under this section's budget (#1505 codex P2). Falls back to `sessionId`.
15
+ */
16
+ sessionIds?: readonly (string | undefined)[];
7
17
  query: string;
8
18
  maxChars: number;
9
19
  maxItemChars?: number;
@@ -157,7 +167,17 @@ export async function buildResponseGuidanceRecallSection(
157
167
  ) {
158
168
  return "";
159
169
  }
160
- const items = await collectGuidanceItems(options, intents);
170
+ // #1505 codex P2: gather candidates across the ordered LCM read key set
171
+ // (primary overlay → project/root fallbacks) and UNION them into the existing
172
+ // rank/dedupe/budget pass, so stronger project-fallback guidance is not masked
173
+ // by a weak primary-key hit. `gatherAcrossReadSessions` isolates a per-key read
174
+ // failure so a corrupt/locked fallback index can't discard the primary key's
175
+ // guidance; the single-key path collects exactly once and propagates a failure
176
+ // as before — byte-for-byte the pre-#1505 behavior.
177
+ const items: EvidencePackItem[] = [];
178
+ await gatherAcrossReadSessions(resolveLcmReadSessionIds(options), async (sessionId) => {
179
+ items.push(...(await collectGuidanceItems({ ...options, sessionId }, intents)));
180
+ });
161
181
  const ranked = rankAndDedupeGuidanceItems(items, options.query, intents)
162
182
  .slice(0, maxResults);
163
183
  if (ranked.length === 0) {
@@ -42,6 +42,12 @@ export function resolveEnsureCollectionArgs(
42
42
  export interface SearchBackend {
43
43
  // ── Lifecycle ──
44
44
  probe(): Promise<boolean>;
45
+ /**
46
+ * Optional non-mutating availability probe for health/readiness checks.
47
+ * Implementations must avoid auto-upgrades, collection creation, daemon
48
+ * startup, or any other runtime-modifying side effects.
49
+ */
50
+ checkAvailability?(execution?: SearchExecutionOptions): Promise<boolean>;
45
51
  isAvailable(): boolean;
46
52
  debugStatus(): string;
47
53
 
@@ -4,10 +4,20 @@ import {
4
4
  type EvidencePackItem,
5
5
  } from "./evidence-pack.js";
6
6
  import type { ExplicitCueRecallEngine } from "./explicit-cue-recall.js";
7
+ import {
8
+ gatherAcrossReadSessions,
9
+ resolveLcmReadSessionIds,
10
+ } from "./lcm-fallback-read.js";
7
11
 
8
12
  export interface TargetedFactRecallOptions {
9
13
  engine: ExplicitCueRecallEngine | null | undefined;
10
14
  sessionId?: string;
15
+ /**
16
+ * Ordered, read-authorized LCM read key set (primary overlay → project/root
17
+ * fallbacks). When present, evidence is gathered across EVERY key and merged
18
+ * under this section's budget (#1505 codex P2). Falls back to `sessionId`.
19
+ */
20
+ sessionIds?: readonly (string | undefined)[];
11
21
  query: string;
12
22
  maxChars: number;
13
23
  maxItemChars?: number;
@@ -43,10 +53,21 @@ export async function buildTargetedFactRecallSection(
43
53
  return "";
44
54
  }
45
55
 
46
- const searchItems = await collectTargetedFactSearchItems(options);
47
- const scannedItems = await collectTargetedFactScanItems(options);
56
+ // #1505 codex P2: gather candidates across the ordered LCM read key set
57
+ // (primary overlay project/root fallbacks) and UNION them into the existing
58
+ // rank/dedupe/budget pass, so stronger project-fallback evidence is not masked
59
+ // by a weak primary-key hit. `gatherAcrossReadSessions` isolates a per-key read
60
+ // failure so a corrupt/locked fallback index can't discard the primary key's
61
+ // evidence; the single-key path collects exactly one search+scan pair and
62
+ // propagates a failure as before — byte-for-byte the pre-#1505 behavior.
63
+ const collected: EvidencePackItem[] = [];
64
+ await gatherAcrossReadSessions(resolveLcmReadSessionIds(options), async (sessionId) => {
65
+ const scoped = { ...options, sessionId };
66
+ collected.push(...(await collectTargetedFactSearchItems(scoped)));
67
+ collected.push(...(await collectTargetedFactScanItems(scoped)));
68
+ });
48
69
  const ranked = rankAndDedupeTargetedFactItems(
49
- [...searchItems, ...scannedItems],
70
+ collected,
50
71
  options.query,
51
72
  ).slice(0, maxResults);
52
73