akm-cli 0.9.1 → 0.9.2-alpha.2
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 +103 -28
- package/README.md +3 -1
- package/SECURITY.md +1 -1
- package/STABILITY.md +1 -1
- package/dist/akm +2 -2
- package/dist/akm-migrate +2 -2
- package/dist/assets/hints/cli-hints-full.md +14 -9
- package/dist/assets/improve-strategies/proactive-maintenance.json +1 -1
- package/dist/assets/improve-strategies/reflect-distill.json +1 -1
- package/dist/assets/models.json +35 -0
- package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +3 -4
- package/dist/assets/stash-skeleton/facts/conventions/organization.md +1 -3
- package/dist/assets/tasks/core/extract.yml +6 -5
- package/dist/assets/tasks/core/improve.yml +6 -5
- package/dist/assets/tasks/core/index-refresh.yml +6 -5
- package/dist/assets/tasks/core/sync.yml +6 -5
- package/dist/assets/tasks/core/version-check.yml +6 -5
- package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-catchup.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-consolidate.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-frequent.yml +6 -5
- package/dist/assets/tasks/improve/akm-improve-nightly.yml +6 -5
- package/dist/cli/confirm.js +2 -2
- package/dist/cli/parse-args.js +3 -24
- package/dist/cli/retired-commands.js +1 -1
- package/dist/cli/shared.js +2 -2
- package/dist/cli.js +11 -9
- package/dist/commands/agent/agent-dispatch.js +55 -89
- package/dist/commands/agent/contribute-cli.js +12 -45
- package/dist/commands/command/builtin-action.js +32 -0
- package/dist/commands/command/command-cli.js +99 -0
- package/dist/commands/command/command-execution.js +308 -0
- package/dist/commands/command/execution-source-loader.js +176 -0
- package/dist/commands/command/portable-template.js +60 -0
- package/dist/commands/config-cli.js +10 -4
- package/dist/commands/env/env.js +4 -2
- package/dist/commands/feedback-cli.js +1 -1
- package/dist/commands/health/checks.js +241 -29
- package/dist/commands/health/html-report.js +0 -14
- package/dist/commands/health/report-view-model.js +0 -1
- package/dist/commands/health/surfaces.js +6 -7
- package/dist/commands/health/types.js +0 -2
- package/dist/commands/health.js +63 -18
- package/dist/commands/improve/collapse-detector.js +5 -6
- package/dist/commands/improve/consolidate.js +251 -214
- package/dist/commands/improve/distill/promote-memory.js +71 -34
- package/dist/commands/improve/distill/quality-gate.js +17 -5
- package/dist/commands/improve/distill.js +232 -155
- package/dist/commands/improve/eligibility.js +112 -79
- package/dist/commands/improve/execution.js +57 -0
- package/dist/commands/improve/extract-cli.js +5 -5
- package/dist/commands/improve/extract-prompt.js +64 -22
- package/dist/commands/improve/extract.js +608 -360
- package/dist/commands/improve/improve-strategies.js +43 -14
- package/dist/commands/improve/improve.js +249 -29
- package/dist/commands/improve/loop-stages.js +11 -17
- package/dist/commands/improve/memory/memory-contradiction-detect.js +90 -66
- package/dist/commands/improve/outcome-loop.js +22 -38
- package/dist/commands/improve/planner.js +134 -0
- package/dist/commands/improve/preparation.js +730 -409
- package/dist/commands/improve/reflect.js +386 -223
- package/dist/commands/improve/run-context.js +3 -4
- package/dist/commands/improve/salience.js +6 -58
- package/dist/commands/improve/session-asset.js +12 -12
- package/dist/commands/lint/index.js +101 -29
- package/dist/commands/migrate-cli.js +11 -69
- package/dist/commands/migration-tool.js +6 -9
- package/dist/commands/models-cli.js +27 -0
- package/dist/commands/proposal/drain.js +258 -186
- package/dist/commands/proposal/proposal-cli.js +32 -10
- package/dist/commands/proposal/proposal.js +2 -5
- package/dist/commands/proposal/propose.js +192 -172
- package/dist/commands/proposal/repository.js +54 -91
- package/dist/commands/proposal/validators/proposal-validators.js +9 -7
- package/dist/commands/read/curate.js +53 -22
- package/dist/commands/read/registry-search.js +25 -9
- package/dist/commands/read/remember-cli.js +14 -2
- package/dist/commands/read/search.js +10 -4
- package/dist/commands/read/show.js +139 -153
- package/dist/commands/registry-cli.js +16 -7
- package/dist/commands/remember.js +33 -18
- package/dist/commands/sources/add-cli.js +19 -178
- package/dist/commands/sources/bundle-cli.js +15 -3
- package/dist/commands/sources/dangerous-env-audit.js +135 -0
- package/dist/commands/sources/info.js +2 -1
- package/dist/commands/sources/installed-stashes.js +901 -177
- package/dist/commands/sources/schema-repair.js +174 -95
- package/dist/commands/sources/self-update.js +30 -74
- package/dist/commands/sources/source-add.js +3 -5
- package/dist/commands/sources/sources-cli.js +2 -15
- package/dist/commands/sources/update-transaction.js +220 -0
- package/dist/commands/tasks/tasks-cli.js +3 -3
- package/dist/commands/tasks/tasks.js +736 -317
- package/dist/commands/workflow-cli.js +2 -2
- package/dist/core/adapter/adapters/agent-skills-adapter.js +3 -0
- package/dist/core/adapter/adapters/akm-adapter.js +85 -35
- package/dist/core/adapter/adapters/akm-lint.js +54 -39
- package/dist/core/adapter/adapters/akm-metadata.js +45 -45
- package/dist/core/adapter/adapters/akm-task-adapter.js +32 -49
- package/dist/core/adapter/adapters/akm-workflow-adapter.js +38 -23
- package/dist/core/adapter/adapters/dotenv-adapter.js +30 -1
- package/dist/core/adapter/adapters/generic-files-adapter.js +11 -0
- package/dist/core/adapter/adapters/index.js +0 -9
- package/dist/core/adapter/adapters/llm-wiki-adapter.js +4 -0
- package/dist/core/adapter/adapters/okf-adapter.js +4 -0
- package/dist/core/adapter/adapters/opencode-adapter.js +5 -8
- package/dist/core/adapter/adapters/tool-dir-shared.js +63 -6
- package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -0
- package/dist/core/adapter/execution-source.js +308 -0
- package/dist/core/adapter/recognize-match.js +36 -13
- package/dist/core/adapter/registry.js +0 -9
- package/dist/core/asset/stash-meta.js +94 -4
- package/dist/core/common.js +6 -11
- package/dist/core/config/config-io.js +3 -3
- package/dist/core/config/config-schema.js +18 -40
- package/dist/core/config/config-sources.js +11 -21
- package/dist/core/config/config-walker.js +31 -13
- package/dist/core/config/config.js +23 -26
- package/dist/core/config/schema/engines.js +8 -7
- package/dist/core/config/schema/improve-processes.js +29 -5
- package/dist/core/config/schema/index-config.js +0 -27
- package/dist/core/config/schema/primitives.js +1 -23
- package/dist/core/config/schema/sources-bundles.js +13 -16
- package/dist/core/errors.js +2 -0
- package/dist/core/events.js +68 -32
- package/dist/core/extra-params.js +1 -0
- package/dist/core/improve-result.js +315 -0
- package/dist/core/lesson-lint.js +0 -6
- package/dist/core/maintenance-barrier.js +4 -4
- package/dist/core/network-policy.js +152 -0
- package/dist/core/paths.js +1 -1
- package/dist/core/recognition-util.js +4 -4
- package/dist/core/registry-url.js +456 -0
- package/dist/core/state/migrations.js +161 -47
- package/dist/core/state-db.js +453 -80
- package/dist/core/system-error.js +32 -0
- package/dist/core/time.js +2 -12
- package/dist/core/write-source.js +0 -18
- package/dist/execution/directory-identity.js +52 -0
- package/dist/execution/executable-identity.js +107 -0
- package/dist/execution/guarded-source.js +398 -0
- package/dist/execution/json.js +95 -0
- package/dist/{commands/health/types-session-log.js → execution/limits.js} +2 -1
- package/dist/execution/record.js +55 -0
- package/dist/execution/resolved-request.js +730 -0
- package/dist/execution/source.js +320 -0
- package/dist/indexer/bundle-identity-guard.js +5 -4
- package/dist/indexer/db/graph-db.js +33 -0
- package/dist/indexer/graph/graph-boost.js +3 -4
- package/dist/indexer/graph/graph-extraction.js +562 -373
- package/dist/indexer/index-written-assets.js +78 -39
- package/dist/indexer/indexer.js +471 -432
- package/dist/indexer/installations.js +6 -0
- package/dist/indexer/lookup/adapter-concept-owner.js +283 -0
- package/dist/indexer/materialize-embeddings.js +155 -0
- package/dist/indexer/passes/memory-inference.js +227 -174
- package/dist/indexer/passes/metadata.js +263 -118
- package/dist/indexer/scan/doc-to-entry.js +7 -10
- package/dist/indexer/scan/drain-dir.js +51 -23
- package/dist/indexer/search/db-search.js +156 -50
- package/dist/indexer/search/fts-query.js +40 -40
- package/dist/indexer/search/ranking.js +36 -1
- package/dist/indexer/search/search-attribution.js +3 -1
- package/dist/indexer/search/search-fields.js +23 -14
- package/dist/indexer/search/search-hit-enrichers.js +1 -1
- package/dist/indexer/search/search-source.js +7 -16
- package/dist/indexer/search/semantic-status.js +10 -1
- package/dist/indexer/usage/show-usage.js +105 -0
- package/dist/indexer/usage/usage-events.js +7 -2
- package/dist/indexer/walk/matchers.js +40 -10
- package/dist/indexer/walk/path-resolver.js +5 -2
- package/dist/indexer/walk/walker.js +20 -2
- package/dist/integrations/agent/builder-shared.js +3 -6
- package/dist/integrations/agent/conversation-fallback.js +16 -0
- package/dist/integrations/agent/engine-resolution.js +87 -87
- package/dist/integrations/agent/execution-cascade.js +566 -0
- package/dist/integrations/agent/execution-definitions.js +211 -0
- package/dist/integrations/agent/execution-lowering.js +811 -0
- package/dist/integrations/agent/execution-preparation.js +67 -0
- package/dist/integrations/agent/index.js +0 -2
- package/dist/integrations/agent/inline-execution.js +74 -0
- package/dist/integrations/agent/model-map.js +515 -0
- package/dist/integrations/agent/persona-fallback.js +30 -0
- package/dist/integrations/agent/request-lowering.js +186 -0
- package/dist/integrations/agent/runner-dispatch.js +230 -37
- package/dist/integrations/agent/runner.js +12 -83
- package/dist/integrations/harnesses/aider/agent-builder.js +8 -0
- package/dist/integrations/harnesses/aider/index.js +0 -1
- package/dist/integrations/harnesses/amazonq/agent-builder.js +8 -0
- package/dist/integrations/harnesses/amazonq/index.js +0 -1
- package/dist/integrations/harnesses/claude/agent-builder.js +14 -1
- package/dist/integrations/harnesses/claude/index.js +1 -5
- package/dist/integrations/harnesses/claude/session-log.js +3 -33
- package/dist/integrations/harnesses/codex/agent-builder.js +8 -0
- package/dist/integrations/harnesses/codex/index.js +0 -1
- package/dist/integrations/harnesses/copilot/agent-builder.js +8 -0
- package/dist/integrations/harnesses/copilot/index.js +0 -1
- package/dist/integrations/harnesses/gemini/agent-builder.js +8 -0
- package/dist/integrations/harnesses/gemini/index.js +0 -1
- package/dist/integrations/harnesses/index.js +4 -44
- package/dist/integrations/harnesses/opencode/agent-builder.js +16 -9
- package/dist/integrations/harnesses/opencode/index.js +0 -2
- package/dist/integrations/harnesses/opencode/session-log.js +14 -204
- package/dist/integrations/harnesses/opencode-sdk/harness.js +12 -1
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +40 -42
- package/dist/integrations/harnesses/openhands/agent-builder.js +8 -0
- package/dist/integrations/harnesses/openhands/index.js +0 -1
- package/dist/integrations/harnesses/pi/agent-builder.js +8 -0
- package/dist/integrations/harnesses/pi/index.js +0 -1
- package/dist/integrations/harnesses/shared.js +0 -1
- package/dist/integrations/harnesses/types.js +1 -3
- package/dist/integrations/lockfile.js +82 -79
- package/dist/integrations/session-logs/index.js +6 -17
- package/dist/integrations/session-logs/provider-base.js +1 -29
- package/dist/llm/client.js +10 -5
- package/dist/llm/embedder.js +6 -7
- package/dist/llm/embedders/local.js +37 -88
- package/dist/llm/embedders/types.js +1 -1
- package/dist/llm/graph-extract.js +75 -50
- package/dist/llm/index-passes.js +43 -5
- package/dist/llm/memory-infer.js +8 -6
- package/dist/llm/metadata-enhance.js +5 -3
- package/dist/llm/structured-call.js +122 -25
- package/dist/output/format-exempt.js +1 -1
- package/dist/output/render-registry.js +0 -16
- package/dist/output/renderers.js +12 -7
- package/dist/output/shapes/curate.js +1 -0
- package/dist/output/shapes/helpers.js +10 -2
- package/dist/output/shapes/passthrough.js +2 -0
- package/dist/output/text/command-format.js +31 -33
- package/dist/output/text/health-format.js +1 -29
- package/dist/output/text/migrate.js +6 -56
- package/dist/output/text/proposal-format.js +16 -1
- package/dist/output/text/workflow-format.js +16 -0
- package/dist/registry/network.js +279 -0
- package/dist/registry/pinned-request-helper.js +247 -0
- package/dist/registry/pinned-transport.js +717 -0
- package/dist/registry/providers/skills-sh.js +18 -6
- package/dist/registry/providers/static-index.js +20 -7
- package/dist/registry/resolve.js +53 -28
- package/dist/scripts/akm-migrate-node.js +19334 -52269
- package/dist/scripts/akm-migrate.js +19270 -51612
- package/dist/setup/registry-stash-loader.js +64 -20
- package/dist/setup/semantic-assets.js +9 -34
- package/dist/setup/setup.js +12 -30
- package/dist/setup/source-identity.js +17 -0
- package/dist/setup/steps/sources.js +36 -15
- package/dist/setup/steps/tasks.js +39 -11
- package/dist/sources/providers/git-provider.js +3 -3
- package/dist/sources/providers/npm.js +2 -2
- package/dist/sources/providers/provider-utils.js +4 -3
- package/dist/sources/providers/website.js +11 -7
- package/dist/sources/snapshot-fetchers/host-guard.js +9 -136
- package/dist/sources/snapshot-fetchers/website-ingest.js +25 -109
- package/dist/sources/website-url.js +73 -0
- package/dist/storage/engines/sqlite-migrations.js +81 -26
- package/dist/storage/managed-db.js +27 -24
- package/dist/storage/repositories/events-repository.js +3 -0
- package/dist/storage/repositories/index-connection.js +42 -10
- package/dist/storage/repositories/index-entries-repository.js +203 -229
- package/dist/storage/repositories/index-entry-mapper.js +8 -12
- package/dist/storage/repositories/index-entry-schema.js +255 -0
- package/dist/storage/repositories/index-fts-repository.js +64 -71
- package/dist/storage/repositories/index-llm-cache-repository.js +8 -13
- package/dist/storage/repositories/index-meta-repository.js +0 -11
- package/dist/storage/repositories/index-schema.js +74 -350
- package/dist/storage/repositories/index-utility-repository.js +12 -17
- package/dist/storage/repositories/index-vec-repository.js +56 -7
- package/dist/storage/repositories/proposals-repository.js +4 -127
- package/dist/storage/repositories/registry-cache.js +2 -1
- package/dist/storage/repositories/task-history-repository.js +20 -40
- package/dist/storage/repositories/workflow-runs-repository.js +228 -129
- package/dist/storage/sqlite-read-snapshot.js +148 -0
- package/dist/tasks/backends/cron.js +170 -42
- package/dist/tasks/backends/index.js +1 -1
- package/dist/tasks/backends/launchd.js +787 -202
- package/dist/tasks/backends/schtasks.js +282 -83
- package/dist/tasks/embedded.js +7 -7
- package/dist/tasks/frozen-script.js +50 -0
- package/dist/tasks/resolve-akm-bin.js +5 -1
- package/dist/tasks/runner.js +239 -251
- package/dist/tasks/runtime-v3.js +281 -0
- package/dist/tasks/scheduler-binding.js +272 -0
- package/dist/tasks/scheduler-invocation.js +57 -43
- package/dist/tasks/scheduler-sync.js +654 -0
- package/dist/tasks/source-v3.js +752 -0
- package/dist/tasks/standalone-script-entry.js +5 -0
- package/dist/tasks/task-id.js +29 -0
- package/dist/workflows/authoring/authoring.js +15 -32
- package/dist/workflows/exec/dispatch-redaction.js +14 -8
- package/dist/workflows/exec/exec-unit.js +7 -28
- package/dist/workflows/exec/frozen-judge.js +57 -89
- package/dist/workflows/exec/lowering-notices.js +23 -0
- package/dist/workflows/exec/native-executor.js +301 -458
- package/dist/workflows/exec/param-secrets.js +4 -3
- package/dist/workflows/exec/run-workflow.js +26 -32
- package/dist/workflows/exec/step-work.js +105 -109
- package/dist/workflows/exec/unit-dispatch.js +103 -27
- package/dist/workflows/exec/unit-writer.js +3 -3
- package/dist/workflows/exec/worktree.js +2 -2
- package/dist/workflows/ir/compile.js +86 -72
- package/dist/workflows/ir/environment-v4.js +328 -0
- package/dist/workflows/ir/freeze-v4.js +122 -0
- package/dist/workflows/ir/plan-hash.js +13 -7
- package/dist/workflows/ir/schema-v4.js +525 -0
- package/dist/workflows/ir/schema.js +25 -284
- package/dist/workflows/ir/source-freeze-v4.js +506 -0
- package/dist/workflows/parser.js +27 -24
- package/dist/workflows/program/schema.js +1 -2
- package/dist/workflows/renderer.js +42 -29
- package/dist/workflows/resource-limits.js +4 -5
- package/dist/workflows/runtime/agent-identity.js +11 -13
- package/dist/workflows/runtime/plan-classifier.js +8 -8
- package/dist/workflows/runtime/runs.js +27 -43
- package/dist/workflows/runtime/workflow-asset-loader.js +45 -205
- package/dist/workflows/source-files.js +373 -0
- package/dist/workflows/source-ir/compile.js +196 -0
- package/dist/workflows/source-ir/github-yaml.js +577 -0
- package/dist/workflows/source-ir/ordering.js +38 -0
- package/dist/workflows/source-ir/program.js +50 -0
- package/dist/workflows/source-ir/result.js +26 -0
- package/dist/workflows/source-ir/schema.js +772 -0
- package/dist/workflows/source-ir/semantics.js +242 -0
- package/dist/workflows/source-ir/uses.js +14 -0
- package/docs/README.md +2 -0
- package/docs/migration/README.md +3 -1
- package/docs/migration/release-notes/0.9.2.md +55 -0
- package/docs/migration/release-notes/README.md +5 -0
- package/docs/migration/v0.8-to-v0.9.md +76 -1077
- package/docs/migration/v0.9.0-troubleshooting.md +104 -516
- package/docs/migration/v0.9.1-to-v0.9.2.md +150 -0
- package/docs/reference/README.md +1 -0
- package/docs/reference/cli.md +230 -98
- package/docs/reference/configuration.md +159 -36
- package/docs/reference/data-and-telemetry.md +19 -1
- package/docs/reference/supported-formats.md +23 -3
- package/docs/reference/tasks.md +182 -0
- package/docs/reference/workflow-schema.md +91 -40
- package/docs/reference/workflows.md +33 -6
- package/package.json +10 -6
- package/schemas/akm-config.json +372 -224
- package/schemas/akm-task.json +324 -80
- package/schemas/akm-workflow.json +6 -9
- package/dist/core/migration-operation.js +0 -75
- package/dist/integrations/agent/model-aliases.js +0 -74
- package/dist/tasks/parser.js +0 -380
- package/dist/tasks/schema.js +0 -123
- package/dist/tasks/validator.js +0 -80
- package/dist/workflows/ir/freeze.js +0 -320
- package/dist/workflows/runtime/document-cache.js +0 -13
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
4
|
import { spawnSync } from "node:child_process";
|
|
5
5
|
import { loadConfig } from "../../core/config/config.js";
|
|
6
|
+
import { ConfigError } from "../../core/errors.js";
|
|
6
7
|
import { withEngineFallback } from "../../integrations/agent/engine-fallback.js";
|
|
7
8
|
import { resolveEngine } from "../../integrations/agent/engine-resolution.js";
|
|
8
|
-
import {
|
|
9
|
+
import { executionEngineDefinitionsFromConfig } from "../../integrations/agent/execution-definitions.js";
|
|
10
|
+
import { loadModelMap, mergeModelMapLayers, parseModelMapLayer, readInstalledModelMapText, resolveModelMapAlias, userModelMapPath, } from "../../integrations/agent/model-map.js";
|
|
9
11
|
import { resolveImprovePlan } from "../improve/improve-strategies.js";
|
|
10
|
-
import { ACTIVE_RUN_WARN_MS, TASK_FAIL_RATE_WARN
|
|
12
|
+
import { ACTIVE_RUN_WARN_MS, TASK_FAIL_RATE_WARN } from "./types.js";
|
|
11
13
|
function credentialAvailable(credential, env) {
|
|
12
14
|
return !credential?.required || credential.names.some((name) => Boolean(env[name]?.trim()));
|
|
13
15
|
}
|
|
@@ -36,8 +38,21 @@ function runConfiguredEngineProbe(checkName, engineName, config, deps) {
|
|
|
36
38
|
packageAvailable = false;
|
|
37
39
|
}
|
|
38
40
|
const binary = configuredEngine.bin ?? "opencode";
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
let binaryAvailable;
|
|
42
|
+
try {
|
|
43
|
+
const version = (deps.spawnSync ?? spawnSync)(binary, ["--version"], { encoding: "utf8", timeout: 5_000 });
|
|
44
|
+
binaryAvailable = (version.status ?? 1) === 0;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return {
|
|
48
|
+
name: checkName,
|
|
49
|
+
kind: "deterministic",
|
|
50
|
+
status: "warn",
|
|
51
|
+
confidence: "high",
|
|
52
|
+
message: `SDK engine "${engineName}" executable availability could not be checked.`,
|
|
53
|
+
evidence: { engine: engineName, runtimeKind: "sdk", binaryAvailable: false },
|
|
54
|
+
};
|
|
55
|
+
}
|
|
41
56
|
const fallbackEngine = configuredEngine.llmEngine ?? config.defaults?.llmEngine;
|
|
42
57
|
let fallback;
|
|
43
58
|
let fallbackCredential;
|
|
@@ -51,8 +66,8 @@ function runConfiguredEngineProbe(checkName, engineName, config, deps) {
|
|
|
51
66
|
catch {
|
|
52
67
|
sdkRunner = undefined;
|
|
53
68
|
}
|
|
54
|
-
if (sdkRunner?.fallbackConnection) {
|
|
55
|
-
fallback = { kind: "llm", connection: sdkRunner.fallbackConnection };
|
|
69
|
+
if (sdkRunner?.fallbackConnection && fallbackEngine) {
|
|
70
|
+
fallback = { kind: "llm", engine: fallbackEngine, connection: sdkRunner.fallbackConnection };
|
|
56
71
|
fallbackCredential = sdkRunner.fallbackCredential;
|
|
57
72
|
}
|
|
58
73
|
else if (fallbackEngine) {
|
|
@@ -67,9 +82,7 @@ function runConfiguredEngineProbe(checkName, engineName, config, deps) {
|
|
|
67
82
|
fallback = undefined;
|
|
68
83
|
}
|
|
69
84
|
}
|
|
70
|
-
const configuredModel = configuredEngine.model
|
|
71
|
-
? resolveModel(configuredEngine.model, "opencode-sdk", configuredEngine.modelAliases, config.modelAliases)
|
|
72
|
-
: undefined;
|
|
85
|
+
const configuredModel = configuredEngine.model;
|
|
73
86
|
const effectiveModel = sdkRunner?.profile.model ?? configuredModel ?? fallback?.connection.model;
|
|
74
87
|
const fallbackCredentialAvailable = credentialAvailable(fallbackCredential, env);
|
|
75
88
|
const missing = [
|
|
@@ -169,6 +182,58 @@ function runConfiguredEngineProbe(checkName, engineName, config, deps) {
|
|
|
169
182
|
};
|
|
170
183
|
}
|
|
171
184
|
}
|
|
185
|
+
function projectEngineProbe(result, name) {
|
|
186
|
+
return result.name === name ? result : { ...result, name };
|
|
187
|
+
}
|
|
188
|
+
function projectSelectedEngineProbe(availability, engineName, checkName) {
|
|
189
|
+
const result = availability.get(engineName);
|
|
190
|
+
if (result)
|
|
191
|
+
return projectEngineProbe(result, checkName);
|
|
192
|
+
return {
|
|
193
|
+
name: checkName,
|
|
194
|
+
kind: "deterministic",
|
|
195
|
+
status: "warn",
|
|
196
|
+
confidence: "high",
|
|
197
|
+
message: `Engine "${engineName}" availability could not be checked.`,
|
|
198
|
+
evidence: { engine: engineName },
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function unconfiguredEngineProbe(name) {
|
|
202
|
+
return {
|
|
203
|
+
name,
|
|
204
|
+
kind: "deterministic",
|
|
205
|
+
status: "unknown",
|
|
206
|
+
confidence: "high",
|
|
207
|
+
message: name === "default-llm-engine" ? "No default LLM engine is configured." : "No default engine is configured.",
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function configuredEnginesProjection(engineNames, availability) {
|
|
211
|
+
if (engineNames.length === 0) {
|
|
212
|
+
return {
|
|
213
|
+
name: "configured-engines",
|
|
214
|
+
kind: "deterministic",
|
|
215
|
+
status: "unknown",
|
|
216
|
+
confidence: "high",
|
|
217
|
+
message: "No engines are explicitly configured.",
|
|
218
|
+
evidence: { engines: [] },
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
const engines = engineNames.map((engine) => ({
|
|
222
|
+
engine,
|
|
223
|
+
status: availability.get(engine)?.status === "pass" ? "pass" : "warn",
|
|
224
|
+
}));
|
|
225
|
+
const unavailable = engines.filter((engine) => engine.status === "warn").length;
|
|
226
|
+
return {
|
|
227
|
+
name: "configured-engines",
|
|
228
|
+
kind: "deterministic",
|
|
229
|
+
status: unavailable === 0 ? "pass" : "warn",
|
|
230
|
+
confidence: "high",
|
|
231
|
+
message: unavailable === 0
|
|
232
|
+
? `All ${engines.length} explicitly configured engines are available.`
|
|
233
|
+
: `${unavailable} of ${engines.length} explicitly configured engines ${unavailable === 1 ? "is" : "are"} unavailable.`,
|
|
234
|
+
evidence: { engines },
|
|
235
|
+
};
|
|
236
|
+
}
|
|
172
237
|
export function runDefaultEngineProbe(deps = {}) {
|
|
173
238
|
// Probe the effective view: an install with no `defaults.engine` but a usable
|
|
174
239
|
// opencode binary DOES have a working default, and reporting otherwise would
|
|
@@ -180,6 +245,38 @@ export function runDefaultLlmEngineProbe(deps = {}) {
|
|
|
180
245
|
const config = deps.loadConfig?.() ?? loadConfig();
|
|
181
246
|
return runConfiguredEngineProbe("default-llm-engine", config.defaults?.llmEngine, config, deps);
|
|
182
247
|
}
|
|
248
|
+
/** Probe every explicitly configured engine without exposing connection or model material. */
|
|
249
|
+
export function runConfiguredEnginesProbe(deps = {}) {
|
|
250
|
+
const config = deps.loadConfig?.() ?? loadConfig();
|
|
251
|
+
const engineNames = Object.keys(config.engines ?? {}).sort();
|
|
252
|
+
const availability = new Map(engineNames.map((engine) => [engine, runConfiguredEngineProbe("configured-engine", engine, config, deps)]));
|
|
253
|
+
return configuredEnginesProjection(engineNames, availability);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Build the per-health-run availability snapshot. Each distinct selected or
|
|
257
|
+
* explicitly configured engine is probed once; the three public checks are
|
|
258
|
+
* projections of that immutable result set.
|
|
259
|
+
*/
|
|
260
|
+
export function runHealthEngineProbes(deps = {}) {
|
|
261
|
+
const configured = deps.loadConfig?.() ?? loadConfig();
|
|
262
|
+
const effective = withEngineFallback(configured, deps.which).config;
|
|
263
|
+
const defaultEngineName = effective.defaults?.engine;
|
|
264
|
+
const defaultLlmEngineName = configured.defaults?.llmEngine;
|
|
265
|
+
const explicitEngineNames = Object.keys(configured.engines ?? {}).sort();
|
|
266
|
+
const probeNames = [...new Set([...explicitEngineNames, defaultEngineName, defaultLlmEngineName])]
|
|
267
|
+
.filter((name) => name !== undefined)
|
|
268
|
+
.sort();
|
|
269
|
+
const availability = new Map(probeNames.map((engine) => [engine, runConfiguredEngineProbe("configured-engine", engine, effective, deps)]));
|
|
270
|
+
return Object.freeze({
|
|
271
|
+
defaultEngine: defaultEngineName
|
|
272
|
+
? projectSelectedEngineProbe(availability, defaultEngineName, "default-engine")
|
|
273
|
+
: unconfiguredEngineProbe("default-engine"),
|
|
274
|
+
defaultLlmEngine: defaultLlmEngineName
|
|
275
|
+
? projectSelectedEngineProbe(availability, defaultLlmEngineName, "default-llm-engine")
|
|
276
|
+
: unconfiguredEngineProbe("default-llm-engine"),
|
|
277
|
+
configuredEngines: configuredEnginesProjection(explicitEngineNames, availability),
|
|
278
|
+
});
|
|
279
|
+
}
|
|
183
280
|
export function runActiveImproveStrategyProbe(deps = {}) {
|
|
184
281
|
const config = deps.loadConfig?.() ?? loadConfig();
|
|
185
282
|
const strategyName = config.defaults?.improveStrategy ?? "default";
|
|
@@ -226,6 +323,124 @@ export function runActiveImproveStrategyProbe(deps = {}) {
|
|
|
226
323
|
};
|
|
227
324
|
}
|
|
228
325
|
}
|
|
326
|
+
/**
|
|
327
|
+
* Validate the immutable installed model map and optional user overlay.
|
|
328
|
+
* Installed corruption is a package defect (fail); a bad optional user file
|
|
329
|
+
* is operator-fixable configuration (warn); absence is the normal state.
|
|
330
|
+
*/
|
|
331
|
+
export function runModelMapProbe(options = {}) {
|
|
332
|
+
let installedText;
|
|
333
|
+
try {
|
|
334
|
+
installedText = readInstalledModelMapText(options);
|
|
335
|
+
mergeModelMapLayers(parseModelMapLayer(installedText, "installed models.json"));
|
|
336
|
+
}
|
|
337
|
+
catch (error) {
|
|
338
|
+
return {
|
|
339
|
+
name: "model-map-files",
|
|
340
|
+
kind: "deterministic",
|
|
341
|
+
status: "fail",
|
|
342
|
+
confidence: "high",
|
|
343
|
+
message: `Installed models.json is invalid; this AKM installation cannot resolve model aliases: ${error instanceof Error ? error.message : String(error)}`,
|
|
344
|
+
evidence: { installedSource: "package asset" },
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
try {
|
|
348
|
+
const loaded = loadModelMap({ ...options, installedText });
|
|
349
|
+
return {
|
|
350
|
+
name: "model-map-files",
|
|
351
|
+
kind: "deterministic",
|
|
352
|
+
status: "pass",
|
|
353
|
+
confidence: "high",
|
|
354
|
+
message: loaded.userStatus === "loaded"
|
|
355
|
+
? `Installed defaults and user models.json at ${loaded.userPath} are valid.`
|
|
356
|
+
: "Installed model defaults are valid; no optional user models.json is present.",
|
|
357
|
+
evidence: {
|
|
358
|
+
installedSource: "package asset",
|
|
359
|
+
userPath: loaded.userPath,
|
|
360
|
+
userStatus: loaded.userStatus,
|
|
361
|
+
aliases: Object.keys(loaded.map.aliases).sort(),
|
|
362
|
+
},
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
catch (error) {
|
|
366
|
+
const hint = error instanceof ConfigError ? error.hint() : undefined;
|
|
367
|
+
return {
|
|
368
|
+
name: "model-map-files",
|
|
369
|
+
kind: "deterministic",
|
|
370
|
+
status: "warn",
|
|
371
|
+
confidence: "high",
|
|
372
|
+
message: `${error instanceof Error ? error.message : String(error)}${hint ? ` ${hint}` : ""}`,
|
|
373
|
+
evidence: {
|
|
374
|
+
installedSource: "package asset",
|
|
375
|
+
userPath: userModelMapPath(options.env),
|
|
376
|
+
userStatus: "invalid",
|
|
377
|
+
},
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Check model selections owned by configured engines against the common model
|
|
383
|
+
* map. Unknown identifiers are deliberate exact-model pass-throughs; only an
|
|
384
|
+
* alias known somewhere in the map but absent for the selected engine warns.
|
|
385
|
+
*/
|
|
386
|
+
export function runSelectedModelAliasesProbe(deps = {}) {
|
|
387
|
+
const config = deps.loadConfig?.() ?? loadConfig();
|
|
388
|
+
const definitions = executionEngineDefinitionsFromConfig(config);
|
|
389
|
+
const selected = Object.entries(definitions)
|
|
390
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
391
|
+
.flatMap(([engine, definition]) => {
|
|
392
|
+
const model = definition.defaults?.model;
|
|
393
|
+
const modelMapKey = definition.modelMapKey ?? definition.selection.platform ?? definition.selection.name;
|
|
394
|
+
return typeof model === "string" ? [{ engine, alias: model, modelMapKey }] : [];
|
|
395
|
+
});
|
|
396
|
+
if (selected.length === 0) {
|
|
397
|
+
return {
|
|
398
|
+
name: "selected-model-aliases",
|
|
399
|
+
kind: "deterministic",
|
|
400
|
+
status: "unknown",
|
|
401
|
+
confidence: "high",
|
|
402
|
+
message: "No configured engines select a model.",
|
|
403
|
+
evidence: { checked: [], missing: [] },
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
let modelMap;
|
|
407
|
+
try {
|
|
408
|
+
modelMap = (deps.loadModelMap ?? loadModelMap)({ env: deps.env, installedText: deps.installedText });
|
|
409
|
+
}
|
|
410
|
+
catch {
|
|
411
|
+
return {
|
|
412
|
+
name: "selected-model-aliases",
|
|
413
|
+
kind: "deterministic",
|
|
414
|
+
status: "unknown",
|
|
415
|
+
confidence: "high",
|
|
416
|
+
message: "Configured model selections could not be checked because the model map is invalid.",
|
|
417
|
+
evidence: { checked: [], missing: [] },
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
const outcomes = selected.map(({ engine, alias, modelMapKey }) => {
|
|
421
|
+
try {
|
|
422
|
+
const resolution = resolveModelMapAlias(alias, modelMapKey, modelMap.map);
|
|
423
|
+
return resolution.interpretation === "alias"
|
|
424
|
+
? { kind: "alias", evidence: { engine, alias, modelMapKey } }
|
|
425
|
+
: { kind: "exact" };
|
|
426
|
+
}
|
|
427
|
+
catch {
|
|
428
|
+
return { kind: "missing", evidence: { engine, alias, modelMapKey } };
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
const checked = outcomes.flatMap((outcome) => (outcome.kind === "exact" ? [] : [outcome.evidence]));
|
|
432
|
+
const missing = outcomes.flatMap((outcome) => (outcome.kind === "missing" ? [outcome.evidence] : []));
|
|
433
|
+
return {
|
|
434
|
+
name: "selected-model-aliases",
|
|
435
|
+
kind: "deterministic",
|
|
436
|
+
status: missing.length === 0 ? "pass" : "warn",
|
|
437
|
+
confidence: "high",
|
|
438
|
+
message: missing.length === 0
|
|
439
|
+
? `All ${selected.length} configured model selections resolve for their selected engines.`
|
|
440
|
+
: `${missing.length} of ${selected.length} configured model selections ${missing.length === 1 ? "has" : "have"} no mapping for ${missing.length === 1 ? "its" : "their"} selected engine${missing.length === 1 ? "" : "s"}.`,
|
|
441
|
+
evidence: { checked, missing },
|
|
442
|
+
};
|
|
443
|
+
}
|
|
229
444
|
/**
|
|
230
445
|
* The ordered health-check registry. ORDER IS LOAD-BEARING: `akmHealth`
|
|
231
446
|
* iterates this array and appends to hardChecks/advisories in sequence, so the
|
|
@@ -312,12 +527,27 @@ export const HEALTH_CHECKS = [
|
|
|
312
527
|
{
|
|
313
528
|
name: "default-engine",
|
|
314
529
|
channel: "hard",
|
|
315
|
-
run: () =>
|
|
530
|
+
run: (ctx) => ctx.engineProbes.defaultEngine,
|
|
531
|
+
},
|
|
532
|
+
{
|
|
533
|
+
name: "model-map-files",
|
|
534
|
+
channel: "hard",
|
|
535
|
+
run: () => runModelMapProbe(),
|
|
536
|
+
},
|
|
537
|
+
{
|
|
538
|
+
name: "selected-model-aliases",
|
|
539
|
+
channel: "hard",
|
|
540
|
+
run: () => runSelectedModelAliasesProbe(),
|
|
316
541
|
},
|
|
317
542
|
{
|
|
318
543
|
name: "default-llm-engine",
|
|
319
544
|
channel: "hard",
|
|
320
|
-
run: () =>
|
|
545
|
+
run: (ctx) => ctx.engineProbes.defaultLlmEngine,
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
name: "configured-engines",
|
|
549
|
+
channel: "hard",
|
|
550
|
+
run: (ctx) => ctx.engineProbes.configuredEngines,
|
|
321
551
|
},
|
|
322
552
|
{
|
|
323
553
|
name: "active-improve-strategy",
|
|
@@ -409,24 +639,6 @@ export const HEALTH_CHECKS = [
|
|
|
409
639
|
};
|
|
410
640
|
},
|
|
411
641
|
},
|
|
412
|
-
{
|
|
413
|
-
// session-log-failures: demoted to informational — the ERROR_PATTERNS regex
|
|
414
|
-
// scans pre-LLM session text and produces false positives on diagnostic
|
|
415
|
-
// conversation. It does not gate the real extraction pipeline (akmExtract).
|
|
416
|
-
// Never triggers warn; kept for backward-compat visibility only.
|
|
417
|
-
name: "session-log-failures",
|
|
418
|
-
channel: "advisory",
|
|
419
|
-
run: (ctx) => ({
|
|
420
|
-
name: "session-log-failures",
|
|
421
|
-
kind: "heuristic",
|
|
422
|
-
status: "pass",
|
|
423
|
-
confidence: "low",
|
|
424
|
-
message: ctx.sessionLogEntries.length === 0
|
|
425
|
-
? "No repeated external session-log failure patterns were detected."
|
|
426
|
-
: `${ctx.sessionLogEntries.length} raw session-log keyword match(es) detected (pre-LLM, informational only).`,
|
|
427
|
-
evidence: { candidates: ctx.sessionLogEntries.slice(0, 5) },
|
|
428
|
-
}),
|
|
429
|
-
},
|
|
430
642
|
{
|
|
431
643
|
name: "session-extraction",
|
|
432
644
|
channel: "advisory",
|
|
@@ -369,20 +369,6 @@ function renderActionItems(vm) {
|
|
|
369
369
|
remedy: "akm config list",
|
|
370
370
|
});
|
|
371
371
|
}
|
|
372
|
-
// Session-log notes (informational, lowest priority).
|
|
373
|
-
if (vm.sessionLogAdvisories.length > 0) {
|
|
374
|
-
const patterns = vm.sessionLogAdvisories
|
|
375
|
-
.slice(0, 6)
|
|
376
|
-
.map((p) => `<li>${esc(p.topic)}</li>`)
|
|
377
|
-
.join("");
|
|
378
|
-
pushItem({
|
|
379
|
-
key: "session-log-notes",
|
|
380
|
-
prio: "P3",
|
|
381
|
-
cls: "warn",
|
|
382
|
-
title: `${vm.sessionLogAdvisories.length} session-log note(s) (informational)`,
|
|
383
|
-
descHtml: `<ul style="margin:4px 0 0 16px;padding:0;">${patterns}</ul>`,
|
|
384
|
-
});
|
|
385
|
-
}
|
|
386
372
|
items.sort((a, b) => PRIO_RANK[a.prio] - PRIO_RANK[b.prio]);
|
|
387
373
|
return items.length > 0
|
|
388
374
|
? items.map(actionItemCard).join("\n")
|
|
@@ -17,14 +17,13 @@
|
|
|
17
17
|
import { MAX_CONFIG_FILE_BYTES, readTextFileWithLimit } from "../../core/common.js";
|
|
18
18
|
import { CURRENT_CONFIG_VERSION } from "../../core/config/config-schema.js";
|
|
19
19
|
import { compareConfigVersion } from "../../core/config/config-version.js";
|
|
20
|
+
import { formatRegistryUrl } from "../../core/registry-url.js";
|
|
20
21
|
/**
|
|
21
22
|
* `binary-config-skew` (08-F3): warn when config.json carries a configVersion
|
|
22
23
|
* NEWER than (or unorderable against) this binary's CURRENT_CONFIG_VERSION —
|
|
23
24
|
* i.e. a newer/foreign akm wrote the shared config and this install is stale.
|
|
24
|
-
* That is
|
|
25
|
-
*
|
|
26
|
-
* same/older versions (auto-migration handles those) and unreadable configs
|
|
27
|
-
* (config loading surfaces its own errors).
|
|
25
|
+
* That is the proven multi-install incident class. Silent for current or older
|
|
26
|
+
* versions and unreadable configs (config loading surfaces its own errors).
|
|
28
27
|
*/
|
|
29
28
|
export function collectConfigSkewAdvisory(configPath) {
|
|
30
29
|
let raw;
|
|
@@ -45,8 +44,8 @@ export function collectConfigSkewAdvisory(configPath) {
|
|
|
45
44
|
status: "warn",
|
|
46
45
|
confidence: "high",
|
|
47
46
|
message: `config.json has configVersion ${JSON.stringify(onDisk)} but this binary knows ${CURRENT_CONFIG_VERSION} — ` +
|
|
48
|
-
"a newer akm wrote the shared config, so this install is stale
|
|
49
|
-
"
|
|
47
|
+
"a newer akm wrote the shared config, so this install is stale. Upgrade this install; do not keep a stale " +
|
|
48
|
+
"binary against the shared config/DBs.",
|
|
50
49
|
evidence: { onDiskConfigVersion: onDisk, binaryConfigVersion: CURRENT_CONFIG_VERSION },
|
|
51
50
|
};
|
|
52
51
|
}
|
|
@@ -64,7 +63,7 @@ export function collectEgressAdvisory(config) {
|
|
|
64
63
|
for (const reg of config.registries ?? []) {
|
|
65
64
|
if (reg.enabled === false || !reg.url)
|
|
66
65
|
continue;
|
|
67
|
-
endpoints.push(`registry ${reg.name ?? "(unnamed)"}: ${reg.url}`);
|
|
66
|
+
endpoints.push(`registry ${reg.name ?? "(unnamed)"}: ${formatRegistryUrl(reg.url)}`);
|
|
68
67
|
}
|
|
69
68
|
for (const [key, bundle] of Object.entries(config.bundles ?? {})) {
|
|
70
69
|
if (!bundle)
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
* - `./types-improve` — the {@link ImproveHealthMetrics} accumulator and its
|
|
17
17
|
* WS-5 sub-rollups (coverage, perf telemetry, degradation, enrichment
|
|
18
18
|
* minting).
|
|
19
|
-
* - `./types-session-log` — {@link SessionLogAdvisory}.
|
|
20
19
|
* - `./types-runs` — {@link ImproveRunSummary} (`--group-by run`).
|
|
21
20
|
* - `./types-windows` — {@link WindowSpec}/{@link WindowResult}/{@link DeltaEntry}
|
|
22
21
|
* (`--window-compare`/`--windows`).
|
|
@@ -27,5 +26,4 @@ export * from "./types-improve.js";
|
|
|
27
26
|
export * from "./types-metrics.js";
|
|
28
27
|
export * from "./types-result.js";
|
|
29
28
|
export * from "./types-runs.js";
|
|
30
|
-
export * from "./types-session-log.js";
|
|
31
29
|
export * from "./types-windows.js";
|
package/dist/commands/health.js
CHANGED
|
@@ -10,14 +10,14 @@ import { readEvents } from "../core/events.js";
|
|
|
10
10
|
import { listTxnJournalsTolerant, TXN_SWEEP_GRACE_MS } from "../core/fs-txn.js";
|
|
11
11
|
import { openLogsDatabase } from "../core/logs-db.js";
|
|
12
12
|
import { classifyPathAccess, describeInaccessiblePath } from "../core/path-access.js";
|
|
13
|
-
import { getConfigPath, getDataDir, getStateDbPathInDataDir } from "../core/paths.js";
|
|
13
|
+
import { getConfigPath, getDataDir, getDbPath, getStateDbPathInDataDir } from "../core/paths.js";
|
|
14
14
|
import { listExistingTableNames, openStateDatabase } from "../core/state-db.js";
|
|
15
15
|
import { DURATION_UNITS, parseDuration, parseSinceToIso } from "../core/time.js";
|
|
16
16
|
import { readSemanticStatus } from "../indexer/search/semantic-status.js";
|
|
17
|
-
import {
|
|
17
|
+
import { closeDatabase, openReadonlyExistingDatabase } from "../storage/repositories/index-connection.js";
|
|
18
18
|
import { queryTaskHistory } from "../storage/repositories/task-history-repository.js";
|
|
19
19
|
import { collectImproveAdvisories } from "./health/advisories.js";
|
|
20
|
-
import { HEALTH_CHECKS } from "./health/checks.js";
|
|
20
|
+
import { HEALTH_CHECKS, runHealthEngineProbes } from "./health/checks.js";
|
|
21
21
|
import { buildImproveSkipSummary, computeWallTimeStats, parseTaskMetadata, roundRate, summarizeImproveCompleted, summarizeImproveRuns, } from "./health/improve-metrics.js";
|
|
22
22
|
import { emptyLlmUsageAggregate, readLlmUsageAggregate } from "./health/llm-usage.js";
|
|
23
23
|
import { computeDegradationMetrics, computeDenominatorFixedCoverage, computeEnrichmentMintingRollup, probeStateDbRoundTrip, } from "./health/metrics.js";
|
|
@@ -235,6 +235,9 @@ function gatherImproveSummaryPhase(db, stateDbPath, since, now) {
|
|
|
235
235
|
*/
|
|
236
236
|
function gatherAncillaryAdvisories(db, stateDbPath, since, improveSummary, options, egressConfigView) {
|
|
237
237
|
const advisories = [...collectImproveAdvisories(db, stateDbPath, since, improveSummary)];
|
|
238
|
+
const indexStateMismatch = detectIndexStateGenerationMismatch(db);
|
|
239
|
+
if (indexStateMismatch)
|
|
240
|
+
advisories.push(indexStateMismatch);
|
|
238
241
|
// 08-F1: surface a `stash-git-exposure` advisory when env/secret assets are
|
|
239
242
|
// git-tracked AND a remote is configured (the leak moment). Best-effort.
|
|
240
243
|
// Cheap guard: only shell out to git when the stash has its OWN `.git` (or a
|
|
@@ -268,19 +271,64 @@ function gatherAncillaryAdvisories(db, stateDbPath, since, improveSummary, optio
|
|
|
268
271
|
}
|
|
269
272
|
return advisories;
|
|
270
273
|
}
|
|
271
|
-
/**
|
|
272
|
-
|
|
274
|
+
/**
|
|
275
|
+
* Detect the durable signature of an interrupted cross-database update.
|
|
276
|
+
*
|
|
277
|
+
* `usage_events.entry_ref` is the stable identity while `entry_id` names the
|
|
278
|
+
* current, regenerable index row. A linked event whose id is absent or resolves
|
|
279
|
+
* to a different ref means index.db and state.db describe adjacent generations.
|
|
280
|
+
* Legacy/bare refs and deliberately detached rows are excluded. The scan is
|
|
281
|
+
* streaming and keeps only a bounded evidence sample so health cannot mirror
|
|
282
|
+
* either database into the JS heap.
|
|
283
|
+
*
|
|
284
|
+
* Best-effort by design: an absent/unreadable/incompatible index has its own
|
|
285
|
+
* diagnostics and must not make the state health path throw.
|
|
286
|
+
*/
|
|
287
|
+
function detectIndexStateGenerationMismatch(stateDb) {
|
|
288
|
+
let indexDb;
|
|
273
289
|
try {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
290
|
+
indexDb = openReadonlyExistingDatabase(getDbPath());
|
|
291
|
+
if (!indexDb)
|
|
292
|
+
return undefined;
|
|
293
|
+
const byId = indexDb.prepare("SELECT item_ref FROM entries WHERE id = ?");
|
|
294
|
+
const rows = stateDb
|
|
295
|
+
.prepare("SELECT DISTINCT entry_id, entry_ref FROM usage_events " +
|
|
296
|
+
"WHERE entry_id IS NOT NULL AND entry_ref IS NOT NULL AND instr(entry_ref, '//') > 0")
|
|
297
|
+
.iterate();
|
|
298
|
+
let mismatches = 0;
|
|
299
|
+
const sample = [];
|
|
300
|
+
for (const row of rows) {
|
|
301
|
+
const indexedRef = byId.get(row.entry_id)?.item_ref ?? null;
|
|
302
|
+
if (indexedRef === row.entry_ref)
|
|
303
|
+
continue;
|
|
304
|
+
mismatches += 1;
|
|
305
|
+
if (sample.length < 5)
|
|
306
|
+
sample.push({ entryId: row.entry_id, entryRef: row.entry_ref, indexedRef });
|
|
307
|
+
}
|
|
308
|
+
if (mismatches === 0)
|
|
309
|
+
return undefined;
|
|
310
|
+
return {
|
|
311
|
+
name: "index-state-generation",
|
|
312
|
+
kind: "deterministic",
|
|
313
|
+
status: "warn",
|
|
314
|
+
confidence: "high",
|
|
315
|
+
message: `${mismatches} durable usage link(s) disagree with the current searchable index generation. ` +
|
|
316
|
+
"Stop concurrent writers and run 'akm index --full' to relink state to the current index.",
|
|
317
|
+
evidence: { mismatches, sample },
|
|
318
|
+
};
|
|
281
319
|
}
|
|
282
320
|
catch {
|
|
283
|
-
return
|
|
321
|
+
return undefined;
|
|
322
|
+
}
|
|
323
|
+
finally {
|
|
324
|
+
if (indexDb) {
|
|
325
|
+
try {
|
|
326
|
+
closeDatabase(indexDb);
|
|
327
|
+
}
|
|
328
|
+
catch {
|
|
329
|
+
// Best-effort advisory: a close failure must not abort health.
|
|
330
|
+
}
|
|
331
|
+
}
|
|
284
332
|
}
|
|
285
333
|
}
|
|
286
334
|
/**
|
|
@@ -370,7 +418,6 @@ function unreadableStateDbReport(detail, options) {
|
|
|
370
418
|
llmUsage: emptyLlmUsageAggregate(),
|
|
371
419
|
},
|
|
372
420
|
improve: summarizeImproveCompleted([]),
|
|
373
|
-
sessionLogAdvisories: [],
|
|
374
421
|
};
|
|
375
422
|
}
|
|
376
423
|
export function akmHealth(options = {}) {
|
|
@@ -380,7 +427,6 @@ export function akmHealth(options = {}) {
|
|
|
380
427
|
const stateDbPath = options.stateDbPath ?? getStateDbPathInDataDir();
|
|
381
428
|
const hardChecks = [];
|
|
382
429
|
const advisories = [];
|
|
383
|
-
const getExecutionLogCandidatesFn = options.getExecutionLogCandidatesFn ?? getExecutionLogCandidates;
|
|
384
430
|
// #791: an UNREADABLE state.db is the one failure `akm health` most needs to
|
|
385
431
|
// be able to report, because it is the command an operator runs to find out
|
|
386
432
|
// why everything else is behaving oddly. Dying here with exit 78 meant health
|
|
@@ -414,7 +460,7 @@ export function akmHealth(options = {}) {
|
|
|
414
460
|
const { semanticStatus, semanticSearchMode, embeddingEndpoint, egressConfigView } = gatherSemanticConfigPhase();
|
|
415
461
|
const { improveSummary } = gatherImproveSummaryPhase(db, stateDbPath, since, now);
|
|
416
462
|
advisories.push(...gatherAncillaryAdvisories(db, stateDbPath, since, improveSummary, options, egressConfigView));
|
|
417
|
-
const
|
|
463
|
+
const engineProbes = runHealthEngineProbes();
|
|
418
464
|
// Run the ordered health-check registry. Each check projects the shared
|
|
419
465
|
// context computed above into one HealthCheckResult; `channel` routes it to
|
|
420
466
|
// hardChecks or advisories. Declaration order in HEALTH_CHECKS is the
|
|
@@ -437,9 +483,9 @@ export function akmHealth(options = {}) {
|
|
|
437
483
|
semanticStatus,
|
|
438
484
|
semanticSearchMode,
|
|
439
485
|
embeddingEndpoint,
|
|
440
|
-
sessionLogEntries,
|
|
441
486
|
sessionExtraction: improveSummary.sessionExtraction,
|
|
442
487
|
autoAccept: improveSummary.autoAccept,
|
|
488
|
+
engineProbes,
|
|
443
489
|
};
|
|
444
490
|
for (const check of HEALTH_CHECKS) {
|
|
445
491
|
const result = check.run(checkContext);
|
|
@@ -475,7 +521,6 @@ export function akmHealth(options = {}) {
|
|
|
475
521
|
advisories,
|
|
476
522
|
metrics,
|
|
477
523
|
improve: improveSummary,
|
|
478
|
-
sessionLogAdvisories: sessionLogEntries,
|
|
479
524
|
...(runs ? { runs } : {}),
|
|
480
525
|
...(windowResults ? { windows: windowResults } : {}),
|
|
481
526
|
...(deltas ? { deltas } : {}),
|
|
@@ -163,10 +163,9 @@ export function refreshCanarySet(stateDb, indexDb, cfg) {
|
|
|
163
163
|
* Name-free content fingerprint text for entropy metrics. The indexed
|
|
164
164
|
* search_text EMBEDS the (unique) entry name, which would pin the
|
|
165
165
|
* distinct-content ratio at 1.0 forever; convergence shows up in the
|
|
166
|
-
* description/tags/heading fields, so those are what get hashed.
|
|
167
|
-
* is
|
|
168
|
-
*
|
|
169
|
-
* generic merged assets converge on.)
|
|
166
|
+
* description/tags/heading fields, so those are what get hashed. Bounded body
|
|
167
|
+
* content is searchable but deliberately excluded from this fingerprint: the
|
|
168
|
+
* detector measures the structured surface generic merged assets converge on.
|
|
170
169
|
*/
|
|
171
170
|
function contentFingerprint(entry) {
|
|
172
171
|
const parts = [entry.description ?? "", (entry.tags ?? []).join(" "), (entry.toc ?? []).map((h) => h.text).join(" ")];
|
|
@@ -262,9 +261,9 @@ export function computeCycleMetrics(stateDb, indexDb, args) {
|
|
|
262
261
|
contentHashes.add(normHash(fingerprint));
|
|
263
262
|
if ((e.entry.generation ?? 0) > maxGeneration)
|
|
264
263
|
overGeneration++;
|
|
265
|
-
learningTexts.push({ key: e.
|
|
264
|
+
learningTexts.push({ key: e.itemRef, text: fingerprint });
|
|
266
265
|
}
|
|
267
|
-
// Deterministic diversity sample: sort by
|
|
266
|
+
// Deterministic diversity sample: sort by item ref, take every ⌈N/cap⌉-th row.
|
|
268
267
|
learningTexts.sort((a, b) => (a.key < b.key ? -1 : 1));
|
|
269
268
|
const step = Math.max(1, Math.ceil(learningTexts.length / DIVERSITY_SAMPLE_CAP));
|
|
270
269
|
let diversitySum = 0;
|