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
@@ -17,7 +17,7 @@ import { UsageError } from "../../core/errors.js";
17
17
  import { resolveTriageJudgmentRunner } from "../../integrations/agent/runner.js";
18
18
  import { installLlmUsagePersistenceIfAbsent } from "../../llm/usage-persist.js";
19
19
  import { withLlmStage } from "../../llm/usage-telemetry.js";
20
- import { resolveImproveProfile } from "../improve/improve-profiles.js";
20
+ import { resolveImproveStrategy } from "../improve/improve-strategies.js";
21
21
  import { drainProposals } from "./drain.js";
22
22
  import { resolveDrainPolicy } from "./drain-policies.js";
23
23
  import { akmProposalAccept, akmProposalDiff, akmProposalList, akmProposalReject, akmProposalRevert, akmProposalShow, } from "./proposal.js";
@@ -353,17 +353,21 @@ const proposalDrainCommand = defineJsonCommand({
353
353
  description: "Opt into the judgment tier (llm by default; agent/sdk per config) for deferred items. No-op with a logged triage_deferred summary when no runner is configured.",
354
354
  default: false,
355
355
  },
356
- profile: {
356
+ strategy: {
357
357
  type: "string",
358
- description: "Read the triage block (policy, applyMode, ceilings, judgment) from this improve profile.",
358
+ description: "Read the triage block (policy, applyMode, ceilings, judgment) from this improve strategy.",
359
359
  },
360
360
  },
361
- async run({ args }) {
361
+ async run({ args, rawArgs }) {
362
+ if (rawArgs.some((arg) => arg === "--profile" || arg.startsWith("--profile="))) {
363
+ throw new UsageError("proposal drain: --profile is retired; use --strategy.", "INVALID_FLAG_VALUE");
364
+ }
362
365
  const stashDir = resolveStashDir();
363
366
  const cfg = loadConfig();
364
- // Phase 2: read the triage block from the named improve profile. CLI flags
367
+ // Phase 2: read the triage block from the named improve strategy. CLI flags
365
368
  // always override config; config supplies defaults for any flag omitted.
366
- const triageConfig = args.profile !== undefined ? resolveImproveProfile(args.profile, cfg).processes?.triage : undefined;
369
+ const selectedStrategy = resolveImproveStrategy(args.strategy, cfg);
370
+ const triageConfig = selectedStrategy.config.processes?.triage;
367
371
  const policy = resolveDrainPolicy(args.policy ?? triageConfig?.policy);
368
372
  const dryRun = args["dry-run"] === true;
369
373
  const applyMode = args.promote === true ? "promote" : (triageConfig?.applyMode ?? "queue");
@@ -404,10 +408,12 @@ const proposalDrainCommand = defineJsonCommand({
404
408
  }
405
409
  // Phase 3: resolve the judgment runner when --judgment is set. Default
406
410
  // mode is llm; falls back to defaults.llm when the triage block sets
407
- // neither mode nor profile (mirrors resolveValidationRunner). null when
411
+ // no explicit engine selection. null when
408
412
  // nothing is configured → the engine leaves deferred items unresolved and
409
413
  // emits triage_deferred.
410
- const judgment = args.judgment === true ? resolveTriageJudgmentRunner(triageConfig?.judgment, cfg) : null;
414
+ const judgment = args.judgment === true
415
+ ? resolveTriageJudgmentRunner(triageConfig?.judgment, cfg, triageConfig, selectedStrategy.config)
416
+ : null;
411
417
  // #576: persist + attribute per-call LLM usage for the standalone drain
412
418
  // path. `IfAbsent` keeps an enclosing `akm improve` sink in charge when
413
419
  // drain runs as a sub-step; the disposer clears only a sink we installed.
@@ -434,10 +440,14 @@ const proposalDrainCommand = defineJsonCommand({
434
440
  policy: policy.name,
435
441
  applyMode,
436
442
  dryRun,
443
+ strategy: selectedStrategy.name,
444
+ judgmentEngine: judgment?.engine ?? null,
445
+ judgmentKind: judgment?.kind ?? null,
437
446
  promoted: result.promoted,
438
447
  rejected: result.rejected,
439
448
  deferred: result.deferred,
440
449
  skippedByCap: result.skippedByCap,
450
+ staged: result.staged,
441
451
  });
442
452
  },
443
453
  });
@@ -18,18 +18,20 @@ import { TYPE_DIRS } from "../../core/asset/asset-spec.js";
18
18
  import { resolveStashDir } from "../../core/common.js";
19
19
  import { ConfigError, UsageError } from "../../core/errors.js";
20
20
  import { appendEvent } from "../../core/events.js";
21
+ import { redactSensitiveText } from "../../core/redaction.js";
21
22
  import { resolveStandardsContext } from "../../core/standards/resolve-standards-context.js";
22
- import { runAgent, } from "../../integrations/agent/index.js";
23
- import { resolveProcessAgentProfile } from "../../integrations/agent/config.js";
23
+ import { resolveEngine } from "../../integrations/agent/engine-resolution.js";
24
24
  import { buildProposePrompt, parseAgentProposalPayload } from "../../integrations/agent/prompts.js";
25
- import { runOpencodeSdk } from "../../integrations/harnesses/opencode-sdk/index.js";
26
- import { baseFailureFields, enoentHintMessage, isEnoentFailure, loadAgentConfigFromDisk, resolveAgentProfile, } from "../agent/agent-support.js";
25
+ import { collectDispatchSensitiveValues, executeRunner } from "../../integrations/agent/runner-dispatch.js";
26
+ import { baseFailureFields, enoentHintMessage, isEnoentFailure } from "../agent/agent-support.js";
27
27
  import { createProposal, isProposalSkipped, } from "./repository.js";
28
- function failureEnvelope(result, type, name, fallbackReason = "non_zero_exit") {
28
+ function failureEnvelope(result, type, name, engine, fallbackReason = "non_zero_exit") {
29
29
  return {
30
30
  ...baseFailureFields(result, fallbackReason),
31
+ schemaVersion: 2,
31
32
  type,
32
33
  name,
34
+ engine,
33
35
  };
34
36
  }
35
37
  export async function akmPropose(options) {
@@ -54,41 +56,17 @@ export async function akmPropose(options) {
54
56
  type: options.type,
55
57
  name: options.name,
56
58
  task: options.task,
57
- ...(options.profile ? { profile: options.profile } : {}),
59
+ ...(options.engine ? { engine: options.engine } : {}),
58
60
  },
59
61
  });
60
- // 2. Resolve profile.
61
- // When an explicit --profile flag is given, honour it directly (existing
62
- // behaviour). Otherwise use resolveProcessAgentProfile so that per-process
63
- // agent config (agent.processes["propose"]) is picked up automatically.
64
- let profile;
65
- let resolvedTimeoutMs = options.timeoutMs;
66
- try {
67
- if (options.agentProfile) {
68
- // Test seam: injected profile bypasses all config.
69
- profile = options.agentProfile;
70
- }
71
- else if (options.profile) {
72
- // Explicit --profile flag wins over process config.
73
- profile = resolveAgentProfile(options);
74
- }
75
- else {
76
- // Use per-process config resolution (falls back to agent.default).
77
- const agent = options.agentConfig ?? loadAgentConfigFromDisk();
78
- const processName = options.agentProcess ?? "propose";
79
- const resolved = resolveProcessAgentProfile(processName, agent);
80
- profile = resolved.profile;
81
- // Only apply process-resolved timeoutMs when caller didn't supply one.
82
- if (resolvedTimeoutMs === undefined) {
83
- resolvedTimeoutMs = resolved.timeoutMs;
84
- }
85
- }
86
- }
87
- catch (err) {
88
- if (err instanceof ConfigError || err instanceof UsageError)
89
- throw err;
90
- throw err;
91
- }
62
+ // 2. Resolve the selected engine exactly once. Propose accepts either kind;
63
+ // the LLM arm uses the caller-specific plain-chat handler below.
64
+ const config = options.agentConfig ?? (await import("../../core/config/config.js")).loadConfig();
65
+ const engineName = options.engine ?? config.defaults?.engine;
66
+ if (!engineName)
67
+ throw new ConfigError("propose requires --engine or defaults.engine.", "INVALID_CONFIG_FILE");
68
+ const runner = resolveEngine(engineName, config);
69
+ const profile = runner.kind === "llm" ? undefined : runner.profile;
92
70
  // 3. Build prompt.
93
71
  // Synthesize a temp draft path so opencode can write the asset content
94
72
  // directly using its file tools rather than returning JSON via stdout.
@@ -104,39 +82,51 @@ export async function akmPropose(options) {
104
82
  ...(standardsContext.trim() ? { standardsContext } : {}),
105
83
  draftFilePath: resolvedDraftPath,
106
84
  });
107
- // 4. Spawn the agent.
85
+ // 4. Dispatch the selected engine.
108
86
  // Real agent runs use interactive mode so file tools can write the draft.
109
87
  // Injected/custom spawns still need captured stdout for JSON payload tests.
110
- // Use callAi for the unified AI dispatch path (agent CLI preferred, LLM HTTP fallback).
88
+ // All kinds cross the unified RunnerSpec dispatch boundary.
111
89
  const useCustomSpawn = Boolean(options.runAgentOptions?.spawn);
112
90
  let result;
113
- if (useCustomSpawn) {
114
- // Test seam: use raw runAgent with injected spawn so tests remain deterministic.
115
- const runOptions = {
116
- stdio: "captured",
117
- parseOutput: "text",
118
- ...(resolvedTimeoutMs !== undefined ? { timeoutMs: resolvedTimeoutMs } : {}),
119
- ...(options.runAgentOptions ?? {}),
120
- };
121
- result = await runAgent(profile, prompt, runOptions);
122
- }
123
- else {
124
- // Production path: dispatch directly to the appropriate runner.
125
- const runOptions = {
126
- stdio: resolvedDraftPath ? "interactive" : "captured",
127
- parseOutput: "text",
128
- ...(resolvedTimeoutMs !== undefined ? { timeoutMs: resolvedTimeoutMs } : {}),
129
- };
130
- result = profile.sdkMode
131
- ? await runOpencodeSdk(profile, prompt ?? "", runOptions)
132
- : await runAgent(profile, prompt, runOptions);
133
- }
91
+ const runOptions = {
92
+ stdio: useCustomSpawn ? "captured" : "interactive",
93
+ parseOutput: "text",
94
+ ...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),
95
+ ...(options.runAgentOptions ?? {}),
96
+ };
97
+ const sensitiveValues = collectDispatchSensitiveValues(runner, runOptions);
98
+ result = await executeRunner(runner, prompt, runOptions, {
99
+ llm: async (spec, llmPrompt, opts) => {
100
+ const { chatCompletion } = await import("../../llm/client.js");
101
+ const started = Date.now();
102
+ try {
103
+ const stdout = await chatCompletion(spec.connection, [{ role: "user", content: llmPrompt }], {
104
+ ...(Object.hasOwn(opts, "timeoutMs") ? { timeoutMs: opts.timeoutMs } : {}),
105
+ });
106
+ return { ok: true, exitCode: 0, stdout, stderr: "", durationMs: Date.now() - started };
107
+ }
108
+ catch (error) {
109
+ return {
110
+ ok: false,
111
+ exitCode: null,
112
+ stdout: "",
113
+ stderr: "",
114
+ durationMs: Date.now() - started,
115
+ error: String(error),
116
+ reason: "spawn_failed",
117
+ };
118
+ }
119
+ },
120
+ });
134
121
  if (!result.ok) {
135
122
  // B3: ENOENT / not-found gives an actionable hint.
136
123
  if (isEnoentFailure(result)) {
137
- return { ...failureEnvelope(result, options.type, options.name), error: enoentHintMessage(profile.bin) };
124
+ return {
125
+ ...failureEnvelope(result, options.type, options.name, engineName),
126
+ error: enoentHintMessage(profile?.bin ?? engineName),
127
+ };
138
128
  }
139
- return failureEnvelope(result, options.type, options.name);
129
+ return failureEnvelope(result, options.type, options.name, engineName);
140
130
  }
141
131
  // 5. Resolve the proposal content.
142
132
  // Path A: opencode wrote the draft file — read it directly (no stdout parse).
@@ -156,12 +146,13 @@ export async function akmPropose(options) {
156
146
  const stdioWasInteractive = !useCustomSpawn;
157
147
  if (stdioWasInteractive && (result.stdout ?? "") === "") {
158
148
  return {
159
- schemaVersion: 1,
149
+ schemaVersion: 2,
160
150
  ok: false,
161
151
  reason: "parse_error",
162
152
  error: "Agent did not write draft file and stdout was not captured (interactive mode). Check that the agent CLI understood the file-write instruction, or configure a headless profile with stdio: 'captured'.",
163
153
  type: options.type,
164
154
  name: options.name,
155
+ engine: engineName,
165
156
  exitCode: result.exitCode,
166
157
  ...(result.stderr ? { stderr: result.stderr } : {}),
167
158
  };
@@ -171,18 +162,20 @@ export async function akmPropose(options) {
171
162
  }
172
163
  catch (err) {
173
164
  return {
174
- schemaVersion: 1,
165
+ schemaVersion: 2,
175
166
  ok: false,
176
167
  reason: "parse_error",
177
168
  error: err instanceof Error ? err.message : String(err),
178
169
  type: options.type,
179
170
  name: options.name,
171
+ engine: engineName,
180
172
  exitCode: result.exitCode,
181
173
  stdout: result.stdout,
182
174
  ...(result.stderr ? { stderr: result.stderr } : {}),
183
175
  };
184
176
  }
185
177
  }
178
+ payload = { ...payload, content: redactSensitiveText(payload.content, sensitiveValues) };
186
179
  // 6. Insert the proposal. Note: we allow the agent's `ref` to normalise the
187
180
  // asset name (e.g. path-cleanup), but only after validating that the ref is
188
181
  // well-formed and the type still matches the requested type.
@@ -195,12 +188,13 @@ export async function akmPropose(options) {
195
188
  }
196
189
  catch (err) {
197
190
  return {
198
- schemaVersion: 1,
191
+ schemaVersion: 2,
199
192
  ok: false,
200
193
  reason: "parse_error",
201
194
  error: err instanceof Error ? err.message : String(err),
202
195
  type: options.type,
203
196
  name: options.name,
197
+ engine: engineName,
204
198
  exitCode: result.exitCode,
205
199
  stdout: result.stdout,
206
200
  ...(result.stderr ? { stderr: result.stderr } : {}),
@@ -208,12 +202,13 @@ export async function akmPropose(options) {
208
202
  }
209
203
  if (parsedRef.type !== options.type) {
210
204
  return {
211
- schemaVersion: 1,
205
+ schemaVersion: 2,
212
206
  ok: false,
213
207
  reason: "parse_error",
214
208
  error: `Agent returned ref type ${parsedRef.type} but expected ${options.type}`,
215
209
  type: options.type,
216
210
  name: options.name,
211
+ engine: engineName,
217
212
  exitCode: result.exitCode,
218
213
  stdout: result.stdout,
219
214
  ...(result.stderr ? { stderr: result.stderr } : {}),
@@ -241,11 +236,11 @@ export async function akmPropose(options) {
241
236
  }
242
237
  const proposal = proposalResult;
243
238
  return {
244
- schemaVersion: 1,
239
+ schemaVersion: 2,
245
240
  ok: true,
246
241
  proposal,
247
242
  ref: proposal.ref,
248
- agentProfile: profile.name,
243
+ engine: engineName,
249
244
  durationMs: result.durationMs,
250
245
  };
251
246
  }