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
package/dist/akm CHANGED
@@ -4,212 +4,110 @@
4
4
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
5
 
6
6
  import { spawn, spawnSync } from "node:child_process";
7
- import { createHash } from "node:crypto";
8
- import fs from "node:fs";
9
- import path from "node:path";
10
7
  import { fileURLToPath } from "node:url";
11
8
 
12
- const CONTEXT_ARG = "--scheduler-context";
13
- const CONTEXT_KEYS = ["AKM_BUNDLE_DIR", "AKM_CONFIG_DIR", "AKM_DATA_DIR", "AKM_CACHE_DIR", "AKM_STATE_DIR"];
14
- const CONTROL_CHARACTER = /[\u0000-\u001f\u007f-\u009f]/;
9
+ // `--scheduler-context <descriptor>` passes through untouched: the CLI loads
10
+ // and validates it before anything else (`consumeSchedulerContextArg` in
11
+ // src/cli.ts). The scheduler supplies PATH itself (the crontab's `# akm:env`
12
+ // line, a launchd plist's EnvironmentVariables), so runtime selection below
13
+ // needs nothing from the descriptor. This launcher used to re-validate the
14
+ // descriptor with its own copy of the schema, and that copy rejected every
15
+ // descriptor the CLI writes since 0.9.17.
15
16
 
16
- function invalidContext(file, reason) {
17
- throw new Error(`Invalid scheduler context descriptor "${file}": ${reason}.`);
18
- }
19
-
20
- function readSchedulerContext(file) {
21
- if (CONTROL_CHARACTER.test(file) || (!path.posix.isAbsolute(file) && !path.win32.isAbsolute(file))) {
22
- invalidContext(file, "path must be absolute");
23
- }
24
- let linkStat;
25
- try {
26
- linkStat = fs.lstatSync(file);
27
- } catch (error) {
28
- invalidContext(file, error instanceof Error ? error.message : String(error));
29
- }
30
- if (linkStat.isSymbolicLink()) invalidContext(file, "symbolic links are not allowed");
31
- if (!linkStat.isFile()) invalidContext(file, "path is not a regular file");
32
-
33
- let bytes;
34
- let fd;
35
- try {
36
- const noFollow = process.platform === "win32" ? 0 : fs.constants.O_NOFOLLOW;
37
- fd = fs.openSync(file, fs.constants.O_RDONLY | noFollow);
38
- const stat = fs.fstatSync(fd);
39
- if (!stat.isFile()) invalidContext(file, "path is not a regular file");
40
- if (process.platform !== "win32") {
41
- if (typeof process.getuid !== "function") {
42
- invalidContext(file, "current uid is unavailable for ownership verification");
43
- }
44
- const uid = process.getuid();
45
- if (stat.uid !== uid) invalidContext(file, `file owner ${stat.uid} does not match uid ${uid}`);
46
- if ((stat.mode & 0o077) !== 0) invalidContext(file, "group or other permissions must be disabled");
47
- }
48
- bytes = fs.readFileSync(fd);
49
- } finally {
50
- if (fd !== undefined) fs.closeSync(fd);
51
- }
52
-
53
- const match = /^([a-f0-9]{64})\.json$/.exec(path.basename(file));
54
- const digest = createHash("sha256").update(bytes).digest("hex");
55
- if (!match || match[1] !== digest) {
56
- invalidContext(file, "content SHA-256 does not match the descriptor filename");
57
- }
58
-
59
- let descriptor;
60
- try {
61
- descriptor = JSON.parse(bytes.toString("utf8"));
62
- } catch (error) {
63
- invalidContext(file, error instanceof Error ? error.message : String(error));
64
- }
65
- if (
66
- typeof descriptor !== "object" ||
67
- descriptor === null ||
68
- Array.isArray(descriptor) ||
69
- descriptor.version !== 1 ||
70
- Object.keys(descriptor).sort().join("\0") !== "environment\0version"
71
- ) {
72
- invalidContext(file, "expected the scheduler context v1 schema");
73
- }
74
- const environment = descriptor.environment;
75
- const expected = [...CONTEXT_KEYS, "PATH"].sort();
76
- const keys =
77
- environment && typeof environment === "object" && !Array.isArray(environment)
78
- ? Object.keys(environment).sort()
79
- : [];
80
- if (keys.length !== expected.length || keys.some((key, index) => key !== expected[index])) {
81
- invalidContext(file, "environment keys do not match the scheduler context v1 schema");
82
- }
83
- for (const key of CONTEXT_KEYS) {
84
- const value = environment[key];
85
- if (
86
- typeof value !== "string" ||
87
- value.trim() === "" ||
88
- CONTROL_CHARACTER.test(value) ||
89
- (!path.posix.isAbsolute(value) && !path.win32.isAbsolute(value))
90
- ) {
91
- invalidContext(file, `${key} must be an absolute path without control characters`);
92
- }
93
- }
94
- if (typeof environment.PATH !== "string" || CONTROL_CHARACTER.test(environment.PATH)) {
95
- invalidContext(file, "PATH must be a string without control characters");
96
- }
97
- return environment;
98
- }
17
+ process.env.AKM_LAUNCHER_NODE = process.execPath;
18
+ process.env.AKM_LAUNCHER_PATH = fileURLToPath(import.meta.url);
19
+ // #956: lets the child (and its lock payloads) name the launcher pid
20
+ // alongside its own, and lets the child's parent-death watchdog tell a
21
+ // real launcher-managed run apart from a direct `bun src/cli.ts` run.
22
+ process.env.AKM_LAUNCHER_PID = String(process.pid);
99
23
 
100
- const contextIndex = process.argv.indexOf(CONTEXT_ARG);
101
- let contextValid = true;
102
- if (contextIndex !== -1) {
103
- try {
104
- const contextPath = process.argv[contextIndex + 1];
105
- if (!contextPath) throw new Error(`${CONTEXT_ARG} requires an absolute path.`);
106
- const environment = readSchedulerContext(contextPath);
107
- const expected = [...CONTEXT_KEYS, "PATH"].sort();
108
- for (const key of expected) process.env[key] = environment[key];
109
- process.argv.splice(contextIndex, 2);
110
- } catch (error) {
111
- console.error(`akm: ${error instanceof Error ? error.message : String(error)}`);
112
- process.exitCode = 78;
113
- contextValid = false;
24
+ if (!process.versions.bun) {
25
+ const [major = 0, minor = 0] = process.versions.node.split(".").map(Number);
26
+ if (major < 22) {
27
+ console.error("The akm-cli npm package requires Node.js >= 22 to bootstrap.");
28
+ process.exit(1);
114
29
  }
115
30
  }
116
31
 
117
- if (contextValid) {
118
- process.env.AKM_LAUNCHER_NODE = process.execPath;
119
- process.env.AKM_LAUNCHER_PATH = fileURLToPath(import.meta.url);
120
- // #956: lets the child (and its lock payloads) name the launcher pid
121
- // alongside its own, and lets the child's parent-death watchdog tell a
122
- // real launcher-managed run apart from a direct `bun src/cli.ts` run.
123
- process.env.AKM_LAUNCHER_PID = String(process.pid);
32
+ const bunProbe = process.versions.bun
33
+ ? { status: 0, stdout: process.versions.bun }
34
+ : spawnSync("bun", ["--version"], {
35
+ encoding: "utf8",
36
+ stdio: ["ignore", "pipe", "ignore"],
37
+ timeout: 5_000,
38
+ });
39
+ const [bunMajor = 0] = (bunProbe.status === 0 ? bunProbe.stdout.trim() : "").split(".").map(Number);
40
+ const useBun = bunMajor >= 1;
41
+ const bunEntry = fileURLToPath(new URL("./cli.js", import.meta.url));
42
+ const nodeEntry = fileURLToPath(new URL("./cli-node.mjs", import.meta.url));
124
43
 
125
- if (!process.versions.bun) {
126
- const [major = 0, minor = 0] = process.versions.node.split(".").map(Number);
127
- if (major < 22) {
128
- console.error("The akm-cli npm package requires Node.js >= 22 to bootstrap.");
129
- process.exit(1);
44
+ if (!useBun && !process.versions.bun) {
45
+ await import("./cli-node.mjs");
46
+ } else {
47
+ const command = useBun ? (process.versions.bun ? process.execPath : "bun") : "node";
48
+ const entry = useBun ? bunEntry : nodeEntry;
49
+ const runtime = useBun ? "Bun" : "Node.js";
50
+ const result = await new Promise((resolve) => {
51
+ const child = spawn(command, [entry, ...process.argv.slice(2)], {
52
+ stdio: "inherit",
53
+ env: process.env,
54
+ // #956: give the child its OWN process group on POSIX (`setsid()` —
55
+ // does not touch its inherited stdio; see `spawnsOwnProcessGroup` in
56
+ // src/core/subprocess.ts for the Windows caveat that rules this out
57
+ // there). Without this, a spawned child shares the launcher's
58
+ // process group by default, so a signal delivered to that GROUP (a
59
+ // terminal's Ctrl-C, or `kill -SIGINT -<pgid>`) reaches the child
60
+ // directly AND the launcher, which then also forwards it — a real
61
+ // second signal lands microseconds after the first. The child's
62
+ // `process.once(signal, ...)` handler has already unregistered
63
+ // itself for its first (direct) copy, so the second falls through
64
+ // to the runtime's default disposition (immediate termination) and
65
+ // the child's graceful shutdown (lock release, in-flight abort)
66
+ // never runs. Detaching the child's process group makes the
67
+ // launcher's forward below the SOLE delivery path to the child, so
68
+ // it is always exactly one signal.
69
+ detached: process.platform !== "win32",
70
+ });
71
+ // A `kill <launcher-pid>` (a scheduler timeout, a supervisor, an
72
+ // operator) used to end only this wrapper, orphaning the bun child —
73
+ // it kept running (and holding locks) for as long as its own work took.
74
+ // Forward the same signal so the child dies with its parent. Never
75
+ // forward once the child has already exited: forwarding to a
76
+ // dead/replaced pid would be at best a no-op and at worst a signal to
77
+ // an unrelated process that reused the pid.
78
+ let childExited = false;
79
+ child.once("exit", () => {
80
+ childExited = true;
81
+ });
82
+ const forwardSignal = (signal) => {
83
+ if (childExited) return;
84
+ try {
85
+ child.kill(signal);
86
+ } catch {
87
+ // Child exited in the race between the check above and here.
88
+ }
89
+ };
90
+ // `.once`, not `.on`: Node/Bun suppress a signal's default
91
+ // (process-terminating) disposition for as long as ANY listener stays
92
+ // registered for it. A persistent `.on` listener would still be
93
+ // registered when the `process.kill(process.pid, result.signal)`
94
+ // re-raise below runs at shutdown, swallowing it and leaving this
95
+ // launcher exiting 0 instead of reflecting the child's signal. `.once`
96
+ // consumes only the externally-delivered signal that triggers the
97
+ // forward, so the re-raise correctly falls through to the OS default.
98
+ for (const signal of ["SIGTERM", "SIGINT", "SIGHUP"]) {
99
+ process.once(signal, () => forwardSignal(signal));
130
100
  }
131
- }
101
+ child.once("error", (error) => resolve({ error }));
102
+ child.once("exit", (code, signal) => resolve({ code, signal }));
103
+ });
132
104
 
133
- const bunProbe = process.versions.bun
134
- ? { status: 0, stdout: process.versions.bun }
135
- : spawnSync("bun", ["--version"], {
136
- encoding: "utf8",
137
- stdio: ["ignore", "pipe", "ignore"],
138
- timeout: 5_000,
139
- });
140
- const [bunMajor = 0] = (bunProbe.status === 0 ? bunProbe.stdout.trim() : "").split(".").map(Number);
141
- const useBun = bunMajor >= 1;
142
- const bunEntry = fileURLToPath(new URL("./cli.js", import.meta.url));
143
- const nodeEntry = fileURLToPath(new URL("./cli-node.mjs", import.meta.url));
144
-
145
- if (!useBun && !process.versions.bun) {
146
- await import("./cli-node.mjs");
105
+ if (result.error) {
106
+ console.error(`akm launcher could not start ${runtime}: ${result.error.message}`);
107
+ process.exitCode = 127;
108
+ } else if (result.signal) {
109
+ process.kill(process.pid, result.signal);
147
110
  } else {
148
- const command = useBun ? (process.versions.bun ? process.execPath : "bun") : "node";
149
- const entry = useBun ? bunEntry : nodeEntry;
150
- const runtime = useBun ? "Bun" : "Node.js";
151
- const result = await new Promise((resolve) => {
152
- const child = spawn(command, [entry, ...process.argv.slice(2)], {
153
- stdio: "inherit",
154
- env: process.env,
155
- // #956: give the child its OWN process group on POSIX (`setsid()` —
156
- // does not touch its inherited stdio; see `spawnsOwnProcessGroup` in
157
- // src/core/subprocess.ts for the Windows caveat that rules this out
158
- // there). Without this, a spawned child shares the launcher's
159
- // process group by default, so a signal delivered to that GROUP (a
160
- // terminal's Ctrl-C, or `kill -SIGINT -<pgid>`) reaches the child
161
- // directly AND the launcher, which then also forwards it — a real
162
- // second signal lands microseconds after the first. The child's
163
- // `process.once(signal, ...)` handler has already unregistered
164
- // itself for its first (direct) copy, so the second falls through
165
- // to the runtime's default disposition (immediate termination) and
166
- // the child's graceful shutdown (lock release, in-flight abort)
167
- // never runs. Detaching the child's process group makes the
168
- // launcher's forward below the SOLE delivery path to the child, so
169
- // it is always exactly one signal.
170
- detached: process.platform !== "win32",
171
- });
172
- // A `kill <launcher-pid>` (a scheduler timeout, a supervisor, an
173
- // operator) used to end only this wrapper, orphaning the bun child —
174
- // it kept running (and holding locks) for as long as its own work took.
175
- // Forward the same signal so the child dies with its parent. Never
176
- // forward once the child has already exited: forwarding to a
177
- // dead/replaced pid would be at best a no-op and at worst a signal to
178
- // an unrelated process that reused the pid.
179
- let childExited = false;
180
- child.once("exit", () => {
181
- childExited = true;
182
- });
183
- const forwardSignal = (signal) => {
184
- if (childExited) return;
185
- try {
186
- child.kill(signal);
187
- } catch {
188
- // Child exited in the race between the check above and here.
189
- }
190
- };
191
- // `.once`, not `.on`: Node/Bun suppress a signal's default
192
- // (process-terminating) disposition for as long as ANY listener stays
193
- // registered for it. A persistent `.on` listener would still be
194
- // registered when the `process.kill(process.pid, result.signal)`
195
- // re-raise below runs at shutdown, swallowing it and leaving this
196
- // launcher exiting 0 instead of reflecting the child's signal. `.once`
197
- // consumes only the externally-delivered signal that triggers the
198
- // forward, so the re-raise correctly falls through to the OS default.
199
- for (const signal of ["SIGTERM", "SIGINT", "SIGHUP"]) {
200
- process.once(signal, () => forwardSignal(signal));
201
- }
202
- child.once("error", (error) => resolve({ error }));
203
- child.once("exit", (code, signal) => resolve({ code, signal }));
204
- });
205
-
206
- if (result.error) {
207
- console.error(`akm launcher could not start ${runtime}: ${result.error.message}`);
208
- process.exitCode = 127;
209
- } else if (result.signal) {
210
- process.kill(process.pid, result.signal);
211
- } else {
212
- process.exitCode = result.code ?? 1;
213
- }
111
+ process.exitCode = result.code ?? 1;
214
112
  }
215
113
  }
@@ -9,12 +9,13 @@
9
9
  */
10
10
  import { defineCommand } from "citty";
11
11
  import { stringify as yamlStringify } from "yaml";
12
+ import { renderHealthHtml } from "../commands/health/renderers.js";
12
13
  import { assertNever } from "../core/assert.js";
13
14
  import { AkmError, UsageError } from "../core/errors.js";
14
15
  import { getOutputMode } from "../output/context.js";
15
16
  import { renderGenericHtml, renderGenericMarkdown, renderGenericText } from "../output/generic-render.js";
16
17
  import { deliverRendered } from "../output/html-render.js";
17
- import { getHtmlRendererHandler, getMdRendererHandler } from "../output/render-registry.js";
18
+ import { getMdRendererHandler } from "../output/render-registry.js";
18
19
  import { shapeForCommand } from "../output/shapes.js";
19
20
  import { formatPlain, outputJsonl } from "../output/text.js";
20
21
  import { parseAllFlagValues } from "./invocation.js";
@@ -330,7 +331,10 @@ export function output(command, result) {
330
331
  return;
331
332
  }
332
333
  case "html": {
333
- const rendered = getHtmlRendererHandler(command)?.(shaped, mode.detail);
334
+ // `akm health` is the only command with a bespoke HTML report, so it is
335
+ // called directly rather than through a registry with one possible
336
+ // registrant (see `output/render-registry.ts`).
337
+ const rendered = command === "health" ? renderHealthHtml(shaped) : null;
334
338
  deliverRendered(rendered ?? renderGenericHtml(command, shaped), mode.outputPath);
335
339
  return;
336
340
  }
package/dist/cli.js CHANGED
@@ -177,7 +177,7 @@ const setupCommand = defineCommand({
177
177
  // the work, matching the `sync --push/--no-push` pattern. A flag
178
178
  // DECLARED as `no-init` can never be negated: `--no-init` parses as
179
179
  // "negate `init`", a name nothing declared, leaving the real key at its
180
- // default forever — see `search --no-project-context`'s identical fix.
180
+ // default forever — see `search --no-track-usage`'s identical fix.
181
181
  init: {
182
182
  type: "boolean",
183
183
  default: true,
@@ -545,19 +545,32 @@ const helpCommand = defineGroupCommand({
545
545
  process.stdout.write(`${await renderSectionedRootHelp()}\n`);
546
546
  },
547
547
  });
548
+ // Root `--help` text for the exit-code table. `EXIT_CODES` (src/cli/shared.ts)
549
+ // is the single source of the numbers; this only supplies the prose next to
550
+ // each one, so there is no third, independently-drifting copy of the table
551
+ // (the other copy is errors.ts's doc comment, which documents by reference
552
+ // instead of repeating the numbers).
553
+ const EXIT_CODE_DESCRIPTIONS = [
554
+ [EXIT_CODES.SUCCESS, "success"],
555
+ [EXIT_CODES.GENERAL, "not found / command-reported failure"],
556
+ [EXIT_CODES.USAGE, "usage error"],
557
+ [EXIT_CODES.HEALTH_WARN, "health warn (akm health only)"],
558
+ [EXIT_CODES.INTERNAL, "internal / unclassified error"],
559
+ [
560
+ EXIT_CODES.TEMPFAIL,
561
+ "transient (retry shortly — another akm process holds a lock or is writing state.db or index.db)",
562
+ ],
563
+ [EXIT_CODES.CONFIG, "config error"],
564
+ ];
565
+ function renderExitCodesHelp() {
566
+ return EXIT_CODE_DESCRIPTIONS.map(([code, description]) => ` ${String(code).padEnd(4)}${description}`).join("\n");
567
+ }
548
568
  export const main = defineCommand({
549
569
  meta: {
550
570
  name: "akm",
551
571
  version: pkgVersion,
552
572
  description: "Agent Knowledge Manager — search, show, and manage assets from your bundle.\n\n" +
553
- "Exit codes:\n" +
554
- " 0 success\n" +
555
- " 1 not found / command-reported failure\n" +
556
- " 2 usage error\n" +
557
- " 4 health warn (akm health only)\n" +
558
- " 70 internal / unclassified error\n" +
559
- " 75 transient (retry shortly — another akm process holds a lock or is writing state.db or index.db)\n" +
560
- " 78 config error",
573
+ `Exit codes:\n${renderExitCodesHelp()}`,
561
574
  },
562
575
  args: {
563
576
  // Single-sourced from GLOBAL_OUTPUT_ARGS (src/cli/shared.ts) so root help
@@ -5,7 +5,7 @@ import { UsageError } from "../../core/errors.js";
5
5
  import { warn } from "../../core/warn.js";
6
6
  import { isPortableExecutionAgentSelector } from "../../execution/source.js";
7
7
  import { fallbackAnnouncement, NO_ENGINE_MESSAGE_SUFFIX, NO_ENGINE_REMEDY, withEngineFallback, } from "../../integrations/agent/engine-fallback.js";
8
- import { executeInteractiveAgentInvocation } from "../../integrations/agent/execution-lowering.js";
8
+ import { executeInteractiveAgentInvocation } from "../../integrations/agent/runner-dispatch.js";
9
9
  import { executeCommandInvocation } from "../command/command-execution.js";
10
10
  function canonicalCurrent(options) {
11
11
  const current = {};
@@ -1,31 +1,23 @@
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 { ConfigError, UsageError } from "../../core/errors.js";
5
- import { cloneExecutionJsonObject } from "../../execution/json.js";
4
+ import { UsageError } from "../../core/errors.js";
6
5
  import { createInlineResolvedCommand, createResolvedCommand, createResolvedPersona, } from "../../execution/resolved-request.js";
7
6
  import { isPortableExecutionAgentSelector, } from "../../execution/source.js";
8
7
  import { recordIndexedShowUsage } from "../../indexer/usage/show-usage.js";
9
8
  import { resolveUsageEventSource } from "../../indexer/usage/usage-events.js";
10
- import { fallbackAnnouncement, NO_ENGINE_MESSAGE_SUFFIX, NO_ENGINE_REMEDY, } from "../../integrations/agent/engine-fallback.js";
11
- import { requireAuthorizedExecutionPlan, } from "../../integrations/agent/execution-cascade.js";
12
- import { dispatchLoweredExecutionRequest, lowerResolvedExecutionRequest, } from "../../integrations/agent/execution-lowering.js";
13
- import { prepareResolvedExecution } from "../../integrations/agent/execution-preparation.js";
9
+ import { fallbackAnnouncement } from "../../integrations/agent/engine-fallback.js";
10
+ import { buildExecution, resolveExecution, } from "../../integrations/agent/execution.js";
11
+ import { runExecution } from "../../integrations/agent/runner-dispatch.js";
14
12
  import { parseBuiltinCommandAction } from "./builtin-action.js";
15
13
  import { loadAdapterExecutionSource, } from "./execution-source-loader.js";
16
14
  import { applyPortableCommandArguments } from "./portable-template.js";
17
15
  /**
18
- * F-1 (spec §5.2 point 3): resolve the ambient-first provenance stamp
19
- * (matching the native arm's own `process.env.AKM_EVENT_SOURCE ?? …`) and
20
- * hand it to `dispatchLoweredExecutionRequest`'s dedicated, single-purpose
21
- * `eventSource` field — never through `runOptions`/`agentOptions`, which
22
- * stays exactly as untrusted for overriding resolved content as it was
23
- * before P1b (a caller-supplied `runOptions.env` still cannot replace frozen
24
- * request data, including a scheduler-restored directory value — see
25
- * `tests/integration/tasks-runner.test.ts`'s "forwards scheduled AKM
26
- * directory context … without trusting task or caller overrides").
16
+ * The ambient `AKM_EVENT_SOURCE` wins over the task runner's provenance; it
17
+ * rides `runExecution`'s single-purpose `eventSource`, never `runOptions.env`,
18
+ * so a caller cannot replace the request's own environment.
27
19
  */
28
- function loweredDispatchOptions(options) {
20
+ function runOptionsFor(options) {
29
21
  const { eventSource, ...rest } = options;
30
22
  if (eventSource === undefined)
31
23
  return rest;
@@ -59,11 +51,7 @@ function qualifyCommandSelectedPersona(selector, command) {
59
51
  return selector;
60
52
  return `${command.identity.bundle}//${selector}`;
61
53
  }
62
- function snapshotCommandConfig(config, path = "command config") {
63
- return cloneExecutionJsonObject(config, path);
64
- }
65
54
  export async function prepareCommandInvocation(options) {
66
- const inputConfig = snapshotCommandConfig(options.config, "command input config");
67
55
  const action = parseBuiltinCommandAction(options.action);
68
56
  const sourceLoader = options.sourceLoader ?? defaultSourceLoader;
69
57
  let renderedCommand;
@@ -71,7 +59,7 @@ export async function prepareCommandInvocation(options) {
71
59
  let command;
72
60
  if (action.kind === "stored") {
73
61
  const rendered = await sourceLoader(action.ref, "command", {
74
- config: inputConfig,
62
+ config: options.config,
75
63
  ...(options.sourceLookup ? { lookup: options.sourceLookup } : {}),
76
64
  });
77
65
  if (rendered.kind !== "command")
@@ -110,7 +98,7 @@ export async function prepareCommandInvocation(options) {
110
98
  ? qualifyCommandSelectedPersona(selectedAgent.value, renderedCommand)
111
99
  : selectedAgent.value;
112
100
  const rendered = await sourceLoader(lookupRef, "persona", {
113
- config: inputConfig,
101
+ config: options.config,
114
102
  ...(options.sourceLookup ? { lookup: options.sourceLookup } : {}),
115
103
  });
116
104
  if (rendered.kind !== "persona")
@@ -118,10 +106,9 @@ export async function prepareCommandInvocation(options) {
118
106
  renderedPersona = rendered;
119
107
  }
120
108
  const persona = renderedPersona ? createResolvedPersona(renderedPersona) : selectedAgent.present ? null : undefined;
121
- return prepareResolvedExecution({
109
+ return resolveExecution({
122
110
  command,
123
- config: inputConfig,
124
- invocationKind: options.invocationKind ?? "direct",
111
+ config: options.config,
125
112
  ...(persona !== undefined ? { persona } : {}),
126
113
  ...(renderedPersona ? { agentLayer: { id: renderedPersona.identity.ref, values: renderedPersona.defaults } } : {}),
127
114
  commandLayer: {
@@ -131,7 +118,6 @@ export async function prepareCommandInvocation(options) {
131
118
  ...(options.invocationDefaults ? { invocationDefaults: options.invocationDefaults } : {}),
132
119
  ...(options.current ? { current: options.current } : {}),
133
120
  ...(options.modelMap ? { modelMap: options.modelMap } : {}),
134
- ...(options.authorizeTools ? { authorizeTools: options.authorizeTools } : {}),
135
121
  });
136
122
  }
137
123
  function compareText(left, right) {
@@ -207,21 +193,6 @@ function safeDiagnosticNotice(notice) {
207
193
  if (!safeDiagnosticToken(notice.adapter))
208
194
  return undefined;
209
195
  switch (notice.code) {
210
- case "engine-fallback":
211
- return Object.freeze({
212
- code: "engine-fallback",
213
- severity: "info",
214
- adapter: "akm",
215
- field: "engine",
216
- message: "No engine was selected; using the fixed opencode-sdk fallback.",
217
- });
218
- case "unrecognized-request-notice":
219
- return Object.freeze({
220
- code: "unrecognized-request-notice",
221
- severity: "warning",
222
- adapter: "akm",
223
- message: "An unrecognized durable execution notice was omitted at the engine lowering boundary.",
224
- });
225
196
  case "untranslated-field": {
226
197
  const field = canonicalDiagnosticNoticeField(notice.field);
227
198
  if (!field)
@@ -264,26 +235,20 @@ function diagnosticNotices(notices) {
264
235
  return Object.freeze([...canonical.values()].sort((left, right) => compareText(JSON.stringify(left), JSON.stringify(right))));
265
236
  }
266
237
  /**
267
- * Authorize and lower a prepared invocation, then project only safe structural
268
- * diagnostics. It never acquires a dispatch lease, materializes credentials,
269
- * records usage, or exposes request/plan values.
238
+ * Build a prepared invocation and project only safe structural diagnostics.
239
+ * Reads no credential, records no usage, and exposes no request values.
270
240
  */
271
241
  export function inspectPreparedCommandInvocation(prepared) {
272
- const request = requireAuthorizedExecutionPlan(prepared.plan);
273
- const lowered = lowerResolvedExecutionRequest(request, prepared.config);
274
- const selectedEngine = request.engine.name;
275
- if (!selectedEngine) {
276
- throw new ConfigError(`command ${NO_ENGINE_MESSAGE_SUFFIX} ${NO_ENGINE_REMEDY}`, "INVALID_CONFIG_FILE");
277
- }
278
- return Object.freeze({
242
+ const built = buildExecution(prepared.request, prepared.runner);
243
+ return {
279
244
  schemaVersion: 1,
280
245
  shape: "command-dry-run",
281
246
  ok: true,
282
247
  dryRun: true,
283
- engine: selectedEngine,
284
- provenance: diagnosticProvenance(prepared.plan.provenance),
285
- notices: diagnosticNotices(lowered.notices),
286
- });
248
+ engine: prepared.request.engine.name,
249
+ provenance: diagnosticProvenance(prepared.provenance),
250
+ notices: diagnosticNotices(built.notices),
251
+ };
287
252
  }
288
253
  function resultEnvelope(result, engine, warnings, notices) {
289
254
  return {
@@ -302,13 +267,10 @@ function resultEnvelope(result, engine, warnings, notices) {
302
267
  };
303
268
  }
304
269
  export async function dispatchPreparedCommandInvocation(prepared, options = {}) {
305
- const request = requireAuthorizedExecutionPlan(prepared.plan);
306
- const lowered = lowerResolvedExecutionRequest(request, prepared.config);
270
+ const { request } = prepared;
271
+ const built = buildExecution(request, prepared.runner);
307
272
  const selectedEngine = request.engine.name;
308
- if (!selectedEngine) {
309
- throw new ConfigError(`command ${NO_ENGINE_MESSAGE_SUFFIX} ${NO_ENGINE_REMEDY}`, "INVALID_CONFIG_FILE");
310
- }
311
- const result = await dispatchLoweredExecutionRequest(lowered, loweredDispatchOptions(options));
273
+ const result = await runExecution(built, runOptionsFor(options));
312
274
  const consumedRefs = new Set();
313
275
  if (request.command.source)
314
276
  consumedRefs.add(request.command.source.ref);
@@ -322,7 +284,7 @@ export async function dispatchPreparedCommandInvocation(prepared, options = {})
322
284
  for (const ref of consumedRefs)
323
285
  recordIndexedShowUsage(ref, eventSource);
324
286
  const announcement = fallbackAnnouncement(prepared.fallbackEngineName, selectedEngine);
325
- return resultEnvelope(result, selectedEngine, announcement ? [announcement] : [], lowered.notices);
287
+ return resultEnvelope(result, selectedEngine, announcement ? [announcement] : [], built.notices);
326
288
  }
327
289
  export async function executeCommandInvocation(options, dispatchOptions = {}) {
328
290
  return dispatchPreparedCommandInvocation(await prepareCommandInvocation(options), dispatchOptions);
@@ -95,7 +95,6 @@ function appendLessonStrength(refInput, feedbackRef) {
95
95
  let mutationStarted = false;
96
96
  try {
97
97
  withWriteTargetMutation(resolved.target, [filePath], {
98
- ignored: "reject",
99
98
  purpose: "feedback-lesson-credit",
100
99
  message: `Update ${makeBundleRef(bundleId, conceptId)}`,
101
100
  }, () => {
@@ -15,7 +15,7 @@
15
15
  * same read extended to the accepted/rejected archive.
16
16
  */
17
17
  import { resolveStashDir } from "../../core/common.js";
18
- import { isStaleTargetRejection } from "../proposal/proposal-types.js";
18
+ import { isProceduralRejection } from "../proposal/proposal-types.js";
19
19
  import { listProposals } from "../proposal/repository.js";
20
20
  /**
21
21
  * Compute accept-rate-per-source metrics from the proposal store. Defaults to
@@ -32,7 +32,7 @@ export function computeAcceptRateBySource(stashDir) {
32
32
  // A stale-target auto-reject (STALE, R20) is procedural, not a
33
33
  // judgement on the content — counting it would understate the
34
34
  // source's real accept rate for content the drain will re-propose.
35
- if (status === "rejected" && isStaleTargetRejection(p))
35
+ if (status === "rejected" && isProceduralRejection(p))
36
36
  continue;
37
37
  const src = p.source || "(unknown)";
38
38
  const entry = bySource.get(src) ?? { accepted: 0, rejected: 0, pending: 0 };