@gmickel/gno 1.18.0 → 1.19.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.
- package/README.md +5 -3
- package/assets/skill/SKILL.md +27 -0
- package/package.json +2 -1
- package/spec/AGENTS.md +83 -0
- package/spec/CLAUDE.md +83 -0
- package/spec/bench-fixture.schema.json +137 -0
- package/spec/cli.md +2894 -0
- package/spec/db/schema.sql +442 -0
- package/spec/evals-agentic.md +510 -0
- package/spec/evals.md +1106 -0
- package/spec/mcp.md +2229 -0
- package/spec/output-schemas/activation-verification.schema.json +515 -0
- package/spec/output-schemas/ask.schema.json +366 -0
- package/spec/output-schemas/backlinks.schema.json +131 -0
- package/spec/output-schemas/bench-result.schema.json +120 -0
- package/spec/output-schemas/capture-receipt.schema.json +143 -0
- package/spec/output-schemas/collection-list.schema.json +45 -0
- package/spec/output-schemas/context-capsule-v1.schema.json +691 -0
- package/spec/output-schemas/context-capsule-verification.schema.json +1338 -0
- package/spec/output-schemas/context-list.schema.json +21 -0
- package/spec/output-schemas/doctor.schema.json +313 -0
- package/spec/output-schemas/error.schema.json +30 -0
- package/spec/output-schemas/expansion.schema.json +37 -0
- package/spec/output-schemas/get.schema.json +140 -0
- package/spec/output-schemas/graph-query.schema.json +99 -0
- package/spec/output-schemas/graph.schema.json +371 -0
- package/spec/output-schemas/links-list.schema.json +186 -0
- package/spec/output-schemas/mcp-add-collection-result.schema.json +23 -0
- package/spec/output-schemas/mcp-capture-result.schema.json +152 -0
- package/spec/output-schemas/mcp-http-error.schema.json +30 -0
- package/spec/output-schemas/mcp-job-list.schema.json +58 -0
- package/spec/output-schemas/mcp-job-status.schema.json +224 -0
- package/spec/output-schemas/mcp-remove-result.schema.json +39 -0
- package/spec/output-schemas/mcp-sync-result.schema.json +41 -0
- package/spec/output-schemas/mcp-tag-result.schema.json +33 -0
- package/spec/output-schemas/models-list.schema.json +93 -0
- package/spec/output-schemas/multi-get.schema.json +103 -0
- package/spec/output-schemas/process-status.schema.json +119 -0
- package/spec/output-schemas/query-diagnose.schema.json +123 -0
- package/spec/output-schemas/resident-status.schema.json +154 -0
- package/spec/output-schemas/retrieval-trace-common.schema.json +492 -0
- package/spec/output-schemas/retrieval-trace-delete.schema.json +16 -0
- package/spec/output-schemas/retrieval-trace-export.schema.json +61 -0
- package/spec/output-schemas/retrieval-trace-filters.schema.json +139 -0
- package/spec/output-schemas/retrieval-trace-judgment.schema.json +15 -0
- package/spec/output-schemas/retrieval-trace-list.schema.json +18 -0
- package/spec/output-schemas/retrieval-trace-payloads.schema.json +178 -0
- package/spec/output-schemas/retrieval-trace-purge.schema.json +31 -0
- package/spec/output-schemas/retrieval-trace-qrels.schema.json +303 -0
- package/spec/output-schemas/retrieval-trace-replay.schema.json +286 -0
- package/spec/output-schemas/retrieval-trace-show.schema.json +69 -0
- package/spec/output-schemas/retrieval-trace-summary.schema.json +65 -0
- package/spec/output-schemas/search-result.schema.json +154 -0
- package/spec/output-schemas/search-results.schema.json +338 -0
- package/spec/output-schemas/similar.schema.json +84 -0
- package/spec/output-schemas/status.schema.json +676 -0
- package/spec/output-schemas/tags-list.schema.json +48 -0
- package/src/app/context-runtime-types.ts +3 -0
- package/src/app/context-runtime.ts +15 -1
- package/src/cli/commands/ask.ts +106 -36
- package/src/cli/commands/context-build.ts +56 -9
- package/src/cli/commands/get.ts +64 -3
- package/src/cli/commands/query.ts +62 -23
- package/src/cli/commands/replay.ts +140 -0
- package/src/cli/commands/search.ts +48 -3
- package/src/cli/commands/shared.ts +3 -1
- package/src/cli/commands/trace.ts +200 -0
- package/src/cli/commands/vsearch.ts +75 -53
- package/src/cli/program.ts +255 -0
- package/src/config/index.ts +9 -0
- package/src/config/retrieval-traces.ts +56 -0
- package/src/config/types.ts +4 -0
- package/src/core/context-compiler.ts +11 -4
- package/src/core/retrieval-qrels.ts +405 -0
- package/src/core/retrieval-replay-candidate.ts +368 -0
- package/src/core/retrieval-replay-types.ts +109 -0
- package/src/core/retrieval-replay-validation.ts +89 -0
- package/src/core/retrieval-replay.ts +441 -0
- package/src/core/retrieval-trace-evidence-origin.ts +175 -0
- package/src/core/retrieval-trace-export.ts +113 -0
- package/src/core/retrieval-trace-filter-normalization.ts +27 -0
- package/src/core/retrieval-trace-filters.ts +19 -0
- package/src/core/retrieval-trace-management-helpers.ts +247 -0
- package/src/core/retrieval-trace-management-types.ts +132 -0
- package/src/core/retrieval-trace-management.ts +422 -0
- package/src/core/retrieval-trace-request.ts +141 -0
- package/src/core/retrieval-trace-session.ts +494 -0
- package/src/core/retrieval-trace.ts +472 -0
- package/src/mcp/tools/context.ts +59 -8
- package/src/mcp/tools/get.ts +35 -1
- package/src/mcp/tools/index.ts +74 -0
- package/src/mcp/tools/query.ts +95 -64
- package/src/mcp/tools/search.ts +36 -13
- package/src/mcp/tools/trace.ts +143 -0
- package/src/mcp/tools/vsearch.ts +71 -38
- package/src/pipeline/answer.ts +167 -26
- package/src/pipeline/graph-retrieval.ts +15 -1
- package/src/pipeline/hybrid.ts +151 -43
- package/src/pipeline/search.ts +36 -3
- package/src/pipeline/trace-metadata.ts +47 -0
- package/src/pipeline/types.ts +43 -0
- package/src/pipeline/vsearch.ts +101 -38
- package/src/sdk/client.ts +380 -71
- package/src/sdk/documents.ts +48 -1
- package/src/sdk/index.ts +17 -0
- package/src/sdk/types.ts +28 -0
- package/src/serve/context-capsule.ts +67 -8
- package/src/serve/public/app.tsx +12 -1
- package/src/serve/public/globals.built.css +1 -1
- package/src/serve/public/lib/workspace-tabs.ts +2 -0
- package/src/serve/public/pages/Dashboard.tsx +10 -0
- package/src/serve/public/pages/TraceHistory.tsx +478 -0
- package/src/serve/public/pages/trace-history-detail.tsx +224 -0
- package/src/serve/retrieval-trace.ts +28 -0
- package/src/serve/routes/api.ts +366 -72
- package/src/serve/routes/traces.ts +156 -0
- package/src/serve/server.ts +87 -2
- package/src/store/index.ts +31 -0
- package/src/store/migrations/014-retrieval-traces.ts +303 -0
- package/src/store/migrations/index.ts +2 -0
- package/src/store/retrieval-trace-codec.ts +384 -0
- package/src/store/sqlite/adapter.ts +153 -1
- package/src/store/sqlite/retrieval-trace-management-store.ts +341 -0
- package/src/store/sqlite/retrieval-trace-retention.ts +349 -0
- package/src/store/sqlite/retrieval-trace-rows.ts +267 -0
- package/src/store/sqlite/retrieval-trace-store.ts +515 -0
- package/src/store/types.ts +297 -0
- package/src/store/vector/sqlite-vec.ts +76 -1
- package/src/store/vector/types.ts +1 -1
package/src/pipeline/hybrid.ts
CHANGED
|
@@ -55,9 +55,10 @@ import {
|
|
|
55
55
|
shouldSortByRecency,
|
|
56
56
|
} from "./temporal";
|
|
57
57
|
import {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
} from "./
|
|
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: {
|
|
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
|
-
{
|
|
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
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
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
|
-
|
|
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
|
}
|
package/src/pipeline/search.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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);
|
package/src/pipeline/types.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* @module src/pipeline/types
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import type { RetrievalTraceSession } from "../core/retrieval-trace-session";
|
|
8
9
|
import type { StoreResult } from "../store/types";
|
|
9
10
|
|
|
10
11
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -48,6 +49,11 @@ export interface SearchResultPlannerMetadata {
|
|
|
48
49
|
seq: number;
|
|
49
50
|
sources: FusionSource[];
|
|
50
51
|
graphExpanded: boolean;
|
|
52
|
+
/** Exact canonical chunk coordinates, retained even for full-content output. */
|
|
53
|
+
startLine?: number;
|
|
54
|
+
endLine?: number;
|
|
55
|
+
/** SHA-256 of the complete canonical chunk lines. */
|
|
56
|
+
passageHash?: string;
|
|
51
57
|
}
|
|
52
58
|
|
|
53
59
|
/** Single search result matching output schema */
|
|
@@ -122,10 +128,26 @@ export interface SearchMeta {
|
|
|
122
128
|
trace?: QueryDiagnoseTrace;
|
|
123
129
|
}
|
|
124
130
|
|
|
131
|
+
export const SEARCH_RESULTS_TRACE_METADATA = Symbol(
|
|
132
|
+
"gno.searchResultsTraceMetadata"
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
export interface SearchCapabilityOutcome {
|
|
136
|
+
capability: string;
|
|
137
|
+
status: "attempted" | "used" | "unavailable" | "failed";
|
|
138
|
+
reasonCode?: string;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface SearchResultsTraceMetadata {
|
|
142
|
+
capabilityOutcomes: SearchCapabilityOutcome[];
|
|
143
|
+
fallbackCodes: string[];
|
|
144
|
+
}
|
|
145
|
+
|
|
125
146
|
/** Complete search results wrapper */
|
|
126
147
|
export interface SearchResults {
|
|
127
148
|
results: SearchResult[];
|
|
128
149
|
meta: SearchMeta;
|
|
150
|
+
[SEARCH_RESULTS_TRACE_METADATA]?: SearchResultsTraceMetadata;
|
|
129
151
|
}
|
|
130
152
|
|
|
131
153
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -134,12 +156,19 @@ export interface SearchResults {
|
|
|
134
156
|
|
|
135
157
|
/** Common options for all search commands */
|
|
136
158
|
export interface SearchOptions {
|
|
159
|
+
/** Internal receipt seam; never serialized or included in public schemas. */
|
|
160
|
+
traceSession?: RetrievalTraceSession;
|
|
137
161
|
/** Max results */
|
|
138
162
|
limit?: number;
|
|
139
163
|
/** Min score threshold (0-1) */
|
|
140
164
|
minScore?: number;
|
|
141
165
|
/** Filter by collection */
|
|
142
166
|
collection?: string;
|
|
167
|
+
/** Internal exact corpus scope used by deterministic retrieval replay. */
|
|
168
|
+
retrievalScope?: {
|
|
169
|
+
relPathPrefix?: string;
|
|
170
|
+
allowedMirrorHashes: string[];
|
|
171
|
+
};
|
|
143
172
|
/** Language filter/hint (BCP-47) */
|
|
144
173
|
lang?: string;
|
|
145
174
|
/** Include full content instead of snippet */
|
|
@@ -357,11 +386,25 @@ export const DEFAULT_PIPELINE_CONFIG: PipelineConfig = {
|
|
|
357
386
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
358
387
|
|
|
359
388
|
/** Citation reference */
|
|
389
|
+
export const CITATION_TRACE_METADATA = Symbol("gno.citationTraceMetadata");
|
|
390
|
+
|
|
391
|
+
export interface CitationTraceMetadata {
|
|
392
|
+
sourceHash: string;
|
|
393
|
+
mirrorHash: string;
|
|
394
|
+
passageHash: string;
|
|
395
|
+
seq?: number;
|
|
396
|
+
rank: number;
|
|
397
|
+
plannerRank?: number;
|
|
398
|
+
sources?: FusionSource[];
|
|
399
|
+
graphExpanded?: boolean;
|
|
400
|
+
}
|
|
401
|
+
|
|
360
402
|
export interface Citation {
|
|
361
403
|
docid: string;
|
|
362
404
|
uri: string;
|
|
363
405
|
startLine?: number;
|
|
364
406
|
endLine?: number;
|
|
407
|
+
[CITATION_TRACE_METADATA]?: CitationTraceMetadata;
|
|
365
408
|
}
|
|
366
409
|
|
|
367
410
|
/** Source selection entry for answer-generation explain */
|