akm-cli 0.9.0-rc.1 → 0.9.0-rc.13

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 (587) hide show
  1. package/CHANGELOG.md +1190 -52
  2. package/README.md +62 -37
  3. package/SECURITY.md +46 -31
  4. package/dist/akm +162 -38
  5. package/dist/akm-migrate +44 -0
  6. package/dist/assets/backends/schtasks-template.xml +2 -1
  7. package/dist/assets/hints/cli-hints-full.md +268 -118
  8. package/dist/assets/hints/cli-hints-short.md +87 -24
  9. package/dist/assets/{profiles → improve-strategies}/catchup.json +3 -1
  10. package/dist/assets/{profiles → improve-strategies}/consolidate.json +3 -1
  11. package/dist/assets/{profiles → improve-strategies}/default.json +6 -7
  12. package/dist/assets/improve-strategies/frequent.json +15 -0
  13. package/dist/assets/{profiles → improve-strategies}/graph-refresh.json +4 -2
  14. package/dist/assets/{profiles → improve-strategies}/memory-focus.json +4 -1
  15. package/dist/assets/{profiles → improve-strategies}/proactive-maintenance.json +5 -5
  16. package/dist/assets/{profiles → improve-strategies}/quick.json +4 -2
  17. package/dist/assets/improve-strategies/reflect-distill.json +30 -0
  18. package/dist/assets/{profiles → improve-strategies}/thorough.json +1 -1
  19. package/dist/assets/prompts/consolidate-system.md +5 -5
  20. package/dist/assets/prompts/extract-session.md +2 -6
  21. package/dist/assets/prompts/memory-infer-user.md +2 -3
  22. package/dist/assets/prompts/reflect-llm-framed-contract.md +11 -0
  23. package/dist/assets/prompts/reflect-llm-schema-contract.md +3 -0
  24. package/dist/assets/prompts/reflect-output-repair.md +3 -0
  25. package/dist/assets/stash-skeleton/README.md +38 -10
  26. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +8 -0
  27. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +8 -0
  28. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +14 -1
  29. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +13 -1
  30. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +9 -1
  31. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +11 -0
  32. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +9 -0
  33. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +9 -0
  34. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +8 -0
  35. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +100 -0
  36. package/dist/assets/stash-skeleton/facts/conventions/domains.md +64 -0
  37. package/dist/assets/stash-skeleton/facts/conventions/organization.md +136 -0
  38. package/dist/assets/tasks/core/extract.yml +3 -2
  39. package/dist/assets/tasks/core/improve.yml +2 -1
  40. package/dist/assets/tasks/core/index-refresh.yml +1 -0
  41. package/dist/assets/tasks/core/sync.yml +1 -0
  42. package/dist/assets/tasks/core/version-check.yml +2 -1
  43. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
  44. package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
  45. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
  46. package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
  47. package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
  48. package/dist/assets/templates/html/health.html +5 -4
  49. package/dist/assets/workflows/workflow-template.md +31 -15
  50. package/dist/cli/invocation.js +279 -0
  51. package/dist/cli/parse-args.js +5 -90
  52. package/dist/cli/retired-commands.js +78 -0
  53. package/dist/cli/shared.js +158 -48
  54. package/dist/cli-node.mjs +2 -1
  55. package/dist/cli.js +747 -293
  56. package/dist/commands/agent/agent-dispatch.js +19 -18
  57. package/dist/commands/agent/agent-support.js +0 -24
  58. package/dist/commands/agent/contribute-cli.js +43 -97
  59. package/dist/commands/completions.js +80 -23
  60. package/dist/commands/config-cli.js +44 -281
  61. package/dist/commands/env/env-binding.js +13 -9
  62. package/dist/commands/env/env-cli.js +76 -159
  63. package/dist/commands/env/env.js +12 -163
  64. package/dist/commands/env/marker-path.js +6 -0
  65. package/dist/commands/env/secret-cli.js +45 -61
  66. package/dist/commands/env/secret.js +32 -62
  67. package/dist/commands/feedback-cli.js +179 -85
  68. package/dist/commands/health/accept-rate.js +58 -0
  69. package/dist/commands/health/advisories.js +7 -8
  70. package/dist/commands/health/checks.js +279 -94
  71. package/dist/commands/health/html-report.js +197 -578
  72. package/dist/commands/health/improve-metrics.js +277 -246
  73. package/dist/commands/health/llm-usage.js +19 -19
  74. package/dist/commands/health/md-report.js +16 -7
  75. package/dist/commands/health/metrics.js +67 -32
  76. package/dist/commands/health/renderers.js +47 -0
  77. package/dist/commands/health/report-view-model.js +508 -0
  78. package/dist/commands/health/stash-exposure.js +1 -1
  79. package/dist/commands/health/surfaces.js +16 -56
  80. package/dist/commands/health/task-runs.js +3 -67
  81. package/dist/{migrate-storage-node.mjs → commands/health/types-checks.js} +1 -5
  82. package/dist/commands/health/types-improve.js +29 -0
  83. package/dist/{output/text/save.js → commands/health/types-metrics.js} +1 -2
  84. package/dist/commands/health/types-result.js +7 -0
  85. package/dist/commands/health/types-runs.js +4 -0
  86. package/dist/commands/health/types-session-log.js +4 -0
  87. package/dist/commands/health/types-windows.js +4 -0
  88. package/dist/commands/health/types.js +26 -21
  89. package/dist/commands/health/windows.js +2 -3
  90. package/dist/commands/health.js +296 -167
  91. package/dist/commands/improve/anti-collapse.js +5 -5
  92. package/dist/commands/improve/autonomy-gate.js +68 -0
  93. package/dist/commands/improve/collapse-detector.js +65 -52
  94. package/dist/commands/improve/consolidate/chunking.js +9 -7
  95. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  96. package/dist/commands/improve/consolidate/merge.js +4 -0
  97. package/dist/commands/improve/consolidate.js +454 -1354
  98. package/dist/commands/improve/content-hash.js +39 -0
  99. package/dist/commands/improve/distill/content-repair.js +4 -10
  100. package/dist/commands/improve/distill/promote-memory.js +89 -64
  101. package/dist/commands/improve/distill/quality-gate.js +118 -42
  102. package/dist/commands/improve/distill-guards.js +1 -1
  103. package/dist/commands/improve/distill-promotion-policy.js +33 -888
  104. package/dist/commands/improve/distill.js +607 -363
  105. package/dist/commands/improve/eligibility.js +165 -79
  106. package/dist/commands/improve/extract-cli.js +35 -126
  107. package/dist/commands/improve/extract-prompt.js +6 -35
  108. package/dist/commands/improve/extract.js +640 -391
  109. package/dist/commands/improve/feedback-valence.js +2 -12
  110. package/dist/commands/improve/improve-cli.js +134 -135
  111. package/dist/commands/improve/improve-result-file.js +30 -50
  112. package/dist/commands/improve/improve-run-types.js +4 -0
  113. package/dist/commands/improve/improve-strategies.js +135 -0
  114. package/dist/commands/improve/improve.js +904 -701
  115. package/dist/commands/improve/locks.js +64 -111
  116. package/dist/commands/improve/loop-stages.js +1110 -923
  117. package/dist/commands/improve/memory/derived-ref.js +124 -0
  118. package/dist/commands/improve/memory/memory-belief.js +79 -7
  119. package/dist/commands/improve/memory/memory-contradiction-detect.js +49 -52
  120. package/dist/commands/improve/memory/memory-improve.js +25 -37
  121. package/dist/commands/improve/outcome-loop.js +25 -88
  122. package/dist/commands/improve/preparation.js +1034 -813
  123. package/dist/commands/improve/proactive-maintenance.js +34 -9
  124. package/dist/commands/improve/proposal-envelope.js +31 -0
  125. package/dist/commands/improve/reflect.js +983 -794
  126. package/dist/commands/improve/run-context.js +119 -0
  127. package/dist/commands/improve/salience.js +24 -127
  128. package/dist/commands/improve/session-asset.js +7 -3
  129. package/dist/commands/improve/shared.js +14 -34
  130. package/dist/commands/improve/source-identity.js +28 -0
  131. package/dist/commands/improve/triage.js +20 -17
  132. package/dist/commands/lint/base-linter.js +340 -313
  133. package/dist/commands/lint/env-key-rules.js +31 -47
  134. package/dist/commands/lint/index.js +185 -30
  135. package/dist/commands/{events.js → log.js} +28 -38
  136. package/dist/commands/migrate-cli.js +54 -0
  137. package/dist/commands/migration-tool.js +55 -0
  138. package/dist/commands/observability-cli.js +70 -208
  139. package/dist/commands/proposal/diff-format.js +50 -0
  140. package/dist/commands/proposal/drain-policies.js +0 -6
  141. package/dist/commands/proposal/drain.js +91 -40
  142. package/dist/commands/proposal/proposal-cli.js +134 -132
  143. package/dist/commands/proposal/proposal-types.js +56 -0
  144. package/dist/commands/proposal/proposal.js +83 -65
  145. package/dist/commands/proposal/propose-cli.js +88 -0
  146. package/dist/commands/proposal/propose.js +105 -88
  147. package/dist/commands/proposal/repository.js +1303 -278
  148. package/dist/commands/proposal/validators/proposal-quality-validators.js +16 -6
  149. package/dist/commands/proposal/validators/proposal-validators.js +61 -12
  150. package/dist/commands/proposal/validators/proposals.js +6 -8
  151. package/dist/commands/read/curate.js +78 -73
  152. package/dist/commands/read/knowledge.js +510 -13
  153. package/dist/commands/read/registry-search.js +2 -2
  154. package/dist/commands/read/remember-cli.js +84 -15
  155. package/dist/commands/read/search-cli.js +203 -96
  156. package/dist/commands/read/search.js +126 -94
  157. package/dist/commands/read/show.js +226 -250
  158. package/dist/commands/registry-cli.js +34 -60
  159. package/dist/commands/remember.js +18 -57
  160. package/dist/commands/sources/add-cli.js +104 -49
  161. package/dist/commands/sources/bundle-cli.js +166 -0
  162. package/dist/commands/sources/bundle-config-ops.js +63 -0
  163. package/dist/commands/sources/info.js +27 -15
  164. package/dist/commands/sources/init.js +30 -40
  165. package/dist/commands/sources/installed-stashes.js +469 -172
  166. package/dist/commands/sources/schema-repair.js +10 -9
  167. package/dist/commands/sources/self-update.js +182 -121
  168. package/dist/commands/sources/source-add.js +169 -178
  169. package/dist/commands/sources/source-clone.js +144 -41
  170. package/dist/commands/sources/source-manage.js +94 -59
  171. package/dist/commands/sources/sources-cli.js +64 -205
  172. package/dist/commands/sources/stash-cli.js +91 -54
  173. package/dist/commands/sources/stash-skeleton.js +1 -1
  174. package/dist/commands/tasks/tasks-cli.js +106 -104
  175. package/dist/commands/tasks/tasks.js +445 -262
  176. package/dist/commands/workflow-cli.js +75 -228
  177. package/dist/core/action-contributors.js +1 -1
  178. package/dist/core/activation-policy.js +49 -0
  179. package/dist/core/adapter/adapters/agent-skills-adapter.js +181 -0
  180. package/dist/core/adapter/adapters/akm-adapter.js +528 -0
  181. package/dist/core/adapter/adapters/akm-lint.js +392 -0
  182. package/dist/core/adapter/adapters/akm-metadata.js +387 -0
  183. package/dist/core/adapter/adapters/akm-task-adapter.js +149 -0
  184. package/dist/core/adapter/adapters/akm-workflow-adapter.js +180 -0
  185. package/dist/core/adapter/adapters/claude-adapter.js +61 -0
  186. package/dist/core/adapter/adapters/dotenv-adapter.js +187 -0
  187. package/dist/core/adapter/adapters/generic-files-adapter.js +119 -0
  188. package/dist/core/adapter/adapters/index.js +80 -0
  189. package/dist/core/adapter/adapters/llm-wiki-adapter.js +419 -0
  190. package/dist/core/adapter/adapters/okf-adapter.js +391 -0
  191. package/dist/core/adapter/adapters/opencode-adapter.js +68 -0
  192. package/dist/core/adapter/adapters/shared.js +286 -0
  193. package/dist/core/adapter/adapters/tool-dir-shared.js +217 -0
  194. package/dist/core/adapter/adapters/website-snapshot-adapter.js +155 -0
  195. package/dist/core/adapter/bundle-adapter.js +4 -0
  196. package/dist/core/adapter/detect-adapter.js +17 -0
  197. package/dist/core/adapter/recognize-match.js +44 -0
  198. package/dist/core/adapter/registry.js +56 -0
  199. package/dist/core/adapter/types.js +4 -0
  200. package/dist/core/asset/akm-markdown.js +30 -0
  201. package/dist/core/asset/asset-placement.js +243 -0
  202. package/dist/core/asset/asset-ref.js +110 -79
  203. package/dist/core/asset/asset-serialize.js +20 -0
  204. package/dist/core/asset/frontmatter.js +28 -12
  205. package/dist/core/asset/markdown.js +40 -51
  206. package/dist/core/asset/resolve-ref.js +274 -0
  207. package/dist/core/asset/stash-meta.js +2 -2
  208. package/dist/core/bundle-id.js +51 -0
  209. package/dist/core/common.js +281 -86
  210. package/dist/core/config/config-io.js +42 -128
  211. package/dist/core/config/config-schema.js +233 -855
  212. package/dist/core/config/config-sources.js +162 -39
  213. package/dist/core/config/config-types.js +16 -11
  214. package/dist/core/config/config-version.js +29 -0
  215. package/dist/core/config/config-walker.js +126 -37
  216. package/dist/core/config/config.js +154 -331
  217. package/dist/core/config/deep-merge.js +41 -0
  218. package/dist/core/config/engine-semantics.js +28 -0
  219. package/dist/core/config/experimental.js +21 -0
  220. package/dist/core/config/schema/embedding.js +38 -0
  221. package/dist/core/config/schema/engines.js +116 -0
  222. package/dist/core/config/schema/experimental.js +47 -0
  223. package/dist/core/config/schema/feedback.js +31 -0
  224. package/dist/core/config/schema/improve-processes.js +389 -0
  225. package/dist/core/config/schema/improve.js +94 -0
  226. package/dist/core/config/schema/index-config.js +176 -0
  227. package/dist/core/config/schema/output.js +18 -0
  228. package/dist/core/config/schema/primitives.js +94 -0
  229. package/dist/core/config/schema/search.js +30 -0
  230. package/dist/core/config/schema/setup.js +18 -0
  231. package/dist/core/config/schema/sources-bundles.js +169 -0
  232. package/dist/core/config/schema/workflow.js +29 -0
  233. package/dist/core/env-secret-ref.js +155 -20
  234. package/dist/core/errors.js +17 -15
  235. package/dist/core/events-types.js +4 -0
  236. package/dist/core/events.js +46 -128
  237. package/dist/core/extra-params.js +62 -0
  238. package/dist/core/file-change.js +17 -0
  239. package/dist/core/file-lock.js +202 -57
  240. package/dist/core/fs-txn.js +392 -0
  241. package/dist/core/git-message.js +59 -0
  242. package/dist/core/improve-result.js +167 -0
  243. package/dist/core/lesson-lint.js +1 -17
  244. package/dist/core/logs-db.js +1 -1
  245. package/dist/core/maintenance-barrier.js +135 -0
  246. package/dist/core/migration-operation.js +44 -0
  247. package/dist/core/mutation-target.js +78 -0
  248. package/dist/core/paths.js +22 -25
  249. package/dist/core/platform.js +10 -0
  250. package/dist/core/recognition-util.js +128 -0
  251. package/dist/core/redaction.js +392 -0
  252. package/dist/core/standards/resolve-standards-context.js +36 -65
  253. package/dist/core/standards/resolve-stash-standards.js +2 -2
  254. package/dist/core/standards/resolve-type-conventions.js +5 -5
  255. package/dist/core/state/migrations.js +242 -11
  256. package/dist/core/state-db.js +98 -10
  257. package/dist/core/structured.js +1 -1
  258. package/dist/core/subprocess.js +303 -0
  259. package/dist/core/text-truncation.js +9 -5
  260. package/dist/core/time.js +20 -0
  261. package/dist/core/type-presentation.js +130 -0
  262. package/dist/core/warn.js +0 -3
  263. package/dist/core/write-source.js +834 -118
  264. package/dist/indexer/bundle-identity-guard.js +92 -0
  265. package/dist/indexer/db/graph-db.js +1 -25
  266. package/dist/indexer/db/llm-cache.js +1 -1
  267. package/dist/indexer/ensure-index.js +30 -9
  268. package/dist/indexer/graph/graph-boost.js +9 -30
  269. package/dist/indexer/graph/graph-extraction.js +41 -27
  270. package/dist/indexer/graph/graph-types.js +4 -0
  271. package/dist/indexer/index-writer-lock.js +93 -49
  272. package/dist/indexer/index-written-assets.js +100 -53
  273. package/dist/indexer/indexer.js +746 -329
  274. package/dist/indexer/init.js +18 -25
  275. package/dist/indexer/installations.js +142 -0
  276. package/dist/indexer/passes/dir-staleness.js +18 -10
  277. package/dist/indexer/passes/memory-inference.js +25 -15
  278. package/dist/indexer/passes/metadata.js +412 -243
  279. package/dist/indexer/scan/doc-to-entry.js +160 -0
  280. package/dist/indexer/scan/drain-dir.js +134 -0
  281. package/dist/indexer/search/db-search.js +292 -108
  282. package/dist/indexer/search/fts-query.js +64 -0
  283. package/dist/indexer/search/ranking-contributors.js +145 -25
  284. package/dist/indexer/search/ranking-types.js +4 -0
  285. package/dist/indexer/search/ranking.js +28 -71
  286. package/dist/indexer/search/search-attribution.js +67 -0
  287. package/dist/indexer/search/search-fields.js +18 -3
  288. package/dist/indexer/search/search-hit-enrichers.js +30 -40
  289. package/dist/indexer/search/search-source.js +157 -111
  290. package/dist/indexer/search/semantic-status.js +4 -1
  291. package/dist/indexer/usage/usage-events.js +10 -30
  292. package/dist/indexer/walk/file-context.js +3 -45
  293. package/dist/indexer/walk/matchers.js +42 -73
  294. package/dist/indexer/walk/path-resolver.js +11 -5
  295. package/dist/indexer/walk/walker.js +42 -14
  296. package/dist/integrations/agent/builder-shared.js +7 -0
  297. package/dist/integrations/agent/builders.js +5 -58
  298. package/dist/integrations/agent/config.js +3 -143
  299. package/dist/integrations/agent/detect.js +17 -2
  300. package/dist/integrations/agent/engine-resolution.js +231 -0
  301. package/dist/integrations/agent/index.js +1 -2
  302. package/dist/integrations/agent/model-aliases.js +8 -3
  303. package/dist/integrations/agent/profiles.js +6 -99
  304. package/dist/integrations/agent/prompts.js +46 -18
  305. package/dist/integrations/agent/runner-dispatch.js +78 -13
  306. package/dist/integrations/agent/runner.js +76 -208
  307. package/dist/integrations/agent/spawn.js +48 -279
  308. package/dist/integrations/harnesses/aider/agent-builder.js +9 -8
  309. package/dist/integrations/harnesses/aider/index.js +2 -12
  310. package/dist/integrations/harnesses/amazonq/agent-builder.js +10 -16
  311. package/dist/integrations/harnesses/amazonq/index.js +3 -17
  312. package/dist/integrations/harnesses/claude/agent-builder.js +2 -3
  313. package/dist/integrations/harnesses/claude/config-import.js +1 -3
  314. package/dist/integrations/harnesses/claude/index.js +1 -14
  315. package/dist/integrations/harnesses/claude/session-log.js +27 -75
  316. package/dist/integrations/harnesses/codex/agent-builder.js +8 -7
  317. package/dist/integrations/harnesses/codex/index.js +2 -13
  318. package/dist/integrations/harnesses/copilot/agent-builder.js +9 -9
  319. package/dist/integrations/harnesses/copilot/index.js +1 -13
  320. package/dist/integrations/harnesses/gemini/agent-builder.js +8 -9
  321. package/dist/integrations/harnesses/gemini/index.js +1 -13
  322. package/dist/integrations/harnesses/ids.js +24 -0
  323. package/dist/integrations/harnesses/index.js +31 -33
  324. package/dist/integrations/harnesses/opencode/agent-builder.js +23 -5
  325. package/dist/integrations/harnesses/opencode/config-import.js +1 -3
  326. package/dist/integrations/harnesses/opencode/index.js +1 -18
  327. package/dist/integrations/harnesses/opencode/session-log.js +67 -125
  328. package/dist/integrations/harnesses/opencode-sdk/harness.js +3 -17
  329. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +274 -191
  330. package/dist/integrations/harnesses/openhands/agent-builder.js +12 -10
  331. package/dist/integrations/harnesses/openhands/index.js +2 -12
  332. package/dist/integrations/harnesses/pi/agent-builder.js +11 -18
  333. package/dist/integrations/harnesses/pi/index.js +3 -16
  334. package/dist/integrations/harnesses/shared.js +17 -0
  335. package/dist/integrations/harnesses/types.js +38 -34
  336. package/dist/integrations/lockfile.js +211 -24
  337. package/dist/integrations/session-logs/index.js +24 -40
  338. package/dist/integrations/session-logs/provider-base.js +113 -0
  339. package/dist/llm/client.js +182 -110
  340. package/dist/llm/embedders/deterministic.js +2 -2
  341. package/dist/llm/embedders/remote.js +21 -9
  342. package/dist/llm/feature-gate.js +17 -57
  343. package/dist/llm/graph-extract.js +12 -13
  344. package/dist/llm/index-passes.js +8 -42
  345. package/dist/llm/memory-infer.js +144 -1
  346. package/dist/llm/metadata-enhance.js +45 -30
  347. package/dist/llm/structured-call.js +16 -8
  348. package/dist/llm/usage-persist.js +30 -5
  349. package/dist/llm/usage-telemetry.js +59 -6
  350. package/dist/output/cli-hints.js +1 -2
  351. package/dist/output/command-registry.js +27 -0
  352. package/dist/output/context.js +22 -7
  353. package/dist/output/format-exempt.js +80 -0
  354. package/dist/output/generic-render.js +251 -0
  355. package/dist/output/html-render.js +11 -16
  356. package/dist/output/render-registry.js +57 -0
  357. package/dist/output/renderers.js +15 -281
  358. package/dist/output/shapes/curate.js +10 -1
  359. package/dist/output/shapes/events.js +12 -7
  360. package/dist/output/shapes/helpers.js +58 -84
  361. package/dist/output/shapes/passthrough.js +8 -40
  362. package/dist/output/shapes/proposal/producer.js +15 -7
  363. package/dist/output/shapes/registry.js +12 -6
  364. package/dist/output/shapes.js +0 -9
  365. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  366. package/dist/output/text/bundle-show.js +7 -0
  367. package/dist/output/text/command-format.js +562 -0
  368. package/dist/output/text/env.js +1 -3
  369. package/dist/output/text/events.js +8 -7
  370. package/dist/output/text/helpers.js +15 -1375
  371. package/dist/output/text/proposal/producer.js +4 -2
  372. package/dist/output/text/proposal-format.js +202 -0
  373. package/dist/output/text/registry-commands.js +1 -2
  374. package/dist/output/text/registry.js +12 -6
  375. package/dist/output/text/show-directives.js +117 -0
  376. package/dist/output/text/show-format.js +103 -0
  377. package/dist/output/text/sync.js +5 -0
  378. package/dist/output/text/workflow-format.js +332 -0
  379. package/dist/output/text/workflow.js +1 -2
  380. package/dist/output/text.js +10 -19
  381. package/dist/registry/factory.js +4 -6
  382. package/dist/registry/origin-resolve.js +16 -27
  383. package/dist/registry/providers/skills-sh.js +3 -3
  384. package/dist/registry/providers/static-index.js +15 -25
  385. package/dist/registry/resolve.js +43 -94
  386. package/dist/registry/semver.js +43 -0
  387. package/dist/runtime.js +81 -12
  388. package/dist/scripts/akm-migrate.js +35529 -0
  389. package/dist/setup/detect.js +5 -7
  390. package/dist/setup/detected-engines.js +136 -0
  391. package/dist/setup/engine-config.js +100 -0
  392. package/dist/setup/registry-stash-loader.js +3 -3
  393. package/dist/setup/semantic-assets.js +12 -9
  394. package/dist/setup/setup.js +444 -208
  395. package/dist/setup/steps/connection-shared.js +120 -0
  396. package/dist/setup/steps/connection.js +108 -305
  397. package/dist/setup/steps/platforms.js +13 -12
  398. package/dist/setup/steps/semantic.js +15 -3
  399. package/dist/setup/steps/sources.js +21 -15
  400. package/dist/setup/steps/stashdir.js +6 -4
  401. package/dist/setup/steps/tasks.js +236 -119
  402. package/dist/setup/steps.js +3 -2
  403. package/dist/sources/freshness.js +39 -0
  404. package/dist/sources/provider-factory.js +11 -17
  405. package/dist/sources/providers/filesystem.js +2 -3
  406. package/dist/sources/providers/git-install.js +278 -34
  407. package/dist/sources/providers/git-provider.js +54 -56
  408. package/dist/sources/providers/git-stash.js +420 -91
  409. package/dist/sources/providers/git.js +2 -2
  410. package/dist/sources/providers/npm.js +16 -19
  411. package/dist/sources/providers/provider-utils.js +47 -22
  412. package/dist/sources/providers/sync-from-ref.js +3 -9
  413. package/dist/sources/providers/website.js +2 -2
  414. package/dist/sources/resolve.js +11 -10
  415. package/dist/sources/snapshot-fetchers/types.js +4 -0
  416. package/dist/sources/{website-ingest.js → snapshot-fetchers/website-ingest.js} +110 -41
  417. package/dist/storage/database.js +60 -4
  418. package/dist/storage/engines/sqlite-migrations.js +156 -5
  419. package/dist/storage/locations.js +1 -2
  420. package/dist/storage/repositories/canaries-repository.js +1 -1
  421. package/dist/storage/repositories/events-repository.js +51 -11
  422. package/dist/storage/repositories/improve-runs-repository.js +6 -32
  423. package/dist/storage/repositories/index-connection.js +79 -0
  424. package/dist/storage/repositories/index-db.js +4 -3
  425. package/dist/storage/repositories/index-entries-repository.js +863 -0
  426. package/dist/{indexer/db/entry-mapper.js → storage/repositories/index-entry-mapper.js} +19 -2
  427. package/dist/storage/repositories/index-entry-types.js +4 -0
  428. package/dist/storage/repositories/index-fts-repository.js +167 -0
  429. package/dist/storage/repositories/index-llm-cache-repository.js +108 -0
  430. package/dist/storage/repositories/index-meta-repository.js +49 -0
  431. package/dist/{indexer/db/schema.js → storage/repositories/index-schema.js} +226 -100
  432. package/dist/storage/repositories/index-sql.js +12 -0
  433. package/dist/storage/repositories/index-utility-repository.js +356 -0
  434. package/dist/storage/repositories/index-vec-repository.js +250 -0
  435. package/dist/storage/repositories/outcome-repository.js +119 -0
  436. package/dist/storage/repositories/proposals-repository.js +317 -75
  437. package/dist/storage/repositories/registry-cache.js +1 -1
  438. package/dist/storage/repositories/salience-repository.js +172 -0
  439. package/dist/storage/repositories/task-history-repository.js +110 -3
  440. package/dist/storage/repositories/workflow-runs-repository.js +68 -35
  441. package/dist/tasks/backends/cron.js +169 -46
  442. package/dist/tasks/backends/exec-utils.js +76 -3
  443. package/dist/tasks/backends/index.js +6 -9
  444. package/dist/tasks/backends/launchd.js +292 -55
  445. package/dist/tasks/backends/schtasks.js +557 -70
  446. package/dist/tasks/backends/types.js +4 -0
  447. package/dist/tasks/command-executable.js +93 -0
  448. package/dist/tasks/embedded.js +56 -38
  449. package/dist/tasks/parser.js +156 -64
  450. package/dist/tasks/resolve-akm-bin.js +144 -51
  451. package/dist/tasks/runner.js +377 -209
  452. package/dist/tasks/schedule.js +108 -19
  453. package/dist/tasks/scheduler-invocation.js +296 -0
  454. package/dist/tasks/schema.js +1 -1
  455. package/dist/tasks/task-id.js +35 -0
  456. package/dist/tasks/validator.js +30 -16
  457. package/dist/workflows/authoring/authoring.js +96 -148
  458. package/dist/workflows/authoring/scope-key.js +1 -1
  459. package/dist/workflows/cli.js +0 -20
  460. package/dist/workflows/concurrency-policy.js +15 -0
  461. package/dist/workflows/exec/brief.js +25 -59
  462. package/dist/workflows/exec/frozen-judge.js +47 -0
  463. package/dist/workflows/exec/native-executor.js +157 -94
  464. package/dist/workflows/exec/report.js +365 -200
  465. package/dist/workflows/exec/run-workflow.js +46 -40
  466. package/dist/workflows/exec/scheduler.js +12 -41
  467. package/dist/workflows/exec/step-work.js +239 -205
  468. package/dist/workflows/exec/workflow-engine-gate.js +67 -0
  469. package/dist/workflows/ir/compile.js +141 -283
  470. package/dist/workflows/ir/freeze.js +233 -0
  471. package/dist/workflows/ir/plan-hash.js +40 -5
  472. package/dist/workflows/ir/schema.js +537 -1
  473. package/dist/workflows/parser.js +878 -306
  474. package/dist/workflows/program/expressions.js +20 -208
  475. package/dist/workflows/program/schema.js +7 -10
  476. package/dist/workflows/renderer.js +99 -121
  477. package/dist/workflows/resource-limits.js +22 -0
  478. package/dist/workflows/runtime/checkin.js +1 -1
  479. package/dist/workflows/runtime/plan-classifier.js +131 -0
  480. package/dist/workflows/runtime/runs.js +200 -113
  481. package/dist/workflows/runtime/unit-checkin.js +1 -1
  482. package/dist/workflows/runtime/unit-phases.js +20 -0
  483. package/dist/workflows/runtime/workflow-asset-loader.js +235 -97
  484. package/dist/workflows/schema.js +1 -11
  485. package/dist/workflows/validate-summary.js +4 -26
  486. package/dist/workflows/validator.js +52 -30
  487. package/docs/README.md +42 -78
  488. package/docs/migration/README.md +8 -0
  489. package/docs/migration/release-notes/0.6.0.md +1 -1
  490. package/docs/migration/release-notes/0.7.0.md +9 -8
  491. package/docs/migration/release-notes/0.9.0.md +158 -14
  492. package/docs/migration/v0.7-to-v0.8.md +46 -47
  493. package/docs/migration/v0.8-to-v0.9.md +844 -0
  494. package/docs/reference/README.md +12 -0
  495. package/docs/reference/data-and-telemetry.md +333 -0
  496. package/package.json +21 -17
  497. package/schemas/akm-asset-envelope.json +93 -0
  498. package/schemas/akm-config.json +4636 -0
  499. package/schemas/akm-task.json +87 -0
  500. package/{dist/schemas → schemas}/akm-workflow.json +127 -82
  501. package/dist/akm-migrate-storage +0 -38
  502. package/dist/assets/help/help-accept.md +0 -12
  503. package/dist/assets/help/help-improve.md +0 -84
  504. package/dist/assets/help/help-proposals.md +0 -17
  505. package/dist/assets/help/help-propose.md +0 -17
  506. package/dist/assets/help/help-reject.md +0 -11
  507. package/dist/assets/profiles/frequent.json +0 -13
  508. package/dist/assets/profiles/recombine-only.json +0 -21
  509. package/dist/assets/profiles/reflect-distill.json +0 -30
  510. package/dist/assets/profiles/synthesize.json +0 -15
  511. package/dist/assets/prompts/procedural-system.md +0 -44
  512. package/dist/assets/prompts/recombine-system.md +0 -40
  513. package/dist/assets/prompts/staleness-detect-system.md +0 -6
  514. package/dist/assets/tasks/core/backup.yml +0 -4
  515. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  516. package/dist/assets/templates/html/default.html +0 -78
  517. package/dist/assets/templates/html/vendor/echarts.min.js +0 -45
  518. package/dist/assets/wiki/index-template.md +0 -12
  519. package/dist/assets/wiki/ingest-workflow-template.md +0 -83
  520. package/dist/assets/wiki/log-template.md +0 -8
  521. package/dist/assets/wiki/schema-template.md +0 -61
  522. package/dist/cli/config-migrate.js +0 -150
  523. package/dist/cli/config-validate.js +0 -39
  524. package/dist/commands/graph/graph-cli.js +0 -124
  525. package/dist/commands/graph/graph.js +0 -487
  526. package/dist/commands/improve/calibration.js +0 -161
  527. package/dist/commands/improve/dedup.js +0 -482
  528. package/dist/commands/improve/extract-watch.js +0 -140
  529. package/dist/commands/improve/hot-probation.js +0 -45
  530. package/dist/commands/improve/improve-auto-accept.js +0 -276
  531. package/dist/commands/improve/improve-profiles.js +0 -168
  532. package/dist/commands/improve/procedural.js +0 -398
  533. package/dist/commands/improve/recombine.js +0 -818
  534. package/dist/commands/improve/schema-similarity-gate.js +0 -168
  535. package/dist/commands/lint/agent-linter.js +0 -44
  536. package/dist/commands/lint/command-linter.js +0 -44
  537. package/dist/commands/lint/default-linter.js +0 -16
  538. package/dist/commands/lint/fact-linter.js +0 -39
  539. package/dist/commands/lint/knowledge-linter.js +0 -16
  540. package/dist/commands/lint/memory-linter.js +0 -61
  541. package/dist/commands/lint/registry.js +0 -41
  542. package/dist/commands/lint/skill-linter.js +0 -45
  543. package/dist/commands/lint/task-linter.js +0 -50
  544. package/dist/commands/lint/workflow-linter.js +0 -81
  545. package/dist/commands/proposal/legacy-import.js +0 -115
  546. package/dist/commands/sources/history.js +0 -196
  547. package/dist/commands/tasks/default-tasks.js +0 -186
  548. package/dist/commands/wiki-cli.js +0 -292
  549. package/dist/core/asset/asset-registry.js +0 -76
  550. package/dist/core/asset/asset-spec.js +0 -316
  551. package/dist/core/config/config-migration.js +0 -602
  552. package/dist/core/deep-merge.js +0 -38
  553. package/dist/core/eval/rank-metrics.js +0 -113
  554. package/dist/core/ripgrep/install.js +0 -163
  555. package/dist/core/ripgrep/resolve.js +0 -81
  556. package/dist/indexer/db/db.js +0 -1414
  557. package/dist/indexer/manifest.js +0 -170
  558. package/dist/indexer/passes/metadata-contributors.js +0 -31
  559. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -94
  560. package/dist/integrations/harnesses/opencode-sdk/index.js +0 -25
  561. package/dist/llm/call-ai.js +0 -62
  562. package/dist/llm/memory-infer-impl.js +0 -138
  563. package/dist/output/shapes/distill.js +0 -14
  564. package/dist/output/shapes/history.js +0 -11
  565. package/dist/output/text/distill.js +0 -6
  566. package/dist/output/text/enable-disable.js +0 -8
  567. package/dist/output/text/history.js +0 -6
  568. package/dist/output/text/wiki.js +0 -16
  569. package/dist/registry/build-index.js +0 -386
  570. package/dist/schemas/akm-config.json +0 -14225
  571. package/dist/scripts/migrate-storage.js +0 -13169
  572. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +0 -10169
  573. package/dist/scripts/migrations/v16-to-v17.js +0 -141
  574. package/dist/setup/legacy-config.js +0 -106
  575. package/dist/storage/repositories/consolidation-repository.js +0 -38
  576. package/dist/storage/repositories/recombine-repository.js +0 -213
  577. package/dist/wiki/wiki-templates.js +0 -15
  578. package/dist/wiki/wiki.js +0 -1012
  579. package/dist/workflows/authoring/workflow-program-template.yaml +0 -31
  580. package/dist/workflows/db.js +0 -350
  581. package/dist/workflows/exec/watch.js +0 -116
  582. package/dist/workflows/program/parser.js +0 -760
  583. package/dist/workflows/program/project.js +0 -105
  584. package/docs/data-and-telemetry.md +0 -227
  585. package/docs/migration/release-notes/0.9.0-beta.60.md +0 -19
  586. /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/registry.js +0 -0
  587. /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/youtube.js +0 -0
@@ -56,27 +56,29 @@
56
56
  * dropped rather than approximated — never silently widened.
57
57
  * - **resume/session** — NOT expressible: per the matrix OpenHands resumes
58
58
  * from *workspace state*, not a session-id flag, so there is no flag-shaped
59
- * `HarnessResumeSupport` to export. The extractor still captures a
59
+ * resume to describe. The extractor still captures a
60
60
  * conversation/session id opportunistically when the stream reveals one;
61
61
  * akm's `workflow_run_units` remains the durable source of truth either way
62
62
  * (plan §"Session, MCP, and identity across harnesses").
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"`, and no
69
- * `resume`). 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
- import { assertNotFlag } from "../../agent/builder-shared.js";
72
- import { resolveModel } from "../../agent/model-aliases.js";
74
+ import { assertNotFlag, resolveDispatchModel, } from "../../agent/builder-shared.js";
73
75
  /** Canonical harness/platform id used for model-alias resolution. */
74
76
  export const OPENHANDS_PLATFORM = "openhands";
75
77
  /**
76
78
  * Env var OpenHands reads its LLM model from — the platform's documented
77
79
  * model-selection channel for headless runs (there is no headless model
78
- * flag). Exported so the follow-up integration task and tests share the one
79
- * constant.
80
+ * flag). Exported (re-exported from `./index.ts`) so this harness's tests
81
+ * share the one constant.
80
82
  */
81
83
  export const OPENHANDS_MODEL_ENV = "LLM_MODEL";
82
84
  /**
@@ -117,7 +119,7 @@ export const openhandsBuilder = {
117
119
  args.push(`--task=${buildTaskPayload(req)}`);
118
120
  let env;
119
121
  if (req.model) {
120
- const resolved = resolveModel(req.model, OPENHANDS_PLATFORM, profile.modelAliases, profile.globalModelAliases);
122
+ const resolved = resolveDispatchModel(req, profile, OPENHANDS_PLATFORM);
121
123
  // Model travels via env, not argv — OpenHands' documented channel.
122
124
  env = { [OPENHANDS_MODEL_ENV]: resolved };
123
125
  }
@@ -12,22 +12,12 @@
12
12
  * that `HARNESS_REGISTRY` registers. Dispatch-only: no native session-log
13
13
  * reader or config importer yet.
14
14
  */
15
+ import { caps } from "../shared.js";
15
16
  import { BaseHarness } from "../types.js";
16
17
  import { openhandsBuilder } from "./agent-builder.js";
17
18
  import { openhandsResultExtractor } from "./result-extractor.js";
18
19
  export { OPENHANDS_MODEL_ENV, OPENHANDS_PLATFORM, openhandsBuilder } from "./agent-builder.js";
19
20
  export { openhandsResultExtractor } from "./result-extractor.js";
20
- function caps(c) {
21
- return {
22
- sessionLogs: false,
23
- agentDispatch: false,
24
- detection: false,
25
- configImport: false,
26
- runtimeIdentity: false,
27
- v1Migration: false,
28
- ...c,
29
- };
30
- }
31
21
  /**
32
22
  * OpenHands CLI.
33
23
  *
@@ -45,7 +35,7 @@ export class OpenhandsHarness extends BaseHarness {
45
35
  // `--json` emits a documented JSONL event stream akm parses, then validates
46
36
  // against the node schema ⇒ native-json tier.
47
37
  structuredOutput = "native-json";
48
- // No `resume`: per the matrix OpenHands resumes from workspace state, not a
38
+ // No flag-shaped resume: per the matrix OpenHands resumes from workspace state, not a
49
39
  // session-id flag. The extractor still captures a conversation id
50
40
  // opportunistically; akm's `workflow_run_units` remains the durable source
51
41
  // of truth.
@@ -12,9 +12,9 @@
12
12
  * pi -p "<prompt>"
13
13
  *
14
14
  * with `--mode json` for structured (JSONL) output, `--model <m>` for model
15
- * selection, and `-c`/`-r`/`--session <id>` for resume. Resume is
16
- * registry-side (`AkmHarness.resume`, flag-shaped: {@link PI_RESUME_FLAG}) —
17
- * not built here because `AgentDispatchRequest` carries no session id.
15
+ * selection, and `-c`/`-r`/`--session <id>` for resume — not built here
16
+ * because `AgentDispatchRequest` carries no session id (akm's
17
+ * `workflow_run_units` is the durable resume source of truth).
18
18
  *
19
19
  * Platform-specific mapping decisions (all localized here, per the adapter
20
20
  * contract):
@@ -43,23 +43,16 @@
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
- import { assertNotFlag } from "../../agent/builder-shared.js";
52
- import { resolveModel } from "../../agent/model-aliases.js";
53
+ import { assertNotFlag, resolveDispatchModel, } from "../../agent/builder-shared.js";
53
54
  /** Canonical harness/platform id used for model-alias resolution. */
54
55
  export const PI_PLATFORM = "pi";
55
- /**
56
- * Flag-shaped resume support per the capability matrix (`--session <id>`).
57
- * Exported for the integration task's `AkmHarness.resume` registry entry; the
58
- * harness-native session id comes from the unit row (stored opportunistically
59
- * by the result extractor) — akm never depends on it (plan §"Session, MCP,
60
- * and identity across harnesses").
61
- */
62
- export const PI_RESUME_FLAG = "--session";
63
56
  /**
64
57
  * Assemble the positional prompt payload: the task prompt and — when a schema
65
58
  * is requested — the same schema directive the workflow engine's prompt
@@ -87,7 +80,7 @@ export const piBuilder = {
87
80
  args.push("--system-prompt", req.systemPrompt);
88
81
  }
89
82
  if (req.model) {
90
- const resolved = resolveModel(req.model, PI_PLATFORM, profile.modelAliases, profile.globalModelAliases);
83
+ const resolved = resolveDispatchModel(req, profile, PI_PLATFORM);
91
84
  args.push("--model", resolved);
92
85
  }
93
86
  if (req.schema) {
@@ -12,22 +12,12 @@
12
12
  * `HARNESS_REGISTRY` registers. Dispatch-only: no native session-log reader or
13
13
  * config importer yet.
14
14
  */
15
+ import { caps } from "../shared.js";
15
16
  import { BaseHarness } from "../types.js";
16
- import { PI_RESUME_FLAG, piBuilder } from "./agent-builder.js";
17
+ import { piBuilder } from "./agent-builder.js";
17
18
  import { piResultExtractor } from "./result-extractor.js";
18
- export { PI_PLATFORM, PI_RESUME_FLAG, piBuilder } from "./agent-builder.js";
19
+ export { PI_PLATFORM, piBuilder } from "./agent-builder.js";
19
20
  export { piResultExtractor } from "./result-extractor.js";
20
- function caps(c) {
21
- return {
22
- sessionLogs: false,
23
- agentDispatch: false,
24
- detection: false,
25
- configImport: false,
26
- runtimeIdentity: false,
27
- v1Migration: false,
28
- ...c,
29
- };
30
- }
31
21
  /**
32
22
  * Pi coding-agent CLI.
33
23
  *
@@ -45,9 +35,6 @@ export class PiHarness extends BaseHarness {
45
35
  // `--mode json` emits a documented JSONL event stream akm parses, then
46
36
  // validates against the node schema ⇒ native-json tier.
47
37
  structuredOutput = "native-json";
48
- // `pi --session <id>` replays a previous session (the matrix's -c/-r/
49
- // --session family; the id-taking long form is the registered flag).
50
- resume = { flag: PI_RESUME_FLAG, takesSessionId: true };
51
38
  // Session-id env marker only — the matrix's bare PI_* presence vars must
52
39
  // not stamp identity onto manual runs (see `AkmHarness.identityEnv`).
53
40
  identityEnv = ["PI_SESSION_ID"];
@@ -0,0 +1,17 @@
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
+ export function caps(c) {
5
+ return {
6
+ sessionLogs: false,
7
+ agentDispatch: false,
8
+ detection: false,
9
+ configImport: false,
10
+ runtimeIdentity: false,
11
+ ...c,
12
+ };
13
+ }
14
+ /** Home directory used for filesystem-only harness config detection. */
15
+ export function homeDir() {
16
+ return process.env.HOME ?? process.env.USERPROFILE ?? "";
17
+ }
@@ -1,14 +1,47 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ /**
5
+ * Type-guard narrowing a harness to {@link SessionLogCapableHarness}.
6
+ *
7
+ * `Array.prototype.filter`'s type-predicate overload requires the predicate's
8
+ * inferred type to extend the array's element type — a plain
9
+ * `(h: AkmHarness) => h is SessionLogCapableHarness` predicate fails that
10
+ * constraint against `HARNESS_REGISTRY`'s element type (the union of the ten
11
+ * concrete harness classes, each with literal `id`s narrower than
12
+ * `SessionLogCapableHarness`'s `id: string`), so `.filter` would silently fall
13
+ * back to its non-narrowing overload. Generic over the input `H` and
14
+ * intersecting it into the predicate (`H & SessionLogCapableHarness`) keeps
15
+ * the result always a subtype of `H`, so the constraint holds regardless of
16
+ * what concrete harness type(s) `H` resolves to — this is what lets
17
+ * `HARNESS_REGISTRY.filter(isSessionLogHarness)` (in `./index.ts`) actually
18
+ * narrow to a type where `sessionLogProvider` is known-present, retiring the
19
+ * load-time throw that used to guard this in `session-logs/index.ts`.
20
+ */
21
+ export function isSessionLogHarness(h) {
22
+ return h.capabilities.sessionLogs;
23
+ }
4
24
  /**
5
25
  * Shared base for harness descriptors (#566).
6
26
  *
7
- * Provides the default v1-profile-name matcher so the inference logic lives in
8
- * ONE place instead of being duplicated across the old `guessAgentPlatform()`
9
- * (config-migration) and the `name.includes("claude")` heuristic (setup). A
10
- * concrete harness sets `id`/`aliases`/`capabilities` and, when its v1 profile
11
- * names could be decorated (e.g. `"opencode-sdk-fast"`), `v1ProfilePrefixes`.
27
+ * Provides shared optional descriptor fields for concrete harnesses.
28
+ *
29
+ * Deliberately implements {@link AkmHarnessCommon}, NOT the discriminated
30
+ * `AkmHarness` union (WI-9.7, H1): `AkmHarnessCommon` excludes `capabilities`
31
+ * and `sessionLogProvider`, the two fields whose pairing the union enforces.
32
+ * A base class shared by both branches cannot itself satisfy either specific
33
+ * union member — `capabilities` stays declared `abstract` at the (widened)
34
+ * `HarnessCapabilities` union type here, and each subclass narrows it to a
35
+ * literal `sessionLogs: true`/`false` variant via `caps({...})`.
36
+ * `sessionLogProvider` is intentionally NOT declared here at all (not even
37
+ * optional): the 8 non-session-log subclasses simply never declare it, which
38
+ * satisfies `NonSessionLogHarness`'s `sessionLogProvider?: undefined` (an
39
+ * absent optional property satisfies an `undefined`-typed optional); had this
40
+ * class declared it as `sessionLogProvider?: () => SessionLogHarness`
41
+ * instead, every non-session-log subclass would inherit that (function |
42
+ * undefined) type and fail to satisfy `NonSessionLogHarness` at the
43
+ * `HARNESS_REGISTRY` `satisfies` check. The two session-log subclasses
44
+ * (Claude, OpenCode) declare their own required `sessionLogProvider`.
12
45
  */
13
46
  export class BaseHarness {
14
47
  runtimeId;
@@ -16,36 +49,7 @@ export class BaseHarness {
16
49
  agentBuilder;
17
50
  pattern;
18
51
  structuredOutput;
19
- resume;
20
52
  identityEnv;
21
53
  presenceEnv;
22
54
  resultExtractor;
23
- sessionLogProvider;
24
- /**
25
- * Lowercase prefixes that a decorated v1 profile name may start with and
26
- * still belong to this harness (e.g. `["opencode-sdk"]`). The canonical id
27
- * and aliases are always matched in addition to these; an empty list means
28
- * only exact id/alias matches.
29
- */
30
- v1ProfilePrefixes = [];
31
- matchesV1ProfileName(name) {
32
- // Only harnesses with a v1→v2 mapping participate; others never claim a
33
- // legacy profile name (so unknown names are dropped, not misclassified).
34
- if (!this.capabilities.v1Migration)
35
- return false;
36
- const lower = name.trim().toLowerCase();
37
- if (!lower)
38
- return false;
39
- if (lower === this.id.toLowerCase())
40
- return true;
41
- for (const alias of this.aliases) {
42
- if (lower === alias.toLowerCase())
43
- return true;
44
- }
45
- for (const prefix of this.v1ProfilePrefixes) {
46
- if (lower.startsWith(prefix.toLowerCase()))
47
- return true;
48
- }
49
- return false;
50
- }
51
55
  }
@@ -4,8 +4,9 @@
4
4
  import fs from "node:fs";
5
5
  import path from "node:path";
6
6
  import { writeFileAtomic } from "../core/common.js";
7
- import { rethrowIfTestIsolationError } from "../core/errors.js";
8
- import { probeLock, releaseLock, tryAcquireLockSync } from "../core/file-lock.js";
7
+ import { ConfigError, rethrowIfTestIsolationError } from "../core/errors.js";
8
+ import { createLockPayload, probeLock, reclaimStaleLock, releaseLock, tryAcquireLockSync } from "../core/file-lock.js";
9
+ import { acquireMaintenanceBarrier } from "../core/maintenance-barrier.js";
9
10
  import { getDataDir, getLockfileLockPath, getLockfilePath } from "../core/paths.js";
10
11
  // ── Lock sentinel ────────────────────────────────────────────────────────────
11
12
  const LOCK_MAX_RETRIES = 3;
@@ -15,23 +16,26 @@ async function acquireLockSentinel() {
15
16
  // Ensure the directory exists before attempting to create the sentinel.
16
17
  fs.mkdirSync(path.dirname(sentinelPath), { recursive: true });
17
18
  for (let attempt = 0; attempt < LOCK_MAX_RETRIES; attempt++) {
18
- if (tryAcquireLockSync(sentinelPath, String(process.pid))) {
19
- return true; // Sentinel created — we own the lock.
19
+ const releaseBarrier = acquireMaintenanceBarrier();
20
+ try {
21
+ const ownership = tryAcquireLockSync(sentinelPath, createLockPayload());
22
+ if (ownership) {
23
+ return () => releaseLock(ownership);
24
+ }
25
+ const probe = probeLock(sentinelPath);
26
+ if (probe.state === "stale" && reclaimStaleLock(sentinelPath, probe)) {
27
+ continue; // Reclaimed — retry immediately.
28
+ }
20
29
  }
21
- if (probeLock(sentinelPath).state === "stale") {
22
- releaseLock(sentinelPath);
23
- continue; // Reclaimed — retry immediately.
30
+ finally {
31
+ releaseBarrier();
24
32
  }
25
33
  // Another process holds the lock — wait briefly before retrying.
26
34
  if (attempt < LOCK_MAX_RETRIES - 1) {
27
35
  await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_DELAY_MS));
28
36
  }
29
37
  }
30
- // Best-effort: proceed without the lock rather than failing the install.
31
- return false;
32
- }
33
- function releaseLockSentinel() {
34
- releaseLock(getLockfileLockPath());
38
+ throw new ConfigError(`Could not acquire lockfile sentinel at ${sentinelPath}; refusing to write without exclusive ownership.`, "INVALID_CONFIG_FILE");
35
39
  }
36
40
  // ── Read / Write ────────────────────────────────────────────────────────────
37
41
  export function readLockfile() {
@@ -50,40 +54,223 @@ export function readLockfile() {
50
54
  return [];
51
55
  }
52
56
  }
53
- export function writeLockfile(entries) {
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
+ }
99
+ /**
100
+ * The materialized content root recorded in the lock for a managed (git/npm)
101
+ * bundle — spec §10.2 desired/resolved split, where the desired config carries
102
+ * only the source LOCATOR and the resolved `localRoot` lives here.
103
+ *
104
+ * This is the SINGLE lock-first resolution point shared by the indexer READ
105
+ * path (`resolveEntryContentDir` in indexer/search) and the command-layer WRITE
106
+ * path (`adaptConfiguredSource` in core/write-source): consulting it first makes
107
+ * a write land in exactly the directory a read walks. Returns `undefined` for a
108
+ * bundle with no lock `localRoot` (e.g. a config migrated from a `sources[]`
109
+ * url, whose provider re-derives the cache path) or a non-managed type, so both
110
+ * callers fall back to the identical provider-path derivation.
111
+ */
112
+ export function lockContentRootFor(bundleId, type) {
113
+ if (!bundleId || (type !== "git" && type !== "npm"))
114
+ return undefined;
115
+ for (const lock of readLockfile()) {
116
+ if (lock.id === bundleId && typeof lock.localRoot === "string" && lock.localRoot.length > 0) {
117
+ return lock.localRoot;
118
+ }
119
+ }
120
+ return undefined;
121
+ }
122
+ function writeLockfileUnlocked(entries) {
54
123
  // Always write to $DATA — never to the legacy $CONFIG location.
55
124
  const lockfilePath = getLockfilePath();
56
125
  const dir = path.dirname(lockfilePath);
57
126
  fs.mkdirSync(dir, { recursive: true });
58
127
  writeFileAtomic(lockfilePath, `${JSON.stringify(entries, null, 2)}\n`);
59
128
  }
129
+ export async function writeLockfile(entries) {
130
+ const release = await acquireLockSentinel();
131
+ try {
132
+ writeLockfileUnlocked(entries);
133
+ }
134
+ finally {
135
+ release();
136
+ }
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
+ }
60
152
  export async function upsertLockEntry(entry) {
61
- const acquired = await acquireLockSentinel();
153
+ const release = await acquireLockSentinel();
62
154
  try {
63
- 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();
64
159
  const withoutExisting = entries.filter((e) => e.id !== entry.id);
65
- writeLockfile([...withoutExisting, entry]);
160
+ writeLockfileUnlocked([...withoutExisting, entry]);
66
161
  }
67
162
  finally {
68
- if (acquired)
69
- releaseLockSentinel();
163
+ release();
70
164
  }
71
165
  }
166
+ /**
167
+ * Synchronously upsert lock entries (merge by id) WITHOUT acquiring the async
168
+ * sentinel — for a caller already holding an exclusive lifecycle lock (e.g.
169
+ * migrate-apply's config lock + maintenance barrier) whose synchronous body
170
+ * cannot await the sentinel's retry loop. No-op for an empty list.
171
+ */
172
+ export function mergeLockEntriesSync(entries) {
173
+ if (entries.length === 0)
174
+ return;
175
+ let existing = [];
176
+ const lockfilePath = getLockfilePath();
177
+ if (fs.existsSync(lockfilePath)) {
178
+ let raw;
179
+ try {
180
+ raw = JSON.parse(fs.readFileSync(lockfilePath, "utf8"));
181
+ }
182
+ catch (error) {
183
+ throw new ConfigError(`Cannot merge migration lock entries into unreadable lockfile ${lockfilePath}: ${error instanceof Error ? error.message : String(error)}.`, "INVALID_CONFIG_FILE");
184
+ }
185
+ if (!Array.isArray(raw) || !raw.every(isValidLockfileEntry)) {
186
+ throw new ConfigError(`Cannot merge migration lock entries into malformed lockfile ${lockfilePath}.`, "INVALID_CONFIG_FILE");
187
+ }
188
+ existing = raw;
189
+ }
190
+ // MERGE per id, never replace: the migrator's entries are sparse (id/source/
191
+ // ref/localRoot), while an existing row may carry `resolvedVersion`,
192
+ // `resolvedRevision`, `integrity`, `installedAt`, … from a real install.
193
+ // Replacing the whole row discarded the user's recorded resolution/pin and
194
+ // left later update reporting comparing against an unknown prior version.
195
+ // Incoming DEFINED fields win; existing fields absent from the incoming
196
+ // entry are preserved.
197
+ const byId = new Map(existing.map((e) => [e.id, e]));
198
+ const merged = entries.map((incoming) => {
199
+ const prior = byId.get(incoming.id);
200
+ return prior ? { ...prior, ...incoming } : incoming;
201
+ });
202
+ const incomingIds = new Set(entries.map((e) => e.id));
203
+ writeLockfileUnlocked([...existing.filter((e) => !incomingIds.has(e.id)), ...merged]);
204
+ }
205
+ /** Freeze exact old/desired lockfile bytes while the migration lifecycle lock is held. */
206
+ export function planLockMergeSync(entries) {
207
+ const lockfilePath = getLockfilePath();
208
+ let oldBytes = null;
209
+ let existing = [];
210
+ try {
211
+ oldBytes = fs.readFileSync(lockfilePath, "utf8");
212
+ }
213
+ catch (error) {
214
+ if (error.code !== "ENOENT")
215
+ throw error;
216
+ }
217
+ if (oldBytes !== null) {
218
+ let parsed;
219
+ try {
220
+ parsed = JSON.parse(oldBytes);
221
+ }
222
+ catch (error) {
223
+ throw new ConfigError(`Cannot plan migration lock entries from unreadable lockfile ${lockfilePath}: ${error instanceof Error ? error.message : String(error)}.`, "INVALID_CONFIG_FILE");
224
+ }
225
+ if (!Array.isArray(parsed) || !parsed.every(isValidLockfileEntry)) {
226
+ throw new ConfigError(`Cannot plan migration lock entries from malformed lockfile ${lockfilePath}.`, "INVALID_CONFIG_FILE");
227
+ }
228
+ existing = parsed;
229
+ }
230
+ if (entries.length === 0)
231
+ return { oldBytes, desiredBytes: oldBytes };
232
+ const byId = new Map(existing.map((entry) => [entry.id, entry]));
233
+ for (const entry of entries)
234
+ byId.set(entry.id, { ...byId.get(entry.id), ...entry });
235
+ return { oldBytes, desiredBytes: `${JSON.stringify([...byId.values()], null, 2)}\n` };
236
+ }
237
+ /** Publish a frozen migration lock generation, accepting only exact old or exact desired bytes. */
238
+ export function publishPlannedLockMergeSync(plan) {
239
+ const lockfilePath = getLockfilePath();
240
+ let current = null;
241
+ try {
242
+ current = fs.readFileSync(lockfilePath, "utf8");
243
+ }
244
+ catch (error) {
245
+ if (error.code !== "ENOENT")
246
+ throw error;
247
+ }
248
+ if (current === plan.desiredBytes)
249
+ return;
250
+ if (current !== plan.oldBytes) {
251
+ throw new ConfigError(`Refusing migration lockfile publication because ${lockfilePath} is a third generation, neither the journaled old nor desired bytes.`, "INVALID_CONFIG_FILE");
252
+ }
253
+ if (plan.desiredBytes === null)
254
+ return;
255
+ fs.mkdirSync(path.dirname(lockfilePath), { recursive: true });
256
+ writeFileAtomic(lockfilePath, plan.desiredBytes, 0o600);
257
+ }
72
258
  export async function removeLockEntry(id) {
73
259
  if (!fs.existsSync(getDataDir()))
74
260
  return;
75
- const acquired = await acquireLockSentinel();
261
+ const release = await acquireLockSentinel();
76
262
  try {
77
- const entries = readLockfile();
78
- writeLockfile(entries.filter((e) => e.id !== id));
263
+ // R-012: see upsertLockEntry — same destructive-overwrite risk on a
264
+ // corrupt lockfile, so the same strict reader is used here.
265
+ const entries = readLockfileOrThrow();
266
+ writeLockfileUnlocked(entries.filter((e) => e.id !== id));
79
267
  }
80
268
  finally {
81
- if (acquired)
82
- releaseLockSentinel();
269
+ release();
83
270
  }
84
271
  }
85
272
  // ── Helpers ─────────────────────────────────────────────────────────────────
86
- function isValidLockfileEntry(value) {
273
+ export function isValidLockfileEntry(value) {
87
274
  if (typeof value !== "object" || value === null || Array.isArray(value))
88
275
  return false;
89
276
  const obj = value;