akm-cli 0.9.0-rc.8 → 0.9.0

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 (450) hide show
  1. package/CHANGELOG.md +1063 -44
  2. package/README.md +51 -25
  3. package/SECURITY.md +14 -1
  4. package/STABILITY.md +497 -0
  5. package/dist/akm +148 -35
  6. package/dist/{akm-migrate-storage → akm-migrate} +6 -9
  7. package/dist/assets/hints/cli-hints-full.md +223 -95
  8. package/dist/assets/hints/cli-hints-short.md +85 -22
  9. package/dist/assets/improve-strategies/default.json +1 -1
  10. package/dist/assets/improve-strategies/reflect-distill.json +1 -1
  11. package/dist/assets/prompts/memory-infer-user.md +2 -3
  12. package/dist/assets/stash-skeleton/README.md +6 -5
  13. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +2 -0
  14. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +2 -0
  15. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +2 -0
  16. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +2 -0
  17. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +2 -0
  18. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +2 -0
  19. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +2 -0
  20. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +2 -0
  21. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +2 -0
  22. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +2 -0
  23. package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -0
  24. package/dist/assets/stash-skeleton/facts/conventions/organization.md +20 -9
  25. package/dist/assets/tasks/core/extract.yml +1 -1
  26. package/dist/assets/tasks/core/version-check.yml +1 -1
  27. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
  28. package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
  29. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
  30. package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
  31. package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
  32. package/dist/assets/templates/html/health.html +1 -3
  33. package/dist/assets/workflows/workflow-template.md +32 -15
  34. package/dist/cli/invocation.js +40 -15
  35. package/dist/cli/parse-args.js +0 -22
  36. package/dist/cli/retired-commands.js +121 -0
  37. package/dist/cli/shared.js +154 -22
  38. package/dist/cli/unknown-flags.js +236 -0
  39. package/dist/cli-node.mjs +2 -1
  40. package/dist/cli.js +696 -258
  41. package/dist/commands/agent/agent-dispatch.js +14 -3
  42. package/dist/commands/agent/contribute-cli.js +73 -88
  43. package/dist/commands/completions.js +79 -22
  44. package/dist/commands/config-cli.js +17 -150
  45. package/dist/commands/env/env-cli.js +59 -143
  46. package/dist/commands/env/env.js +12 -163
  47. package/dist/commands/env/marker-path.js +6 -0
  48. package/dist/commands/env/secret-cli.js +36 -66
  49. package/dist/commands/env/secret.js +24 -57
  50. package/dist/commands/feedback-cli.js +141 -87
  51. package/dist/commands/health/accept-rate.js +58 -0
  52. package/dist/commands/health/advisories.js +3 -4
  53. package/dist/commands/health/checks.js +85 -23
  54. package/dist/commands/health/html-report.js +7 -10
  55. package/dist/commands/health/improve-metrics.js +25 -83
  56. package/dist/commands/health/md-report.js +5 -9
  57. package/dist/commands/health/metrics.js +62 -20
  58. package/dist/commands/health/renderers.js +47 -0
  59. package/dist/commands/health/report-view-model.js +4 -5
  60. package/dist/commands/health/stash-exposure.js +1 -1
  61. package/dist/commands/health/surfaces.js +3 -48
  62. package/dist/commands/health/task-runs.js +3 -67
  63. package/dist/commands/health/types-improve.js +7 -0
  64. package/dist/commands/health.js +99 -28
  65. package/dist/commands/improve/anti-collapse.js +2 -2
  66. package/dist/commands/improve/autonomy-gate.js +68 -0
  67. package/dist/commands/improve/collapse-detector.js +41 -40
  68. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  69. package/dist/commands/improve/consolidate/merge.js +4 -0
  70. package/dist/commands/improve/consolidate.js +140 -1000
  71. package/dist/commands/improve/distill/promote-memory.js +12 -12
  72. package/dist/commands/improve/distill/quality-gate.js +6 -6
  73. package/dist/commands/improve/distill.js +58 -69
  74. package/dist/commands/improve/eligibility.js +105 -57
  75. package/dist/commands/improve/extract-cli.js +14 -133
  76. package/dist/commands/improve/improve-cli.js +98 -114
  77. package/dist/commands/improve/improve-result-file.js +1 -28
  78. package/dist/commands/improve/improve-strategies.js +8 -5
  79. package/dist/commands/improve/improve.js +128 -91
  80. package/dist/commands/improve/loop-stages.js +182 -20
  81. package/dist/commands/improve/memory/derived-ref.js +45 -43
  82. package/dist/commands/improve/memory/memory-belief.js +1 -1
  83. package/dist/commands/improve/memory/memory-contradiction-detect.js +4 -12
  84. package/dist/commands/improve/memory/memory-improve.js +6 -5
  85. package/dist/commands/improve/outcome-loop.js +22 -65
  86. package/dist/commands/improve/preparation.js +114 -123
  87. package/dist/commands/improve/proactive-maintenance.js +2 -5
  88. package/dist/commands/improve/reflect.js +56 -160
  89. package/dist/commands/improve/salience.js +11 -122
  90. package/dist/commands/improve/source-identity.js +10 -38
  91. package/dist/commands/lint/base-linter.js +20 -124
  92. package/dist/commands/lint/env-key-rules.js +31 -47
  93. package/dist/commands/lint/index.js +249 -43
  94. package/dist/commands/{events.js → log.js} +33 -38
  95. package/dist/commands/migrate-cli.js +92 -12
  96. package/dist/commands/migration-tool.js +46 -0
  97. package/dist/commands/observability-cli.js +70 -209
  98. package/dist/commands/proposal/drain.js +101 -29
  99. package/dist/commands/proposal/proposal-cli.js +76 -48
  100. package/dist/commands/proposal/proposal.js +54 -18
  101. package/dist/commands/proposal/propose-cli.js +88 -0
  102. package/dist/commands/proposal/propose.js +23 -15
  103. package/dist/commands/proposal/repository.js +701 -278
  104. package/dist/commands/proposal/validators/proposal-quality-validators.js +2 -8
  105. package/dist/commands/proposal/validators/proposal-validators.js +55 -7
  106. package/dist/commands/proposal/validators/proposals.js +4 -7
  107. package/dist/commands/read/curate.js +34 -53
  108. package/dist/commands/read/knowledge.js +150 -95
  109. package/dist/commands/read/registry-search.js +2 -2
  110. package/dist/commands/read/remember-cli.js +42 -15
  111. package/dist/commands/read/search-cli.js +180 -78
  112. package/dist/commands/read/search.js +58 -43
  113. package/dist/commands/read/show.js +197 -141
  114. package/dist/commands/registry-cli.js +12 -51
  115. package/dist/commands/remember.js +14 -57
  116. package/dist/commands/sources/add-cli.js +100 -31
  117. package/dist/commands/sources/bundle-cli.js +166 -0
  118. package/dist/commands/sources/bundle-config-ops.js +7 -2
  119. package/dist/commands/sources/info.js +18 -5
  120. package/dist/commands/sources/init.js +12 -12
  121. package/dist/commands/sources/installed-stashes.js +382 -98
  122. package/dist/commands/sources/schema-repair.js +3 -2
  123. package/dist/commands/sources/self-update.js +131 -38
  124. package/dist/commands/sources/source-add.js +72 -17
  125. package/dist/commands/sources/source-clone.js +129 -45
  126. package/dist/commands/sources/source-manage.js +43 -23
  127. package/dist/commands/sources/sources-cli.js +57 -208
  128. package/dist/commands/sources/stash-cli.js +46 -53
  129. package/dist/commands/tasks/tasks-cli.js +91 -97
  130. package/dist/commands/tasks/tasks.js +276 -421
  131. package/dist/commands/workflow-cli.js +175 -450
  132. package/dist/core/adapter/adapters/akm-adapter.js +47 -28
  133. package/dist/core/adapter/adapters/akm-lint.js +42 -27
  134. package/dist/core/adapter/adapters/akm-metadata.js +15 -44
  135. package/dist/core/adapter/adapters/akm-task-adapter.js +15 -13
  136. package/dist/core/adapter/adapters/akm-workflow-adapter.js +55 -71
  137. package/dist/core/adapter/adapters/dotenv-adapter.js +1 -1
  138. package/dist/core/adapter/adapters/generic-files-adapter.js +2 -0
  139. package/dist/core/adapter/adapters/index.js +6 -6
  140. package/dist/core/adapter/adapters/llm-wiki-adapter.js +14 -8
  141. package/dist/core/adapter/adapters/okf-adapter.js +187 -19
  142. package/dist/core/adapter/adapters/shared.js +3 -19
  143. package/dist/core/adapter/adapters/tool-dir-shared.js +8 -3
  144. package/dist/core/adapter/adapters/website-snapshot-adapter.js +1 -0
  145. package/dist/core/adapter/detect-adapter.js +17 -0
  146. package/dist/core/adapter/recognize-match.js +6 -4
  147. package/dist/core/adapter/validate-context.js +214 -0
  148. package/dist/core/asset/akm-markdown.js +63 -0
  149. package/dist/core/asset/asset-placement.js +20 -6
  150. package/dist/core/asset/asset-ref.js +11 -9
  151. package/dist/core/asset/frontmatter-lint.js +30 -0
  152. package/dist/core/asset/frontmatter.js +37 -9
  153. package/dist/core/asset/markdown.js +40 -51
  154. package/dist/core/asset/resolve-ref.js +89 -18
  155. package/dist/core/asset/stash-meta.js +1 -1
  156. package/dist/core/bundle-id.js +51 -0
  157. package/dist/core/common.js +152 -38
  158. package/dist/core/config/config-io.js +12 -1
  159. package/dist/core/config/config-schema.js +35 -8
  160. package/dist/core/config/config-sources.js +55 -11
  161. package/dist/core/config/config-walker.js +25 -9
  162. package/dist/core/config/config.js +9 -48
  163. package/dist/core/config/experimental.js +21 -0
  164. package/dist/core/config/schema/embedding.js +5 -1
  165. package/dist/core/config/schema/experimental.js +30 -0
  166. package/dist/core/config/schema/improve-processes.js +0 -6
  167. package/dist/core/config/schema/improve.js +21 -3
  168. package/dist/core/config/schema/index-config.js +8 -15
  169. package/dist/core/config/schema/output.js +4 -1
  170. package/dist/core/config/schema/setup.js +9 -18
  171. package/dist/core/config/schema/sources-bundles.js +49 -33
  172. package/dist/core/config/schema/workflow.js +3 -3
  173. package/dist/core/env-secret-ref.js +76 -46
  174. package/dist/core/errors.js +18 -12
  175. package/dist/core/events.js +46 -128
  176. package/dist/core/file-change.js +6 -5
  177. package/dist/core/fs-txn.js +83 -7
  178. package/dist/core/git-message.js +2 -2
  179. package/dist/core/improve-result.js +1 -100
  180. package/dist/core/lesson-lint.js +1 -17
  181. package/dist/core/logs-db.js +2 -1
  182. package/dist/core/migration-operation.js +16 -0
  183. package/dist/core/mutation-target.js +78 -0
  184. package/dist/core/parse.js +4 -1
  185. package/dist/core/paths.js +17 -20
  186. package/dist/core/recognition-util.js +12 -14
  187. package/dist/core/redaction.js +34 -0
  188. package/dist/core/standards/resolve-standards-context.js +2 -14
  189. package/dist/core/standards/resolve-stash-standards.js +2 -2
  190. package/dist/core/standards/resolve-type-conventions.js +2 -2
  191. package/dist/core/state/migrations.js +41 -18
  192. package/dist/core/state-db.js +5 -14
  193. package/dist/core/structured.js +1 -1
  194. package/dist/core/subprocess.js +6 -4
  195. package/dist/core/text-truncation.js +9 -5
  196. package/dist/core/type-presentation.js +3 -3
  197. package/dist/core/warn.js +0 -3
  198. package/dist/core/write-source.js +771 -95
  199. package/dist/indexer/bundle-identity-guard.js +3 -2
  200. package/dist/indexer/db/graph-db.js +0 -24
  201. package/dist/indexer/ensure-index.js +1 -0
  202. package/dist/indexer/graph/graph-boost.js +9 -34
  203. package/dist/indexer/graph/graph-extraction.js +8 -5
  204. package/dist/indexer/index-writer-lock.js +53 -17
  205. package/dist/indexer/index-written-assets.js +16 -22
  206. package/dist/indexer/indexer.js +497 -239
  207. package/dist/indexer/installations.js +14 -96
  208. package/dist/indexer/passes/dir-staleness.js +16 -9
  209. package/dist/indexer/passes/memory-inference.js +11 -9
  210. package/dist/indexer/passes/metadata.js +113 -47
  211. package/dist/indexer/scan/doc-to-entry.js +38 -1
  212. package/dist/indexer/scan/drain-dir.js +13 -23
  213. package/dist/indexer/search/db-search.js +99 -54
  214. package/dist/indexer/search/fts-query.js +47 -24
  215. package/dist/indexer/search/ranking-contributors.js +42 -20
  216. package/dist/indexer/search/ranking.js +18 -99
  217. package/dist/indexer/search/search-fields.js +7 -2
  218. package/dist/indexer/search/search-source.js +82 -93
  219. package/dist/indexer/usage/usage-events.js +0 -89
  220. package/dist/indexer/walk/file-context.js +2 -1
  221. package/dist/indexer/walk/matchers.js +30 -43
  222. package/dist/indexer/walk/path-resolver.js +7 -2
  223. package/dist/indexer/walk/walker.js +38 -12
  224. package/dist/integrations/agent/builders.js +0 -6
  225. package/dist/integrations/agent/config.js +2 -2
  226. package/dist/integrations/agent/detect.js +49 -19
  227. package/dist/integrations/agent/engine-fallback.js +76 -0
  228. package/dist/integrations/agent/profiles.js +14 -0
  229. package/dist/integrations/agent/prompts.js +12 -8
  230. package/dist/integrations/agent/runner-dispatch.js +4 -2
  231. package/dist/integrations/agent/runner.js +0 -1
  232. package/dist/integrations/agent/spawn.js +5 -6
  233. package/dist/integrations/github.js +1 -1
  234. package/dist/integrations/harnesses/aider/agent-builder.js +6 -4
  235. package/dist/integrations/harnesses/amazonq/agent-builder.js +7 -4
  236. package/dist/integrations/harnesses/claude/session-log.js +0 -10
  237. package/dist/integrations/harnesses/codex/agent-builder.js +5 -2
  238. package/dist/integrations/harnesses/copilot/agent-builder.js +5 -3
  239. package/dist/integrations/harnesses/gemini/agent-builder.js +5 -3
  240. package/dist/integrations/harnesses/index.js +3 -7
  241. package/dist/integrations/harnesses/opencode/agent-builder.js +21 -2
  242. package/dist/integrations/harnesses/opencode/session-log.js +0 -15
  243. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +13 -4
  244. package/dist/integrations/harnesses/openhands/agent-builder.js +9 -6
  245. package/dist/integrations/harnesses/pi/agent-builder.js +6 -4
  246. package/dist/integrations/lockfile.js +101 -6
  247. package/dist/integrations/session-logs/index.js +3 -28
  248. package/dist/llm/client.js +136 -100
  249. package/dist/llm/embedders/remote.js +13 -5
  250. package/dist/llm/feature-gate.js +4 -12
  251. package/dist/llm/graph-extract.js +5 -11
  252. package/dist/llm/memory-infer.js +144 -1
  253. package/dist/llm/metadata-enhance.js +5 -7
  254. package/dist/llm/structured-call.js +1 -1
  255. package/dist/llm/usage-persist.js +26 -5
  256. package/dist/llm/usage-telemetry.js +25 -2
  257. package/dist/output/cli-hints.js +1 -2
  258. package/dist/output/context.js +22 -7
  259. package/dist/output/format-exempt.js +80 -0
  260. package/dist/output/generic-render.js +259 -0
  261. package/dist/output/render-registry.js +57 -0
  262. package/dist/output/renderers.js +14 -36
  263. package/dist/output/shapes/curate.js +10 -1
  264. package/dist/output/shapes/events.js +12 -7
  265. package/dist/output/shapes/helpers.js +56 -83
  266. package/dist/output/shapes/migrate.js +8 -0
  267. package/dist/output/shapes/passthrough.js +7 -41
  268. package/dist/output/shapes/proposal/producer.js +15 -7
  269. package/dist/output/shapes.js +2 -9
  270. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  271. package/dist/output/text/bundle-show.js +7 -0
  272. package/dist/output/text/command-format.js +164 -96
  273. package/dist/output/text/env.js +1 -3
  274. package/dist/output/text/events.js +8 -7
  275. package/dist/output/text/health-format.js +103 -0
  276. package/dist/output/text/health.js +7 -0
  277. package/dist/output/text/helpers.js +10 -8
  278. package/dist/output/text/lint-format.js +43 -0
  279. package/dist/output/text/{save.js → lint.js} +2 -2
  280. package/dist/output/text/migrate.js +88 -0
  281. package/dist/output/text/proposal/producer.js +4 -2
  282. package/dist/output/text/proposal-format.js +44 -72
  283. package/dist/output/text/registry-commands.js +1 -2
  284. package/dist/output/text/show-directives.js +15 -7
  285. package/dist/output/text/status-list.js +32 -0
  286. package/dist/output/text/sync.js +5 -0
  287. package/dist/output/text/workflow-format.js +24 -203
  288. package/dist/output/text/workflow.js +1 -7
  289. package/dist/output/text.js +16 -17
  290. package/dist/registry/factory.js +4 -6
  291. package/dist/registry/origin-resolve.js +16 -27
  292. package/dist/registry/providers/skills-sh.js +3 -3
  293. package/dist/registry/providers/static-index.js +13 -23
  294. package/dist/registry/resolve.js +42 -7
  295. package/dist/registry/semver.js +34 -84
  296. package/dist/runtime.js +2 -23
  297. package/dist/scripts/akm-migrate-node.js +60290 -0
  298. package/dist/scripts/akm-migrate.js +59628 -0
  299. package/dist/setup/detect.js +42 -15
  300. package/dist/setup/registry-stash-loader.js +2 -2
  301. package/dist/setup/setup.js +236 -136
  302. package/dist/setup/steps/connection.js +7 -9
  303. package/dist/setup/steps/platforms.js +9 -9
  304. package/dist/setup/steps/semantic.js +15 -3
  305. package/dist/setup/steps/sources.js +12 -13
  306. package/dist/setup/steps/stashdir.js +2 -3
  307. package/dist/setup/steps/tasks.js +237 -120
  308. package/dist/sources/freshness.js +1 -1
  309. package/dist/sources/provider-factory.js +11 -17
  310. package/dist/sources/providers/filesystem.js +2 -3
  311. package/dist/sources/providers/git-install.js +278 -34
  312. package/dist/sources/providers/git-provider.js +25 -23
  313. package/dist/sources/providers/git-stash.js +395 -106
  314. package/dist/sources/providers/git.js +2 -2
  315. package/dist/sources/providers/npm.js +16 -19
  316. package/dist/sources/providers/provider-utils.js +7 -4
  317. package/dist/sources/providers/sync-from-ref.js +3 -9
  318. package/dist/sources/providers/website.js +6 -1
  319. package/dist/sources/resolve.js +6 -5
  320. package/dist/sources/snapshot-fetchers/bluesky.js +146 -0
  321. package/dist/sources/snapshot-fetchers/content-extract.js +566 -0
  322. package/dist/sources/snapshot-fetchers/fetcher-util.js +41 -0
  323. package/dist/sources/snapshot-fetchers/github.js +100 -0
  324. package/dist/sources/snapshot-fetchers/host-guard.js +291 -0
  325. package/dist/sources/snapshot-fetchers/registry.js +17 -1
  326. package/dist/sources/snapshot-fetchers/robots.js +348 -0
  327. package/dist/sources/snapshot-fetchers/rss.js +282 -0
  328. package/dist/sources/snapshot-fetchers/secret-seam.js +42 -0
  329. package/dist/sources/snapshot-fetchers/website-ingest.js +566 -268
  330. package/dist/sources/snapshot-fetchers/x.js +910 -0
  331. package/dist/storage/database.js +7 -0
  332. package/dist/storage/engines/sqlite-migrations.js +23 -111
  333. package/dist/storage/managed-db.js +2 -2
  334. package/dist/storage/repositories/canaries-repository.js +1 -1
  335. package/dist/storage/repositories/events-repository.js +27 -11
  336. package/dist/storage/repositories/improve-runs-repository.js +6 -12
  337. package/dist/storage/repositories/index-connection.js +17 -6
  338. package/dist/storage/repositories/index-entries-repository.js +151 -240
  339. package/dist/storage/repositories/index-entry-mapper.js +15 -11
  340. package/dist/storage/repositories/index-fts-repository.js +5 -2
  341. package/dist/storage/repositories/index-llm-cache-repository.js +0 -1
  342. package/dist/storage/repositories/index-meta-repository.js +2 -3
  343. package/dist/storage/repositories/index-schema.js +10 -25
  344. package/dist/storage/repositories/index-utility-repository.js +15 -28
  345. package/dist/storage/repositories/index-vec-repository.js +6 -1
  346. package/dist/storage/repositories/outcome-repository.js +119 -0
  347. package/dist/storage/repositories/proposals-repository.js +296 -59
  348. package/dist/storage/repositories/registry-cache.js +19 -0
  349. package/dist/storage/repositories/salience-repository.js +172 -0
  350. package/dist/storage/repositories/task-history-repository.js +15 -13
  351. package/dist/storage/repositories/workflow-runs-repository.js +52 -40
  352. package/dist/tasks/backends/cron.js +105 -15
  353. package/dist/tasks/backends/index.js +1 -1
  354. package/dist/tasks/backends/launchd.js +85 -38
  355. package/dist/tasks/backends/schtasks.js +135 -15
  356. package/dist/tasks/embedded.js +56 -40
  357. package/dist/tasks/parser.js +7 -157
  358. package/dist/tasks/resolve-akm-bin.js +137 -59
  359. package/dist/tasks/runner.js +79 -42
  360. package/dist/tasks/scheduler-invocation.js +220 -10
  361. package/dist/tasks/schema.js +24 -1
  362. package/dist/tasks/task-id.js +1 -3
  363. package/dist/tasks/validator.js +20 -6
  364. package/dist/workflows/authoring/authoring.js +94 -143
  365. package/dist/workflows/authoring/scope-key.js +1 -1
  366. package/dist/workflows/exec/frozen-judge.js +28 -2
  367. package/dist/workflows/exec/native-executor.js +77 -57
  368. package/dist/workflows/exec/param-secrets.js +9 -9
  369. package/dist/workflows/exec/run-workflow.js +133 -79
  370. package/dist/workflows/exec/step-work.js +219 -346
  371. package/dist/{migrate-storage-node.mjs → workflows/exec/unit-dispatch.js} +1 -5
  372. package/dist/workflows/ir/compile.js +141 -270
  373. package/dist/workflows/ir/freeze.js +40 -30
  374. package/dist/workflows/ir/params.js +135 -11
  375. package/dist/workflows/ir/plan-hash.js +1 -1
  376. package/dist/workflows/ir/schema.js +25 -26
  377. package/dist/workflows/parser.js +872 -307
  378. package/dist/workflows/program/expressions.js +20 -208
  379. package/dist/workflows/program/schema.js +7 -10
  380. package/dist/workflows/renderer.js +95 -68
  381. package/dist/workflows/resource-limits.js +2 -0
  382. package/dist/workflows/runtime/checkin.js +3 -3
  383. package/dist/workflows/runtime/plan-classifier.js +16 -75
  384. package/dist/workflows/runtime/runs.js +186 -127
  385. package/dist/workflows/runtime/unit-checkin.js +1 -1
  386. package/dist/workflows/runtime/unit-phases.js +2 -2
  387. package/dist/workflows/runtime/workflow-asset-loader.js +232 -83
  388. package/dist/workflows/schema.js +1 -11
  389. package/dist/workflows/validate-summary.js +30 -36
  390. package/dist/workflows/validator.js +21 -62
  391. package/docs/README.md +68 -0
  392. package/docs/migration/README.md +8 -0
  393. package/docs/migration/release-notes/0.7.0.md +11 -11
  394. package/docs/migration/release-notes/0.9.0.md +208 -27
  395. package/docs/migration/v0.7-to-v0.8.md +46 -47
  396. package/docs/migration/v0.8-to-v0.9.md +564 -208
  397. package/docs/migration/v0.9.0-troubleshooting.md +561 -0
  398. package/docs/reference/README.md +12 -0
  399. package/docs/reference/cli.md +2253 -0
  400. package/docs/reference/configuration.md +358 -0
  401. package/docs/reference/data-and-telemetry.md +105 -42
  402. package/docs/reference/workflows.md +647 -0
  403. package/package.json +22 -11
  404. package/schemas/akm-asset-envelope.json +93 -0
  405. package/schemas/akm-config.json +81 -128
  406. package/schemas/akm-workflow.json +74 -73
  407. package/dist/assets/tasks/core/backup.yml +0 -5
  408. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  409. package/dist/cli/config-migrate.js +0 -1806
  410. package/dist/cli/config-validate.js +0 -41
  411. package/dist/commands/backup-cli.js +0 -56
  412. package/dist/commands/bundle/bundle-cli.js +0 -68
  413. package/dist/commands/bundle/bundle.js +0 -219
  414. package/dist/commands/graph/graph-cli.js +0 -124
  415. package/dist/commands/graph/graph.js +0 -489
  416. package/dist/commands/improve/extract-watch.js +0 -140
  417. package/dist/commands/mv-cli.js +0 -1221
  418. package/dist/commands/sources/history.js +0 -201
  419. package/dist/commands/tasks/default-tasks.js +0 -186
  420. package/dist/core/migration-backup.js +0 -1234
  421. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -95
  422. package/dist/llm/memory-infer-impl.js +0 -138
  423. package/dist/migrate/legacy/config-source-migration.js +0 -223
  424. package/dist/migrate/legacy/content-migration.js +0 -305
  425. package/dist/migrate/legacy/legacy-layout.js +0 -779
  426. package/dist/migrate/legacy/legacy-paths.js +0 -25
  427. package/dist/migrate/legacy/legacy-stash-json.js +0 -72
  428. package/dist/migrate/legacy/proposal-fs-import.js +0 -168
  429. package/dist/migrate/legacy/task-target-ref-migration.js +0 -272
  430. package/dist/migrate/legacy/three-db-cutover.js +0 -841
  431. package/dist/migrate/legacy/workflow-migrations-bodies.js +0 -52
  432. package/dist/migrate/legacy/workflow-migrations-frozen.js +0 -21
  433. package/dist/migrate/legacy-ref-grammar.js +0 -214
  434. package/dist/output/shapes/distill.js +0 -14
  435. package/dist/output/shapes/history.js +0 -11
  436. package/dist/output/text/distill.js +0 -6
  437. package/dist/output/text/enable-disable.js +0 -8
  438. package/dist/output/text/history.js +0 -6
  439. package/dist/registry/build-index.js +0 -382
  440. package/dist/schemas/akm-config.json +0 -4704
  441. package/dist/schemas/akm-task.json +0 -87
  442. package/dist/schemas/akm-workflow.json +0 -372
  443. package/dist/scripts/migrate-storage.js +0 -3816
  444. package/dist/workflows/authoring/workflow-program-template.yaml +0 -31
  445. package/dist/workflows/cli.js +0 -53
  446. package/dist/workflows/exec/brief.js +0 -481
  447. package/dist/workflows/exec/report.js +0 -1460
  448. package/dist/workflows/exec/watch.js +0 -116
  449. package/dist/workflows/program/parser.js +0 -813
  450. package/dist/workflows/program/project.js +0 -104
@@ -14,7 +14,7 @@ function readGithubTokenFromEnv() {
14
14
  return undefined;
15
15
  }
16
16
  function readGithubTokenFromGhCli() {
17
- const result = childProcess.spawnSync("gh", ["auth", "token"], {
17
+ const result = childProcess.spawnSync("gh", ["auth", "token", "--hostname", "github.com"], {
18
18
  encoding: "utf8",
19
19
  timeout: 5_000,
20
20
  stdio: ["ignore", "pipe", "ignore"],
@@ -60,10 +60,12 @@
60
60
  * - **effort** — stays unconsumed (reserved; the shared request contract's
61
61
  * "no builder consumes it yet" note stays true).
62
62
  *
63
- * NOT registered anywhere: `builders.ts` / `harnesses/index.ts` wiring is a
64
- * follow-up integration task (as is the registry entry declaring
65
- * `structuredOutput: "none"`). Exported standalone so that
66
- * task only adds a registry entry.
63
+ * Registered: `aiderBuilder` is `AiderHarness.agentBuilder` (`./index.ts`),
64
+ * one of the ten harnesses `HARNESS_REGISTRY` constructs
65
+ * (`harnesses/index.ts`); `agent/builders.ts` derives `BUILTIN_BUILDERS` from
66
+ * that registry, so this builder is reachable under the `"aider"` platform
67
+ * name without any further wiring. The registry entry also declares
68
+ * `structuredOutput: "none"` alongside it (`./index.ts`).
67
69
  */
68
70
  import { assertNotFlag, resolveDispatchModel, } from "../../agent/builder-shared.js";
69
71
  /** Canonical harness/platform id used for model-alias resolution. */
@@ -54,10 +54,13 @@
54
54
  * - **effort** — stays unconsumed (reserved; the shared request contract's
55
55
  * "no builder consumes it yet" note stays true).
56
56
  *
57
- * NOT registered anywhere: `builders.ts` / `harnesses/index.ts` wiring is a
58
- * follow-up integration task (as is the registry-side capability entry —
59
- * pattern `local-runner`, structuredOutput `none`). Exported standalone so
60
- * that task only adds a registry entry.
57
+ * Registered: `amazonqBuilder` is `AmazonqHarness.agentBuilder`
58
+ * (`./index.ts`), one of the ten harnesses `HARNESS_REGISTRY` constructs
59
+ * (`harnesses/index.ts`); `agent/builders.ts` derives `BUILTIN_BUILDERS` from
60
+ * that registry, so this builder is reachable under the `"amazonq"` platform
61
+ * name without any further wiring. The registry-side capability entry —
62
+ * pattern `local-runner`, structuredOutput `none` — is declared alongside it
63
+ * (`./index.ts`).
61
64
  */
62
65
  import { assertNotFlag, resolveDispatchModel, } from "../../agent/builder-shared.js";
63
66
  /** Canonical harness/platform id used for model-alias resolution. */
@@ -108,16 +108,6 @@ export class ClaudeCodeProvider extends AbstractSessionLogProvider {
108
108
  availabilityRoot() {
109
109
  return claudeProjectsDir();
110
110
  }
111
- /**
112
- * Directory holding Claude Code's per-project session JSONL files
113
- * (`~/.claude/projects`, honoring `AKM_CLAUDE_PROJECTS_DIR`). Returns `[]`
114
- * when the directory does not exist on this machine. See {@link
115
- * SessionLogHarness.watchRoots}.
116
- */
117
- watchRoots() {
118
- const dir = claudeProjectsDir();
119
- return fs.existsSync(dir) ? [dir] : [];
120
- }
121
111
  *readEvents(input) {
122
112
  try {
123
113
  for (const jsonlPath of this.#walkJsonl(claudeProjectsDir())) {
@@ -46,8 +46,11 @@
46
46
  * `-c model_reasoning_effort=<v>` — left to the integration task so the
47
47
  * shared request contract's "no builder consumes it yet" note stays true).
48
48
  *
49
- * NOT registered anywhere yet: `HARNESS_REGISTRY` / `BUILTIN_BUILDERS` wiring
50
- * is a follow-up integration task. Exported cleanly for that task to import.
49
+ * Registered: `codexBuilder` is `CodexHarness.agentBuilder` (`./index.ts`),
50
+ * one of the ten harnesses `HARNESS_REGISTRY` constructs
51
+ * (`harnesses/index.ts`); `agent/builders.ts` derives `BUILTIN_BUILDERS` from
52
+ * that registry, so this builder is reachable under the `"codex"` platform
53
+ * name without any further wiring.
51
54
  */
52
55
  import { mkdtempSync, writeFileSync } from "node:fs";
53
56
  import { tmpdir } from "node:os";
@@ -39,9 +39,11 @@
39
39
  * programmatic mode Copilot then denies unapproved tool calls, which is the
40
40
  * conservative failure mode.
41
41
  *
42
- * NOT registered anywhere: `builders.ts` / `harnesses/index.ts` wiring is a
43
- * follow-up integration task. Exported standalone so that task only adds a
44
- * registry entry.
42
+ * Registered: `copilotBuilder` is `CopilotHarness.agentBuilder`
43
+ * (`./index.ts`), one of the ten harnesses `HARNESS_REGISTRY` constructs
44
+ * (`harnesses/index.ts`); `agent/builders.ts` derives `BUILTIN_BUILDERS` from
45
+ * that registry, so this builder is reachable under the `"copilot"` platform
46
+ * name without any further wiring.
45
47
  */
46
48
  import { assertNotFlag, resolveDispatchModel, } from "../../agent/builder-shared.js";
47
49
  /** Canonical harness/platform id used for model-alias resolution. */
@@ -43,9 +43,11 @@
43
43
  * `gemini -p "<p>"`; autonomy flags (`--yolo`, `--approval-mode`) belong in
44
44
  * `profile.args` where the operator opts in explicitly.
45
45
  *
46
- * NOT registered anywhere: `builders.ts` / `harnesses/index.ts` wiring is a
47
- * follow-up integration task. Exported standalone so that task only adds a
48
- * registry entry.
46
+ * Registered: `geminiBuilder` is `GeminiHarness.agentBuilder` (`./index.ts`),
47
+ * one of the ten harnesses `HARNESS_REGISTRY` constructs
48
+ * (`harnesses/index.ts`); `agent/builders.ts` derives `BUILTIN_BUILDERS` from
49
+ * that registry, so this builder is reachable under the `"gemini"` platform
50
+ * name without any further wiring.
49
51
  */
50
52
  import { assertNotFlag, resolveDispatchModel, } from "../../agent/builder-shared.js";
51
53
  /** Canonical harness/platform id used for model-alias resolution. */
@@ -12,10 +12,6 @@
12
12
  * This module is a dependency-graph LEAF: it imports nothing from
13
13
  * `core/config`. `core/config/config-types.ts` derives `VALID_HARNESS_IDS`
14
14
  * from here, which keeps the import direction acyclic (config ← harnesses).
15
- *
16
- * Implementations (session-log readers, agent profiles) are migrated under each
17
- * harness in #563/#564; this step only owns ids + capability membership and
18
- * wires the existing call sites to consult it (behaviour-preserving).
19
15
  */
20
16
  import { AiderHarness } from "./aider/index.js";
21
17
  import { AmazonqHarness } from "./amazonq/index.js";
@@ -51,9 +47,9 @@ export { isSessionLogHarness } from "./types.js";
51
47
  // Order is significant: VALID_HARNESS_IDS derives from this array and feeds the
52
48
  // committed JSON-schema enum order. The original [opencode, claude,
53
49
  // opencode-sdk] prefix is preserved so the pre-unification portion of the
54
- // generated schema enum does not reorder (#562); the seven P2 harness adapters
55
- // (plan §"Capability matrix") are appended after it, which extends the enum
56
- // additively (schemas/akm-config.json is regenerated in lockstep).
50
+ // generated schema enum does not reorder; the seven P2 harness adapters are
51
+ // appended after it, which extends the enum additively (schemas/akm-config.json
52
+ // is regenerated in lockstep).
57
53
  export const HARNESS_REGISTRY = Object.freeze([
58
54
  new OpencodeHarness(),
59
55
  new ClaudeHarness(),
@@ -27,13 +27,32 @@ export const opencodeBuilder = {
27
27
  build(profile, req) {
28
28
  assertNotFlag(req.systemPrompt, "systemPrompt");
29
29
  assertNotFlag(req.model, "model");
30
- const args = [...profile.args]; // starts with ["run"]
30
+ let configuredModel;
31
+ const args = req.model ? [] : [...profile.args];
32
+ if (req.model) {
33
+ for (let index = 0; index < profile.args.length; index += 1) {
34
+ const arg = profile.args[index];
35
+ if (arg === undefined)
36
+ continue;
37
+ if (arg === "--model") {
38
+ configuredModel = profile.args[index + 1];
39
+ index += 1;
40
+ }
41
+ else if (arg.startsWith("--model=")) {
42
+ configuredModel = arg.slice("--model=".length);
43
+ }
44
+ else {
45
+ args.push(arg);
46
+ }
47
+ }
48
+ }
31
49
  if (req.systemPrompt) {
32
50
  args.push("--system-prompt", req.systemPrompt);
33
51
  }
34
52
  if (req.model) {
35
53
  const resolved = resolveDispatchModel(req, profile, "opencode");
36
- args.push("--model", resolved);
54
+ const provider = configuredModel?.split("/", 1)[0];
55
+ args.push("--model", provider && !resolved.includes("/") ? `${provider}/${resolved}` : resolved);
37
56
  }
38
57
  args.push("--");
39
58
  args.push(req.prompt);
@@ -42,21 +42,6 @@ export class OpenCodeProvider extends AbstractSessionLogProvider {
42
42
  #dbPath(base) {
43
43
  return path.join(base, OPENCODE_DB_FILENAME);
44
44
  }
45
- /**
46
- * Directories/files opencode writes session data under. Returns the base dir
47
- * when the SQLite store (`opencode.db`) exists, the legacy JSON session root
48
- * (`<base>/storage/session`) when present, or both during a migration overlap.
49
- * Empty when neither exists. See {@link SessionLogHarness.watchRoots}.
50
- */
51
- watchRoots() {
52
- const roots = [];
53
- if (fs.existsSync(this.#dbPath(this.#baseDir)))
54
- roots.push(this.#baseDir);
55
- const sessionRoot = path.join(this.#baseDir, "storage", "session");
56
- if (fs.existsSync(sessionRoot))
57
- roots.push(sessionRoot);
58
- return roots;
59
- }
60
45
  *readEvents(input) {
61
46
  // Legacy behavior: stream raw log lines from the top-level dir and `log/`
62
47
  // subdirectory. Kept to keep `getExecutionLogCandidates` working without
@@ -4,9 +4,13 @@
4
4
  /**
5
5
  * OpenCode SDK agent runner (migrated from `agent/sdk-runner.ts`, #564).
6
6
  *
7
- * Uses the embedded `@opencode-ai/sdk` instead of `Bun.spawn`. Requires no
8
- * agent CLI binary to be installed. The user provides an OpenAI-compatible
9
- * endpoint (or inherits from the selected fallback LLM engine) for the SDK.
7
+ * Uses the embedded `@opencode-ai/sdk` instead of `Bun.spawn` for dispatch, so
8
+ * no agent CLI argv is ever constructed — but the SDK is not self-contained:
9
+ * {@link createManagedOpencode} spawns `opencode serve` and talks HTTP to it,
10
+ * so the `opencode` binary must be on PATH (which is why setup's
11
+ * `detectHarness` gates this harness on that probe). The user provides an
12
+ * OpenAI-compatible endpoint (or inherits from the selected fallback LLM
13
+ * engine) for the SDK.
10
14
  *
11
15
  * This is the runtime surface of the {@link OpencodeSdkHarness} (`id =
12
16
  * 'opencode-sdk'`). It is the dispatch path for SDK runner specs; it exposes
@@ -70,7 +74,12 @@
70
74
  * until the child ACTUALLY exits — and a real `opencode serve` (a live HTTP
71
75
  * server with provider children) can outlive SIGTERM long enough to hang the
72
76
  * caller indefinitely. {@link createManagedOpencode} therefore owns the spawn
73
- * (the SDK package is used only for `createOpencodeClient`):
77
+ * (the SDK package is used only for `createOpencodeClient`).
78
+ *
79
+ * Owning the spawn changes the LIFECYCLE, not the REQUIREMENT: the SDK's
80
+ * `createOpencodeServer` is itself a `spawn("opencode", ["serve", ...])`, and
81
+ * the package declares `"dependencies": {}`, so `opencode` must be on PATH
82
+ * either way. akm's version differs only in how it supervises the child:
74
83
  *
75
84
  * - after the URL handshake, the child and its stdio are `unref()`ed /
76
85
  * destroyed, so the handle can never hold akm open;
@@ -63,10 +63,13 @@
63
63
  * - **effort** — stays unconsumed (reserved; the shared request contract's
64
64
  * "no builder consumes it yet" note stays true).
65
65
  *
66
- * NOT registered anywhere: `builders.ts` / `harnesses/index.ts` wiring is a
67
- * follow-up integration task (as is the registry entry declaring
68
- * `pattern: "local-runner"`, `structuredOutput: "native-json"`).
69
- * Exported standalone so that task only adds a registry entry.
66
+ * Registered: `openhandsBuilder` is `OpenhandsHarness.agentBuilder`
67
+ * (`./index.ts`), one of the ten harnesses `HARNESS_REGISTRY` constructs
68
+ * (`harnesses/index.ts`); `agent/builders.ts` derives `BUILTIN_BUILDERS` from
69
+ * that registry, so this builder is reachable under the `"openhands"`
70
+ * platform name without any further wiring. The registry entry also declares
71
+ * `pattern: "local-runner"`, `structuredOutput: "native-json"` alongside it
72
+ * (`./index.ts`).
70
73
  */
71
74
  import { assertNotFlag, resolveDispatchModel, } from "../../agent/builder-shared.js";
72
75
  /** Canonical harness/platform id used for model-alias resolution. */
@@ -74,8 +77,8 @@ export const OPENHANDS_PLATFORM = "openhands";
74
77
  /**
75
78
  * Env var OpenHands reads its LLM model from — the platform's documented
76
79
  * model-selection channel for headless runs (there is no headless model
77
- * flag). Exported so the follow-up integration task and tests share the one
78
- * constant.
80
+ * flag). Exported (re-exported from `./index.ts`) so this harness's tests
81
+ * share the one constant.
79
82
  */
80
83
  export const OPENHANDS_MODEL_ENV = "LLM_MODEL";
81
84
  /**
@@ -43,10 +43,12 @@
43
43
  * - **effort** — stays unconsumed (reserved; the shared request contract's
44
44
  * "no builder consumes it yet" note stays true).
45
45
  *
46
- * NOT registered anywhere: `builders.ts` / `harnesses/index.ts` wiring is a
47
- * follow-up integration task (as are the `PI_*` identity-env markers, which
48
- * are registry-side). Exported standalone so that task only adds a registry
49
- * entry.
46
+ * Registered: `piBuilder` is `PiHarness.agentBuilder` (`./index.ts`), one of
47
+ * the ten harnesses `HARNESS_REGISTRY` constructs (`harnesses/index.ts`);
48
+ * `agent/builders.ts` derives `BUILTIN_BUILDERS` from that registry, so this
49
+ * builder is reachable under the `"pi"` platform name without any further
50
+ * wiring. The `PI_SESSION_ID` identity-env marker is declared alongside it
51
+ * (`./index.ts`).
50
52
  */
51
53
  import { assertNotFlag, resolveDispatchModel, } from "../../agent/builder-shared.js";
52
54
  /** Canonical harness/platform id used for model-alias resolution. */
@@ -54,6 +54,48 @@ export function readLockfile() {
54
54
  return [];
55
55
  }
56
56
  }
57
+ /**
58
+ * Like {@link readLockfile}, but THROWS instead of silently degrading to `[]`
59
+ * when the on-disk lockfile exists yet is not parseable JSON or not a JSON
60
+ * array (R-012).
61
+ *
62
+ * `readLockfile`'s fail-open contract is intentional for READ paths — a
63
+ * corrupt lock degrades a managed bundle to "unmanaged" rather than erroring
64
+ * every read-only command (`list`, `installed-stashes`, …). But
65
+ * {@link upsertLockEntry} and {@link removeLockEntry} read the current
66
+ * entries and then WRITE `[...entries, change]` back out; if that read
67
+ * silently returned `[]` for a corrupt file, the write would silently
68
+ * replace the corrupt file with one containing only the single new/changed
69
+ * entry — permanently destroying every other surviving lock record. Write
70
+ * paths use this strict variant so a corrupt lockfile fails the operation
71
+ * loudly (matching the guard `mergeLockEntriesSync` already applies) instead
72
+ * of quietly deleting user state. A missing file is NOT corruption — there
73
+ * is nothing to preserve, so that case still returns `[]`. Entries that fail
74
+ * per-entry validation are still tolerated (filtered out), matching
75
+ * `readLockfile`'s existing shape-tolerant behavior.
76
+ */
77
+ function readLockfileOrThrow() {
78
+ const lockfilePath = getLockfilePath();
79
+ let raw;
80
+ try {
81
+ raw = fs.readFileSync(lockfilePath, "utf8");
82
+ }
83
+ catch (err) {
84
+ rethrowIfTestIsolationError(err);
85
+ return []; // File does not exist (or is otherwise unreadable) — nothing to preserve.
86
+ }
87
+ let parsed;
88
+ try {
89
+ parsed = JSON.parse(raw);
90
+ }
91
+ catch (error) {
92
+ throw new ConfigError(`Refusing to modify lockfile ${lockfilePath}: existing content is not valid JSON (${error instanceof Error ? error.message : String(error)}). Fix or remove the file by hand before retrying — every existing lock entry would otherwise be lost.`, "INVALID_CONFIG_FILE");
93
+ }
94
+ if (!Array.isArray(parsed)) {
95
+ throw new ConfigError(`Refusing to modify lockfile ${lockfilePath}: existing content is not a JSON array. Fix or remove the file by hand before retrying — every existing lock entry would otherwise be lost.`, "INVALID_CONFIG_FILE");
96
+ }
97
+ return parsed.filter(isValidLockfileEntry);
98
+ }
57
99
  /**
58
100
  * The materialized content root recorded in the lock for a managed (git/npm)
59
101
  * bundle — spec §10.2 desired/resolved split, where the desired config carries
@@ -93,10 +135,27 @@ export async function writeLockfile(entries) {
93
135
  release();
94
136
  }
95
137
  }
138
+ /** Replace one exact parsed lock generation while the lockfile sentinel is held. */
139
+ export async function compareAndSwapLockfile(expected, desired) {
140
+ const release = await acquireLockSentinel();
141
+ try {
142
+ const current = readLockfileOrThrow();
143
+ if (JSON.stringify(current) !== JSON.stringify(expected))
144
+ return false;
145
+ writeLockfileUnlocked(desired);
146
+ return true;
147
+ }
148
+ finally {
149
+ release();
150
+ }
151
+ }
96
152
  export async function upsertLockEntry(entry) {
97
153
  const release = await acquireLockSentinel();
98
154
  try {
99
- const entries = readLockfile();
155
+ // R-012: readLockfileOrThrow (not readLockfile) — a corrupt/malformed
156
+ // lockfile must abort the upsert loudly rather than read as `[]` and get
157
+ // silently overwritten with just this one entry.
158
+ const entries = readLockfileOrThrow();
100
159
  const withoutExisting = entries.filter((e) => e.id !== entry.id);
101
160
  writeLockfileUnlocked([...withoutExisting, entry]);
102
161
  }
@@ -110,19 +169,55 @@ export async function upsertLockEntry(entry) {
110
169
  * migrate-apply's config lock + maintenance barrier) whose synchronous body
111
170
  * cannot await the sentinel's retry loop. No-op for an empty list.
112
171
  */
172
+ function readLockEntriesForMigration() {
173
+ let existing = [];
174
+ const lockfilePath = getLockfilePath();
175
+ if (fs.existsSync(lockfilePath)) {
176
+ let raw;
177
+ try {
178
+ raw = JSON.parse(fs.readFileSync(lockfilePath, "utf8"));
179
+ }
180
+ catch (error) {
181
+ throw new ConfigError(`Cannot merge migration lock entries into unreadable lockfile ${lockfilePath}: ${error instanceof Error ? error.message : String(error)}.`, "INVALID_CONFIG_FILE");
182
+ }
183
+ if (!Array.isArray(raw) || !raw.every(isValidLockfileEntry)) {
184
+ throw new ConfigError(`Cannot merge migration lock entries into malformed lockfile ${lockfilePath}.`, "INVALID_CONFIG_FILE");
185
+ }
186
+ existing = raw;
187
+ }
188
+ return existing;
189
+ }
190
+ /** Validate the current lockfile before migrate-apply creates its backup or sentinel. */
191
+ export function assertMigrationLockfileReadable() {
192
+ readLockEntriesForMigration();
193
+ }
113
194
  export function mergeLockEntriesSync(entries) {
195
+ const existing = readLockEntriesForMigration();
114
196
  if (entries.length === 0)
115
197
  return;
116
- const existing = readLockfile();
117
- const incoming = new Set(entries.map((e) => e.id));
118
- writeLockfileUnlocked([...existing.filter((e) => !incoming.has(e.id)), ...entries]);
198
+ // MERGE per id, never replace: the migrator's entries are sparse (id/source/
199
+ // ref/localRoot), while an existing row may carry `resolvedVersion`,
200
+ // `resolvedRevision`, `integrity`, `installedAt`, … from a real install.
201
+ // Replacing the whole row discarded the user's recorded resolution/pin and
202
+ // left later update reporting comparing against an unknown prior version.
203
+ // Incoming DEFINED fields win; existing fields absent from the incoming
204
+ // entry are preserved.
205
+ const byId = new Map(existing.map((e) => [e.id, e]));
206
+ const merged = entries.map((incoming) => {
207
+ const prior = byId.get(incoming.id);
208
+ return prior ? { ...prior, ...incoming } : incoming;
209
+ });
210
+ const incomingIds = new Set(entries.map((e) => e.id));
211
+ writeLockfileUnlocked([...existing.filter((e) => !incomingIds.has(e.id)), ...merged]);
119
212
  }
120
213
  export async function removeLockEntry(id) {
121
214
  if (!fs.existsSync(getDataDir()))
122
215
  return;
123
216
  const release = await acquireLockSentinel();
124
217
  try {
125
- const entries = readLockfile();
218
+ // R-012: see upsertLockEntry — same destructive-overwrite risk on a
219
+ // corrupt lockfile, so the same strict reader is used here.
220
+ const entries = readLockfileOrThrow();
126
221
  writeLockfileUnlocked(entries.filter((e) => e.id !== id));
127
222
  }
128
223
  finally {
@@ -130,7 +225,7 @@ export async function removeLockEntry(id) {
130
225
  }
131
226
  }
132
227
  // ── Helpers ─────────────────────────────────────────────────────────────────
133
- function isValidLockfileEntry(value) {
228
+ export function isValidLockfileEntry(value) {
134
229
  if (typeof value !== "object" || value === null || Array.isArray(value))
135
230
  return false;
136
231
  const obj = value;
@@ -50,22 +50,6 @@ const ERROR_PATTERNS = /error|failed|exception|cannot|undefined|null pointer|ENO
50
50
  export function getAvailableHarnesses() {
51
51
  return HARNESSES.filter((harness) => harness.isAvailable());
52
52
  }
53
- /**
54
- * Map each available harness to its `{ harnessName, roots }` watch target,
55
- * skipping harnesses that expose no roots (absent `watchRoots()` or an empty
56
- * result). This is the one stable entry point the watcher uses so it never
57
- * reaches into providers directly.
58
- */
59
- export function getWatchTargets() {
60
- const targets = [];
61
- for (const harness of getAvailableHarnesses()) {
62
- const roots = harness.watchRoots?.() ?? [];
63
- if (roots.length === 0)
64
- continue;
65
- targets.push({ harnessName: harness.name, roots });
66
- }
67
- return targets;
68
- }
69
53
  export function normalizeSessionTopic(text) {
70
54
  const normalized = text.replace(/\s+/g, " ").trim().toLowerCase();
71
55
  if (normalized.length < 10)
@@ -107,16 +91,13 @@ export function aggregateSessionEvents(events) {
107
91
  * Collect normalized session events from a set of harnesses for the health
108
92
  * candidate scan (#568).
109
93
  *
110
- * Pipeline selection per harness (capability-gated):
111
- * - readSession-capable harness (`supportsReadSession !== false`): drive the
112
- * richer `listSessions()` + `readSession()` pipeline. `readSession` flattens
94
+ * Pipeline selection per harness:
95
+ * - Drive the richer `listSessions()` + `readSession()` pipeline. `readSession` flattens
113
96
  * structured content — tool calls, assistant content blocks, thinking,
114
97
  * tool_result — into event text (e.g. ClaudeCodeProvider's `parseClaudeEvent`
115
98
  * surfaces `[tool:*]` / `[tool_result]` blocks that the legacy flat
116
99
  * `readEvents` scan drops entirely). This is what lets health advisories see
117
100
  * repeated tool failures / long runs that the flat scan hid.
118
- * - legacy-only harness (`supportsReadSession === false`): fall back to the
119
- * legacy flat `readEvents()` scan (behaviour-preserving).
120
101
  *
121
102
  * Extracted as a pure function (harnesses injected) so it is unit-testable
122
103
  * without touching the real on-disk session-log locations.
@@ -129,19 +110,13 @@ export function aggregateSessionEvents(events) {
129
110
  * health command (`akm health`, which calls this synchronously) blow past its
130
111
  * latency budget. `listSessions()` returns summaries sorted newest-first, so
131
112
  * capping to the most-recent N sessions per harness keeps the richer signal
132
- * for what actually matters (recent activity) while bounding cost. The legacy
133
- * flat-scan path is naturally cheaper and is left uncapped.
113
+ * for what actually matters (recent activity) while bounding cost.
134
114
  */
135
115
  const DEFAULT_MAX_SESSIONS_PER_HARNESS = 50;
136
116
  export function collectSessionEvents(harnesses, sinceMs, maxSessionsPerHarness = DEFAULT_MAX_SESSIONS_PER_HARNESS) {
137
117
  const events = [];
138
118
  for (const harness of harnesses) {
139
119
  try {
140
- if (harness.supportsReadSession === false) {
141
- // Legacy-only harness: only the flat event scan is available.
142
- events.push(...harness.readEvents({ sinceMs }));
143
- continue;
144
- }
145
120
  // Rich path: enumerate sessions cheaply, then read each one's full
146
121
  // structured event stream. Falls back to readEvents if listSessions
147
122
  // surfaces nothing (e.g. a harness that wired readSession but whose