akm-cli 0.9.0-rc.0 → 0.9.0-rc.13
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 +1283 -22
- package/README.md +62 -37
- package/SECURITY.md +46 -31
- package/dist/akm +162 -38
- package/dist/akm-migrate +44 -0
- package/dist/assets/backends/schtasks-template.xml +2 -1
- package/dist/assets/hints/cli-hints-full.md +268 -118
- package/dist/assets/hints/cli-hints-short.md +87 -24
- package/dist/assets/{profiles → improve-strategies}/catchup.json +3 -1
- package/dist/assets/{profiles → improve-strategies}/consolidate.json +3 -1
- package/dist/assets/{profiles → improve-strategies}/default.json +6 -7
- package/dist/assets/improve-strategies/frequent.json +15 -0
- package/dist/assets/{profiles → improve-strategies}/graph-refresh.json +4 -2
- package/dist/assets/{profiles → improve-strategies}/memory-focus.json +4 -1
- package/dist/assets/{profiles → improve-strategies}/proactive-maintenance.json +5 -5
- package/dist/assets/{profiles → improve-strategies}/quick.json +4 -2
- package/dist/assets/improve-strategies/reflect-distill.json +30 -0
- package/dist/assets/{profiles → improve-strategies}/thorough.json +1 -1
- package/dist/assets/prompts/consolidate-system.md +5 -5
- package/dist/assets/prompts/extract-session.md +2 -6
- package/dist/assets/prompts/memory-infer-user.md +2 -3
- package/dist/assets/prompts/reflect-llm-framed-contract.md +11 -0
- package/dist/assets/prompts/reflect-llm-schema-contract.md +3 -0
- package/dist/assets/prompts/reflect-output-repair.md +3 -0
- package/dist/assets/prompts/workflow-unit-preamble.md +26 -0
- package/dist/assets/stash-skeleton/README.md +38 -10
- package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +8 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +8 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +14 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +13 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +9 -1
- package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +11 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +9 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +9 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +8 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +100 -0
- package/dist/assets/stash-skeleton/facts/conventions/domains.md +64 -0
- package/dist/assets/stash-skeleton/facts/conventions/organization.md +136 -0
- package/dist/assets/tasks/core/extract.yml +3 -2
- package/dist/assets/tasks/core/improve.yml +2 -1
- package/dist/assets/tasks/core/index-refresh.yml +1 -0
- package/dist/assets/tasks/core/sync.yml +1 -0
- package/dist/assets/tasks/core/version-check.yml +2 -1
- package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
- package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
- package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
- package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
- package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
- package/dist/assets/templates/html/health.html +5 -4
- package/dist/assets/workflows/workflow-template.md +31 -15
- package/dist/cli/invocation.js +279 -0
- package/dist/cli/parse-args.js +5 -90
- package/dist/cli/retired-commands.js +78 -0
- package/dist/cli/shared.js +158 -48
- package/dist/cli-node.mjs +2 -1
- package/dist/cli.js +747 -293
- package/dist/commands/agent/agent-dispatch.js +19 -18
- package/dist/commands/agent/agent-support.js +0 -24
- package/dist/commands/agent/contribute-cli.js +43 -97
- package/dist/commands/completions.js +80 -23
- package/dist/commands/config-cli.js +44 -281
- package/dist/commands/env/env-binding.js +99 -0
- package/dist/commands/env/env-cli.js +84 -224
- package/dist/commands/env/env.js +12 -163
- package/dist/commands/env/marker-path.js +6 -0
- package/dist/commands/env/secret-cli.js +45 -61
- package/dist/commands/env/secret.js +32 -62
- package/dist/commands/feedback-cli.js +179 -85
- package/dist/commands/health/accept-rate.js +58 -0
- package/dist/commands/health/advisories.js +7 -8
- package/dist/commands/health/checks.js +279 -94
- package/dist/commands/health/html-report.js +197 -578
- package/dist/commands/health/improve-metrics.js +277 -246
- package/dist/commands/health/llm-usage.js +19 -19
- package/dist/commands/health/md-report.js +16 -7
- package/dist/commands/health/metrics.js +67 -32
- package/dist/commands/health/renderers.js +47 -0
- package/dist/commands/health/report-view-model.js +508 -0
- package/dist/commands/health/stash-exposure.js +1 -1
- package/dist/commands/health/surfaces.js +16 -56
- package/dist/commands/health/task-runs.js +3 -67
- package/dist/{migrate-storage-node.mjs → commands/health/types-checks.js} +1 -5
- package/dist/commands/health/types-improve.js +29 -0
- package/dist/{output/text/save.js → commands/health/types-metrics.js} +1 -2
- package/dist/commands/health/types-result.js +7 -0
- package/dist/commands/health/types-runs.js +4 -0
- package/dist/commands/health/types-session-log.js +4 -0
- package/dist/commands/health/types-windows.js +4 -0
- package/dist/commands/health/types.js +26 -21
- package/dist/commands/health/windows.js +2 -3
- package/dist/commands/health.js +296 -167
- package/dist/commands/improve/anti-collapse.js +5 -5
- package/dist/commands/improve/autonomy-gate.js +68 -0
- package/dist/commands/improve/collapse-detector.js +65 -52
- package/dist/commands/improve/consolidate/chunking.js +9 -7
- package/dist/commands/improve/consolidate/eligibility.js +1 -23
- package/dist/commands/improve/consolidate/merge.js +4 -0
- package/dist/commands/improve/consolidate.js +454 -1354
- package/dist/commands/improve/content-hash.js +39 -0
- package/dist/commands/improve/distill/content-repair.js +4 -10
- package/dist/commands/improve/distill/promote-memory.js +89 -64
- package/dist/commands/improve/distill/quality-gate.js +118 -42
- package/dist/commands/improve/distill-guards.js +1 -1
- package/dist/commands/improve/distill-promotion-policy.js +33 -888
- package/dist/commands/improve/distill.js +607 -363
- package/dist/commands/improve/eligibility.js +165 -79
- package/dist/commands/improve/extract-cli.js +35 -126
- package/dist/commands/improve/extract-prompt.js +6 -35
- package/dist/commands/improve/extract.js +640 -391
- package/dist/commands/improve/feedback-valence.js +2 -12
- package/dist/commands/improve/improve-cli.js +134 -135
- package/dist/commands/improve/improve-result-file.js +30 -50
- package/dist/commands/improve/improve-run-types.js +4 -0
- package/dist/commands/improve/improve-strategies.js +135 -0
- package/dist/commands/improve/improve.js +904 -701
- package/dist/commands/improve/locks.js +64 -111
- package/dist/commands/improve/loop-stages.js +1110 -923
- package/dist/commands/improve/memory/derived-ref.js +124 -0
- package/dist/commands/improve/memory/memory-belief.js +79 -7
- package/dist/commands/improve/memory/memory-contradiction-detect.js +49 -52
- package/dist/commands/improve/memory/memory-improve.js +25 -37
- package/dist/commands/improve/outcome-loop.js +25 -88
- package/dist/commands/improve/preparation.js +1034 -813
- package/dist/commands/improve/proactive-maintenance.js +34 -9
- package/dist/commands/improve/proposal-envelope.js +31 -0
- package/dist/commands/improve/reflect.js +983 -794
- package/dist/commands/improve/run-context.js +119 -0
- package/dist/commands/improve/salience.js +24 -127
- package/dist/commands/improve/session-asset.js +7 -3
- package/dist/commands/improve/shared.js +14 -34
- package/dist/commands/improve/source-identity.js +28 -0
- package/dist/commands/improve/triage.js +20 -17
- package/dist/commands/lint/base-linter.js +340 -313
- package/dist/commands/lint/env-key-rules.js +31 -47
- package/dist/commands/lint/index.js +185 -30
- package/dist/commands/{events.js → log.js} +28 -38
- package/dist/commands/migrate-cli.js +54 -0
- package/dist/commands/migration-tool.js +55 -0
- package/dist/commands/observability-cli.js +70 -208
- package/dist/commands/proposal/diff-format.js +50 -0
- package/dist/commands/proposal/drain-policies.js +0 -6
- package/dist/commands/proposal/drain.js +91 -40
- package/dist/commands/proposal/proposal-cli.js +134 -132
- package/dist/commands/proposal/proposal-types.js +56 -0
- package/dist/commands/proposal/proposal.js +83 -65
- package/dist/commands/proposal/propose-cli.js +88 -0
- package/dist/commands/proposal/propose.js +105 -88
- package/dist/commands/proposal/repository.js +1303 -278
- package/dist/commands/proposal/validators/proposal-quality-validators.js +16 -6
- package/dist/commands/proposal/validators/proposal-validators.js +61 -12
- package/dist/commands/proposal/validators/proposals.js +6 -8
- package/dist/commands/read/curate.js +78 -73
- package/dist/commands/read/knowledge.js +510 -13
- package/dist/commands/read/registry-search.js +2 -2
- package/dist/commands/read/remember-cli.js +84 -15
- package/dist/commands/read/search-cli.js +203 -96
- package/dist/commands/read/search.js +126 -94
- package/dist/commands/read/show.js +226 -250
- package/dist/commands/registry-cli.js +34 -60
- package/dist/commands/remember.js +18 -57
- package/dist/commands/sources/add-cli.js +104 -49
- package/dist/commands/sources/bundle-cli.js +166 -0
- package/dist/commands/sources/bundle-config-ops.js +63 -0
- package/dist/commands/sources/info.js +27 -15
- package/dist/commands/sources/init.js +30 -40
- package/dist/commands/sources/installed-stashes.js +469 -172
- package/dist/commands/sources/migration-help.js +7 -4
- package/dist/commands/sources/schema-repair.js +10 -9
- package/dist/commands/sources/self-update.js +182 -121
- package/dist/commands/sources/source-add.js +169 -178
- package/dist/commands/sources/source-clone.js +144 -41
- package/dist/commands/sources/source-manage.js +94 -59
- package/dist/commands/sources/sources-cli.js +64 -205
- package/dist/commands/sources/stash-cli.js +91 -54
- package/dist/commands/sources/stash-skeleton.js +1 -1
- package/dist/commands/tasks/tasks-cli.js +106 -104
- package/dist/commands/tasks/tasks.js +445 -262
- package/dist/commands/workflow-cli.js +232 -121
- package/dist/core/action-contributors.js +1 -1
- package/dist/core/activation-policy.js +49 -0
- package/dist/core/adapter/adapters/agent-skills-adapter.js +181 -0
- package/dist/core/adapter/adapters/akm-adapter.js +528 -0
- package/dist/core/adapter/adapters/akm-lint.js +392 -0
- package/dist/core/adapter/adapters/akm-metadata.js +387 -0
- package/dist/core/adapter/adapters/akm-task-adapter.js +149 -0
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +180 -0
- package/dist/core/adapter/adapters/claude-adapter.js +61 -0
- package/dist/core/adapter/adapters/dotenv-adapter.js +187 -0
- package/dist/core/adapter/adapters/generic-files-adapter.js +119 -0
- package/dist/core/adapter/adapters/index.js +80 -0
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +419 -0
- package/dist/core/adapter/adapters/okf-adapter.js +391 -0
- package/dist/core/adapter/adapters/opencode-adapter.js +68 -0
- package/dist/core/adapter/adapters/shared.js +286 -0
- package/dist/core/adapter/adapters/tool-dir-shared.js +217 -0
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +155 -0
- package/dist/core/adapter/bundle-adapter.js +4 -0
- package/dist/core/adapter/detect-adapter.js +17 -0
- package/dist/core/adapter/recognize-match.js +44 -0
- package/dist/core/adapter/registry.js +56 -0
- package/dist/core/adapter/types.js +4 -0
- package/dist/core/asset/akm-markdown.js +30 -0
- package/dist/core/asset/asset-placement.js +243 -0
- package/dist/core/asset/asset-ref.js +110 -79
- package/dist/core/asset/asset-serialize.js +20 -0
- package/dist/core/asset/frontmatter.js +28 -12
- package/dist/core/asset/markdown.js +40 -51
- package/dist/core/asset/resolve-ref.js +274 -0
- package/dist/core/asset/stash-meta.js +2 -2
- package/dist/core/bundle-id.js +51 -0
- package/dist/core/common.js +281 -86
- package/dist/core/config/config-io.js +42 -128
- package/dist/core/config/config-schema.js +233 -834
- package/dist/core/config/config-sources.js +162 -39
- package/dist/core/config/config-types.js +16 -11
- package/dist/core/config/config-version.js +29 -0
- package/dist/core/config/config-walker.js +126 -37
- package/dist/core/config/config.js +154 -331
- package/dist/core/config/deep-merge.js +41 -0
- package/dist/core/config/engine-semantics.js +28 -0
- package/dist/core/config/experimental.js +21 -0
- package/dist/core/config/schema/embedding.js +38 -0
- package/dist/core/config/schema/engines.js +116 -0
- package/dist/core/config/schema/experimental.js +47 -0
- package/dist/core/config/schema/feedback.js +31 -0
- package/dist/core/config/schema/improve-processes.js +389 -0
- package/dist/core/config/schema/improve.js +94 -0
- package/dist/core/config/schema/index-config.js +176 -0
- package/dist/core/config/schema/output.js +18 -0
- package/dist/core/config/schema/primitives.js +94 -0
- package/dist/core/config/schema/search.js +30 -0
- package/dist/core/config/schema/setup.js +18 -0
- package/dist/core/config/schema/sources-bundles.js +169 -0
- package/dist/core/config/schema/workflow.js +29 -0
- package/dist/core/env-secret-ref.js +155 -20
- package/dist/core/errors.js +17 -15
- package/dist/core/events-types.js +4 -0
- package/dist/core/events.js +46 -128
- package/dist/core/extra-params.js +62 -0
- package/dist/core/file-change.js +17 -0
- package/dist/core/file-lock.js +202 -57
- package/dist/core/fs-txn.js +392 -0
- package/dist/core/git-message.js +59 -0
- package/dist/core/improve-result.js +167 -0
- package/dist/core/json-schema.js +142 -0
- package/dist/core/lesson-lint.js +1 -17
- package/dist/core/logs-db.js +1 -1
- package/dist/core/maintenance-barrier.js +135 -0
- package/dist/core/migration-operation.js +44 -0
- package/dist/core/mutation-target.js +78 -0
- package/dist/core/paths.js +22 -25
- package/dist/core/platform.js +10 -0
- package/dist/core/recognition-util.js +128 -0
- package/dist/core/redaction.js +392 -0
- package/dist/core/standards/resolve-standards-context.js +36 -65
- package/dist/core/standards/resolve-stash-standards.js +2 -2
- package/dist/core/standards/resolve-type-conventions.js +5 -5
- package/dist/core/state/migrations.js +242 -11
- package/dist/core/state-db.js +98 -10
- package/dist/core/structured.js +1 -1
- package/dist/core/subprocess.js +303 -0
- package/dist/core/text-truncation.js +9 -5
- package/dist/core/time.js +20 -0
- package/dist/core/type-presentation.js +130 -0
- package/dist/core/warn.js +0 -3
- package/dist/core/write-source.js +834 -118
- package/dist/indexer/bundle-identity-guard.js +92 -0
- package/dist/indexer/db/graph-db.js +1 -25
- package/dist/indexer/db/llm-cache.js +1 -1
- package/dist/indexer/ensure-index.js +30 -9
- package/dist/indexer/graph/graph-boost.js +9 -30
- package/dist/indexer/graph/graph-extraction.js +41 -27
- package/dist/indexer/graph/graph-types.js +4 -0
- package/dist/indexer/index-writer-lock.js +93 -49
- package/dist/indexer/index-written-assets.js +100 -53
- package/dist/indexer/indexer.js +746 -329
- package/dist/indexer/init.js +18 -25
- package/dist/indexer/installations.js +142 -0
- package/dist/indexer/passes/dir-staleness.js +18 -10
- package/dist/indexer/passes/memory-inference.js +25 -15
- package/dist/indexer/passes/metadata.js +412 -243
- package/dist/indexer/scan/doc-to-entry.js +160 -0
- package/dist/indexer/scan/drain-dir.js +134 -0
- package/dist/indexer/search/db-search.js +292 -108
- package/dist/indexer/search/fts-query.js +64 -0
- package/dist/indexer/search/ranking-contributors.js +145 -25
- package/dist/indexer/search/ranking-types.js +4 -0
- package/dist/indexer/search/ranking.js +28 -71
- package/dist/indexer/search/search-attribution.js +67 -0
- package/dist/indexer/search/search-fields.js +18 -3
- package/dist/indexer/search/search-hit-enrichers.js +30 -40
- package/dist/indexer/search/search-source.js +157 -111
- package/dist/indexer/search/semantic-status.js +4 -1
- package/dist/indexer/usage/usage-events.js +10 -30
- package/dist/indexer/walk/file-context.js +3 -45
- package/dist/indexer/walk/matchers.js +42 -34
- package/dist/indexer/walk/path-resolver.js +11 -5
- package/dist/indexer/walk/walker.js +42 -14
- package/dist/integrations/agent/builder-shared.js +7 -0
- package/dist/integrations/agent/builders.js +5 -56
- package/dist/integrations/agent/config.js +3 -143
- package/dist/integrations/agent/detect.js +17 -2
- package/dist/integrations/agent/engine-resolution.js +231 -0
- package/dist/integrations/agent/index.js +1 -2
- package/dist/integrations/agent/model-aliases.js +16 -2
- package/dist/integrations/agent/profiles.js +36 -62
- package/dist/integrations/agent/prompts.js +46 -18
- package/dist/integrations/agent/runner-dispatch.js +93 -4
- package/dist/integrations/agent/runner.js +76 -208
- package/dist/integrations/agent/spawn.js +88 -196
- package/dist/integrations/harnesses/aider/agent-builder.js +114 -0
- package/dist/integrations/harnesses/aider/index.js +48 -0
- package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
- package/dist/integrations/harnesses/amazonq/agent-builder.js +147 -0
- package/dist/integrations/harnesses/amazonq/index.js +45 -0
- package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
- package/dist/integrations/harnesses/claude/agent-builder.js +46 -8
- package/dist/integrations/harnesses/claude/config-import.js +1 -3
- package/dist/integrations/harnesses/claude/index.js +24 -35
- package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
- package/dist/integrations/harnesses/claude/session-log.js +27 -75
- package/dist/integrations/harnesses/codex/agent-builder.js +138 -0
- package/dist/integrations/harnesses/codex/index.js +52 -0
- package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
- package/dist/integrations/harnesses/copilot/agent-builder.js +122 -0
- package/dist/integrations/harnesses/copilot/index.js +48 -0
- package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
- package/dist/integrations/harnesses/gemini/agent-builder.js +120 -0
- package/dist/integrations/harnesses/gemini/index.js +48 -0
- package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
- package/dist/integrations/harnesses/ids.js +24 -0
- package/dist/integrations/harnesses/index.js +54 -34
- package/dist/integrations/harnesses/opencode/agent-builder.js +23 -5
- package/dist/integrations/harnesses/opencode/config-import.js +1 -3
- package/dist/integrations/harnesses/opencode/index.js +14 -32
- package/dist/integrations/harnesses/opencode/session-log.js +67 -125
- package/dist/integrations/harnesses/opencode-sdk/harness.js +51 -0
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +681 -108
- package/dist/integrations/harnesses/openhands/agent-builder.js +128 -0
- package/dist/integrations/harnesses/openhands/index.js +48 -0
- package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
- package/dist/integrations/harnesses/pi/agent-builder.js +97 -0
- package/dist/integrations/harnesses/pi/index.js +45 -0
- package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
- package/dist/integrations/harnesses/shared.js +17 -0
- package/dist/integrations/harnesses/types.js +43 -32
- package/dist/integrations/lockfile.js +211 -24
- package/dist/integrations/session-logs/index.js +36 -39
- package/dist/integrations/session-logs/provider-base.js +113 -0
- package/dist/llm/client.js +182 -110
- package/dist/llm/embedders/deterministic.js +2 -2
- package/dist/llm/embedders/remote.js +21 -9
- package/dist/llm/feature-gate.js +17 -57
- package/dist/llm/graph-extract.js +12 -13
- package/dist/llm/index-passes.js +8 -42
- package/dist/llm/memory-infer.js +144 -1
- package/dist/llm/metadata-enhance.js +45 -30
- package/dist/llm/structured-call.js +16 -8
- package/dist/llm/usage-persist.js +30 -5
- package/dist/llm/usage-telemetry.js +59 -6
- package/dist/output/cli-hints.js +1 -2
- package/dist/output/command-registry.js +27 -0
- package/dist/output/context.js +22 -7
- package/dist/output/format-exempt.js +80 -0
- package/dist/output/generic-render.js +251 -0
- package/dist/output/html-render.js +11 -16
- package/dist/output/render-registry.js +57 -0
- package/dist/output/renderers.js +14 -279
- package/dist/output/shapes/curate.js +10 -1
- package/dist/output/shapes/events.js +12 -7
- package/dist/output/shapes/helpers.js +58 -84
- package/dist/output/shapes/passthrough.js +11 -39
- package/dist/output/shapes/proposal/producer.js +15 -7
- package/dist/output/shapes/registry.js +12 -6
- package/dist/output/shapes.js +0 -9
- package/dist/output/text/{init.js → bundle-create.js} +3 -1
- package/dist/output/text/bundle-show.js +7 -0
- package/dist/output/text/command-format.js +562 -0
- package/dist/output/text/env.js +1 -3
- package/dist/output/text/events.js +8 -7
- package/dist/output/text/helpers.js +15 -1164
- package/dist/output/text/proposal/producer.js +4 -2
- package/dist/output/text/proposal-format.js +202 -0
- package/dist/output/text/registry-commands.js +1 -2
- package/dist/output/text/registry.js +12 -6
- package/dist/output/text/show-directives.js +117 -0
- package/dist/output/text/show-format.js +103 -0
- package/dist/output/text/sync.js +5 -0
- package/dist/output/text/workflow-format.js +332 -0
- package/dist/output/text/workflow.js +3 -2
- package/dist/output/text.js +10 -19
- package/dist/registry/factory.js +4 -6
- package/dist/registry/origin-resolve.js +16 -27
- package/dist/registry/providers/skills-sh.js +3 -3
- package/dist/registry/providers/static-index.js +15 -25
- package/dist/registry/resolve.js +43 -94
- package/dist/registry/semver.js +43 -0
- package/dist/runtime.js +81 -12
- package/dist/scripts/akm-migrate.js +35529 -0
- package/dist/setup/detect.js +5 -7
- package/dist/setup/detected-engines.js +136 -0
- package/dist/setup/engine-config.js +100 -0
- package/dist/setup/registry-stash-loader.js +3 -3
- package/dist/setup/semantic-assets.js +12 -9
- package/dist/setup/setup.js +444 -208
- package/dist/setup/steps/connection-shared.js +120 -0
- package/dist/setup/steps/connection.js +108 -305
- package/dist/setup/steps/platforms.js +13 -12
- package/dist/setup/steps/semantic.js +15 -3
- package/dist/setup/steps/sources.js +21 -15
- package/dist/setup/steps/stashdir.js +6 -4
- package/dist/setup/steps/tasks.js +236 -119
- package/dist/setup/steps.js +3 -2
- package/dist/sources/freshness.js +39 -0
- package/dist/sources/provider-factory.js +11 -17
- package/dist/sources/providers/filesystem.js +2 -3
- package/dist/sources/providers/git-install.js +278 -34
- package/dist/sources/providers/git-provider.js +54 -56
- package/dist/sources/providers/git-stash.js +420 -91
- package/dist/sources/providers/git.js +2 -2
- package/dist/sources/providers/npm.js +16 -19
- package/dist/sources/providers/provider-utils.js +47 -22
- package/dist/sources/providers/sync-from-ref.js +3 -9
- package/dist/sources/providers/website.js +2 -2
- package/dist/sources/resolve.js +11 -10
- package/dist/sources/snapshot-fetchers/types.js +4 -0
- package/dist/sources/{website-ingest.js → snapshot-fetchers/website-ingest.js} +110 -41
- package/dist/storage/database.js +60 -4
- package/dist/storage/engines/sqlite-migrations.js +156 -5
- package/dist/storage/locations.js +1 -2
- package/dist/storage/repositories/canaries-repository.js +1 -1
- package/dist/storage/repositories/events-repository.js +51 -11
- package/dist/storage/repositories/improve-runs-repository.js +6 -32
- package/dist/storage/repositories/index-connection.js +79 -0
- package/dist/storage/repositories/index-db.js +4 -3
- package/dist/storage/repositories/index-entries-repository.js +863 -0
- package/dist/{indexer/db/entry-mapper.js → storage/repositories/index-entry-mapper.js} +19 -2
- package/dist/storage/repositories/index-entry-types.js +4 -0
- package/dist/storage/repositories/index-fts-repository.js +167 -0
- package/dist/storage/repositories/index-llm-cache-repository.js +108 -0
- package/dist/storage/repositories/index-meta-repository.js +49 -0
- package/dist/{indexer/db/schema.js → storage/repositories/index-schema.js} +226 -100
- package/dist/storage/repositories/index-sql.js +12 -0
- package/dist/storage/repositories/index-utility-repository.js +356 -0
- package/dist/storage/repositories/index-vec-repository.js +250 -0
- package/dist/storage/repositories/outcome-repository.js +119 -0
- package/dist/storage/repositories/proposals-repository.js +317 -75
- package/dist/storage/repositories/registry-cache.js +1 -1
- package/dist/storage/repositories/salience-repository.js +172 -0
- package/dist/storage/repositories/task-history-repository.js +110 -3
- package/dist/storage/repositories/workflow-runs-repository.js +240 -19
- package/dist/tasks/backends/cron.js +169 -46
- package/dist/tasks/backends/exec-utils.js +76 -3
- package/dist/tasks/backends/index.js +6 -9
- package/dist/tasks/backends/launchd.js +292 -55
- package/dist/tasks/backends/schtasks.js +557 -70
- package/dist/tasks/backends/types.js +4 -0
- package/dist/tasks/command-executable.js +93 -0
- package/dist/tasks/embedded.js +56 -38
- package/dist/tasks/parser.js +156 -64
- package/dist/tasks/resolve-akm-bin.js +144 -51
- package/dist/tasks/runner.js +377 -209
- package/dist/tasks/schedule.js +108 -19
- package/dist/tasks/scheduler-invocation.js +296 -0
- package/dist/tasks/schema.js +1 -1
- package/dist/tasks/task-id.js +35 -0
- package/dist/tasks/validator.js +30 -16
- package/dist/text-import-hook.mjs +1 -1
- package/dist/workflows/authoring/authoring.js +104 -43
- package/dist/workflows/authoring/scope-key.js +1 -1
- package/dist/workflows/cli.js +0 -16
- package/dist/workflows/concurrency-policy.js +15 -0
- package/dist/workflows/exec/brief.js +450 -0
- package/dist/workflows/exec/frozen-judge.js +47 -0
- package/dist/workflows/exec/native-executor.js +1038 -0
- package/dist/workflows/exec/param-secrets.js +115 -0
- package/dist/workflows/exec/report.js +1460 -0
- package/dist/workflows/exec/run-workflow.js +602 -0
- package/dist/workflows/exec/scheduler.js +71 -0
- package/dist/workflows/exec/step-work.js +1190 -0
- package/dist/workflows/exec/unit-writer.js +23 -0
- package/dist/workflows/exec/workflow-engine-gate.js +67 -0
- package/dist/workflows/exec/worktree.js +171 -0
- package/dist/workflows/ir/compile.js +246 -0
- package/dist/workflows/ir/freeze.js +233 -0
- package/dist/workflows/ir/params.js +54 -0
- package/dist/workflows/ir/plan-hash.js +68 -0
- package/dist/workflows/ir/schema.js +540 -0
- package/dist/workflows/parser.js +878 -304
- package/dist/workflows/program/expressions.js +181 -0
- package/dist/workflows/program/schema.js +51 -0
- package/dist/workflows/renderer.js +100 -45
- package/dist/workflows/resource-limits.js +22 -0
- package/dist/workflows/runtime/agent-identity.js +59 -14
- package/dist/workflows/runtime/checkin.js +1 -1
- package/dist/workflows/runtime/plan-classifier.js +131 -0
- package/dist/workflows/runtime/runs.js +376 -119
- package/dist/workflows/runtime/unit-checkin.js +45 -0
- package/dist/workflows/runtime/unit-phases.js +20 -0
- package/dist/workflows/runtime/workflow-asset-loader.js +241 -40
- package/dist/workflows/schema.js +1 -11
- package/dist/workflows/validate-summary.js +2 -3
- package/dist/workflows/validator.js +52 -30
- package/docs/README.md +42 -78
- package/docs/migration/README.md +8 -0
- package/docs/migration/release-notes/0.6.0.md +1 -1
- package/docs/migration/release-notes/0.7.0.md +9 -8
- package/docs/migration/release-notes/0.9.0.md +158 -14
- package/docs/migration/v0.7-to-v0.8.md +46 -47
- package/docs/migration/v0.8-to-v0.9.md +844 -0
- package/docs/reference/README.md +12 -0
- package/docs/reference/data-and-telemetry.md +333 -0
- package/package.json +21 -17
- package/schemas/akm-asset-envelope.json +93 -0
- package/schemas/akm-config.json +4636 -0
- package/schemas/akm-task.json +87 -0
- package/schemas/akm-workflow.json +373 -0
- package/dist/akm-migrate-storage +0 -38
- package/dist/assets/help/help-accept.md +0 -12
- package/dist/assets/help/help-improve.md +0 -84
- package/dist/assets/help/help-proposals.md +0 -17
- package/dist/assets/help/help-propose.md +0 -17
- package/dist/assets/help/help-reject.md +0 -11
- package/dist/assets/profiles/frequent.json +0 -13
- package/dist/assets/profiles/recombine-only.json +0 -21
- package/dist/assets/profiles/reflect-distill.json +0 -30
- package/dist/assets/profiles/synthesize.json +0 -15
- package/dist/assets/prompts/procedural-system.md +0 -44
- package/dist/assets/prompts/recombine-system.md +0 -40
- package/dist/assets/prompts/staleness-detect-system.md +0 -6
- package/dist/assets/tasks/core/backup.yml +0 -4
- package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
- package/dist/assets/templates/html/default.html +0 -78
- package/dist/assets/templates/html/vendor/echarts.min.js +0 -45
- package/dist/assets/wiki/index-template.md +0 -12
- package/dist/assets/wiki/ingest-workflow-template.md +0 -83
- package/dist/assets/wiki/log-template.md +0 -8
- package/dist/assets/wiki/schema-template.md +0 -61
- package/dist/cli/config-migrate.js +0 -150
- package/dist/cli/config-validate.js +0 -39
- package/dist/commands/graph/graph-cli.js +0 -124
- package/dist/commands/graph/graph.js +0 -487
- package/dist/commands/improve/calibration.js +0 -161
- package/dist/commands/improve/dedup.js +0 -482
- package/dist/commands/improve/extract-watch.js +0 -140
- package/dist/commands/improve/hot-probation.js +0 -45
- package/dist/commands/improve/improve-auto-accept.js +0 -276
- package/dist/commands/improve/improve-profiles.js +0 -168
- package/dist/commands/improve/procedural.js +0 -398
- package/dist/commands/improve/recombine.js +0 -818
- package/dist/commands/improve/schema-similarity-gate.js +0 -168
- package/dist/commands/lint/agent-linter.js +0 -44
- package/dist/commands/lint/command-linter.js +0 -44
- package/dist/commands/lint/default-linter.js +0 -16
- package/dist/commands/lint/fact-linter.js +0 -39
- package/dist/commands/lint/knowledge-linter.js +0 -16
- package/dist/commands/lint/memory-linter.js +0 -61
- package/dist/commands/lint/registry.js +0 -41
- package/dist/commands/lint/skill-linter.js +0 -45
- package/dist/commands/lint/task-linter.js +0 -50
- package/dist/commands/lint/workflow-linter.js +0 -81
- package/dist/commands/proposal/legacy-import.js +0 -115
- package/dist/commands/sources/history.js +0 -196
- package/dist/commands/tasks/default-tasks.js +0 -186
- package/dist/commands/wiki-cli.js +0 -292
- package/dist/core/asset/asset-registry.js +0 -76
- package/dist/core/asset/asset-spec.js +0 -259
- package/dist/core/config/config-migration.js +0 -602
- package/dist/core/deep-merge.js +0 -38
- package/dist/core/eval/rank-metrics.js +0 -113
- package/dist/core/ripgrep/install.js +0 -163
- package/dist/core/ripgrep/resolve.js +0 -81
- package/dist/indexer/db/db.js +0 -1413
- package/dist/indexer/manifest.js +0 -170
- package/dist/indexer/passes/metadata-contributors.js +0 -31
- package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -94
- package/dist/integrations/harnesses/opencode-sdk/index.js +0 -49
- package/dist/llm/call-ai.js +0 -62
- package/dist/llm/memory-infer-impl.js +0 -138
- package/dist/output/shapes/distill.js +0 -14
- package/dist/output/shapes/history.js +0 -11
- package/dist/output/text/distill.js +0 -6
- package/dist/output/text/enable-disable.js +0 -8
- package/dist/output/text/history.js +0 -6
- package/dist/output/text/wiki.js +0 -16
- package/dist/registry/build-index.js +0 -386
- package/dist/scripts/migrate-storage.js +0 -19108
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +0 -9411
- package/dist/scripts/migrations/v16-to-v17.js +0 -141
- package/dist/setup/legacy-config.js +0 -106
- package/dist/storage/repositories/consolidation-repository.js +0 -38
- package/dist/storage/repositories/recombine-repository.js +0 -213
- package/dist/wiki/wiki-templates.js +0 -15
- package/dist/wiki/wiki.js +0 -1012
- package/dist/workflows/db.js +0 -215
- package/docs/data-and-telemetry.md +0 -226
- /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/registry.js +0 -0
- /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/youtube.js +0 -0
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
/**
|
|
5
|
+
* `index.db` utility-score + retrieval-count repository (MemRL signals).
|
|
6
|
+
*
|
|
7
|
+
* Owns the raw SQL for `utility_scores` / `utility_scores_scoped` and the
|
|
8
|
+
* retrieval-frequency counting over `usage_events`. The bounded-step EMA policy
|
|
9
|
+
* itself lives in `indexer/feedback/utility-policy`; this repo only reads/writes.
|
|
10
|
+
*/
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
import { conceptIdFromTypeName, parseRefInput } from "../../core/asset/resolve-ref.js";
|
|
13
|
+
import { computeNextUtility } from "../../indexer/feedback/utility-policy.js";
|
|
14
|
+
import { SQLITE_CHUNK_SIZE } from "./index-sql.js";
|
|
15
|
+
/**
|
|
16
|
+
* Get the utility score for an entry, or undefined if none exists.
|
|
17
|
+
*/
|
|
18
|
+
export function getUtilityScore(db, entryId) {
|
|
19
|
+
const row = db
|
|
20
|
+
.prepare("SELECT entry_id, utility, show_count, search_count, select_rate, last_used_at, updated_at FROM utility_scores WHERE entry_id = ?")
|
|
21
|
+
.get(entryId);
|
|
22
|
+
if (!row)
|
|
23
|
+
return undefined;
|
|
24
|
+
return {
|
|
25
|
+
entryId: row.entry_id,
|
|
26
|
+
utility: row.utility,
|
|
27
|
+
showCount: row.show_count,
|
|
28
|
+
searchCount: row.search_count,
|
|
29
|
+
selectRate: row.select_rate,
|
|
30
|
+
lastUsedAt: row.last_used_at ?? undefined,
|
|
31
|
+
updatedAt: row.updated_at,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Batch-load utility scores for multiple entry IDs in a single query.
|
|
36
|
+
* Returns a `{ global, scoped }` pair, both Maps keyed by entry_id.
|
|
37
|
+
*
|
|
38
|
+
* When `scopeKey` is provided a second query runs against
|
|
39
|
+
* `utility_scores_scoped` and the result is returned as `scoped`.
|
|
40
|
+
* Both maps are always present; `scoped` is empty when `scopeKey` is absent.
|
|
41
|
+
*/
|
|
42
|
+
export function getUtilityScoresByIds(db, ids, scopeKey) {
|
|
43
|
+
const global = new Map();
|
|
44
|
+
const scoped = new Map();
|
|
45
|
+
if (ids.length === 0)
|
|
46
|
+
return { global, scoped };
|
|
47
|
+
// Process in chunks to stay within SQLITE_MAX_VARIABLE_NUMBER
|
|
48
|
+
for (let i = 0; i < ids.length; i += SQLITE_CHUNK_SIZE) {
|
|
49
|
+
const chunk = ids.slice(i, i + SQLITE_CHUNK_SIZE);
|
|
50
|
+
const placeholders = chunk.map(() => "?").join(",");
|
|
51
|
+
const rows = db
|
|
52
|
+
.prepare(`SELECT entry_id, utility, show_count, search_count, select_rate, last_used_at, updated_at FROM utility_scores WHERE entry_id IN (${placeholders})`)
|
|
53
|
+
.all(...chunk);
|
|
54
|
+
for (const row of rows) {
|
|
55
|
+
global.set(row.entry_id, {
|
|
56
|
+
entryId: row.entry_id,
|
|
57
|
+
utility: row.utility,
|
|
58
|
+
showCount: row.show_count,
|
|
59
|
+
searchCount: row.search_count,
|
|
60
|
+
selectRate: row.select_rate,
|
|
61
|
+
lastUsedAt: row.last_used_at ?? undefined,
|
|
62
|
+
updatedAt: row.updated_at,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
if (scopeKey) {
|
|
66
|
+
const scopedRows = db
|
|
67
|
+
.prepare(`SELECT entry_id, scope_key, utility, last_used_at FROM utility_scores_scoped WHERE scope_key = ? AND entry_id IN (${placeholders})`)
|
|
68
|
+
.all(scopeKey, ...chunk);
|
|
69
|
+
for (const row of scopedRows) {
|
|
70
|
+
scoped.set(row.entry_id, {
|
|
71
|
+
entryId: row.entry_id,
|
|
72
|
+
scopeKey: row.scope_key,
|
|
73
|
+
utility: row.utility,
|
|
74
|
+
lastUsedAt: row.last_used_at,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return { global, scoped };
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Insert or update a utility score for an entry.
|
|
83
|
+
*/
|
|
84
|
+
export function upsertUtilityScore(db, entryId, data) {
|
|
85
|
+
// Pre-flight FK guard (mirrors `upsertEmbedding`): when an entry is
|
|
86
|
+
// deleted between when its id is aggregated from usage_events and when
|
|
87
|
+
// this INSERT runs, the FK constraint fails and rolls back the entire
|
|
88
|
+
// finalize transaction. A cheap SELECT here turns the race into a
|
|
89
|
+
// clean skip. Returns false when the entry no longer exists.
|
|
90
|
+
const exists = db.prepare("SELECT 1 FROM entries WHERE id = ?").get(entryId);
|
|
91
|
+
if (!exists)
|
|
92
|
+
return false;
|
|
93
|
+
db.prepare(`
|
|
94
|
+
INSERT INTO utility_scores (entry_id, utility, show_count, search_count, select_rate, last_used_at, updated_at)
|
|
95
|
+
VALUES (?, ?, ?, ?, ?, ?, datetime('now'))
|
|
96
|
+
ON CONFLICT(entry_id) DO UPDATE SET
|
|
97
|
+
utility = excluded.utility,
|
|
98
|
+
show_count = excluded.show_count,
|
|
99
|
+
search_count = excluded.search_count,
|
|
100
|
+
select_rate = excluded.select_rate,
|
|
101
|
+
last_used_at = excluded.last_used_at,
|
|
102
|
+
updated_at = datetime('now')
|
|
103
|
+
`).run(entryId, data.utility, data.showCount, data.searchCount, data.selectRate, data.lastUsedAt ?? null);
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Reduce a stored ref to its bare form, dropping any `origin//` / `bundle//`
|
|
108
|
+
* prefix, matching the counting SQL's `substr` after the first `//`.
|
|
109
|
+
*/
|
|
110
|
+
function bareRef(ref) {
|
|
111
|
+
const boundary = ref.indexOf("//");
|
|
112
|
+
return boundary >= 0 ? ref.slice(boundary + 2) : ref;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* The bare-form (conceptId) candidate a caller ref matches a stored
|
|
116
|
+
* `usage_events` row under. Post-Chunk-8 every stored row is conceptId-spelled
|
|
117
|
+
* (the one-time §11.4 re-key ran at the migration cutover), so only the conceptId
|
|
118
|
+
* candidate remains — the legacy `type:name` sibling is retired.
|
|
119
|
+
*/
|
|
120
|
+
function bareRefCandidates(ref) {
|
|
121
|
+
const bare = bareRef(ref.trim());
|
|
122
|
+
const parsed = parseRefInput(bare);
|
|
123
|
+
return [conceptIdFromTypeName(parsed.type, parsed.name)];
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Count retrieval events for the given entry refs.
|
|
127
|
+
*
|
|
128
|
+
* Counts `search`, `show`, and `curate` usage events. Returns a
|
|
129
|
+
* Map<inputRef, count> keyed by the *input* ref strings (only those with at
|
|
130
|
+
* least one matching event appear). Used by the improve loop's
|
|
131
|
+
* proactive-maintenance selector to rank zero-feedback assets by retrieval
|
|
132
|
+
* frequency.
|
|
133
|
+
*
|
|
134
|
+
* Source-scoped callers require either an event linked to an entry in the
|
|
135
|
+
* selected stash root or a detached ref qualified with the selected source
|
|
136
|
+
* name.
|
|
137
|
+
*
|
|
138
|
+
* `curate` events are included: their per-item rows are written with
|
|
139
|
+
* entry_ref populated (see logCurateEvent), so curation is a real retrieval
|
|
140
|
+
* signal here. Summary-only rows with a NULL entry_ref contribute nothing.
|
|
141
|
+
*
|
|
142
|
+
* Machine/eval/unattributed/extension events are excluded: this count feeds
|
|
143
|
+
* salience and ranking, so only explicitly attributed `user` traffic counts.
|
|
144
|
+
*/
|
|
145
|
+
export function getRetrievalCounts(indexDb, stateDb, refs, options = {}) {
|
|
146
|
+
if (refs.length === 0)
|
|
147
|
+
return new Map();
|
|
148
|
+
if (options.sourceName || options.stashDir) {
|
|
149
|
+
return getSourceScopedRetrievalCounts(indexDb, stateDb, refs, options);
|
|
150
|
+
}
|
|
151
|
+
// Map each conceptId back to the input refs that produced it so DB results
|
|
152
|
+
// can be re-keyed onto the caller's strings.
|
|
153
|
+
const bareToInputs = new Map();
|
|
154
|
+
for (const ref of refs) {
|
|
155
|
+
for (const bare of bareRefCandidates(ref)) {
|
|
156
|
+
const existing = bareToInputs.get(bare);
|
|
157
|
+
if (existing)
|
|
158
|
+
existing.push(ref);
|
|
159
|
+
else
|
|
160
|
+
bareToInputs.set(bare, [ref]);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const bareForms = [...bareToInputs.keys()];
|
|
164
|
+
// Accumulate counts per bare form across chunks before re-keying.
|
|
165
|
+
const countsByBare = new Map();
|
|
166
|
+
// Chunk to stay within SQLITE_MAX_VARIABLE_NUMBER (same pattern as getUtilityScoresByIds).
|
|
167
|
+
for (let i = 0; i < bareForms.length; i += SQLITE_CHUNK_SIZE) {
|
|
168
|
+
const chunk = bareForms.slice(i, i + SQLITE_CHUNK_SIZE);
|
|
169
|
+
const placeholders = chunk.map(() => "?").join(", ");
|
|
170
|
+
// Normalize the stored entry_ref to its bare form inside SQL by stripping
|
|
171
|
+
// everything up to and including the last `//` separator. SQLite has no
|
|
172
|
+
// rfind, but stored origins never themselves contain `//`, so a stash ref
|
|
173
|
+
// has exactly one `//` and `substr(... instr ...)` is exact; bare refs have
|
|
174
|
+
// no `//` and are excluded above.
|
|
175
|
+
// usage_events lives in state.db (Chunk-8 WI-8.3) — this global count needs
|
|
176
|
+
// no entries join, so it reads state.db directly.
|
|
177
|
+
const rows = stateDb
|
|
178
|
+
.prepare(`SELECT
|
|
179
|
+
CASE
|
|
180
|
+
WHEN instr(entry_ref, '//') > 0
|
|
181
|
+
THEN substr(entry_ref, instr(entry_ref, '//') + 2)
|
|
182
|
+
ELSE entry_ref
|
|
183
|
+
END AS bare_ref,
|
|
184
|
+
COUNT(*) AS cnt
|
|
185
|
+
FROM usage_events
|
|
186
|
+
WHERE event_type IN ('search','show','curate')
|
|
187
|
+
AND entry_ref IS NOT NULL
|
|
188
|
+
AND instr(entry_ref, '//') > 0
|
|
189
|
+
AND source = 'user'
|
|
190
|
+
AND CASE
|
|
191
|
+
WHEN instr(entry_ref, '//') > 0
|
|
192
|
+
THEN substr(entry_ref, instr(entry_ref, '//') + 2)
|
|
193
|
+
ELSE entry_ref
|
|
194
|
+
END IN (${placeholders})
|
|
195
|
+
GROUP BY bare_ref`)
|
|
196
|
+
.all(...chunk);
|
|
197
|
+
for (const r of rows) {
|
|
198
|
+
countsByBare.set(r.bare_ref, (countsByBare.get(r.bare_ref) ?? 0) + r.cnt);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Re-key bare-form counts onto every input ref that maps to that bare form.
|
|
202
|
+
const result = new Map();
|
|
203
|
+
for (const [bare, count] of countsByBare) {
|
|
204
|
+
for (const input of bareToInputs.get(bare) ?? []) {
|
|
205
|
+
result.set(input, count);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return result;
|
|
209
|
+
}
|
|
210
|
+
function getSourceScopedRetrievalCounts(indexDb, stateDb, refs, options) {
|
|
211
|
+
const bareToInputs = new Map();
|
|
212
|
+
for (const ref of refs) {
|
|
213
|
+
for (const bare of bareRefCandidates(ref)) {
|
|
214
|
+
const inputs = bareToInputs.get(bare);
|
|
215
|
+
if (inputs)
|
|
216
|
+
inputs.push(ref);
|
|
217
|
+
else
|
|
218
|
+
bareToInputs.set(bare, [ref]);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// Cross-DB (Chunk-8 WI-8.3): usage_events rows come from state.db; the
|
|
222
|
+
// per-row `stash_dir` (formerly a LEFT JOIN on entries) is resolved from
|
|
223
|
+
// index.db by entry_id. Read the usage rows first, then batch-look-up the
|
|
224
|
+
// stash_dir for their entry_ids and join in JS.
|
|
225
|
+
const entryStashDir = indexDb.prepare("SELECT stash_dir FROM entries WHERE id = ?");
|
|
226
|
+
const stashDirFor = (entryId) => {
|
|
227
|
+
if (entryId === null)
|
|
228
|
+
return null;
|
|
229
|
+
const row = entryStashDir.get(entryId);
|
|
230
|
+
return row?.stash_dir ?? null;
|
|
231
|
+
};
|
|
232
|
+
const countsByBare = new Map();
|
|
233
|
+
const bareForms = [...bareToInputs.keys()];
|
|
234
|
+
const selectedRoot = options.stashDir ? path.resolve(options.stashDir) : undefined;
|
|
235
|
+
for (let i = 0; i < bareForms.length; i += SQLITE_CHUNK_SIZE) {
|
|
236
|
+
const chunk = bareForms.slice(i, i + SQLITE_CHUNK_SIZE);
|
|
237
|
+
const placeholders = chunk.map(() => "?").join(", ");
|
|
238
|
+
const rawRows = stateDb
|
|
239
|
+
.prepare(`SELECT ue.entry_ref, ue.entry_id
|
|
240
|
+
FROM usage_events ue
|
|
241
|
+
WHERE ue.event_type IN ('search','show','curate')
|
|
242
|
+
AND ue.entry_ref IS NOT NULL
|
|
243
|
+
AND instr(ue.entry_ref, '//') > 0
|
|
244
|
+
AND ue.source = 'user'
|
|
245
|
+
AND CASE
|
|
246
|
+
WHEN instr(ue.entry_ref, '//') > 0
|
|
247
|
+
THEN substr(ue.entry_ref, instr(ue.entry_ref, '//') + 2)
|
|
248
|
+
ELSE ue.entry_ref
|
|
249
|
+
END IN (${placeholders})`)
|
|
250
|
+
.all(...chunk);
|
|
251
|
+
const rows = rawRows.map((r) => ({ ...r, stash_dir: stashDirFor(r.entry_id) }));
|
|
252
|
+
for (const row of rows) {
|
|
253
|
+
const bare = bareRef(row.entry_ref);
|
|
254
|
+
const linkedToSelectedRoot = row.entry_id !== null && selectedRoot !== undefined && row.stash_dir !== null
|
|
255
|
+
? path.resolve(row.stash_dir) === selectedRoot
|
|
256
|
+
: false;
|
|
257
|
+
const detached = row.entry_id === null || selectedRoot === undefined;
|
|
258
|
+
const qualifiedForSource = detached && options.sourceName !== undefined && row.entry_ref === `${options.sourceName}//${bare}`;
|
|
259
|
+
if (!linkedToSelectedRoot && !qualifiedForSource)
|
|
260
|
+
continue;
|
|
261
|
+
countsByBare.set(bare, (countsByBare.get(bare) ?? 0) + 1);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
const result = new Map();
|
|
265
|
+
for (const [bare, count] of countsByBare) {
|
|
266
|
+
for (const input of bareToInputs.get(bare) ?? [])
|
|
267
|
+
result.set(input, count);
|
|
268
|
+
}
|
|
269
|
+
return result;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Apply a MemRL reward signal to a batch of entries via exponential moving
|
|
273
|
+
* average (EMA): next = clamp(current + lr * (reward - current), 0, 1).
|
|
274
|
+
*
|
|
275
|
+
* Wrapped in a single transaction so all bumps succeed or fail together.
|
|
276
|
+
* The indexer (`akm index`) will overwrite these values at next reindex run;
|
|
277
|
+
* bumps are intentionally temporary hints between index runs, not permanent
|
|
278
|
+
* overrides.
|
|
279
|
+
*
|
|
280
|
+
* When `scopeKey` is provided, also writes a scoped bump to
|
|
281
|
+
* `utility_scores_scoped` so per-project usage signals accumulate alongside
|
|
282
|
+
* the global ones. The global table is always updated regardless.
|
|
283
|
+
*/
|
|
284
|
+
export function bumpUtilityScoresBatch(db, entryIds, reward, lr = 0.1, scopeKey) {
|
|
285
|
+
if (entryIds.length === 0)
|
|
286
|
+
return;
|
|
287
|
+
db.transaction(() => {
|
|
288
|
+
const { global: scoreMap } = getUtilityScoresByIds(db, entryIds);
|
|
289
|
+
const now = new Date().toISOString();
|
|
290
|
+
const nowMs = Date.now();
|
|
291
|
+
const stmt = db.prepare(`INSERT INTO utility_scores (entry_id, utility, show_count, search_count, select_rate, last_used_at, updated_at)
|
|
292
|
+
VALUES (?, ?, 0, 0, 0, ?, ?)
|
|
293
|
+
ON CONFLICT(entry_id) DO UPDATE SET
|
|
294
|
+
utility = excluded.utility,
|
|
295
|
+
updated_at = excluded.updated_at`);
|
|
296
|
+
// Prepare scoped upsert once outside the loop when scopeKey is present.
|
|
297
|
+
const scopedStmt = scopeKey
|
|
298
|
+
? db.prepare(`INSERT INTO utility_scores_scoped (entry_id, scope_key, utility, last_used_at)
|
|
299
|
+
VALUES (?, ?, ?, ?)
|
|
300
|
+
ON CONFLICT(entry_id, scope_key) DO UPDATE SET
|
|
301
|
+
utility = excluded.utility,
|
|
302
|
+
last_used_at = excluded.last_used_at`)
|
|
303
|
+
: null;
|
|
304
|
+
for (const entryId of entryIds) {
|
|
305
|
+
const existing = scoreMap.get(entryId);
|
|
306
|
+
const current = existing?.utility ?? 0;
|
|
307
|
+
const next = Math.max(0, Math.min(1, current + lr * (reward - current)));
|
|
308
|
+
stmt.run(entryId, next, now, now);
|
|
309
|
+
if (scopedStmt && scopeKey) {
|
|
310
|
+
// Retrieve the current scoped utility so we can apply the same EMA.
|
|
311
|
+
const scopedCurrent = getScopedUtility(db, entryId, scopeKey);
|
|
312
|
+
const scopedNext = Math.max(0, Math.min(1, scopedCurrent + lr * (reward - scopedCurrent)));
|
|
313
|
+
scopedStmt.run(entryId, scopeKey, scopedNext, nowMs);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
})();
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Return the current utility value for a single (entry_id, scope_key) pair.
|
|
320
|
+
* Returns 0 when no row exists yet.
|
|
321
|
+
*/
|
|
322
|
+
function getScopedUtility(db, entryId, scopeKey) {
|
|
323
|
+
const row = db
|
|
324
|
+
.prepare("SELECT utility FROM utility_scores_scoped WHERE entry_id = ? AND scope_key = ?")
|
|
325
|
+
.get(entryId, scopeKey);
|
|
326
|
+
return row?.utility ?? 0;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Apply accumulated feedback counts to the utility score of an entry, persisting
|
|
330
|
+
* the result. The bounded-step EMA policy itself (MemRL, F-5 / #386,
|
|
331
|
+
* arXiv:2601.03192) lives in {@link computeNextUtility} (feedback/utility-policy);
|
|
332
|
+
* this function only reads the current utility, applies the policy, and writes
|
|
333
|
+
* the new value.
|
|
334
|
+
*
|
|
335
|
+
* A new entry starts at 0.5 (neutral midpoint) before the EMA step is applied.
|
|
336
|
+
* When there is no feedback (both counts zero) the score is left untouched — no
|
|
337
|
+
* DB write. Returns a {@link FeedbackUtilityResult} so the caller can detect a
|
|
338
|
+
* previously high-utility asset crossing below the review threshold and escalate.
|
|
339
|
+
*/
|
|
340
|
+
export function applyFeedbackToUtilityScore(db, entryId, positiveCount, negativeCount) {
|
|
341
|
+
const existing = getUtilityScore(db, entryId);
|
|
342
|
+
const previousUtility = existing?.utility ?? 0.5;
|
|
343
|
+
const result = computeNextUtility(previousUtility, positiveCount, negativeCount);
|
|
344
|
+
if (positiveCount === 0 && negativeCount === 0) {
|
|
345
|
+
return result;
|
|
346
|
+
}
|
|
347
|
+
const now = new Date().toISOString();
|
|
348
|
+
db.prepare(`
|
|
349
|
+
INSERT INTO utility_scores (entry_id, utility, show_count, search_count, select_rate, last_used_at, updated_at)
|
|
350
|
+
VALUES (?, ?, 0, 0, 0, ?, ?)
|
|
351
|
+
ON CONFLICT(entry_id) DO UPDATE SET
|
|
352
|
+
utility = ?,
|
|
353
|
+
updated_at = ?
|
|
354
|
+
`).run(entryId, result.nextUtility, now, now, result.nextUtility, now);
|
|
355
|
+
return result;
|
|
356
|
+
}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
/**
|
|
5
|
+
* `index.db` vector + embedding repository.
|
|
6
|
+
*
|
|
7
|
+
* Owns the sqlite-vec extension load/availability probe, the BLOB `embeddings`
|
|
8
|
+
* table, the `entries_vec` virtual table, and the JS-cosine fallback path.
|
|
9
|
+
*/
|
|
10
|
+
import { createRequire } from "node:module";
|
|
11
|
+
import { bestEffort } from "../../core/best-effort.js";
|
|
12
|
+
import { warn } from "../../core/warn.js";
|
|
13
|
+
import { cosineSimilarity } from "../../llm/embedders/types.js";
|
|
14
|
+
import { getMeta, setMeta } from "./index-meta-repository.js";
|
|
15
|
+
// ── sqlite-vec extension ────────────────────────────────────────────────────
|
|
16
|
+
const vecStatus = new WeakMap();
|
|
17
|
+
/**
|
|
18
|
+
* Attempt to load the sqlite-vec extension into `db`, recording availability.
|
|
19
|
+
* Exported so the connection lifecycle can arm it at open time.
|
|
20
|
+
*/
|
|
21
|
+
export function loadVecExtension(db) {
|
|
22
|
+
try {
|
|
23
|
+
const esmRequire = createRequire(import.meta.url);
|
|
24
|
+
const sqliteVec = esmRequire("sqlite-vec");
|
|
25
|
+
// `db` here is the genuine underlying driver handle returned by the storage
|
|
26
|
+
// boundary (bun:sqlite on Bun, better-sqlite3 on Node) — only structurally
|
|
27
|
+
// narrowed for callers. sqlite-vec's `load()` accepts either real handle,
|
|
28
|
+
// so no raw-handle escape hatch is required.
|
|
29
|
+
sqliteVec.load(db);
|
|
30
|
+
vecStatus.set(db, true);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
vecStatus.set(db, false);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function isVecAvailable(db) {
|
|
37
|
+
return vecStatus.get(db) ?? false;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Meta key persisting whether the sqlite-vec fast-path table (`entries_vec`) is
|
|
41
|
+
* fully populated and trustworthy for this index. Set to "0" by the embedding
|
|
42
|
+
* phase when one or more vec inserts FAILED (e.g. a vec0 dimension mismatch)
|
|
43
|
+
* while their BLOB rows still wrote — so semantic search reads the complete
|
|
44
|
+
* BLOB table via the JS-cosine fallback instead of a partial/mismatched vec
|
|
45
|
+
* table. Absent (legacy indexes) and "1" both mean the fast path is trusted.
|
|
46
|
+
*/
|
|
47
|
+
const VEC_FAST_PATH_READY_META = "vecFastPathReady";
|
|
48
|
+
/** Persist whether the sqlite-vec fast path is trustworthy (see the meta doc). */
|
|
49
|
+
export function setVecFastPathReady(db, ready) {
|
|
50
|
+
setMeta(db, VEC_FAST_PATH_READY_META, ready ? "1" : "0");
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* True unless the embedding phase recorded a vec insert failure. Reflects the
|
|
54
|
+
* ACTUAL insert outcomes recorded at index time — not an inference from how many
|
|
55
|
+
* BLOB rows exist — so a degraded vec table routes search to the JS fallback
|
|
56
|
+
* rather than silently returning partial fast-path results.
|
|
57
|
+
*/
|
|
58
|
+
export function isVecFastPathReady(db) {
|
|
59
|
+
return getMeta(db, VEC_FAST_PATH_READY_META) !== "0";
|
|
60
|
+
}
|
|
61
|
+
/** Remove both vector representations for an entry whose embedding input changed. */
|
|
62
|
+
export function deleteEntryVectors(db, id) {
|
|
63
|
+
db.prepare("DELETE FROM embeddings WHERE id = ?").run(id);
|
|
64
|
+
if (isVecAvailable(db))
|
|
65
|
+
db.prepare("DELETE FROM entries_vec WHERE id = ?").run(id);
|
|
66
|
+
}
|
|
67
|
+
const VEC_DOCS_URL = "https://github.com/itlackey/akm/blob/main/docs/reference/configuration.md#sqlite-vec-extension";
|
|
68
|
+
const VEC_FALLBACK_THRESHOLD = 10_000;
|
|
69
|
+
// Per-database warning state: tracks which databases have already emitted the
|
|
70
|
+
// vec-missing warning so we don't spam on every openDatabase() call.
|
|
71
|
+
const vecInitWarnedDbs = new WeakSet();
|
|
72
|
+
/**
|
|
73
|
+
* Warn if sqlite-vec is unavailable and embedding count exceeds threshold.
|
|
74
|
+
* Called from openDatabase (once at init) and from indexer (each run).
|
|
75
|
+
*/
|
|
76
|
+
export function warnIfVecMissing(db, { once } = { once: false }) {
|
|
77
|
+
if (isVecAvailable(db))
|
|
78
|
+
return;
|
|
79
|
+
if (once && vecInitWarnedDbs.has(db))
|
|
80
|
+
return;
|
|
81
|
+
bestEffort(() => {
|
|
82
|
+
const row = db.prepare("SELECT COUNT(*) AS cnt FROM embeddings").get();
|
|
83
|
+
const count = row?.cnt ?? 0;
|
|
84
|
+
if (count >= VEC_FALLBACK_THRESHOLD) {
|
|
85
|
+
warn("Semantic search is using JS fallback for %d entries. Install sqlite-vec for faster performance.\n See: %s", count, VEC_DOCS_URL);
|
|
86
|
+
if (once)
|
|
87
|
+
vecInitWarnedDbs.add(db);
|
|
88
|
+
}
|
|
89
|
+
}, "embeddings table may not exist yet during init");
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Purge stored embeddings (BLOB rows in `embeddings`, plus the `entries_vec`
|
|
93
|
+
* virtual table) and mark the index as embedding-free. The single place that
|
|
94
|
+
* invalidates embeddings — used on a dimension change, a model/provider change,
|
|
95
|
+
* and a full rebuild.
|
|
96
|
+
*
|
|
97
|
+
* No backup: embeddings are a derived cache, fully regenerable from the markdown
|
|
98
|
+
* by the next `akm index`. (Recovery model decided 2026-06-25.)
|
|
99
|
+
*
|
|
100
|
+
* `dropVecTable: true` DROPs `entries_vec` — used on a DIMENSION change, where
|
|
101
|
+
* the vec0 table must be recreated at the new width by the caller. The default
|
|
102
|
+
* clears its rows in place (same dimension, stale vectors).
|
|
103
|
+
*/
|
|
104
|
+
export function purgeEmbeddings(db, opts) {
|
|
105
|
+
bestEffort(() => db.exec("DELETE FROM embeddings"), "purge embeddings");
|
|
106
|
+
if (isVecAvailable(db)) {
|
|
107
|
+
bestEffort(() => db.exec(opts?.dropVecTable ? "DROP TABLE IF EXISTS entries_vec" : "DELETE FROM entries_vec"), "purge entries_vec");
|
|
108
|
+
}
|
|
109
|
+
setMeta(db, "hasEmbeddings", "0");
|
|
110
|
+
}
|
|
111
|
+
export function upsertEmbedding(db, entryId, embedding) {
|
|
112
|
+
// Pre-flight FK guard: when an entry is deleted between when its id is queued
|
|
113
|
+
// for embedding and when this INSERT runs (e.g. consolidation deletes during
|
|
114
|
+
// a concurrent improve cycle), the INSERT throws "FOREIGN KEY constraint failed"
|
|
115
|
+
// and rolls back the entire batch transaction in the caller, losing every
|
|
116
|
+
// embedding for that run. A cheap SELECT here turns the race into a clean skip.
|
|
117
|
+
const exists = db.prepare("SELECT 1 FROM entries WHERE id = ?").get(entryId);
|
|
118
|
+
if (!exists)
|
|
119
|
+
return { stored: false, vec: "unavailable" };
|
|
120
|
+
const buf = float32Buffer(embedding);
|
|
121
|
+
// Always write to BLOB table (works without sqlite-vec; the JS-cosine fallback
|
|
122
|
+
// reads it, so semantic search survives a vec fast-path failure).
|
|
123
|
+
db.prepare("INSERT OR REPLACE INTO embeddings (id, embedding) VALUES (?, ?)").run(entryId, buf);
|
|
124
|
+
if (!isVecAvailable(db))
|
|
125
|
+
return { stored: true, vec: "unavailable" };
|
|
126
|
+
// Fast path: mirror into the sqlite-vec table. Wrapped in a transaction so a
|
|
127
|
+
// crash between DELETE and INSERT does not leave the entry missing. A THROW
|
|
128
|
+
// here — previously swallowed silently by bestEffort — is now surfaced to the
|
|
129
|
+
// caller so the embedding phase can count it, warn, and route search to the
|
|
130
|
+
// (complete) BLOB table rather than a partial/mismatched vec table.
|
|
131
|
+
try {
|
|
132
|
+
db.transaction(() => {
|
|
133
|
+
db.prepare("DELETE FROM entries_vec WHERE id = ?").run(entryId);
|
|
134
|
+
db.prepare("INSERT INTO entries_vec (id, embedding) VALUES (?, ?)").run(entryId, buf);
|
|
135
|
+
})();
|
|
136
|
+
return { stored: true, vec: "ok" };
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return { stored: true, vec: "failed" };
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
export function searchVec(db, queryEmbedding, k) {
|
|
143
|
+
// Fast path: sqlite-vec, but ONLY when the extension is loaded AND the
|
|
144
|
+
// embedding phase did not record a vec insert failure. A degraded fast-path
|
|
145
|
+
// table (partial or dimension-mismatched) would return wrong or missing
|
|
146
|
+
// neighbours, so we honestly fall back to the JS-cosine scan over the
|
|
147
|
+
// complete BLOB table instead.
|
|
148
|
+
if (isVecAvailable(db) && isVecFastPathReady(db)) {
|
|
149
|
+
const buf = float32Buffer(queryEmbedding);
|
|
150
|
+
try {
|
|
151
|
+
return db
|
|
152
|
+
.prepare("SELECT id, distance FROM entries_vec WHERE embedding MATCH ? AND k = ?")
|
|
153
|
+
.all(buf, k);
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
// Log the failure so it's visible in diagnostics
|
|
157
|
+
warn("[db] searchVec (sqlite-vec path) failed:", err instanceof Error ? err.message : String(err));
|
|
158
|
+
return [];
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Fallback: JS-based cosine similarity over BLOB table
|
|
162
|
+
return searchBlobVec(db, queryEmbedding, k);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Return the k nearest neighbours of an already-indexed entry using its
|
|
166
|
+
* persisted embedding — no re-embedding, no network. Decodes the stored BLOB by
|
|
167
|
+
* byte length (dim = bytes / 4) and reuses searchVec (sqlite-vec fast path or
|
|
168
|
+
* JS-cosine fallback). Returns [] when the entry has no stored embedding or the
|
|
169
|
+
* BLOB is corrupt. The query entry itself is typically returned with distance
|
|
170
|
+
* ~0 — callers should filter it out by id.
|
|
171
|
+
*/
|
|
172
|
+
export function getNeighborsByEntryId(db, id, k) {
|
|
173
|
+
const row = db.prepare("SELECT embedding FROM embeddings WHERE id = ?").get(id);
|
|
174
|
+
if (!row)
|
|
175
|
+
return [];
|
|
176
|
+
const queryEmbedding = bufferToFloat32(row.embedding, Math.floor(row.embedding.byteLength / 4));
|
|
177
|
+
if (!queryEmbedding)
|
|
178
|
+
return [];
|
|
179
|
+
return searchVec(db, queryEmbedding, k);
|
|
180
|
+
}
|
|
181
|
+
function float32Buffer(vec) {
|
|
182
|
+
const f32 = new Float32Array(vec);
|
|
183
|
+
return Buffer.from(f32.buffer);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Decode a stored embedding BLOB into a Float32 array of `expectedDim`
|
|
187
|
+
* dimensions. Returns `null` (and emits a warning) when the byte length does
|
|
188
|
+
* not exactly match `expectedDim * 4`, including the legacy partial-trailing
|
|
189
|
+
* float case the previous truncating-divide silently swallowed.
|
|
190
|
+
*
|
|
191
|
+
* BUG-M2: the previous `buf.byteLength / 4` divide would truncate any
|
|
192
|
+
* trailing partial float and a misaligned `byteOffset` would throw — both
|
|
193
|
+
* surfaced as opaque generic errors caught upstream.
|
|
194
|
+
*/
|
|
195
|
+
function bufferToFloat32(buf, expectedDim) {
|
|
196
|
+
if (buf.byteLength !== expectedDim * 4) {
|
|
197
|
+
warn("[db] bufferToFloat32: skipping embedding row — expected %d bytes (%d dim x 4), got %d", expectedDim * 4, expectedDim, buf.byteLength);
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
// Copy into a fresh ArrayBuffer to sidestep any byteOffset alignment
|
|
201
|
+
// requirements imposed by Float32Array's typed-array view contract.
|
|
202
|
+
const aligned = new ArrayBuffer(buf.byteLength);
|
|
203
|
+
new Uint8Array(aligned).set(buf);
|
|
204
|
+
const f32 = new Float32Array(aligned);
|
|
205
|
+
return Array.from(f32);
|
|
206
|
+
}
|
|
207
|
+
function searchBlobVec(db, queryEmbedding, k) {
|
|
208
|
+
try {
|
|
209
|
+
const rows = db.prepare("SELECT id, embedding FROM embeddings").all();
|
|
210
|
+
if (rows.length === 0)
|
|
211
|
+
return [];
|
|
212
|
+
const expectedDim = queryEmbedding.length;
|
|
213
|
+
const scored = [];
|
|
214
|
+
for (const row of rows) {
|
|
215
|
+
const embedding = bufferToFloat32(row.embedding, expectedDim);
|
|
216
|
+
if (embedding === null)
|
|
217
|
+
continue;
|
|
218
|
+
const similarity = cosineSimilarity(queryEmbedding, embedding);
|
|
219
|
+
scored.push({ id: row.id, similarity });
|
|
220
|
+
}
|
|
221
|
+
scored.sort((a, b) => b.similarity - a.similarity);
|
|
222
|
+
// Convert cosine similarity to L2 distance for compatibility with sqlite-vec interface
|
|
223
|
+
// For normalized vectors: L2² = 2(1 - cos_sim)
|
|
224
|
+
return scored.slice(0, k).map(({ id, similarity }) => ({
|
|
225
|
+
id,
|
|
226
|
+
distance: Math.sqrt(2 * Math.max(0, 1 - similarity)),
|
|
227
|
+
}));
|
|
228
|
+
}
|
|
229
|
+
catch (err) {
|
|
230
|
+
// MD-5: Log the failure so it's visible in diagnostics
|
|
231
|
+
warn("[db] searchBlobVec (JS fallback) failed:", err instanceof Error ? err.message : String(err));
|
|
232
|
+
return [];
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Return all entries that do not yet have an embedding row.
|
|
237
|
+
* Used by the embedding phase to determine which entries need vectors generated.
|
|
238
|
+
*/
|
|
239
|
+
export function getAllEntriesForEmbedding(db) {
|
|
240
|
+
return db
|
|
241
|
+
.prepare(`
|
|
242
|
+
SELECT e.id, e.search_text AS searchText, e.entry_key AS entryKey, e.file_path AS filePath FROM entries e
|
|
243
|
+
WHERE NOT EXISTS (SELECT 1 FROM embeddings b WHERE b.id = e.id)
|
|
244
|
+
`)
|
|
245
|
+
.all();
|
|
246
|
+
}
|
|
247
|
+
export function getEmbeddingCount(db) {
|
|
248
|
+
const row = db.prepare("SELECT COUNT(*) AS cnt FROM embeddings").get();
|
|
249
|
+
return row.cnt;
|
|
250
|
+
}
|