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
package/dist/commands/health.js
CHANGED
|
@@ -5,36 +5,35 @@ import fs from "node:fs";
|
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { daysToMs, resolveStashDir } from "../core/common.js";
|
|
7
7
|
import { loadConfig } from "../core/config/config.js";
|
|
8
|
-
import { ConfigError, UsageError } from "../core/errors.js";
|
|
9
|
-
import { readEvents } from "../core/events.js";
|
|
10
|
-
import { openLogsDatabase } from "../core/logs-db.js";
|
|
8
|
+
import { ConfigError, rethrowIfTestIsolationError, UsageError } from "../core/errors.js";
|
|
11
9
|
import { classifyPathAccess, describeInaccessiblePath } from "../core/path-access.js";
|
|
12
10
|
import { getConfigPath, getDataDir, getDbPath, getStateDbPathInDataDir } from "../core/paths.js";
|
|
13
|
-
import { listExistingTableNames, listPendingStateMigrations,
|
|
11
|
+
import { listExistingTableNames, listPendingStateMigrations, openStateDatabaseWithReport, withStateDb, } from "../core/state-db.js";
|
|
14
12
|
import { DURATION_UNITS, parseDuration, parseSinceToIso } from "../core/time.js";
|
|
15
13
|
import { probeLlmEndpoint } from "../llm/client.js";
|
|
14
|
+
import { insertEvent } from "../storage/repositories/events-repository.js";
|
|
16
15
|
import { getExtractOutcomeCountsSince } from "../storage/repositories/extract-sessions-repository.js";
|
|
17
16
|
import { countImproveRunsSince } from "../storage/repositories/improve-runs-repository.js";
|
|
18
17
|
import { closeDatabase, openReadonlyExistingDatabase } from "../storage/repositories/index-connection.js";
|
|
19
18
|
import { getAllEntries } from "../storage/repositories/index-entries-repository.js";
|
|
20
19
|
import { queryTaskHistory } from "../storage/repositories/task-history-repository.js";
|
|
20
|
+
import { getStateDbFreelistInfo, runStateDbQuickCheck } from "../storage/state-db-integrity.js";
|
|
21
21
|
import { pkgVersion } from "../version.js";
|
|
22
|
-
import {
|
|
22
|
+
import { collectArchiveUsageAdvisory } from "./health/archive-usage.js";
|
|
23
23
|
import { HEALTH_CHECKS, probeActiveImproveStrategy, runHealthEngineProbes, runPendingStateMigrationsCheck, SESSION_EXTRACTION_LEDGER_WINDOW_DAYS, } from "./health/checks.js";
|
|
24
|
+
import { collectConfigSkewAdvisory } from "./health/config-skew.js";
|
|
24
25
|
import { collectDataDirUsageAdvisory } from "./health/data-dir-usage.js";
|
|
26
|
+
import { collectEgressAdvisory } from "./health/egress.js";
|
|
25
27
|
import { engineLastUsedSince, readLastEngineUsage } from "./health/engine-usage.js";
|
|
26
|
-
import {
|
|
28
|
+
import { emptyImproveMetrics, roundRate } from "./health/improve-metrics.js";
|
|
27
29
|
import { emptyLlmUsageAggregate, readLlmUsageAggregate } from "./health/llm-usage.js";
|
|
28
|
-
import { computeDegradationMetrics, computeDenominatorFixedCoverage, computeEnrichmentMintingRollup, probeStateDbRoundTrip, } from "./health/metrics.js";
|
|
29
30
|
import { collectPluginStalenessAdvisories } from "./health/plugin-staleness.js";
|
|
30
31
|
import { collectSchedulerBinaryAdvisory } from "./health/scheduler-binary.js";
|
|
31
32
|
import { collectStashExposureAdvisory } from "./health/stash-exposure.js";
|
|
32
|
-
import { collectSurfacesAdvisories } from "./health/surfaces.js";
|
|
33
|
-
import { buildPerRunSummaries } from "./health/task-runs.js";
|
|
34
33
|
import { buildTypeDirectoryAdvisory } from "./health/type-directory-check.js";
|
|
35
|
-
import {
|
|
34
|
+
import { MIN_ROWS_FOR_WORST_TASK_FAIL_RATE, } from "./health/types.js";
|
|
36
35
|
import { collectVersionDriftAdvisory } from "./health/version-drift.js";
|
|
37
|
-
import { buildWindowMetrics, computeDeltas,
|
|
36
|
+
import { buildImproveWindowSummary, buildWindowMetrics, computeDeltas, computeTaskWindowRates, resolveWindowCompare, } from "./health/windows.js";
|
|
38
37
|
const DEFAULT_SINCE_MS = 24 * 60 * 60 * 1000;
|
|
39
38
|
export function parseHealthSince(since) {
|
|
40
39
|
if (since === undefined || since.trim() === "") {
|
|
@@ -71,6 +70,65 @@ function validateAkmHealthOptions(options) {
|
|
|
71
70
|
}
|
|
72
71
|
}
|
|
73
72
|
}
|
|
73
|
+
// ── akmHealth phase helpers (chunk-9 WI-9.5b; file-level decompose following
|
|
74
|
+
// the function's natural gather/advise/check/assemble phases) ───────────────
|
|
75
|
+
/** Event type appended + read back by the state.db round-trip probe. */
|
|
76
|
+
const HEALTH_PROBE_EVENT = "health_probe";
|
|
77
|
+
/** Synthetic sentinel ref (ref-grammar decision D-R3): a colon-free
|
|
78
|
+
* `<subsystem>/_<marker>` label. `health` has no asset stash-subdir, so
|
|
79
|
+
* `health/_probe` names the subsystem. */
|
|
80
|
+
const HEALTH_PROBE_REF = "health/_probe";
|
|
81
|
+
/**
|
|
82
|
+
* Verify state.db can accept a write and read it back — WITHOUT leaving any
|
|
83
|
+
* permanent trace (R-030). Earlier versions appended a `health_probe` event
|
|
84
|
+
* on every `akm health` invocation and never removed it: a read-only health
|
|
85
|
+
* check ran on a cron would grow state.db without bound (the only purge is
|
|
86
|
+
* `improve`'s retention pass, which a health-only user never runs). The probe
|
|
87
|
+
* row inserted here is deleted again inside the SAME connection once the
|
|
88
|
+
* round trip is confirmed, so the net effect on the `events` table is always
|
|
89
|
+
* zero rows — the round trip still genuinely exercises append + read against
|
|
90
|
+
* the real table, it just doesn't accumulate.
|
|
91
|
+
*/
|
|
92
|
+
export function probeStateDbRoundTrip(stateDbPath) {
|
|
93
|
+
const started = Date.now();
|
|
94
|
+
try {
|
|
95
|
+
return withStateDb((db) => {
|
|
96
|
+
const ts = new Date().toISOString();
|
|
97
|
+
const insertedId = insertEvent(db, {
|
|
98
|
+
eventType: HEALTH_PROBE_EVENT,
|
|
99
|
+
ts,
|
|
100
|
+
ref: HEALTH_PROBE_REF,
|
|
101
|
+
metadata: { source: "akm health" },
|
|
102
|
+
});
|
|
103
|
+
const durationMs = Date.now() - started;
|
|
104
|
+
if (insertedId === undefined) {
|
|
105
|
+
return { ok: false, durationMs, error: "probe event insert did not return a row id" };
|
|
106
|
+
}
|
|
107
|
+
// The round-trip matches on the exact (id, eventType, ref) triple
|
|
108
|
+
// written above, then removes the row regardless of outcome — a
|
|
109
|
+
// failed round trip must not leak a row any more than a successful
|
|
110
|
+
// one should.
|
|
111
|
+
let roundTripOk = false;
|
|
112
|
+
try {
|
|
113
|
+
const row = db
|
|
114
|
+
.prepare("SELECT 1 AS present FROM events WHERE id = ? AND event_type = ? AND ref = ?")
|
|
115
|
+
.get(insertedId, HEALTH_PROBE_EVENT, HEALTH_PROBE_REF);
|
|
116
|
+
roundTripOk = row !== undefined;
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
db.prepare("DELETE FROM events WHERE id = ?").run(insertedId);
|
|
120
|
+
}
|
|
121
|
+
if (!roundTripOk) {
|
|
122
|
+
return { ok: false, durationMs, error: "probe event was not readable after append" };
|
|
123
|
+
}
|
|
124
|
+
return { ok: true, durationMs };
|
|
125
|
+
}, { path: stateDbPath });
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
rethrowIfTestIsolationError(err);
|
|
129
|
+
return { ok: false, durationMs: Date.now() - started, error: err instanceof Error ? err.message : String(err) };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
74
132
|
/**
|
|
75
133
|
* Item 7: dedupe stuck-active rows by task_id (keeping the oldest/largest age
|
|
76
134
|
* per id) so the `active-runs` check can name WHICH tasks are stuck instead of
|
|
@@ -118,43 +176,37 @@ function computeWorstTaskFailRate(rows) {
|
|
|
118
176
|
return worst;
|
|
119
177
|
}
|
|
120
178
|
/** Table presence, the state.db round-trip probe, and task_history-derived rates. */
|
|
121
|
-
function gatherTaskHistoryPhase(db,
|
|
179
|
+
function gatherTaskHistoryPhase(db, since, stateDbPath, now) {
|
|
122
180
|
const tables = listExistingTableNames(db, ["events", "task_history", "proposals", "schema_migrations"]);
|
|
123
181
|
const tableNames = tables.map((row) => row.name).sort();
|
|
124
182
|
const requiredTables = ["events", "proposals", "schema_migrations", "task_history"];
|
|
125
183
|
const missingTables = requiredTables.filter((name) => !tableNames.includes(name));
|
|
126
184
|
const probe = probeStateDbRoundTrip(stateDbPath);
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
const
|
|
131
|
-
const
|
|
185
|
+
// R0: read-only, independent of the round-trip probe above — quick_check
|
|
186
|
+
// catches corruption a successful append/read cannot (out-of-order rowids,
|
|
187
|
+
// bad index entry counts), and the freelist reading is purely informational.
|
|
188
|
+
const stateDbIntegrity = runStateDbQuickCheck(stateDbPath);
|
|
189
|
+
const stateDbFreelist = getStateDbFreelistInfo(stateDbPath);
|
|
132
190
|
// D8 (spec §5.3): a marked "command" row or a legacy (unmarked) "prompt"
|
|
133
191
|
// row is the agent/LLM arm; an unmarked "command" row is the legacy
|
|
134
192
|
// native shell/script arm and must not be counted here (see
|
|
135
|
-
// isAgentTaskHistoryRow's header comment
|
|
136
|
-
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
return typeof detail?.reason === "string" && detail.reason.length > 0;
|
|
140
|
-
});
|
|
141
|
-
const logBackingRate = taskRowsWithLogs.length === 0 ? 1 : existingLogRows.length / taskRowsWithLogs.length;
|
|
142
|
-
const taskFailRate = taskRows.length === 0 ? 0 : failedTaskRows.length / taskRows.length;
|
|
143
|
-
const agentFailureRate = agentRows.length === 0 ? 0 : agentFailures.length / agentRows.length;
|
|
193
|
+
// isAgentTaskHistoryRow's header comment, referenced from
|
|
194
|
+
// computeTaskWindowRates, for the full mapping).
|
|
195
|
+
const taskRows = queryTaskHistory(db, { since });
|
|
196
|
+
const rates = computeTaskWindowRates(taskRows, now);
|
|
144
197
|
return {
|
|
145
198
|
tableNames,
|
|
146
199
|
missingTables,
|
|
147
200
|
probe,
|
|
201
|
+
stateDbIntegrity,
|
|
202
|
+
stateDbFreelist,
|
|
148
203
|
taskRowCount: taskRows.length,
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
stuckActiveTasks: dedupeStuckActiveTasks(stuckActiveRows, now),
|
|
153
|
-
logBackingRate,
|
|
154
|
-
taskFailRate,
|
|
204
|
+
stuckActiveRuns: rates.stuckActiveRuns,
|
|
205
|
+
stuckActiveTasks: dedupeStuckActiveTasks(rates.stuckActiveRows, now),
|
|
206
|
+
taskFailRate: rates.taskFailRate,
|
|
155
207
|
worstTaskFailRate: computeWorstTaskFailRate(taskRows),
|
|
156
|
-
agentFailureRate,
|
|
157
|
-
agentFailureReasonCounts:
|
|
208
|
+
agentFailureRate: rates.agentFailureRate,
|
|
209
|
+
agentFailureReasonCounts: rates.agentFailureReasonCounts,
|
|
158
210
|
};
|
|
159
211
|
}
|
|
160
212
|
/**
|
|
@@ -183,47 +235,16 @@ function gatherSessionExtractionLedgerPhase(db, now) {
|
|
|
183
235
|
return { since, rows: getExtractOutcomeCountsSince(db, since) };
|
|
184
236
|
}
|
|
185
237
|
/**
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
const improveSkippedEvents = readEvents({ since, type: "improve_skipped" }, { dbPath: stateDbPath }).events;
|
|
194
|
-
const eventsMetrics = summarizeImproveCompleted(improveCompletedEvents);
|
|
195
|
-
const { metrics: improveSummary } = summarizeImproveRuns(db, since);
|
|
196
|
-
improveSummary.invoked = improveInvoked;
|
|
197
|
-
improveSummary.completed = eventsMetrics.completed;
|
|
198
|
-
const skipSummary = buildImproveSkipSummary(improveSkippedEvents);
|
|
199
|
-
improveSummary.skipped = skipSummary.skipped;
|
|
200
|
-
improveSummary.skipReasons = skipSummary.skipReasons;
|
|
201
|
-
const perRunSummaries = buildPerRunSummaries(db, since);
|
|
202
|
-
const wallTimes = perRunSummaries.map((run) => run.wallTimeMs).filter((ms) => Number.isFinite(ms) && ms > 0);
|
|
203
|
-
improveSummary.wallTime = computeWallTimeStats(wallTimes, improveSummary.wallTime.byPhase);
|
|
204
|
-
// WS-5: Compute denominator-fixed coverage and per-run degradation metrics
|
|
205
|
-
// for the main health path (not just window-compare mode).
|
|
206
|
-
const until = new Date(now()).toISOString();
|
|
207
|
-
const totalAssetsMain = improveSummary.memorySummary.eligible + improveSummary.memorySummary.derived;
|
|
208
|
-
improveSummary.coverage = computeDenominatorFixedCoverage(db, totalAssetsMain, improveSummary.memorySummary.eligible, since, until);
|
|
209
|
-
const degradationMain = computeDegradationMetrics(db, since, until);
|
|
210
|
-
if (degradationMain) {
|
|
211
|
-
improveSummary.degradation = degradationMain;
|
|
212
|
-
}
|
|
213
|
-
improveSummary.enrichmentMinting = computeEnrichmentMintingRollup(db, since, until);
|
|
214
|
-
return { improveSummary, perRunSummaries };
|
|
215
|
-
}
|
|
216
|
-
/**
|
|
217
|
-
* The best-effort advisory groups beyond the health-check registry: improve
|
|
218
|
-
* advisories, the `stash-git-exposure` probe, the 08 surfaces group
|
|
219
|
-
* (binary-config-skew, egress-endpoints), `type-directory-disagreement`
|
|
220
|
-
* (#831), `data-dir-usage` (#896), and `plugin-version` (itlackey/akm#832).
|
|
221
|
-
* Order matches emission order in the returned array. A probe/filesystem
|
|
222
|
-
* failure in any try/catch must not abort the health report — each group
|
|
223
|
-
* degrades to "no advisory" independently.
|
|
238
|
+
* The best-effort advisory groups beyond the health-check registry: the
|
|
239
|
+
* `stash-git-exposure` probe, the 08 surfaces group (binary-config-skew,
|
|
240
|
+
* egress-endpoints), `type-directory-disagreement` (#831), `data-dir-usage`
|
|
241
|
+
* (#896), and `plugin-version` (itlackey/akm#832). Order matches emission
|
|
242
|
+
* order in the returned array. A probe/filesystem failure in any try/catch
|
|
243
|
+
* must not abort the health report — each group degrades to "no advisory"
|
|
244
|
+
* independently.
|
|
224
245
|
*/
|
|
225
|
-
function gatherAncillaryAdvisories(db,
|
|
226
|
-
const advisories = [
|
|
246
|
+
function gatherAncillaryAdvisories(db, options, egressConfigView) {
|
|
247
|
+
const advisories = [];
|
|
227
248
|
const indexStateMismatch = detectIndexStateGenerationMismatch(db);
|
|
228
249
|
if (indexStateMismatch)
|
|
229
250
|
advisories.push(indexStateMismatch);
|
|
@@ -250,10 +271,12 @@ function gatherAncillaryAdvisories(db, stateDbPath, since, improveSummary, optio
|
|
|
250
271
|
// egress-endpoints). Best-effort — a filesystem probe failure must not abort
|
|
251
272
|
// the health report.
|
|
252
273
|
try {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
274
|
+
const configSkew = collectConfigSkewAdvisory(getConfigPath());
|
|
275
|
+
if (configSkew)
|
|
276
|
+
advisories.push(configSkew);
|
|
277
|
+
const egress = collectEgressAdvisory(egressConfigView);
|
|
278
|
+
if (egress)
|
|
279
|
+
advisories.push(egress);
|
|
257
280
|
}
|
|
258
281
|
catch {
|
|
259
282
|
// Non-fatal.
|
|
@@ -269,6 +292,18 @@ function gatherAncillaryAdvisories(db, stateDbPath, since, improveSummary, optio
|
|
|
269
292
|
catch {
|
|
270
293
|
// Non-fatal.
|
|
271
294
|
}
|
|
295
|
+
// Item 4 (alpha.9 plan §5.4/§8 step 8): a bundle with no git history of its
|
|
296
|
+
// own never gets the purge sweep, so its memory-cleanup archive only ever
|
|
297
|
+
// grows — report its size and file count instead. Best-effort — an
|
|
298
|
+
// unreadable archive must not abort the health report.
|
|
299
|
+
try {
|
|
300
|
+
const archiveUsage = collectArchiveUsageAdvisory(options.stashDir ?? resolveStashDir());
|
|
301
|
+
if (archiveUsage)
|
|
302
|
+
advisories.push(archiveUsage);
|
|
303
|
+
}
|
|
304
|
+
catch {
|
|
305
|
+
// Non-fatal.
|
|
306
|
+
}
|
|
272
307
|
// #896: report the data dir's total size and its largest top-level
|
|
273
308
|
// subdirectory, so a disk-usage blowup (e.g. unpruned migration snapshot
|
|
274
309
|
// backups, #897) is self-diagnosing instead of requiring `du` archaeology.
|
|
@@ -391,7 +426,7 @@ function detectIndexStateGenerationMismatch(stateDb) {
|
|
|
391
426
|
* per-window bundles and computes deltas between the earliest and latest
|
|
392
427
|
* window. Top-level metrics retain the primary `--since` query.
|
|
393
428
|
*/
|
|
394
|
-
function resolveWindowComparePhase(options, db, stateDbPath, now
|
|
429
|
+
function resolveWindowComparePhase(options, db, stateDbPath, now) {
|
|
395
430
|
let windowSpecs;
|
|
396
431
|
if (options.windowCompare) {
|
|
397
432
|
windowSpecs = resolveWindowCompare(options.windowCompare, now);
|
|
@@ -405,7 +440,7 @@ function resolveWindowComparePhase(options, db, stateDbPath, now, logsDb) {
|
|
|
405
440
|
windowResults = windowSpecs.map((spec) => {
|
|
406
441
|
const winSince = parseHealthSince(spec.since);
|
|
407
442
|
const winUntil = spec.until ? parseHealthSince(spec.until) : new Date(now()).toISOString();
|
|
408
|
-
const bundle = buildWindowMetrics(db, stateDbPath, winSince, winUntil, now
|
|
443
|
+
const bundle = buildWindowMetrics(db, stateDbPath, winSince, winUntil, now);
|
|
409
444
|
return {
|
|
410
445
|
name: spec.name,
|
|
411
446
|
since: winSince,
|
|
@@ -430,19 +465,13 @@ function resolveWindowComparePhase(options, db, stateDbPath, now, logsDb) {
|
|
|
430
465
|
return { windowResults, deltas };
|
|
431
466
|
}
|
|
432
467
|
/**
|
|
433
|
-
* The health report for a state.db
|
|
434
|
-
*
|
|
435
|
-
*
|
|
436
|
-
* deliberate consent (`akm upgrade` / `akm migrate apply` are the
|
|
437
|
-
* only two callers allowed to admit a historical-destructive migration; see
|
|
438
|
-
* `beforeMigrationLocked` in `src/core/state/migrations.ts`).
|
|
468
|
+
* The health report for a state.db the open cannot reach at all: either the
|
|
469
|
+
* file is not readable (#791), or — the same shape, a different cause — the
|
|
470
|
+
* open failed while applying a pending migration, which is left pending.
|
|
439
471
|
*
|
|
440
472
|
* `akm health` is what an operator (or a bundler's boot check) runs when
|
|
441
473
|
* something else is misbehaving, so it must survive either problem long
|
|
442
|
-
* enough to NAME it
|
|
443
|
-
* way, but a pending migration was not: the managed open's refusal escaped as
|
|
444
|
-
* a thrown `ConfigError` (exit 78) and crashed the whole command before any
|
|
445
|
-
* check — including this one — could report anything.
|
|
474
|
+
* enough to NAME it rather than exit 78 before any check could report.
|
|
446
475
|
*
|
|
447
476
|
* Reported as a single hard-channel `fail` check — the run genuinely could
|
|
448
477
|
* not open state.db, so every check that depends on it is skipped rather than
|
|
@@ -462,13 +491,9 @@ function degradedStateDbReport(hardCheck, options) {
|
|
|
462
491
|
agentFailureRate: 0,
|
|
463
492
|
agentFailureReasonCounts: {},
|
|
464
493
|
stuckActiveRuns: 0,
|
|
465
|
-
logBackingRate: 0,
|
|
466
|
-
// `null`, not 0: the round-trip probe did not run, which is not the same
|
|
467
|
-
// as it running instantly.
|
|
468
|
-
probeRoundTripMs: null,
|
|
469
494
|
llmUsage: emptyLlmUsageAggregate(),
|
|
470
495
|
},
|
|
471
|
-
improve:
|
|
496
|
+
improve: emptyImproveMetrics(),
|
|
472
497
|
};
|
|
473
498
|
}
|
|
474
499
|
function unreadableStateDbCheck(detail) {
|
|
@@ -490,29 +515,25 @@ export async function akmHealth(options = {}) {
|
|
|
490
515
|
const stateDbPath = options.stateDbPath ?? getStateDbPathInDataDir();
|
|
491
516
|
const hardChecks = [];
|
|
492
517
|
const advisories = [];
|
|
493
|
-
// #791: an UNREADABLE state.db, or one
|
|
494
|
-
//
|
|
495
|
-
//
|
|
496
|
-
//
|
|
497
|
-
//
|
|
498
|
-
//
|
|
499
|
-
|
|
500
|
-
let db;
|
|
518
|
+
// #791: an UNREADABLE state.db, or one whose pending migration failed to
|
|
519
|
+
// apply, are the two failures `akm health` most needs to be able to report,
|
|
520
|
+
// because this is the command an operator (or a bundler's boot check) runs
|
|
521
|
+
// to find out why everything else is behaving oddly. Report it as a finding
|
|
522
|
+
// instead of dying before any check runs. The open itself applies every
|
|
523
|
+
// pending migration; `state-db-migrations` reports what it applied.
|
|
524
|
+
let opened;
|
|
501
525
|
try {
|
|
502
|
-
|
|
526
|
+
opened = openStateDatabaseWithReport(stateDbPath);
|
|
503
527
|
}
|
|
504
528
|
catch (error) {
|
|
505
529
|
const { access, code } = classifyPathAccess(stateDbPath);
|
|
506
530
|
if (access === "inaccessible") {
|
|
507
531
|
return degradedStateDbReport(unreadableStateDbCheck(describeInaccessiblePath(stateDbPath, code)), options);
|
|
508
532
|
}
|
|
509
|
-
//
|
|
510
|
-
//
|
|
511
|
-
//
|
|
512
|
-
//
|
|
513
|
-
// A ledger too broken to enumerate at all throws here too; in that case
|
|
514
|
-
// fall through to the generic config-error report below, since it is a
|
|
515
|
-
// genuinely different, rarer failure this check cannot explain.
|
|
533
|
+
// A migration that failed to apply rolled back and is still pending:
|
|
534
|
+
// name it from a read-only listing (which never applies anything) rather
|
|
535
|
+
// than pattern-matching the error text. A ledger too broken to enumerate
|
|
536
|
+
// throws here too and falls through to the generic config error below.
|
|
516
537
|
let pendingMigrationsCheck;
|
|
517
538
|
try {
|
|
518
539
|
pendingMigrationsCheck = runPendingStateMigrationsCheck(stateDbPath, { listPendingStateMigrations });
|
|
@@ -525,16 +546,7 @@ export async function akmHealth(options = {}) {
|
|
|
525
546
|
}
|
|
526
547
|
throw new ConfigError(`Unable to open state.db: ${error instanceof Error ? error.message : String(error)}`, "INVALID_CONFIG_FILE");
|
|
527
548
|
}
|
|
528
|
-
|
|
529
|
-
// be opened, partitionLogBackedRows falls back to the on-disk file check, so
|
|
530
|
-
// health never hard-fails on a missing/locked logs database.
|
|
531
|
-
let logsDb;
|
|
532
|
-
try {
|
|
533
|
-
logsDb = openLogsDatabase(options.logsDbPath);
|
|
534
|
-
}
|
|
535
|
-
catch {
|
|
536
|
-
logsDb = undefined;
|
|
537
|
-
}
|
|
549
|
+
const { db } = opened;
|
|
538
550
|
try {
|
|
539
551
|
// Network probes overlap the local database phases below; awaited where consumed.
|
|
540
552
|
const engineProbesPromise = runHealthEngineProbes({ probeReachable: options.probe ? probeLlmEndpoint : undefined });
|
|
@@ -549,11 +561,15 @@ export async function akmHealth(options = {}) {
|
|
|
549
561
|
cliVersion: pkgVersion,
|
|
550
562
|
});
|
|
551
563
|
schedulerBinaryDriftPromise.catch(() => undefined);
|
|
552
|
-
const taskHistory = gatherTaskHistoryPhase(db,
|
|
564
|
+
const taskHistory = gatherTaskHistoryPhase(db, since, stateDbPath, now);
|
|
553
565
|
const { tableNames, missingTables, probe } = taskHistory;
|
|
554
566
|
const { egressConfigView, thinkingOffEngines } = gatherEgressConfigPhase();
|
|
555
|
-
|
|
556
|
-
|
|
567
|
+
// Same window bundle `--window-compare` builds per-window (windows.ts) —
|
|
568
|
+
// reused here for the main `--since` window's improve summary and for
|
|
569
|
+
// `--group-by run`, so neither re-reads `improve_runs`.
|
|
570
|
+
const until = new Date(now()).toISOString();
|
|
571
|
+
const { improve: improveSummary, perRunSummaries } = buildImproveWindowSummary(db, stateDbPath, since, until);
|
|
572
|
+
advisories.push(...gatherAncillaryAdvisories(db, options, egressConfigView));
|
|
557
573
|
const sessionExtractionLedger = gatherSessionExtractionLedgerPhase(db, now);
|
|
558
574
|
// #950: computed once (no IO beyond config/env, same as gatherEgressConfigPhase)
|
|
559
575
|
// so `active-improve-strategy` and `engine-last-used` project the same
|
|
@@ -580,16 +596,15 @@ export async function akmHealth(options = {}) {
|
|
|
580
596
|
tableNames,
|
|
581
597
|
missingTables,
|
|
582
598
|
probe,
|
|
599
|
+
stateDbIntegrity: taskHistory.stateDbIntegrity,
|
|
600
|
+
stateDbFreelist: taskHistory.stateDbFreelist,
|
|
601
|
+
stateDbMigrations: { applied: opened.applied, ...(opened.backupPath ? { backupPath: opened.backupPath } : {}) },
|
|
583
602
|
taskRowCount: taskHistory.taskRowCount,
|
|
584
603
|
taskFailRate: taskHistory.taskFailRate,
|
|
585
|
-
taskRowsWithLogsCount: taskHistory.taskRowsWithLogsCount,
|
|
586
|
-
existingLogRowsCount: taskHistory.existingLogRowsCount,
|
|
587
|
-
logBackingRate: taskHistory.logBackingRate,
|
|
588
604
|
stuckActiveRuns: taskHistory.stuckActiveRuns,
|
|
589
605
|
stuckActiveTasks: taskHistory.stuckActiveTasks,
|
|
590
606
|
worstTaskFailRate: taskHistory.worstTaskFailRate,
|
|
591
607
|
agentFailureReasonCounts: taskHistory.agentFailureReasonCounts,
|
|
592
|
-
sessionExtraction: improveSummary.sessionExtraction,
|
|
593
608
|
sessionExtractionLedger,
|
|
594
609
|
autoAccept: improveSummary.autoAccept,
|
|
595
610
|
engineProbes,
|
|
@@ -614,20 +629,15 @@ export async function akmHealth(options = {}) {
|
|
|
614
629
|
agentFailureRate: roundRate(taskHistory.agentFailureRate),
|
|
615
630
|
agentFailureReasonCounts: taskHistory.agentFailureReasonCounts,
|
|
616
631
|
stuckActiveRuns: taskHistory.stuckActiveRuns,
|
|
617
|
-
logBackingRate: roundRate(taskHistory.logBackingRate),
|
|
618
|
-
probeRoundTripMs: probe.durationMs,
|
|
619
632
|
llmUsage,
|
|
620
633
|
};
|
|
621
634
|
const hardFailure = hardChecks.some((check) => check.status === "fail");
|
|
622
635
|
const deterministicWarnings = [...hardChecks, ...advisories].some((check) => check.status === "warn" && check.kind === "deterministic");
|
|
623
636
|
const status = hardFailure ? "fail" : deterministicWarnings ? "warn" : "pass";
|
|
624
637
|
// ── Window-compare mode (Phase 3) ─────────────────────────────────────
|
|
625
|
-
const { windowResults, deltas } = resolveWindowComparePhase(options, db, stateDbPath, now
|
|
638
|
+
const { windowResults, deltas } = resolveWindowComparePhase(options, db, stateDbPath, now);
|
|
626
639
|
// ── Per-run mode (Phase 2) ────────────────────────────────────────────
|
|
627
|
-
|
|
628
|
-
if (options.groupBy === "run") {
|
|
629
|
-
runs = buildPerRunSummaries(db, since);
|
|
630
|
-
}
|
|
640
|
+
const runs = options.groupBy === "run" ? perRunSummaries : undefined;
|
|
631
641
|
return {
|
|
632
642
|
schemaVersion: 3,
|
|
633
643
|
ok: !hardFailure,
|
|
@@ -644,14 +654,6 @@ export async function akmHealth(options = {}) {
|
|
|
644
654
|
}
|
|
645
655
|
finally {
|
|
646
656
|
db.close();
|
|
647
|
-
if (logsDb) {
|
|
648
|
-
try {
|
|
649
|
-
logsDb.close();
|
|
650
|
-
}
|
|
651
|
-
catch {
|
|
652
|
-
// best-effort
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
657
|
}
|
|
656
658
|
}
|
|
657
659
|
// Markdown renderers (renderRunsDetailMd / renderWindowCompareMd) live in
|
|
@@ -1,145 +1,54 @@
|
|
|
1
1
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
2
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
-
|
|
5
|
-
// prompt-string construction over MemoryEntry inputs — no LLM call, no embedder,
|
|
6
|
-
// no orchestrator coupling.
|
|
4
|
+
/** Consolidate's chunk sizing and per-chunk prompt (pure token math and text). */
|
|
7
5
|
import fs from "node:fs";
|
|
8
6
|
import { parseFrontmatter } from "../../../core/asset/frontmatter.js";
|
|
9
|
-
import {
|
|
10
|
-
/**
|
|
11
|
-
* Conservative chars-per-token estimate used when computing prompt budgets.
|
|
12
|
-
* English text averages roughly 4 chars/token for most LLM tokenizers. We use
|
|
13
|
-
* 3 to stay conservative (shorter tokens = more tokens per char). Exported so
|
|
14
|
-
* other context-length-driven budgets (e.g. reflect's direct-LLM content cap,
|
|
15
|
-
* #952) share this single estimate instead of redeclaring it.
|
|
16
|
-
*/
|
|
7
|
+
import { contentHash, stripFrontmatterBody } from "../content-hash.js";
|
|
8
|
+
/** A conservative chars-per-token estimate, shared by every context-length budget. */
|
|
17
9
|
export const CHARS_PER_TOKEN = 3;
|
|
18
|
-
/**
|
|
19
|
-
* Overhead budget reserved for the system prompt, chunk header lines, and per-
|
|
20
|
-
* memory metadata lines (name, description, tags, separator). Measured at
|
|
21
|
-
* roughly 600 chars for the system prompt + ~100 chars of header + ~50 chars
|
|
22
|
-
* per memory × chunk size. We round up to 2 000 tokens to leave room for the
|
|
23
|
-
* model's own output.
|
|
24
|
-
*/
|
|
10
|
+
/** System prompt, headers and per-memory metadata, with room for the output. */
|
|
25
11
|
const PROMPT_OVERHEAD_TOKENS = 2_000;
|
|
26
12
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* - When the agent path is used, the agent CLI (e.g. opencode)
|
|
32
|
-
* prepends its own large system prompt + conversation history before
|
|
33
|
-
* forwarding to the model. That overhead easily consumes 30K+ tokens on
|
|
34
|
-
* a model with a 16K context window, leaving very little room for
|
|
35
|
-
* chunk content.
|
|
36
|
-
* - When the HTTP path is used (an LLM profile is selected), only the akm
|
|
37
|
-
* system prompt and user prompt are sent, so the budget can be set to the
|
|
38
|
-
* model's actual context length via the selected LLM engine's contextLength.
|
|
39
|
-
*
|
|
40
|
-
* Set the selected LLM engine's contextLength in your config file to the
|
|
41
|
-
* model's actual context window to allow larger chunks on the HTTP path.
|
|
13
|
+
* The budget when the engine sets no `contextLength`. Conservative on purpose:
|
|
14
|
+
* an agent CLI prepends its own large prompt; set `contextLength` on a direct
|
|
15
|
+
* LLM engine to allow bigger chunks.
|
|
42
16
|
*/
|
|
43
17
|
export const DEFAULT_CONTEXT_LENGTH_TOKENS = 4_096;
|
|
44
|
-
/**
|
|
45
|
-
* Given the model's context window and the per-memory body truncation limit,
|
|
46
|
-
* return the maximum number of memories that can safely fit in one chunk
|
|
47
|
-
* without the prompt overflowing the context window.
|
|
48
|
-
*
|
|
49
|
-
* The formula is:
|
|
50
|
-
* usableTokens = contextLength - PROMPT_OVERHEAD_TOKENS
|
|
51
|
-
* tokensPerMemory = ceil(bodyTruncation / CHARS_PER_TOKEN)
|
|
52
|
-
* chunkSize = floor(usableTokens / tokensPerMemory)
|
|
53
|
-
*
|
|
54
|
-
* Result is clamped between 1 and 50 to avoid degenerate values.
|
|
55
|
-
*
|
|
56
|
-
* @param contextLength - Model context window in tokens.
|
|
57
|
-
* @param bodyTruncation - Max chars per memory body included in the prompt.
|
|
58
|
-
* @param maxChunkSize - Optional override for the hardcoded cap of 50 (1–50).
|
|
59
|
-
*/
|
|
18
|
+
/** Memories per chunk: `(contextLength − overhead) / ceil(bodyTruncation / CHARS_PER_TOKEN)`, within 1…cap (50). */
|
|
60
19
|
export function computeSafeChunkSize(contextLength, bodyTruncation, maxChunkSize) {
|
|
61
20
|
const usableTokens = Math.max(contextLength - PROMPT_OVERHEAD_TOKENS, 0);
|
|
62
21
|
const tokensPerMemory = Math.max(Math.ceil(bodyTruncation / CHARS_PER_TOKEN), 1);
|
|
63
|
-
|
|
64
|
-
return Math.max(1, Math.min(maxChunkSize ?? 50, raw));
|
|
22
|
+
return Math.max(1, Math.min(maxChunkSize ?? 50, Math.floor(usableTokens / tokensPerMemory)));
|
|
65
23
|
}
|
|
66
24
|
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* forbids proposing delete. ~60 wasted LLM verdicts/4h on this user's
|
|
73
|
-
* stack before this annotation.
|
|
74
|
-
* - `(already queued)` — the memory's body hash matches a pending
|
|
75
|
-
* consolidate proposal; system prompt rule 3 forbids proposing
|
|
76
|
-
* promote/merge/contradict. ~107/4h before this annotation.
|
|
77
|
-
*
|
|
78
|
-
* Both annotations are visible to the LLM. `pendingProposalBodyHashes`
|
|
79
|
-
* is precomputed once per run by `loadPendingConsolidateProposalHashes`
|
|
80
|
-
* so the cost stays O(memories) inside the chunk loop.
|
|
25
|
+
* One chunk's user prompt. Each memory shows its body only (frontmatter can
|
|
26
|
+
* outrun the truncation and leave the model judging metadata), marked
|
|
27
|
+
* `captureMode: hot` when the user captured it explicitly and `already queued`
|
|
28
|
+
* when a pending consolidate proposal carries its body hash — the system
|
|
29
|
+
* prompt forbids promoting those.
|
|
81
30
|
*/
|
|
82
|
-
export function buildChunkPrompt(sourceName, memories, chunkIndex, totalChunks, bodyTruncation, pendingProposalBodyHashes = new Set()
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
let body
|
|
31
|
+
export function buildChunkPrompt(sourceName, memories, chunkIndex, totalChunks, bodyTruncation, pendingProposalBodyHashes = new Set()) {
|
|
32
|
+
const lines = [
|
|
33
|
+
`Source: ${sourceName}`,
|
|
34
|
+
`Chunk ${chunkIndex + 1} of ${totalChunks} (${memories.length} memories):`,
|
|
35
|
+
"",
|
|
36
|
+
];
|
|
37
|
+
memories.forEach((m, i) => {
|
|
38
|
+
let body;
|
|
90
39
|
try {
|
|
91
40
|
body = fs.readFileSync(m.filePath, "utf8");
|
|
92
41
|
}
|
|
93
42
|
catch {
|
|
94
43
|
body = "(unreadable)";
|
|
95
44
|
}
|
|
96
|
-
const parsed = parseFrontmatter(body);
|
|
97
|
-
const isHot = parsed.data.captureMode === "hot";
|
|
98
|
-
// Use cacheHash (case-preserving stripped body) to match the domain used
|
|
99
|
-
// by loadPendingConsolidateProposalHashes and the body-embedding cache.
|
|
100
|
-
const bodyHash = cacheHash(body);
|
|
101
|
-
const isAlreadyQueued = pendingProposalBodyHashes.has(bodyHash);
|
|
102
|
-
annotationsByIndex.push({ isHot, isAlreadyQueued, body });
|
|
103
|
-
if (isHot)
|
|
104
|
-
hotRefs.push(`memories/${m.name}`);
|
|
105
|
-
}
|
|
106
|
-
const lines = [
|
|
107
|
-
`Source: ${sourceName}`,
|
|
108
|
-
`Chunk ${chunkIndex + 1} of ${totalChunks}, memories ${start}–${end}:`,
|
|
109
|
-
"",
|
|
110
|
-
];
|
|
111
|
-
if (standardsContext.trim()) {
|
|
112
|
-
lines.push("Standards to follow (the rulebook for this target):");
|
|
113
|
-
lines.push(standardsContext.trim());
|
|
114
|
-
lines.push("");
|
|
115
|
-
}
|
|
116
|
-
// Top-of-prompt protection block for hot refs. Neutral phrasing — avoid
|
|
117
|
-
// op-words like "promote", "merge", "contradict" so the model doesn't
|
|
118
|
-
// accidentally treat the warning as a hint to use that op elsewhere
|
|
119
|
-
// (variant B leaked the word "contradict" into the control sample
|
|
120
|
-
// during the diagnostic).
|
|
121
|
-
if (hotRefs.length > 0) {
|
|
122
|
-
lines.push("⛔ DO NOT propose any `delete` operation for these refs — they are user-explicit (captureMode: hot) and the downstream guard refuses them regardless. Proposing delete for any of these only wastes tokens.");
|
|
123
|
-
for (const ref of hotRefs)
|
|
124
|
-
lines.push(` - ${ref}`);
|
|
125
|
-
lines.push("");
|
|
126
|
-
}
|
|
127
|
-
for (let i = 0; i < memories.length; i++) {
|
|
128
|
-
const m = memories[i];
|
|
129
|
-
// `annotationsByIndex` has exactly one entry per memory (built in the loop above).
|
|
130
|
-
const { isHot, isAlreadyQueued, body } = annotationsByIndex[i];
|
|
131
45
|
const annotations = [];
|
|
132
|
-
if (
|
|
46
|
+
if (parseFrontmatter(body).data.captureMode === "hot")
|
|
133
47
|
annotations.push("captureMode: hot");
|
|
134
|
-
if (
|
|
48
|
+
if (pendingProposalBodyHashes.has(contentHash(body, "body")))
|
|
135
49
|
annotations.push("already queued");
|
|
136
50
|
const annotationSuffix = annotations.length > 0 ? ` (${annotations.join("; ")})` : "";
|
|
137
|
-
lines.push(`[${i + 1}] memories/${m.name}${annotationSuffix}`);
|
|
138
|
-
|
|
139
|
-
lines.push(`Tags: ${m.tags.length > 0 ? m.tags.join(", ") : "(none)"}`);
|
|
140
|
-
lines.push("---");
|
|
141
|
-
lines.push(body.slice(0, bodyTruncation));
|
|
142
|
-
lines.push("");
|
|
143
|
-
}
|
|
51
|
+
lines.push(`[${i + 1}] memories/${m.name}${annotationSuffix}`, `Description: ${m.description || "(none)"}`, `Tags: ${m.tags.length > 0 ? m.tags.join(", ") : "(none)"}`, "---", stripFrontmatterBody(body).slice(0, bodyTruncation), "");
|
|
52
|
+
});
|
|
144
53
|
return lines.join("\n");
|
|
145
54
|
}
|