akm-cli 0.9.16 → 0.9.17-alpha.10
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 +2101 -0
- package/STABILITY.md +11 -10
- package/dist/akm +124 -193
- package/dist/akm-migrate +38 -19
- package/dist/assets/hints/cli-hints-full.md +6 -7
- package/dist/assets/improve-strategies/catchup.json +0 -3
- package/dist/assets/improve-strategies/consolidate.json +0 -1
- package/dist/assets/improve-strategies/default.json +1 -2
- package/dist/assets/improve-strategies/proactive-maintenance.json +1 -2
- package/dist/assets/improve-strategies/quick.json +1 -2
- package/dist/assets/improve-strategies/reflect-distill.json +1 -2
- package/dist/assets/improve-strategies/thorough.json +0 -3
- package/dist/assets/prompts/consolidate-pair.md +20 -0
- package/dist/assets/prompts/consolidate-system.md +4 -11
- package/dist/assets/prompts/retrieval-relevance-judge.md +6 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +20 -20
- package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -2
- package/dist/assets/templates/html/health.html +3 -5
- package/dist/cli/retired-commands.js +1 -1
- package/dist/cli/shared.js +6 -2
- package/dist/cli/unknown-flags.js +24 -1
- package/dist/cli.js +68 -10
- 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 +6 -0
- package/dist/commands/health/archive-usage.js +92 -0
- package/dist/commands/health/checks.js +83 -74
- package/dist/commands/health/config-skew.js +38 -0
- package/dist/commands/health/data-dir-usage.js +25 -13
- package/dist/commands/health/egress.js +54 -0
- package/dist/commands/health/html-report.js +1 -42
- package/dist/commands/health/improve-metrics.js +136 -591
- package/dist/commands/health/md-report.js +1 -6
- 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 +14 -120
- package/dist/commands/health/types-improve.js +4 -19
- package/dist/commands/health/windows.js +64 -74
- package/dist/commands/health.js +145 -143
- package/dist/commands/improve/consolidate/chunking.js +26 -117
- package/dist/commands/improve/consolidate/continuity-check.js +137 -0
- package/dist/commands/improve/consolidate/pair-pass.js +791 -0
- package/dist/commands/improve/consolidate/sanitize.js +54 -149
- package/dist/commands/improve/consolidate.js +589 -1127
- 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 -1041
- package/dist/commands/improve/eligibility.js +126 -390
- package/dist/commands/improve/execution.js +8 -10
- package/dist/commands/improve/extract-prompt.js +1 -2
- package/dist/commands/improve/extract.js +487 -1046
- package/dist/commands/improve/feedback-valence.js +0 -25
- package/dist/commands/improve/improve-cli.js +75 -169
- package/dist/commands/improve/improve-result-file.js +10 -66
- package/dist/commands/improve/improve-strategies.js +52 -4
- package/dist/commands/improve/improve-usage-report.js +18 -64
- package/dist/commands/improve/improve.js +480 -1074
- package/dist/commands/improve/ledger.js +119 -0
- package/dist/commands/improve/locks.js +2 -8
- package/dist/commands/improve/loop-stages.js +415 -1073
- package/dist/commands/improve/memory/derived-ref.js +12 -77
- package/dist/commands/improve/memory/memory-belief.js +16 -118
- package/dist/commands/improve/memory/memory-improve.js +266 -14
- package/dist/commands/improve/outcome-loop.js +28 -156
- package/dist/commands/improve/planner.js +5 -15
- package/dist/commands/improve/preparation.js +779 -2319
- package/dist/commands/improve/proactive-maintenance.js +34 -101
- package/dist/commands/improve/reflect-noise.js +104 -280
- package/dist/commands/improve/reflect.js +642 -1353
- package/dist/commands/improve/retrieval-gate.js +127 -0
- package/dist/commands/improve/retrieval-scope.js +92 -0
- package/dist/commands/improve/salience.js +41 -240
- package/dist/commands/improve/session-asset.js +19 -100
- package/dist/commands/improve/stage.js +322 -0
- package/dist/commands/lint/base-linter.js +37 -15
- package/dist/commands/proposal/drain.js +261 -578
- package/dist/commands/proposal/proposal-cli.js +19 -20
- package/dist/commands/proposal/proposal-types.js +31 -24
- package/dist/commands/proposal/proposal.js +38 -8
- package/dist/commands/proposal/propose.js +134 -160
- package/dist/commands/proposal/repository.js +1097 -1394
- 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 +22 -89
- package/dist/commands/read/curate.js +105 -462
- package/dist/commands/read/knowledge.js +3 -2
- package/dist/commands/read/search-cli.js +16 -33
- package/dist/commands/read/search.js +17 -23
- package/dist/commands/read/show.js +57 -108
- package/dist/commands/sources/bundle-cli.js +25 -2
- package/dist/commands/sources/bundle-config-ops.js +4 -0
- package/dist/commands/sources/dangerous-env-audit.js +1 -2
- package/dist/commands/sources/info.js +127 -29
- 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/source-manage.js +9 -2
- package/dist/commands/sources/stash-cli.js +24 -4
- package/dist/commands/tasks/explain.js +10 -13
- package/dist/commands/tasks/tasks-cli.js +12 -13
- package/dist/commands/tasks/tasks.js +350 -936
- package/dist/commands/tasks/validate.js +26 -24
- package/dist/commands/workflow/plan.js +22 -29
- package/dist/commands/workflow-cli.js +4 -4
- package/dist/core/adapter/adapters/akm-adapter.js +2 -1
- package/dist/core/adapter/adapters/akm-lint.js +2 -3
- package/dist/core/adapter/adapters/akm-metadata.js +42 -12
- package/dist/core/adapter/adapters/akm-task-adapter.js +29 -8
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
- package/dist/core/adapter/execution-source.js +17 -29
- package/dist/core/asset/asset-placement.js +4 -13
- package/dist/core/asset/frontmatter.js +106 -1
- package/dist/core/asset/resolve-ref.js +1 -1
- package/dist/core/bundle-id.js +42 -5
- package/dist/core/bundle-rename.js +285 -0
- package/dist/core/config/config-io.js +1 -2
- package/dist/core/config/config-schema.js +9 -34
- package/dist/core/config/config-walker.js +1 -1
- package/dist/core/config/config.js +184 -111
- package/dist/core/config/engine-semantics.js +0 -2
- 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 +54 -125
- package/dist/core/config/schema/improve.js +4 -42
- package/dist/core/config/schema/index-config.js +9 -48
- 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-change.js +13 -5
- package/dist/core/file-lock.js +76 -173
- package/dist/core/improve-result.js +35 -7
- package/dist/core/improve-types.js +0 -1
- package/dist/core/logs-db.js +2 -2
- package/dist/core/loopback.js +7 -12
- package/dist/core/non-task-input.js +20 -0
- package/dist/core/parse.js +13 -16
- package/dist/core/paths.js +0 -24
- 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 +123 -61
- package/dist/core/state-db-scope.js +2 -4
- package/dist/core/state-db.js +126 -692
- package/dist/core/time.js +0 -20
- package/dist/core/type-presentation.js +1 -9
- package/dist/core/write-source.js +294 -1005
- 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/llm-cache.js +2 -2
- package/dist/indexer/ensure-index.js +77 -73
- 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 +141 -154
- package/dist/indexer/indexer.js +400 -1124
- package/dist/indexer/links/declared-links.js +90 -0
- package/dist/indexer/materialize-embeddings.js +60 -397
- package/dist/indexer/passes/memory-inference.js +96 -90
- package/dist/indexer/passes/metadata.js +132 -219
- package/dist/indexer/read-preflight.js +0 -7
- package/dist/indexer/scan/doc-to-entry.js +2 -3
- package/dist/indexer/scan/drain-dir.js +1 -1
- package/dist/indexer/search/db-search.js +190 -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 +36 -7
- package/dist/indexer/walk/walker.js +3 -4
- 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/client.js +18 -6
- package/dist/llm/embedders/deterministic.js +2 -3
- package/dist/llm/embedders/profile.js +71 -0
- package/dist/llm/embedders/remote.js +11 -17
- package/dist/llm/feature-gate.js +0 -8
- package/dist/llm/index-passes.js +3 -5
- package/dist/llm/memory-infer.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 +25 -38
- package/dist/output/shapes/passthrough.js +1 -9
- package/dist/{indexer/graph/graph-types.js → output/text/bundle-rename.js} +4 -1
- package/dist/output/text/command-format.js +69 -31
- package/dist/output/text/helpers.js +1 -1
- package/dist/output/text/migrate.js +5 -14
- package/dist/output/text/proposal-format.js +48 -3
- package/dist/output/text/show-format.js +13 -17
- 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 +42948 -92369
- package/dist/scripts/akm-migrate.js +42935 -92354
- 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/connection.js +5 -6
- package/dist/setup/steps/platforms.js +2 -2
- 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 +83 -21
- 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 +209 -0
- package/dist/storage/repositories/index-connection.js +39 -72
- package/dist/storage/repositories/index-entries-repository.js +131 -129
- package/dist/storage/repositories/index-entry-mapper.js +1 -2
- package/dist/storage/repositories/index-entry-schema.js +101 -268
- package/dist/storage/repositories/index-fts-repository.js +86 -256
- package/dist/storage/repositories/index-links-repository.js +143 -0
- package/dist/storage/repositories/index-llm-cache-repository.js +7 -9
- package/dist/storage/repositories/index-meta-repository.js +6 -4
- package/dist/storage/repositories/index-schema.js +257 -325
- 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 +104 -1
- package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
- package/dist/storage/repositories/salience-repository.js +1 -19
- 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 +130 -0
- package/dist/tasks/activation-config.js +134 -62
- package/dist/tasks/backends/cron.js +191 -302
- package/dist/tasks/backends/exec-utils.js +2 -5
- package/dist/tasks/backends/launchd.js +141 -748
- package/dist/tasks/backends/schtasks.js +119 -623
- 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/run/load-task.js +1 -1
- package/dist/tasks/scheduler-binding.js +20 -238
- package/dist/tasks/scheduler-invocation.js +136 -244
- package/dist/tasks/scheduler-lock.js +53 -0
- package/dist/tasks/scheduler-sync.js +368 -679
- package/dist/tasks/source/parse-task-source.js +55 -9
- package/dist/tasks/source/task-source-v3-frozen.js +3 -4
- package/dist/tasks/source/task-to-v4.js +464 -88
- 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 +43 -0
- package/docs/migration/v0.9.1-to-v0.9.2.md +23 -7
- package/docs/reference/cli.md +232 -135
- package/docs/reference/configuration.md +71 -57
- package/docs/reference/data-and-telemetry.md +20 -21
- package/docs/reference/tasks.md +105 -39
- 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 +115 -738
- package/schemas/akm-workflow.json +1 -0
- package/dist/assets/improve-strategies/graph-refresh.json +0 -15
- package/dist/assets/prompts/contradiction-judge.md +0 -33
- package/dist/assets/prompts/graph-extract-system.md +0 -1
- package/dist/assets/prompts/graph-extract-user-prompt.md +0 -35
- package/dist/assets/prompts/metadata-enhance-system.md +0 -1
- package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +0 -4
- 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 -149
- package/dist/commands/improve/distill/promote-memory.js +0 -291
- package/dist/commands/improve/distill/quality-gate.js +0 -337
- package/dist/commands/improve/eval-cases.js +0 -52
- 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 -31
- 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/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/db/graph-db.js +0 -444
- package/dist/indexer/graph/graph-boost.js +0 -427
- package/dist/indexer/graph/graph-dedup.js +0 -95
- package/dist/indexer/graph/graph-extraction.js +0 -1108
- package/dist/indexer/search/name-match.js +0 -35
- package/dist/indexer/search/ranking-contributors.js +0 -515
- package/dist/indexer/search/ranking-types.js +0 -4
- 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/llm/graph-extract.js +0 -728
- package/dist/llm/metadata-enhance.js +0 -96
- 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/tasks/source/task-to-v3.js +0 -507
- 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 -77
- 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
|
@@ -8,11 +8,12 @@
|
|
|
8
8
|
* (block scalars, multi-line strings, nested objects, flow sequences, escape
|
|
9
9
|
* sequences) is handled correctly without a brittle hand-rolled state machine.
|
|
10
10
|
*/
|
|
11
|
+
import { createHash } from "node:crypto";
|
|
11
12
|
import fs from "node:fs";
|
|
12
13
|
import { parse as yamlParse, stringify as yamlStringify } from "yaml";
|
|
13
14
|
import { existingFileMode, writeFileAtomic } from "../common.js";
|
|
14
15
|
import { recordWrittenPath } from "../write-provenance.js";
|
|
15
|
-
import { assembleAsset, serializeFrontmatter } from "./asset-serialize.js";
|
|
16
|
+
import { assembleAsset, assembleAssetFromString, serializeFrontmatter } from "./asset-serialize.js";
|
|
16
17
|
/**
|
|
17
18
|
* Parse YAML frontmatter from a Markdown (or similar) string.
|
|
18
19
|
*
|
|
@@ -451,6 +452,110 @@ export function parseYamlScalar(value) {
|
|
|
451
452
|
}
|
|
452
453
|
return value;
|
|
453
454
|
}
|
|
455
|
+
// ── Bookkeeping-insensitive freshness (STALE, R20) ────────────────────────────
|
|
456
|
+
/**
|
|
457
|
+
* Frontmatter keys akm's own improve/index passes write into an EXISTING
|
|
458
|
+
* asset as pipeline bookkeeping — never a change to the asset's authored
|
|
459
|
+
* meaning. `salience`/`salienceInputs` come from {@link writeSalienceToFrontmatter}
|
|
460
|
+
* (distill); `inferenceProcessed` comes from memory inference
|
|
461
|
+
* (`indexer/passes/memory-inference.ts`). A pending proposal's mint-time
|
|
462
|
+
* before-hash is sensitive to these rewrites even though nothing a reviewer
|
|
463
|
+
* or the proposal's own diff cares about actually changed, which stales out
|
|
464
|
+
* proposals that a same-run bookkeeping pass touches after mint. See
|
|
465
|
+
* {@link computeNormalizedContentHash} and {@link carryForwardBookkeepingFrontmatter}.
|
|
466
|
+
*
|
|
467
|
+
* Deliberately excludes `beliefState`/`contradictedBy`/`supersededBy`: those
|
|
468
|
+
* are editorial state (contradiction/supersession demotions), not derived
|
|
469
|
+
* audit metadata — a promote should still refuse when one of those changed
|
|
470
|
+
* underneath a pending proposal.
|
|
471
|
+
*/
|
|
472
|
+
export const BOOKKEEPING_FRONTMATTER_KEYS = ["salience", "salienceInputs", "inferenceProcessed"];
|
|
473
|
+
function sha256Hex(content) {
|
|
474
|
+
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Hash of `raw` with {@link BOOKKEEPING_FRONTMATTER_KEYS} removed from its
|
|
478
|
+
* frontmatter and the remaining frontmatter re-serialized with sorted keys,
|
|
479
|
+
* so pipeline-only bookkeeping rewrites (and incidental key-order churn)
|
|
480
|
+
* cannot change the result. The body's boundary is normalized the same way
|
|
481
|
+
* {@link assembleAssetFromString} normalizes it (leading newlines stripped,
|
|
482
|
+
* exactly one trailing newline) before hashing, so the body-boundary shift
|
|
483
|
+
* that `writeSalienceToFrontmatter` and the `assembleAsset` bookkeeping
|
|
484
|
+
* rewrite both introduce cannot change the result either; the body's
|
|
485
|
+
* interior is still hashed verbatim.
|
|
486
|
+
*
|
|
487
|
+
* An empty frontmatter block (`---\n---\n…`) is treated as `{}` rather than
|
|
488
|
+
* falling back to the raw hash, so adding bookkeeping keys to a memory with
|
|
489
|
+
* no prior frontmatter fields is still insensitive.
|
|
490
|
+
*
|
|
491
|
+
* Falls back to hashing `raw` verbatim when there is no frontmatter block at
|
|
492
|
+
* all or it fails to parse — normalizing unparsable frontmatter isn't safe,
|
|
493
|
+
* and the caller's existing raw-hash check already covers that case.
|
|
494
|
+
*/
|
|
495
|
+
export function computeNormalizedContentHash(raw) {
|
|
496
|
+
const block = parseFrontmatterBlock(raw);
|
|
497
|
+
if (!block)
|
|
498
|
+
return sha256Hex(raw);
|
|
499
|
+
let data;
|
|
500
|
+
try {
|
|
501
|
+
data = block.frontmatter.trim() ? yamlParse(block.frontmatter) : {};
|
|
502
|
+
}
|
|
503
|
+
catch {
|
|
504
|
+
return sha256Hex(raw);
|
|
505
|
+
}
|
|
506
|
+
if (data === null || typeof data !== "object" || Array.isArray(data))
|
|
507
|
+
return sha256Hex(raw);
|
|
508
|
+
const normalized = { ...data };
|
|
509
|
+
for (const key of BOOKKEEPING_FRONTMATTER_KEYS)
|
|
510
|
+
delete normalized[key];
|
|
511
|
+
const canonicalFrontmatter = yamlStringify(normalized, { sortMapEntries: true }).trimEnd();
|
|
512
|
+
return sha256Hex(assembleAssetFromString(canonicalFrontmatter, block.content));
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Fold the live target's {@link BOOKKEEPING_FRONTMATTER_KEYS} into
|
|
516
|
+
* `proposedRaw` wherever the proposal's own frontmatter does not already set
|
|
517
|
+
* them, so promoting a proposal minted before a bookkeeping rewrite never
|
|
518
|
+
* drops that bookkeeping (STALE, R20) — e.g. dropping `inferenceProcessed`
|
|
519
|
+
* would make memory inference reprocess the memory.
|
|
520
|
+
*
|
|
521
|
+
* Returns `proposedRaw` unchanged when either side has no parseable
|
|
522
|
+
* frontmatter block or nothing needs to carry forward.
|
|
523
|
+
*/
|
|
524
|
+
export function carryForwardBookkeepingFrontmatter(proposedRaw, liveRaw) {
|
|
525
|
+
const proposedBlock = parseFrontmatterBlock(proposedRaw);
|
|
526
|
+
const liveBlock = parseFrontmatterBlock(liveRaw);
|
|
527
|
+
if (!proposedBlock || !liveBlock)
|
|
528
|
+
return proposedRaw;
|
|
529
|
+
let proposedData;
|
|
530
|
+
let liveData;
|
|
531
|
+
try {
|
|
532
|
+
const parsedProposed = proposedBlock.frontmatter.trim() ? yamlParse(proposedBlock.frontmatter) : {};
|
|
533
|
+
const parsedLive = liveBlock.frontmatter.trim() ? yamlParse(liveBlock.frontmatter) : {};
|
|
534
|
+
if (parsedProposed === null || typeof parsedProposed !== "object" || Array.isArray(parsedProposed)) {
|
|
535
|
+
return proposedRaw;
|
|
536
|
+
}
|
|
537
|
+
if (parsedLive === null || typeof parsedLive !== "object" || Array.isArray(parsedLive))
|
|
538
|
+
return proposedRaw;
|
|
539
|
+
proposedData = parsedProposed;
|
|
540
|
+
liveData = parsedLive;
|
|
541
|
+
}
|
|
542
|
+
catch {
|
|
543
|
+
return proposedRaw;
|
|
544
|
+
}
|
|
545
|
+
const merged = { ...proposedData };
|
|
546
|
+
let changed = false;
|
|
547
|
+
for (const key of BOOKKEEPING_FRONTMATTER_KEYS) {
|
|
548
|
+
if (!(key in merged) && key in liveData) {
|
|
549
|
+
merged[key] = liveData[key];
|
|
550
|
+
changed = true;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
if (!changed)
|
|
554
|
+
return proposedRaw;
|
|
555
|
+
const newFrontmatter = yamlStringify(merged).trimEnd();
|
|
556
|
+
const separator = proposedBlock.content.startsWith("\n") ? "" : "\n";
|
|
557
|
+
return `---\n${newFrontmatter}\n---\n${separator}${proposedBlock.content}`;
|
|
558
|
+
}
|
|
454
559
|
// ── Minimum score delta to trigger a frontmatter salience rewrite ─────────────
|
|
455
560
|
const SALIENCE_WRITE_DELTA_THRESHOLD = 0.05;
|
|
456
561
|
/**
|
|
@@ -89,7 +89,7 @@ function notFound(conceptId, triedForms, ctx) {
|
|
|
89
89
|
*
|
|
90
90
|
* Exported (Chunk-8 WI-8.5c) as the ONE conceptId derivation the improve
|
|
91
91
|
* correlation sites (`eligibility.ts` candidate refs, `salience.ts`
|
|
92
|
-
* last-use lookup
|
|
92
|
+
* last-use lookup) share with the
|
|
93
93
|
* display rule — the permanent successor to the retired transient
|
|
94
94
|
* `legacyConceptId`.
|
|
95
95
|
*/
|
package/dist/core/bundle-id.js
CHANGED
|
@@ -4,18 +4,55 @@
|
|
|
4
4
|
import crypto from "node:crypto";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { isBundleSlug } from "./asset/asset-ref.js";
|
|
7
|
+
import { UsageError } from "./errors.js";
|
|
7
8
|
/** Deterministic, filesystem-safe bundle slug from a source path. */
|
|
8
9
|
export function slugForPath(sourcePath) {
|
|
9
10
|
const resolved = path.resolve(sourcePath);
|
|
10
|
-
const base = path
|
|
11
|
-
.basename(resolved)
|
|
12
|
-
.toLowerCase()
|
|
13
|
-
.replace(/[^a-z0-9]+/g, "-")
|
|
14
|
-
.replace(/^-+|-+$/g, "");
|
|
11
|
+
const base = slugify(path.basename(resolved));
|
|
15
12
|
if (base.length > 0)
|
|
16
13
|
return base;
|
|
17
14
|
return `bundle-${shortHash(resolved)}`;
|
|
18
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Bundle slug from the package/repo name a registry install id names
|
|
18
|
+
* (`npm:@scope/pkg` → `pkg`, `github:owner/repo` → `repo`,
|
|
19
|
+
* `git:https://host/owner/repo` → `repo`); empty when it names none.
|
|
20
|
+
*/
|
|
21
|
+
export function slugForRegistryId(registryId) {
|
|
22
|
+
const locator = registryId.slice(registryId.indexOf(":") + 1);
|
|
23
|
+
return slugify(locator.split(/[/:]/).filter(Boolean).pop() ?? "");
|
|
24
|
+
}
|
|
25
|
+
function slugify(name) {
|
|
26
|
+
return name
|
|
27
|
+
.toLowerCase()
|
|
28
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
29
|
+
.replace(/^-+|-+$/g, "");
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Enforce the `--name` contract on every add path (D6): an explicit name is
|
|
33
|
+
* not a silent hint that may fall back to a derived id — it is a request
|
|
34
|
+
* that either succeeds exactly or fails loudly, before anything is written.
|
|
35
|
+
*
|
|
36
|
+
* `existingKeyForThisInstall` is the bundle key this exact source (path/URL/
|
|
37
|
+
* registry id) is already configured under, if any. Re-adding that same
|
|
38
|
+
* source under a *different* explicit name is rejected in favor of
|
|
39
|
+
* `akm bundle rename`, which is the one command allowed to change a bundle's
|
|
40
|
+
* key. A caller with no existing install for this source (the common case)
|
|
41
|
+
* omits it.
|
|
42
|
+
*/
|
|
43
|
+
export function validateExplicitBundleName(bundles, name, existingKeyForThisInstall) {
|
|
44
|
+
if (!isBundleSlug(name)) {
|
|
45
|
+
throw new UsageError(`"${name}" is not a legal bundle name: it must not contain whitespace or the characters ":" "." "#" "/".`, "INVALID_FLAG_VALUE");
|
|
46
|
+
}
|
|
47
|
+
if (existingKeyForThisInstall !== undefined && name !== existingKeyForThisInstall) {
|
|
48
|
+
throw new UsageError(`This source is already installed as bundle "${existingKeyForThisInstall}". Re-adding it under a ` +
|
|
49
|
+
`different name is not supported — run \`akm bundle rename ${existingKeyForThisInstall} ${name}\` instead.`, "INVALID_FLAG_VALUE");
|
|
50
|
+
}
|
|
51
|
+
if (Object.hasOwn(bundles, name) && name !== existingKeyForThisInstall) {
|
|
52
|
+
throw new UsageError(`Bundle name "${name}" already exists. Choose another name, or run ` +
|
|
53
|
+
`\`akm bundle rename ${name} <new-name>\` first.`, "INVALID_FLAG_VALUE");
|
|
54
|
+
}
|
|
55
|
+
}
|
|
19
56
|
/** Derive one batch-unique bundle id. */
|
|
20
57
|
export function deriveBundleId(registryId, sourcePath, usedIds) {
|
|
21
58
|
const preferred = registryId && registryId.length > 0 && isBundleSlug(registryId) ? registryId : slugForPath(sourcePath);
|
|
@@ -0,0 +1,285 @@
|
|
|
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
|
+
* `akm bundle rename <old> <new>` (D6) — the one command allowed to change a
|
|
6
|
+
* bundle's key. A bundle id is a mass identity prefix: every durable ref this
|
|
7
|
+
* tool minted (`entries.item_ref`/`bundle_id`, `proposals.ref`, a pending
|
|
8
|
+
* proposal's `proposedTarget.source`, a workflow task_history row's
|
|
9
|
+
* `target_ref`, `scheduler.enabled[].ref`) carries it, and a hand-edited
|
|
10
|
+
* `bundles` key strands all of it (see `src/indexer/bundle-identity-guard.ts`
|
|
11
|
+
* §11.5). This module is the rekey transaction that guard's docstring points
|
|
12
|
+
* to.
|
|
13
|
+
*
|
|
14
|
+
* Rewritten: the config `bundles` key, `defaultBundle`/`defaultWriteTarget`
|
|
15
|
+
* when they name the old id, every `scheduler.enabled[].ref` with the old
|
|
16
|
+
* `//` prefix, the lockfile entry id, and the index/state rows above.
|
|
17
|
+
*
|
|
18
|
+
* Reported, never rewritten: refs inside bundle CONTENT (xrefs,
|
|
19
|
+
* `supersededBy`, task `uses:`) — rewriting a bundle's own files is
|
|
20
|
+
* `akm mv`/editing territory, not a config-identity rename. `--dry-run`
|
|
21
|
+
* (and every real run) lists the indexed files that still spell the old
|
|
22
|
+
* `<old>//` prefix so the operator can follow up.
|
|
23
|
+
*
|
|
24
|
+
* Left behind on purpose: `usage_events`/`asset_salience`/`asset_outcome`
|
|
25
|
+
* keyed by the old bundle prefix (learned utility/outcome history) — outside
|
|
26
|
+
* this item's scope; `warnOnBundleRenameDrift` already documents this class
|
|
27
|
+
* of gap and `akm index --full` re-mints under the new id.
|
|
28
|
+
*/
|
|
29
|
+
import fs from "node:fs";
|
|
30
|
+
import { readLockfile, renameLockEntry } from "../integrations/lockfile.js";
|
|
31
|
+
import { closeDatabase, openIndexDatabase, openReadonlyExistingDatabase, } from "../storage/repositories/index-connection.js";
|
|
32
|
+
import { getFilePathsByBundle, renameEntriesBundleId } from "../storage/repositories/index-entries-repository.js";
|
|
33
|
+
import { countProposalsForBundleRename, renameProposalsBundleRef } from "../storage/repositories/proposals-repository.js";
|
|
34
|
+
import { countTaskHistoryTargetRefs, renameTaskHistoryTargetRefs, } from "../storage/repositories/task-history-repository.js";
|
|
35
|
+
import { selectBackend } from "../tasks/backends/index.js";
|
|
36
|
+
import { bundleRefToString, parseBundleRef } from "./asset/asset-ref.js";
|
|
37
|
+
import { validateExplicitBundleName } from "./bundle-id.js";
|
|
38
|
+
import { loadConfig, mutateConfig } from "./config/config.js";
|
|
39
|
+
import { NotFoundError, UsageError } from "./errors.js";
|
|
40
|
+
import { getDbPath } from "./paths.js";
|
|
41
|
+
import { getStateDbPath, withStateDb } from "./state-db.js";
|
|
42
|
+
/** Throws if `newId` cannot become `oldId`'s new key. Re-run under the config lock at apply time. */
|
|
43
|
+
function validateRename(config, oldId, newId) {
|
|
44
|
+
if (!config.bundles?.[oldId]) {
|
|
45
|
+
throw new NotFoundError(`No configured bundle named "${oldId}".`, "SOURCE_NOT_FOUND");
|
|
46
|
+
}
|
|
47
|
+
if (oldId === newId) {
|
|
48
|
+
throw new UsageError(`"${oldId}" and "${newId}" are the same bundle name.`, "INVALID_FLAG_VALUE");
|
|
49
|
+
}
|
|
50
|
+
const withoutOld = { ...(config.bundles ?? {}) };
|
|
51
|
+
delete withoutOld[oldId];
|
|
52
|
+
validateExplicitBundleName(withoutOld, newId);
|
|
53
|
+
}
|
|
54
|
+
function bundleOfRef(ref) {
|
|
55
|
+
try {
|
|
56
|
+
return parseBundleRef(ref).bundle;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function schedulerRefsToRewrite(config, oldId) {
|
|
63
|
+
return (config.scheduler?.enabled ?? []).filter((ref) => bundleOfRef(ref) === oldId);
|
|
64
|
+
}
|
|
65
|
+
function renameBundleInConfig(config, oldId, newId) {
|
|
66
|
+
validateRename(config, oldId, newId);
|
|
67
|
+
const bundles = {};
|
|
68
|
+
for (const [key, entry] of Object.entries(config.bundles ?? {})) {
|
|
69
|
+
bundles[key === oldId ? newId : key] = entry;
|
|
70
|
+
}
|
|
71
|
+
const enabled = config.scheduler?.enabled;
|
|
72
|
+
const scheduler = enabled === undefined
|
|
73
|
+
? config.scheduler
|
|
74
|
+
: {
|
|
75
|
+
...config.scheduler,
|
|
76
|
+
enabled: enabled.map((ref) => bundleOfRef(ref) === oldId ? bundleRefToString({ ...parseBundleRef(ref), bundle: newId }) : ref),
|
|
77
|
+
};
|
|
78
|
+
return {
|
|
79
|
+
...config,
|
|
80
|
+
bundles,
|
|
81
|
+
...(config.defaultBundle === oldId ? { defaultBundle: newId } : {}),
|
|
82
|
+
...(config.defaultWriteTarget === oldId ? { defaultWriteTarget: newId } : {}),
|
|
83
|
+
...(scheduler ? { scheduler } : {}),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/** Indexed files (already read into `filePaths`) whose content still spells `<oldId>//`, best-effort. */
|
|
87
|
+
function filesStillMentioning(filePaths, oldId) {
|
|
88
|
+
const needle = `${oldId}//`;
|
|
89
|
+
const hits = [];
|
|
90
|
+
for (const filePath of filePaths) {
|
|
91
|
+
try {
|
|
92
|
+
if (fs.readFileSync(filePath, "utf8").includes(needle))
|
|
93
|
+
hits.push(filePath);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// Best-effort: a since-removed or unreadable file is skipped, not fatal —
|
|
97
|
+
// this scan is a report, not a correctness requirement.
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return hits;
|
|
101
|
+
}
|
|
102
|
+
/** True when an installed binding's invocation names `bundleId` — either as a `task run` entry's `--bundle <id>` argument, or as a `workflow run <id>//…` ref. */
|
|
103
|
+
function invocationNamesBundle(invocation, bundleId) {
|
|
104
|
+
if (!invocation)
|
|
105
|
+
return false;
|
|
106
|
+
for (let i = 0; i < invocation.length; i++) {
|
|
107
|
+
const token = invocation[i];
|
|
108
|
+
if (token === bundleId && invocation[i - 1] === "--bundle")
|
|
109
|
+
return true;
|
|
110
|
+
if (token?.startsWith(`${bundleId}//`))
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Installed native scheduler rows whose invocation names `oldId`, best-effort
|
|
117
|
+
* (a backend whose listing throws reports none — this is a preview, not a
|
|
118
|
+
* correctness requirement).
|
|
119
|
+
*/
|
|
120
|
+
async function nativeSchedulerRowsNamingBundle(sched, oldId) {
|
|
121
|
+
try {
|
|
122
|
+
return (await sched.list())
|
|
123
|
+
.filter((entry) => invocationNamesBundle(entry.invocation, oldId))
|
|
124
|
+
.map((entry) => entry.invocation?.join(" ") ?? entry.id);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Build the rename plan — every count and ref this rename would touch — by
|
|
132
|
+
* reading, never writing. Shared by `--dry-run` and a real run so the two
|
|
133
|
+
* can never disagree about what was reported vs. what happened.
|
|
134
|
+
*/
|
|
135
|
+
async function buildPlan(config, oldId, newId, sched) {
|
|
136
|
+
const schedulerRefs = schedulerRefsToRewrite(config, oldId);
|
|
137
|
+
const lockPresent = readLockfile().some((entry) => entry.id === oldId);
|
|
138
|
+
const nativeSchedulerRows = await nativeSchedulerRowsNamingBundle(sched, oldId);
|
|
139
|
+
let indexEntries = 0;
|
|
140
|
+
let contentRefs = [];
|
|
141
|
+
const readIndexDb = openReadonlyExistingDatabase(getDbPath());
|
|
142
|
+
if (readIndexDb) {
|
|
143
|
+
try {
|
|
144
|
+
const filePaths = getFilePathsByBundle(readIndexDb, oldId);
|
|
145
|
+
indexEntries = filePaths.length;
|
|
146
|
+
contentRefs = filesStillMentioning(filePaths, oldId);
|
|
147
|
+
}
|
|
148
|
+
finally {
|
|
149
|
+
closeDatabase(readIndexDb);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
let proposalRefs = 0;
|
|
153
|
+
let proposalTargets = 0;
|
|
154
|
+
let taskHistoryRefs = 0;
|
|
155
|
+
if (fs.existsSync(getStateDbPath())) {
|
|
156
|
+
withStateDb((db) => {
|
|
157
|
+
const counts = countProposalsForBundleRename(db, oldId);
|
|
158
|
+
proposalRefs = counts.refs;
|
|
159
|
+
proposalTargets = counts.targets;
|
|
160
|
+
taskHistoryRefs = countTaskHistoryTargetRefs(db, oldId);
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
oldId,
|
|
165
|
+
newId,
|
|
166
|
+
config: {
|
|
167
|
+
defaultBundleChanges: config.defaultBundle === oldId,
|
|
168
|
+
defaultWriteTargetChanges: config.defaultWriteTarget === oldId,
|
|
169
|
+
schedulerRefs,
|
|
170
|
+
},
|
|
171
|
+
lock: { present: lockPresent },
|
|
172
|
+
index: { entries: indexEntries },
|
|
173
|
+
state: { proposalRefs, proposalTargets, taskHistoryRefs },
|
|
174
|
+
contentRefs,
|
|
175
|
+
nativeSchedulerRows,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function taskSyncErrorMessage(cause) {
|
|
179
|
+
return cause instanceof Error ? cause.message : String(cause);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Remove every installed native row still naming `oldId`, best-effort,
|
|
183
|
+
* before the sync below installs the new-named ones. Without this, a plain
|
|
184
|
+
* `akmTasksSync(deps, newId)` alone cannot clean the old rows up: a task
|
|
185
|
+
* binding's native id depends only on the task id, so the OLD row and the
|
|
186
|
+
* new one it should become collide on that id, and `installedRowScope`
|
|
187
|
+
* (scheduler-sync.ts) attributes a row by the bundle NAME it was installed
|
|
188
|
+
* under — the physical-path check only confirms a name match, it never
|
|
189
|
+
* substitutes for one — so the sync reports the old row as another bundle's
|
|
190
|
+
* and leaves the new binding out instead of replacing it. A workflow binding's native id
|
|
191
|
+
* depends on its fully-qualified ref instead, so old and new never collide
|
|
192
|
+
* at all and the old row would otherwise be left installed forever. Both
|
|
193
|
+
* fail the same way this function fixes: uninstall the exact native rows
|
|
194
|
+
* `nativeSchedulerRowsNamingBundle` already found, by their native id, so
|
|
195
|
+
* the sync that follows starts from a clean slate.
|
|
196
|
+
*/
|
|
197
|
+
async function removeStaleNativeSchedulerRows(sched, oldId) {
|
|
198
|
+
let installed;
|
|
199
|
+
try {
|
|
200
|
+
installed = await sched.list();
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
for (const entry of installed) {
|
|
206
|
+
if (!invocationNamesBundle(entry.invocation, oldId))
|
|
207
|
+
continue;
|
|
208
|
+
try {
|
|
209
|
+
await sched.uninstall(entry.nativeId ?? entry.id);
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
// Best-effort: a row this process can't remove is left for the
|
|
213
|
+
// operator's own `akm task sync` to reconcile, same as every other
|
|
214
|
+
// per-item failure the sync itself reports rather than throws.
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Rename a configured bundle's key everywhere akm itself persists it. With
|
|
220
|
+
* `dryRun: true`, only {@link buildPlan} runs — nothing is written, matching
|
|
221
|
+
* a `applied: false` result the caller renders as the plan. `deps.backend`
|
|
222
|
+
* lets a caller (tests) inject a fake scheduler backend instead of the real
|
|
223
|
+
* OS one `selectBackend()` would otherwise pick. `deps.syncTasks` is how the
|
|
224
|
+
* caller runs the post-rename `akmTasksSync` — `src/core` sits below
|
|
225
|
+
* `src/commands` (see `src/core/improve-types.ts`'s note on the same
|
|
226
|
+
* direction), so this module never imports `akmTasksSync` itself; the real
|
|
227
|
+
* caller (`akm bundle rename`'s command handler) always supplies it, and is
|
|
228
|
+
* required so a missing wire-up is a type error, not a silently skipped sync.
|
|
229
|
+
*/
|
|
230
|
+
export async function renameBundle(oldId, newId, options = {}, deps) {
|
|
231
|
+
const config = loadConfig();
|
|
232
|
+
validateRename(config, oldId, newId);
|
|
233
|
+
const sched = deps.backend ?? selectBackend();
|
|
234
|
+
const plan = await buildPlan(config, oldId, newId, sched);
|
|
235
|
+
if (options.dryRun)
|
|
236
|
+
return { ...plan, applied: false };
|
|
237
|
+
// Config: the bundles key, defaultBundle/defaultWriteTarget, and every
|
|
238
|
+
// scheduler.enabled[].ref with the old `//` prefix — all under one write
|
|
239
|
+
// (validated again inside the config lock).
|
|
240
|
+
mutateConfig((current) => renameBundleInConfig(current, oldId, newId));
|
|
241
|
+
// Lockfile: the resolved-install entry id (no-op for a bundle with none,
|
|
242
|
+
// e.g. a plain filesystem bundle).
|
|
243
|
+
await renameLockEntry(oldId, newId);
|
|
244
|
+
// Index: bundle_id/item_ref on every entry row (see index-entries-repository's
|
|
245
|
+
// renameEntriesBundleId docstring for why no FTS/vector rebuild is needed).
|
|
246
|
+
const readIndexDb = openReadonlyExistingDatabase(getDbPath());
|
|
247
|
+
if (readIndexDb) {
|
|
248
|
+
closeDatabase(readIndexDb);
|
|
249
|
+
const writeIndexDb = openIndexDatabase(getDbPath());
|
|
250
|
+
try {
|
|
251
|
+
renameEntriesBundleId(writeIndexDb, oldId, newId);
|
|
252
|
+
}
|
|
253
|
+
finally {
|
|
254
|
+
closeDatabase(writeIndexDb);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// State: proposals.ref, a pending proposal's proposedTarget.source, and
|
|
258
|
+
// workflow task_history.target_ref.
|
|
259
|
+
if (fs.existsSync(getStateDbPath())) {
|
|
260
|
+
withStateDb((db) => {
|
|
261
|
+
renameProposalsBundleRef(db, oldId, newId);
|
|
262
|
+
renameTaskHistoryTargetRefs(db, oldId, newId);
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
// Scheduler: remove native rows still naming `<old>//` (see
|
|
266
|
+
// removeStaleNativeSchedulerRows for why a plain sync alone can't do
|
|
267
|
+
// this), then re-sync so a scheduled run's invocation stops naming the
|
|
268
|
+
// old bundle the moment the rename applies, instead of waiting on the
|
|
269
|
+
// operator to run `akm task sync` by hand. Reported, never thrown:
|
|
270
|
+
// config/index/state above are already renamed by this point, so a sync
|
|
271
|
+
// problem must not make the rename itself look like it failed. `ok` is
|
|
272
|
+
// false both when the sync call throws and when it comes back with
|
|
273
|
+
// `result.failures` — a binding that failed to prepare has already lost
|
|
274
|
+
// its old native row above and is not scheduled until a retry.
|
|
275
|
+
let taskSync;
|
|
276
|
+
try {
|
|
277
|
+
await removeStaleNativeSchedulerRows(sched, oldId);
|
|
278
|
+
const result = await deps.syncTasks(newId, sched);
|
|
279
|
+
taskSync = { ok: result.failures.length === 0, result };
|
|
280
|
+
}
|
|
281
|
+
catch (cause) {
|
|
282
|
+
taskSync = { ok: false, error: taskSyncErrorMessage(cause) };
|
|
283
|
+
}
|
|
284
|
+
return { ...plan, applied: true, taskSync };
|
|
285
|
+
}
|
|
@@ -124,8 +124,7 @@ function isTimestampedConfigBackup(name) {
|
|
|
124
124
|
* Keep the `keep` most-recently-modified entries of `dir` that `select`
|
|
125
125
|
* admits and remove the rest. Best-effort: an unreadable dir is a no-op, an
|
|
126
126
|
* unreadable entry sorts oldest (pruned first), and a failed removal is
|
|
127
|
-
* retried by the next call.
|
|
128
|
-
* migration snapshots (#897).
|
|
127
|
+
* retried by the next call.
|
|
129
128
|
*/
|
|
130
129
|
export function pruneToNewest(dir, keep, select) {
|
|
131
130
|
let entries;
|
|
@@ -39,8 +39,6 @@
|
|
|
39
39
|
* enforced at save time via `superRefine` on the top-level schema.
|
|
40
40
|
*/
|
|
41
41
|
import { z } from "zod";
|
|
42
|
-
import { bundleRefToString, parseBundleRef } from "../asset/asset-ref.js";
|
|
43
|
-
import { warnOnce } from "../warn.js";
|
|
44
42
|
import { BUILTIN_IMPROVE_STRATEGY_NAMES, IMPROVE_PROCESS_ENGINE_CAPABILITIES } from "./engine-semantics.js";
|
|
45
43
|
import { EmbeddingConnectionConfigSchema } from "./schema/embedding.js";
|
|
46
44
|
import { EnginesSchema } from "./schema/engines.js";
|
|
@@ -62,11 +60,11 @@ export { EngineConfigSchema, EnginesSchema, LlmConnectionConfigSchema, LlmProfil
|
|
|
62
60
|
export { ExperimentalConfigSchema } from "./schema/experimental.js";
|
|
63
61
|
export { FEEDBACK_FAILURE_MODES, FeedbackConfigSchema } from "./schema/feedback.js";
|
|
64
62
|
export { ImproveConfigSchema } from "./schema/improve.js";
|
|
65
|
-
export { ConsolidateProcessConfigSchema, DistillProcessConfigSchema, ExtractProcessConfigSchema,
|
|
63
|
+
export { ConsolidateProcessConfigSchema, DistillProcessConfigSchema, ExtractProcessConfigSchema, ImproveProcessConfigSchema, ImproveProfileConfigSchema, MemoryInferenceProcessConfigSchema, ProactiveMaintenanceProcessConfigSchema, ReflectProcessConfigSchema, TriageProcessConfigSchema, ValidationProcessConfigSchema, } from "./schema/improve-processes.js";
|
|
66
64
|
export { IndexConfigSchema, IndexPassConfigSchema } from "./schema/index-config.js";
|
|
67
65
|
export { OutputConfigSchema } from "./schema/output.js";
|
|
68
66
|
export { CURRENT_CONFIG_VERSION, LlmInvocationOverridesSchema } from "./schema/primitives.js";
|
|
69
|
-
export {
|
|
67
|
+
export { SchedulerConfigSchema } from "./schema/scheduler.js";
|
|
70
68
|
export { SearchConfigSchema } from "./schema/search.js";
|
|
71
69
|
export { SetupConfigSchema } from "./schema/setup.js";
|
|
72
70
|
export { BundleConfigEntrySchema, BundlesConfigSchema, RegistryConfigEntrySchema, SourceConfigEntrySchema, } from "./schema/sources-bundles.js";
|
|
@@ -143,11 +141,6 @@ const RETIRED_SOURCE_SHAPE_KEY_MESSAGES = {
|
|
|
143
141
|
};
|
|
144
142
|
export const AkmConfigSchema = AkmConfigBaseSchema.superRefine((config, ctx) => {
|
|
145
143
|
const raw = config;
|
|
146
|
-
for (const key of ["profiles", "llm", "agent", "features", "stashes", "modelAliases", "bindings", "writable"]) {
|
|
147
|
-
if (key in raw) {
|
|
148
|
-
warnOnce(`config:retired-key:${key}`, `Config key "${key}" is retired in 0.9 and is ignored; configure engines/improve.strategies/bundles.<id> instead.`);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
144
|
// Only the current source shape enters the runtime. There is no config
|
|
152
145
|
// compatibility path; `bundles` + `defaultBundle` fully supersede these keys.
|
|
153
146
|
for (const key of ["stashDir", "sources", "installed"]) {
|
|
@@ -200,31 +193,6 @@ export const AkmConfigSchema = AkmConfigBaseSchema.superRefine((config, ctx) =>
|
|
|
200
193
|
});
|
|
201
194
|
}
|
|
202
195
|
}
|
|
203
|
-
const activationKeys = new Set();
|
|
204
|
-
for (const [index, activation] of (config.scheduler?.enabled ?? []).entries()) {
|
|
205
|
-
try {
|
|
206
|
-
const parsed = parseBundleRef(activation.ref);
|
|
207
|
-
if (!parsed.bundle || parsed.fragment !== undefined || bundleRefToString(parsed) !== activation.ref) {
|
|
208
|
-
throw new Error("not canonical");
|
|
209
|
-
}
|
|
210
|
-
const key = `${activation.kind}\0${activation.ref}`;
|
|
211
|
-
if (activationKeys.has(key)) {
|
|
212
|
-
ctx.addIssue({
|
|
213
|
-
code: z.ZodIssueCode.custom,
|
|
214
|
-
path: ["scheduler", "enabled", index],
|
|
215
|
-
message: "duplicates an earlier scheduler activation",
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
activationKeys.add(key);
|
|
219
|
-
}
|
|
220
|
-
catch {
|
|
221
|
-
ctx.addIssue({
|
|
222
|
-
code: z.ZodIssueCode.custom,
|
|
223
|
-
path: ["scheduler", "enabled", index, "ref"],
|
|
224
|
-
message: "must be one canonical fully-qualified ref without a fragment",
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
196
|
for (const key of ["llm", "agent", "improve"]) {
|
|
229
197
|
if (config.defaults && key in config.defaults) {
|
|
230
198
|
ctx.addIssue({
|
|
@@ -272,6 +240,13 @@ export const AkmConfigSchema = AkmConfigBaseSchema.superRefine((config, ctx) =>
|
|
|
272
240
|
}
|
|
273
241
|
const defaultStrategy = config.defaults?.improveStrategy;
|
|
274
242
|
if (defaultStrategy &&
|
|
243
|
+
// graph-refresh is exempt from this eager, config-load-time check: unlike
|
|
244
|
+
// a plain typo, it is a specific, known retirement, and refusing it here
|
|
245
|
+
// would fail every command's config load (including `akm migrate apply`,
|
|
246
|
+
// the one that fixes this). `resolveImproveStrategy`
|
|
247
|
+
// (src/commands/improve/improve-strategies.ts) refuses it lazily, at
|
|
248
|
+
// improve-invocation time, with the same message either way.
|
|
249
|
+
defaultStrategy !== "graph-refresh" &&
|
|
275
250
|
!BUILTIN_IMPROVE_STRATEGY_NAMES.includes(defaultStrategy) &&
|
|
276
251
|
!config.improve?.strategies?.[defaultStrategy]) {
|
|
277
252
|
ctx.addIssue({
|
|
@@ -78,7 +78,7 @@ function unwrap(schema) {
|
|
|
78
78
|
* AkmConfig schema. Returns `undefined` if any path segment doesn't match a
|
|
79
79
|
* known schema field.
|
|
80
80
|
*/
|
|
81
|
-
function resolveSchemaAt(path, config, raw) {
|
|
81
|
+
export function resolveSchemaAt(path, config, raw) {
|
|
82
82
|
let schema = AkmConfigBaseSchema;
|
|
83
83
|
let existing = config;
|
|
84
84
|
for (const [index, segment] of path.entries()) {
|