akm-cli 0.9.17-alpha.3 → 0.9.17-alpha.5

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 (346) hide show
  1. package/CHANGELOG.md +760 -0
  2. package/dist/akm +94 -196
  3. package/dist/cli/shared.js +6 -2
  4. package/dist/cli.js +22 -9
  5. package/dist/commands/agent/agent-dispatch.js +1 -1
  6. package/dist/commands/command/command-execution.js +24 -62
  7. package/dist/commands/feedback-cli.js +0 -1
  8. package/dist/commands/health/accept-rate.js +2 -2
  9. package/dist/commands/health/checks.js +30 -75
  10. package/dist/commands/health/config-skew.js +38 -0
  11. package/dist/commands/health/egress.js +54 -0
  12. package/dist/commands/health/html-report.js +0 -38
  13. package/dist/commands/health/improve-metrics.js +123 -562
  14. package/dist/commands/health/plugin-staleness.js +53 -3
  15. package/dist/commands/health/renderers.js +12 -4
  16. package/dist/commands/health/report-view-model.js +11 -106
  17. package/dist/commands/health/types-improve.js +4 -19
  18. package/dist/commands/health/windows.js +64 -73
  19. package/dist/commands/health.js +122 -143
  20. package/dist/commands/improve/consolidate/chunking.js +25 -100
  21. package/dist/commands/improve/consolidate/sanitize.js +54 -149
  22. package/dist/commands/improve/consolidate.js +538 -1075
  23. package/dist/commands/improve/content-hash.js +16 -24
  24. package/dist/commands/improve/distill/content-repair.js +18 -100
  25. package/dist/commands/improve/distill-guards.js +20 -81
  26. package/dist/commands/improve/distill-promotion-policy.js +23 -243
  27. package/dist/commands/improve/distill.js +608 -1075
  28. package/dist/commands/improve/eligibility.js +126 -400
  29. package/dist/commands/improve/execution.js +3 -5
  30. package/dist/commands/improve/extract.js +487 -1046
  31. package/dist/commands/improve/feedback-valence.js +0 -25
  32. package/dist/commands/improve/improve-cli.js +29 -166
  33. package/dist/commands/improve/improve-result-file.js +10 -66
  34. package/dist/commands/improve/improve-strategies.js +12 -7
  35. package/dist/commands/improve/improve-usage-report.js +18 -64
  36. package/dist/commands/improve/improve.js +443 -1063
  37. package/dist/commands/improve/ledger.js +114 -0
  38. package/dist/commands/improve/locks.js +2 -8
  39. package/dist/commands/improve/loop-stages.js +459 -1172
  40. package/dist/commands/improve/memory/derived-ref.js +12 -77
  41. package/dist/commands/improve/memory/memory-belief.js +14 -118
  42. package/dist/commands/improve/memory/memory-improve.js +4 -3
  43. package/dist/commands/improve/outcome-loop.js +28 -156
  44. package/dist/commands/improve/planner.js +5 -10
  45. package/dist/commands/improve/preparation.js +851 -2339
  46. package/dist/commands/improve/proactive-maintenance.js +34 -101
  47. package/dist/commands/improve/reflect-noise.js +104 -280
  48. package/dist/commands/improve/reflect.js +621 -1367
  49. package/dist/commands/improve/salience.js +46 -232
  50. package/dist/commands/improve/session-asset.js +19 -100
  51. package/dist/commands/improve/stage.js +323 -0
  52. package/dist/commands/lint/base-linter.js +19 -5
  53. package/dist/commands/proposal/drain.js +251 -644
  54. package/dist/commands/proposal/proposal-cli.js +3 -18
  55. package/dist/commands/proposal/proposal-types.js +20 -41
  56. package/dist/commands/proposal/proposal.js +1 -2
  57. package/dist/commands/proposal/propose.js +134 -160
  58. package/dist/commands/proposal/repository.js +502 -1487
  59. package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
  60. package/dist/commands/proposal/validators/proposal-validators.js +1 -1
  61. package/dist/commands/proposal/validators/proposals.js +13 -89
  62. package/dist/commands/read/curate.js +63 -413
  63. package/dist/commands/read/search-cli.js +16 -33
  64. package/dist/commands/read/search.js +17 -23
  65. package/dist/commands/read/show.js +2 -13
  66. package/dist/commands/sources/bundle-cli.js +25 -2
  67. package/dist/commands/sources/bundle-config-ops.js +4 -0
  68. package/dist/commands/sources/dangerous-env-audit.js +1 -2
  69. package/dist/commands/sources/info.js +2 -11
  70. package/dist/commands/sources/installed-stashes.js +197 -746
  71. package/dist/commands/sources/schema-repair.js +98 -129
  72. package/dist/commands/sources/source-add.js +62 -12
  73. package/dist/commands/sources/source-manage.js +9 -2
  74. package/dist/commands/sources/stash-cli.js +1 -1
  75. package/dist/commands/tasks/explain.js +10 -13
  76. package/dist/commands/tasks/tasks-cli.js +9 -8
  77. package/dist/commands/tasks/tasks.js +326 -930
  78. package/dist/commands/tasks/validate.js +42 -21
  79. package/dist/commands/workflow/plan.js +22 -29
  80. package/dist/commands/workflow-cli.js +4 -4
  81. package/dist/core/adapter/adapters/akm-adapter.js +0 -1
  82. package/dist/core/adapter/adapters/akm-lint.js +2 -3
  83. package/dist/core/adapter/adapters/akm-metadata.js +11 -12
  84. package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
  85. package/dist/core/adapter/execution-source.js +17 -29
  86. package/dist/core/asset/asset-placement.js +4 -13
  87. package/dist/core/asset/resolve-ref.js +1 -1
  88. package/dist/core/bundle-id.js +42 -5
  89. package/dist/core/bundle-rename.js +291 -0
  90. package/dist/core/config/config-io.js +1 -2
  91. package/dist/core/config/config-schema.js +1 -33
  92. package/dist/core/config/config-walker.js +1 -1
  93. package/dist/core/config/config.js +163 -68
  94. package/dist/core/config/legacy-source-shape-shim.js +38 -9
  95. package/dist/core/config/schema/embedding.js +20 -5
  96. package/dist/core/config/schema/engines.js +5 -0
  97. package/dist/core/config/schema/execution.js +1 -1
  98. package/dist/core/config/schema/experimental.js +1 -1
  99. package/dist/core/config/schema/improve-processes.js +21 -95
  100. package/dist/core/config/schema/improve.js +4 -42
  101. package/dist/core/config/schema/scheduler.js +12 -12
  102. package/dist/core/config/schema/search.js +6 -22
  103. package/dist/core/env-secret-ref.js +0 -1
  104. package/dist/core/errors.js +8 -9
  105. package/dist/core/file-lock.js +76 -173
  106. package/dist/core/logs-db.js +2 -2
  107. package/dist/core/paths.js +0 -27
  108. package/dist/core/redaction.js +109 -2
  109. package/dist/core/run-lock.js +2 -5
  110. package/dist/core/spawn-env.js +1 -1
  111. package/dist/core/state/migrations.js +108 -61
  112. package/dist/core/state-db-scope.js +2 -4
  113. package/dist/core/state-db.js +126 -692
  114. package/dist/core/type-presentation.js +1 -9
  115. package/dist/core/write-source.js +293 -1012
  116. package/dist/execution/input-contract.js +1 -1
  117. package/dist/execution/resolved-request.js +135 -689
  118. package/dist/execution/source.js +63 -257
  119. package/dist/execution/target-ref.js +1 -1
  120. package/dist/indexer/bundle-identity-guard.js +2 -2
  121. package/dist/indexer/db/graph-db.js +106 -46
  122. package/dist/indexer/ensure-index.js +44 -85
  123. package/dist/indexer/graph/graph-extraction.js +340 -562
  124. package/dist/indexer/graph/graph-related.js +130 -0
  125. package/dist/indexer/index-rebuild-lock.js +3 -11
  126. package/dist/indexer/index-writer-lock.js +8 -17
  127. package/dist/indexer/index-written-assets.js +139 -151
  128. package/dist/indexer/indexer.js +524 -846
  129. package/dist/indexer/materialize-embeddings.js +60 -397
  130. package/dist/indexer/passes/memory-inference.js +81 -90
  131. package/dist/indexer/passes/metadata.js +132 -200
  132. package/dist/indexer/read-preflight.js +0 -7
  133. package/dist/indexer/scan/doc-to-entry.js +1 -3
  134. package/dist/indexer/scan/drain-dir.js +1 -1
  135. package/dist/indexer/search/db-search.js +181 -590
  136. package/dist/indexer/search/fts-query.js +30 -41
  137. package/dist/indexer/search/ranking.js +28 -154
  138. package/dist/indexer/search/search-attribution.js +12 -32
  139. package/dist/indexer/search/search-fields.js +11 -15
  140. package/dist/indexer/search/search-hit-enrichers.js +54 -85
  141. package/dist/indexer/search/search-source.js +1 -4
  142. package/dist/indexer/usage/usage-events.js +2 -7
  143. package/dist/integrations/agent/engine-fallback.js +23 -40
  144. package/dist/integrations/agent/engine-resolution.js +93 -183
  145. package/dist/integrations/agent/execution.js +507 -0
  146. package/dist/integrations/agent/model-map.js +28 -156
  147. package/dist/integrations/agent/request-lowering.js +66 -141
  148. package/dist/integrations/agent/runner-dispatch.js +143 -321
  149. package/dist/integrations/agent/runner.js +54 -14
  150. package/dist/integrations/lockfile.js +53 -101
  151. package/dist/llm/embedders/deterministic.js +2 -3
  152. package/dist/llm/embedders/profile.js +71 -0
  153. package/dist/llm/embedders/remote.js +10 -15
  154. package/dist/llm/graph-extract.js +3 -12
  155. package/dist/llm/index-passes.js +3 -5
  156. package/dist/llm/memory-infer.js +1 -2
  157. package/dist/llm/metadata-enhance.js +1 -2
  158. package/dist/llm/structured-call.js +5 -24
  159. package/dist/output/generic-render.js +23 -11
  160. package/dist/output/html-render.js +13 -10
  161. package/dist/output/render-registry.js +3 -32
  162. package/dist/output/shapes/helpers.js +2 -34
  163. package/dist/output/shapes/passthrough.js +1 -9
  164. package/dist/{indexer/search/ranking-types.js → output/text/bundle-rename.js} +4 -1
  165. package/dist/output/text/command-format.js +60 -23
  166. package/dist/output/text/helpers.js +1 -1
  167. package/dist/output/text/migrate.js +5 -14
  168. package/dist/output/text/proposal-format.js +1 -2
  169. package/dist/output/text/workflow-format.js +0 -32
  170. package/dist/output/text.js +2 -0
  171. package/dist/registry/factory.js +4 -19
  172. package/dist/registry/network.js +66 -220
  173. package/dist/registry/providers/index.js +0 -2
  174. package/dist/registry/providers/skills-sh.js +3 -14
  175. package/dist/registry/providers/static-index.js +24 -26
  176. package/dist/registry/resolve.js +55 -131
  177. package/dist/scripts/akm-migrate-node.js +43940 -93320
  178. package/dist/scripts/akm-migrate.js +43700 -93078
  179. package/dist/setup/registry-stash-loader.js +4 -13
  180. package/dist/setup/semantic-assets.js +3 -44
  181. package/dist/setup/setup.js +1 -1
  182. package/dist/setup/steps/tasks.js +25 -15
  183. package/dist/sources/provider-factory.js +17 -18
  184. package/dist/sources/providers/filesystem.js +2 -3
  185. package/dist/sources/providers/git-install.js +7 -1
  186. package/dist/sources/providers/git-provider.js +0 -3
  187. package/dist/sources/providers/git-stash.js +0 -17
  188. package/dist/sources/providers/npm.js +2 -4
  189. package/dist/sources/providers/provider-utils.js +5 -10
  190. package/dist/sources/providers/website.js +0 -2
  191. package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
  192. package/dist/sources/website-url.js +2 -2
  193. package/dist/storage/database.js +9 -35
  194. package/dist/storage/repositories/improve-ledger-repository.js +168 -0
  195. package/dist/storage/repositories/index-connection.js +34 -70
  196. package/dist/storage/repositories/index-entries-repository.js +69 -111
  197. package/dist/storage/repositories/index-entry-mapper.js +1 -2
  198. package/dist/storage/repositories/index-entry-schema.js +83 -269
  199. package/dist/storage/repositories/index-fts-repository.js +86 -256
  200. package/dist/storage/repositories/index-llm-cache-repository.js +17 -0
  201. package/dist/storage/repositories/index-meta-repository.js +6 -4
  202. package/dist/storage/repositories/index-schema.js +192 -220
  203. package/dist/storage/repositories/index-utility-repository.js +8 -29
  204. package/dist/storage/repositories/index-vec-repository.js +133 -414
  205. package/dist/storage/repositories/outcome-repository.js +2 -1
  206. package/dist/storage/repositories/proposals-repository.js +35 -0
  207. package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
  208. package/dist/storage/repositories/task-history-repository.js +26 -4
  209. package/dist/storage/repositories/workflow-runs-repository.js +53 -244
  210. package/dist/storage/sqlite-migrations.js +136 -0
  211. package/dist/storage/sqlite-pragmas.js +11 -9
  212. package/dist/storage/sqlite-transaction.js +170 -0
  213. package/dist/storage/state-db-integrity.js +34 -27
  214. package/dist/tasks/activation-config.js +134 -62
  215. package/dist/tasks/backends/cron.js +129 -277
  216. package/dist/tasks/backends/exec-utils.js +2 -5
  217. package/dist/tasks/backends/launchd.js +125 -745
  218. package/dist/tasks/backends/schtasks.js +101 -620
  219. package/dist/tasks/prepare/prepare-support.js +5 -15
  220. package/dist/tasks/prepare/prepare.js +0 -2
  221. package/dist/tasks/resolve-akm-bin.js +20 -79
  222. package/dist/tasks/run/attempt-lifecycle.js +0 -1
  223. package/dist/tasks/scheduler-binding.js +18 -238
  224. package/dist/tasks/scheduler-invocation.js +52 -52
  225. package/dist/tasks/scheduler-lock.js +53 -0
  226. package/dist/tasks/scheduler-sync.js +361 -751
  227. package/dist/tasks/source/parse-task-source.js +160 -10
  228. package/dist/tasks/source/task-source-v3-frozen.js +3 -4
  229. package/dist/tasks/source/task-to-v4.js +2 -2
  230. package/dist/workflows/authoring/authoring.js +3 -12
  231. package/dist/workflows/compile.js +211 -0
  232. package/dist/workflows/concurrency-policy.js +13 -74
  233. package/dist/workflows/exec/child-invocation.js +3 -17
  234. package/dist/workflows/exec/child-workflow.js +32 -141
  235. package/dist/workflows/exec/dispatch-redaction.js +13 -53
  236. package/dist/workflows/exec/environment.js +98 -0
  237. package/dist/workflows/exec/exec-unit.js +33 -140
  238. package/dist/workflows/exec/frozen-judge.js +7 -59
  239. package/dist/workflows/exec/native-executor.js +82 -341
  240. package/dist/workflows/exec/param-secrets.js +29 -47
  241. package/dist/workflows/exec/run-workflow.js +154 -387
  242. package/dist/workflows/exec/scheduler.js +9 -36
  243. package/dist/workflows/exec/step-work.js +127 -430
  244. package/dist/workflows/exec/unit-dispatch.js +11 -63
  245. package/dist/workflows/exec/unit-writer.js +8 -52
  246. package/dist/workflows/exec/worktree.js +39 -273
  247. package/dist/workflows/freeze/child-output-references.js +4 -15
  248. package/dist/workflows/freeze/environment.js +99 -92
  249. package/dist/workflows/freeze/freeze.js +172 -0
  250. package/dist/workflows/freeze/step-values.js +19 -21
  251. package/dist/workflows/freeze/targets/child-workflow.js +23 -92
  252. package/dist/workflows/freeze/targets/command.js +10 -33
  253. package/dist/workflows/freeze/targets/script.js +5 -12
  254. package/dist/workflows/freeze/targets/shell.js +3 -6
  255. package/dist/workflows/freeze/targets/task.js +25 -80
  256. package/dist/workflows/freeze/task-bindings.js +20 -67
  257. package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
  258. package/dist/workflows/ir/params.js +6 -51
  259. package/dist/workflows/ir/plan-hash.js +2 -34
  260. package/dist/workflows/parser.js +140 -43
  261. package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
  262. package/dist/workflows/renderer.js +36 -69
  263. package/dist/workflows/resource-limits.js +12 -120
  264. package/dist/workflows/runtime/agent-identity.js +8 -40
  265. package/dist/workflows/runtime/run-outputs.js +3 -6
  266. package/dist/workflows/runtime/run-plan.js +316 -0
  267. package/dist/workflows/runtime/runs.js +48 -200
  268. package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
  269. package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
  270. package/dist/workflows/validate-summary.js +2 -7
  271. package/docs/integration/bundling-akm.md +49 -42
  272. package/docs/migration/README.md +1 -0
  273. package/docs/migration/release-notes/0.9.17.md +41 -0
  274. package/docs/migration/v0.9.1-to-v0.9.2.md +19 -7
  275. package/docs/reference/cli.md +182 -125
  276. package/docs/reference/configuration.md +49 -56
  277. package/docs/reference/data-and-telemetry.md +19 -20
  278. package/docs/reference/tasks.md +86 -38
  279. package/docs/reference/workflow-schema.md +14 -18
  280. package/docs/reference/workflows.md +6 -9
  281. package/package.json +1 -1
  282. package/schemas/akm-config.json +87 -406
  283. package/dist/commands/health/advisories.js +0 -150
  284. package/dist/commands/health/metrics.js +0 -329
  285. package/dist/commands/health/surfaces.js +0 -102
  286. package/dist/commands/improve/anti-collapse.js +0 -83
  287. package/dist/commands/improve/collapse-detector.js +0 -432
  288. package/dist/commands/improve/consolidate/eligibility.js +0 -48
  289. package/dist/commands/improve/consolidate/merge.js +0 -146
  290. package/dist/commands/improve/distill/promote-memory.js +0 -329
  291. package/dist/commands/improve/distill/quality-gate.js +0 -500
  292. package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
  293. package/dist/commands/improve/proposal-envelope.js +0 -31
  294. package/dist/commands/improve/run-context.js +0 -123
  295. package/dist/commands/improve/shared.js +0 -21
  296. package/dist/commands/improve/source-identity.js +0 -28
  297. package/dist/commands/improve/triage.js +0 -96
  298. package/dist/commands/proposal/drain-policies.js +0 -151
  299. package/dist/commands/sources/update-transaction.js +0 -220
  300. package/dist/core/action-contributors.js +0 -28
  301. package/dist/core/config/config-version-shim.js +0 -101
  302. package/dist/core/config/retired-experimental-keys-shim.js +0 -62
  303. package/dist/core/fs-txn.js +0 -405
  304. package/dist/core/lexical-score.js +0 -25
  305. package/dist/core/maintenance-barrier.js +0 -167
  306. package/dist/execution/executable-identity.js +0 -105
  307. package/dist/execution/guarded-source.js +0 -441
  308. package/dist/indexer/graph/graph-boost.js +0 -427
  309. package/dist/indexer/graph/graph-dedup.js +0 -95
  310. package/dist/indexer/search/name-match.js +0 -35
  311. package/dist/indexer/search/ranking-contributors.js +0 -515
  312. package/dist/indexer/walk/project-context.js +0 -192
  313. package/dist/integrations/agent/execution-cascade.js +0 -566
  314. package/dist/integrations/agent/execution-definitions.js +0 -202
  315. package/dist/integrations/agent/execution-lowering.js +0 -841
  316. package/dist/integrations/agent/execution-preparation.js +0 -98
  317. package/dist/integrations/agent/inline-execution.js +0 -74
  318. package/dist/registry/create-provider-registry.js +0 -29
  319. package/dist/registry/pinned-request-helper.js +0 -247
  320. package/dist/registry/pinned-transport.js +0 -717
  321. package/dist/sources/providers/index.js +0 -14
  322. package/dist/storage/engines/sqlite-migrations.js +0 -271
  323. package/dist/storage/repositories/canaries-repository.js +0 -107
  324. package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
  325. package/dist/storage/repositories/registry-cache.js +0 -113
  326. package/dist/tasks/scheduler-sync-preview.js +0 -52
  327. package/dist/workflows/freeze/resolve-steps.js +0 -86
  328. package/dist/workflows/freeze/source-freeze.js +0 -64
  329. package/dist/workflows/ir/compile.js +0 -321
  330. package/dist/workflows/ir/environment-v4.js +0 -330
  331. package/dist/workflows/ir/freeze-v4.js +0 -153
  332. package/dist/workflows/ir/schema-v4.js +0 -745
  333. package/dist/workflows/ir/schema.js +0 -354
  334. package/dist/workflows/program/schema.js +0 -78
  335. package/dist/workflows/runtime/checkin.js +0 -57
  336. package/dist/workflows/runtime/plan-classifier.js +0 -196
  337. package/dist/workflows/runtime/unit-checkin.js +0 -45
  338. package/dist/workflows/runtime/unit-phases.js +0 -20
  339. package/dist/workflows/schema.js +0 -4
  340. package/dist/workflows/source-ir/compile.js +0 -200
  341. package/dist/workflows/source-ir/program.js +0 -50
  342. package/dist/workflows/source-ir/result.js +0 -26
  343. package/dist/workflows/source-ir/schema.js +0 -786
  344. package/dist/workflows/source-ir/triggers.js +0 -79
  345. package/dist/workflows/source-ir/uses.js +0 -40
  346. package/dist/workflows/validator.js +0 -60
@@ -317,6 +317,85 @@ function addPlainMatches(coverageDelta, text, needle) {
317
317
  offset = match + needle.length;
318
318
  }
319
319
  }
320
+ /**
321
+ * Name components (after splitting an identifier on `_`/`-` and camelCase
322
+ * boundaries) that mark a `NAME=value` / `NAME: value` pair as
323
+ * credential-shaped regardless of length — the free-text-pattern sibling of
324
+ * `isInferredSecretName` in `src/tasks/log-redaction.ts`, which does the same
325
+ * job for environment variable names. Bare-word matching only: this
326
+ * deliberately does NOT flag "author" ("auth" is not a whole word/component
327
+ * of "author", the same anchoring reasoning `isInferredSecretName` documents)
328
+ * nor prose like "token budget" (no `=`/`:` for the pattern below to anchor
329
+ * on in the first place).
330
+ */
331
+ const CREDENTIAL_KEY_WORDS = new Set(["password", "passwd", "secret", "token", "auth", "credential", "credentials"]);
332
+ /** Split an identifier into lowercase words at `_`/`-` and camelCase boundaries. */
333
+ function identifierWords(identifier) {
334
+ return identifier
335
+ .replace(/([a-z0-9])([A-Z])/g, "$1_$2")
336
+ .toLowerCase()
337
+ .split(/[_-]+/)
338
+ .filter(Boolean);
339
+ }
340
+ /**
341
+ * True when `identifier` names a credential by shape: a whole word from
342
+ * {@link CREDENTIAL_KEY_WORDS}, or `api[_-]?key` / `private[_-]?key` in any
343
+ * of their glued, snake_case, kebab-case or camelCase spellings.
344
+ */
345
+ function isCredentialLikeKeyName(identifier) {
346
+ const lower = identifier.toLowerCase();
347
+ if (lower.includes("apikey") || lower.includes("privatekey"))
348
+ return true;
349
+ const words = identifierWords(identifier);
350
+ for (let i = 0; i < words.length; i++) {
351
+ const word = words[i];
352
+ if (CREDENTIAL_KEY_WORDS.has(word))
353
+ return true;
354
+ if ((word === "api" || word === "private") && words[i + 1] === "key")
355
+ return true;
356
+ }
357
+ return false;
358
+ }
359
+ /**
360
+ * `NAME=value` / `NAME: value`, name captured in group 1, separator in group
361
+ * 2. Source string (not a shared `RegExp`, which would carry `lastIndex`
362
+ * state across calls) for {@link redactNamedCredentialValues} to instantiate
363
+ * fresh each call.
364
+ */
365
+ const NAMED_CREDENTIAL_VALUE_SOURCE = String.raw `\b([A-Za-z][A-Za-z0-9_-]*)(\s*[:=]\s*)(?:"[^"]*"|'[^']*'|[^\s,;"']+)`;
366
+ /**
367
+ * Redact `NAME=value` / `NAME: value` pairs whose name is credential-shaped
368
+ * (see {@link isCredentialLikeKeyName}).
369
+ *
370
+ * Not a plain `input.replace(re, fn)`: the greedy unquoted-value alternative
371
+ * happily matches straight through a NESTED assignment when a non-credential
372
+ * name comes first with no separator of its own — `"config: DB_PASSWORD=…"`
373
+ * matches name `config` with value `DB_PASSWORD=…` (one contiguous
374
+ * whitespace-free run), and a plain global replace treats that as ONE
375
+ * consumed, non-matching span and never revisits `DB_PASSWORD=…` on its own,
376
+ * silently leaking the credential. Rejecting a non-credential candidate here
377
+ * instead rewinds the scan to one character past where IT started, so the
378
+ * engine keeps looking and still finds the real assignment nested inside.
379
+ */
380
+ function redactNamedCredentialValues(input) {
381
+ const re = new RegExp(NAMED_CREDENTIAL_VALUE_SOURCE, "g");
382
+ let result = "";
383
+ let cursor = 0;
384
+ let match = re.exec(input);
385
+ while (match !== null) {
386
+ const [full, name, sep] = match;
387
+ if (isCredentialLikeKeyName(name)) {
388
+ result += `${input.slice(cursor, match.index)}${name}${sep}[REDACTED]`;
389
+ cursor = match.index + full.length;
390
+ re.lastIndex = cursor;
391
+ }
392
+ else {
393
+ re.lastIndex = match.index + 1;
394
+ }
395
+ match = re.exec(input);
396
+ }
397
+ return result + input.slice(cursor);
398
+ }
320
399
  /**
321
400
  * Redact credential-shaped substrings from arbitrary text by pattern alone —
322
401
  * unlike {@link redactSensitiveText}, which requires the exact secret value
@@ -333,11 +412,23 @@ function addPlainMatches(coverageDelta, text, needle) {
333
412
  * is kept, only the token segment is redacted
334
413
  * - Slack incoming-webhook URLs (`hooks.slack.com/services/<team>/<channel>/<token>`)
335
414
  * — the team/channel ids are kept, only the trailing token is redacted
415
+ * - PEM private-key blocks (`-----BEGIN … PRIVATE KEY-----…-----END … PRIVATE KEY-----`)
416
+ * — the BEGIN/END markers are kept, only the body is redacted
417
+ * - JWTs (`eyJ…….…….…`)
418
+ * - GitHub tokens (`ghp_`/`gho_`/`ghs_`/`ghu_`/`ghr_…`)
419
+ * - Slack tokens (`xoxa-`/`xoxb-`/`xoxp-`/`xoxr-…`)
420
+ * - AWS access key ids (`AKIA…`)
421
+ * - `scheme://user:pass@host` URLs — the scheme and host are kept, only the
422
+ * userinfo is redacted
423
+ * - `NAME=value` / `NAME: value` where `NAME` names a credential by shape
424
+ * (password, passwd, secret, token, auth, credential(s), api/private key
425
+ * — see {@link isCredentialLikeKeyName}) — the name is kept, only the
426
+ * value is redacted
336
427
  */
337
428
  export function redactCredentialPatterns(input) {
338
429
  if (!input)
339
430
  return "";
340
- return (input
431
+ const patterned = input
341
432
  // Bearer tokens (case-insensitive)
342
433
  .replace(/\bBearer\s+[A-Za-z0-9._\-+/=]+/gi, "Bearer [REDACTED]")
343
434
  // sk-/sk_ style keys
@@ -349,7 +440,23 @@ export function redactCredentialPatterns(input) {
349
440
  // Discord webhook URLs: keep the webhook id, redact the token segment.
350
441
  .replace(/(discord(?:app)?\.com\/api\/webhooks\/\d+\/)[A-Za-z0-9_-]+/gi, "$1[REDACTED]")
351
442
  // Slack incoming-webhook URLs: keep the team/channel ids, redact the token.
352
- .replace(/(hooks\.slack\.com\/services\/[A-Za-z0-9]+\/[A-Za-z0-9]+\/)[A-Za-z0-9]+/gi, "$1[REDACTED]"));
443
+ .replace(/(hooks\.slack\.com\/services\/[A-Za-z0-9]+\/[A-Za-z0-9]+\/)[A-Za-z0-9]+/gi, "$1[REDACTED]")
444
+ // PEM private-key blocks: keep the BEGIN/END markers, redact the body.
445
+ .replace(/(-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----)[\s\S]*?(-----END [A-Z0-9 ]*PRIVATE KEY-----)/g, "$1[REDACTED]$2")
446
+ // JWTs: three dot-separated base64url segments; the header always
447
+ // starts "eyJ" (base64 of `{"`).
448
+ .replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g, "[REDACTED]")
449
+ // GitHub tokens (ghp_/gho_/ghs_/ghu_/ghr_...)
450
+ .replace(/\bgh[posur]_[A-Za-z0-9]{20,255}\b/g, "[REDACTED]")
451
+ // Slack tokens (xoxa-/xoxb-/xoxp-/xoxr-...)
452
+ .replace(/\bxox[abpr]-[A-Za-z0-9-]+\b/g, "[REDACTED]")
453
+ // AWS access key ids
454
+ .replace(/\bAKIA[0-9A-Z]{16}\b/g, "[REDACTED]")
455
+ // URLs carrying `user:pass@` userinfo: keep the scheme and host.
456
+ .replace(/\b([a-z][a-z0-9+.-]*:\/\/)[^\s/@]+:[^\s/@]+@/gi, "$1[REDACTED]@");
457
+ // NAME=value / NAME: value for credential-shaped names. Not a plain
458
+ // `.replace()` in the chain above — see `redactNamedCredentialValues`.
459
+ return redactNamedCredentialValues(patterned);
353
460
  }
354
461
  /**
355
462
  * Replace exact sensitive values in text.
@@ -20,11 +20,8 @@
20
20
  * absent-race and stale-reclaim retries `improve` established) and reports
21
21
  * whether the caller now owns the lock or, if not, who currently holds it.
22
22
  * It does not decide what "held" means — skip, throw, or warn-and-proceed is
23
- * entirely up to the caller — and it does not serialize the attempt itself:
24
- * every caller must wrap the call in
25
- * `withMaintenanceStartBarrier`/`tryWithMaintenanceStartBarrier`
26
- * (`core/maintenance-barrier.ts`) so two racing processes never both create
27
- * the sentinel in the same window.
23
+ * entirely up to the caller. Creating the sentinel is an `O_EXCL` write, so
24
+ * two racing processes never both create it.
28
25
  */
29
26
  import fs from "node:fs";
30
27
  import path from "node:path";
@@ -144,7 +144,7 @@ export function collectAllowlistedEnv(names, source = process.env) {
144
144
  * a shell's own command-path caching already does, and the cost of not
145
145
  * memoizing is paid on every child-env build: two PATH splits and up to seven
146
146
  * SYNCHRONOUS `existsSync` probes, on the event loop a 10 000-unit fan-out
147
- * shares with the run's lease heartbeat.
147
+ * shares with every other in-flight unit.
148
148
  *
149
149
  * Bounded so a caller that somehow varies its PATH cannot grow it without
150
150
  * limit; a spawn path only ever sees a handful of distinct PATH strings, so
@@ -1,7 +1,12 @@
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 { assertMigrationLedger, assertMigrationRegistry, runMigrations as runSqliteMigrations, } from "../../storage/engines/sqlite-migrations.js";
4
+ // The state.db schema migration registry. The MIGRATIONS array is the single
5
+ // append-only ordered source of truth: new migrations are APPENDED here and an
6
+ // existing fragment is NEVER renumbered or reordered (that would corrupt the
7
+ // schema_migrations ledger on already-deployed databases). The shared runner
8
+ // at src/storage/sqlite-migrations.ts applies them in array order.
9
+ import { assertMigrationRegistry } from "../../storage/sqlite-migrations.js";
5
10
  /**
6
11
  * Explicit safety classification for every released state migration ID.
7
12
  *
@@ -9,6 +14,9 @@ import { assertMigrationLedger, assertMigrationRegistry, runMigrations as runSql
9
14
  * released IDs, order, and SQL stay byte-for-byte unchanged. A runtime
10
15
  * assertion below requires this key order to match {@link STATE_MIGRATIONS}
11
16
  * exactly, so appending a migration also requires an explicit safety decision.
17
+ * The one consumer of the classification is `openStateDatabase`
18
+ * (src/core/state-db.ts): before a `historical-destructive` migration runs
19
+ * against an existing database, the file is copied beside itself first.
12
20
  */
13
21
  export const STATE_MIGRATION_SAFETY_BY_ID = Object.freeze({
14
22
  "001-initial-schema": "additive",
@@ -38,6 +46,7 @@ export const STATE_MIGRATION_SAFETY_BY_ID = Object.freeze({
38
46
  "025-task-history-vocabulary-backfill": "data-preserving-rebuild",
39
47
  "026-proposals-strip-legacy-fragment-refs": "data-preserving-rebuild",
40
48
  "027-extract-sessions-seen-harness-rename": "data-preserving-rebuild",
49
+ "028-improve-ledger": "historical-destructive",
41
50
  });
42
51
  export const STATE_MIGRATIONS = [
43
52
  // ── Migration 001 — initial schema ──────────────────────────────────────────
@@ -1177,6 +1186,104 @@ export const STATE_MIGRATIONS = [
1177
1186
  UPDATE workflow_runs SET agent_harness = 'claude' WHERE agent_harness = 'claude-code';
1178
1187
  `,
1179
1188
  },
1189
+ // ── Migration 028 — the improve ledger ──
1190
+ //
1191
+ // One table records what improve last did with each (stash, ref, source) and
1192
+ // when it may try again (`src/storage/repositories/improve-ledger-repository.ts`
1193
+ // owns the outcome → cadence rule). Backfilled from the latest proposals row
1194
+ // per (stash_dir, ref, source) so the rejection windows and pending state an
1195
+ // upgrade inherits are the ones the previous release was already honouring:
1196
+ // pending → proposed (revisit in 7 d)
1197
+ // accepted → accepted (eligible)
1198
+ // rejected → expired when the review reason is the expiry pass's
1199
+ // ("expired: …", 1 d grace), failed when the drain's
1200
+ // stale-target auto-reject or the orphan purge (eligible now —
1201
+ // neither was a content judgement), else rejected (reflect
1202
+ // 14 d, distill 30 d, other 7 d)
1203
+ // reverted → rejected (the operator undid the content)
1204
+ // `strftime` returns NULL for an unparseable timestamp, which reads as
1205
+ // "eligible now" — the safe direction.
1206
+ //
1207
+ // The same migration drops the tables the ledger supersedes or whose code is
1208
+ // gone: `proposal_fingerprints` was a dedup cache derived from proposals
1209
+ // rows, `improve_gate_thresholds` has had no reader since 0.9.0,
1210
+ // `proposal_fs_imports` lost its importer, `canary_queries` /
1211
+ // `improve_cycle_metrics` belonged to the removed collapse detector, and
1212
+ // `consolidation_judged` was already dropped by 018 (a no-op here for any
1213
+ // ledger that ran it). None holds operator state, but dropping tables is
1214
+ // destructive, so it is classified that way: `openStateDatabase` copies an
1215
+ // existing state.db beside itself before it runs.
1216
+ {
1217
+ id: "028-improve-ledger",
1218
+ up: `
1219
+ CREATE TABLE IF NOT EXISTS improve_ledger (
1220
+ stash_dir TEXT NOT NULL,
1221
+ ref TEXT NOT NULL,
1222
+ source TEXT NOT NULL,
1223
+ last_attempt_at TEXT NOT NULL,
1224
+ outcome TEXT NOT NULL,
1225
+ next_eligible_at TEXT,
1226
+ proposal_id TEXT,
1227
+ detail TEXT,
1228
+ PRIMARY KEY (stash_dir, ref, source)
1229
+ );
1230
+
1231
+ INSERT OR REPLACE INTO improve_ledger
1232
+ (stash_dir, ref, source, last_attempt_at, outcome, next_eligible_at, proposal_id, detail)
1233
+ SELECT
1234
+ p.stash_dir,
1235
+ p.ref,
1236
+ p.source,
1237
+ p.updated_at,
1238
+ o.outcome,
1239
+ CASE o.outcome
1240
+ WHEN 'rejected' THEN strftime('%Y-%m-%dT%H:%M:%fZ', p.updated_at,
1241
+ CASE p.source WHEN 'reflect' THEN '+14 days' WHEN 'distill' THEN '+30 days' ELSE '+7 days' END)
1242
+ WHEN 'expired' THEN strftime('%Y-%m-%dT%H:%M:%fZ', p.updated_at, '+1 days')
1243
+ WHEN 'proposed' THEN strftime('%Y-%m-%dT%H:%M:%fZ', p.updated_at, '+7 days')
1244
+ ELSE NULL
1245
+ END,
1246
+ p.id,
1247
+ CASE WHEN json_valid(p.metadata_json)
1248
+ THEN json_extract(p.metadata_json, '$.review.reason') ELSE NULL END
1249
+ FROM proposals p
1250
+ JOIN (
1251
+ SELECT stash_dir, ref, source, MAX(updated_at) AS latest_updated_at
1252
+ FROM proposals GROUP BY stash_dir, ref, source
1253
+ ) latest
1254
+ ON latest.stash_dir = p.stash_dir AND latest.ref = p.ref AND latest.source = p.source
1255
+ AND latest.latest_updated_at = p.updated_at
1256
+ JOIN (
1257
+ SELECT rowid AS proposal_rowid,
1258
+ CASE status
1259
+ WHEN 'pending' THEN 'proposed'
1260
+ WHEN 'accepted' THEN 'accepted'
1261
+ WHEN 'reverted' THEN 'rejected'
1262
+ WHEN 'rejected' THEN
1263
+ CASE WHEN json_valid(metadata_json) THEN
1264
+ CASE
1265
+ WHEN json_extract(metadata_json, '$.review.reason') LIKE 'expired:%' THEN 'expired'
1266
+ WHEN json_extract(metadata_json, '$.gateDecision.reason') = 'stale-target' THEN 'failed'
1267
+ WHEN json_extract(metadata_json, '$.review.reason') = 'Asset no longer exists on disk' THEN 'failed'
1268
+ ELSE 'rejected'
1269
+ END
1270
+ ELSE 'rejected' END
1271
+ ELSE NULL
1272
+ END AS outcome
1273
+ FROM proposals
1274
+ ) o ON o.proposal_rowid = p.rowid
1275
+ WHERE o.outcome IS NOT NULL
1276
+ ORDER BY p.rowid ASC;
1277
+
1278
+ DROP INDEX IF EXISTS idx_proposal_fingerprints_ref;
1279
+ DROP TABLE IF EXISTS proposal_fingerprints;
1280
+ DROP TABLE IF EXISTS improve_gate_thresholds;
1281
+ DROP TABLE IF EXISTS proposal_fs_imports;
1282
+ DROP TABLE IF EXISTS consolidation_judged;
1283
+ DROP TABLE IF EXISTS improve_cycle_metrics;
1284
+ DROP TABLE IF EXISTS canary_queries;
1285
+ `,
1286
+ },
1180
1287
  ];
1181
1288
  assertMigrationRegistry(STATE_MIGRATIONS);
1182
1289
  function assertStateMigrationSafetyRegistry() {
@@ -1194,63 +1301,3 @@ export function getStateMigrationSafety(migrationId) {
1194
1301
  throw new Error(`State migration ${migrationId} has no safety classification.`);
1195
1302
  return safety;
1196
1303
  }
1197
- /**
1198
- * Apply every pending migration in a single transaction per migration.
1199
- *
1200
- * Delegates to the shared SQLite migration engine; state.db has no
1201
- * pre-versioning bootstrap step, so no `bootstrap` hook is passed.
1202
- *
1203
- * Called automatically by `openStateDatabase()`.
1204
- */
1205
- export function runMigrations(db, options) {
1206
- const initialMigration = STATE_MIGRATIONS[0];
1207
- if (!initialMigration)
1208
- throw new Error("State migration registry has no initial migration.");
1209
- let existingUnversionedSnapshotPrepared = false;
1210
- const prepareExistingUnversionedState = (lockedDb) => {
1211
- if (existingUnversionedSnapshotPrepared)
1212
- return;
1213
- if (!options?.existingUnversionedDatabase) {
1214
- throw new Error("Refusing state.db initialization because its migration ledger disappeared after preflight.");
1215
- }
1216
- if (!options.allowHistoricalDestructiveStateUpgrade) {
1217
- throw new Error("Refusing to migrate an existing unversioned state.db during an ordinary managed open. " +
1218
- "Run `akm upgrade` (or `akm migrate apply`) to snapshot it before migration 001 and apply it deliberately.");
1219
- }
1220
- const ledger = assertMigrationLedger(lockedDb, STATE_MIGRATIONS);
1221
- if (ledger.migrationIds.length !== 0) {
1222
- throw new Error("Refusing an existing unversioned state.db whose migration ledger changed after preflight.");
1223
- }
1224
- if (!options.beforeExistingUnversionedStateMigration) {
1225
- throw new Error("An existing unversioned state.db requires a verified pre-migration safety-copy hook.");
1226
- }
1227
- options.beforeExistingUnversionedStateMigration(initialMigration);
1228
- existingUnversionedSnapshotPrepared = true;
1229
- };
1230
- runSqliteMigrations(db, STATE_MIGRATIONS, {
1231
- lockInitialMigrationPrefixThrough: options?.existingUnversionedDatabase ? "002-task-history-per-run" : undefined,
1232
- beforeLedgerInitializationLocked(lockedDb) {
1233
- if (options?.freshDatabase)
1234
- return;
1235
- prepareExistingUnversionedState(lockedDb);
1236
- },
1237
- beforeMigrationLocked(migration, lockedDb) {
1238
- if (options?.existingUnversionedDatabase && !existingUnversionedSnapshotPrepared) {
1239
- prepareExistingUnversionedState(lockedDb);
1240
- }
1241
- if (getStateMigrationSafety(migration.id) !== "historical-destructive" || options?.freshDatabase)
1242
- return;
1243
- // The writer lock closes the window between this exact-prefix decision,
1244
- // the recovery snapshot, and the destructive migration transaction.
1245
- assertMigrationLedger(lockedDb, STATE_MIGRATIONS);
1246
- if (!options?.allowHistoricalDestructiveStateUpgrade) {
1247
- throw new Error(`Refusing to apply historical destructive state migration ${migration.id} during an ordinary managed open. ` +
1248
- "Run `akm upgrade` (or `akm migrate apply`) to create a sibling state.db safety copy and apply it deliberately.");
1249
- }
1250
- if (!options.beforeHistoricalDestructiveMigration) {
1251
- throw new Error(`Historical destructive state migration ${migration.id} requires a verified safety-copy hook.`);
1252
- }
1253
- options.beforeHistoricalDestructiveMigration(migration);
1254
- },
1255
- });
1256
- }
@@ -4,10 +4,8 @@
4
4
  /**
5
5
  * Ambient, run-scoped state.db connection reuse.
6
6
  *
7
- * `openStateDatabase` is NOT cheap: every call registers a `state-db`
8
- * maintenance activity (a lockfile create under the maintenance barrier), opens
9
- * a throwaway read-only preflight handle to assert the migration ledger, then
10
- * opens the real handle and applies pragmas. Paying that per repository call
7
+ * `openStateDatabase` is not free: every call opens a handle, applies the
8
+ * pragmas and reads the migration ledger. Paying that per repository call
11
9
  * — twice per dispatched workflow unit (insert + finish), plus once per
12
10
  * `appendEvent` (two events per unit) — is the dominant cost of a wide `map`
13
11
  * fan-out.