akm-cli 0.9.17-alpha.1 → 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 +1623 -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/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 +2 -2
- package/dist/commands/health/archive-usage.js +92 -0
- package/dist/commands/health/checks.js +30 -75
- 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 -587
- 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 -119
- package/dist/commands/health/types-improve.js +4 -19
- package/dist/commands/health/windows.js +64 -74
- package/dist/commands/health.js +135 -143
- package/dist/commands/improve/consolidate/chunking.js +25 -100
- 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 +573 -1124
- package/dist/commands/improve/content-hash.js +16 -24
- package/dist/commands/improve/distill/content-repair.js +18 -100
- package/dist/commands/improve/distill-guards.js +20 -81
- package/dist/commands/improve/distill-promotion-policy.js +23 -243
- package/dist/commands/improve/distill.js +608 -1075
- package/dist/commands/improve/eligibility.js +126 -400
- package/dist/commands/improve/execution.js +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 +54 -171
- package/dist/commands/improve/improve-result-file.js +10 -66
- package/dist/commands/improve/improve-strategies.js +35 -9
- package/dist/commands/improve/improve-usage-report.js +18 -64
- package/dist/commands/improve/improve.js +451 -1082
- package/dist/commands/improve/ledger.js +119 -0
- package/dist/commands/improve/locks.js +2 -8
- package/dist/commands/improve/loop-stages.js +401 -1192
- 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 +776 -2349
- 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 -1364
- 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 +258 -644
- package/dist/commands/proposal/proposal-cli.js +19 -20
- package/dist/commands/proposal/proposal-types.js +27 -41
- package/dist/commands/proposal/proposal.js +38 -8
- package/dist/commands/proposal/propose.js +134 -160
- package/dist/commands/proposal/repository.js +1099 -1475
- 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/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 +37 -135
- 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 +26 -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 -31
- 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 +46 -87
- 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 +81 -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 +8 -10
- 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 +42938 -92375
- package/dist/scripts/akm-migrate.js +42930 -92365
- 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 +88 -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 +100 -0
- 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 +34 -27
- 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 +229 -133
- package/docs/reference/configuration.md +71 -57
- package/docs/reference/data-and-telemetry.md +19 -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 +87 -754
- 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 -146
- package/dist/commands/improve/distill/promote-memory.js +0 -329
- package/dist/commands/improve/distill/quality-gate.js +0 -500
- package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
- package/dist/commands/improve/proposal-envelope.js +0 -31
- package/dist/commands/improve/run-context.js +0 -123
- package/dist/commands/improve/shared.js +0 -21
- package/dist/commands/improve/source-identity.js +0 -28
- package/dist/commands/improve/triage.js +0 -96
- package/dist/commands/proposal/drain-policies.js +0 -151
- package/dist/commands/sources/update-transaction.js +0 -220
- package/dist/core/action-contributors.js +0 -28
- package/dist/core/config/config-version-shim.js +0 -101
- package/dist/core/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 -1182
- 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 -872
- 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 -78
- package/dist/workflows/runtime/checkin.js +0 -57
- package/dist/workflows/runtime/plan-classifier.js +0 -196
- package/dist/workflows/runtime/unit-checkin.js +0 -45
- package/dist/workflows/runtime/unit-phases.js +0 -20
- package/dist/workflows/schema.js +0 -4
- package/dist/workflows/source-ir/compile.js +0 -200
- package/dist/workflows/source-ir/program.js +0 -50
- package/dist/workflows/source-ir/result.js +0 -26
- package/dist/workflows/source-ir/schema.js +0 -786
- package/dist/workflows/source-ir/triggers.js +0 -79
- package/dist/workflows/source-ir/uses.js +0 -40
- package/dist/workflows/validator.js +0 -60
package/dist/commands/health.js
CHANGED
|
@@ -5,14 +5,13 @@ 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";
|
|
@@ -20,22 +19,21 @@ import { getAllEntries } from "../storage/repositories/index-entries-repository.
|
|
|
20
19
|
import { queryTaskHistory } from "../storage/repositories/task-history-repository.js";
|
|
21
20
|
import { getStateDbFreelistInfo, runStateDbQuickCheck } from "../storage/state-db-integrity.js";
|
|
22
21
|
import { pkgVersion } from "../version.js";
|
|
23
|
-
import {
|
|
22
|
+
import { collectArchiveUsageAdvisory } from "./health/archive-usage.js";
|
|
24
23
|
import { HEALTH_CHECKS, probeActiveImproveStrategy, runHealthEngineProbes, runPendingStateMigrationsCheck, SESSION_EXTRACTION_LEDGER_WINDOW_DAYS, } from "./health/checks.js";
|
|
24
|
+
import { collectConfigSkewAdvisory } from "./health/config-skew.js";
|
|
25
25
|
import { collectDataDirUsageAdvisory } from "./health/data-dir-usage.js";
|
|
26
|
+
import { collectEgressAdvisory } from "./health/egress.js";
|
|
26
27
|
import { engineLastUsedSince, readLastEngineUsage } from "./health/engine-usage.js";
|
|
27
|
-
import {
|
|
28
|
+
import { emptyImproveMetrics, roundRate } from "./health/improve-metrics.js";
|
|
28
29
|
import { emptyLlmUsageAggregate, readLlmUsageAggregate } from "./health/llm-usage.js";
|
|
29
|
-
import { computeDegradationMetrics, computeDenominatorFixedCoverage, computeEnrichmentMintingRollup, probeStateDbRoundTrip, } from "./health/metrics.js";
|
|
30
30
|
import { collectPluginStalenessAdvisories } from "./health/plugin-staleness.js";
|
|
31
31
|
import { collectSchedulerBinaryAdvisory } from "./health/scheduler-binary.js";
|
|
32
32
|
import { collectStashExposureAdvisory } from "./health/stash-exposure.js";
|
|
33
|
-
import { collectSurfacesAdvisories } from "./health/surfaces.js";
|
|
34
|
-
import { buildPerRunSummaries } from "./health/task-runs.js";
|
|
35
33
|
import { buildTypeDirectoryAdvisory } from "./health/type-directory-check.js";
|
|
36
|
-
import {
|
|
34
|
+
import { MIN_ROWS_FOR_WORST_TASK_FAIL_RATE, } from "./health/types.js";
|
|
37
35
|
import { collectVersionDriftAdvisory } from "./health/version-drift.js";
|
|
38
|
-
import { buildWindowMetrics, computeDeltas,
|
|
36
|
+
import { buildImproveWindowSummary, buildWindowMetrics, computeDeltas, computeTaskWindowRates, resolveWindowCompare, } from "./health/windows.js";
|
|
39
37
|
const DEFAULT_SINCE_MS = 24 * 60 * 60 * 1000;
|
|
40
38
|
export function parseHealthSince(since) {
|
|
41
39
|
if (since === undefined || since.trim() === "") {
|
|
@@ -72,6 +70,65 @@ function validateAkmHealthOptions(options) {
|
|
|
72
70
|
}
|
|
73
71
|
}
|
|
74
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
|
+
}
|
|
75
132
|
/**
|
|
76
133
|
* Item 7: dedupe stuck-active rows by task_id (keeping the oldest/largest age
|
|
77
134
|
* per id) so the `active-runs` check can name WHICH tasks are stuck instead of
|
|
@@ -119,7 +176,7 @@ function computeWorstTaskFailRate(rows) {
|
|
|
119
176
|
return worst;
|
|
120
177
|
}
|
|
121
178
|
/** Table presence, the state.db round-trip probe, and task_history-derived rates. */
|
|
122
|
-
function gatherTaskHistoryPhase(db,
|
|
179
|
+
function gatherTaskHistoryPhase(db, since, stateDbPath, now) {
|
|
123
180
|
const tables = listExistingTableNames(db, ["events", "task_history", "proposals", "schema_migrations"]);
|
|
124
181
|
const tableNames = tables.map((row) => row.name).sort();
|
|
125
182
|
const requiredTables = ["events", "proposals", "schema_migrations", "task_history"];
|
|
@@ -130,23 +187,13 @@ function gatherTaskHistoryPhase(db, logsDb, since, stateDbPath, now) {
|
|
|
130
187
|
// bad index entry counts), and the freelist reading is purely informational.
|
|
131
188
|
const stateDbIntegrity = runStateDbQuickCheck(stateDbPath);
|
|
132
189
|
const stateDbFreelist = getStateDbFreelistInfo(stateDbPath);
|
|
133
|
-
const taskRows = queryTaskHistory(db, { since });
|
|
134
|
-
const { withLogs: taskRowsWithLogs, backed: existingLogRows } = partitionLogBackedRows(taskRows, logsDb);
|
|
135
|
-
const failedTaskRows = taskRows.filter((row) => row.status === "failed");
|
|
136
|
-
const activeRows = taskRows.filter((row) => row.status === "active" && row.completed_at === null);
|
|
137
|
-
const stuckActiveRows = activeRows.filter((row) => now() - new Date(row.started_at).getTime() > ACTIVE_RUN_WARN_MS);
|
|
138
190
|
// D8 (spec §5.3): a marked "command" row or a legacy (unmarked) "prompt"
|
|
139
191
|
// row is the agent/LLM arm; an unmarked "command" row is the legacy
|
|
140
192
|
// native shell/script arm and must not be counted here (see
|
|
141
|
-
// isAgentTaskHistoryRow's header comment
|
|
142
|
-
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
return typeof detail?.reason === "string" && detail.reason.length > 0;
|
|
146
|
-
});
|
|
147
|
-
const logBackingRate = taskRowsWithLogs.length === 0 ? 1 : existingLogRows.length / taskRowsWithLogs.length;
|
|
148
|
-
const taskFailRate = taskRows.length === 0 ? 0 : failedTaskRows.length / taskRows.length;
|
|
149
|
-
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);
|
|
150
197
|
return {
|
|
151
198
|
tableNames,
|
|
152
199
|
missingTables,
|
|
@@ -154,15 +201,12 @@ function gatherTaskHistoryPhase(db, logsDb, since, stateDbPath, now) {
|
|
|
154
201
|
stateDbIntegrity,
|
|
155
202
|
stateDbFreelist,
|
|
156
203
|
taskRowCount: taskRows.length,
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
stuckActiveTasks: dedupeStuckActiveTasks(stuckActiveRows, now),
|
|
161
|
-
logBackingRate,
|
|
162
|
-
taskFailRate,
|
|
204
|
+
stuckActiveRuns: rates.stuckActiveRuns,
|
|
205
|
+
stuckActiveTasks: dedupeStuckActiveTasks(rates.stuckActiveRows, now),
|
|
206
|
+
taskFailRate: rates.taskFailRate,
|
|
163
207
|
worstTaskFailRate: computeWorstTaskFailRate(taskRows),
|
|
164
|
-
agentFailureRate,
|
|
165
|
-
agentFailureReasonCounts:
|
|
208
|
+
agentFailureRate: rates.agentFailureRate,
|
|
209
|
+
agentFailureReasonCounts: rates.agentFailureReasonCounts,
|
|
166
210
|
};
|
|
167
211
|
}
|
|
168
212
|
/**
|
|
@@ -191,47 +235,16 @@ function gatherSessionExtractionLedgerPhase(db, now) {
|
|
|
191
235
|
return { since, rows: getExtractOutcomeCountsSince(db, since) };
|
|
192
236
|
}
|
|
193
237
|
/**
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const improveSkippedEvents = readEvents({ since, type: "improve_skipped" }, { dbPath: stateDbPath }).events;
|
|
202
|
-
const eventsMetrics = summarizeImproveCompleted(improveCompletedEvents);
|
|
203
|
-
const { metrics: improveSummary } = summarizeImproveRuns(db, since);
|
|
204
|
-
improveSummary.invoked = improveInvoked;
|
|
205
|
-
improveSummary.completed = eventsMetrics.completed;
|
|
206
|
-
const skipSummary = buildImproveSkipSummary(improveSkippedEvents);
|
|
207
|
-
improveSummary.skipped = skipSummary.skipped;
|
|
208
|
-
improveSummary.skipReasons = skipSummary.skipReasons;
|
|
209
|
-
const perRunSummaries = buildPerRunSummaries(db, since);
|
|
210
|
-
const wallTimes = perRunSummaries.map((run) => run.wallTimeMs).filter((ms) => Number.isFinite(ms) && ms > 0);
|
|
211
|
-
improveSummary.wallTime = computeWallTimeStats(wallTimes, improveSummary.wallTime.byPhase);
|
|
212
|
-
// WS-5: Compute denominator-fixed coverage and per-run degradation metrics
|
|
213
|
-
// for the main health path (not just window-compare mode).
|
|
214
|
-
const until = new Date(now()).toISOString();
|
|
215
|
-
const totalAssetsMain = improveSummary.memorySummary.eligible + improveSummary.memorySummary.derived;
|
|
216
|
-
improveSummary.coverage = computeDenominatorFixedCoverage(db, totalAssetsMain, improveSummary.memorySummary.eligible, since, until);
|
|
217
|
-
const degradationMain = computeDegradationMetrics(db, since, until);
|
|
218
|
-
if (degradationMain) {
|
|
219
|
-
improveSummary.degradation = degradationMain;
|
|
220
|
-
}
|
|
221
|
-
improveSummary.enrichmentMinting = computeEnrichmentMintingRollup(db, since, until);
|
|
222
|
-
return { improveSummary, perRunSummaries };
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* The best-effort advisory groups beyond the health-check registry: improve
|
|
226
|
-
* advisories, the `stash-git-exposure` probe, the 08 surfaces group
|
|
227
|
-
* (binary-config-skew, egress-endpoints), `type-directory-disagreement`
|
|
228
|
-
* (#831), `data-dir-usage` (#896), and `plugin-version` (itlackey/akm#832).
|
|
229
|
-
* Order matches emission order in the returned array. A probe/filesystem
|
|
230
|
-
* failure in any try/catch must not abort the health report — each group
|
|
231
|
-
* 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.
|
|
232
245
|
*/
|
|
233
|
-
function gatherAncillaryAdvisories(db,
|
|
234
|
-
const advisories = [
|
|
246
|
+
function gatherAncillaryAdvisories(db, options, egressConfigView) {
|
|
247
|
+
const advisories = [];
|
|
235
248
|
const indexStateMismatch = detectIndexStateGenerationMismatch(db);
|
|
236
249
|
if (indexStateMismatch)
|
|
237
250
|
advisories.push(indexStateMismatch);
|
|
@@ -258,10 +271,12 @@ function gatherAncillaryAdvisories(db, stateDbPath, since, improveSummary, optio
|
|
|
258
271
|
// egress-endpoints). Best-effort — a filesystem probe failure must not abort
|
|
259
272
|
// the health report.
|
|
260
273
|
try {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
274
|
+
const configSkew = collectConfigSkewAdvisory(getConfigPath());
|
|
275
|
+
if (configSkew)
|
|
276
|
+
advisories.push(configSkew);
|
|
277
|
+
const egress = collectEgressAdvisory(egressConfigView);
|
|
278
|
+
if (egress)
|
|
279
|
+
advisories.push(egress);
|
|
265
280
|
}
|
|
266
281
|
catch {
|
|
267
282
|
// Non-fatal.
|
|
@@ -277,6 +292,18 @@ function gatherAncillaryAdvisories(db, stateDbPath, since, improveSummary, optio
|
|
|
277
292
|
catch {
|
|
278
293
|
// Non-fatal.
|
|
279
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
|
+
}
|
|
280
307
|
// #896: report the data dir's total size and its largest top-level
|
|
281
308
|
// subdirectory, so a disk-usage blowup (e.g. unpruned migration snapshot
|
|
282
309
|
// backups, #897) is self-diagnosing instead of requiring `du` archaeology.
|
|
@@ -399,7 +426,7 @@ function detectIndexStateGenerationMismatch(stateDb) {
|
|
|
399
426
|
* per-window bundles and computes deltas between the earliest and latest
|
|
400
427
|
* window. Top-level metrics retain the primary `--since` query.
|
|
401
428
|
*/
|
|
402
|
-
function resolveWindowComparePhase(options, db, stateDbPath, now
|
|
429
|
+
function resolveWindowComparePhase(options, db, stateDbPath, now) {
|
|
403
430
|
let windowSpecs;
|
|
404
431
|
if (options.windowCompare) {
|
|
405
432
|
windowSpecs = resolveWindowCompare(options.windowCompare, now);
|
|
@@ -413,7 +440,7 @@ function resolveWindowComparePhase(options, db, stateDbPath, now, logsDb) {
|
|
|
413
440
|
windowResults = windowSpecs.map((spec) => {
|
|
414
441
|
const winSince = parseHealthSince(spec.since);
|
|
415
442
|
const winUntil = spec.until ? parseHealthSince(spec.until) : new Date(now()).toISOString();
|
|
416
|
-
const bundle = buildWindowMetrics(db, stateDbPath, winSince, winUntil, now
|
|
443
|
+
const bundle = buildWindowMetrics(db, stateDbPath, winSince, winUntil, now);
|
|
417
444
|
return {
|
|
418
445
|
name: spec.name,
|
|
419
446
|
since: winSince,
|
|
@@ -438,19 +465,13 @@ function resolveWindowComparePhase(options, db, stateDbPath, now, logsDb) {
|
|
|
438
465
|
return { windowResults, deltas };
|
|
439
466
|
}
|
|
440
467
|
/**
|
|
441
|
-
* The health report for a state.db
|
|
442
|
-
*
|
|
443
|
-
*
|
|
444
|
-
* deliberate consent (`akm upgrade` / `akm migrate apply` are the
|
|
445
|
-
* only two callers allowed to admit a historical-destructive migration; see
|
|
446
|
-
* `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.
|
|
447
471
|
*
|
|
448
472
|
* `akm health` is what an operator (or a bundler's boot check) runs when
|
|
449
473
|
* something else is misbehaving, so it must survive either problem long
|
|
450
|
-
* enough to NAME it
|
|
451
|
-
* way, but a pending migration was not: the managed open's refusal escaped as
|
|
452
|
-
* a thrown `ConfigError` (exit 78) and crashed the whole command before any
|
|
453
|
-
* check — including this one — could report anything.
|
|
474
|
+
* enough to NAME it rather than exit 78 before any check could report.
|
|
454
475
|
*
|
|
455
476
|
* Reported as a single hard-channel `fail` check — the run genuinely could
|
|
456
477
|
* not open state.db, so every check that depends on it is skipped rather than
|
|
@@ -470,13 +491,9 @@ function degradedStateDbReport(hardCheck, options) {
|
|
|
470
491
|
agentFailureRate: 0,
|
|
471
492
|
agentFailureReasonCounts: {},
|
|
472
493
|
stuckActiveRuns: 0,
|
|
473
|
-
logBackingRate: 0,
|
|
474
|
-
// `null`, not 0: the round-trip probe did not run, which is not the same
|
|
475
|
-
// as it running instantly.
|
|
476
|
-
probeRoundTripMs: null,
|
|
477
494
|
llmUsage: emptyLlmUsageAggregate(),
|
|
478
495
|
},
|
|
479
|
-
improve:
|
|
496
|
+
improve: emptyImproveMetrics(),
|
|
480
497
|
};
|
|
481
498
|
}
|
|
482
499
|
function unreadableStateDbCheck(detail) {
|
|
@@ -498,29 +515,25 @@ export async function akmHealth(options = {}) {
|
|
|
498
515
|
const stateDbPath = options.stateDbPath ?? getStateDbPathInDataDir();
|
|
499
516
|
const hardChecks = [];
|
|
500
517
|
const advisories = [];
|
|
501
|
-
// #791: an UNREADABLE state.db, or one
|
|
502
|
-
//
|
|
503
|
-
//
|
|
504
|
-
//
|
|
505
|
-
//
|
|
506
|
-
//
|
|
507
|
-
|
|
508
|
-
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;
|
|
509
525
|
try {
|
|
510
|
-
|
|
526
|
+
opened = openStateDatabaseWithReport(stateDbPath);
|
|
511
527
|
}
|
|
512
528
|
catch (error) {
|
|
513
529
|
const { access, code } = classifyPathAccess(stateDbPath);
|
|
514
530
|
if (access === "inaccessible") {
|
|
515
531
|
return degradedStateDbReport(unreadableStateDbCheck(describeInaccessiblePath(stateDbPath, code)), options);
|
|
516
532
|
}
|
|
517
|
-
//
|
|
518
|
-
//
|
|
519
|
-
//
|
|
520
|
-
//
|
|
521
|
-
// A ledger too broken to enumerate at all throws here too; in that case
|
|
522
|
-
// fall through to the generic config-error report below, since it is a
|
|
523
|
-
// 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.
|
|
524
537
|
let pendingMigrationsCheck;
|
|
525
538
|
try {
|
|
526
539
|
pendingMigrationsCheck = runPendingStateMigrationsCheck(stateDbPath, { listPendingStateMigrations });
|
|
@@ -533,16 +546,7 @@ export async function akmHealth(options = {}) {
|
|
|
533
546
|
}
|
|
534
547
|
throw new ConfigError(`Unable to open state.db: ${error instanceof Error ? error.message : String(error)}`, "INVALID_CONFIG_FILE");
|
|
535
548
|
}
|
|
536
|
-
|
|
537
|
-
// be opened, partitionLogBackedRows falls back to the on-disk file check, so
|
|
538
|
-
// health never hard-fails on a missing/locked logs database.
|
|
539
|
-
let logsDb;
|
|
540
|
-
try {
|
|
541
|
-
logsDb = openLogsDatabase(options.logsDbPath);
|
|
542
|
-
}
|
|
543
|
-
catch {
|
|
544
|
-
logsDb = undefined;
|
|
545
|
-
}
|
|
549
|
+
const { db } = opened;
|
|
546
550
|
try {
|
|
547
551
|
// Network probes overlap the local database phases below; awaited where consumed.
|
|
548
552
|
const engineProbesPromise = runHealthEngineProbes({ probeReachable: options.probe ? probeLlmEndpoint : undefined });
|
|
@@ -557,11 +561,15 @@ export async function akmHealth(options = {}) {
|
|
|
557
561
|
cliVersion: pkgVersion,
|
|
558
562
|
});
|
|
559
563
|
schedulerBinaryDriftPromise.catch(() => undefined);
|
|
560
|
-
const taskHistory = gatherTaskHistoryPhase(db,
|
|
564
|
+
const taskHistory = gatherTaskHistoryPhase(db, since, stateDbPath, now);
|
|
561
565
|
const { tableNames, missingTables, probe } = taskHistory;
|
|
562
566
|
const { egressConfigView, thinkingOffEngines } = gatherEgressConfigPhase();
|
|
563
|
-
|
|
564
|
-
|
|
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));
|
|
565
573
|
const sessionExtractionLedger = gatherSessionExtractionLedgerPhase(db, now);
|
|
566
574
|
// #950: computed once (no IO beyond config/env, same as gatherEgressConfigPhase)
|
|
567
575
|
// so `active-improve-strategy` and `engine-last-used` project the same
|
|
@@ -590,16 +598,13 @@ export async function akmHealth(options = {}) {
|
|
|
590
598
|
probe,
|
|
591
599
|
stateDbIntegrity: taskHistory.stateDbIntegrity,
|
|
592
600
|
stateDbFreelist: taskHistory.stateDbFreelist,
|
|
601
|
+
stateDbMigrations: { applied: opened.applied, ...(opened.backupPath ? { backupPath: opened.backupPath } : {}) },
|
|
593
602
|
taskRowCount: taskHistory.taskRowCount,
|
|
594
603
|
taskFailRate: taskHistory.taskFailRate,
|
|
595
|
-
taskRowsWithLogsCount: taskHistory.taskRowsWithLogsCount,
|
|
596
|
-
existingLogRowsCount: taskHistory.existingLogRowsCount,
|
|
597
|
-
logBackingRate: taskHistory.logBackingRate,
|
|
598
604
|
stuckActiveRuns: taskHistory.stuckActiveRuns,
|
|
599
605
|
stuckActiveTasks: taskHistory.stuckActiveTasks,
|
|
600
606
|
worstTaskFailRate: taskHistory.worstTaskFailRate,
|
|
601
607
|
agentFailureReasonCounts: taskHistory.agentFailureReasonCounts,
|
|
602
|
-
sessionExtraction: improveSummary.sessionExtraction,
|
|
603
608
|
sessionExtractionLedger,
|
|
604
609
|
autoAccept: improveSummary.autoAccept,
|
|
605
610
|
engineProbes,
|
|
@@ -624,20 +629,15 @@ export async function akmHealth(options = {}) {
|
|
|
624
629
|
agentFailureRate: roundRate(taskHistory.agentFailureRate),
|
|
625
630
|
agentFailureReasonCounts: taskHistory.agentFailureReasonCounts,
|
|
626
631
|
stuckActiveRuns: taskHistory.stuckActiveRuns,
|
|
627
|
-
logBackingRate: roundRate(taskHistory.logBackingRate),
|
|
628
|
-
probeRoundTripMs: probe.durationMs,
|
|
629
632
|
llmUsage,
|
|
630
633
|
};
|
|
631
634
|
const hardFailure = hardChecks.some((check) => check.status === "fail");
|
|
632
635
|
const deterministicWarnings = [...hardChecks, ...advisories].some((check) => check.status === "warn" && check.kind === "deterministic");
|
|
633
636
|
const status = hardFailure ? "fail" : deterministicWarnings ? "warn" : "pass";
|
|
634
637
|
// ── Window-compare mode (Phase 3) ─────────────────────────────────────
|
|
635
|
-
const { windowResults, deltas } = resolveWindowComparePhase(options, db, stateDbPath, now
|
|
638
|
+
const { windowResults, deltas } = resolveWindowComparePhase(options, db, stateDbPath, now);
|
|
636
639
|
// ── Per-run mode (Phase 2) ────────────────────────────────────────────
|
|
637
|
-
|
|
638
|
-
if (options.groupBy === "run") {
|
|
639
|
-
runs = buildPerRunSummaries(db, since);
|
|
640
|
-
}
|
|
640
|
+
const runs = options.groupBy === "run" ? perRunSummaries : undefined;
|
|
641
641
|
return {
|
|
642
642
|
schemaVersion: 3,
|
|
643
643
|
ok: !hardFailure,
|
|
@@ -654,14 +654,6 @@ export async function akmHealth(options = {}) {
|
|
|
654
654
|
}
|
|
655
655
|
finally {
|
|
656
656
|
db.close();
|
|
657
|
-
if (logsDb) {
|
|
658
|
-
try {
|
|
659
|
-
logsDb.close();
|
|
660
|
-
}
|
|
661
|
-
catch {
|
|
662
|
-
// best-effort
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
657
|
}
|
|
666
658
|
}
|
|
667
659
|
// Markdown renderers (renderRunsDetailMd / renderWindowCompareMd) live in
|
|
@@ -1,129 +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
|
-
* - `(already queued)` — the memory's body hash matches a pending
|
|
73
|
-
* consolidate proposal; the system prompt's PROMOTE rule forbids
|
|
74
|
-
* proposing promote for these. ~107/4h before this annotation.
|
|
75
|
-
*
|
|
76
|
-
* Both annotations are visible to the LLM. `pendingProposalBodyHashes`
|
|
77
|
-
* is precomputed once per run by `loadPendingConsolidateProposalHashes`
|
|
78
|
-
* 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.
|
|
79
30
|
*/
|
|
80
31
|
export function buildChunkPrompt(sourceName, memories, chunkIndex, totalChunks, bodyTruncation, pendingProposalBodyHashes = new Set()) {
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
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;
|
|
84
39
|
try {
|
|
85
40
|
body = fs.readFileSync(m.filePath, "utf8");
|
|
86
41
|
}
|
|
87
42
|
catch {
|
|
88
43
|
body = "(unreadable)";
|
|
89
44
|
}
|
|
90
|
-
// Hot/queued detection stays on the raw body — frontmatter is exactly
|
|
91
|
-
// what parseFrontmatter and the pending-proposal hash domain need.
|
|
92
|
-
const parsed = parseFrontmatter(body);
|
|
93
|
-
const isHot = parsed.data.captureMode === "hot";
|
|
94
|
-
// Use cacheHash (case-preserving stripped body) to match the domain used
|
|
95
|
-
// by loadPendingConsolidateProposalHashes and the body-embedding cache.
|
|
96
|
-
const bodyHash = cacheHash(body);
|
|
97
|
-
const isAlreadyQueued = pendingProposalBodyHashes.has(bodyHash);
|
|
98
|
-
// The excerpt shown to the LLM is the body ONLY — frontmatter can run
|
|
99
|
-
// longer than bodyTruncation (~21% of memories), which used to leave the
|
|
100
|
-
// model judging metadata instead of content. stripFrontmatterBody falls
|
|
101
|
-
// back to raw.trim() on unparseable/absent frontmatter, so this is a
|
|
102
|
-
// no-op for the "(unreadable)" placeholder.
|
|
103
|
-
const excerpt = stripFrontmatterBody(body);
|
|
104
|
-
annotationsByIndex.push({ isHot, isAlreadyQueued, excerpt });
|
|
105
|
-
}
|
|
106
|
-
const lines = [
|
|
107
|
-
`Source: ${sourceName}`,
|
|
108
|
-
`Chunk ${chunkIndex + 1} of ${totalChunks} (${memories.length} memories):`,
|
|
109
|
-
"",
|
|
110
|
-
];
|
|
111
|
-
for (let i = 0; i < memories.length; i++) {
|
|
112
|
-
const m = memories[i];
|
|
113
|
-
// `annotationsByIndex` has exactly one entry per memory (built in the loop above).
|
|
114
|
-
const { isHot, isAlreadyQueued, excerpt } = annotationsByIndex[i];
|
|
115
45
|
const annotations = [];
|
|
116
|
-
if (
|
|
46
|
+
if (parseFrontmatter(body).data.captureMode === "hot")
|
|
117
47
|
annotations.push("captureMode: hot");
|
|
118
|
-
if (
|
|
48
|
+
if (pendingProposalBodyHashes.has(contentHash(body, "body")))
|
|
119
49
|
annotations.push("already queued");
|
|
120
50
|
const annotationSuffix = annotations.length > 0 ? ` (${annotations.join("; ")})` : "";
|
|
121
|
-
lines.push(`[${i + 1}] memories/${m.name}${annotationSuffix}`);
|
|
122
|
-
|
|
123
|
-
lines.push(`Tags: ${m.tags.length > 0 ? m.tags.join(", ") : "(none)"}`);
|
|
124
|
-
lines.push("---");
|
|
125
|
-
lines.push(excerpt.slice(0, bodyTruncation));
|
|
126
|
-
lines.push("");
|
|
127
|
-
}
|
|
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
|
+
});
|
|
128
53
|
return lines.join("\n");
|
|
129
54
|
}
|