@jcode.labs/ragmir 2.17.2 → 2.18.1

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 (205) hide show
  1. package/README.md +53 -43
  2. package/dist/access-log.d.ts +10 -0
  3. package/dist/access-log.d.ts.map +1 -1
  4. package/dist/access-log.js +119 -49
  5. package/dist/access-log.js.map +1 -1
  6. package/dist/chunking.d.ts +5 -1
  7. package/dist/chunking.d.ts.map +1 -1
  8. package/dist/chunking.js +102 -16
  9. package/dist/chunking.js.map +1 -1
  10. package/dist/citation.d.ts +18 -0
  11. package/dist/citation.d.ts.map +1 -0
  12. package/dist/citation.js +54 -0
  13. package/dist/citation.js.map +1 -0
  14. package/dist/cli-entry.d.ts +3 -0
  15. package/dist/cli-entry.d.ts.map +1 -0
  16. package/dist/cli-entry.js +16 -0
  17. package/dist/cli-entry.js.map +1 -0
  18. package/dist/cli-fast.d.ts +13 -0
  19. package/dist/cli-fast.d.ts.map +1 -0
  20. package/dist/cli-fast.js +145 -0
  21. package/dist/cli-fast.js.map +1 -0
  22. package/dist/cli-options.d.ts +3 -0
  23. package/dist/cli-options.d.ts.map +1 -1
  24. package/dist/cli-options.js +7 -0
  25. package/dist/cli-options.js.map +1 -1
  26. package/dist/cli-stdin.d.ts +3 -0
  27. package/dist/cli-stdin.d.ts.map +1 -0
  28. package/dist/cli-stdin.js +15 -0
  29. package/dist/cli-stdin.js.map +1 -0
  30. package/dist/cli.js +251 -44
  31. package/dist/cli.js.map +1 -1
  32. package/dist/client.d.ts +13 -2
  33. package/dist/client.d.ts.map +1 -1
  34. package/dist/client.js +155 -10
  35. package/dist/client.js.map +1 -1
  36. package/dist/config.d.ts +6 -0
  37. package/dist/config.d.ts.map +1 -1
  38. package/dist/config.js +227 -49
  39. package/dist/config.js.map +1 -1
  40. package/dist/context-resources.d.ts +4 -2
  41. package/dist/context-resources.d.ts.map +1 -1
  42. package/dist/context-resources.js +55 -24
  43. package/dist/context-resources.js.map +1 -1
  44. package/dist/defaults.d.ts +27 -0
  45. package/dist/defaults.d.ts.map +1 -1
  46. package/dist/defaults.js +42 -2
  47. package/dist/defaults.js.map +1 -1
  48. package/dist/destroy.d.ts +2 -2
  49. package/dist/destroy.d.ts.map +1 -1
  50. package/dist/destroy.js +34 -20
  51. package/dist/destroy.js.map +1 -1
  52. package/dist/doctor.d.ts +3 -2
  53. package/dist/doctor.d.ts.map +1 -1
  54. package/dist/doctor.js +86 -41
  55. package/dist/doctor.js.map +1 -1
  56. package/dist/durable-file.d.ts +13 -0
  57. package/dist/durable-file.d.ts.map +1 -0
  58. package/dist/durable-file.js +56 -0
  59. package/dist/durable-file.js.map +1 -0
  60. package/dist/embeddings.d.ts +14 -3
  61. package/dist/embeddings.d.ts.map +1 -1
  62. package/dist/embeddings.js +233 -95
  63. package/dist/embeddings.js.map +1 -1
  64. package/dist/errors.d.ts +1 -1
  65. package/dist/errors.d.ts.map +1 -1
  66. package/dist/errors.js.map +1 -1
  67. package/dist/evaluate.d.ts +4 -3
  68. package/dist/evaluate.d.ts.map +1 -1
  69. package/dist/evaluate.js +611 -110
  70. package/dist/evaluate.js.map +1 -1
  71. package/dist/files.d.ts +4 -1
  72. package/dist/files.d.ts.map +1 -1
  73. package/dist/files.js +119 -33
  74. package/dist/files.js.map +1 -1
  75. package/dist/generation-retention.d.ts +52 -0
  76. package/dist/generation-retention.d.ts.map +1 -0
  77. package/dist/generation-retention.js +318 -0
  78. package/dist/generation-retention.js.map +1 -0
  79. package/dist/index-diagnostics.d.ts +3 -2
  80. package/dist/index-diagnostics.d.ts.map +1 -1
  81. package/dist/index-diagnostics.js +32 -3
  82. package/dist/index-diagnostics.js.map +1 -1
  83. package/dist/index-policy.d.ts.map +1 -1
  84. package/dist/index-policy.js +3 -0
  85. package/dist/index-policy.js.map +1 -1
  86. package/dist/index-write-lock.d.ts +16 -1
  87. package/dist/index-write-lock.d.ts.map +1 -1
  88. package/dist/index-write-lock.js +392 -13
  89. package/dist/index-write-lock.js.map +1 -1
  90. package/dist/index.d.ts +12 -4
  91. package/dist/index.d.ts.map +1 -1
  92. package/dist/index.js +5 -2
  93. package/dist/index.js.map +1 -1
  94. package/dist/ingest.d.ts +11 -2
  95. package/dist/ingest.d.ts.map +1 -1
  96. package/dist/ingest.js +680 -165
  97. package/dist/ingest.js.map +1 -1
  98. package/dist/ingestion-metrics.d.ts +58 -0
  99. package/dist/ingestion-metrics.d.ts.map +1 -0
  100. package/dist/ingestion-metrics.js +216 -0
  101. package/dist/ingestion-metrics.js.map +1 -0
  102. package/dist/ingestion-state.d.ts +11 -2
  103. package/dist/ingestion-state.d.ts.map +1 -1
  104. package/dist/ingestion-state.js +480 -49
  105. package/dist/ingestion-state.js.map +1 -1
  106. package/dist/limits.d.ts.map +1 -1
  107. package/dist/limits.js +11 -1
  108. package/dist/limits.js.map +1 -1
  109. package/dist/mcp-output.d.ts +44 -4
  110. package/dist/mcp-output.d.ts.map +1 -1
  111. package/dist/mcp-output.js +132 -159
  112. package/dist/mcp-output.js.map +1 -1
  113. package/dist/mcp-summaries.d.ts +32 -0
  114. package/dist/mcp-summaries.d.ts.map +1 -0
  115. package/dist/mcp-summaries.js +227 -0
  116. package/dist/mcp-summaries.js.map +1 -0
  117. package/dist/mcp.d.ts +1 -1
  118. package/dist/mcp.d.ts.map +1 -1
  119. package/dist/mcp.js +85 -46
  120. package/dist/mcp.js.map +1 -1
  121. package/dist/ocr-cache.d.ts +15 -0
  122. package/dist/ocr-cache.d.ts.map +1 -0
  123. package/dist/ocr-cache.js +90 -0
  124. package/dist/ocr-cache.js.map +1 -0
  125. package/dist/ocr.d.ts +31 -0
  126. package/dist/ocr.d.ts.map +1 -1
  127. package/dist/ocr.js +193 -17
  128. package/dist/ocr.js.map +1 -1
  129. package/dist/parsing.d.ts.map +1 -1
  130. package/dist/parsing.js +482 -85
  131. package/dist/parsing.js.map +1 -1
  132. package/dist/permissions.js +2 -2
  133. package/dist/permissions.js.map +1 -1
  134. package/dist/preview.d.ts.map +1 -1
  135. package/dist/preview.js +10 -11
  136. package/dist/preview.js.map +1 -1
  137. package/dist/quality-report.d.ts +12 -0
  138. package/dist/quality-report.d.ts.map +1 -0
  139. package/dist/quality-report.js +146 -0
  140. package/dist/quality-report.js.map +1 -0
  141. package/dist/query.d.ts +7 -4
  142. package/dist/query.d.ts.map +1 -1
  143. package/dist/query.js +371 -284
  144. package/dist/query.js.map +1 -1
  145. package/dist/ranking.d.ts +38 -0
  146. package/dist/ranking.d.ts.map +1 -0
  147. package/dist/ranking.js +278 -0
  148. package/dist/ranking.js.map +1 -0
  149. package/dist/redaction.d.ts +6 -1
  150. package/dist/redaction.d.ts.map +1 -1
  151. package/dist/redaction.js +118 -4
  152. package/dist/redaction.js.map +1 -1
  153. package/dist/research.d.ts +6 -1
  154. package/dist/research.d.ts.map +1 -1
  155. package/dist/research.js +330 -130
  156. package/dist/research.js.map +1 -1
  157. package/dist/runtime-info.d.ts +3 -0
  158. package/dist/runtime-info.d.ts.map +1 -0
  159. package/dist/runtime-info.js +81 -0
  160. package/dist/runtime-info.js.map +1 -0
  161. package/dist/security.d.ts +3 -2
  162. package/dist/security.d.ts.map +1 -1
  163. package/dist/security.js +242 -45
  164. package/dist/security.js.map +1 -1
  165. package/dist/semantic-config.d.ts +7 -1
  166. package/dist/semantic-config.d.ts.map +1 -1
  167. package/dist/semantic-config.js +30 -4
  168. package/dist/semantic-config.js.map +1 -1
  169. package/dist/setup.js +6 -3
  170. package/dist/setup.js.map +1 -1
  171. package/dist/skill.js +34 -12
  172. package/dist/skill.js.map +1 -1
  173. package/dist/source-fingerprint-cache.d.ts +20 -0
  174. package/dist/source-fingerprint-cache.d.ts.map +1 -0
  175. package/dist/source-fingerprint-cache.js +133 -0
  176. package/dist/source-fingerprint-cache.js.map +1 -0
  177. package/dist/storage-maintenance.d.ts +46 -0
  178. package/dist/storage-maintenance.d.ts.map +1 -0
  179. package/dist/storage-maintenance.js +329 -0
  180. package/dist/storage-maintenance.js.map +1 -0
  181. package/dist/store.d.ts +39 -2
  182. package/dist/store.d.ts.map +1 -1
  183. package/dist/store.js +559 -24
  184. package/dist/store.js.map +1 -1
  185. package/dist/text.d.ts +1 -1
  186. package/dist/text.d.ts.map +1 -1
  187. package/dist/text.js +12 -2
  188. package/dist/text.js.map +1 -1
  189. package/dist/types.d.ts +414 -5
  190. package/dist/types.d.ts.map +1 -1
  191. package/dist/vector-index.d.ts +42 -0
  192. package/dist/vector-index.d.ts.map +1 -0
  193. package/dist/vector-index.js +249 -0
  194. package/dist/vector-index.js.map +1 -0
  195. package/dist/version.d.ts +1 -1
  196. package/dist/version.js +1 -1
  197. package/dist/workload.d.ts +22 -0
  198. package/dist/workload.d.ts.map +1 -0
  199. package/dist/workload.js +181 -0
  200. package/dist/workload.js.map +1 -0
  201. package/examples/document-evidence-benchmark/README.md +25 -63
  202. package/examples/document-evidence-benchmark/golden-queries.json +1 -1
  203. package/examples/library-api-demo/README.md +17 -57
  204. package/examples/sovereign-rag-demo/README.md +37 -97
  205. package/package.json +36 -11
package/dist/cli.js CHANGED
@@ -3,7 +3,8 @@ import path from "node:path";
3
3
  import { Command } from "commander";
4
4
  import pc from "picocolors";
5
5
  import { accessLogUsageReport } from "./access-log.js";
6
- import { audioAllowRemoteModels, audioEngine, audioLanguage, parseAgentInstallMode, parseAgentInstallScope, parseNonNegativeInt, parseNumber, parsePositiveInt, parseRecallThreshold, } from "./cli-options.js";
6
+ import { audioAllowRemoteModels, audioEngine, audioLanguage, parseAgentInstallMode, parseAgentInstallScope, parseIncrementalFailurePolicy, parseNonNegativeInt, parseNumber, parsePositiveInt, parseRecallThreshold, } from "./cli-options.js";
7
+ import { readBoundedStdin } from "./cli-stdin.js";
7
8
  import { loadConfig } from "./config.js";
8
9
  import { DEFAULT_SKILL_TARGET_DIR } from "./defaults.js";
9
10
  import { destroyIndex } from "./destroy.js";
@@ -11,14 +12,15 @@ import { doctor } from "./doctor.js";
11
12
  import { pullEmbeddingModel } from "./embeddings.js";
12
13
  import { evaluateGoldenQueries } from "./evaluate.js";
13
14
  import { countSkippedByReason } from "./files.js";
14
- import { getIndexFreshnessWarning, getLexicalScanWarning } from "./index-diagnostics.js";
15
+ import { collectGenerationGarbage } from "./generation-retention.js";
16
+ import { indexFreshnessWarning } from "./index-diagnostics.js";
15
17
  import { audit, ingest } from "./ingest.js";
16
18
  import { getIngestionProgress } from "./ingestion-state.js";
17
19
  import { initProject } from "./init.js";
18
20
  import { discoverKnowledgeBases, knowledgeBaseIdentity } from "./knowledge-bases.js";
19
21
  import { ingestionLimits } from "./limits.js";
20
22
  import { serveMcp } from "./mcp.js";
21
- import { configurePdfOcr, extractPdfPage, inspectPdfOcr, parsePdfOcrEngine } from "./ocr.js";
23
+ import { configurePdfOcr, extractPdfPage, extractPdfPages, inspectPdfOcr, parsePdfOcrEngine, parsePdfOcrPages, } from "./ocr.js";
22
24
  import { rgrCommand } from "./package-manager.js";
23
25
  import { previewChunks } from "./preview.js";
24
26
  import { routePrompt } from "./prompt-routing.js";
@@ -29,7 +31,8 @@ import { enableSemanticEmbeddings } from "./semantic-config.js";
29
31
  import { setupProject } from "./setup.js";
30
32
  import { bundledSkillPath, installAgentSkills, installSkill, parseAgentTargets, SUPPORTED_AGENT_TARGETS, } from "./skill.js";
31
33
  import { addSourceEntries, listSourceEntries } from "./sources.js";
32
- import { countRows } from "./store.js";
34
+ import { optimizeStorage } from "./storage-maintenance.js";
35
+ import { readIndexManifestHeader } from "./store.js";
33
36
  import { VERSION } from "./version.js";
34
37
  const SEARCH_TEXT_PREVIEW_LENGTH = 900;
35
38
  const CHAT_PACKAGE_NAME = "@jcode.labs/ragmir-chat";
@@ -56,13 +59,20 @@ modelsCommand
56
59
  const cwd = projectRoot(command);
57
60
  const config = await loadConfig(cwd);
58
61
  const result = await pullEmbeddingModel(config);
59
- const semanticConfig = options.enable ? await enableSemanticEmbeddings(cwd) : null;
62
+ const semanticConfig = options.enable
63
+ ? await enableSemanticEmbeddings(cwd, {
64
+ embeddingModelRevision: result.embeddingModelRevision,
65
+ embeddingModelDigest: result.embeddingModelDigest,
66
+ })
67
+ : null;
60
68
  if (options.json) {
61
69
  console.log(JSON.stringify(semanticConfig ? { ...result, semanticConfig } : result, null, 2));
62
70
  return;
63
71
  }
64
72
  console.log(pc.green("Embedding model ready."));
65
73
  console.log(`embeddingModel=${result.embeddingModel}`);
74
+ console.log(`embeddingModelRevision=${result.embeddingModelRevision}`);
75
+ console.log(`embeddingModelDigest=${result.embeddingModelDigest}`);
66
76
  console.log(`embeddingModelPath=${result.embeddingModelPath}`);
67
77
  if (semanticConfig) {
68
78
  console.log(`semanticConfig=${semanticConfig.configPath}`);
@@ -97,10 +107,10 @@ ocrCommand
97
107
  });
98
108
  ocrCommand
99
109
  .command("setup")
100
- .description("Detect a local PDF OCR engine and write a safe page-aware configuration.")
110
+ .description("Detect a local PDF OCR engine and write a safe batched-page configuration.")
101
111
  .option("--engine <engine>", "OCR engine: auto, ocrmypdf, or tesseract.", "auto")
102
112
  .option("--language <codes>", "Tesseract language codes such as eng, fra, or eng+fra.", "eng")
103
- .option("--timeout-ms <number>", "Per-page OCR timeout in milliseconds.", parsePositiveInt)
113
+ .option("--timeout-ms <number>", "Per-batch OCR timeout in milliseconds.", parsePositiveInt)
104
114
  .option("--json", "Print machine-readable JSON.")
105
115
  .action(async (options, command) => {
106
116
  const configureOptions = {
@@ -141,10 +151,28 @@ ocrCommand
141
151
  addOption(extractOptions, "timeoutMs", options.timeoutMs);
142
152
  process.stdout.write(await extractPdfPage(extractOptions));
143
153
  });
154
+ ocrCommand
155
+ .command("extract-pages", { hidden: true })
156
+ .requiredOption("--engine <engine>", "OCR engine: ocrmypdf or tesseract.")
157
+ .requiredOption("--language <codes>", "Tesseract language codes.")
158
+ .requiredOption("--input <path>", "PDF file to process.")
159
+ .requiredOption("--pages <numbers>", "Comma-separated one-based PDF page numbers.")
160
+ .option("--timeout-ms <number>", "OCR timeout in milliseconds.", parsePositiveInt)
161
+ .action(async (options, command) => {
162
+ const extractOptions = {
163
+ engine: parsePdfOcrEngine(options.engine),
164
+ language: options.language,
165
+ input: path.resolve(projectRoot(command), options.input),
166
+ pages: parsePdfOcrPages(options.pages),
167
+ };
168
+ addOption(extractOptions, "timeoutMs", options.timeoutMs);
169
+ process.stdout.write(JSON.stringify(await extractPdfPages(extractOptions)));
170
+ });
144
171
  program
145
172
  .command("doctor")
146
173
  .description("Diagnose setup, index freshness, privacy posture, and next steps.")
147
174
  .option("--fix", "Create missing scaffolding, install the agent kit, and rebuild stale indexes.")
175
+ .option("--deep", "Run the O(corpus) source inventory and executable security probes.")
148
176
  .option("--json", "Print machine-readable JSON.")
149
177
  .action(async (options, command) => {
150
178
  const cwd = projectRoot(command);
@@ -157,7 +185,7 @@ program
157
185
  printSetup(result, "Ragmir repair complete.");
158
186
  return;
159
187
  }
160
- const report = await doctor(cwd);
188
+ const report = await doctor(cwd, { deep: options.deep === true });
161
189
  if (options.json) {
162
190
  console.log(JSON.stringify(report, null, 2));
163
191
  return;
@@ -273,12 +301,16 @@ program
273
301
  .description("Parse changed documents, redact, chunk, embed locally, and update LanceDB.")
274
302
  .option("--rebuild", "Force a full local index rebuild instead of reusing unchanged rows.")
275
303
  .option("--batch-size <number>", "Files committed per resumable batch.", parsePositiveInt)
304
+ .option("--incremental-failure-policy <policy>", "Preserve last-good rows after a file failure, or remove stale rows.", parseIncrementalFailurePolicy)
305
+ .option("--metrics", "Collect privacy-safe phase and throughput metrics.")
276
306
  .option("--json", "Print machine-readable JSON.")
277
307
  .action(async (options, command) => {
278
308
  const cwd = projectRoot(command);
279
309
  const ingestOptions = { cwd };
280
310
  addOption(ingestOptions, "rebuild", options.rebuild);
281
311
  addOption(ingestOptions, "batchSize", options.batchSize);
312
+ addOption(ingestOptions, "incrementalFailurePolicy", options.incrementalFailurePolicy);
313
+ addOption(ingestOptions, "collectMetrics", options.metrics);
282
314
  const result = await ingest(ingestOptions);
283
315
  if (options.json) {
284
316
  console.log(JSON.stringify(result, null, 2));
@@ -287,7 +319,7 @@ program
287
319
  }
288
320
  return;
289
321
  }
290
- console.log(pc.green(`Done. runId=${result.runId} resumed=${result.resumed} batchSize=${result.batchSize} discoveredFiles=${result.discoveredFiles} supportedFiles=${result.supportedFiles} supportedBytes=${result.supportedBytes} largestFileBytes=${result.largestFileBytes} indexedFiles=${result.indexedFiles} rebuiltFiles=${result.rebuiltFiles} reusedFiles=${result.reusedFiles} chunks=${result.chunks} skippedFiles=${result.skippedFiles} unsupportedFiles=${result.unsupportedFiles} oversizedFiles=${result.oversizedFiles} sensitiveFiles=${result.sensitiveFiles} emptyTextFiles=${result.emptyTextFiles.length} redactions=${result.redactions} errors=${result.errors.length}`));
322
+ console.log(pc.green(`Done. runId=${result.runId} resumed=${result.resumed} batchSize=${result.batchSize} discoveredFiles=${result.discoveredFiles} supportedFiles=${result.supportedFiles} supportedBytes=${result.supportedBytes} largestFileBytes=${result.largestFileBytes} indexedFiles=${result.indexedFiles} rebuiltFiles=${result.rebuiltFiles} reusedFiles=${result.reusedFiles} staleLastKnownGood=${result.staleLastKnownGood.length} chunks=${result.chunks} skippedFiles=${result.skippedFiles} unsupportedFiles=${result.unsupportedFiles} oversizedFiles=${result.oversizedFiles} sensitiveFiles=${result.sensitiveFiles} emptyTextFiles=${result.emptyTextFiles.length} redactions=${result.redactions} ocrPages=${result.ocr.pages} ocrCacheHits=${result.ocr.cacheHits} ocrBatches=${result.ocr.batches} ocrSubprocesses=${result.ocr.subprocesses} ocrDurationMs=${result.ocr.durationMs} errors=${result.errors.length}`));
291
323
  printUnsupportedSummary(result.unsupportedExtensions);
292
324
  printEmptyTextFiles(result.emptyTextFiles);
293
325
  if (result.vectorIndexWarning) {
@@ -296,6 +328,9 @@ program
296
328
  if (result.lexicalIndexWarning) {
297
329
  console.log(pc.yellow(result.lexicalIndexWarning));
298
330
  }
331
+ if (result.storageWarning) {
332
+ console.log(pc.yellow(result.storageWarning));
333
+ }
299
334
  if (result.unsupportedFiles > 0 || result.oversizedFiles > 0 || result.sensitiveFiles > 0) {
300
335
  const auditCommand = await rgrCommand(cwd, ["audit", "--unsupported"]);
301
336
  console.log(pc.yellow(`Some files were not indexed. Run \`${auditCommand.display}\` for details.`));
@@ -340,6 +375,9 @@ program
340
375
  }
341
376
  for (const file of report.files) {
342
377
  console.log(`\n${pc.cyan(file.relativePath)} chunks=${file.chunkStats.count} redactions=${file.redactions} minChars=${file.chunkStats.minChars} p50Chars=${file.chunkStats.p50Chars} p95Chars=${file.chunkStats.p95Chars} maxChars=${file.chunkStats.maxChars} contextualRatio=${file.chunkStats.contextualRatio.toFixed(3)}`);
378
+ if (file.ocr) {
379
+ console.log(`ocrPages=${file.ocr.pages} ocrCacheHits=${file.ocr.cacheHits} ocrBatches=${file.ocr.batches} ocrSubprocesses=${file.ocr.subprocesses} ocrDurationMs=${file.ocr.durationMs}`);
380
+ }
343
381
  for (const chunk of file.chunks) {
344
382
  const context = chunk.contextPath ? ` context=${chunk.contextPath}` : "";
345
383
  console.log(`\n${pc.dim(chunk.citation)}${context}`);
@@ -367,6 +405,7 @@ program
367
405
  .option("--exclude-path <prefix>", "Exclude indexed source paths under this prefix. Repeat for multiple prefixes.", collectOptionValue, [])
368
406
  .option("--context-path <prefix>", "Search only chunks under this structural context. Repeat for multiple prefixes.", collectOptionValue, [])
369
407
  .option("--explain", "Include hybrid score contributions, ranks, and matched terms.")
408
+ .option("--exact-vector-search", "Bypass ANN and use exhaustive vector search for diagnostics.")
370
409
  .option("--compact", "Return short snippets instead of full passages.")
371
410
  .option("--json", "Print machine-readable JSON.")
372
411
  .action(async (query, options, command) => {
@@ -386,7 +425,6 @@ program
386
425
  process.exitCode = 1;
387
426
  return;
388
427
  }
389
- await printStaleIndexWarnings(cwd);
390
428
  for (const [index, result] of outputResults.entries()) {
391
429
  const distance = result.distance === null ? "n/a" : result.distance.toFixed(4);
392
430
  console.log(`\n${pc.cyan(`[${index + 1}] ${result.citation}`)} chunk=${result.chunkIndex} distance=${distance}`);
@@ -408,6 +446,7 @@ program
408
446
  .option("--exclude-path <prefix>", "Exclude indexed source paths under this prefix. Repeat for multiple prefixes.", collectOptionValue, [])
409
447
  .option("--context-path <prefix>", "Use only chunks under this structural context. Repeat for multiple prefixes.", collectOptionValue, [])
410
448
  .option("--explain", "Include hybrid score contributions, ranks, and matched terms.")
449
+ .option("--exact-vector-search", "Bypass ANN and use exhaustive vector search for diagnostics.")
411
450
  .option("--json", "Print machine-readable JSON.")
412
451
  .action(async (query, options, command) => {
413
452
  const cwd = projectRoot(command);
@@ -433,10 +472,16 @@ program
433
472
  });
434
473
  program
435
474
  .command("research")
436
- .description("Run an audit-backed multi-query research pass with cited evidence.")
475
+ .description("Run a bounded multi-query research pass with cited evidence.")
437
476
  .argument("<query>", "Research question or topic.")
438
477
  .option("-k, --top-k <number>", "Maximum number of evidence passages to keep.", parsePositiveInt)
439
478
  .option("--no-code", "Skip the lightweight repository code search.")
479
+ .option("--full-audit", "Run a full source inventory instead of manifest health checks.")
480
+ .option("--timeout-ms <number>", "Bound the complete research operation.", parsePositiveInt)
481
+ .option("--code-top-k <number>", "Maximum number of code matches to keep.", parsePositiveInt)
482
+ .option("--code-scan-max-files <number>", "Maximum code files to read.", parsePositiveInt)
483
+ .option("--code-scan-max-bytes <number>", "Maximum total code bytes to read.", parsePositiveInt)
484
+ .option("--code-scan-concurrency <number>", "Maximum concurrent code file reads.", parsePositiveInt)
440
485
  .option("--include-path <prefix>", "Use only indexed source paths under this prefix. Repeat for multiple prefixes.", collectOptionValue, [])
441
486
  .option("--exclude-path <prefix>", "Exclude indexed source paths under this prefix. Repeat for multiple prefixes.", collectOptionValue, [])
442
487
  .option("--context-path <prefix>", "Use only chunks under this structural context. Repeat for multiple prefixes.", collectOptionValue, [])
@@ -447,6 +492,12 @@ program
447
492
  const researchOptions = { cwd };
448
493
  addOption(researchOptions, "topK", options.topK);
449
494
  addOption(researchOptions, "includeCode", options.code);
495
+ addOption(researchOptions, "fullAudit", options.fullAudit);
496
+ addOption(researchOptions, "timeoutMs", options.timeoutMs);
497
+ addOption(researchOptions, "codeTopK", options.codeTopK);
498
+ addOption(researchOptions, "codeScanMaxFiles", options.codeScanMaxFiles);
499
+ addOption(researchOptions, "codeScanMaxBytes", options.codeScanMaxBytes);
500
+ addOption(researchOptions, "codeScanConcurrency", options.codeScanConcurrency);
450
501
  addPathFilters(researchOptions, options);
451
502
  const report = await research(query, researchOptions);
452
503
  const output = options.compact ? compactResearchReport(report) : report;
@@ -492,10 +543,10 @@ program
492
543
  });
493
544
  program
494
545
  .command("evaluate")
495
- .description("Measure retrieval recall against a JSON golden query file.")
546
+ .description("Measure retrieval quality against a JSON golden query file.")
496
547
  .requiredOption("--golden <path>", "JSON file with queries and expected paths or exact path:Lx-Ly#chunk citations.")
497
548
  .option("-k, --top-k <number>", "Default number of passages to evaluate per query.", parsePositiveInt)
498
- .option("--fail-under <recall>", "Exit non-zero only when recall is below this threshold from 0 to 1.", parseRecallThreshold)
549
+ .option("--fail-under <recall>", "Legacy average-recall gate from 0 to 1, combined with golden-file quality gates.", parseRecallThreshold)
499
550
  .option("--json", "Print machine-readable JSON.")
500
551
  .action(async (options, command) => {
501
552
  const cwd = projectRoot(command);
@@ -506,9 +557,12 @@ program
506
557
  addOption(evaluationOptions, "topK", options.topK);
507
558
  const result = await evaluateGoldenQueries(evaluationOptions);
508
559
  const minimumRecall = options.failUnder;
509
- const passed = minimumRecall === undefined || result.recall >= minimumRecall;
560
+ const legacyRecallPassed = minimumRecall === undefined || result.recall >= minimumRecall;
561
+ const passed = result.passed && legacyRecallPassed;
510
562
  if (options.json) {
511
- const payload = minimumRecall === undefined ? result : { ...result, minimumRecall, passed };
563
+ const payload = minimumRecall === undefined
564
+ ? result
565
+ : { ...result, minimumRecall, legacyRecallPassed, passed };
512
566
  console.log(JSON.stringify(payload, null, 2));
513
567
  if (!passed) {
514
568
  process.exitCode = 1;
@@ -517,8 +571,11 @@ program
517
571
  }
518
572
  const thresholdSummary = minimumRecall === undefined
519
573
  ? ""
520
- : ` minimumRecall=${minimumRecall.toFixed(3)} passed=${passed}`;
521
- console.log(`golden=${result.goldenPath} total=${result.total} hits=${result.hits} misses=${result.misses} hitRate=${result.hitRate.toFixed(3)} recall=${result.recall.toFixed(3)} precision=${result.precision.toFixed(3)} mrr=${result.meanReciprocalRank.toFixed(3)} ndcg=${result.ndcg.toFixed(3)} p50Ms=${result.p50LatencyMs.toFixed(1)} p95Ms=${result.p95LatencyMs.toFixed(1)}${thresholdSummary}`);
574
+ : ` minimumRecall=${minimumRecall.toFixed(3)} legacyRecallPassed=${legacyRecallPassed}`;
575
+ console.log(`golden=${result.goldenPath} total=${result.total} hits=${result.hits} misses=${result.misses} hitRate=${result.hitRate.toFixed(3)} recallAt1=${result.recallAt[1].toFixed(3)} recallAt3=${result.recallAt[3].toFixed(3)} recallAt5=${result.recallAt[5].toFixed(3)} recallAt10=${result.recallAt[10].toFixed(3)} precisionAt5=${result.precisionAt5.toFixed(3)} mrrAt10=${result.meanReciprocalRankAt10.toFixed(3)} ndcgAt10=${result.ndcgAt10.toFixed(3)} exactCitationRate=${result.exactCitationRate?.toFixed(3) ?? "n/a"} falsePositiveRate=${result.falsePositiveRate?.toFixed(3) ?? "n/a"} passed=${passed} p50Ms=${result.p50LatencyMs.toFixed(1)} p95Ms=${result.p95LatencyMs.toFixed(1)}${thresholdSummary}`);
576
+ for (const gate of result.gates.filter((candidate) => !candidate.passed)) {
577
+ console.log(pc.red(`failedGate=${gate.metric} direction=${gate.direction} threshold=${gate.threshold.toFixed(3)} actual=${gate.actual?.toFixed(3) ?? "n/a"}`));
578
+ }
522
579
  for (const testCase of result.cases) {
523
580
  const label = testCase.id ? `${testCase.id}: ${testCase.query}` : testCase.query;
524
581
  const status = testCase.hit ? pc.green("hit") : pc.red("miss");
@@ -541,7 +598,7 @@ program
541
598
  });
542
599
  program
543
600
  .command("audit")
544
- .description("Compare supported files on disk with the current vector index.")
601
+ .description("Run a deep O(corpus) comparison of source files and the current vector index.")
545
602
  .option("--unsupported", "List skipped file paths and reasons.")
546
603
  .option("--json", "Print machine-readable JSON.")
547
604
  .action(async (options, command) => {
@@ -551,6 +608,9 @@ program
551
608
  console.log(JSON.stringify(report, null, 2));
552
609
  return;
553
610
  }
611
+ console.log(`mode=${report.mode}`);
612
+ console.log(`cost=${report.cost}`);
613
+ console.log(`inventoryVerified=${report.inventoryVerified}`);
554
614
  console.log(`discoveredFiles=${report.discoveredFiles}`);
555
615
  console.log(`supportedFiles=${report.supportedFiles.length}`);
556
616
  console.log(`supportedBytes=${report.supportedBytes}`);
@@ -641,8 +701,16 @@ program
641
701
  return;
642
702
  }
643
703
  console.log(`maxFileBytes=${report.maxFileBytes}`);
704
+ console.log(`hardMaxFileBytes=${report.hardMaxFileBytes}`);
644
705
  console.log(`maxFiles=${report.maxFiles ?? "unbounded"}`);
645
706
  console.log(`maxCorpusBytes=${report.maxCorpusBytes ?? "unbounded"}`);
707
+ console.log(`maxFileBatchSize=${report.maxFileBatchSize}`);
708
+ console.log(`maxIngestConcurrency=${report.maxIngestConcurrency}`);
709
+ console.log(`maxEmbeddingBatchSize=${report.maxEmbeddingBatchSize}`);
710
+ console.log(`maxSourceWindowBytes=${report.maxSourceWindowBytes}`);
711
+ console.log(`maxChunkWindow=${report.maxChunkWindow}`);
712
+ console.log(`maxChunksPerFile=${report.maxChunksPerFile}`);
713
+ console.log(`maxVectorBytesPerFile=${report.maxVectorBytesPerFile}`);
646
714
  console.log(`maxPdfPages=${report.maxPdfPages}`);
647
715
  console.log(`maxPdfTextCharacters=${report.maxPdfTextCharacters}`);
648
716
  console.log(`maxOfficeTextEntries=${report.maxOfficeTextEntries}`);
@@ -674,13 +742,24 @@ program
674
742
  });
675
743
  program
676
744
  .command("status")
677
- .description("Show active configuration and index row count.")
745
+ .description("Show active configuration, readiness, corpus fingerprint, and index counts.")
678
746
  .option("--json", "Print machine-readable JSON.")
679
747
  .action(async (options, command) => {
680
748
  const cwd = projectRoot(command);
681
749
  const config = await loadConfig(cwd);
682
750
  const identity = knowledgeBaseIdentity(config.projectRoot);
683
- const rows = await countRows(config);
751
+ const manifest = await readIndexManifestHeader(config);
752
+ const health = manifest?.health;
753
+ const freshnessWarning = indexFreshnessWarning(config, manifest);
754
+ const ready = manifest !== null &&
755
+ health !== undefined &&
756
+ manifest.chunkCount > 0 &&
757
+ health.missingFromIndex === 0 &&
758
+ health.staleInIndex === 0 &&
759
+ health.emptyTextFiles === 0 &&
760
+ health.oversizedFiles === 0 &&
761
+ health.securityWarnings.length === 0 &&
762
+ freshnessWarning === null;
684
763
  const ingestion = await getIngestionProgress(config);
685
764
  const status = {
686
765
  knowledgeBaseId: identity?.id ?? null,
@@ -703,6 +782,7 @@ program
703
782
  maxFileBytes: config.maxFileBytes,
704
783
  ingestConcurrency: config.ingestConcurrency,
705
784
  embeddingBatchSize: config.embeddingBatchSize,
785
+ incrementalFailurePolicy: config.incrementalFailurePolicy,
706
786
  includeExtensions: config.includeExtensions,
707
787
  pdfOcrCommand: config.pdfOcrCommand,
708
788
  pdfOcrTimeoutMs: config.pdfOcrTimeoutMs,
@@ -710,7 +790,14 @@ program
710
790
  imageOcrTimeoutMs: config.imageOcrTimeoutMs,
711
791
  legacyWordCommand: config.legacyWordCommand,
712
792
  legacyWordTimeoutMs: config.legacyWordTimeoutMs,
713
- chunksIndexed: rows,
793
+ manifestFound: manifest !== null,
794
+ ready,
795
+ corpusFingerprint: manifest?.corpusFingerprint ?? null,
796
+ indexFreshnessWarning: freshnessWarning,
797
+ indexedFiles: manifest?.fileCount ?? 0,
798
+ chunksIndexed: manifest?.chunkCount ?? 0,
799
+ health: health ?? null,
800
+ maintenance: manifest?.maintenance ?? null,
714
801
  ingestion,
715
802
  };
716
803
  if (options.json) {
@@ -737,6 +824,7 @@ program
737
824
  console.log(`maxFileBytes=${config.maxFileBytes}`);
738
825
  console.log(`ingestConcurrency=${config.ingestConcurrency}`);
739
826
  console.log(`embeddingBatchSize=${config.embeddingBatchSize}`);
827
+ console.log(`incrementalFailurePolicy=${config.incrementalFailurePolicy}`);
740
828
  console.log(`includeExtensions=${config.includeExtensions.join(",")}`);
741
829
  console.log(`pdfOcrCommand=${config.pdfOcrCommand.join(" ")}`);
742
830
  console.log(`pdfOcrTimeoutMs=${config.pdfOcrTimeoutMs}`);
@@ -744,14 +832,21 @@ program
744
832
  console.log(`imageOcrTimeoutMs=${config.imageOcrTimeoutMs}`);
745
833
  console.log(`legacyWordCommand=${config.legacyWordCommand.join(" ")}`);
746
834
  console.log(`legacyWordTimeoutMs=${config.legacyWordTimeoutMs}`);
747
- console.log(`chunksIndexed=${rows}`);
835
+ console.log(`manifestFound=${manifest !== null}`);
836
+ console.log(`ready=${ready}`);
837
+ console.log(`corpusFingerprint=${manifest?.corpusFingerprint ?? "unavailable"}`);
838
+ console.log(`indexedFiles=${manifest?.fileCount ?? 0}`);
839
+ console.log(`chunksIndexed=${manifest?.chunkCount ?? 0}`);
840
+ if (freshnessWarning) {
841
+ console.log(`indexFreshnessWarning=${freshnessWarning}`);
842
+ }
748
843
  if (ingestion) {
749
844
  console.log(`ingestionRunId=${ingestion.runId}`);
750
845
  console.log(`ingestionStatus=${ingestion.status}`);
751
846
  console.log(`ingestionMode=${ingestion.mode}`);
752
847
  console.log(`ingestionResumed=${ingestion.resumed}`);
753
848
  console.log(`ingestionBatchSize=${ingestion.batchSize}`);
754
- console.log(`ingestionProgress=${ingestion.indexedFiles}/${ingestion.totalFiles} indexed, ${ingestion.errorFiles} errors, ${ingestion.pendingFiles} pending`);
849
+ console.log(`ingestionProgress=${ingestion.indexedFiles}/${ingestion.totalFiles} indexed, ${ingestion.errorFiles} errors, ${ingestion.staleFiles} stale, ${ingestion.pendingFiles} pending`);
755
850
  console.log(`ingestionLastActivityAt=${ingestion.lastActivityAt}`);
756
851
  }
757
852
  });
@@ -789,6 +884,66 @@ program
789
884
  process.exitCode = 1;
790
885
  }
791
886
  });
887
+ const storageCommand = program.command("storage").description("Inspect and maintain local storage.");
888
+ storageCommand
889
+ .command("optimize")
890
+ .description("Compact LanceDB fragments, prune old versions, and refresh index coverage.")
891
+ .option("--dry-run", "Report planned maintenance without creating a LanceDB version.")
892
+ .option("--json", "Print machine-readable JSON.")
893
+ .action(async (options, command) => {
894
+ const report = await optimizeStorage({
895
+ cwd: projectRoot(command),
896
+ ...(options.dryRun === undefined ? {} : { dryRun: options.dryRun }),
897
+ });
898
+ if (options.json) {
899
+ console.log(JSON.stringify(report, null, 2));
900
+ }
901
+ else {
902
+ printStorageMaintenance(report);
903
+ }
904
+ if (report.status === "warning") {
905
+ process.exitCode = 1;
906
+ }
907
+ });
908
+ storageCommand
909
+ .command("gc")
910
+ .description("Inspect or reclaim expired local index generations.")
911
+ .option("--dry-run", "Report generation roles and reclaimable bytes without deleting tables.")
912
+ .option("--json", "Print machine-readable JSON.")
913
+ .action(async (options, command) => {
914
+ const report = await collectGenerationGarbage({
915
+ cwd: projectRoot(command),
916
+ ...(options.dryRun === undefined ? {} : { dryRun: options.dryRun }),
917
+ });
918
+ if (options.json) {
919
+ console.log(JSON.stringify(report, null, 2));
920
+ }
921
+ else {
922
+ printGenerationGarbageCollection(report);
923
+ }
924
+ if (report.warning) {
925
+ process.exitCode = 1;
926
+ }
927
+ });
928
+ storageCommand
929
+ .command("generations")
930
+ .description("Show active, resumable, rollback, leased, retained, and orphaned generations.")
931
+ .option("--json", "Print machine-readable JSON.")
932
+ .action(async (options, command) => {
933
+ const report = await collectGenerationGarbage({
934
+ cwd: projectRoot(command),
935
+ dryRun: true,
936
+ });
937
+ if (options.json) {
938
+ console.log(JSON.stringify(report, null, 2));
939
+ }
940
+ else {
941
+ printGenerationGarbageCollection(report);
942
+ }
943
+ if (report.warning) {
944
+ process.exitCode = 1;
945
+ }
946
+ });
792
947
  program
793
948
  .command("destroy-index")
794
949
  .description("Remove the generated local vector index from Ragmir storage.")
@@ -1055,11 +1210,7 @@ async function promptInput(promptParts) {
1055
1210
  if (process.stdin.isTTY) {
1056
1211
  return "";
1057
1212
  }
1058
- const chunks = [];
1059
- for await (const chunk of process.stdin) {
1060
- chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
1061
- }
1062
- return Buffer.concat(chunks).toString("utf8");
1213
+ return readBoundedStdin(process.stdin);
1063
1214
  }
1064
1215
  function withTopK(cwd, topK) {
1065
1216
  return topK === undefined ? { cwd } : { cwd, topK };
@@ -1070,6 +1221,9 @@ function withSearchOptions(cwd, options) {
1070
1221
  addOption(result, "contextRadius", options.contextRadius);
1071
1222
  addPathFilters(result, options);
1072
1223
  addOption(result, "explain", options.explain);
1224
+ if (options.exactVectorSearch) {
1225
+ result.vectorSearchMode = "exact";
1226
+ }
1073
1227
  return result;
1074
1228
  }
1075
1229
  function addPathFilters(target, options) {
@@ -1166,25 +1320,25 @@ function isTtsModule(value) {
1166
1320
  "renderSpeech" in value &&
1167
1321
  typeof value.renderSpeech === "function");
1168
1322
  }
1169
- async function printStaleIndexWarnings(cwd) {
1170
- const config = await loadConfig(cwd);
1171
- const freshnessWarning = await getIndexFreshnessWarning(config);
1172
- if (freshnessWarning) {
1173
- console.error(pc.yellow(freshnessWarning));
1174
- return;
1175
- }
1176
- const chunkCount = await countRows(config);
1177
- const lexicalScanWarning = getLexicalScanWarning(config, chunkCount);
1178
- if (lexicalScanWarning) {
1179
- console.error(pc.yellow(lexicalScanWarning));
1180
- }
1181
- }
1182
1323
  function printDoctor(report) {
1324
+ console.log(`mode=${report.mode}`);
1325
+ console.log(`cost=${report.cost}`);
1326
+ console.log(`inventoryVerified=${report.inventoryVerified}`);
1327
+ console.log(`securityVerified=${report.securityVerified}`);
1183
1328
  console.log(`projectRoot=${report.projectRoot}`);
1184
1329
  console.log(`initialized=${report.initialized}`);
1185
1330
  console.log(`ready=${report.ready}`);
1186
1331
  console.log(`packageManager=${report.packageManager}`);
1187
1332
  console.log(`runCommand=${report.runCommand}`);
1333
+ console.log(`runtime.ragmir=${report.runtime.ragmir}`);
1334
+ console.log(`runtime.node=${report.runtime.node}`);
1335
+ console.log(`runtime.v8=${report.runtime.v8}`);
1336
+ console.log(`runtime.napi=${report.runtime.napi ?? "unavailable"}`);
1337
+ console.log(`runtime.platform=${report.runtime.platform}`);
1338
+ console.log(`runtime.architecture=${report.runtime.architecture}`);
1339
+ for (const [name, dependency] of Object.entries(report.runtime.dependencies)) {
1340
+ console.log(`runtime.dependencies.${name}=${dependency ? `${dependency.name}@${dependency.version}` : "unavailable"}`);
1341
+ }
1188
1342
  console.log(`agentKitInstalled=${report.agentKitInstalled}`);
1189
1343
  console.log(`agentIntegration.ready=${report.agentIntegration.ready}`);
1190
1344
  console.log(`agentIntegration.runnerReady=${report.agentIntegration.runnerReady}`);
@@ -1212,6 +1366,7 @@ function printDoctor(report) {
1212
1366
  console.log(`chunksIndexed=${report.chunksIndexed}`);
1213
1367
  console.log(`missingFromIndex=${report.missingFromIndex}`);
1214
1368
  console.log(`staleInIndex=${report.staleInIndex}`);
1369
+ console.log(`corpusFingerprint=${report.corpusFingerprint ?? "unavailable"}`);
1215
1370
  console.log(`securityWarnings=${report.securityWarnings.length}`);
1216
1371
  if (report.securityWarnings.length > 0) {
1217
1372
  for (const warning of report.securityWarnings) {
@@ -1222,16 +1377,68 @@ function printDoctor(report) {
1222
1377
  if (report.indexFreshness.warning) {
1223
1378
  console.log(pc.yellow(`indexFreshness: ${report.indexFreshness.warning}`));
1224
1379
  }
1380
+ console.log(`readiness.retrievalQualityVerified=${report.readiness.retrievalQualityVerified}`);
1225
1381
  console.log("nextSteps:");
1226
1382
  for (const step of report.nextSteps) {
1227
1383
  console.log(` - ${step}`);
1228
1384
  }
1229
1385
  }
1386
+ function printStorageMaintenance(report) {
1387
+ console.log(`status=${report.status}`);
1388
+ console.log(`tableName=${report.tableName}`);
1389
+ console.log(`dryRun=${report.dryRun}`);
1390
+ console.log(`tableVersion=${report.tableVersion ?? "none"}`);
1391
+ console.log(`totalRows=${report.totalRows}`);
1392
+ console.log(`mutationsSinceOptimization=${report.mutationsSinceOptimization}`);
1393
+ console.log(`fragments.total=${report.fragments.total}`);
1394
+ console.log(`fragments.small=${report.fragments.small}`);
1395
+ console.log(`fragments.smallRatio=${report.fragments.smallRatio}`);
1396
+ console.log(`fullTextIndex.present=${report.fullTextIndex.present}`);
1397
+ console.log(`fullTextIndex.indexedRows=${report.fullTextIndex.indexedRows}`);
1398
+ console.log(`fullTextIndex.unindexedRows=${report.fullTextIndex.unindexedRows}`);
1399
+ console.log(`fullTextIndex.complete=${report.fullTextIndex.complete}`);
1400
+ if (report.adaptiveIndices) {
1401
+ console.log(`vectorIndex.strategy=${report.adaptiveIndices.vectorIndex.strategy}`);
1402
+ console.log(`vectorIndex.indexType=${report.adaptiveIndices.vectorIndex.indexType ?? "none"}`);
1403
+ console.log(`vectorIndex.coverage=${report.adaptiveIndices.vectorIndex.coverage}`);
1404
+ console.log(`vectorIndex.indexedRows=${report.adaptiveIndices.vectorIndex.indexedRows}`);
1405
+ console.log(`vectorIndex.unindexedRows=${report.adaptiveIndices.vectorIndex.unindexedRows}`);
1406
+ console.log(`relativePathIndex.present=${report.adaptiveIndices.relativePathIndex.present}`);
1407
+ console.log(`relativePathIndex.coverage=${report.adaptiveIndices.relativePathIndex.coverage}`);
1408
+ console.log(`adaptivePlannedActions=${report.adaptiveIndices.plannedActions.join(",")}`);
1409
+ console.log(`adaptiveCompletedActions=${report.adaptiveIndices.completedActions.join(",")}`);
1410
+ if (report.adaptiveIndices.warning) {
1411
+ console.log(pc.yellow(`vectorIndex.warning: ${report.adaptiveIndices.warning}`));
1412
+ }
1413
+ }
1414
+ console.log(`reasons=${report.reasons.join(",")}`);
1415
+ console.log(`plannedActions=${report.plannedActions.join(",")}`);
1416
+ console.log(`completedActions=${report.completedActions.join(",")}`);
1417
+ if (report.warning) {
1418
+ console.log(pc.yellow(`warning: ${report.warning}`));
1419
+ }
1420
+ }
1421
+ function printGenerationGarbageCollection(report) {
1422
+ console.log(`activeTableName=${report.activeTableName}`);
1423
+ console.log(`resumableTableName=${report.resumableTableName ?? "none"}`);
1424
+ console.log(`rollbackTableName=${report.rollbackTableName ?? "none"}`);
1425
+ console.log(`generations=${report.generations.length}`);
1426
+ console.log(`reclaimableBytes=${report.reclaimableBytes}`);
1427
+ console.log(`reclaimedBytes=${report.reclaimedBytes}`);
1428
+ console.log(`deletedTables=${report.deletedTables.join(",")}`);
1429
+ for (const generation of report.generations) {
1430
+ console.log(`generation=${generation.tableName} role=${generation.role} bytes=${generation.bytes} ageMs=${generation.ageMs} leased=${generation.leased} reclaimable=${generation.reclaimable} deleted=${generation.deleted}`);
1431
+ }
1432
+ if (report.warning) {
1433
+ console.log(pc.yellow(`warning: ${report.warning}`));
1434
+ }
1435
+ }
1230
1436
  function printResearchReport(report) {
1231
1437
  console.log(`query=${report.query}`);
1232
1438
  console.log(`ready=${report.ready}`);
1233
1439
  console.log(`generatedQueries=${report.generatedQueries.length}`);
1234
- console.log(`audit.supportedFiles=${report.audit.supportedFiles} audit.supportedBytes=${report.audit.supportedBytes} audit.largestFileBytes=${report.audit.largestFileBytes} audit.indexedFiles=${report.audit.indexedFiles} audit.totalChunks=${report.audit.totalChunks} audit.skippedFiles=${report.audit.skippedFiles} audit.oversizedFiles=${report.audit.oversizedFiles} audit.missingFromIndex=${report.audit.missingFromIndex} audit.staleInIndex=${report.audit.staleInIndex}`);
1440
+ console.log(`audit.mode=${report.audit.mode} audit.inventoryVerified=${report.audit.inventoryVerified} audit.supportedFiles=${report.audit.supportedFiles} audit.supportedBytes=${report.audit.supportedBytes} audit.largestFileBytes=${report.audit.largestFileBytes} audit.indexedFiles=${report.audit.indexedFiles} audit.totalChunks=${report.audit.totalChunks} audit.skippedFiles=${report.audit.skippedFiles} audit.oversizedFiles=${report.audit.oversizedFiles} audit.missingFromIndex=${report.audit.missingFromIndex} audit.staleInIndex=${report.audit.staleInIndex}`);
1441
+ console.log(`budgets.timeoutMs=${report.budgets.timeoutMs ?? "none"} budgets.evidenceTopK=${report.budgets.evidenceTopK} budgets.codeEvidenceTopK=${report.budgets.codeEvidenceTopK} budgets.codeFiles=${report.budgets.codeFilesScanned}/${report.budgets.codeScanMaxFiles} budgets.codeBytes=${report.budgets.codeBytesScanned}/${report.budgets.codeScanMaxBytes} budgets.codeConcurrency=${report.budgets.codeScanConcurrency} budgets.codeTruncated=${report.budgets.codeScanTruncated}`);
1235
1442
  console.log(`securityWarnings=${report.securityWarnings.length}`);
1236
1443
  console.log(`sourceDiagnostics.duplicates=${report.sourceDiagnostics.duplicateCandidates.length} sourceDiagnostics.archives=${report.sourceDiagnostics.archiveCandidates.length} sourceDiagnostics.mirrors=${report.sourceDiagnostics.mirrorCandidates.length}`);
1237
1444
  if (report.sourceDiagnostics.duplicateCandidates.length > 0) {
@@ -1246,7 +1453,7 @@ function printResearchReport(report) {
1246
1453
  console.log(pc.cyan("Evidence:"));
1247
1454
  for (const [index, evidence] of report.evidence.entries()) {
1248
1455
  const distance = evidence.distance === null ? "n/a" : evidence.distance.toFixed(4);
1249
- console.log(` [${index + 1}] ${evidence.relativePath} chunk=${evidence.chunkIndex} distance=${distance}`);
1456
+ console.log(` [${index + 1}] ${evidence.relativePath} chunk=${evidence.chunkIndex} distance=${distance} researchScore=${evidence.researchScore.toFixed(6)} bestRank=${evidence.bestRank}`);
1250
1457
  console.log(` ${researchEvidencePreview(evidence)}`);
1251
1458
  }
1252
1459
  }
@@ -1254,7 +1461,7 @@ function printResearchReport(report) {
1254
1461
  console.log("");
1255
1462
  console.log(pc.cyan("Code Evidence:"));
1256
1463
  for (const evidence of report.codeEvidence.slice(0, 10)) {
1257
- console.log(` - ${evidence.relativePath}:${evidence.lineNumber} terms=${evidence.matchedTerms.join(",")}`);
1464
+ console.log(` - ${evidence.relativePath}:${evidence.lineNumber} score=${evidence.score} terms=${evidence.matchedTerms.join(",")}`);
1258
1465
  console.log(` ${evidence.snippet}`);
1259
1466
  }
1260
1467
  }