@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.
Files changed (129) hide show
  1. package/README.md +5 -3
  2. package/assets/skill/SKILL.md +27 -0
  3. package/package.json +2 -1
  4. package/spec/AGENTS.md +83 -0
  5. package/spec/CLAUDE.md +83 -0
  6. package/spec/bench-fixture.schema.json +137 -0
  7. package/spec/cli.md +2894 -0
  8. package/spec/db/schema.sql +442 -0
  9. package/spec/evals-agentic.md +510 -0
  10. package/spec/evals.md +1106 -0
  11. package/spec/mcp.md +2229 -0
  12. package/spec/output-schemas/activation-verification.schema.json +515 -0
  13. package/spec/output-schemas/ask.schema.json +366 -0
  14. package/spec/output-schemas/backlinks.schema.json +131 -0
  15. package/spec/output-schemas/bench-result.schema.json +120 -0
  16. package/spec/output-schemas/capture-receipt.schema.json +143 -0
  17. package/spec/output-schemas/collection-list.schema.json +45 -0
  18. package/spec/output-schemas/context-capsule-v1.schema.json +691 -0
  19. package/spec/output-schemas/context-capsule-verification.schema.json +1338 -0
  20. package/spec/output-schemas/context-list.schema.json +21 -0
  21. package/spec/output-schemas/doctor.schema.json +313 -0
  22. package/spec/output-schemas/error.schema.json +30 -0
  23. package/spec/output-schemas/expansion.schema.json +37 -0
  24. package/spec/output-schemas/get.schema.json +140 -0
  25. package/spec/output-schemas/graph-query.schema.json +99 -0
  26. package/spec/output-schemas/graph.schema.json +371 -0
  27. package/spec/output-schemas/links-list.schema.json +186 -0
  28. package/spec/output-schemas/mcp-add-collection-result.schema.json +23 -0
  29. package/spec/output-schemas/mcp-capture-result.schema.json +152 -0
  30. package/spec/output-schemas/mcp-http-error.schema.json +30 -0
  31. package/spec/output-schemas/mcp-job-list.schema.json +58 -0
  32. package/spec/output-schemas/mcp-job-status.schema.json +224 -0
  33. package/spec/output-schemas/mcp-remove-result.schema.json +39 -0
  34. package/spec/output-schemas/mcp-sync-result.schema.json +41 -0
  35. package/spec/output-schemas/mcp-tag-result.schema.json +33 -0
  36. package/spec/output-schemas/models-list.schema.json +93 -0
  37. package/spec/output-schemas/multi-get.schema.json +103 -0
  38. package/spec/output-schemas/process-status.schema.json +119 -0
  39. package/spec/output-schemas/query-diagnose.schema.json +123 -0
  40. package/spec/output-schemas/resident-status.schema.json +154 -0
  41. package/spec/output-schemas/retrieval-trace-common.schema.json +492 -0
  42. package/spec/output-schemas/retrieval-trace-delete.schema.json +16 -0
  43. package/spec/output-schemas/retrieval-trace-export.schema.json +61 -0
  44. package/spec/output-schemas/retrieval-trace-filters.schema.json +139 -0
  45. package/spec/output-schemas/retrieval-trace-judgment.schema.json +15 -0
  46. package/spec/output-schemas/retrieval-trace-list.schema.json +18 -0
  47. package/spec/output-schemas/retrieval-trace-payloads.schema.json +178 -0
  48. package/spec/output-schemas/retrieval-trace-purge.schema.json +31 -0
  49. package/spec/output-schemas/retrieval-trace-qrels.schema.json +303 -0
  50. package/spec/output-schemas/retrieval-trace-replay.schema.json +286 -0
  51. package/spec/output-schemas/retrieval-trace-show.schema.json +69 -0
  52. package/spec/output-schemas/retrieval-trace-summary.schema.json +65 -0
  53. package/spec/output-schemas/search-result.schema.json +154 -0
  54. package/spec/output-schemas/search-results.schema.json +338 -0
  55. package/spec/output-schemas/similar.schema.json +84 -0
  56. package/spec/output-schemas/status.schema.json +676 -0
  57. package/spec/output-schemas/tags-list.schema.json +48 -0
  58. package/src/app/context-runtime-types.ts +3 -0
  59. package/src/app/context-runtime.ts +15 -1
  60. package/src/cli/commands/ask.ts +106 -36
  61. package/src/cli/commands/context-build.ts +56 -9
  62. package/src/cli/commands/get.ts +64 -3
  63. package/src/cli/commands/query.ts +62 -23
  64. package/src/cli/commands/replay.ts +140 -0
  65. package/src/cli/commands/search.ts +48 -3
  66. package/src/cli/commands/shared.ts +3 -1
  67. package/src/cli/commands/trace.ts +200 -0
  68. package/src/cli/commands/vsearch.ts +75 -53
  69. package/src/cli/program.ts +255 -0
  70. package/src/config/index.ts +9 -0
  71. package/src/config/retrieval-traces.ts +56 -0
  72. package/src/config/types.ts +4 -0
  73. package/src/core/context-compiler.ts +11 -4
  74. package/src/core/retrieval-qrels.ts +405 -0
  75. package/src/core/retrieval-replay-candidate.ts +368 -0
  76. package/src/core/retrieval-replay-types.ts +109 -0
  77. package/src/core/retrieval-replay-validation.ts +89 -0
  78. package/src/core/retrieval-replay.ts +441 -0
  79. package/src/core/retrieval-trace-evidence-origin.ts +175 -0
  80. package/src/core/retrieval-trace-export.ts +113 -0
  81. package/src/core/retrieval-trace-filter-normalization.ts +27 -0
  82. package/src/core/retrieval-trace-filters.ts +19 -0
  83. package/src/core/retrieval-trace-management-helpers.ts +247 -0
  84. package/src/core/retrieval-trace-management-types.ts +132 -0
  85. package/src/core/retrieval-trace-management.ts +422 -0
  86. package/src/core/retrieval-trace-request.ts +141 -0
  87. package/src/core/retrieval-trace-session.ts +494 -0
  88. package/src/core/retrieval-trace.ts +472 -0
  89. package/src/mcp/tools/context.ts +59 -8
  90. package/src/mcp/tools/get.ts +35 -1
  91. package/src/mcp/tools/index.ts +74 -0
  92. package/src/mcp/tools/query.ts +95 -64
  93. package/src/mcp/tools/search.ts +36 -13
  94. package/src/mcp/tools/trace.ts +143 -0
  95. package/src/mcp/tools/vsearch.ts +71 -38
  96. package/src/pipeline/answer.ts +167 -26
  97. package/src/pipeline/graph-retrieval.ts +15 -1
  98. package/src/pipeline/hybrid.ts +151 -43
  99. package/src/pipeline/search.ts +36 -3
  100. package/src/pipeline/trace-metadata.ts +47 -0
  101. package/src/pipeline/types.ts +43 -0
  102. package/src/pipeline/vsearch.ts +101 -38
  103. package/src/sdk/client.ts +380 -71
  104. package/src/sdk/documents.ts +48 -1
  105. package/src/sdk/index.ts +17 -0
  106. package/src/sdk/types.ts +28 -0
  107. package/src/serve/context-capsule.ts +67 -8
  108. package/src/serve/public/app.tsx +12 -1
  109. package/src/serve/public/globals.built.css +1 -1
  110. package/src/serve/public/lib/workspace-tabs.ts +2 -0
  111. package/src/serve/public/pages/Dashboard.tsx +10 -0
  112. package/src/serve/public/pages/TraceHistory.tsx +478 -0
  113. package/src/serve/public/pages/trace-history-detail.tsx +224 -0
  114. package/src/serve/retrieval-trace.ts +28 -0
  115. package/src/serve/routes/api.ts +366 -72
  116. package/src/serve/routes/traces.ts +156 -0
  117. package/src/serve/server.ts +87 -2
  118. package/src/store/index.ts +31 -0
  119. package/src/store/migrations/014-retrieval-traces.ts +303 -0
  120. package/src/store/migrations/index.ts +2 -0
  121. package/src/store/retrieval-trace-codec.ts +384 -0
  122. package/src/store/sqlite/adapter.ts +153 -1
  123. package/src/store/sqlite/retrieval-trace-management-store.ts +341 -0
  124. package/src/store/sqlite/retrieval-trace-retention.ts +349 -0
  125. package/src/store/sqlite/retrieval-trace-rows.ts +267 -0
  126. package/src/store/sqlite/retrieval-trace-store.ts +515 -0
  127. package/src/store/types.ts +297 -0
  128. package/src/store/vector/sqlite-vec.ts +76 -1
  129. package/src/store/vector/types.ts +1 -1
@@ -16,6 +16,7 @@ import {
16
16
  } from "../../app/context-surface";
17
17
  import { CAPTURE_MAX_TEXT_BYTES } from "../../core/capture";
18
18
  import { NOTE_PRESETS, type NotePresetId } from "../../core/note-presets";
19
+ import { RETRIEVAL_TRACE_METADATA } from "../../core/retrieval-trace-session";
19
20
  import { normalizeTag } from "../../core/tags";
20
21
  import { handleAddCollection } from "./add-collection";
21
22
  import { handleCapture } from "./capture";
@@ -42,6 +43,20 @@ import { handleRemoveCollection } from "./remove-collection";
42
43
  import { handleSearch } from "./search";
43
44
  import { handleStatus } from "./status";
44
45
  import { handleSync } from "./sync";
46
+ import {
47
+ handleTraceDelete,
48
+ handleTraceExport,
49
+ handleTraceLabel,
50
+ handleTraceList,
51
+ handleTracePurge,
52
+ handleTraceShow,
53
+ traceDeleteInputSchema,
54
+ traceExportInputSchema,
55
+ traceLabelInputSchema,
56
+ traceListInputSchema,
57
+ tracePurgeInputSchema,
58
+ traceShowInputSchema,
59
+ } from "./trace";
45
60
  import { handleVsearch } from "./vsearch";
46
61
  import {
47
62
  handleCreateFolder,
@@ -96,6 +111,10 @@ export const MCP_WRITE_TOOL_NAMES = new Set([
96
111
  "gno_rename_note",
97
112
  "gno_move_note",
98
113
  "gno_duplicate_note",
114
+ "gno_trace_label",
115
+ "gno_trace_export",
116
+ "gno_trace_delete",
117
+ "gno_trace_purge",
99
118
  ]);
100
119
 
101
120
  // ─────────────────────────────────────────────────────────────────────────────
@@ -563,6 +582,12 @@ const getInputSchema = z.object({
563
582
  .boolean()
564
583
  .default(true)
565
584
  .describe("Include line numbers in output"),
585
+ traceId: z
586
+ .string()
587
+ .min(1)
588
+ .max(128)
589
+ .optional()
590
+ .describe("Continue an open retrieval trace returned by a search/query"),
566
591
  });
567
592
 
568
593
  const multiGetInputSchema = z.object({
@@ -847,9 +872,16 @@ export async function runTool<T>(
847
872
  const modelLease = ctx.acquireModelLease?.();
848
873
  try {
849
874
  const data = await (ctx.runWithSnapshot?.(fn) ?? fn());
875
+ const traceMetadata =
876
+ data !== null && typeof data === "object"
877
+ ? (data as Record<PropertyKey, unknown>)[RETRIEVAL_TRACE_METADATA]
878
+ : undefined;
850
879
  return {
851
880
  content: [{ type: "text", text: formatText(data) }],
852
881
  structuredContent: data as { [x: string]: unknown },
882
+ ...(traceMetadata
883
+ ? { _meta: { gno: { retrievalTrace: traceMetadata } } }
884
+ : {}),
853
885
  };
854
886
  } catch (e) {
855
887
  // Exception firewall: never throw, always return isError
@@ -995,6 +1027,20 @@ export function registerTools(server: McpServer, ctx: ToolContext): void {
995
1027
  (args) => handleStatus(args, ctx)
996
1028
  );
997
1029
 
1030
+ server.tool(
1031
+ "gno_trace_list",
1032
+ "List bounded metadata-only summaries of private local retrieval traces. Raw replay queries are omitted from history.",
1033
+ traceListInputSchema.shape,
1034
+ (args) => handleTraceList(args, ctx)
1035
+ );
1036
+
1037
+ server.tool(
1038
+ "gno_trace_show",
1039
+ "Inspect one bounded local retrieval trace. Replay-mode content is returned only for the explicitly requested trace.",
1040
+ traceShowInputSchema.shape,
1041
+ (args) => handleTraceShow(args, ctx)
1042
+ );
1043
+
998
1044
  server.tool(
999
1045
  "gno_list_tags",
1000
1046
  "List all tags with document counts. Use prefix to filter hierarchical tags (e.g. 'project/').",
@@ -1052,6 +1098,34 @@ export function registerTools(server: McpServer, ctx: ToolContext): void {
1052
1098
  );
1053
1099
 
1054
1100
  if (ctx.enableWrite) {
1101
+ server.tool(
1102
+ "gno_trace_label",
1103
+ "Append an explicit relevant, irrelevant, or missing_expected judgment to a local retrieval trace.",
1104
+ traceLabelInputSchema.shape,
1105
+ (args) => handleTraceLabel(args, ctx)
1106
+ );
1107
+
1108
+ server.tool(
1109
+ "gno_trace_export",
1110
+ "Build a deterministic local agentic receipt from one or more immutable terminal retrieval traces.",
1111
+ traceExportInputSchema.shape,
1112
+ (args) => handleTraceExport(args, ctx)
1113
+ );
1114
+
1115
+ server.tool(
1116
+ "gno_trace_delete",
1117
+ "Delete one private local retrieval trace and all owned receipt records.",
1118
+ traceDeleteInputSchema.shape,
1119
+ (args) => handleTraceDelete(args, ctx)
1120
+ );
1121
+
1122
+ server.tool(
1123
+ "gno_trace_purge",
1124
+ "Purge every private local retrieval trace receipt. Requires confirm=true.",
1125
+ tracePurgeInputSchema.shape,
1126
+ (args) => handleTracePurge(args, ctx)
1127
+ );
1128
+
1055
1129
  server.tool(
1056
1130
  "gno_capture",
1057
1131
  "Create a new document in a collection. Writes to disk. Does NOT auto-embed; run gno_index after to make it searchable via vector search.",
@@ -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());