akm-cli 0.9.17-alpha.2 → 0.9.17-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +756 -0
- package/dist/akm +94 -196
- package/dist/cli/shared.js +6 -2
- package/dist/cli.js +22 -9
- package/dist/commands/agent/agent-dispatch.js +1 -1
- package/dist/commands/command/command-execution.js +24 -62
- package/dist/commands/feedback-cli.js +0 -1
- package/dist/commands/health/accept-rate.js +2 -2
- package/dist/commands/health/checks.js +30 -75
- package/dist/commands/health/config-skew.js +38 -0
- package/dist/commands/health/egress.js +54 -0
- package/dist/commands/health/html-report.js +0 -38
- package/dist/commands/health/improve-metrics.js +123 -562
- package/dist/commands/health/plugin-staleness.js +53 -3
- package/dist/commands/health/renderers.js +12 -4
- package/dist/commands/health/report-view-model.js +11 -106
- package/dist/commands/health/types-improve.js +4 -19
- package/dist/commands/health/windows.js +64 -73
- package/dist/commands/health.js +122 -143
- package/dist/commands/improve/consolidate/chunking.js +25 -100
- package/dist/commands/improve/consolidate/sanitize.js +54 -149
- package/dist/commands/improve/consolidate.js +538 -1075
- package/dist/commands/improve/content-hash.js +16 -24
- package/dist/commands/improve/distill/content-repair.js +18 -100
- package/dist/commands/improve/distill-guards.js +20 -81
- package/dist/commands/improve/distill-promotion-policy.js +23 -243
- package/dist/commands/improve/distill.js +608 -1075
- package/dist/commands/improve/eligibility.js +126 -400
- package/dist/commands/improve/execution.js +3 -5
- package/dist/commands/improve/extract.js +487 -1046
- package/dist/commands/improve/feedback-valence.js +0 -25
- package/dist/commands/improve/improve-cli.js +29 -166
- package/dist/commands/improve/improve-result-file.js +10 -66
- package/dist/commands/improve/improve-strategies.js +12 -7
- package/dist/commands/improve/improve-usage-report.js +18 -64
- package/dist/commands/improve/improve.js +443 -1063
- package/dist/commands/improve/ledger.js +114 -0
- package/dist/commands/improve/locks.js +2 -8
- package/dist/commands/improve/loop-stages.js +459 -1172
- package/dist/commands/improve/memory/derived-ref.js +12 -77
- package/dist/commands/improve/memory/memory-belief.js +14 -118
- package/dist/commands/improve/memory/memory-improve.js +4 -3
- package/dist/commands/improve/outcome-loop.js +28 -156
- package/dist/commands/improve/planner.js +5 -10
- package/dist/commands/improve/preparation.js +851 -2339
- package/dist/commands/improve/proactive-maintenance.js +34 -101
- package/dist/commands/improve/reflect-noise.js +104 -280
- package/dist/commands/improve/reflect.js +621 -1367
- package/dist/commands/improve/salience.js +46 -232
- package/dist/commands/improve/session-asset.js +19 -100
- package/dist/commands/improve/stage.js +323 -0
- package/dist/commands/proposal/drain.js +251 -644
- package/dist/commands/proposal/proposal-cli.js +3 -18
- package/dist/commands/proposal/proposal-types.js +20 -41
- package/dist/commands/proposal/proposal.js +1 -2
- package/dist/commands/proposal/propose.js +134 -160
- package/dist/commands/proposal/repository.js +502 -1487
- package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
- package/dist/commands/proposal/validators/proposal-validators.js +1 -1
- package/dist/commands/proposal/validators/proposals.js +13 -89
- package/dist/commands/read/curate.js +63 -413
- package/dist/commands/read/search-cli.js +16 -33
- package/dist/commands/read/search.js +17 -23
- package/dist/commands/read/show.js +2 -13
- package/dist/commands/sources/bundle-cli.js +25 -2
- package/dist/commands/sources/bundle-config-ops.js +7 -0
- package/dist/commands/sources/dangerous-env-audit.js +1 -2
- package/dist/commands/sources/info.js +2 -11
- package/dist/commands/sources/installed-stashes.js +197 -746
- package/dist/commands/sources/schema-repair.js +98 -129
- package/dist/commands/sources/source-add.js +62 -12
- package/dist/commands/sources/stash-cli.js +1 -1
- package/dist/commands/tasks/explain.js +10 -13
- package/dist/commands/tasks/tasks-cli.js +9 -8
- package/dist/commands/tasks/tasks.js +326 -930
- package/dist/commands/tasks/validate.js +42 -21
- package/dist/commands/workflow/plan.js +22 -29
- package/dist/commands/workflow-cli.js +4 -4
- package/dist/core/adapter/adapters/akm-adapter.js +0 -1
- package/dist/core/adapter/adapters/akm-lint.js +2 -3
- package/dist/core/adapter/adapters/akm-metadata.js +11 -12
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
- package/dist/core/adapter/execution-source.js +17 -29
- package/dist/core/asset/resolve-ref.js +1 -1
- package/dist/core/bundle-id.js +42 -5
- package/dist/core/bundle-rename.js +291 -0
- package/dist/core/config/config-io.js +1 -2
- package/dist/core/config/config-schema.js +1 -33
- package/dist/core/config/config-walker.js +1 -1
- package/dist/core/config/config.js +163 -68
- package/dist/core/config/legacy-source-shape-shim.js +38 -9
- package/dist/core/config/schema/embedding.js +20 -5
- package/dist/core/config/schema/engines.js +5 -0
- package/dist/core/config/schema/execution.js +1 -1
- package/dist/core/config/schema/experimental.js +1 -1
- package/dist/core/config/schema/improve-processes.js +21 -95
- package/dist/core/config/schema/improve.js +4 -42
- package/dist/core/config/schema/scheduler.js +12 -12
- package/dist/core/config/schema/search.js +6 -22
- package/dist/core/env-secret-ref.js +0 -1
- package/dist/core/errors.js +8 -9
- package/dist/core/file-lock.js +76 -173
- package/dist/core/logs-db.js +2 -2
- package/dist/core/paths.js +0 -27
- package/dist/core/redaction.js +109 -2
- package/dist/core/run-lock.js +2 -5
- package/dist/core/spawn-env.js +1 -1
- package/dist/core/state/migrations.js +108 -61
- package/dist/core/state-db-scope.js +2 -4
- package/dist/core/state-db.js +126 -692
- package/dist/core/type-presentation.js +1 -9
- package/dist/core/write-source.js +293 -1012
- package/dist/execution/input-contract.js +1 -1
- package/dist/execution/resolved-request.js +135 -689
- package/dist/execution/source.js +63 -257
- package/dist/execution/target-ref.js +1 -1
- package/dist/indexer/bundle-identity-guard.js +2 -2
- package/dist/indexer/db/graph-db.js +106 -46
- package/dist/indexer/ensure-index.js +44 -85
- package/dist/indexer/graph/graph-extraction.js +340 -562
- package/dist/indexer/graph/graph-related.js +130 -0
- package/dist/indexer/index-rebuild-lock.js +3 -11
- package/dist/indexer/index-writer-lock.js +8 -17
- package/dist/indexer/index-written-assets.js +139 -151
- package/dist/indexer/indexer.js +524 -846
- package/dist/indexer/materialize-embeddings.js +60 -397
- package/dist/indexer/passes/memory-inference.js +81 -90
- package/dist/indexer/passes/metadata.js +132 -200
- package/dist/indexer/read-preflight.js +0 -7
- package/dist/indexer/scan/doc-to-entry.js +1 -3
- package/dist/indexer/scan/drain-dir.js +1 -1
- package/dist/indexer/search/db-search.js +181 -590
- package/dist/indexer/search/fts-query.js +30 -41
- package/dist/indexer/search/ranking.js +28 -154
- package/dist/indexer/search/search-attribution.js +12 -32
- package/dist/indexer/search/search-fields.js +11 -15
- package/dist/indexer/search/search-hit-enrichers.js +54 -85
- package/dist/indexer/search/search-source.js +1 -4
- package/dist/indexer/usage/usage-events.js +2 -7
- package/dist/integrations/agent/engine-fallback.js +23 -40
- package/dist/integrations/agent/engine-resolution.js +93 -183
- package/dist/integrations/agent/execution.js +507 -0
- package/dist/integrations/agent/model-map.js +28 -156
- package/dist/integrations/agent/request-lowering.js +66 -141
- package/dist/integrations/agent/runner-dispatch.js +143 -321
- package/dist/integrations/agent/runner.js +54 -14
- package/dist/integrations/lockfile.js +53 -101
- package/dist/llm/embedders/deterministic.js +2 -3
- package/dist/llm/embedders/profile.js +71 -0
- package/dist/llm/embedders/remote.js +10 -15
- package/dist/llm/graph-extract.js +3 -12
- package/dist/llm/index-passes.js +3 -5
- package/dist/llm/memory-infer.js +1 -2
- package/dist/llm/metadata-enhance.js +1 -2
- package/dist/llm/structured-call.js +5 -24
- package/dist/output/generic-render.js +23 -11
- package/dist/output/html-render.js +13 -10
- package/dist/output/render-registry.js +3 -32
- package/dist/output/shapes/helpers.js +2 -34
- package/dist/output/shapes/passthrough.js +1 -9
- package/dist/{indexer/search/ranking-types.js → output/text/bundle-rename.js} +4 -1
- package/dist/output/text/command-format.js +60 -23
- package/dist/output/text/helpers.js +1 -1
- package/dist/output/text/migrate.js +5 -14
- package/dist/output/text/proposal-format.js +1 -2
- package/dist/output/text/workflow-format.js +0 -32
- package/dist/output/text.js +2 -0
- package/dist/registry/factory.js +4 -19
- package/dist/registry/network.js +66 -220
- package/dist/registry/providers/index.js +0 -2
- package/dist/registry/providers/skills-sh.js +3 -14
- package/dist/registry/providers/static-index.js +24 -26
- package/dist/registry/resolve.js +55 -131
- package/dist/scripts/akm-migrate-node.js +43937 -93313
- package/dist/scripts/akm-migrate.js +43697 -93071
- package/dist/setup/registry-stash-loader.js +4 -13
- package/dist/setup/semantic-assets.js +3 -44
- package/dist/setup/setup.js +1 -1
- package/dist/setup/steps/tasks.js +25 -15
- package/dist/sources/provider-factory.js +17 -18
- package/dist/sources/providers/filesystem.js +2 -3
- package/dist/sources/providers/git-install.js +7 -1
- package/dist/sources/providers/git-provider.js +0 -3
- package/dist/sources/providers/git-stash.js +0 -17
- package/dist/sources/providers/npm.js +2 -4
- package/dist/sources/providers/provider-utils.js +5 -10
- package/dist/sources/providers/website.js +0 -2
- package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
- package/dist/sources/website-url.js +2 -2
- package/dist/storage/database.js +9 -35
- package/dist/storage/repositories/improve-ledger-repository.js +168 -0
- package/dist/storage/repositories/index-connection.js +34 -70
- package/dist/storage/repositories/index-entries-repository.js +69 -111
- package/dist/storage/repositories/index-entry-mapper.js +1 -2
- package/dist/storage/repositories/index-entry-schema.js +83 -269
- package/dist/storage/repositories/index-fts-repository.js +86 -256
- package/dist/storage/repositories/index-llm-cache-repository.js +17 -0
- package/dist/storage/repositories/index-meta-repository.js +6 -4
- package/dist/storage/repositories/index-schema.js +192 -220
- package/dist/storage/repositories/index-utility-repository.js +8 -29
- package/dist/storage/repositories/index-vec-repository.js +133 -414
- package/dist/storage/repositories/outcome-repository.js +2 -1
- package/dist/storage/repositories/proposals-repository.js +35 -0
- package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
- package/dist/storage/repositories/task-history-repository.js +26 -4
- package/dist/storage/repositories/workflow-runs-repository.js +53 -244
- package/dist/storage/sqlite-migrations.js +136 -0
- package/dist/storage/sqlite-pragmas.js +11 -9
- package/dist/storage/sqlite-transaction.js +170 -0
- package/dist/storage/state-db-integrity.js +34 -27
- package/dist/tasks/activation-config.js +134 -62
- package/dist/tasks/backends/cron.js +129 -277
- package/dist/tasks/backends/exec-utils.js +2 -5
- package/dist/tasks/backends/launchd.js +125 -745
- package/dist/tasks/backends/schtasks.js +101 -620
- package/dist/tasks/prepare/prepare-support.js +5 -15
- package/dist/tasks/prepare/prepare.js +0 -2
- package/dist/tasks/resolve-akm-bin.js +20 -79
- package/dist/tasks/run/attempt-lifecycle.js +0 -1
- package/dist/tasks/scheduler-binding.js +18 -238
- package/dist/tasks/scheduler-invocation.js +52 -52
- package/dist/tasks/scheduler-lock.js +53 -0
- package/dist/tasks/scheduler-sync.js +363 -679
- package/dist/tasks/source/parse-task-source.js +160 -10
- package/dist/tasks/source/task-source-v3-frozen.js +3 -4
- package/dist/tasks/source/task-to-v4.js +2 -2
- package/dist/workflows/authoring/authoring.js +3 -12
- package/dist/workflows/compile.js +211 -0
- package/dist/workflows/concurrency-policy.js +13 -74
- package/dist/workflows/exec/child-invocation.js +3 -17
- package/dist/workflows/exec/child-workflow.js +32 -141
- package/dist/workflows/exec/dispatch-redaction.js +13 -53
- package/dist/workflows/exec/environment.js +98 -0
- package/dist/workflows/exec/exec-unit.js +33 -140
- package/dist/workflows/exec/frozen-judge.js +7 -59
- package/dist/workflows/exec/native-executor.js +82 -341
- package/dist/workflows/exec/param-secrets.js +29 -47
- package/dist/workflows/exec/run-workflow.js +154 -387
- package/dist/workflows/exec/scheduler.js +9 -36
- package/dist/workflows/exec/step-work.js +127 -430
- package/dist/workflows/exec/unit-dispatch.js +11 -63
- package/dist/workflows/exec/unit-writer.js +8 -52
- package/dist/workflows/exec/worktree.js +39 -273
- package/dist/workflows/freeze/child-output-references.js +4 -15
- package/dist/workflows/freeze/environment.js +99 -92
- package/dist/workflows/freeze/freeze.js +172 -0
- package/dist/workflows/freeze/step-values.js +19 -21
- package/dist/workflows/freeze/targets/child-workflow.js +23 -92
- package/dist/workflows/freeze/targets/command.js +10 -33
- package/dist/workflows/freeze/targets/script.js +5 -12
- package/dist/workflows/freeze/targets/shell.js +3 -6
- package/dist/workflows/freeze/targets/task.js +25 -80
- package/dist/workflows/freeze/task-bindings.js +20 -67
- package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
- package/dist/workflows/ir/params.js +6 -51
- package/dist/workflows/ir/plan-hash.js +2 -34
- package/dist/workflows/parser.js +140 -43
- package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
- package/dist/workflows/renderer.js +36 -69
- package/dist/workflows/resource-limits.js +12 -120
- package/dist/workflows/runtime/agent-identity.js +8 -40
- package/dist/workflows/runtime/run-outputs.js +3 -6
- package/dist/workflows/runtime/run-plan.js +316 -0
- package/dist/workflows/runtime/runs.js +48 -200
- package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
- package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
- package/dist/workflows/validate-summary.js +2 -7
- package/docs/integration/bundling-akm.md +49 -42
- package/docs/migration/README.md +1 -0
- package/docs/migration/release-notes/0.9.17.md +41 -0
- package/docs/migration/v0.9.1-to-v0.9.2.md +19 -7
- package/docs/reference/cli.md +182 -125
- package/docs/reference/configuration.md +49 -56
- package/docs/reference/data-and-telemetry.md +19 -20
- package/docs/reference/tasks.md +86 -38
- package/docs/reference/workflow-schema.md +14 -18
- package/docs/reference/workflows.md +6 -9
- package/package.json +1 -1
- package/schemas/akm-config.json +87 -406
- package/dist/commands/health/advisories.js +0 -150
- package/dist/commands/health/metrics.js +0 -329
- package/dist/commands/health/surfaces.js +0 -102
- package/dist/commands/improve/anti-collapse.js +0 -83
- package/dist/commands/improve/collapse-detector.js +0 -432
- package/dist/commands/improve/consolidate/eligibility.js +0 -48
- package/dist/commands/improve/consolidate/merge.js +0 -146
- package/dist/commands/improve/distill/promote-memory.js +0 -329
- package/dist/commands/improve/distill/quality-gate.js +0 -500
- package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
- package/dist/commands/improve/proposal-envelope.js +0 -31
- package/dist/commands/improve/run-context.js +0 -123
- package/dist/commands/improve/shared.js +0 -21
- package/dist/commands/improve/source-identity.js +0 -28
- package/dist/commands/improve/triage.js +0 -96
- package/dist/commands/proposal/drain-policies.js +0 -151
- package/dist/commands/sources/update-transaction.js +0 -220
- package/dist/core/action-contributors.js +0 -28
- package/dist/core/config/config-version-shim.js +0 -101
- package/dist/core/config/retired-experimental-keys-shim.js +0 -62
- package/dist/core/fs-txn.js +0 -405
- package/dist/core/lexical-score.js +0 -25
- package/dist/core/maintenance-barrier.js +0 -167
- package/dist/execution/executable-identity.js +0 -105
- package/dist/execution/guarded-source.js +0 -427
- package/dist/indexer/graph/graph-boost.js +0 -427
- package/dist/indexer/graph/graph-dedup.js +0 -95
- package/dist/indexer/search/name-match.js +0 -35
- package/dist/indexer/search/ranking-contributors.js +0 -515
- package/dist/indexer/walk/project-context.js +0 -192
- package/dist/integrations/agent/execution-cascade.js +0 -566
- package/dist/integrations/agent/execution-definitions.js +0 -202
- package/dist/integrations/agent/execution-lowering.js +0 -841
- package/dist/integrations/agent/execution-preparation.js +0 -98
- package/dist/integrations/agent/inline-execution.js +0 -74
- package/dist/registry/create-provider-registry.js +0 -29
- package/dist/registry/pinned-request-helper.js +0 -247
- package/dist/registry/pinned-transport.js +0 -717
- package/dist/sources/providers/index.js +0 -14
- package/dist/storage/engines/sqlite-migrations.js +0 -271
- package/dist/storage/repositories/canaries-repository.js +0 -107
- package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
- package/dist/storage/repositories/registry-cache.js +0 -113
- package/dist/tasks/scheduler-sync-preview.js +0 -52
- package/dist/workflows/freeze/resolve-steps.js +0 -86
- package/dist/workflows/freeze/source-freeze.js +0 -64
- package/dist/workflows/ir/compile.js +0 -321
- package/dist/workflows/ir/environment-v4.js +0 -330
- package/dist/workflows/ir/freeze-v4.js +0 -153
- package/dist/workflows/ir/schema-v4.js +0 -745
- package/dist/workflows/ir/schema.js +0 -354
- package/dist/workflows/program/schema.js +0 -78
- package/dist/workflows/runtime/checkin.js +0 -57
- package/dist/workflows/runtime/plan-classifier.js +0 -196
- package/dist/workflows/runtime/unit-checkin.js +0 -45
- package/dist/workflows/runtime/unit-phases.js +0 -20
- package/dist/workflows/schema.js +0 -4
- package/dist/workflows/source-ir/compile.js +0 -200
- package/dist/workflows/source-ir/program.js +0 -50
- package/dist/workflows/source-ir/result.js +0 -26
- package/dist/workflows/source-ir/schema.js +0 -786
- package/dist/workflows/source-ir/triggers.js +0 -79
- package/dist/workflows/source-ir/uses.js +0 -40
- package/dist/workflows/validator.js +0 -60
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,20 @@ 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 { collectImproveAdvisories } from "./health/advisories.js";
|
|
24
22
|
import { HEALTH_CHECKS, probeActiveImproveStrategy, runHealthEngineProbes, runPendingStateMigrationsCheck, SESSION_EXTRACTION_LEDGER_WINDOW_DAYS, } from "./health/checks.js";
|
|
23
|
+
import { collectConfigSkewAdvisory } from "./health/config-skew.js";
|
|
25
24
|
import { collectDataDirUsageAdvisory } from "./health/data-dir-usage.js";
|
|
25
|
+
import { collectEgressAdvisory } from "./health/egress.js";
|
|
26
26
|
import { engineLastUsedSince, readLastEngineUsage } from "./health/engine-usage.js";
|
|
27
|
-
import {
|
|
27
|
+
import { emptyImproveMetrics, roundRate } from "./health/improve-metrics.js";
|
|
28
28
|
import { emptyLlmUsageAggregate, readLlmUsageAggregate } from "./health/llm-usage.js";
|
|
29
|
-
import { computeDegradationMetrics, computeDenominatorFixedCoverage, computeEnrichmentMintingRollup, probeStateDbRoundTrip, } from "./health/metrics.js";
|
|
30
29
|
import { collectPluginStalenessAdvisories } from "./health/plugin-staleness.js";
|
|
31
30
|
import { collectSchedulerBinaryAdvisory } from "./health/scheduler-binary.js";
|
|
32
31
|
import { collectStashExposureAdvisory } from "./health/stash-exposure.js";
|
|
33
|
-
import { collectSurfacesAdvisories } from "./health/surfaces.js";
|
|
34
|
-
import { buildPerRunSummaries } from "./health/task-runs.js";
|
|
35
32
|
import { buildTypeDirectoryAdvisory } from "./health/type-directory-check.js";
|
|
36
|
-
import {
|
|
33
|
+
import { MIN_ROWS_FOR_WORST_TASK_FAIL_RATE, } from "./health/types.js";
|
|
37
34
|
import { collectVersionDriftAdvisory } from "./health/version-drift.js";
|
|
38
|
-
import { buildWindowMetrics, computeDeltas,
|
|
35
|
+
import { buildImproveWindowSummary, buildWindowMetrics, computeDeltas, computeTaskWindowRates, resolveWindowCompare, } from "./health/windows.js";
|
|
39
36
|
const DEFAULT_SINCE_MS = 24 * 60 * 60 * 1000;
|
|
40
37
|
export function parseHealthSince(since) {
|
|
41
38
|
if (since === undefined || since.trim() === "") {
|
|
@@ -72,6 +69,65 @@ function validateAkmHealthOptions(options) {
|
|
|
72
69
|
}
|
|
73
70
|
}
|
|
74
71
|
}
|
|
72
|
+
// ── akmHealth phase helpers (chunk-9 WI-9.5b; file-level decompose following
|
|
73
|
+
// the function's natural gather/advise/check/assemble phases) ───────────────
|
|
74
|
+
/** Event type appended + read back by the state.db round-trip probe. */
|
|
75
|
+
const HEALTH_PROBE_EVENT = "health_probe";
|
|
76
|
+
/** Synthetic sentinel ref (ref-grammar decision D-R3): a colon-free
|
|
77
|
+
* `<subsystem>/_<marker>` label. `health` has no asset stash-subdir, so
|
|
78
|
+
* `health/_probe` names the subsystem. */
|
|
79
|
+
const HEALTH_PROBE_REF = "health/_probe";
|
|
80
|
+
/**
|
|
81
|
+
* Verify state.db can accept a write and read it back — WITHOUT leaving any
|
|
82
|
+
* permanent trace (R-030). Earlier versions appended a `health_probe` event
|
|
83
|
+
* on every `akm health` invocation and never removed it: a read-only health
|
|
84
|
+
* check ran on a cron would grow state.db without bound (the only purge is
|
|
85
|
+
* `improve`'s retention pass, which a health-only user never runs). The probe
|
|
86
|
+
* row inserted here is deleted again inside the SAME connection once the
|
|
87
|
+
* round trip is confirmed, so the net effect on the `events` table is always
|
|
88
|
+
* zero rows — the round trip still genuinely exercises append + read against
|
|
89
|
+
* the real table, it just doesn't accumulate.
|
|
90
|
+
*/
|
|
91
|
+
export function probeStateDbRoundTrip(stateDbPath) {
|
|
92
|
+
const started = Date.now();
|
|
93
|
+
try {
|
|
94
|
+
return withStateDb((db) => {
|
|
95
|
+
const ts = new Date().toISOString();
|
|
96
|
+
const insertedId = insertEvent(db, {
|
|
97
|
+
eventType: HEALTH_PROBE_EVENT,
|
|
98
|
+
ts,
|
|
99
|
+
ref: HEALTH_PROBE_REF,
|
|
100
|
+
metadata: { source: "akm health" },
|
|
101
|
+
});
|
|
102
|
+
const durationMs = Date.now() - started;
|
|
103
|
+
if (insertedId === undefined) {
|
|
104
|
+
return { ok: false, durationMs, error: "probe event insert did not return a row id" };
|
|
105
|
+
}
|
|
106
|
+
// The round-trip matches on the exact (id, eventType, ref) triple
|
|
107
|
+
// written above, then removes the row regardless of outcome — a
|
|
108
|
+
// failed round trip must not leak a row any more than a successful
|
|
109
|
+
// one should.
|
|
110
|
+
let roundTripOk = false;
|
|
111
|
+
try {
|
|
112
|
+
const row = db
|
|
113
|
+
.prepare("SELECT 1 AS present FROM events WHERE id = ? AND event_type = ? AND ref = ?")
|
|
114
|
+
.get(insertedId, HEALTH_PROBE_EVENT, HEALTH_PROBE_REF);
|
|
115
|
+
roundTripOk = row !== undefined;
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
db.prepare("DELETE FROM events WHERE id = ?").run(insertedId);
|
|
119
|
+
}
|
|
120
|
+
if (!roundTripOk) {
|
|
121
|
+
return { ok: false, durationMs, error: "probe event was not readable after append" };
|
|
122
|
+
}
|
|
123
|
+
return { ok: true, durationMs };
|
|
124
|
+
}, { path: stateDbPath });
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
rethrowIfTestIsolationError(err);
|
|
128
|
+
return { ok: false, durationMs: Date.now() - started, error: err instanceof Error ? err.message : String(err) };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
75
131
|
/**
|
|
76
132
|
* Item 7: dedupe stuck-active rows by task_id (keeping the oldest/largest age
|
|
77
133
|
* per id) so the `active-runs` check can name WHICH tasks are stuck instead of
|
|
@@ -119,7 +175,7 @@ function computeWorstTaskFailRate(rows) {
|
|
|
119
175
|
return worst;
|
|
120
176
|
}
|
|
121
177
|
/** Table presence, the state.db round-trip probe, and task_history-derived rates. */
|
|
122
|
-
function gatherTaskHistoryPhase(db,
|
|
178
|
+
function gatherTaskHistoryPhase(db, since, stateDbPath, now) {
|
|
123
179
|
const tables = listExistingTableNames(db, ["events", "task_history", "proposals", "schema_migrations"]);
|
|
124
180
|
const tableNames = tables.map((row) => row.name).sort();
|
|
125
181
|
const requiredTables = ["events", "proposals", "schema_migrations", "task_history"];
|
|
@@ -130,23 +186,13 @@ function gatherTaskHistoryPhase(db, logsDb, since, stateDbPath, now) {
|
|
|
130
186
|
// bad index entry counts), and the freelist reading is purely informational.
|
|
131
187
|
const stateDbIntegrity = runStateDbQuickCheck(stateDbPath);
|
|
132
188
|
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
189
|
// D8 (spec §5.3): a marked "command" row or a legacy (unmarked) "prompt"
|
|
139
190
|
// row is the agent/LLM arm; an unmarked "command" row is the legacy
|
|
140
191
|
// 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;
|
|
192
|
+
// isAgentTaskHistoryRow's header comment, referenced from
|
|
193
|
+
// computeTaskWindowRates, for the full mapping).
|
|
194
|
+
const taskRows = queryTaskHistory(db, { since });
|
|
195
|
+
const rates = computeTaskWindowRates(taskRows, now);
|
|
150
196
|
return {
|
|
151
197
|
tableNames,
|
|
152
198
|
missingTables,
|
|
@@ -154,15 +200,12 @@ function gatherTaskHistoryPhase(db, logsDb, since, stateDbPath, now) {
|
|
|
154
200
|
stateDbIntegrity,
|
|
155
201
|
stateDbFreelist,
|
|
156
202
|
taskRowCount: taskRows.length,
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
stuckActiveTasks: dedupeStuckActiveTasks(stuckActiveRows, now),
|
|
161
|
-
logBackingRate,
|
|
162
|
-
taskFailRate,
|
|
203
|
+
stuckActiveRuns: rates.stuckActiveRuns,
|
|
204
|
+
stuckActiveTasks: dedupeStuckActiveTasks(rates.stuckActiveRows, now),
|
|
205
|
+
taskFailRate: rates.taskFailRate,
|
|
163
206
|
worstTaskFailRate: computeWorstTaskFailRate(taskRows),
|
|
164
|
-
agentFailureRate,
|
|
165
|
-
agentFailureReasonCounts:
|
|
207
|
+
agentFailureRate: rates.agentFailureRate,
|
|
208
|
+
agentFailureReasonCounts: rates.agentFailureReasonCounts,
|
|
166
209
|
};
|
|
167
210
|
}
|
|
168
211
|
/**
|
|
@@ -191,47 +234,16 @@ function gatherSessionExtractionLedgerPhase(db, now) {
|
|
|
191
234
|
return { since, rows: getExtractOutcomeCountsSince(db, since) };
|
|
192
235
|
}
|
|
193
236
|
/**
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
237
|
+
* The best-effort advisory groups beyond the health-check registry: the
|
|
238
|
+
* `stash-git-exposure` probe, the 08 surfaces group (binary-config-skew,
|
|
239
|
+
* egress-endpoints), `type-directory-disagreement` (#831), `data-dir-usage`
|
|
240
|
+
* (#896), and `plugin-version` (itlackey/akm#832). Order matches emission
|
|
241
|
+
* order in the returned array. A probe/filesystem failure in any try/catch
|
|
242
|
+
* must not abort the health report — each group degrades to "no advisory"
|
|
243
|
+
* independently.
|
|
197
244
|
*/
|
|
198
|
-
function
|
|
199
|
-
const
|
|
200
|
-
const improveCompletedEvents = readEvents({ since, type: IMPROVE_COMPLETED_EVENT }, { dbPath: stateDbPath }).events;
|
|
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.
|
|
232
|
-
*/
|
|
233
|
-
function gatherAncillaryAdvisories(db, stateDbPath, since, improveSummary, options, egressConfigView) {
|
|
234
|
-
const advisories = [...collectImproveAdvisories(db, stateDbPath, since, improveSummary)];
|
|
245
|
+
function gatherAncillaryAdvisories(db, options, egressConfigView) {
|
|
246
|
+
const advisories = [];
|
|
235
247
|
const indexStateMismatch = detectIndexStateGenerationMismatch(db);
|
|
236
248
|
if (indexStateMismatch)
|
|
237
249
|
advisories.push(indexStateMismatch);
|
|
@@ -258,10 +270,12 @@ function gatherAncillaryAdvisories(db, stateDbPath, since, improveSummary, optio
|
|
|
258
270
|
// egress-endpoints). Best-effort — a filesystem probe failure must not abort
|
|
259
271
|
// the health report.
|
|
260
272
|
try {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
273
|
+
const configSkew = collectConfigSkewAdvisory(getConfigPath());
|
|
274
|
+
if (configSkew)
|
|
275
|
+
advisories.push(configSkew);
|
|
276
|
+
const egress = collectEgressAdvisory(egressConfigView);
|
|
277
|
+
if (egress)
|
|
278
|
+
advisories.push(egress);
|
|
265
279
|
}
|
|
266
280
|
catch {
|
|
267
281
|
// Non-fatal.
|
|
@@ -399,7 +413,7 @@ function detectIndexStateGenerationMismatch(stateDb) {
|
|
|
399
413
|
* per-window bundles and computes deltas between the earliest and latest
|
|
400
414
|
* window. Top-level metrics retain the primary `--since` query.
|
|
401
415
|
*/
|
|
402
|
-
function resolveWindowComparePhase(options, db, stateDbPath, now
|
|
416
|
+
function resolveWindowComparePhase(options, db, stateDbPath, now) {
|
|
403
417
|
let windowSpecs;
|
|
404
418
|
if (options.windowCompare) {
|
|
405
419
|
windowSpecs = resolveWindowCompare(options.windowCompare, now);
|
|
@@ -413,7 +427,7 @@ function resolveWindowComparePhase(options, db, stateDbPath, now, logsDb) {
|
|
|
413
427
|
windowResults = windowSpecs.map((spec) => {
|
|
414
428
|
const winSince = parseHealthSince(spec.since);
|
|
415
429
|
const winUntil = spec.until ? parseHealthSince(spec.until) : new Date(now()).toISOString();
|
|
416
|
-
const bundle = buildWindowMetrics(db, stateDbPath, winSince, winUntil, now
|
|
430
|
+
const bundle = buildWindowMetrics(db, stateDbPath, winSince, winUntil, now);
|
|
417
431
|
return {
|
|
418
432
|
name: spec.name,
|
|
419
433
|
since: winSince,
|
|
@@ -438,19 +452,13 @@ function resolveWindowComparePhase(options, db, stateDbPath, now, logsDb) {
|
|
|
438
452
|
return { windowResults, deltas };
|
|
439
453
|
}
|
|
440
454
|
/**
|
|
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`).
|
|
455
|
+
* The health report for a state.db the open cannot reach at all: either the
|
|
456
|
+
* file is not readable (#791), or — the same shape, a different cause — the
|
|
457
|
+
* open failed while applying a pending migration, which is left pending.
|
|
447
458
|
*
|
|
448
459
|
* `akm health` is what an operator (or a bundler's boot check) runs when
|
|
449
460
|
* 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.
|
|
461
|
+
* enough to NAME it rather than exit 78 before any check could report.
|
|
454
462
|
*
|
|
455
463
|
* Reported as a single hard-channel `fail` check — the run genuinely could
|
|
456
464
|
* not open state.db, so every check that depends on it is skipped rather than
|
|
@@ -470,13 +478,9 @@ function degradedStateDbReport(hardCheck, options) {
|
|
|
470
478
|
agentFailureRate: 0,
|
|
471
479
|
agentFailureReasonCounts: {},
|
|
472
480
|
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
481
|
llmUsage: emptyLlmUsageAggregate(),
|
|
478
482
|
},
|
|
479
|
-
improve:
|
|
483
|
+
improve: emptyImproveMetrics(),
|
|
480
484
|
};
|
|
481
485
|
}
|
|
482
486
|
function unreadableStateDbCheck(detail) {
|
|
@@ -498,29 +502,25 @@ export async function akmHealth(options = {}) {
|
|
|
498
502
|
const stateDbPath = options.stateDbPath ?? getStateDbPathInDataDir();
|
|
499
503
|
const hardChecks = [];
|
|
500
504
|
const advisories = [];
|
|
501
|
-
// #791: an UNREADABLE state.db, or one
|
|
502
|
-
//
|
|
503
|
-
//
|
|
504
|
-
//
|
|
505
|
-
//
|
|
506
|
-
//
|
|
507
|
-
|
|
508
|
-
let db;
|
|
505
|
+
// #791: an UNREADABLE state.db, or one whose pending migration failed to
|
|
506
|
+
// apply, are the two failures `akm health` most needs to be able to report,
|
|
507
|
+
// because this is the command an operator (or a bundler's boot check) runs
|
|
508
|
+
// to find out why everything else is behaving oddly. Report it as a finding
|
|
509
|
+
// instead of dying before any check runs. The open itself applies every
|
|
510
|
+
// pending migration; `state-db-migrations` reports what it applied.
|
|
511
|
+
let opened;
|
|
509
512
|
try {
|
|
510
|
-
|
|
513
|
+
opened = openStateDatabaseWithReport(stateDbPath);
|
|
511
514
|
}
|
|
512
515
|
catch (error) {
|
|
513
516
|
const { access, code } = classifyPathAccess(stateDbPath);
|
|
514
517
|
if (access === "inaccessible") {
|
|
515
518
|
return degradedStateDbReport(unreadableStateDbCheck(describeInaccessiblePath(stateDbPath, code)), options);
|
|
516
519
|
}
|
|
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.
|
|
520
|
+
// A migration that failed to apply rolled back and is still pending:
|
|
521
|
+
// name it from a read-only listing (which never applies anything) rather
|
|
522
|
+
// than pattern-matching the error text. A ledger too broken to enumerate
|
|
523
|
+
// throws here too and falls through to the generic config error below.
|
|
524
524
|
let pendingMigrationsCheck;
|
|
525
525
|
try {
|
|
526
526
|
pendingMigrationsCheck = runPendingStateMigrationsCheck(stateDbPath, { listPendingStateMigrations });
|
|
@@ -533,16 +533,7 @@ export async function akmHealth(options = {}) {
|
|
|
533
533
|
}
|
|
534
534
|
throw new ConfigError(`Unable to open state.db: ${error instanceof Error ? error.message : String(error)}`, "INVALID_CONFIG_FILE");
|
|
535
535
|
}
|
|
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
|
-
}
|
|
536
|
+
const { db } = opened;
|
|
546
537
|
try {
|
|
547
538
|
// Network probes overlap the local database phases below; awaited where consumed.
|
|
548
539
|
const engineProbesPromise = runHealthEngineProbes({ probeReachable: options.probe ? probeLlmEndpoint : undefined });
|
|
@@ -557,11 +548,15 @@ export async function akmHealth(options = {}) {
|
|
|
557
548
|
cliVersion: pkgVersion,
|
|
558
549
|
});
|
|
559
550
|
schedulerBinaryDriftPromise.catch(() => undefined);
|
|
560
|
-
const taskHistory = gatherTaskHistoryPhase(db,
|
|
551
|
+
const taskHistory = gatherTaskHistoryPhase(db, since, stateDbPath, now);
|
|
561
552
|
const { tableNames, missingTables, probe } = taskHistory;
|
|
562
553
|
const { egressConfigView, thinkingOffEngines } = gatherEgressConfigPhase();
|
|
563
|
-
|
|
564
|
-
|
|
554
|
+
// Same window bundle `--window-compare` builds per-window (windows.ts) —
|
|
555
|
+
// reused here for the main `--since` window's improve summary and for
|
|
556
|
+
// `--group-by run`, so neither re-reads `improve_runs`.
|
|
557
|
+
const until = new Date(now()).toISOString();
|
|
558
|
+
const { improve: improveSummary, perRunSummaries } = buildImproveWindowSummary(db, stateDbPath, since, until);
|
|
559
|
+
advisories.push(...gatherAncillaryAdvisories(db, options, egressConfigView));
|
|
565
560
|
const sessionExtractionLedger = gatherSessionExtractionLedgerPhase(db, now);
|
|
566
561
|
// #950: computed once (no IO beyond config/env, same as gatherEgressConfigPhase)
|
|
567
562
|
// so `active-improve-strategy` and `engine-last-used` project the same
|
|
@@ -590,16 +585,13 @@ export async function akmHealth(options = {}) {
|
|
|
590
585
|
probe,
|
|
591
586
|
stateDbIntegrity: taskHistory.stateDbIntegrity,
|
|
592
587
|
stateDbFreelist: taskHistory.stateDbFreelist,
|
|
588
|
+
stateDbMigrations: { applied: opened.applied, ...(opened.backupPath ? { backupPath: opened.backupPath } : {}) },
|
|
593
589
|
taskRowCount: taskHistory.taskRowCount,
|
|
594
590
|
taskFailRate: taskHistory.taskFailRate,
|
|
595
|
-
taskRowsWithLogsCount: taskHistory.taskRowsWithLogsCount,
|
|
596
|
-
existingLogRowsCount: taskHistory.existingLogRowsCount,
|
|
597
|
-
logBackingRate: taskHistory.logBackingRate,
|
|
598
591
|
stuckActiveRuns: taskHistory.stuckActiveRuns,
|
|
599
592
|
stuckActiveTasks: taskHistory.stuckActiveTasks,
|
|
600
593
|
worstTaskFailRate: taskHistory.worstTaskFailRate,
|
|
601
594
|
agentFailureReasonCounts: taskHistory.agentFailureReasonCounts,
|
|
602
|
-
sessionExtraction: improveSummary.sessionExtraction,
|
|
603
595
|
sessionExtractionLedger,
|
|
604
596
|
autoAccept: improveSummary.autoAccept,
|
|
605
597
|
engineProbes,
|
|
@@ -624,20 +616,15 @@ export async function akmHealth(options = {}) {
|
|
|
624
616
|
agentFailureRate: roundRate(taskHistory.agentFailureRate),
|
|
625
617
|
agentFailureReasonCounts: taskHistory.agentFailureReasonCounts,
|
|
626
618
|
stuckActiveRuns: taskHistory.stuckActiveRuns,
|
|
627
|
-
logBackingRate: roundRate(taskHistory.logBackingRate),
|
|
628
|
-
probeRoundTripMs: probe.durationMs,
|
|
629
619
|
llmUsage,
|
|
630
620
|
};
|
|
631
621
|
const hardFailure = hardChecks.some((check) => check.status === "fail");
|
|
632
622
|
const deterministicWarnings = [...hardChecks, ...advisories].some((check) => check.status === "warn" && check.kind === "deterministic");
|
|
633
623
|
const status = hardFailure ? "fail" : deterministicWarnings ? "warn" : "pass";
|
|
634
624
|
// ── Window-compare mode (Phase 3) ─────────────────────────────────────
|
|
635
|
-
const { windowResults, deltas } = resolveWindowComparePhase(options, db, stateDbPath, now
|
|
625
|
+
const { windowResults, deltas } = resolveWindowComparePhase(options, db, stateDbPath, now);
|
|
636
626
|
// ── Per-run mode (Phase 2) ────────────────────────────────────────────
|
|
637
|
-
|
|
638
|
-
if (options.groupBy === "run") {
|
|
639
|
-
runs = buildPerRunSummaries(db, since);
|
|
640
|
-
}
|
|
627
|
+
const runs = options.groupBy === "run" ? perRunSummaries : undefined;
|
|
641
628
|
return {
|
|
642
629
|
schemaVersion: 3,
|
|
643
630
|
ok: !hardFailure,
|
|
@@ -654,14 +641,6 @@ export async function akmHealth(options = {}) {
|
|
|
654
641
|
}
|
|
655
642
|
finally {
|
|
656
643
|
db.close();
|
|
657
|
-
if (logsDb) {
|
|
658
|
-
try {
|
|
659
|
-
logsDb.close();
|
|
660
|
-
}
|
|
661
|
-
catch {
|
|
662
|
-
// best-effort
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
644
|
}
|
|
666
645
|
}
|
|
667
646
|
// 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
|
}
|