@remnic/core 10.0.39 → 10.0.41

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 (199) hide show
  1. package/dist/access-admin-ops-surface.js +14 -14
  2. package/dist/access-authorization-probe.js +15 -15
  3. package/dist/access-boundary.js +14 -14
  4. package/dist/access-cli.js +53 -53
  5. package/dist/access-extraction-force-flush.js +14 -14
  6. package/dist/access-http-briefing.js +15 -15
  7. package/dist/access-http-query.js +16 -16
  8. package/dist/access-http.js +30 -30
  9. package/dist/access-identity-continuity-surface.js +14 -14
  10. package/dist/access-lcm-surface.js +14 -14
  11. package/dist/access-mcp.js +23 -23
  12. package/dist/access-namespace-preflight.js +14 -14
  13. package/dist/access-observe-write-surface.js +14 -14
  14. package/dist/access-operations-batch.js +15 -15
  15. package/dist/access-operations.js +22 -22
  16. package/dist/access-recall-concurrency.js +14 -14
  17. package/dist/access-recall-response.js +14 -14
  18. package/dist/access-recall-surface.js +14 -14
  19. package/dist/access-service.d.ts +2 -2
  20. package/dist/access-service.d.ts.map +1 -1
  21. package/dist/access-service.js +14 -14
  22. package/dist/access-surface-catalog.js +16 -16
  23. package/dist/active-recall.js +2 -2
  24. package/dist/briefing.js +3 -3
  25. package/dist/causal-consolidation.js +4 -4
  26. package/dist/{chunk-M7KGIW5F.js → chunk-4KS4IHKH.js} +2 -2
  27. package/dist/{chunk-JORSMFL6.js → chunk-574N53FL.js} +5 -5
  28. package/dist/{chunk-BUKZ7MTN.js → chunk-5TRU6QQC.js} +2 -2
  29. package/dist/{chunk-SRPGA6WN.js → chunk-7BCYAJKP.js} +2 -2
  30. package/dist/{chunk-MD3W2H3G.js → chunk-7G4O5EQT.js} +31 -31
  31. package/dist/{chunk-MD3W2H3G.js.map → chunk-7G4O5EQT.js.map} +1 -1
  32. package/dist/{chunk-F2IU6A2L.js → chunk-7XGWW3LQ.js} +16 -16
  33. package/dist/{chunk-ZXFKG7VZ.js → chunk-AOBDA3VC.js} +2 -2
  34. package/dist/{chunk-JRTKYFFY.js → chunk-AY6LO7GD.js} +2 -2
  35. package/dist/{chunk-5KZNWERU.js → chunk-BIYVUPC6.js} +19 -3
  36. package/dist/{chunk-5KZNWERU.js.map → chunk-BIYVUPC6.js.map} +1 -1
  37. package/dist/{chunk-V2XMG7GM.js → chunk-CHNZPOWX.js} +3 -2
  38. package/dist/{chunk-V2XMG7GM.js.map → chunk-CHNZPOWX.js.map} +1 -1
  39. package/dist/{chunk-GPVGAHXL.js → chunk-CNAL35D5.js} +2 -2
  40. package/dist/{chunk-NJ2DZYEK.js → chunk-DBM2NQBG.js} +2 -2
  41. package/dist/{chunk-U66CTIF5.js → chunk-F2RDRE7M.js} +2 -2
  42. package/dist/{chunk-VIS23B6O.js → chunk-F7MP76CB.js} +15 -13
  43. package/dist/chunk-F7MP76CB.js.map +1 -0
  44. package/dist/{chunk-B7VKRCAG.js → chunk-FNEYSTB6.js} +2 -2
  45. package/dist/{chunk-GSBSGT43.js → chunk-FPHU6KVI.js} +2 -2
  46. package/dist/{chunk-RO2NNWR5.js → chunk-GAUS4XGN.js} +3 -3
  47. package/dist/{chunk-4UT2NUAC.js → chunk-GCDIHTWF.js} +2 -2
  48. package/dist/{chunk-4HJ2QK32.js → chunk-GDOI4U4D.js} +2 -2
  49. package/dist/{chunk-VCOCOBUS.js → chunk-GZMHBRIR.js} +3 -3
  50. package/dist/{chunk-LJXBFVI2.js → chunk-ICTJOOWZ.js} +2 -2
  51. package/dist/{chunk-6Z2ZWBOV.js → chunk-IWDFHVPJ.js} +2 -2
  52. package/dist/{chunk-TTQYX7VU.js → chunk-J3FPMHQG.js} +2 -2
  53. package/dist/{chunk-GCW7UT5I.js → chunk-JJSMSS45.js} +2 -2
  54. package/dist/{chunk-O2O4FDAH.js → chunk-JXQTECMU.js} +2 -2
  55. package/dist/{chunk-ANV373S5.js → chunk-KYYN2FK4.js} +2 -2
  56. package/dist/{chunk-PJRKLB4Z.js → chunk-L5PM7B3I.js} +16 -2
  57. package/dist/{chunk-PJRKLB4Z.js.map → chunk-L5PM7B3I.js.map} +1 -1
  58. package/dist/{chunk-5TJEOU6Y.js → chunk-LKRXGOQW.js} +18 -8
  59. package/dist/{chunk-5TJEOU6Y.js.map → chunk-LKRXGOQW.js.map} +1 -1
  60. package/dist/{chunk-B3I2QQU7.js → chunk-LWIU5BMA.js} +3 -3
  61. package/dist/{chunk-WQYS47G4.js → chunk-N3PUGD7E.js} +2 -2
  62. package/dist/{chunk-EKR6OO4P.js → chunk-NTD3VWJ2.js} +2 -2
  63. package/dist/{chunk-JQCP5QW3.js → chunk-O67H7OXJ.js} +2 -2
  64. package/dist/{chunk-4LLV2N3R.js → chunk-ONIVKGNC.js} +2 -2
  65. package/dist/{chunk-GLB6VV3J.js → chunk-OUYYEPXD.js} +3 -3
  66. package/dist/{chunk-RWW5KPIY.js → chunk-PCZZMMBL.js} +12 -12
  67. package/dist/chunk-PCZZMMBL.js.map +1 -0
  68. package/dist/{chunk-WT2VVYBM.js → chunk-PJXSBXEC.js} +2 -2
  69. package/dist/{chunk-P3YUEPSQ.js → chunk-PUMP554C.js} +2 -2
  70. package/dist/{chunk-SJHFU52W.js → chunk-Q47WGGKI.js} +5 -5
  71. package/dist/chunk-Q47WGGKI.js.map +1 -0
  72. package/dist/{chunk-RIAKNXZB.js → chunk-Q77SLBWT.js} +3 -3
  73. package/dist/{chunk-22YJQTPN.js → chunk-QGNZ5QPV.js} +2 -2
  74. package/dist/{chunk-GE5ZNDLE.js → chunk-R42VUN5V.js} +3 -3
  75. package/dist/{chunk-7IANFJ24.js → chunk-RWMLDC2F.js} +17 -17
  76. package/dist/chunk-RWMLDC2F.js.map +1 -0
  77. package/dist/{chunk-LPD6I2U6.js → chunk-SLVR3U4E.js} +2 -2
  78. package/dist/{chunk-IAOW2L6K.js → chunk-TNTYAH7T.js} +2 -2
  79. package/dist/{chunk-CR72WZ3M.js → chunk-UBGFPD62.js} +4 -4
  80. package/dist/{chunk-CR72WZ3M.js.map → chunk-UBGFPD62.js.map} +1 -1
  81. package/dist/{chunk-JZFJZLJY.js → chunk-UGV256H3.js} +2 -2
  82. package/dist/{chunk-L74NWAD4.js → chunk-UKIIAEJF.js} +2 -2
  83. package/dist/{chunk-5Q6CR65T.js → chunk-VTZSIG6O.js} +2 -2
  84. package/dist/{chunk-IZRNMXZC.js → chunk-WCUYCTTG.js} +2 -2
  85. package/dist/{chunk-JT3IXJIL.js → chunk-XBHUMPIQ.js} +7 -7
  86. package/dist/{chunk-JM6U3SAE.js → chunk-XJ77IOAD.js} +2 -2
  87. package/dist/{chunk-ODJ2HRFW.js → chunk-XXBCUIYQ.js} +7 -7
  88. package/dist/{chunk-JOOGPZJE.js → chunk-XXW3P3RX.js} +2 -2
  89. package/dist/cli.js +40 -40
  90. package/dist/coding/export-okf-codegraph.js +2 -2
  91. package/dist/compounding/engine.js +4 -4
  92. package/dist/config.js +2 -2
  93. package/dist/connectors/codex-materialize-runner.js +3 -3
  94. package/dist/connectors/index.js +4 -4
  95. package/dist/convergence-refresh.js +5 -5
  96. package/dist/corpus-watermark.js +5 -5
  97. package/dist/entity-retrieval.js +3 -3
  98. package/dist/external-wiki-access.js +15 -15
  99. package/dist/extraction-error-classification.d.ts +15 -0
  100. package/dist/extraction-error-classification.d.ts.map +1 -1
  101. package/dist/extraction-error-classification.js +7 -3
  102. package/dist/extraction.js +8 -8
  103. package/dist/harmonic-retrieval.js +3 -3
  104. package/dist/index.js +53 -53
  105. package/dist/location/index.js +5 -5
  106. package/dist/maintenance/memory-governance.js +3 -3
  107. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
  108. package/dist/maintenance/rebuild-memory-projection-runner.js +5 -5
  109. package/dist/maintenance/rebuild-memory-projection.js +4 -4
  110. package/dist/memory-browse-operations.js +15 -15
  111. package/dist/namespaces/migrate.js +4 -4
  112. package/dist/namespaces/search.d.ts +1 -0
  113. package/dist/namespaces/search.d.ts.map +1 -1
  114. package/dist/namespaces/search.js +5 -3
  115. package/dist/namespaces/storage.js +3 -3
  116. package/dist/operator-doctor-corpus.js +6 -6
  117. package/dist/operator-toolkit.js +11 -11
  118. package/dist/orchestration/maintenance.js +13 -13
  119. package/dist/orchestration/qmd-result-resolver.d.ts +5 -0
  120. package/dist/orchestration/qmd-result-resolver.d.ts.map +1 -1
  121. package/dist/orchestration/recall-search-pipeline.d.ts.map +1 -1
  122. package/dist/orchestrator.js +53 -53
  123. package/dist/recall-navigation-operations.js +15 -15
  124. package/dist/recall-navigation.js +14 -14
  125. package/dist/resume-bundles.js +2 -2
  126. package/dist/runtime/daemon-health.d.ts.map +1 -1
  127. package/dist/semantic-consolidation.js +4 -4
  128. package/dist/semantic-rule-promotion.js +3 -3
  129. package/dist/semantic-rule-verifier.js +3 -3
  130. package/dist/source-agent-qualifier.js +6 -6
  131. package/dist/standup/build.js +3 -3
  132. package/dist/storage.js +2 -2
  133. package/dist/support-passport/index.js +17 -17
  134. package/dist/transfer/export-okf.js +3 -3
  135. package/dist/verified-recall.js +3 -3
  136. package/dist/who-knows-cli.js +4 -4
  137. package/dist/who-knows.js +3 -3
  138. package/package.json +3 -3
  139. package/src/access-entity-principal.test.ts +190 -0
  140. package/src/access-http.ts +2 -2
  141. package/src/access-operations-batch.ts +2 -2
  142. package/src/access-service.ts +7 -6
  143. package/src/chat/chat-executor.ts +1 -1
  144. package/src/extraction-error-classification.ts +23 -0
  145. package/src/extraction-local-failure-class.test.ts +55 -0
  146. package/src/extraction.ts +9 -9
  147. package/src/namespaces/search.ts +1 -1
  148. package/src/orchestration/extraction-run.test.ts +83 -2
  149. package/src/orchestration/qmd-result-resolver.ts +26 -1
  150. package/src/orchestration/recall-search-pipeline.ts +11 -12
  151. package/src/runtime/daemon-health.test.ts +85 -0
  152. package/src/runtime/daemon-health.ts +20 -7
  153. package/dist/chunk-7IANFJ24.js.map +0 -1
  154. package/dist/chunk-RWW5KPIY.js.map +0 -1
  155. package/dist/chunk-SJHFU52W.js.map +0 -1
  156. package/dist/chunk-VIS23B6O.js.map +0 -1
  157. /package/dist/{chunk-M7KGIW5F.js.map → chunk-4KS4IHKH.js.map} +0 -0
  158. /package/dist/{chunk-JORSMFL6.js.map → chunk-574N53FL.js.map} +0 -0
  159. /package/dist/{chunk-BUKZ7MTN.js.map → chunk-5TRU6QQC.js.map} +0 -0
  160. /package/dist/{chunk-SRPGA6WN.js.map → chunk-7BCYAJKP.js.map} +0 -0
  161. /package/dist/{chunk-F2IU6A2L.js.map → chunk-7XGWW3LQ.js.map} +0 -0
  162. /package/dist/{chunk-ZXFKG7VZ.js.map → chunk-AOBDA3VC.js.map} +0 -0
  163. /package/dist/{chunk-JRTKYFFY.js.map → chunk-AY6LO7GD.js.map} +0 -0
  164. /package/dist/{chunk-GPVGAHXL.js.map → chunk-CNAL35D5.js.map} +0 -0
  165. /package/dist/{chunk-NJ2DZYEK.js.map → chunk-DBM2NQBG.js.map} +0 -0
  166. /package/dist/{chunk-U66CTIF5.js.map → chunk-F2RDRE7M.js.map} +0 -0
  167. /package/dist/{chunk-B7VKRCAG.js.map → chunk-FNEYSTB6.js.map} +0 -0
  168. /package/dist/{chunk-GSBSGT43.js.map → chunk-FPHU6KVI.js.map} +0 -0
  169. /package/dist/{chunk-RO2NNWR5.js.map → chunk-GAUS4XGN.js.map} +0 -0
  170. /package/dist/{chunk-4UT2NUAC.js.map → chunk-GCDIHTWF.js.map} +0 -0
  171. /package/dist/{chunk-4HJ2QK32.js.map → chunk-GDOI4U4D.js.map} +0 -0
  172. /package/dist/{chunk-VCOCOBUS.js.map → chunk-GZMHBRIR.js.map} +0 -0
  173. /package/dist/{chunk-LJXBFVI2.js.map → chunk-ICTJOOWZ.js.map} +0 -0
  174. /package/dist/{chunk-6Z2ZWBOV.js.map → chunk-IWDFHVPJ.js.map} +0 -0
  175. /package/dist/{chunk-TTQYX7VU.js.map → chunk-J3FPMHQG.js.map} +0 -0
  176. /package/dist/{chunk-GCW7UT5I.js.map → chunk-JJSMSS45.js.map} +0 -0
  177. /package/dist/{chunk-O2O4FDAH.js.map → chunk-JXQTECMU.js.map} +0 -0
  178. /package/dist/{chunk-ANV373S5.js.map → chunk-KYYN2FK4.js.map} +0 -0
  179. /package/dist/{chunk-B3I2QQU7.js.map → chunk-LWIU5BMA.js.map} +0 -0
  180. /package/dist/{chunk-WQYS47G4.js.map → chunk-N3PUGD7E.js.map} +0 -0
  181. /package/dist/{chunk-EKR6OO4P.js.map → chunk-NTD3VWJ2.js.map} +0 -0
  182. /package/dist/{chunk-JQCP5QW3.js.map → chunk-O67H7OXJ.js.map} +0 -0
  183. /package/dist/{chunk-4LLV2N3R.js.map → chunk-ONIVKGNC.js.map} +0 -0
  184. /package/dist/{chunk-GLB6VV3J.js.map → chunk-OUYYEPXD.js.map} +0 -0
  185. /package/dist/{chunk-WT2VVYBM.js.map → chunk-PJXSBXEC.js.map} +0 -0
  186. /package/dist/{chunk-P3YUEPSQ.js.map → chunk-PUMP554C.js.map} +0 -0
  187. /package/dist/{chunk-RIAKNXZB.js.map → chunk-Q77SLBWT.js.map} +0 -0
  188. /package/dist/{chunk-22YJQTPN.js.map → chunk-QGNZ5QPV.js.map} +0 -0
  189. /package/dist/{chunk-GE5ZNDLE.js.map → chunk-R42VUN5V.js.map} +0 -0
  190. /package/dist/{chunk-LPD6I2U6.js.map → chunk-SLVR3U4E.js.map} +0 -0
  191. /package/dist/{chunk-IAOW2L6K.js.map → chunk-TNTYAH7T.js.map} +0 -0
  192. /package/dist/{chunk-JZFJZLJY.js.map → chunk-UGV256H3.js.map} +0 -0
  193. /package/dist/{chunk-L74NWAD4.js.map → chunk-UKIIAEJF.js.map} +0 -0
  194. /package/dist/{chunk-5Q6CR65T.js.map → chunk-VTZSIG6O.js.map} +0 -0
  195. /package/dist/{chunk-IZRNMXZC.js.map → chunk-WCUYCTTG.js.map} +0 -0
  196. /package/dist/{chunk-JT3IXJIL.js.map → chunk-XBHUMPIQ.js.map} +0 -0
  197. /package/dist/{chunk-JM6U3SAE.js.map → chunk-XJ77IOAD.js.map} +0 -0
  198. /package/dist/{chunk-ODJ2HRFW.js.map → chunk-XXBCUIYQ.js.map} +0 -0
  199. /package/dist/{chunk-JOOGPZJE.js.map → chunk-XXW3P3RX.js.map} +0 -0
@@ -27,7 +27,7 @@ import { inferIntentFromText, intentCompatibilityScore } from "../intent.js";
27
27
  import { log } from "../logger.js";
28
28
  import { NamespaceStorageRouter } from "../namespaces/storage.js";
29
29
  import { NegativeExampleStore } from "../negative.js";
30
- import { qmdCollectionPathParts } from "./qmd-result-resolver.js";
30
+ import { normalizeFlatQmdResults, qmdCollectionPathParts } from "./qmd-result-resolver.js";
31
31
  import type { RecallRerankCoordinator, RecallResultPartitionSink } from "./recall-rerank-coordinator.js";
32
32
  import type { GraphRecallExpansionOptions, GraphRecallExpansionResult } from "./graph-recall-coordinator.js";
33
33
  import { RelevanceStore } from "../relevance.js";
@@ -394,7 +394,7 @@ export class RecallSearchPipelineCoordinator {
394
394
  break;
395
395
  }
396
396
 
397
- const primaryResults = options.collection
397
+ let primaryResults = options.collection
398
398
  ? options.abortSignal
399
399
  ? await this.deps.qmd.search(
400
400
  prompt,
@@ -426,6 +426,7 @@ export class RecallSearchPipelineCoordinator {
426
426
  onDegradation: options.onDegradation,
427
427
  },
428
428
  });
429
+ primaryResults = normalizeFlatQmdResults(primaryResults, this.deps.config, options.collection);
429
430
  lastPrimaryResultCount = primaryResults.length;
430
431
  lastHybridResultCount = 0;
431
432
  lastHybridTopUpUsed = false;
@@ -452,7 +453,7 @@ export class RecallSearchPipelineCoordinator {
452
453
  // uniform across primary + top-up (gotcha #39). Issue #1335 (codex review #1422).
453
454
  lastHybridTopUpSkippedReason = "lex_strategy";
454
455
  } else {
455
- const hybridResults = options.collection
456
+ let hybridResults = options.collection
456
457
  ? await this.deps.qmd.hybridSearch(
457
458
  prompt,
458
459
  options.collection,
@@ -474,6 +475,7 @@ export class RecallSearchPipelineCoordinator {
474
475
  onDegradation: options.onDegradation,
475
476
  },
476
477
  });
478
+ hybridResults = normalizeFlatQmdResults(hybridResults, this.deps.config, options.collection);
477
479
  lastHybridResultCount = hybridResults.length;
478
480
  lastHybridTopUpUsed = hybridResults.length > 0;
479
481
  if (hybridResults.length > 0) {
@@ -740,9 +742,7 @@ export class RecallSearchPipelineCoordinator {
740
742
  // limps home later cannot mutate a recorded recall's collector.
741
743
  coldQmdObserverActive = false;
742
744
  if (longTerm.length > 0) {
743
- log.debug(
744
- `cold-tier recall source=cold-qmd collection=${coldCollection} hits=${longTerm.length}`,
745
- );
745
+ log.debug(`cold-tier recall source=cold-qmd collection=${coldCollection} hits=${longTerm.length}`);
746
746
  }
747
747
  }
748
748
  }
@@ -940,9 +940,7 @@ export class RecallSearchPipelineCoordinator {
940
940
  }
941
941
  }
942
942
  if (caps.rerank && this.deps.config.rerankProvider === "cloud") {
943
- log.debug(
944
- "rerankProvider=cloud is reserved/experimental in v2.2.0; skipping rerank",
945
- );
943
+ log.debug("rerankProvider=cloud is reserved/experimental in v2.2.0; skipping rerank");
946
944
  }
947
945
 
948
946
  if (caps.recallTrustScore && results.length > 0) {
@@ -1149,6 +1147,9 @@ export class RecallSearchPipelineCoordinator {
1149
1147
  : null;
1150
1148
  const buildRecallRow = (r: QmdSearchResult, memory: MemoryFile | undefined): QmdSearchResult => ({
1151
1149
  ...r,
1150
+ // Render loaded flat memories, not QMD's front-matter/chunk preview (#3194).
1151
+ snippet: memory && !resolveNamespaceCapabilities(this.deps.config).namespaces ? memory.content : r.snippet,
1152
+ line: memory && !resolveNamespaceCapabilities(this.deps.config).namespaces ? undefined : r.line,
1152
1153
  sourceConnector: memory ? memory.frontmatter.sourceConnector : undefined,
1153
1154
  origin: memory ? memory.frontmatter.origin : undefined,
1154
1155
  // #1952: an active state view carries the chain fields the inject
@@ -1358,9 +1359,7 @@ export class RecallSearchPipelineCoordinator {
1358
1359
  }
1359
1360
  }
1360
1361
  if (connectorPartitionFilteredCount > 0) {
1361
- log.debug(
1362
- `connector partition filter removed ${connectorPartitionFilteredCount} tool-scoped candidates from recall`,
1363
- );
1362
+ log.debug(`connector partition filter removed ${connectorPartitionFilteredCount} tool-scoped candidates from recall`);
1364
1363
  }
1365
1364
  if (supportPassportFilteredCount > 0) {
1366
1365
  log.debug(
@@ -0,0 +1,85 @@
1
+ import assert from "node:assert/strict";
2
+ import { once } from "node:events";
3
+ import test from "node:test";
4
+ import { Worker } from "node:worker_threads";
5
+ import { checkDaemonHealth } from "./daemon-health.js";
6
+
7
+ test("checkDaemonHealth probes /live without waiting on diagnostic /health", { timeout: 30_000 }, async (t) => {
8
+ // The synchronous probe blocks this thread, so HTTP must run on a worker.
9
+ const worker = new Worker(`
10
+ const { parentPort } = require("node:worker_threads");
11
+ const { createServer } = require("node:http");
12
+ const paths = [];
13
+ const server = createServer((request, response) => {
14
+ paths.push(request.url);
15
+ if (request.url === "/engram/v1/health") return;
16
+ response.writeHead(request.headers.authorization === "Bearer test-token" ? 200 : 401).end();
17
+ });
18
+ server.listen(0, "127.0.0.1", () => parentPort.postMessage(server.address().port));
19
+ parentPort.on("message", () => parentPort.postMessage(paths));
20
+ `, { eval: true });
21
+ t.after(() => worker.terminate());
22
+ let port = 0;
23
+ [port] = await once(worker, "message");
24
+ assert.ok(Number.isInteger(port) && port > 0 && port <= 65535);
25
+ assert.equal(checkDaemonHealth("127.0.0.1", port, "test-token"), true);
26
+ assert.equal(checkDaemonHealth("127.0.0.1", port, "wrong-token"), false);
27
+ const paths = once(worker, "message");
28
+ worker.postMessage("paths");
29
+ assert.deepEqual((await paths)[0], [
30
+ "/engram/v1/live",
31
+ "/engram/v1/live",
32
+ "/engram/v1/live",
33
+ ]);
34
+ await worker.terminate();
35
+ assert.equal(checkDaemonHealth("127.0.0.1", port, "test-token"), false);
36
+ });
37
+
38
+ test("checkDaemonHealth retries legacy /health only on 404 (#3257)", { timeout: 30_000 }, async (t) => {
39
+ const worker = new Worker(`
40
+ const { parentPort } = require("node:worker_threads");
41
+ const { createServer } = require("node:http");
42
+ const paths = [];
43
+ const server = createServer((request, response) => {
44
+ paths.push(request.url);
45
+ if (request.url === "/engram/v1/live") return response.writeHead(404).end();
46
+ response.writeHead(200).end();
47
+ });
48
+ server.listen(0, "127.0.0.1", () => parentPort.postMessage(server.address().port));
49
+ parentPort.on("message", () => parentPort.postMessage(paths));
50
+ `, { eval: true });
51
+ t.after(() => worker.terminate());
52
+ let port = 0;
53
+ [port] = await once(worker, "message");
54
+ assert.equal(checkDaemonHealth("127.0.0.1", port, "test-token"), true);
55
+ const paths = once(worker, "message");
56
+ worker.postMessage("paths");
57
+ assert.deepEqual((await paths)[0], [
58
+ "/engram/v1/live",
59
+ "/engram/v1/health",
60
+ ]);
61
+ await worker.terminate();
62
+ });
63
+
64
+ test("checkDaemonHealth treats 503 as final with no legacy retry (#3257)", { timeout: 30_000 }, async (t) => {
65
+ const worker = new Worker(`
66
+ const { parentPort } = require("node:worker_threads");
67
+ const { createServer } = require("node:http");
68
+ const paths = [];
69
+ const server = createServer((request, response) => {
70
+ paths.push(request.url);
71
+ if (request.url === "/engram/v1/live") return response.writeHead(503).end();
72
+ response.writeHead(200).end();
73
+ });
74
+ server.listen(0, "127.0.0.1", () => parentPort.postMessage(server.address().port));
75
+ parentPort.on("message", () => parentPort.postMessage(paths));
76
+ `, { eval: true });
77
+ t.after(() => worker.terminate());
78
+ let port = 0;
79
+ [port] = await once(worker, "message");
80
+ assert.equal(checkDaemonHealth("127.0.0.1", port, "test-token"), false);
81
+ const paths = once(worker, "message");
82
+ worker.postMessage("paths");
83
+ assert.deepEqual((await paths)[0], ["/engram/v1/live"]);
84
+ await worker.terminate();
85
+ });
@@ -16,6 +16,9 @@ export function checkDaemonHealth(
16
16
  if (!Number.isFinite(safePort) || safePort < 1 || safePort > 65535) return false;
17
17
 
18
18
  const bareHost = host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
19
+ // One 3 s aggregate budget: /engram/v1/live, and only a 404 (daemon older
20
+ // than the live route) retries the legacy diagnostic path with the
21
+ // remaining time. 401/503/other statuses are final answers, never re-probed.
19
22
  const script = [
20
23
  "const env = process['env'];",
21
24
  "const protocol = env.REMNIC_HEALTH_PROTOCOL;",
@@ -23,13 +26,23 @@ export function checkDaemonHealth(
23
26
  "const transport = require(protocol);",
24
27
  "const headers = {};",
25
28
  "if (env.REMNIC_HEALTH_TOKEN) headers['authorization'] = 'Bearer ' + env.REMNIC_HEALTH_TOKEN;",
26
- "const req = transport.get({",
27
- " host: env.REMNIC_HEALTH_HOST,",
28
- " port: parseInt(env.REMNIC_HEALTH_PORT, 10),",
29
- " path: '/engram/v1/health', headers, timeout: 3000,",
30
- "}, (res) => { process.exit(res.statusCode === 200 ? 0 : res.statusCode === 401 ? 2 : 1); });",
31
- "req.on('error', () => process.exit(1));",
32
- "req.on('timeout', () => { req.destroy(); process.exit(1); });",
29
+ "const exitFor = (status) => process.exit(status === 200 ? 0 : status === 401 ? 2 : 1);",
30
+ "const attempt = (path, timeoutMs, onStatus) => {",
31
+ " const req = transport.get({",
32
+ " host: env.REMNIC_HEALTH_HOST,",
33
+ " port: parseInt(env.REMNIC_HEALTH_PORT, 10),",
34
+ " path, headers, timeout: timeoutMs,",
35
+ " }, (res) => { res.resume(); onStatus(res.statusCode); });",
36
+ " req.on('error', () => process.exit(1));",
37
+ " req.on('timeout', () => { req.destroy(); process.exit(1); });",
38
+ "};",
39
+ "const deadline = Date.now() + 3000;",
40
+ "attempt('/engram/v1/live', 3000, (status) => {",
41
+ " if (status !== 404) exitFor(status);",
42
+ " const remaining = deadline - Date.now();",
43
+ " if (remaining <= 0) process.exit(1);",
44
+ " attempt('/engram/v1/health', remaining, exitFor);",
45
+ "});",
33
46
  ].join("\n");
34
47
  const env: NodeJS.ProcessEnv = mergeEnv({
35
48
  REMNIC_HEALTH_HOST: bareHost,