akm-cli 0.8.6 → 0.8.14

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 (324) hide show
  1. package/CHANGELOG.md +442 -0
  2. package/dist/assets/help/help-proposals.md +1 -2
  3. package/dist/assets/hints/cli-hints-full.md +34 -19
  4. package/dist/assets/hints/cli-hints-short.md +1 -1
  5. package/dist/assets/profiles/catchup.json +13 -0
  6. package/dist/assets/profiles/consolidate.json +13 -0
  7. package/dist/assets/profiles/frequent.json +13 -0
  8. package/dist/assets/tasks/core/backup.yml +4 -0
  9. package/dist/assets/tasks/core/extract.yml +4 -0
  10. package/dist/assets/tasks/core/improve.yml +4 -0
  11. package/dist/assets/tasks/core/index-refresh.yml +4 -0
  12. package/dist/assets/tasks/core/sync.yml +4 -0
  13. package/dist/assets/tasks/core/update-stashes.yml +4 -0
  14. package/dist/assets/tasks/core/version-check.yml +4 -0
  15. package/dist/assets/templates/html/default.html +78 -0
  16. package/dist/assets/templates/html/health.html +560 -0
  17. package/dist/assets/templates/html/vendor/echarts.min.js +45 -0
  18. package/dist/cli/config-migrate.js +6 -6
  19. package/dist/cli/config-validate.js +4 -4
  20. package/dist/cli/confirm.js +3 -3
  21. package/dist/cli/parse-args.js +1 -1
  22. package/dist/cli/shared.js +72 -19
  23. package/dist/cli-node.mjs +26 -0
  24. package/dist/cli.js +206 -3866
  25. package/dist/commands/{agent-dispatch.js → agent/agent-dispatch.js} +6 -6
  26. package/dist/commands/{agent-support.js → agent/agent-support.js} +2 -2
  27. package/dist/commands/agent/contribute-cli.js +200 -0
  28. package/dist/commands/completions.js +1 -1
  29. package/dist/commands/config-cli.js +230 -3
  30. package/dist/commands/db-cli.js +2 -2
  31. package/dist/commands/env/env-cli.js +529 -0
  32. package/dist/commands/env/env.js +410 -0
  33. package/dist/commands/env/secret-cli.js +259 -0
  34. package/dist/commands/{secret.js → env/secret.js} +6 -47
  35. package/dist/commands/events.js +4 -4
  36. package/dist/commands/feedback-cli.js +18 -34
  37. package/dist/commands/graph/graph-cli.js +132 -0
  38. package/dist/commands/{graph.js → graph/graph.js} +22 -16
  39. package/dist/commands/health/checks.js +279 -0
  40. package/dist/commands/health/html-report.js +448 -0
  41. package/dist/commands/health.js +189 -266
  42. package/dist/commands/{consolidate.js → improve/consolidate.js} +63 -38
  43. package/dist/commands/{distill-promotion-policy.js → improve/distill-promotion-policy.js} +3 -3
  44. package/dist/commands/{distill.js → improve/distill.js} +39 -18
  45. package/dist/commands/{eval-cases.js → improve/eval-cases.js} +1 -1
  46. package/dist/commands/{extract-cli.js → improve/extract-cli.js} +4 -4
  47. package/dist/commands/{extract-prompt.js → improve/extract-prompt.js} +2 -2
  48. package/dist/commands/{extract.js → improve/extract.js} +221 -26
  49. package/dist/commands/{improve-auto-accept.js → improve/improve-auto-accept.js} +30 -4
  50. package/dist/commands/{improve-cli.js → improve/improve-cli.js} +44 -22
  51. package/dist/commands/{improve-profiles.js → improve/improve-profiles.js} +13 -7
  52. package/dist/commands/{improve-result-file.js → improve/improve-result-file.js} +1 -1
  53. package/dist/commands/{improve.js → improve/improve.js} +672 -292
  54. package/dist/{core → commands/improve/memory}/memory-belief.js +2 -2
  55. package/dist/{core → commands/improve/memory}/memory-contradiction-detect.js +5 -5
  56. package/dist/{core → commands/improve/memory}/memory-improve.js +4 -4
  57. package/dist/commands/improve/reflect-noise.js +0 -0
  58. package/dist/commands/{reflect.js → improve/reflect.js} +58 -28
  59. package/dist/commands/improve/session-asset.js +248 -0
  60. package/dist/commands/lint/agent-linter.js +1 -1
  61. package/dist/commands/lint/base-linter.js +55 -37
  62. package/dist/commands/lint/command-linter.js +1 -1
  63. package/dist/commands/lint/default-linter.js +1 -1
  64. package/dist/commands/lint/env-key-rules.js +1 -1
  65. package/dist/commands/lint/index.js +19 -25
  66. package/dist/commands/lint/knowledge-linter.js +1 -1
  67. package/dist/commands/lint/memory-linter.js +1 -1
  68. package/dist/commands/lint/registry.js +8 -8
  69. package/dist/commands/lint/skill-linter.js +1 -1
  70. package/dist/commands/lint/task-linter.js +1 -1
  71. package/dist/commands/lint/workflow-linter.js +1 -1
  72. package/dist/commands/lint.js +1 -1
  73. package/dist/commands/observability-cli.js +244 -0
  74. package/dist/commands/proposal/drain-policies.js +3 -3
  75. package/dist/commands/proposal/drain.js +87 -15
  76. package/dist/commands/proposal/proposal-cli.js +490 -0
  77. package/dist/commands/{proposal.js → proposal/proposal.js} +17 -6
  78. package/dist/commands/{propose.js → proposal/propose.js} +11 -11
  79. package/dist/{core → commands/proposal/validators}/proposal-quality-validators.js +8 -3
  80. package/dist/{core → commands/proposal/validators}/proposal-validators.js +5 -5
  81. package/dist/{core → commands/proposal/validators}/proposals.js +374 -345
  82. package/dist/commands/{curate.js → read/curate.js} +7 -7
  83. package/dist/commands/{knowledge.js → read/knowledge.js} +22 -9
  84. package/dist/commands/{registry-search.js → read/registry-search.js} +5 -5
  85. package/dist/commands/{remember-cli.js → read/remember-cli.js} +15 -7
  86. package/dist/commands/read/search-cli.js +207 -0
  87. package/dist/commands/{search.js → read/search.js} +22 -27
  88. package/dist/commands/{show.js → read/show.js} +31 -45
  89. package/dist/commands/registry-cli.js +8 -8
  90. package/dist/commands/remember.js +14 -10
  91. package/dist/commands/sources/add-cli.js +293 -0
  92. package/dist/commands/{history.js → sources/history.js} +27 -25
  93. package/dist/commands/{info.js → sources/info.js} +6 -6
  94. package/dist/commands/{init.js → sources/init.js} +6 -6
  95. package/dist/commands/{installed-stashes.js → sources/installed-stashes.js} +12 -12
  96. package/dist/commands/{migration-help.js → sources/migration-help.js} +3 -2
  97. package/dist/commands/{schema-repair.js → sources/schema-repair.js} +8 -8
  98. package/dist/commands/{self-update.js → sources/self-update.js} +10 -9
  99. package/dist/commands/{source-add.js → sources/source-add.js} +10 -10
  100. package/dist/commands/{source-clone.js → sources/source-clone.js} +7 -7
  101. package/dist/commands/{source-manage.js → sources/source-manage.js} +4 -4
  102. package/dist/commands/sources/sources-cli.js +305 -0
  103. package/dist/commands/sources/stash-cli.js +219 -0
  104. package/dist/commands/{stash-skeleton.js → sources/stash-skeleton.js} +2 -1
  105. package/dist/commands/tasks/default-tasks.js +173 -0
  106. package/dist/commands/tasks/tasks-cli.js +210 -0
  107. package/dist/commands/{tasks.js → tasks/tasks.js} +14 -14
  108. package/dist/commands/wiki-cli.js +307 -0
  109. package/dist/commands/workflow-cli.js +329 -0
  110. package/dist/core/action-contributors.js +1 -1
  111. package/dist/core/assert.js +40 -0
  112. package/dist/core/asset/asset-create.js +54 -0
  113. package/dist/core/{asset-ref.js → asset/asset-ref.js} +21 -4
  114. package/dist/core/{asset-registry.js → asset/asset-registry.js} +3 -3
  115. package/dist/core/{asset-spec.js → asset/asset-spec.js} +17 -31
  116. package/dist/core/{markdown.js → asset/markdown.js} +1 -1
  117. package/dist/core/{stash-meta.js → asset/stash-meta.js} +1 -1
  118. package/dist/core/best-effort.js +64 -0
  119. package/dist/core/common.js +32 -18
  120. package/dist/core/{config-io.js → config/config-io.js} +29 -19
  121. package/dist/core/{config-migration.js → config/config-migration.js} +11 -9
  122. package/dist/core/{config-schema.js → config/config-schema.js} +50 -7
  123. package/dist/core/config/config-types.js +16 -0
  124. package/dist/core/{config-walker.js → config/config-walker.js} +2 -2
  125. package/dist/core/{config.js → config/config.js} +10 -8
  126. package/dist/core/env-secret-ref.js +90 -0
  127. package/dist/core/errors.js +13 -3
  128. package/dist/core/events.js +27 -4
  129. package/dist/core/file-lock.js +1 -1
  130. package/dist/core/improve-types.js +48 -0
  131. package/dist/core/lesson-lint.js +2 -2
  132. package/dist/core/logs-db.js +304 -0
  133. package/dist/core/paths.js +2 -2
  134. package/dist/core/ripgrep/install.js +2 -2
  135. package/dist/core/ripgrep/resolve.js +2 -2
  136. package/dist/core/state-db.js +195 -60
  137. package/dist/core/text-truncation.js +148 -0
  138. package/dist/core/time.js +1 -1
  139. package/dist/core/write-source.js +98 -85
  140. package/dist/indexer/{db-backup.js → db/db-backup.js} +9 -24
  141. package/dist/indexer/{db.js → db/db.js} +128 -118
  142. package/dist/indexer/{graph-db.js → db/graph-db.js} +9 -4
  143. package/dist/indexer/{llm-cache.js → db/llm-cache.js} +15 -12
  144. package/dist/indexer/ensure-index.js +4 -4
  145. package/dist/indexer/{graph-boost.js → graph/graph-boost.js} +1 -1
  146. package/dist/indexer/{graph-extraction.js → graph/graph-extraction.js} +55 -13
  147. package/dist/indexer/indexer.js +37 -30
  148. package/dist/indexer/init.js +54 -0
  149. package/dist/indexer/manifest.js +10 -10
  150. package/dist/indexer/{memory-inference.js → passes/memory-inference.js} +141 -33
  151. package/dist/indexer/{metadata-contributors.js → passes/metadata-contributors.js} +10 -8
  152. package/dist/indexer/{metadata.js → passes/metadata.js} +15 -19
  153. package/dist/indexer/{staleness-detect.js → passes/staleness-detect.js} +53 -12
  154. package/dist/indexer/{db-search.js → search/db-search.js} +28 -16
  155. package/dist/indexer/{ranking-contributors.js → search/ranking-contributors.js} +1 -1
  156. package/dist/indexer/{ranking.js → search/ranking.js} +2 -2
  157. package/dist/indexer/{search-hit-enrichers.js → search/search-hit-enrichers.js} +3 -3
  158. package/dist/indexer/{search-source.js → search/search-source.js} +8 -8
  159. package/dist/indexer/{semantic-status.js → search/semantic-status.js} +3 -3
  160. package/dist/indexer/usage/unmigrated-vaults-guard.js +94 -0
  161. package/dist/indexer/{usage-events.js → usage/usage-events.js} +32 -0
  162. package/dist/indexer/{file-context.js → walk/file-context.js} +10 -15
  163. package/dist/indexer/{matchers.js → walk/matchers.js} +13 -9
  164. package/dist/indexer/{path-resolver.js → walk/path-resolver.js} +6 -6
  165. package/dist/indexer/{project-context.js → walk/project-context.js} +1 -1
  166. package/dist/indexer/{walker.js → walk/walker.js} +4 -3
  167. package/dist/integrations/agent/builder-shared.js +39 -0
  168. package/dist/integrations/agent/builders.js +14 -81
  169. package/dist/integrations/agent/config.js +6 -4
  170. package/dist/integrations/agent/detect.js +1 -1
  171. package/dist/integrations/agent/index.js +23 -8
  172. package/dist/integrations/agent/prompts.js +2 -3
  173. package/dist/integrations/agent/runner.js +22 -3
  174. package/dist/integrations/agent/spawn.js +9 -10
  175. package/dist/integrations/harnesses/claude/agent-builder.js +48 -0
  176. package/dist/integrations/harnesses/claude/config-import.js +70 -0
  177. package/dist/integrations/harnesses/claude/index.js +64 -0
  178. package/dist/integrations/{session-logs/providers/claude-code.js → harnesses/claude/session-log.js} +32 -5
  179. package/dist/integrations/harnesses/index.js +144 -0
  180. package/dist/integrations/harnesses/opencode/agent-builder.js +43 -0
  181. package/dist/integrations/harnesses/opencode/config-import.js +82 -0
  182. package/dist/integrations/harnesses/opencode/index.js +59 -0
  183. package/dist/integrations/{session-logs/providers/opencode.js → harnesses/opencode/session-log.js} +1 -1
  184. package/dist/integrations/harnesses/opencode-sdk/index.js +49 -0
  185. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +234 -0
  186. package/dist/integrations/harnesses/types.js +43 -0
  187. package/dist/integrations/lockfile.js +7 -16
  188. package/dist/integrations/session-logs/index.js +82 -9
  189. package/dist/llm/call-ai.js +4 -4
  190. package/dist/llm/client.js +146 -6
  191. package/dist/llm/embedder.js +6 -6
  192. package/dist/llm/embedders/local.js +9 -22
  193. package/dist/llm/embedders/remote.js +2 -2
  194. package/dist/llm/embedders/types.js +1 -1
  195. package/dist/llm/graph-extract.js +31 -12
  196. package/dist/llm/index-passes.js +1 -1
  197. package/dist/llm/memory-infer.js +12 -5
  198. package/dist/llm/metadata-enhance.js +2 -2
  199. package/dist/llm/usage-persist.js +77 -0
  200. package/dist/llm/usage-telemetry.js +103 -0
  201. package/dist/output/context.js +9 -46
  202. package/dist/output/html-render.js +73 -0
  203. package/dist/output/renderers.js +88 -58
  204. package/dist/output/shapes/curate.js +7 -3
  205. package/dist/output/shapes/distill.js +7 -3
  206. package/dist/output/shapes/env-list.js +18 -16
  207. package/dist/output/shapes/events.js +5 -4
  208. package/dist/output/shapes/helpers.js +19 -5
  209. package/dist/output/shapes/history.js +7 -3
  210. package/dist/output/shapes/passthrough.js +8 -11
  211. package/dist/output/shapes/{proposal-accept.js → proposal/accept.js} +7 -3
  212. package/dist/output/shapes/{proposal-diff.js → proposal/diff.js} +7 -3
  213. package/dist/output/shapes/{proposal-list.js → proposal/list.js} +7 -3
  214. package/dist/output/shapes/{proposal-producer.js → proposal/producer.js} +5 -4
  215. package/dist/output/shapes/{proposal-reject.js → proposal/reject.js} +7 -3
  216. package/dist/output/shapes/{proposal-show.js → proposal/show.js} +7 -3
  217. package/dist/output/shapes/registry-search.js +7 -3
  218. package/dist/output/shapes/registry.js +12 -0
  219. package/dist/output/shapes/search.js +7 -3
  220. package/dist/output/shapes/secret-list.js +18 -16
  221. package/dist/output/shapes/show.js +7 -3
  222. package/dist/output/shapes.js +55 -30
  223. package/dist/output/text/add.js +2 -3
  224. package/dist/output/text/clone.js +2 -3
  225. package/dist/output/text/config.js +2 -3
  226. package/dist/output/text/curate.js +4 -3
  227. package/dist/output/text/distill.js +2 -3
  228. package/dist/output/text/enable-disable.js +5 -4
  229. package/dist/output/text/env.js +13 -0
  230. package/dist/output/text/events.js +5 -4
  231. package/dist/output/text/feedback.js +4 -3
  232. package/dist/output/text/helpers.js +123 -40
  233. package/dist/output/text/history.js +2 -3
  234. package/dist/output/text/import.js +2 -3
  235. package/dist/output/text/index.js +2 -3
  236. package/dist/output/text/info.js +2 -3
  237. package/dist/output/text/init.js +2 -3
  238. package/dist/output/text/list.js +2 -3
  239. package/dist/output/text/proposal/producer.js +9 -0
  240. package/dist/output/text/proposal/proposal.js +13 -0
  241. package/dist/output/text/registry-commands.js +8 -7
  242. package/dist/output/text/registry.js +12 -0
  243. package/dist/output/text/remember.js +4 -3
  244. package/dist/output/text/remove.js +2 -3
  245. package/dist/output/text/save.js +2 -3
  246. package/dist/output/text/search.js +4 -3
  247. package/dist/output/text/show.js +4 -3
  248. package/dist/output/text/update.js +2 -3
  249. package/dist/output/text/upgrade.js +2 -3
  250. package/dist/output/text/wiki.js +12 -11
  251. package/dist/output/text/workflow.js +12 -10
  252. package/dist/output/text.js +66 -32
  253. package/dist/registry/build-index.js +11 -10
  254. package/dist/registry/factory.js +1 -1
  255. package/dist/registry/origin-resolve.js +1 -1
  256. package/dist/registry/providers/index.js +2 -2
  257. package/dist/registry/providers/skills-sh.js +91 -72
  258. package/dist/registry/providers/static-index.js +75 -52
  259. package/dist/registry/resolve.js +3 -3
  260. package/dist/runtime.js +242 -0
  261. package/dist/scripts/migrate-storage.js +1654 -683
  262. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +254 -168
  263. package/dist/setup/detect.js +311 -9
  264. package/dist/setup/harness-config-import.js +6 -120
  265. package/dist/setup/setup.js +454 -43
  266. package/dist/sources/include.js +1 -1
  267. package/dist/sources/provider-factory.js +2 -2
  268. package/dist/sources/providers/filesystem.js +3 -3
  269. package/dist/sources/providers/git.js +9 -9
  270. package/dist/sources/providers/index.js +4 -4
  271. package/dist/sources/providers/npm.js +6 -6
  272. package/dist/sources/providers/provider-utils.js +13 -20
  273. package/dist/sources/providers/sync-from-ref.js +5 -5
  274. package/dist/sources/providers/tar-utils.js +2 -2
  275. package/dist/sources/providers/website.js +2 -2
  276. package/dist/sources/resolve.js +5 -5
  277. package/dist/sources/website-ingest.js +5 -5
  278. package/dist/storage/database.js +102 -0
  279. package/dist/storage/engines/sqlite-migrations.js +42 -0
  280. package/dist/storage/locations.js +25 -0
  281. package/dist/storage/repositories/index-db.js +43 -0
  282. package/dist/storage/repositories/workflow-runs-repository.js +141 -0
  283. package/dist/tasks/backends/cron.js +4 -4
  284. package/dist/tasks/backends/exec-utils.js +32 -0
  285. package/dist/tasks/backends/index.js +3 -3
  286. package/dist/tasks/backends/launchd.js +7 -14
  287. package/dist/tasks/backends/schtasks.js +7 -16
  288. package/dist/tasks/embedded.js +71 -0
  289. package/dist/tasks/parser.js +2 -2
  290. package/dist/tasks/resolveAkmBin.js +1 -1
  291. package/dist/tasks/runner.js +127 -31
  292. package/dist/tasks/schedule.js +1 -1
  293. package/dist/tasks/validator.js +7 -7
  294. package/dist/text-import-hook.mjs +51 -0
  295. package/dist/version.js +2 -1
  296. package/dist/wiki/wiki.js +7 -7
  297. package/dist/workflows/{authoring.js → authoring/authoring.js} +6 -6
  298. package/dist/workflows/{scope-key.js → authoring/scope-key.js} +1 -1
  299. package/dist/workflows/cli.js +1 -1
  300. package/dist/workflows/db.js +54 -32
  301. package/dist/workflows/parser.js +4 -4
  302. package/dist/workflows/renderer.js +5 -5
  303. package/dist/workflows/runtime/agent-identity.js +56 -0
  304. package/dist/workflows/runtime/checkin.js +57 -0
  305. package/dist/workflows/{runs.js → runtime/runs.js} +197 -101
  306. package/dist/workflows/validate-summary.js +82 -0
  307. package/docs/README.md +1 -1
  308. package/docs/data-and-telemetry.md +6 -6
  309. package/package.json +17 -8
  310. package/dist/commands/add-cli.js +0 -279
  311. package/dist/commands/env.js +0 -213
  312. package/dist/integrations/agent/sdk-runner.js +0 -126
  313. package/dist/output/shapes/vault-list.js +0 -19
  314. package/dist/output/text/proposal-producer.js +0 -8
  315. package/dist/output/text/proposal.js +0 -12
  316. package/dist/output/text/vault.js +0 -16
  317. /package/dist/core/{asset-serialize.js → asset/asset-serialize.js} +0 -0
  318. /package/dist/core/{frontmatter.js → asset/frontmatter.js} +0 -0
  319. /package/dist/core/{config-sources.js → config/config-sources.js} +0 -0
  320. /package/dist/indexer/{graph-dedup.js → graph/graph-dedup.js} +0 -0
  321. /package/dist/{core/config-types.js → indexer/passes/pass-context.js} +0 -0
  322. /package/dist/indexer/{search-fields.js → search/search-fields.js} +0 -0
  323. /package/dist/indexer/{index-context.js → walk/index-context.js} +0 -0
  324. /package/dist/workflows/{document-cache.js → runtime/document-cache.js} +0 -0
@@ -0,0 +1,40 @@
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
+ /**
5
+ * assert.ts — exhaustiveness keystone.
6
+ *
7
+ * `assertNever` is the single helper the exhaustive-switch refactors consume.
8
+ * Placing it in the `never` arm of a `switch`/`if` chain turns any unhandled
9
+ * union variant into a *compile-time* error (the argument no longer narrows to
10
+ * `never`), and — if the impossible case is somehow reached at runtime — throws
11
+ * with the offending value serialized for diagnostics instead of silently
12
+ * falling through.
13
+ */
14
+ /**
15
+ * Assert that a code path is unreachable.
16
+ *
17
+ * Call this in the default/else arm of an exhaustive dispatch over a union. If
18
+ * a new variant is added to the union without a handling arm, the call site
19
+ * stops type-checking, surfacing the drift at compile time.
20
+ *
21
+ * @param x the value the type system has narrowed to `never`
22
+ * @param context optional label included in the thrown message for diagnostics
23
+ * @throws always — this function never returns
24
+ */
25
+ export function assertNever(x, context) {
26
+ let serialized;
27
+ try {
28
+ serialized = JSON.stringify(x);
29
+ }
30
+ catch {
31
+ // Circular structures / non-serializable values fall back to String().
32
+ serialized = String(x);
33
+ }
34
+ if (serialized === undefined) {
35
+ // JSON.stringify(undefined) returns undefined, not a string.
36
+ serialized = String(x);
37
+ }
38
+ const where = context ? ` (${context})` : "";
39
+ throw new Error(`Unexpected value reached assertNever${where}: ${serialized}`);
40
+ }
@@ -0,0 +1,54 @@
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
+ /**
5
+ * Shared `--path` / `--name` semantics for asset-creating commands
6
+ * (`remember`, `import`, `propose`, `workflow create`, ...).
7
+ *
8
+ * The contract, applied consistently across the command surface:
9
+ * - `--name` (or the name positional) is a FLAT asset name — no `/`.
10
+ * - `--path` is a relative directory, applied rooted at the asset's type
11
+ * directory. The final asset name is `<path>/<name>`.
12
+ *
13
+ * Subdirectory placement is `--path`'s job, not `--name`'s. System-derived
14
+ * names (e.g. a URL-path-derived knowledge name) are exempt — only the user's
15
+ * explicit name should be flat-checked, at the command layer.
16
+ */
17
+ import { UsageError } from "../errors.js";
18
+ /**
19
+ * Normalise an optional `--path` value: a relative directory, applied rooted
20
+ * at the asset's type directory (e.g. `personal/projects` under `memories/`).
21
+ * Rejects absolute paths and `.`/`..` segments. Returns `""` when unset.
22
+ */
23
+ export function normalizeCreateSubPath(subPath) {
24
+ if (subPath === undefined)
25
+ return "";
26
+ const trimmed = subPath
27
+ .trim()
28
+ .replace(/\\/g, "/")
29
+ .replace(/^\/+|\/+$/g, "");
30
+ if (!trimmed)
31
+ return "";
32
+ if (trimmed.split("/").some((segment) => !segment || segment === "." || segment === "..")) {
33
+ throw new UsageError("--path must be a relative directory without '.' or '..' segments.");
34
+ }
35
+ return trimmed;
36
+ }
37
+ /**
38
+ * Enforce that an explicit, user-supplied name is a flat (single-segment)
39
+ * name. A `/` in the name is rejected with guidance to use `--path`. Applied
40
+ * at the command layer to the user's name only — system-derived names may nest.
41
+ */
42
+ export function assertFlatAssetName(name) {
43
+ if (name?.replace(/\\/g, "/").replace(/\.md$/i, "").includes("/")) {
44
+ throw new UsageError("Asset --name must be a flat name without '/'. Use --path to choose a subdirectory " +
45
+ "(e.g. --path personal --name grocery-list).");
46
+ }
47
+ }
48
+ /**
49
+ * Combine a normalised `--path` subdirectory with a flat base name into the
50
+ * nested asset name the path resolver expects. `subPath` may be `""`.
51
+ */
52
+ export function combineCreatePath(subPath, baseName) {
53
+ return subPath ? `${subPath}/${baseName}` : baseName;
54
+ }
@@ -2,8 +2,8 @@
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
4
  import path from "node:path";
5
- import { isAssetType } from "./common";
6
- import { UsageError } from "./errors";
5
+ import { isAssetType } from "../common.js";
6
+ import { UsageError } from "../errors.js";
7
7
  /** Accepted spelling aliases mapping to a canonical asset type. */
8
8
  const TYPE_ALIASES = {
9
9
  environment: "env",
@@ -30,6 +30,19 @@ export function makeAssetRef(type, name, origin) {
30
30
  return asset;
31
31
  return `${origin}//${asset}`;
32
32
  }
33
+ /**
34
+ * Serialize a parsed {@link AssetRef} value-object back to its canonical
35
+ * `[origin//]type:name` string form. The single formatter for refs — call
36
+ * this instead of hand-building `${type}:${name}` template strings so the
37
+ * serialization rules (origin prefix, name normalization) live in one place
38
+ * and stay in lockstep with {@link parseAssetRef}.
39
+ *
40
+ * `refToString(parseAssetRef(s))` round-trips for any `s` that
41
+ * `parseAssetRef` accepts.
42
+ */
43
+ export function refToString(ref) {
44
+ return makeAssetRef(ref.type, ref.name, ref.origin);
45
+ }
33
46
  // ── Parsing ─────────────────────────────────────────────────────────────────
34
47
  /**
35
48
  * Parse a ref string in the format `[origin//]type:name`.
@@ -53,9 +66,13 @@ export function parseAssetRef(ref) {
53
66
  }
54
67
  const rawType = body.slice(0, colon);
55
68
  const rawName = body.slice(colon + 1);
69
+ // The `vault` asset type was removed in 0.9.0. Point callers at its
70
+ // replacements rather than failing with a generic unknown-type error.
71
+ if (rawType === "vault") {
72
+ throw new UsageError("The `vault` asset type was removed in 0.9.0 — use `env:` (whole .env config) or `secret:` (a single value).", "MISSING_REQUIRED_ARGUMENT");
73
+ }
56
74
  // Type aliases: `environment:` is an accepted spelling of the canonical
57
- // `env:` type. (`vault:` remains its own deprecated type so the frozen
58
- // `vaults/` copy keeps resolving through the 0.8.x window.)
75
+ // `env:` type.
59
76
  const resolvedType = TYPE_ALIASES[rawType] ?? rawType;
60
77
  if (!isAssetType(resolvedType)) {
61
78
  throw new UsageError(`Invalid asset type: "${rawType}".`, "MISSING_REQUIRED_ARGUMENT");
@@ -13,7 +13,7 @@
13
13
  * `db-search.ts` import from, eliminating the import-order dependency
14
14
  * entirely.
15
15
  */
16
- import { buildWorkflowAction } from "../output/renderers";
16
+ import { buildWorkflowAction } from "../../output/renderers.js";
17
17
  /** Map asset types to their primary renderer names. */
18
18
  export const TYPE_TO_RENDERER = {
19
19
  script: "script-source",
@@ -25,10 +25,10 @@ export const TYPE_TO_RENDERER = {
25
25
  memory: "memory-md",
26
26
  workflow: "workflow-md",
27
27
  env: "env-file",
28
- vault: "vault-env",
29
28
  secret: "secret-file",
30
29
  wiki: "wiki-md",
31
30
  task: "task-yaml",
31
+ session: "session-md",
32
32
  };
33
33
  /** Map asset types to action builder functions for search results. */
34
34
  export const ACTION_BUILDERS = {
@@ -41,10 +41,10 @@ export const ACTION_BUILDERS = {
41
41
  memory: (ref) => `akm show ${ref} -> recall context`,
42
42
  workflow: (ref) => buildWorkflowAction(ref),
43
43
  env: (ref) => `akm show ${ref} -> inspect key names; akm env run ${ref} -- <command> -> run with the whole .env injected (the agent-safe path — values never reach stdout). akm env export ${ref} --out <file> writes a sourceable script (values to a file, not stdout).`,
44
- vault: (ref) => `DEPRECATED (use env): akm show ${ref} -> inspect key names; akm env run ${ref} -- <command> -> run with injected env`,
45
44
  secret: (ref) => `akm show ${ref} -> name only (value never shown); akm secret path ${ref} -> file path; akm secret run ${ref} <VAR> -- <command> -> run with value injected into $VAR`,
46
45
  wiki: (ref) => `akm show ${ref} -> read the wiki page`,
47
46
  task: (ref) => `akm tasks show ${ref.replace(/^task:/, "")} -> inspect; akm tasks run <id> -> run now; akm tasks remove <id> -> unschedule`,
47
+ session: (ref) => `akm show ${ref} -> read the session summary; follow the \`access\` frontmatter to open the raw log at \`log_path\``,
48
48
  };
49
49
  /**
50
50
  * Register a type-to-renderer mapping.
@@ -2,9 +2,9 @@
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
4
  import path from "node:path";
5
- import { buildWorkflowAction } from "../output/renderers";
6
- import { registerActionBuilder, registerTypeRenderer } from "./asset-registry";
7
- import { toPosix } from "./common";
5
+ import { buildWorkflowAction } from "../../output/renderers.js";
6
+ import { toPosix } from "../common.js";
7
+ import { registerActionBuilder, registerTypeRenderer } from "./asset-registry.js";
8
8
  const buildTaskAction = (ref) => `akm tasks show ${ref.replace(/^task:/, "")} -> inspect; akm tasks run <id> -> run now; akm tasks remove <id> -> unschedule`;
9
9
  const markdownSpec = {
10
10
  isRelevantFile: (fileName) => path.extname(fileName).toLowerCase() === ".md",
@@ -66,9 +66,9 @@ const ASSET_SPECS_INTERNAL = {
66
66
  },
67
67
  script: { stashDir: "scripts", ...scriptSpec },
68
68
  memory: { stashDir: "memories", ...markdownSpec },
69
- // Environment assets — whole `.env` files sourced/injected wholesale. Replaces
70
- // the deprecated `vault` type (see below). Key NAMES + start-of-line comments
71
- // are surfaced as metadata; values are never read for indexing.
69
+ // Environment assets — whole `.env` files sourced/injected wholesale. Replaced
70
+ // the deprecated `vault` type (removed in 0.9.0). Key NAMES + start-of-line
71
+ // comments are surfaced as metadata; values are never read for indexing.
72
72
  env: {
73
73
  stashDir: "env",
74
74
  isRelevantFile: (fileName) => fileName === ".env" || fileName.endsWith(".env"),
@@ -91,31 +91,6 @@ const ASSET_SPECS_INTERNAL = {
91
91
  rendererName: "env-file",
92
92
  actionBuilder: (ref) => `akm show ${ref} -> inspect key names; akm env run ${ref} -- <command> -> run with the whole .env injected (values never reach stdout); akm env export ${ref} --out <file> -> write a sourceable script to a file`,
93
93
  },
94
- // DEPRECATED in 0.8.0, removed in 0.9.0 — use `env` instead. Retained so the
95
- // frozen `vaults/` copy left by the migration still resolves and so existing
96
- // `vault:` refs keep working through the deprecation window.
97
- vault: {
98
- stashDir: "vaults",
99
- isRelevantFile: (fileName) => fileName === ".env" || fileName.endsWith(".env"),
100
- toCanonicalName: (typeRoot, filePath) => {
101
- const rel = toPosix(path.relative(typeRoot, filePath));
102
- const fileName = path.basename(rel);
103
- // Treat ".env" as the "default" vault; "<name>.env" → "<name>"
104
- if (fileName === ".env") {
105
- const dir = path.dirname(rel);
106
- return dir === "." || dir === "" ? "default" : `${dir}/default`;
107
- }
108
- const stripped = rel.endsWith(".env") ? rel.slice(0, -4) : rel;
109
- return stripped;
110
- },
111
- toAssetPath: (typeRoot, name) => {
112
- if (name === "default")
113
- return path.join(typeRoot, ".env");
114
- return path.join(typeRoot, name.endsWith(".env") ? name : `${name}.env`);
115
- },
116
- rendererName: "vault-env",
117
- actionBuilder: (ref) => `DEPRECATED (use env): akm show ${ref} -> inspect key names; akm env run ${ref} -- <command> -> run with injected env`,
118
- },
119
94
  // Secrets — a single sensitive value used on its own for authentication (a
120
95
  // PEM key, API token, TLS cert). Unlike `env` (a group of related .env
121
96
  // configuration), the ENTIRE file is the one secret value — there is no safe
@@ -168,6 +143,17 @@ const ASSET_SPECS_INTERNAL = {
168
143
  rendererName: "task-yaml",
169
144
  actionBuilder: buildTaskAction,
170
145
  },
146
+ // #561 — agent sessions indexed as a first-class searchable asset type.
147
+ // Generated (derived) markdown written by the `extract` pass to
148
+ // `sessions/<harness>/<session-id>.md`; carries `log_path` + `access`
149
+ // frontmatter so any agent can navigate into the raw session log. A plain
150
+ // markdown spec — the summary body is the searchable surface.
151
+ session: {
152
+ stashDir: "sessions",
153
+ ...markdownSpec,
154
+ rendererName: "session-md",
155
+ actionBuilder: (ref) => `akm show ${ref} -> read the session summary; follow the \`access\` frontmatter to open the raw log at \`log_path\``,
156
+ },
171
157
  };
172
158
  export const ASSET_SPECS = ASSET_SPECS_INTERNAL;
173
159
  /**
@@ -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 { parseFrontmatter } from "./frontmatter";
4
+ import { parseFrontmatter } from "./frontmatter.js";
5
5
  // ── Parsing ─────────────────────────────────────────────────────────────────
6
6
  export function parseMarkdownToc(content) {
7
7
  const lines = content.split(/\r?\n/);
@@ -18,7 +18,7 @@
18
18
  */
19
19
  import fs from "node:fs";
20
20
  import path from "node:path";
21
- import { UsageError } from "./errors";
21
+ import { UsageError } from "../errors.js";
22
22
  /** Root-relative directory holding a stash's meta docs. */
23
23
  export const META_DIR = ".meta";
24
24
  /** Default meta doc shown when no name is given (`akm show <ref>//meta`). */
@@ -0,0 +1,64 @@
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
+ /**
5
+ * `bestEffort` — a single chokepoint for the project's many intentionally
6
+ * silent error swallows (`} catch { /* nothing *\/ }`).
7
+ *
8
+ * Historically these were scattered as bare `try/catch {}` blocks with no
9
+ * binding and no handling, hiding errors with no way to opt into visibility.
10
+ * This helper centralizes that pattern WITHOUT changing default behaviour:
11
+ *
12
+ * - It runs `fn`. On success it returns the result.
13
+ * - On throw it SWALLOWS the error and returns `undefined` — byte-identical
14
+ * to the previous bare swallow at the default verbosity level.
15
+ * - ONLY when verbose/debug output is enabled (`isVerbose()`) does it route a
16
+ * one-line diagnostic to the existing verbose stderr seam (`warnVerbose`).
17
+ * At the default (non-verbose) verbosity this emits nothing — no new
18
+ * stdout, no new stderr, no changed control flow.
19
+ *
20
+ * It deliberately does NOT add a test-isolation rethrow or any other handling:
21
+ * the contract is "centralize the existing silent swallow + add opt-in verbose
22
+ * visibility", nothing more. Sites that need `rethrowIfTestIsolationError` or
23
+ * any real handling already bind the error and are out of scope for this
24
+ * helper.
25
+ */
26
+ import { isVerbose, warnVerbose } from "./warn.js";
27
+ /**
28
+ * Run `fn` for its side effect/value, swallowing any thrown error and
29
+ * returning `undefined` on failure — exactly as a bare `try { … } catch {}`
30
+ * would. When verbose output is enabled, the swallowed error is surfaced on the
31
+ * existing verbose stderr seam (prefixed with `context` when provided).
32
+ *
33
+ * @param fn the operation to attempt
34
+ * @param context short human-readable reason for the swallow (for verbose logs)
35
+ * @returns the result of `fn`, or `undefined` if it threw
36
+ */
37
+ export function bestEffort(fn, context) {
38
+ try {
39
+ return fn();
40
+ }
41
+ catch (err) {
42
+ if (isVerbose()) {
43
+ warnVerbose(`[akm:best-effort] ${context ? `${context}: ` : ""}swallowed error`, err);
44
+ }
45
+ return undefined;
46
+ }
47
+ }
48
+ /**
49
+ * Async variant of {@link bestEffort}. Awaits `fn()`, swallowing any rejection
50
+ * and resolving to `undefined` on failure — byte-identical to a bare
51
+ * `try { await … } catch {}` at the default verbosity level. Surfaces the
52
+ * swallowed error on the verbose seam only when verbose output is enabled.
53
+ */
54
+ export async function bestEffortAsync(fn, context) {
55
+ try {
56
+ return await fn();
57
+ }
58
+ catch (err) {
59
+ if (isVerbose()) {
60
+ warnVerbose(`[akm:best-effort] ${context ? `${context}: ` : ""}swallowed error`, err);
61
+ }
62
+ return undefined;
63
+ }
64
+ }
@@ -4,24 +4,26 @@
4
4
  import crypto from "node:crypto";
5
5
  import fs from "node:fs";
6
6
  import path from "node:path";
7
- import { TYPE_DIRS } from "./asset-spec";
8
- import { ConfigError } from "./errors";
9
- import { getConfigPath, getDefaultStashDir } from "./paths";
7
+ import { getAssetTypes, TYPE_DIRS } from "./asset/asset-spec.js";
8
+ import { ConfigError } from "./errors.js";
9
+ import { getConfigPath, getDefaultStashDir } from "./paths.js";
10
10
  // ── Types ───────────────────────────────────────────────────────────────────
11
- export const ASSET_TYPES = [
12
- "skill",
13
- "command",
14
- "agent",
15
- "knowledge",
16
- "workflow",
17
- "script",
18
- "memory",
19
- "env",
20
- "vault",
21
- "secret",
22
- "wiki",
23
- "lesson",
24
- ];
11
+ /**
12
+ * The canonical catalog of built-in asset types.
13
+ *
14
+ * SINGLE SOURCE OF TRUTH: derived from the {@link ASSET_SPECS} registry in
15
+ * `asset-spec.ts` rather than hand-maintained here. Before #490/WS7 this was a
16
+ * hand-written literal array that had DRIFTED from the registry (it omitted
17
+ * `task`, which the registry has always carried). Deriving from the registry
18
+ * kills that drift — see `tests/asset-type-union-source.test.ts` for the
19
+ * intentional-`task`-delta guard.
20
+ *
21
+ * Note: `AkmAssetType` stays a static literal union of the BUILT-IN types
22
+ * (those present at module-eval time). Dynamically `registerAssetType`-d types
23
+ * are accepted at runtime via {@link isAssetType} but are not part of the
24
+ * static union — identical to the pre-WS7 contract.
25
+ */
26
+ export const ASSET_TYPES = Object.freeze([...getAssetTypes()]);
25
27
  export const ASSET_TYPE_SET = new Set(ASSET_TYPES);
26
28
  // ── Constants ───────────────────────────────────────────────────────────────
27
29
  export const IS_WINDOWS = process.platform === "win32";
@@ -67,6 +69,11 @@ export function isAssetType(type) {
67
69
  * The temp file is opened with the target `mode` (default 0o600) from the
68
70
  * start, so it is never world-readable even briefly.
69
71
  *
72
+ * `content` may be a string or a `Buffer`. Buffer callers (e.g. secrets, where
73
+ * binary certs and CRLF/LF endings must round-trip byte-exact) get the same
74
+ * fsync'd temp-file-plus-rename guarantees as string callers — there is a
75
+ * single atomic-write implementation.
76
+ *
70
77
  * Durability: fsync'd against the May 2026 config-clobber incident (#472).
71
78
  * On ext4 (data=ordered) and NVMe-with-TRIM, a power-loss inside the kernel
72
79
  * writeback window could leave the renamed file truncated to zero — defeating
@@ -82,7 +89,14 @@ export function writeFileAtomic(target, content, mode) {
82
89
  const tmp = `${target}.tmp.${process.pid}.${crypto.randomBytes(8).toString("hex")}`;
83
90
  const fd = fs.openSync(tmp, "w", mode ?? 0o600);
84
91
  try {
85
- fs.writeSync(fd, content);
92
+ // fs.writeSync has two non-overlapping overloads (Buffer vs string); branch
93
+ // so each call resolves to a single overload. Both write byte-exact.
94
+ if (typeof content === "string") {
95
+ fs.writeSync(fd, content);
96
+ }
97
+ else {
98
+ fs.writeSync(fd, content);
99
+ }
86
100
  try {
87
101
  fs.fdatasyncSync(fd);
88
102
  }
@@ -14,10 +14,11 @@
14
14
  */
15
15
  import fs from "node:fs";
16
16
  import path from "node:path";
17
- import { writeFileAtomic } from "./common";
18
- import { ConfigError } from "./errors";
19
- import { probeLock, releaseLock, tryAcquireLockSync } from "./file-lock";
20
- import { getCacheDir, getConfigDir } from "./paths";
17
+ import { sleepSync } from "../../runtime.js";
18
+ import { writeFileAtomic } from "../common.js";
19
+ import { ConfigError } from "../errors.js";
20
+ import { probeLock, releaseLock, tryAcquireLockSync } from "../file-lock.js";
21
+ import { getCacheDir, getConfigDir } from "../paths.js";
21
22
  /**
22
23
  * Read the raw text of a config file. Returns `undefined` when the file does
23
24
  * not exist (legitimate cold-start). Other I/O errors propagate.
@@ -79,22 +80,18 @@ export function writeConfigAtomic(configPath, config) {
79
80
  }
80
81
  /** Maximum number of timestamped config backups to retain (#459). */
81
82
  const MAX_CONFIG_BACKUPS = 5;
82
- /**
83
- * Snapshot the current config file to `<cacheDir>/config-backups/`. Writes
84
- * both a timestamped copy and a `config.latest.json` pointer, then prunes the
85
- * timestamped set to {@link MAX_CONFIG_BACKUPS} most-recent entries.
86
- *
87
- * No-op when the source file does not exist (cold-start safe).
88
- */
89
83
  export function backupExistingConfig(configPath) {
90
84
  if (!fs.existsSync(configPath))
91
- return;
85
+ return undefined;
92
86
  const backupDir = path.join(getCacheDir(), "config-backups");
93
87
  fs.mkdirSync(backupDir, { recursive: true });
94
88
  const timestamp = new Date().toISOString().replace(/[.:]/g, "-");
95
- fs.copyFileSync(configPath, path.join(backupDir, `config-${timestamp}.json`));
96
- fs.copyFileSync(configPath, path.join(backupDir, "config.latest.json"));
89
+ const timestamped = path.join(backupDir, `config-${timestamp}.json`);
90
+ const latest = path.join(backupDir, "config.latest.json");
91
+ fs.copyFileSync(configPath, timestamped);
92
+ fs.copyFileSync(configPath, latest);
97
93
  pruneOldBackups(backupDir);
94
+ return { timestamped, latest };
98
95
  }
99
96
  function pruneOldBackups(backupDir) {
100
97
  let entries;
@@ -140,6 +137,14 @@ function getConfigLockPath() {
140
137
  }
141
138
  const CONFIG_LOCK_MAX_RETRIES = 10;
142
139
  const CONFIG_LOCK_RETRY_DELAY_MS = 50;
140
+ /**
141
+ * Block the current thread for `ms` without busy-spinning (H8). Delegates to
142
+ * the runtime boundary's `sleepSync`, a real blocking sleep that yields the
143
+ * thread to the OS scheduler.
144
+ */
145
+ function sleepSyncMs(ms) {
146
+ sleepSync(ms);
147
+ }
143
148
  /**
144
149
  * Acquire an exclusive sentinel around config writes.
145
150
  *
@@ -169,11 +174,16 @@ export function acquireConfigLock() {
169
174
  continue; // Reclaimed — retry immediately.
170
175
  }
171
176
  if (attempt < CONFIG_LOCK_MAX_RETRIES - 1) {
172
- // Busy spin (synchronous) config writes are fast.
173
- const deadline = Date.now() + CONFIG_LOCK_RETRY_DELAY_MS;
174
- while (Date.now() < deadline) {
175
- // spin
176
- }
177
+ // H8: yield the thread between retries instead of busy-spinning.
178
+ // The previous `while (Date.now() < deadline)` loop burned CPU for up to
179
+ // 50ms per retry (≈500ms total), freezing the single JS thread and
180
+ // starving co-scheduled work under parallel load. `sleepSync` is a
181
+ // real blocking sleep that releases the thread to the OS scheduler.
182
+ // Kept synchronous (rather than an async sleep) to preserve the sync
183
+ // `withConfigLock` signature and avoid an async ripple through every
184
+ // `saveConfig`/`loadConfig` caller. Lock semantics are unchanged: same
185
+ // retry count, same delay budget, same best-effort fall-through.
186
+ sleepSyncMs(CONFIG_LOCK_RETRY_DELAY_MS);
177
187
  }
178
188
  }
179
189
  // Best-effort: proceed without lock.
@@ -12,7 +12,8 @@
12
12
  * location and stripped from the raw config. Production code reads ONLY the
13
13
  * new shape. There are no backward-compat shims after this migration.
14
14
  */
15
- import { warn } from "./warn";
15
+ import { v1ProfilePlatform } from "../../integrations/harnesses/index.js";
16
+ import { warn } from "../warn.js";
16
17
  /**
17
18
  * Current config schema version sentinel.
18
19
  * Configs at this version are considered fully migrated and will not be rewritten.
@@ -579,14 +580,15 @@ export function migrateConfigShape(raw) {
579
580
  * platform).
580
581
  */
581
582
  function guessAgentPlatform(name) {
582
- const lower = name.toLowerCase();
583
- if (lower === "claude" || lower === "claude-code")
584
- return "claude";
585
- if (lower.startsWith("opencode-sdk"))
586
- return "opencode-sdk";
587
- if (lower.startsWith("opencode"))
588
- return "opencode";
589
- return undefined;
583
+ // #566: consult the harness registry instead of re-deriving the heuristic.
584
+ // `v1ProfilePlatform` iterates HARNESS_REGISTRY and asks each harness's own
585
+ // `matchesV1ProfileName()`, so:
586
+ // - 'claude' AND the 'claude-code' alias both resolve to canonical 'claude'
587
+ // (the normalization bridge keeps legacy v1 names round-tripping);
588
+ // - decorated names like 'opencode-sdk-x' resolve most-specific-first;
589
+ // - an unknown harness name returns undefined (caller drops it) instead of
590
+ // being silently misclassified. Adding a harness needs no edit here.
591
+ return v1ProfilePlatform(name);
590
592
  }
591
593
  /**
592
594
  * Map a v1 process name (e.g. `"reflect"`, `"propose"`) to its v2 improve
@@ -28,6 +28,7 @@
28
28
  * enforced at save time via `superRefine` on the top-level schema.
29
29
  */
30
30
  import { z } from "zod";
31
+ import { VALID_HARNESS_IDS } from "./config-types.js";
31
32
  // ── Reusable atomic schemas ─────────────────────────────────────────────────
32
33
  /** Positive integer (used for tokens, timeouts, batch sizes). */
33
34
  const positiveInt = z.number().int().positive();
@@ -108,7 +109,9 @@ export const EmbeddingConnectionConfigSchema = z
108
109
  })
109
110
  .strict();
110
111
  // ── Agent profiles ──────────────────────────────────────────────────────────
111
- const AgentPlatformSchema = z.enum(["opencode", "claude", "opencode-sdk"]);
112
+ // Derives from the canonical VALID_HARNESS_IDS (#565) so the Zod gate cannot
113
+ // drift from the TS union / parse check / setup detection.
114
+ const AgentPlatformSchema = z.enum(VALID_HARNESS_IDS);
112
115
  export const AgentProfileConfigSchema = z
113
116
  .object({
114
117
  platform: AgentPlatformSchema,
@@ -126,18 +129,33 @@ export const ImproveProcessConfigSchema = z
126
129
  profile: z.string().min(1).optional(),
127
130
  timeoutMs: z.union([positiveInt, z.null()]).optional(),
128
131
  allowedTypes: z.array(z.string().min(1)).optional(),
132
+ // Consolidate process: minimum eligible-memory pool size below which the
133
+ // consolidation pass skips entirely (emits `pool_below_min_size`). 0 disables
134
+ // the guard. Only meaningful on the `consolidate` process. Default 500.
135
+ minPoolSize: z.number().int().min(0).optional(),
129
136
  qualityGate: z.object({ enabled: z.boolean().optional() }).strict().optional(),
130
137
  contradictionDetection: z.object({ enabled: z.boolean().optional() }).strict().optional(),
131
138
  // Extract process config (only meaningful for extract process)
132
139
  defaultSince: z.string().min(1).optional(),
133
140
  maxTotalChars: positiveInt.optional(),
141
+ // Extract process: minimum raw session size (pre-filter inputCount) below
142
+ // which the extract LLM call is skipped (#595/#596). 0 disables the gate.
143
+ // Absent = default 10 (skip only truly empty sessions). Only meaningful
144
+ // on the `extract` process.
145
+ minContentChars: z.number().int().min(0).optional(),
134
146
  maxChunkSize: z.number().int().min(1).max(50).optional(),
135
- // Consolidate process: when set, narrows the candidate pool to memories
136
- // modified within this window (e.g. "7d", "48h") plus their graph
137
- // neighbours. Useful when consolidation runs more than once per day —
138
- // keeps each pass focused on recent changes without re-scanning the full
139
- // pool. Leave unset (full-pool sweep) for the nightly default pass.
140
- incrementalSince: z.string().min(1).optional(),
147
+ // Extract process: minimum number of new (unseen, in-window) candidate
148
+ // sessions below which the extract pass skips entirely (emits an
149
+ // `improve_skipped` event with `reason: "below_min_new_sessions"`). 0
150
+ // disables the guard. Only meaningful on the `extract` process. Default 0
151
+ // (disabled) so existing behaviour is preserved; only opted-in profiles set it.
152
+ minNewSessions: z.number().int().min(0).optional(),
153
+ // #561 — index agent sessions as a searchable `session` asset (extract
154
+ // process). Absent = on-when-an-LLM-is-available (fail-open when offline).
155
+ indexSessions: z.boolean().optional(),
156
+ // #561 — minimum session duration in minutes for session indexing. 0
157
+ // disables the gate. Absent = default 5. Only meaningful on `extract`.
158
+ minSessionDuration: z.number().min(0).optional(),
141
159
  // Triage process config (only meaningful for the `triage` process)
142
160
  applyMode: z.enum(["queue", "promote"]).optional(),
143
161
  policy: z.string().min(1).optional(),
@@ -231,6 +249,11 @@ export const DefaultsSchema = z
231
249
  // ── Sources / registries / installed ────────────────────────────────────────
232
250
  const SourceConfigEntryOptionsSchema = z
233
251
  .object({
252
+ /**
253
+ * @deprecated 0.9.0 (issue #507). Retired per-asset push-on-commit. Kept so
254
+ * old configs still parse; its intent maps onto the batch push gate and
255
+ * encountering it emits a one-time deprecation warning.
256
+ */
234
257
  pushOnCommit: z.boolean().optional(),
235
258
  })
236
259
  .passthrough();
@@ -479,6 +502,25 @@ export const IndexConfigSchema = z.preprocess((raw, ctx) => {
479
502
  stalenessDetection: StalenessDetectionSchema.optional(),
480
503
  })
481
504
  .catchall(IndexPassConfigSchema));
505
+ // ── Setup-derived recommendations ──────────────────────────────────────────
506
+ /**
507
+ * Cron-style schedule hints derived by `akm setup --reset-recommended`.
508
+ *
509
+ * These record the *recommended* cadence for the improve and index background
510
+ * tasks. They are advisory metadata persisted into config so the value
511
+ * survives a re-run; actual task scheduling lives in the tasks subsystem.
512
+ */
513
+ export const SetupTaskSchedulesSchema = z
514
+ .object({
515
+ improve: z.string().min(1).optional(),
516
+ index: z.string().min(1).optional(),
517
+ })
518
+ .strict();
519
+ export const SetupConfigSchema = z
520
+ .object({
521
+ taskSchedules: SetupTaskSchedulesSchema.optional(),
522
+ })
523
+ .strict();
482
524
  // ── Top-level AkmConfig ────────────────────────────────────────────────────
483
525
  /**
484
526
  * Base object schema used both as the top-level shape and as the source of
@@ -508,6 +550,7 @@ export const AkmConfigShape = {
508
550
  feedback: FeedbackConfigSchema.optional(),
509
551
  archiveRetentionDays: nonNegativeNumber.optional(),
510
552
  improve: ImproveConfigSchema.optional(),
553
+ setup: SetupConfigSchema.optional(),
511
554
  };
512
555
  export const AkmConfigBaseSchema = z.object(AkmConfigShape).strict();
513
556
  export const AkmConfigSchema = AkmConfigBaseSchema.superRefine((config, ctx) => {
@@ -0,0 +1,16 @@
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
+ // VALID_HARNESS_IDS now derives from the unified HARNESS_REGISTRY (#562), which
5
+ // is the single source of truth replacing the previously-disconnected
6
+ // registries. config ← harnesses is the only import direction (harnesses/ is a
7
+ // dependency-graph leaf), so there is no cycle.
8
+ import { VALID_HARNESS_IDS } from "../../integrations/harnesses/index.js";
9
+ /**
10
+ * Canonical list of valid agent harness / platform ids. Re-exported from the
11
+ * unified harness registry (#562) so the Zod `AgentPlatformSchema` enum, the
12
+ * `AgentProfileConfigV2` platform union, `parseAgentProfilesMapV2`'s membership
13
+ * check, and setup's `DetectedHarness` union all derive from one place and
14
+ * cannot drift. Add a harness in `src/integrations/harnesses/index.ts`.
15
+ */
16
+ export { VALID_HARNESS_IDS };