akm-cli 0.9.17-alpha.2 → 0.9.17-alpha.4

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 (343) hide show
  1. package/CHANGELOG.md +756 -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/proposal/drain.js +251 -644
  53. package/dist/commands/proposal/proposal-cli.js +3 -18
  54. package/dist/commands/proposal/proposal-types.js +20 -41
  55. package/dist/commands/proposal/proposal.js +1 -2
  56. package/dist/commands/proposal/propose.js +134 -160
  57. package/dist/commands/proposal/repository.js +502 -1487
  58. package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
  59. package/dist/commands/proposal/validators/proposal-validators.js +1 -1
  60. package/dist/commands/proposal/validators/proposals.js +13 -89
  61. package/dist/commands/read/curate.js +63 -413
  62. package/dist/commands/read/search-cli.js +16 -33
  63. package/dist/commands/read/search.js +17 -23
  64. package/dist/commands/read/show.js +2 -13
  65. package/dist/commands/sources/bundle-cli.js +25 -2
  66. package/dist/commands/sources/bundle-config-ops.js +7 -0
  67. package/dist/commands/sources/dangerous-env-audit.js +1 -2
  68. package/dist/commands/sources/info.js +2 -11
  69. package/dist/commands/sources/installed-stashes.js +197 -746
  70. package/dist/commands/sources/schema-repair.js +98 -129
  71. package/dist/commands/sources/source-add.js +62 -12
  72. package/dist/commands/sources/stash-cli.js +1 -1
  73. package/dist/commands/tasks/explain.js +10 -13
  74. package/dist/commands/tasks/tasks-cli.js +9 -8
  75. package/dist/commands/tasks/tasks.js +326 -930
  76. package/dist/commands/tasks/validate.js +42 -21
  77. package/dist/commands/workflow/plan.js +22 -29
  78. package/dist/commands/workflow-cli.js +4 -4
  79. package/dist/core/adapter/adapters/akm-adapter.js +0 -1
  80. package/dist/core/adapter/adapters/akm-lint.js +2 -3
  81. package/dist/core/adapter/adapters/akm-metadata.js +11 -12
  82. package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
  83. package/dist/core/adapter/execution-source.js +17 -29
  84. package/dist/core/asset/resolve-ref.js +1 -1
  85. package/dist/core/bundle-id.js +42 -5
  86. package/dist/core/bundle-rename.js +291 -0
  87. package/dist/core/config/config-io.js +1 -2
  88. package/dist/core/config/config-schema.js +1 -33
  89. package/dist/core/config/config-walker.js +1 -1
  90. package/dist/core/config/config.js +163 -68
  91. package/dist/core/config/legacy-source-shape-shim.js +38 -9
  92. package/dist/core/config/schema/embedding.js +20 -5
  93. package/dist/core/config/schema/engines.js +5 -0
  94. package/dist/core/config/schema/execution.js +1 -1
  95. package/dist/core/config/schema/experimental.js +1 -1
  96. package/dist/core/config/schema/improve-processes.js +21 -95
  97. package/dist/core/config/schema/improve.js +4 -42
  98. package/dist/core/config/schema/scheduler.js +12 -12
  99. package/dist/core/config/schema/search.js +6 -22
  100. package/dist/core/env-secret-ref.js +0 -1
  101. package/dist/core/errors.js +8 -9
  102. package/dist/core/file-lock.js +76 -173
  103. package/dist/core/logs-db.js +2 -2
  104. package/dist/core/paths.js +0 -27
  105. package/dist/core/redaction.js +109 -2
  106. package/dist/core/run-lock.js +2 -5
  107. package/dist/core/spawn-env.js +1 -1
  108. package/dist/core/state/migrations.js +108 -61
  109. package/dist/core/state-db-scope.js +2 -4
  110. package/dist/core/state-db.js +126 -692
  111. package/dist/core/type-presentation.js +1 -9
  112. package/dist/core/write-source.js +293 -1012
  113. package/dist/execution/input-contract.js +1 -1
  114. package/dist/execution/resolved-request.js +135 -689
  115. package/dist/execution/source.js +63 -257
  116. package/dist/execution/target-ref.js +1 -1
  117. package/dist/indexer/bundle-identity-guard.js +2 -2
  118. package/dist/indexer/db/graph-db.js +106 -46
  119. package/dist/indexer/ensure-index.js +44 -85
  120. package/dist/indexer/graph/graph-extraction.js +340 -562
  121. package/dist/indexer/graph/graph-related.js +130 -0
  122. package/dist/indexer/index-rebuild-lock.js +3 -11
  123. package/dist/indexer/index-writer-lock.js +8 -17
  124. package/dist/indexer/index-written-assets.js +139 -151
  125. package/dist/indexer/indexer.js +524 -846
  126. package/dist/indexer/materialize-embeddings.js +60 -397
  127. package/dist/indexer/passes/memory-inference.js +81 -90
  128. package/dist/indexer/passes/metadata.js +132 -200
  129. package/dist/indexer/read-preflight.js +0 -7
  130. package/dist/indexer/scan/doc-to-entry.js +1 -3
  131. package/dist/indexer/scan/drain-dir.js +1 -1
  132. package/dist/indexer/search/db-search.js +181 -590
  133. package/dist/indexer/search/fts-query.js +30 -41
  134. package/dist/indexer/search/ranking.js +28 -154
  135. package/dist/indexer/search/search-attribution.js +12 -32
  136. package/dist/indexer/search/search-fields.js +11 -15
  137. package/dist/indexer/search/search-hit-enrichers.js +54 -85
  138. package/dist/indexer/search/search-source.js +1 -4
  139. package/dist/indexer/usage/usage-events.js +2 -7
  140. package/dist/integrations/agent/engine-fallback.js +23 -40
  141. package/dist/integrations/agent/engine-resolution.js +93 -183
  142. package/dist/integrations/agent/execution.js +507 -0
  143. package/dist/integrations/agent/model-map.js +28 -156
  144. package/dist/integrations/agent/request-lowering.js +66 -141
  145. package/dist/integrations/agent/runner-dispatch.js +143 -321
  146. package/dist/integrations/agent/runner.js +54 -14
  147. package/dist/integrations/lockfile.js +53 -101
  148. package/dist/llm/embedders/deterministic.js +2 -3
  149. package/dist/llm/embedders/profile.js +71 -0
  150. package/dist/llm/embedders/remote.js +10 -15
  151. package/dist/llm/graph-extract.js +3 -12
  152. package/dist/llm/index-passes.js +3 -5
  153. package/dist/llm/memory-infer.js +1 -2
  154. package/dist/llm/metadata-enhance.js +1 -2
  155. package/dist/llm/structured-call.js +5 -24
  156. package/dist/output/generic-render.js +23 -11
  157. package/dist/output/html-render.js +13 -10
  158. package/dist/output/render-registry.js +3 -32
  159. package/dist/output/shapes/helpers.js +2 -34
  160. package/dist/output/shapes/passthrough.js +1 -9
  161. package/dist/{indexer/search/ranking-types.js → output/text/bundle-rename.js} +4 -1
  162. package/dist/output/text/command-format.js +60 -23
  163. package/dist/output/text/helpers.js +1 -1
  164. package/dist/output/text/migrate.js +5 -14
  165. package/dist/output/text/proposal-format.js +1 -2
  166. package/dist/output/text/workflow-format.js +0 -32
  167. package/dist/output/text.js +2 -0
  168. package/dist/registry/factory.js +4 -19
  169. package/dist/registry/network.js +66 -220
  170. package/dist/registry/providers/index.js +0 -2
  171. package/dist/registry/providers/skills-sh.js +3 -14
  172. package/dist/registry/providers/static-index.js +24 -26
  173. package/dist/registry/resolve.js +55 -131
  174. package/dist/scripts/akm-migrate-node.js +43937 -93313
  175. package/dist/scripts/akm-migrate.js +43697 -93071
  176. package/dist/setup/registry-stash-loader.js +4 -13
  177. package/dist/setup/semantic-assets.js +3 -44
  178. package/dist/setup/setup.js +1 -1
  179. package/dist/setup/steps/tasks.js +25 -15
  180. package/dist/sources/provider-factory.js +17 -18
  181. package/dist/sources/providers/filesystem.js +2 -3
  182. package/dist/sources/providers/git-install.js +7 -1
  183. package/dist/sources/providers/git-provider.js +0 -3
  184. package/dist/sources/providers/git-stash.js +0 -17
  185. package/dist/sources/providers/npm.js +2 -4
  186. package/dist/sources/providers/provider-utils.js +5 -10
  187. package/dist/sources/providers/website.js +0 -2
  188. package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
  189. package/dist/sources/website-url.js +2 -2
  190. package/dist/storage/database.js +9 -35
  191. package/dist/storage/repositories/improve-ledger-repository.js +168 -0
  192. package/dist/storage/repositories/index-connection.js +34 -70
  193. package/dist/storage/repositories/index-entries-repository.js +69 -111
  194. package/dist/storage/repositories/index-entry-mapper.js +1 -2
  195. package/dist/storage/repositories/index-entry-schema.js +83 -269
  196. package/dist/storage/repositories/index-fts-repository.js +86 -256
  197. package/dist/storage/repositories/index-llm-cache-repository.js +17 -0
  198. package/dist/storage/repositories/index-meta-repository.js +6 -4
  199. package/dist/storage/repositories/index-schema.js +192 -220
  200. package/dist/storage/repositories/index-utility-repository.js +8 -29
  201. package/dist/storage/repositories/index-vec-repository.js +133 -414
  202. package/dist/storage/repositories/outcome-repository.js +2 -1
  203. package/dist/storage/repositories/proposals-repository.js +35 -0
  204. package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
  205. package/dist/storage/repositories/task-history-repository.js +26 -4
  206. package/dist/storage/repositories/workflow-runs-repository.js +53 -244
  207. package/dist/storage/sqlite-migrations.js +136 -0
  208. package/dist/storage/sqlite-pragmas.js +11 -9
  209. package/dist/storage/sqlite-transaction.js +170 -0
  210. package/dist/storage/state-db-integrity.js +34 -27
  211. package/dist/tasks/activation-config.js +134 -62
  212. package/dist/tasks/backends/cron.js +129 -277
  213. package/dist/tasks/backends/exec-utils.js +2 -5
  214. package/dist/tasks/backends/launchd.js +125 -745
  215. package/dist/tasks/backends/schtasks.js +101 -620
  216. package/dist/tasks/prepare/prepare-support.js +5 -15
  217. package/dist/tasks/prepare/prepare.js +0 -2
  218. package/dist/tasks/resolve-akm-bin.js +20 -79
  219. package/dist/tasks/run/attempt-lifecycle.js +0 -1
  220. package/dist/tasks/scheduler-binding.js +18 -238
  221. package/dist/tasks/scheduler-invocation.js +52 -52
  222. package/dist/tasks/scheduler-lock.js +53 -0
  223. package/dist/tasks/scheduler-sync.js +363 -679
  224. package/dist/tasks/source/parse-task-source.js +160 -10
  225. package/dist/tasks/source/task-source-v3-frozen.js +3 -4
  226. package/dist/tasks/source/task-to-v4.js +2 -2
  227. package/dist/workflows/authoring/authoring.js +3 -12
  228. package/dist/workflows/compile.js +211 -0
  229. package/dist/workflows/concurrency-policy.js +13 -74
  230. package/dist/workflows/exec/child-invocation.js +3 -17
  231. package/dist/workflows/exec/child-workflow.js +32 -141
  232. package/dist/workflows/exec/dispatch-redaction.js +13 -53
  233. package/dist/workflows/exec/environment.js +98 -0
  234. package/dist/workflows/exec/exec-unit.js +33 -140
  235. package/dist/workflows/exec/frozen-judge.js +7 -59
  236. package/dist/workflows/exec/native-executor.js +82 -341
  237. package/dist/workflows/exec/param-secrets.js +29 -47
  238. package/dist/workflows/exec/run-workflow.js +154 -387
  239. package/dist/workflows/exec/scheduler.js +9 -36
  240. package/dist/workflows/exec/step-work.js +127 -430
  241. package/dist/workflows/exec/unit-dispatch.js +11 -63
  242. package/dist/workflows/exec/unit-writer.js +8 -52
  243. package/dist/workflows/exec/worktree.js +39 -273
  244. package/dist/workflows/freeze/child-output-references.js +4 -15
  245. package/dist/workflows/freeze/environment.js +99 -92
  246. package/dist/workflows/freeze/freeze.js +172 -0
  247. package/dist/workflows/freeze/step-values.js +19 -21
  248. package/dist/workflows/freeze/targets/child-workflow.js +23 -92
  249. package/dist/workflows/freeze/targets/command.js +10 -33
  250. package/dist/workflows/freeze/targets/script.js +5 -12
  251. package/dist/workflows/freeze/targets/shell.js +3 -6
  252. package/dist/workflows/freeze/targets/task.js +25 -80
  253. package/dist/workflows/freeze/task-bindings.js +20 -67
  254. package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
  255. package/dist/workflows/ir/params.js +6 -51
  256. package/dist/workflows/ir/plan-hash.js +2 -34
  257. package/dist/workflows/parser.js +140 -43
  258. package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
  259. package/dist/workflows/renderer.js +36 -69
  260. package/dist/workflows/resource-limits.js +12 -120
  261. package/dist/workflows/runtime/agent-identity.js +8 -40
  262. package/dist/workflows/runtime/run-outputs.js +3 -6
  263. package/dist/workflows/runtime/run-plan.js +316 -0
  264. package/dist/workflows/runtime/runs.js +48 -200
  265. package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
  266. package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
  267. package/dist/workflows/validate-summary.js +2 -7
  268. package/docs/integration/bundling-akm.md +49 -42
  269. package/docs/migration/README.md +1 -0
  270. package/docs/migration/release-notes/0.9.17.md +41 -0
  271. package/docs/migration/v0.9.1-to-v0.9.2.md +19 -7
  272. package/docs/reference/cli.md +182 -125
  273. package/docs/reference/configuration.md +49 -56
  274. package/docs/reference/data-and-telemetry.md +19 -20
  275. package/docs/reference/tasks.md +86 -38
  276. package/docs/reference/workflow-schema.md +14 -18
  277. package/docs/reference/workflows.md +6 -9
  278. package/package.json +1 -1
  279. package/schemas/akm-config.json +87 -406
  280. package/dist/commands/health/advisories.js +0 -150
  281. package/dist/commands/health/metrics.js +0 -329
  282. package/dist/commands/health/surfaces.js +0 -102
  283. package/dist/commands/improve/anti-collapse.js +0 -83
  284. package/dist/commands/improve/collapse-detector.js +0 -432
  285. package/dist/commands/improve/consolidate/eligibility.js +0 -48
  286. package/dist/commands/improve/consolidate/merge.js +0 -146
  287. package/dist/commands/improve/distill/promote-memory.js +0 -329
  288. package/dist/commands/improve/distill/quality-gate.js +0 -500
  289. package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
  290. package/dist/commands/improve/proposal-envelope.js +0 -31
  291. package/dist/commands/improve/run-context.js +0 -123
  292. package/dist/commands/improve/shared.js +0 -21
  293. package/dist/commands/improve/source-identity.js +0 -28
  294. package/dist/commands/improve/triage.js +0 -96
  295. package/dist/commands/proposal/drain-policies.js +0 -151
  296. package/dist/commands/sources/update-transaction.js +0 -220
  297. package/dist/core/action-contributors.js +0 -28
  298. package/dist/core/config/config-version-shim.js +0 -101
  299. package/dist/core/config/retired-experimental-keys-shim.js +0 -62
  300. package/dist/core/fs-txn.js +0 -405
  301. package/dist/core/lexical-score.js +0 -25
  302. package/dist/core/maintenance-barrier.js +0 -167
  303. package/dist/execution/executable-identity.js +0 -105
  304. package/dist/execution/guarded-source.js +0 -427
  305. package/dist/indexer/graph/graph-boost.js +0 -427
  306. package/dist/indexer/graph/graph-dedup.js +0 -95
  307. package/dist/indexer/search/name-match.js +0 -35
  308. package/dist/indexer/search/ranking-contributors.js +0 -515
  309. package/dist/indexer/walk/project-context.js +0 -192
  310. package/dist/integrations/agent/execution-cascade.js +0 -566
  311. package/dist/integrations/agent/execution-definitions.js +0 -202
  312. package/dist/integrations/agent/execution-lowering.js +0 -841
  313. package/dist/integrations/agent/execution-preparation.js +0 -98
  314. package/dist/integrations/agent/inline-execution.js +0 -74
  315. package/dist/registry/create-provider-registry.js +0 -29
  316. package/dist/registry/pinned-request-helper.js +0 -247
  317. package/dist/registry/pinned-transport.js +0 -717
  318. package/dist/sources/providers/index.js +0 -14
  319. package/dist/storage/engines/sqlite-migrations.js +0 -271
  320. package/dist/storage/repositories/canaries-repository.js +0 -107
  321. package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
  322. package/dist/storage/repositories/registry-cache.js +0 -113
  323. package/dist/tasks/scheduler-sync-preview.js +0 -52
  324. package/dist/workflows/freeze/resolve-steps.js +0 -86
  325. package/dist/workflows/freeze/source-freeze.js +0 -64
  326. package/dist/workflows/ir/compile.js +0 -321
  327. package/dist/workflows/ir/environment-v4.js +0 -330
  328. package/dist/workflows/ir/freeze-v4.js +0 -153
  329. package/dist/workflows/ir/schema-v4.js +0 -745
  330. package/dist/workflows/ir/schema.js +0 -354
  331. package/dist/workflows/program/schema.js +0 -78
  332. package/dist/workflows/runtime/checkin.js +0 -57
  333. package/dist/workflows/runtime/plan-classifier.js +0 -196
  334. package/dist/workflows/runtime/unit-checkin.js +0 -45
  335. package/dist/workflows/runtime/unit-phases.js +0 -20
  336. package/dist/workflows/schema.js +0 -4
  337. package/dist/workflows/source-ir/compile.js +0 -200
  338. package/dist/workflows/source-ir/program.js +0 -50
  339. package/dist/workflows/source-ir/result.js +0 -26
  340. package/dist/workflows/source-ir/schema.js +0 -786
  341. package/dist/workflows/source-ir/triggers.js +0 -79
  342. package/dist/workflows/source-ir/uses.js +0 -40
  343. 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
+ }