akm-cli 0.9.17-alpha.2 → 0.9.17-alpha.4
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 +756 -0
- package/dist/akm +94 -196
- package/dist/cli/shared.js +6 -2
- package/dist/cli.js +22 -9
- package/dist/commands/agent/agent-dispatch.js +1 -1
- package/dist/commands/command/command-execution.js +24 -62
- package/dist/commands/feedback-cli.js +0 -1
- package/dist/commands/health/accept-rate.js +2 -2
- package/dist/commands/health/checks.js +30 -75
- package/dist/commands/health/config-skew.js +38 -0
- package/dist/commands/health/egress.js +54 -0
- package/dist/commands/health/html-report.js +0 -38
- package/dist/commands/health/improve-metrics.js +123 -562
- package/dist/commands/health/plugin-staleness.js +53 -3
- package/dist/commands/health/renderers.js +12 -4
- package/dist/commands/health/report-view-model.js +11 -106
- package/dist/commands/health/types-improve.js +4 -19
- package/dist/commands/health/windows.js +64 -73
- package/dist/commands/health.js +122 -143
- package/dist/commands/improve/consolidate/chunking.js +25 -100
- package/dist/commands/improve/consolidate/sanitize.js +54 -149
- package/dist/commands/improve/consolidate.js +538 -1075
- package/dist/commands/improve/content-hash.js +16 -24
- package/dist/commands/improve/distill/content-repair.js +18 -100
- package/dist/commands/improve/distill-guards.js +20 -81
- package/dist/commands/improve/distill-promotion-policy.js +23 -243
- package/dist/commands/improve/distill.js +608 -1075
- package/dist/commands/improve/eligibility.js +126 -400
- package/dist/commands/improve/execution.js +3 -5
- package/dist/commands/improve/extract.js +487 -1046
- package/dist/commands/improve/feedback-valence.js +0 -25
- package/dist/commands/improve/improve-cli.js +29 -166
- package/dist/commands/improve/improve-result-file.js +10 -66
- package/dist/commands/improve/improve-strategies.js +12 -7
- package/dist/commands/improve/improve-usage-report.js +18 -64
- package/dist/commands/improve/improve.js +443 -1063
- package/dist/commands/improve/ledger.js +114 -0
- package/dist/commands/improve/locks.js +2 -8
- package/dist/commands/improve/loop-stages.js +459 -1172
- package/dist/commands/improve/memory/derived-ref.js +12 -77
- package/dist/commands/improve/memory/memory-belief.js +14 -118
- package/dist/commands/improve/memory/memory-improve.js +4 -3
- package/dist/commands/improve/outcome-loop.js +28 -156
- package/dist/commands/improve/planner.js +5 -10
- package/dist/commands/improve/preparation.js +851 -2339
- package/dist/commands/improve/proactive-maintenance.js +34 -101
- package/dist/commands/improve/reflect-noise.js +104 -280
- package/dist/commands/improve/reflect.js +621 -1367
- package/dist/commands/improve/salience.js +46 -232
- package/dist/commands/improve/session-asset.js +19 -100
- package/dist/commands/improve/stage.js +323 -0
- package/dist/commands/proposal/drain.js +251 -644
- package/dist/commands/proposal/proposal-cli.js +3 -18
- package/dist/commands/proposal/proposal-types.js +20 -41
- package/dist/commands/proposal/proposal.js +1 -2
- package/dist/commands/proposal/propose.js +134 -160
- package/dist/commands/proposal/repository.js +502 -1487
- package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
- package/dist/commands/proposal/validators/proposal-validators.js +1 -1
- package/dist/commands/proposal/validators/proposals.js +13 -89
- package/dist/commands/read/curate.js +63 -413
- package/dist/commands/read/search-cli.js +16 -33
- package/dist/commands/read/search.js +17 -23
- package/dist/commands/read/show.js +2 -13
- package/dist/commands/sources/bundle-cli.js +25 -2
- package/dist/commands/sources/bundle-config-ops.js +7 -0
- package/dist/commands/sources/dangerous-env-audit.js +1 -2
- package/dist/commands/sources/info.js +2 -11
- package/dist/commands/sources/installed-stashes.js +197 -746
- package/dist/commands/sources/schema-repair.js +98 -129
- package/dist/commands/sources/source-add.js +62 -12
- package/dist/commands/sources/stash-cli.js +1 -1
- package/dist/commands/tasks/explain.js +10 -13
- package/dist/commands/tasks/tasks-cli.js +9 -8
- package/dist/commands/tasks/tasks.js +326 -930
- package/dist/commands/tasks/validate.js +42 -21
- package/dist/commands/workflow/plan.js +22 -29
- package/dist/commands/workflow-cli.js +4 -4
- package/dist/core/adapter/adapters/akm-adapter.js +0 -1
- package/dist/core/adapter/adapters/akm-lint.js +2 -3
- package/dist/core/adapter/adapters/akm-metadata.js +11 -12
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
- package/dist/core/adapter/execution-source.js +17 -29
- package/dist/core/asset/resolve-ref.js +1 -1
- package/dist/core/bundle-id.js +42 -5
- package/dist/core/bundle-rename.js +291 -0
- package/dist/core/config/config-io.js +1 -2
- package/dist/core/config/config-schema.js +1 -33
- package/dist/core/config/config-walker.js +1 -1
- package/dist/core/config/config.js +163 -68
- package/dist/core/config/legacy-source-shape-shim.js +38 -9
- package/dist/core/config/schema/embedding.js +20 -5
- package/dist/core/config/schema/engines.js +5 -0
- package/dist/core/config/schema/execution.js +1 -1
- package/dist/core/config/schema/experimental.js +1 -1
- package/dist/core/config/schema/improve-processes.js +21 -95
- package/dist/core/config/schema/improve.js +4 -42
- package/dist/core/config/schema/scheduler.js +12 -12
- package/dist/core/config/schema/search.js +6 -22
- package/dist/core/env-secret-ref.js +0 -1
- package/dist/core/errors.js +8 -9
- package/dist/core/file-lock.js +76 -173
- package/dist/core/logs-db.js +2 -2
- package/dist/core/paths.js +0 -27
- package/dist/core/redaction.js +109 -2
- package/dist/core/run-lock.js +2 -5
- package/dist/core/spawn-env.js +1 -1
- package/dist/core/state/migrations.js +108 -61
- package/dist/core/state-db-scope.js +2 -4
- package/dist/core/state-db.js +126 -692
- package/dist/core/type-presentation.js +1 -9
- package/dist/core/write-source.js +293 -1012
- package/dist/execution/input-contract.js +1 -1
- package/dist/execution/resolved-request.js +135 -689
- package/dist/execution/source.js +63 -257
- package/dist/execution/target-ref.js +1 -1
- package/dist/indexer/bundle-identity-guard.js +2 -2
- package/dist/indexer/db/graph-db.js +106 -46
- package/dist/indexer/ensure-index.js +44 -85
- package/dist/indexer/graph/graph-extraction.js +340 -562
- package/dist/indexer/graph/graph-related.js +130 -0
- package/dist/indexer/index-rebuild-lock.js +3 -11
- package/dist/indexer/index-writer-lock.js +8 -17
- package/dist/indexer/index-written-assets.js +139 -151
- package/dist/indexer/indexer.js +524 -846
- package/dist/indexer/materialize-embeddings.js +60 -397
- package/dist/indexer/passes/memory-inference.js +81 -90
- package/dist/indexer/passes/metadata.js +132 -200
- package/dist/indexer/read-preflight.js +0 -7
- package/dist/indexer/scan/doc-to-entry.js +1 -3
- package/dist/indexer/scan/drain-dir.js +1 -1
- package/dist/indexer/search/db-search.js +181 -590
- package/dist/indexer/search/fts-query.js +30 -41
- package/dist/indexer/search/ranking.js +28 -154
- package/dist/indexer/search/search-attribution.js +12 -32
- package/dist/indexer/search/search-fields.js +11 -15
- package/dist/indexer/search/search-hit-enrichers.js +54 -85
- package/dist/indexer/search/search-source.js +1 -4
- package/dist/indexer/usage/usage-events.js +2 -7
- package/dist/integrations/agent/engine-fallback.js +23 -40
- package/dist/integrations/agent/engine-resolution.js +93 -183
- package/dist/integrations/agent/execution.js +507 -0
- package/dist/integrations/agent/model-map.js +28 -156
- package/dist/integrations/agent/request-lowering.js +66 -141
- package/dist/integrations/agent/runner-dispatch.js +143 -321
- package/dist/integrations/agent/runner.js +54 -14
- package/dist/integrations/lockfile.js +53 -101
- package/dist/llm/embedders/deterministic.js +2 -3
- package/dist/llm/embedders/profile.js +71 -0
- package/dist/llm/embedders/remote.js +10 -15
- package/dist/llm/graph-extract.js +3 -12
- package/dist/llm/index-passes.js +3 -5
- package/dist/llm/memory-infer.js +1 -2
- package/dist/llm/metadata-enhance.js +1 -2
- package/dist/llm/structured-call.js +5 -24
- package/dist/output/generic-render.js +23 -11
- package/dist/output/html-render.js +13 -10
- package/dist/output/render-registry.js +3 -32
- package/dist/output/shapes/helpers.js +2 -34
- package/dist/output/shapes/passthrough.js +1 -9
- package/dist/{indexer/search/ranking-types.js → output/text/bundle-rename.js} +4 -1
- package/dist/output/text/command-format.js +60 -23
- package/dist/output/text/helpers.js +1 -1
- package/dist/output/text/migrate.js +5 -14
- package/dist/output/text/proposal-format.js +1 -2
- package/dist/output/text/workflow-format.js +0 -32
- package/dist/output/text.js +2 -0
- package/dist/registry/factory.js +4 -19
- package/dist/registry/network.js +66 -220
- package/dist/registry/providers/index.js +0 -2
- package/dist/registry/providers/skills-sh.js +3 -14
- package/dist/registry/providers/static-index.js +24 -26
- package/dist/registry/resolve.js +55 -131
- package/dist/scripts/akm-migrate-node.js +43937 -93313
- package/dist/scripts/akm-migrate.js +43697 -93071
- package/dist/setup/registry-stash-loader.js +4 -13
- package/dist/setup/semantic-assets.js +3 -44
- package/dist/setup/setup.js +1 -1
- package/dist/setup/steps/tasks.js +25 -15
- package/dist/sources/provider-factory.js +17 -18
- package/dist/sources/providers/filesystem.js +2 -3
- package/dist/sources/providers/git-install.js +7 -1
- package/dist/sources/providers/git-provider.js +0 -3
- package/dist/sources/providers/git-stash.js +0 -17
- package/dist/sources/providers/npm.js +2 -4
- package/dist/sources/providers/provider-utils.js +5 -10
- package/dist/sources/providers/website.js +0 -2
- package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
- package/dist/sources/website-url.js +2 -2
- package/dist/storage/database.js +9 -35
- package/dist/storage/repositories/improve-ledger-repository.js +168 -0
- package/dist/storage/repositories/index-connection.js +34 -70
- package/dist/storage/repositories/index-entries-repository.js +69 -111
- package/dist/storage/repositories/index-entry-mapper.js +1 -2
- package/dist/storage/repositories/index-entry-schema.js +83 -269
- package/dist/storage/repositories/index-fts-repository.js +86 -256
- package/dist/storage/repositories/index-llm-cache-repository.js +17 -0
- package/dist/storage/repositories/index-meta-repository.js +6 -4
- package/dist/storage/repositories/index-schema.js +192 -220
- package/dist/storage/repositories/index-utility-repository.js +8 -29
- package/dist/storage/repositories/index-vec-repository.js +133 -414
- package/dist/storage/repositories/outcome-repository.js +2 -1
- package/dist/storage/repositories/proposals-repository.js +35 -0
- package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
- package/dist/storage/repositories/task-history-repository.js +26 -4
- package/dist/storage/repositories/workflow-runs-repository.js +53 -244
- package/dist/storage/sqlite-migrations.js +136 -0
- package/dist/storage/sqlite-pragmas.js +11 -9
- package/dist/storage/sqlite-transaction.js +170 -0
- package/dist/storage/state-db-integrity.js +34 -27
- package/dist/tasks/activation-config.js +134 -62
- package/dist/tasks/backends/cron.js +129 -277
- package/dist/tasks/backends/exec-utils.js +2 -5
- package/dist/tasks/backends/launchd.js +125 -745
- package/dist/tasks/backends/schtasks.js +101 -620
- package/dist/tasks/prepare/prepare-support.js +5 -15
- package/dist/tasks/prepare/prepare.js +0 -2
- package/dist/tasks/resolve-akm-bin.js +20 -79
- package/dist/tasks/run/attempt-lifecycle.js +0 -1
- package/dist/tasks/scheduler-binding.js +18 -238
- package/dist/tasks/scheduler-invocation.js +52 -52
- package/dist/tasks/scheduler-lock.js +53 -0
- package/dist/tasks/scheduler-sync.js +363 -679
- package/dist/tasks/source/parse-task-source.js +160 -10
- package/dist/tasks/source/task-source-v3-frozen.js +3 -4
- package/dist/tasks/source/task-to-v4.js +2 -2
- package/dist/workflows/authoring/authoring.js +3 -12
- package/dist/workflows/compile.js +211 -0
- package/dist/workflows/concurrency-policy.js +13 -74
- package/dist/workflows/exec/child-invocation.js +3 -17
- package/dist/workflows/exec/child-workflow.js +32 -141
- package/dist/workflows/exec/dispatch-redaction.js +13 -53
- package/dist/workflows/exec/environment.js +98 -0
- package/dist/workflows/exec/exec-unit.js +33 -140
- package/dist/workflows/exec/frozen-judge.js +7 -59
- package/dist/workflows/exec/native-executor.js +82 -341
- package/dist/workflows/exec/param-secrets.js +29 -47
- package/dist/workflows/exec/run-workflow.js +154 -387
- package/dist/workflows/exec/scheduler.js +9 -36
- package/dist/workflows/exec/step-work.js +127 -430
- package/dist/workflows/exec/unit-dispatch.js +11 -63
- package/dist/workflows/exec/unit-writer.js +8 -52
- package/dist/workflows/exec/worktree.js +39 -273
- package/dist/workflows/freeze/child-output-references.js +4 -15
- package/dist/workflows/freeze/environment.js +99 -92
- package/dist/workflows/freeze/freeze.js +172 -0
- package/dist/workflows/freeze/step-values.js +19 -21
- package/dist/workflows/freeze/targets/child-workflow.js +23 -92
- package/dist/workflows/freeze/targets/command.js +10 -33
- package/dist/workflows/freeze/targets/script.js +5 -12
- package/dist/workflows/freeze/targets/shell.js +3 -6
- package/dist/workflows/freeze/targets/task.js +25 -80
- package/dist/workflows/freeze/task-bindings.js +20 -67
- package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
- package/dist/workflows/ir/params.js +6 -51
- package/dist/workflows/ir/plan-hash.js +2 -34
- package/dist/workflows/parser.js +140 -43
- package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
- package/dist/workflows/renderer.js +36 -69
- package/dist/workflows/resource-limits.js +12 -120
- package/dist/workflows/runtime/agent-identity.js +8 -40
- package/dist/workflows/runtime/run-outputs.js +3 -6
- package/dist/workflows/runtime/run-plan.js +316 -0
- package/dist/workflows/runtime/runs.js +48 -200
- package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
- package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
- package/dist/workflows/validate-summary.js +2 -7
- package/docs/integration/bundling-akm.md +49 -42
- package/docs/migration/README.md +1 -0
- package/docs/migration/release-notes/0.9.17.md +41 -0
- package/docs/migration/v0.9.1-to-v0.9.2.md +19 -7
- package/docs/reference/cli.md +182 -125
- package/docs/reference/configuration.md +49 -56
- package/docs/reference/data-and-telemetry.md +19 -20
- package/docs/reference/tasks.md +86 -38
- package/docs/reference/workflow-schema.md +14 -18
- package/docs/reference/workflows.md +6 -9
- package/package.json +1 -1
- package/schemas/akm-config.json +87 -406
- package/dist/commands/health/advisories.js +0 -150
- package/dist/commands/health/metrics.js +0 -329
- package/dist/commands/health/surfaces.js +0 -102
- package/dist/commands/improve/anti-collapse.js +0 -83
- package/dist/commands/improve/collapse-detector.js +0 -432
- package/dist/commands/improve/consolidate/eligibility.js +0 -48
- package/dist/commands/improve/consolidate/merge.js +0 -146
- package/dist/commands/improve/distill/promote-memory.js +0 -329
- package/dist/commands/improve/distill/quality-gate.js +0 -500
- package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
- package/dist/commands/improve/proposal-envelope.js +0 -31
- package/dist/commands/improve/run-context.js +0 -123
- package/dist/commands/improve/shared.js +0 -21
- package/dist/commands/improve/source-identity.js +0 -28
- package/dist/commands/improve/triage.js +0 -96
- package/dist/commands/proposal/drain-policies.js +0 -151
- package/dist/commands/sources/update-transaction.js +0 -220
- package/dist/core/action-contributors.js +0 -28
- package/dist/core/config/config-version-shim.js +0 -101
- package/dist/core/config/retired-experimental-keys-shim.js +0 -62
- package/dist/core/fs-txn.js +0 -405
- package/dist/core/lexical-score.js +0 -25
- package/dist/core/maintenance-barrier.js +0 -167
- package/dist/execution/executable-identity.js +0 -105
- package/dist/execution/guarded-source.js +0 -427
- package/dist/indexer/graph/graph-boost.js +0 -427
- package/dist/indexer/graph/graph-dedup.js +0 -95
- package/dist/indexer/search/name-match.js +0 -35
- package/dist/indexer/search/ranking-contributors.js +0 -515
- package/dist/indexer/walk/project-context.js +0 -192
- package/dist/integrations/agent/execution-cascade.js +0 -566
- package/dist/integrations/agent/execution-definitions.js +0 -202
- package/dist/integrations/agent/execution-lowering.js +0 -841
- package/dist/integrations/agent/execution-preparation.js +0 -98
- package/dist/integrations/agent/inline-execution.js +0 -74
- package/dist/registry/create-provider-registry.js +0 -29
- package/dist/registry/pinned-request-helper.js +0 -247
- package/dist/registry/pinned-transport.js +0 -717
- package/dist/sources/providers/index.js +0 -14
- package/dist/storage/engines/sqlite-migrations.js +0 -271
- package/dist/storage/repositories/canaries-repository.js +0 -107
- package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
- package/dist/storage/repositories/registry-cache.js +0 -113
- package/dist/tasks/scheduler-sync-preview.js +0 -52
- package/dist/workflows/freeze/resolve-steps.js +0 -86
- package/dist/workflows/freeze/source-freeze.js +0 -64
- package/dist/workflows/ir/compile.js +0 -321
- package/dist/workflows/ir/environment-v4.js +0 -330
- package/dist/workflows/ir/freeze-v4.js +0 -153
- package/dist/workflows/ir/schema-v4.js +0 -745
- package/dist/workflows/ir/schema.js +0 -354
- package/dist/workflows/program/schema.js +0 -78
- package/dist/workflows/runtime/checkin.js +0 -57
- package/dist/workflows/runtime/plan-classifier.js +0 -196
- package/dist/workflows/runtime/unit-checkin.js +0 -45
- package/dist/workflows/runtime/unit-phases.js +0 -20
- package/dist/workflows/schema.js +0 -4
- package/dist/workflows/source-ir/compile.js +0 -200
- package/dist/workflows/source-ir/program.js +0 -50
- package/dist/workflows/source-ir/result.js +0 -26
- package/dist/workflows/source-ir/schema.js +0 -786
- package/dist/workflows/source-ir/triggers.js +0 -79
- package/dist/workflows/source-ir/uses.js +0 -40
- package/dist/workflows/validator.js +0 -60
|
@@ -1,50 +1,39 @@
|
|
|
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
|
-
const UNICODE_TOKEN = /[\p{L}\p{N}]+/gu;
|
|
5
|
-
function quoteToken(token) {
|
|
6
|
-
return `"${token}"`;
|
|
7
|
-
}
|
|
8
|
-
function prefixToken(token) {
|
|
9
|
-
return [...token].length >= 3 ? `${quoteToken(token)}*` : quoteToken(token);
|
|
10
|
-
}
|
|
11
4
|
/**
|
|
12
|
-
*
|
|
5
|
+
* Pure FTS5 query building and ref-query helpers.
|
|
13
6
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* its first 16, dropping the tail — which for natural-language input is
|
|
22
|
-
* usually where the discriminating words are. It was unexplained in both the
|
|
23
|
-
* code and the commit that introduced it, unreachable from any flag, config
|
|
24
|
-
* key, or env var, and the user was never told their query had been altered.
|
|
25
|
-
* A wrong answer delivered silently is worse than a slow one.
|
|
7
|
+
* The lexical channel matches ANY query word: BM25 over an OR of the query's
|
|
8
|
+
* non-stopword tokens. Requiring every word first and relaxing only when that
|
|
9
|
+
* found nothing cost 0.108 nDCG@10 on the retrieval suite
|
|
10
|
+
* (`akm/eval/retrieval/reports/baseline-2026-09-27.md`): one conjunctive match
|
|
11
|
+
* in a long document suppressed every better OR candidate.
|
|
12
|
+
* `parseRefPrefixQuery` is the one non-FTS helper: it decides whether a raw
|
|
13
|
+
* query should bypass FTS entirely (SPEC-4 ref-prefix enumeration).
|
|
26
14
|
*/
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
15
|
+
/** English function words dropped from the lexical query (the retrieval lab's list). */
|
|
16
|
+
const STOPWORDS = new Set(`a about above after again against all am an and any are as at be because been before being below between both but
|
|
17
|
+
by can could did do does doing down during each few for from further had has have having he her here hers herself
|
|
18
|
+
him himself his how i if in into is it its itself just me more most my myself no nor not now of off on once only or
|
|
19
|
+
other our ours out over own same she should so some such than that the their theirs them then there these they this
|
|
20
|
+
those through to too under until up very was we were what when where which while who whom why will with would you
|
|
21
|
+
your yours yourself`.split(/\s+/));
|
|
22
|
+
const UNICODE_TOKEN = /[\p{L}\p{N}]+/gu;
|
|
23
|
+
/**
|
|
24
|
+
* The query's lexical tokens: Unicode letters and numbers (the useful part of
|
|
25
|
+
* FTS5's `unicode61` tokenizer), NFKC-normalized, lowercased and deduplicated,
|
|
26
|
+
* with stopwords removed. A query made only of stopwords keeps all of them, so
|
|
27
|
+
* "how to" still searches for something.
|
|
28
|
+
*/
|
|
29
|
+
export function ftsQueryTokens(query) {
|
|
30
|
+
const tokens = [...new Set(query.normalize("NFKC").toLowerCase().match(UNICODE_TOKEN) ?? [])];
|
|
31
|
+
const content = tokens.filter((token) => !STOPWORDS.has(token));
|
|
32
|
+
return content.length > 0 ? content : tokens;
|
|
33
|
+
}
|
|
34
|
+
/** FTS5 MATCH expression matching any token. Quoting makes FTS operators ordinary words. */
|
|
35
|
+
export function ftsOrMatch(tokens) {
|
|
36
|
+
return tokens.map((token) => `"${token}"`).join(" OR ");
|
|
48
37
|
}
|
|
49
38
|
/**
|
|
50
39
|
* D4 — parse a conceptId-prefix browse query.
|
|
@@ -1,163 +1,37 @@
|
|
|
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
|
-
import { stableFtsScore } from "../../core/lexical-score.js";
|
|
5
|
-
import { getUtilityScoresByIds } from "../../storage/repositories/index-utility-repository.js";
|
|
6
|
-
import { buildLexicalQueryPlan } from "./fts-query.js";
|
|
7
|
-
import { lexicalNameTokens, structuralNameTokenMatch } from "./name-match.js";
|
|
8
|
-
import { applyBeliefStateScoreCeiling, applyScoreContributors, applyUtilityContributors, defaultRankingContributors, defaultUtilityRankingContributors, } from "./ranking-contributors.js";
|
|
9
4
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* run after retrieval (notably the bounded graph boost). This is a
|
|
13
|
-
* calibration for the one search pipeline, not a claim that BM25 is
|
|
14
|
-
* comparable across different queries or FTS tables.
|
|
15
|
-
*/
|
|
16
|
-
/**
|
|
17
|
-
* Convert FTS5's negative BM25 value into the lexical contribution used by
|
|
18
|
-
* this pipeline. The transform is fixed and monotone: it depends only on a
|
|
19
|
-
* row's own BM25 value, so appending weaker candidates cannot rewrite an
|
|
20
|
-
* existing row's score. FTS5 commonly emits relevance near 1e-6 for broad
|
|
21
|
-
* queries, so first put relevance on a log scale around that observed value.
|
|
22
|
-
* The shape constant intentionally makes the curve approach its ceiling
|
|
23
|
-
* slowly: rare-term scores retain separation instead of all reading as 0.8.
|
|
5
|
+
* Reciprocal rank fusion (Cormack, Clarke and Büttcher, SIGIR 2009) of the
|
|
6
|
+
* lexical and vector candidate lists — the whole of search ranking.
|
|
24
7
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
|
|
30
|
-
export function normalizeFtsScores(results) {
|
|
31
|
-
const ftsScoreMap = new Map();
|
|
32
|
-
for (const result of results) {
|
|
33
|
-
ftsScoreMap.set(result.id, { score: result.lexicalScore ?? stableFtsScore(result.bm25Score), result });
|
|
34
|
-
}
|
|
35
|
-
return ftsScoreMap;
|
|
36
|
-
}
|
|
37
|
-
export function combineSearchScores(options) {
|
|
38
|
-
const FTS_WEIGHT = 0.7;
|
|
39
|
-
const VEC_WEIGHT = 0.3;
|
|
40
|
-
const excludeTypeSet = options.excludeTypes && options.excludeTypes.length > 0 ? new Set(options.excludeTypes) : null;
|
|
41
|
-
const scored = [];
|
|
42
|
-
const seenIds = new Set();
|
|
43
|
-
for (const [id, { score: ftsScore, result }] of options.ftsScoreMap) {
|
|
44
|
-
seenIds.add(id);
|
|
45
|
-
const embedScore = options.embedScoreMap.get(id);
|
|
46
|
-
const combinedScore = embedScore !== undefined ? ftsScore * FTS_WEIGHT + embedScore * VEC_WEIGHT : ftsScore;
|
|
47
|
-
scored.push({
|
|
48
|
-
id,
|
|
49
|
-
entry: result.entry,
|
|
50
|
-
filePath: result.filePath,
|
|
51
|
-
score: combinedScore,
|
|
52
|
-
rankingMode: embedScore !== undefined ? "hybrid" : "fts",
|
|
53
|
-
lexicalMatch: result.lexicalMatch,
|
|
54
|
-
itemRef: result.itemRef,
|
|
55
|
-
bundleId: result.bundleId,
|
|
56
|
-
conceptId: result.conceptId,
|
|
57
|
-
fragmentId: result.fragmentId,
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
for (const [id, cosine] of options.embedScoreMap) {
|
|
61
|
-
if (seenIds.has(id))
|
|
62
|
-
continue;
|
|
63
|
-
const found = options.getEntryById(id);
|
|
64
|
-
if (!found)
|
|
65
|
-
continue;
|
|
66
|
-
if (options.typeFilter && found.entry.type !== options.typeFilter)
|
|
67
|
-
continue;
|
|
68
|
-
// #627 — drop vector-only neighbors whose type is excluded on the default path.
|
|
69
|
-
if (excludeTypeSet?.has(found.entry.type))
|
|
70
|
-
continue;
|
|
71
|
-
scored.push({
|
|
72
|
-
id,
|
|
73
|
-
entry: found.entry,
|
|
74
|
-
filePath: found.filePath,
|
|
75
|
-
score: cosine * VEC_WEIGHT,
|
|
76
|
-
rankingMode: "semantic",
|
|
77
|
-
itemRef: found.itemRef,
|
|
78
|
-
bundleId: found.bundleId,
|
|
79
|
-
conceptId: found.conceptId,
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
return scored;
|
|
83
|
-
}
|
|
84
|
-
export function applyRankingRules(options) {
|
|
85
|
-
const queryTokens = buildLexicalQueryPlan(options.query).tokens.map((token) => token.toLowerCase());
|
|
86
|
-
const queryLower = options.query.toLowerCase().trim();
|
|
87
|
-
const rankingContext = {
|
|
88
|
-
db: options.db,
|
|
89
|
-
query: options.query,
|
|
90
|
-
queryLower,
|
|
91
|
-
queryTokens,
|
|
92
|
-
graphContext: options.graphContext,
|
|
93
|
-
projectContext: options.projectContext,
|
|
94
|
-
};
|
|
95
|
-
for (const item of options.items) {
|
|
96
|
-
applyScoreContributors(item, rankingContext, defaultRankingContributors);
|
|
97
|
-
}
|
|
98
|
-
const { global: utilScoresMap, scoped: scopedUtilScoresMap } = getUtilityScoresByIds(options.db, options.items.map((item) => item.id), options.scopeKey);
|
|
99
|
-
// R2 / #692 — salience-ranking is not in defaultUtilityRankingContributors
|
|
100
|
-
// (see ranking-contributors.ts), so this is never consumed by the default
|
|
101
|
-
// ranking path below; it exists only for a caller that explicitly builds a
|
|
102
|
-
// contributor list including salienceRankingContributor. undefined/null
|
|
103
|
-
// both normalize to "no data" — there is no state.db fallback load.
|
|
104
|
-
const salienceRankScores = options.salienceRankScores ?? new Map();
|
|
105
|
-
const utilityContext = {
|
|
106
|
-
...rankingContext,
|
|
107
|
-
utilityScores: utilScoresMap,
|
|
108
|
-
scopedUtilityScores: scopedUtilScoresMap,
|
|
109
|
-
utilityDecayConfig: options.utilityDecayConfig,
|
|
110
|
-
positiveFeedbackCounts: options.positiveFeedbackCounts,
|
|
111
|
-
salienceRankScores,
|
|
112
|
-
};
|
|
113
|
-
for (const item of options.items) {
|
|
114
|
-
applyUtilityContributors(item, utilityContext, defaultUtilityRankingContributors);
|
|
115
|
-
applyRelaxedLexicalScoreCeiling(item, queryTokens);
|
|
116
|
-
// SPEC-5: demoting belief states (superseded/contradicted/archived/
|
|
117
|
-
// deprecated) cap the FINAL score. The additive belief penalty inside the
|
|
118
|
-
// multiplicative boost sum can still overwhelm an additive belief penalty,
|
|
119
|
-
// so without the ceiling a superseded incumbent can outrank its correction.
|
|
120
|
-
applyBeliefStateScoreCeiling(item);
|
|
121
|
-
}
|
|
122
|
-
return options.items;
|
|
123
|
-
}
|
|
124
|
-
const RELAXED_NON_NAME_SCORE_CEILING = 0.65;
|
|
125
|
-
/**
|
|
126
|
-
* Rank name evidence without relying on punctuation or ASCII-only splitting.
|
|
127
|
-
* The tiers are intentionally structural: an exact normalized name, all query
|
|
128
|
-
* tokens in a longer name, any query token in the name, or no name evidence.
|
|
8
|
+
* Fusing ranks rather than scores needs no calibration between BM25 and cosine
|
|
9
|
+
* similarity, so it holds for every embedding model. On the retrieval suite,
|
|
10
|
+
* BM25 fused with document vectors this way scored nDCG@10 0.568 against 0.349
|
|
11
|
+
* for the additive boost stack it replaced
|
|
12
|
+
* (`akm/eval/retrieval/reports/baseline-2026-09-27.md`).
|
|
129
13
|
*/
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const nameBase = entry.name.toLowerCase().split("/").pop() ?? entry.name.toLowerCase();
|
|
134
|
-
const nameTokens = lexicalNameTokens(nameBase);
|
|
135
|
-
if (nameTokens.length === queryTokens.length &&
|
|
136
|
-
nameTokens.every((token, index) => structuralNameTokenMatch(token, queryTokens[index]))) {
|
|
137
|
-
return 3;
|
|
138
|
-
}
|
|
139
|
-
const matched = queryTokens.filter((token) => nameTokens.some((nameToken) => structuralNameTokenMatch(nameToken, token))).length;
|
|
140
|
-
if (matched === queryTokens.length)
|
|
141
|
-
return 2;
|
|
142
|
-
return matched > 0 ? 1 : 0;
|
|
143
|
-
}
|
|
14
|
+
import { compareCodePoints } from "../../core/common.js";
|
|
15
|
+
/** The RRF constant: a candidate at rank r in a channel adds 1 / (RRF_K + r). */
|
|
16
|
+
export const RRF_K = 60;
|
|
144
17
|
/**
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
* share the same bounded displayed score as stronger name-bearing recoveries.
|
|
148
|
-
* The raw ceiling is 0.65; the public score projection is applied later, so
|
|
149
|
-
* callers never literally receive `0.65` just because this ceiling bound.
|
|
150
|
-
*
|
|
151
|
-
* Preserve the pre-ceiling relevance separately from `preCeilingScore`, which
|
|
152
|
-
* belongs to belief-state demotion and may be written afterwards. A relaxed,
|
|
153
|
-
* belief-demoted candidate otherwise loses both its body relevance and its
|
|
154
|
-
* ordering signal when the second ceiling overwrites the first.
|
|
18
|
+
* Fuse ranked channels with equal weights. Candidates are ordered by fused
|
|
19
|
+
* score, then by `itemRef`, so equal scores always come back in the same order.
|
|
155
20
|
*/
|
|
156
|
-
function
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
21
|
+
export function reciprocalRankFusion(channels) {
|
|
22
|
+
const fused = new Map();
|
|
23
|
+
channels.forEach((channel, channelIndex) => {
|
|
24
|
+
channel.forEach((candidate, position) => {
|
|
25
|
+
let entry = fused.get(candidate.id);
|
|
26
|
+
if (entry === undefined) {
|
|
27
|
+
entry = { id: candidate.id, itemRef: candidate.itemRef, score: 0, ranks: channels.map(() => undefined) };
|
|
28
|
+
fused.set(candidate.id, entry);
|
|
29
|
+
}
|
|
30
|
+
if (entry.ranks[channelIndex] !== undefined)
|
|
31
|
+
return;
|
|
32
|
+
entry.ranks[channelIndex] = position + 1;
|
|
33
|
+
entry.score += 1 / (RRF_K + position + 1);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
return [...fused.values()].sort((a, b) => b.score - a.score || compareCodePoints(a.itemRef, b.itemRef));
|
|
163
37
|
}
|
|
@@ -10,52 +10,32 @@ export function attachSearchHitAttribution(target, attribution) {
|
|
|
10
10
|
};
|
|
11
11
|
}
|
|
12
12
|
export function copySearchHitAttribution(from, to, outputDescription) {
|
|
13
|
-
const
|
|
14
|
-
if (!
|
|
13
|
+
const memoryInference = from[ATTRIBUTION]?.memoryInference;
|
|
14
|
+
if (!memoryInference)
|
|
15
15
|
return;
|
|
16
|
-
const
|
|
17
|
-
const memorySurvives = memoryInference?.exposure !== "surface" ||
|
|
16
|
+
const memorySurvives = memoryInference.exposure !== "surface" ||
|
|
18
17
|
(memoryInference.surfaceDescription !== undefined && memoryInference.surfaceDescription === outputDescription);
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
...(memorySurvives && memoryInference ? { memoryInference } : {}),
|
|
22
|
-
...(attribution.graphExtraction ? { graphExtraction: attribution.graphExtraction } : {}),
|
|
23
|
-
};
|
|
24
|
-
if (applicable.lexical || applicable.memoryInference || applicable.graphExtraction) {
|
|
25
|
-
attachSearchHitAttribution(to, applicable);
|
|
26
|
-
}
|
|
18
|
+
if (memorySurvives)
|
|
19
|
+
attachSearchHitAttribution(to, { memoryInference });
|
|
27
20
|
}
|
|
28
21
|
export function getSearchHitAttribution(target) {
|
|
29
22
|
return target[ATTRIBUTION];
|
|
30
23
|
}
|
|
31
24
|
export function buildUsageEventAttribution(attribution, entryRef, projection = "full") {
|
|
32
|
-
|
|
25
|
+
const memoryInference = attribution?.memoryInference;
|
|
26
|
+
if (!memoryInference)
|
|
33
27
|
return undefined;
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
const graphApplies = graphExtraction !== undefined && Number.isFinite(graphExtraction.boost) && graphExtraction.boost > 0;
|
|
38
|
-
const surfaceFields = memoryInference?.surfaceFields ?? [];
|
|
39
|
-
const surfaceVisible = memoryInference?.exposure !== "surface" ||
|
|
28
|
+
const childRef = memoryInference.exposure === "direct" ? entryRef : memoryInference.childRef;
|
|
29
|
+
const surfaceFields = memoryInference.surfaceFields ?? [];
|
|
30
|
+
const surfaceVisible = memoryInference.exposure !== "surface" ||
|
|
40
31
|
(projection === "full"
|
|
41
32
|
? surfaceFields.length > 0
|
|
42
33
|
: projection === "normal" || projection === "agent"
|
|
43
34
|
? surfaceFields.includes("description")
|
|
44
35
|
: false);
|
|
45
|
-
|
|
46
|
-
if (!memoryApplies && !graphApplies)
|
|
36
|
+
if (childRef?.includes("//") !== true || !surfaceVisible)
|
|
47
37
|
return undefined;
|
|
48
|
-
return {
|
|
49
|
-
...(memoryApplies
|
|
50
|
-
? {
|
|
51
|
-
memoryInference: {
|
|
52
|
-
exposure: memoryInference.exposure,
|
|
53
|
-
childRef,
|
|
54
|
-
},
|
|
55
|
-
}
|
|
56
|
-
: {}),
|
|
57
|
-
...(graphApplies ? { graphExtraction } : {}),
|
|
58
|
-
};
|
|
38
|
+
return { memoryInference: { exposure: memoryInference.exposure, childRef } };
|
|
59
39
|
}
|
|
60
40
|
export function usageEventAttributionMetadata(attribution, entryRef, projection = "full") {
|
|
61
41
|
const applicable = buildUsageEventAttribution(attribution, entryRef, projection);
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
/**
|
|
5
5
|
* Return per-field search text for multi-column FTS5 indexing.
|
|
6
6
|
*
|
|
7
|
+
* Text keeps its case: FTS5's `unicode61` tokenizer folds case itself, and
|
|
8
|
+
* `buildSearchText` feeds the embedder, where case carries meaning.
|
|
9
|
+
*
|
|
7
10
|
* Fields:
|
|
8
11
|
* - name: entry name with hyphens/underscores replaced by spaces
|
|
9
12
|
* - description: entry description
|
|
@@ -12,22 +15,15 @@
|
|
|
12
15
|
* - content: bounded native/adapter body projection + TOC headings + parameters
|
|
13
16
|
* (lowest-weight catch-all)
|
|
14
17
|
*/
|
|
15
|
-
// NOTE (R5): the collapse detector's frozen canary queries are built from the
|
|
16
|
-
// same surface this function indexes (name tokens / tags / description) and
|
|
17
|
-
// scored via FTS against it. Changing what buildSearchFields includes shifts
|
|
18
|
-
// the detector's recall baseline for ALL existing canary sets — coordinate
|
|
19
|
-
// with src/commands/improve/collapse-detector.ts (buildCanaryQuery) and expect
|
|
20
|
-
// operators to re-mint via `bun scripts/refresh-canary-set.ts --refresh` after
|
|
21
|
-
// such a change.
|
|
22
18
|
export function buildSearchFields(entry) {
|
|
23
|
-
const name = entry.name.replace(/[-_]/g, " ")
|
|
24
|
-
const description =
|
|
19
|
+
const name = entry.name.replace(/[-_]/g, " ");
|
|
20
|
+
const description = entry.description ?? "";
|
|
25
21
|
const tagParts = [];
|
|
26
22
|
if (entry.tags)
|
|
27
23
|
tagParts.push(entry.tags.join(" "));
|
|
28
24
|
if (entry.aliases)
|
|
29
25
|
tagParts.push(entry.aliases.join(" "));
|
|
30
|
-
const tags = tagParts.join(" ")
|
|
26
|
+
const tags = tagParts.join(" ");
|
|
31
27
|
const hintParts = [];
|
|
32
28
|
if (entry.hints)
|
|
33
29
|
hintParts.push(entry.hints.join(" "));
|
|
@@ -51,7 +47,7 @@ export function buildSearchFields(entry) {
|
|
|
51
47
|
hintParts.push(entry.pageKind);
|
|
52
48
|
if (entry.whenToUse)
|
|
53
49
|
hintParts.push(entry.whenToUse);
|
|
54
|
-
const hints = hintParts.join(" ")
|
|
50
|
+
const hints = hintParts.join(" ");
|
|
55
51
|
const contentParts = [];
|
|
56
52
|
if (entry.toc) {
|
|
57
53
|
contentParts.push(entry.toc.map((h) => h.text).join(" "));
|
|
@@ -65,13 +61,13 @@ export function buildSearchFields(entry) {
|
|
|
65
61
|
}
|
|
66
62
|
if (entry.content)
|
|
67
63
|
contentParts.push(entry.content);
|
|
68
|
-
const content = contentParts.join(" ")
|
|
64
|
+
const content = contentParts.join(" ");
|
|
69
65
|
return { name, description, tags, hints, content };
|
|
70
66
|
}
|
|
71
67
|
/**
|
|
72
|
-
* Build a single concatenated search text string for an entry
|
|
73
|
-
*
|
|
74
|
-
*
|
|
68
|
+
* Build a single concatenated search text string for an entry: the text its
|
|
69
|
+
* vector is embedded from, derived from the stored document when the entry is
|
|
70
|
+
* embedded (`entries.embed_hash` holds its hash).
|
|
75
71
|
*/
|
|
76
72
|
export function buildSearchText(entry) {
|
|
77
73
|
const fields = buildSearchFields(entry);
|
|
@@ -2,100 +2,69 @@
|
|
|
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
4
|
import { displayRef } from "../../core/asset/resolve-ref.js";
|
|
5
|
+
import { presentationFor } from "../../core/type-presentation.js";
|
|
5
6
|
import { getDerivedForParent, getItemRefById } from "../../storage/repositories/index-entries-repository.js";
|
|
6
7
|
import { getRenderer } from "../walk/file-context.js";
|
|
7
8
|
import { attachSearchHitAttribution } from "./search-attribution.js";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
if (!rendererName)
|
|
16
|
-
return;
|
|
9
|
+
/**
|
|
10
|
+
* Let the hit's type renderer enrich it, then — for a memory, when a DB is
|
|
11
|
+
* open — surface its derived child ({@link surfaceDerivedMemory}).
|
|
12
|
+
*/
|
|
13
|
+
export async function enrichSearchHit(hit, ctx) {
|
|
14
|
+
const rendererName = presentationFor(ctx.type).renderer;
|
|
15
|
+
if (rendererName) {
|
|
17
16
|
const renderer = await getRenderer(rendererName);
|
|
18
17
|
renderer?.enrichSearchHit?.(hit, ctx.stashDir);
|
|
19
|
-
}
|
|
20
|
-
|
|
18
|
+
}
|
|
19
|
+
if (ctx.type === "memory" && ctx.db)
|
|
20
|
+
surfaceDerivedMemory(hit, ctx.db, ctx.bundleId);
|
|
21
|
+
}
|
|
21
22
|
/**
|
|
22
|
-
* Phase 5A / Advantage D5 —
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* derived child's description / searchHints / tags AND sets `expandTo` to
|
|
27
|
-
* the derived child's ref so callers can fetch it via `akm show <ref>`.
|
|
23
|
+
* Phase 5A / Advantage D5 — when a parent memory has a `.derived` child
|
|
24
|
+
* indexed (the LLM-distilled lesson surface), rewrite the parent hit to
|
|
25
|
+
* surface the derived child's description / tags AND set `expandTo` to the
|
|
26
|
+
* derived child's ref so callers can fetch it via `akm show <ref>`.
|
|
28
27
|
*
|
|
29
28
|
* The parent ref is preserved on the hit — only the surface text is
|
|
30
29
|
* swapped, so links and provenance still point at the canonical parent.
|
|
31
|
-
*
|
|
32
|
-
* Skipped for:
|
|
33
|
-
* - non-memory hits
|
|
34
|
-
* - memory hits that are themselves derived children (name ends with
|
|
35
|
-
* `.derived`) — we never recurse parent→child→grandchild
|
|
36
|
-
* - contexts without an open DB connection
|
|
37
30
|
*/
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
surfaceFields.push("tags");
|
|
77
|
-
}
|
|
78
|
-
// F4b output-spelling flip: `expandTo` is a user-facing `akm show <ref>`
|
|
79
|
-
// target, so emit the 0.9.0 short conceptId grammar (`memories/<name>`).
|
|
80
|
-
hit.expandTo = displayRef({ type: "memory", name: derived.entry.name });
|
|
81
|
-
const childRef = getItemRefById(ctx.db, derived.id);
|
|
82
|
-
if (childRef && surfaceFields.length > 0) {
|
|
83
|
-
attachSearchHitAttribution(hit, {
|
|
84
|
-
memoryInference: {
|
|
85
|
-
exposure: "surface",
|
|
86
|
-
childRef,
|
|
87
|
-
surfaceFields,
|
|
88
|
-
...(surfaceDescription ? { surfaceDescription } : {}),
|
|
89
|
-
},
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
},
|
|
93
|
-
};
|
|
94
|
-
export const defaultSearchHitEnrichers = [rendererSearchHitEnricher, derivedMemoryEnricher];
|
|
95
|
-
export async function enrichSearchHit(hit, ctx, enrichers = defaultSearchHitEnrichers) {
|
|
96
|
-
for (const enricher of enrichers) {
|
|
97
|
-
if (!enricher.appliesTo(ctx))
|
|
98
|
-
continue;
|
|
99
|
-
await enricher.enrich(hit, ctx);
|
|
31
|
+
function surfaceDerivedMemory(hit, db, bundleId) {
|
|
32
|
+
// Never recurse: a `.derived` hit is itself the child surface; leaving
|
|
33
|
+
// it untouched also avoids `<parent>.derived.derived` chains.
|
|
34
|
+
if (hit.name.toLowerCase().endsWith(".derived"))
|
|
35
|
+
return;
|
|
36
|
+
// Parent ref shape: the 0.9.0 `memories/<name>` conceptId. Re-build from the
|
|
37
|
+
// entry's name so we don't depend on whatever wiki/registry prefix `hit.ref`
|
|
38
|
+
// carries. INTERNAL lookup key into `getDerivedForParent`: the `derived_from`
|
|
39
|
+
// column stores this same conceptId grammar.
|
|
40
|
+
const derived = getDerivedForParent(db, `memories/${hit.name}`, bundleId);
|
|
41
|
+
if (!derived)
|
|
42
|
+
return;
|
|
43
|
+
// Swap description / tags from the derived child (SourceSearchHit carries no
|
|
44
|
+
// `searchHints`).
|
|
45
|
+
const surfaceFields = [];
|
|
46
|
+
let surfaceDescription;
|
|
47
|
+
if (typeof derived.entry.description === "string" && derived.entry.description.length > 0) {
|
|
48
|
+
hit.description = derived.entry.description;
|
|
49
|
+
surfaceDescription = derived.entry.description;
|
|
50
|
+
surfaceFields.push("description");
|
|
51
|
+
}
|
|
52
|
+
if (Array.isArray(derived.entry.tags) && derived.entry.tags.length > 0) {
|
|
53
|
+
hit.tags = derived.entry.tags;
|
|
54
|
+
surfaceFields.push("tags");
|
|
55
|
+
}
|
|
56
|
+
// `expandTo` is a user-facing `akm show <ref>` target, so emit the 0.9.0
|
|
57
|
+
// short conceptId grammar (`memories/<name>`).
|
|
58
|
+
hit.expandTo = displayRef({ type: "memory", name: derived.entry.name });
|
|
59
|
+
const childRef = getItemRefById(db, derived.id);
|
|
60
|
+
if (childRef && surfaceFields.length > 0) {
|
|
61
|
+
attachSearchHitAttribution(hit, {
|
|
62
|
+
memoryInference: {
|
|
63
|
+
exposure: "surface",
|
|
64
|
+
childRef,
|
|
65
|
+
surfaceFields,
|
|
66
|
+
...(surfaceDescription ? { surfaceDescription } : {}),
|
|
67
|
+
},
|
|
68
|
+
});
|
|
100
69
|
}
|
|
101
70
|
}
|
|
@@ -7,14 +7,11 @@ import { isWithin, resolveStashDir } from "../../core/common.js";
|
|
|
7
7
|
import { bundleComponentConfig, bundleKeyForContentRoot, bundlesToSourceEntries, getSources, isBundleEnabled, loadConfig, } from "../../core/config/config.js";
|
|
8
8
|
import { ConfigError } from "../../core/errors.js";
|
|
9
9
|
import { getUnresolvedSourcesDir } from "../../core/paths.js";
|
|
10
|
+
import { warn } from "../../core/warn.js";
|
|
10
11
|
import { resolveGitContentRoot, resolveWritable } from "../../core/write-source.js";
|
|
11
12
|
import { lockContentRootFor } from "../../integrations/lockfile.js";
|
|
12
13
|
import { resolveSourceProviderFactory } from "../../sources/provider-factory.js";
|
|
13
14
|
import { ensureWebsiteMirror } from "../../sources/snapshot-fetchers/website-ingest.js";
|
|
14
|
-
// Eager side-effect imports so all built-in source providers self-register
|
|
15
|
-
// before resolveEntryContentDir() runs.
|
|
16
|
-
import "../../sources/providers/index.js";
|
|
17
|
-
import { warn } from "../../core/warn.js";
|
|
18
15
|
// ── Resolution ──────────────────────────────────────────────────────────────
|
|
19
16
|
/**
|
|
20
17
|
* Build the ordered list of stash sources, walking every configured stash
|
|
@@ -94,17 +94,12 @@ export function countUsageEventsByType(db, eventType) {
|
|
|
94
94
|
/**
|
|
95
95
|
* Delete usage events older than the given number of days.
|
|
96
96
|
*/
|
|
97
|
-
export function purgeOldUsageEvents(db, retentionDays
|
|
97
|
+
export function purgeOldUsageEvents(db, retentionDays) {
|
|
98
98
|
if (!Number.isFinite(retentionDays) || retentionDays <= 0)
|
|
99
99
|
return;
|
|
100
100
|
try {
|
|
101
|
-
const stateSchema = options?.stateSchema;
|
|
102
|
-
if (stateSchema !== undefined && !/^[A-Za-z_][A-Za-z0-9_]*$/.test(stateSchema)) {
|
|
103
|
-
throw new Error("Invalid attached state schema name.");
|
|
104
|
-
}
|
|
105
|
-
const usageEvents = stateSchema === undefined ? "usage_events" : `"${stateSchema}".usage_events`;
|
|
106
101
|
const cutoff = new Date(Date.now() - retentionDays * 86_400_000).toISOString();
|
|
107
|
-
db.prepare(
|
|
102
|
+
db.prepare("DELETE FROM usage_events WHERE created_at < ?").run(cutoff);
|
|
108
103
|
}
|
|
109
104
|
catch {
|
|
110
105
|
/* Table may not exist yet */
|