@gmickel/gno 1.17.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 (167) hide show
  1. package/README.md +18 -4
  2. package/assets/skill/SKILL.md +44 -1
  3. package/assets/skill/mcp-reference.md +21 -0
  4. package/package.json +3 -2
  5. package/spec/AGENTS.md +83 -0
  6. package/spec/CLAUDE.md +83 -0
  7. package/spec/bench-fixture.schema.json +137 -0
  8. package/spec/cli.md +2894 -0
  9. package/spec/db/schema.sql +442 -0
  10. package/spec/evals-agentic.md +510 -0
  11. package/spec/evals.md +1106 -0
  12. package/spec/mcp.md +2229 -0
  13. package/spec/output-schemas/activation-verification.schema.json +515 -0
  14. package/spec/output-schemas/ask.schema.json +366 -0
  15. package/spec/output-schemas/backlinks.schema.json +131 -0
  16. package/spec/output-schemas/bench-result.schema.json +120 -0
  17. package/spec/output-schemas/capture-receipt.schema.json +143 -0
  18. package/spec/output-schemas/collection-list.schema.json +45 -0
  19. package/spec/output-schemas/context-capsule-v1.schema.json +691 -0
  20. package/spec/output-schemas/context-capsule-verification.schema.json +1338 -0
  21. package/spec/output-schemas/context-list.schema.json +21 -0
  22. package/spec/output-schemas/doctor.schema.json +313 -0
  23. package/spec/output-schemas/error.schema.json +30 -0
  24. package/spec/output-schemas/expansion.schema.json +37 -0
  25. package/spec/output-schemas/get.schema.json +140 -0
  26. package/spec/output-schemas/graph-query.schema.json +99 -0
  27. package/spec/output-schemas/graph.schema.json +371 -0
  28. package/spec/output-schemas/links-list.schema.json +186 -0
  29. package/spec/output-schemas/mcp-add-collection-result.schema.json +23 -0
  30. package/spec/output-schemas/mcp-capture-result.schema.json +152 -0
  31. package/spec/output-schemas/mcp-http-error.schema.json +30 -0
  32. package/spec/output-schemas/mcp-job-list.schema.json +58 -0
  33. package/spec/output-schemas/mcp-job-status.schema.json +224 -0
  34. package/spec/output-schemas/mcp-remove-result.schema.json +39 -0
  35. package/spec/output-schemas/mcp-sync-result.schema.json +41 -0
  36. package/spec/output-schemas/mcp-tag-result.schema.json +33 -0
  37. package/spec/output-schemas/models-list.schema.json +93 -0
  38. package/spec/output-schemas/multi-get.schema.json +103 -0
  39. package/spec/output-schemas/process-status.schema.json +119 -0
  40. package/spec/output-schemas/query-diagnose.schema.json +123 -0
  41. package/spec/output-schemas/resident-status.schema.json +154 -0
  42. package/spec/output-schemas/retrieval-trace-common.schema.json +492 -0
  43. package/spec/output-schemas/retrieval-trace-delete.schema.json +16 -0
  44. package/spec/output-schemas/retrieval-trace-export.schema.json +61 -0
  45. package/spec/output-schemas/retrieval-trace-filters.schema.json +139 -0
  46. package/spec/output-schemas/retrieval-trace-judgment.schema.json +15 -0
  47. package/spec/output-schemas/retrieval-trace-list.schema.json +18 -0
  48. package/spec/output-schemas/retrieval-trace-payloads.schema.json +178 -0
  49. package/spec/output-schemas/retrieval-trace-purge.schema.json +31 -0
  50. package/spec/output-schemas/retrieval-trace-qrels.schema.json +303 -0
  51. package/spec/output-schemas/retrieval-trace-replay.schema.json +286 -0
  52. package/spec/output-schemas/retrieval-trace-show.schema.json +69 -0
  53. package/spec/output-schemas/retrieval-trace-summary.schema.json +65 -0
  54. package/spec/output-schemas/search-result.schema.json +154 -0
  55. package/spec/output-schemas/search-results.schema.json +338 -0
  56. package/spec/output-schemas/similar.schema.json +84 -0
  57. package/spec/output-schemas/status.schema.json +676 -0
  58. package/spec/output-schemas/tags-list.schema.json +48 -0
  59. package/src/app/context-runtime-types.ts +3 -0
  60. package/src/app/context-runtime.ts +15 -1
  61. package/src/cli/commands/ask.ts +106 -36
  62. package/src/cli/commands/context-build.ts +56 -9
  63. package/src/cli/commands/daemon.ts +69 -2
  64. package/src/cli/commands/get.ts +64 -3
  65. package/src/cli/commands/models/pull.ts +13 -3
  66. package/src/cli/commands/query.ts +62 -23
  67. package/src/cli/commands/replay.ts +140 -0
  68. package/src/cli/commands/search.ts +48 -3
  69. package/src/cli/commands/shared.ts +3 -1
  70. package/src/cli/commands/status.ts +2 -0
  71. package/src/cli/commands/trace.ts +200 -0
  72. package/src/cli/commands/vsearch.ts +75 -53
  73. package/src/cli/detach.ts +37 -20
  74. package/src/cli/program.ts +329 -27
  75. package/src/config/index.ts +12 -0
  76. package/src/config/retrieval-traces.ts +56 -0
  77. package/src/config/types.ts +41 -0
  78. package/src/core/context-compiler.ts +11 -4
  79. package/src/core/job-manager.ts +19 -0
  80. package/src/core/mutation-generations.ts +33 -0
  81. package/src/core/retrieval-qrels.ts +405 -0
  82. package/src/core/retrieval-replay-candidate.ts +368 -0
  83. package/src/core/retrieval-replay-types.ts +109 -0
  84. package/src/core/retrieval-replay-validation.ts +89 -0
  85. package/src/core/retrieval-replay.ts +441 -0
  86. package/src/core/retrieval-trace-evidence-origin.ts +175 -0
  87. package/src/core/retrieval-trace-export.ts +113 -0
  88. package/src/core/retrieval-trace-filter-normalization.ts +27 -0
  89. package/src/core/retrieval-trace-filters.ts +19 -0
  90. package/src/core/retrieval-trace-management-helpers.ts +247 -0
  91. package/src/core/retrieval-trace-management-types.ts +132 -0
  92. package/src/core/retrieval-trace-management.ts +422 -0
  93. package/src/core/retrieval-trace-request.ts +141 -0
  94. package/src/core/retrieval-trace-session.ts +494 -0
  95. package/src/core/retrieval-trace.ts +472 -0
  96. package/src/llm/cache.ts +13 -3
  97. package/src/llm/nodeLlamaCpp/adapter.ts +10 -1
  98. package/src/llm/nodeLlamaCpp/lifecycle.ts +71 -0
  99. package/src/mcp/context.ts +161 -0
  100. package/src/mcp/http-security.ts +477 -0
  101. package/src/mcp/http-session.ts +272 -0
  102. package/src/mcp/http-transport.ts +370 -0
  103. package/src/mcp/resources/index.ts +141 -134
  104. package/src/mcp/server.ts +19 -79
  105. package/src/mcp/tools/add-collection.ts +3 -1
  106. package/src/mcp/tools/capture.ts +3 -0
  107. package/src/mcp/tools/clear-collection-embeddings.ts +2 -0
  108. package/src/mcp/tools/context.ts +68 -16
  109. package/src/mcp/tools/embed.ts +62 -52
  110. package/src/mcp/tools/get.ts +35 -1
  111. package/src/mcp/tools/index-cmd.ts +88 -74
  112. package/src/mcp/tools/index.ts +96 -2
  113. package/src/mcp/tools/query.ts +95 -64
  114. package/src/mcp/tools/remove-collection.ts +2 -0
  115. package/src/mcp/tools/search.ts +36 -13
  116. package/src/mcp/tools/status.ts +11 -0
  117. package/src/mcp/tools/sync.ts +16 -14
  118. package/src/mcp/tools/trace.ts +143 -0
  119. package/src/mcp/tools/vsearch.ts +71 -38
  120. package/src/mcp/tools/workspace-write.ts +7 -3
  121. package/src/pipeline/answer.ts +167 -26
  122. package/src/pipeline/graph-retrieval.ts +15 -1
  123. package/src/pipeline/hybrid.ts +151 -43
  124. package/src/pipeline/search.ts +36 -3
  125. package/src/pipeline/trace-metadata.ts +47 -0
  126. package/src/pipeline/types.ts +43 -0
  127. package/src/pipeline/vsearch.ts +101 -38
  128. package/src/sdk/client.ts +380 -71
  129. package/src/sdk/documents.ts +48 -1
  130. package/src/sdk/index.ts +17 -0
  131. package/src/sdk/types.ts +28 -0
  132. package/src/serve/background-runtime.ts +12 -212
  133. package/src/serve/context-capsule.ts +67 -8
  134. package/src/serve/embed-scheduler.ts +74 -43
  135. package/src/serve/index.ts +9 -0
  136. package/src/serve/jobs.ts +78 -80
  137. package/src/serve/public/app.tsx +12 -1
  138. package/src/serve/public/components/HealthCenter.tsx +74 -1
  139. package/src/serve/public/globals.built.css +1 -1
  140. package/src/serve/public/lib/workspace-tabs.ts +2 -0
  141. package/src/serve/public/pages/Dashboard.tsx +11 -0
  142. package/src/serve/public/pages/TraceHistory.tsx +478 -0
  143. package/src/serve/public/pages/trace-history-detail.tsx +224 -0
  144. package/src/serve/resident-admission.ts +159 -0
  145. package/src/serve/resident-background-work.ts +39 -0
  146. package/src/serve/resident-request.ts +55 -0
  147. package/src/serve/resident-runtime.ts +490 -0
  148. package/src/serve/resident-status.ts +96 -0
  149. package/src/serve/retrieval-trace.ts +28 -0
  150. package/src/serve/routes/api.ts +629 -239
  151. package/src/serve/routes/mcp.ts +69 -0
  152. package/src/serve/routes/traces.ts +156 -0
  153. package/src/serve/server.ts +276 -37
  154. package/src/serve/status-model.ts +51 -0
  155. package/src/serve/status.ts +5 -0
  156. package/src/store/index.ts +31 -0
  157. package/src/store/migrations/014-retrieval-traces.ts +303 -0
  158. package/src/store/migrations/index.ts +2 -0
  159. package/src/store/retrieval-trace-codec.ts +384 -0
  160. package/src/store/sqlite/adapter.ts +179 -10
  161. package/src/store/sqlite/retrieval-trace-management-store.ts +341 -0
  162. package/src/store/sqlite/retrieval-trace-retention.ts +349 -0
  163. package/src/store/sqlite/retrieval-trace-rows.ts +267 -0
  164. package/src/store/sqlite/retrieval-trace-store.ts +515 -0
  165. package/src/store/types.ts +297 -0
  166. package/src/store/vector/sqlite-vec.ts +76 -1
  167. package/src/store/vector/types.ts +1 -1
@@ -9,6 +9,7 @@ import type { ToolContext } from "../server";
9
9
  import { MCP_ERRORS } from "../../core/errors";
10
10
  import { acquireWriteLock, type WriteLockHandle } from "../../core/file-lock";
11
11
  import { JobError } from "../../core/job-manager";
12
+ import { recordIndexMutation } from "../../core/mutation-generations";
12
13
  import { embedBacklog } from "../../embed";
13
14
  import { LlmAdapter } from "../../llm/nodeLlamaCpp/adapter";
14
15
  import { resolveModelUri } from "../../llm/registry";
@@ -81,64 +82,73 @@ export function handleEmbed(
81
82
  "embed",
82
83
  lock,
83
84
  async () => {
84
- // Create LLM adapter with offline policy (fail-fast, no download)
85
- const llm = new LlmAdapter(ctx.config);
86
- const embedResult = await llm.createEmbeddingPort(modelUri, {
87
- policy: { offline: true, allowDownload: false },
88
- });
89
-
90
- if (!embedResult.ok) {
91
- throw new Error(
92
- `MODEL_NOT_FOUND: Embedding model not cached. ` +
93
- `Model: ${modelUri}. ` +
94
- `Run 'gno models pull embed' first.`
95
- );
96
- }
97
-
98
- const embedPort = embedResult.value;
99
-
85
+ const lease = ctx.acquireModelLease?.();
100
86
  try {
101
- // Initialize and get dimensions from port interface
102
- const initResult = await embedPort.init();
103
- if (!initResult.ok) {
104
- throw new Error(initResult.error.message);
105
- }
106
- const dimensions = embedPort.dimensions();
107
-
108
- // Create vector index port
109
- const db = ctx.store.getRawDb();
110
- const vectorResult = await createVectorIndexPort(db, {
111
- model: modelUri,
112
- dimensions,
113
- });
114
- if (!vectorResult.ok) {
115
- throw new Error(vectorResult.error.message);
116
- }
117
- const vectorIndex = vectorResult.value;
118
-
119
- // Create stats port for backlog
120
- const statsPort = createVectorStatsPort(db);
121
-
122
- // Run embedding
123
- const result = await embedBacklog({
124
- statsPort,
125
- embedPort,
126
- vectorIndex,
127
- collection: collection?.name,
128
- modelUri,
129
- batchSize: 32,
87
+ // Create LLM adapter with offline policy (fail-fast, no download)
88
+ const llm = new LlmAdapter(ctx.config);
89
+ const embedResult = await llm.createEmbeddingPort(modelUri, {
90
+ policy: { offline: true, allowDownload: false },
130
91
  });
131
92
 
132
- if (!result.ok) {
133
- throw new Error(result.error.message);
93
+ if (!embedResult.ok) {
94
+ throw new Error(
95
+ `MODEL_NOT_FOUND: Embedding model not cached. ` +
96
+ `Model: ${modelUri}. ` +
97
+ `Run 'gno models pull embed' first.`
98
+ );
134
99
  }
135
100
 
136
- return {
137
- kind: "embed" as const,
138
- value: result.value,
139
- };
101
+ const embedPort = embedResult.value;
102
+
103
+ try {
104
+ // Initialize and get dimensions from port interface
105
+ const initResult = await embedPort.init();
106
+ if (!initResult.ok) {
107
+ throw new Error(initResult.error.message);
108
+ }
109
+ const dimensions = embedPort.dimensions();
110
+
111
+ // Create vector index port
112
+ const db = ctx.store.getRawDb();
113
+ const vectorResult = await createVectorIndexPort(db, {
114
+ model: modelUri,
115
+ dimensions,
116
+ });
117
+ if (!vectorResult.ok) {
118
+ throw new Error(vectorResult.error.message);
119
+ }
120
+ const vectorIndex = vectorResult.value;
121
+
122
+ // Create stats port for backlog
123
+ const statsPort = createVectorStatsPort(db);
124
+
125
+ // Run embedding
126
+ const result = await embedBacklog({
127
+ statsPort,
128
+ embedPort,
129
+ vectorIndex,
130
+ collection: collection?.name,
131
+ modelUri,
132
+ batchSize: 32,
133
+ });
134
+
135
+ if (!result.ok) {
136
+ throw new Error(result.error.message);
137
+ }
138
+ recordIndexMutation(
139
+ result.value.embedded,
140
+ ctx.markIndexMutation
141
+ );
142
+
143
+ return {
144
+ kind: "embed" as const,
145
+ value: result.value,
146
+ };
147
+ } finally {
148
+ await embedPort.dispose();
149
+ }
140
150
  } finally {
141
- await embedPort.dispose();
151
+ lease?.release();
142
152
  }
143
153
  }
144
154
  );
@@ -16,6 +16,11 @@ import {
16
16
  } from "../../core/document-capabilities";
17
17
  import { resolveEffectiveIndex } from "../../core/indexed-reference";
18
18
  import { parseRef } from "../../core/ref-parser";
19
+ import {
20
+ attachRetrievalTraceMetadata,
21
+ evidenceFromExactDocument,
22
+ RetrievalTraceSession,
23
+ } from "../../core/retrieval-trace-session";
19
24
  import { openScopedIndexStore } from "../../store/sqlite/scoped-index";
20
25
  import { runTool, type ToolResult } from "./index";
21
26
 
@@ -24,6 +29,7 @@ interface GetInput {
24
29
  fromLine?: number;
25
30
  lineCount?: number;
26
31
  lineNumbers?: boolean;
32
+ traceId?: string;
27
33
  }
28
34
 
29
35
  interface GetResponse {
@@ -165,6 +171,7 @@ export function handleGet(
165
171
 
166
172
  // Apply line range if specified
167
173
  let content = fullContent;
174
+ let exactContent = fullContent;
168
175
  let returnedLines: { start: number; end: number } | undefined;
169
176
 
170
177
  // lineNumbers defaults to true per spec
@@ -176,12 +183,14 @@ export function handleGet(
176
183
  if (startLine > totalLines) {
177
184
  // Return empty content for out-of-range request
178
185
  content = "";
186
+ exactContent = "";
179
187
  returnedLines = undefined;
180
188
  } else {
181
189
  const count = args.lineCount ?? totalLines - startLine + 1;
182
190
  const endLine = Math.min(startLine + count - 1, totalLines);
183
191
 
184
192
  const slicedLines = contentLines.slice(startLine - 1, endLine);
193
+ exactContent = slicedLines.join("\n");
185
194
 
186
195
  if (showLineNumbers) {
187
196
  content = slicedLines
@@ -242,7 +251,32 @@ export function handleGet(
242
251
  }),
243
252
  };
244
253
 
245
- return response;
254
+ if (!args.traceId) return response;
255
+ const resumed = await RetrievalTraceSession.resume({
256
+ store: scoped.store,
257
+ config: ctx.config.retrievalTraces,
258
+ traceId: args.traceId,
259
+ });
260
+ if (!resumed.ok) throw new Error(resumed.error.message);
261
+ const traceSession = resumed.value;
262
+ if (!traceSession) return response;
263
+ const lines = returnedLines ?? { start: 1, end: totalLines };
264
+ const evidence = evidenceFromExactDocument({
265
+ docid: response.docid,
266
+ uri: response.uri,
267
+ sourceHash: response.source.sourceHash,
268
+ mirrorHash: response.conversion?.mirrorHash,
269
+ content: exactContent,
270
+ startLine: lines.start,
271
+ endLine: lines.end,
272
+ });
273
+ if (evidence) {
274
+ const got = await traceSession.recordEvidence("get", [evidence]);
275
+ if (!got.ok) throw new Error(got.error.message);
276
+ const opened = await traceSession.recordEvidence("open", [evidence]);
277
+ if (!opened.ok) throw new Error(opened.error.message);
278
+ }
279
+ return attachRetrievalTraceMetadata(response, traceSession);
246
280
  } finally {
247
281
  await scoped.close();
248
282
  }
@@ -10,6 +10,10 @@ import type { ToolContext } from "../server";
10
10
  import { MCP_ERRORS } from "../../core/errors";
11
11
  import { acquireWriteLock, type WriteLockHandle } from "../../core/file-lock";
12
12
  import { JobError } from "../../core/job-manager";
13
+ import {
14
+ recordContentMutation,
15
+ recordIndexMutation,
16
+ } from "../../core/mutation-generations";
13
17
  import { normalizeCollectionName } from "../../core/validation";
14
18
  import { embedBacklog } from "../../embed";
15
19
  import { defaultSyncService, withContentTypeRules } from "../../ingestion";
@@ -115,86 +119,96 @@ export function handleIndex(
115
119
  "index",
116
120
  lock,
117
121
  async () => {
118
- // Phase 1: Sync
119
- const syncResult = collection
120
- ? await defaultSyncService
121
- .syncCollection(collection, ctx.store, options)
122
- .then((r) => ({
123
- collections: [r],
124
- totalDurationMs: r.durationMs,
125
- totalFilesProcessed: r.filesProcessed,
126
- totalFilesAdded: r.filesAdded,
127
- totalFilesUpdated: r.filesUpdated,
128
- totalFilesErrored: r.filesErrored,
129
- totalFilesSkipped: r.filesSkipped,
130
- }))
131
- : await defaultSyncService.syncAll(
132
- ctx.collections,
133
- ctx.store,
134
- options
135
- );
136
-
137
- // Phase 2: Embed
138
- const llm = new LlmAdapter(ctx.config);
139
- const embedResult = await llm.createEmbeddingPort(modelUri, {
140
- policy: { offline: true, allowDownload: false },
141
- });
142
-
143
- if (!embedResult.ok) {
144
- throw new Error(
145
- `MODEL_NOT_FOUND: Embedding model not cached. ` +
146
- `Model: ${modelUri}. ` +
147
- `Run 'gno models pull embed' first.`
148
- );
149
- }
150
-
151
- const embedPort = embedResult.value;
152
-
122
+ const lease = ctx.acquireModelLease?.();
153
123
  try {
154
- // Initialize and get dimensions from port interface
155
- const initResult = await embedPort.init();
156
- if (!initResult.ok) {
157
- throw new Error(initResult.error.message);
158
- }
159
- const dimensions = embedPort.dimensions();
160
-
161
- // Create vector index port
162
- const db = ctx.store.getRawDb();
163
- const vectorResult = await createVectorIndexPort(db, {
164
- model: modelUri,
165
- dimensions,
166
- });
167
- if (!vectorResult.ok) {
168
- throw new Error(vectorResult.error.message);
169
- }
170
- const vectorIndex = vectorResult.value;
171
-
172
- // Create stats port for backlog
173
- const statsPort = createVectorStatsPort(db);
174
-
175
- // Run embedding
176
- const backlogResult = await embedBacklog({
177
- statsPort,
178
- embedPort,
179
- vectorIndex,
180
- collection: collection?.name,
181
- modelUri,
182
- batchSize: 32,
124
+ // Phase 1: Sync
125
+ const syncResult = collection
126
+ ? await defaultSyncService
127
+ .syncCollection(collection, ctx.store, options)
128
+ .then((r) => ({
129
+ collections: [r],
130
+ totalDurationMs: r.durationMs,
131
+ totalFilesProcessed: r.filesProcessed,
132
+ totalFilesAdded: r.filesAdded,
133
+ totalFilesUpdated: r.filesUpdated,
134
+ totalFilesErrored: r.filesErrored,
135
+ totalFilesSkipped: r.filesSkipped,
136
+ }))
137
+ : await defaultSyncService.syncAll(
138
+ ctx.collections,
139
+ ctx.store,
140
+ options
141
+ );
142
+ recordContentMutation(syncResult, ctx.markContentMutation);
143
+
144
+ // Phase 2: Embed
145
+ const llm = new LlmAdapter(ctx.config);
146
+ const embedResult = await llm.createEmbeddingPort(modelUri, {
147
+ policy: { offline: true, allowDownload: false },
183
148
  });
184
149
 
185
- if (!backlogResult.ok) {
186
- throw new Error(backlogResult.error.message);
150
+ if (!embedResult.ok) {
151
+ throw new Error(
152
+ `MODEL_NOT_FOUND: Embedding model not cached. ` +
153
+ `Model: ${modelUri}. ` +
154
+ `Run 'gno models pull embed' first.`
155
+ );
187
156
  }
188
157
 
189
- return {
190
- kind: "index" as const,
191
- value: {
192
- sync: syncResult,
193
- embed: backlogResult.value,
194
- },
195
- };
158
+ const embedPort = embedResult.value;
159
+
160
+ try {
161
+ // Initialize and get dimensions from port interface
162
+ const initResult = await embedPort.init();
163
+ if (!initResult.ok) {
164
+ throw new Error(initResult.error.message);
165
+ }
166
+ const dimensions = embedPort.dimensions();
167
+
168
+ // Create vector index port
169
+ const db = ctx.store.getRawDb();
170
+ const vectorResult = await createVectorIndexPort(db, {
171
+ model: modelUri,
172
+ dimensions,
173
+ });
174
+ if (!vectorResult.ok) {
175
+ throw new Error(vectorResult.error.message);
176
+ }
177
+ const vectorIndex = vectorResult.value;
178
+
179
+ // Create stats port for backlog
180
+ const statsPort = createVectorStatsPort(db);
181
+
182
+ // Run embedding
183
+ const backlogResult = await embedBacklog({
184
+ statsPort,
185
+ embedPort,
186
+ vectorIndex,
187
+ collection: collection?.name,
188
+ modelUri,
189
+ batchSize: 32,
190
+ });
191
+
192
+ if (!backlogResult.ok) {
193
+ throw new Error(backlogResult.error.message);
194
+ }
195
+ recordIndexMutation(
196
+ backlogResult.value.embedded,
197
+ ctx.markIndexMutation
198
+ );
199
+
200
+ return {
201
+ kind: "index" as const,
202
+ value: {
203
+ sync: syncResult,
204
+ embed: backlogResult.value,
205
+ },
206
+ };
207
+ } finally {
208
+ await embedPort.dispose();
209
+ }
196
210
  } finally {
197
- await embedPort.dispose();
211
+ lease?.release();
198
212
  }
199
213
  }
200
214
  );
@@ -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,
@@ -83,6 +98,25 @@ export const MCP_TOOL_DESCRIPTIONS = {
83
98
  "Verify a saved Context Capsule without rebuilding or mutating it. Reports unchanged, stale, missing, reranked, and fingerprint drift states against the active index.",
84
99
  } as const;
85
100
 
101
+ /** Tool names whose execution mutates disk, config, or index state. */
102
+ export const MCP_WRITE_TOOL_NAMES = new Set([
103
+ "gno_capture",
104
+ "gno_add_collection",
105
+ "gno_sync",
106
+ "gno_embed",
107
+ "gno_index",
108
+ "gno_remove_collection",
109
+ "gno_clear_collection_embeddings",
110
+ "gno_create_folder",
111
+ "gno_rename_note",
112
+ "gno_move_note",
113
+ "gno_duplicate_note",
114
+ "gno_trace_label",
115
+ "gno_trace_export",
116
+ "gno_trace_delete",
117
+ "gno_trace_purge",
118
+ ]);
119
+
86
120
  // ─────────────────────────────────────────────────────────────────────────────
87
121
  // Shared Input Schemas
88
122
  // ─────────────────────────────────────────────────────────────────────────────
@@ -548,6 +582,12 @@ const getInputSchema = z.object({
548
582
  .boolean()
549
583
  .default(true)
550
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"),
551
591
  });
552
592
 
553
593
  const multiGetInputSchema = z.object({
@@ -829,11 +869,19 @@ export async function runTool<T>(
829
869
 
830
870
  // Sequential execution via mutex
831
871
  const release = await ctx.toolMutex.acquire();
872
+ const modelLease = ctx.acquireModelLease?.();
832
873
  try {
833
- const data = await fn();
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;
834
879
  return {
835
880
  content: [{ type: "text", text: formatText(data) }],
836
881
  structuredContent: data as { [x: string]: unknown },
882
+ ...(traceMetadata
883
+ ? { _meta: { gno: { retrievalTrace: traceMetadata } } }
884
+ : {}),
837
885
  };
838
886
  } catch (e) {
839
887
  // Exception firewall: never throw, always return isError
@@ -846,6 +894,7 @@ export async function runTool<T>(
846
894
  structuredContent: parsedError,
847
895
  };
848
896
  } finally {
897
+ modelLease?.release();
849
898
  release();
850
899
  }
851
900
  }
@@ -869,8 +918,9 @@ export async function runToolNoMutex<T>(
869
918
  };
870
919
  }
871
920
 
921
+ const modelLease = ctx.acquireModelLease?.();
872
922
  try {
873
- const data = await fn();
923
+ const data = await (ctx.runWithSnapshot?.(fn) ?? fn());
874
924
  return {
875
925
  content: [{ type: "text", text: formatText(data) }],
876
926
  structuredContent: data as { [x: string]: unknown },
@@ -885,6 +935,8 @@ export async function runToolNoMutex<T>(
885
935
  content: [{ type: "text", text: `Error: ${message}` }],
886
936
  structuredContent: parsedError,
887
937
  };
938
+ } finally {
939
+ modelLease?.release();
888
940
  }
889
941
  }
890
942
 
@@ -975,6 +1027,20 @@ export function registerTools(server: McpServer, ctx: ToolContext): void {
975
1027
  (args) => handleStatus(args, ctx)
976
1028
  );
977
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
+
978
1044
  server.tool(
979
1045
  "gno_list_tags",
980
1046
  "List all tags with document counts. Use prefix to filter hierarchical tags (e.g. 'project/').",
@@ -1032,6 +1098,34 @@ export function registerTools(server: McpServer, ctx: ToolContext): void {
1032
1098
  );
1033
1099
 
1034
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
+
1035
1129
  server.tool(
1036
1130
  "gno_capture",
1037
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.",