@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
@@ -5,6 +5,8 @@
5
5
  * @module src/cli/commands/ask
6
6
  */
7
7
 
8
+ import type { RetrievalTraceSurfaceMetadata } from "../../core/retrieval-trace-session";
9
+ import type { RetrievalTraceSession } from "../../core/retrieval-trace-session";
8
10
  import type {
9
11
  EmbeddingPort,
10
12
  GenerationPort,
@@ -12,12 +14,19 @@ import type {
12
14
  } from "../../llm/types";
13
15
  import type { AskOptions, AskResult, Citation } from "../../pipeline/types";
14
16
 
17
+ import { buildVerifiedAsk } from "../../app/verified-ask";
18
+ import {
19
+ finishRetrievalTraceAfterError,
20
+ retrievalTraceFilters,
21
+ startRetrievalTraceRequest,
22
+ } from "../../core/retrieval-trace-request";
15
23
  import { LlmAdapter } from "../../llm/nodeLlamaCpp/adapter";
16
24
  import { resolveDownloadPolicy } from "../../llm/policy";
17
25
  import { resolveModelUri } from "../../llm/registry";
18
26
  import {
27
+ answerTraceTerminalStatus,
19
28
  generateGroundedAnswer,
20
- processAnswerResult,
29
+ processAnswerResultWithTrace,
21
30
  } from "../../pipeline/answer";
22
31
  import { type HybridSearchDeps, searchHybrid } from "../../pipeline/hybrid";
23
32
  import {
@@ -31,6 +40,8 @@ import {
31
40
  } from "../progress";
32
41
  import { initStore } from "./shared";
33
42
 
43
+ export { formatAsk } from "./ask-format";
44
+
34
45
  // ─────────────────────────────────────────────────────────────────────────────
35
46
  // Types
36
47
  // ─────────────────────────────────────────────────────────────────────────────
@@ -55,7 +66,11 @@ export type AskCommandOptions = AskOptions & {
55
66
  };
56
67
 
57
68
  export type AskCommandResult =
58
- | { success: true; data: AskResult }
69
+ | {
70
+ success: true;
71
+ data: AskResult;
72
+ metadata?: RetrievalTraceSurfaceMetadata;
73
+ }
59
74
  | { success: false; error: string };
60
75
 
61
76
  // ─────────────────────────────────────────────────────────────────────────────
@@ -71,9 +86,11 @@ export async function ask(
71
86
  options: AskCommandOptions = {}
72
87
  ): Promise<AskCommandResult> {
73
88
  const limit = options.limit ?? 5;
89
+ const globals = getGlobals();
74
90
 
75
91
  const initResult = await initStore({
76
92
  configPath: options.configPath,
93
+ indexName: globals.index,
77
94
  collection: options.collection,
78
95
  syncConfig: false,
79
96
  });
@@ -88,12 +105,56 @@ export async function ask(
88
105
  let expandPort: GenerationPort | null = null;
89
106
  let answerPort: GenerationPort | null = null;
90
107
  let rerankPort: RerankPort | null = null;
108
+ let traceSession: RetrievalTraceSession | undefined;
91
109
 
92
110
  try {
111
+ const verificationRequested = options.verify === true;
112
+ const answerRequested =
113
+ verificationRequested || Boolean(options.answer && !options.noAnswer);
114
+ const embedUri = resolveModelUri(
115
+ config,
116
+ "embed",
117
+ options.embedModel,
118
+ options.collection
119
+ );
120
+ const expandUri =
121
+ !verificationRequested && !options.noExpand && !options.queryModes?.length
122
+ ? resolveModelUri(
123
+ config,
124
+ "expand",
125
+ options.expandModel ?? options.genModel,
126
+ options.collection
127
+ )
128
+ : undefined;
129
+ const answerUri = answerRequested
130
+ ? resolveModelUri(config, "gen", options.genModel, options.collection)
131
+ : undefined;
132
+ const rerankUri = !options.noRerank
133
+ ? resolveModelUri(
134
+ config,
135
+ "rerank",
136
+ options.rerankModel,
137
+ options.collection
138
+ )
139
+ : undefined;
140
+ const traceStart = await startRetrievalTraceRequest({
141
+ store,
142
+ config,
143
+ query,
144
+ filters: retrievalTraceFilters({ ...options, limit }),
145
+ pipeline: "ask",
146
+ indexName: globals.index,
147
+ modelUris: [embedUri, expandUri, answerUri, rerankUri].filter(
148
+ (value): value is string => Boolean(value)
149
+ ),
150
+ });
151
+ if (!traceStart.ok) {
152
+ return { success: false, error: traceStart.error.message };
153
+ }
154
+ traceSession = traceStart.value ?? undefined;
93
155
  const llm = new LlmAdapter(config);
94
156
 
95
157
  // Resolve download policy from env/flags
96
- const globals = getGlobals();
97
158
  const policy = resolveDownloadPolicy(process.env, {
98
159
  offline: globals.offline,
99
160
  });
@@ -105,12 +166,6 @@ export async function ask(
105
166
  : undefined;
106
167
 
107
168
  // Create embedding port
108
- const embedUri = resolveModelUri(
109
- config,
110
- "embed",
111
- options.embedModel,
112
- options.collection
113
- );
114
169
  const embedResult = await llm.createEmbeddingPort(embedUri, {
115
170
  policy,
116
171
  onProgress: downloadProgress
@@ -122,13 +177,7 @@ export async function ask(
122
177
  }
123
178
 
124
179
  // Create expansion port when expansion is enabled.
125
- if (!options.noExpand && !options.queryModes?.length) {
126
- const expandUri = resolveModelUri(
127
- config,
128
- "expand",
129
- options.expandModel ?? options.genModel,
130
- options.collection
131
- );
180
+ if (expandUri) {
132
181
  const genResult = await llm.createExpansionPort(expandUri, {
133
182
  policy,
134
183
  onProgress: downloadProgress
@@ -141,14 +190,8 @@ export async function ask(
141
190
  }
142
191
 
143
192
  // Create answer generation port when answers are requested.
144
- if (options.answer) {
145
- const genUri = resolveModelUri(
146
- config,
147
- "gen",
148
- options.genModel,
149
- options.collection
150
- );
151
- const genResult = await llm.createGenerationPort(genUri, {
193
+ if (answerUri) {
194
+ const genResult = await llm.createGenerationPort(answerUri, {
152
195
  policy,
153
196
  onProgress: downloadProgress
154
197
  ? (progress) => downloadProgress("gen", progress)
@@ -160,13 +203,7 @@ export async function ask(
160
203
  }
161
204
 
162
205
  // Create rerank port (unless --fast or --no-rerank)
163
- if (!options.noRerank) {
164
- const rerankUri = resolveModelUri(
165
- config,
166
- "rerank",
167
- options.rerankModel,
168
- options.collection
169
- );
206
+ if (rerankUri) {
170
207
  const rerankResult = await llm.createRerankPort(rerankUri, {
171
208
  policy,
172
209
  onProgress: downloadProgress
@@ -208,12 +245,14 @@ export async function ask(
208
245
  expandPort,
209
246
  rerankPort,
210
247
  };
211
-
212
- // Check if answer generation is explicitly requested
213
- const answerRequested = options.answer && !options.noAnswer;
214
-
215
248
  // Fail early if --answer is requested but no generation model available
216
249
  if (answerRequested && answerPort === null) {
250
+ await traceSession?.recordCapability(
251
+ "answer_generation",
252
+ "unavailable",
253
+ "model_unavailable"
254
+ );
255
+ await traceSession?.finish("failed");
217
256
  return {
218
257
  success: false,
219
258
  error:
@@ -222,6 +261,34 @@ export async function ask(
222
261
  };
223
262
  }
224
263
 
264
+ if (verificationRequested && answerPort) {
265
+ const verified = await buildVerifiedAsk(
266
+ query,
267
+ { ...options, limit },
268
+ {
269
+ store,
270
+ config,
271
+ indexName: globals.index,
272
+ vectorIndex,
273
+ embedPort,
274
+ rerankPort,
275
+ genPort: answerPort,
276
+ traceSession,
277
+ }
278
+ );
279
+ const finalized = await traceSession?.finish(
280
+ answerTraceTerminalStatus(verified.citations)
281
+ );
282
+ if (finalized && !finalized.ok) {
283
+ return { success: false, error: finalized.error.message };
284
+ }
285
+ return {
286
+ success: true,
287
+ data: verified,
288
+ metadata: traceSession?.metadata(),
289
+ };
290
+ }
291
+
225
292
  // Run hybrid search
226
293
  const searchResult = await searchHybrid(deps, query, {
227
294
  limit,
@@ -235,13 +302,17 @@ export async function ask(
235
302
  tagsAll: options.tagsAll,
236
303
  tagsAny: options.tagsAny,
237
304
  exclude: options.exclude,
305
+ minScore: options.minScore,
306
+ graph: options.graph,
238
307
  queryModes: options.queryModes,
239
308
  noExpand: options.noExpand,
240
309
  noRerank: options.noRerank,
241
310
  candidateLimit: options.candidateLimit,
311
+ traceSession,
242
312
  });
243
313
 
244
314
  if (!searchResult.ok) {
315
+ await traceSession?.finish("failed");
245
316
  return { success: false, error: searchResult.error.message };
246
317
  }
247
318
 
@@ -261,6 +332,7 @@ export async function ask(
261
332
  answerRequested && answerPort !== null && results.length > 0;
262
333
 
263
334
  if (shouldGenerateAnswer && answerPort) {
335
+ await traceSession?.recordCapability("answer_generation", "attempted");
264
336
  const maxTokens = options.maxAnswerTokens ?? 512;
265
337
  const rawResult = await generateGroundedAnswer(
266
338
  { genPort: answerPort, store },
@@ -271,15 +343,25 @@ export async function ask(
271
343
 
272
344
  // Fail loudly if generation was requested but failed
273
345
  if (!rawResult) {
346
+ await traceSession?.recordCapability(
347
+ "answer_generation",
348
+ "failed",
349
+ "generation_failed"
350
+ );
351
+ await traceSession?.finish("failed");
274
352
  return {
275
353
  success: false,
276
354
  error:
277
355
  "Answer generation failed. The generation model may have encountered an error.",
278
356
  };
279
357
  }
358
+ await traceSession?.recordCapability("answer_generation", "used");
280
359
 
281
360
  // Process answer: extract valid citations, filter, renumber
282
- const processed = processAnswerResult(rawResult);
361
+ const processed = await processAnswerResultWithTrace(
362
+ rawResult,
363
+ traceSession
364
+ );
283
365
  answer = processed.answer;
284
366
  citations = processed.citations;
285
367
  answerContext = processed.answerContext;
@@ -307,7 +389,32 @@ export async function ask(
307
389
  },
308
390
  };
309
391
 
310
- return { success: true, data: askResult };
392
+ if (answerRequested) {
393
+ if (!answerGenerated) {
394
+ await traceSession?.recordCapability(
395
+ "answer_generation",
396
+ "unavailable",
397
+ "no_evidence"
398
+ );
399
+ }
400
+ const finalized = await traceSession?.finish(
401
+ answerTraceTerminalStatus(citations)
402
+ );
403
+ if (finalized && !finalized.ok) {
404
+ return { success: false, error: finalized.error.message };
405
+ }
406
+ }
407
+ return {
408
+ success: true,
409
+ data: askResult,
410
+ metadata: traceSession?.metadata(),
411
+ };
412
+ } catch (cause) {
413
+ await finishRetrievalTraceAfterError(traceSession, cause);
414
+ return {
415
+ success: false,
416
+ error: cause instanceof Error ? cause.message : "Ask failed",
417
+ };
311
418
  } finally {
312
419
  if (embedPort) {
313
420
  await embedPort.dispose();
@@ -324,149 +431,3 @@ export async function ask(
324
431
  await store.close();
325
432
  }
326
433
  }
327
-
328
- // ─────────────────────────────────────────────────────────────────────────────
329
- // Formatters
330
- // ─────────────────────────────────────────────────────────────────────────────
331
-
332
- interface FormatOptions {
333
- showSources?: boolean;
334
- }
335
-
336
- // oxlint-disable-next-line max-lines-per-function -- terminal formatting with conditional sections
337
- function formatTerminal(data: AskResult, opts: FormatOptions = {}): string {
338
- const lines: string[] = [];
339
- const hasAnswer = Boolean(data.answer);
340
-
341
- // Show answer if present
342
- if (data.answer) {
343
- lines.push("Answer:");
344
- lines.push(data.answer);
345
- lines.push("");
346
- }
347
-
348
- // Show cited sources (only sources actually referenced in answer)
349
- if (data.citations && data.citations.length > 0) {
350
- lines.push("Cited Sources:");
351
- for (let i = 0; i < data.citations.length; i++) {
352
- const c = data.citations[i];
353
- if (c) {
354
- lines.push(` [${i + 1}] ${c.uri}`);
355
- }
356
- }
357
- lines.push("");
358
- }
359
-
360
- // Show all retrieved sources if:
361
- // - No answer was generated (retrieval-only mode)
362
- // - User explicitly requested with --show-sources
363
- const showAllSources = !hasAnswer || opts.showSources;
364
- if (showAllSources && data.results.length > 0) {
365
- lines.push(hasAnswer ? "All Retrieved Sources:" : "Sources:");
366
- for (const r of data.results) {
367
- lines.push(` [${r.docid}] ${r.uri}`);
368
- if (r.title) {
369
- lines.push(` ${r.title}`);
370
- }
371
- }
372
- } else if (hasAnswer && data.results.length > 0) {
373
- // Hint about --show-sources when we have more sources
374
- const citedCount = data.citations?.length ?? 0;
375
- if (data.results.length > citedCount) {
376
- lines.push(
377
- `(${data.results.length} sources retrieved, use --show-sources to list all)`
378
- );
379
- }
380
- }
381
-
382
- if (!data.answer && data.results.length === 0) {
383
- lines.push("No relevant sources found.");
384
- }
385
-
386
- return lines.join("\n");
387
- }
388
-
389
- function formatMarkdown(data: AskResult, opts: FormatOptions = {}): string {
390
- const lines: string[] = [];
391
- const hasAnswer = Boolean(data.answer);
392
-
393
- lines.push(`# Question: ${data.query}`);
394
- lines.push("");
395
-
396
- if (data.answer) {
397
- lines.push("## Answer");
398
- lines.push("");
399
- lines.push(data.answer);
400
- lines.push("");
401
- }
402
-
403
- // Show cited sources (only sources actually referenced in answer)
404
- if (data.citations && data.citations.length > 0) {
405
- lines.push("## Cited Sources");
406
- lines.push("");
407
- for (let i = 0; i < data.citations.length; i++) {
408
- const c = data.citations[i];
409
- if (c) {
410
- lines.push(`**[${i + 1}]** \`${c.uri}\``);
411
- }
412
- }
413
- lines.push("");
414
- }
415
-
416
- // Show all retrieved sources if no answer or --show-sources
417
- const showAllSources = !hasAnswer || opts.showSources;
418
- if (showAllSources) {
419
- lines.push(hasAnswer ? "## All Retrieved Sources" : "## Sources");
420
- lines.push("");
421
-
422
- for (let i = 0; i < data.results.length; i++) {
423
- const r = data.results[i];
424
- if (!r) {
425
- continue;
426
- }
427
- lines.push(`${i + 1}. **${r.title || r.source.relPath}**`);
428
- lines.push(` - URI: \`${r.uri}\``);
429
- lines.push(` - Score: ${r.score.toFixed(2)}`);
430
- }
431
-
432
- if (data.results.length === 0) {
433
- lines.push("*No relevant sources found.*");
434
- }
435
- }
436
-
437
- lines.push("");
438
- lines.push("---");
439
- lines.push(
440
- `*Mode: ${data.mode} | Expanded: ${data.meta.expanded} | Reranked: ${data.meta.reranked}*`
441
- );
442
-
443
- return lines.join("\n");
444
- }
445
-
446
- /**
447
- * Format ask result for output.
448
- */
449
- export function formatAsk(
450
- result: AskCommandResult,
451
- options: AskCommandOptions
452
- ): string {
453
- if (!result.success) {
454
- return options.json
455
- ? JSON.stringify({
456
- error: { code: "ASK_FAILED", message: result.error },
457
- })
458
- : `Error: ${result.error}`;
459
- }
460
-
461
- const formatOpts: FormatOptions = { showSources: options.showSources };
462
-
463
- if (options.json) {
464
- return JSON.stringify(result.data, null, 2);
465
- }
466
-
467
- if (options.md) {
468
- return formatMarkdown(result.data, formatOpts);
469
- }
470
-
471
- return formatTerminal(result.data, formatOpts);
472
- }
@@ -1,6 +1,7 @@
1
1
  /** Context Capsule build command over the shared application runtime. */
2
2
 
3
3
  import type { ContextCapsuleBuildInput } from "../../app/context-runtime";
4
+ import type { RetrievalTraceSession } from "../../core/retrieval-trace-session";
4
5
  import type { EmbeddingPort, RerankPort } from "../../llm/types";
5
6
  import type { VectorIndexPort } from "../../store/vector";
6
7
 
@@ -10,6 +11,10 @@ import {
10
11
  canonicalBuiltContextCapsuleJson,
11
12
  validateContextCapsuleBuildInput,
12
13
  } from "../../app/context-runtime";
14
+ import {
15
+ finishRetrievalTraceAfterError,
16
+ startRetrievalTraceRequest,
17
+ } from "../../core/retrieval-trace-request";
13
18
  import { LlmAdapter } from "../../llm/nodeLlamaCpp/adapter";
14
19
  import { resolveDownloadPolicy } from "../../llm/policy";
15
20
  import { resolveModelUri } from "../../llm/registry";
@@ -77,13 +82,56 @@ export const contextBuild = async (
77
82
  let embedPort: EmbeddingPort | null = null;
78
83
  let rerankPort: RerankPort | null = null;
79
84
  let vectorIndex: VectorIndexPort | null = null;
85
+ let traceSession: RetrievalTraceSession | undefined;
80
86
  try {
81
87
  validateContextCapsuleBuildInput(
82
88
  { goal, ...options },
83
89
  options.indexName,
84
90
  config.collections.map((collection) => collection.name)
85
91
  );
92
+ const collection =
93
+ options.collections?.length === 1 ? options.collections[0] : undefined;
94
+ const embedUri =
95
+ options.depthPolicy === "fast"
96
+ ? undefined
97
+ : resolveModelUri(config, "embed", undefined, collection);
98
+ const rerankUri =
99
+ options.depthPolicy === "fast"
100
+ ? undefined
101
+ : resolveModelUri(config, "rerank", undefined, collection);
102
+ const traceStart = await startRetrievalTraceRequest({
103
+ store,
104
+ config,
105
+ query: options.query ?? goal,
106
+ goal,
107
+ filters: {
108
+ limit: options.limit,
109
+ collection,
110
+ collections: [...(options.collections ?? [])].sort(),
111
+ lang: options.lang,
112
+ tagsAll: options.tagsAll,
113
+ tagsAny: options.tagsAny,
114
+ since: options.since,
115
+ until: options.until,
116
+ categories: options.categories,
117
+ author: options.author,
118
+ graph: options.graph,
119
+ candidateLimit: options.candidateLimit,
120
+ queryModes: options.queryModes,
121
+ uriPrefix: options.uriPrefix ?? undefined,
122
+ },
123
+ pipeline: "context",
124
+ indexName: options.indexName,
125
+ modelUris: [embedUri, rerankUri].filter((value): value is string =>
126
+ Boolean(value)
127
+ ),
128
+ });
129
+ if (!traceStart.ok) throw new Error(traceStart.error.message);
130
+ traceSession = traceStart.value ?? undefined;
86
131
  if (options.depthPolicy !== "fast") {
132
+ if (!(embedUri && rerankUri)) {
133
+ throw new Error("Context model identities could not be resolved");
134
+ }
87
135
  const globals = getGlobals();
88
136
  const policy = resolveDownloadPolicy(process.env, {
89
137
  offline: globals.offline,
@@ -92,9 +140,6 @@ export const contextBuild = async (
92
140
  const progress = showProgress
93
141
  ? createThrottledProgressRenderer(createProgressRenderer())
94
142
  : undefined;
95
- const collection =
96
- options.collections?.length === 1 ? options.collections[0] : undefined;
97
- const embedUri = resolveModelUri(config, "embed", undefined, collection);
98
143
  const embedResult = await llm.createEmbeddingPort(embedUri, {
99
144
  policy,
100
145
  onProgress: progress ? (value) => progress("embed", value) : undefined,
@@ -110,12 +155,6 @@ export const contextBuild = async (
110
155
  if (vectorResult.ok) vectorIndex = vectorResult.value;
111
156
  }
112
157
  }
113
- const rerankUri = resolveModelUri(
114
- config,
115
- "rerank",
116
- undefined,
117
- collection
118
- );
119
158
  const rerankResult = await llm.createRerankPort(rerankUri, {
120
159
  policy,
121
160
  onProgress: progress ? (value) => progress("rerank", value) : undefined,
@@ -132,12 +171,20 @@ export const contextBuild = async (
132
171
  vectorIndex,
133
172
  embedPort,
134
173
  rerankPort,
174
+ traceSession,
135
175
  }
136
176
  );
177
+ const finished = await traceSession?.finish("completed");
178
+ if (finished && !finished.ok) throw new Error(finished.error.message);
179
+ const traceMetadata = traceSession?.metadata();
180
+ if (traceMetadata) {
181
+ process.stderr.write(`Trace: ${traceMetadata.traceId}\n`);
182
+ }
137
183
  return options.format === "md"
138
184
  ? formatContextCapsuleMarkdown(capsule)
139
185
  : canonicalBuiltContextCapsuleJson(capsule);
140
186
  } catch (error) {
187
+ await finishRetrievalTraceAfterError(traceSession, error);
141
188
  if (error instanceof CliError) throw error;
142
189
  throw contextCliError(error);
143
190
  } finally {
@@ -5,6 +5,10 @@
5
5
  * @module src/cli/commands/get
6
6
  */
7
7
 
8
+ import type {
9
+ RetrievalTraceSession,
10
+ RetrievalTraceSurfaceMetadata,
11
+ } from "../../core/retrieval-trace-session";
8
12
  import type { DocumentRow, StorePort, StoreResult } from "../../store/types";
9
13
  import type { ParsedRef } from "./ref-parser";
10
14
 
@@ -17,6 +21,10 @@ import {
17
21
  getDocumentCapabilities,
18
22
  type DocumentCapabilities,
19
23
  } from "../../core/document-capabilities";
24
+ import {
25
+ evidenceFromExactDocument,
26
+ RetrievalTraceSession as TraceSession,
27
+ } from "../../core/retrieval-trace-session";
20
28
  import { parseRef } from "./ref-parser";
21
29
  import { initStore } from "./shared";
22
30
 
@@ -41,10 +49,18 @@ export interface GetCommandOptions {
41
49
  json?: boolean;
42
50
  /** Markdown output */
43
51
  md?: boolean;
52
+ /** Continue an open retrieval receipt returned by a prior query. */
53
+ traceId?: string;
54
+ /** Internal shared-session seam for in-process callers. */
55
+ traceSession?: RetrievalTraceSession;
44
56
  }
45
57
 
46
58
  export type GetResult =
47
- | { success: true; data: GetResponse }
59
+ | {
60
+ success: true;
61
+ data: GetResponse;
62
+ metadata?: RetrievalTraceSurfaceMetadata;
63
+ }
48
64
  | { success: false; error: string; isValidation?: boolean };
49
65
 
50
66
  export interface GetResponse {
@@ -138,7 +154,53 @@ export async function get(
138
154
  const { store, config } = initResult;
139
155
 
140
156
  try {
141
- return await fetchDocument(store, parsed, options, config, indexName);
157
+ let traceSession = options.traceSession;
158
+ if (!traceSession && options.traceId) {
159
+ const resumed = await TraceSession.resume({
160
+ store,
161
+ config: config.retrievalTraces,
162
+ traceId: options.traceId,
163
+ });
164
+ if (!resumed.ok) {
165
+ return { success: false, error: resumed.error.message };
166
+ }
167
+ traceSession = resumed.value ?? undefined;
168
+ }
169
+ const result = await fetchDocument(
170
+ store,
171
+ parsed,
172
+ options,
173
+ config,
174
+ indexName
175
+ );
176
+ if (!result.success || !traceSession) return result;
177
+ const lines = result.data.returnedLines ?? {
178
+ start: 1,
179
+ end: result.data.totalLines,
180
+ };
181
+ const evidence = evidenceFromExactDocument({
182
+ docid: result.data.docid,
183
+ uri: result.data.uri,
184
+ sourceHash: result.data.source.sourceHash,
185
+ mirrorHash: result.data.conversion?.mirrorHash,
186
+ content: result.data.content,
187
+ startLine: lines.start,
188
+ endLine: lines.end,
189
+ });
190
+ if (evidence) {
191
+ const getEvent = await traceSession.recordEvidence("get", [evidence]);
192
+ if (!getEvent.ok) {
193
+ return { success: false, error: getEvent.error.message };
194
+ }
195
+ const openEvent = await traceSession.recordEvidence("open", [evidence]);
196
+ if (!openEvent.ok) {
197
+ return { success: false, error: openEvent.error.message };
198
+ }
199
+ }
200
+ return {
201
+ ...result,
202
+ metadata: traceSession.metadata(),
203
+ };
142
204
  } finally {
143
205
  await store.close();
144
206
  }
@@ -375,7 +437,6 @@ export function formatGet(
375
437
  }
376
438
  return `Error: ${result.error}`;
377
439
  }
378
-
379
440
  const { data } = result;
380
441
 
381
442
  if (options.json) {