@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
@@ -26,6 +26,15 @@ import {
26
26
  normalizeContentTypes,
27
27
  } from "../../config";
28
28
  import { resolveDepthPolicy } from "../../core/depth-policy";
29
+ import {
30
+ finishRetrievalTraceAfterError,
31
+ retrievalTraceFilters,
32
+ startRetrievalTraceRequest,
33
+ } from "../../core/retrieval-trace-request";
34
+ import {
35
+ attachRetrievalTraceMetadata,
36
+ type RetrievalTraceSession,
37
+ } from "../../core/retrieval-trace-session";
29
38
  import { normalizeStructuredQueryInput } from "../../core/structured-query";
30
39
  import { LlmAdapter } from "../../llm/nodeLlamaCpp/adapter";
31
40
  import { resolveDownloadPolicy } from "../../llm/policy";
@@ -195,26 +204,79 @@ export function handleQuery(
195
204
  : undefined;
196
205
 
197
206
  const preset = getActivePreset(ctx.config);
198
- const llm = new LlmAdapter(ctx.config);
199
-
200
- // Resolve download policy from env (MCP has no CLI flags)
201
- const policy = resolveDownloadPolicy(process.env, {});
202
-
203
- // Non-TTY progress for MCP (periodic lines to stderr, not \r)
204
- const downloadProgress = createNonTtyProgressRenderer();
205
207
 
206
208
  let embedPort: EmbeddingPort | null = null;
207
209
  let expandPort: GenerationPort | null = null;
208
210
  let rerankPort: RerankPort | null = null;
209
211
  let vectorIndex: VectorIndexPort | null = null;
212
+ let traceSession: RetrievalTraceSession | undefined;
210
213
  const embedUri = resolveModelUri(
211
214
  ctx.config,
212
215
  "embed",
213
216
  undefined,
214
217
  args.collection
215
218
  );
219
+ const hasStructuredModes = Boolean(queryModes?.length);
220
+ const depthPolicy = resolveDepthPolicy({
221
+ presetId: preset.id,
222
+ fast: args.fast,
223
+ thorough: args.thorough,
224
+ expand: args.expand,
225
+ rerank: args.rerank,
226
+ candidateLimit: args.candidateLimit,
227
+ hasStructuredModes,
228
+ });
229
+ const { noExpand, noRerank } = depthPolicy;
230
+ const expandUri =
231
+ !noExpand && !hasStructuredModes
232
+ ? resolveModelUri(ctx.config, "expand", undefined, args.collection)
233
+ : undefined;
234
+ const rerankUri = !noRerank
235
+ ? resolveModelUri(ctx.config, "rerank", undefined, args.collection)
236
+ : undefined;
237
+ const options = {
238
+ limit: args.limit ?? 5,
239
+ minScore: args.minScore,
240
+ collection: args.collection,
241
+ queryLanguageHint: args.lang,
242
+ intent: args.intent,
243
+ candidateLimit: depthPolicy.candidateLimit,
244
+ exclude: args.exclude,
245
+ since: args.since,
246
+ until: args.until,
247
+ categories: args.categories,
248
+ author: args.author,
249
+ noExpand,
250
+ noRerank,
251
+ graph: args.graph === true,
252
+ noGraph: args.noGraph || args.fast,
253
+ queryModes,
254
+ tagsAll: normalizeTagFilters(args.tagsAll),
255
+ tagsAny: normalizeTagFilters(args.tagsAny),
256
+ };
216
257
 
217
258
  try {
259
+ const traceStart = await startRetrievalTraceRequest({
260
+ store: ctx.store,
261
+ config: ctx.config,
262
+ query: queryText,
263
+ filters: retrievalTraceFilters(options),
264
+ pipeline: "hybrid",
265
+ indexName: ctx.indexName,
266
+ modelUris: [embedUri, expandUri, rerankUri].filter(
267
+ (value): value is string => Boolean(value)
268
+ ),
269
+ });
270
+ if (!traceStart.ok) throw new Error(traceStart.error.message);
271
+ traceSession = traceStart.value ?? undefined;
272
+ const llm = new LlmAdapter(ctx.config);
273
+
274
+ // Resolve download policy from env (MCP has no CLI flags)
275
+ const policy = resolveDownloadPolicy(process.env, {});
276
+
277
+ // Non-TTY progress for MCP (periodic lines to stderr, not \r)
278
+ const downloadProgress = createNonTtyProgressRenderer();
279
+
218
280
  // Create embedding port (for vector search) - optional
219
281
  const embedResult = await llm.createEmbeddingPort(embedUri, {
220
282
  policy,
@@ -224,41 +286,23 @@ export function handleQuery(
224
286
  embedPort = embedResult.value;
225
287
  }
226
288
 
227
- const hasStructuredModes = Boolean(queryModes?.length);
228
- const depthPolicy = resolveDepthPolicy({
229
- presetId: preset.id,
230
- fast: args.fast,
231
- thorough: args.thorough,
232
- expand: args.expand,
233
- rerank: args.rerank,
234
- candidateLimit: args.candidateLimit,
235
- hasStructuredModes,
236
- });
237
- const { noExpand, noRerank } = depthPolicy;
238
-
239
289
  // Create expansion port - optional
240
- if (!noExpand && !hasStructuredModes) {
241
- const genResult = await llm.createExpansionPort(
242
- resolveModelUri(ctx.config, "expand", undefined, args.collection),
243
- {
244
- policy,
245
- onProgress: (progress) => downloadProgress("expand", progress),
246
- }
247
- );
290
+ if (expandUri) {
291
+ const genResult = await llm.createExpansionPort(expandUri, {
292
+ policy,
293
+ onProgress: (progress) => downloadProgress("expand", progress),
294
+ });
248
295
  if (genResult.ok) {
249
296
  expandPort = genResult.value;
250
297
  }
251
298
  }
252
299
 
253
300
  // Create rerank port - optional
254
- if (!noRerank) {
255
- const rerankResult = await llm.createRerankPort(
256
- resolveModelUri(ctx.config, "rerank", undefined, args.collection),
257
- {
258
- policy,
259
- onProgress: (progress) => downloadProgress("rerank", progress),
260
- }
261
- );
301
+ if (rerankUri) {
302
+ const rerankResult = await llm.createRerankPort(rerankUri, {
303
+ policy,
304
+ onProgress: (progress) => downloadProgress("rerank", progress),
305
+ });
262
306
  if (rerankResult.ok) {
263
307
  rerankPort = rerankResult.value;
264
308
  }
@@ -289,28 +333,9 @@ export function handleQuery(
289
333
  rerankPort,
290
334
  };
291
335
 
292
- // Note: per spec, lang is a "hint" for query, not a filter
293
- // Pass as queryLanguageHint to affect expansion prompt selection
294
- // but NOT retrieval filtering (that would be options.lang)
295
336
  const result = await searchHybrid(deps, queryText, {
296
- limit: args.limit ?? 5,
297
- minScore: args.minScore,
298
- collection: args.collection,
299
- queryLanguageHint: args.lang, // Affects expansion prompt, not retrieval
300
- intent: args.intent,
301
- candidateLimit: depthPolicy.candidateLimit,
302
- exclude: args.exclude,
303
- since: args.since,
304
- until: args.until,
305
- categories: args.categories,
306
- author: args.author,
307
- noExpand,
308
- noRerank,
309
- graph: args.graph === true,
310
- noGraph: args.noGraph || args.fast,
311
- queryModes,
312
- tagsAll: normalizeTagFilters(args.tagsAll),
313
- tagsAny: normalizeTagFilters(args.tagsAny),
337
+ ...options,
338
+ traceSession,
314
339
  });
315
340
 
316
341
  if (!result.ok) {
@@ -320,15 +345,21 @@ export function handleQuery(
320
345
  // Enrich with absPath
321
346
  const enrichedResults = enrichWithAbsPath(result.value.results, ctx);
322
347
 
323
- return {
324
- ...result.value,
325
- results: enrichedResults,
326
- meta: {
327
- ...result.value.meta,
328
- // Add queryLanguage hint if provided
329
- ...(args.lang ? { queryLanguage: args.lang } : {}),
348
+ return attachRetrievalTraceMetadata(
349
+ {
350
+ ...result.value,
351
+ results: enrichedResults,
352
+ meta: {
353
+ ...result.value.meta,
354
+ // Add queryLanguage hint if provided
355
+ ...(args.lang ? { queryLanguage: args.lang } : {}),
356
+ },
330
357
  },
331
- };
358
+ traceSession
359
+ );
360
+ } catch (cause) {
361
+ await finishRetrievalTraceAfterError(traceSession, cause);
362
+ throw cause;
332
363
  } finally {
333
364
  if (embedPort) {
334
365
  await embedPort.dispose();
@@ -6,10 +6,17 @@
6
6
 
7
7
  import { join as pathJoin } from "node:path";
8
8
 
9
+ import type { RetrievalTraceSession } from "../../core/retrieval-trace-session";
9
10
  import type { SearchResult, SearchResults } from "../../pipeline/types";
10
11
  import type { ToolContext } from "../server";
11
12
 
12
13
  import { decorateUriForIndex, parseUri } from "../../app/constants";
14
+ import {
15
+ finishRetrievalTraceAfterError,
16
+ retrievalTraceFilters,
17
+ startRetrievalTraceRequest,
18
+ } from "../../core/retrieval-trace-request";
19
+ import { attachRetrievalTraceMetadata } from "../../core/retrieval-trace-session";
13
20
  import { searchBm25 } from "../../pipeline/search";
14
21
  import { normalizeTagFilters, runTool, type ToolResult } from "./index";
15
22
 
@@ -106,7 +113,7 @@ export function handleSearch(
106
113
  }
107
114
  }
108
115
 
109
- const result = await searchBm25(ctx.store, args.query, {
116
+ const options = {
110
117
  limit: args.limit ?? 5,
111
118
  minScore: args.minScore,
112
119
  collection: args.collection,
@@ -119,19 +126,35 @@ export function handleSearch(
119
126
  author: args.author,
120
127
  tagsAll: normalizeTagFilters(args.tagsAll),
121
128
  tagsAny: normalizeTagFilters(args.tagsAny),
122
- });
123
-
124
- if (!result.ok) {
125
- throw new Error(result.error.message);
126
- }
127
-
128
- // Enrich with absPath
129
- const enrichedResults = enrichWithAbsPath(result.value.results, ctx);
130
-
131
- return {
132
- ...result.value,
133
- results: enrichedResults,
134
129
  };
130
+ let traceSession: RetrievalTraceSession | undefined;
131
+ try {
132
+ const traceStart = await startRetrievalTraceRequest({
133
+ store: ctx.store,
134
+ config: ctx.config,
135
+ query: args.query,
136
+ filters: retrievalTraceFilters(options),
137
+ pipeline: "bm25",
138
+ indexName: ctx.indexName,
139
+ });
140
+ if (!traceStart.ok) throw new Error(traceStart.error.message);
141
+ traceSession = traceStart.value ?? undefined;
142
+ const result = await searchBm25(ctx.store, args.query, {
143
+ ...options,
144
+ traceSession,
145
+ });
146
+ if (!result.ok) throw new Error(result.error.message);
147
+ return attachRetrievalTraceMetadata(
148
+ {
149
+ ...result.value,
150
+ results: enrichWithAbsPath(result.value.results, ctx),
151
+ },
152
+ traceSession
153
+ );
154
+ } catch (cause) {
155
+ await finishRetrievalTraceAfterError(traceSession, cause);
156
+ throw cause;
157
+ }
135
158
  },
136
159
  formatSearchResults
137
160
  );
@@ -0,0 +1,143 @@
1
+ /** MCP tools for explicit local retrieval-trace management. */
2
+
3
+ import { z } from "zod";
4
+
5
+ import type {
6
+ RetrievalTraceExportRequest,
7
+ RetrievalTraceLabelRequest,
8
+ } from "../../core/retrieval-trace-management";
9
+ import type { StoreResult } from "../../store/types";
10
+ import type { ToolContext } from "../server";
11
+
12
+ import { withWriteLock } from "../../core/file-lock";
13
+ import { RetrievalTraceManagementService } from "../../core/retrieval-trace-management";
14
+ import { runTool, type ToolResult } from "./index";
15
+
16
+ export const traceListInputSchema = z.object({
17
+ limit: z.number().int().min(1).max(1000).default(50),
18
+ cursor: z.string().min(1).max(512).optional(),
19
+ });
20
+
21
+ export const traceShowInputSchema = z.object({
22
+ traceId: z.string().min(1).max(128),
23
+ detailLimit: z.number().int().min(1).max(5000).default(500),
24
+ });
25
+
26
+ export const traceLabelInputSchema = z.object({
27
+ traceId: z.string().min(1).max(128),
28
+ label: z.enum(["relevant", "irrelevant", "missing_expected"]),
29
+ targetRef: z.string().min(1).max(4096),
30
+ targetKind: z.enum(["document", "chunk", "span"]).optional(),
31
+ startLine: z.number().int().positive().optional(),
32
+ endLine: z.number().int().positive().optional(),
33
+ sourceHash: z
34
+ .string()
35
+ .regex(/^[a-f0-9]{64}$/)
36
+ .optional(),
37
+ docid: z.string().min(1).max(256).optional(),
38
+ idempotencyKey: z.string().min(1).max(256).optional(),
39
+ });
40
+
41
+ export const traceExportInputSchema = z.object({
42
+ traceIds: z.array(z.string().min(1).max(128)).min(1).max(1000),
43
+ format: z.literal("agentic-receipt").default("agentic-receipt"),
44
+ });
45
+
46
+ export const traceDeleteInputSchema = z.object({
47
+ traceId: z.string().min(1).max(128),
48
+ });
49
+
50
+ export const tracePurgeInputSchema = z.object({
51
+ confirm: z.literal(true),
52
+ });
53
+
54
+ const unwrap = <T>(result: StoreResult<T>): T => {
55
+ if (result.ok) return result.value;
56
+ throw new Error(`${result.error.code}: ${result.error.message}`);
57
+ };
58
+
59
+ const jsonText = (value: unknown): string => JSON.stringify(value, null, 2);
60
+
61
+ const writeDisabled = (): never => {
62
+ throw new Error(
63
+ "WRITE_DISABLED: Trace mutations require gateway.enableWrite or --mcp-enable-write"
64
+ );
65
+ };
66
+
67
+ export const handleTraceList = (
68
+ args: z.infer<typeof traceListInputSchema>,
69
+ ctx: ToolContext
70
+ ): Promise<ToolResult> =>
71
+ runTool(
72
+ ctx,
73
+ "gno_trace_list",
74
+ async () =>
75
+ unwrap(await new RetrievalTraceManagementService(ctx.store).list(args)),
76
+ jsonText
77
+ );
78
+
79
+ export const handleTraceShow = (
80
+ args: z.infer<typeof traceShowInputSchema>,
81
+ ctx: ToolContext
82
+ ): Promise<ToolResult> =>
83
+ runTool(
84
+ ctx,
85
+ "gno_trace_show",
86
+ async () =>
87
+ unwrap(
88
+ await new RetrievalTraceManagementService(ctx.store).show(
89
+ args.traceId,
90
+ { detailLimit: args.detailLimit }
91
+ )
92
+ ),
93
+ jsonText
94
+ );
95
+
96
+ const runTraceWrite = <T>(
97
+ ctx: ToolContext,
98
+ name: string,
99
+ operation: (
100
+ service: RetrievalTraceManagementService
101
+ ) => Promise<StoreResult<T>>
102
+ ): Promise<ToolResult> =>
103
+ runTool(
104
+ ctx,
105
+ name,
106
+ async () => {
107
+ if (!ctx.enableWrite) return writeDisabled();
108
+ return withWriteLock(ctx.writeLockPath, async () =>
109
+ unwrap(await operation(new RetrievalTraceManagementService(ctx.store)))
110
+ );
111
+ },
112
+ jsonText
113
+ );
114
+
115
+ export const handleTraceLabel = (
116
+ args: z.infer<typeof traceLabelInputSchema>,
117
+ ctx: ToolContext
118
+ ): Promise<ToolResult> =>
119
+ runTraceWrite(ctx, "gno_trace_label", (service) =>
120
+ service.label(args as RetrievalTraceLabelRequest)
121
+ );
122
+
123
+ export const handleTraceExport = (
124
+ args: z.infer<typeof traceExportInputSchema>,
125
+ ctx: ToolContext
126
+ ): Promise<ToolResult> =>
127
+ runTraceWrite(ctx, "gno_trace_export", (service) =>
128
+ service.export(args as RetrievalTraceExportRequest)
129
+ );
130
+
131
+ export const handleTraceDelete = (
132
+ args: z.infer<typeof traceDeleteInputSchema>,
133
+ ctx: ToolContext
134
+ ): Promise<ToolResult> =>
135
+ runTraceWrite(ctx, "gno_trace_delete", (service) =>
136
+ service.delete(args.traceId)
137
+ );
138
+
139
+ export const handleTracePurge = (
140
+ _args: z.infer<typeof tracePurgeInputSchema>,
141
+ ctx: ToolContext
142
+ ): Promise<ToolResult> =>
143
+ runTraceWrite(ctx, "gno_trace_purge", (service) => service.purge());
@@ -6,11 +6,21 @@
6
6
 
7
7
  import { join as pathJoin } from "node:path";
8
8
 
9
+ import type { EmbeddingPort } from "../../llm/types";
9
10
  import type { SearchResult, SearchResults } from "../../pipeline/types";
10
11
  import type { ToolContext } from "../server";
11
12
 
12
13
  import { decorateUriForIndex, parseUri } from "../../app/constants";
13
14
  import { createNonTtyProgressRenderer } from "../../cli/progress";
15
+ import {
16
+ finishRetrievalTraceAfterError,
17
+ retrievalTraceFilters,
18
+ startRetrievalTraceRequest,
19
+ } from "../../core/retrieval-trace-request";
20
+ import {
21
+ attachRetrievalTraceMetadata,
22
+ type RetrievalTraceSession,
23
+ } from "../../core/retrieval-trace-session";
14
24
  import { LlmAdapter } from "../../llm/nodeLlamaCpp/adapter";
15
25
  import { resolveDownloadPolicy } from "../../llm/policy";
16
26
  import { resolveModelUri } from "../../llm/registry";
@@ -125,29 +135,55 @@ export function handleVsearch(
125
135
  undefined,
126
136
  args.collection
127
137
  );
138
+ const options = {
139
+ limit: args.limit ?? 5,
140
+ minScore: args.minScore,
141
+ collection: args.collection,
142
+ intent: args.intent,
143
+ exclude: args.exclude,
144
+ since: args.since,
145
+ until: args.until,
146
+ categories: args.categories,
147
+ author: args.author,
148
+ tagsAll: normalizeTagFilters(args.tagsAll),
149
+ tagsAny: normalizeTagFilters(args.tagsAny),
150
+ };
151
+ let traceSession: RetrievalTraceSession | undefined;
152
+ const traceStart = await startRetrievalTraceRequest({
153
+ store: ctx.store,
154
+ config: ctx.config,
155
+ query: args.query,
156
+ filters: retrievalTraceFilters(options),
157
+ pipeline: "vector",
158
+ indexName: ctx.indexName,
159
+ modelUris: [modelUri],
160
+ });
161
+ if (!traceStart.ok) throw new Error(traceStart.error.message);
162
+ traceSession = traceStart.value ?? undefined;
128
163
 
129
- // Resolve download policy from env (MCP has no CLI flags)
130
- const policy = resolveDownloadPolicy(process.env, {});
164
+ let embedPort: EmbeddingPort | null = null;
131
165
 
132
- // Non-TTY progress for MCP (periodic lines to stderr, not \r)
133
- const downloadProgress = createNonTtyProgressRenderer();
166
+ try {
167
+ // Resolve download policy from env (MCP has no CLI flags)
168
+ const policy = resolveDownloadPolicy(process.env, {});
134
169
 
135
- // Create LLM adapter for embeddings
136
- const llm = new LlmAdapter(ctx.config);
137
- const embedResult = await llm.createEmbeddingPort(modelUri, {
138
- policy,
139
- onProgress: (progress) => downloadProgress("embed", progress),
140
- });
141
- if (!embedResult.ok) {
142
- throw new Error(
143
- `Failed to load embedding model: ${embedResult.error.message}. ` +
144
- "Ensure models are downloaded with: gno models pull"
145
- );
146
- }
170
+ // Non-TTY progress for MCP (periodic lines to stderr, not \r)
171
+ const downloadProgress = createNonTtyProgressRenderer();
147
172
 
148
- const embedPort = embedResult.value;
173
+ // Create LLM adapter for embeddings
174
+ const llm = new LlmAdapter(ctx.config);
175
+ const embedResult = await llm.createEmbeddingPort(modelUri, {
176
+ policy,
177
+ onProgress: (progress) => downloadProgress("embed", progress),
178
+ });
179
+ if (!embedResult.ok) {
180
+ throw new Error(
181
+ `Failed to load embedding model: ${embedResult.error.message}. ` +
182
+ "Ensure models are downloaded with: gno models pull"
183
+ );
184
+ }
185
+ embedPort = embedResult.value;
149
186
 
150
- try {
151
187
  // Embed query with contextual formatting
152
188
  const queryEmbedResult = await embedPort.embed(
153
189
  formatQueryForEmbedding(args.query, embedPort.modelUri)
@@ -196,17 +232,8 @@ export function handleVsearch(
196
232
  args.query,
197
233
  queryEmbedding,
198
234
  {
199
- limit: args.limit ?? 5,
200
- minScore: args.minScore,
201
- collection: args.collection,
202
- intent: args.intent,
203
- exclude: args.exclude,
204
- since: args.since,
205
- until: args.until,
206
- categories: args.categories,
207
- author: args.author,
208
- tagsAll: normalizeTagFilters(args.tagsAll),
209
- tagsAny: normalizeTagFilters(args.tagsAny),
235
+ ...options,
236
+ traceSession,
210
237
  }
211
238
  );
212
239
 
@@ -217,17 +244,23 @@ export function handleVsearch(
217
244
  // Enrich with absPath
218
245
  const enrichedResults = enrichWithAbsPath(result.value.results, ctx);
219
246
 
220
- return {
221
- ...result.value,
222
- results: enrichedResults,
223
- meta: {
224
- ...result.value.meta,
225
- // Add queryLanguage hint if provided (per spec, lang is a hint for vsearch)
226
- ...(args.lang ? { queryLanguage: args.lang } : {}),
247
+ return attachRetrievalTraceMetadata(
248
+ {
249
+ ...result.value,
250
+ results: enrichedResults,
251
+ meta: {
252
+ ...result.value.meta,
253
+ // Add queryLanguage hint if provided (per spec, lang is a hint for vsearch)
254
+ ...(args.lang ? { queryLanguage: args.lang } : {}),
255
+ },
227
256
  },
228
- };
257
+ traceSession
258
+ );
259
+ } catch (cause) {
260
+ await finishRetrievalTraceAfterError(traceSession, cause);
261
+ throw cause;
229
262
  } finally {
230
- await embedPort.dispose();
263
+ await embedPort?.dispose();
231
264
  }
232
265
  },
233
266
  formatSearchResults