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
@@ -8,16 +8,24 @@ import { output, runWithJsonErrors } from "../../cli/shared.js";
8
8
  import { loadConfig } from "../../core/config/config.js";
9
9
  import { UsageError } from "../../core/errors.js";
10
10
  import { getCacheDir } from "../../core/paths.js";
11
+ import { redactSensitiveText } from "../../core/redaction.js";
11
12
  import { withStateDb } from "../../core/state-db.js";
12
13
  import { clearLogFile, setLogFile } from "../../core/warn.js";
13
14
  import { closeDatabase, openExistingDatabase } from "../../indexer/db/db.js";
14
15
  import { resolveSourceEntries } from "../../indexer/search/search-source.js";
16
+ import { collectEngineCredentialValues } from "../../integrations/agent/engine-resolution.js";
15
17
  import { parseFlagValue } from "../../output/context.js";
16
18
  import { getActiveCanaries, queryRecentCycleMetrics } from "../../storage/repositories/canaries-repository.js";
17
19
  import { refreshCanarySet } from "./collapse-detector.js";
18
20
  import { akmImprove } from "./improve.js";
19
21
  import { buildImproveRunId, recordTerminatedImproveRun, relativeImproveResultPath, writeImproveResultFile, } from "./improve-result-file.js";
20
22
  import { runImproveSession } from "./improve-session.js";
23
+ import { resolveImprovePlan } from "./improve-strategies.js";
24
+ let akmImproveForRun = akmImprove;
25
+ /** Swap the CLI's improve work implementation in deterministic subprocess tests. */
26
+ export function _setAkmImproveForTests(fake) {
27
+ akmImproveForRun = fake ?? akmImprove;
28
+ }
21
29
  // R5 — collapse-detector canary set inspection / explicit refresh. The
22
30
  // detector NEVER auto-refreshes the canary set (silent re-baselining is how a
23
31
  // slow collapse hides); this verb is the only refresh path.
@@ -72,7 +80,7 @@ async function runCanaryInspection(refresh) {
72
80
  export const improveCommand = defineCommand({
73
81
  meta: {
74
82
  name: "improve",
75
- description: "Analyze existing AKM assets and generate improvement proposals; also consolidates memories when profiles.improve.default.processes.consolidate.enabled is true. `akm improve canary [--refresh]` inspects the collapse-detector canary set.",
83
+ description: "Analyze existing AKM assets and generate improvement proposals; also consolidates memories when the selected strategy enables consolidate. `akm improve canary [--refresh]` inspects the collapse-detector canary set.",
76
84
  },
77
85
  args: {
78
86
  scope: {
@@ -120,9 +128,9 @@ export const improveCommand = defineCommand({
120
128
  description: "If another improve run already holds the lock, skip gracefully (exit 0) instead of failing with 'already running' (exit 78). Use for high-frequency scheduled runs so they don't pile up failures while a longer run is in progress.",
121
129
  default: false,
122
130
  },
123
- profile: {
131
+ strategy: {
124
132
  type: "string",
125
- description: "Named improve profile from profiles.improve or built-in profiles (default, quick, thorough, memory-focus, graph-refresh). Controls which sub-processes run and which asset types are processed.",
133
+ description: "Named improve strategy from improve.strategies or built-in strategies (default, quick, thorough, memory-focus, graph-refresh). Controls which sub-processes run and which asset types are processed.",
126
134
  },
127
135
  sync: {
128
136
  type: "boolean",
@@ -166,7 +174,13 @@ export const improveCommand = defineCommand({
166
174
  const minRetrievalCount = parseNonNegativeIntFlag(minRetrievalCountRaw, "--min-retrieval-count");
167
175
  const requireFeedbackSignal = args["require-feedback-signal"];
168
176
  const skipIfLocked = args["skip-if-locked"];
169
- const profileArg = getStringArg(args, "profile");
177
+ const strategyArg = getStringArg(args, "strategy");
178
+ const effectiveConfig = loadConfig();
179
+ // Resolve every enabled model-backed process before logging, signal
180
+ // lifecycle setup, or any filesystem/database side effect.
181
+ const resolvedPlan = resolveImprovePlan(strategyArg, effectiveConfig);
182
+ const selectedStrategyName = resolvedPlan.strategy.name;
183
+ const sensitiveValues = collectEngineCredentialValues(effectiveConfig);
170
184
  // Only set the keys the user actually passed (citty leaves the flag
171
185
  // undefined unless `--sync`/`--no-sync` / `--push`/`--no-push` appears),
172
186
  // so the resolved profile `sync` block wins by default.
@@ -177,8 +191,10 @@ export const improveCommand = defineCommand({
177
191
  syncOverride.enabled = syncFlag;
178
192
  if (pushFlag !== undefined)
179
193
  syncOverride.push = pushFlag;
180
- const improveLogFile = path.join(getCacheDir(), "logs", "improve", `${new Date().toISOString().replace(/[:.]/g, "-")}.log`);
181
- setLogFile(improveLogFile);
194
+ if (!dryRun) {
195
+ const improveLogFile = path.join(getCacheDir(), "logs", "improve", `${new Date().toISOString().replace(/[:.]/g, "-")}.log`);
196
+ setLogFile(improveLogFile);
197
+ }
182
198
  const startedAtMs = Date.now();
183
199
  const startedAtIso = new Date(startedAtMs).toISOString();
184
200
  // Mint the run-id up front so signal handlers can persist a partial
@@ -186,7 +202,7 @@ export const improveCommand = defineCommand({
186
202
  // was minted at end-of-run, so SIGTERM'd runs (cron timeout) left no
187
203
  // row in improve_runs and effectively disappeared from `akm health`.
188
204
  const runId = buildImproveRunId();
189
- const primaryStashDir = resolveSourceEntries(undefined, loadConfig())[0]?.path;
205
+ const primaryStashDir = resolveSourceEntries(undefined, effectiveConfig)[0]?.path;
190
206
  const scopeArg = getStringArg(args, "scope");
191
207
  const inferredScopeMode = (scopeArg ?? "").includes(":") ? "ref" : scopeArg ? "type" : "all";
192
208
  // Signal handler + exception path both flow through this helper so
@@ -194,6 +210,8 @@ export const improveCommand = defineCommand({
194
210
  // reason in metadata.terminated.
195
211
  let runRecorded = false;
196
212
  const persistTerminated = (reason, errorMessage) => {
213
+ if (dryRun)
214
+ return;
197
215
  if (runRecorded)
198
216
  return;
199
217
  if (!primaryStashDir)
@@ -204,8 +222,9 @@ export const improveCommand = defineCommand({
204
222
  scopeMode: inferredScopeMode,
205
223
  scopeValue: scopeArg ?? null,
206
224
  dryRun: Boolean(dryRun),
207
- profile: profileArg ?? null,
208
- ...(errorMessage ? { errorMessage } : {}),
225
+ strategy: selectedStrategyName,
226
+ ...(errorMessage ? { errorMessage: redactSensitiveText(errorMessage, sensitiveValues) } : {}),
227
+ sensitiveValues,
209
228
  });
210
229
  }
211
230
  catch (err) {
@@ -223,10 +242,11 @@ export const improveCommand = defineCommand({
223
242
  let improveResult;
224
243
  try {
225
244
  improveResult = await runImproveSession({
226
- runWork: () => akmImprove({
245
+ runWork: () => akmImproveForRun({
227
246
  scope: scopeArg,
228
247
  task: taskArg,
229
248
  dryRun,
249
+ resolvedPlan,
230
250
  target: targetArg,
231
251
  autoAccept,
232
252
  ...(runId !== undefined ? { runId } : {}),
@@ -235,7 +255,7 @@ export const improveCommand = defineCommand({
235
255
  ...(minRetrievalCount !== undefined ? { minRetrievalCount } : {}),
236
256
  ...(requireFeedbackSignal ? { requireFeedbackSignal } : {}),
237
257
  ...(skipIfLocked ? { skipIfLocked } : {}),
238
- ...(profileArg !== undefined ? { profile: profileArg } : {}),
258
+ ...(strategyArg !== undefined ? { strategy: strategyArg } : {}),
239
259
  ...(Object.keys(syncOverride).length > 0 ? { sync: syncOverride } : {}),
240
260
  consolidateOptions: {
241
261
  target: targetArg,
@@ -249,7 +269,9 @@ export const improveCommand = defineCommand({
249
269
  signalSource: process,
250
270
  exit: process.exit,
251
271
  onTerminate: (reason) => persistTerminated(reason),
252
- ack: (message) => process.stderr.write(`[improve] ${message}; recorded terminated run ${runId}\n`),
272
+ ack: (message) => process.stderr.write(dryRun
273
+ ? `[improve] ${message}; dry-run state was not persisted\n`
274
+ : `[improve] ${message}; recorded terminated run ${runId}\n`),
253
275
  });
254
276
  }
255
277
  catch (err) {
@@ -264,9 +286,9 @@ export const improveCommand = defineCommand({
264
286
  clearLogFile();
265
287
  }
266
288
  const durationMs = Date.now() - startedAtMs;
267
- if (jsonToStdout) {
268
- // Legacy / escape-hatch mode: full JSON on stdout, no file write.
269
- // Kept for scripts/agents that already pipe to jq.
289
+ if (dryRun || jsonToStdout) {
290
+ // A dry-run never persists its result, so stdout is its only result
291
+ // channel. --json-to-stdout remains the live-run escape hatch.
270
292
  output("improve", improveResult);
271
293
  process.exit(0);
272
294
  }
@@ -288,7 +310,7 @@ export const improveCommand = defineCommand({
288
310
  runRecorded = true; // Suppress any late signal-handler write — the success path owns the row now.
289
311
  if (primaryStashDir) {
290
312
  try {
291
- writeImproveResultFile(primaryStashDir, runId, improveResult, startedAtIso, profileArg ?? null);
313
+ writeImproveResultFile(primaryStashDir, runId, improveResult, startedAtIso, sensitiveValues);
292
314
  }
293
315
  catch (err) {
294
316
  // Stderr warning on the failure path is preferable to crashing
@@ -27,6 +27,8 @@
27
27
  */
28
28
  import crypto from "node:crypto";
29
29
  import path from "node:path";
30
+ import { decodeImproveResult } from "../../core/improve-result.js";
31
+ import { redactSensitiveValue } from "../../core/redaction.js";
30
32
  import { withStateDb } from "../../core/state-db.js";
31
33
  import { recordImproveRun } from "../../storage/repositories/improve-runs-repository.js";
32
34
  /**
@@ -74,14 +76,10 @@ export function relativeImproveResultPath(runId) {
74
76
  * (closes the dry-run/real-run artifact-trap recorded in MEMORY.md
75
77
  * `feedback_akm_dryrun_artifact_trap`).
76
78
  *
77
- * @param profile - The `--profile` value passed to this invocation (e.g.
78
- * `quick`, `reflect-distill`), or `null`/`undefined` when no profile was
79
- * given. Mirrors {@link recordTerminatedImproveRun}'s `ctx.profile`
80
- * convention so successful and terminated runs are equally queryable by
81
- * profile. Previously hardcoded to `null` here, which meant only
82
- * abnormally-terminated runs recorded their profile in state.db.
83
79
  */
84
- export function writeImproveResultFile(stashDir, runId, result, startedAt, profile) {
80
+ export function writeImproveResultFile(stashDir, runId, result, startedAt, sensitiveValues = []) {
81
+ const decoded = decodeImproveResult(result);
82
+ const persistedResult = redactSensitiveValue(result, sensitiveValues);
85
83
  withStateDb((db) => {
86
84
  const completedAt = new Date().toISOString();
87
85
  // startedAt is the ISO timestamp captured at process launch (passed from the
@@ -95,12 +93,13 @@ export function writeImproveResultFile(stashDir, runId, result, startedAt, profi
95
93
  completedAt,
96
94
  stashDir,
97
95
  dryRun: Boolean(result.dryRun),
98
- profile: profile ?? null,
96
+ legacyProfile: redactSensitiveValue(decoded.legacyProfile, sensitiveValues),
97
+ strategy: redactSensitiveValue(decoded.strategy, sensitiveValues),
99
98
  scopeMode: result.scope?.mode ?? "all",
100
- scopeValue: result.scope?.value ?? null,
101
- guidance: result.guidance ?? null,
99
+ scopeValue: persistedResult.scope?.value ?? null,
100
+ guidance: persistedResult.guidance ?? null,
102
101
  ok: Boolean(result.ok),
103
- result,
102
+ result: persistedResult,
104
103
  });
105
104
  });
106
105
  return relativeImproveResultPath(runId);
@@ -124,37 +123,44 @@ export function writeImproveResultFile(stashDir, runId, result, startedAt, profi
124
123
  */
125
124
  export function recordTerminatedImproveRun(stashDir, runId, startedAt, reason, ctx) {
126
125
  const completedAt = new Date().toISOString();
127
- const minimalResult = {
128
- schemaVersion: 1,
126
+ const persistedReason = redactSensitiveValue(reason, ctx.sensitiveValues ?? []);
127
+ const persistedScopeValue = redactSensitiveValue(ctx.scopeValue, ctx.sensitiveValues ?? []);
128
+ const persistedStrategy = redactSensitiveValue(ctx.strategy, ctx.sensitiveValues ?? []);
129
+ const minimalResult = redactSensitiveValue({
130
+ schemaVersion: 2,
129
131
  ok: false,
130
- scope: { mode: ctx?.scopeMode ?? "all", ...(ctx?.scopeValue ? { value: ctx.scopeValue } : {}) },
131
- dryRun: Boolean(ctx?.dryRun),
132
+ strategy: persistedStrategy,
133
+ scope: { mode: ctx.scopeMode ?? "all", ...(persistedScopeValue ? { value: persistedScopeValue } : {}) },
134
+ dryRun: Boolean(ctx.dryRun),
135
+ memorySummary: { eligible: 0, derived: 0 },
132
136
  actions: [],
133
137
  plannedRefs: [],
134
138
  terminated: {
135
- reason,
139
+ reason: persistedReason,
136
140
  at: completedAt,
137
- ...(ctx?.errorMessage ? { errorMessage: ctx.errorMessage } : {}),
141
+ ...(ctx.errorMessage ? { errorMessage: ctx.errorMessage } : {}),
138
142
  },
139
- };
143
+ }, ctx.sensitiveValues ?? []);
140
144
  withStateDb((db) => {
141
145
  recordImproveRun(db, {
142
146
  id: runId,
143
147
  startedAt,
144
148
  completedAt,
145
149
  stashDir,
146
- dryRun: Boolean(ctx?.dryRun),
147
- profile: ctx?.profile ?? null,
148
- scopeMode: ctx?.scopeMode ?? "all",
149
- scopeValue: ctx?.scopeValue ?? null,
150
+ dryRun: Boolean(ctx.dryRun),
151
+ strategy: persistedStrategy,
152
+ scopeMode: ctx.scopeMode ?? "all",
153
+ scopeValue: persistedScopeValue ?? null,
150
154
  guidance: null,
151
155
  ok: false,
152
156
  result: minimalResult,
153
157
  metadata: {
154
158
  terminated: {
155
- reason,
159
+ reason: persistedReason,
156
160
  at: completedAt,
157
- ...(ctx?.errorMessage ? { errorMessage: ctx.errorMessage } : {}),
161
+ ...(ctx.errorMessage
162
+ ? { errorMessage: redactSensitiveValue(ctx.errorMessage, ctx.sensitiveValues ?? []) }
163
+ : {}),
158
164
  },
159
165
  },
160
166
  });
@@ -0,0 +1,137 @@
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
+ import catchup from "../../assets/improve-strategies/catchup.json" with { type: "json" };
5
+ import consolidate from "../../assets/improve-strategies/consolidate.json" with { type: "json" };
6
+ import defaultStrategy from "../../assets/improve-strategies/default.json" with { type: "json" };
7
+ import frequent from "../../assets/improve-strategies/frequent.json" with { type: "json" };
8
+ import graphRefresh from "../../assets/improve-strategies/graph-refresh.json" with { type: "json" };
9
+ import memoryFocus from "../../assets/improve-strategies/memory-focus.json" with { type: "json" };
10
+ import proactiveMaintenance from "../../assets/improve-strategies/proactive-maintenance.json" with { type: "json" };
11
+ import quick from "../../assets/improve-strategies/quick.json" with { type: "json" };
12
+ import recombineOnly from "../../assets/improve-strategies/recombine-only.json" with { type: "json" };
13
+ import reflectDistill from "../../assets/improve-strategies/reflect-distill.json" with { type: "json" };
14
+ import synthesize from "../../assets/improve-strategies/synthesize.json" with { type: "json" };
15
+ import thorough from "../../assets/improve-strategies/thorough.json" with { type: "json" };
16
+ import { parseAssetRef } from "../../core/asset/asset-ref.js";
17
+ import { deepMergeConfig } from "../../core/config/deep-merge.js";
18
+ import { BUILTIN_IMPROVE_STRATEGY_NAMES, IMPROVE_PROCESS_ENGINE_CAPABILITIES, } from "../../core/config/engine-semantics.js";
19
+ import { ConfigError } from "../../core/errors.js";
20
+ import { resolveImproveProcessRunner, resolveTriageJudgmentRunner, } from "../../integrations/agent/runner.js";
21
+ export const DEFAULT_ALLOWED_TYPES = {
22
+ reflect: ["agent", "command", "knowledge", "lesson", "memory", "skill", "wiki", "workflow"],
23
+ distill: ["memory"],
24
+ consolidate: ["memory"],
25
+ };
26
+ /** Resolve process enablement from the selected strategy, the sole improve authority. */
27
+ export function resolveProcessEnabled(processName, strategy) {
28
+ const processes = strategy.processes;
29
+ return processes?.[processName]?.enabled === true;
30
+ }
31
+ export function shouldSkipRef(ref, processName, strategy) {
32
+ const process = strategy.processes?.[processName];
33
+ if (process?.enabled === false)
34
+ return { skip: true, reason: "process-disabled" };
35
+ const parsed = parseAssetRef(ref);
36
+ const allowed = process?.allowedTypes ?? DEFAULT_ALLOWED_TYPES[processName];
37
+ if (!allowed.includes(parsed.type))
38
+ return { skip: true, reason: "type-filter" };
39
+ if (parsed.type === "wiki" && parsed.name.split("/")[1] === "raw")
40
+ return { skip: true, reason: "raw-wiki" };
41
+ return { skip: false, reason: "" };
42
+ }
43
+ export function isStrategyFilteredForAllPasses(ref, strategy) {
44
+ return shouldSkipRef(ref, "reflect", strategy).skip && shouldSkipRef(ref, "distill", strategy).skip;
45
+ }
46
+ const BUILTIN_STRATEGIES = {
47
+ default: defaultStrategy,
48
+ quick: quick,
49
+ thorough: thorough,
50
+ "memory-focus": memoryFocus,
51
+ "graph-refresh": graphRefresh,
52
+ frequent: frequent,
53
+ consolidate: consolidate,
54
+ catchup: catchup,
55
+ synthesize: synthesize,
56
+ "reflect-distill": reflectDistill,
57
+ "proactive-maintenance": proactiveMaintenance,
58
+ "recombine-only": recombineOnly,
59
+ };
60
+ if (BUILTIN_IMPROVE_STRATEGY_NAMES.some((name) => !(name in BUILTIN_STRATEGIES))) {
61
+ throw new Error("Built-in improve strategy names are out of sync with their assets");
62
+ }
63
+ export function resolveImproveStrategy(name, config) {
64
+ const selectedName = name ?? config.defaults?.improveStrategy ?? "default";
65
+ const userStrategies = config.improve?.strategies ?? {};
66
+ if (!(selectedName in BUILTIN_STRATEGIES) && !userStrategies[selectedName]) {
67
+ const valid = [...new Set([...Object.keys(BUILTIN_STRATEGIES), ...Object.keys(userStrategies)])].sort();
68
+ throw new ConfigError(`Improve strategy "${selectedName}" not found. Valid strategies: ${valid.join(", ")}.`, "UNKNOWN_IMPROVE_STRATEGY");
69
+ }
70
+ const baseStrategy = BUILTIN_STRATEGIES[selectedName] ?? BUILTIN_STRATEGIES.default;
71
+ const resolved = deepMergeConfig(baseStrategy, (userStrategies[selectedName] ?? {}));
72
+ return { name: selectedName, config: resolved };
73
+ }
74
+ export function resolveStrategyProcessEnabled(strategy, processName) {
75
+ return resolveProcessEnabled(processName, strategy.config);
76
+ }
77
+ function cloneAndFreeze(value) {
78
+ const clone = structuredClone(value);
79
+ const freeze = (item) => {
80
+ if (typeof item !== "object" || item === null || Object.isFrozen(item))
81
+ return;
82
+ for (const child of Object.values(item))
83
+ freeze(child);
84
+ Object.freeze(item);
85
+ };
86
+ freeze(clone);
87
+ return clone;
88
+ }
89
+ /** Resolve every enabled process structurally before improve emits signals or performs I/O. */
90
+ export function resolveImprovePlan(name, config, options = {}) {
91
+ const strategy = resolveImproveStrategy(name, config);
92
+ return buildImprovePlan(strategy, config, options);
93
+ }
94
+ function buildImprovePlan(strategy, config, options) {
95
+ const processes = {};
96
+ for (const processName of Object.keys(IMPROVE_PROCESS_ENGINE_CAPABILITIES)) {
97
+ const processConfig = cloneAndFreeze(strategy.config.processes?.[processName] ?? {});
98
+ const enabled = processConfig.enabled === true;
99
+ let runner = null;
100
+ if (IMPROVE_PROCESS_ENGINE_CAPABILITIES[processName] !== "llm" || !enabled) {
101
+ processes[processName] = Object.freeze({ enabled, config: processConfig, runner });
102
+ continue;
103
+ }
104
+ // Validation itself is structural and always runs. Only its optional repair
105
+ // step needs a model, so disabling repair must not create an LLM preflight.
106
+ if (processName !== "validation" || options.repairValidationFailures !== false) {
107
+ runner = resolveImproveProcessRunner(strategy.config, processName, config);
108
+ }
109
+ if (!runner && !(processName === "validation" && options.repairValidationFailures === false)) {
110
+ throw new ConfigError(`Enabled improve process "${processName}" requires an LLM engine. Set defaults.llmEngine or improve.strategies.${strategy.name}.processes.${processName}.engine.`, "LLM_NOT_CONFIGURED");
111
+ }
112
+ if (runner)
113
+ runner = cloneAndFreeze(runner);
114
+ processes[processName] = Object.freeze({ enabled, config: processConfig, runner });
115
+ }
116
+ const triage = strategy.config.processes?.triage;
117
+ const judgmentOptedIn = triage !== undefined && Object.hasOwn(triage, "judgment");
118
+ const triageJudgment = processes.triage.enabled && judgmentOptedIn
119
+ ? resolveTriageJudgmentRunner(triage.judgment, config, triage, strategy.config)
120
+ : null;
121
+ if (processes.triage.enabled && judgmentOptedIn && !triageJudgment) {
122
+ throw new ConfigError(`Enabled improve triage judgment requires an engine. Set defaults.llmEngine or improve.strategies.${strategy.name}.processes.triage.judgment.engine.`, "LLM_NOT_CONFIGURED");
123
+ }
124
+ const frozenProcesses = Object.freeze(processes);
125
+ const frozenStrategy = Object.freeze({
126
+ name: strategy.name,
127
+ config: cloneAndFreeze({
128
+ ...strategy.config,
129
+ processes: Object.fromEntries(Object.entries(frozenProcesses).map(([name, process]) => [name, process.config])),
130
+ }),
131
+ });
132
+ return Object.freeze({
133
+ strategy: frozenStrategy,
134
+ processes: frozenProcesses,
135
+ triageJudgment: triageJudgment ? cloneAndFreeze(triageJudgment) : null,
136
+ });
137
+ }