@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,384 @@
|
|
|
1
|
+
/** Runtime validation and deterministic encoding for local retrieval receipts. */
|
|
2
|
+
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
RetrievalTraceEventInput,
|
|
7
|
+
RetrievalTraceExportInput,
|
|
8
|
+
RetrievalTraceInput,
|
|
9
|
+
RetrievalTraceJudgmentInput,
|
|
10
|
+
RetrievalTraceRunInput,
|
|
11
|
+
} from "./types";
|
|
12
|
+
|
|
13
|
+
const MAX_ID_LENGTH = 128;
|
|
14
|
+
const MAX_IDEMPOTENCY_KEY_LENGTH = 256;
|
|
15
|
+
const sha256Schema = z.string().regex(/^[a-f0-9]{64}$/);
|
|
16
|
+
const idSchema = z.string().min(1).max(MAX_ID_LENGTH);
|
|
17
|
+
const idempotencyKeySchema = z.string().min(1).max(MAX_IDEMPOTENCY_KEY_LENGTH);
|
|
18
|
+
const epochMsSchema = z.number().int().safe().nonnegative();
|
|
19
|
+
const DANGEROUS_JSON_KEYS = new Set(["__proto__", "constructor", "prototype"]);
|
|
20
|
+
|
|
21
|
+
const jsonValueSchema: z.ZodType<unknown> = z.lazy(() =>
|
|
22
|
+
z.union([
|
|
23
|
+
z.string(),
|
|
24
|
+
z.number().finite(),
|
|
25
|
+
z.boolean(),
|
|
26
|
+
z.null(),
|
|
27
|
+
z.array(jsonValueSchema),
|
|
28
|
+
z.record(z.string(), jsonValueSchema),
|
|
29
|
+
])
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
export const traceJsonObjectSchema = z.record(z.string(), jsonValueSchema);
|
|
33
|
+
|
|
34
|
+
const SAFE_RECEIPT_STRING_KEYS = new Set([
|
|
35
|
+
"capability",
|
|
36
|
+
"capsuleId",
|
|
37
|
+
"collection",
|
|
38
|
+
"code",
|
|
39
|
+
"docid",
|
|
40
|
+
"filterFingerprint",
|
|
41
|
+
"heading",
|
|
42
|
+
"id",
|
|
43
|
+
"kind",
|
|
44
|
+
"mirrorHash",
|
|
45
|
+
"mode",
|
|
46
|
+
"name",
|
|
47
|
+
"outcome",
|
|
48
|
+
"passageHash",
|
|
49
|
+
"reasonCode",
|
|
50
|
+
"ref",
|
|
51
|
+
"sourceHash",
|
|
52
|
+
"status",
|
|
53
|
+
"type",
|
|
54
|
+
"uri",
|
|
55
|
+
]);
|
|
56
|
+
const SAFE_RECEIPT_STRING_LIST_KEYS = new Set([
|
|
57
|
+
"capabilities",
|
|
58
|
+
"categories",
|
|
59
|
+
"collections",
|
|
60
|
+
"fallbackCodes",
|
|
61
|
+
"fallbacks",
|
|
62
|
+
"itemTypes",
|
|
63
|
+
"sources",
|
|
64
|
+
"tags",
|
|
65
|
+
]);
|
|
66
|
+
|
|
67
|
+
const validateReceiptStrings = (
|
|
68
|
+
value: unknown,
|
|
69
|
+
context: z.RefinementCtx,
|
|
70
|
+
key = "",
|
|
71
|
+
path: Array<string | number> = []
|
|
72
|
+
): void => {
|
|
73
|
+
if (typeof value === "string") {
|
|
74
|
+
if (
|
|
75
|
+
!(
|
|
76
|
+
SAFE_RECEIPT_STRING_KEYS.has(key) ||
|
|
77
|
+
SAFE_RECEIPT_STRING_LIST_KEYS.has(key)
|
|
78
|
+
)
|
|
79
|
+
) {
|
|
80
|
+
context.addIssue({
|
|
81
|
+
code: "custom",
|
|
82
|
+
path,
|
|
83
|
+
message: `String field ${key || "<root>"} is not allowed in trace receipt payloads`,
|
|
84
|
+
});
|
|
85
|
+
} else if (key === "uri" && !value.startsWith("gno://")) {
|
|
86
|
+
context.addIssue({
|
|
87
|
+
code: "custom",
|
|
88
|
+
path,
|
|
89
|
+
message: "Trace evidence URI must use gno://",
|
|
90
|
+
});
|
|
91
|
+
} else if (traceUtf8Bytes(value) > 4096) {
|
|
92
|
+
context.addIssue({
|
|
93
|
+
code: "custom",
|
|
94
|
+
path,
|
|
95
|
+
message: `String field ${key} exceeds 4096 UTF-8 bytes`,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (Array.isArray(value)) {
|
|
101
|
+
for (const [index, child] of value.entries()) {
|
|
102
|
+
validateReceiptStrings(child, context, key, [...path, index]);
|
|
103
|
+
}
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (value !== null && typeof value === "object") {
|
|
107
|
+
for (const [childKey, child] of Object.entries(value)) {
|
|
108
|
+
validateReceiptStrings(child, context, childKey, [...path, childKey]);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const traceReceiptPayloadSchema = traceJsonObjectSchema.superRefine(
|
|
114
|
+
(value, context) => validateReceiptStrings(value, context)
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
const queryShapeSchema = z
|
|
118
|
+
.object({
|
|
119
|
+
characters: z.number().int().nonnegative(),
|
|
120
|
+
terms: z.number().int().nonnegative(),
|
|
121
|
+
})
|
|
122
|
+
.strict();
|
|
123
|
+
|
|
124
|
+
const traceInputBaseSchema = z
|
|
125
|
+
.object({
|
|
126
|
+
traceId: idSchema,
|
|
127
|
+
schemaVersion: z.literal("1.0"),
|
|
128
|
+
redactionMode: z.enum(["metadata", "replay"]),
|
|
129
|
+
replayCapable: z.boolean(),
|
|
130
|
+
queryText: z.string().max(8192).nullable(),
|
|
131
|
+
queryDigest: sha256Schema.nullable(),
|
|
132
|
+
queryShape: queryShapeSchema,
|
|
133
|
+
goalText: z.string().max(8192).nullable(),
|
|
134
|
+
goalDigest: sha256Schema.nullable(),
|
|
135
|
+
goalShape: queryShapeSchema,
|
|
136
|
+
filters: traceJsonObjectSchema,
|
|
137
|
+
fingerprints: z
|
|
138
|
+
.object({
|
|
139
|
+
pipeline: sha256Schema,
|
|
140
|
+
model: sha256Schema,
|
|
141
|
+
config: sha256Schema,
|
|
142
|
+
index: sha256Schema,
|
|
143
|
+
})
|
|
144
|
+
.strict(),
|
|
145
|
+
status: z.literal("open"),
|
|
146
|
+
createdAtMs: epochMsSchema,
|
|
147
|
+
updatedAtMs: epochMsSchema,
|
|
148
|
+
expiresAtMs: epochMsSchema,
|
|
149
|
+
})
|
|
150
|
+
.strict();
|
|
151
|
+
|
|
152
|
+
export const retrievalTraceInputSchema = traceInputBaseSchema.superRefine(
|
|
153
|
+
(value, context) => {
|
|
154
|
+
if (value.updatedAtMs < value.createdAtMs) {
|
|
155
|
+
context.addIssue({
|
|
156
|
+
code: "custom",
|
|
157
|
+
path: ["updatedAtMs"],
|
|
158
|
+
message: "updatedAtMs must not precede createdAtMs",
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
if (value.updatedAtMs !== value.createdAtMs) {
|
|
162
|
+
context.addIssue({
|
|
163
|
+
code: "custom",
|
|
164
|
+
path: ["updatedAtMs"],
|
|
165
|
+
message: "new traces must start with updatedAtMs equal to createdAtMs",
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
if (value.expiresAtMs <= value.createdAtMs) {
|
|
169
|
+
context.addIssue({
|
|
170
|
+
code: "custom",
|
|
171
|
+
path: ["expiresAtMs"],
|
|
172
|
+
message: "expiresAtMs must be later than createdAtMs",
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
const metadataInvariant =
|
|
176
|
+
value.redactionMode === "metadata" &&
|
|
177
|
+
!value.replayCapable &&
|
|
178
|
+
value.queryText === null &&
|
|
179
|
+
value.queryDigest === null &&
|
|
180
|
+
value.goalText === null &&
|
|
181
|
+
value.goalDigest === null;
|
|
182
|
+
const replayInvariant =
|
|
183
|
+
value.redactionMode === "replay" &&
|
|
184
|
+
value.replayCapable &&
|
|
185
|
+
value.queryText !== null &&
|
|
186
|
+
value.queryDigest !== null &&
|
|
187
|
+
((value.goalText === null && value.goalDigest === null) ||
|
|
188
|
+
(value.goalText !== null && value.goalDigest !== null));
|
|
189
|
+
if (!(metadataInvariant || replayInvariant)) {
|
|
190
|
+
context.addIssue({
|
|
191
|
+
code: "custom",
|
|
192
|
+
message:
|
|
193
|
+
"metadata traces cannot retain query material; replay traces require explicit replay inputs",
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
if (value.redactionMode === "replay" && value.queryText !== null) {
|
|
197
|
+
const expectedDigest = new Bun.CryptoHasher("sha256")
|
|
198
|
+
.update(value.queryText)
|
|
199
|
+
.digest("hex");
|
|
200
|
+
if (value.queryDigest !== expectedDigest) {
|
|
201
|
+
context.addIssue({
|
|
202
|
+
code: "custom",
|
|
203
|
+
path: ["queryDigest"],
|
|
204
|
+
message: "queryDigest does not match queryText",
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
const expectedShape = shapeTraceText(value.queryText);
|
|
208
|
+
if (
|
|
209
|
+
value.queryShape.characters !== expectedShape.characters ||
|
|
210
|
+
value.queryShape.terms !== expectedShape.terms
|
|
211
|
+
) {
|
|
212
|
+
context.addIssue({
|
|
213
|
+
code: "custom",
|
|
214
|
+
path: ["queryShape"],
|
|
215
|
+
message: "queryShape does not match queryText",
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (value.redactionMode === "replay" && value.goalText !== null) {
|
|
220
|
+
const expectedDigest = new Bun.CryptoHasher("sha256")
|
|
221
|
+
.update(value.goalText)
|
|
222
|
+
.digest("hex");
|
|
223
|
+
if (value.goalDigest !== expectedDigest) {
|
|
224
|
+
context.addIssue({
|
|
225
|
+
code: "custom",
|
|
226
|
+
path: ["goalDigest"],
|
|
227
|
+
message: "goalDigest does not match goalText",
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
const expectedShape = shapeTraceText(value.goalText);
|
|
231
|
+
if (
|
|
232
|
+
value.goalShape.characters !== expectedShape.characters ||
|
|
233
|
+
value.goalShape.terms !== expectedShape.terms
|
|
234
|
+
) {
|
|
235
|
+
context.addIssue({
|
|
236
|
+
code: "custom",
|
|
237
|
+
path: ["goalShape"],
|
|
238
|
+
message: "goalShape does not match goalText",
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
export const retrievalTraceRunInputSchema = z
|
|
246
|
+
.object({
|
|
247
|
+
runId: idSchema,
|
|
248
|
+
traceId: idSchema,
|
|
249
|
+
idempotencyKey: idempotencyKeySchema,
|
|
250
|
+
kind: z.enum(["retrieval", "context", "get"]),
|
|
251
|
+
payload: traceReceiptPayloadSchema,
|
|
252
|
+
createdAtMs: epochMsSchema,
|
|
253
|
+
})
|
|
254
|
+
.strict();
|
|
255
|
+
|
|
256
|
+
export const retrievalTraceEventInputSchema = z
|
|
257
|
+
.object({
|
|
258
|
+
eventId: idSchema,
|
|
259
|
+
traceId: idSchema,
|
|
260
|
+
runId: idSchema.nullable(),
|
|
261
|
+
idempotencyKey: idempotencyKeySchema,
|
|
262
|
+
kind: z.enum([
|
|
263
|
+
"query",
|
|
264
|
+
"retrieval",
|
|
265
|
+
"context",
|
|
266
|
+
"get",
|
|
267
|
+
"open",
|
|
268
|
+
"cite",
|
|
269
|
+
"pin",
|
|
270
|
+
"capability",
|
|
271
|
+
"complete",
|
|
272
|
+
]),
|
|
273
|
+
payload: traceReceiptPayloadSchema,
|
|
274
|
+
createdAtMs: epochMsSchema,
|
|
275
|
+
})
|
|
276
|
+
.strict();
|
|
277
|
+
|
|
278
|
+
export const retrievalTraceJudgmentInputSchema = z
|
|
279
|
+
.object({
|
|
280
|
+
judgmentId: idSchema,
|
|
281
|
+
traceId: idSchema,
|
|
282
|
+
runId: idSchema.nullable(),
|
|
283
|
+
idempotencyKey: idempotencyKeySchema,
|
|
284
|
+
label: z.enum(["relevant", "irrelevant", "missing_expected"]),
|
|
285
|
+
targetKind: z.enum(["document", "chunk", "span", "query"]),
|
|
286
|
+
targetRef: z.string().min(1).max(4096),
|
|
287
|
+
target: traceReceiptPayloadSchema,
|
|
288
|
+
createdAtMs: epochMsSchema,
|
|
289
|
+
})
|
|
290
|
+
.strict()
|
|
291
|
+
.superRefine((value, context) => {
|
|
292
|
+
if (value.label === "missing_expected" && value.targetKind !== "document") {
|
|
293
|
+
context.addIssue({
|
|
294
|
+
code: "custom",
|
|
295
|
+
path: ["targetKind"],
|
|
296
|
+
message: "missing_expected judgments must target a document",
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
export const retrievalTraceExportInputSchema = z
|
|
302
|
+
.object({
|
|
303
|
+
exportId: idSchema,
|
|
304
|
+
traceId: idSchema,
|
|
305
|
+
format: z.enum(["agentic-receipt", "qrels"]),
|
|
306
|
+
artifactHash: sha256Schema,
|
|
307
|
+
createdAtMs: epochMsSchema,
|
|
308
|
+
})
|
|
309
|
+
.strict();
|
|
310
|
+
|
|
311
|
+
const compareCodeUnits = (left: string, right: string): number =>
|
|
312
|
+
left < right ? -1 : left > right ? 1 : 0;
|
|
313
|
+
|
|
314
|
+
const canonicalizeJsonValue = (value: unknown): unknown => {
|
|
315
|
+
if (Array.isArray(value)) return value.map(canonicalizeJsonValue);
|
|
316
|
+
if (value !== null && typeof value === "object") {
|
|
317
|
+
const sorted: Record<string, unknown> = Object.create(null) as Record<
|
|
318
|
+
string,
|
|
319
|
+
unknown
|
|
320
|
+
>;
|
|
321
|
+
for (const key of Object.keys(value).sort(compareCodeUnits)) {
|
|
322
|
+
if (DANGEROUS_JSON_KEYS.has(key)) {
|
|
323
|
+
throw new Error(`Canonical JSON rejects dangerous key ${key}`);
|
|
324
|
+
}
|
|
325
|
+
const child = (value as Record<string, unknown>)[key];
|
|
326
|
+
if (child === undefined) {
|
|
327
|
+
throw new Error(`Canonical JSON rejects undefined at ${key}`);
|
|
328
|
+
}
|
|
329
|
+
sorted[key] = canonicalizeJsonValue(child);
|
|
330
|
+
}
|
|
331
|
+
return sorted;
|
|
332
|
+
}
|
|
333
|
+
if (typeof value === "number" && !Number.isFinite(value)) {
|
|
334
|
+
throw new Error("Canonical JSON rejects non-finite numbers");
|
|
335
|
+
}
|
|
336
|
+
return value;
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
const shapeTraceText = (
|
|
340
|
+
value: string
|
|
341
|
+
): { characters: number; terms: number } => ({
|
|
342
|
+
characters: Array.from(value).length,
|
|
343
|
+
terms: value.trim() ? value.trim().split(/\s+/u).length : 0,
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
export const canonicalTraceJson = (value: unknown): string =>
|
|
347
|
+
JSON.stringify(canonicalizeJsonValue(value));
|
|
348
|
+
|
|
349
|
+
export const traceUtf8Bytes = (value: string): number =>
|
|
350
|
+
new TextEncoder().encode(value).byteLength;
|
|
351
|
+
|
|
352
|
+
export const hashTraceCanonical = (value: unknown): string =>
|
|
353
|
+
new Bun.CryptoHasher("sha256")
|
|
354
|
+
.update(canonicalTraceJson(value))
|
|
355
|
+
.digest("hex");
|
|
356
|
+
|
|
357
|
+
/** Stable trace identity excludes terminal status and mutable updatedAtMs. */
|
|
358
|
+
export const hashRetrievalTraceCreation = (
|
|
359
|
+
trace: RetrievalTraceInput
|
|
360
|
+
): string => {
|
|
361
|
+
const { status: _status, updatedAtMs: _updatedAtMs, ...immutable } = trace;
|
|
362
|
+
return hashTraceCanonical(immutable);
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
export const parseRetrievalTraceInput = (
|
|
366
|
+
input: RetrievalTraceInput
|
|
367
|
+
): RetrievalTraceInput => retrievalTraceInputSchema.parse(input);
|
|
368
|
+
|
|
369
|
+
export const parseRetrievalTraceRunInput = (
|
|
370
|
+
input: RetrievalTraceRunInput
|
|
371
|
+
): RetrievalTraceRunInput => retrievalTraceRunInputSchema.parse(input);
|
|
372
|
+
|
|
373
|
+
export const parseRetrievalTraceEventInput = (
|
|
374
|
+
input: RetrievalTraceEventInput
|
|
375
|
+
): RetrievalTraceEventInput => retrievalTraceEventInputSchema.parse(input);
|
|
376
|
+
|
|
377
|
+
export const parseRetrievalTraceJudgmentInput = (
|
|
378
|
+
input: RetrievalTraceJudgmentInput
|
|
379
|
+
): RetrievalTraceJudgmentInput =>
|
|
380
|
+
retrievalTraceJudgmentInputSchema.parse(input);
|
|
381
|
+
|
|
382
|
+
export const parseRetrievalTraceExportInput = (
|
|
383
|
+
input: RetrievalTraceExportInput
|
|
384
|
+
): RetrievalTraceExportInput => retrievalTraceExportInputSchema.parse(input);
|
|
@@ -52,6 +52,24 @@ import type {
|
|
|
52
52
|
IngestErrorInput,
|
|
53
53
|
IngestErrorRow,
|
|
54
54
|
MigrationResult,
|
|
55
|
+
RetrievalTraceAppendResult,
|
|
56
|
+
RetrievalTraceBundle,
|
|
57
|
+
RetrievalTraceCursor,
|
|
58
|
+
RetrievalTraceBoundedBundle,
|
|
59
|
+
RetrievalTraceDeleteCounts,
|
|
60
|
+
RetrievalTraceEventInput,
|
|
61
|
+
RetrievalTraceExportInput,
|
|
62
|
+
RetrievalTraceExportBundle,
|
|
63
|
+
RetrievalTraceExportManifestInput,
|
|
64
|
+
RetrievalTraceExportManifestRow,
|
|
65
|
+
RetrievalTraceInput,
|
|
66
|
+
RetrievalTraceJudgmentInput,
|
|
67
|
+
RetrievalTracePurgeResult,
|
|
68
|
+
RetrievalTraceRetentionPolicy,
|
|
69
|
+
RetrievalTraceRetentionResult,
|
|
70
|
+
RetrievalTraceRow,
|
|
71
|
+
RetrievalTraceRunInput,
|
|
72
|
+
RetrievalTraceTerminalStatus,
|
|
55
73
|
StorePort,
|
|
56
74
|
StoreResult,
|
|
57
75
|
TagCount,
|
|
@@ -78,6 +96,28 @@ import { err, ok } from "../types";
|
|
|
78
96
|
import { getStoredEmbeddingFingerprint } from "../vector/freshness";
|
|
79
97
|
import { modelTableName } from "../vector/sqlite-vec";
|
|
80
98
|
import { loadFts5Snowball } from "./fts5-snowball";
|
|
99
|
+
import {
|
|
100
|
+
appendExportManifest as appendStoredTraceExportManifest,
|
|
101
|
+
getBoundedTrace as getBoundedStoredTrace,
|
|
102
|
+
getExportBundle as getStoredTraceExportBundle,
|
|
103
|
+
getExportManifest as getStoredTraceExportManifest,
|
|
104
|
+
getOrCreateRedactionSecret as getOrCreateStoredTraceRedactionSecret,
|
|
105
|
+
} from "./retrieval-trace-management-store";
|
|
106
|
+
import {
|
|
107
|
+
deleteTrace as deleteStoredTrace,
|
|
108
|
+
enforceRetention as enforceStoredTraceRetention,
|
|
109
|
+
purgeTraces as purgeStoredTraces,
|
|
110
|
+
} from "./retrieval-trace-retention";
|
|
111
|
+
import {
|
|
112
|
+
appendEvent as appendStoredTraceEvent,
|
|
113
|
+
appendExport as appendStoredTraceExport,
|
|
114
|
+
appendJudgment as appendStoredTraceJudgment,
|
|
115
|
+
appendRun as appendStoredTraceRun,
|
|
116
|
+
createTrace as createStoredTrace,
|
|
117
|
+
finalizeTrace as finalizeStoredTrace,
|
|
118
|
+
getTrace as getStoredTrace,
|
|
119
|
+
listTraces as listStoredTraces,
|
|
120
|
+
} from "./retrieval-trace-store";
|
|
81
121
|
|
|
82
122
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
83
123
|
// FTS5 Query Escaping
|
|
@@ -782,6 +822,107 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
782
822
|
}
|
|
783
823
|
}
|
|
784
824
|
|
|
825
|
+
async createRetrievalTrace(
|
|
826
|
+
input: RetrievalTraceInput
|
|
827
|
+
): Promise<StoreResult<RetrievalTraceAppendResult>> {
|
|
828
|
+
return createStoredTrace(this.ensureOpen(), input);
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
async getRetrievalTrace(
|
|
832
|
+
traceId: string
|
|
833
|
+
): Promise<StoreResult<RetrievalTraceBundle | null>> {
|
|
834
|
+
return getStoredTrace(this.ensureOpen(), traceId);
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
async getBoundedRetrievalTrace(
|
|
838
|
+
traceId: string,
|
|
839
|
+
detailLimit: number
|
|
840
|
+
): Promise<StoreResult<RetrievalTraceBoundedBundle | null>> {
|
|
841
|
+
return getBoundedStoredTrace(this.ensureOpen(), traceId, detailLimit);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
async listRetrievalTraces(
|
|
845
|
+
limit: number,
|
|
846
|
+
cursor?: RetrievalTraceCursor
|
|
847
|
+
): Promise<StoreResult<RetrievalTraceRow[]>> {
|
|
848
|
+
return listStoredTraces(this.ensureOpen(), limit, cursor);
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
async finalizeRetrievalTrace(
|
|
852
|
+
traceId: string,
|
|
853
|
+
status: RetrievalTraceTerminalStatus,
|
|
854
|
+
updatedAtMs: number
|
|
855
|
+
): Promise<StoreResult<RetrievalTraceAppendResult>> {
|
|
856
|
+
return finalizeStoredTrace(this.ensureOpen(), traceId, status, updatedAtMs);
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
async appendRetrievalTraceRun(
|
|
860
|
+
input: RetrievalTraceRunInput
|
|
861
|
+
): Promise<StoreResult<RetrievalTraceAppendResult>> {
|
|
862
|
+
return appendStoredTraceRun(this.ensureOpen(), input);
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
async appendRetrievalTraceEvent(
|
|
866
|
+
input: RetrievalTraceEventInput
|
|
867
|
+
): Promise<StoreResult<RetrievalTraceAppendResult>> {
|
|
868
|
+
return appendStoredTraceEvent(this.ensureOpen(), input);
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
async appendRetrievalTraceJudgment(
|
|
872
|
+
input: RetrievalTraceJudgmentInput
|
|
873
|
+
): Promise<StoreResult<RetrievalTraceAppendResult>> {
|
|
874
|
+
return appendStoredTraceJudgment(this.ensureOpen(), input);
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
async appendRetrievalTraceExport(
|
|
878
|
+
input: RetrievalTraceExportInput
|
|
879
|
+
): Promise<StoreResult<RetrievalTraceAppendResult>> {
|
|
880
|
+
return appendStoredTraceExport(this.ensureOpen(), input);
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
async appendRetrievalTraceExportManifest(
|
|
884
|
+
input: RetrievalTraceExportManifestInput
|
|
885
|
+
): Promise<StoreResult<RetrievalTraceAppendResult>> {
|
|
886
|
+
return appendStoredTraceExportManifest(this.ensureOpen(), input);
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
async getRetrievalTraceExportManifest(
|
|
890
|
+
exportId: string
|
|
891
|
+
): Promise<StoreResult<RetrievalTraceExportManifestRow | null>> {
|
|
892
|
+
return getStoredTraceExportManifest(this.ensureOpen(), exportId);
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
async getRetrievalTraceExportBundle(
|
|
896
|
+
exportId: string
|
|
897
|
+
): Promise<StoreResult<RetrievalTraceExportBundle | null>> {
|
|
898
|
+
return getStoredTraceExportBundle(this.ensureOpen(), exportId);
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
async getOrCreateRetrievalTraceRedactionSecret(): Promise<
|
|
902
|
+
StoreResult<string>
|
|
903
|
+
> {
|
|
904
|
+
return getOrCreateStoredTraceRedactionSecret(this.ensureOpen());
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
async deleteRetrievalTrace(
|
|
908
|
+
traceId: string
|
|
909
|
+
): Promise<StoreResult<RetrievalTraceDeleteCounts>> {
|
|
910
|
+
return deleteStoredTrace(this.ensureOpen(), traceId);
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
async purgeRetrievalTraces(): Promise<
|
|
914
|
+
StoreResult<RetrievalTracePurgeResult>
|
|
915
|
+
> {
|
|
916
|
+
return purgeStoredTraces(this.ensureOpen());
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
async enforceRetrievalTraceRetention(
|
|
920
|
+
policy: RetrievalTraceRetentionPolicy,
|
|
921
|
+
nowMs: number
|
|
922
|
+
): Promise<StoreResult<RetrievalTraceRetentionResult>> {
|
|
923
|
+
return enforceStoredTraceRetention(this.ensureOpen(), policy, nowMs);
|
|
924
|
+
}
|
|
925
|
+
|
|
785
926
|
getContextGeneration(): number {
|
|
786
927
|
return this.contextGeneration;
|
|
787
928
|
}
|
|
@@ -1652,6 +1793,11 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
1652
1793
|
SELECT id FROM documents
|
|
1653
1794
|
WHERE active = 1
|
|
1654
1795
|
${options.collection ? "AND collection = ?" : ""}
|
|
1796
|
+
${
|
|
1797
|
+
options.relPathPrefix !== undefined
|
|
1798
|
+
? "AND (rel_path = ? OR substr(rel_path, 1, length(?) + 1) = ? || '/')"
|
|
1799
|
+
: ""
|
|
1800
|
+
}
|
|
1655
1801
|
)
|
|
1656
1802
|
ORDER BY score
|
|
1657
1803
|
LIMIT ?
|
|
@@ -1705,6 +1851,13 @@ export class SqliteAdapter implements StorePort, SqliteDbProvider {
|
|
|
1705
1851
|
const queryParams = [
|
|
1706
1852
|
builtQuery.query,
|
|
1707
1853
|
...(options.collection ? [options.collection] : []),
|
|
1854
|
+
...(options.relPathPrefix !== undefined
|
|
1855
|
+
? [
|
|
1856
|
+
options.relPathPrefix,
|
|
1857
|
+
options.relPathPrefix,
|
|
1858
|
+
options.relPathPrefix,
|
|
1859
|
+
]
|
|
1860
|
+
: []),
|
|
1708
1861
|
ftsLimit,
|
|
1709
1862
|
...params,
|
|
1710
1863
|
];
|
|
@@ -4622,7 +4775,6 @@ interface DbIngestErrorRow {
|
|
|
4622
4775
|
details_json: string | null;
|
|
4623
4776
|
}
|
|
4624
4777
|
|
|
4625
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
4626
4778
|
// Row Mappers (snake_case -> camelCase)
|
|
4627
4779
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
4628
4780
|
|