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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (343) hide show
  1. package/CHANGELOG.md +756 -0
  2. package/dist/akm +94 -196
  3. package/dist/cli/shared.js +6 -2
  4. package/dist/cli.js +22 -9
  5. package/dist/commands/agent/agent-dispatch.js +1 -1
  6. package/dist/commands/command/command-execution.js +24 -62
  7. package/dist/commands/feedback-cli.js +0 -1
  8. package/dist/commands/health/accept-rate.js +2 -2
  9. package/dist/commands/health/checks.js +30 -75
  10. package/dist/commands/health/config-skew.js +38 -0
  11. package/dist/commands/health/egress.js +54 -0
  12. package/dist/commands/health/html-report.js +0 -38
  13. package/dist/commands/health/improve-metrics.js +123 -562
  14. package/dist/commands/health/plugin-staleness.js +53 -3
  15. package/dist/commands/health/renderers.js +12 -4
  16. package/dist/commands/health/report-view-model.js +11 -106
  17. package/dist/commands/health/types-improve.js +4 -19
  18. package/dist/commands/health/windows.js +64 -73
  19. package/dist/commands/health.js +122 -143
  20. package/dist/commands/improve/consolidate/chunking.js +25 -100
  21. package/dist/commands/improve/consolidate/sanitize.js +54 -149
  22. package/dist/commands/improve/consolidate.js +538 -1075
  23. package/dist/commands/improve/content-hash.js +16 -24
  24. package/dist/commands/improve/distill/content-repair.js +18 -100
  25. package/dist/commands/improve/distill-guards.js +20 -81
  26. package/dist/commands/improve/distill-promotion-policy.js +23 -243
  27. package/dist/commands/improve/distill.js +608 -1075
  28. package/dist/commands/improve/eligibility.js +126 -400
  29. package/dist/commands/improve/execution.js +3 -5
  30. package/dist/commands/improve/extract.js +487 -1046
  31. package/dist/commands/improve/feedback-valence.js +0 -25
  32. package/dist/commands/improve/improve-cli.js +29 -166
  33. package/dist/commands/improve/improve-result-file.js +10 -66
  34. package/dist/commands/improve/improve-strategies.js +12 -7
  35. package/dist/commands/improve/improve-usage-report.js +18 -64
  36. package/dist/commands/improve/improve.js +443 -1063
  37. package/dist/commands/improve/ledger.js +114 -0
  38. package/dist/commands/improve/locks.js +2 -8
  39. package/dist/commands/improve/loop-stages.js +459 -1172
  40. package/dist/commands/improve/memory/derived-ref.js +12 -77
  41. package/dist/commands/improve/memory/memory-belief.js +14 -118
  42. package/dist/commands/improve/memory/memory-improve.js +4 -3
  43. package/dist/commands/improve/outcome-loop.js +28 -156
  44. package/dist/commands/improve/planner.js +5 -10
  45. package/dist/commands/improve/preparation.js +851 -2339
  46. package/dist/commands/improve/proactive-maintenance.js +34 -101
  47. package/dist/commands/improve/reflect-noise.js +104 -280
  48. package/dist/commands/improve/reflect.js +621 -1367
  49. package/dist/commands/improve/salience.js +46 -232
  50. package/dist/commands/improve/session-asset.js +19 -100
  51. package/dist/commands/improve/stage.js +323 -0
  52. package/dist/commands/proposal/drain.js +251 -644
  53. package/dist/commands/proposal/proposal-cli.js +3 -18
  54. package/dist/commands/proposal/proposal-types.js +20 -41
  55. package/dist/commands/proposal/proposal.js +1 -2
  56. package/dist/commands/proposal/propose.js +134 -160
  57. package/dist/commands/proposal/repository.js +502 -1487
  58. package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
  59. package/dist/commands/proposal/validators/proposal-validators.js +1 -1
  60. package/dist/commands/proposal/validators/proposals.js +13 -89
  61. package/dist/commands/read/curate.js +63 -413
  62. package/dist/commands/read/search-cli.js +16 -33
  63. package/dist/commands/read/search.js +17 -23
  64. package/dist/commands/read/show.js +2 -13
  65. package/dist/commands/sources/bundle-cli.js +25 -2
  66. package/dist/commands/sources/bundle-config-ops.js +7 -0
  67. package/dist/commands/sources/dangerous-env-audit.js +1 -2
  68. package/dist/commands/sources/info.js +2 -11
  69. package/dist/commands/sources/installed-stashes.js +197 -746
  70. package/dist/commands/sources/schema-repair.js +98 -129
  71. package/dist/commands/sources/source-add.js +62 -12
  72. package/dist/commands/sources/stash-cli.js +1 -1
  73. package/dist/commands/tasks/explain.js +10 -13
  74. package/dist/commands/tasks/tasks-cli.js +9 -8
  75. package/dist/commands/tasks/tasks.js +326 -930
  76. package/dist/commands/tasks/validate.js +42 -21
  77. package/dist/commands/workflow/plan.js +22 -29
  78. package/dist/commands/workflow-cli.js +4 -4
  79. package/dist/core/adapter/adapters/akm-adapter.js +0 -1
  80. package/dist/core/adapter/adapters/akm-lint.js +2 -3
  81. package/dist/core/adapter/adapters/akm-metadata.js +11 -12
  82. package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
  83. package/dist/core/adapter/execution-source.js +17 -29
  84. package/dist/core/asset/resolve-ref.js +1 -1
  85. package/dist/core/bundle-id.js +42 -5
  86. package/dist/core/bundle-rename.js +291 -0
  87. package/dist/core/config/config-io.js +1 -2
  88. package/dist/core/config/config-schema.js +1 -33
  89. package/dist/core/config/config-walker.js +1 -1
  90. package/dist/core/config/config.js +163 -68
  91. package/dist/core/config/legacy-source-shape-shim.js +38 -9
  92. package/dist/core/config/schema/embedding.js +20 -5
  93. package/dist/core/config/schema/engines.js +5 -0
  94. package/dist/core/config/schema/execution.js +1 -1
  95. package/dist/core/config/schema/experimental.js +1 -1
  96. package/dist/core/config/schema/improve-processes.js +21 -95
  97. package/dist/core/config/schema/improve.js +4 -42
  98. package/dist/core/config/schema/scheduler.js +12 -12
  99. package/dist/core/config/schema/search.js +6 -22
  100. package/dist/core/env-secret-ref.js +0 -1
  101. package/dist/core/errors.js +8 -9
  102. package/dist/core/file-lock.js +76 -173
  103. package/dist/core/logs-db.js +2 -2
  104. package/dist/core/paths.js +0 -27
  105. package/dist/core/redaction.js +109 -2
  106. package/dist/core/run-lock.js +2 -5
  107. package/dist/core/spawn-env.js +1 -1
  108. package/dist/core/state/migrations.js +108 -61
  109. package/dist/core/state-db-scope.js +2 -4
  110. package/dist/core/state-db.js +126 -692
  111. package/dist/core/type-presentation.js +1 -9
  112. package/dist/core/write-source.js +293 -1012
  113. package/dist/execution/input-contract.js +1 -1
  114. package/dist/execution/resolved-request.js +135 -689
  115. package/dist/execution/source.js +63 -257
  116. package/dist/execution/target-ref.js +1 -1
  117. package/dist/indexer/bundle-identity-guard.js +2 -2
  118. package/dist/indexer/db/graph-db.js +106 -46
  119. package/dist/indexer/ensure-index.js +44 -85
  120. package/dist/indexer/graph/graph-extraction.js +340 -562
  121. package/dist/indexer/graph/graph-related.js +130 -0
  122. package/dist/indexer/index-rebuild-lock.js +3 -11
  123. package/dist/indexer/index-writer-lock.js +8 -17
  124. package/dist/indexer/index-written-assets.js +139 -151
  125. package/dist/indexer/indexer.js +524 -846
  126. package/dist/indexer/materialize-embeddings.js +60 -397
  127. package/dist/indexer/passes/memory-inference.js +81 -90
  128. package/dist/indexer/passes/metadata.js +132 -200
  129. package/dist/indexer/read-preflight.js +0 -7
  130. package/dist/indexer/scan/doc-to-entry.js +1 -3
  131. package/dist/indexer/scan/drain-dir.js +1 -1
  132. package/dist/indexer/search/db-search.js +181 -590
  133. package/dist/indexer/search/fts-query.js +30 -41
  134. package/dist/indexer/search/ranking.js +28 -154
  135. package/dist/indexer/search/search-attribution.js +12 -32
  136. package/dist/indexer/search/search-fields.js +11 -15
  137. package/dist/indexer/search/search-hit-enrichers.js +54 -85
  138. package/dist/indexer/search/search-source.js +1 -4
  139. package/dist/indexer/usage/usage-events.js +2 -7
  140. package/dist/integrations/agent/engine-fallback.js +23 -40
  141. package/dist/integrations/agent/engine-resolution.js +93 -183
  142. package/dist/integrations/agent/execution.js +507 -0
  143. package/dist/integrations/agent/model-map.js +28 -156
  144. package/dist/integrations/agent/request-lowering.js +66 -141
  145. package/dist/integrations/agent/runner-dispatch.js +143 -321
  146. package/dist/integrations/agent/runner.js +54 -14
  147. package/dist/integrations/lockfile.js +53 -101
  148. package/dist/llm/embedders/deterministic.js +2 -3
  149. package/dist/llm/embedders/profile.js +71 -0
  150. package/dist/llm/embedders/remote.js +10 -15
  151. package/dist/llm/graph-extract.js +3 -12
  152. package/dist/llm/index-passes.js +3 -5
  153. package/dist/llm/memory-infer.js +1 -2
  154. package/dist/llm/metadata-enhance.js +1 -2
  155. package/dist/llm/structured-call.js +5 -24
  156. package/dist/output/generic-render.js +23 -11
  157. package/dist/output/html-render.js +13 -10
  158. package/dist/output/render-registry.js +3 -32
  159. package/dist/output/shapes/helpers.js +2 -34
  160. package/dist/output/shapes/passthrough.js +1 -9
  161. package/dist/{indexer/search/ranking-types.js → output/text/bundle-rename.js} +4 -1
  162. package/dist/output/text/command-format.js +60 -23
  163. package/dist/output/text/helpers.js +1 -1
  164. package/dist/output/text/migrate.js +5 -14
  165. package/dist/output/text/proposal-format.js +1 -2
  166. package/dist/output/text/workflow-format.js +0 -32
  167. package/dist/output/text.js +2 -0
  168. package/dist/registry/factory.js +4 -19
  169. package/dist/registry/network.js +66 -220
  170. package/dist/registry/providers/index.js +0 -2
  171. package/dist/registry/providers/skills-sh.js +3 -14
  172. package/dist/registry/providers/static-index.js +24 -26
  173. package/dist/registry/resolve.js +55 -131
  174. package/dist/scripts/akm-migrate-node.js +43937 -93313
  175. package/dist/scripts/akm-migrate.js +43697 -93071
  176. package/dist/setup/registry-stash-loader.js +4 -13
  177. package/dist/setup/semantic-assets.js +3 -44
  178. package/dist/setup/setup.js +1 -1
  179. package/dist/setup/steps/tasks.js +25 -15
  180. package/dist/sources/provider-factory.js +17 -18
  181. package/dist/sources/providers/filesystem.js +2 -3
  182. package/dist/sources/providers/git-install.js +7 -1
  183. package/dist/sources/providers/git-provider.js +0 -3
  184. package/dist/sources/providers/git-stash.js +0 -17
  185. package/dist/sources/providers/npm.js +2 -4
  186. package/dist/sources/providers/provider-utils.js +5 -10
  187. package/dist/sources/providers/website.js +0 -2
  188. package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
  189. package/dist/sources/website-url.js +2 -2
  190. package/dist/storage/database.js +9 -35
  191. package/dist/storage/repositories/improve-ledger-repository.js +168 -0
  192. package/dist/storage/repositories/index-connection.js +34 -70
  193. package/dist/storage/repositories/index-entries-repository.js +69 -111
  194. package/dist/storage/repositories/index-entry-mapper.js +1 -2
  195. package/dist/storage/repositories/index-entry-schema.js +83 -269
  196. package/dist/storage/repositories/index-fts-repository.js +86 -256
  197. package/dist/storage/repositories/index-llm-cache-repository.js +17 -0
  198. package/dist/storage/repositories/index-meta-repository.js +6 -4
  199. package/dist/storage/repositories/index-schema.js +192 -220
  200. package/dist/storage/repositories/index-utility-repository.js +8 -29
  201. package/dist/storage/repositories/index-vec-repository.js +133 -414
  202. package/dist/storage/repositories/outcome-repository.js +2 -1
  203. package/dist/storage/repositories/proposals-repository.js +35 -0
  204. package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
  205. package/dist/storage/repositories/task-history-repository.js +26 -4
  206. package/dist/storage/repositories/workflow-runs-repository.js +53 -244
  207. package/dist/storage/sqlite-migrations.js +136 -0
  208. package/dist/storage/sqlite-pragmas.js +11 -9
  209. package/dist/storage/sqlite-transaction.js +170 -0
  210. package/dist/storage/state-db-integrity.js +34 -27
  211. package/dist/tasks/activation-config.js +134 -62
  212. package/dist/tasks/backends/cron.js +129 -277
  213. package/dist/tasks/backends/exec-utils.js +2 -5
  214. package/dist/tasks/backends/launchd.js +125 -745
  215. package/dist/tasks/backends/schtasks.js +101 -620
  216. package/dist/tasks/prepare/prepare-support.js +5 -15
  217. package/dist/tasks/prepare/prepare.js +0 -2
  218. package/dist/tasks/resolve-akm-bin.js +20 -79
  219. package/dist/tasks/run/attempt-lifecycle.js +0 -1
  220. package/dist/tasks/scheduler-binding.js +18 -238
  221. package/dist/tasks/scheduler-invocation.js +52 -52
  222. package/dist/tasks/scheduler-lock.js +53 -0
  223. package/dist/tasks/scheduler-sync.js +363 -679
  224. package/dist/tasks/source/parse-task-source.js +160 -10
  225. package/dist/tasks/source/task-source-v3-frozen.js +3 -4
  226. package/dist/tasks/source/task-to-v4.js +2 -2
  227. package/dist/workflows/authoring/authoring.js +3 -12
  228. package/dist/workflows/compile.js +211 -0
  229. package/dist/workflows/concurrency-policy.js +13 -74
  230. package/dist/workflows/exec/child-invocation.js +3 -17
  231. package/dist/workflows/exec/child-workflow.js +32 -141
  232. package/dist/workflows/exec/dispatch-redaction.js +13 -53
  233. package/dist/workflows/exec/environment.js +98 -0
  234. package/dist/workflows/exec/exec-unit.js +33 -140
  235. package/dist/workflows/exec/frozen-judge.js +7 -59
  236. package/dist/workflows/exec/native-executor.js +82 -341
  237. package/dist/workflows/exec/param-secrets.js +29 -47
  238. package/dist/workflows/exec/run-workflow.js +154 -387
  239. package/dist/workflows/exec/scheduler.js +9 -36
  240. package/dist/workflows/exec/step-work.js +127 -430
  241. package/dist/workflows/exec/unit-dispatch.js +11 -63
  242. package/dist/workflows/exec/unit-writer.js +8 -52
  243. package/dist/workflows/exec/worktree.js +39 -273
  244. package/dist/workflows/freeze/child-output-references.js +4 -15
  245. package/dist/workflows/freeze/environment.js +99 -92
  246. package/dist/workflows/freeze/freeze.js +172 -0
  247. package/dist/workflows/freeze/step-values.js +19 -21
  248. package/dist/workflows/freeze/targets/child-workflow.js +23 -92
  249. package/dist/workflows/freeze/targets/command.js +10 -33
  250. package/dist/workflows/freeze/targets/script.js +5 -12
  251. package/dist/workflows/freeze/targets/shell.js +3 -6
  252. package/dist/workflows/freeze/targets/task.js +25 -80
  253. package/dist/workflows/freeze/task-bindings.js +20 -67
  254. package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
  255. package/dist/workflows/ir/params.js +6 -51
  256. package/dist/workflows/ir/plan-hash.js +2 -34
  257. package/dist/workflows/parser.js +140 -43
  258. package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
  259. package/dist/workflows/renderer.js +36 -69
  260. package/dist/workflows/resource-limits.js +12 -120
  261. package/dist/workflows/runtime/agent-identity.js +8 -40
  262. package/dist/workflows/runtime/run-outputs.js +3 -6
  263. package/dist/workflows/runtime/run-plan.js +316 -0
  264. package/dist/workflows/runtime/runs.js +48 -200
  265. package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
  266. package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
  267. package/dist/workflows/validate-summary.js +2 -7
  268. package/docs/integration/bundling-akm.md +49 -42
  269. package/docs/migration/README.md +1 -0
  270. package/docs/migration/release-notes/0.9.17.md +41 -0
  271. package/docs/migration/v0.9.1-to-v0.9.2.md +19 -7
  272. package/docs/reference/cli.md +182 -125
  273. package/docs/reference/configuration.md +49 -56
  274. package/docs/reference/data-and-telemetry.md +19 -20
  275. package/docs/reference/tasks.md +86 -38
  276. package/docs/reference/workflow-schema.md +14 -18
  277. package/docs/reference/workflows.md +6 -9
  278. package/package.json +1 -1
  279. package/schemas/akm-config.json +87 -406
  280. package/dist/commands/health/advisories.js +0 -150
  281. package/dist/commands/health/metrics.js +0 -329
  282. package/dist/commands/health/surfaces.js +0 -102
  283. package/dist/commands/improve/anti-collapse.js +0 -83
  284. package/dist/commands/improve/collapse-detector.js +0 -432
  285. package/dist/commands/improve/consolidate/eligibility.js +0 -48
  286. package/dist/commands/improve/consolidate/merge.js +0 -146
  287. package/dist/commands/improve/distill/promote-memory.js +0 -329
  288. package/dist/commands/improve/distill/quality-gate.js +0 -500
  289. package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
  290. package/dist/commands/improve/proposal-envelope.js +0 -31
  291. package/dist/commands/improve/run-context.js +0 -123
  292. package/dist/commands/improve/shared.js +0 -21
  293. package/dist/commands/improve/source-identity.js +0 -28
  294. package/dist/commands/improve/triage.js +0 -96
  295. package/dist/commands/proposal/drain-policies.js +0 -151
  296. package/dist/commands/sources/update-transaction.js +0 -220
  297. package/dist/core/action-contributors.js +0 -28
  298. package/dist/core/config/config-version-shim.js +0 -101
  299. package/dist/core/config/retired-experimental-keys-shim.js +0 -62
  300. package/dist/core/fs-txn.js +0 -405
  301. package/dist/core/lexical-score.js +0 -25
  302. package/dist/core/maintenance-barrier.js +0 -167
  303. package/dist/execution/executable-identity.js +0 -105
  304. package/dist/execution/guarded-source.js +0 -427
  305. package/dist/indexer/graph/graph-boost.js +0 -427
  306. package/dist/indexer/graph/graph-dedup.js +0 -95
  307. package/dist/indexer/search/name-match.js +0 -35
  308. package/dist/indexer/search/ranking-contributors.js +0 -515
  309. package/dist/indexer/walk/project-context.js +0 -192
  310. package/dist/integrations/agent/execution-cascade.js +0 -566
  311. package/dist/integrations/agent/execution-definitions.js +0 -202
  312. package/dist/integrations/agent/execution-lowering.js +0 -841
  313. package/dist/integrations/agent/execution-preparation.js +0 -98
  314. package/dist/integrations/agent/inline-execution.js +0 -74
  315. package/dist/registry/create-provider-registry.js +0 -29
  316. package/dist/registry/pinned-request-helper.js +0 -247
  317. package/dist/registry/pinned-transport.js +0 -717
  318. package/dist/sources/providers/index.js +0 -14
  319. package/dist/storage/engines/sqlite-migrations.js +0 -271
  320. package/dist/storage/repositories/canaries-repository.js +0 -107
  321. package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
  322. package/dist/storage/repositories/registry-cache.js +0 -113
  323. package/dist/tasks/scheduler-sync-preview.js +0 -52
  324. package/dist/workflows/freeze/resolve-steps.js +0 -86
  325. package/dist/workflows/freeze/source-freeze.js +0 -64
  326. package/dist/workflows/ir/compile.js +0 -321
  327. package/dist/workflows/ir/environment-v4.js +0 -330
  328. package/dist/workflows/ir/freeze-v4.js +0 -153
  329. package/dist/workflows/ir/schema-v4.js +0 -745
  330. package/dist/workflows/ir/schema.js +0 -354
  331. package/dist/workflows/program/schema.js +0 -78
  332. package/dist/workflows/runtime/checkin.js +0 -57
  333. package/dist/workflows/runtime/plan-classifier.js +0 -196
  334. package/dist/workflows/runtime/unit-checkin.js +0 -45
  335. package/dist/workflows/runtime/unit-phases.js +0 -20
  336. package/dist/workflows/schema.js +0 -4
  337. package/dist/workflows/source-ir/compile.js +0 -200
  338. package/dist/workflows/source-ir/program.js +0 -50
  339. package/dist/workflows/source-ir/result.js +0 -26
  340. package/dist/workflows/source-ir/schema.js +0 -786
  341. package/dist/workflows/source-ir/triggers.js +0 -79
  342. package/dist/workflows/source-ir/uses.js +0 -40
  343. package/dist/workflows/validator.js +0 -60
@@ -2,278 +2,49 @@
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
4
  /**
5
- * The low-level exhaustive dispatch seam for the {@link RunnerSpec} transport
6
- * union (`llm | agent | sdk`). User/model work reaches this function only
7
- * after `ResolvedExecutionRequestV1` preparation and engine-owned lowering;
8
- * `dispatchLoweredExecutionRequest` supplies the direct-LLM handler. A
9
- * prompt-free native launch crosses the explicit payload-free interactive
10
- * lowerer in that same shared layer.
11
- *
12
- * Agent and SDK arms use the shared profile runners. The LLM arm deliberately
13
- * requires a handler because structured callers own their parse/fallback
14
- * contract. The `assertNever` arm keeps a fourth transport kind from becoming
15
- * an implicit fallthrough. Symbolic LLM and SDK-fallback credentials are
16
- * materialized here, at authorized operation-lease acquisition or the final
17
- * single-call boundary, then scrubbed from the result.
5
+ * Run a built execution: the one place credentials are read. A direct LLM
6
+ * goes to `chatCompletion`, an agent engine to `runAgent` (the harness builder
7
+ * turns the request into argv), an SDK engine to `runOpencodeSdk`. Every
8
+ * credential and passthrough value that could reach the child is redacted
9
+ * from the result.
18
10
  */
19
11
  import { assertNever } from "../../core/assert.js";
12
+ import { UsageError } from "../../core/errors.js";
20
13
  import { collectSensitiveValues, isEnvPassthroughValueSafeToExpose, redactSensitiveText, redactSensitiveValue, } from "../../core/redaction.js";
21
- import { spawnEnvNamesFor } from "../../core/spawn-env.js";
22
- import { closeServer as disposeOpencodeSdkServers, opencodeSdkServerEnvironmentNames, runOpencodeSdk, } from "../harnesses/opencode-sdk/sdk-runner.js";
23
- import { lookupApiKeyFileValue, lookupApiKeySecretRefValue, lookupCredentialFromEnv, materializeLlmConnection, materializeLlmConnectionWithCredential, resolveLlmCredentialValue, } from "./engine-resolution.js";
24
- import { materializeLlmRunnerConnection, materializeLlmRunnerConnectionWithCredential, } from "./runner.js";
14
+ import { chatCompletion, LlmCallError } from "../../llm/client.js";
15
+ import { closeServer as disposeOpencodeSdkServers, runOpencodeSdk } from "../harnesses/opencode-sdk/sdk-runner.js";
16
+ import { lookupApiKeyFileValue, lookupApiKeySecretRefValue, lookupCredentialFromEnv, resolveEngine, } from "./engine-resolution.js";
17
+ import { materializeLlmRunnerConnection, materializeSdkFallbackConnection } from "./runner.js";
25
18
  import { runAgent } from "./spawn.js";
26
- const liveRunnerDispatchLeases = new WeakMap();
27
- const issuedRunnerDispatchLeases = new WeakSet();
28
- const disposedRunnerDispatchLeases = new WeakSet();
29
- function credentialBinding(credential) {
30
- return credential ? { names: [...credential.names], required: credential.required } : null;
31
- }
32
- function passthroughBinding(spec) {
33
- return [...new Set(spec.profile.envPassthrough)].sort();
34
- }
35
- function dispatchEnvironmentNames(spec) {
36
- switch (spec.kind) {
37
- case "llm":
38
- return [];
39
- case "agent":
40
- return spawnEnvNamesFor(spec.profile.envPassthrough);
41
- case "sdk":
42
- return opencodeSdkServerEnvironmentNames(spec.profile);
43
- default:
44
- return assertNever(spec);
45
- }
46
- }
47
- function snapshotDispatchEnvironment(spec, envSource) {
48
- const snapshot = new Map();
49
- const read = (name) => {
50
- if (!snapshot.has(name))
51
- snapshot.set(name, envSource[name]);
52
- return snapshot.get(name);
53
- };
54
- const environmentSource = new Proxy(Object.create(null), {
55
- get(_target, property) {
56
- return typeof property === "string" ? snapshot.get(property) : undefined;
57
- },
58
- });
59
- for (const name of dispatchEnvironmentNames(spec))
60
- read(name);
61
- const passthroughSensitiveValues = new Set();
62
- if (spec.kind !== "llm") {
63
- for (const name of new Set(spec.profile.envPassthrough)) {
64
- const value = snapshot.get(name);
65
- if (!isEnvPassthroughValueSafeToExpose(name, value) && value)
66
- passthroughSensitiveValues.add(value);
67
- }
19
+ const OPERATIONAL_OPTIONS = [
20
+ "stdio",
21
+ "parseOutput",
22
+ "signal",
23
+ "envSource",
24
+ "spawn",
25
+ "setTimeoutFn",
26
+ "clearTimeoutFn",
27
+ "onEvent",
28
+ ];
29
+ /** Every value that can reach one dispatch and must never be echoed back: credentials and secret-looking env. */
30
+ export function collectDispatchSensitiveValues(runner, opts, envSource = opts.envSource ?? process.env) {
31
+ const values = [];
32
+ if (runner.kind === "llm") {
33
+ values.push(runner.connection.apiKey, lookupCredentialFromEnv(runner.credential, envSource), runner.apiKeyFile ? lookupApiKeyFileValue(runner.apiKeyFile) : undefined, runner.apiKeySecretRef ? lookupApiKeySecretRefValue(runner.apiKeySecretRef) : undefined);
68
34
  }
69
- return { environmentSnapshot: snapshot, environmentSource, passthroughSensitiveValues, read };
70
- }
71
- /** Bind only transport identity; request model/inference/schema/timeout remain per-call. */
72
- function runnerLeaseBinding(spec) {
73
- switch (spec.kind) {
74
- case "llm":
75
- return JSON.stringify({
76
- kind: spec.kind,
77
- engine: spec.engine ?? null,
78
- endpoint: spec.connection.endpoint,
79
- provider: spec.connection.provider ?? null,
80
- credential: credentialBinding(spec.credential),
81
- apiKeyFile: spec.apiKeyFile ?? null,
82
- });
83
- case "agent":
84
- return JSON.stringify({
85
- kind: spec.kind,
86
- engine: spec.engine ?? null,
87
- platform: spec.profile.platform ?? null,
88
- name: spec.profile.name,
89
- bin: spec.profile.bin,
90
- envPassthrough: passthroughBinding(spec),
91
- });
92
- case "sdk":
93
- return JSON.stringify({
94
- kind: spec.kind,
95
- engine: spec.engine ?? null,
96
- platform: spec.profile.platform ?? null,
97
- name: spec.profile.name,
98
- bin: spec.profile.bin,
99
- envPassthrough: passthroughBinding(spec),
100
- fallbackEndpoint: spec.fallbackConnection?.endpoint ?? null,
101
- fallbackProvider: spec.fallbackConnection?.provider ?? null,
102
- fallbackCredential: credentialBinding(spec.fallbackCredential),
103
- fallbackApiKeyFile: spec.fallbackApiKeyFile ?? null,
104
- });
105
- default:
106
- return assertNever(spec);
107
- }
108
- }
109
- function requireRunnerDispatchLease(lease, spec) {
110
- if (typeof lease !== "object" || lease === null)
111
- throw new TypeError("invalid dispatch lease");
112
- const state = liveRunnerDispatchLeases.get(lease);
113
- if (!state) {
114
- if (issuedRunnerDispatchLeases.has(lease) && disposedRunnerDispatchLeases.has(lease)) {
115
- throw new TypeError("dispatch lease is disposed");
35
+ else {
36
+ if (runner.kind === "sdk") {
37
+ values.push(runner.fallbackConnection?.apiKey, lookupCredentialFromEnv(runner.fallbackCredential, envSource), runner.fallbackApiKeyFile ? lookupApiKeyFileValue(runner.fallbackApiKeyFile) : undefined, runner.fallbackApiKeySecretRef ? lookupApiKeySecretRefValue(runner.fallbackApiKeySecretRef) : undefined);
116
38
  }
117
- throw new TypeError("invalid dispatch lease");
118
- }
119
- if (spec && state.binding !== runnerLeaseBinding(spec)) {
120
- throw new TypeError("dispatch lease does not match the lowered runner transport");
121
- }
122
- return state;
123
- }
124
- /** @internal Verify provenance, liveness, and transport binding without exposing state. */
125
- export function assertRunnerDispatchLease(lease, spec) {
126
- requireRunnerDispatchLease(lease, spec);
127
- }
128
- /** @internal Acquire only through execution-lowering after authorization/provenance validation. */
129
- export function acquireRunnerDispatchLease(spec, envSource = process.env) {
130
- const environment = snapshotDispatchEnvironment(spec, envSource);
131
- const credentialSource = new Proxy(Object.create(null), {
132
- get(_target, property) {
133
- return typeof property === "string" ? environment.read(property) : undefined;
134
- },
135
- });
136
- const primaryCredential = spec.kind === "llm"
137
- ? resolveLlmCredentialValue(spec.engine, spec.credential, spec.apiKeyFile, spec.apiKeySecretRef, credentialSource)
138
- : undefined;
139
- const fallbackCredential = spec.kind === "sdk"
140
- ? resolveLlmCredentialValue(spec.engine, spec.fallbackCredential, spec.fallbackApiKeyFile, spec.fallbackApiKeySecretRef, credentialSource)
141
- : undefined;
142
- const handle = Object.create(null);
143
- Object.defineProperty(handle, "toJSON", {
144
- configurable: false,
145
- enumerable: false,
146
- writable: false,
147
- value: () => {
148
- throw new TypeError("dispatch lease is not serializable");
149
- },
150
- });
151
- Object.freeze(handle);
152
- const sensitiveValues = collectSensitiveValues([
153
- primaryCredential,
154
- fallbackCredential,
155
- ...environment.passthroughSensitiveValues,
156
- ]);
157
- liveRunnerDispatchLeases.set(handle, {
158
- binding: runnerLeaseBinding(spec),
159
- primaryCredential,
160
- fallbackCredential,
161
- environmentSnapshot: environment.environmentSnapshot,
162
- environmentSource: environment.environmentSource,
163
- sensitiveValues,
164
- });
165
- issuedRunnerDispatchLeases.add(handle);
166
- return handle;
167
- }
168
- /** @internal Scrub private material and invalidate one issued handle. */
169
- export function disposeRunnerDispatchLease(lease) {
170
- if (typeof lease !== "object" || lease === null || !issuedRunnerDispatchLeases.has(lease)) {
171
- throw new TypeError("invalid dispatch lease");
172
- }
173
- const state = liveRunnerDispatchLeases.get(lease);
174
- if (!state)
175
- return;
176
- state.primaryCredential = undefined;
177
- state.fallbackCredential = undefined;
178
- state.environmentSnapshot.clear();
179
- state.sensitiveValues.fill("");
180
- state.sensitiveValues = [];
181
- liveRunnerDispatchLeases.delete(lease);
182
- disposedRunnerDispatchLeases.add(lease);
183
- }
184
- /** @internal Redact with private credential material without exposing its values. */
185
- export function redactWithRunnerDispatchLease(lease, value) {
186
- return redactSensitiveText(value, requireRunnerDispatchLease(lease).sensitiveValues);
187
- }
188
- /**
189
- * Release every long-lived resource the dispatch runners CACHE for reuse, so a
190
- * one-shot process (the CLI) can exit cleanly once dispatching is done.
191
- *
192
- * The `sdk` runner keeps a per-material registry of `opencode serve` CHILD
193
- * PROCESSES (see `opencode-sdk/sdk-runner.ts`), started lazily and reused
194
- * across units within a process. Each live child is an OS handle that keeps
195
- * Bun's event loop open — and the registry's own teardown is wired ONLY to
196
- * `process.once('exit')`, which never fires while such a child holds the loop
197
- * open. That is a deadlock: a successful `akm workflow run` that dispatched via
198
- * the SDK path would hang the CLI (owner finding 4) because the process is
199
- * never idle enough for the exit hook to run and close the children it is
200
- * waiting on.
201
- *
202
- * The CLI composition root and workflow engine call this in `finally` blocks to
203
- * drain the registry deterministically before relying on the event loop. Started
204
- * servers close synchronously; in-flight starts are awaited and closed on
205
- * arrival. When no SDK server was started this is an idempotent no-op.
206
- */
207
- export async function disposeDispatchResources() {
208
- await disposeOpencodeSdkServers();
209
- }
210
- /** Collect every materialized value that can reach one runner dispatch. */
211
- export function collectDispatchSensitiveValues(spec, opts, envSource = opts.envSource ?? process.env) {
212
- const values = new Set();
213
- const add = (value) => {
214
- if (value !== undefined && value.length > 0)
215
- values.add(value);
216
- };
217
- const addConnection = (connection) => add(connection?.apiKey);
218
- if (spec.kind === "llm")
219
- addConnection(spec.connection);
220
- if (spec.kind === "sdk")
221
- addConnection(spec.fallbackConnection);
222
- if (spec.kind === "llm")
223
- add(lookupCredentialFromEnv(spec.credential, envSource));
224
- if (spec.kind === "sdk")
225
- add(lookupCredentialFromEnv(spec.fallbackCredential, envSource));
226
- // #905: a file-backed credential is read at dispatch, so it must be in the
227
- // scrub set for the same reason the env-backed one is.
228
- if (spec.kind === "llm" && spec.apiKeyFile)
229
- add(lookupApiKeyFileValue(spec.apiKeyFile));
230
- if (spec.kind === "sdk" && spec.fallbackApiKeyFile)
231
- add(lookupApiKeyFileValue(spec.fallbackApiKeyFile));
232
- // #953: a secret-store-backed credential is read at dispatch too — same
233
- // best-effort scrub-set inclusion rationale.
234
- if (spec.kind === "llm" && spec.apiKeySecretRef)
235
- add(lookupApiKeySecretRefValue(spec.apiKeySecretRef));
236
- if (spec.kind === "sdk" && spec.fallbackApiKeySecretRef)
237
- add(lookupApiKeySecretRefValue(spec.fallbackApiKeySecretRef));
238
- if (spec.kind !== "llm") {
239
- for (const value of Object.values(spec.profile.env ?? {}))
240
- add(value);
241
- for (const name of spec.profile.envPassthrough) {
242
- const value = envSource[name];
243
- if (!isEnvPassthroughValueSafeToExpose(name, value))
244
- add(value);
39
+ values.push(...Object.values(runner.profile.env ?? {}));
40
+ for (const name of runner.profile.envPassthrough) {
41
+ if (!isEnvPassthroughValueSafeToExpose(name, envSource[name]))
42
+ values.push(envSource[name]);
245
43
  }
246
44
  }
247
45
  for (const [name, value] of Object.entries(opts.env ?? {})) {
248
46
  if (!isEnvPassthroughValueSafeToExpose(name, value))
249
- add(value);
250
- }
251
- return collectSensitiveValues(values);
252
- }
253
- function collectNonCredentialDispatchSensitiveValues(spec, opts, envSource = opts.envSource ?? process.env, includeProfilePassthrough = true) {
254
- const values = new Set();
255
- const add = (value) => {
256
- if (value !== undefined && value.length > 0)
257
- values.add(value);
258
- };
259
- if (spec.kind === "llm")
260
- add(spec.connection.apiKey);
261
- if (spec.kind === "sdk")
262
- add(spec.fallbackConnection?.apiKey);
263
- if (spec.kind !== "llm") {
264
- for (const value of Object.values(spec.profile.env ?? {}))
265
- add(value);
266
- if (includeProfilePassthrough) {
267
- for (const name of spec.profile.envPassthrough) {
268
- const value = envSource[name];
269
- if (!isEnvPassthroughValueSafeToExpose(name, value))
270
- add(value);
271
- }
272
- }
273
- }
274
- for (const [name, value] of Object.entries(opts.env ?? {})) {
275
- if (!isEnvPassthroughValueSafeToExpose(name, value))
276
- add(value);
47
+ values.push(value);
277
48
  }
278
49
  return collectSensitiveValues(values);
279
50
  }
@@ -286,75 +57,126 @@ function redactResult(result, sensitiveValues) {
286
57
  ...(result.parsed !== undefined ? { parsed: redactSensitiveValue(result.parsed, sensitiveValues) } : {}),
287
58
  };
288
59
  }
289
- /**
290
- * Dispatch a {@link RunnerSpec} to its runner and return the raw
291
- * {@link AgentRunResult}. `opts` is the {@link RunAgentOptions} for the profile
292
- * (`agent` / `sdk`) arms. Callers keep their per-call options; a bound lease
293
- * replaces only `envSource` with its private immutable environment snapshot.
294
- */
295
- export async function executeRunner(spec, prompt, opts, seams = {}, lease) {
296
- const withSpecOptions = (timeoutMs, workspace, environmentSource) => ({
297
- ...opts,
298
- ...(Object.hasOwn(opts, "timeoutMs") ? {} : timeoutMs !== undefined ? { timeoutMs } : {}),
299
- ...(opts.cwd === undefined && workspace ? { cwd: workspace } : {}),
300
- ...(environmentSource ? { envSource: environmentSource } : {}),
301
- });
60
+ function llmFailureReason(error) {
61
+ if (!(error instanceof LlmCallError))
62
+ return "spawn_failed";
63
+ switch (error.code) {
64
+ case "aborted":
65
+ return "aborted";
66
+ case "timeout":
67
+ return "timeout";
68
+ case "rate_limited":
69
+ return "llm_rate_limit";
70
+ case "parse_error":
71
+ case "provider_html_error":
72
+ return "parse_error";
73
+ default:
74
+ return "spawn_failed";
75
+ }
76
+ }
77
+ async function dispatchRunner(runner, prompt, opts, seams, llm) {
78
+ const envSource = opts.envSource ?? process.env;
79
+ const secrets = collectDispatchSensitiveValues(runner, opts, envSource);
302
80
  let result;
303
- const dispatchSensitiveValues = [];
304
- const leaseState = lease ? requireRunnerDispatchLease(lease, spec) : undefined;
305
- const preDispatchSensitiveValues = leaseState
306
- ? collectSensitiveValues([
307
- ...leaseState.sensitiveValues,
308
- ...collectNonCredentialDispatchSensitiveValues(spec, opts, undefined, false),
309
- ])
310
- : collectDispatchSensitiveValues(spec, opts);
311
- switch (spec.kind) {
81
+ switch (runner.kind) {
312
82
  case "llm": {
313
- if (!seams.llm) {
314
- throw new Error("executeRunner: an `llm` runner requires a `seams.llm` handler (no default LLM dispatch).");
315
- }
316
- const connection = leaseState
317
- ? materializeLlmRunnerConnectionWithCredential(spec, leaseState.primaryCredential)
318
- : materializeLlmRunnerConnection(spec);
83
+ if (!llm)
84
+ throw new Error("an llm runner dispatches only a built execution's chat messages");
85
+ const connection = materializeLlmRunnerConnection(runner, envSource);
319
86
  if (connection.apiKey)
320
- dispatchSensitiveValues.push(connection.apiKey);
321
- result = await seams.llm({ ...spec, connection }, prompt, withSpecOptions(spec.timeoutMs));
87
+ secrets.push(connection.apiKey);
88
+ result = await llm(connection, opts);
322
89
  break;
323
90
  }
324
- case "agent": {
325
- const run = seams.runAgent ?? runAgent;
326
- result = await run(spec.profile, prompt, withSpecOptions(spec.timeoutMs, spec.profile.workspace, leaseState?.environmentSource));
91
+ case "agent":
92
+ result = await (seams.runAgent ?? runAgent)(runner.profile, prompt, opts);
327
93
  break;
328
- }
329
94
  case "sdk": {
330
- const run = seams.runSdk ?? runOpencodeSdk;
331
- const fallbackMaterial = spec.fallbackConnection
332
- ? {
333
- engine: spec.engine ?? "unnamed-sdk-fallback",
334
- connection: spec.fallbackConnection,
335
- ...(spec.fallbackCredential ? { credential: spec.fallbackCredential } : {}),
336
- ...(spec.fallbackApiKeyFile ? { apiKeyFile: spec.fallbackApiKeyFile } : {}),
337
- timeoutMs: spec.fallbackTimeoutMs !== undefined
338
- ? spec.fallbackTimeoutMs
339
- : Object.hasOwn(spec.fallbackConnection, "timeoutMs")
340
- ? (spec.fallbackConnection.timeoutMs ?? null)
341
- : null,
342
- }
343
- : undefined;
344
- const fallbackConnection = fallbackMaterial
345
- ? leaseState
346
- ? materializeLlmConnectionWithCredential(fallbackMaterial, leaseState.fallbackCredential)
347
- : materializeLlmConnection(fallbackMaterial)
348
- : undefined;
95
+ const fallbackConnection = materializeSdkFallbackConnection(runner, envSource);
349
96
  if (fallbackConnection?.apiKey)
350
- dispatchSensitiveValues.push(fallbackConnection.apiKey);
351
- result = await run(spec.profile, prompt, withSpecOptions(spec.timeoutMs, spec.profile.workspace, leaseState?.environmentSource), fallbackConnection);
97
+ secrets.push(fallbackConnection.apiKey);
98
+ result = await (seams.runSdk ?? runOpencodeSdk)(runner.profile, prompt, opts, fallbackConnection);
352
99
  break;
353
100
  }
354
101
  default:
355
- // Exhaustiveness arm: a 4th RunnerSpec kind becomes a compile error here.
356
- return assertNever(spec);
102
+ return assertNever(runner);
103
+ }
104
+ return redactResult(result, collectSensitiveValues(secrets));
105
+ }
106
+ /** Run a built execution. Credentials are read here, once per call, and never returned. */
107
+ export async function runExecution(execution, options = {}) {
108
+ const opts = { ...execution.options };
109
+ const operational = options.runOptions ?? {};
110
+ for (const key of OPERATIONAL_OPTIONS) {
111
+ if (operational[key] !== undefined)
112
+ opts[key] = operational[key];
113
+ }
114
+ if (options.eventSource !== undefined) {
115
+ opts.env = { ...execution.options.env, AKM_EVENT_SOURCE: options.eventSource };
357
116
  }
358
- const sensitiveValues = collectSensitiveValues([...preDispatchSensitiveValues, ...dispatchSensitiveValues]);
359
- return redactResult(result, sensitiveValues);
117
+ const chat = options.chat ?? chatCompletion;
118
+ const llm = async (connection, callOpts) => {
119
+ const started = Date.now();
120
+ try {
121
+ const stdout = await chat(connection, [...(execution.messages ?? [])], {
122
+ ...execution.chatOptions,
123
+ ...(Object.hasOwn(callOpts, "timeoutMs") ? { timeoutMs: callOpts.timeoutMs } : {}),
124
+ ...(callOpts.signal ? { signal: callOpts.signal } : {}),
125
+ ...(options.onRetryAttempt ? { onRetryAttempt: options.onRetryAttempt } : {}),
126
+ });
127
+ return { ok: true, exitCode: 0, stdout, stderr: "", durationMs: Date.now() - started };
128
+ }
129
+ catch (error) {
130
+ // Returned rather than thrown so the redaction below covers provider bodies.
131
+ return {
132
+ ok: false,
133
+ exitCode: null,
134
+ stdout: "",
135
+ stderr: "",
136
+ durationMs: Date.now() - started,
137
+ error: error instanceof Error ? error.message : String(error),
138
+ reason: llmFailureReason(error),
139
+ ...(error instanceof LlmCallError ? { llmErrorCode: error.code } : {}),
140
+ };
141
+ }
142
+ };
143
+ return dispatchRunner(execution.runner, execution.prompt, opts, options, llm);
144
+ }
145
+ /** `akm agent`: launch an agent engine interactively, with no prompt of its own. */
146
+ export async function executeInteractiveAgentInvocation(input, seams = {}) {
147
+ const runner = resolveEngine(input.engine, input.config);
148
+ if (runner.kind === "llm") {
149
+ throw new UsageError(`Engine "${input.engine}" is an LLM engine; akm agent requires an agent engine.`, "INVALID_FLAG_VALUE");
150
+ }
151
+ const opts = {
152
+ stdio: runner.kind === "sdk" ? runner.profile.stdio : "interactive",
153
+ parseOutput: "text",
154
+ ...(input.timeoutMs !== undefined
155
+ ? { timeoutMs: input.timeoutMs }
156
+ : runner.timeoutMs !== undefined
157
+ ? { timeoutMs: runner.timeoutMs }
158
+ : {}),
159
+ ...(input.cwd ? { cwd: input.cwd } : runner.profile.workspace ? { cwd: runner.profile.workspace } : {}),
160
+ };
161
+ return { engine: input.engine, result: await dispatchRunner(runner, "", opts, seams) };
162
+ }
163
+ /**
164
+ * Close the `opencode serve` children the SDK runner keeps for reuse. Its own
165
+ * teardown hangs off `process.once("exit")`, which never fires while a child
166
+ * holds the event loop open, so the CLI and workflow engine call this in
167
+ * `finally` blocks. A no-op when no SDK server was started.
168
+ */
169
+ export async function disposeDispatchResources() {
170
+ await disposeOpencodeSdkServers();
171
+ }
172
+ /**
173
+ * Fail fast, before an operation makes any durable change, when `runner`'s
174
+ * required credential is missing. Reads it and keeps nothing: every dispatch
175
+ * reads it again.
176
+ */
177
+ export function assertRunnerCredentials(runner, envSource) {
178
+ if (runner.kind === "llm")
179
+ materializeLlmRunnerConnection(runner, envSource);
180
+ if (runner.kind === "sdk")
181
+ materializeSdkFallbackConnection(runner, envSource);
360
182
  }
@@ -1,28 +1,68 @@
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 { materializeLlmConnection, materializeLlmConnectionWithCredential, } from "./engine-resolution.js";
5
- /** Resolve the current credential value for one frozen LLM runner at dispatch. */
6
- export function materializeLlmRunnerConnection(runner) {
7
- return materializeLlmConnection({
4
+ import { materializeLlmConnection } from "./engine-resolution.js";
5
+ /** The symbolic LLM use behind one llm runner; nothing is read here. */
6
+ export function llmUseFromRunner(runner) {
7
+ return {
8
8
  engine: runner.engine,
9
9
  connection: runner.connection,
10
10
  ...(runner.credential ? { credential: runner.credential } : {}),
11
11
  ...(runner.apiKeyFile ? { apiKeyFile: runner.apiKeyFile } : {}),
12
12
  ...(runner.apiKeySecretRef ? { apiKeySecretRef: runner.apiKeySecretRef } : {}),
13
13
  timeoutMs: runner.timeoutMs ?? null,
14
- });
14
+ };
15
15
  }
16
- /** Inject an already-snapshotted credential without consulting live process state. */
17
- export function materializeLlmRunnerConnectionWithCredential(runner, credentialValue) {
18
- return materializeLlmConnectionWithCredential({
16
+ /** The symbolic LLM use behind an sdk runner's provider fallback, when it has one. */
17
+ export function sdkFallbackUseFromRunner(runner) {
18
+ if (!runner.fallbackConnection)
19
+ return undefined;
20
+ return {
19
21
  engine: runner.engine,
20
- connection: runner.connection,
21
- ...(runner.credential ? { credential: runner.credential } : {}),
22
- ...(runner.apiKeyFile ? { apiKeyFile: runner.apiKeyFile } : {}),
23
- ...(runner.apiKeySecretRef ? { apiKeySecretRef: runner.apiKeySecretRef } : {}),
24
- timeoutMs: runner.timeoutMs ?? null,
25
- }, credentialValue);
22
+ connection: runner.fallbackConnection,
23
+ ...(runner.fallbackCredential ? { credential: runner.fallbackCredential } : {}),
24
+ ...(runner.fallbackApiKeyFile ? { apiKeyFile: runner.fallbackApiKeyFile } : {}),
25
+ ...(runner.fallbackApiKeySecretRef ? { apiKeySecretRef: runner.fallbackApiKeySecretRef } : {}),
26
+ timeoutMs: runner.fallbackTimeoutMs !== undefined ? runner.fallbackTimeoutMs : (runner.fallbackConnection.timeoutMs ?? null),
27
+ };
28
+ }
29
+ /** Read the current credential and inject it: the dispatch boundary for one llm runner. */
30
+ export function materializeLlmRunnerConnection(runner, envSource = process.env) {
31
+ return materializeLlmConnection(llmUseFromRunner(runner), envSource);
32
+ }
33
+ /** The same for an sdk runner's provider fallback, when it has one. */
34
+ export function materializeSdkFallbackConnection(runner, envSource = process.env) {
35
+ const use = sdkFallbackUseFromRunner(runner);
36
+ return use ? materializeLlmConnection(use, envSource) : undefined;
37
+ }
38
+ function isRecord(value) {
39
+ return value !== null && typeof value === "object" && !Array.isArray(value);
40
+ }
41
+ /**
42
+ * Read a journaled runner back. Tolerant: unknown keys are kept and missing
43
+ * profile defaults are filled in; only a shape nothing could dispatch is refused.
44
+ */
45
+ export function decodeFrozenRunnerSpec(value) {
46
+ if (!isRecord(value) || typeof value.engine !== "string") {
47
+ throw new TypeError("a frozen runner must be an object with an engine name");
48
+ }
49
+ if (value.kind === "llm") {
50
+ if (!isRecord(value.connection) || typeof value.connection.endpoint !== "string") {
51
+ throw new TypeError("a frozen llm runner must carry connection.endpoint");
52
+ }
53
+ return value;
54
+ }
55
+ if (value.kind !== "agent" && value.kind !== "sdk") {
56
+ throw new TypeError("a frozen runner's kind must be llm, agent, or sdk");
57
+ }
58
+ const profile = value.profile;
59
+ if (!isRecord(profile) || typeof profile.name !== "string" || typeof profile.bin !== "string") {
60
+ throw new TypeError("a frozen agent runner must carry profile.name and profile.bin");
61
+ }
62
+ return {
63
+ ...value,
64
+ profile: { args: [], envPassthrough: [], stdio: "captured", parseOutput: "text", ...profile },
65
+ };
26
66
  }
27
67
  export function runnerIsLlm(runner) {
28
68
  return runner.kind === "llm";