akm-cli 0.9.0-rc.0 → 0.9.0-rc.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.
Files changed (262) hide show
  1. package/CHANGELOG.md +339 -2
  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}/default.json +1 -0
  7. package/dist/assets/{profiles → improve-strategies}/graph-refresh.json +1 -1
  8. package/dist/assets/{profiles → improve-strategies}/proactive-maintenance.json +2 -3
  9. package/dist/assets/{profiles → improve-strategies}/reflect-distill.json +3 -4
  10. package/dist/assets/prompts/workflow-unit-preamble.md +26 -0
  11. package/dist/assets/stash-skeleton/README.md +28 -0
  12. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +6 -0
  13. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +6 -0
  14. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +12 -1
  15. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +11 -1
  16. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +6 -0
  17. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +9 -0
  18. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +7 -0
  19. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +7 -0
  20. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +6 -0
  21. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +98 -0
  22. package/dist/assets/stash-skeleton/facts/conventions/domains.md +63 -0
  23. package/dist/assets/stash-skeleton/facts/conventions/organization.md +127 -0
  24. package/dist/assets/tasks/core/backup.yml +1 -0
  25. package/dist/assets/tasks/core/extract.yml +1 -0
  26. package/dist/assets/tasks/core/improve.yml +1 -0
  27. package/dist/assets/tasks/core/index-refresh.yml +1 -0
  28. package/dist/assets/tasks/core/sync.yml +1 -0
  29. package/dist/assets/tasks/core/version-check.yml +1 -0
  30. package/dist/assets/tasks/graph-refresh-weekly.yml +4 -4
  31. package/dist/assets/templates/html/health.html +5 -1
  32. package/dist/cli/config-migrate.js +31 -138
  33. package/dist/cli/config-validate.js +10 -8
  34. package/dist/cli.js +48 -14
  35. package/dist/commands/agent/agent-dispatch.js +17 -16
  36. package/dist/commands/agent/agent-support.js +0 -24
  37. package/dist/commands/agent/contribute-cli.js +5 -15
  38. package/dist/commands/backup-cli.js +54 -0
  39. package/dist/commands/config-cli.js +45 -159
  40. package/dist/commands/env/env-binding.js +95 -0
  41. package/dist/commands/env/env-cli.js +8 -65
  42. package/dist/commands/env/secret.js +8 -5
  43. package/dist/commands/health/checks.js +130 -83
  44. package/dist/commands/health/html-report.js +4 -0
  45. package/dist/commands/health/improve-metrics.js +30 -32
  46. package/dist/commands/health/llm-usage.js +19 -19
  47. package/dist/commands/health/md-report.js +4 -0
  48. package/dist/commands/health/metrics.js +2 -1
  49. package/dist/commands/health/surfaces.js +5 -4
  50. package/dist/commands/health.js +1 -1
  51. package/dist/commands/improve/consolidate/chunking.js +2 -2
  52. package/dist/commands/improve/consolidate.js +28 -25
  53. package/dist/commands/improve/distill/promote-memory.js +5 -12
  54. package/dist/commands/improve/distill/quality-gate.js +5 -7
  55. package/dist/commands/improve/distill.js +16 -5
  56. package/dist/commands/improve/eligibility.js +22 -12
  57. package/dist/commands/improve/extract-cli.js +47 -19
  58. package/dist/commands/improve/extract.js +110 -62
  59. package/dist/commands/improve/improve-cli.js +38 -16
  60. package/dist/commands/improve/improve-result-file.js +30 -24
  61. package/dist/commands/improve/improve-strategies.js +137 -0
  62. package/dist/commands/improve/improve.js +60 -30
  63. package/dist/commands/improve/locks.js +66 -45
  64. package/dist/commands/improve/loop-stages.js +75 -33
  65. package/dist/commands/improve/memory/memory-belief.js +79 -7
  66. package/dist/commands/improve/memory/memory-contradiction-detect.js +12 -4
  67. package/dist/commands/improve/preparation.js +71 -73
  68. package/dist/commands/improve/procedural.js +3 -2
  69. package/dist/commands/improve/recombine.js +2 -1
  70. package/dist/commands/improve/reflect.js +119 -214
  71. package/dist/commands/improve/shared.js +11 -5
  72. package/dist/commands/lint/base-linter.js +152 -42
  73. package/dist/commands/mv-cli.js +809 -0
  74. package/dist/commands/proposal/proposal-cli.js +18 -8
  75. package/dist/commands/proposal/propose.js +64 -69
  76. package/dist/commands/read/knowledge.js +436 -4
  77. package/dist/commands/read/remember-cli.js +39 -2
  78. package/dist/commands/read/search-cli.js +6 -1
  79. package/dist/commands/registry-cli.js +29 -14
  80. package/dist/commands/remember.js +2 -0
  81. package/dist/commands/sources/init.js +13 -14
  82. package/dist/commands/sources/migration-help.js +7 -4
  83. package/dist/commands/sources/schema-repair.js +2 -4
  84. package/dist/commands/sources/source-add.js +62 -73
  85. package/dist/commands/sources/source-manage.js +50 -46
  86. package/dist/commands/sources/stash-cli.js +41 -4
  87. package/dist/commands/tasks/default-tasks.js +12 -12
  88. package/dist/commands/tasks/tasks-cli.js +7 -3
  89. package/dist/commands/tasks/tasks.js +113 -18
  90. package/dist/commands/wiki-cli.js +9 -10
  91. package/dist/commands/workflow-cli.js +276 -12
  92. package/dist/core/asset/asset-spec.js +58 -1
  93. package/dist/core/asset/frontmatter.js +12 -2
  94. package/dist/core/common.js +5 -3
  95. package/dist/core/config/config-io.js +28 -17
  96. package/dist/core/config/config-schema.js +379 -66
  97. package/dist/core/config/config-types.js +3 -3
  98. package/dist/core/config/config-version.js +29 -0
  99. package/dist/core/config/config-walker.js +98 -27
  100. package/dist/core/config/config.js +132 -266
  101. package/dist/core/config/deep-merge.js +41 -0
  102. package/dist/core/config/engine-semantics.js +32 -0
  103. package/dist/core/errors.js +2 -2
  104. package/dist/core/extra-params.js +61 -0
  105. package/dist/core/file-lock.js +201 -56
  106. package/dist/core/improve-result.js +178 -0
  107. package/dist/core/json-schema.js +142 -0
  108. package/dist/core/maintenance-barrier.js +119 -0
  109. package/dist/core/migration-backup.js +416 -0
  110. package/dist/core/paths.js +3 -0
  111. package/dist/core/redaction.js +358 -0
  112. package/dist/core/state/migrations.js +17 -2
  113. package/dist/core/state-db.js +44 -1
  114. package/dist/indexer/db/db.js +118 -2
  115. package/dist/indexer/graph/graph-extraction.js +28 -16
  116. package/dist/indexer/index-writer-lock.js +31 -24
  117. package/dist/indexer/index-written-assets.js +15 -6
  118. package/dist/indexer/indexer.js +47 -2
  119. package/dist/indexer/passes/memory-inference.js +10 -6
  120. package/dist/indexer/passes/metadata.js +250 -0
  121. package/dist/indexer/search/db-search.js +111 -44
  122. package/dist/indexer/search/fts-query.js +41 -0
  123. package/dist/indexer/search/ranking-contributors.js +48 -0
  124. package/dist/indexer/search/ranking.js +36 -23
  125. package/dist/indexer/search/search-fields.js +11 -1
  126. package/dist/indexer/walk/matchers.js +39 -0
  127. package/dist/integrations/agent/builder-shared.js +7 -0
  128. package/dist/integrations/agent/builders.js +5 -50
  129. package/dist/integrations/agent/config.js +3 -143
  130. package/dist/integrations/agent/detect.js +17 -2
  131. package/dist/integrations/agent/engine-resolution.js +202 -0
  132. package/dist/integrations/agent/index.js +1 -2
  133. package/dist/integrations/agent/model-aliases.js +16 -2
  134. package/dist/integrations/agent/profiles.js +36 -62
  135. package/dist/integrations/agent/runner-dispatch.js +91 -4
  136. package/dist/integrations/agent/runner.js +76 -207
  137. package/dist/integrations/agent/spawn.js +141 -20
  138. package/dist/integrations/harnesses/aider/agent-builder.js +112 -0
  139. package/dist/integrations/harnesses/aider/index.js +57 -0
  140. package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
  141. package/dist/integrations/harnesses/amazonq/agent-builder.js +152 -0
  142. package/dist/integrations/harnesses/amazonq/index.js +58 -0
  143. package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
  144. package/dist/integrations/harnesses/claude/agent-builder.js +46 -8
  145. package/dist/integrations/harnesses/claude/index.js +25 -25
  146. package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
  147. package/dist/integrations/harnesses/codex/agent-builder.js +136 -0
  148. package/dist/integrations/harnesses/codex/index.js +62 -0
  149. package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
  150. package/dist/integrations/harnesses/copilot/agent-builder.js +121 -0
  151. package/dist/integrations/harnesses/copilot/index.js +59 -0
  152. package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
  153. package/dist/integrations/harnesses/gemini/agent-builder.js +120 -0
  154. package/dist/integrations/harnesses/gemini/index.js +59 -0
  155. package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
  156. package/dist/integrations/harnesses/index.js +27 -28
  157. package/dist/integrations/harnesses/opencode/agent-builder.js +2 -3
  158. package/dist/integrations/harnesses/opencode/index.js +15 -22
  159. package/dist/integrations/harnesses/opencode-sdk/harness.js +60 -0
  160. package/dist/integrations/harnesses/opencode-sdk/index.js +8 -32
  161. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +681 -108
  162. package/dist/integrations/harnesses/openhands/agent-builder.js +125 -0
  163. package/dist/integrations/harnesses/openhands/index.js +57 -0
  164. package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
  165. package/dist/integrations/harnesses/pi/agent-builder.js +103 -0
  166. package/dist/integrations/harnesses/pi/index.js +57 -0
  167. package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
  168. package/dist/integrations/harnesses/types.js +8 -32
  169. package/dist/integrations/lockfile.js +32 -21
  170. package/dist/integrations/session-logs/index.js +24 -11
  171. package/dist/llm/client.js +48 -14
  172. package/dist/llm/feature-gate.js +15 -47
  173. package/dist/llm/graph-extract.js +1 -1
  174. package/dist/llm/index-passes.js +8 -42
  175. package/dist/llm/memory-infer-impl.js +1 -1
  176. package/dist/llm/usage-persist.js +4 -0
  177. package/dist/llm/usage-telemetry.js +35 -5
  178. package/dist/output/renderers.js +3 -2
  179. package/dist/output/shapes/helpers.js +2 -1
  180. package/dist/output/shapes/passthrough.js +6 -0
  181. package/dist/output/text/helpers.js +215 -2
  182. package/dist/output/text/workflow.js +3 -1
  183. package/dist/schemas/akm-config.json +16638 -0
  184. package/dist/schemas/akm-task.json +87 -0
  185. package/dist/schemas/akm-workflow.json +372 -0
  186. package/dist/scripts/migrate-storage.js +10944 -8801
  187. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +9247 -350
  188. package/dist/setup/detected-engines.js +142 -0
  189. package/dist/setup/engine-config.js +89 -0
  190. package/dist/setup/setup.js +236 -132
  191. package/dist/setup/steps/connection.js +61 -32
  192. package/dist/setup/steps/platforms.js +4 -4
  193. package/dist/setup/steps.js +3 -2
  194. package/dist/storage/database.js +13 -1
  195. package/dist/storage/engines/sqlite-migrations.js +1 -0
  196. package/dist/storage/repositories/improve-runs-repository.js +5 -5
  197. package/dist/storage/repositories/task-history-repository.js +78 -0
  198. package/dist/storage/repositories/workflow-runs-repository.js +190 -1
  199. package/dist/tasks/parser.js +138 -52
  200. package/dist/tasks/runner.js +71 -75
  201. package/dist/tasks/schema.js +1 -1
  202. package/dist/tasks/validator.js +11 -6
  203. package/dist/text-import-hook.mjs +1 -1
  204. package/dist/wiki/wiki.js +9 -8
  205. package/dist/workflows/authoring/authoring.js +123 -10
  206. package/dist/workflows/authoring/workflow-program-template.yaml +31 -0
  207. package/dist/workflows/cli.js +4 -0
  208. package/dist/workflows/concurrency-policy.js +15 -0
  209. package/dist/workflows/db.js +200 -13
  210. package/dist/workflows/exec/brief.js +478 -0
  211. package/dist/workflows/exec/frozen-judge.js +47 -0
  212. package/dist/workflows/exec/native-executor.js +1034 -0
  213. package/dist/workflows/exec/param-secrets.js +115 -0
  214. package/dist/workflows/exec/report.js +1355 -0
  215. package/dist/workflows/exec/run-workflow.js +609 -0
  216. package/dist/workflows/exec/scheduler.js +71 -0
  217. package/dist/workflows/exec/step-work.js +1212 -0
  218. package/dist/workflows/exec/unit-writer.js +23 -0
  219. package/dist/workflows/exec/watch.js +116 -0
  220. package/dist/workflows/exec/worktree.js +171 -0
  221. package/dist/workflows/ir/compile.js +375 -0
  222. package/dist/workflows/ir/freeze.js +243 -0
  223. package/dist/workflows/ir/params.js +54 -0
  224. package/dist/workflows/ir/plan-hash.js +68 -0
  225. package/dist/workflows/ir/schema.js +545 -0
  226. package/dist/workflows/parser.js +10 -1
  227. package/dist/workflows/program/expressions.js +369 -0
  228. package/dist/workflows/program/parser.js +869 -0
  229. package/dist/workflows/program/project.js +104 -0
  230. package/dist/workflows/program/schema.js +54 -0
  231. package/dist/workflows/renderer.js +82 -5
  232. package/dist/workflows/resource-limits.js +20 -0
  233. package/dist/workflows/runtime/agent-identity.js +59 -14
  234. package/dist/workflows/runtime/plan-classifier.js +187 -0
  235. package/dist/workflows/runtime/runs.js +246 -69
  236. package/dist/workflows/runtime/unit-checkin.js +45 -0
  237. package/dist/workflows/runtime/workflow-asset-loader.js +42 -1
  238. package/dist/workflows/validate-summary.js +24 -3
  239. package/dist/workflows/validator.js +26 -1
  240. package/docs/data-and-telemetry.md +4 -3
  241. package/docs/migration/release-notes/0.6.0.md +1 -1
  242. package/docs/migration/release-notes/0.7.0.md +5 -4
  243. package/docs/migration/release-notes/0.9.0-beta.60.md +19 -0
  244. package/docs/migration/v0.8-to-v0.9.md +401 -0
  245. package/package.json +4 -2
  246. package/schemas/akm-config.json +16638 -0
  247. package/schemas/akm-task.json +87 -0
  248. package/schemas/akm-workflow.json +372 -0
  249. package/dist/commands/improve/improve-profiles.js +0 -168
  250. package/dist/core/config/config-migration.js +0 -602
  251. package/dist/core/deep-merge.js +0 -38
  252. package/dist/llm/call-ai.js +0 -62
  253. package/dist/setup/legacy-config.js +0 -106
  254. package/docs/README.md +0 -104
  255. /package/dist/assets/{profiles → improve-strategies}/catchup.json +0 -0
  256. /package/dist/assets/{profiles → improve-strategies}/consolidate.json +0 -0
  257. /package/dist/assets/{profiles → improve-strategies}/frequent.json +0 -0
  258. /package/dist/assets/{profiles → improve-strategies}/memory-focus.json +0 -0
  259. /package/dist/assets/{profiles → improve-strategies}/quick.json +0 -0
  260. /package/dist/assets/{profiles → improve-strategies}/recombine-only.json +0 -0
  261. /package/dist/assets/{profiles → improve-strategies}/synthesize.json +0 -0
  262. /package/dist/assets/{profiles → improve-strategies}/thorough.json +0 -0
@@ -2,111 +2,158 @@
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";
7
9
  import { TASK_FAIL_RATE_WARN, } from "./types.js";
8
10
  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];
11
+ export function runDefaultEngineProbe(deps = {}) {
12
+ const config = deps.loadConfig?.() ?? loadConfig();
13
+ const engineName = config.defaults?.engine ?? config.defaults?.llmEngine;
14
+ if (!engineName) {
31
15
  return {
32
- name: "agent-profile",
33
- kind: "deterministic",
34
- status: "pass",
35
- confidence: "high",
36
- message: `v2 agent profile "${profileName}" configured (platform: ${profile?.platform ?? "unknown"}).`,
37
- evidence: { profile: profileName, platform: profile?.platform, profileCount },
38
- };
39
- }
40
- if (!config.profiles?.agent && !config.defaults?.agent) {
41
- return {
42
- name: "agent-profile",
16
+ name: "default-engine",
43
17
  kind: "deterministic",
44
18
  status: "unknown",
45
19
  confidence: "high",
46
- message: "No agent config present.",
47
- };
48
- }
49
- let profile;
50
- try {
51
- profile = requireAgentProfile(config);
52
- }
53
- catch (error) {
54
- return {
55
- name: "agent-profile",
56
- kind: "deterministic",
57
- status: "warn",
58
- confidence: "high",
59
- message: error instanceof Error ? error.message : String(error),
20
+ message: "No default engine is configured.",
60
21
  };
61
22
  }
62
- if (profile.sdkMode === true) {
23
+ const configuredEngine = config.engines?.[engineName];
24
+ if (configuredEngine?.kind === "agent" && configuredEngine.platform === "opencode-sdk") {
25
+ let packageAvailable = false;
26
+ try {
27
+ const resolvePackage = deps.resolvePackage ?? ((name) => createRequire(import.meta.url).resolve(name));
28
+ resolvePackage("@opencode-ai/sdk");
29
+ packageAvailable = true;
30
+ }
31
+ catch {
32
+ packageAvailable = false;
33
+ }
34
+ const binary = configuredEngine.bin ?? "opencode";
35
+ const version = (deps.spawnSync ?? spawnSync)(binary, ["--version"], { encoding: "utf8", timeout: 5_000 });
36
+ const binaryAvailable = (version.status ?? 1) === 0;
37
+ const fallbackEngine = configuredEngine.llmEngine ?? config.defaults?.llmEngine;
38
+ let fallback;
39
+ let sdkRunner;
40
+ const resolve = deps.resolveEngine ?? resolveEngine;
41
+ try {
42
+ const resolved = resolve(engineName, config);
43
+ if (resolved.kind === "sdk")
44
+ sdkRunner = resolved;
45
+ }
46
+ catch {
47
+ sdkRunner = undefined;
48
+ }
49
+ if (sdkRunner?.fallbackConnection) {
50
+ fallback = { kind: "llm", connection: sdkRunner.fallbackConnection };
51
+ }
52
+ else if (fallbackEngine) {
53
+ try {
54
+ const resolved = resolve(fallbackEngine, config);
55
+ if (resolved.kind === "llm")
56
+ fallback = resolved;
57
+ }
58
+ catch {
59
+ fallback = undefined;
60
+ }
61
+ }
62
+ const configuredModel = configuredEngine.model
63
+ ? resolveModel(configuredEngine.model, "opencode-sdk", configuredEngine.modelAliases, config.modelAliases)
64
+ : undefined;
65
+ const effectiveModel = sdkRunner?.profile.model ?? configuredModel ?? fallback?.connection.model;
66
+ const missing = [
67
+ !packageAvailable ? "@opencode-ai/sdk package" : undefined,
68
+ !binaryAvailable ? `${binary} binary` : undefined,
69
+ !fallback ? "fallback LLM connection" : undefined,
70
+ !effectiveModel ? "effective SDK model" : undefined,
71
+ ].filter((value) => value !== undefined);
63
72
  return {
64
- name: "agent-profile",
73
+ name: "default-engine",
65
74
  kind: "deterministic",
66
- status: profile.model ? "pass" : "warn",
75
+ status: missing.length === 0 ? "pass" : "warn",
67
76
  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 },
77
+ message: missing.length === 0
78
+ ? `SDK engine "${engineName}" is available.`
79
+ : `SDK engine "${engineName}" is incomplete: missing ${missing.join(", ")}.`,
80
+ evidence: {
81
+ engine: engineName,
82
+ platform: configuredEngine.platform,
83
+ runtimeKind: "sdk",
84
+ binary,
85
+ binaryAvailable,
86
+ package: "@opencode-ai/sdk",
87
+ packageAvailable,
88
+ model: effectiveModel ?? null,
89
+ configuredModel: configuredModel ?? null,
90
+ modelSource: configuredModel ? "sdk" : effectiveModel ? "fallback" : null,
91
+ fallbackEngine: fallbackEngine ?? null,
92
+ fallbackEndpoint: fallback?.connection.endpoint ?? null,
93
+ fallbackModel: fallback?.connection.model ?? null,
94
+ },
72
95
  };
73
96
  }
74
- const detections = detectAgentCliProfiles(config);
75
- const detection = detections.find((entry) => entry.name === profile.name);
76
- if (!detection?.available) {
97
+ try {
98
+ const runner = (deps.resolveEngine ?? resolveEngine)(engineName, config);
99
+ if (runner.kind === "llm") {
100
+ return {
101
+ name: "default-engine",
102
+ kind: "deterministic",
103
+ status: "pass",
104
+ confidence: "high",
105
+ message: `LLM engine "${engineName}" is configured.`,
106
+ evidence: {
107
+ engine: engineName,
108
+ platform: null,
109
+ runtimeKind: "llm",
110
+ model: runner.connection.model,
111
+ endpoint: runner.connection.endpoint,
112
+ },
113
+ };
114
+ }
115
+ const profile = runner.profile;
116
+ if (runner.kind === "sdk")
117
+ throw new Error(`SDK engine "${engineName}" has no matching SDK config.`);
118
+ const version = (deps.spawnSync ?? spawnSync)(profile.bin, ["--version"], { encoding: "utf8", timeout: 5_000 });
119
+ if ((version.status ?? 1) !== 0) {
120
+ return {
121
+ name: "default-engine",
122
+ kind: "deterministic",
123
+ status: "warn",
124
+ confidence: "medium",
125
+ message: `Agent engine "${engineName}" was found but \`--version\` failed.`,
126
+ evidence: {
127
+ engine: engineName,
128
+ platform: profile.platform ?? null,
129
+ runtimeKind: "agent",
130
+ model: profile.model ?? null,
131
+ },
132
+ };
133
+ }
77
134
  return {
78
- name: "agent-profile",
135
+ name: "default-engine",
79
136
  kind: "deterministic",
80
- status: "fail",
137
+ status: "pass",
81
138
  confidence: "high",
82
- message: `Default agent profile "${profile.name}" is not available on PATH.`,
83
- evidence: { profile: profile.name, bin: profile.bin },
139
+ message: `Agent engine "${engineName}" is available.`,
140
+ evidence: {
141
+ engine: engineName,
142
+ platform: profile.platform ?? null,
143
+ runtimeKind: "agent",
144
+ model: profile.model ?? null,
145
+ },
84
146
  };
85
147
  }
86
- const version = spawnSync(profile.bin, ["--version"], { encoding: "utf8", timeout: 5_000 });
87
- if ((version.status ?? 1) !== 0) {
148
+ catch (error) {
88
149
  return {
89
- name: "agent-profile",
150
+ name: "default-engine",
90
151
  kind: "deterministic",
91
152
  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
- },
153
+ confidence: "high",
154
+ message: error instanceof Error ? error.message : String(error),
100
155
  };
101
156
  }
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
157
  }
111
158
  /**
112
159
  * The ordered health-check registry. ORDER IS LOAD-BEARING: `akmHealth`
@@ -185,9 +232,9 @@ export const HEALTH_CHECKS = [
185
232
  }),
186
233
  },
187
234
  {
188
- name: "agent-profile",
235
+ name: "default-engine",
189
236
  channel: "hard",
190
- run: () => runAgentProbe(),
237
+ run: () => runDefaultEngineProbe(),
191
238
  },
192
239
  {
193
240
  // 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,8 @@
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 { CURRENT_CONFIG_VERSION } from "../../core/config/config-schema.js";
22
+ import { compareConfigVersion } from "../../core/config/config-version.js";
22
23
  const GROUP_OTHER_BITS = 0o077;
23
24
  const OFFENDER_EVIDENCE_CAP = 50;
24
25
  function modeOctal(mode) {
@@ -182,10 +183,10 @@ export function collectEgressAdvisory(config) {
182
183
  continue;
183
184
  endpoints.push(`source ${source.name ?? "(unnamed)"} (${source.type ?? "?"}): ${source.url}`);
184
185
  }
185
- for (const [name, profile] of Object.entries(config.profiles?.llm ?? {})) {
186
- if (!profile?.endpoint)
186
+ for (const [name, engine] of Object.entries(config.engines ?? {})) {
187
+ if (engine?.kind !== "llm" || !engine.endpoint)
187
188
  continue;
188
- endpoints.push(`llm ${name}: ${profile.endpoint}`);
189
+ endpoints.push(`llm ${name}: ${engine.endpoint}`);
189
190
  }
190
191
  if (config.embedding?.endpoint)
191
192
  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,
@@ -33,9 +33,9 @@ const PROMPT_OVERHEAD_TOKENS = 2_000;
33
33
  * chunk content.
34
34
  * - When the HTTP path is used (an LLM profile is selected), only the akm
35
35
  * system prompt and user prompt are sent, so the budget can be set to the
36
- * model's actual context length via profiles.llm[defaults.llm].contextLength.
36
+ * model's actual context length via the selected LLM engine's contextLength.
37
37
  *
38
- * Set profiles.llm[defaults.llm].contextLength in your config file to the
38
+ * Set the selected LLM engine's contextLength in your config file to the
39
39
  * model's actual context window to allow larger chunks on the HTTP path.
40
40
  */
41
41
  export const DEFAULT_CONTEXT_LENGTH_TOKENS = 4_096;