akm-cli 0.9.17-alpha.3 → 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 +731 -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 +361 -751
  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 -441
  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
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 };