@gmickel/gno 2.3.2 → 2.4.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 +24 -4
- package/assets/skill/SKILL.md +30 -0
- package/assets/skill/cli-reference.md +12 -0
- package/assets/skill/examples.md +15 -0
- package/assets/skill/mcp-reference.md +16 -0
- package/assets/spa-production.json.gz +0 -0
- package/browser-extension/artifacts/{gno-browser-clipper-v2.3.2.zip → gno-browser-clipper-v2.4.0.zip} +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v2.4.0.zip.sha256 +1 -0
- package/browser-extension/dist/manifest.json +1 -1
- package/package.json +1 -1
- package/spec/cli.md +30 -0
- package/spec/db/schema.sql +2 -0
- package/spec/evals.md +48 -0
- package/spec/mcp.md +12 -0
- package/spec/output-schemas/ask.schema.json +12 -0
- package/spec/output-schemas/context-capsule-v1.schema.json +205 -32
- package/spec/output-schemas/context-capsule-verification.schema.json +32 -9
- package/spec/output-schemas/evidence-fixtures.schema.json +176 -0
- package/spec/output-schemas/evidence-report.schema.json +322 -0
- package/spec/output-schemas/evidence-run.schema.json +277 -0
- package/spec/output-schemas/metadata-predicate.schema.json +460 -0
- package/spec/output-schemas/retrieval-trace-filters.schema.json +13 -4
- package/spec/output-schemas/search-results.schema.json +12 -0
- package/spec/output-schemas/status.schema.json +37 -6
- package/src/app/context-runtime-contract.ts +33 -2
- package/src/app/context-runtime-input.ts +17 -0
- package/src/app/context-runtime-types.ts +2 -0
- package/src/app/context-runtime.ts +27 -3
- package/src/app/context-surface.ts +2 -0
- package/src/app/verified-ask.ts +1 -0
- package/src/cli/commands/ask.ts +1 -0
- package/src/cli/commands/context-build.ts +1 -0
- package/src/cli/commands/search.ts +1 -0
- package/src/cli/commands/status.ts +10 -0
- package/src/cli/options.ts +19 -0
- package/src/cli/program.ts +12 -0
- package/src/converters/types.ts +2 -0
- package/src/core/context-capsule-retrieval-schema.ts +8 -0
- package/src/core/context-capsule-schema.ts +47 -3
- package/src/core/context-capsule-validation.ts +14 -2
- package/src/core/context-capsule-verification.ts +1 -0
- package/src/core/context-capsule.ts +16 -2
- package/src/core/context-compiler.ts +3 -0
- package/src/core/record-metadata.ts +1 -1
- package/src/core/retrieval-replay-candidate.ts +5 -0
- package/src/core/retrieval-trace-filter-normalization.ts +4 -0
- package/src/core/retrieval-trace-request.ts +1 -0
- package/src/core/retrieval-trace-session.ts +5 -1
- package/src/core/retrieval-trace.ts +2 -0
- package/src/core/typed-metadata.ts +224 -0
- package/src/ingestion/record-adapter-canonical.ts +13 -2
- package/src/ingestion/record-container.ts +9 -0
- package/src/ingestion/sync.ts +11 -1
- package/src/ingestion/typed-metadata.ts +55 -0
- package/src/mcp/tools/ask.ts +10 -2
- package/src/mcp/tools/context.ts +1 -0
- package/src/mcp/tools/index.ts +12 -1
- package/src/mcp/tools/query.ts +13 -0
- package/src/mcp/tools/search.ts +9 -0
- package/src/mcp/tools/status.ts +4 -0
- package/src/mcp/tools/vsearch.ts +9 -0
- package/src/pipeline/diagnose.ts +6 -0
- package/src/pipeline/filters.ts +65 -0
- package/src/pipeline/graph-retrieval.ts +9 -1
- package/src/pipeline/hybrid.ts +22 -2
- package/src/pipeline/search.ts +11 -0
- package/src/pipeline/types.ts +7 -1
- package/src/pipeline/vsearch.ts +21 -1
- package/src/sdk/client.ts +29 -1
- package/src/sdk/index.ts +4 -0
- package/src/sdk/types.ts +7 -0
- package/src/serve/context-capsule.ts +1 -0
- package/src/serve/public/app.tsx +1 -1
- package/src/serve/public/components/MetadataFilter.tsx +186 -0
- package/src/serve/public/globals.built.css +1 -1
- package/src/serve/public/lib/metadata-filter.ts +28 -0
- package/src/serve/public/lib/retrieval-filters.ts +3 -0
- package/src/serve/public/pages/Ask.tsx +48 -3
- package/src/serve/public/pages/DocView.tsx +33 -0
- package/src/serve/public/pages/Search.tsx +44 -1
- package/src/serve/routes/api.ts +50 -0
- package/src/store/migrations/030-typed-metadata.ts +13 -0
- package/src/store/migrations/index.ts +2 -0
- package/src/store/sqlite/adapter.ts +55 -4
- package/src/store/sqlite/eligibility.ts +12 -0
- package/src/store/sqlite/metadata-predicate.ts +71 -0
- package/src/store/types.ts +11 -0
- package/browser-extension/artifacts/gno-browser-clipper-v2.3.2.zip.sha256 +0 -1
|
@@ -63,6 +63,7 @@ export const buildContextCapsule = async (
|
|
|
63
63
|
deps.config.collections.map((collection) => collection.name)
|
|
64
64
|
);
|
|
65
65
|
const noRerank = normalized.depthPolicy === "fast" || normalized.noRerank;
|
|
66
|
+
let metadataCoverageIncomplete = false;
|
|
66
67
|
const plan = await compileContextEvidence<ContextCapsuleV1>(
|
|
67
68
|
{
|
|
68
69
|
goal: normalized.goal,
|
|
@@ -74,6 +75,7 @@ export const buildContextCapsule = async (
|
|
|
74
75
|
tagsAll: normalized.tagsAll,
|
|
75
76
|
tagsAny: normalized.tagsAny,
|
|
76
77
|
categories: normalized.categories,
|
|
78
|
+
filter: normalized.filter,
|
|
77
79
|
author: normalized.author ?? undefined,
|
|
78
80
|
lang: normalized.lang ?? undefined,
|
|
79
81
|
intent: normalized.intent ?? undefined,
|
|
@@ -104,8 +106,14 @@ export const buildContextCapsule = async (
|
|
|
104
106
|
store: deps.store,
|
|
105
107
|
hydration: deps.hydration,
|
|
106
108
|
config: deps.config,
|
|
107
|
-
vectorIndex:
|
|
108
|
-
|
|
109
|
+
vectorIndex:
|
|
110
|
+
normalized.depthPolicy === "fast"
|
|
111
|
+
? null
|
|
112
|
+
: (deps.vectorIndex ?? null),
|
|
113
|
+
embedPort:
|
|
114
|
+
normalized.depthPolicy === "fast"
|
|
115
|
+
? null
|
|
116
|
+
: (deps.embedPort ?? null),
|
|
109
117
|
expandPort: null,
|
|
110
118
|
rerankPort: requestNoRerank ? null : (deps.rerankPort ?? null),
|
|
111
119
|
},
|
|
@@ -124,10 +132,26 @@ export const buildContextCapsule = async (
|
|
|
124
132
|
result.error.cause
|
|
125
133
|
);
|
|
126
134
|
}
|
|
135
|
+
if (
|
|
136
|
+
normalized.filter &&
|
|
137
|
+
result.value.meta.warnings?.some(
|
|
138
|
+
(warning) =>
|
|
139
|
+
warning.code === "METADATA_COVERAGE_UNKNOWN" ||
|
|
140
|
+
warning.code === "METADATA_COVERAGE_INCOMPLETE"
|
|
141
|
+
)
|
|
142
|
+
) {
|
|
143
|
+
metadataCoverageIncomplete = true;
|
|
144
|
+
}
|
|
127
145
|
return result.value;
|
|
128
146
|
},
|
|
129
147
|
projectCanonical: (draft, snapshots) =>
|
|
130
|
-
projectContextCapsule(
|
|
148
|
+
projectContextCapsule(
|
|
149
|
+
draft,
|
|
150
|
+
snapshots,
|
|
151
|
+
normalized,
|
|
152
|
+
deps,
|
|
153
|
+
metadataCoverageIncomplete
|
|
154
|
+
),
|
|
131
155
|
}
|
|
132
156
|
);
|
|
133
157
|
if (!plan.projection) {
|
|
@@ -5,6 +5,7 @@ import { z } from "zod";
|
|
|
5
5
|
import type { ContextCapsuleBuildInput } from "./context-runtime-types";
|
|
6
6
|
|
|
7
7
|
import { ContextCapsuleContractError } from "../core/context-capsule";
|
|
8
|
+
import { metadataPredicateSchema } from "../core/typed-metadata";
|
|
8
9
|
|
|
9
10
|
const queryModeSchema = z
|
|
10
11
|
.object({
|
|
@@ -28,6 +29,7 @@ export const contextBuildSurfaceSchema = z
|
|
|
28
29
|
tagsAll: stringList.optional(),
|
|
29
30
|
tagsAny: stringList.optional(),
|
|
30
31
|
categories: stringList.optional(),
|
|
32
|
+
filter: metadataPredicateSchema.optional(),
|
|
31
33
|
author: z.string().optional(),
|
|
32
34
|
lang: z.string().optional(),
|
|
33
35
|
intent: z.string().optional(),
|
package/src/app/verified-ask.ts
CHANGED
package/src/cli/commands/ask.ts
CHANGED
|
@@ -124,6 +124,7 @@ export const contextBuild = async (
|
|
|
124
124
|
until: options.until,
|
|
125
125
|
categories: options.categories,
|
|
126
126
|
author: options.author,
|
|
127
|
+
filter: options.filter,
|
|
127
128
|
graph: options.graph,
|
|
128
129
|
candidateLimit: options.candidateLimit,
|
|
129
130
|
queryModes: options.queryModes,
|
|
@@ -87,6 +87,11 @@ function formatTerminal(
|
|
|
87
87
|
): string {
|
|
88
88
|
const lines: string[] = [];
|
|
89
89
|
|
|
90
|
+
if (indexStatus.typedMetadata) {
|
|
91
|
+
lines.push(
|
|
92
|
+
`Typed metadata: ${indexStatus.typedMetadata.pending} pending sync, ${indexStatus.typedMetadata.invalid} invalid`
|
|
93
|
+
);
|
|
94
|
+
}
|
|
90
95
|
lines.push(`Index: ${indexStatus.indexName}`);
|
|
91
96
|
lines.push(`Config: ${indexStatus.configPath}`);
|
|
92
97
|
lines.push(`Database: ${indexStatus.dbPath}`);
|
|
@@ -200,6 +205,10 @@ function formatMarkdown(
|
|
|
200
205
|
lines.push(`- **Documents**: ${indexStatus.activeDocuments}`);
|
|
201
206
|
lines.push(`- **Chunks**: ${indexStatus.totalChunks}`);
|
|
202
207
|
lines.push(`- **Embedding backlog**: ${indexStatus.embeddingBacklog}`);
|
|
208
|
+
if (indexStatus.typedMetadata)
|
|
209
|
+
lines.push(
|
|
210
|
+
`- **Typed metadata**: ${indexStatus.typedMetadata.pending} pending sync, ${indexStatus.typedMetadata.invalid} invalid`
|
|
211
|
+
);
|
|
203
212
|
lines.push(`- **Recent errors**: ${indexStatus.recentErrors}`);
|
|
204
213
|
|
|
205
214
|
const chunking = formatChunkingStatus(indexStatus.chunking);
|
|
@@ -347,6 +356,7 @@ export function formatStatus(
|
|
|
347
356
|
totalDocuments: s.activeDocuments,
|
|
348
357
|
totalChunks: s.totalChunks,
|
|
349
358
|
embeddingBacklog: s.embeddingBacklog,
|
|
359
|
+
typedMetadata: s.typedMetadata,
|
|
350
360
|
chunking: s.chunking,
|
|
351
361
|
lastUpdated: s.lastUpdatedAt,
|
|
352
362
|
healthy: isStatusHealthy(s, result.activation),
|
package/src/cli/options.ts
CHANGED
|
@@ -9,6 +9,10 @@ import {
|
|
|
9
9
|
normalizeProjectAffinityValues,
|
|
10
10
|
ProjectAffinityInputError,
|
|
11
11
|
} from "../core/project-affinity-surface";
|
|
12
|
+
import {
|
|
13
|
+
normalizeMetadataPredicate,
|
|
14
|
+
type MetadataPredicate,
|
|
15
|
+
} from "../core/typed-metadata";
|
|
12
16
|
import { CliError } from "./errors";
|
|
13
17
|
|
|
14
18
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -259,3 +263,18 @@ export function parseOptionalFloat(
|
|
|
259
263
|
}
|
|
260
264
|
return num;
|
|
261
265
|
}
|
|
266
|
+
|
|
267
|
+
export const parseCliMetadataFilter = (
|
|
268
|
+
value: unknown
|
|
269
|
+
): MetadataPredicate | undefined => {
|
|
270
|
+
if (value === undefined) return undefined;
|
|
271
|
+
try {
|
|
272
|
+
if (typeof value !== "string") throw new Error("Expected JSON text");
|
|
273
|
+
return normalizeMetadataPredicate(JSON.parse(value));
|
|
274
|
+
} catch (error) {
|
|
275
|
+
throw new CliError(
|
|
276
|
+
"VALIDATION",
|
|
277
|
+
`filter: ${error instanceof Error ? error.message : "Invalid predicate"}`
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
};
|
package/src/cli/program.ts
CHANGED
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
collectRepeatableValue,
|
|
50
50
|
getDefaultLimit,
|
|
51
51
|
parseCliProjectAffinityOptions,
|
|
52
|
+
parseCliMetadataFilter,
|
|
52
53
|
parseOptionalFloat,
|
|
53
54
|
parsePositiveInt,
|
|
54
55
|
} from "./options";
|
|
@@ -652,6 +653,7 @@ function wireSearchCommands(program: Command): void {
|
|
|
652
653
|
"modified-at upper bound (ISO date/time or token)"
|
|
653
654
|
)
|
|
654
655
|
.option("--category <values>", "require category match (comma-separated)")
|
|
656
|
+
.option("--filter <json>", "typed custom metadata predicate (JSON)")
|
|
655
657
|
.option("--author <text>", "filter by author (case-insensitive contains)")
|
|
656
658
|
.option("--intent <text>", "disambiguating context for ambiguous queries")
|
|
657
659
|
.option(
|
|
@@ -736,6 +738,7 @@ function wireSearchCommands(program: Command): void {
|
|
|
736
738
|
until: cmdOpts.until as string | undefined,
|
|
737
739
|
categories,
|
|
738
740
|
author: cmdOpts.author as string | undefined,
|
|
741
|
+
filter: parseCliMetadataFilter(cmdOpts.filter),
|
|
739
742
|
intent: cmdOpts.intent as string | undefined,
|
|
740
743
|
exclude,
|
|
741
744
|
tagsAll,
|
|
@@ -790,6 +793,7 @@ function wireSearchCommands(program: Command): void {
|
|
|
790
793
|
"modified-at upper bound (ISO date/time or token)"
|
|
791
794
|
)
|
|
792
795
|
.option("--category <values>", "require category match (comma-separated)")
|
|
796
|
+
.option("--filter <json>", "typed custom metadata predicate (JSON)")
|
|
793
797
|
.option("--author <text>", "filter by author (case-insensitive contains)")
|
|
794
798
|
.option("--intent <text>", "disambiguating context for ambiguous queries")
|
|
795
799
|
.option(
|
|
@@ -864,6 +868,7 @@ function wireSearchCommands(program: Command): void {
|
|
|
864
868
|
until: cmdOpts.until as string | undefined,
|
|
865
869
|
categories,
|
|
866
870
|
author: cmdOpts.author as string | undefined,
|
|
871
|
+
filter: parseCliMetadataFilter(cmdOpts.filter),
|
|
867
872
|
intent: cmdOpts.intent as string | undefined,
|
|
868
873
|
exclude,
|
|
869
874
|
tagsAll,
|
|
@@ -916,6 +921,7 @@ function wireSearchCommands(program: Command): void {
|
|
|
916
921
|
"modified-at upper bound (ISO date/time or token)"
|
|
917
922
|
)
|
|
918
923
|
.option("--category <values>", "require category match (comma-separated)")
|
|
924
|
+
.option("--filter <json>", "typed custom metadata predicate (JSON)")
|
|
919
925
|
.option("--author <text>", "filter by author (case-insensitive contains)")
|
|
920
926
|
.option("--intent <text>", "disambiguating context for ambiguous queries")
|
|
921
927
|
.option(
|
|
@@ -1080,6 +1086,7 @@ function wireSearchCommands(program: Command): void {
|
|
|
1080
1086
|
until: cmdOpts.until as string | undefined,
|
|
1081
1087
|
categories,
|
|
1082
1088
|
author: cmdOpts.author as string | undefined,
|
|
1089
|
+
filter: parseCliMetadataFilter(cmdOpts.filter),
|
|
1083
1090
|
intent: cmdOpts.intent as string | undefined,
|
|
1084
1091
|
exclude,
|
|
1085
1092
|
tagsAll,
|
|
@@ -1116,6 +1123,7 @@ function wireSearchCommands(program: Command): void {
|
|
|
1116
1123
|
until: cmdOpts.until as string | undefined,
|
|
1117
1124
|
categories,
|
|
1118
1125
|
author: cmdOpts.author as string | undefined,
|
|
1126
|
+
filter: parseCliMetadataFilter(cmdOpts.filter),
|
|
1119
1127
|
intent: cmdOpts.intent as string | undefined,
|
|
1120
1128
|
exclude,
|
|
1121
1129
|
tagsAll,
|
|
@@ -1219,6 +1227,7 @@ function wireSearchCommands(program: Command): void {
|
|
|
1219
1227
|
"modified-at upper bound (ISO date/time or token)"
|
|
1220
1228
|
)
|
|
1221
1229
|
.option("--category <values>", "require category match (comma-separated)")
|
|
1230
|
+
.option("--filter <json>", "typed custom metadata predicate (JSON)")
|
|
1222
1231
|
.option("--author <text>", "filter by author (case-insensitive contains)")
|
|
1223
1232
|
.option("--intent <text>", "disambiguating context for ambiguous queries")
|
|
1224
1233
|
.option(
|
|
@@ -1350,6 +1359,7 @@ function wireSearchCommands(program: Command): void {
|
|
|
1350
1359
|
until: cmdOpts.until as string | undefined,
|
|
1351
1360
|
categories,
|
|
1352
1361
|
author: cmdOpts.author as string | undefined,
|
|
1362
|
+
filter: parseCliMetadataFilter(cmdOpts.filter),
|
|
1353
1363
|
intent: cmdOpts.intent as string | undefined,
|
|
1354
1364
|
exclude,
|
|
1355
1365
|
minScore,
|
|
@@ -2695,6 +2705,7 @@ function wireManagementCommands(program: Command): void {
|
|
|
2695
2705
|
.option("--tags-all <tags>", "require ALL tags (comma-separated)")
|
|
2696
2706
|
.option("--tags-any <tags>", "require ANY tag (comma-separated)")
|
|
2697
2707
|
.option("--category <values>", "require category match (comma-separated)")
|
|
2708
|
+
.option("--filter <json>", "typed custom metadata predicate (JSON)")
|
|
2698
2709
|
.option("--author <text>", "filter by author")
|
|
2699
2710
|
.option("--lang <code>", "language hint (BCP-47)")
|
|
2700
2711
|
.option("--since <date>", "modified-at lower bound")
|
|
@@ -2765,6 +2776,7 @@ function wireManagementCommands(program: Command): void {
|
|
|
2765
2776
|
tagsAny: parseCsvValues(cmdOpts.tagsAny),
|
|
2766
2777
|
categories: parseCsvValues(cmdOpts.category),
|
|
2767
2778
|
author: cmdOpts.author as string | undefined,
|
|
2779
|
+
filter: parseCliMetadataFilter(cmdOpts.filter),
|
|
2768
2780
|
lang: cmdOpts.lang as string | undefined,
|
|
2769
2781
|
since: cmdOpts.since as string | undefined,
|
|
2770
2782
|
until: cmdOpts.until as string | undefined,
|
package/src/converters/types.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TypedMetadata } from "../core/typed-metadata";
|
|
1
2
|
/**
|
|
2
3
|
* Converter subsystem types.
|
|
3
4
|
* PRD §8.2 - Converter interfaces
|
|
@@ -157,6 +158,7 @@ export const RECORD_METADATA_LIMITS = {
|
|
|
157
158
|
|
|
158
159
|
/** Metadata shared by export adapters and later search/get projections. */
|
|
159
160
|
export interface RecordMetadata {
|
|
161
|
+
custom?: TypedMetadata;
|
|
160
162
|
author?: string;
|
|
161
163
|
participants?: string[];
|
|
162
164
|
categories?: string[];
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
|
|
5
5
|
import { contextCapsuleIndexSnapshotSchema } from "./context-capsule-index-schema";
|
|
6
|
+
import { metadataPredicateSchema } from "./typed-metadata";
|
|
6
7
|
|
|
7
8
|
const nonEmptyText = z.string().min(1);
|
|
8
9
|
const capabilityOutcomeSchema = z.enum([
|
|
@@ -83,3 +84,10 @@ export const contextCapsuleRetrievalSchema = z
|
|
|
83
84
|
export type ContextCapabilityState = z.infer<
|
|
84
85
|
typeof contextCapabilityStateSchema
|
|
85
86
|
>;
|
|
87
|
+
|
|
88
|
+
export const contextCapsuleRetrievalV1_2Schema =
|
|
89
|
+
contextCapsuleRetrievalSchema.extend({
|
|
90
|
+
request: contextCapsuleRetrievalSchema.shape.request.extend({
|
|
91
|
+
filter: metadataPredicateSchema,
|
|
92
|
+
}),
|
|
93
|
+
});
|
|
@@ -4,6 +4,7 @@ import { buildUri, deriveDocid, parseUri } from "../app/constants";
|
|
|
4
4
|
import { isValidIndexName } from "../app/index-name";
|
|
5
5
|
import { RECORD_METADATA_LIMITS } from "../converters/types";
|
|
6
6
|
import { contextCapsuleIndexSnapshotSchema } from "./context-capsule-index-schema";
|
|
7
|
+
import { contextCapsuleRetrievalV1_2Schema } from "./context-capsule-retrieval-schema";
|
|
7
8
|
import { contextCapsuleRetrievalSchema } from "./context-capsule-retrieval-schema";
|
|
8
9
|
import {
|
|
9
10
|
contextCapsuleEvidenceIdentity,
|
|
@@ -12,11 +13,15 @@ import {
|
|
|
12
13
|
validateContextCapsulePayload,
|
|
13
14
|
} from "./context-capsule-validation";
|
|
14
15
|
import { egressLineageSchema } from "./egress-provenance";
|
|
16
|
+
import {
|
|
17
|
+
normalizeMetadataPredicate,
|
|
18
|
+
metadataPredicateSchema,
|
|
19
|
+
} from "./typed-metadata";
|
|
15
20
|
|
|
16
21
|
export { contextCapsuleIndexSnapshotSchema } from "./context-capsule-index-schema";
|
|
17
22
|
|
|
18
23
|
export const CONTEXT_CAPSULE_SCHEMA_VERSION = "1.0" as const;
|
|
19
|
-
export const CONTEXT_CAPSULE_CURRENT_SCHEMA_VERSION = "1.
|
|
24
|
+
export const CONTEXT_CAPSULE_CURRENT_SCHEMA_VERSION = "1.2" as const;
|
|
20
25
|
export const CONTEXT_CAPSULE_COORDINATE_SPACE = "canonical_mirror" as const;
|
|
21
26
|
|
|
22
27
|
const SHA256_PATTERN = /^[a-f0-9]{64}$/;
|
|
@@ -654,7 +659,7 @@ const validateContextCapsuleV1_1Lineage = (
|
|
|
654
659
|
|
|
655
660
|
export const contextCapsulePayloadV1_1Schema = z
|
|
656
661
|
.object({
|
|
657
|
-
schemaVersion: z.literal(
|
|
662
|
+
schemaVersion: z.literal("1.1"),
|
|
658
663
|
coordinateSpace: z.literal(CONTEXT_CAPSULE_COORDINATE_SPACE),
|
|
659
664
|
goal: nonEmptyTextSchema,
|
|
660
665
|
query: nonEmptyTextSchema,
|
|
@@ -682,6 +687,45 @@ export type ContextCapsulePayloadV1_1 = z.infer<
|
|
|
682
687
|
typeof contextCapsulePayloadV1_1Schema
|
|
683
688
|
>;
|
|
684
689
|
|
|
690
|
+
export const contextCapsulePayloadV1_2Schema = z
|
|
691
|
+
.object({
|
|
692
|
+
...contextCapsulePayloadV1_1Schema.shape,
|
|
693
|
+
schemaVersion: z.literal(CONTEXT_CAPSULE_CURRENT_SCHEMA_VERSION),
|
|
694
|
+
scope: scopeSchema.extend({ filter: metadataPredicateSchema }),
|
|
695
|
+
retrieval: contextCapsuleRetrievalV1_2Schema,
|
|
696
|
+
warnings: z
|
|
697
|
+
.array(
|
|
698
|
+
z
|
|
699
|
+
.object({
|
|
700
|
+
code: z.union([
|
|
701
|
+
contextCapsuleWarningCodeSchema,
|
|
702
|
+
z.literal("metadata_coverage_incomplete"),
|
|
703
|
+
]),
|
|
704
|
+
})
|
|
705
|
+
.strict()
|
|
706
|
+
)
|
|
707
|
+
.max(32),
|
|
708
|
+
})
|
|
709
|
+
.strict()
|
|
710
|
+
.superRefine((value, context) => {
|
|
711
|
+
validateContextCapsulePayload(value, context);
|
|
712
|
+
validateContextCapsuleV1_1Lineage(value, context);
|
|
713
|
+
if (
|
|
714
|
+
JSON.stringify(normalizeMetadataPredicate(value.scope.filter)) !==
|
|
715
|
+
JSON.stringify(normalizeMetadataPredicate(value.retrieval.request.filter))
|
|
716
|
+
) {
|
|
717
|
+
context.addIssue({
|
|
718
|
+
code: "custom",
|
|
719
|
+
path: ["retrieval", "request", "filter"],
|
|
720
|
+
message: "Retrieval predicate must match scope predicate",
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
});
|
|
724
|
+
export type ContextCapsulePayloadV1_2 = z.infer<
|
|
725
|
+
typeof contextCapsulePayloadV1_2Schema
|
|
726
|
+
>;
|
|
727
|
+
|
|
685
728
|
export type ContextCapsulePayload =
|
|
686
729
|
| ContextCapsulePayloadV1
|
|
687
|
-
| ContextCapsulePayloadV1_1
|
|
730
|
+
| ContextCapsulePayloadV1_1
|
|
731
|
+
| ContextCapsulePayloadV1_2;
|
|
@@ -318,11 +318,18 @@ const validateCoverageBindings = (
|
|
|
318
318
|
path: ["coverage", "gaps"],
|
|
319
319
|
});
|
|
320
320
|
}
|
|
321
|
-
const
|
|
321
|
+
const metadataIncomplete =
|
|
322
|
+
value.schemaVersion === "1.2" &&
|
|
323
|
+
value.warnings.some(({ code }) => code === "metadata_coverage_incomplete");
|
|
324
|
+
const complete =
|
|
325
|
+
unresolved.length === 0 &&
|
|
326
|
+
value.coverage.gaps.length === 0 &&
|
|
327
|
+
!metadataIncomplete;
|
|
322
328
|
if (value.coverage.complete !== complete) {
|
|
323
329
|
context.addIssue({
|
|
324
330
|
code: "custom",
|
|
325
|
-
message:
|
|
331
|
+
message:
|
|
332
|
+
"complete must reflect unresolved facets, gaps, and metadata coverage",
|
|
326
333
|
path: ["coverage", "complete"],
|
|
327
334
|
});
|
|
328
335
|
}
|
|
@@ -379,6 +386,11 @@ export const validateContextCapsulePayload = (
|
|
|
379
386
|
});
|
|
380
387
|
}
|
|
381
388
|
const expectedWarnings = new Set<string>();
|
|
389
|
+
if (
|
|
390
|
+
value.schemaVersion === "1.2" &&
|
|
391
|
+
warningSet.has("metadata_coverage_incomplete")
|
|
392
|
+
)
|
|
393
|
+
expectedWarnings.add("metadata_coverage_incomplete");
|
|
382
394
|
if (!value.coverage.complete) expectedWarnings.add("incomplete_coverage");
|
|
383
395
|
if (value.omissions.truncated) expectedWarnings.add("omissions_truncated");
|
|
384
396
|
if (fallbackTokenizer) expectedWarnings.add("token_estimate_used");
|
|
@@ -123,6 +123,7 @@ export const contextCapsuleVerificationSchema = z
|
|
|
123
123
|
.object({
|
|
124
124
|
schemaVersion: z.union([
|
|
125
125
|
z.literal(CONTEXT_CAPSULE_SCHEMA_VERSION),
|
|
126
|
+
z.literal("1.1"),
|
|
126
127
|
z.literal(CONTEXT_CAPSULE_CURRENT_SCHEMA_VERSION),
|
|
127
128
|
]),
|
|
128
129
|
coordinateSpace: z.literal(CONTEXT_CAPSULE_COORDINATE_SPACE),
|
|
@@ -4,9 +4,11 @@ import { canonicalizeIndexName } from "../app/index-name";
|
|
|
4
4
|
import {
|
|
5
5
|
contextCapsulePayloadV1Schema,
|
|
6
6
|
contextCapsulePayloadV1_1Schema,
|
|
7
|
+
contextCapsulePayloadV1_2Schema,
|
|
7
8
|
type ContextCapsulePayload,
|
|
8
9
|
} from "./context-capsule-schema";
|
|
9
10
|
import { createEgressLineage, type EgressLineage } from "./egress-provenance";
|
|
11
|
+
import { normalizeMetadataPredicate } from "./typed-metadata";
|
|
10
12
|
|
|
11
13
|
const sha256Schema = z.string().regex(/^[a-f0-9]{64}$/);
|
|
12
14
|
|
|
@@ -61,6 +63,9 @@ const normalizePayload = (
|
|
|
61
63
|
query: normalizeText(value.query),
|
|
62
64
|
scope: {
|
|
63
65
|
...value.scope,
|
|
66
|
+
...(value.schemaVersion === "1.2"
|
|
67
|
+
? { filter: normalizeMetadataPredicate(value.scope.filter) }
|
|
68
|
+
: {}),
|
|
64
69
|
indexName: canonicalizeIndexName(value.scope.indexName),
|
|
65
70
|
collections: normalizeSet(value.scope.collections),
|
|
66
71
|
uriPrefix:
|
|
@@ -79,6 +84,13 @@ const normalizePayload = (
|
|
|
79
84
|
queryVariants: value.retrieval.queryVariants.map(normalizeText),
|
|
80
85
|
request: {
|
|
81
86
|
...value.retrieval.request,
|
|
87
|
+
...(value.schemaVersion === "1.2"
|
|
88
|
+
? {
|
|
89
|
+
filter: normalizeMetadataPredicate(
|
|
90
|
+
value.retrieval.request.filter
|
|
91
|
+
),
|
|
92
|
+
}
|
|
93
|
+
: {}),
|
|
82
94
|
author:
|
|
83
95
|
value.retrieval.request.author === null
|
|
84
96
|
? null
|
|
@@ -116,7 +128,7 @@ const normalizePayload = (
|
|
|
116
128
|
`${right.code}\0${right.capability}`
|
|
117
129
|
)
|
|
118
130
|
),
|
|
119
|
-
...(value.schemaVersion
|
|
131
|
+
...(value.schemaVersion !== "1.0"
|
|
120
132
|
? {
|
|
121
133
|
egressLineage: {
|
|
122
134
|
...value.egressLineage,
|
|
@@ -291,6 +303,7 @@ const contractError = (
|
|
|
291
303
|
const contextCapsulePayloadSchema = z.union([
|
|
292
304
|
contextCapsulePayloadV1Schema,
|
|
293
305
|
contextCapsulePayloadV1_1Schema,
|
|
306
|
+
contextCapsulePayloadV1_2Schema,
|
|
294
307
|
]);
|
|
295
308
|
|
|
296
309
|
const parsePayload = (input: unknown): ContextCapsulePayload => {
|
|
@@ -391,6 +404,7 @@ const fixedPointCapsule = (
|
|
|
391
404
|
const contextCapsuleBaseV1Schema = z.union([
|
|
392
405
|
contextCapsulePayloadV1Schema.extend({ capsuleId: sha256Schema }),
|
|
393
406
|
contextCapsulePayloadV1_1Schema.extend({ capsuleId: sha256Schema }),
|
|
407
|
+
contextCapsulePayloadV1_2Schema.extend({ capsuleId: sha256Schema }),
|
|
394
408
|
]);
|
|
395
409
|
|
|
396
410
|
export const contextCapsuleV1Schema = contextCapsuleBaseV1Schema.superRefine(
|
|
@@ -503,7 +517,7 @@ export const canonicalContextCapsuleAccountingJson = (input: unknown): string =>
|
|
|
503
517
|
*/
|
|
504
518
|
export const contextCapsuleEgressLineage = (input: unknown): EgressLineage => {
|
|
505
519
|
const capsule = parseContextCapsuleV1(input);
|
|
506
|
-
if (capsule.schemaVersion
|
|
520
|
+
if (capsule.schemaVersion !== "1.0") return capsule.egressLineage;
|
|
507
521
|
return createEgressLineage(
|
|
508
522
|
[...new Set(capsule.evidence.map(({ collection }) => collection))].map(
|
|
509
523
|
(collection) => ({
|
|
@@ -26,6 +26,7 @@ import type {
|
|
|
26
26
|
MaterializedContextCandidate,
|
|
27
27
|
} from "./context-budget";
|
|
28
28
|
import type { ContextConfiguredGuidance } from "./context-guidance";
|
|
29
|
+
import type { MetadataPredicate } from "./typed-metadata";
|
|
29
30
|
|
|
30
31
|
import { decorateUriForIndex } from "../app/constants";
|
|
31
32
|
import { canonicalizeIndexName } from "../app/index-name";
|
|
@@ -96,6 +97,7 @@ export interface ContextCompilerInput {
|
|
|
96
97
|
tagsAll?: string[];
|
|
97
98
|
tagsAny?: string[];
|
|
98
99
|
categories?: string[];
|
|
100
|
+
filter?: MetadataPredicate;
|
|
99
101
|
author?: string;
|
|
100
102
|
lang?: string;
|
|
101
103
|
intent?: string;
|
|
@@ -358,6 +360,7 @@ export const planContextEvidence = async <T, P>(
|
|
|
358
360
|
tagsAll: input.tagsAll,
|
|
359
361
|
tagsAny: input.tagsAny,
|
|
360
362
|
categories: input.categories,
|
|
363
|
+
filter: input.filter,
|
|
361
364
|
author: input.author,
|
|
362
365
|
lang: input.lang,
|
|
363
366
|
intent: input.intent,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { RecordAnchor, RecordMetadata } from "../converters/types";
|
|
2
2
|
|
|
3
|
-
export interface RecordEvidenceMetadata extends RecordMetadata {
|
|
3
|
+
export interface RecordEvidenceMetadata extends Omit<RecordMetadata, "custom"> {
|
|
4
4
|
recordKey: string;
|
|
5
5
|
sourceLocator: string;
|
|
6
6
|
anchors: RecordAnchor[];
|
|
@@ -18,6 +18,7 @@ import { searchBm25 } from "../pipeline/search";
|
|
|
18
18
|
import { SEARCH_RESULTS_TRACE_METADATA } from "../pipeline/types";
|
|
19
19
|
import { searchVector } from "../pipeline/vsearch";
|
|
20
20
|
import { err, ok } from "../store/types";
|
|
21
|
+
import { normalizeMetadataPredicate } from "./typed-metadata";
|
|
21
22
|
|
|
22
23
|
export interface RetrievalReplayDeps extends HybridSearchDeps {
|
|
23
24
|
indexName?: string;
|
|
@@ -108,6 +109,10 @@ export const buildRetrievalReplaySearchOptions = (
|
|
|
108
109
|
categories: Array.isArray(filters.categories)
|
|
109
110
|
? (filters.categories as string[])
|
|
110
111
|
: undefined,
|
|
112
|
+
filter:
|
|
113
|
+
filters.filter === undefined
|
|
114
|
+
? undefined
|
|
115
|
+
: normalizeMetadataPredicate(filters.filter),
|
|
111
116
|
author: typeof filters.author === "string" ? filters.author : undefined,
|
|
112
117
|
intent: typeof filters.intent === "string" ? filters.intent : undefined,
|
|
113
118
|
exclude: Array.isArray(filters.exclude)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/** Deterministic set-like retrieval-filter normalization before persistence. */
|
|
2
2
|
|
|
3
|
+
import { normalizeMetadataPredicate } from "./typed-metadata";
|
|
4
|
+
|
|
3
5
|
const SET_ARRAY_KEYS = [
|
|
4
6
|
"categories",
|
|
5
7
|
"collections",
|
|
@@ -23,5 +25,7 @@ export const canonicalizeRetrievalTraceFilters = <
|
|
|
23
25
|
normalized[key] = [...new Set(values)].sort(compareCodeUnits);
|
|
24
26
|
}
|
|
25
27
|
}
|
|
28
|
+
if (filters.filter !== undefined)
|
|
29
|
+
normalized.filter = normalizeMetadataPredicate(filters.filter);
|
|
26
30
|
return normalized as Filters;
|
|
27
31
|
};
|
|
@@ -380,7 +380,11 @@ export class RetrievalTraceSession {
|
|
|
380
380
|
latencyMs?: number
|
|
381
381
|
): Promise<StoreResult<"inserted" | "duplicate" | "disabled">> {
|
|
382
382
|
if (!this.hasCapacity(2)) return ok("disabled");
|
|
383
|
-
if (
|
|
383
|
+
if (
|
|
384
|
+
capsule.schemaVersion === "1.0" ||
|
|
385
|
+
capsule.schemaVersion === "1.1" ||
|
|
386
|
+
capsule.schemaVersion === "1.2"
|
|
387
|
+
) {
|
|
384
388
|
const widened = await this.ensureLineageCoverage([
|
|
385
389
|
contextCapsuleEgressLineage(capsule),
|
|
386
390
|
]);
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
legacyLocalOnlyEgressLineage,
|
|
30
30
|
} from "./egress-provenance";
|
|
31
31
|
import { canonicalizeRetrievalTraceFilters } from "./retrieval-trace-filter-normalization";
|
|
32
|
+
import { metadataPredicateSchema } from "./typed-metadata";
|
|
32
33
|
|
|
33
34
|
const sha256Schema = z.string().regex(/^[a-f0-9]{64}$/);
|
|
34
35
|
const gnoUriSchema = z
|
|
@@ -57,6 +58,7 @@ export const traceFiltersSchema = z
|
|
|
57
58
|
since: z.string().max(128).optional(),
|
|
58
59
|
until: z.string().max(128).optional(),
|
|
59
60
|
categories: z.array(z.string().max(512)).max(1000).optional(),
|
|
61
|
+
filter: metadataPredicateSchema.optional(),
|
|
60
62
|
author: z.string().max(1024).optional(),
|
|
61
63
|
intent: z.string().max(8192).optional(),
|
|
62
64
|
exclude: z.array(z.string().max(1024)).max(1000).optional(),
|