@gmickel/gno 2.3.3 → 2.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/README.md +22 -3
  2. package/assets/skill/SKILL.md +53 -0
  3. package/assets/skill/cli-reference.md +29 -0
  4. package/assets/skill/examples.md +30 -0
  5. package/assets/skill/mcp-reference.md +26 -0
  6. package/assets/spa-production.json.gz +0 -0
  7. package/browser-extension/artifacts/{gno-browser-clipper-v2.3.3.zip → gno-browser-clipper-v2.5.1.zip} +0 -0
  8. package/browser-extension/artifacts/gno-browser-clipper-v2.5.1.zip.sha256 +1 -0
  9. package/browser-extension/dist/manifest.json +1 -1
  10. package/package.json +1 -1
  11. package/spec/cli.md +41 -0
  12. package/spec/compiled-context.md +68 -0
  13. package/spec/db/schema.sql +2 -0
  14. package/spec/evals.md +48 -0
  15. package/spec/mcp.md +37 -0
  16. package/spec/output-schemas/ask.schema.json +12 -0
  17. package/spec/output-schemas/compiled-context-check.schema.json +44 -0
  18. package/spec/output-schemas/compiled-context-file.schema.json +165 -0
  19. package/spec/output-schemas/compiled-context-preview.schema.json +142 -0
  20. package/spec/output-schemas/context-capsule-v1.schema.json +205 -32
  21. package/spec/output-schemas/context-capsule-verification.schema.json +32 -9
  22. package/spec/output-schemas/evidence-fixtures.schema.json +176 -0
  23. package/spec/output-schemas/evidence-report.schema.json +322 -0
  24. package/spec/output-schemas/evidence-run.schema.json +277 -0
  25. package/spec/output-schemas/metadata-predicate.schema.json +460 -0
  26. package/spec/output-schemas/retrieval-trace-filters.schema.json +13 -4
  27. package/spec/output-schemas/search-results.schema.json +12 -0
  28. package/spec/output-schemas/status.schema.json +37 -6
  29. package/src/app/compiled-context-files.ts +361 -0
  30. package/src/app/compiled-context.ts +240 -0
  31. package/src/app/context-runtime-contract.ts +33 -2
  32. package/src/app/context-runtime-input.ts +17 -0
  33. package/src/app/context-runtime-types.ts +2 -0
  34. package/src/app/context-runtime.ts +27 -3
  35. package/src/app/context-surface.ts +7 -0
  36. package/src/app/verified-ask.ts +1 -0
  37. package/src/cli/commands/ask.ts +1 -0
  38. package/src/cli/commands/audit.ts +4 -0
  39. package/src/cli/commands/context-build.ts +1 -0
  40. package/src/cli/commands/context-compiled.ts +136 -0
  41. package/src/cli/commands/search.ts +1 -0
  42. package/src/cli/commands/status.ts +10 -0
  43. package/src/cli/errors.ts +10 -3
  44. package/src/cli/options.ts +19 -0
  45. package/src/cli/program.ts +82 -0
  46. package/src/converters/types.ts +2 -0
  47. package/src/core/compiled-context.ts +254 -0
  48. package/src/core/context-budget.ts +2 -10
  49. package/src/core/context-capsule-retrieval-schema.ts +8 -0
  50. package/src/core/context-capsule-schema.ts +47 -3
  51. package/src/core/context-capsule-validation.ts +14 -2
  52. package/src/core/context-capsule-verification.ts +1 -0
  53. package/src/core/context-capsule.ts +16 -2
  54. package/src/core/context-compiler.ts +3 -0
  55. package/src/core/file-lock.ts +22 -5
  56. package/src/core/folder-setup-planning.ts +2 -1
  57. package/src/core/network-boundary-inventory.ts +8 -0
  58. package/src/core/record-metadata.ts +1 -1
  59. package/src/core/retrieval-replay-candidate.ts +5 -0
  60. package/src/core/retrieval-trace-filter-normalization.ts +4 -0
  61. package/src/core/retrieval-trace-request.ts +1 -0
  62. package/src/core/retrieval-trace-session.ts +5 -1
  63. package/src/core/retrieval-trace.ts +2 -0
  64. package/src/core/setup-receipt.ts +27 -20
  65. package/src/core/typed-metadata.ts +228 -0
  66. package/src/core/validation.ts +9 -2
  67. package/src/core/windows-private-path.ts +96 -0
  68. package/src/index.ts +2 -2
  69. package/src/ingestion/compiled-context.ts +15 -0
  70. package/src/ingestion/record-adapter-canonical.ts +13 -2
  71. package/src/ingestion/record-container.ts +9 -0
  72. package/src/ingestion/sync.ts +51 -9
  73. package/src/ingestion/typed-metadata.ts +55 -0
  74. package/src/ingestion/walker.ts +5 -4
  75. package/src/llm/nodeLlamaCpp/simulator-install.ts +6 -2
  76. package/src/mcp/http-egress.ts +11 -3
  77. package/src/mcp/retrieval-warnings.ts +24 -0
  78. package/src/mcp/tools/ask.ts +23 -2
  79. package/src/mcp/tools/context.ts +47 -2
  80. package/src/mcp/tools/index.ts +58 -1
  81. package/src/mcp/tools/query.ts +16 -1
  82. package/src/mcp/tools/search.ts +12 -1
  83. package/src/mcp/tools/status.ts +4 -0
  84. package/src/mcp/tools/vsearch.ts +12 -1
  85. package/src/pipeline/diagnose.ts +6 -0
  86. package/src/pipeline/filters.ts +65 -0
  87. package/src/pipeline/graph-retrieval.ts +9 -1
  88. package/src/pipeline/hybrid.ts +22 -2
  89. package/src/pipeline/search.ts +11 -0
  90. package/src/pipeline/types.ts +7 -1
  91. package/src/pipeline/vsearch.ts +21 -1
  92. package/src/sdk/client.ts +98 -3
  93. package/src/sdk/index.ts +11 -0
  94. package/src/sdk/types.ts +35 -1
  95. package/src/serve/compiled-context.ts +84 -0
  96. package/src/serve/context-capsule.ts +1 -0
  97. package/src/serve/public/app.tsx +12 -1
  98. package/src/serve/public/components/MetadataFilter.tsx +186 -0
  99. package/src/serve/public/globals.built.css +1 -1
  100. package/src/serve/public/lib/metadata-filter.ts +28 -0
  101. package/src/serve/public/lib/retrieval-filters.ts +3 -0
  102. package/src/serve/public/lib/workspace-tabs.ts +2 -0
  103. package/src/serve/public/pages/Ask.tsx +48 -3
  104. package/src/serve/public/pages/CompiledContext.tsx +364 -0
  105. package/src/serve/public/pages/Dashboard.tsx +7 -0
  106. package/src/serve/public/pages/DocView.tsx +33 -0
  107. package/src/serve/public/pages/Search.tsx +44 -1
  108. package/src/serve/routes/api.ts +50 -0
  109. package/src/serve/server.ts +43 -2
  110. package/src/serve/spa-production-build.ts +6 -5
  111. package/src/store/migrations/030-typed-metadata.ts +13 -0
  112. package/src/store/migrations/index.ts +2 -0
  113. package/src/store/sqlite/adapter.ts +69 -5
  114. package/src/store/sqlite/eligibility.ts +12 -0
  115. package/src/store/sqlite/metadata-predicate.ts +71 -0
  116. package/src/store/types.ts +11 -0
  117. package/browser-extension/artifacts/gno-browser-clipper-v2.3.3.zip.sha256 +0 -1
package/src/sdk/index.ts CHANGED
@@ -80,6 +80,10 @@ export type {
80
80
  SectionTargetCreateSelector,
81
81
  SectionTargetResolveResult,
82
82
  SectionTargetV1,
83
+ MetadataPredicate,
84
+ MetadataScalar,
85
+ TypedMetadata,
86
+ TypedValue,
83
87
  MemoryCandidate,
84
88
  MemoryFact,
85
89
  MemoryRecallReceipt,
@@ -124,3 +128,10 @@ export type {
124
128
  RetrievalTracePurgeResult,
125
129
  RetrievalTraceSummary,
126
130
  } from "../core/retrieval-trace-management";
131
+
132
+ export type {
133
+ CompiledContextPreviewInput,
134
+ CompiledContextCheckInput,
135
+ CompiledContextPreview,
136
+ CompiledContextCheck,
137
+ } from "../core/compiled-context";
package/src/sdk/types.ts CHANGED
@@ -1,9 +1,17 @@
1
+ import type {
2
+ previewCompiledContext,
3
+ checkCompiledContext,
4
+ } from "../app/compiled-context";
5
+ import type {
6
+ compileContextFile,
7
+ checkContextFile,
8
+ refreshContextFile,
9
+ } from "../app/compiled-context-files";
1
10
  /**
2
11
  * Public SDK types.
3
12
  *
4
13
  * @module src/sdk/types
5
14
  */
6
-
7
15
  import type {
8
16
  ContextCapsuleBuildInput,
9
17
  ContextRuntimeErrorCode,
@@ -18,6 +26,10 @@ import type {
18
26
  CollectionEgressPolicyState,
19
27
  EgressRelaxationConfirmation,
20
28
  } from "../core/collection-egress-policy-service";
29
+ import type {
30
+ CompiledContextPreviewInput,
31
+ CompiledContextCheckInput,
32
+ } from "../core/compiled-context";
21
33
  import type {
22
34
  ContextCapsuleErrorCode,
23
35
  ContextCapsuleV1,
@@ -328,6 +340,21 @@ export interface GnoClient {
328
340
  query(query: string, options?: GnoQueryOptions): Promise<SearchResults>;
329
341
  ask(query: string, options?: GnoAskOptions): Promise<AskResult>;
330
342
  context(input: GnoContextInput): Promise<GnoContextResult>;
343
+ compiledContextPreview(
344
+ input: CompiledContextPreviewInput
345
+ ): ReturnType<typeof previewCompiledContext>;
346
+ compiledContextCheck(
347
+ input: CompiledContextCheckInput
348
+ ): ReturnType<typeof checkCompiledContext>;
349
+ compileContextFile(
350
+ input: Parameters<typeof compileContextFile>[0]
351
+ ): ReturnType<typeof compileContextFile>;
352
+ checkContextFile(
353
+ input: Parameters<typeof checkContextFile>[0]
354
+ ): ReturnType<typeof checkContextFile>;
355
+ refreshContextFile(
356
+ input: Parameters<typeof refreshContextFile>[0]
357
+ ): ReturnType<typeof refreshContextFile>;
331
358
  verifyContext(
332
359
  capsule: ContextCapsuleV1
333
360
  ): Promise<GnoContextVerificationResult>;
@@ -435,3 +462,10 @@ export interface GnoClient {
435
462
  ): Promise<SectionTargetResolveResult>;
436
463
  close(): Promise<void>;
437
464
  }
465
+
466
+ export type {
467
+ MetadataPredicate,
468
+ MetadataScalar,
469
+ TypedMetadata,
470
+ TypedValue,
471
+ } from "../core/typed-metadata";
@@ -0,0 +1,84 @@
1
+ import type { ServerContext } from "./context";
2
+
3
+ /** Read-only compiled context adapters; caller authority comes from the socket. */
4
+ import {
5
+ previewCompiledContext,
6
+ checkCompiledContext,
7
+ } from "../app/compiled-context";
8
+ import {
9
+ compiledContextPreviewInputSchema,
10
+ compiledContextCheckInputSchema,
11
+ } from "../core/compiled-context";
12
+ import {
13
+ isLocalClientRequest,
14
+ type RequestPeerServer,
15
+ } from "./request-locality";
16
+
17
+ export async function handleCompiledContext(
18
+ context: ServerContext,
19
+ request: Request,
20
+ server: RequestPeerServer | undefined,
21
+ operation: "preview" | "check"
22
+ ): Promise<Response> {
23
+ try {
24
+ // Bound bytes before JSON parsing, even when Content-Length is absent.
25
+ const reader = request.body?.getReader();
26
+ if (!reader)
27
+ return Response.json(
28
+ { error: { code: "invalid_input", message: "JSON body required" } },
29
+ { status: 400 }
30
+ );
31
+ const chunks: Uint8Array[] = [];
32
+ let size = 0;
33
+ while (true) {
34
+ const part = await reader.read();
35
+ if (part.done) break;
36
+ size += part.value.byteLength;
37
+ if (size > 8 * 1024 * 1024 + 65536) {
38
+ await reader.cancel();
39
+ return Response.json(
40
+ {
41
+ error: {
42
+ code: "invalid_input",
43
+ message: "Compiled context request exceeds byte limit",
44
+ },
45
+ },
46
+ { status: 413 }
47
+ );
48
+ }
49
+ chunks.push(part.value);
50
+ }
51
+ const raw: unknown = JSON.parse(
52
+ await new Blob(chunks.map((chunk) => new Uint8Array(chunk))).text()
53
+ );
54
+ const input =
55
+ operation === "preview"
56
+ ? compiledContextPreviewInputSchema.parse(raw)
57
+ : compiledContextCheckInputSchema.parse(raw);
58
+ const local = isLocalClientRequest(request, server);
59
+ const deps = {
60
+ store: context.store,
61
+ config: context.config,
62
+ indexName: context.indexName,
63
+ destinationZone: local ? ("local_process" as const) : ("remote" as const),
64
+ caller: { authenticated: local, operationAuthorized: true },
65
+ };
66
+ const result =
67
+ operation === "preview"
68
+ ? await previewCompiledContext(input, deps)
69
+ : await checkCompiledContext(input, deps);
70
+ return Response.json(result);
71
+ } catch {
72
+ // Supplied Capsules may contain private metadata; errors never echo inputs.
73
+ return Response.json(
74
+ {
75
+ error: {
76
+ code: "compiled_context_unavailable",
77
+ message:
78
+ "Compiled context is invalid, stale, or unavailable for this caller",
79
+ },
80
+ },
81
+ { status: 400 }
82
+ );
83
+ }
84
+ }
@@ -126,6 +126,7 @@ export const handleContextBuild = async (
126
126
  since: input.since,
127
127
  until: input.until,
128
128
  categories: input.categories,
129
+ filter: input.filter,
129
130
  author: input.author,
130
131
  graph: input.graph,
131
132
  candidateLimit: input.candidateLimit,
@@ -47,6 +47,7 @@ const Collections = lazy(() => import("./pages/Collections"));
47
47
  const Connectors = lazy(() => import("./pages/Connectors"));
48
48
  const Ask = lazy(() => import("./pages/Ask"));
49
49
  const GraphView = lazy(() => import("./pages/GraphView"));
50
+ const CompiledContext = lazy(() => import("./pages/CompiledContext"));
50
51
  const TraceHistory = lazy(() => import("./pages/TraceHistory"));
51
52
 
52
53
  type Route =
@@ -59,6 +60,7 @@ type Route =
59
60
  | "/collections"
60
61
  | "/graph"
61
62
  | "/connectors"
63
+ | "/context/compiled"
62
64
  | "/traces";
63
65
  type Navigate = (to: string | number) => void;
64
66
 
@@ -78,6 +80,7 @@ const routes: Record<Route, React.ComponentType<RoutePageProps>> = {
78
80
  "/ask": Ask,
79
81
  "/graph": GraphView,
80
82
  "/traces": TraceHistory,
83
+ "/context/compiled": CompiledContext,
81
84
  };
82
85
 
83
86
  interface AppContentProps {
@@ -179,7 +182,7 @@ function AppContent({
179
182
  <div className="ornament mx-auto mb-4 max-w-[8rem] text-muted-foreground/20">
180
183
  <span className="text-[10px]">◆</span>
181
184
  </div>
182
- <div className="flex items-center justify-center gap-5 text-muted-foreground/60">
185
+ <div className="flex flex-wrap items-center justify-center gap-x-3 gap-y-2 px-4 text-muted-foreground/60 sm:gap-x-5">
183
186
  <button
184
187
  className="transition-colors duration-300 hover:text-primary"
185
188
  onClick={() => navigate("/collections")}
@@ -196,6 +199,14 @@ function AppContent({
196
199
  Trace history
197
200
  </button>
198
201
  <span className="text-border/30">—</span>
202
+ <button
203
+ className="transition-colors duration-300 hover:text-primary"
204
+ onClick={() => navigate("/context/compiled")}
205
+ type="button"
206
+ >
207
+ Compiled context
208
+ </button>
209
+ <span className="text-border/30">—</span>
199
210
  <a
200
211
  className="transition-colors duration-300 hover:text-primary"
201
212
  href="https://github.com/gmickel/gno"
@@ -0,0 +1,186 @@
1
+ import { useId, useState } from "react";
2
+
3
+ import { parseMetadataFilter } from "../lib/metadata-filter";
4
+ import { Button } from "./ui/button";
5
+ import { Input } from "./ui/input";
6
+ import { Textarea } from "./ui/textarea";
7
+
8
+ const OPERATORS = [
9
+ "eq",
10
+ "ne",
11
+ "gt",
12
+ "gte",
13
+ "lt",
14
+ "lte",
15
+ "in",
16
+ "nin",
17
+ "all",
18
+ "exists",
19
+ ];
20
+
21
+ /** The JSON text is the single source of truth, including invalid drafts. */
22
+ export function MetadataFilter({
23
+ value,
24
+ onChange,
25
+ }: {
26
+ value: string;
27
+ onChange: (value: string) => void;
28
+ }) {
29
+ const id = useId();
30
+ const [draftValue, setDraftValue] = useState<string | null>(null);
31
+ const [draftLeaf, setDraftLeaf] = useState<Record<string, unknown> | null>(
32
+ null
33
+ );
34
+ const { error } = parseMetadataFilter(value);
35
+ let leaf: Record<string, unknown> | null = null;
36
+ try {
37
+ const parsed: unknown = JSON.parse(value);
38
+ if (
39
+ parsed &&
40
+ typeof parsed === "object" &&
41
+ !Array.isArray(parsed) &&
42
+ "op" in parsed &&
43
+ OPERATORS.includes(String(parsed.op))
44
+ )
45
+ leaf = parsed as Record<string, unknown>;
46
+ } catch {
47
+ /* Invalid JSON stays editable below. */
48
+ }
49
+ if (value.trim()) leaf ??= draftLeaf;
50
+ const update = (patch: Record<string, unknown>) => {
51
+ const next = { ...leaf, ...patch };
52
+ setDraftLeaf(next);
53
+ if (draftValue !== null) {
54
+ const property = "values" in next ? "values" : "value";
55
+ onChange(
56
+ `{"op":${JSON.stringify(next.op)},"key":${JSON.stringify(next.key)},"${property}":${draftValue}}`
57
+ );
58
+ } else onChange(JSON.stringify(next));
59
+ };
60
+ return (
61
+ <fieldset className="min-w-0 space-y-3 border-border/40 border-t pt-3">
62
+ <legend className="px-1 font-mono text-muted-foreground text-xs">
63
+ Custom metadata
64
+ </legend>
65
+ <p className="text-muted-foreground text-xs">
66
+ Filter indexed gno.metadata fields. Text is case-sensitive. Values use
67
+ JSON: "approved", 0.8, true, or ["alpha"]. Filters are included in this
68
+ page’s URL.
69
+ </p>
70
+ {leaf ? (
71
+ <div className="grid min-w-0 gap-2 sm:grid-cols-3">
72
+ <label className="space-y-1 text-xs">
73
+ Field
74
+ <Input
75
+ onChange={(e) => update({ key: e.target.value })}
76
+ value={typeof leaf.key === "string" ? leaf.key : ""}
77
+ />
78
+ </label>
79
+ <label className="space-y-1 text-xs">
80
+ Operator
81
+ <select
82
+ className="h-9 w-full cursor-pointer rounded-md border border-input bg-background px-2 focus-visible:ring-2 focus-visible:ring-primary/50"
83
+ onChange={(e) => {
84
+ setDraftValue(null);
85
+ setDraftLeaf(null);
86
+ const op = e.target.value;
87
+ onChange(
88
+ JSON.stringify({
89
+ op,
90
+ key: leaf?.key ?? "",
91
+ ...(["in", "nin", "all"].includes(op)
92
+ ? { values: [] }
93
+ : { value: op === "exists" ? true : "" }),
94
+ })
95
+ );
96
+ }}
97
+ value={String(leaf.op)}
98
+ >
99
+ {OPERATORS.map((op) => (
100
+ <option key={op} value={op}>
101
+ {op}
102
+ </option>
103
+ ))}
104
+ </select>
105
+ </label>
106
+ <label className="space-y-1 text-xs">
107
+ Value (JSON)
108
+ <Input
109
+ onChange={(e) => {
110
+ const raw = e.target.value;
111
+ setDraftValue(raw);
112
+ setDraftLeaf(leaf);
113
+ const property = ["in", "nin", "all"].includes(String(leaf?.op))
114
+ ? "values"
115
+ : "value";
116
+ // Preserve malformed JSON verbatim for correction; never drop the filter.
117
+ onChange(
118
+ `{"op":${JSON.stringify(leaf?.op)},"key":${JSON.stringify(leaf?.key ?? "")},"${property}":${raw}}`
119
+ );
120
+ }}
121
+ value={
122
+ draftValue ??
123
+ JSON.stringify("values" in leaf ? leaf.values : leaf.value) ??
124
+ ""
125
+ }
126
+ />
127
+ </label>
128
+ </div>
129
+ ) : !value.trim() ? (
130
+ <Button
131
+ onClick={() => {
132
+ setDraftValue(null);
133
+ setDraftLeaf(null);
134
+ onChange(
135
+ JSON.stringify({ op: "eq", key: "status", value: "approved" })
136
+ );
137
+ }}
138
+ size="sm"
139
+ type="button"
140
+ variant="outline"
141
+ >
142
+ Add metadata filter
143
+ </Button>
144
+ ) : null}
145
+ <label className="block space-y-1 text-xs" htmlFor={`${id}-json`}>
146
+ Advanced filter (JSON)
147
+ <Textarea
148
+ aria-describedby={error ? `${id}-error` : undefined}
149
+ aria-invalid={Boolean(error)}
150
+ className="min-h-20 font-mono text-xs"
151
+ id={`${id}-json`}
152
+ onChange={(e) => {
153
+ setDraftValue(null);
154
+ setDraftLeaf(null);
155
+ onChange(e.target.value);
156
+ }}
157
+ placeholder='{"op":"gte","key":"confidence","value":0.8}'
158
+ value={value}
159
+ />
160
+ </label>
161
+ {error && (
162
+ <p
163
+ className="break-words text-destructive text-xs"
164
+ id={`${id}-error`}
165
+ role="alert"
166
+ >
167
+ {error}
168
+ </p>
169
+ )}
170
+ {value && (
171
+ <Button
172
+ onClick={() => {
173
+ setDraftValue(null);
174
+ setDraftLeaf(null);
175
+ onChange("");
176
+ }}
177
+ size="sm"
178
+ type="button"
179
+ variant="ghost"
180
+ >
181
+ Clear metadata filter
182
+ </Button>
183
+ )}
184
+ </fieldset>
185
+ );
186
+ }