@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
|
@@ -5,8 +5,18 @@
|
|
|
5
5
|
* @module src/cli/commands/vsearch
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import type {
|
|
9
|
+
RetrievalTraceSession,
|
|
10
|
+
RetrievalTraceSurfaceMetadata,
|
|
11
|
+
} from "../../core/retrieval-trace-session";
|
|
12
|
+
import type { EmbeddingPort } from "../../llm/types";
|
|
8
13
|
import type { SearchOptions, SearchResults } from "../../pipeline/types";
|
|
9
14
|
|
|
15
|
+
import {
|
|
16
|
+
finishRetrievalTraceAfterError,
|
|
17
|
+
retrievalTraceFilters,
|
|
18
|
+
startRetrievalTraceRequest,
|
|
19
|
+
} from "../../core/retrieval-trace-request";
|
|
10
20
|
import { LlmAdapter } from "../../llm/nodeLlamaCpp/adapter";
|
|
11
21
|
import { resolveModelUri } from "../../llm/registry";
|
|
12
22
|
import { formatQueryForEmbedding } from "../../pipeline/contextual";
|
|
@@ -47,7 +57,11 @@ export type VsearchCommandOptions = SearchOptions & {
|
|
|
47
57
|
};
|
|
48
58
|
|
|
49
59
|
export type VsearchResult =
|
|
50
|
-
| {
|
|
60
|
+
| {
|
|
61
|
+
success: true;
|
|
62
|
+
data: SearchResults;
|
|
63
|
+
metadata?: RetrievalTraceSurfaceMetadata;
|
|
64
|
+
}
|
|
51
65
|
| { success: false; error: string };
|
|
52
66
|
|
|
53
67
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -78,6 +92,8 @@ export async function vsearch(
|
|
|
78
92
|
}
|
|
79
93
|
|
|
80
94
|
const { store, config } = initResult;
|
|
95
|
+
let embedPort: EmbeddingPort | null = null;
|
|
96
|
+
let traceSession: RetrievalTraceSession | undefined;
|
|
81
97
|
|
|
82
98
|
try {
|
|
83
99
|
// Get model URI from preset
|
|
@@ -87,67 +103,74 @@ export async function vsearch(
|
|
|
87
103
|
options.model,
|
|
88
104
|
options.collection
|
|
89
105
|
);
|
|
106
|
+
const traceStart = await startRetrievalTraceRequest({
|
|
107
|
+
store,
|
|
108
|
+
config,
|
|
109
|
+
query,
|
|
110
|
+
filters: retrievalTraceFilters({ ...options, limit }),
|
|
111
|
+
pipeline: "vector",
|
|
112
|
+
indexName: options.indexName,
|
|
113
|
+
modelUris: [modelUri],
|
|
114
|
+
});
|
|
115
|
+
if (!traceStart.ok) {
|
|
116
|
+
return { success: false, error: traceStart.error.message };
|
|
117
|
+
}
|
|
118
|
+
traceSession = traceStart.value ?? undefined;
|
|
90
119
|
|
|
91
120
|
// Create LLM adapter for embeddings
|
|
92
121
|
const llm = new LlmAdapter(config);
|
|
93
122
|
const embedResult = await llm.createEmbeddingPort(modelUri);
|
|
94
123
|
if (!embedResult.ok) {
|
|
124
|
+
await traceSession?.finish("failed");
|
|
95
125
|
return { success: false, error: embedResult.error.message };
|
|
96
126
|
}
|
|
97
127
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (!queryEmbedResult.ok) {
|
|
106
|
-
return { success: false, error: queryEmbedResult.error.message };
|
|
107
|
-
}
|
|
108
|
-
const queryEmbedding = new Float32Array(queryEmbedResult.value);
|
|
109
|
-
const dimensions = queryEmbedding.length;
|
|
110
|
-
|
|
111
|
-
// Create vector index port
|
|
112
|
-
const db = store.getRawDb();
|
|
113
|
-
const vectorResult = await createVectorIndexPort(db, {
|
|
114
|
-
model: modelUri,
|
|
115
|
-
dimensions,
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
if (!vectorResult.ok) {
|
|
119
|
-
return { success: false, error: vectorResult.error.message };
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const vectorIndex = vectorResult.value;
|
|
123
|
-
|
|
124
|
-
const deps: VectorSearchDeps = {
|
|
125
|
-
store,
|
|
126
|
-
vectorIndex,
|
|
127
|
-
embedPort,
|
|
128
|
-
config,
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
// Pass pre-computed embedding to avoid double-embed
|
|
132
|
-
const result = await searchVectorWithEmbedding(
|
|
133
|
-
deps,
|
|
134
|
-
query,
|
|
135
|
-
queryEmbedding,
|
|
136
|
-
{ ...options, limit }
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
if (!result.ok) {
|
|
140
|
-
return { success: false, error: result.error.message };
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return {
|
|
144
|
-
success: true,
|
|
145
|
-
data: decorateSearchResultsForIndex(result.value, options.indexName),
|
|
146
|
-
};
|
|
147
|
-
} finally {
|
|
148
|
-
await embedPort.dispose();
|
|
128
|
+
embedPort = embedResult.value;
|
|
129
|
+
const queryEmbedResult = await embedPort.embed(
|
|
130
|
+
formatQueryForEmbedding(query, embedPort.modelUri)
|
|
131
|
+
);
|
|
132
|
+
if (!queryEmbedResult.ok) {
|
|
133
|
+
await traceSession?.finish("failed");
|
|
134
|
+
return { success: false, error: queryEmbedResult.error.message };
|
|
149
135
|
}
|
|
136
|
+
const queryEmbedding = new Float32Array(queryEmbedResult.value);
|
|
137
|
+
const vectorResult = await createVectorIndexPort(store.getRawDb(), {
|
|
138
|
+
model: modelUri,
|
|
139
|
+
dimensions: queryEmbedding.length,
|
|
140
|
+
});
|
|
141
|
+
if (!vectorResult.ok) {
|
|
142
|
+
await traceSession?.finish("failed");
|
|
143
|
+
return { success: false, error: vectorResult.error.message };
|
|
144
|
+
}
|
|
145
|
+
const deps: VectorSearchDeps = {
|
|
146
|
+
store,
|
|
147
|
+
vectorIndex: vectorResult.value,
|
|
148
|
+
embedPort,
|
|
149
|
+
config,
|
|
150
|
+
};
|
|
151
|
+
const result = await searchVectorWithEmbedding(
|
|
152
|
+
deps,
|
|
153
|
+
query,
|
|
154
|
+
queryEmbedding,
|
|
155
|
+
{ ...options, limit, traceSession }
|
|
156
|
+
);
|
|
157
|
+
if (!result.ok) {
|
|
158
|
+
await traceSession?.finish("failed");
|
|
159
|
+
return { success: false, error: result.error.message };
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
success: true,
|
|
163
|
+
data: decorateSearchResultsForIndex(result.value, options.indexName),
|
|
164
|
+
metadata: traceSession?.metadata(),
|
|
165
|
+
};
|
|
166
|
+
} catch (cause) {
|
|
167
|
+
await finishRetrievalTraceAfterError(traceSession, cause);
|
|
168
|
+
return {
|
|
169
|
+
success: false,
|
|
170
|
+
error: cause instanceof Error ? cause.message : "Vector search failed",
|
|
171
|
+
};
|
|
150
172
|
} finally {
|
|
173
|
+
await embedPort?.dispose();
|
|
151
174
|
await store.close();
|
|
152
175
|
}
|
|
153
176
|
}
|
|
@@ -194,7 +217,6 @@ export function formatVsearch(
|
|
|
194
217
|
})
|
|
195
218
|
: `Error: ${result.error}`;
|
|
196
219
|
}
|
|
197
|
-
|
|
198
220
|
const formatOpts: FormatOptions = {
|
|
199
221
|
format: getFormatType(options),
|
|
200
222
|
full: options.full,
|
package/src/cli/program.ts
CHANGED
|
@@ -164,6 +164,15 @@ async function writeOutput(
|
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
+
/** Emit opt-in trace identity without changing command stdout payloads. */
|
|
168
|
+
export function writeRetrievalTraceReceipt(
|
|
169
|
+
metadata: { traceId: string } | undefined
|
|
170
|
+
): void {
|
|
171
|
+
if (metadata) {
|
|
172
|
+
process.stderr.write(`Trace: ${metadata.traceId}\n`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
167
176
|
async function resolveTerminalLinkPolicy(
|
|
168
177
|
format: "terminal" | "json" | "files" | "csv" | "md" | "xml"
|
|
169
178
|
): Promise<
|
|
@@ -292,6 +301,7 @@ export function createProgram(): Command {
|
|
|
292
301
|
wireOnboardingCommands(program);
|
|
293
302
|
wireCaptureCommand(program);
|
|
294
303
|
wireManagementCommands(program);
|
|
304
|
+
wireTraceCommands(program);
|
|
295
305
|
wirePublishCommand(program);
|
|
296
306
|
wireVecCommands(program);
|
|
297
307
|
wireRetrievalCommands(program);
|
|
@@ -315,6 +325,244 @@ Report issues: ${ISSUES_URL}`
|
|
|
315
325
|
return program;
|
|
316
326
|
}
|
|
317
327
|
|
|
328
|
+
function wireTraceCommands(program: Command): void {
|
|
329
|
+
const traceCmd = program
|
|
330
|
+
.command("trace")
|
|
331
|
+
.description("Inspect and manage private local retrieval traces");
|
|
332
|
+
|
|
333
|
+
const outputFormat = (options: Record<string, unknown>): "json" | "md" =>
|
|
334
|
+
getFormat(options) === "json" ? "json" : "md";
|
|
335
|
+
|
|
336
|
+
traceCmd
|
|
337
|
+
.command("list")
|
|
338
|
+
.description("List bounded, redacted retrieval trace summaries")
|
|
339
|
+
.option("-n, --limit <num>", "maximum traces", "50")
|
|
340
|
+
.option("--cursor <cursor>", "continue from a previous list receipt")
|
|
341
|
+
.option("--json", "JSON output")
|
|
342
|
+
.option("--md", "Markdown output")
|
|
343
|
+
.action(async (cmdOpts: Record<string, unknown>) => {
|
|
344
|
+
const globals = getGlobals();
|
|
345
|
+
const { traceList } = await import("./commands/trace");
|
|
346
|
+
const output = await traceList(
|
|
347
|
+
{
|
|
348
|
+
limit: parsePositiveInt("limit", cmdOpts.limit),
|
|
349
|
+
cursor: cmdOpts.cursor as string | undefined,
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
configPath: globals.config,
|
|
353
|
+
indexName: globals.index,
|
|
354
|
+
format: outputFormat(cmdOpts),
|
|
355
|
+
}
|
|
356
|
+
);
|
|
357
|
+
process.stdout.write(output);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
traceCmd
|
|
361
|
+
.command("show <trace-id>")
|
|
362
|
+
.description("Inspect one bounded retrieval trace receipt")
|
|
363
|
+
.option("--detail-limit <num>", "maximum records per detail section", "500")
|
|
364
|
+
.option("--json", "JSON output")
|
|
365
|
+
.option("--md", "Markdown output")
|
|
366
|
+
.action(async (traceId: string, cmdOpts: Record<string, unknown>) => {
|
|
367
|
+
const globals = getGlobals();
|
|
368
|
+
const { traceShow } = await import("./commands/trace");
|
|
369
|
+
const output = await traceShow(
|
|
370
|
+
traceId,
|
|
371
|
+
{
|
|
372
|
+
detailLimit: parsePositiveInt("detail-limit", cmdOpts.detailLimit),
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
configPath: globals.config,
|
|
376
|
+
indexName: globals.index,
|
|
377
|
+
format: outputFormat(cmdOpts),
|
|
378
|
+
}
|
|
379
|
+
);
|
|
380
|
+
process.stdout.write(output);
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
traceCmd
|
|
384
|
+
.command("label <trace-id>")
|
|
385
|
+
.description("Append an explicit retrieval relevance judgment")
|
|
386
|
+
.requiredOption(
|
|
387
|
+
"--label <label>",
|
|
388
|
+
"relevant, irrelevant, or missing-expected"
|
|
389
|
+
)
|
|
390
|
+
.requiredOption("--target <ref>", "evidence or expected-document reference")
|
|
391
|
+
.option("--target-kind <kind>", "document, chunk, or span")
|
|
392
|
+
.option("--from-line <num>", "exact evidence start line")
|
|
393
|
+
.option("--to-line <num>", "exact evidence end line")
|
|
394
|
+
.option("--source-hash <sha256>", "expected immutable source hash")
|
|
395
|
+
.option("--docid <docid>", "expected document ID")
|
|
396
|
+
.option("--idempotency-key <key>", "caller retry key")
|
|
397
|
+
.option("--json", "JSON output")
|
|
398
|
+
.option("--md", "Markdown output")
|
|
399
|
+
.action(async (traceId: string, cmdOpts: Record<string, unknown>) => {
|
|
400
|
+
const globals = getGlobals();
|
|
401
|
+
const rawLabel = String(cmdOpts.label).replace("-", "_");
|
|
402
|
+
if (!["relevant", "irrelevant", "missing_expected"].includes(rawLabel)) {
|
|
403
|
+
throw new CliError(
|
|
404
|
+
"VALIDATION",
|
|
405
|
+
"--label must be relevant, irrelevant, or missing-expected"
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
const rawTargetKind = cmdOpts.targetKind;
|
|
409
|
+
if (
|
|
410
|
+
rawTargetKind !== undefined &&
|
|
411
|
+
(typeof rawTargetKind !== "string" ||
|
|
412
|
+
!["document", "chunk", "span"].includes(rawTargetKind))
|
|
413
|
+
) {
|
|
414
|
+
throw new CliError(
|
|
415
|
+
"VALIDATION",
|
|
416
|
+
"--target-kind must be document, chunk, or span"
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
const { traceLabel } = await import("./commands/trace");
|
|
420
|
+
const output = await traceLabel(
|
|
421
|
+
{
|
|
422
|
+
traceId,
|
|
423
|
+
label: rawLabel as "relevant" | "irrelevant" | "missing_expected",
|
|
424
|
+
targetRef: String(cmdOpts.target),
|
|
425
|
+
targetKind: rawTargetKind as
|
|
426
|
+
| "document"
|
|
427
|
+
| "chunk"
|
|
428
|
+
| "span"
|
|
429
|
+
| undefined,
|
|
430
|
+
startLine:
|
|
431
|
+
cmdOpts.fromLine === undefined
|
|
432
|
+
? undefined
|
|
433
|
+
: parsePositiveInt("from-line", cmdOpts.fromLine),
|
|
434
|
+
endLine:
|
|
435
|
+
cmdOpts.toLine === undefined
|
|
436
|
+
? undefined
|
|
437
|
+
: parsePositiveInt("to-line", cmdOpts.toLine),
|
|
438
|
+
sourceHash: cmdOpts.sourceHash as string | undefined,
|
|
439
|
+
docid: cmdOpts.docid as string | undefined,
|
|
440
|
+
idempotencyKey: cmdOpts.idempotencyKey as string | undefined,
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
configPath: globals.config,
|
|
444
|
+
indexName: globals.index,
|
|
445
|
+
format: outputFormat(cmdOpts),
|
|
446
|
+
}
|
|
447
|
+
);
|
|
448
|
+
process.stdout.write(output);
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
traceCmd
|
|
452
|
+
.command("export <trace-ids...>")
|
|
453
|
+
.description("Export immutable terminal traces as one local receipt")
|
|
454
|
+
.option("--format <format>", "agentic-receipt or qrels", "agentic-receipt")
|
|
455
|
+
.option("--output <path>", "write canonical artifact atomically")
|
|
456
|
+
.option("--json", "JSON output")
|
|
457
|
+
.action(async (traceIds: string[], cmdOpts: Record<string, unknown>) => {
|
|
458
|
+
const globals = getGlobals();
|
|
459
|
+
const exportFormat = String(cmdOpts.format);
|
|
460
|
+
if (!["agentic-receipt", "qrels"].includes(exportFormat)) {
|
|
461
|
+
throw new CliError(
|
|
462
|
+
"VALIDATION",
|
|
463
|
+
"--format must be agentic-receipt or qrels"
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
const { traceExport } = await import("./commands/trace");
|
|
467
|
+
process.stdout.write(
|
|
468
|
+
await traceExport(traceIds, {
|
|
469
|
+
configPath: globals.config,
|
|
470
|
+
indexName: globals.index,
|
|
471
|
+
format: "json",
|
|
472
|
+
output: cmdOpts.output as string | undefined,
|
|
473
|
+
exportFormat: exportFormat as "agentic-receipt" | "qrels",
|
|
474
|
+
})
|
|
475
|
+
);
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
traceCmd
|
|
479
|
+
.command("replay <export-id>")
|
|
480
|
+
.description("Compare one immutable qrels baseline with a candidate")
|
|
481
|
+
.requiredOption("--candidate <type>", "bm25, vector, or hybrid")
|
|
482
|
+
.option("-n, --limit <num>", "result cutoff")
|
|
483
|
+
.option("--candidate-limit <num>", "candidate pool size")
|
|
484
|
+
.option("--no-expand", "disable query expansion")
|
|
485
|
+
.option("--no-rerank", "disable reranking")
|
|
486
|
+
.option("--json", "JSON output")
|
|
487
|
+
.option("--md", "Markdown output")
|
|
488
|
+
.action(async (exportId: string, cmdOpts: Record<string, unknown>) => {
|
|
489
|
+
const globals = getGlobals();
|
|
490
|
+
const candidateType = String(cmdOpts.candidate);
|
|
491
|
+
if (!["bm25", "vector", "hybrid"].includes(candidateType)) {
|
|
492
|
+
throw new CliError(
|
|
493
|
+
"VALIDATION",
|
|
494
|
+
"--candidate must be bm25, vector, or hybrid"
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
const { traceReplay } = await import("./commands/replay");
|
|
498
|
+
process.stdout.write(
|
|
499
|
+
await traceReplay(
|
|
500
|
+
exportId,
|
|
501
|
+
{
|
|
502
|
+
id: `cli-${candidateType}`,
|
|
503
|
+
type: candidateType as "bm25" | "vector" | "hybrid",
|
|
504
|
+
limit:
|
|
505
|
+
cmdOpts.limit === undefined
|
|
506
|
+
? undefined
|
|
507
|
+
: parsePositiveInt("limit", cmdOpts.limit),
|
|
508
|
+
candidateLimit:
|
|
509
|
+
cmdOpts.candidateLimit === undefined
|
|
510
|
+
? undefined
|
|
511
|
+
: parsePositiveInt("candidate-limit", cmdOpts.candidateLimit),
|
|
512
|
+
noExpand: cmdOpts.expand === false,
|
|
513
|
+
noRerank: cmdOpts.rerank === false,
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
configPath: globals.config,
|
|
517
|
+
indexName: globals.index,
|
|
518
|
+
format: outputFormat(cmdOpts),
|
|
519
|
+
offline: globals.offline,
|
|
520
|
+
}
|
|
521
|
+
)
|
|
522
|
+
);
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
traceCmd
|
|
526
|
+
.command("delete <trace-id>")
|
|
527
|
+
.description("Delete one trace and every owned local record")
|
|
528
|
+
.option("--json", "JSON output")
|
|
529
|
+
.option("--md", "Markdown output")
|
|
530
|
+
.action(async (traceId: string, cmdOpts: Record<string, unknown>) => {
|
|
531
|
+
const globals = getGlobals();
|
|
532
|
+
const { traceDelete } = await import("./commands/trace");
|
|
533
|
+
process.stdout.write(
|
|
534
|
+
await traceDelete(traceId, {
|
|
535
|
+
configPath: globals.config,
|
|
536
|
+
indexName: globals.index,
|
|
537
|
+
format: outputFormat(cmdOpts),
|
|
538
|
+
})
|
|
539
|
+
);
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
traceCmd
|
|
543
|
+
.command("purge")
|
|
544
|
+
.description("Delete every local retrieval trace receipt")
|
|
545
|
+
.option("--json", "JSON output")
|
|
546
|
+
.option("--md", "Markdown output")
|
|
547
|
+
.action(async (cmdOpts: Record<string, unknown>) => {
|
|
548
|
+
const globals = getGlobals();
|
|
549
|
+
if (!globals.yes) {
|
|
550
|
+
throw new CliError(
|
|
551
|
+
"VALIDATION",
|
|
552
|
+
"Trace purge requires the global --yes confirmation"
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
const { tracePurge } = await import("./commands/trace");
|
|
556
|
+
process.stdout.write(
|
|
557
|
+
await tracePurge({
|
|
558
|
+
configPath: globals.config,
|
|
559
|
+
indexName: globals.index,
|
|
560
|
+
format: outputFormat(cmdOpts),
|
|
561
|
+
})
|
|
562
|
+
);
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
|
|
318
566
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
319
567
|
// Search Commands (search, vsearch, query, ask)
|
|
320
568
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -435,6 +683,7 @@ function wireSearchCommands(program: Command): void {
|
|
|
435
683
|
terminalLinks: await resolveTerminalLinkPolicy(format),
|
|
436
684
|
});
|
|
437
685
|
await writeOutput(output, format);
|
|
686
|
+
writeRetrievalTraceReceipt(result.metadata);
|
|
438
687
|
});
|
|
439
688
|
|
|
440
689
|
// vsearch - Vector similarity search
|
|
@@ -547,6 +796,7 @@ function wireSearchCommands(program: Command): void {
|
|
|
547
796
|
terminalLinks: await resolveTerminalLinkPolicy(format),
|
|
548
797
|
});
|
|
549
798
|
await writeOutput(output, format);
|
|
799
|
+
writeRetrievalTraceReceipt(result.metadata);
|
|
550
800
|
});
|
|
551
801
|
|
|
552
802
|
// query - Hybrid search with expansion and reranking
|
|
@@ -768,6 +1018,7 @@ function wireSearchCommands(program: Command): void {
|
|
|
768
1018
|
terminalLinks: await resolveTerminalLinkPolicy(format),
|
|
769
1019
|
});
|
|
770
1020
|
await writeOutput(output, format);
|
|
1021
|
+
writeRetrievalTraceReceipt(result.metadata);
|
|
771
1022
|
});
|
|
772
1023
|
|
|
773
1024
|
// bench - Retrieval benchmark fixture runner
|
|
@@ -960,6 +1211,7 @@ function wireSearchCommands(program: Command): void {
|
|
|
960
1211
|
showSources,
|
|
961
1212
|
});
|
|
962
1213
|
await writeOutput(output, format);
|
|
1214
|
+
writeRetrievalTraceReceipt(result.metadata);
|
|
963
1215
|
});
|
|
964
1216
|
}
|
|
965
1217
|
|
|
@@ -1191,6 +1443,7 @@ function wireRetrievalCommands(program: Command): void {
|
|
|
1191
1443
|
parsePositiveInt.bind(null, "limit")
|
|
1192
1444
|
)
|
|
1193
1445
|
.option("--line-numbers", "Prefix lines with numbers")
|
|
1446
|
+
.option("--trace-id <id>", "Continue an open retrieval trace")
|
|
1194
1447
|
.option("--source", "Include source metadata")
|
|
1195
1448
|
.option("--json", "JSON output")
|
|
1196
1449
|
.option("--md", "Markdown output")
|
|
@@ -1209,6 +1462,7 @@ function wireRetrievalCommands(program: Command): void {
|
|
|
1209
1462
|
source: Boolean(cmdOpts.source),
|
|
1210
1463
|
json: format === "json",
|
|
1211
1464
|
md: format === "md",
|
|
1465
|
+
traceId: cmdOpts.traceId as string | undefined,
|
|
1212
1466
|
});
|
|
1213
1467
|
|
|
1214
1468
|
if (!result.success) {
|
|
@@ -1225,6 +1479,7 @@ function wireRetrievalCommands(program: Command): void {
|
|
|
1225
1479
|
md: format === "md",
|
|
1226
1480
|
})}\n`
|
|
1227
1481
|
);
|
|
1482
|
+
writeRetrievalTraceReceipt(result.metadata);
|
|
1228
1483
|
});
|
|
1229
1484
|
|
|
1230
1485
|
// multi-get - Retrieve multiple documents
|
package/src/config/index.ts
CHANGED
|
@@ -70,3 +70,12 @@ export {
|
|
|
70
70
|
ScopeTypeSchema,
|
|
71
71
|
} from "./types";
|
|
72
72
|
export type { HttpGatewayConfig } from "./types";
|
|
73
|
+
export {
|
|
74
|
+
RETRIEVAL_TRACE_DEFAULT_RETENTION,
|
|
75
|
+
type RetrievalTraceConfig,
|
|
76
|
+
RetrievalTraceConfigSchema,
|
|
77
|
+
type RetrievalTraceRedactionMode,
|
|
78
|
+
RetrievalTraceRedactionModeSchema,
|
|
79
|
+
type RetrievalTraceRetention,
|
|
80
|
+
RetrievalTraceRetentionSchema,
|
|
81
|
+
} from "./retrieval-traces";
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/** Opt-in retrieval trace configuration and bounded-retention defaults. */
|
|
2
|
+
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
export const RETRIEVAL_TRACE_DEFAULT_RETENTION = {
|
|
6
|
+
maxAgeDays: 30,
|
|
7
|
+
maxTraces: 1_000,
|
|
8
|
+
maxRecordsPerTrace: 10_000,
|
|
9
|
+
maxBytes: 16 * 1024 * 1024,
|
|
10
|
+
} as const;
|
|
11
|
+
|
|
12
|
+
export const RetrievalTraceRetentionSchema = z
|
|
13
|
+
.object({
|
|
14
|
+
maxAgeDays: z.number().int().min(1).max(3_650),
|
|
15
|
+
maxTraces: z.number().int().min(1).max(1_000_000),
|
|
16
|
+
maxRecordsPerTrace: z.number().int().min(1).max(100_000),
|
|
17
|
+
maxBytes: z
|
|
18
|
+
.number()
|
|
19
|
+
.int()
|
|
20
|
+
.min(64 * 1024)
|
|
21
|
+
.max(1024 * 1024 * 1024),
|
|
22
|
+
})
|
|
23
|
+
.strict();
|
|
24
|
+
|
|
25
|
+
export const RetrievalTraceRedactionModeSchema = z.enum(["metadata", "replay"]);
|
|
26
|
+
|
|
27
|
+
const DisabledRetrievalTraceConfigSchema = z
|
|
28
|
+
.object({
|
|
29
|
+
enabled: z.literal(false),
|
|
30
|
+
})
|
|
31
|
+
.strict();
|
|
32
|
+
|
|
33
|
+
const EnabledRetrievalTraceConfigSchema = z
|
|
34
|
+
.object({
|
|
35
|
+
enabled: z.literal(true),
|
|
36
|
+
/**
|
|
37
|
+
* `replay` stores replay inputs such as the raw query and filters. Choosing
|
|
38
|
+
* it is explicit consent; the safer metadata-only projection is recommended.
|
|
39
|
+
*/
|
|
40
|
+
redactionMode: RetrievalTraceRedactionModeSchema,
|
|
41
|
+
retention: RetrievalTraceRetentionSchema,
|
|
42
|
+
})
|
|
43
|
+
.strict();
|
|
44
|
+
|
|
45
|
+
export const RetrievalTraceConfigSchema = z.discriminatedUnion("enabled", [
|
|
46
|
+
DisabledRetrievalTraceConfigSchema,
|
|
47
|
+
EnabledRetrievalTraceConfigSchema,
|
|
48
|
+
]);
|
|
49
|
+
|
|
50
|
+
export type RetrievalTraceConfig = z.infer<typeof RetrievalTraceConfigSchema>;
|
|
51
|
+
export type RetrievalTraceRetention = z.infer<
|
|
52
|
+
typeof RetrievalTraceRetentionSchema
|
|
53
|
+
>;
|
|
54
|
+
export type RetrievalTraceRedactionMode = z.infer<
|
|
55
|
+
typeof RetrievalTraceRedactionModeSchema
|
|
56
|
+
>;
|
package/src/config/types.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
|
|
10
10
|
import { URI_PREFIX } from "../app/constants";
|
|
11
|
+
import { RetrievalTraceConfigSchema } from "./retrieval-traces";
|
|
11
12
|
|
|
12
13
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
13
14
|
// Constants
|
|
@@ -338,6 +339,9 @@ export const ConfigSchema = z.object({
|
|
|
338
339
|
|
|
339
340
|
/** Resident Streamable HTTP MCP gateway configuration */
|
|
340
341
|
gateway: HttpGatewayConfigSchema.optional(),
|
|
342
|
+
|
|
343
|
+
/** Private local retrieval trace recording. Absent means recording off. */
|
|
344
|
+
retrievalTraces: RetrievalTraceConfigSchema.optional(),
|
|
341
345
|
});
|
|
342
346
|
|
|
343
347
|
export type Config = Omit<z.infer<typeof ConfigSchema>, "contentTypes"> & {
|
|
@@ -29,6 +29,7 @@ import type { ContextConfiguredGuidance } from "./context-guidance";
|
|
|
29
29
|
import { decorateUriForIndex } from "../app/constants";
|
|
30
30
|
import { canonicalizeIndexName } from "../app/index-name";
|
|
31
31
|
import { resolveTemporalRange } from "../pipeline/temporal";
|
|
32
|
+
import { attachSearchResultPlannerMetadata } from "../pipeline/trace-metadata";
|
|
32
33
|
import {
|
|
33
34
|
SEARCH_RESULT_PLANNER_METADATA,
|
|
34
35
|
type SearchResultPlannerMetadata,
|
|
@@ -363,10 +364,16 @@ export const planContextEvidence = async <T, P>(
|
|
|
363
364
|
}
|
|
364
365
|
const decoratedResults = responses
|
|
365
366
|
.flatMap((response) => response.results)
|
|
366
|
-
.map((result) =>
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
367
|
+
.map((result) => {
|
|
368
|
+
const decorated = {
|
|
369
|
+
...result,
|
|
370
|
+
uri: decorateUriForIndex(result.uri, indexName),
|
|
371
|
+
};
|
|
372
|
+
const metadata = result[SEARCH_RESULT_PLANNER_METADATA];
|
|
373
|
+
return metadata
|
|
374
|
+
? attachSearchResultPlannerMetadata(decorated, metadata)
|
|
375
|
+
: decorated;
|
|
376
|
+
});
|
|
370
377
|
const results = decoratedResults
|
|
371
378
|
.map((result, index) => ({
|
|
372
379
|
result,
|