@gmickel/gno 2.12.2 → 2.14.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 +1 -1
- package/assets/skill/SKILL.md +36 -5
- package/assets/skill/cli-reference.md +62 -11
- package/assets/skill/examples.md +30 -4
- package/assets/skill/mcp-reference.md +16 -0
- package/assets/spa-production.json.gz +0 -0
- package/browser-extension/artifacts/{gno-browser-clipper-v2.12.2.zip → gno-browser-clipper-v2.14.0.zip} +0 -0
- package/browser-extension/artifacts/gno-browser-clipper-v2.14.0.zip.sha256 +1 -0
- package/browser-extension/dist/manifest.json +1 -1
- package/package.json +1 -1
- package/spec/cli.md +139 -16
- package/spec/mcp.md +111 -3
- package/spec/output-schemas/collection-list.schema.json +28 -0
- package/spec/output-schemas/convert.schema.json +97 -0
- package/spec/output-schemas/ls.schema.json +65 -0
- package/spec/output-schemas/mcp-job-status.schema.json +17 -1
- package/spec/output-schemas/mcp-sync-result.schema.json +9 -2
- package/spec/output-schemas/search-results.schema.json +7 -0
- package/src/cli/commands/index-cmd.ts +50 -12
- package/src/cli/commands/ls.ts +20 -3
- package/src/cli/commands/profile-apply.ts +2 -1
- package/src/cli/commands/query.ts +21 -8
- package/src/cli/commands/search.ts +11 -0
- package/src/cli/commands/vsearch.ts +17 -4
- package/src/cli/options.ts +19 -0
- package/src/cli/program.ts +90 -10
- package/src/config/loader.ts +11 -1
- package/src/config/types.ts +90 -1
- package/src/core/audit-collection-rules.ts +331 -0
- package/src/core/audit-links.ts +3 -1
- package/src/core/audit-workspace.ts +69 -5
- package/src/core/collection-egress-policy-service.ts +4 -1
- package/src/core/collection-scope.ts +105 -0
- package/src/core/egress-enforcement.ts +20 -4
- package/src/core/egress-policy.ts +4 -0
- package/src/core/egress-provenance.ts +33 -11
- package/src/core/errors.ts +5 -0
- package/src/core/job-manager.ts +238 -19
- package/src/core/project-profile-apply-state.ts +6 -15
- package/src/core/project-profile-apply.ts +32 -1
- package/src/core/reindex-paths.ts +167 -0
- package/src/core/retrieval-trace-request.ts +1 -0
- package/src/ingestion/convert-file.ts +181 -0
- package/src/llm/registry.ts +20 -0
- package/src/mcp/http-egress.ts +21 -2
- package/src/mcp/tools/collection-scope.ts +25 -0
- package/src/mcp/tools/index-cmd.ts +66 -48
- package/src/mcp/tools/index.ts +74 -10
- package/src/mcp/tools/job-status.ts +7 -0
- package/src/mcp/tools/query.ts +28 -7
- package/src/mcp/tools/search.ts +4 -0
- package/src/mcp/tools/sync.ts +68 -60
- package/src/mcp/tools/vsearch.ts +9 -3
- package/src/pipeline/filters.ts +48 -5
- package/src/pipeline/graph-retrieval.ts +1 -0
- package/src/pipeline/hybrid.ts +15 -2
- package/src/pipeline/hydration.ts +7 -2
- package/src/pipeline/owner-fusion.ts +1 -0
- package/src/pipeline/search.ts +2 -0
- package/src/pipeline/types.ts +7 -0
- package/src/pipeline/vsearch.ts +10 -5
- package/src/sdk/client.ts +45 -12
- package/src/sdk/convert.ts +27 -0
- package/src/sdk/documents.ts +15 -2
- package/src/sdk/index.ts +2 -0
- package/src/sdk/types.ts +4 -1
- package/src/serve/jobs.ts +4 -1
- package/src/serve/public/components/IndexingProgress.tsx +25 -3
- package/src/serve/routes/api.ts +164 -24
- package/src/store/sqlite/adapter.ts +97 -27
- package/src/store/sqlite/eligibility.ts +4 -0
- package/src/store/types.ts +7 -0
- package/src/store/vector/sqlite-vec.ts +67 -15
- package/src/store/vector/variant-search.ts +41 -4
- package/browser-extension/artifacts/gno-browser-clipper-v2.12.2.zip.sha256 +0 -1
|
@@ -68,6 +68,37 @@ export function modelTableName(modelUri: string): string {
|
|
|
68
68
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
69
69
|
|
|
70
70
|
const indexDatabases = new WeakMap<VectorIndexPort, Database>();
|
|
71
|
+
/** Query-only read connections, mapped to the shared connection that writes. */
|
|
72
|
+
const writerDatabases = new WeakMap<
|
|
73
|
+
Database,
|
|
74
|
+
{ writer: Database; idle: () => Promise<void> }
|
|
75
|
+
>();
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Register `reader` as a query-only connection whose writes go to `writer`;
|
|
79
|
+
* `idle` resolves once no write transaction is open on `writer`.
|
|
80
|
+
*/
|
|
81
|
+
export function bindReadConnection(
|
|
82
|
+
reader: Database,
|
|
83
|
+
writer: Database,
|
|
84
|
+
idle: () => Promise<void>
|
|
85
|
+
): void {
|
|
86
|
+
writerDatabases.set(reader, { writer, idle });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The connection to write on for `db`: itself, or for a query-only read
|
|
91
|
+
* connection, the shared connection behind it. Partition resolution records
|
|
92
|
+
* runtime callers and migrates legacy partitions, so it runs there.
|
|
93
|
+
*/
|
|
94
|
+
export function writerConnection(db: Database): Database {
|
|
95
|
+
return writerDatabases.get(db)?.writer ?? db;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Wait until the shared connection behind `db` has committed its open batch. */
|
|
99
|
+
export function writerIdle(db: Database): Promise<void> {
|
|
100
|
+
return writerDatabases.get(db)?.idle() ?? Promise.resolve();
|
|
101
|
+
}
|
|
71
102
|
|
|
72
103
|
/** Partition resolution needs the database behind a vector index port. */
|
|
73
104
|
export function getVectorIndexDatabase(
|
|
@@ -131,9 +162,22 @@ export async function createVectorIndexPort(
|
|
|
131
162
|
loadError = e instanceof Error ? e.message : String(e);
|
|
132
163
|
}
|
|
133
164
|
|
|
165
|
+
// A query-only (read) connection cannot create the table. A missing table
|
|
166
|
+
// there means no vectors were ever stored for this model: search is empty.
|
|
167
|
+
const tableExists =
|
|
168
|
+
db
|
|
169
|
+
.query<{ found: number }, [string]>(
|
|
170
|
+
"SELECT 1 AS found FROM sqlite_master WHERE name = ?"
|
|
171
|
+
)
|
|
172
|
+
.get(tableName) !== null;
|
|
173
|
+
const queryOnly =
|
|
174
|
+
db.query<{ query_only: number }, []>("PRAGMA query_only").get()
|
|
175
|
+
?.query_only === 1;
|
|
176
|
+
const noVectorsYet = searchAvailable && !tableExists && queryOnly;
|
|
177
|
+
|
|
134
178
|
// Create per-model vec0 table if extension available
|
|
135
179
|
// Graceful degradation: if table creation fails, storage still works
|
|
136
|
-
if (searchAvailable) {
|
|
180
|
+
if (searchAvailable && !tableExists && !queryOnly) {
|
|
137
181
|
try {
|
|
138
182
|
db.exec(`
|
|
139
183
|
CREATE VIRTUAL TABLE IF NOT EXISTS ${tableName} USING vec0(
|
|
@@ -161,26 +205,32 @@ export async function createVectorIndexPort(
|
|
|
161
205
|
`);
|
|
162
206
|
|
|
163
207
|
// Prepared statements for vec0 table (if available)
|
|
164
|
-
const deleteVecChunkStmt =
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
208
|
+
const deleteVecChunkStmt =
|
|
209
|
+
searchAvailable && !noVectorsYet
|
|
210
|
+
? db.prepare(`DELETE FROM ${tableName} WHERE chunk_id = ?`)
|
|
211
|
+
: null;
|
|
212
|
+
|
|
213
|
+
const insertVecStmt =
|
|
214
|
+
searchAvailable && !noVectorsYet
|
|
215
|
+
? db.prepare(
|
|
216
|
+
`INSERT INTO ${tableName} (chunk_id, embedding) VALUES (?, ?)`
|
|
217
|
+
)
|
|
218
|
+
: null;
|
|
171
219
|
|
|
172
|
-
const searchStmt =
|
|
173
|
-
|
|
220
|
+
const searchStmt =
|
|
221
|
+
searchAvailable && !noVectorsYet
|
|
222
|
+
? db.prepare(`
|
|
174
223
|
SELECT chunk_id, distance
|
|
175
224
|
FROM ${tableName}
|
|
176
225
|
WHERE embedding MATCH ?
|
|
177
226
|
AND k = ?
|
|
178
227
|
`)
|
|
179
|
-
|
|
228
|
+
: null;
|
|
180
229
|
|
|
181
|
-
const deleteVecStmt =
|
|
182
|
-
|
|
183
|
-
|
|
230
|
+
const deleteVecStmt =
|
|
231
|
+
searchAvailable && !noVectorsYet
|
|
232
|
+
? db.prepare(`DELETE FROM ${tableName} WHERE chunk_id LIKE ? || ':%'`)
|
|
233
|
+
: null;
|
|
184
234
|
|
|
185
235
|
// Track if vec0 writes failed this session (needs sync)
|
|
186
236
|
let vecDirty = false;
|
|
@@ -291,7 +341,7 @@ export async function createVectorIndexPort(
|
|
|
291
341
|
k: number,
|
|
292
342
|
searchOptions?: VectorSearchOptions
|
|
293
343
|
): Promise<StoreResult<VectorSearchResult[]>> {
|
|
294
|
-
if (!
|
|
344
|
+
if (!searchAvailable || !(searchStmt || noVectorsYet)) {
|
|
295
345
|
return Promise.resolve(
|
|
296
346
|
err("VEC_SEARCH_UNAVAILABLE", formatUnavailableMessage(loadError))
|
|
297
347
|
);
|
|
@@ -397,6 +447,8 @@ export async function createVectorIndexPort(
|
|
|
397
447
|
)
|
|
398
448
|
);
|
|
399
449
|
}
|
|
450
|
+
// No per-model table on a read connection: nothing was ever stored.
|
|
451
|
+
if (!searchStmt) return Promise.resolve(ok([]));
|
|
400
452
|
const results = searchStmt.all(encodeEmbedding(embedding), k) as {
|
|
401
453
|
chunk_id: string;
|
|
402
454
|
distance: number;
|
|
@@ -13,10 +13,16 @@ import { buildEligibleDocumentQuery } from "../sqlite/eligibility";
|
|
|
13
13
|
import {
|
|
14
14
|
embeddingPartitionIdentity,
|
|
15
15
|
identityPartitionId,
|
|
16
|
+
isPartitionActivated,
|
|
16
17
|
resolveRuntimePartition,
|
|
17
18
|
retrievalUse,
|
|
18
19
|
} from "./runtime-compat";
|
|
19
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
encodeEmbedding,
|
|
22
|
+
getVectorIndexDatabase,
|
|
23
|
+
writerConnection,
|
|
24
|
+
writerIdle,
|
|
25
|
+
} from "./sqlite-vec";
|
|
20
26
|
import { embeddingInputHash } from "./variants";
|
|
21
27
|
|
|
22
28
|
/** Fallback/warning code: this runtime may not read the activated vectors. */
|
|
@@ -35,13 +41,22 @@ export type SearchPartition =
|
|
|
35
41
|
* an activated partition gets a reason instead of an identity; vector spaces
|
|
36
42
|
* are never mixed.
|
|
37
43
|
*/
|
|
38
|
-
export
|
|
44
|
+
export function resolveVectorSearchIdentity(
|
|
39
45
|
port: EmbeddingPort,
|
|
40
46
|
vectorIndex: VectorIndexPort
|
|
47
|
+
): Promise<SearchPartition> {
|
|
48
|
+
return resolveSearchPartition(port, vectorIndex, false);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function resolveSearchPartition(
|
|
52
|
+
port: EmbeddingPort,
|
|
53
|
+
vectorIndex: VectorIndexPort,
|
|
54
|
+
waited: boolean
|
|
41
55
|
): Promise<SearchPartition> {
|
|
42
56
|
const primary = embeddingPartitionIdentity(port);
|
|
43
|
-
const
|
|
44
|
-
if (!primary || !
|
|
57
|
+
const indexDb = getVectorIndexDatabase(vectorIndex);
|
|
58
|
+
if (!primary || !indexDb) return { identity: primary };
|
|
59
|
+
const db = writerConnection(indexDb);
|
|
45
60
|
let resolved;
|
|
46
61
|
try {
|
|
47
62
|
resolved = await resolveRuntimePartition(db, port, primary);
|
|
@@ -61,6 +76,28 @@ export async function resolveVectorSearchIdentity(
|
|
|
61
76
|
);
|
|
62
77
|
if (use.kind === "unavailable")
|
|
63
78
|
return { unavailable: { reason: use.reason, building: use.building } };
|
|
79
|
+
// A first query on an upgraded index can migrate its legacy partition inside
|
|
80
|
+
// a sync batch still open on the shared connection; the read connection sees
|
|
81
|
+
// it only once that batch commits. Wait for that one batch, then resolve
|
|
82
|
+
// again (the migration commits with it, or reruns if the batch rolled back).
|
|
83
|
+
const partitionId = identityPartitionId(resolved.identity);
|
|
84
|
+
if (
|
|
85
|
+
indexDb !== db &&
|
|
86
|
+
isPartitionActivated(db, partitionId) &&
|
|
87
|
+
!isPartitionActivated(indexDb, partitionId)
|
|
88
|
+
) {
|
|
89
|
+
if (!waited) {
|
|
90
|
+
await writerIdle(indexDb);
|
|
91
|
+
return resolveSearchPartition(port, vectorIndex, true);
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
unavailable: {
|
|
95
|
+
reason:
|
|
96
|
+
"its vector partition is being migrated while the index syncs; retry shortly",
|
|
97
|
+
building: false,
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
64
101
|
return { identity: resolved.identity };
|
|
65
102
|
}
|
|
66
103
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
34953d40a06904ec1c0fc7c6df54aaa14cb24acb05b38c3f15d572a285f7b3fc gno-browser-clipper-v2.12.2.zip
|