akm-cli 0.9.0-rc.1 → 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 (228) hide show
  1. package/CHANGELOG.md +223 -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}/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/stash-skeleton/README.md +28 -0
  11. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +6 -0
  12. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +6 -0
  13. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +12 -1
  14. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +11 -1
  15. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +6 -0
  16. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +9 -0
  17. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +7 -0
  18. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +7 -0
  19. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +6 -0
  20. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +98 -0
  21. package/dist/assets/stash-skeleton/facts/conventions/domains.md +63 -0
  22. package/dist/assets/stash-skeleton/facts/conventions/organization.md +127 -0
  23. package/dist/assets/tasks/core/backup.yml +1 -0
  24. package/dist/assets/tasks/core/extract.yml +1 -0
  25. package/dist/assets/tasks/core/improve.yml +1 -0
  26. package/dist/assets/tasks/core/index-refresh.yml +1 -0
  27. package/dist/assets/tasks/core/sync.yml +1 -0
  28. package/dist/assets/tasks/core/version-check.yml +1 -0
  29. package/dist/assets/tasks/graph-refresh-weekly.yml +4 -4
  30. package/dist/assets/templates/html/health.html +5 -1
  31. package/dist/cli/config-migrate.js +31 -138
  32. package/dist/cli/config-validate.js +10 -8
  33. package/dist/cli.js +48 -14
  34. package/dist/commands/agent/agent-dispatch.js +17 -16
  35. package/dist/commands/agent/agent-support.js +0 -24
  36. package/dist/commands/agent/contribute-cli.js +5 -15
  37. package/dist/commands/backup-cli.js +54 -0
  38. package/dist/commands/config-cli.js +45 -159
  39. package/dist/commands/env/secret.js +8 -5
  40. package/dist/commands/health/checks.js +130 -83
  41. package/dist/commands/health/html-report.js +4 -0
  42. package/dist/commands/health/improve-metrics.js +30 -32
  43. package/dist/commands/health/llm-usage.js +19 -19
  44. package/dist/commands/health/md-report.js +4 -0
  45. package/dist/commands/health/metrics.js +2 -1
  46. package/dist/commands/health/surfaces.js +5 -4
  47. package/dist/commands/health.js +1 -1
  48. package/dist/commands/improve/consolidate/chunking.js +2 -2
  49. package/dist/commands/improve/consolidate.js +28 -25
  50. package/dist/commands/improve/distill/promote-memory.js +5 -12
  51. package/dist/commands/improve/distill/quality-gate.js +5 -7
  52. package/dist/commands/improve/distill.js +16 -5
  53. package/dist/commands/improve/eligibility.js +22 -12
  54. package/dist/commands/improve/extract-cli.js +47 -19
  55. package/dist/commands/improve/extract.js +110 -62
  56. package/dist/commands/improve/improve-cli.js +38 -16
  57. package/dist/commands/improve/improve-result-file.js +30 -24
  58. package/dist/commands/improve/improve-strategies.js +137 -0
  59. package/dist/commands/improve/improve.js +60 -30
  60. package/dist/commands/improve/locks.js +66 -45
  61. package/dist/commands/improve/loop-stages.js +75 -33
  62. package/dist/commands/improve/memory/memory-belief.js +79 -7
  63. package/dist/commands/improve/memory/memory-contradiction-detect.js +12 -4
  64. package/dist/commands/improve/preparation.js +71 -73
  65. package/dist/commands/improve/procedural.js +3 -2
  66. package/dist/commands/improve/recombine.js +2 -1
  67. package/dist/commands/improve/reflect.js +119 -214
  68. package/dist/commands/improve/shared.js +11 -5
  69. package/dist/commands/lint/base-linter.js +152 -42
  70. package/dist/commands/mv-cli.js +809 -0
  71. package/dist/commands/proposal/proposal-cli.js +18 -8
  72. package/dist/commands/proposal/propose.js +64 -69
  73. package/dist/commands/read/knowledge.js +436 -4
  74. package/dist/commands/read/remember-cli.js +39 -2
  75. package/dist/commands/read/search-cli.js +6 -1
  76. package/dist/commands/registry-cli.js +29 -14
  77. package/dist/commands/remember.js +2 -0
  78. package/dist/commands/sources/init.js +13 -14
  79. package/dist/commands/sources/schema-repair.js +2 -4
  80. package/dist/commands/sources/source-add.js +62 -73
  81. package/dist/commands/sources/source-manage.js +50 -46
  82. package/dist/commands/sources/stash-cli.js +41 -4
  83. package/dist/commands/tasks/default-tasks.js +12 -12
  84. package/dist/commands/tasks/tasks-cli.js +7 -3
  85. package/dist/commands/tasks/tasks.js +113 -18
  86. package/dist/commands/wiki-cli.js +9 -10
  87. package/dist/core/asset/frontmatter.js +12 -2
  88. package/dist/core/common.js +5 -3
  89. package/dist/core/config/config-io.js +28 -17
  90. package/dist/core/config/config-schema.js +358 -66
  91. package/dist/core/config/config-types.js +3 -3
  92. package/dist/core/config/config-version.js +29 -0
  93. package/dist/core/config/config-walker.js +98 -27
  94. package/dist/core/config/config.js +132 -266
  95. package/dist/core/config/deep-merge.js +41 -0
  96. package/dist/core/config/engine-semantics.js +32 -0
  97. package/dist/core/errors.js +2 -2
  98. package/dist/core/extra-params.js +61 -0
  99. package/dist/core/file-lock.js +201 -56
  100. package/dist/core/improve-result.js +178 -0
  101. package/dist/core/maintenance-barrier.js +119 -0
  102. package/dist/core/migration-backup.js +416 -0
  103. package/dist/core/paths.js +3 -0
  104. package/dist/core/redaction.js +358 -0
  105. package/dist/core/state/migrations.js +17 -2
  106. package/dist/core/state-db.js +44 -1
  107. package/dist/indexer/db/db.js +116 -1
  108. package/dist/indexer/graph/graph-extraction.js +28 -16
  109. package/dist/indexer/index-writer-lock.js +31 -24
  110. package/dist/indexer/index-written-assets.js +15 -6
  111. package/dist/indexer/indexer.js +47 -2
  112. package/dist/indexer/passes/memory-inference.js +10 -6
  113. package/dist/indexer/passes/metadata.js +250 -0
  114. package/dist/indexer/search/db-search.js +111 -44
  115. package/dist/indexer/search/fts-query.js +41 -0
  116. package/dist/indexer/search/ranking-contributors.js +48 -0
  117. package/dist/indexer/search/ranking.js +36 -23
  118. package/dist/indexer/search/search-fields.js +11 -1
  119. package/dist/integrations/agent/builder-shared.js +7 -0
  120. package/dist/integrations/agent/builders.js +5 -52
  121. package/dist/integrations/agent/config.js +3 -143
  122. package/dist/integrations/agent/detect.js +17 -2
  123. package/dist/integrations/agent/engine-resolution.js +202 -0
  124. package/dist/integrations/agent/index.js +1 -2
  125. package/dist/integrations/agent/model-aliases.js +7 -2
  126. package/dist/integrations/agent/profiles.js +6 -99
  127. package/dist/integrations/agent/runner-dispatch.js +76 -13
  128. package/dist/integrations/agent/runner.js +76 -207
  129. package/dist/integrations/agent/spawn.js +4 -6
  130. package/dist/integrations/harnesses/aider/agent-builder.js +2 -3
  131. package/dist/integrations/harnesses/aider/index.js +0 -1
  132. package/dist/integrations/harnesses/amazonq/agent-builder.js +2 -3
  133. package/dist/integrations/harnesses/amazonq/index.js +0 -1
  134. package/dist/integrations/harnesses/claude/agent-builder.js +2 -3
  135. package/dist/integrations/harnesses/claude/index.js +0 -2
  136. package/dist/integrations/harnesses/codex/agent-builder.js +2 -3
  137. package/dist/integrations/harnesses/codex/index.js +0 -1
  138. package/dist/integrations/harnesses/copilot/agent-builder.js +2 -3
  139. package/dist/integrations/harnesses/copilot/index.js +0 -1
  140. package/dist/integrations/harnesses/gemini/agent-builder.js +2 -3
  141. package/dist/integrations/harnesses/gemini/index.js +0 -1
  142. package/dist/integrations/harnesses/index.js +1 -24
  143. package/dist/integrations/harnesses/opencode/agent-builder.js +2 -3
  144. package/dist/integrations/harnesses/opencode/index.js +0 -6
  145. package/dist/integrations/harnesses/opencode-sdk/harness.js +1 -6
  146. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +274 -191
  147. package/dist/integrations/harnesses/openhands/agent-builder.js +2 -3
  148. package/dist/integrations/harnesses/openhands/index.js +0 -1
  149. package/dist/integrations/harnesses/pi/agent-builder.js +2 -3
  150. package/dist/integrations/harnesses/pi/index.js +0 -1
  151. package/dist/integrations/harnesses/types.js +1 -32
  152. package/dist/integrations/lockfile.js +32 -21
  153. package/dist/llm/client.js +48 -14
  154. package/dist/llm/feature-gate.js +15 -47
  155. package/dist/llm/graph-extract.js +1 -1
  156. package/dist/llm/index-passes.js +8 -42
  157. package/dist/llm/memory-infer-impl.js +1 -1
  158. package/dist/llm/usage-persist.js +4 -0
  159. package/dist/llm/usage-telemetry.js +35 -5
  160. package/dist/output/shapes/helpers.js +2 -1
  161. package/dist/output/shapes/passthrough.js +2 -0
  162. package/dist/output/text/helpers.js +3 -1
  163. package/dist/schemas/akm-config.json +11013 -8600
  164. package/dist/schemas/akm-task.json +87 -0
  165. package/dist/schemas/akm-workflow.json +57 -13
  166. package/dist/scripts/migrate-storage.js +8591 -509
  167. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +8623 -484
  168. package/dist/setup/detected-engines.js +142 -0
  169. package/dist/setup/engine-config.js +89 -0
  170. package/dist/setup/setup.js +236 -132
  171. package/dist/setup/steps/connection.js +61 -32
  172. package/dist/setup/steps/platforms.js +4 -4
  173. package/dist/setup/steps.js +3 -2
  174. package/dist/storage/database.js +13 -1
  175. package/dist/storage/engines/sqlite-migrations.js +1 -0
  176. package/dist/storage/repositories/improve-runs-repository.js +5 -5
  177. package/dist/storage/repositories/task-history-repository.js +78 -0
  178. package/dist/storage/repositories/workflow-runs-repository.js +9 -8
  179. package/dist/tasks/parser.js +138 -52
  180. package/dist/tasks/runner.js +71 -75
  181. package/dist/tasks/schema.js +1 -1
  182. package/dist/tasks/validator.js +11 -6
  183. package/dist/wiki/wiki.js +9 -8
  184. package/dist/workflows/authoring/workflow-program-template.yaml +3 -3
  185. package/dist/workflows/concurrency-policy.js +15 -0
  186. package/dist/workflows/db.js +65 -13
  187. package/dist/workflows/exec/brief.js +18 -24
  188. package/dist/workflows/exec/frozen-judge.js +47 -0
  189. package/dist/workflows/exec/native-executor.js +124 -65
  190. package/dist/workflows/exec/report.js +93 -33
  191. package/dist/workflows/exec/run-workflow.js +38 -25
  192. package/dist/workflows/exec/scheduler.js +12 -41
  193. package/dist/workflows/exec/step-work.js +91 -35
  194. package/dist/workflows/ir/compile.js +13 -26
  195. package/dist/workflows/ir/freeze.js +243 -0
  196. package/dist/workflows/ir/plan-hash.js +40 -5
  197. package/dist/workflows/ir/schema.js +542 -1
  198. package/dist/workflows/parser.js +7 -0
  199. package/dist/workflows/program/parser.js +132 -23
  200. package/dist/workflows/program/project.js +3 -4
  201. package/dist/workflows/program/schema.js +2 -2
  202. package/dist/workflows/resource-limits.js +20 -0
  203. package/dist/workflows/runtime/plan-classifier.js +187 -0
  204. package/dist/workflows/runtime/runs.js +54 -47
  205. package/dist/workflows/runtime/workflow-asset-loader.js +0 -22
  206. package/dist/workflows/validator.js +25 -0
  207. package/docs/data-and-telemetry.md +2 -2
  208. package/docs/migration/release-notes/0.6.0.md +1 -1
  209. package/docs/migration/release-notes/0.7.0.md +5 -4
  210. package/docs/migration/v0.8-to-v0.9.md +401 -0
  211. package/package.json +4 -2
  212. package/schemas/akm-config.json +16638 -0
  213. package/schemas/akm-task.json +87 -0
  214. package/schemas/akm-workflow.json +372 -0
  215. package/dist/commands/improve/improve-profiles.js +0 -168
  216. package/dist/core/config/config-migration.js +0 -602
  217. package/dist/core/deep-merge.js +0 -38
  218. package/dist/llm/call-ai.js +0 -62
  219. package/dist/setup/legacy-config.js +0 -106
  220. package/docs/README.md +0 -104
  221. /package/dist/assets/{profiles → improve-strategies}/catchup.json +0 -0
  222. /package/dist/assets/{profiles → improve-strategies}/consolidate.json +0 -0
  223. /package/dist/assets/{profiles → improve-strategies}/frequent.json +0 -0
  224. /package/dist/assets/{profiles → improve-strategies}/memory-focus.json +0 -0
  225. /package/dist/assets/{profiles → improve-strategies}/quick.json +0 -0
  226. /package/dist/assets/{profiles → improve-strategies}/recombine-only.json +0 -0
  227. /package/dist/assets/{profiles → improve-strategies}/synthesize.json +0 -0
  228. /package/dist/assets/{profiles → improve-strategies}/thorough.json +0 -0
@@ -1,602 +0,0 @@
1
- // This Source Code Form is subject to the terms of the Mozilla Public
2
- // License, v. 2.0. If a copy of the MPL was not distributed with this
3
- // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- /**
5
- * Config shape migration logic for AKM v0.8.0.
6
- *
7
- * This module is intentionally kept free of imports from `config.ts` to avoid
8
- * circular dependencies: `config.ts` imports `migrateConfigShape` from here,
9
- * and `config-migrate.ts` (the CLI command) also imports from here.
10
- *
11
- * Migration policy (0.8.0): every legacy field is migrated to its NEW final
12
- * location and stripped from the raw config. Production code reads ONLY the
13
- * new shape. There are no backward-compat shims after this migration.
14
- */
15
- import { v1ProfilePlatform } from "../../integrations/harnesses/index.js";
16
- import { warn } from "../warn.js";
17
- /**
18
- * Current config schema version sentinel.
19
- * Configs at this version are considered fully migrated and will not be rewritten.
20
- */
21
- export const CURRENT_CONFIG_VERSION = "0.8.0";
22
- /**
23
- * Compare two `configVersion` values and return -1/0/1 (a<b / a==b / a>b).
24
- *
25
- * Both the legacy numeric scheme (`1`, `2`, …) and the semver-string scheme
26
- * (`"0.8.0"`, `"0.9.1"`) are accepted. Mixed comparisons promote the numeric
27
- * value to a semver-like string of the form `0.N.0` (so legacy `2` ≈ `"0.2.0"`
28
- * is compared element-wise against the string form). Returns `undefined`
29
- * when either value cannot be parsed at all.
30
- */
31
- export function compareConfigVersion(a, b) {
32
- const partsA = normalizeVersion(a);
33
- const partsB = normalizeVersion(b);
34
- if (!partsA || !partsB)
35
- return undefined;
36
- const len = Math.max(partsA.length, partsB.length);
37
- for (let i = 0; i < len; i++) {
38
- const ai = partsA[i] ?? 0;
39
- const bi = partsB[i] ?? 0;
40
- if (ai < bi)
41
- return -1;
42
- if (ai > bi)
43
- return 1;
44
- }
45
- return 0;
46
- }
47
- function normalizeVersion(v) {
48
- if (typeof v === "number" && Number.isFinite(v)) {
49
- return [0, Math.trunc(v), 0];
50
- }
51
- if (typeof v === "string") {
52
- const trimmed = v.trim();
53
- if (!trimmed)
54
- return undefined;
55
- const cleaned = trimmed.replace(/^v/i, "").split(/[-+]/, 1)[0];
56
- const segments = cleaned.split(".").map((part) => Number.parseInt(part, 10));
57
- if (segments.length === 0 || segments.some((n) => !Number.isFinite(n)))
58
- return undefined;
59
- return segments;
60
- }
61
- return undefined;
62
- }
63
- // ── Helpers for deep-merging into nested locations ──────────────────────────
64
- function getObj(parent, key) {
65
- const existing = parent[key];
66
- if (existing && typeof existing === "object" && !Array.isArray(existing)) {
67
- return existing;
68
- }
69
- const next = {};
70
- parent[key] = next;
71
- return next;
72
- }
73
- /** Get the `profiles.improve.default.processes.<name>` object (creating intermediate nodes). */
74
- function getImproveProcess(result, processName) {
75
- const profiles = getObj(result, "profiles");
76
- const improve = getObj(profiles, "improve");
77
- const defaultProfile = getObj(improve, "default");
78
- const processes = getObj(defaultProfile, "processes");
79
- return getObj(processes, processName);
80
- }
81
- function isObj(v) {
82
- return !!v && typeof v === "object" && !Array.isArray(v);
83
- }
84
- function hasOpenvikingSource(raw) {
85
- const sources = raw.sources;
86
- if (!Array.isArray(sources))
87
- return false;
88
- for (const entry of sources) {
89
- if (isObj(entry) && entry.type === "openviking")
90
- return true;
91
- }
92
- return false;
93
- }
94
- /**
95
- * Convert a snake_case or kebab-case identifier into camelCase. Leaves an
96
- * already-camelCased value untouched. Used by the catch-all branches to
97
- * normalize unknown legacy keys (e.g. `my_custom_process` → `myCustomProcess`).
98
- */
99
- function toCamelCase(key) {
100
- return key.replace(/[-_]([a-zA-Z0-9])/g, (_, ch) => ch.toUpperCase());
101
- }
102
- /**
103
- * Migrate a generic feature-gate-shaped value (boolean OR an object that may
104
- * carry `{ enabled, options }`) into a target object at `result[section][key]`
105
- * (where section is "index" or "search"). Preserves recognized `options.*`
106
- * fields by spreading them onto the target shallowly.
107
- *
108
- * Returns true when the value was understood enough to set anything.
109
- */
110
- function migrateGenericGateToSection(result, section, camelKey, legacy) {
111
- const parent = getObj(result, section);
112
- const target = getObj(parent, camelKey);
113
- if (typeof legacy === "boolean") {
114
- target.enabled = legacy;
115
- return true;
116
- }
117
- if (!isObj(legacy))
118
- return false;
119
- let touched = false;
120
- if (typeof legacy.enabled === "boolean") {
121
- target.enabled = legacy.enabled;
122
- touched = true;
123
- }
124
- if (isObj(legacy.options)) {
125
- // Best-effort: copy primitive option values onto the target so they aren't
126
- // dropped. The exact final shape is unknown for unrecognized keys, so we
127
- // place them under an `options` sub-object to keep the new shape clean.
128
- target.options = { ...(isObj(target.options) ? target.options : {}), ...legacy.options };
129
- touched = true;
130
- }
131
- return touched;
132
- }
133
- /**
134
- * Migrate a single legacy ProcessEntry-like value (boolean or
135
- * { enabled, mode, profile, timeoutMs, options }) into an ImproveProcessConfig
136
- * at the target location. Merges shallowly with anything already present.
137
- */
138
- function migrateProcessEntryToImprove(result, processName, legacy) {
139
- const target = getImproveProcess(result, processName);
140
- if (typeof legacy === "boolean") {
141
- target.enabled = legacy;
142
- return;
143
- }
144
- if (!isObj(legacy))
145
- return;
146
- if (typeof legacy.enabled === "boolean")
147
- target.enabled = legacy.enabled;
148
- if (typeof legacy.mode === "string")
149
- target.mode = legacy.mode;
150
- if (typeof legacy.profile === "string")
151
- target.profile = legacy.profile;
152
- if (legacy.timeoutMs === null || typeof legacy.timeoutMs === "number")
153
- target.timeoutMs = legacy.timeoutMs;
154
- if (isObj(legacy.options)) {
155
- const opts = legacy.options;
156
- // 0.8.0 removed `cooldown` / `cooldownDays` — drop them silently. Reflect
157
- // and distill now use signal-delta eligibility; consolidate uses
158
- // pool-delta. Carrying these forward would just trip the schema gate.
159
- if (Array.isArray(opts.allowedTypes)) {
160
- target.allowedTypes = opts.allowedTypes;
161
- }
162
- }
163
- }
164
- /**
165
- * Determine whether a raw config object needs migration to the 0.8.0 shape and
166
- * apply any necessary field renames or promotions.
167
- *
168
- * A config is considered already migrated when `configVersion === "0.8.0"`
169
- * (canonical string sentinel for this release) or when `configVersion` is a
170
- * number ≥ 2 AND the config carries no recognized legacy keys.
171
- */
172
- export function migrateConfigShape(raw, opts) {
173
- const emitWarn = opts?.warn ?? warn;
174
- const hasLegacyKeys = Object.hasOwn(raw, "features") ||
175
- (isObj(raw.llm) && (Object.hasOwn(raw.llm, "endpoint") || Object.hasOwn(raw.llm, "features"))) ||
176
- isObj(raw.agent) ||
177
- Object.hasOwn(raw, "stashes") ||
178
- typeof raw.semanticSearchMode === "boolean" ||
179
- hasOpenvikingSource(raw);
180
- // Already migrated — string sentinel "0.8.0" with no legacy keys present.
181
- if (raw.configVersion === CURRENT_CONFIG_VERSION && !hasLegacyKeys) {
182
- return { changed: false, result: raw };
183
- }
184
- // Legacy numeric versioning sentinel (>= 2) with no legacy keys present.
185
- if (typeof raw.configVersion === "number" && raw.configVersion >= 2 && !hasLegacyKeys) {
186
- return { changed: false, result: raw };
187
- }
188
- const result = { ...raw };
189
- let changed = false;
190
- // ── 0) Pre-migrations: legacy keys and shape coercions ─────────────────
191
- //
192
- // These run before the deeper feature-block migrations because they
193
- // affect keys the later passes assume are already canonical.
194
- // 0a) Coerce semanticSearchMode boolean → string ("auto" | "off").
195
- if (typeof result.semanticSearchMode === "boolean") {
196
- result.semanticSearchMode = result.semanticSearchMode ? "auto" : "off";
197
- changed = true;
198
- }
199
- // 0b) Rename legacy stashes[] → sources[].
200
- if (Array.isArray(result.stashes)) {
201
- if (!Array.isArray(result.sources)) {
202
- result.sources = result.stashes;
203
- emitWarn("[akm config-migrate] Legacy `stashes[]` config key renamed to `sources[]`. " +
204
- "Re-save your config to remove the deprecation notice.");
205
- }
206
- else {
207
- emitWarn("[akm config-migrate] Both `stashes[]` and `sources[]` present; `stashes[]` dropped (sources takes precedence).");
208
- }
209
- delete result.stashes;
210
- changed = true;
211
- }
212
- // 0c) Rename openviking source type. The old type is gone in 0.8.0; the
213
- // canonical replacement is a website source. Users with non-trivial
214
- // openviking config likely need manual intervention — we warn loudly.
215
- if (Array.isArray(result.sources)) {
216
- const sources = result.sources;
217
- const mapped = [];
218
- let renamed = false;
219
- for (const entry of sources) {
220
- if (isObj(entry) && entry.type === "openviking") {
221
- const name = typeof entry.name === "string" && entry.name ? entry.name : "unnamed";
222
- emitWarn(`[akm config-migrate] Source "${name}" (type: openviking) is no longer supported. ` +
223
- "Remove it from your config, or replace with a `website`/`git` source. " +
224
- "Entry dropped from sources[].");
225
- renamed = true;
226
- continue;
227
- }
228
- mapped.push(entry);
229
- }
230
- if (renamed) {
231
- result.sources = mapped;
232
- changed = true;
233
- }
234
- }
235
- // ── 1) Migrate `llm.features.*` → new homes ────────────────────────────
236
- if (isObj(result.llm)) {
237
- const llm = { ...result.llm };
238
- if (isObj(llm.features)) {
239
- const llmFeatures = llm.features;
240
- const setProcessEnabled = (processName, enabled) => {
241
- if (typeof enabled !== "boolean")
242
- return;
243
- const target = getImproveProcess(result, processName);
244
- target.enabled = enabled;
245
- };
246
- if ("memory_consolidation" in llmFeatures) {
247
- setProcessEnabled("consolidate", llmFeatures.memory_consolidation);
248
- }
249
- if ("feedback_distillation" in llmFeatures) {
250
- // 0.8.0 unified the feedback_distillation gate into processes.distill.enabled.
251
- setProcessEnabled("distill", llmFeatures.feedback_distillation);
252
- }
253
- if ("memory_inference" in llmFeatures) {
254
- setProcessEnabled("memoryInference", llmFeatures.memory_inference);
255
- }
256
- if ("graph_extraction" in llmFeatures) {
257
- setProcessEnabled("graphExtraction", llmFeatures.graph_extraction);
258
- }
259
- if ("metadata_enhance" in llmFeatures) {
260
- const index = getObj(result, "index");
261
- const me = getObj(index, "metadataEnhance");
262
- if (typeof llmFeatures.metadata_enhance === "boolean")
263
- me.enabled = llmFeatures.metadata_enhance;
264
- }
265
- if ("lesson_quality_gate" in llmFeatures) {
266
- const distill = getImproveProcess(result, "distill");
267
- const qg = getObj(distill, "qualityGate");
268
- if (typeof llmFeatures.lesson_quality_gate === "boolean")
269
- qg.enabled = llmFeatures.lesson_quality_gate;
270
- }
271
- if ("proposal_quality_gate" in llmFeatures) {
272
- const reflect = getImproveProcess(result, "reflect");
273
- const qg = getObj(reflect, "qualityGate");
274
- if (typeof llmFeatures.proposal_quality_gate === "boolean")
275
- qg.enabled = llmFeatures.proposal_quality_gate;
276
- }
277
- if ("memory_contradiction_detection" in llmFeatures) {
278
- const consolidate = getImproveProcess(result, "consolidate");
279
- const cd = getObj(consolidate, "contradictionDetection");
280
- if (typeof llmFeatures.memory_contradiction_detection === "boolean") {
281
- cd.enabled = llmFeatures.memory_contradiction_detection;
282
- }
283
- }
284
- delete llm.features;
285
- changed = true;
286
- }
287
- // 2) Migrate top-level `llm` (LlmConnectionConfig) → `profiles.llm.default`
288
- // + `defaults.llm = "default"` if it actually has connection fields.
289
- if (typeof llm.endpoint === "string") {
290
- const profiles = getObj(result, "profiles");
291
- const llmProfiles = getObj(profiles, "llm");
292
- // Don't overwrite if a "default" entry already exists.
293
- if (!isObj(llmProfiles.default)) {
294
- llmProfiles.default = { ...llm };
295
- }
296
- const defaults = getObj(result, "defaults");
297
- if (typeof defaults.llm !== "string")
298
- defaults.llm = "default";
299
- changed = true;
300
- }
301
- // Always strip the legacy `llm` block — its only remaining job was holding
302
- // connection fields and `features`, both migrated.
303
- delete result.llm;
304
- }
305
- // ── 3) Migrate `features.*` (top-level) → new homes ────────────────────
306
- if (isObj(result.features)) {
307
- const features = result.features;
308
- if (isObj(features.improve)) {
309
- const fi = features.improve;
310
- // memory_consolidation (bool or ProcessEntry) → processes.consolidate
311
- if ("memory_consolidation" in fi) {
312
- migrateProcessEntryToImprove(result, "consolidate", fi.memory_consolidation);
313
- }
314
- // feedback_distillation (bool) → processes.distill (0.8.0 unification).
315
- if ("feedback_distillation" in fi) {
316
- migrateProcessEntryToImprove(result, "distill", fi.feedback_distillation);
317
- }
318
- // validation (ProcessEntry) → processes.validation
319
- if ("validation" in fi) {
320
- migrateProcessEntryToImprove(result, "validation", fi.validation);
321
- }
322
- // reflect/distill/consolidate (ProcessEntry) — merged with .options.cooldown → cooldownByType
323
- if ("reflect" in fi)
324
- migrateProcessEntryToImprove(result, "reflect", fi.reflect);
325
- if ("distill" in fi)
326
- migrateProcessEntryToImprove(result, "distill", fi.distill);
327
- if ("consolidate" in fi)
328
- migrateProcessEntryToImprove(result, "consolidate", fi.consolidate);
329
- // Catch-all: any remaining keys are treated as custom processes and
330
- // migrated to profiles.improve.default.processes.<camelKey>. Without
331
- // this branch, user-defined entries would be silently dropped when the
332
- // legacy `features` block is deleted below.
333
- const knownImproveKeys = new Set([
334
- "memory_consolidation",
335
- "feedback_distillation",
336
- "validation",
337
- "reflect",
338
- "distill",
339
- "consolidate",
340
- ]);
341
- for (const [legacyKey, legacyVal] of Object.entries(fi)) {
342
- if (knownImproveKeys.has(legacyKey))
343
- continue;
344
- const camelKey = toCamelCase(legacyKey);
345
- if (typeof legacyVal === "boolean" || isObj(legacyVal)) {
346
- migrateProcessEntryToImprove(result, camelKey, legacyVal);
347
- emitWarn(`[akm config-migrate] Unknown features.improve.${legacyKey} migrated to ` +
348
- `profiles.improve.default.processes.${camelKey}. Please verify the new location.`);
349
- }
350
- else {
351
- emitWarn(`[akm config-migrate] features.improve.${legacyKey} has an unrecognized value type ` +
352
- `(${typeof legacyVal}); dropping. Please re-add it under profiles.improve.* manually.`);
353
- }
354
- }
355
- changed = true;
356
- }
357
- if (isObj(features.index)) {
358
- const findex = features.index;
359
- if ("memory_inference" in findex) {
360
- migrateProcessEntryToImprove(result, "memoryInference", findex.memory_inference);
361
- }
362
- if ("graph_extraction" in findex) {
363
- migrateProcessEntryToImprove(result, "graphExtraction", findex.graph_extraction);
364
- }
365
- if ("metadata_enhance" in findex) {
366
- const index = getObj(result, "index");
367
- const me = getObj(index, "metadataEnhance");
368
- const val = findex.metadata_enhance;
369
- if (typeof val === "boolean")
370
- me.enabled = val;
371
- else if (isObj(val) && typeof val.enabled === "boolean")
372
- me.enabled = val.enabled;
373
- }
374
- // staleness_detection: the pass was retired (meta-review 10-Q3), so the
375
- // legacy features.index.staleness_detection entry is deliberately DROPPED
376
- // during migration ("staleness_detection" stays in knownIndexKeys below so
377
- // the catch-all does not resurrect it under index.stalenessDetection).
378
- // Catch-all: unknown features.index.<key> entries land at
379
- // index.<keyAsCamelCase> (preserving { enabled, options } when present).
380
- const knownIndexKeys = new Set([
381
- "memory_inference",
382
- "graph_extraction",
383
- "metadata_enhance",
384
- "staleness_detection",
385
- ]);
386
- for (const [legacyKey, legacyVal] of Object.entries(findex)) {
387
- if (knownIndexKeys.has(legacyKey))
388
- continue;
389
- const camelKey = toCamelCase(legacyKey);
390
- const ok = migrateGenericGateToSection(result, "index", camelKey, legacyVal);
391
- if (ok) {
392
- emitWarn(`[akm config-migrate] Unknown features.index.${legacyKey} migrated to ` +
393
- `index.${camelKey}. Please verify the new location is correct.`);
394
- }
395
- else {
396
- emitWarn(`[akm config-migrate] features.index.${legacyKey} has an unrecognized value shape; ` +
397
- `dropping. Please re-add it under index.${camelKey} manually if needed.`);
398
- }
399
- }
400
- changed = true;
401
- }
402
- if (isObj(features.search)) {
403
- const fsearch = features.search;
404
- // Catch-all: unknown features.search.<key> entries land at
405
- // search.<keyAsCamelCase> (preserving { enabled, options } when present).
406
- // `curate_rerank` is a removed dead feature — listed here so the catch-all
407
- // skips it and it is simply dropped (not resurrected under search.curateRerank).
408
- const knownSearchKeys = new Set(["curate_rerank"]);
409
- for (const [legacyKey, legacyVal] of Object.entries(fsearch)) {
410
- if (knownSearchKeys.has(legacyKey))
411
- continue;
412
- const camelKey = toCamelCase(legacyKey);
413
- const ok = migrateGenericGateToSection(result, "search", camelKey, legacyVal);
414
- if (ok) {
415
- emitWarn(`[akm config-migrate] Unknown features.search.${legacyKey} migrated to ` +
416
- `search.${camelKey}. Please verify the new location is correct.`);
417
- }
418
- else {
419
- emitWarn(`[akm config-migrate] features.search.${legacyKey} has an unrecognized value shape; ` +
420
- `dropping. Please re-add it under search.${camelKey} manually if needed.`);
421
- }
422
- }
423
- changed = true;
424
- }
425
- delete result.features;
426
- }
427
- // ── 4) Migrate `agent.*` (v1) → `profiles.agent` + `defaults.agent` ──────
428
- if (isObj(result.agent)) {
429
- const agent = result.agent;
430
- // 4a) agent.default → defaults.agent
431
- if (typeof agent.default === "string" && agent.default.trim()) {
432
- const defaults = getObj(result, "defaults");
433
- if (typeof defaults.agent !== "string")
434
- defaults.agent = agent.default.trim();
435
- changed = true;
436
- }
437
- // 4b) agent.profiles → profiles.agent (only entries with valid `platform`)
438
- if (isObj(agent.profiles)) {
439
- const v1Profiles = agent.profiles;
440
- const profiles = getObj(result, "profiles");
441
- const agentProfiles = getObj(profiles, "agent");
442
- for (const [name, raw] of Object.entries(v1Profiles)) {
443
- if (!isObj(raw))
444
- continue;
445
- if (isObj(agentProfiles[name]))
446
- continue; // do not overwrite existing v2 entries
447
- // v1 profiles do not carry a "platform" — synthesize one from name where possible.
448
- const platform = typeof raw.platform === "string" ? raw.platform : guessAgentPlatform(name);
449
- if (!platform)
450
- continue;
451
- const v2 = { platform };
452
- if (typeof raw.bin === "string" && raw.bin.trim())
453
- v2.bin = raw.bin.trim();
454
- if (Array.isArray(raw.args) && raw.args.every((a) => typeof a === "string"))
455
- v2.args = raw.args;
456
- if (typeof raw.model === "string" && raw.model.trim())
457
- v2.model = raw.model.trim();
458
- if (typeof raw.workspace === "string" && raw.workspace.trim())
459
- v2.workspace = raw.workspace.trim();
460
- agentProfiles[name] = v2;
461
- changed = true;
462
- }
463
- }
464
- // 4c) agent.processes.<name> (v1 binding) → profiles.improve.default.processes.<name>.profile
465
- if (isObj(agent.processes)) {
466
- const v1Processes = agent.processes;
467
- for (const [processName, raw] of Object.entries(v1Processes)) {
468
- if (processName === "task")
469
- continue; // legacy v1-only; drop
470
- let profileName;
471
- let timeoutMs;
472
- if (typeof raw === "string" && raw.trim()) {
473
- profileName = raw.trim();
474
- }
475
- else if (isObj(raw)) {
476
- if (typeof raw.profile === "string" && raw.profile.trim())
477
- profileName = raw.profile.trim();
478
- if (raw.timeoutMs === null || typeof raw.timeoutMs === "number") {
479
- timeoutMs = raw.timeoutMs;
480
- }
481
- }
482
- if (!profileName)
483
- continue;
484
- // Map v1 process names to v2 improve process names.
485
- const v2Name = mapV1ProcessName(processName);
486
- if (!v2Name)
487
- continue;
488
- const target = getImproveProcess(result, v2Name);
489
- target.profile = profileName;
490
- target.mode = "agent";
491
- if (timeoutMs !== undefined)
492
- target.timeoutMs = timeoutMs;
493
- changed = true;
494
- }
495
- }
496
- delete result.agent;
497
- }
498
- // ── 5) Migrate `improve.*` (top-level pipeline options) ──────────────────
499
- if (isObj(result.improve)) {
500
- const improve = { ...result.improve };
501
- // 0.8.0 removed reflectCooldownByType — reflect now uses signal-delta
502
- // eligibility. Drop the key silently rather than carrying it into a shape
503
- // the schema rejects.
504
- if (typeof improve.limit === "number") {
505
- const profiles = getObj(result, "profiles");
506
- const improveProfiles = getObj(profiles, "improve");
507
- const defaultProfile = getObj(improveProfiles, "default");
508
- defaultProfile.limit = improve.limit;
509
- changed = true;
510
- }
511
- delete improve.reflectCooldownByType;
512
- delete improve.limit;
513
- delete improve.schedule;
514
- if (Object.keys(improve).length > 0) {
515
- result.improve = improve;
516
- }
517
- else {
518
- delete result.improve;
519
- }
520
- }
521
- // ── 6) Legacy `defaults.improve` object form ({ limit, preset }) ─────────
522
- if (isObj(result.defaults)) {
523
- const defaultsRaw = { ...result.defaults };
524
- const defaultsImprove = defaultsRaw.improve;
525
- if (isObj(defaultsImprove)) {
526
- const improveObj = defaultsImprove;
527
- if (typeof improveObj.limit === "number") {
528
- const profiles = getObj(result, "profiles");
529
- const improveProfiles = getObj(profiles, "improve");
530
- const defaultProfile = getObj(improveProfiles, "default");
531
- if (typeof defaultProfile.limit !== "number")
532
- defaultProfile.limit = improveObj.limit;
533
- changed = true;
534
- }
535
- if (improveObj.preset !== undefined) {
536
- emitWarn("[akm config-migrate] defaults.improve.preset is no longer supported. " +
537
- "Use `--profile <name>` (built-ins: default, quick, thorough, memory-focus) instead.");
538
- }
539
- delete defaultsRaw.improve;
540
- if (Object.keys(defaultsRaw).length > 0) {
541
- result.defaults = defaultsRaw;
542
- }
543
- else {
544
- delete result.defaults;
545
- }
546
- changed = true;
547
- }
548
- }
549
- // Stamp the new version sentinel on any migration that did substantive work.
550
- if (changed) {
551
- result.configVersion = CURRENT_CONFIG_VERSION;
552
- }
553
- return { changed, result };
554
- }
555
- /**
556
- * Guess a v2 agent platform for a known v1 profile name. Returns `undefined`
557
- * for unknown names — the caller drops those entries (they have no usable
558
- * platform).
559
- */
560
- function guessAgentPlatform(name) {
561
- // #566: consult the harness registry instead of re-deriving the heuristic.
562
- // `v1ProfilePlatform` iterates HARNESS_REGISTRY and asks each harness's own
563
- // `matchesV1ProfileName()`, so:
564
- // - 'claude' AND the 'claude-code' alias both resolve to canonical 'claude'
565
- // (the normalization bridge keeps legacy v1 names round-tripping);
566
- // - decorated names like 'opencode-sdk-x' resolve most-specific-first;
567
- // - an unknown harness name returns undefined (caller drops it) instead of
568
- // being silently misclassified. Adding a harness needs no edit here.
569
- return v1ProfilePlatform(name);
570
- }
571
- /**
572
- * Map a v1 process name (e.g. `"reflect"`, `"propose"`) to its v2 improve
573
- * process name. Returns `undefined` for names that have no v2 home (e.g.
574
- * `"task"`, which was removed).
575
- */
576
- function mapV1ProcessName(name) {
577
- switch (name) {
578
- case "reflect":
579
- return "reflect";
580
- case "distill":
581
- return "distill";
582
- case "consolidate":
583
- return "consolidate";
584
- case "propose":
585
- // v1 "propose" mapped to reflect/distill at runtime; bind to reflect by default.
586
- return "reflect";
587
- case "validation":
588
- return "validation";
589
- case "memoryInference":
590
- case "memory_inference":
591
- return "memoryInference";
592
- case "graphExtraction":
593
- case "graph_extraction":
594
- return "graphExtraction";
595
- case "feedbackDistillation":
596
- case "feedback_distillation":
597
- // 0.8.0 unified feedbackDistillation into distill (single source of truth).
598
- return "distill";
599
- default:
600
- return undefined;
601
- }
602
- }
@@ -1,38 +0,0 @@
1
- // This Source Code Form is subject to the terms of the Mozilla Public
2
- // License, v. 2.0. If a copy of the MPL was not distributed with this
3
- // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- /**
5
- * Generic recursive object merge, extracted from the setup wizard (it is not
6
- * setup-specific). Plain objects merge key-by-key; arrays and scalars replace
7
- * wholesale. Used to apply a partial `--file` config over the existing config
8
- * without dropping sibling subkeys.
9
- */
10
- /** True for non-null, non-array plain objects. */
11
- export function isPlainObject(value) {
12
- return typeof value === "object" && value !== null && !Array.isArray(value);
13
- }
14
- /**
15
- * Recursively merge `incoming` into `base`: plain objects merge key-by-key,
16
- * while arrays and scalars replace wholesale. A partial input therefore only
17
- * updates the keys it carries and never drops sibling subkeys (e.g. a file
18
- * containing `{ output: { format: "text" } }` leaves `output.detail` intact).
19
- *
20
- * `base` is treated as immutable — a fresh object graph is returned.
21
- */
22
- export function deepMergeConfig(base, incoming) {
23
- if (!isPlainObject(incoming))
24
- return incoming;
25
- const baseObj = isPlainObject(base) ? base : {};
26
- const out = { ...baseObj };
27
- for (const [key, value] of Object.entries(incoming)) {
28
- if (value === undefined)
29
- continue;
30
- if (isPlainObject(value) && isPlainObject(baseObj[key])) {
31
- out[key] = deepMergeConfig(baseObj[key], value);
32
- }
33
- else {
34
- out[key] = value;
35
- }
36
- }
37
- return out;
38
- }