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
@@ -4,23 +4,17 @@
4
4
  /**
5
5
  * Low-level OpenAI-compatible chat completions client and capability probing.
6
6
  *
7
- * Split out of `llm.ts` to keep the transport-layer concerns (HTTP request,
8
- * response parsing, JSON-fence stripping, capability probe, availability
9
- * check) separate from higher-level metadata-enhancement workflows.
10
- *
11
- * `llm.ts` re-exports everything from this module for backward compatibility.
7
+ * Keeps transport-layer concerns (HTTP request, response parsing, capability
8
+ * probing, and availability checks) separate from higher-level workflows.
12
9
  */
13
- import { fetchWithTimeout } from "../core/common.js";
10
+ import { fetchWithTimeout, readBodyWithByteCap } from "../core/common.js";
14
11
  import { resolveSecret } from "../core/config/config.js";
15
12
  import { formatExtraParamsIssue, validateExtraParams } from "../core/extra-params.js";
16
- import { escapeJsonStringControls, parseJsonResponse, stripCodeFences, stripThinkBlocks } from "../core/parse.js";
17
- import { redactSensitiveText } from "../core/redaction.js";
13
+ import { parseJsonResponse } from "../core/parse.js";
14
+ import { redactCredentialPatterns, redactSensitiveText } from "../core/redaction.js";
18
15
  import { warnVerbose } from "../core/warn.js";
19
16
  import { DEFAULT_LLM_TIMEOUT_MS } from "../integrations/agent/config.js";
20
- import { emitLlmUsage, extractUsageTokens } from "./usage-telemetry.js";
21
- // Re-export shared parse utilities so existing importers of `client.ts` continue
22
- // to resolve `parseJsonResponse` and `parseEmbeddedJsonResponse` from this module.
23
- export { escapeJsonStringControls, parseEmbeddedJsonResponse, parseJsonResponse, stripCodeFences, stripThinkBlocks, } from "../core/parse.js";
17
+ import { emitLlmUsage, extractUsageTokens, } from "./usage-telemetry.js";
24
18
  /** Maximum length of an LLM error response body included in thrown errors. */
25
19
  const ERROR_BODY_MAX_LEN = 200;
26
20
  /** Stable OpenAI-compatible response-schema name used for every structured call. */
@@ -30,24 +24,14 @@ const JSON_SCHEMA_RESPONSE_NAME = "akm_response";
30
24
  * including it in a thrown Error. The body is also trimmed to a fixed length
31
25
  * so that a verbose provider response cannot leak large amounts of context.
32
26
  *
33
- * Targets:
34
- * - `Bearer <token>` headers echoed back by the provider
35
- * - `sk-…` / `sk_…` style API keys (OpenAI / Anthropic-shaped)
36
- * - `key-…` / `key_…` shorthand keys
37
- * - `"api_key": "…"` / `"apiKey": "…"` JSON fields
27
+ * The pattern set itself lives in {@link redactCredentialPatterns}
28
+ * (src/core/redaction.ts) so other output paths (e.g. task run logs) can
29
+ * reuse it without this function's length cap.
38
30
  */
39
31
  export function redactErrorBody(input) {
40
32
  if (!input)
41
33
  return "";
42
- let out = input
43
- // Bearer tokens (case-insensitive)
44
- .replace(/\bBearer\s+[A-Za-z0-9._\-+/=]+/gi, "Bearer [REDACTED]")
45
- // sk-/sk_ style keys
46
- .replace(/\bsk[-_][A-Za-z0-9._-]{6,}/g, "[REDACTED]")
47
- // key-/key_ shorthand keys
48
- .replace(/\bkey[-_][A-Za-z0-9._-]{6,}/g, "[REDACTED]")
49
- // JSON-style "api_key": "...", "apiKey": "...", "api-key": "..."
50
- .replace(/("(?:api[_-]?key|apiKey|authorization|token)"\s*:\s*")([^"]*)(")/gi, "$1[REDACTED]$3");
34
+ let out = redactCredentialPatterns(input);
51
35
  if (out.length > ERROR_BODY_MAX_LEN) {
52
36
  out = `${out.slice(0, ERROR_BODY_MAX_LEN)}…`;
53
37
  }
@@ -103,6 +87,19 @@ const RETRY_BUDGET_FRACTION = 0.9;
103
87
  export function sleep(ms) {
104
88
  return new Promise((resolve) => setTimeout(resolve, ms));
105
89
  }
90
+ async function waitForRetry(ms, wait, signal) {
91
+ if (!signal)
92
+ return wait(ms);
93
+ if (signal.aborted)
94
+ throw new LlmCallError("LLM request aborted", "aborted");
95
+ await new Promise((resolve, reject) => {
96
+ const onAbort = () => reject(new LlmCallError("LLM request aborted", "aborted"));
97
+ signal.addEventListener("abort", onAbort, { once: true });
98
+ wait(ms)
99
+ .then(resolve, reject)
100
+ .finally(() => signal.removeEventListener("abort", onAbort));
101
+ });
102
+ }
106
103
  /** Compute a uniform jittered backoff in the [200, 800)ms range. */
107
104
  function retryBackoffMs() {
108
105
  return RETRY_BACKOFF_MIN_MS + Math.random() * (RETRY_BACKOFF_MAX_MS - RETRY_BACKOFF_MIN_MS);
@@ -209,7 +206,7 @@ async function chatCompletionReal(config, messages, options) {
209
206
  // authoritative.
210
207
  warnVerbose(`[akm] LLM transient failure (${err.code}); retrying once: ${err.message}`);
211
208
  const wait = retryBackoffMs();
212
- await (options?.sleep ?? sleep)(wait);
209
+ await waitForRetry(wait, options?.sleep ?? sleep, options?.signal);
213
210
  // The retry must not exceed the original budget.
214
211
  return await chatCompletionAttempt(config, messages, options, remaining);
215
212
  }
@@ -248,90 +245,129 @@ async function chatCompletionAttempt(config, messages, options, timeoutMs) {
248
245
  : config.provider === "vllm"
249
246
  ? { chat_template_kwargs: { enable_thinking: resolvedEnableThinking } }
250
247
  : { enable_thinking: resolvedEnableThinking };
251
- // Wall-clock start for per-call usage telemetry (#576). Captured here so the
248
+ const requestBody = JSON.stringify({
249
+ model: config.model,
250
+ messages,
251
+ temperature: options?.temperature ?? config.temperature ?? 0.3,
252
+ ...(resolvedMaxTokens !== undefined ? { max_tokens: resolvedMaxTokens } : {}),
253
+ ...responseFormat,
254
+ ...thinkingParams,
255
+ ...config.extraParams,
256
+ });
257
+ // Wall-clock start for per-attempt usage telemetry (#576). Captured here so the
252
258
  // emitted duration covers the full request/response/parse cycle of a single
253
259
  // attempt, not the retry-wrapping `chatCompletion`.
254
260
  const requestStartedAt = Date.now();
255
- let response;
261
+ const requestDeadlineAt = timeoutMs === null ? null : requestStartedAt + timeoutMs;
262
+ const remainingAttemptMs = () => requestDeadlineAt === null ? undefined : Math.max(0, requestDeadlineAt - Date.now());
263
+ let terminalFields = {
264
+ model: config.model,
265
+ modelSource: "configured",
266
+ };
256
267
  try {
257
- response = await fetchWithTimeout(config.endpoint, {
258
- method: "POST",
259
- headers,
260
- body: JSON.stringify({
261
- model: config.model,
262
- messages,
263
- temperature: options?.temperature ?? config.temperature ?? 0.3,
264
- ...(resolvedMaxTokens !== undefined ? { max_tokens: resolvedMaxTokens } : {}),
265
- ...responseFormat,
266
- ...thinkingParams,
267
- ...config.extraParams,
268
- }),
269
- }, timeoutMs, options?.signal);
270
- }
271
- catch (err) {
272
- // fetchWithTimeout throws a plain Error with a message containing
273
- // "timed out" for AbortController-driven timeouts, or "aborted" for
274
- // caller-driven cancellations. Map both to typed LlmCallError.
275
- const msg = err instanceof Error ? err.message : String(err);
276
- if (err instanceof DOMException && err.name === "AbortError") {
277
- throw new LlmCallError(`Request timed out${timeoutMs === null ? "" : ` after ${timeoutMs}ms`}`, "timeout");
268
+ let response;
269
+ try {
270
+ response = await fetchWithTimeout(config.endpoint, {
271
+ method: "POST",
272
+ headers,
273
+ body: requestBody,
274
+ }, timeoutMs, options?.signal);
278
275
  }
279
- if (msg.includes("timed out")) {
280
- throw new LlmCallError(`Request timed out${timeoutMs === null ? "" : ` after ${timeoutMs}ms`}`, "timeout");
276
+ catch (err) {
277
+ // fetchWithTimeout throws a plain Error with a message containing
278
+ // "timed out" for AbortController-driven timeouts, or "aborted" for
279
+ // caller-driven cancellations. Map both to typed LlmCallError.
280
+ const msg = err instanceof Error ? err.message : String(err);
281
+ if (options?.signal?.aborted || msg.includes("Request aborted")) {
282
+ throw new LlmCallError("LLM request aborted", "aborted");
283
+ }
284
+ if (err instanceof DOMException && err.name === "AbortError") {
285
+ throw new LlmCallError(`Request timed out${timeoutMs === null ? "" : ` after ${timeoutMs}ms`}`, "timeout");
286
+ }
287
+ if (msg.includes("timed out")) {
288
+ throw new LlmCallError(`Request timed out${timeoutMs === null ? "" : ` after ${timeoutMs}ms`}`, "timeout");
289
+ }
290
+ throw new LlmCallError(`Network error: ${msg}`, "network_error");
281
291
  }
282
- throw new LlmCallError(`Network error: ${msg}`, "network_error");
283
- }
284
- if (!response.ok) {
285
- const rawBody = await response.text().catch(() => "");
286
- const safeBody = redactSensitiveText(redactErrorBody(rawBody), resolvedKey ? [resolvedKey] : []);
287
- const status = response.status;
288
- if (status === 429) {
289
- throw new LlmCallError(`LLM request rate limited (429) ${config.endpoint}: ${safeBody}`, "rate_limited", status);
292
+ if (!response.ok) {
293
+ const rawBody = await readBodyWithByteCap(response, undefined, {
294
+ ...(requestDeadlineAt === null ? {} : { bodyTimeoutMs: remainingAttemptMs() }),
295
+ signal: options?.signal,
296
+ }).catch((err) => {
297
+ if (options?.signal?.aborted)
298
+ throw new LlmCallError("LLM response read aborted", "aborted");
299
+ if (err instanceof Error && err.name === "BodyReadTimeoutError") {
300
+ throw new LlmCallError("LLM response body read timed out", "timeout");
301
+ }
302
+ return "";
303
+ });
304
+ const safeBody = redactSensitiveText(redactErrorBody(rawBody), resolvedKey ? [resolvedKey] : []);
305
+ const status = response.status;
306
+ if (status === 429) {
307
+ throw new LlmCallError(`LLM request rate limited (429) ${config.endpoint}: ${safeBody}`, "rate_limited", status);
308
+ }
309
+ if (status >= 500 && isHtmlResponse(rawBody)) {
310
+ throw new LlmCallError(`LLM provider returned HTML instead of JSON (${status}) ${config.endpoint}: ${redactSensitiveText(htmlExcerpt(rawBody), resolvedKey ? [resolvedKey] : [])}`, "provider_html_error", status);
311
+ }
312
+ if (status >= 500) {
313
+ throw new LlmCallError(`LLM provider error (${status}) ${config.endpoint}: ${safeBody}`, "provider_error", status);
314
+ }
315
+ throw new LlmCallError(`LLM request failed (${status}) ${config.endpoint}: ${safeBody}`, "provider_error", status);
290
316
  }
291
- if (status >= 500 && isHtmlResponse(rawBody)) {
292
- throw new LlmCallError(`LLM provider returned HTML instead of JSON (${status}) ${config.endpoint}: ${redactSensitiveText(htmlExcerpt(rawBody), resolvedKey ? [resolvedKey] : [])}`, "provider_html_error", status);
317
+ // A 2xx response is still an error if the body is HTML where JSON was
318
+ // expected (e.g. a provider serving its web UI). Read the raw body first so
319
+ // we can categorize an HTML page distinctly from a malformed-JSON parse_error.
320
+ let rawOkBody;
321
+ try {
322
+ rawOkBody = await readBodyWithByteCap(response, undefined, {
323
+ ...(requestDeadlineAt === null ? {} : { bodyTimeoutMs: remainingAttemptMs() }),
324
+ signal: options?.signal,
325
+ });
293
326
  }
294
- if (status >= 500) {
295
- throw new LlmCallError(`LLM provider error (${status}) ${config.endpoint}: ${safeBody}`, "provider_error", status);
327
+ catch (err) {
328
+ if (options?.signal?.aborted)
329
+ throw new LlmCallError("LLM response read aborted", "aborted");
330
+ if (err instanceof Error && err.name === "BodyReadTimeoutError") {
331
+ throw new LlmCallError("LLM response body read timed out", "timeout");
332
+ }
333
+ throw err;
296
334
  }
297
- throw new LlmCallError(`LLM request failed (${status}) ${config.endpoint}: ${safeBody}`, "provider_error", status);
298
- }
299
- // A 2xx response is still an error if the body is HTML where JSON was
300
- // expected (e.g. a provider serving its web UI). Read the raw body first so
301
- // we can categorize an HTML page distinctly from a malformed-JSON parse_error.
302
- const rawOkBody = await response.text();
303
- if (isHtmlResponse(rawOkBody)) {
304
- throw new LlmCallError(`LLM provider returned HTML instead of JSON (${response.status}) ${config.endpoint}: ${redactSensitiveText(htmlExcerpt(rawOkBody), resolvedKey ? [resolvedKey] : [])}`, "provider_html_error", response.status);
305
- }
306
- let json;
307
- try {
308
- json = JSON.parse(rawOkBody);
335
+ if (isHtmlResponse(rawOkBody)) {
336
+ throw new LlmCallError(`LLM provider returned HTML instead of JSON (${response.status}) ${config.endpoint}: ${redactSensitiveText(htmlExcerpt(rawOkBody), resolvedKey ? [resolvedKey] : [])}`, "provider_html_error", response.status);
337
+ }
338
+ let json;
339
+ try {
340
+ json = JSON.parse(rawOkBody);
341
+ }
342
+ catch {
343
+ throw new LlmCallError(`LLM response was not valid JSON ${config.endpoint}: ${redactSensitiveText(redactErrorBody(rawOkBody), resolvedKey ? [resolvedKey] : [])}`, "parse_error", response.status);
344
+ }
345
+ const responseModel = typeof json.model === "string" && json.model.trim().length > 0 ? json.model : undefined;
346
+ terminalFields = {
347
+ model: responseModel ?? config.model,
348
+ modelSource: responseModel === undefined ? "configured" : "response",
349
+ finishReason: typeof json.choices?.[0]?.finish_reason === "string" ? json.choices[0].finish_reason : undefined,
350
+ ...extractUsageTokens(json.usage),
351
+ };
352
+ const content = (json.choices?.[0]?.message?.content ?? "").trim();
353
+ const reasoning = (json.choices?.[0]?.message?.reasoning_content ?? "").trim();
354
+ const result = redactSensitiveText(content || reasoning, resolvedKey ? [resolvedKey] : []);
355
+ emitLlmUsage({
356
+ ...terminalFields,
357
+ outcome: "success",
358
+ durationMs: Date.now() - requestStartedAt,
359
+ });
360
+ return result;
309
361
  }
310
- catch {
311
- throw new LlmCallError(`LLM response was not valid JSON ${config.endpoint}: ${redactSensitiveText(redactErrorBody(rawOkBody), resolvedKey ? [resolvedKey] : [])}`, "parse_error", response.status);
362
+ catch (err) {
363
+ emitLlmUsage({
364
+ ...terminalFields,
365
+ outcome: "error",
366
+ durationMs: Date.now() - requestStartedAt,
367
+ errorCode: err instanceof LlmCallError ? err.code : "unknown_error",
368
+ });
369
+ throw err;
312
370
  }
313
- // Per-call usage telemetry (#576). Best-effort and fully isolated: a missing
314
- // or garbled usage block still records duration + model, and a throwing sink
315
- // can never fail the call (emitLlmUsage swallows its own errors). The stage
316
- // is supplied ambiently by emitLlmUsage; no `stage` param is threaded here.
317
- emitLlmUsage({
318
- model: typeof json.model === "string" && json.model ? json.model : config.model,
319
- durationMs: Date.now() - requestStartedAt,
320
- finishReason: typeof json.choices?.[0]?.finish_reason === "string" ? json.choices[0].finish_reason : undefined,
321
- ...extractUsageTokens(json.usage),
322
- });
323
- const content = (json.choices?.[0]?.message?.content ?? "").trim();
324
- const reasoning = (json.choices?.[0]?.message?.reasoning_content ?? "").trim();
325
- return redactSensitiveText(content || reasoning, resolvedKey ? [resolvedKey] : []);
326
- }
327
- /**
328
- * Strip `<think>` blocks, code fences, and escape control characters in JSON
329
- * strings. Thin wrapper kept for backward compatibility with call sites that
330
- * import `stripJsonFences` from this module. New code should prefer the
331
- * granular helpers from `../core/parse`.
332
- */
333
- export function stripJsonFences(raw) {
334
- return escapeJsonStringControls(stripCodeFences(stripThinkBlocks(raw)));
335
371
  }
336
372
  // ── Availability check ──────────────────────────────────────────────────────
337
373
  /**
@@ -7,7 +7,7 @@
7
7
  * Calls the configured `/embeddings` endpoint and L2-normalizes the returned
8
8
  * vectors so the scoring pipeline's L2-to-cosine conversion is correct.
9
9
  */
10
- import { fetchWithTimeout, isHttpUrl } from "../../core/common.js";
10
+ import { fetchWithTimeout, isHttpUrl, readBodyWithByteCap } from "../../core/common.js";
11
11
  import { resolveSecret } from "../../core/config/config.js";
12
12
  const DEFAULT_REMOTE_BATCH_SIZE = 100;
13
13
  /** Cheap token estimator: 4 chars ≈ 1 token. Used in verbose logging and error messages. */
@@ -49,10 +49,14 @@ export class RemoteEmbedder {
49
49
  body: JSON.stringify(body),
50
50
  }, 30_000, signal);
51
51
  if (!response.ok) {
52
- const errBody = await response.text().catch(() => "");
52
+ const errBody = await readBodyWithByteCap(response, undefined, { bodyTimeoutMs: 30_000, signal }).catch((err) => {
53
+ if (signal?.aborted)
54
+ throw err;
55
+ return "";
56
+ });
53
57
  throw new Error(`Embedding request failed (${response.status}): ${errBody}`);
54
58
  }
55
- const json = (await response.json());
59
+ const json = JSON.parse(await readBodyWithByteCap(response, undefined, { bodyTimeoutMs: 30_000, signal }));
56
60
  if (!json.data?.[0]?.embedding) {
57
61
  throw new Error(`Unexpected embedding response format: missing data[0].embedding.${embeddingEndpointPathHint(this.endpoint)}`);
58
62
  }
@@ -85,10 +89,14 @@ export class RemoteEmbedder {
85
89
  body: JSON.stringify(body),
86
90
  }, 30_000, signal);
87
91
  if (!response.ok) {
88
- const respBody = await response.text().catch(() => "");
92
+ const respBody = await readBodyWithByteCap(response, undefined, { bodyTimeoutMs: 30_000, signal }).catch((err) => {
93
+ if (signal?.aborted)
94
+ throw err;
95
+ return "";
96
+ });
89
97
  throw new Error(`Embedding batch request failed (${response.status}): ${respBody}`);
90
98
  }
91
- const json = (await response.json());
99
+ const json = JSON.parse(await readBodyWithByteCap(response, undefined, { bodyTimeoutMs: 30_000, signal }));
92
100
  if (!json.data || json.data.length !== batch.length) {
93
101
  throw new Error(`Unexpected embedding batch response: expected ${batch.length} embeddings, got ${json.data?.length ?? 0}.${embeddingEndpointPathHint(this.endpoint)}`);
94
102
  }
@@ -3,16 +3,11 @@
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  /**
5
5
  * For each feature key, return the effective enabled state by reading the
6
- * 0.9.0 config shape. Defaults match the legacy `LlmFeatureFlags` docstrings.
6
+ * 0.9.0 config shape.
7
7
  */
8
- // Defaults below mirror the legacy LlmFeatureFlags docstrings so existing
9
- // behaviour is preserved when a config is silent on a flag.
10
8
  const FEATURE_LOCATION = {
11
- // Legacy default: true
12
9
  memory_inference: (cfg) => cfg.index?.memory?.enabled ?? true,
13
- // Legacy default: true
14
10
  graph_extraction: (cfg) => cfg.index?.graph?.enabled ?? true,
15
- // Legacy default: false
16
11
  metadata_enhance: (cfg) => cfg.index?.metadataEnhance?.enabled ?? false,
17
12
  // Always on at the LLM-wrapper level. Enablement is decided ONCE at the
18
13
  // extract entry point (`akmExtract`): the `extract.enabled` process toggle
@@ -26,8 +21,7 @@ const FEATURE_LOCATION = {
26
21
  /**
27
22
  * Pure predicate: is the named feature gate enabled in `config`?
28
23
  *
29
- * Reads from the unified 0.9.0 config shape. Defaults follow the legacy
30
- * `LlmFeatureFlags` docstring defaults.
24
+ * Reads from the unified 0.9.0 config shape.
31
25
  */
32
26
  export function isLlmFeatureEnabled(config, feature, improveEnabled) {
33
27
  if (!config)
@@ -71,10 +65,8 @@ export async function tryLlmFeature(feature, config, fn, fallback, opts) {
71
65
  }
72
66
  }
73
67
  /**
74
- * Section-agnostic process gate. After the 0.8.0 migration, the canonical
75
- * accessor is the `FEATURE_LOCATION` map above; this helper exists so older
76
- * call sites that knew the (section, processName) pair don't all need to
77
- * relearn the new mapping.
68
+ * Section-agnostic process gate that maps process identifiers to their current
69
+ * config locations.
78
70
  *
79
71
  * For unknown (section, processName) pairs the result is `false`.
80
72
  */
@@ -7,7 +7,7 @@
7
7
  * Given a single asset body (typically a `memory:` or `knowledge:` file),
8
8
  * asks the configured LLM to surface the entities mentioned in it and the
9
9
  * relations between them. The pass itself
10
- * (`src/indexer/graph-extraction.ts`) is responsible for deciding which
10
+ * (`src/indexer/graph/graph-extraction.ts`) is responsible for deciding which
11
11
  * files to extract, persisting the resulting nodes/edges to the index DB,
12
12
  * and feeding the graph data into the FTS5+boosts
13
13
  * search pipeline as a single boost component.
@@ -15,16 +15,15 @@
15
15
  * This module is intentionally tiny and stateless so tests can stub it via
16
16
  * `mock.module("../src/llm/graph-extract", ...)` without hitting a network.
17
17
  *
18
- * Locked v1 contract (#208): the LLM connection always comes from the
19
- * selected named LLM engine. Callers obtain
20
- * the connection via `resolveIndexPassLLM("graph", config)` and pass it
21
- * straight through.
18
+ * The LLM connection comes from the selected named engine. Callers obtain it
19
+ * via `resolveIndexPassLLM("graph", config)` and pass it straight through.
22
20
  */
23
21
  import systemPromptTemplate from "../assets/prompts/graph-extract-system.md" with { type: "text" };
24
22
  import userPromptTemplate from "../assets/prompts/graph-extract-user-prompt.md" with { type: "text" };
25
23
  import { toErrorMessage } from "../core/common.js";
24
+ import { parseEmbeddedJsonResponse } from "../core/parse.js";
26
25
  import { warn, warnVerbose } from "../core/warn.js";
27
- import { chatCompletion, isContextSizeError, parseEmbeddedJsonResponse } from "./client.js";
26
+ import { chatCompletion, isContextSizeError } from "./client.js";
28
27
  import { tryLlmFeature } from "./feature-gate.js";
29
28
  import { callStructured } from "./structured-call.js";
30
29
  /**
@@ -47,11 +46,6 @@ const SYSTEM_PROMPT = systemPromptTemplate;
47
46
  const USER_PROMPT_PREFIX = userPromptTemplate
48
47
  .replace("{{MAX_ENTITIES}}", String(MAX_ENTITIES_PER_ASSET))
49
48
  .replace("{{MAX_RELATIONS}}", String(MAX_RELATIONS_PER_ASSET));
50
- // `isContextSizeError` is defined in `./client` and re-exported here so the
51
- // graph extractor and the retry classifier (`isRetryable`) share one
52
- // definition (#496). Re-exported (not just imported) to preserve existing
53
- // importers of this module — including its unit test.
54
- export { isContextSizeError } from "./client.js";
55
49
  const GENERIC_ENTITIES = new Set([
56
50
  "agent",
57
51
  "application",
@@ -1,4 +1,147 @@
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
- export { compressMemoryToDerivedMemory, } from "./memory-infer-impl.js";
4
+ /**
5
+ * LLM helper for the `akm index` memory-inference pass (#201).
6
+ *
7
+ * Compresses a single memory body into one higher-signal derived memory. The
8
+ * pass itself (in `src/indexer/passes/memory-inference.ts`) is responsible for
9
+ * deciding which memories are pending, persisting the derived memory with the
10
+ * correct frontmatter (`inferred: true`, `source: <parent-ref>`), and marking
11
+ * the parent as processed for idempotency.
12
+ *
13
+ * This module is intentionally tiny and stateless so tests can stub it via
14
+ * `mock.module("../src/llm/memory-infer", ...)` without hitting a network.
15
+ *
16
+ * The LLM connection comes from the selected named engine. Callers obtain it
17
+ * via `resolveIndexPassLLM("memory", config)` and pass it straight through.
18
+ */
19
+ import memoryInferSystemPrompt from "../assets/prompts/memory-infer-system.md" with { type: "text" };
20
+ import memoryInferUserPrompt from "../assets/prompts/memory-infer-user.md" with { type: "text" };
21
+ import { toErrorMessage } from "../core/common.js";
22
+ import { parseEmbeddedJsonResponse } from "../core/parse.js";
23
+ import { warn } from "../core/warn.js";
24
+ import { callStructured } from "./structured-call.js";
25
+ /** Hard cap on body chars sent to the model — pragmatic and matches `runLlmEnrich`. */
26
+ const MAX_BODY_CHARS = 4000;
27
+ const SYSTEM_PROMPT = memoryInferSystemPrompt;
28
+ const USER_PROMPT_PREFIX = memoryInferUserPrompt;
29
+ const PROMPT_PLACEHOLDERS = new Set([
30
+ "short title string",
31
+ "one sentence summary string",
32
+ "tag1",
33
+ "tag2",
34
+ "search phrase 1",
35
+ "search phrase 2",
36
+ "2-3 sentence compressed body preserving key facts verbatim",
37
+ ]);
38
+ /**
39
+ * Strict JSON Schema for the derived-memory payload. Sent to providers that
40
+ * opt in via `ChatCompletionConfig.supportsJsonSchema = true`; the client
41
+ * silently drops the schema for providers that don't.
42
+ *
43
+ * Extends the responseSchema lift (PR 1, asset-writers-investigation §5) to
44
+ * the memory-inference path. Mirrors the validation gate below
45
+ * (title/description/content + non-empty tags/searchHints) so a
46
+ * schema-compliant response is guaranteed to pass the downstream check
47
+ * — no more "incomplete derived memory payload from LLM; skipping memory"
48
+ * for shape-only failures.
49
+ */
50
+ const DERIVED_MEMORY_JSON_SCHEMA = {
51
+ type: "object",
52
+ properties: {
53
+ title: { type: "string", minLength: 1 },
54
+ description: { type: "string", minLength: 1 },
55
+ content: { type: "string", minLength: 1 },
56
+ tags: { type: "array", items: { type: "string" }, minItems: 1, maxItems: 8 },
57
+ searchHints: { type: "array", items: { type: "string" }, minItems: 1, maxItems: 6 },
58
+ },
59
+ required: ["title", "description", "content", "tags", "searchHints"],
60
+ additionalProperties: false,
61
+ };
62
+ /**
63
+ * Compress a single memory body into one derived memory via the configured LLM.
64
+ *
65
+ * Returns `undefined` on any failure (timeout, invalid JSON, empty response).
66
+ * Errors are logged via `warn()` but never thrown — a failed split for one memory
67
+ * must not abort the rest of the index pass.
68
+ *
69
+ * Routes through `callStructured({ feature: "memory_inference", ... })` so the
70
+ * feature gate, error classification, and onFallback hook are honoured uniformly
71
+ * (Fix C5).
72
+ */
73
+ export async function compressMemoryToDerivedMemory(llmConfig, body, signal, akmConfig, onFallback, telemetry, onRetryAttempt) {
74
+ const trimmedBody = body.trim();
75
+ if (!trimmedBody)
76
+ return undefined;
77
+ const userPrompt = `${USER_PROMPT_PREFIX}${trimmedBody.slice(0, MAX_BODY_CHARS)}`;
78
+ // Memory inference is always gated: no config closes the gate instead of
79
+ // taking callStructured's ungated path for direct callers.
80
+ if (!akmConfig) {
81
+ onFallback?.({ feature: "memory_inference", reason: "disabled" });
82
+ return undefined;
83
+ }
84
+ return callStructured({
85
+ feature: "memory_inference",
86
+ akmConfig,
87
+ config: llmConfig,
88
+ messages: [
89
+ { role: "system", content: SYSTEM_PROMPT },
90
+ { role: "user", content: userPrompt },
91
+ ],
92
+ request: {
93
+ temperature: 0.1,
94
+ timeoutMs: llmConfig.timeoutMs,
95
+ signal,
96
+ responseSchema: DERIVED_MEMORY_JSON_SCHEMA,
97
+ onRetryAttempt,
98
+ },
99
+ parse: (raw) => {
100
+ if (!raw)
101
+ return undefined;
102
+ const parsed = parseEmbeddedJsonResponse(raw);
103
+ if (!parsed) {
104
+ warn("memory inference: invalid JSON response from LLM; skipping memory.");
105
+ return undefined;
106
+ }
107
+ const title = typeof parsed.title === "string" ? parsed.title.trim() : "";
108
+ const description = typeof parsed.description === "string" ? parsed.description.trim() : "";
109
+ const content = typeof parsed.content === "string" ? parsed.content.trim() : "";
110
+ const tags = Array.isArray(parsed.tags)
111
+ ? parsed.tags
112
+ .filter((t) => typeof t === "string")
113
+ .map((t) => t.trim())
114
+ .filter(Boolean)
115
+ .slice(0, 8)
116
+ : [];
117
+ const searchHints = Array.isArray(parsed.searchHints)
118
+ ? parsed.searchHints
119
+ .filter((h) => typeof h === "string")
120
+ .map((h) => h.trim())
121
+ .filter(Boolean)
122
+ .slice(0, 6)
123
+ : [];
124
+ if (!title || !description || !content || tags.length === 0 || searchHints.length === 0) {
125
+ warn("memory inference: incomplete derived memory payload from LLM; skipping memory.");
126
+ return undefined;
127
+ }
128
+ if ([title, description, content, ...tags, ...searchHints].some((value) => PROMPT_PLACEHOLDERS.has(value.toLowerCase()))) {
129
+ warn("memory inference: prompt placeholder response from LLM; skipping memory.");
130
+ return undefined;
131
+ }
132
+ return { title, description, tags, searchHints, content };
133
+ },
134
+ onError: (cls, err) => {
135
+ if (cls === "html") {
136
+ if (telemetry)
137
+ telemetry.htmlErrorCount = (telemetry.htmlErrorCount ?? 0) + 1;
138
+ warn(`memory inference: provider returned HTML instead of JSON; skipping memory: ${toErrorMessage(err)}`);
139
+ return undefined;
140
+ }
141
+ warn(`memory inference failed: ${toErrorMessage(err)}`);
142
+ return undefined;
143
+ },
144
+ fallback: undefined,
145
+ onFallback,
146
+ });
147
+ }
@@ -4,12 +4,11 @@
4
4
  /**
5
5
  * LLM-driven metadata enhancement for stash entries.
6
6
  *
7
- * Split out of `llm.ts` so the higher-level workflow (prompting the LLM to
8
- * improve descriptions/tags/searchHints) lives separately from the low-level
9
- * transport client in `client.ts`.
7
+ * Keeps the higher-level workflow for improving descriptions, tags, and search
8
+ * hints separate from the low-level transport client.
10
9
  */
11
10
  import metadataEnhanceSystemPrompt from "../assets/prompts/metadata-enhance-system.md" with { type: "text" };
12
- import { parseJsonResponse } from "./client.js";
11
+ import { parseJsonResponse } from "../core/parse.js";
13
12
  import { callStructured } from "./structured-call.js";
14
13
  const SYSTEM_PROMPT = metadataEnhanceSystemPrompt;
15
14
  /**
@@ -20,9 +19,8 @@ const SYSTEM_PROMPT = metadataEnhanceSystemPrompt;
20
19
  * `tryLlmFeature("metadata_enhance", ...)` so the feature gate is honoured: a
21
20
  * closed gate yields `{ status: "skipped" }` and a thrown/timed-out call yields
22
21
  * `{ status: "failed" }` — neither is reported as an enrichment. When
23
- * `akmConfig` is `undefined` the gate is bypassed entirely — the LLM call runs
24
- * unconditionally and errors propagate to the caller (pre-gate behaviour, used
25
- * by direct callers such as tests).
22
+ * `akmConfig` is `undefined` the gate is bypassed entirely: the LLM call runs
23
+ * unconditionally and errors propagate to direct callers such as tests.
26
24
  */
27
25
  export async function enhanceMetadata(config, entry, fileContent, signal, akmConfig) {
28
26
  const contextParts = [`Name: ${entry.name}`, `Type: ${entry.type}`];
@@ -1,7 +1,7 @@
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
- import { chatCompletion, isContextSizeError, LlmCallError } from "./client.js";
4
+ import { chatCompletion, isContextSizeError, LlmCallError, } from "./client.js";
5
5
  import { tryLlmFeature } from "./feature-gate.js";
6
6
  /**
7
7
  * Classify a thrown LLM error into one of the three buckets. This is the single