akm-cli 0.9.16-alpha.1 → 0.9.16-alpha.2
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/CHANGELOG.md +40 -132
- package/dist/assets/hints/cli-hints-full.md +13 -6
- package/dist/assets/tasks/core/index-refresh.yml +1 -1
- package/dist/assets/tasks/improve/akm-improve-catchup.yml +3 -6
- package/dist/cli/retired-commands.js +0 -4
- package/dist/cli/unknown-flags.js +3 -36
- package/dist/commands/env/env-binding.js +4 -4
- package/dist/commands/env/env-cli.js +3 -3
- package/dist/commands/improve/collapse-detector.js +2 -2
- package/dist/commands/improve/consolidate.js +4 -6
- package/dist/commands/improve/improve-cli.js +20 -15
- package/dist/commands/improve/reflect.js +23 -2
- package/dist/commands/lint/base-linter.js +9 -0
- package/dist/commands/lint/env-key-rules.js +2 -2
- package/dist/commands/proposal/propose.js +15 -1
- package/dist/commands/proposal/repository.js +3 -12
- package/dist/commands/proposal/validators/proposal-quality-validators.js +40 -3
- package/dist/commands/proposal/validators/proposal-validators.js +5 -4
- package/dist/commands/read/curate.js +44 -34
- package/dist/commands/read/search.js +35 -54
- package/dist/commands/read/show.js +21 -2
- package/dist/commands/registry-cli.js +5 -5
- package/dist/commands/sources/add-cli.js +59 -16
- package/dist/commands/sources/bundle-cli.js +35 -11
- package/dist/commands/sources/bundle-config-ops.js +30 -0
- package/dist/commands/sources/dangerous-env-audit.js +4 -4
- package/dist/commands/sources/info.js +8 -8
- package/dist/commands/sources/installed-stashes.js +55 -61
- package/dist/commands/sources/source-add.js +39 -38
- package/dist/commands/sources/source-manage.js +34 -12
- package/dist/commands/sources/stash-cli.js +111 -119
- package/dist/commands/sources/stash-skeleton.js +6 -3
- package/dist/commands/tasks/explain.js +4 -1
- package/dist/commands/tasks/tasks-cli.js +31 -9
- package/dist/commands/tasks/tasks.js +239 -194
- package/dist/commands/tasks/validate.js +20 -32
- package/dist/core/activation-policy.js +4 -4
- package/dist/core/adapter/adapters/akm-adapter.js +8 -35
- package/dist/core/adapter/adapters/akm-metadata.js +1 -11
- package/dist/core/adapter/execution-source.js +10 -29
- package/dist/core/asset/asset-placement.js +0 -35
- package/dist/core/config/config-schema.js +64 -8
- package/dist/core/config/config-sources.js +96 -2
- package/dist/core/config/config.js +190 -24
- package/dist/core/config/legacy-source-shape-shim.js +9 -0
- package/dist/core/config/schema/embedding.js +30 -7
- package/dist/core/config/schema/execution.js +23 -0
- package/dist/core/config/schema/experimental.js +1 -1
- package/dist/core/config/schema/scheduler.js +20 -0
- package/dist/core/config/schema/search.js +10 -12
- package/dist/core/config/schema/sources-bundles.js +32 -1
- package/dist/core/content-safety.js +52 -0
- package/dist/core/errors.js +2 -5
- package/dist/core/maintenance-barrier.js +11 -13
- package/dist/core/paths.js +11 -0
- package/dist/core/run-lock.js +2 -5
- package/dist/core/state/migrations.js +1 -26
- package/dist/core/state-db.js +27 -63
- package/dist/core/type-presentation.js +1 -1
- package/dist/core/write-source.js +13 -8
- package/dist/indexer/bundle-identity-guard.js +45 -8
- package/dist/indexer/ensure-index.js +0 -5
- package/dist/indexer/index-db-contention.js +56 -0
- package/dist/indexer/index-rebuild-lock.js +73 -0
- package/dist/indexer/index-written-assets.js +171 -133
- package/dist/indexer/indexer.js +1621 -458
- package/dist/indexer/lookup/adapter-concept-owner.js +5 -19
- package/dist/indexer/materialize-embeddings.js +785 -0
- package/dist/indexer/passes/dir-staleness.js +161 -0
- package/dist/indexer/passes/metadata.js +1 -18
- package/dist/indexer/scan/drain-dir.js +70 -27
- package/dist/indexer/search/db-search.js +89 -373
- package/dist/indexer/search/ranking-contributors.js +16 -21
- package/dist/indexer/search/ranking.js +57 -135
- package/dist/indexer/search/search-source.js +29 -11
- package/dist/integrations/agent/execution-lowering.js +3 -2
- package/dist/integrations/agent/execution-preparation.js +32 -1
- package/dist/integrations/agent/prompts.js +1 -1
- package/dist/integrations/agent/request-lowering.js +3 -2
- package/dist/llm/client.js +3 -11
- package/dist/llm/embedder.js +3 -10
- package/dist/llm/embedders/remote.js +104 -133
- package/dist/llm/feature-gate.js +2 -4
- package/dist/llm/rerank-client.js +3 -3
- package/dist/output/shapes/passthrough.js +2 -1
- package/dist/output/text/command-format.js +13 -19
- package/dist/output/text/helpers.js +1 -1
- package/dist/output/text/index.js +2 -5
- package/dist/registry/resolve.js +37 -10
- package/dist/scripts/akm-migrate-node.js +15197 -11351
- package/dist/scripts/akm-migrate.js +15514 -11668
- package/dist/setup/semantic-assets.js +2 -2
- package/dist/setup/setup.js +3 -3
- package/dist/setup/steps/connection.js +2 -3
- package/dist/setup/steps/tasks.js +29 -36
- package/dist/sources/providers/git-install.js +17 -11
- package/dist/sources/providers/git-provider.js +12 -5
- package/dist/sources/providers/git-stash.js +38 -16
- package/dist/sources/snapshot-fetchers/website-ingest.js +3 -3
- package/dist/storage/repositories/embedding-salvage-repository.js +184 -0
- package/dist/storage/repositories/index-connection.js +3 -1
- package/dist/storage/repositories/index-entries-repository.js +68 -77
- package/dist/storage/repositories/index-entry-schema.js +25 -16
- package/dist/storage/repositories/index-fts-repository.js +263 -29
- package/dist/storage/repositories/index-meta-repository.js +29 -0
- package/dist/storage/repositories/index-schema.js +122 -115
- package/dist/storage/repositories/index-utility-repository.js +1 -1
- package/dist/storage/repositories/index-vec-repository.js +435 -22
- package/dist/tasks/activation-config.js +90 -0
- package/dist/tasks/backends/cron.js +9 -0
- package/dist/tasks/backends/launchd.js +1 -0
- package/dist/tasks/backends/schtasks.js +2 -0
- package/dist/tasks/embedded.js +4 -5
- package/dist/tasks/scheduler-binding.js +2 -2
- package/dist/tasks/scheduler-sync-preview.js +8 -1
- package/dist/tasks/scheduler-sync.js +19 -10
- package/dist/tasks/source/parse-task-source.js +10 -113
- package/dist/tasks/source/project-v4.js +2 -2
- package/dist/tasks/source/task-source-v4.js +4 -12
- package/dist/tasks/source/task-to-v3.js +4 -12
- package/dist/tasks/source/task-to-v4.js +40 -7
- package/docs/migration/README.md +1 -0
- package/docs/migration/release-notes/0.9.15.md +36 -34
- package/docs/migration/release-notes/0.9.16.md +60 -98
- package/docs/migration/release-notes/README.md +0 -5
- package/docs/migration/v0.9.1-to-v0.9.2.md +6 -9
- package/docs/reference/cli.md +124 -122
- package/docs/reference/configuration.md +137 -133
- package/docs/reference/data-and-telemetry.md +1 -2
- package/docs/reference/tasks.md +34 -29
- package/package.json +1 -1
- package/schemas/akm-config.json +170 -6
- package/schemas/akm-task.json +1 -2
- package/dist/commands/sources/index-status.js +0 -99
- package/dist/core/hash.js +0 -18
- package/dist/indexer/drain.js +0 -306
- package/dist/indexer/embedding-identity.js +0 -20
- package/dist/indexer/enrich.js +0 -260
- package/dist/indexer/reconcile.js +0 -890
- package/dist/indexer/scan/parse-file.js +0 -66
- package/dist/indexer/units/unit.js +0 -159
- package/dist/llm/embedders/provider-limits.js +0 -288
- package/dist/storage/repositories/files-repository.js +0 -181
- package/dist/storage/repositories/units-repository.js +0 -510
|
@@ -6,18 +6,16 @@
|
|
|
6
6
|
* storage layer. This isolates the one genuinely risky area (schema
|
|
7
7
|
* evolution) from the CRUD/FTS/vector queries.
|
|
8
8
|
*
|
|
9
|
-
* The meta accessors and vec-availability probe that
|
|
10
|
-
* live in the sibling `index-meta-repository` /
|
|
11
|
-
* modules.
|
|
9
|
+
* The meta accessors, embedding purge, and vec-availability probe that
|
|
10
|
+
* `ensureSchema` leans on live in the sibling `index-meta-repository` /
|
|
11
|
+
* `index-vec-repository` modules.
|
|
12
12
|
*/
|
|
13
13
|
import { ConfigError } from "../../core/errors.js";
|
|
14
|
-
import { withImmediateTransaction } from "../../core/state-db.js";
|
|
15
14
|
import { warn } from "../../core/warn.js";
|
|
16
|
-
import {
|
|
15
|
+
import { ensureEmbeddingSalvageTable, salvageEmbeddingsBeforeDiscard } from "./embedding-salvage-repository.js";
|
|
17
16
|
import { CANONICAL_ENTRY_SCHEMA_SQL, CANONICAL_INDEX_DB_VERSION, classifyIndexGeneration, isCanonicalIndexGeneration, } from "./index-entry-schema.js";
|
|
18
17
|
import { getMeta, setMeta } from "./index-meta-repository.js";
|
|
19
|
-
import { isVecAvailable } from "./index-vec-repository.js";
|
|
20
|
-
import { ensureUnitTables } from "./units-repository.js";
|
|
18
|
+
import { isVecAvailable, purgeEmbeddings } from "./index-vec-repository.js";
|
|
21
19
|
// ── Constants ───────────────────────────────────────────────────────────────
|
|
22
20
|
// index.db is a regenerable cache. Incompatible entry-schema changes advance
|
|
23
21
|
// this generation and discard only derived index tables; durable state remains
|
|
@@ -169,10 +167,8 @@ function ensureGraphTables(db) {
|
|
|
169
167
|
/**
|
|
170
168
|
* Cross the incompatible entry-schema boundary by discarding the derived index
|
|
171
169
|
* generation. No row conversion or dual-schema compatibility is attempted:
|
|
172
|
-
* the next index run rebuilds entries, FTS, utility aggregates,
|
|
173
|
-
* extraction, and enrichment caches from current sources/state.
|
|
174
|
-
* content-addressed (`units`/`units_vec`) and are never part of this
|
|
175
|
-
* discard — see `ensureUnitTables`.
|
|
170
|
+
* the next index run rebuilds entries, FTS, embeddings, utility aggregates,
|
|
171
|
+
* graph extraction, and enrichment caches from current sources/state.
|
|
176
172
|
*/
|
|
177
173
|
function rebuildIncompatibleIndexGeneration(db) {
|
|
178
174
|
const version = getMeta(db, "version");
|
|
@@ -188,22 +184,26 @@ function rebuildIncompatibleIndexGeneration(db) {
|
|
|
188
184
|
"to use this index.", "INDEX_SCHEMA_INCOMPATIBLE", "Upgrade akm to a version that understands this index generation.");
|
|
189
185
|
}
|
|
190
186
|
warn(`Index database generation ${classification.storedVersion ?? "unknown"} is older than this akm's generation ` +
|
|
191
|
-
`${CANONICAL_INDEX_DB_VERSION} — rebuilding the derived index (entries, FTS, graph tables,
|
|
192
|
-
"and the LLM enrichment cache). This re-walks and re-indexes every source on the next run.");
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
187
|
+
`${CANONICAL_INDEX_DB_VERSION} — rebuilding the derived index (entries, FTS, embeddings, graph tables, ` +
|
|
188
|
+
"utility scores, and the LLM enrichment cache). This re-walks and re-indexes every source on the next run.");
|
|
189
|
+
let vecResetPending = false;
|
|
190
|
+
try {
|
|
191
|
+
db.exec("DROP TABLE IF EXISTS entries_vec");
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
// A vec0 table cannot be dropped while sqlite-vec is unavailable. It does
|
|
195
|
+
// not reference entries, so leave a marker and drop it on the first later
|
|
196
|
+
// open where the extension is available.
|
|
197
|
+
vecResetPending = true;
|
|
198
|
+
}
|
|
199
|
+
db.transaction(() => {
|
|
200
|
+
// #955: copy embeddings about to be discarded wholesale into
|
|
201
|
+
// `embedding_salvage` (keyed by content hash + the fingerprint they were
|
|
202
|
+
// generated under) BEFORE dropping `embeddings`, in the same transaction
|
|
203
|
+
// as the drop, so the copy and the discard commit or roll back together.
|
|
204
|
+
// The next embedding pass hands salvaged vectors back to unchanged
|
|
205
|
+
// content instead of re-embedding the whole corpus after this bump.
|
|
206
|
+
salvageEmbeddingsBeforeDiscard(db);
|
|
207
207
|
db.exec("DROP TABLE IF EXISTS graph_file_relations");
|
|
208
208
|
db.exec("DROP TABLE IF EXISTS graph_file_entities");
|
|
209
209
|
db.exec("DROP TABLE IF EXISTS graph_files");
|
|
@@ -214,53 +214,32 @@ function rebuildIncompatibleIndexGeneration(db) {
|
|
|
214
214
|
db.exec("DROP TABLE IF EXISTS entry_fragments");
|
|
215
215
|
db.exec("DROP TABLE IF EXISTS entries_fts");
|
|
216
216
|
db.exec("DROP TABLE IF EXISTS embeddings");
|
|
217
|
-
if (isVecAvailable(db))
|
|
218
|
-
db.exec("DROP TABLE IF EXISTS entries_vec");
|
|
219
217
|
db.exec("DROP TABLE IF EXISTS utility_scores_scoped");
|
|
220
218
|
db.exec("DROP TABLE IF EXISTS utility_scores");
|
|
221
219
|
db.exec("DROP TABLE IF EXISTS llm_enrichment_cache");
|
|
222
220
|
db.exec("DROP TABLE IF EXISTS index_dir_state");
|
|
223
221
|
db.exec("DROP TABLE IF EXISTS entries");
|
|
224
222
|
db.exec("DELETE FROM index_meta");
|
|
225
|
-
|
|
223
|
+
// embedding_salvage is deliberately absent from the drop list above —
|
|
224
|
+
// it is the ONE piece of derived state a generation rebuild must not
|
|
225
|
+
// discard.
|
|
226
|
+
})();
|
|
227
|
+
if (vecResetPending)
|
|
228
|
+
setMeta(db, "vecResetPending", "1");
|
|
226
229
|
}
|
|
227
230
|
export function ensureSchema(db, embeddingDim) {
|
|
228
|
-
// Create meta table first
|
|
229
|
-
// survive even a rolled-back schema attempt, since
|
|
230
|
-
// "does not stamp a generation until every required DDL surface succeeds"
|
|
231
|
-
// (canonical-entry-schema.test.ts) opens index_meta on a failed partial
|
|
232
|
-
// schema and expects the table to exist with no version row, not to be
|
|
233
|
-
// missing entirely.
|
|
231
|
+
// Create meta table first so we can check version
|
|
234
232
|
db.exec(`
|
|
235
233
|
CREATE TABLE IF NOT EXISTS index_meta (
|
|
236
234
|
key TEXT PRIMARY KEY,
|
|
237
235
|
value TEXT NOT NULL
|
|
238
236
|
);
|
|
239
237
|
`);
|
|
240
|
-
//
|
|
241
|
-
//
|
|
242
|
-
//
|
|
243
|
-
//
|
|
244
|
-
|
|
245
|
-
// already-canonical database (the common case) takes the plain path below,
|
|
246
|
-
// unchanged from before: wrapping every open unconditionally was tried
|
|
247
|
-
// first and reintroduced a lock on ordinary index runs, turning a single
|
|
248
|
-
// `busy_timeout` wait under a held writer into one per retry attempt
|
|
249
|
-
// (timed out `index-db-contention.test.ts`). This read can never race a
|
|
250
|
-
// genuine in-progress rebuild — WAL readers only see committed state, and a
|
|
251
|
-
// rebuild only runs when the last-committed state was itself non-canonical.
|
|
252
|
-
if (isCanonicalIndexGeneration(db)) {
|
|
253
|
-
applySchemaBody(db, embeddingDim);
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
// Not (yet) canonical — a fresh database or a genuine stale generation.
|
|
257
|
-
// `withImmediateTransaction(db, ..., "index")` makes a concurrent opener
|
|
258
|
-
// block on `BEGIN IMMEDIATE` until this commits, so it only ever observes
|
|
259
|
-
// fully-fresh or fully-canonical. A real generation mismatch still
|
|
260
|
-
// rebuilds, via the nested call joining this same transaction.
|
|
261
|
-
withImmediateTransaction(db, () => applySchemaBody(db, embeddingDim), "index");
|
|
262
|
-
}
|
|
263
|
-
function applySchemaBody(db, embeddingDim) {
|
|
238
|
+
// #955: created before the generation-rebuild check below so a discard
|
|
239
|
+
// has somewhere to copy vectors to. Additive-only — it carries no bearing
|
|
240
|
+
// on the `entries` generation fingerprint (`hasCanonicalEntrySchema`), so
|
|
241
|
+
// adding it does not require a `CANONICAL_INDEX_DB_VERSION` bump.
|
|
242
|
+
ensureEmbeddingSalvageTable(db);
|
|
264
243
|
rebuildIncompatibleIndexGeneration(db);
|
|
265
244
|
db.exec(CANONICAL_ENTRY_SCHEMA_SQL);
|
|
266
245
|
// Workflow source is compiled directly into source IR at each command
|
|
@@ -268,32 +247,14 @@ function applySchemaBody(db, embeddingDim) {
|
|
|
268
247
|
// second persisted representation and was never used by current execution.
|
|
269
248
|
// index.db is derived state, so remove the obsolete table on every open.
|
|
270
249
|
db.exec("DROP TABLE IF EXISTS workflow_documents");
|
|
271
|
-
//
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
// `entries_vec`, its vec0 mirror) are retired (index redesign, B5h) —
|
|
280
|
-
// vectors live only in the content-addressed `units`/`units_vec` store
|
|
281
|
-
// now. Drop both unconditionally on every open, not gated on a generation
|
|
282
|
-
// bump: a v24 index built before this change may still carry them, and v24
|
|
283
|
-
// tolerates either shape. `embeddings` is a plain table, always safe to
|
|
284
|
-
// drop; `entries_vec` is a vec0 virtual table that can only be dropped
|
|
285
|
-
// while the extension is loaded, so a drop that cannot run yet is deferred
|
|
286
|
-
// the same way a generation rebuild used to defer it — a `vecResetPending`
|
|
287
|
-
// marker, finished the first later open where sqlite-vec is available.
|
|
288
|
-
db.exec("DROP TABLE IF EXISTS embeddings");
|
|
289
|
-
if (isVecAvailable(db)) {
|
|
290
|
-
db.exec("DROP TABLE IF EXISTS entries_vec");
|
|
291
|
-
if (getMeta(db, "vecResetPending") === "1")
|
|
292
|
-
setMeta(db, "vecResetPending", "0");
|
|
293
|
-
}
|
|
294
|
-
else if (db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'entries_vec'").get()) {
|
|
295
|
-
setMeta(db, "vecResetPending", "1");
|
|
296
|
-
}
|
|
250
|
+
// BLOB-based embedding storage (always available, no sqlite-vec needed)
|
|
251
|
+
db.exec(`
|
|
252
|
+
CREATE TABLE IF NOT EXISTS embeddings (
|
|
253
|
+
id INTEGER PRIMARY KEY,
|
|
254
|
+
embedding BLOB NOT NULL,
|
|
255
|
+
FOREIGN KEY (id) REFERENCES entries(id)
|
|
256
|
+
);
|
|
257
|
+
`);
|
|
297
258
|
// usage_events lives in state.db. utility_scores remains a regenerable
|
|
298
259
|
// index.db cache.
|
|
299
260
|
// Utility scores table (aggregated per-entry utility metrics)
|
|
@@ -323,6 +284,17 @@ function applySchemaBody(db, embeddingDim) {
|
|
|
323
284
|
CREATE INDEX IF NOT EXISTS idx_utility_scores_scoped_entry_id
|
|
324
285
|
ON utility_scores_scoped(entry_id);
|
|
325
286
|
`);
|
|
287
|
+
db.exec(`
|
|
288
|
+
CREATE TABLE IF NOT EXISTS index_dir_state (
|
|
289
|
+
dir_path TEXT PRIMARY KEY,
|
|
290
|
+
file_set_hash TEXT NOT NULL,
|
|
291
|
+
file_mtime_max_ms REAL NOT NULL,
|
|
292
|
+
reason TEXT NOT NULL,
|
|
293
|
+
updated_at TEXT NOT NULL,
|
|
294
|
+
row_count INTEGER
|
|
295
|
+
);
|
|
296
|
+
`);
|
|
297
|
+
ensureIndexDirStateRowCountColumn(db);
|
|
326
298
|
// LLM enrichment result cache. Stores a SHA-256 body hash and the JSON
|
|
327
299
|
// result for each asset so that subsequent `akm index --enrich` runs can
|
|
328
300
|
// skip the LLM call when the body hasn't changed. The cache is keyed by
|
|
@@ -357,46 +329,68 @@ function applySchemaBody(db, embeddingDim) {
|
|
|
357
329
|
// child rows are removed when a graph_files row is replaced.
|
|
358
330
|
//
|
|
359
331
|
ensureGraphTables(db);
|
|
360
|
-
//
|
|
332
|
+
// If a generation rebuild could not drop a vec0 table while the extension
|
|
333
|
+
// was unavailable, finish that reset as soon as vec0 can be loaded again.
|
|
334
|
+
if (isVecAvailable(db) && getMeta(db, "vecResetPending") === "1") {
|
|
335
|
+
db.exec("DROP TABLE IF EXISTS entries_vec");
|
|
336
|
+
setMeta(db, "vecResetPending", "0");
|
|
337
|
+
}
|
|
338
|
+
// sqlite-vec table
|
|
361
339
|
//
|
|
362
340
|
// Dimension contract:
|
|
363
341
|
// - When `embeddingDim` is `undefined`, the caller did NOT request a
|
|
364
|
-
// specific dim. Do not touch `index_meta.embeddingDim`
|
|
365
|
-
// the stored dim (or the static
|
|
366
|
-
//
|
|
367
|
-
//
|
|
368
|
-
// the
|
|
342
|
+
// specific dim. Do not touch `index_meta.embeddingDim` and do not run
|
|
343
|
+
// the dim-change wipe — fall back to the stored dim (or the static
|
|
344
|
+
// default) only when we have to materialise the vec table for the
|
|
345
|
+
// first time. Without this guard, registry-side and other dim-unaware
|
|
346
|
+
// `openDatabase()` callers would silently overwrite the dim-aware
|
|
347
|
+
// improve/index value and oscillate the stored dim.
|
|
369
348
|
// - When `embeddingDim` is a number, the caller explicitly asked for
|
|
370
|
-
// that dim
|
|
371
|
-
//
|
|
372
|
-
// A genuine dimension change (a real model swap) is NOT handled here: it
|
|
373
|
-
// surfaces as a different observed embedding identity
|
|
374
|
-
// (`deriveObservedEmbeddingIdentity` folds the observed vector width into
|
|
375
|
-
// the identity string), and `dropOtherIdentities` — called from the
|
|
376
|
-
// embedding loop when the active identity changes — recreates `units_vec`
|
|
377
|
-
// at the new width then. `units_vec` itself is never dropped or purged
|
|
378
|
-
// here; ensureUnitTables only creates it if missing, at whatever width is
|
|
379
|
-
// effective the first time that happens.
|
|
349
|
+
// that dim and owns the dim-change/backup/wipe semantics.
|
|
380
350
|
const dimExplicit = embeddingDim !== undefined;
|
|
381
351
|
const requestedDim = embeddingDim ?? (Number(getMeta(db, "embeddingDim")) || EMBEDDING_DIM);
|
|
382
352
|
const effectiveDim = Number.isInteger(requestedDim) && requestedDim > 0 ? requestedDim : EMBEDDING_DIM;
|
|
383
353
|
if (effectiveDim !== requestedDim) {
|
|
384
354
|
warn(`Invalid embedding dimension ${requestedDim} — falling back to the default (${EMBEDDING_DIM}).`);
|
|
385
355
|
}
|
|
386
|
-
if (
|
|
387
|
-
|
|
356
|
+
if (isVecAvailable(db)) {
|
|
357
|
+
// Check if stored embedding dimension differs from configured one
|
|
358
|
+
if (dimExplicit) {
|
|
359
|
+
const storedDim = getMeta(db, "embeddingDim");
|
|
360
|
+
if (storedDim && storedDim !== String(effectiveDim)) {
|
|
361
|
+
// Stored vectors are incompatible with the new dimension. Drop the vec
|
|
362
|
+
// table so the block below recreates it at the new width; the BLOB rows
|
|
363
|
+
// go too. Regenerable from markdown — re-embedded by the next index.
|
|
364
|
+
purgeEmbeddings(db, { dropVecTable: true });
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
const vecExists = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='entries_vec'").get();
|
|
368
|
+
if (!vecExists) {
|
|
369
|
+
db.exec(`
|
|
370
|
+
CREATE VIRTUAL TABLE entries_vec USING vec0(
|
|
371
|
+
id INTEGER PRIMARY KEY,
|
|
372
|
+
embedding FLOAT[${effectiveDim}]
|
|
373
|
+
);
|
|
374
|
+
`);
|
|
375
|
+
}
|
|
376
|
+
if (dimExplicit) {
|
|
377
|
+
setMeta(db, "embeddingDim", String(effectiveDim));
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
// Also purge BLOB embeddings on dimension change (JS fallback path).
|
|
382
|
+
// When sqlite-vec is unavailable, entries_vec doesn't exist but the BLOB
|
|
383
|
+
// embeddings table still stores vectors. If the configured dimension
|
|
384
|
+
// changes, those stored BLOBs become silently incompatible.
|
|
385
|
+
if (dimExplicit) {
|
|
386
|
+
const storedDim = getMeta(db, "embeddingDim");
|
|
387
|
+
if (storedDim && storedDim !== String(effectiveDim)) {
|
|
388
|
+
// JS-fallback path: no vec table, just clear the stale BLOB vectors.
|
|
389
|
+
purgeEmbeddings(db);
|
|
390
|
+
}
|
|
391
|
+
setMeta(db, "embeddingDim", String(effectiveDim));
|
|
392
|
+
}
|
|
388
393
|
}
|
|
389
|
-
// units / units_vec / entry_units (docs/plans/index-fragment-vectors.md):
|
|
390
|
-
// created if missing, and NEVER dropped by the generation rebuild above or
|
|
391
|
-
// by any purge — only dropOtherIdentities (called from the embedding loop
|
|
392
|
-
// on a real identity change) removes rows. ensureUnitTables is idempotent,
|
|
393
|
-
// so this runs on every ensureSchema call, not just the first.
|
|
394
|
-
ensureUnitTables(db, effectiveDim);
|
|
395
|
-
// files / unit_texts / units_fts (docs/plans/index-redesign-contract.md, B1):
|
|
396
|
-
// the reconcile engine's stat cache and content-addressed unit text store.
|
|
397
|
-
// Created if missing; reconcile.ts and pruneOrphanUnitTexts own all row-level
|
|
398
|
-
// writes and deletes, never this ensure path.
|
|
399
|
-
ensureFileAndUnitTextTables(db);
|
|
400
394
|
// Usage telemetry (usage_events) lives in state.db since Chunk-8 WI-8.3 —
|
|
401
395
|
// no longer created here.
|
|
402
396
|
// Registry index cache table — caches remote registry index documents so
|
|
@@ -414,3 +408,16 @@ function tableExists(db, name) {
|
|
|
414
408
|
const row = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name=? LIMIT 1").get(name);
|
|
415
409
|
return row !== undefined && row !== null;
|
|
416
410
|
}
|
|
411
|
+
/**
|
|
412
|
+
* #900: `row_count` was added after the table's first release, so a database
|
|
413
|
+
* created before it needs an `ALTER TABLE` (`CREATE TABLE IF NOT EXISTS` only
|
|
414
|
+
* shapes a fresh table). Idempotent. Pre-existing rows keep NULL until their
|
|
415
|
+
* directory is next drained; index.db is a regenerable cache, so nothing is
|
|
416
|
+
* backfilled.
|
|
417
|
+
*/
|
|
418
|
+
function ensureIndexDirStateRowCountColumn(db) {
|
|
419
|
+
const columns = db.prepare("PRAGMA table_info(index_dir_state)").all();
|
|
420
|
+
if (!columns.some((column) => column.name === "row_count")) {
|
|
421
|
+
db.exec("ALTER TABLE index_dir_state ADD COLUMN row_count INTEGER");
|
|
422
|
+
}
|
|
423
|
+
}
|
|
@@ -81,7 +81,7 @@ export function getUtilityScoresByIds(db, ids, scopeKey) {
|
|
|
81
81
|
* Insert or update a utility score for an entry.
|
|
82
82
|
*/
|
|
83
83
|
export function upsertUtilityScore(db, entryId, data) {
|
|
84
|
-
// Pre-flight FK guard: when an entry is
|
|
84
|
+
// Pre-flight FK guard (mirrors `upsertEmbedding`): when an entry is
|
|
85
85
|
// deleted between when its id is aggregated from usage_events and when
|
|
86
86
|
// this INSERT runs, the FK constraint fails and rolls back the entire
|
|
87
87
|
// finalize transaction. A cheap SELECT here turns the race into a
|