@gmickel/gno 1.18.0 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/README.md +14 -7
  2. package/assets/skill/SKILL.md +54 -12
  3. package/assets/skill/mcp-reference.md +7 -2
  4. package/assets/skill/recipes/citation-and-provenance.md +32 -9
  5. package/package.json +2 -1
  6. package/spec/AGENTS.md +83 -0
  7. package/spec/CLAUDE.md +83 -0
  8. package/spec/bench-fixture.schema.json +137 -0
  9. package/spec/cli.md +2919 -0
  10. package/spec/db/schema.sql +442 -0
  11. package/spec/evals-agentic.md +592 -0
  12. package/spec/evals.md +1106 -0
  13. package/spec/mcp.md +2279 -0
  14. package/spec/output-schemas/activation-verification.schema.json +515 -0
  15. package/spec/output-schemas/ask.schema.json +564 -0
  16. package/spec/output-schemas/backlinks.schema.json +131 -0
  17. package/spec/output-schemas/bench-result.schema.json +120 -0
  18. package/spec/output-schemas/capture-receipt.schema.json +143 -0
  19. package/spec/output-schemas/claim-verification.schema.json +291 -0
  20. package/spec/output-schemas/collection-list.schema.json +45 -0
  21. package/spec/output-schemas/context-capsule-v1.schema.json +726 -0
  22. package/spec/output-schemas/context-capsule-verification.schema.json +1338 -0
  23. package/spec/output-schemas/context-list.schema.json +21 -0
  24. package/spec/output-schemas/doctor.schema.json +313 -0
  25. package/spec/output-schemas/error.schema.json +30 -0
  26. package/spec/output-schemas/expansion.schema.json +37 -0
  27. package/spec/output-schemas/get.schema.json +140 -0
  28. package/spec/output-schemas/graph-query.schema.json +99 -0
  29. package/spec/output-schemas/graph.schema.json +371 -0
  30. package/spec/output-schemas/links-list.schema.json +186 -0
  31. package/spec/output-schemas/mcp-add-collection-result.schema.json +23 -0
  32. package/spec/output-schemas/mcp-capture-result.schema.json +152 -0
  33. package/spec/output-schemas/mcp-http-error.schema.json +30 -0
  34. package/spec/output-schemas/mcp-job-list.schema.json +58 -0
  35. package/spec/output-schemas/mcp-job-status.schema.json +224 -0
  36. package/spec/output-schemas/mcp-remove-result.schema.json +39 -0
  37. package/spec/output-schemas/mcp-sync-result.schema.json +41 -0
  38. package/spec/output-schemas/mcp-tag-result.schema.json +33 -0
  39. package/spec/output-schemas/models-list.schema.json +93 -0
  40. package/spec/output-schemas/multi-get.schema.json +103 -0
  41. package/spec/output-schemas/process-status.schema.json +119 -0
  42. package/spec/output-schemas/query-diagnose.schema.json +123 -0
  43. package/spec/output-schemas/resident-status.schema.json +154 -0
  44. package/spec/output-schemas/retrieval-trace-common.schema.json +492 -0
  45. package/spec/output-schemas/retrieval-trace-delete.schema.json +16 -0
  46. package/spec/output-schemas/retrieval-trace-export.schema.json +61 -0
  47. package/spec/output-schemas/retrieval-trace-filters.schema.json +139 -0
  48. package/spec/output-schemas/retrieval-trace-judgment.schema.json +15 -0
  49. package/spec/output-schemas/retrieval-trace-list.schema.json +18 -0
  50. package/spec/output-schemas/retrieval-trace-payloads.schema.json +178 -0
  51. package/spec/output-schemas/retrieval-trace-purge.schema.json +31 -0
  52. package/spec/output-schemas/retrieval-trace-qrels.schema.json +303 -0
  53. package/spec/output-schemas/retrieval-trace-replay.schema.json +286 -0
  54. package/spec/output-schemas/retrieval-trace-show.schema.json +69 -0
  55. package/spec/output-schemas/retrieval-trace-summary.schema.json +65 -0
  56. package/spec/output-schemas/search-result.schema.json +154 -0
  57. package/spec/output-schemas/search-results.schema.json +338 -0
  58. package/spec/output-schemas/similar.schema.json +84 -0
  59. package/spec/output-schemas/status.schema.json +676 -0
  60. package/spec/output-schemas/tags-list.schema.json +48 -0
  61. package/src/app/context-runtime-contract.ts +10 -5
  62. package/src/app/context-runtime-input.ts +29 -1
  63. package/src/app/context-runtime-types.ts +7 -0
  64. package/src/app/context-runtime.ts +20 -2
  65. package/src/app/context-surface.ts +4 -0
  66. package/src/app/verified-ask.ts +291 -0
  67. package/src/cli/commands/ask-format.ts +255 -0
  68. package/src/cli/commands/ask.ts +144 -183
  69. package/src/cli/commands/context-build.ts +56 -9
  70. package/src/cli/commands/get.ts +64 -3
  71. package/src/cli/commands/query.ts +62 -23
  72. package/src/cli/commands/replay.ts +140 -0
  73. package/src/cli/commands/search.ts +48 -3
  74. package/src/cli/commands/shared.ts +3 -1
  75. package/src/cli/commands/trace.ts +200 -0
  76. package/src/cli/commands/vsearch.ts +75 -53
  77. package/src/cli/program.ts +287 -1
  78. package/src/config/index.ts +9 -0
  79. package/src/config/retrieval-traces.ts +56 -0
  80. package/src/config/types.ts +4 -0
  81. package/src/core/context-budget.ts +6 -0
  82. package/src/core/context-capsule-retrieval-schema.ts +4 -0
  83. package/src/core/context-capsule-schema.ts +17 -0
  84. package/src/core/context-capsule-validation.ts +3 -2
  85. package/src/core/context-capsule.ts +18 -0
  86. package/src/core/context-compiler.ts +44 -25
  87. package/src/core/context-evidence.ts +6 -0
  88. package/src/core/retrieval-qrels.ts +405 -0
  89. package/src/core/retrieval-replay-candidate.ts +368 -0
  90. package/src/core/retrieval-replay-types.ts +109 -0
  91. package/src/core/retrieval-replay-validation.ts +89 -0
  92. package/src/core/retrieval-replay.ts +441 -0
  93. package/src/core/retrieval-trace-evidence-origin.ts +178 -0
  94. package/src/core/retrieval-trace-export.ts +113 -0
  95. package/src/core/retrieval-trace-filter-normalization.ts +27 -0
  96. package/src/core/retrieval-trace-filters.ts +19 -0
  97. package/src/core/retrieval-trace-management-helpers.ts +247 -0
  98. package/src/core/retrieval-trace-management-types.ts +132 -0
  99. package/src/core/retrieval-trace-management.ts +422 -0
  100. package/src/core/retrieval-trace-request.ts +141 -0
  101. package/src/core/retrieval-trace-session.ts +507 -0
  102. package/src/core/retrieval-trace.ts +472 -0
  103. package/src/llm/errors.ts +10 -1
  104. package/src/llm/httpGeneration.ts +11 -1
  105. package/src/llm/nodeLlamaCpp/generation.ts +54 -10
  106. package/src/llm/types.ts +6 -0
  107. package/src/mcp/tools/ask.ts +228 -0
  108. package/src/mcp/tools/context.ts +87 -15
  109. package/src/mcp/tools/get.ts +35 -1
  110. package/src/mcp/tools/index.ts +83 -0
  111. package/src/mcp/tools/query.ts +95 -64
  112. package/src/mcp/tools/search.ts +36 -13
  113. package/src/mcp/tools/trace.ts +143 -0
  114. package/src/mcp/tools/vsearch.ts +71 -38
  115. package/src/pipeline/answer.ts +167 -26
  116. package/src/pipeline/claim-verification-schema.ts +235 -0
  117. package/src/pipeline/claim-verification.ts +487 -0
  118. package/src/pipeline/claim-verifier.ts +474 -0
  119. package/src/pipeline/graph-retrieval.ts +15 -1
  120. package/src/pipeline/hybrid.ts +151 -43
  121. package/src/pipeline/search.ts +36 -3
  122. package/src/pipeline/trace-metadata.ts +47 -0
  123. package/src/pipeline/types.ts +68 -0
  124. package/src/pipeline/vsearch.ts +101 -38
  125. package/src/sdk/client.ts +415 -73
  126. package/src/sdk/documents.ts +48 -1
  127. package/src/sdk/index.ts +17 -0
  128. package/src/sdk/types.ts +28 -0
  129. package/src/serve/context-capsule.ts +67 -8
  130. package/src/serve/public/app.tsx +12 -1
  131. package/src/serve/public/components/AskVerificationPanel.tsx +189 -0
  132. package/src/serve/public/globals.built.css +1 -1
  133. package/src/serve/public/lib/workspace-tabs.ts +2 -0
  134. package/src/serve/public/pages/Ask.tsx +42 -4
  135. package/src/serve/public/pages/Dashboard.tsx +10 -0
  136. package/src/serve/public/pages/TraceHistory.tsx +478 -0
  137. package/src/serve/public/pages/trace-history-detail.tsx +224 -0
  138. package/src/serve/retrieval-trace.ts +28 -0
  139. package/src/serve/routes/api.ts +508 -68
  140. package/src/serve/routes/traces.ts +156 -0
  141. package/src/serve/server.ts +87 -2
  142. package/src/store/index.ts +31 -0
  143. package/src/store/migrations/014-retrieval-traces.ts +303 -0
  144. package/src/store/migrations/index.ts +2 -0
  145. package/src/store/retrieval-trace-codec.ts +384 -0
  146. package/src/store/sqlite/adapter.ts +153 -1
  147. package/src/store/sqlite/retrieval-trace-management-store.ts +341 -0
  148. package/src/store/sqlite/retrieval-trace-retention.ts +349 -0
  149. package/src/store/sqlite/retrieval-trace-rows.ts +267 -0
  150. package/src/store/sqlite/retrieval-trace-store.ts +515 -0
  151. package/src/store/types.ts +297 -0
  152. package/src/store/vector/sqlite-vec.ts +76 -1
  153. package/src/store/vector/types.ts +1 -1
@@ -55,9 +55,10 @@ import {
55
55
  shouldSortByRecency,
56
56
  } from "./temporal";
57
57
  import {
58
- DEFAULT_PIPELINE_CONFIG,
59
- SEARCH_RESULT_PLANNER_METADATA,
60
- } from "./types";
58
+ attachSearchResultPlannerMetadata,
59
+ attachSearchResultsTraceMetadata,
60
+ } from "./trace-metadata";
61
+ import { DEFAULT_PIPELINE_CONFIG } from "./types";
61
62
 
62
63
  // ─────────────────────────────────────────────────────────────────────────────
63
64
  // Dependencies
@@ -121,11 +122,13 @@ async function checkBm25Strength(
121
122
  until?: string;
122
123
  categories?: string[];
123
124
  author?: string;
125
+ relPathPrefix?: string;
124
126
  }
125
127
  ): Promise<boolean> {
126
128
  const result = await store.searchFts(query, {
127
129
  limit: 5,
128
130
  collection: options?.collection,
131
+ relPathPrefix: options?.relPathPrefix,
129
132
  language: options?.lang,
130
133
  tagsAll: options?.tagsAll,
131
134
  tagsAny: options?.tagsAny,
@@ -179,11 +182,13 @@ async function searchFtsChunks(
179
182
  until?: string;
180
183
  categories?: string[];
181
184
  author?: string;
185
+ relPathPrefix?: string;
182
186
  }
183
187
  ): Promise<FtsChunksResult> {
184
188
  const result = await store.searchFts(query, {
185
189
  limit: options.limit,
186
190
  collection: options.collection,
191
+ relPathPrefix: options.relPathPrefix,
187
192
  language: options.lang,
188
193
  tagsAll: options.tagsAll,
189
194
  tagsAny: options.tagsAny,
@@ -216,7 +221,11 @@ async function searchVectorChunks(
216
221
  vectorIndex: VectorIndexPort,
217
222
  embedPort: EmbeddingPort,
218
223
  query: string,
219
- options: { limit: number; minScore?: number }
224
+ options: {
225
+ limit: number;
226
+ minScore?: number;
227
+ allowedMirrorHashes?: string[];
228
+ }
220
229
  ): Promise<ChunkId[]> {
221
230
  if (!vectorIndex.searchAvailable) {
222
231
  return [];
@@ -234,7 +243,10 @@ async function searchVectorChunks(
234
243
  const searchResult = await vectorIndex.searchNearest(
235
244
  queryEmbedding,
236
245
  options.limit,
237
- { minScore: options.minScore }
246
+ {
247
+ minScore: options.minScore,
248
+ allowedMirrorHashes: options.allowedMirrorHashes,
249
+ }
238
250
  );
239
251
 
240
252
  if (!searchResult.ok) {
@@ -381,6 +393,7 @@ export async function searchHybrid(
381
393
  until: temporalRange.until,
382
394
  categories: options.categories,
383
395
  author: options.author,
396
+ relPathPrefix: options.retrievalScope?.relPathPrefix,
384
397
  });
385
398
 
386
399
  if (hasStrongSignal) {
@@ -397,6 +410,8 @@ export async function searchHybrid(
397
410
  });
398
411
  if (expandResult.ok) {
399
412
  expansion = expandResult.value;
413
+ } else {
414
+ counters.fallbackEvents.push("expansion_error");
400
415
  }
401
416
  }
402
417
  }
@@ -428,6 +443,7 @@ export async function searchHybrid(
428
443
  until: temporalRange.until,
429
444
  categories: options.categories,
430
445
  author: options.author,
446
+ relPathPrefix: options.retrievalScope?.relPathPrefix,
431
447
  });
432
448
 
433
449
  // Propagate FTS syntax errors as INVALID_INPUT
@@ -462,6 +478,7 @@ export async function searchHybrid(
462
478
  until: temporalRange.until,
463
479
  categories: options.categories,
464
480
  author: options.author,
481
+ relPathPrefix: options.retrievalScope?.relPathPrefix,
465
482
  })
466
483
  )
467
484
  );
@@ -509,6 +526,7 @@ export async function searchHybrid(
509
526
  query,
510
527
  {
511
528
  limit: limit * 2 * retrievalMultiplier,
529
+ allowedMirrorHashes: options.retrievalScope?.allowedMirrorHashes,
512
530
  }
513
531
  );
514
532
 
@@ -552,7 +570,10 @@ export async function searchHybrid(
552
570
 
553
571
  const searchResult = await vectorIndex.searchNearest(
554
572
  new Float32Array(embedding),
555
- variant.limit
573
+ variant.limit,
574
+ {
575
+ allowedMirrorHashes: options.retrievalScope?.allowedMirrorHashes,
576
+ }
556
577
  );
557
578
  if (!searchResult.ok || searchResult.value.length === 0) {
558
579
  continue;
@@ -615,6 +636,7 @@ export async function searchHybrid(
615
636
  limit,
616
637
  candidateLimit,
617
638
  disabled: !options.graph || options.noGraph,
639
+ relPathPrefix: options.retrievalScope?.relPathPrefix,
618
640
  lang: options.lang,
619
641
  tagsAll: options.tagsAll,
620
642
  tagsAny: options.tagsAny,
@@ -735,6 +757,14 @@ export async function searchHybrid(
735
757
  const matchesMetadataFilters = (
736
758
  doc: (typeof docsResult.value)[number]
737
759
  ): boolean => {
760
+ const relPathPrefix = options.retrievalScope?.relPathPrefix;
761
+ if (
762
+ relPathPrefix !== undefined &&
763
+ doc.relPath !== relPathPrefix &&
764
+ !doc.relPath.startsWith(`${relPathPrefix}/`)
765
+ ) {
766
+ return false;
767
+ }
738
768
  if (!isWithinTemporalRange(doc.sourceMtime, temporalRange)) {
739
769
  return false;
740
770
  }
@@ -923,42 +953,51 @@ export async function searchHybrid(
923
953
  if (!docidMap.has(docidKey)) docidMap.set(docidKey, doc.docid);
924
954
  const collectionPath = collectionPaths.get(doc.collection);
925
955
  seenDocids.add(doc.docid);
926
- results.push({
927
- docid: doc.docid,
928
- score: candidate.blendedScore,
929
- uri: doc.uri,
930
- title: doc.title ?? undefined,
931
- contentType: doc.contentType ?? undefined,
932
- categories: doc.categories ?? undefined,
933
- line: snippetChunk.startLine,
934
- snippet,
935
- snippetLanguage: chunk.language ?? undefined,
936
- snippetRange,
937
- source: {
938
- relPath: doc.relPath,
939
- absPath: collectionPath
940
- ? `${collectionPath}/${doc.relPath}`
941
- : undefined,
942
- mime: doc.sourceMime,
943
- ext: doc.sourceExt,
944
- modifiedAt: doc.sourceMtime,
945
- documentDate: doc.frontmatterDate ?? undefined,
946
- sizeBytes: doc.sourceSize,
947
- sourceHash: doc.sourceHash,
948
- },
949
- conversion: {
950
- mirrorHash: candidate.mirrorHash,
951
- converterId: doc.converterId ?? undefined,
952
- converterVersion: doc.converterVersion ?? undefined,
953
- },
954
- [SEARCH_RESULT_PLANNER_METADATA]: {
955
- retrievalRank: candidateIndex + 1,
956
- mirrorHash: candidate.mirrorHash,
957
- seq: snippetChunk.seq,
958
- sources: [...candidate.sources].sort(),
959
- graphExpanded: candidate.sources.includes("graph"),
960
- },
961
- });
956
+ results.push(
957
+ attachSearchResultPlannerMetadata(
958
+ {
959
+ docid: doc.docid,
960
+ score: candidate.blendedScore,
961
+ uri: doc.uri,
962
+ title: doc.title ?? undefined,
963
+ contentType: doc.contentType ?? undefined,
964
+ categories: doc.categories ?? undefined,
965
+ line: snippetChunk.startLine,
966
+ snippet,
967
+ snippetLanguage: chunk.language ?? undefined,
968
+ snippetRange,
969
+ source: {
970
+ relPath: doc.relPath,
971
+ absPath: collectionPath
972
+ ? `${collectionPath}/${doc.relPath}`
973
+ : undefined,
974
+ mime: doc.sourceMime,
975
+ ext: doc.sourceExt,
976
+ modifiedAt: doc.sourceMtime,
977
+ documentDate: doc.frontmatterDate ?? undefined,
978
+ sizeBytes: doc.sourceSize,
979
+ sourceHash: doc.sourceHash,
980
+ },
981
+ conversion: {
982
+ mirrorHash: candidate.mirrorHash,
983
+ converterId: doc.converterId ?? undefined,
984
+ converterVersion: doc.converterVersion ?? undefined,
985
+ },
986
+ },
987
+ {
988
+ retrievalRank: candidateIndex + 1,
989
+ mirrorHash: candidate.mirrorHash,
990
+ seq: snippetChunk.seq,
991
+ sources: [...candidate.sources].sort(),
992
+ graphExpanded: candidate.sources.includes("graph"),
993
+ startLine: snippetChunk.startLine,
994
+ endLine: snippetChunk.endLine,
995
+ passageHash: new Bun.CryptoHasher("sha256")
996
+ .update(snippetChunk.text)
997
+ .digest("hex"),
998
+ }
999
+ )
1000
+ );
962
1001
  }
963
1002
  }
964
1003
  timings.assemblyMs = performance.now() - assemblyStartedAt;
@@ -1002,7 +1041,7 @@ export async function searchHybrid(
1002
1041
  const finalResults = results.slice(0, limit);
1003
1042
  await attachSearchResultContexts(store, finalResults);
1004
1043
 
1005
- return ok({
1044
+ const output: SearchResults = {
1006
1045
  results: finalResults,
1007
1046
  meta: {
1008
1047
  query,
@@ -1033,5 +1072,74 @@ export async function searchHybrid(
1033
1072
  explain: explainData,
1034
1073
  trace: diagnoseTrace,
1035
1074
  },
1075
+ };
1076
+ const fallbackCodes = [...new Set(counters.fallbackEvents)].sort();
1077
+ const capabilityOutcomes = [
1078
+ { capability: "lexical_search", status: "used" as const },
1079
+ vectorAvailable
1080
+ ? fallbackCodes.includes("vector_embed_error")
1081
+ ? {
1082
+ capability: "semantic_search",
1083
+ status: "failed" as const,
1084
+ reasonCode: "vector_embed_error",
1085
+ }
1086
+ : { capability: "semantic_search", status: "used" as const }
1087
+ : {
1088
+ capability: "semantic_search",
1089
+ status: "unavailable" as const,
1090
+ reasonCode: "vector_unavailable",
1091
+ },
1092
+ expansion !== null
1093
+ ? { capability: "query_expansion", status: "used" as const }
1094
+ : {
1095
+ capability: "query_expansion",
1096
+ status:
1097
+ expansionStatus === "attempted"
1098
+ ? ("failed" as const)
1099
+ : ("unavailable" as const),
1100
+ reasonCode:
1101
+ expansionStatus === "attempted"
1102
+ ? "expansion_error"
1103
+ : expansionStatus === "skipped_strong"
1104
+ ? "expansion_skipped_strong"
1105
+ : "expansion_disabled",
1106
+ },
1107
+ rerankResult.reranked
1108
+ ? { capability: "reranking", status: "used" as const }
1109
+ : {
1110
+ capability: "reranking",
1111
+ status:
1112
+ rerankResult.fallbackReason === "error"
1113
+ ? ("failed" as const)
1114
+ : ("unavailable" as const),
1115
+ reasonCode:
1116
+ rerankResult.fallbackReason === "error"
1117
+ ? "rerank_error"
1118
+ : "rerank_disabled",
1119
+ },
1120
+ graphExpansion.meta.enabled
1121
+ ? { capability: "graph_expansion", status: "used" as const }
1122
+ : {
1123
+ capability: "graph_expansion",
1124
+ status: "unavailable" as const,
1125
+ reasonCode:
1126
+ graphExpansion.meta.fallbackReasons[0] ?? "graph_disabled",
1127
+ },
1128
+ ];
1129
+ attachSearchResultsTraceMetadata(output, {
1130
+ capabilityOutcomes,
1131
+ fallbackCodes,
1036
1132
  });
1133
+ const traceResult = await options.traceSession?.recordRetrieval(
1134
+ output,
1135
+ timings.totalMs
1136
+ );
1137
+ if (traceResult && !traceResult.ok) {
1138
+ return err(
1139
+ "QUERY_FAILED",
1140
+ `Trace recording failed: ${traceResult.error.message}`,
1141
+ traceResult.error.cause
1142
+ );
1143
+ }
1144
+ return ok(output);
1037
1145
  }
@@ -27,6 +27,8 @@ import {
27
27
  resolveTemporalRange,
28
28
  shouldSortByRecency,
29
29
  } from "./temporal";
30
+ import { attachSearchResultPlannerMetadata } from "./trace-metadata";
31
+ import { SEARCH_RESULT_PLANNER_METADATA } from "./types";
30
32
 
31
33
  // ─────────────────────────────────────────────────────────────────────────────
32
34
  // Score Normalization
@@ -113,7 +115,7 @@ function buildSearchResult(ctx: BuildResultContext): SearchResult {
113
115
  : undefined;
114
116
  }
115
117
 
116
- return {
118
+ const result: SearchResult = {
117
119
  docid: fts.docid ?? "",
118
120
  score: fts.score, // Raw score, normalized later as batch
119
121
  uri: fts.uri ?? "",
@@ -127,6 +129,19 @@ function buildSearchResult(ctx: BuildResultContext): SearchResult {
127
129
  source,
128
130
  conversion: fts.mirrorHash ? { mirrorHash: fts.mirrorHash } : undefined,
129
131
  };
132
+ if (!(chunk && fts.mirrorHash)) return result;
133
+ return attachSearchResultPlannerMetadata(result, {
134
+ retrievalRank: 0,
135
+ mirrorHash: fts.mirrorHash,
136
+ seq: chunk.seq,
137
+ sources: ["bm25"],
138
+ graphExpanded: false,
139
+ startLine: chunk.startLine,
140
+ endLine: chunk.endLine,
141
+ passageHash: new Bun.CryptoHasher("sha256")
142
+ .update(chunk.text)
143
+ .digest("hex"),
144
+ });
130
145
  }
131
146
 
132
147
  // ─────────────────────────────────────────────────────────────────────────────
@@ -142,6 +157,7 @@ export async function searchBm25(
142
157
  query: string,
143
158
  options: SearchOptions = {}
144
159
  ): Promise<ReturnType<typeof ok<SearchResults>>> {
160
+ const traceStartedAt = options.traceSession ? performance.now() : 0;
145
161
  const limit = options.limit ?? 20;
146
162
  const minScore = options.minScore ?? 0;
147
163
  const recencySort = shouldSortByRecency(query);
@@ -161,6 +177,7 @@ export async function searchBm25(
161
177
  const ftsResult = await store.searchFts(query, {
162
178
  limit: retrievalLimit,
163
179
  collection: options.collection,
180
+ relPathPrefix: options.retrievalScope?.relPathPrefix,
164
181
  language: options.lang,
165
182
  snippet: !(options.full || options.lineNumbers),
166
183
  tagsAll: options.tagsAll,
@@ -331,9 +348,13 @@ export async function searchBm25(
331
348
  }
332
349
 
333
350
  const finalResults = filteredResults.slice(0, limit);
351
+ for (const [index, result] of finalResults.entries()) {
352
+ const metadata = result[SEARCH_RESULT_PLANNER_METADATA];
353
+ if (metadata) metadata.retrievalRank = index + 1;
354
+ }
334
355
  await attachSearchResultContexts(store, finalResults);
335
356
 
336
- return ok({
357
+ const output: SearchResults = {
337
358
  results: finalResults,
338
359
  meta: {
339
360
  query,
@@ -349,5 +370,17 @@ export async function searchBm25(
349
370
  author: options.author,
350
371
  queryLanguage,
351
372
  },
352
- });
373
+ };
374
+ const traceResult = await options.traceSession?.recordRetrieval(
375
+ output,
376
+ performance.now() - traceStartedAt
377
+ );
378
+ if (traceResult && !traceResult.ok) {
379
+ return err(
380
+ "QUERY_FAILED",
381
+ `Trace recording failed: ${traceResult.error.message}`,
382
+ traceResult.error.cause
383
+ );
384
+ }
385
+ return ok(output);
353
386
  }
@@ -0,0 +1,47 @@
1
+ /** Non-canonical retrieval metadata attached without changing public payloads. */
2
+
3
+ import type {
4
+ Citation,
5
+ CitationTraceMetadata,
6
+ SearchResult,
7
+ SearchResultPlannerMetadata,
8
+ SearchResults,
9
+ SearchResultsTraceMetadata,
10
+ } from "./types";
11
+
12
+ import {
13
+ CITATION_TRACE_METADATA,
14
+ SEARCH_RESULT_PLANNER_METADATA,
15
+ SEARCH_RESULTS_TRACE_METADATA,
16
+ } from "./types";
17
+
18
+ const attachHiddenMetadata = <Value extends object, Metadata>(
19
+ value: Value,
20
+ key: symbol,
21
+ metadata: Metadata
22
+ ): Value => {
23
+ Object.defineProperty(value, key, {
24
+ configurable: false,
25
+ enumerable: false,
26
+ value: metadata,
27
+ writable: false,
28
+ });
29
+ return value;
30
+ };
31
+
32
+ export const attachSearchResultPlannerMetadata = <Result extends SearchResult>(
33
+ result: Result,
34
+ metadata: SearchResultPlannerMetadata
35
+ ): Result =>
36
+ attachHiddenMetadata(result, SEARCH_RESULT_PLANNER_METADATA, metadata);
37
+
38
+ export const attachCitationTraceMetadata = <Result extends Citation>(
39
+ citation: Result,
40
+ metadata: CitationTraceMetadata
41
+ ): Result => attachHiddenMetadata(citation, CITATION_TRACE_METADATA, metadata);
42
+
43
+ export const attachSearchResultsTraceMetadata = <Result extends SearchResults>(
44
+ results: Result,
45
+ metadata: SearchResultsTraceMetadata
46
+ ): Result =>
47
+ attachHiddenMetadata(results, SEARCH_RESULTS_TRACE_METADATA, metadata);
@@ -5,7 +5,14 @@
5
5
  * @module src/pipeline/types
6
6
  */
7
7
 
8
+ import type {
9
+ ContextCapsuleV1,
10
+ ContextCapsuleVerification,
11
+ } from "../core/context-capsule";
12
+ import type { RetrievalTraceSession } from "../core/retrieval-trace-session";
8
13
  import type { StoreResult } from "../store/types";
14
+ import type { ClaimVerificationResult } from "./claim-verification";
15
+ import type { SemanticVerificationCapability } from "./claim-verifier";
9
16
 
10
17
  // ─────────────────────────────────────────────────────────────────────────────
11
18
  // Search Result Types
@@ -48,6 +55,11 @@ export interface SearchResultPlannerMetadata {
48
55
  seq: number;
49
56
  sources: FusionSource[];
50
57
  graphExpanded: boolean;
58
+ /** Exact canonical chunk coordinates, retained even for full-content output. */
59
+ startLine?: number;
60
+ endLine?: number;
61
+ /** SHA-256 of the complete canonical chunk lines. */
62
+ passageHash?: string;
51
63
  }
52
64
 
53
65
  /** Single search result matching output schema */
@@ -122,10 +134,26 @@ export interface SearchMeta {
122
134
  trace?: QueryDiagnoseTrace;
123
135
  }
124
136
 
137
+ export const SEARCH_RESULTS_TRACE_METADATA = Symbol(
138
+ "gno.searchResultsTraceMetadata"
139
+ );
140
+
141
+ export interface SearchCapabilityOutcome {
142
+ capability: string;
143
+ status: "attempted" | "used" | "unavailable" | "failed";
144
+ reasonCode?: string;
145
+ }
146
+
147
+ export interface SearchResultsTraceMetadata {
148
+ capabilityOutcomes: SearchCapabilityOutcome[];
149
+ fallbackCodes: string[];
150
+ }
151
+
125
152
  /** Complete search results wrapper */
126
153
  export interface SearchResults {
127
154
  results: SearchResult[];
128
155
  meta: SearchMeta;
156
+ [SEARCH_RESULTS_TRACE_METADATA]?: SearchResultsTraceMetadata;
129
157
  }
130
158
 
131
159
  // ─────────────────────────────────────────────────────────────────────────────
@@ -134,12 +162,19 @@ export interface SearchResults {
134
162
 
135
163
  /** Common options for all search commands */
136
164
  export interface SearchOptions {
165
+ /** Internal receipt seam; never serialized or included in public schemas. */
166
+ traceSession?: RetrievalTraceSession;
137
167
  /** Max results */
138
168
  limit?: number;
139
169
  /** Min score threshold (0-1) */
140
170
  minScore?: number;
141
171
  /** Filter by collection */
142
172
  collection?: string;
173
+ /** Internal exact corpus scope used by deterministic retrieval replay. */
174
+ retrievalScope?: {
175
+ relPathPrefix?: string;
176
+ allowedMirrorHashes: string[];
177
+ };
143
178
  /** Language filter/hint (BCP-47) */
144
179
  lang?: string;
145
180
  /** Include full content instead of snippet */
@@ -210,6 +245,12 @@ export type AskOptions = HybridSearchOptions & {
210
245
  noAnswer?: boolean;
211
246
  /** Max tokens for answer */
212
247
  maxAnswerTokens?: number;
248
+ /** Verify generated claims against a closed Context Capsule. */
249
+ verify?: boolean;
250
+ /** Global Context Capsule budget used by verified Ask. */
251
+ contextBudgetTokens?: number;
252
+ /** Optional explicit byte budget used by verified Ask. */
253
+ contextBudgetBytes?: number;
213
254
  };
214
255
 
215
256
  // ─────────────────────────────────────────────────────────────────────────────
@@ -357,11 +398,26 @@ export const DEFAULT_PIPELINE_CONFIG: PipelineConfig = {
357
398
  // ─────────────────────────────────────────────────────────────────────────────
358
399
 
359
400
  /** Citation reference */
401
+ export const CITATION_TRACE_METADATA = Symbol("gno.citationTraceMetadata");
402
+
403
+ export interface CitationTraceMetadata {
404
+ sourceHash: string;
405
+ mirrorHash: string;
406
+ passageHash: string;
407
+ seq?: number;
408
+ rank: number;
409
+ plannerRank?: number;
410
+ sources?: FusionSource[];
411
+ graphExpanded?: boolean;
412
+ }
413
+
360
414
  export interface Citation {
415
+ evidenceId?: string;
361
416
  docid: string;
362
417
  uri: string;
363
418
  startLine?: number;
364
419
  endLine?: number;
420
+ [CITATION_TRACE_METADATA]?: CitationTraceMetadata;
365
421
  }
366
422
 
367
423
  /** Source selection entry for answer-generation explain */
@@ -395,6 +451,17 @@ export interface AskMeta {
395
451
  answerGenerated?: boolean;
396
452
  totalResults?: number;
397
453
  answerContext?: AnswerContextExplain;
454
+ verificationRequested?: boolean;
455
+ abstained?: boolean;
456
+ }
457
+
458
+ export interface AskVerification {
459
+ schemaVersion: "1.0";
460
+ mode: "closed_capsule";
461
+ capsule: ContextCapsuleV1;
462
+ freshness: ContextCapsuleVerification;
463
+ claims: ClaimVerificationResult;
464
+ semantic: SemanticVerificationCapability;
398
465
  }
399
466
 
400
467
  /** Ask command result */
@@ -406,6 +473,7 @@ export interface AskResult {
406
473
  citations?: Citation[];
407
474
  results: SearchResult[];
408
475
  meta: AskMeta;
476
+ verification?: AskVerification;
409
477
  }
410
478
 
411
479
  // ─────────────────────────────────────────────────────────────────────────────