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
@@ -7,7 +7,7 @@ import path from "node:path";
7
7
  import { bundleRefToString, parseBundleRef } from "../core/asset/asset-ref.js";
8
8
  import { resolveStashDir } from "../core/common.js";
9
9
  import { ConfigError } from "../core/errors.js";
10
- import { getCacheDir, getConfigDir, getDataDir, getTaskContextDir } from "../core/paths.js";
10
+ import { getTaskContextDir } from "../core/paths.js";
11
11
  import { INPUT_NAME_PATTERN } from "../execution/input-contract.js";
12
12
  import { normaliseTaskConceptId } from "./task-id.js";
13
13
  export const SCHEDULED_TASK_CONTEXT_KEYS = [
@@ -17,6 +17,17 @@ export const SCHEDULED_TASK_CONTEXT_KEYS = [
17
17
  "AKM_CACHE_DIR",
18
18
  "AKM_STATE_DIR",
19
19
  ];
20
+ /**
21
+ * Directories a descriptor carries ONLY when the process that ran `task sync`
22
+ * had them set explicitly in its environment. They are never resolved from
23
+ * defaults: a descriptor written from inside an app whose environment pointed
24
+ * `$STATE` somewhere private (an OpenCode desktop session, 2026-08 → 2026-09)
25
+ * froze that directory into eight cron rows, and the scheduled improve runs
26
+ * then held their locks in a `locks/` directory no interactive command could
27
+ * see. At fire time an absent key resolves exactly as it does for an
28
+ * interactive command on the same host.
29
+ */
30
+ const EXPLICIT_CONTEXT_KEYS = ["AKM_CONFIG_DIR", "AKM_DATA_DIR", "AKM_CACHE_DIR", "AKM_STATE_DIR"];
20
31
  /**
21
32
  * The AKM directory context currently restored from a scheduler descriptor.
22
33
  * Child environments that are built from an allowlist must forward this
@@ -32,16 +43,20 @@ export function scheduledTaskContextEnv(env = process.env) {
32
43
  return out;
33
44
  }
34
45
  export const SCHEDULER_CONTEXT_ARG = "--scheduler-context";
35
- /** Resolve the complete non-secret AKM directory context captured by schedulers. */
36
- export function resolveScheduledTaskContext(env = process.env, platform = process.platform) {
37
- return canonicalContext({
38
- AKM_BUNDLE_DIR: path.resolve(resolveStashDir(env)),
39
- AKM_CONFIG_DIR: path.resolve(getConfigDir(env, platform)),
40
- AKM_DATA_DIR: path.resolve(getDataDir(env, platform)),
41
- AKM_CACHE_DIR: path.resolve(getCacheDir(env)),
42
- // Capture the complete process directory context used by scheduled commands.
43
- AKM_STATE_DIR: path.resolve(resolveStateDir(env, platform)),
44
- });
46
+ /**
47
+ * The directory context a scheduler descriptor carries: the resolved bundle
48
+ * path, plus whichever `AKM_*_DIR` overrides the syncing process had set
49
+ * explicitly. Resolved defaults are deliberately not captured — see
50
+ * {@link EXPLICIT_CONTEXT_KEYS}.
51
+ */
52
+ export function resolveScheduledTaskContext(env = process.env) {
53
+ const context = { AKM_BUNDLE_DIR: path.resolve(resolveStashDir(env)) };
54
+ for (const key of EXPLICIT_CONTEXT_KEYS) {
55
+ const value = env[key]?.trim();
56
+ if (value)
57
+ context[key] = path.resolve(value);
58
+ }
59
+ return canonicalContext(context);
45
60
  }
46
61
  /** Build an installed argv from one already-validated public scheduler tail. */
47
62
  export function buildScheduledBindingInvocation(akmArgv, contextPath, invocation) {
@@ -52,16 +67,15 @@ export function buildScheduledBindingInvocation(akmArgv, contextPath, invocation
52
67
  argv: [...akmArgv, SCHEDULER_CONTEXT_ARG, assertAbsolutePath(contextPath), ...parsed.invocation],
53
68
  };
54
69
  }
55
- export function schedulerContextDescriptor(context = resolveScheduledTaskContext(), envPath = process.env.PATH ?? "") {
56
- return {
57
- version: 1,
58
- environment: { ...canonicalContext(context), PATH: validatePathValue(envPath) },
59
- };
70
+ export function schedulerContextDescriptor(context = resolveScheduledTaskContext()) {
71
+ return { version: 1, environment: canonicalContext(context) };
60
72
  }
61
73
  export function schedulerContextPath(descriptor) {
62
74
  const bytes = serializeDescriptor(descriptor);
63
75
  const digest = createHash("sha256").update(bytes).digest("hex");
64
- return path.join(getTaskContextDir(descriptor.environment), `${digest}.json`);
76
+ // The descriptor lives under the data directory it names when it names
77
+ // one, otherwise under this process's own.
78
+ return path.join(getTaskContextDir({ ...process.env, ...descriptor.environment }), `${digest}.json`);
65
79
  }
66
80
  /** Write a content-addressed descriptor without ever replacing existing content. */
67
81
  export function writeSchedulerContextDescriptor(descriptor = schedulerContextDescriptor()) {
@@ -96,9 +110,13 @@ export function writeSchedulerContextDescriptor(descriptor = schedulerContextDes
96
110
  }
97
111
  export function loadSchedulerContextDescriptor(file, env = process.env) {
98
112
  const descriptor = validateSchedulerContextDescriptor(file);
99
- for (const key of SCHEDULED_TASK_CONTEXT_KEYS)
100
- env[key] = descriptor.environment[key];
101
- env.PATH = descriptor.environment.PATH;
113
+ for (const key of SCHEDULED_TASK_CONTEXT_KEYS) {
114
+ const value = descriptor.environment[key];
115
+ if (value !== undefined)
116
+ env[key] = value;
117
+ }
118
+ if (descriptor.environment.PATH !== undefined)
119
+ env.PATH = descriptor.environment.PATH;
102
120
  }
103
121
  export function validateSchedulerContextDescriptor(file) {
104
122
  const absolute = assertAbsolutePath(file);
@@ -291,14 +309,17 @@ function isValidSchedulerInputFlagTail(tail) {
291
309
  return true;
292
310
  }
293
311
  function canonicalContext(input) {
294
- const inputKeys = Object.keys(input);
295
- if (inputKeys.length !== SCHEDULED_TASK_CONTEXT_KEYS.length ||
296
- inputKeys.some((key) => !SCHEDULED_TASK_CONTEXT_KEYS.includes(key))) {
312
+ if (Object.keys(input).some((key) => !SCHEDULED_TASK_CONTEXT_KEYS.includes(key))) {
297
313
  throw invalidSchedulerContext();
298
314
  }
299
315
  const context = {};
300
316
  for (const key of SCHEDULED_TASK_CONTEXT_KEYS) {
301
317
  const value = input[key];
318
+ if (value === undefined) {
319
+ if (key === "AKM_BUNDLE_DIR")
320
+ throw invalidSchedulerContext();
321
+ continue;
322
+ }
302
323
  if (typeof value !== "string" ||
303
324
  value.trim().length === 0 ||
304
325
  containsControlCharacter(value) ||
@@ -319,12 +340,12 @@ function canonicalDescriptor(input) {
319
340
  if (typeof rawEnvironment !== "object" || rawEnvironment === null || Array.isArray(rawEnvironment)) {
320
341
  throw invalidSchedulerContext();
321
342
  }
322
- const environment = rawEnvironment;
323
- if (Object.keys(environment).length !== SCHEDULED_TASK_CONTEXT_KEYS.length + 1)
324
- throw invalidSchedulerContext();
325
- const PATH = validatePathValue(environment.PATH);
326
- const { PATH: _, ...rawContext } = environment;
327
- return { version: 1, environment: { ...canonicalContext(rawContext), PATH } };
343
+ const { PATH, ...rawContext } = rawEnvironment;
344
+ const context = canonicalContext(rawContext);
345
+ return {
346
+ version: 1,
347
+ environment: PATH === undefined ? context : { ...context, PATH: validatePathValue(PATH) },
348
+ };
328
349
  }
329
350
  function serializeDescriptor(descriptor) {
330
351
  return `${JSON.stringify(canonicalDescriptor(descriptor))}\n`;
@@ -365,30 +386,9 @@ function containsControlCharacter(value) {
365
386
  }
366
387
  return false;
367
388
  }
368
- function resolveStateDir(env, platform) {
369
- const override = env.AKM_STATE_DIR?.trim();
370
- if (override)
371
- return override;
372
- if (platform === "win32") {
373
- const localAppData = env.LOCALAPPDATA?.trim();
374
- if (localAppData)
375
- return path.join(localAppData, "akm", "state");
376
- const userProfile = env.USERPROFILE?.trim();
377
- if (userProfile)
378
- return path.join(userProfile, "AppData", "Local", "akm", "state");
379
- const appData = env.APPDATA?.trim();
380
- if (appData)
381
- return path.join(appData, "..", "Local", "akm", "state");
382
- throw new ConfigError("Unable to determine state directory. Set LOCALAPPDATA, USERPROFILE, or APPDATA.", "CONFIG_DIR_UNRESOLVABLE");
383
- }
384
- const xdgStateHome = env.XDG_STATE_HOME?.trim();
385
- if (xdgStateHome)
386
- return path.join(xdgStateHome, "akm");
387
- const home = env.HOME?.trim();
388
- return home ? path.join(home, ".local", "state", "akm") : path.join("/tmp", "akm-state");
389
- }
390
389
  function invalidSchedulerContext() {
391
- return new ConfigError(`Invalid scheduler context; expected exactly ${SCHEDULED_TASK_CONTEXT_KEYS.join(", ")} as absolute paths.`, "INVALID_CONFIG_FILE");
390
+ return new ConfigError("Invalid scheduler context; expected AKM_BUNDLE_DIR as an absolute path, " +
391
+ `optionally with ${EXPLICIT_CONTEXT_KEYS.join(", ")} as absolute paths.`, "INVALID_CONFIG_FILE");
392
392
  }
393
393
  function invalidSchedulerInvocation() {
394
394
  return new ConfigError("Invalid scheduler invocation; expected public " +
@@ -0,0 +1,53 @@
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
+ * The one process lock around native scheduler writes.
6
+ *
7
+ * `task sync|add|enable|disable|prune --yes` read the installed rows, plan,
8
+ * and apply under it, so two akm processes never interleave their writes to
9
+ * the same crontab or LaunchAgents directory. It is an `O_EXCL` lock file
10
+ * under `$STATE/locks`, reclaimed only from a verifiably dead holder, and
11
+ * re-entrant within one process (`add` runs `sync` inside it).
12
+ */
13
+ import fs from "node:fs";
14
+ import path from "node:path";
15
+ import { TransientError } from "../core/errors.js";
16
+ import { createLockPayload, probeLock, reclaimStaleLock, releaseLock, tryAcquireLockSync } from "../core/file-lock.js";
17
+ import { getStateDir } from "../core/paths.js";
18
+ export function schedulerLockPath() {
19
+ return path.join(getStateDir(), "locks", "scheduler.lock");
20
+ }
21
+ let depth = 0;
22
+ export async function withSchedulerLock(fn) {
23
+ if (depth > 0) {
24
+ depth += 1;
25
+ try {
26
+ return await fn();
27
+ }
28
+ finally {
29
+ depth -= 1;
30
+ }
31
+ }
32
+ const lockPath = schedulerLockPath();
33
+ fs.mkdirSync(path.dirname(lockPath), { recursive: true });
34
+ const acquire = () => tryAcquireLockSync(lockPath, createLockPayload({ purpose: "scheduler" }));
35
+ let ownership = acquire();
36
+ if (!ownership) {
37
+ const probe = probeLock(lockPath);
38
+ if (probe.state === "absent" || (probe.state === "stale" && reclaimStaleLock(lockPath, probe))) {
39
+ ownership = acquire();
40
+ }
41
+ }
42
+ if (!ownership) {
43
+ throw new TransientError("Another akm process is syncing the scheduler right now.", "SCHEDULER_LOCK_HELD");
44
+ }
45
+ depth = 1;
46
+ try {
47
+ return await fn();
48
+ }
49
+ finally {
50
+ depth = 0;
51
+ releaseLock(ownership);
52
+ }
53
+ }