@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.
- package/README.md +14 -7
- package/assets/skill/SKILL.md +54 -12
- package/assets/skill/mcp-reference.md +7 -2
- package/assets/skill/recipes/citation-and-provenance.md +32 -9
- package/package.json +2 -1
- package/spec/AGENTS.md +83 -0
- package/spec/CLAUDE.md +83 -0
- package/spec/bench-fixture.schema.json +137 -0
- package/spec/cli.md +2919 -0
- package/spec/db/schema.sql +442 -0
- package/spec/evals-agentic.md +592 -0
- package/spec/evals.md +1106 -0
- package/spec/mcp.md +2279 -0
- package/spec/output-schemas/activation-verification.schema.json +515 -0
- package/spec/output-schemas/ask.schema.json +564 -0
- package/spec/output-schemas/backlinks.schema.json +131 -0
- package/spec/output-schemas/bench-result.schema.json +120 -0
- package/spec/output-schemas/capture-receipt.schema.json +143 -0
- package/spec/output-schemas/claim-verification.schema.json +291 -0
- package/spec/output-schemas/collection-list.schema.json +45 -0
- package/spec/output-schemas/context-capsule-v1.schema.json +726 -0
- package/spec/output-schemas/context-capsule-verification.schema.json +1338 -0
- package/spec/output-schemas/context-list.schema.json +21 -0
- package/spec/output-schemas/doctor.schema.json +313 -0
- package/spec/output-schemas/error.schema.json +30 -0
- package/spec/output-schemas/expansion.schema.json +37 -0
- package/spec/output-schemas/get.schema.json +140 -0
- package/spec/output-schemas/graph-query.schema.json +99 -0
- package/spec/output-schemas/graph.schema.json +371 -0
- package/spec/output-schemas/links-list.schema.json +186 -0
- package/spec/output-schemas/mcp-add-collection-result.schema.json +23 -0
- package/spec/output-schemas/mcp-capture-result.schema.json +152 -0
- package/spec/output-schemas/mcp-http-error.schema.json +30 -0
- package/spec/output-schemas/mcp-job-list.schema.json +58 -0
- package/spec/output-schemas/mcp-job-status.schema.json +224 -0
- package/spec/output-schemas/mcp-remove-result.schema.json +39 -0
- package/spec/output-schemas/mcp-sync-result.schema.json +41 -0
- package/spec/output-schemas/mcp-tag-result.schema.json +33 -0
- package/spec/output-schemas/models-list.schema.json +93 -0
- package/spec/output-schemas/multi-get.schema.json +103 -0
- package/spec/output-schemas/process-status.schema.json +119 -0
- package/spec/output-schemas/query-diagnose.schema.json +123 -0
- package/spec/output-schemas/resident-status.schema.json +154 -0
- package/spec/output-schemas/retrieval-trace-common.schema.json +492 -0
- package/spec/output-schemas/retrieval-trace-delete.schema.json +16 -0
- package/spec/output-schemas/retrieval-trace-export.schema.json +61 -0
- package/spec/output-schemas/retrieval-trace-filters.schema.json +139 -0
- package/spec/output-schemas/retrieval-trace-judgment.schema.json +15 -0
- package/spec/output-schemas/retrieval-trace-list.schema.json +18 -0
- package/spec/output-schemas/retrieval-trace-payloads.schema.json +178 -0
- package/spec/output-schemas/retrieval-trace-purge.schema.json +31 -0
- package/spec/output-schemas/retrieval-trace-qrels.schema.json +303 -0
- package/spec/output-schemas/retrieval-trace-replay.schema.json +286 -0
- package/spec/output-schemas/retrieval-trace-show.schema.json +69 -0
- package/spec/output-schemas/retrieval-trace-summary.schema.json +65 -0
- package/spec/output-schemas/search-result.schema.json +154 -0
- package/spec/output-schemas/search-results.schema.json +338 -0
- package/spec/output-schemas/similar.schema.json +84 -0
- package/spec/output-schemas/status.schema.json +676 -0
- package/spec/output-schemas/tags-list.schema.json +48 -0
- package/src/app/context-runtime-contract.ts +10 -5
- package/src/app/context-runtime-input.ts +29 -1
- package/src/app/context-runtime-types.ts +7 -0
- package/src/app/context-runtime.ts +20 -2
- package/src/app/context-surface.ts +4 -0
- package/src/app/verified-ask.ts +291 -0
- package/src/cli/commands/ask-format.ts +255 -0
- package/src/cli/commands/ask.ts +144 -183
- package/src/cli/commands/context-build.ts +56 -9
- package/src/cli/commands/get.ts +64 -3
- package/src/cli/commands/query.ts +62 -23
- package/src/cli/commands/replay.ts +140 -0
- package/src/cli/commands/search.ts +48 -3
- package/src/cli/commands/shared.ts +3 -1
- package/src/cli/commands/trace.ts +200 -0
- package/src/cli/commands/vsearch.ts +75 -53
- package/src/cli/program.ts +287 -1
- package/src/config/index.ts +9 -0
- package/src/config/retrieval-traces.ts +56 -0
- package/src/config/types.ts +4 -0
- package/src/core/context-budget.ts +6 -0
- package/src/core/context-capsule-retrieval-schema.ts +4 -0
- package/src/core/context-capsule-schema.ts +17 -0
- package/src/core/context-capsule-validation.ts +3 -2
- package/src/core/context-capsule.ts +18 -0
- package/src/core/context-compiler.ts +44 -25
- package/src/core/context-evidence.ts +6 -0
- package/src/core/retrieval-qrels.ts +405 -0
- package/src/core/retrieval-replay-candidate.ts +368 -0
- package/src/core/retrieval-replay-types.ts +109 -0
- package/src/core/retrieval-replay-validation.ts +89 -0
- package/src/core/retrieval-replay.ts +441 -0
- package/src/core/retrieval-trace-evidence-origin.ts +178 -0
- package/src/core/retrieval-trace-export.ts +113 -0
- package/src/core/retrieval-trace-filter-normalization.ts +27 -0
- package/src/core/retrieval-trace-filters.ts +19 -0
- package/src/core/retrieval-trace-management-helpers.ts +247 -0
- package/src/core/retrieval-trace-management-types.ts +132 -0
- package/src/core/retrieval-trace-management.ts +422 -0
- package/src/core/retrieval-trace-request.ts +141 -0
- package/src/core/retrieval-trace-session.ts +507 -0
- package/src/core/retrieval-trace.ts +472 -0
- package/src/llm/errors.ts +10 -1
- package/src/llm/httpGeneration.ts +11 -1
- package/src/llm/nodeLlamaCpp/generation.ts +54 -10
- package/src/llm/types.ts +6 -0
- package/src/mcp/tools/ask.ts +228 -0
- package/src/mcp/tools/context.ts +87 -15
- package/src/mcp/tools/get.ts +35 -1
- package/src/mcp/tools/index.ts +83 -0
- package/src/mcp/tools/query.ts +95 -64
- package/src/mcp/tools/search.ts +36 -13
- package/src/mcp/tools/trace.ts +143 -0
- package/src/mcp/tools/vsearch.ts +71 -38
- package/src/pipeline/answer.ts +167 -26
- package/src/pipeline/claim-verification-schema.ts +235 -0
- package/src/pipeline/claim-verification.ts +487 -0
- package/src/pipeline/claim-verifier.ts +474 -0
- package/src/pipeline/graph-retrieval.ts +15 -1
- package/src/pipeline/hybrid.ts +151 -43
- package/src/pipeline/search.ts +36 -3
- package/src/pipeline/trace-metadata.ts +47 -0
- package/src/pipeline/types.ts +68 -0
- package/src/pipeline/vsearch.ts +101 -38
- package/src/sdk/client.ts +415 -73
- package/src/sdk/documents.ts +48 -1
- package/src/sdk/index.ts +17 -0
- package/src/sdk/types.ts +28 -0
- package/src/serve/context-capsule.ts +67 -8
- package/src/serve/public/app.tsx +12 -1
- package/src/serve/public/components/AskVerificationPanel.tsx +189 -0
- package/src/serve/public/globals.built.css +1 -1
- package/src/serve/public/lib/workspace-tabs.ts +2 -0
- package/src/serve/public/pages/Ask.tsx +42 -4
- package/src/serve/public/pages/Dashboard.tsx +10 -0
- package/src/serve/public/pages/TraceHistory.tsx +478 -0
- package/src/serve/public/pages/trace-history-detail.tsx +224 -0
- package/src/serve/retrieval-trace.ts +28 -0
- package/src/serve/routes/api.ts +508 -68
- package/src/serve/routes/traces.ts +156 -0
- package/src/serve/server.ts +87 -2
- package/src/store/index.ts +31 -0
- package/src/store/migrations/014-retrieval-traces.ts +303 -0
- package/src/store/migrations/index.ts +2 -0
- package/src/store/retrieval-trace-codec.ts +384 -0
- package/src/store/sqlite/adapter.ts +153 -1
- package/src/store/sqlite/retrieval-trace-management-store.ts +341 -0
- package/src/store/sqlite/retrieval-trace-retention.ts +349 -0
- package/src/store/sqlite/retrieval-trace-rows.ts +267 -0
- package/src/store/sqlite/retrieval-trace-store.ts +515 -0
- package/src/store/types.ts +297 -0
- package/src/store/vector/sqlite-vec.ts +76 -1
- package/src/store/vector/types.ts +1 -1
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/** MCP verified Ask tool over the shared closed-evidence application boundary. */
|
|
2
|
+
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import type { RetrievalTraceSession } from "../../core/retrieval-trace-session";
|
|
6
|
+
import type { QueryModeInput } from "../../pipeline/types";
|
|
7
|
+
import type { ToolContext } from "../server";
|
|
8
|
+
import type { ToolResult } from "./index";
|
|
9
|
+
|
|
10
|
+
import { buildVerifiedAsk } from "../../app/verified-ask";
|
|
11
|
+
import {
|
|
12
|
+
finishRetrievalTraceAfterError,
|
|
13
|
+
retrievalTraceFilters,
|
|
14
|
+
startRetrievalTraceRequest,
|
|
15
|
+
} from "../../core/retrieval-trace-request";
|
|
16
|
+
import { attachRetrievalTraceMetadata } from "../../core/retrieval-trace-session";
|
|
17
|
+
import { normalizeStructuredQueryInput } from "../../core/structured-query";
|
|
18
|
+
import { resolveModelUri } from "../../llm/registry";
|
|
19
|
+
import { answerTraceTerminalStatus } from "../../pipeline/answer";
|
|
20
|
+
import { createMcpModelPorts, type McpModelPortFactory } from "./context";
|
|
21
|
+
import { normalizeTagFilters, runTool } from "./index";
|
|
22
|
+
|
|
23
|
+
const queryModeSchema = z
|
|
24
|
+
.object({
|
|
25
|
+
mode: z.enum(["term", "intent", "hyde"]),
|
|
26
|
+
text: z.string().trim().min(1),
|
|
27
|
+
})
|
|
28
|
+
.strict();
|
|
29
|
+
|
|
30
|
+
export const askInputSchema = z
|
|
31
|
+
.object({
|
|
32
|
+
query: z.string().trim().min(1),
|
|
33
|
+
verify: z.literal(true),
|
|
34
|
+
collection: z.string().optional(),
|
|
35
|
+
limit: z.number().int().min(1).max(100).default(5),
|
|
36
|
+
minScore: z.number().min(0).max(1).optional(),
|
|
37
|
+
lang: z.string().optional(),
|
|
38
|
+
intent: z.string().optional(),
|
|
39
|
+
candidateLimit: z.number().int().min(1).max(100).optional(),
|
|
40
|
+
exclude: z.array(z.string()).optional(),
|
|
41
|
+
queryModes: z.array(queryModeSchema).optional(),
|
|
42
|
+
tagsAll: z.array(z.string()).optional(),
|
|
43
|
+
tagsAny: z.array(z.string()).optional(),
|
|
44
|
+
since: z.string().optional(),
|
|
45
|
+
until: z.string().optional(),
|
|
46
|
+
categories: z.array(z.string()).optional(),
|
|
47
|
+
author: z.string().optional(),
|
|
48
|
+
graph: z.boolean().optional(),
|
|
49
|
+
noGraph: z.boolean().optional(),
|
|
50
|
+
noRerank: z.boolean().optional(),
|
|
51
|
+
maxAnswerTokens: z.number().int().positive().optional(),
|
|
52
|
+
contextBudgetTokens: z.number().int().positive().optional(),
|
|
53
|
+
contextBudgetBytes: z.number().int().positive().optional(),
|
|
54
|
+
})
|
|
55
|
+
.strict();
|
|
56
|
+
|
|
57
|
+
type AskInput = z.infer<typeof askInputSchema>;
|
|
58
|
+
|
|
59
|
+
export interface HandleAskDependencies {
|
|
60
|
+
modelPortFactory?: McpModelPortFactory;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const exactSpan = (uri: string, startLine: number, endLine: number): string =>
|
|
64
|
+
`${uri}:L${startLine}${startLine === endLine ? "" : `-L${endLine}`}`;
|
|
65
|
+
|
|
66
|
+
export const formatVerifiedAskReadable = (
|
|
67
|
+
result: Awaited<ReturnType<typeof buildVerifiedAsk>>
|
|
68
|
+
): string => {
|
|
69
|
+
const numbers = new Map(
|
|
70
|
+
(result.citations ?? []).flatMap((citation, index) =>
|
|
71
|
+
citation.evidenceId ? [[citation.evidenceId, index + 1] as const] : []
|
|
72
|
+
)
|
|
73
|
+
);
|
|
74
|
+
const answer = (result.answer ?? "").replace(
|
|
75
|
+
/\[evidence:([a-f0-9]{64})\]/g,
|
|
76
|
+
(_marker, evidenceId: string) => {
|
|
77
|
+
const number = numbers.get(evidenceId);
|
|
78
|
+
return number === undefined ? "" : `[${number}]`;
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
const claims = result.verification?.claims;
|
|
82
|
+
const lines = [
|
|
83
|
+
answer || "No verified answer.",
|
|
84
|
+
"",
|
|
85
|
+
`Verification: ${claims?.answerStatus ?? "unavailable"}`,
|
|
86
|
+
];
|
|
87
|
+
if (claims?.abstentionReason) {
|
|
88
|
+
lines.push(`Reason: ${claims.abstentionReason}`);
|
|
89
|
+
}
|
|
90
|
+
if (claims) {
|
|
91
|
+
lines.push(
|
|
92
|
+
`Claims: ${claims.coverage.supportedClaims}/${claims.coverage.totalClaims} supported`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
const semantic = result.verification?.semantic;
|
|
96
|
+
if (semantic) {
|
|
97
|
+
lines.push(`Semantic verifier: ${semantic.status} (${semantic.reason})`);
|
|
98
|
+
}
|
|
99
|
+
for (const claim of claims?.claims ?? []) {
|
|
100
|
+
lines.push(`- ${claim.status}: ${claim.text}`);
|
|
101
|
+
for (const evidence of claim.evidence) {
|
|
102
|
+
lines.push(
|
|
103
|
+
` ${exactSpan(evidence.uri, evidence.startLine, evidence.endLine)} (${evidence.evidenceId})`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
for (const gap of result.verification?.capsule.coverage.gaps ?? []) {
|
|
108
|
+
lines.push(`Gap: ${gap.facet} (${gap.code})`);
|
|
109
|
+
}
|
|
110
|
+
for (const facet of result.verification?.capsule.coverage.unresolvedFacets ??
|
|
111
|
+
[]) {
|
|
112
|
+
lines.push(`Unresolved facet: ${facet}`);
|
|
113
|
+
}
|
|
114
|
+
for (const [name, state] of Object.entries(
|
|
115
|
+
result.verification?.capsule.retrieval.capabilityStates ?? {}
|
|
116
|
+
)) {
|
|
117
|
+
if (state.requested && state.outcome !== "used") {
|
|
118
|
+
lines.push(
|
|
119
|
+
`Capability: ${name} ${state.outcome}${state.fallbackReasons.length > 0 ? ` (${state.fallbackReasons.join(", ")})` : ""}`
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
for (const [index, citation] of (result.citations ?? []).entries()) {
|
|
124
|
+
const span =
|
|
125
|
+
citation.startLine === undefined || citation.endLine === undefined
|
|
126
|
+
? citation.uri
|
|
127
|
+
: exactSpan(citation.uri, citation.startLine, citation.endLine);
|
|
128
|
+
lines.push(`[${index + 1}] ${span} (${citation.evidenceId ?? "unknown"})`);
|
|
129
|
+
}
|
|
130
|
+
return lines.join("\n");
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
export const handleAsk = (
|
|
134
|
+
args: AskInput,
|
|
135
|
+
context: ToolContext,
|
|
136
|
+
dependencies: HandleAskDependencies = {}
|
|
137
|
+
): Promise<ToolResult> =>
|
|
138
|
+
runTool(
|
|
139
|
+
context,
|
|
140
|
+
"gno_ask",
|
|
141
|
+
async () => {
|
|
142
|
+
if (
|
|
143
|
+
args.collection &&
|
|
144
|
+
!context.collections.some(
|
|
145
|
+
(collection) => collection.name === args.collection
|
|
146
|
+
)
|
|
147
|
+
) {
|
|
148
|
+
throw new Error(`Collection not found: ${args.collection}`);
|
|
149
|
+
}
|
|
150
|
+
const normalized = normalizeStructuredQueryInput(
|
|
151
|
+
args.query,
|
|
152
|
+
(args.queryModes ?? []) as QueryModeInput[]
|
|
153
|
+
);
|
|
154
|
+
if (!normalized.ok) throw new Error(normalized.error.message);
|
|
155
|
+
const query = normalized.value.query;
|
|
156
|
+
const options = {
|
|
157
|
+
...args,
|
|
158
|
+
queryModes:
|
|
159
|
+
normalized.value.queryModes.length > 0
|
|
160
|
+
? normalized.value.queryModes
|
|
161
|
+
: undefined,
|
|
162
|
+
tagsAll: normalizeTagFilters(args.tagsAll),
|
|
163
|
+
tagsAny: normalizeTagFilters(args.tagsAny),
|
|
164
|
+
};
|
|
165
|
+
let traceSession: RetrievalTraceSession | undefined;
|
|
166
|
+
let modelPorts: Awaited<ReturnType<typeof createMcpModelPorts>> | null =
|
|
167
|
+
null;
|
|
168
|
+
try {
|
|
169
|
+
const started = await startRetrievalTraceRequest({
|
|
170
|
+
store: context.store,
|
|
171
|
+
config: context.config,
|
|
172
|
+
query,
|
|
173
|
+
filters: retrievalTraceFilters(options),
|
|
174
|
+
pipeline: "ask",
|
|
175
|
+
indexName: context.indexName,
|
|
176
|
+
modelUris: [
|
|
177
|
+
resolveModelUri(
|
|
178
|
+
context.config,
|
|
179
|
+
"embed",
|
|
180
|
+
undefined,
|
|
181
|
+
args.collection
|
|
182
|
+
),
|
|
183
|
+
resolveModelUri(
|
|
184
|
+
context.config,
|
|
185
|
+
"rerank",
|
|
186
|
+
undefined,
|
|
187
|
+
args.collection
|
|
188
|
+
),
|
|
189
|
+
resolveModelUri(context.config, "gen", undefined, args.collection),
|
|
190
|
+
],
|
|
191
|
+
});
|
|
192
|
+
if (!started.ok) throw new Error(started.error.message);
|
|
193
|
+
traceSession = started.value ?? undefined;
|
|
194
|
+
modelPorts = await createMcpModelPorts(
|
|
195
|
+
context,
|
|
196
|
+
args.collection,
|
|
197
|
+
dependencies.modelPortFactory,
|
|
198
|
+
{ generation: true }
|
|
199
|
+
);
|
|
200
|
+
if (!modelPorts.genPort) {
|
|
201
|
+
throw new Error(
|
|
202
|
+
"Answer generation requested but no generation model is available"
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
const result = await buildVerifiedAsk(query, options, {
|
|
206
|
+
store: context.store,
|
|
207
|
+
config: context.config,
|
|
208
|
+
indexName: context.indexName,
|
|
209
|
+
vectorIndex: modelPorts.vectorIndex,
|
|
210
|
+
embedPort: modelPorts.embedPort,
|
|
211
|
+
rerankPort: modelPorts.rerankPort,
|
|
212
|
+
genPort: modelPorts.genPort,
|
|
213
|
+
traceSession,
|
|
214
|
+
});
|
|
215
|
+
const finished = await traceSession?.finish(
|
|
216
|
+
answerTraceTerminalStatus(result.citations)
|
|
217
|
+
);
|
|
218
|
+
if (finished && !finished.ok) throw new Error(finished.error.message);
|
|
219
|
+
return attachRetrievalTraceMetadata(result, traceSession);
|
|
220
|
+
} catch (error) {
|
|
221
|
+
await finishRetrievalTraceAfterError(traceSession, error);
|
|
222
|
+
throw error;
|
|
223
|
+
} finally {
|
|
224
|
+
await modelPorts?.dispose();
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
formatVerifiedAskReadable
|
|
228
|
+
);
|
package/src/mcp/tools/context.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
/** MCP Context Capsule tools over the shared application runtime. */
|
|
2
2
|
|
|
3
|
+
import type { RetrievalTraceSession } from "../../core/retrieval-trace-session";
|
|
3
4
|
import type { ModelLease } from "../../llm/nodeLlamaCpp/lifecycle";
|
|
4
|
-
import type {
|
|
5
|
+
import type {
|
|
6
|
+
EmbeddingPort,
|
|
7
|
+
GenerationPort,
|
|
8
|
+
RerankPort,
|
|
9
|
+
} from "../../llm/types";
|
|
5
10
|
import type { VectorIndexPort } from "../../store/vector";
|
|
6
11
|
import type { ToolContext } from "../server";
|
|
7
12
|
import type { ToolResult } from "./index";
|
|
@@ -20,6 +25,10 @@ import {
|
|
|
20
25
|
parseContextVerifySurfaceInput,
|
|
21
26
|
} from "../../app/context-surface";
|
|
22
27
|
import { createNonTtyProgressRenderer } from "../../cli/progress";
|
|
28
|
+
import {
|
|
29
|
+
finishRetrievalTraceAfterError,
|
|
30
|
+
startRetrievalTraceRequest,
|
|
31
|
+
} from "../../core/retrieval-trace-request";
|
|
23
32
|
import { LlmAdapter } from "../../llm/nodeLlamaCpp/adapter";
|
|
24
33
|
import { resolveDownloadPolicy } from "../../llm/policy";
|
|
25
34
|
import { resolveModelUri } from "../../llm/registry";
|
|
@@ -28,11 +37,15 @@ import { createVectorIndexPort } from "../../store/vector";
|
|
|
28
37
|
interface ContextToolResultData {
|
|
29
38
|
structuredContent: Record<string, unknown>;
|
|
30
39
|
text: string;
|
|
40
|
+
traceId?: string;
|
|
31
41
|
}
|
|
32
42
|
|
|
33
43
|
const asToolResult = (data: ContextToolResultData): ToolResult => ({
|
|
34
44
|
content: [{ type: "text", text: data.text }],
|
|
35
45
|
structuredContent: data.structuredContent,
|
|
46
|
+
...(data.traceId
|
|
47
|
+
? { _meta: { gno: { retrievalTrace: { traceId: data.traceId } } } }
|
|
48
|
+
: {}),
|
|
36
49
|
});
|
|
37
50
|
|
|
38
51
|
const asToolError = (error: unknown): ToolResult => {
|
|
@@ -76,13 +89,15 @@ const runContextTool = async (
|
|
|
76
89
|
|
|
77
90
|
interface McpModelPorts {
|
|
78
91
|
embedPort: EmbeddingPort | null;
|
|
92
|
+
genPort: GenerationPort | null;
|
|
79
93
|
rerankPort: RerankPort | null;
|
|
80
94
|
vectorIndex: VectorIndexPort | null;
|
|
81
95
|
dispose(): Promise<void>;
|
|
82
96
|
}
|
|
83
97
|
|
|
84
|
-
interface McpModelPortFactory {
|
|
98
|
+
export interface McpModelPortFactory {
|
|
85
99
|
createEmbeddingPort: LlmAdapter["createEmbeddingPort"];
|
|
100
|
+
createGenerationPort?: LlmAdapter["createGenerationPort"];
|
|
86
101
|
createRerankPort: LlmAdapter["createRerankPort"];
|
|
87
102
|
acquireModelLease?: LlmAdapter["acquireModelLease"];
|
|
88
103
|
}
|
|
@@ -100,7 +115,8 @@ export const disposeContextModelOwners = async (
|
|
|
100
115
|
export const createMcpModelPorts = async (
|
|
101
116
|
context: ToolContext,
|
|
102
117
|
collection?: string,
|
|
103
|
-
factoryOverride?: McpModelPortFactory
|
|
118
|
+
factoryOverride?: McpModelPortFactory,
|
|
119
|
+
options: { generation?: boolean } = {}
|
|
104
120
|
): Promise<McpModelPorts> => {
|
|
105
121
|
const llm = new LlmAdapter(context.config);
|
|
106
122
|
const factory = factoryOverride ?? llm;
|
|
@@ -116,6 +132,7 @@ export const createMcpModelPorts = async (
|
|
|
116
132
|
let embedPort: EmbeddingPort | null = null;
|
|
117
133
|
let ownedEmbedPort: EmbeddingPort | null = null;
|
|
118
134
|
let rerankPort: RerankPort | null = null;
|
|
135
|
+
let genPort: GenerationPort | null = null;
|
|
119
136
|
let vectorIndex: VectorIndexPort | null = null;
|
|
120
137
|
try {
|
|
121
138
|
const embedResult = await factory.createEmbeddingPort(embedUri, {
|
|
@@ -143,14 +160,26 @@ export const createMcpModelPorts = async (
|
|
|
143
160
|
}
|
|
144
161
|
);
|
|
145
162
|
if (rerankResult.ok) rerankPort = rerankResult.value;
|
|
163
|
+
if (options.generation && factory.createGenerationPort) {
|
|
164
|
+
const genResult = await factory.createGenerationPort(
|
|
165
|
+
resolveModelUri(context.config, "gen", undefined, collection),
|
|
166
|
+
{
|
|
167
|
+
policy,
|
|
168
|
+
onProgress: (value) => progress("gen", value),
|
|
169
|
+
}
|
|
170
|
+
);
|
|
171
|
+
if (genResult.ok) genPort = genResult.value;
|
|
172
|
+
}
|
|
146
173
|
return {
|
|
147
174
|
embedPort,
|
|
175
|
+
genPort,
|
|
148
176
|
rerankPort,
|
|
149
177
|
vectorIndex,
|
|
150
178
|
async dispose() {
|
|
151
179
|
await disposeContextModelOwners(
|
|
152
|
-
[ownedEmbedPort, rerankPort].filter(
|
|
153
|
-
(port): port is EmbeddingPort | RerankPort
|
|
180
|
+
[ownedEmbedPort, rerankPort, genPort].filter(
|
|
181
|
+
(port): port is EmbeddingPort | RerankPort | GenerationPort =>
|
|
182
|
+
port !== null
|
|
154
183
|
),
|
|
155
184
|
lease
|
|
156
185
|
);
|
|
@@ -158,8 +187,9 @@ export const createMcpModelPorts = async (
|
|
|
158
187
|
};
|
|
159
188
|
} catch (error) {
|
|
160
189
|
await disposeContextModelOwners(
|
|
161
|
-
[ownedEmbedPort, rerankPort].filter(
|
|
162
|
-
(port): port is EmbeddingPort | RerankPort
|
|
190
|
+
[ownedEmbedPort, rerankPort, genPort].filter(
|
|
191
|
+
(port): port is EmbeddingPort | RerankPort | GenerationPort =>
|
|
192
|
+
port !== null
|
|
163
193
|
),
|
|
164
194
|
lease
|
|
165
195
|
);
|
|
@@ -180,15 +210,50 @@ export const handleContext = (
|
|
|
180
210
|
context.config.collections.map((collection) => collection.name)
|
|
181
211
|
);
|
|
182
212
|
const useModels = parsed.input.depthPolicy !== "fast";
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
213
|
+
const collection =
|
|
214
|
+
parsed.input.collections?.length === 1
|
|
215
|
+
? parsed.input.collections[0]
|
|
216
|
+
: undefined;
|
|
217
|
+
const modelUris = useModels
|
|
218
|
+
? [
|
|
219
|
+
resolveModelUri(context.config, "embed", undefined, collection),
|
|
220
|
+
resolveModelUri(context.config, "rerank", undefined, collection),
|
|
221
|
+
]
|
|
222
|
+
: [];
|
|
223
|
+
let modelPorts: Awaited<ReturnType<typeof createMcpModelPorts>> | null =
|
|
224
|
+
null;
|
|
225
|
+
let traceSession: RetrievalTraceSession | undefined;
|
|
191
226
|
try {
|
|
227
|
+
const traceStart = await startRetrievalTraceRequest({
|
|
228
|
+
store: context.store,
|
|
229
|
+
config: context.config,
|
|
230
|
+
query: parsed.input.query ?? parsed.input.goal,
|
|
231
|
+
goal: parsed.input.goal,
|
|
232
|
+
filters: {
|
|
233
|
+
limit: parsed.input.limit,
|
|
234
|
+
collection,
|
|
235
|
+
collections: [...(parsed.input.collections ?? [])].sort(),
|
|
236
|
+
lang: parsed.input.lang,
|
|
237
|
+
tagsAll: parsed.input.tagsAll,
|
|
238
|
+
tagsAny: parsed.input.tagsAny,
|
|
239
|
+
since: parsed.input.since,
|
|
240
|
+
until: parsed.input.until,
|
|
241
|
+
categories: parsed.input.categories,
|
|
242
|
+
author: parsed.input.author,
|
|
243
|
+
graph: parsed.input.graph,
|
|
244
|
+
candidateLimit: parsed.input.candidateLimit,
|
|
245
|
+
queryModes: parsed.input.queryModes,
|
|
246
|
+
uriPrefix: parsed.input.uriPrefix ?? undefined,
|
|
247
|
+
},
|
|
248
|
+
pipeline: "context",
|
|
249
|
+
indexName: context.indexName,
|
|
250
|
+
modelUris,
|
|
251
|
+
});
|
|
252
|
+
if (!traceStart.ok) throw new Error(traceStart.error.message);
|
|
253
|
+
traceSession = traceStart.value ?? undefined;
|
|
254
|
+
modelPorts = useModels
|
|
255
|
+
? await createMcpModelPorts(context, collection)
|
|
256
|
+
: null;
|
|
192
257
|
const capsule = await buildContextCapsule(parsed.input, {
|
|
193
258
|
store: context.store,
|
|
194
259
|
config: context.config,
|
|
@@ -196,14 +261,21 @@ export const handleContext = (
|
|
|
196
261
|
vectorIndex: modelPorts?.vectorIndex ?? null,
|
|
197
262
|
embedPort: modelPorts?.embedPort ?? null,
|
|
198
263
|
rerankPort: modelPorts?.rerankPort ?? null,
|
|
264
|
+
traceSession,
|
|
199
265
|
});
|
|
266
|
+
const finalized = await traceSession?.finish("completed");
|
|
267
|
+
if (finalized && !finalized.ok) throw new Error(finalized.error.message);
|
|
200
268
|
return {
|
|
201
269
|
structuredContent: capsule as unknown as Record<string, unknown>,
|
|
202
270
|
// MCP model context receives this projection exactly once. The full
|
|
203
271
|
// canonical capsule remains available to application clients through
|
|
204
272
|
// structuredContent and is deliberately not duplicated in text.
|
|
205
273
|
text: formatContextCapsuleAgentJson(capsule),
|
|
274
|
+
traceId: traceSession?.metadata()?.traceId,
|
|
206
275
|
};
|
|
276
|
+
} catch (cause) {
|
|
277
|
+
await finishRetrievalTraceAfterError(traceSession, cause);
|
|
278
|
+
throw cause;
|
|
207
279
|
} finally {
|
|
208
280
|
await modelPorts?.dispose();
|
|
209
281
|
}
|
package/src/mcp/tools/get.ts
CHANGED
|
@@ -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
|
}
|
package/src/mcp/tools/index.ts
CHANGED
|
@@ -16,8 +16,10 @@ 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";
|
|
22
|
+
import { askInputSchema, handleAsk } from "./ask";
|
|
21
23
|
import { handleCapture } from "./capture";
|
|
22
24
|
import { handleClearCollectionEmbeddings } from "./clear-collection-embeddings";
|
|
23
25
|
import { handleContext, handleContextVerify } from "./context";
|
|
@@ -42,6 +44,20 @@ import { handleRemoveCollection } from "./remove-collection";
|
|
|
42
44
|
import { handleSearch } from "./search";
|
|
43
45
|
import { handleStatus } from "./status";
|
|
44
46
|
import { handleSync } from "./sync";
|
|
47
|
+
import {
|
|
48
|
+
handleTraceDelete,
|
|
49
|
+
handleTraceExport,
|
|
50
|
+
handleTraceLabel,
|
|
51
|
+
handleTraceList,
|
|
52
|
+
handleTracePurge,
|
|
53
|
+
handleTraceShow,
|
|
54
|
+
traceDeleteInputSchema,
|
|
55
|
+
traceExportInputSchema,
|
|
56
|
+
traceLabelInputSchema,
|
|
57
|
+
traceListInputSchema,
|
|
58
|
+
tracePurgeInputSchema,
|
|
59
|
+
traceShowInputSchema,
|
|
60
|
+
} from "./trace";
|
|
45
61
|
import { handleVsearch } from "./vsearch";
|
|
46
62
|
import {
|
|
47
63
|
handleCreateFolder,
|
|
@@ -81,6 +97,7 @@ export const MCP_TOOL_DESCRIPTIONS = {
|
|
|
81
97
|
"Compile one deterministic, budgeted, extractive evidence Capsule with exact line spans, coverage gaps, omissions, provenance, and verification fingerprints. Raw search/get tools remain available for manual retrieval.",
|
|
82
98
|
contextVerify:
|
|
83
99
|
"Verify a saved Context Capsule without rebuilding or mutating it. Reports unchanged, stale, missing, reranked, and fingerprint drift states against the active index.",
|
|
100
|
+
ask: "Generate one answer from a deterministic Context Capsule, verify every substantive claim against exact retained spans, and abstain unless support coverage is complete. Read-only; returns the Capsule, freshness receipt, claim verdicts, gaps, and evidence IDs.",
|
|
84
101
|
} as const;
|
|
85
102
|
|
|
86
103
|
/** Tool names whose execution mutates disk, config, or index state. */
|
|
@@ -96,6 +113,10 @@ export const MCP_WRITE_TOOL_NAMES = new Set([
|
|
|
96
113
|
"gno_rename_note",
|
|
97
114
|
"gno_move_note",
|
|
98
115
|
"gno_duplicate_note",
|
|
116
|
+
"gno_trace_label",
|
|
117
|
+
"gno_trace_export",
|
|
118
|
+
"gno_trace_delete",
|
|
119
|
+
"gno_trace_purge",
|
|
99
120
|
]);
|
|
100
121
|
|
|
101
122
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -563,6 +584,12 @@ const getInputSchema = z.object({
|
|
|
563
584
|
.boolean()
|
|
564
585
|
.default(true)
|
|
565
586
|
.describe("Include line numbers in output"),
|
|
587
|
+
traceId: z
|
|
588
|
+
.string()
|
|
589
|
+
.min(1)
|
|
590
|
+
.max(128)
|
|
591
|
+
.optional()
|
|
592
|
+
.describe("Continue an open retrieval trace returned by a search/query"),
|
|
566
593
|
});
|
|
567
594
|
|
|
568
595
|
const multiGetInputSchema = z.object({
|
|
@@ -847,9 +874,16 @@ export async function runTool<T>(
|
|
|
847
874
|
const modelLease = ctx.acquireModelLease?.();
|
|
848
875
|
try {
|
|
849
876
|
const data = await (ctx.runWithSnapshot?.(fn) ?? fn());
|
|
877
|
+
const traceMetadata =
|
|
878
|
+
data !== null && typeof data === "object"
|
|
879
|
+
? (data as Record<PropertyKey, unknown>)[RETRIEVAL_TRACE_METADATA]
|
|
880
|
+
: undefined;
|
|
850
881
|
return {
|
|
851
882
|
content: [{ type: "text", text: formatText(data) }],
|
|
852
883
|
structuredContent: data as { [x: string]: unknown },
|
|
884
|
+
...(traceMetadata
|
|
885
|
+
? { _meta: { gno: { retrievalTrace: traceMetadata } } }
|
|
886
|
+
: {}),
|
|
853
887
|
};
|
|
854
888
|
} catch (e) {
|
|
855
889
|
// Exception firewall: never throw, always return isError
|
|
@@ -946,6 +980,13 @@ export function registerTools(server: McpServer, ctx: ToolContext): void {
|
|
|
946
980
|
(args) => handleContextVerify(args, ctx)
|
|
947
981
|
);
|
|
948
982
|
|
|
983
|
+
server.tool(
|
|
984
|
+
"gno_ask",
|
|
985
|
+
MCP_TOOL_DESCRIPTIONS.ask,
|
|
986
|
+
askInputSchema.shape,
|
|
987
|
+
(args) => handleAsk(args, ctx)
|
|
988
|
+
);
|
|
989
|
+
|
|
949
990
|
server.tool(
|
|
950
991
|
"gno_search",
|
|
951
992
|
MCP_TOOL_DESCRIPTIONS.search,
|
|
@@ -995,6 +1036,20 @@ export function registerTools(server: McpServer, ctx: ToolContext): void {
|
|
|
995
1036
|
(args) => handleStatus(args, ctx)
|
|
996
1037
|
);
|
|
997
1038
|
|
|
1039
|
+
server.tool(
|
|
1040
|
+
"gno_trace_list",
|
|
1041
|
+
"List bounded metadata-only summaries of private local retrieval traces. Raw replay queries are omitted from history.",
|
|
1042
|
+
traceListInputSchema.shape,
|
|
1043
|
+
(args) => handleTraceList(args, ctx)
|
|
1044
|
+
);
|
|
1045
|
+
|
|
1046
|
+
server.tool(
|
|
1047
|
+
"gno_trace_show",
|
|
1048
|
+
"Inspect one bounded local retrieval trace. Replay-mode content is returned only for the explicitly requested trace.",
|
|
1049
|
+
traceShowInputSchema.shape,
|
|
1050
|
+
(args) => handleTraceShow(args, ctx)
|
|
1051
|
+
);
|
|
1052
|
+
|
|
998
1053
|
server.tool(
|
|
999
1054
|
"gno_list_tags",
|
|
1000
1055
|
"List all tags with document counts. Use prefix to filter hierarchical tags (e.g. 'project/').",
|
|
@@ -1052,6 +1107,34 @@ export function registerTools(server: McpServer, ctx: ToolContext): void {
|
|
|
1052
1107
|
);
|
|
1053
1108
|
|
|
1054
1109
|
if (ctx.enableWrite) {
|
|
1110
|
+
server.tool(
|
|
1111
|
+
"gno_trace_label",
|
|
1112
|
+
"Append an explicit relevant, irrelevant, or missing_expected judgment to a local retrieval trace.",
|
|
1113
|
+
traceLabelInputSchema.shape,
|
|
1114
|
+
(args) => handleTraceLabel(args, ctx)
|
|
1115
|
+
);
|
|
1116
|
+
|
|
1117
|
+
server.tool(
|
|
1118
|
+
"gno_trace_export",
|
|
1119
|
+
"Build a deterministic local agentic receipt from one or more immutable terminal retrieval traces.",
|
|
1120
|
+
traceExportInputSchema.shape,
|
|
1121
|
+
(args) => handleTraceExport(args, ctx)
|
|
1122
|
+
);
|
|
1123
|
+
|
|
1124
|
+
server.tool(
|
|
1125
|
+
"gno_trace_delete",
|
|
1126
|
+
"Delete one private local retrieval trace and all owned receipt records.",
|
|
1127
|
+
traceDeleteInputSchema.shape,
|
|
1128
|
+
(args) => handleTraceDelete(args, ctx)
|
|
1129
|
+
);
|
|
1130
|
+
|
|
1131
|
+
server.tool(
|
|
1132
|
+
"gno_trace_purge",
|
|
1133
|
+
"Purge every private local retrieval trace receipt. Requires confirm=true.",
|
|
1134
|
+
tracePurgeInputSchema.shape,
|
|
1135
|
+
(args) => handleTracePurge(args, ctx)
|
|
1136
|
+
);
|
|
1137
|
+
|
|
1055
1138
|
server.tool(
|
|
1056
1139
|
"gno_capture",
|
|
1057
1140
|
"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.",
|