akm-cli 0.9.1 → 0.9.2-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 +103 -28
- package/README.md +3 -1
- package/SECURITY.md +1 -1
- package/STABILITY.md +1 -1
- package/dist/akm +2 -2
- package/dist/akm-migrate +2 -2
- package/dist/assets/hints/cli-hints-full.md +14 -9
- package/dist/assets/improve-strategies/proactive-maintenance.json +1 -1
- package/dist/assets/improve-strategies/reflect-distill.json +1 -1
- package/dist/assets/models.json +35 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +3 -4
- package/dist/assets/stash-skeleton/facts/conventions/organization.md +1 -3
- package/dist/assets/tasks/core/extract.yml +6 -5
- package/dist/assets/tasks/core/improve.yml +6 -5
- package/dist/assets/tasks/core/index-refresh.yml +6 -5
- package/dist/assets/tasks/core/sync.yml +6 -5
- package/dist/assets/tasks/core/version-check.yml +6 -5
- package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-catchup.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-consolidate.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-frequent.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-nightly.yml +6 -5
- package/dist/cli/confirm.js +2 -2
- package/dist/cli/parse-args.js +3 -24
- package/dist/cli/retired-commands.js +1 -1
- package/dist/cli/shared.js +2 -2
- package/dist/cli.js +11 -9
- package/dist/commands/agent/agent-dispatch.js +55 -89
- package/dist/commands/agent/contribute-cli.js +12 -45
- package/dist/commands/command/builtin-action.js +32 -0
- package/dist/commands/command/command-cli.js +99 -0
- package/dist/commands/command/command-execution.js +308 -0
- package/dist/commands/command/execution-source-loader.js +176 -0
- package/dist/commands/command/portable-template.js +60 -0
- package/dist/commands/config-cli.js +10 -4
- package/dist/commands/env/env.js +4 -2
- package/dist/commands/feedback-cli.js +1 -1
- package/dist/commands/health/checks.js +241 -29
- package/dist/commands/health/html-report.js +0 -14
- package/dist/commands/health/report-view-model.js +0 -1
- package/dist/commands/health/surfaces.js +6 -7
- package/dist/commands/health/types.js +0 -2
- package/dist/commands/health.js +63 -18
- package/dist/commands/improve/collapse-detector.js +5 -6
- package/dist/commands/improve/consolidate.js +251 -214
- package/dist/commands/improve/distill/promote-memory.js +71 -34
- package/dist/commands/improve/distill/quality-gate.js +17 -5
- package/dist/commands/improve/distill.js +232 -155
- package/dist/commands/improve/eligibility.js +112 -79
- package/dist/commands/improve/execution.js +57 -0
- package/dist/commands/improve/extract-cli.js +5 -5
- package/dist/commands/improve/extract-prompt.js +64 -22
- package/dist/commands/improve/extract.js +608 -360
- package/dist/commands/improve/improve-strategies.js +43 -14
- package/dist/commands/improve/improve.js +249 -29
- package/dist/commands/improve/loop-stages.js +11 -17
- package/dist/commands/improve/memory/memory-contradiction-detect.js +90 -66
- package/dist/commands/improve/outcome-loop.js +22 -38
- package/dist/commands/improve/planner.js +134 -0
- package/dist/commands/improve/preparation.js +730 -409
- package/dist/commands/improve/reflect.js +386 -223
- package/dist/commands/improve/run-context.js +3 -4
- package/dist/commands/improve/salience.js +6 -58
- package/dist/commands/improve/session-asset.js +12 -12
- package/dist/commands/lint/index.js +101 -29
- package/dist/commands/migrate-cli.js +11 -69
- package/dist/commands/migration-tool.js +6 -9
- package/dist/commands/models-cli.js +27 -0
- package/dist/commands/proposal/drain.js +258 -186
- package/dist/commands/proposal/proposal-cli.js +32 -10
- package/dist/commands/proposal/proposal.js +2 -5
- package/dist/commands/proposal/propose.js +192 -172
- package/dist/commands/proposal/repository.js +54 -91
- package/dist/commands/proposal/validators/proposal-validators.js +9 -7
- package/dist/commands/read/curate.js +53 -22
- package/dist/commands/read/registry-search.js +25 -9
- package/dist/commands/read/remember-cli.js +14 -2
- package/dist/commands/read/search.js +10 -4
- package/dist/commands/read/show.js +139 -153
- package/dist/commands/registry-cli.js +16 -7
- package/dist/commands/remember.js +33 -18
- package/dist/commands/sources/add-cli.js +19 -178
- package/dist/commands/sources/bundle-cli.js +15 -3
- package/dist/commands/sources/dangerous-env-audit.js +135 -0
- package/dist/commands/sources/info.js +2 -1
- package/dist/commands/sources/installed-stashes.js +901 -177
- package/dist/commands/sources/schema-repair.js +174 -95
- package/dist/commands/sources/self-update.js +30 -74
- package/dist/commands/sources/source-add.js +3 -5
- package/dist/commands/sources/sources-cli.js +2 -15
- package/dist/commands/sources/update-transaction.js +220 -0
- package/dist/commands/tasks/tasks-cli.js +3 -3
- package/dist/commands/tasks/tasks.js +736 -317
- package/dist/commands/workflow-cli.js +2 -2
- package/dist/core/adapter/adapters/agent-skills-adapter.js +3 -0
- package/dist/core/adapter/adapters/akm-adapter.js +85 -35
- package/dist/core/adapter/adapters/akm-lint.js +54 -39
- package/dist/core/adapter/adapters/akm-metadata.js +45 -45
- package/dist/core/adapter/adapters/akm-task-adapter.js +32 -49
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +38 -23
- package/dist/core/adapter/adapters/dotenv-adapter.js +30 -1
- package/dist/core/adapter/adapters/generic-files-adapter.js +11 -0
- package/dist/core/adapter/adapters/index.js +0 -9
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +4 -0
- package/dist/core/adapter/adapters/okf-adapter.js +4 -0
- package/dist/core/adapter/adapters/opencode-adapter.js +5 -8
- package/dist/core/adapter/adapters/tool-dir-shared.js +63 -6
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -0
- package/dist/core/adapter/execution-source.js +308 -0
- package/dist/core/adapter/recognize-match.js +36 -13
- package/dist/core/adapter/registry.js +0 -9
- package/dist/core/asset/stash-meta.js +94 -4
- package/dist/core/common.js +6 -11
- package/dist/core/config/config-io.js +3 -3
- package/dist/core/config/config-schema.js +18 -40
- package/dist/core/config/config-sources.js +11 -21
- package/dist/core/config/config-walker.js +31 -13
- package/dist/core/config/config.js +23 -26
- package/dist/core/config/schema/engines.js +8 -7
- package/dist/core/config/schema/improve-processes.js +29 -5
- package/dist/core/config/schema/index-config.js +0 -27
- package/dist/core/config/schema/primitives.js +1 -23
- package/dist/core/config/schema/sources-bundles.js +13 -16
- package/dist/core/errors.js +2 -0
- package/dist/core/events.js +68 -32
- package/dist/core/extra-params.js +1 -0
- package/dist/core/improve-result.js +315 -0
- package/dist/core/lesson-lint.js +0 -6
- package/dist/core/maintenance-barrier.js +4 -4
- package/dist/core/network-policy.js +152 -0
- package/dist/core/paths.js +1 -1
- package/dist/core/recognition-util.js +4 -4
- package/dist/core/registry-url.js +456 -0
- package/dist/core/state/migrations.js +161 -47
- package/dist/core/state-db.js +453 -80
- package/dist/core/system-error.js +32 -0
- package/dist/core/time.js +2 -12
- package/dist/core/write-source.js +0 -18
- package/dist/execution/directory-identity.js +52 -0
- package/dist/execution/executable-identity.js +107 -0
- package/dist/execution/guarded-source.js +398 -0
- package/dist/execution/json.js +95 -0
- package/dist/{commands/health/types-session-log.js → execution/limits.js} +2 -1
- package/dist/execution/record.js +55 -0
- package/dist/execution/resolved-request.js +730 -0
- package/dist/execution/source.js +320 -0
- package/dist/indexer/bundle-identity-guard.js +5 -4
- package/dist/indexer/db/graph-db.js +33 -0
- package/dist/indexer/graph/graph-boost.js +3 -4
- package/dist/indexer/graph/graph-extraction.js +562 -373
- package/dist/indexer/index-written-assets.js +78 -39
- package/dist/indexer/indexer.js +471 -432
- package/dist/indexer/installations.js +6 -0
- package/dist/indexer/lookup/adapter-concept-owner.js +283 -0
- package/dist/indexer/materialize-embeddings.js +155 -0
- package/dist/indexer/passes/memory-inference.js +227 -174
- package/dist/indexer/passes/metadata.js +263 -118
- package/dist/indexer/scan/doc-to-entry.js +7 -10
- package/dist/indexer/scan/drain-dir.js +51 -23
- package/dist/indexer/search/db-search.js +156 -50
- package/dist/indexer/search/fts-query.js +40 -40
- package/dist/indexer/search/ranking.js +36 -1
- package/dist/indexer/search/search-attribution.js +3 -1
- package/dist/indexer/search/search-fields.js +23 -14
- package/dist/indexer/search/search-hit-enrichers.js +1 -1
- package/dist/indexer/search/search-source.js +7 -16
- package/dist/indexer/search/semantic-status.js +10 -1
- package/dist/indexer/usage/show-usage.js +105 -0
- package/dist/indexer/usage/usage-events.js +7 -2
- package/dist/indexer/walk/matchers.js +40 -10
- package/dist/indexer/walk/path-resolver.js +5 -2
- package/dist/indexer/walk/walker.js +20 -2
- package/dist/integrations/agent/builder-shared.js +3 -6
- package/dist/integrations/agent/conversation-fallback.js +16 -0
- package/dist/integrations/agent/engine-resolution.js +87 -87
- package/dist/integrations/agent/execution-cascade.js +566 -0
- package/dist/integrations/agent/execution-definitions.js +211 -0
- package/dist/integrations/agent/execution-lowering.js +811 -0
- package/dist/integrations/agent/execution-preparation.js +67 -0
- package/dist/integrations/agent/index.js +0 -2
- package/dist/integrations/agent/inline-execution.js +74 -0
- package/dist/integrations/agent/model-map.js +515 -0
- package/dist/integrations/agent/persona-fallback.js +30 -0
- package/dist/integrations/agent/request-lowering.js +186 -0
- package/dist/integrations/agent/runner-dispatch.js +230 -37
- package/dist/integrations/agent/runner.js +12 -83
- package/dist/integrations/harnesses/aider/agent-builder.js +8 -0
- package/dist/integrations/harnesses/aider/index.js +0 -1
- package/dist/integrations/harnesses/amazonq/agent-builder.js +8 -0
- package/dist/integrations/harnesses/amazonq/index.js +0 -1
- package/dist/integrations/harnesses/claude/agent-builder.js +14 -1
- package/dist/integrations/harnesses/claude/index.js +1 -5
- package/dist/integrations/harnesses/claude/session-log.js +3 -33
- package/dist/integrations/harnesses/codex/agent-builder.js +8 -0
- package/dist/integrations/harnesses/codex/index.js +0 -1
- package/dist/integrations/harnesses/copilot/agent-builder.js +8 -0
- package/dist/integrations/harnesses/copilot/index.js +0 -1
- package/dist/integrations/harnesses/gemini/agent-builder.js +8 -0
- package/dist/integrations/harnesses/gemini/index.js +0 -1
- package/dist/integrations/harnesses/index.js +4 -44
- package/dist/integrations/harnesses/opencode/agent-builder.js +16 -9
- package/dist/integrations/harnesses/opencode/index.js +0 -2
- package/dist/integrations/harnesses/opencode/session-log.js +14 -204
- package/dist/integrations/harnesses/opencode-sdk/harness.js +12 -1
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +40 -42
- package/dist/integrations/harnesses/openhands/agent-builder.js +8 -0
- package/dist/integrations/harnesses/openhands/index.js +0 -1
- package/dist/integrations/harnesses/pi/agent-builder.js +8 -0
- package/dist/integrations/harnesses/pi/index.js +0 -1
- package/dist/integrations/harnesses/shared.js +0 -1
- package/dist/integrations/harnesses/types.js +1 -3
- package/dist/integrations/lockfile.js +82 -79
- package/dist/integrations/session-logs/index.js +6 -17
- package/dist/integrations/session-logs/provider-base.js +1 -29
- package/dist/llm/client.js +10 -5
- package/dist/llm/embedder.js +6 -7
- package/dist/llm/embedders/local.js +37 -88
- package/dist/llm/embedders/types.js +1 -1
- package/dist/llm/graph-extract.js +75 -50
- package/dist/llm/index-passes.js +43 -5
- package/dist/llm/memory-infer.js +8 -6
- package/dist/llm/metadata-enhance.js +5 -3
- package/dist/llm/structured-call.js +122 -25
- package/dist/output/format-exempt.js +1 -1
- package/dist/output/render-registry.js +0 -16
- package/dist/output/renderers.js +12 -7
- package/dist/output/shapes/curate.js +1 -0
- package/dist/output/shapes/helpers.js +10 -2
- package/dist/output/shapes/passthrough.js +2 -0
- package/dist/output/text/command-format.js +31 -33
- package/dist/output/text/health-format.js +1 -29
- package/dist/output/text/migrate.js +6 -56
- package/dist/output/text/proposal-format.js +16 -1
- package/dist/output/text/workflow-format.js +16 -0
- package/dist/registry/network.js +279 -0
- package/dist/registry/pinned-request-helper.js +247 -0
- package/dist/registry/pinned-transport.js +717 -0
- package/dist/registry/providers/skills-sh.js +18 -6
- package/dist/registry/providers/static-index.js +20 -7
- package/dist/registry/resolve.js +53 -28
- package/dist/scripts/akm-migrate-node.js +19334 -52269
- package/dist/scripts/akm-migrate.js +19270 -51612
- package/dist/setup/registry-stash-loader.js +64 -20
- package/dist/setup/semantic-assets.js +9 -34
- package/dist/setup/setup.js +12 -30
- package/dist/setup/source-identity.js +17 -0
- package/dist/setup/steps/sources.js +36 -15
- package/dist/setup/steps/tasks.js +39 -11
- package/dist/sources/providers/git-provider.js +3 -3
- package/dist/sources/providers/npm.js +2 -2
- package/dist/sources/providers/provider-utils.js +4 -3
- package/dist/sources/providers/website.js +11 -7
- package/dist/sources/snapshot-fetchers/host-guard.js +9 -136
- package/dist/sources/snapshot-fetchers/website-ingest.js +25 -109
- package/dist/sources/website-url.js +73 -0
- package/dist/storage/engines/sqlite-migrations.js +81 -26
- package/dist/storage/managed-db.js +27 -24
- package/dist/storage/repositories/events-repository.js +3 -0
- package/dist/storage/repositories/index-connection.js +42 -10
- package/dist/storage/repositories/index-entries-repository.js +203 -229
- package/dist/storage/repositories/index-entry-mapper.js +8 -12
- package/dist/storage/repositories/index-entry-schema.js +255 -0
- package/dist/storage/repositories/index-fts-repository.js +64 -71
- package/dist/storage/repositories/index-llm-cache-repository.js +8 -13
- package/dist/storage/repositories/index-meta-repository.js +0 -11
- package/dist/storage/repositories/index-schema.js +74 -350
- package/dist/storage/repositories/index-utility-repository.js +12 -17
- package/dist/storage/repositories/index-vec-repository.js +56 -7
- package/dist/storage/repositories/proposals-repository.js +4 -127
- package/dist/storage/repositories/registry-cache.js +2 -1
- package/dist/storage/repositories/task-history-repository.js +20 -40
- package/dist/storage/repositories/workflow-runs-repository.js +228 -129
- package/dist/storage/sqlite-read-snapshot.js +148 -0
- package/dist/tasks/backends/cron.js +170 -42
- package/dist/tasks/backends/index.js +1 -1
- package/dist/tasks/backends/launchd.js +787 -202
- package/dist/tasks/backends/schtasks.js +282 -83
- package/dist/tasks/embedded.js +7 -7
- package/dist/tasks/frozen-script.js +50 -0
- package/dist/tasks/resolve-akm-bin.js +5 -1
- package/dist/tasks/runner.js +239 -251
- package/dist/tasks/runtime-v3.js +281 -0
- package/dist/tasks/scheduler-binding.js +272 -0
- package/dist/tasks/scheduler-invocation.js +57 -43
- package/dist/tasks/scheduler-sync.js +654 -0
- package/dist/tasks/source-v3.js +752 -0
- package/dist/tasks/standalone-script-entry.js +5 -0
- package/dist/tasks/task-id.js +29 -0
- package/dist/workflows/authoring/authoring.js +15 -32
- package/dist/workflows/exec/dispatch-redaction.js +14 -8
- package/dist/workflows/exec/exec-unit.js +7 -28
- package/dist/workflows/exec/frozen-judge.js +57 -89
- package/dist/workflows/exec/lowering-notices.js +23 -0
- package/dist/workflows/exec/native-executor.js +301 -458
- package/dist/workflows/exec/param-secrets.js +4 -3
- package/dist/workflows/exec/run-workflow.js +26 -32
- package/dist/workflows/exec/step-work.js +105 -109
- package/dist/workflows/exec/unit-dispatch.js +103 -27
- package/dist/workflows/exec/unit-writer.js +3 -3
- package/dist/workflows/exec/worktree.js +2 -2
- package/dist/workflows/ir/compile.js +86 -72
- package/dist/workflows/ir/environment-v4.js +328 -0
- package/dist/workflows/ir/freeze-v4.js +122 -0
- package/dist/workflows/ir/plan-hash.js +13 -7
- package/dist/workflows/ir/schema-v4.js +525 -0
- package/dist/workflows/ir/schema.js +25 -284
- package/dist/workflows/ir/source-freeze-v4.js +506 -0
- package/dist/workflows/parser.js +27 -24
- package/dist/workflows/program/schema.js +1 -2
- package/dist/workflows/renderer.js +42 -29
- package/dist/workflows/resource-limits.js +4 -5
- package/dist/workflows/runtime/agent-identity.js +11 -13
- package/dist/workflows/runtime/plan-classifier.js +8 -8
- package/dist/workflows/runtime/runs.js +27 -43
- package/dist/workflows/runtime/workflow-asset-loader.js +45 -205
- package/dist/workflows/source-files.js +373 -0
- package/dist/workflows/source-ir/compile.js +196 -0
- package/dist/workflows/source-ir/github-yaml.js +577 -0
- package/dist/workflows/source-ir/ordering.js +38 -0
- package/dist/workflows/source-ir/program.js +50 -0
- package/dist/workflows/source-ir/result.js +26 -0
- package/dist/workflows/source-ir/schema.js +772 -0
- package/dist/workflows/source-ir/semantics.js +242 -0
- package/dist/workflows/source-ir/uses.js +14 -0
- package/docs/README.md +2 -0
- package/docs/migration/README.md +3 -1
- package/docs/migration/release-notes/0.9.2.md +55 -0
- package/docs/migration/release-notes/README.md +5 -0
- package/docs/migration/v0.8-to-v0.9.md +76 -1077
- package/docs/migration/v0.9.0-troubleshooting.md +104 -516
- package/docs/migration/v0.9.1-to-v0.9.2.md +150 -0
- package/docs/reference/README.md +1 -0
- package/docs/reference/cli.md +230 -98
- package/docs/reference/configuration.md +159 -36
- package/docs/reference/data-and-telemetry.md +19 -1
- package/docs/reference/supported-formats.md +23 -3
- package/docs/reference/tasks.md +182 -0
- package/docs/reference/workflow-schema.md +91 -40
- package/docs/reference/workflows.md +33 -6
- package/package.json +10 -6
- package/schemas/akm-config.json +372 -224
- package/schemas/akm-task.json +324 -80
- package/schemas/akm-workflow.json +6 -9
- package/dist/core/migration-operation.js +0 -75
- package/dist/integrations/agent/model-aliases.js +0 -74
- package/dist/tasks/parser.js +0 -380
- package/dist/tasks/schema.js +0 -123
- package/dist/tasks/validator.js +0 -80
- package/dist/workflows/ir/freeze.js +0 -320
- package/dist/workflows/runtime/document-cache.js +0 -13
|
@@ -1,77 +1,26 @@
|
|
|
1
1
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
-
|
|
5
|
-
* index.db schema, version stamps, and targeted migrations, kept in the
|
|
6
|
-
* storage layer. This isolates the one genuinely risky area (schema
|
|
7
|
-
* evolution) from the CRUD/FTS/vector queries.
|
|
8
|
-
*
|
|
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
|
-
*/
|
|
13
|
-
import { bestEffort } from "../../core/best-effort.js";
|
|
14
|
-
import { warn } from "../../core/warn.js";
|
|
4
|
+
import { CANONICAL_ENTRY_SCHEMA_SQL, CANONICAL_INDEX_DB_VERSION, isCanonicalIndexGeneration, } from "./index-entry-schema.js";
|
|
15
5
|
import { getMeta, setMeta } from "./index-meta-repository.js";
|
|
16
6
|
import { isVecAvailable, purgeEmbeddings } from "./index-vec-repository.js";
|
|
17
7
|
// ── Constants ───────────────────────────────────────────────────────────────
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
// TARGETED, graph-only migration (migrateGraphFilesSchema) — the model for any
|
|
23
|
-
// incompatible change: migrate in place, never wipe the whole index.
|
|
24
|
-
//
|
|
25
|
-
// v17→v18 (Chunk-5 Step 2, spec §14.4): the `entries` table gains the durable
|
|
26
|
-
// bundle-adapter identity/provenance columns — `item_ref` (`<bundle>//<concept
|
|
27
|
-
// -id>` canonical stored spelling), `bundle_id`/`component_id`/`concept_id`/
|
|
28
|
-
// `adapter_id` provenance, `type` (open token), and `content_hash`/`document
|
|
29
|
-
// _json`. They land ADDITIVELY ALONGSIDE the legacy `entry_key`/`dir_path`/
|
|
30
|
-
// `stash_dir`/`entry_json`/`entry_type` columns (dev-time transitional shape):
|
|
31
|
-
// the writer populates the identity/provenance columns while every reader still
|
|
32
|
-
// keys on the legacy columns, so the battery stays green while the reader
|
|
33
|
-
// repoint + ref-grammar flip land incrementally. The legacy columns + this
|
|
34
|
-
// coexistence are removed once every reader is repointed onto `item_ref`
|
|
35
|
-
// (spec §3.3 — single clean shape, no dual read-path). The index is a
|
|
36
|
-
// regenerable derived cache, so an `akm index` rebuild repopulates the new
|
|
37
|
-
// columns on any DB opened at an older version.
|
|
8
|
+
// index.db is a regenerable cache. Incompatible entry-schema changes advance
|
|
9
|
+
// this generation and discard only derived index tables; durable state remains
|
|
10
|
+
// in state.db. Current readers and writers therefore target exactly one schema
|
|
11
|
+
// and never carry live compatibility SQL for previous generations.
|
|
38
12
|
//
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
// is re-keyed onto item_ref by the one-time §11.4 migration cutover
|
|
44
|
-
// (020-three-db-cutover) with orphan quarantine in `legacy_state`. The index is
|
|
45
|
-
// regenerable, so a rebuild is an acceptable fallback if the UNIQUE upgrade finds
|
|
46
|
-
// a duplicate on a partially-migrated DB.
|
|
13
|
+
// v20→v21: remove the transitional entry_key/dir_path/stash_dir/entry_json/
|
|
14
|
+
// entry_type columns. item_ref is the sole conflict key; document_json is the
|
|
15
|
+
// sole stored document projection; bundle provenance and file_path provide the
|
|
16
|
+
// current identity and materialized read path.
|
|
47
17
|
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
// state
|
|
51
|
-
|
|
52
|
-
// schema path needs no migration — a rebuild simply no longer re-creates them.
|
|
53
|
-
// STANDARD REBUILD NOTE: after this bump, any index.db opened at v19 is rebuilt
|
|
54
|
-
// from the stash on the next `akm index`; usage_events/legacy_state are read
|
|
55
|
-
// exclusively from state.db thereafter.
|
|
56
|
-
//
|
|
57
|
-
// Reader/writer repoint progress (spec §3.3 "single clean shape"): the `entries`
|
|
58
|
-
// upsert (index-entries-repository `getUpsertStmts`) uses the UNIQUE `item_ref`
|
|
59
|
-
// as its PRIMARY conflict target, and the graph-boost related-ref reader
|
|
60
|
-
// (`listRelatedPathsForFile`) resolves the user-facing ref from
|
|
61
|
-
// `concept_id`/`item_ref` instead of stripping `entry_key`. The legacy
|
|
62
|
-
// `entry_key`/`dir_path`/`stash_dir`/`entry_type`/`entry_json` columns are NOT
|
|
63
|
-
// yet removable: they retain live consumers outside this module — `entry_key`
|
|
64
|
-
// (mv-cli re-key, usage-event legacy resolution, index-entry-mapper, the LLM
|
|
65
|
-
// cache), `entry_json` (the row payload every reader decodes), `entry_type`
|
|
66
|
-
// (FTS + workflow loader), `stash_dir`/`dir_path` (scan/delete/utility scoping).
|
|
67
|
-
// The upsert therefore keeps `entry_key` as a NULL-item_ref-safe SECOND conflict
|
|
68
|
-
// target (the LLM metadata-enhance re-upsert still writes existing rows with a
|
|
69
|
-
// NULL item_ref) and degrades to it entirely when the item_ref index is the
|
|
70
|
-
// non-unique fallback. Both are deletable once every remaining reader repoints.
|
|
71
|
-
export const DB_VERSION = 20;
|
|
18
|
+
// v21→v22: entry mutations publish FTS synchronously and no dirty queue exists.
|
|
19
|
+
// Discard the old derived generation so stale FTS rows and caller-managed dirty
|
|
20
|
+
// state cannot cross the mutation-authority boundary.
|
|
21
|
+
export const DB_VERSION = CANONICAL_INDEX_DB_VERSION;
|
|
72
22
|
export const EMBEDDING_DIM = 384;
|
|
73
|
-
// #624-P1: graph_files
|
|
74
|
-
// as a marker; the actual migration is the targeted drop in migrateGraphFilesSchema.
|
|
23
|
+
// #624-P1: graph_files is keyed to (stash_root, file_path, body_hash).
|
|
75
24
|
export const GRAPH_SCHEMA_VERSION = 4;
|
|
76
25
|
// ── Schema ──────────────────────────────────────────────────────────────────
|
|
77
26
|
/**
|
|
@@ -111,9 +60,7 @@ const REGISTRY_INDEX_CACHE_DDL = `
|
|
|
111
60
|
`;
|
|
112
61
|
/**
|
|
113
62
|
* Create the graph-extraction tables (`graph_meta`/`graph_files`/`graph_file_entities`/
|
|
114
|
-
* `graph_file_relations`/`graph_extraction_queue`).
|
|
115
|
-
* {@link ensureSchema} (called at the same point, between `migrateGraphFilesSchema`
|
|
116
|
-
* and `migrateGraphDataFromLegacy`) — a pure, behavior-identical decomposition.
|
|
63
|
+
* `graph_file_relations`/`graph_extraction_queue`).
|
|
117
64
|
*/
|
|
118
65
|
function ensureGraphTables(db) {
|
|
119
66
|
db.exec(`
|
|
@@ -205,6 +152,48 @@ function ensureGraphTables(db) {
|
|
|
205
152
|
ON graph_extraction_queue(stash_root, priority DESC, queued_at);
|
|
206
153
|
`);
|
|
207
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* Cross the incompatible entry-schema boundary by discarding the derived index
|
|
157
|
+
* generation. No row conversion or dual-schema compatibility is attempted:
|
|
158
|
+
* the next index run rebuilds entries, FTS, embeddings, utility aggregates,
|
|
159
|
+
* graph extraction, and enrichment caches from current sources/state.
|
|
160
|
+
*/
|
|
161
|
+
function rebuildIncompatibleIndexGeneration(db) {
|
|
162
|
+
const version = getMeta(db, "version");
|
|
163
|
+
const hasEntries = tableExists(db, "entries");
|
|
164
|
+
if (!hasEntries && version === undefined)
|
|
165
|
+
return;
|
|
166
|
+
if (isCanonicalIndexGeneration(db))
|
|
167
|
+
return;
|
|
168
|
+
let vecResetPending = false;
|
|
169
|
+
try {
|
|
170
|
+
db.exec("DROP TABLE IF EXISTS entries_vec");
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
// A vec0 table cannot be dropped while sqlite-vec is unavailable. It does
|
|
174
|
+
// not reference entries, so leave a marker and drop it on the first later
|
|
175
|
+
// open where the extension is available.
|
|
176
|
+
vecResetPending = true;
|
|
177
|
+
}
|
|
178
|
+
db.transaction(() => {
|
|
179
|
+
db.exec("DROP TABLE IF EXISTS graph_file_relations");
|
|
180
|
+
db.exec("DROP TABLE IF EXISTS graph_file_entities");
|
|
181
|
+
db.exec("DROP TABLE IF EXISTS graph_files");
|
|
182
|
+
db.exec("DROP TABLE IF EXISTS graph_extraction_queue");
|
|
183
|
+
db.exec("DROP TABLE IF EXISTS graph_meta");
|
|
184
|
+
db.exec("DROP TABLE IF EXISTS entries_fts_dirty");
|
|
185
|
+
db.exec("DROP TABLE IF EXISTS entries_fts");
|
|
186
|
+
db.exec("DROP TABLE IF EXISTS embeddings");
|
|
187
|
+
db.exec("DROP TABLE IF EXISTS utility_scores_scoped");
|
|
188
|
+
db.exec("DROP TABLE IF EXISTS utility_scores");
|
|
189
|
+
db.exec("DROP TABLE IF EXISTS llm_enrichment_cache");
|
|
190
|
+
db.exec("DROP TABLE IF EXISTS index_dir_state");
|
|
191
|
+
db.exec("DROP TABLE IF EXISTS entries");
|
|
192
|
+
db.exec("DELETE FROM index_meta");
|
|
193
|
+
})();
|
|
194
|
+
if (vecResetPending)
|
|
195
|
+
setMeta(db, "vecResetPending", "1");
|
|
196
|
+
}
|
|
208
197
|
export function ensureSchema(db, embeddingDim) {
|
|
209
198
|
// Create meta table first so we can check version
|
|
210
199
|
db.exec(`
|
|
@@ -213,87 +202,14 @@ export function ensureSchema(db, embeddingDim) {
|
|
|
213
202
|
value TEXT NOT NULL
|
|
214
203
|
);
|
|
215
204
|
`);
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
//
|
|
219
|
-
//
|
|
220
|
-
//
|
|
221
|
-
//
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
// incompatible change is handled by an additive/targeted migration; the few
|
|
225
|
-
// derived tables that ever must be rebuilt are regenerated by `akm index`.
|
|
226
|
-
db.exec(`
|
|
227
|
-
CREATE TABLE IF NOT EXISTS entries (
|
|
228
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
229
|
-
entry_key TEXT NOT NULL UNIQUE,
|
|
230
|
-
dir_path TEXT NOT NULL,
|
|
231
|
-
file_path TEXT NOT NULL,
|
|
232
|
-
stash_dir TEXT NOT NULL,
|
|
233
|
-
entry_json TEXT NOT NULL,
|
|
234
|
-
search_text TEXT NOT NULL,
|
|
235
|
-
entry_type TEXT NOT NULL,
|
|
236
|
-
derived_from TEXT,
|
|
237
|
-
-- Chunk-5 Step 2 / DB v18 (spec 14.4): bundle-adapter identity + provenance,
|
|
238
|
-
-- ADDITIVE alongside the legacy columns above. item_ref is the durable
|
|
239
|
-
-- <bundle>//<concept-id> spelling; nullable during the transition so a
|
|
240
|
-
-- pre-repoint reader path never trips a NOT NULL on a partially-migrated row.
|
|
241
|
-
item_ref TEXT,
|
|
242
|
-
bundle_id TEXT,
|
|
243
|
-
component_id TEXT,
|
|
244
|
-
concept_id TEXT,
|
|
245
|
-
adapter_id TEXT,
|
|
246
|
-
type TEXT,
|
|
247
|
-
content_hash TEXT,
|
|
248
|
-
document_json TEXT
|
|
249
|
-
);
|
|
250
|
-
|
|
251
|
-
CREATE INDEX IF NOT EXISTS idx_entries_dir ON entries(dir_path);
|
|
252
|
-
CREATE INDEX IF NOT EXISTS idx_entries_type ON entries(entry_type);
|
|
253
|
-
CREATE INDEX IF NOT EXISTS idx_entries_file_path ON entries(file_path);
|
|
254
|
-
`);
|
|
255
|
-
// v18: backfill the bundle-adapter identity/provenance columns on databases
|
|
256
|
-
// created against a pre-v18 binary (partial schema) — same PRAGMA-then-ALTER
|
|
257
|
-
// guard pattern as `ensureDerivedFromColumn`. Runs BEFORE the item_ref index
|
|
258
|
-
// so the CREATE INDEX below never references a not-yet-added column.
|
|
259
|
-
ensureBundleRefColumns(db);
|
|
260
|
-
// v19 (F4c, spec §11.4): item_ref is THE durable identity — its index is
|
|
261
|
-
// UNIQUE. Every indexed row now carries item_ref; SQLite treats NULLs as
|
|
262
|
-
// distinct in a UNIQUE index, so NULL-item_ref write-back stragglers coexist.
|
|
263
|
-
// A pre-v19 DB carries a NON-unique `idx_entries_item_ref`, so drop-then-create.
|
|
264
|
-
ensureUniqueItemRefIndex(db);
|
|
265
|
-
// Phase 5A / DB v17: backfill `derived_from` column + index on databases
|
|
266
|
-
// that were created at v17 fresh OR carry a partial v17 schema (a DB whose
|
|
267
|
-
// `index_meta.version` was bumped to 17 but whose `entries` table still
|
|
268
|
-
// lacks the column — this happens when a previous v17 binary opened a
|
|
269
|
-
// pre-v17 DB without taking the upgrade path because no version mismatch
|
|
270
|
-
// was seen at boot). The PRAGMA-then-ALTER guard runs unconditionally so
|
|
271
|
-
// both fresh and partial schemas converge. The CREATE INDEX for
|
|
272
|
-
// `derived_from` MUST run after this helper so we never reference a
|
|
273
|
-
// column that has not yet been added on partial schemas.
|
|
274
|
-
ensureDerivedFromColumn(db);
|
|
275
|
-
// Validated WorkflowDocument JSON, one row per indexed workflow entry.
|
|
276
|
-
// Pure index data — fully rebuilt on each `akm index`. ON DELETE CASCADE
|
|
277
|
-
// means clearing entries (full rebuild or per-dir delete) drops these too.
|
|
278
|
-
db.exec(`
|
|
279
|
-
CREATE TABLE IF NOT EXISTS workflow_documents (
|
|
280
|
-
entry_id INTEGER PRIMARY KEY REFERENCES entries(id) ON DELETE CASCADE,
|
|
281
|
-
schema_version INTEGER NOT NULL,
|
|
282
|
-
document_json TEXT NOT NULL,
|
|
283
|
-
source_path TEXT NOT NULL,
|
|
284
|
-
source_hash TEXT NOT NULL,
|
|
285
|
-
updated_at TEXT NOT NULL
|
|
286
|
-
);
|
|
287
|
-
|
|
288
|
-
CREATE INDEX IF NOT EXISTS idx_workflow_documents_source_path
|
|
289
|
-
ON workflow_documents(source_path);
|
|
290
|
-
`);
|
|
291
|
-
// Set version immediately after table creation so a crash before the end of
|
|
292
|
-
// ensureSchema() does not leave the database in a versionless state on next open.
|
|
293
|
-
const versionAfterCreate = getMeta(db, "version");
|
|
294
|
-
if (!versionAfterCreate) {
|
|
295
|
-
setMeta(db, "version", String(DB_VERSION));
|
|
296
|
-
}
|
|
205
|
+
rebuildIncompatibleIndexGeneration(db);
|
|
206
|
+
db.exec(CANONICAL_ENTRY_SCHEMA_SQL);
|
|
207
|
+
// Workflow source is compiled directly into source IR at each command
|
|
208
|
+
// boundary. The former workflow_documents cache duplicated that IR in a
|
|
209
|
+
// second persisted representation and was never used by current execution.
|
|
210
|
+
// index.db is derived state, so remove the obsolete table on every open.
|
|
211
|
+
db.exec("DROP TABLE IF EXISTS workflow_documents");
|
|
212
|
+
setMeta(db, "version", String(DB_VERSION));
|
|
297
213
|
// BLOB-based embedding storage (always available, no sqlite-vec needed)
|
|
298
214
|
db.exec(`
|
|
299
215
|
CREATE TABLE IF NOT EXISTS embeddings (
|
|
@@ -317,9 +233,8 @@ export function ensureSchema(db, embeddingDim) {
|
|
|
317
233
|
);
|
|
318
234
|
`);
|
|
319
235
|
}
|
|
320
|
-
// usage_events
|
|
321
|
-
//
|
|
322
|
-
// state.db. utility_scores (a regenerable index.db cache) stays here.
|
|
236
|
+
// usage_events lives in state.db. utility_scores remains a regenerable
|
|
237
|
+
// index.db cache.
|
|
323
238
|
// Utility scores table (aggregated per-entry utility metrics)
|
|
324
239
|
db.exec(`
|
|
325
240
|
CREATE TABLE IF NOT EXISTS utility_scores (
|
|
@@ -360,7 +275,7 @@ export function ensureSchema(db, embeddingDim) {
|
|
|
360
275
|
// result for each asset so that subsequent `akm index --enrich` runs can
|
|
361
276
|
// skip the LLM call when the body hasn't changed. The cache is keyed by
|
|
362
277
|
// a stable asset_ref string (e.g. the absolute file path for graph/memory
|
|
363
|
-
// passes, or `
|
|
278
|
+
// passes, or `itemRef:passId` for the metadata-enhance pass).
|
|
364
279
|
// Entries are cleaned up when assets are removed or --re-enrich is used.
|
|
365
280
|
db.exec(`
|
|
366
281
|
CREATE TABLE IF NOT EXISTS llm_enrichment_cache (
|
|
@@ -389,27 +304,13 @@ export function ensureSchema(db, embeddingDim) {
|
|
|
389
304
|
// body_hash) and declare a composite FK -> graph_files ON DELETE CASCADE so
|
|
390
305
|
// child rows are removed when a graph_files row is replaced.
|
|
391
306
|
//
|
|
392
|
-
// #624-P1 targeted migration: an existing DB may still hold the OLD graph_files
|
|
393
|
-
// (entry_id PK). SQLite can't ALTER a primary key, so we RENAME the 3 graph
|
|
394
|
-
// tables aside (→ *_legacy) here — ONLY the graph tables, never the index/
|
|
395
|
-
// embeddings — then the CREATE block below builds the new shape, then
|
|
396
|
-
// migrateGraphDataFromLegacy() copies the data across so the graph is PRESERVED
|
|
397
|
-
// (not re-extracted).
|
|
398
|
-
migrateGraphFilesSchema(db);
|
|
399
307
|
ensureGraphTables(db);
|
|
400
|
-
//
|
|
401
|
-
//
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
// every call — that DDL would fire thousands of times during a full
|
|
407
|
-
// index. See `markFtsDirty` and `rebuildFts({ incremental: true })`.
|
|
408
|
-
db.exec(`
|
|
409
|
-
CREATE TABLE IF NOT EXISTS entries_fts_dirty (
|
|
410
|
-
entry_id INTEGER PRIMARY KEY
|
|
411
|
-
);
|
|
412
|
-
`);
|
|
308
|
+
// If a generation rebuild could not drop a vec0 table while the extension
|
|
309
|
+
// was unavailable, finish that reset as soon as vec0 can be loaded again.
|
|
310
|
+
if (isVecAvailable(db) && getMeta(db, "vecResetPending") === "1") {
|
|
311
|
+
db.exec("DROP TABLE IF EXISTS entries_vec");
|
|
312
|
+
setMeta(db, "vecResetPending", "0");
|
|
313
|
+
}
|
|
413
314
|
// sqlite-vec table
|
|
414
315
|
//
|
|
415
316
|
// Dimension contract:
|
|
@@ -471,86 +372,6 @@ export function ensureSchema(db, embeddingDim) {
|
|
|
471
372
|
// `akm search` does not hit the network on every invocation.
|
|
472
373
|
db.exec(REGISTRY_INDEX_CACHE_DDL);
|
|
473
374
|
}
|
|
474
|
-
/**
|
|
475
|
-
* Phase 5A / DB v17 schema guard.
|
|
476
|
-
*
|
|
477
|
-
* Ensures the `entries.derived_from` column + index exist on the open
|
|
478
|
-
* connection. Called from `ensureSchema()` after the entries CREATE so that
|
|
479
|
-
* legacy databases (created against a pre-v17 binary) still gain the new column
|
|
480
|
-
* without data loss. Idempotent: a `PRAGMA table_info` lookup gates the ALTER.
|
|
481
|
-
*/
|
|
482
|
-
function ensureDerivedFromColumn(db) {
|
|
483
|
-
bestEffort(() => {
|
|
484
|
-
const cols = db.prepare("PRAGMA table_info(entries)").all();
|
|
485
|
-
const hasColumn = cols.some((c) => c.name === "derived_from");
|
|
486
|
-
if (!hasColumn) {
|
|
487
|
-
db.exec("ALTER TABLE entries ADD COLUMN derived_from TEXT");
|
|
488
|
-
}
|
|
489
|
-
// Index creation is idempotent on its own; safe to call unconditionally.
|
|
490
|
-
db.exec("CREATE INDEX IF NOT EXISTS idx_entries_derived_from ON entries(derived_from)");
|
|
491
|
-
}, "entries table may not exist on a brand-new DB before CREATE — caller is responsible");
|
|
492
|
-
}
|
|
493
|
-
/**
|
|
494
|
-
* Chunk-5 Step 2 / DB v18 schema guard.
|
|
495
|
-
*
|
|
496
|
-
* Ensures the bundle-adapter identity/provenance columns exist on the open
|
|
497
|
-
* `entries` table. Called from `ensureSchema()` after the entries CREATE so a
|
|
498
|
-
* legacy database (created against a pre-v18 binary) gains the new columns
|
|
499
|
-
* without a rebuild. All columns are nullable and added ADDITIVELY — the
|
|
500
|
-
* writer populates `item_ref`/`bundle_id`/`component_id`/`concept_id`/
|
|
501
|
-
* `adapter_id`/`type` while readers still key on the legacy columns. Idempotent:
|
|
502
|
-
* a `PRAGMA table_info` lookup gates each ALTER.
|
|
503
|
-
*/
|
|
504
|
-
function ensureBundleRefColumns(db) {
|
|
505
|
-
bestEffort(() => {
|
|
506
|
-
const cols = db.prepare("PRAGMA table_info(entries)").all();
|
|
507
|
-
const have = new Set(cols.map((c) => c.name));
|
|
508
|
-
const additive = [
|
|
509
|
-
["item_ref", "TEXT"],
|
|
510
|
-
["bundle_id", "TEXT"],
|
|
511
|
-
["component_id", "TEXT"],
|
|
512
|
-
["concept_id", "TEXT"],
|
|
513
|
-
["adapter_id", "TEXT"],
|
|
514
|
-
["type", "TEXT"],
|
|
515
|
-
["content_hash", "TEXT"],
|
|
516
|
-
["document_json", "TEXT"],
|
|
517
|
-
];
|
|
518
|
-
for (const [name, sqlType] of additive) {
|
|
519
|
-
if (!have.has(name))
|
|
520
|
-
db.exec(`ALTER TABLE entries ADD COLUMN ${name} ${sqlType}`);
|
|
521
|
-
}
|
|
522
|
-
}, "entries table may not exist on a brand-new DB before CREATE — caller is responsible");
|
|
523
|
-
}
|
|
524
|
-
/**
|
|
525
|
-
* Chunk-5 flip F4c / DB v19 schema guard.
|
|
526
|
-
*
|
|
527
|
-
* Upgrade `entries.item_ref`'s lookup index to UNIQUE (item_ref is now THE
|
|
528
|
-
* durable identity — spec §11.4). A pre-v19 DB has a NON-unique
|
|
529
|
-
* `idx_entries_item_ref`, so we DROP-then-CREATE (a `CREATE UNIQUE INDEX IF NOT
|
|
530
|
-
* EXISTS` under the same name would no-op against the existing non-unique index).
|
|
531
|
-
* SQLite treats NULLs as distinct in a UNIQUE index. Duplicate durable identities
|
|
532
|
-
* are an invalid index and fail the schema open rather than enabling a dual-key
|
|
533
|
-
* fallback.
|
|
534
|
-
*/
|
|
535
|
-
function ensureUniqueItemRefIndex(db) {
|
|
536
|
-
// Probe before mutating. This ran unconditionally on EVERY open as two
|
|
537
|
-
// separate autocommit statements, so there was always a window in which the
|
|
538
|
-
// index did not exist — a concurrent open (registry-cache search, indexer,
|
|
539
|
-
// improve) could DROP between the other's DROP and CREATE and then fail with
|
|
540
|
-
// "index idx_entries_item_ref already exists", or serve a query with no index
|
|
541
|
-
// at all. A DB whose index is already UNIQUE needs no work.
|
|
542
|
-
const existing = db
|
|
543
|
-
.prepare("SELECT sql FROM sqlite_master WHERE type = 'index' AND name = 'idx_entries_item_ref'")
|
|
544
|
-
.get();
|
|
545
|
-
if (existing?.sql && /\bUNIQUE\b/i.test(existing.sql))
|
|
546
|
-
return;
|
|
547
|
-
// Pre-v19 (non-unique index) or absent: convert atomically so a racing open
|
|
548
|
-
// sees either the old index or the new one, never neither.
|
|
549
|
-
db.transaction(() => {
|
|
550
|
-
db.exec("DROP INDEX IF EXISTS idx_entries_item_ref");
|
|
551
|
-
db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_entries_item_ref ON entries(item_ref)");
|
|
552
|
-
})();
|
|
553
|
-
}
|
|
554
375
|
/**
|
|
555
376
|
* Returns true when a table exists in the current database.
|
|
556
377
|
*/
|
|
@@ -558,100 +379,3 @@ function tableExists(db, name) {
|
|
|
558
379
|
const row = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name=? LIMIT 1").get(name);
|
|
559
380
|
return row !== undefined && row !== null;
|
|
560
381
|
}
|
|
561
|
-
/**
|
|
562
|
-
* #624-P1 targeted graph-schema migration — STEP 1 of 2 (rename).
|
|
563
|
-
*
|
|
564
|
-
* graph_files was re-keyed from `entry_id INTEGER PRIMARY KEY REFERENCES
|
|
565
|
-
* entries(id)` to a self-contained `(stash_root, file_path, body_hash)` PK.
|
|
566
|
-
* SQLite cannot ALTER a primary key, so an existing DB carrying the OLD shape
|
|
567
|
-
* has its 3 graph tables RENAMED to `*_legacy` here; ensureSchema's CREATE block
|
|
568
|
-
* then builds the new-shape tables, and {@link migrateGraphDataFromLegacy} COPIES
|
|
569
|
-
* the data across before dropping the legacy tables. The graph is preserved —
|
|
570
|
-
* NOT re-extracted (re-extraction is ~19s/file of LLM work).
|
|
571
|
-
*
|
|
572
|
-
* Crucially this is GRAPH-SCOPED: it touches ONLY the graph tables, never the
|
|
573
|
-
* index / embeddings / enrichment cache. So users keep their (expensive)
|
|
574
|
-
* embeddings instead of being forced into a full re-embed by a DB_VERSION bump.
|
|
575
|
-
*
|
|
576
|
-
* Detection: the old schema has an `entry_id` column on graph_files. Fresh DBs
|
|
577
|
-
* (no graph_files yet) and already-migrated DBs (no entry_id column) are no-ops.
|
|
578
|
-
* Idempotent.
|
|
579
|
-
*/
|
|
580
|
-
function migrateGraphFilesSchema(db) {
|
|
581
|
-
bestEffort(() => {
|
|
582
|
-
const cols = db.prepare("PRAGMA table_info(graph_files)").all();
|
|
583
|
-
const isLegacyShape = cols.some((c) => c.name === "entry_id");
|
|
584
|
-
if (!isLegacyShape)
|
|
585
|
-
return;
|
|
586
|
-
// A previous interrupted migration may have left *_legacy behind — drop those
|
|
587
|
-
// husks first so the rename below doesn't collide.
|
|
588
|
-
db.exec("DROP TABLE IF EXISTS graph_file_relations_legacy");
|
|
589
|
-
db.exec("DROP TABLE IF EXISTS graph_file_entities_legacy");
|
|
590
|
-
db.exec("DROP TABLE IF EXISTS graph_files_legacy");
|
|
591
|
-
// Rename the 3 entry_id-keyed tables aside. graph_meta is unchanged (stash_root
|
|
592
|
-
// key) so it is left in place. ALTER … RENAME auto-updates child FK refs in
|
|
593
|
-
// SQLite ≥3.25, which is fine — the legacy children are dropped after the copy.
|
|
594
|
-
db.exec("ALTER TABLE graph_files RENAME TO graph_files_legacy");
|
|
595
|
-
if (tableExists(db, "graph_file_entities")) {
|
|
596
|
-
db.exec("ALTER TABLE graph_file_entities RENAME TO graph_file_entities_legacy");
|
|
597
|
-
}
|
|
598
|
-
if (tableExists(db, "graph_file_relations")) {
|
|
599
|
-
db.exec("ALTER TABLE graph_file_relations RENAME TO graph_file_relations_legacy");
|
|
600
|
-
}
|
|
601
|
-
}, "graph_files may not exist on a brand-new DB before CREATE — caller is responsible");
|
|
602
|
-
}
|
|
603
|
-
/**
|
|
604
|
-
* #624-P1 targeted graph-schema migration — STEP 2 of 2 (copy + drop legacy).
|
|
605
|
-
*
|
|
606
|
-
* Runs AFTER the graph CREATE TABLE block, so the new-shape tables exist. Copies
|
|
607
|
-
* every legacy row into the re-keyed tables — the old tables already carry
|
|
608
|
-
* (stash_root, file_path, body_hash) next to entry_id, so the projection is a
|
|
609
|
-
* straight column copy (children JOIN back to graph_files_legacy to resolve the
|
|
610
|
-
* composite key from their entry_id). Then drops the `*_legacy` tables.
|
|
611
|
-
*
|
|
612
|
-
* Best-effort: a copy failure (e.g. a pre-body_hash legacy schema) is tolerated,
|
|
613
|
-
* and the legacy tables are dropped regardless so they never linger. Rows whose
|
|
614
|
-
* body_hash is null/empty can't form the new PK and are skipped (they re-extract).
|
|
615
|
-
*/
|
|
616
|
-
function migrateGraphDataFromLegacy(db) {
|
|
617
|
-
if (!tableExists(db, "graph_files_legacy"))
|
|
618
|
-
return;
|
|
619
|
-
let migratedFiles = 0;
|
|
620
|
-
bestEffort(() => {
|
|
621
|
-
db.transaction(() => {
|
|
622
|
-
const res = db
|
|
623
|
-
.prepare(`INSERT OR IGNORE INTO graph_files
|
|
624
|
-
(stash_root, file_path, body_hash, file_order, file_type, confidence, status, reason, extraction_run_id)
|
|
625
|
-
SELECT stash_root, file_path, body_hash, file_order, file_type, confidence, status, reason, extraction_run_id
|
|
626
|
-
FROM graph_files_legacy
|
|
627
|
-
WHERE body_hash IS NOT NULL AND body_hash != ''`)
|
|
628
|
-
.run();
|
|
629
|
-
migratedFiles = Number(res.changes);
|
|
630
|
-
if (tableExists(db, "graph_file_entities_legacy")) {
|
|
631
|
-
db.exec(`INSERT OR IGNORE INTO graph_file_entities
|
|
632
|
-
(stash_root, file_path, body_hash, entity_order, entity_norm, entity)
|
|
633
|
-
SELECT gf.stash_root, gf.file_path, gf.body_hash, e.entity_order, e.entity_norm, e.entity
|
|
634
|
-
FROM graph_file_entities_legacy e
|
|
635
|
-
JOIN graph_files_legacy gf ON gf.entry_id = e.entry_id
|
|
636
|
-
WHERE gf.body_hash IS NOT NULL AND gf.body_hash != ''`);
|
|
637
|
-
}
|
|
638
|
-
if (tableExists(db, "graph_file_relations_legacy")) {
|
|
639
|
-
db.exec(`INSERT OR IGNORE INTO graph_file_relations
|
|
640
|
-
(stash_root, file_path, body_hash, relation_order, from_entity_norm, from_entity, to_entity_norm, to_entity, relation_type, confidence)
|
|
641
|
-
SELECT gf.stash_root, gf.file_path, gf.body_hash, r.relation_order, r.from_entity_norm, r.from_entity, r.to_entity_norm, r.to_entity, r.relation_type, r.confidence
|
|
642
|
-
FROM graph_file_relations_legacy r
|
|
643
|
-
JOIN graph_files_legacy gf ON gf.entry_id = r.entry_id
|
|
644
|
-
WHERE gf.body_hash IS NOT NULL AND gf.body_hash != ''`);
|
|
645
|
-
}
|
|
646
|
-
})();
|
|
647
|
-
}, "graph data migration is best-effort; legacy tables are dropped regardless below");
|
|
648
|
-
// Always drop the legacy tables (children first), migrated or not.
|
|
649
|
-
bestEffort(() => {
|
|
650
|
-
db.exec("DROP TABLE IF EXISTS graph_file_relations_legacy");
|
|
651
|
-
db.exec("DROP TABLE IF EXISTS graph_file_entities_legacy");
|
|
652
|
-
db.exec("DROP TABLE IF EXISTS graph_files_legacy");
|
|
653
|
-
}, "drop legacy graph tables after migration");
|
|
654
|
-
if (migratedFiles > 0) {
|
|
655
|
-
warn(`[akm] graph index re-keyed (#624): migrated ${migratedFiles} extracted file(s) to the new schema — no re-extraction needed. Index + embeddings untouched.`);
|
|
656
|
-
}
|
|
657
|
-
}
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
* retrieval-frequency counting over `usage_events`. The bounded-step EMA policy
|
|
9
9
|
* itself lives in `indexer/feedback/utility-policy`; this repo only reads/writes.
|
|
10
10
|
*/
|
|
11
|
-
import path from "node:path";
|
|
12
11
|
import { conceptIdFromTypeName, parseRefInput } from "../../core/asset/resolve-ref.js";
|
|
13
12
|
import { computeNextUtility } from "../../indexer/feedback/utility-policy.js";
|
|
14
13
|
import { SQLITE_CHUNK_SIZE } from "./index-sql.js";
|
|
@@ -145,7 +144,7 @@ function bareRefCandidates(ref) {
|
|
|
145
144
|
export function getRetrievalCounts(indexDb, stateDb, refs, options = {}) {
|
|
146
145
|
if (refs.length === 0)
|
|
147
146
|
return new Map();
|
|
148
|
-
if (options.sourceName
|
|
147
|
+
if (options.sourceName) {
|
|
149
148
|
return getSourceScopedRetrievalCounts(indexDb, stateDb, refs, options);
|
|
150
149
|
}
|
|
151
150
|
// Map each conceptId back to the input refs that produced it so DB results
|
|
@@ -218,20 +217,18 @@ function getSourceScopedRetrievalCounts(indexDb, stateDb, refs, options) {
|
|
|
218
217
|
bareToInputs.set(bare, [ref]);
|
|
219
218
|
}
|
|
220
219
|
}
|
|
221
|
-
// Cross-DB
|
|
222
|
-
//
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
const entryStashDir = indexDb.prepare("SELECT stash_dir FROM entries WHERE id = ?");
|
|
226
|
-
const stashDirFor = (entryId) => {
|
|
220
|
+
// Cross-DB: usage rows come from state.db; resolve the linked entry's
|
|
221
|
+
// canonical bundle identity from index.db by entry_id.
|
|
222
|
+
const entryBundle = indexDb.prepare("SELECT bundle_id FROM entries WHERE id = ?");
|
|
223
|
+
const bundleFor = (entryId) => {
|
|
227
224
|
if (entryId === null)
|
|
228
225
|
return null;
|
|
229
|
-
const row =
|
|
230
|
-
return row?.
|
|
226
|
+
const row = entryBundle.get(entryId);
|
|
227
|
+
return row?.bundle_id ?? null;
|
|
231
228
|
};
|
|
232
229
|
const countsByBare = new Map();
|
|
233
230
|
const bareForms = [...bareToInputs.keys()];
|
|
234
|
-
const
|
|
231
|
+
const selectedBundle = options.sourceName;
|
|
235
232
|
for (let i = 0; i < bareForms.length; i += SQLITE_CHUNK_SIZE) {
|
|
236
233
|
const chunk = bareForms.slice(i, i + SQLITE_CHUNK_SIZE);
|
|
237
234
|
const placeholders = chunk.map(() => "?").join(", ");
|
|
@@ -248,15 +245,13 @@ function getSourceScopedRetrievalCounts(indexDb, stateDb, refs, options) {
|
|
|
248
245
|
ELSE ue.entry_ref
|
|
249
246
|
END IN (${placeholders})`)
|
|
250
247
|
.all(...chunk);
|
|
251
|
-
const rows = rawRows.map((r) => ({ ...r,
|
|
248
|
+
const rows = rawRows.map((r) => ({ ...r, bundle_id: bundleFor(r.entry_id) }));
|
|
252
249
|
for (const row of rows) {
|
|
253
250
|
const bare = bareRef(row.entry_ref);
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
: false;
|
|
257
|
-
const detached = row.entry_id === null || selectedRoot === undefined;
|
|
251
|
+
const linkedToSelectedBundle = row.entry_id !== null && row.bundle_id === selectedBundle;
|
|
252
|
+
const detached = row.entry_id === null;
|
|
258
253
|
const qualifiedForSource = detached && options.sourceName !== undefined && row.entry_ref === `${options.sourceName}//${bare}`;
|
|
259
|
-
if (!
|
|
254
|
+
if (!linkedToSelectedBundle && !qualifiedForSource)
|
|
260
255
|
continue;
|
|
261
256
|
countsByBare.set(bare, (countsByBare.get(bare) ?? 0) + 1);
|
|
262
257
|
}
|
|
@@ -12,6 +12,7 @@ import { bestEffort } from "../../core/best-effort.js";
|
|
|
12
12
|
import { warn } from "../../core/warn.js";
|
|
13
13
|
import { cosineSimilarity } from "../../llm/embedders/types.js";
|
|
14
14
|
import { getMeta, setMeta } from "./index-meta-repository.js";
|
|
15
|
+
import { SQLITE_CHUNK_SIZE } from "./index-sql.js";
|
|
15
16
|
// ── sqlite-vec extension ────────────────────────────────────────────────────
|
|
16
17
|
const vecStatus = new WeakMap();
|
|
17
18
|
/**
|
|
@@ -68,6 +69,43 @@ export function isVecFastPathReady(db) {
|
|
|
68
69
|
// flag, so the read path has to verify the table really exists.
|
|
69
70
|
return hasVecTable(db);
|
|
70
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Verify that the vec fast-path table mirrors the complete durable BLOB set.
|
|
74
|
+
*
|
|
75
|
+
* A targeted embedding write preserves the prior readiness decision because
|
|
76
|
+
* its subset cannot prove an older degraded generation is healed. Global
|
|
77
|
+
* materialization uses this aggregate check before promoting the persisted
|
|
78
|
+
* flag; search itself still reads the cheap flag and does not repeat the check
|
|
79
|
+
* per query.
|
|
80
|
+
*/
|
|
81
|
+
export function isVecFastPathComplete(db) {
|
|
82
|
+
if (!isVecAvailable(db) || !hasVecTable(db))
|
|
83
|
+
return false;
|
|
84
|
+
try {
|
|
85
|
+
const missingVecRows = db
|
|
86
|
+
.prepare(`
|
|
87
|
+
SELECT id FROM embeddings
|
|
88
|
+
EXCEPT
|
|
89
|
+
SELECT id FROM entries_vec
|
|
90
|
+
LIMIT 1
|
|
91
|
+
`)
|
|
92
|
+
.all();
|
|
93
|
+
if (missingVecRows.length > 0)
|
|
94
|
+
return false;
|
|
95
|
+
const orphanVecRows = db
|
|
96
|
+
.prepare(`
|
|
97
|
+
SELECT id FROM entries_vec
|
|
98
|
+
EXCEPT
|
|
99
|
+
SELECT id FROM embeddings
|
|
100
|
+
LIMIT 1
|
|
101
|
+
`)
|
|
102
|
+
.all();
|
|
103
|
+
return orphanVecRows.length === 0;
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
71
109
|
const vecTablePresent = new WeakMap();
|
|
72
110
|
/**
|
|
73
111
|
* Whether `entries_vec` exists on this connection, memoized per handle.
|
|
@@ -274,13 +312,24 @@ function searchBlobVec(db, queryEmbedding, k) {
|
|
|
274
312
|
* Return all entries that do not yet have an embedding row.
|
|
275
313
|
* Used by the embedding phase to determine which entries need vectors generated.
|
|
276
314
|
*/
|
|
277
|
-
export function getAllEntriesForEmbedding(db) {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
.all();
|
|
315
|
+
export function getAllEntriesForEmbedding(db, entryIds) {
|
|
316
|
+
const select = `
|
|
317
|
+
SELECT e.id, e.search_text AS searchText, e.item_ref AS itemRef, e.file_path AS filePath FROM entries e
|
|
318
|
+
`;
|
|
319
|
+
const missing = "NOT EXISTS (SELECT 1 FROM embeddings b WHERE b.id = e.id)";
|
|
320
|
+
if (entryIds === undefined) {
|
|
321
|
+
return db.prepare(`${select} WHERE ${missing} ORDER BY e.id`).all();
|
|
322
|
+
}
|
|
323
|
+
const targets = [...new Set(entryIds)].sort((left, right) => left - right);
|
|
324
|
+
const rows = [];
|
|
325
|
+
for (let offset = 0; offset < targets.length; offset += SQLITE_CHUNK_SIZE) {
|
|
326
|
+
const chunk = targets.slice(offset, offset + SQLITE_CHUNK_SIZE);
|
|
327
|
+
if (chunk.length === 0)
|
|
328
|
+
continue;
|
|
329
|
+
const placeholders = chunk.map(() => "?").join(",");
|
|
330
|
+
rows.push(...db.prepare(`${select} WHERE e.id IN (${placeholders}) AND ${missing} ORDER BY e.id`).all(...chunk));
|
|
331
|
+
}
|
|
332
|
+
return rows;
|
|
284
333
|
}
|
|
285
334
|
export function getEmbeddingCount(db) {
|
|
286
335
|
const row = db.prepare("SELECT COUNT(*) AS cnt FROM embeddings").get();
|