akm-cli 0.9.0-rc.1 → 0.9.0-rc.3

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.
Files changed (257) hide show
  1. package/CHANGELOG.md +249 -9
  2. package/SECURITY.md +23 -24
  3. package/dist/assets/help/help-improve.md +10 -10
  4. package/dist/assets/hints/cli-hints-full.md +44 -10
  5. package/dist/assets/hints/cli-hints-short.md +6 -2
  6. package/dist/assets/{profiles → improve-strategies}/catchup.json +3 -1
  7. package/dist/assets/{profiles → improve-strategies}/consolidate.json +3 -1
  8. package/dist/assets/{profiles → improve-strategies}/default.json +1 -0
  9. package/dist/assets/{profiles → improve-strategies}/frequent.json +3 -1
  10. package/dist/assets/{profiles → improve-strategies}/graph-refresh.json +4 -2
  11. package/dist/assets/{profiles → improve-strategies}/memory-focus.json +4 -1
  12. package/dist/assets/{profiles → improve-strategies}/proactive-maintenance.json +3 -3
  13. package/dist/assets/{profiles → improve-strategies}/quick.json +3 -1
  14. package/dist/assets/{profiles → improve-strategies}/recombine-only.json +2 -0
  15. package/dist/assets/{profiles → improve-strategies}/reflect-distill.json +4 -4
  16. package/dist/assets/{profiles → improve-strategies}/synthesize.json +2 -0
  17. package/dist/assets/stash-skeleton/README.md +28 -0
  18. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +6 -0
  19. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +6 -0
  20. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +12 -1
  21. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +11 -1
  22. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +6 -0
  23. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +9 -0
  24. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +7 -0
  25. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +7 -0
  26. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +6 -0
  27. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +98 -0
  28. package/dist/assets/stash-skeleton/facts/conventions/domains.md +63 -0
  29. package/dist/assets/stash-skeleton/facts/conventions/organization.md +127 -0
  30. package/dist/assets/tasks/core/backup.yml +3 -2
  31. package/dist/assets/tasks/core/extract.yml +1 -0
  32. package/dist/assets/tasks/core/improve.yml +1 -0
  33. package/dist/assets/tasks/core/index-refresh.yml +1 -0
  34. package/dist/assets/tasks/core/sync.yml +1 -0
  35. package/dist/assets/tasks/core/version-check.yml +1 -0
  36. package/dist/assets/tasks/graph-refresh-weekly.yml +4 -4
  37. package/dist/assets/templates/html/health.html +5 -1
  38. package/dist/cli/config-migrate.js +547 -126
  39. package/dist/cli/config-validate.js +10 -8
  40. package/dist/cli.js +50 -14
  41. package/dist/commands/agent/agent-dispatch.js +17 -16
  42. package/dist/commands/agent/agent-support.js +0 -24
  43. package/dist/commands/agent/contribute-cli.js +5 -15
  44. package/dist/commands/backup-cli.js +56 -0
  45. package/dist/commands/config-cli.js +48 -155
  46. package/dist/commands/env/secret.js +8 -5
  47. package/dist/commands/feedback-cli.js +24 -7
  48. package/dist/commands/health/checks.js +200 -74
  49. package/dist/commands/health/html-report.js +4 -0
  50. package/dist/commands/health/improve-metrics.js +30 -32
  51. package/dist/commands/health/llm-usage.js +19 -19
  52. package/dist/commands/health/md-report.js +4 -0
  53. package/dist/commands/health/metrics.js +2 -1
  54. package/dist/commands/health/surfaces.js +7 -5
  55. package/dist/commands/health.js +1 -1
  56. package/dist/commands/improve/anti-collapse.js +3 -3
  57. package/dist/commands/improve/collapse-detector.js +5 -5
  58. package/dist/commands/improve/consolidate/chunking.js +2 -2
  59. package/dist/commands/improve/consolidate.js +148 -88
  60. package/dist/commands/improve/distill/promote-memory.js +13 -18
  61. package/dist/commands/improve/distill/quality-gate.js +6 -8
  62. package/dist/commands/improve/distill-guards.js +1 -1
  63. package/dist/commands/improve/distill-promotion-policy.js +32 -26
  64. package/dist/commands/improve/distill.js +68 -41
  65. package/dist/commands/improve/eligibility.js +31 -20
  66. package/dist/commands/improve/extract-cli.js +47 -19
  67. package/dist/commands/improve/extract-prompt.js +2 -2
  68. package/dist/commands/improve/extract.js +153 -73
  69. package/dist/commands/improve/improve-auto-accept.js +14 -28
  70. package/dist/commands/improve/improve-cli.js +41 -17
  71. package/dist/commands/improve/improve-result-file.js +30 -24
  72. package/dist/commands/improve/improve-strategies.js +138 -0
  73. package/dist/commands/improve/improve.js +109 -38
  74. package/dist/commands/improve/locks.js +66 -45
  75. package/dist/commands/improve/loop-stages.js +86 -39
  76. package/dist/commands/improve/memory/memory-belief.js +79 -7
  77. package/dist/commands/improve/memory/memory-contradiction-detect.js +12 -4
  78. package/dist/commands/improve/preparation.js +122 -90
  79. package/dist/commands/improve/procedural.js +13 -8
  80. package/dist/commands/improve/recombine.js +33 -8
  81. package/dist/commands/improve/reflect.js +143 -228
  82. package/dist/commands/improve/salience.js +5 -1
  83. package/dist/commands/improve/shared.js +11 -5
  84. package/dist/commands/improve/source-identity.js +43 -0
  85. package/dist/commands/lint/base-linter.js +152 -42
  86. package/dist/commands/migrate-cli.js +36 -0
  87. package/dist/commands/mv-cli.js +1198 -0
  88. package/dist/commands/proposal/drain.js +16 -4
  89. package/dist/commands/proposal/proposal-cli.js +21 -10
  90. package/dist/commands/proposal/proposal.js +16 -55
  91. package/dist/commands/proposal/propose.js +64 -69
  92. package/dist/commands/proposal/repository.js +664 -58
  93. package/dist/commands/read/curate.js +4 -2
  94. package/dist/commands/read/knowledge.js +440 -5
  95. package/dist/commands/read/remember-cli.js +39 -2
  96. package/dist/commands/read/search-cli.js +6 -1
  97. package/dist/commands/read/search.js +6 -2
  98. package/dist/commands/read/show.js +8 -7
  99. package/dist/commands/registry-cli.js +29 -14
  100. package/dist/commands/remember.js +2 -0
  101. package/dist/commands/sources/init.js +13 -14
  102. package/dist/commands/sources/schema-repair.js +2 -4
  103. package/dist/commands/sources/self-update.js +156 -112
  104. package/dist/commands/sources/source-add.js +62 -73
  105. package/dist/commands/sources/source-manage.js +50 -46
  106. package/dist/commands/sources/sources-cli.js +7 -2
  107. package/dist/commands/sources/stash-cli.js +41 -4
  108. package/dist/commands/tasks/default-tasks.js +12 -12
  109. package/dist/commands/tasks/tasks-cli.js +7 -3
  110. package/dist/commands/tasks/tasks.js +113 -18
  111. package/dist/commands/wiki-cli.js +9 -10
  112. package/dist/core/asset/frontmatter.js +12 -2
  113. package/dist/core/common.js +41 -6
  114. package/dist/core/config/config-io.js +30 -19
  115. package/dist/core/config/config-schema.js +373 -69
  116. package/dist/core/config/config-types.js +3 -3
  117. package/dist/core/config/config-version.js +29 -0
  118. package/dist/core/config/config-walker.js +98 -27
  119. package/dist/core/config/config.js +135 -268
  120. package/dist/core/config/deep-merge.js +41 -0
  121. package/dist/core/config/engine-semantics.js +32 -0
  122. package/dist/core/errors.js +2 -2
  123. package/dist/core/extra-params.js +61 -0
  124. package/dist/core/file-lock.js +202 -57
  125. package/dist/core/improve-result.js +178 -0
  126. package/dist/core/maintenance-barrier.js +119 -0
  127. package/dist/core/migration-backup.js +1061 -0
  128. package/dist/core/migration-operation.js +44 -0
  129. package/dist/core/paths.js +3 -0
  130. package/dist/core/redaction.js +358 -0
  131. package/dist/core/state/migrations.js +15 -3
  132. package/dist/core/state-db.js +60 -2
  133. package/dist/core/write-source.js +86 -18
  134. package/dist/indexer/db/db.js +212 -41
  135. package/dist/indexer/graph/graph-extraction.js +28 -16
  136. package/dist/indexer/index-writer-lock.js +59 -51
  137. package/dist/indexer/index-written-assets.js +75 -49
  138. package/dist/indexer/indexer.js +52 -3
  139. package/dist/indexer/passes/memory-inference.js +10 -6
  140. package/dist/indexer/passes/metadata.js +250 -0
  141. package/dist/indexer/search/db-search.js +111 -44
  142. package/dist/indexer/search/fts-query.js +41 -0
  143. package/dist/indexer/search/ranking-contributors.js +48 -0
  144. package/dist/indexer/search/ranking.js +36 -23
  145. package/dist/indexer/search/search-fields.js +11 -1
  146. package/dist/indexer/search/search-source.js +2 -2
  147. package/dist/indexer/usage/usage-events.js +8 -2
  148. package/dist/integrations/agent/builder-shared.js +7 -0
  149. package/dist/integrations/agent/builders.js +5 -52
  150. package/dist/integrations/agent/config.js +3 -143
  151. package/dist/integrations/agent/detect.js +17 -2
  152. package/dist/integrations/agent/engine-resolution.js +209 -0
  153. package/dist/integrations/agent/index.js +1 -2
  154. package/dist/integrations/agent/model-aliases.js +7 -2
  155. package/dist/integrations/agent/profiles.js +6 -99
  156. package/dist/integrations/agent/runner-dispatch.js +76 -13
  157. package/dist/integrations/agent/runner.js +76 -207
  158. package/dist/integrations/agent/spawn.js +4 -6
  159. package/dist/integrations/harnesses/aider/agent-builder.js +2 -3
  160. package/dist/integrations/harnesses/aider/index.js +0 -1
  161. package/dist/integrations/harnesses/amazonq/agent-builder.js +2 -3
  162. package/dist/integrations/harnesses/amazonq/index.js +0 -1
  163. package/dist/integrations/harnesses/claude/agent-builder.js +2 -3
  164. package/dist/integrations/harnesses/claude/index.js +0 -2
  165. package/dist/integrations/harnesses/codex/agent-builder.js +2 -3
  166. package/dist/integrations/harnesses/codex/index.js +0 -1
  167. package/dist/integrations/harnesses/copilot/agent-builder.js +2 -3
  168. package/dist/integrations/harnesses/copilot/index.js +0 -1
  169. package/dist/integrations/harnesses/gemini/agent-builder.js +2 -3
  170. package/dist/integrations/harnesses/gemini/index.js +0 -1
  171. package/dist/integrations/harnesses/index.js +1 -24
  172. package/dist/integrations/harnesses/opencode/agent-builder.js +2 -3
  173. package/dist/integrations/harnesses/opencode/index.js +0 -6
  174. package/dist/integrations/harnesses/opencode-sdk/harness.js +1 -6
  175. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +274 -191
  176. package/dist/integrations/harnesses/openhands/agent-builder.js +2 -3
  177. package/dist/integrations/harnesses/openhands/index.js +0 -1
  178. package/dist/integrations/harnesses/pi/agent-builder.js +2 -3
  179. package/dist/integrations/harnesses/pi/index.js +0 -1
  180. package/dist/integrations/harnesses/types.js +1 -32
  181. package/dist/integrations/lockfile.js +32 -21
  182. package/dist/llm/client.js +48 -14
  183. package/dist/llm/feature-gate.js +15 -47
  184. package/dist/llm/graph-extract.js +1 -1
  185. package/dist/llm/index-passes.js +8 -42
  186. package/dist/llm/memory-infer-impl.js +1 -1
  187. package/dist/llm/usage-persist.js +4 -0
  188. package/dist/llm/usage-telemetry.js +35 -5
  189. package/dist/output/shapes/helpers.js +2 -1
  190. package/dist/output/shapes/passthrough.js +2 -0
  191. package/dist/output/text/helpers.js +3 -1
  192. package/dist/schemas/akm-config.json +11013 -8600
  193. package/dist/schemas/akm-task.json +87 -0
  194. package/dist/schemas/akm-workflow.json +57 -13
  195. package/dist/scripts/migrate-storage.js +8502 -543
  196. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +8493 -515
  197. package/dist/setup/detected-engines.js +136 -0
  198. package/dist/setup/engine-config.js +99 -0
  199. package/dist/setup/setup.js +237 -133
  200. package/dist/setup/steps/connection.js +62 -33
  201. package/dist/setup/steps/platforms.js +4 -4
  202. package/dist/setup/steps/tasks.js +1 -1
  203. package/dist/setup/steps.js +3 -2
  204. package/dist/sources/providers/git-stash.js +58 -21
  205. package/dist/sources/providers/git.js +1 -1
  206. package/dist/storage/database.js +13 -1
  207. package/dist/storage/engines/sqlite-migrations.js +139 -3
  208. package/dist/storage/repositories/events-repository.js +24 -0
  209. package/dist/storage/repositories/improve-runs-repository.js +5 -5
  210. package/dist/storage/repositories/proposals-repository.js +14 -0
  211. package/dist/storage/repositories/task-history-repository.js +78 -0
  212. package/dist/storage/repositories/workflow-runs-repository.js +9 -8
  213. package/dist/tasks/embedded.js +2 -0
  214. package/dist/tasks/parser.js +138 -52
  215. package/dist/tasks/runner.js +71 -75
  216. package/dist/tasks/schema.js +1 -1
  217. package/dist/tasks/validator.js +11 -6
  218. package/dist/wiki/wiki.js +9 -8
  219. package/dist/workflows/authoring/workflow-program-template.yaml +3 -3
  220. package/dist/workflows/concurrency-policy.js +15 -0
  221. package/dist/workflows/db.js +78 -15
  222. package/dist/workflows/exec/brief.js +18 -24
  223. package/dist/workflows/exec/frozen-judge.js +47 -0
  224. package/dist/workflows/exec/native-executor.js +124 -65
  225. package/dist/workflows/exec/report.js +93 -33
  226. package/dist/workflows/exec/run-workflow.js +38 -25
  227. package/dist/workflows/exec/scheduler.js +12 -41
  228. package/dist/workflows/exec/step-work.js +91 -35
  229. package/dist/workflows/ir/compile.js +13 -26
  230. package/dist/workflows/ir/freeze.js +243 -0
  231. package/dist/workflows/ir/plan-hash.js +40 -5
  232. package/dist/workflows/ir/schema.js +542 -1
  233. package/dist/workflows/parser.js +7 -0
  234. package/dist/workflows/program/parser.js +132 -23
  235. package/dist/workflows/program/project.js +3 -4
  236. package/dist/workflows/program/schema.js +2 -2
  237. package/dist/workflows/resource-limits.js +20 -0
  238. package/dist/workflows/runtime/plan-classifier.js +187 -0
  239. package/dist/workflows/runtime/runs.js +54 -47
  240. package/dist/workflows/runtime/workflow-asset-loader.js +0 -22
  241. package/dist/workflows/validator.js +49 -2
  242. package/docs/data-and-telemetry.md +2 -2
  243. package/docs/migration/release-notes/0.6.0.md +1 -1
  244. package/docs/migration/release-notes/0.7.0.md +5 -4
  245. package/docs/migration/release-notes/0.9.0.md +24 -7
  246. package/docs/migration/v0.8-to-v0.9.md +506 -0
  247. package/package.json +4 -2
  248. package/schemas/akm-config.json +16638 -0
  249. package/schemas/akm-task.json +87 -0
  250. package/schemas/akm-workflow.json +372 -0
  251. package/dist/commands/improve/improve-profiles.js +0 -168
  252. package/dist/core/config/config-migration.js +0 -602
  253. package/dist/core/deep-merge.js +0 -38
  254. package/dist/llm/call-ai.js +0 -62
  255. package/dist/setup/legacy-config.js +0 -106
  256. package/docs/README.md +0 -104
  257. /package/dist/assets/{profiles → improve-strategies}/thorough.json +0 -0
@@ -2,111 +2,227 @@
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
4
  import { spawnSync } from "node:child_process";
5
+ import { createRequire } from "node:module";
5
6
  import { loadConfig } from "../../core/config/config.js";
6
- import { detectAgentCliProfiles, requireAgentProfile } from "../../integrations/agent/index.js";
7
+ import { resolveEngine } from "../../integrations/agent/engine-resolution.js";
8
+ import { resolveModel } from "../../integrations/agent/model-aliases.js";
9
+ import { resolveImprovePlan } from "../improve/improve-strategies.js";
7
10
  import { TASK_FAIL_RATE_WARN, } from "./types.js";
8
11
  const ACTIVE_RUN_WARN_MS = 15 * 60 * 1000;
9
- /**
10
- * Probe the configured agent profile. Self-contained (reads config + PATH); the
11
- * only check that performs IO at dispatch time, preserving the original inline
12
- * `runAgentProbe()` call site behaviour exactly.
13
- */
14
- export function runAgentProbe() {
15
- const config = loadConfig();
16
- // v2: check profiles.agent first
17
- if (config.profiles?.agent) {
18
- const defaultName = config.defaults?.agent;
19
- const profileCount = Object.keys(config.profiles.agent).length;
20
- if (profileCount === 0) {
21
- return {
22
- name: "agent-profile",
23
- kind: "deterministic",
24
- status: "unknown",
25
- confidence: "high",
26
- message: "No agent profiles configured in profiles.agent.",
27
- };
28
- }
29
- const profileName = defaultName ?? Object.keys(config.profiles.agent)[0];
30
- const profile = config.profiles.agent[profileName];
12
+ function credentialAvailable(credential, env) {
13
+ return !credential?.required || credential.names.some((name) => Boolean(env[name]?.trim()));
14
+ }
15
+ function runConfiguredEngineProbe(checkName, engineName, config, deps) {
16
+ if (!engineName) {
31
17
  return {
32
- name: "agent-profile",
18
+ name: checkName,
33
19
  kind: "deterministic",
34
- status: "pass",
20
+ status: "unknown",
35
21
  confidence: "high",
36
- message: `v2 agent profile "${profileName}" configured (platform: ${profile?.platform ?? "unknown"}).`,
37
- evidence: { profile: profileName, platform: profile?.platform, profileCount },
22
+ message: checkName === "default-llm-engine"
23
+ ? "No default LLM engine is configured."
24
+ : "No default engine is configured.",
38
25
  };
39
26
  }
40
- if (!config.profiles?.agent && !config.defaults?.agent) {
27
+ const env = deps.env ?? process.env;
28
+ const configuredEngine = config.engines?.[engineName];
29
+ if (configuredEngine?.kind === "agent" && configuredEngine.platform === "opencode-sdk") {
30
+ let packageAvailable = false;
31
+ try {
32
+ const resolvePackage = deps.resolvePackage ?? ((name) => createRequire(import.meta.url).resolve(name));
33
+ resolvePackage("@opencode-ai/sdk");
34
+ packageAvailable = true;
35
+ }
36
+ catch {
37
+ packageAvailable = false;
38
+ }
39
+ const binary = configuredEngine.bin ?? "opencode";
40
+ const version = (deps.spawnSync ?? spawnSync)(binary, ["--version"], { encoding: "utf8", timeout: 5_000 });
41
+ const binaryAvailable = (version.status ?? 1) === 0;
42
+ const fallbackEngine = configuredEngine.llmEngine ?? config.defaults?.llmEngine;
43
+ let fallback;
44
+ let fallbackCredential;
45
+ let sdkRunner;
46
+ const resolve = deps.resolveEngine ?? resolveEngine;
47
+ try {
48
+ const resolved = resolve(engineName, config);
49
+ if (resolved.kind === "sdk")
50
+ sdkRunner = resolved;
51
+ }
52
+ catch {
53
+ sdkRunner = undefined;
54
+ }
55
+ if (sdkRunner?.fallbackConnection) {
56
+ fallback = { kind: "llm", connection: sdkRunner.fallbackConnection };
57
+ fallbackCredential = sdkRunner.fallbackCredential;
58
+ }
59
+ else if (fallbackEngine) {
60
+ try {
61
+ const resolved = resolve(fallbackEngine, config);
62
+ if (resolved.kind === "llm") {
63
+ fallback = resolved;
64
+ fallbackCredential = resolved.credential;
65
+ }
66
+ }
67
+ catch {
68
+ fallback = undefined;
69
+ }
70
+ }
71
+ const configuredModel = configuredEngine.model
72
+ ? resolveModel(configuredEngine.model, "opencode-sdk", configuredEngine.modelAliases, config.modelAliases)
73
+ : undefined;
74
+ const effectiveModel = sdkRunner?.profile.model ?? configuredModel ?? fallback?.connection.model;
75
+ const fallbackCredentialAvailable = credentialAvailable(fallbackCredential, env);
76
+ const missing = [
77
+ !packageAvailable ? "@opencode-ai/sdk package" : undefined,
78
+ !binaryAvailable ? `${binary} binary` : undefined,
79
+ fallbackEngine && !fallback ? "configured fallback LLM connection" : undefined,
80
+ !fallbackCredentialAvailable ? "required fallback credential" : undefined,
81
+ ].filter((value) => value !== undefined);
41
82
  return {
42
- name: "agent-profile",
83
+ name: checkName,
43
84
  kind: "deterministic",
44
- status: "unknown",
85
+ status: missing.length === 0 ? "pass" : "warn",
45
86
  confidence: "high",
46
- message: "No agent config present.",
87
+ message: missing.length === 0
88
+ ? `SDK engine "${engineName}" is available.`
89
+ : `SDK engine "${engineName}" is incomplete: missing ${missing.join(", ")}.`,
90
+ evidence: {
91
+ engine: engineName,
92
+ platform: configuredEngine.platform,
93
+ runtimeKind: "sdk",
94
+ binary,
95
+ binaryAvailable,
96
+ package: "@opencode-ai/sdk",
97
+ packageAvailable,
98
+ model: effectiveModel ?? null,
99
+ configuredModel: configuredModel ?? null,
100
+ modelSource: configuredModel ? "sdk" : effectiveModel ? "fallback" : null,
101
+ fallbackEngine: fallbackEngine ?? null,
102
+ fallbackEndpoint: fallback?.connection.endpoint ?? null,
103
+ fallbackModel: fallback?.connection.model ?? null,
104
+ requiredCredentialAvailable: fallbackCredentialAvailable,
105
+ },
47
106
  };
48
107
  }
49
- let profile;
50
108
  try {
51
- profile = requireAgentProfile(config);
52
- }
53
- catch (error) {
109
+ const runner = (deps.resolveEngine ?? resolveEngine)(engineName, config);
110
+ if (runner.kind === "llm") {
111
+ const requiredCredentialAvailable = credentialAvailable(runner.credential, env);
112
+ return {
113
+ name: checkName,
114
+ kind: "deterministic",
115
+ status: requiredCredentialAvailable ? "pass" : "warn",
116
+ confidence: "high",
117
+ message: requiredCredentialAvailable
118
+ ? `LLM engine "${engineName}" is configured.`
119
+ : `LLM engine "${engineName}" is configured, but its required credential is unavailable.`,
120
+ evidence: {
121
+ engine: engineName,
122
+ platform: null,
123
+ runtimeKind: "llm",
124
+ model: runner.connection.model,
125
+ endpoint: runner.connection.endpoint,
126
+ requiredCredentialAvailable,
127
+ },
128
+ };
129
+ }
130
+ const profile = runner.profile;
131
+ if (runner.kind === "sdk")
132
+ throw new Error(`SDK engine "${engineName}" has no matching SDK config.`);
133
+ const version = (deps.spawnSync ?? spawnSync)(profile.bin, ["--version"], { encoding: "utf8", timeout: 5_000 });
134
+ if ((version.status ?? 1) !== 0) {
135
+ return {
136
+ name: checkName,
137
+ kind: "deterministic",
138
+ status: "warn",
139
+ confidence: "medium",
140
+ message: `Agent engine "${engineName}" was found but \`--version\` failed.`,
141
+ evidence: {
142
+ engine: engineName,
143
+ platform: profile.platform ?? null,
144
+ runtimeKind: "agent",
145
+ model: profile.model ?? null,
146
+ },
147
+ };
148
+ }
54
149
  return {
55
- name: "agent-profile",
150
+ name: checkName,
56
151
  kind: "deterministic",
57
- status: "warn",
152
+ status: "pass",
58
153
  confidence: "high",
59
- message: error instanceof Error ? error.message : String(error),
154
+ message: `Agent engine "${engineName}" is available.`,
155
+ evidence: {
156
+ engine: engineName,
157
+ platform: profile.platform ?? null,
158
+ runtimeKind: "agent",
159
+ model: profile.model ?? null,
160
+ },
60
161
  };
61
162
  }
62
- if (profile.sdkMode === true) {
163
+ catch (error) {
63
164
  return {
64
- name: "agent-profile",
165
+ name: checkName,
65
166
  kind: "deterministic",
66
- status: profile.model ? "pass" : "warn",
167
+ status: "warn",
67
168
  confidence: "high",
68
- message: profile.model
69
- ? `SDK mode profile "${profile.name}" is configured.`
70
- : `SDK mode profile "${profile.name}" has no explicit model.`,
71
- evidence: { profile: profile.name, sdkMode: true, model: profile.model ?? null },
169
+ message: error instanceof Error ? error.message : String(error),
72
170
  };
73
171
  }
74
- const detections = detectAgentCliProfiles(config);
75
- const detection = detections.find((entry) => entry.name === profile.name);
76
- if (!detection?.available) {
172
+ }
173
+ export function runDefaultEngineProbe(deps = {}) {
174
+ const config = deps.loadConfig?.() ?? loadConfig();
175
+ return runConfiguredEngineProbe("default-engine", config.defaults?.engine, config, deps);
176
+ }
177
+ export function runDefaultLlmEngineProbe(deps = {}) {
178
+ const config = deps.loadConfig?.() ?? loadConfig();
179
+ return runConfiguredEngineProbe("default-llm-engine", config.defaults?.llmEngine, config, deps);
180
+ }
181
+ export function runActiveImproveStrategyProbe(deps = {}) {
182
+ const config = deps.loadConfig?.() ?? loadConfig();
183
+ const strategyName = config.defaults?.improveStrategy ?? "default";
184
+ try {
185
+ const plan = resolveImprovePlan(strategyName, config);
186
+ const env = deps.env ?? process.env;
187
+ const unavailableProcesses = Object.entries(plan.processes).flatMap(([name, process]) => {
188
+ if (!process.enabled || !process.runner)
189
+ return [];
190
+ return credentialAvailable(process.runner.credential, env) ? [] : [name];
191
+ });
192
+ if (plan.triageJudgment) {
193
+ const judgmentCredential = plan.triageJudgment.kind === "llm"
194
+ ? plan.triageJudgment.credential
195
+ : plan.triageJudgment.kind === "sdk"
196
+ ? plan.triageJudgment.fallbackCredential
197
+ : undefined;
198
+ if (!credentialAvailable(judgmentCredential, env))
199
+ unavailableProcesses.push("triage.judgment");
200
+ }
77
201
  return {
78
- name: "agent-profile",
202
+ name: "active-improve-strategy",
79
203
  kind: "deterministic",
80
- status: "fail",
204
+ status: unavailableProcesses.length === 0 ? "pass" : "warn",
81
205
  confidence: "high",
82
- message: `Default agent profile "${profile.name}" is not available on PATH.`,
83
- evidence: { profile: profile.name, bin: profile.bin },
206
+ message: unavailableProcesses.length === 0
207
+ ? `Active improve strategy "${plan.strategy.name}" has available process engines.`
208
+ : `Active improve strategy "${plan.strategy.name}" has unavailable required credentials for: ${unavailableProcesses.join(", ")}.`,
209
+ evidence: {
210
+ strategy: plan.strategy.name,
211
+ unavailableProcesses,
212
+ },
84
213
  };
85
214
  }
86
- const version = spawnSync(profile.bin, ["--version"], { encoding: "utf8", timeout: 5_000 });
87
- if ((version.status ?? 1) !== 0) {
215
+ catch (error) {
216
+ const explicitlyConfigured = config.defaults?.improveStrategy !== undefined || Object.keys(config.improve?.strategies ?? {}).length > 0;
88
217
  return {
89
- name: "agent-profile",
218
+ name: "active-improve-strategy",
90
219
  kind: "deterministic",
91
- status: "warn",
92
- confidence: "medium",
93
- message: `Agent binary "${profile.bin}" was found but \`--version\` failed.`,
94
- evidence: {
95
- profile: profile.name,
96
- bin: profile.bin,
97
- exitCode: version.status ?? null,
98
- stderr: (version.stderr ?? "").trim(),
99
- },
220
+ status: explicitlyConfigured ? "warn" : "unknown",
221
+ confidence: "high",
222
+ message: `Active improve strategy "${strategyName}" is unavailable: ${error instanceof Error ? error.message : String(error)}`,
223
+ evidence: { strategy: strategyName, unavailableProcesses: [] },
100
224
  };
101
225
  }
102
- return {
103
- name: "agent-profile",
104
- kind: "deterministic",
105
- status: "pass",
106
- confidence: "high",
107
- message: `Agent profile "${profile.name}" is available.`,
108
- evidence: { profile: profile.name, bin: profile.bin, version: (version.stdout ?? "").trim() },
109
- };
110
226
  }
111
227
  /**
112
228
  * The ordered health-check registry. ORDER IS LOAD-BEARING: `akmHealth`
@@ -185,9 +301,19 @@ export const HEALTH_CHECKS = [
185
301
  }),
186
302
  },
187
303
  {
188
- name: "agent-profile",
304
+ name: "default-engine",
305
+ channel: "hard",
306
+ run: () => runDefaultEngineProbe(),
307
+ },
308
+ {
309
+ name: "default-llm-engine",
310
+ channel: "hard",
311
+ run: () => runDefaultLlmEngineProbe(),
312
+ },
313
+ {
314
+ name: "active-improve-strategy",
189
315
  channel: "hard",
190
- run: () => runAgentProbe(),
316
+ run: () => runActiveImproveStrategyProbe(),
191
317
  },
192
318
  {
193
319
  // C2 (13-bus-factor): the cron task-failure rate was computed and rendered
@@ -93,6 +93,8 @@ function reshapeRun(r) {
93
93
  return {
94
94
  id: r.id,
95
95
  taskId: r.taskId ?? "manual",
96
+ strategy: r.strategy,
97
+ legacyProfile: r.legacyProfile,
96
98
  startedAt: r.startedAt,
97
99
  completedAt: r.completedAt,
98
100
  wallTimeMs: wall,
@@ -295,6 +297,8 @@ export function buildHealthHtmlReplacements(result, opts) {
295
297
  totalTokens: 0,
296
298
  reasoningTokens: 0,
297
299
  byStage: {},
300
+ byProcess: {},
301
+ byEngine: {},
298
302
  };
299
303
  const totalRuns = runs.length;
300
304
  const failedRuns = runs.filter((r) => !r.ok).length;
@@ -1,18 +1,23 @@
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
+ import { decodeImproveResult } from "../../core/improve-result.js";
4
5
  import { queryImproveRuns } from "../../storage/repositories/improve-runs-repository.js";
6
+ import { decodeTaskHistoryMetadata } from "../../storage/repositories/task-history-repository.js";
5
7
  import { summarizeCalibration } from "../improve/calibration.js";
6
8
  export function roundRate(value) {
7
9
  return Number(value.toFixed(4));
8
10
  }
9
11
  export function parseTaskMetadata(row) {
10
- try {
11
- return JSON.parse(row.metadata_json);
12
- }
13
- catch {
14
- return {};
15
- }
12
+ const metadata = decodeTaskHistoryMetadata(row.metadata_json);
13
+ return {
14
+ ...(metadata.durationMs !== undefined ? { durationMs: metadata.durationMs } : {}),
15
+ ...(metadata.detail ? { detail: metadata.detail } : {}),
16
+ ...(metadata.metadataVersion === 2 && metadata.engine !== undefined ? { engine: metadata.engine } : {}),
17
+ ...(metadata.metadataVersion === 1 && metadata.legacyProfile !== undefined
18
+ ? { legacyProfile: metadata.legacyProfile }
19
+ : {}),
20
+ };
16
21
  }
17
22
  function createUnknownImproveMetrics() {
18
23
  return {
@@ -21,7 +26,7 @@ function createUnknownImproveMetrics() {
21
26
  skipped: 0,
22
27
  skipReasons: {},
23
28
  plannedRefs: 0,
24
- profileFilteredRefs: 0,
29
+ strategyFilteredRefs: 0,
25
30
  actions: {
26
31
  reflect: { ok: 0, failed: 0, cooldown: 0, skipped: 0, guardRejected: 0, skippedByReason: {} },
27
32
  distill: {
@@ -181,12 +186,14 @@ function projectRunMetrics(result) {
181
186
  const plannedRefs = result.plannedRefs;
182
187
  if (Array.isArray(plannedRefs))
183
188
  metrics.plannedRefs += plannedRefs.length;
184
- // profileFilteredRefs (array of {ref, reason}) — 2026-05-27: pre-filter
189
+ // strategyFilteredRefs (array of {ref, reason}) — 2026-05-27: pre-filter
185
190
  // bucket from `collectEligibleRefs` so the metric reflects work the
186
191
  // planner dropped before signal-delta / per-pass dispatch.
187
- const profileFilteredRefs = result.profileFilteredRefs;
188
- if (Array.isArray(profileFilteredRefs))
189
- metrics.profileFilteredRefs += profileFilteredRefs.length;
192
+ // Health v3 reports strategy metrics only. Historical v1 profile filtering
193
+ // remains legacy data and must not be silently relabelled as a strategy metric.
194
+ const strategyFilteredRefs = result.schemaVersion === 2 ? result.strategyFilteredRefs : undefined;
195
+ if (Array.isArray(strategyFilteredRefs))
196
+ metrics.strategyFilteredRefs += strategyFilteredRefs.length;
190
197
  // actions: split reflect / distill by outcome, count others.
191
198
  const actions = result.actions;
192
199
  if (Array.isArray(actions)) {
@@ -496,8 +503,8 @@ function finalizeImproveMetrics(metrics) {
496
503
  */
497
504
  function mergeImproveMetrics(dst, src) {
498
505
  dst.plannedRefs += src.plannedRefs;
499
- // profileFilteredRefs is the count of refs the planner drops up-front for the
500
- // active profile — recomputed against the (stable) stash every run, so it is a
506
+ // strategyFilteredRefs is the count of refs the planner drops up-front for the
507
+ // active strategy — recomputed against the (stable) stash every run, so it is a
501
508
  // snapshot, NOT a per-run increment. Summing it re-counts the same refs each
502
509
  // run (the ~2.4M bug). Set from the most recent run in summarizeImproveRuns.
503
510
  dst.actions.reflect.ok += src.actions.reflect.ok;
@@ -613,22 +620,16 @@ export function summarizeImproveRuns(db, since, until) {
613
620
  // MOST RECENT run's snapshot (current state) — not a sum across runs.
614
621
  let latestStartMs = Number.NEGATIVE_INFINITY;
615
622
  let latestMemorySummary;
616
- let latestProfileFilteredRefs = 0;
623
+ let latestStrategyFilteredRefs = 0;
617
624
  for (const row of rows) {
618
- let result;
619
- try {
620
- result = JSON.parse(row.result_json);
621
- }
622
- catch {
623
- continue;
624
- }
625
+ const result = decodeImproveResult(row.result_json).envelope;
625
626
  const perRow = projectRunMetrics(result);
626
627
  mergeImproveMetrics(accum, perRow);
627
628
  const startMs = new Date(row.started_at).getTime();
628
629
  if (Number.isFinite(startMs) && startMs >= latestStartMs) {
629
630
  latestStartMs = startMs;
630
631
  latestMemorySummary = perRow.memorySummary;
631
- latestProfileFilteredRefs = perRow.profileFilteredRefs;
632
+ latestStrategyFilteredRefs = perRow.strategyFilteredRefs;
632
633
  }
633
634
  // Collect per-phase durations directly off the envelope. consolidation's
634
635
  // duration lives inside the sub-object; memoryInference and graphExtraction
@@ -648,7 +649,7 @@ export function summarizeImproveRuns(db, since, until) {
648
649
  finalizeImproveMetrics(accum);
649
650
  if (latestMemorySummary)
650
651
  accum.memorySummary = latestMemorySummary;
651
- accum.profileFilteredRefs = latestProfileFilteredRefs;
652
+ accum.strategyFilteredRefs = latestStrategyFilteredRefs;
652
653
  accum.wallTime.byPhase = {
653
654
  consolidation: summarizePhaseDurations(phaseDurations.consolidation),
654
655
  memoryInference: summarizePhaseDurations(phaseDurations.memoryInference),
@@ -680,13 +681,8 @@ export function summarizePhaseDurations(samples) {
680
681
  * Used by `akm health --detail per-run`.
681
682
  */
682
683
  export function projectImproveRunSummary(row, wallTimeMs, taskId) {
683
- let result = {};
684
- try {
685
- result = JSON.parse(row.result_json);
686
- }
687
- catch {
688
- // fall through with empty result so per-stage rollups are zeros
689
- }
684
+ const decoded = decodeImproveResult(row.result_json);
685
+ const result = decoded.envelope;
690
686
  const perRow = projectRunMetrics(result);
691
687
  finalizeImproveMetrics(perRow);
692
688
  const orphansPurged = toFiniteNumber(result.orphansPurged);
@@ -699,6 +695,8 @@ export function projectImproveRunSummary(row, wallTimeMs, taskId) {
699
695
  completedAt: row.completed_at,
700
696
  wallTimeMs,
701
697
  ok: row.ok === 1,
698
+ strategy: row.strategy,
699
+ legacyProfile: row.legacyProfile,
702
700
  scope: {
703
701
  mode: row.scope_mode,
704
702
  ...(row.scope_value ? { value: row.scope_value } : {}),
@@ -744,10 +742,10 @@ export function buildImproveSkipSummary(events) {
744
742
  // - Per-occurrence (no `count`): one event per skipped ref → SUM is correct.
745
743
  // - Aggregated snapshot (carries `count`): a single per-run event whose count
746
744
  // is the number of refs that hit a STABLE, whole-stash condition that run
747
- // (`no_new_signal`, `profile_filtered_all_passes`). Each run re-counts the
745
+ // (`no_new_signal`, `strategy_filtered_all_passes`). Each run re-counts the
748
746
  // same stable set, so summing across the window re-counts it N times (the
749
747
  // 2.7M / 3M inflation). For these we keep the MOST RECENT run's count — the
750
- // current snapshot — matching how memorySummary/profileFilteredRefs are
748
+ // current snapshot — matching how memorySummary/strategyFilteredRefs are
751
749
  // handled. Events arrive in chronological (offset) order, so the last
752
750
  // count-bearing event per reason is the latest run's value.
753
751
  const summed = {};
@@ -7,7 +7,7 @@
7
7
  */
8
8
  import { readEvents } from "../../core/events.js";
9
9
  import { LLM_USAGE_EVENT } from "../../llm/usage-persist.js";
10
- import { toFiniteNumber } from "./improve-metrics.js";
10
+ import { decodeLlmUsageRecord } from "../../llm/usage-telemetry.js";
11
11
  /** Stage key used for `llm_usage` events recorded outside any stage scope. */
12
12
  const UNATTRIBUTED_STAGE = "unattributed";
13
13
  function emptyLlmUsageStageAggregate() {
@@ -21,7 +21,7 @@ function emptyLlmUsageStageAggregate() {
21
21
  };
22
22
  }
23
23
  function emptyLlmUsageAggregate() {
24
- return { ...emptyLlmUsageStageAggregate(), byStage: {} };
24
+ return { ...emptyLlmUsageStageAggregate(), byStage: {}, byProcess: {}, byEngine: {} };
25
25
  }
26
26
  /**
27
27
  * Aggregate `llm_usage` events (#576) into a window total plus a per-stage
@@ -32,25 +32,25 @@ function emptyLlmUsageAggregate() {
32
32
  export function summarizeLlmUsage(events) {
33
33
  const aggregate = emptyLlmUsageAggregate();
34
34
  for (const event of events) {
35
- const meta = event.metadata ?? {};
36
- const stageKey = typeof meta.stage === "string" && meta.stage ? meta.stage : UNATTRIBUTED_STAGE;
37
- let stage = aggregate.byStage[stageKey];
38
- if (!stage) {
39
- stage = emptyLlmUsageStageAggregate();
40
- aggregate.byStage[stageKey] = stage;
35
+ const record = decodeLlmUsageRecord(event.metadata);
36
+ if (!record)
37
+ continue;
38
+ const dimensions = [];
39
+ for (const [groups, key] of [
40
+ [aggregate.byStage, record.stage ?? UNATTRIBUTED_STAGE],
41
+ [aggregate.byProcess, record.process ?? UNATTRIBUTED_STAGE],
42
+ [aggregate.byEngine, record.engine ?? UNATTRIBUTED_STAGE],
43
+ ]) {
44
+ groups[key] ??= emptyLlmUsageStageAggregate();
45
+ dimensions.push(groups[key]);
41
46
  }
42
- const durationMs = toFiniteNumber(meta.durationMs);
43
- const promptTokens = toFiniteNumber(meta.promptTokens);
44
- const completionTokens = toFiniteNumber(meta.completionTokens);
45
- const totalTokens = toFiniteNumber(meta.totalTokens);
46
- const reasoningTokens = toFiniteNumber(meta.reasoningTokens);
47
- for (const target of [aggregate, stage]) {
47
+ for (const target of [aggregate, ...dimensions]) {
48
48
  target.calls += 1;
49
- target.totalDurationMs += durationMs;
50
- target.promptTokens += promptTokens;
51
- target.completionTokens += completionTokens;
52
- target.totalTokens += totalTokens;
53
- target.reasoningTokens += reasoningTokens;
49
+ target.totalDurationMs += record.durationMs;
50
+ target.promptTokens += record.promptTokens ?? 0;
51
+ target.completionTokens += record.completionTokens ?? 0;
52
+ target.totalTokens += record.totalTokens ?? 0;
53
+ target.reasoningTokens += record.reasoningTokens ?? 0;
54
54
  }
55
55
  }
56
56
  return aggregate;
@@ -28,6 +28,8 @@ export function renderRunsDetailMd(runs) {
28
28
  const headers = [
29
29
  "ts",
30
30
  "ok",
31
+ "strategy",
32
+ "legacy_profile",
31
33
  "actions",
32
34
  "refl_ok/fail/cd/skip",
33
35
  "distill_q/llm-fail/qrej/cfg/skip",
@@ -54,6 +56,8 @@ export function renderRunsDetailMd(runs) {
54
56
  return [
55
57
  r.startedAt,
56
58
  String(r.ok),
59
+ r.strategy ?? "",
60
+ r.legacyProfile ?? "",
57
61
  String(totalActions),
58
62
  `${r.actions.reflect.ok}/${r.actions.reflect.failed}/${r.actions.reflect.cooldown}/${r.actions.reflect.skipped}`,
59
63
  `${r.actions.distill.queued}/${r.actions.distill.llmFailed}/${r.actions.distill.qualityRejected}/${r.actions.distill.configDisabled}/${r.actions.distill.skipped}`,
@@ -7,6 +7,7 @@
7
7
  * minting rollup, and the WS-5 per-run degradation metrics.
8
8
  */
9
9
  import { appendEvent, readEvents } from "../../core/events.js";
10
+ import { decodeImproveResult } from "../../core/improve-result.js";
10
11
  import { queryImproveRuns } from "../../storage/repositories/improve-runs-repository.js";
11
12
  import { listProposalGateDecisions, listStateProposals } from "../../storage/repositories/proposals-repository.js";
12
13
  import { gateDecisionsToSamples, summarizeCalibration } from "../improve/calibration.js";
@@ -221,7 +222,7 @@ export function computeDegradationMetrics(db, since, until) {
221
222
  let totalProcessed = 0;
222
223
  for (const row of runs) {
223
224
  try {
224
- const result = JSON.parse(row.result_json);
225
+ const result = decodeImproveResult(row.result_json).envelope;
225
226
  const cons = result.consolidation;
226
227
  if (cons) {
227
228
  totalContradicted += toFiniteNumber(cons.contradicted);
@@ -18,7 +18,9 @@
18
18
  */
19
19
  import fs from "node:fs";
20
20
  import path from "node:path";
21
- import { CURRENT_CONFIG_VERSION, compareConfigVersion } from "../../core/config/config-migration.js";
21
+ import { MAX_CONFIG_FILE_BYTES, readTextFileWithLimit } from "../../core/common.js";
22
+ import { CURRENT_CONFIG_VERSION } from "../../core/config/config-schema.js";
23
+ import { compareConfigVersion } from "../../core/config/config-version.js";
22
24
  const GROUP_OTHER_BITS = 0o077;
23
25
  const OFFENDER_EVIDENCE_CAP = 50;
24
26
  function modeOctal(mode) {
@@ -97,7 +99,7 @@ export function collectSecretPermsAdvisory(input, platform = process.platform) {
97
99
  export function collectConfigSkewAdvisory(configPath) {
98
100
  let raw;
99
101
  try {
100
- raw = JSON.parse(fs.readFileSync(configPath, "utf8"));
102
+ raw = JSON.parse(readTextFileWithLimit(configPath, MAX_CONFIG_FILE_BYTES, "Config file"));
101
103
  }
102
104
  catch {
103
105
  return undefined;
@@ -182,10 +184,10 @@ export function collectEgressAdvisory(config) {
182
184
  continue;
183
185
  endpoints.push(`source ${source.name ?? "(unnamed)"} (${source.type ?? "?"}): ${source.url}`);
184
186
  }
185
- for (const [name, profile] of Object.entries(config.profiles?.llm ?? {})) {
186
- if (!profile?.endpoint)
187
+ for (const [name, engine] of Object.entries(config.engines ?? {})) {
188
+ if (engine?.kind !== "llm" || !engine.endpoint)
187
189
  continue;
188
- endpoints.push(`llm ${name}: ${profile.endpoint}`);
190
+ endpoints.push(`llm ${name}: ${engine.endpoint}`);
189
191
  }
190
192
  if (config.embedding?.endpoint)
191
193
  endpoints.push(`embedding: ${config.embedding.endpoint}`);
@@ -284,7 +284,7 @@ export function akmHealth(options = {}) {
284
284
  runs = buildPerRunSummaries(db, since);
285
285
  }
286
286
  return {
287
- schemaVersion: 2,
287
+ schemaVersion: 3,
288
288
  ok: !hardFailure,
289
289
  status,
290
290
  since: topLevelSince,
@@ -72,8 +72,8 @@ function distinctTokens(text) {
72
72
  }
73
73
  /**
74
74
  * A merge must strictly increase information (R5 §4.2):
75
- * 1. Provenance: the merged asset's `source_refs` must be a superset of the
76
- * union of all participants' `source_refs` plus the participant refs
75
+ * 1. Provenance: the merged asset's `xrefs` must be a superset of the union of
76
+ * all participants' `xrefs` plus the participant refs
77
77
  * themselves — provenance never shrinks through a merge.
78
78
  * 2. Specificity: distinctTokens(mergedBody) ≥ minSpecificityRetention ×
79
79
  * |union(distinctTokens(participant bodies))| — a merge that only
@@ -111,7 +111,7 @@ export function checkMergeInformationFloor(mergedBody, mergedSourceRefs, partici
111
111
  const specificityOk = specificityRetention >= minRetention;
112
112
  const reasons = [];
113
113
  if (!provenanceOk) {
114
- reasons.push(`provenance shrank: merged source_refs missing ${missing.length} ref(s) (e.g. ${missing[0]})`);
114
+ reasons.push(`provenance shrank: merged xrefs missing ${missing.length} ref(s) (e.g. ${missing[0]})`);
115
115
  }
116
116
  if (!specificityOk) {
117
117
  reasons.push(`specificity retention ${specificityRetention.toFixed(2)} < ${minRetention} (merge genericized/shortened)`);