@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.
Files changed (75) hide show
  1. package/README.md +1 -1
  2. package/assets/skill/SKILL.md +36 -5
  3. package/assets/skill/cli-reference.md +62 -11
  4. package/assets/skill/examples.md +30 -4
  5. package/assets/skill/mcp-reference.md +16 -0
  6. package/assets/spa-production.json.gz +0 -0
  7. package/browser-extension/artifacts/{gno-browser-clipper-v2.12.2.zip → gno-browser-clipper-v2.14.0.zip} +0 -0
  8. package/browser-extension/artifacts/gno-browser-clipper-v2.14.0.zip.sha256 +1 -0
  9. package/browser-extension/dist/manifest.json +1 -1
  10. package/package.json +1 -1
  11. package/spec/cli.md +139 -16
  12. package/spec/mcp.md +111 -3
  13. package/spec/output-schemas/collection-list.schema.json +28 -0
  14. package/spec/output-schemas/convert.schema.json +97 -0
  15. package/spec/output-schemas/ls.schema.json +65 -0
  16. package/spec/output-schemas/mcp-job-status.schema.json +17 -1
  17. package/spec/output-schemas/mcp-sync-result.schema.json +9 -2
  18. package/spec/output-schemas/search-results.schema.json +7 -0
  19. package/src/cli/commands/index-cmd.ts +50 -12
  20. package/src/cli/commands/ls.ts +20 -3
  21. package/src/cli/commands/profile-apply.ts +2 -1
  22. package/src/cli/commands/query.ts +21 -8
  23. package/src/cli/commands/search.ts +11 -0
  24. package/src/cli/commands/vsearch.ts +17 -4
  25. package/src/cli/options.ts +19 -0
  26. package/src/cli/program.ts +90 -10
  27. package/src/config/loader.ts +11 -1
  28. package/src/config/types.ts +90 -1
  29. package/src/core/audit-collection-rules.ts +331 -0
  30. package/src/core/audit-links.ts +3 -1
  31. package/src/core/audit-workspace.ts +69 -5
  32. package/src/core/collection-egress-policy-service.ts +4 -1
  33. package/src/core/collection-scope.ts +105 -0
  34. package/src/core/egress-enforcement.ts +20 -4
  35. package/src/core/egress-policy.ts +4 -0
  36. package/src/core/egress-provenance.ts +33 -11
  37. package/src/core/errors.ts +5 -0
  38. package/src/core/job-manager.ts +238 -19
  39. package/src/core/project-profile-apply-state.ts +6 -15
  40. package/src/core/project-profile-apply.ts +32 -1
  41. package/src/core/reindex-paths.ts +167 -0
  42. package/src/core/retrieval-trace-request.ts +1 -0
  43. package/src/ingestion/convert-file.ts +181 -0
  44. package/src/llm/registry.ts +20 -0
  45. package/src/mcp/http-egress.ts +21 -2
  46. package/src/mcp/tools/collection-scope.ts +25 -0
  47. package/src/mcp/tools/index-cmd.ts +66 -48
  48. package/src/mcp/tools/index.ts +74 -10
  49. package/src/mcp/tools/job-status.ts +7 -0
  50. package/src/mcp/tools/query.ts +28 -7
  51. package/src/mcp/tools/search.ts +4 -0
  52. package/src/mcp/tools/sync.ts +68 -60
  53. package/src/mcp/tools/vsearch.ts +9 -3
  54. package/src/pipeline/filters.ts +48 -5
  55. package/src/pipeline/graph-retrieval.ts +1 -0
  56. package/src/pipeline/hybrid.ts +15 -2
  57. package/src/pipeline/hydration.ts +7 -2
  58. package/src/pipeline/owner-fusion.ts +1 -0
  59. package/src/pipeline/search.ts +2 -0
  60. package/src/pipeline/types.ts +7 -0
  61. package/src/pipeline/vsearch.ts +10 -5
  62. package/src/sdk/client.ts +45 -12
  63. package/src/sdk/convert.ts +27 -0
  64. package/src/sdk/documents.ts +15 -2
  65. package/src/sdk/index.ts +2 -0
  66. package/src/sdk/types.ts +4 -1
  67. package/src/serve/jobs.ts +4 -1
  68. package/src/serve/public/components/IndexingProgress.tsx +25 -3
  69. package/src/serve/routes/api.ts +164 -24
  70. package/src/store/sqlite/adapter.ts +97 -27
  71. package/src/store/sqlite/eligibility.ts +4 -0
  72. package/src/store/types.ts +7 -0
  73. package/src/store/vector/sqlite-vec.ts +67 -15
  74. package/src/store/vector/variant-search.ts +41 -4
  75. 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 = searchAvailable
165
- ? db.prepare(`DELETE FROM ${tableName} WHERE chunk_id = ?`)
166
- : null;
167
-
168
- const insertVecStmt = searchAvailable
169
- ? db.prepare(`INSERT INTO ${tableName} (chunk_id, embedding) VALUES (?, ?)`)
170
- : null;
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 = searchAvailable
173
- ? db.prepare(`
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
- : null;
228
+ : null;
180
229
 
181
- const deleteVecStmt = searchAvailable
182
- ? db.prepare(`DELETE FROM ${tableName} WHERE chunk_id LIKE ? || ':%'`)
183
- : null;
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 (!(searchAvailable && searchStmt)) {
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 { encodeEmbedding, getVectorIndexDatabase } from "./sqlite-vec";
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 async function resolveVectorSearchIdentity(
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 db = getVectorIndexDatabase(vectorIndex);
44
- if (!primary || !db) return { identity: 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