akm-cli 0.10.261012-beta → 0.10.261014-beta

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 (518) hide show
  1. package/CHANGELOG.md +710 -3
  2. package/README.md +15 -0
  3. package/SECURITY.md +11 -10
  4. package/STABILITY.md +47 -51
  5. package/dist/assets/hints/cli-hints-full.md +21 -19
  6. package/dist/assets/hints/cli-hints-short.md +3 -4
  7. package/dist/assets/opencode/akm-plugin.js +177 -0
  8. package/dist/assets/stash-skeleton/README.md +1 -1
  9. package/dist/assets/tasks/core/improve.yml +1 -1
  10. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +1 -1
  11. package/dist/assets/ui/config.js +27 -10
  12. package/dist/assets/workflows/workflow-template.md +2 -2
  13. package/dist/cli/clack.js +3 -1
  14. package/dist/cli/selection.js +33 -0
  15. package/dist/cli/shared.js +14 -1
  16. package/dist/cli/unknown-flags.js +6 -37
  17. package/dist/cli.js +69 -48
  18. package/dist/commands/agent/agent-dispatch.js +17 -25
  19. package/dist/commands/agent/contribute-cli.js +9 -8
  20. package/dist/commands/command/command-cli.js +4 -8
  21. package/dist/commands/command/command-execution.js +25 -39
  22. package/dist/commands/command/execution-source-loader.js +10 -14
  23. package/dist/commands/completions.js +1 -1
  24. package/dist/commands/config-cli.js +14 -5
  25. package/dist/commands/env/child-env.js +12 -14
  26. package/dist/commands/env/env-binding.js +47 -58
  27. package/dist/commands/env/env-cli.js +22 -24
  28. package/dist/commands/env/env.js +10 -58
  29. package/dist/commands/env/secret-cli.js +11 -29
  30. package/dist/commands/env/secret.js +3 -10
  31. package/dist/commands/health/checks.js +10 -849
  32. package/dist/commands/health/connections-and-harnesses-check.js +109 -0
  33. package/dist/commands/health/egress.js +3 -3
  34. package/dist/commands/health/html-report.js +9 -10
  35. package/dist/commands/health/plugin-staleness.js +18 -11
  36. package/dist/commands/health/report-view-model.js +16 -12
  37. package/dist/commands/health/scheduler-binary.js +1 -1
  38. package/dist/commands/health/semantic-search-check.js +85 -0
  39. package/dist/commands/health/type-directory-check.js +0 -3
  40. package/dist/commands/health.js +30 -42
  41. package/dist/commands/improve/consolidate/pair-pass.js +10 -25
  42. package/dist/commands/improve/consolidate.js +10 -15
  43. package/dist/commands/improve/distill.js +5 -17
  44. package/dist/commands/improve/eligibility.js +1 -3
  45. package/dist/commands/improve/execution.js +37 -40
  46. package/dist/commands/improve/extract-cli.js +6 -11
  47. package/dist/commands/improve/extract-prompt.js +2 -2
  48. package/dist/commands/improve/extract.js +67 -97
  49. package/dist/commands/improve/improve-cli.js +22 -128
  50. package/dist/commands/improve/improve-report.js +1 -1
  51. package/dist/commands/improve/improve-strategies.js +25 -108
  52. package/dist/commands/improve/improve.js +10 -14
  53. package/dist/commands/improve/loop-stages.js +3 -0
  54. package/dist/commands/improve/memory/memory-improve.js +1 -1
  55. package/dist/commands/improve/preparation.js +8 -13
  56. package/dist/commands/improve/reflect.js +203 -332
  57. package/dist/commands/improve/retrieval-scope.js +1 -1
  58. package/dist/commands/improve/session-asset.js +3 -2
  59. package/dist/commands/improve/stage.js +87 -115
  60. package/dist/commands/lint/base-linter.js +3 -3
  61. package/dist/commands/lint/env-key-rules.js +7 -6
  62. package/dist/commands/lint/index.js +3 -3
  63. package/dist/commands/models-cli.js +103 -63
  64. package/dist/commands/proposal/drain.js +26 -39
  65. package/dist/commands/proposal/proposal-cli.js +14 -18
  66. package/dist/commands/proposal/propose-cli.js +4 -2
  67. package/dist/commands/proposal/propose.js +37 -64
  68. package/dist/commands/proposal/repository.js +17 -17
  69. package/dist/commands/proposal/validators/proposal-validators.js +3 -6
  70. package/dist/commands/read/curate.js +10 -6
  71. package/dist/commands/read/registry-search.js +2 -48
  72. package/dist/commands/read/search-cli.js +1 -8
  73. package/dist/commands/read/search.js +5 -6
  74. package/dist/commands/read/show.js +6 -29
  75. package/dist/commands/remember.js +25 -40
  76. package/dist/commands/sources/add-cli.js +8 -63
  77. package/dist/commands/sources/bundle-cli.js +8 -9
  78. package/dist/commands/sources/bundle-config-ops.js +2 -2
  79. package/dist/commands/sources/info.js +12 -1
  80. package/dist/commands/sources/init.js +1 -1
  81. package/dist/commands/sources/installed-stashes.js +119 -63
  82. package/dist/commands/sources/migration-help.js +1 -1
  83. package/dist/commands/sources/plugin-upgrade.js +8 -15
  84. package/dist/commands/sources/schema-repair.js +4 -10
  85. package/dist/commands/sources/source-add.js +2 -2
  86. package/dist/commands/sources/source-clone.js +31 -7
  87. package/dist/commands/sources/source-manage.js +4 -4
  88. package/dist/commands/targets.js +175 -0
  89. package/dist/commands/tasks/explain.js +104 -69
  90. package/dist/commands/tasks/tasks-cli.js +9 -11
  91. package/dist/commands/tasks/tasks.js +75 -97
  92. package/dist/commands/ui/api.js +11 -3
  93. package/dist/commands/ui/redact.js +5 -31
  94. package/dist/commands/ui/workspace-actions.js +1 -1
  95. package/dist/commands/workflow/plan.js +48 -9
  96. package/dist/commands/workflow-cli.js +40 -45
  97. package/dist/core/adapter/adapters/agent-skills-adapter.js +10 -11
  98. package/dist/core/adapter/adapters/akm-adapter.js +36 -41
  99. package/dist/core/adapter/adapters/akm-lint.js +10 -12
  100. package/dist/core/adapter/adapters/akm-metadata.js +2 -2
  101. package/dist/core/adapter/adapters/akm-task-adapter.js +8 -9
  102. package/dist/core/adapter/adapters/akm-workflow-adapter.js +5 -5
  103. package/dist/core/adapter/adapters/claude-adapter.js +1 -1
  104. package/dist/core/adapter/adapters/dotenv-adapter.js +7 -6
  105. package/dist/core/adapter/adapters/generic-files-adapter.js +5 -5
  106. package/dist/core/adapter/adapters/index.js +3 -4
  107. package/dist/core/adapter/adapters/llm-wiki-adapter.js +12 -14
  108. package/dist/core/adapter/adapters/okf-adapter.js +8 -10
  109. package/dist/core/adapter/adapters/opencode-adapter.js +1 -1
  110. package/dist/core/adapter/adapters/shared.js +3 -3
  111. package/dist/core/adapter/adapters/tool-dir-shared.js +7 -25
  112. package/dist/core/adapter/adapters/website-snapshot-adapter.js +3 -3
  113. package/dist/core/adapter/detect-adapter.js +1 -1
  114. package/dist/core/adapter/execution-source.js +13 -35
  115. package/dist/core/adapter/registry.js +1 -1
  116. package/dist/core/adapter/validate-context.js +2 -4
  117. package/dist/core/asset/asset-placement.js +1 -1
  118. package/dist/core/asset/asset-ref.js +3 -3
  119. package/dist/core/asset/resolve-ref.js +5 -0
  120. package/dist/core/bundle-rename.js +12 -4
  121. package/dist/core/closest-match.js +37 -0
  122. package/dist/core/common.js +37 -57
  123. package/dist/core/config/config-io.js +26 -18
  124. package/dist/core/config/config-schema.js +19 -106
  125. package/dist/core/config/config-sources.js +3 -3
  126. package/dist/core/config/config-types.js +4 -4
  127. package/dist/core/config/config-walker.js +50 -77
  128. package/dist/core/config/config.js +438 -109
  129. package/dist/core/config/convert-engine-references.js +114 -0
  130. package/dist/core/config/convert-engines-shared.js +40 -0
  131. package/dist/core/config/convert-engines.js +300 -0
  132. package/dist/core/config/convert-models-overlay.js +137 -0
  133. package/dist/core/config/credentials.js +304 -0
  134. package/dist/core/config/engine-semantics.js +3 -3
  135. package/dist/core/config/migrate-keys.js +222 -0
  136. package/dist/core/config/model-aliases.js +37 -0
  137. package/dist/core/config/models-file.js +34 -0
  138. package/dist/core/config/retired-engines.js +53 -0
  139. package/dist/core/config/schema/connections.js +41 -0
  140. package/dist/core/config/schema/embedding.js +2 -2
  141. package/dist/core/config/schema/harnesses.js +41 -0
  142. package/dist/core/config/schema/improve-processes.js +9 -28
  143. package/dist/core/config/schema/improve.js +36 -1
  144. package/dist/core/config/schema/index-config.js +18 -20
  145. package/dist/core/config/schema/primitives.js +42 -17
  146. package/dist/core/config/schema/search.js +6 -11
  147. package/dist/core/config/schema/sources-bundles.js +9 -9
  148. package/dist/core/config/schema/workflow.js +2 -6
  149. package/dist/core/config/unusable-entries.js +66 -0
  150. package/dist/core/content-safety.js +3 -6
  151. package/dist/core/endpoint-url.js +47 -0
  152. package/dist/core/env-secret-ref.js +84 -73
  153. package/dist/core/errors.js +10 -16
  154. package/dist/core/extra-params.js +3 -3
  155. package/dist/core/improve-result.js +8 -9
  156. package/dist/core/json-schema.js +1 -1
  157. package/dist/core/lesson-lint.js +1 -1
  158. package/dist/core/llm-inference.js +80 -0
  159. package/dist/core/parse.js +2 -2
  160. package/dist/core/paths.js +33 -10
  161. package/dist/core/recognition-util.js +8 -10
  162. package/dist/core/redaction.js +94 -55
  163. package/dist/core/ref-token.js +82 -0
  164. package/dist/core/secret-value.js +14 -0
  165. package/dist/core/state/migrations.js +17 -22
  166. package/dist/core/structured.js +2 -68
  167. package/dist/core/subprocess.js +63 -22
  168. package/dist/core/type-presentation.js +10 -14
  169. package/dist/core/write-source.js +20 -4
  170. package/dist/execution/input-contract.js +3 -6
  171. package/dist/execution/notices.js +49 -0
  172. package/dist/execution/resolved-request.js +24 -40
  173. package/dist/execution/source.js +21 -45
  174. package/dist/execution/target-ref.js +2 -2
  175. package/dist/indexer/bundle-identity-guard.js +6 -5
  176. package/dist/indexer/ensure-index.js +1 -1
  177. package/dist/indexer/index-writer-lock.js +1 -1
  178. package/dist/indexer/indexer.js +46 -42
  179. package/dist/indexer/installations.js +12 -22
  180. package/dist/indexer/lookup/adapter-concept-owner.js +40 -14
  181. package/dist/indexer/materialize-embeddings.js +20 -16
  182. package/dist/indexer/passes/memory-inference.js +21 -50
  183. package/dist/indexer/passes/metadata.js +5 -5
  184. package/dist/indexer/scan/doc-to-entry.js +1 -1
  185. package/dist/indexer/scan/drain-dir.js +9 -11
  186. package/dist/indexer/search/db-search.js +9 -7
  187. package/dist/indexer/search/search-source.js +42 -22
  188. package/dist/indexer/usage/usage-events.js +2 -3
  189. package/dist/indexer/walk/index-context.js +2 -1
  190. package/dist/indexer/walk/matchers.js +3 -11
  191. package/dist/indexer/walk/path-resolver.js +13 -68
  192. package/dist/indexer/walk/walker.js +32 -26
  193. package/dist/integrations/agent/akm-read-tools.js +81 -0
  194. package/dist/integrations/agent/config.js +1 -1
  195. package/dist/integrations/agent/detect.js +32 -36
  196. package/dist/integrations/agent/execution.js +130 -428
  197. package/dist/integrations/agent/index.js +1 -1
  198. package/dist/integrations/agent/profiles.js +24 -28
  199. package/dist/integrations/agent/prompts.js +17 -30
  200. package/dist/integrations/agent/request-lowering.js +67 -101
  201. package/dist/integrations/agent/resolution.js +332 -0
  202. package/dist/integrations/agent/runner-dispatch.js +156 -181
  203. package/dist/integrations/agent/runner.js +20 -70
  204. package/dist/integrations/agent/runtime-port.js +4 -0
  205. package/dist/integrations/agent/runtime.js +10 -0
  206. package/dist/integrations/agent/spawn.js +65 -190
  207. package/dist/integrations/harnesses/aider/index.js +19 -39
  208. package/dist/integrations/harnesses/amazonq/index.js +17 -32
  209. package/dist/integrations/harnesses/claude/index.js +59 -28
  210. package/dist/integrations/harnesses/claude/session-log.js +7 -5
  211. package/dist/integrations/harnesses/codex/index.js +19 -31
  212. package/dist/integrations/harnesses/copilot/index.js +17 -35
  213. package/dist/integrations/harnesses/gemini/index.js +18 -34
  214. package/dist/integrations/harnesses/harness.js +36 -0
  215. package/dist/integrations/harnesses/index.js +20 -51
  216. package/dist/integrations/harnesses/opencode/agent-builder.js +132 -100
  217. package/dist/integrations/harnesses/opencode/index.js +11 -33
  218. package/dist/integrations/harnesses/opencode/roots.js +50 -0
  219. package/dist/integrations/harnesses/opencode/runtime/binary.js +206 -0
  220. package/dist/integrations/harnesses/opencode/runtime/command.js +152 -0
  221. package/dist/integrations/harnesses/opencode/runtime/index.js +177 -0
  222. package/dist/integrations/harnesses/opencode/runtime/tar.js +107 -0
  223. package/dist/integrations/harnesses/opencode/session-log-v1.js +15 -7
  224. package/dist/integrations/harnesses/opencode/session-log-v2.js +72 -48
  225. package/dist/integrations/harnesses/opencode/session-log.js +58 -137
  226. package/dist/integrations/harnesses/opencode/version.js +34 -53
  227. package/dist/integrations/harnesses/openhands/index.js +13 -37
  228. package/dist/integrations/harnesses/pi/index.js +21 -32
  229. package/dist/integrations/lockfile.js +1 -1
  230. package/dist/integrations/session-logs/index.js +11 -31
  231. package/dist/llm/client.js +93 -111
  232. package/dist/llm/embedder.js +19 -34
  233. package/dist/llm/embedders/connection.js +31 -0
  234. package/dist/llm/embedders/local-model-files.js +56 -0
  235. package/dist/llm/embedders/local.js +146 -115
  236. package/dist/llm/embedders/remote.js +26 -23
  237. package/dist/llm/embedders/types.js +1 -1
  238. package/dist/llm/index-passes.js +33 -39
  239. package/dist/llm/memory-infer.js +67 -77
  240. package/dist/llm/model-list.js +35 -0
  241. package/dist/llm/rerank-client.js +8 -14
  242. package/dist/llm/structured-call.js +45 -99
  243. package/dist/llm/structured.js +60 -0
  244. package/dist/output/renderers.js +2 -8
  245. package/dist/output/shapes/helpers.js +18 -44
  246. package/dist/output/shapes/passthrough.js +1 -0
  247. package/dist/output/shapes.js +1 -7
  248. package/dist/output/text/command-format.js +41 -39
  249. package/dist/output/text/health-format.js +8 -2
  250. package/dist/output/text/helpers.js +1 -1
  251. package/dist/output/text/migrate.js +12 -0
  252. package/dist/output/text/models.js +3 -2
  253. package/dist/output/text/registry-commands.js +1 -2
  254. package/dist/output/text/show-directives.js +16 -28
  255. package/dist/output/text/show-format.js +0 -2
  256. package/dist/output/text/targets-table.js +70 -0
  257. package/dist/output/text/workflow-format.js +44 -15
  258. package/dist/output/text.js +1 -1
  259. package/dist/registry/providers/skills-sh.js +1 -28
  260. package/dist/registry/providers/static-index.js +1 -71
  261. package/dist/runtime.js +6 -2
  262. package/dist/scripts/akm-migrate-node.js +47095 -44905
  263. package/dist/scripts/akm-migrate.js +46946 -44755
  264. package/dist/setup/detect.js +28 -273
  265. package/dist/setup/endpoint.js +36 -0
  266. package/dist/setup/semantic-assets.js +13 -19
  267. package/dist/setup/setup-flags.js +77 -0
  268. package/dist/setup/setup-plan.js +144 -0
  269. package/dist/setup/setup.js +134 -358
  270. package/dist/setup/steps/connection-shared.js +49 -101
  271. package/dist/setup/steps/connection.js +121 -524
  272. package/dist/setup/steps/embeddings.js +151 -0
  273. package/dist/setup/steps/harnesses.js +118 -0
  274. package/dist/setup/steps/platforms.js +2 -81
  275. package/dist/setup/steps/tasks.js +1 -2
  276. package/dist/setup/steps.js +10 -0
  277. package/dist/setup/summary.js +67 -0
  278. package/dist/sources/freshness.js +16 -21
  279. package/dist/sources/providers/git-provider.js +2 -9
  280. package/dist/sources/providers/provider-utils.js +13 -5
  281. package/dist/sources/resolve.js +12 -16
  282. package/dist/sources/snapshot-fetchers/robots.js +11 -12
  283. package/dist/sources/snapshot-fetchers/secret-seam.js +2 -4
  284. package/dist/sources/snapshot-fetchers/website-ingest.js +8 -15
  285. package/dist/sources/snapshot-fetchers/x.js +1 -1
  286. package/dist/standalone/onnxruntime-library.js +52 -0
  287. package/dist/storage/repositories/improve-ledger-repository.js +1 -1
  288. package/dist/storage/repositories/improve-runs-repository.js +0 -13
  289. package/dist/storage/repositories/index-entries-repository.js +2 -2
  290. package/dist/storage/repositories/proposals-repository.js +2 -2
  291. package/dist/storage/repositories/task-history-repository.js +2 -2
  292. package/dist/storage/repositories/workflow-runs-repository.js +44 -129
  293. package/dist/storage/sqlite-read-snapshot.js +33 -29
  294. package/dist/tasks/embedded.js +1 -1
  295. package/dist/tasks/log-redaction.js +7 -119
  296. package/dist/tasks/prepare/prepare-support.js +8 -7
  297. package/dist/tasks/prepare/prepare.js +8 -9
  298. package/dist/tasks/prepare/script-capture.js +9 -25
  299. package/dist/tasks/run/attempt-lifecycle.js +17 -35
  300. package/dist/tasks/run/load-task.js +54 -27
  301. package/dist/tasks/run/run-command-task.js +15 -10
  302. package/dist/tasks/run/run-native-task.js +8 -21
  303. package/dist/tasks/run/run-task.js +5 -8
  304. package/dist/tasks/run/run-workflow-task.js +14 -21
  305. package/dist/tasks/run/task-history.js +9 -17
  306. package/dist/tasks/run/task-log.js +1 -5
  307. package/dist/tasks/scheduler-invocation.js +13 -18
  308. package/dist/tasks/scheduler-sync.js +0 -1
  309. package/dist/tasks/source/bounded-document.js +31 -117
  310. package/dist/tasks/source/parse-task-source.js +0 -1
  311. package/dist/tasks/source/project-v4.js +5 -3
  312. package/dist/tasks/source/task-input-diagnostics.js +3 -4
  313. package/dist/tasks/source/task-source-v4.js +48 -72
  314. package/dist/tasks/source-v3.js +2 -5
  315. package/dist/tasks/task-run-reserved-flags.js +1 -2
  316. package/dist/workflows/authoring/authoring.js +2 -7
  317. package/dist/workflows/compile.js +1 -15
  318. package/dist/workflows/concurrency-policy.js +4 -4
  319. package/dist/workflows/exec/child-workflow.js +1 -4
  320. package/dist/workflows/exec/dispatch-redaction.js +35 -86
  321. package/dist/workflows/exec/environment.js +13 -77
  322. package/dist/workflows/exec/exec-unit.js +14 -55
  323. package/dist/workflows/exec/frozen-judge.js +26 -28
  324. package/dist/workflows/exec/native-executor.js +126 -71
  325. package/dist/workflows/exec/run-workflow.js +38 -42
  326. package/dist/workflows/exec/step-work.js +13 -17
  327. package/dist/workflows/exec/unit-dispatch.js +7 -21
  328. package/dist/workflows/freeze/environment.js +14 -119
  329. package/dist/workflows/freeze/freeze.js +14 -33
  330. package/dist/workflows/freeze/step-values.js +6 -13
  331. package/dist/workflows/freeze/targets/child-workflow.js +3 -5
  332. package/dist/workflows/freeze/targets/command.js +9 -15
  333. package/dist/workflows/freeze/targets/shell.js +0 -3
  334. package/dist/workflows/freeze/task-bindings.js +0 -1
  335. package/dist/workflows/parser.js +50 -59
  336. package/dist/workflows/plan.js +8 -2
  337. package/dist/workflows/renderer.js +83 -9
  338. package/dist/workflows/resource-limits.js +2 -2
  339. package/dist/workflows/runtime/run-plan.js +38 -23
  340. package/dist/workflows/runtime/runs.js +55 -93
  341. package/dist/workflows/runtime/workflow-asset-loader.js +55 -86
  342. package/dist/workflows/source-files.js +7 -19
  343. package/docs/README.md +10 -16
  344. package/docs/architecture/README.md +2 -37
  345. package/docs/architecture/adapters.md +2 -2
  346. package/docs/architecture/akm-core-principles.md +256 -118
  347. package/docs/architecture/architecture.md +283 -99
  348. package/docs/architecture/improvement.md +8 -2
  349. package/docs/architecture/internals/classification.md +1 -3
  350. package/docs/architecture/internals/fresh-host-rebuild-runbook.md +7 -6
  351. package/docs/architecture/internals/health-advisories.md +35 -74
  352. package/docs/architecture/internals/improve-workflow.md +27 -26
  353. package/docs/architecture/internals/indexing.md +3 -3
  354. package/docs/architecture/internals/storage-locations.md +48 -15
  355. package/docs/architecture/persisted-data-compat.md +10 -6
  356. package/docs/architecture/specs/okf-support.md +9 -11
  357. package/docs/architecture/specs/ref.md +7 -9
  358. package/docs/architecture/testing/testing-workflow.md +172 -52
  359. package/docs/architecture/workflow-engine.md +51 -34
  360. package/docs/guides/README.md +0 -1
  361. package/docs/guides/author-bundles.md +15 -20
  362. package/docs/guides/author-workflows.md +93 -71
  363. package/docs/guides/bundles.md +20 -16
  364. package/docs/guides/discover-and-load.md +1 -1
  365. package/docs/guides/getting-started.md +49 -3
  366. package/docs/guides/improve-the-library.md +10 -4
  367. package/docs/guides/recipes/headless-install.md +11 -16
  368. package/docs/guides/run-workflows.md +29 -20
  369. package/docs/guides/scheduling.md +2 -2
  370. package/docs/guides/use-with-any-agent.md +20 -22
  371. package/docs/integration/bundling-akm.md +19 -9
  372. package/docs/maintainers/README.md +2 -6
  373. package/docs/maintainers/release-checklist.md +73 -126
  374. package/docs/migration/README.md +10 -3
  375. package/docs/migration/release-notes/0.10.0.md +81 -16
  376. package/docs/migration/release-notes/0.6.0.md +1 -1
  377. package/docs/migration/release-notes/0.7.0.md +1 -1
  378. package/docs/migration/release-notes/0.9.0.md +1 -1
  379. package/docs/migration/release-notes/README.md +1 -1
  380. package/docs/migration/v0.9-to-v0.10.md +300 -60
  381. package/docs/migration/v0.9.1-to-v0.9.2.md +6 -5
  382. package/docs/posts/config-v2-lessons-16.md +1 -1
  383. package/docs/posts/llm-mode-reflect-13.md +1 -1
  384. package/docs/posts/release-0.6.0.md +11 -11
  385. package/docs/posts/release-0.8.0.md +4 -4
  386. package/docs/posts/workflows-vaults-09.md +1 -1
  387. package/docs/reference/README.md +1 -1
  388. package/docs/reference/asset-types.md +2 -2
  389. package/docs/reference/cli.md +477 -388
  390. package/docs/reference/configuration.md +569 -550
  391. package/docs/reference/data-and-telemetry.md +9 -6
  392. package/docs/reference/env-and-secrets.md +72 -15
  393. package/docs/reference/refs.md +3 -20
  394. package/docs/reference/registry.md +12 -38
  395. package/docs/reference/supported-formats.md +1 -1
  396. package/docs/reference/tasks.md +22 -11
  397. package/docs/reference/ui-api.md +14 -9
  398. package/docs/reference/workflow-schema.md +199 -154
  399. package/docs/reference/workflows.md +4 -6
  400. package/package.json +9 -11
  401. package/schemas/akm-config.json +1696 -2922
  402. package/schemas/akm-task.json +4 -4
  403. package/schemas/akm-workflow.json +36 -9
  404. package/ROADMAP.md +0 -112
  405. package/dist/assets/models.json +0 -35
  406. package/dist/commands/health/engine-usage.js +0 -45
  407. package/dist/commands/sources/dangerous-env-audit.js +0 -134
  408. package/dist/core/activation-policy.js +0 -67
  409. package/dist/core/config/schema/engines.js +0 -160
  410. package/dist/core/config/schema/execution.js +0 -23
  411. package/dist/core/spawn-env.js +0 -268
  412. package/dist/execution/directory-identity.js +0 -78
  413. package/dist/integrations/agent/builder-shared.js +0 -33
  414. package/dist/integrations/agent/builders.js +0 -43
  415. package/dist/integrations/agent/engine-fallback.js +0 -59
  416. package/dist/integrations/agent/engine-resolution.js +0 -333
  417. package/dist/integrations/agent/model-map.js +0 -475
  418. package/dist/integrations/harnesses/aider/agent-builder.js +0 -107
  419. package/dist/integrations/harnesses/aider/result-extractor.js +0 -53
  420. package/dist/integrations/harnesses/amazonq/agent-builder.js +0 -138
  421. package/dist/integrations/harnesses/amazonq/result-extractor.js +0 -48
  422. package/dist/integrations/harnesses/claude/agent-builder.js +0 -106
  423. package/dist/integrations/harnesses/claude/config-import.js +0 -67
  424. package/dist/integrations/harnesses/claude/result-extractor.js +0 -67
  425. package/dist/integrations/harnesses/codex/agent-builder.js +0 -150
  426. package/dist/integrations/harnesses/codex/result-extractor.js +0 -110
  427. package/dist/integrations/harnesses/copilot/agent-builder.js +0 -117
  428. package/dist/integrations/harnesses/copilot/result-extractor.js +0 -178
  429. package/dist/integrations/harnesses/gemini/agent-builder.js +0 -114
  430. package/dist/integrations/harnesses/gemini/result-extractor.js +0 -149
  431. package/dist/integrations/harnesses/opencode/config-import.js +0 -149
  432. package/dist/integrations/harnesses/opencode/model-config.js +0 -33
  433. package/dist/integrations/harnesses/opencode/model-work-agent.js +0 -115
  434. package/dist/integrations/harnesses/opencode/session-log-types.js +0 -12
  435. package/dist/integrations/harnesses/opencode-sdk/index.js +0 -44
  436. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +0 -1052
  437. package/dist/integrations/harnesses/opencode-sdk/v1-adapter.js +0 -199
  438. package/dist/integrations/harnesses/opencode-sdk/v2-adapter.js +0 -254
  439. package/dist/integrations/harnesses/opencode-sdk/wire.js +0 -66
  440. package/dist/integrations/harnesses/openhands/agent-builder.js +0 -120
  441. package/dist/integrations/harnesses/openhands/result-extractor.js +0 -135
  442. package/dist/integrations/harnesses/pi/agent-builder.js +0 -89
  443. package/dist/integrations/harnesses/pi/result-extractor.js +0 -168
  444. package/dist/integrations/harnesses/shared.js +0 -18
  445. package/dist/integrations/harnesses/types.js +0 -55
  446. package/dist/llm/feature-gate.js +0 -115
  447. package/dist/output/shapes/models-list.js +0 -16
  448. package/dist/output/shapes/registry-search.js +0 -10
  449. package/dist/setup/detected-engines.js +0 -136
  450. package/dist/setup/engine-config.js +0 -97
  451. package/dist/setup/harness-config-import.js +0 -47
  452. package/dist/setup/providers.js +0 -14
  453. package/dist/setup/steps/semantic.js +0 -39
  454. package/dist/workflows/authoring/scope-key.js +0 -79
  455. package/dist/workflows/exec/lowering-notices.js +0 -23
  456. package/dist/workflows/source-semantics.js +0 -110
  457. package/docs/architecture/0.10-process-rules.md +0 -832
  458. package/docs/architecture/akm-architecture-decision-history.md +0 -2055
  459. package/docs/architecture/comparisons/claude-code-vs-akm-workflows-full.md +0 -550
  460. package/docs/architecture/decisions/0001-no-interpolation-attached-structured-context.md +0 -72
  461. package/docs/architecture/decisions/0002-unit-reuse-and-input-hash-scope.md +0 -142
  462. package/docs/architecture/decisions/0003-child-env-allowlist-and-provenance.md +0 -189
  463. package/docs/architecture/decisions/0004-task-input-contract-and-flag-coercion.md +0 -97
  464. package/docs/architecture/decisions/0005-task-result-vocabulary-and-legacy-read-mapping.md +0 -108
  465. package/docs/architecture/decisions/0006-task-source-version-routing.md +0 -124
  466. package/docs/architecture/decisions/0007-workflow-composition-bounds.md +0 -83
  467. package/docs/architecture/decisions/0008-task-binding-normalization.md +0 -88
  468. package/docs/architecture/decisions/0009-child-run-publication-column-parity.md +0 -80
  469. package/docs/architecture/decisions/0010-driver-protocol-cut.md +0 -36
  470. package/docs/architecture/decisions/0011-engine-run-loop-invariants.md +0 -125
  471. package/docs/architecture/decisions/README.md +0 -62
  472. package/docs/architecture/internals/functional-contract-patterns.md +0 -340
  473. package/docs/architecture/internals/registry-network-boundary.md +0 -57
  474. package/docs/architecture/internals/search.md +0 -248
  475. package/docs/architecture/reviews/env-secret-access.md +0 -319
  476. package/docs/architecture/runtime-boundary-design.md +0 -153
  477. package/docs/architecture/specs/0.9.0-close-out-plan.md +0 -361
  478. package/docs/architecture/specs/0.9.0-decisions.md +0 -532
  479. package/docs/architecture/specs/0.9.0-docs-code-drift-register.md +0 -745
  480. package/docs/architecture/specs/0.9.0-open-items-register.md +0 -135
  481. package/docs/architecture/specs/0.9.0-public-api-issue-backlog.md +0 -740
  482. package/docs/architecture/specs/0.9.0-release-surface-review.md +0 -636
  483. package/docs/architecture/specs/agent-command-engine-model-design.md +0 -513
  484. package/docs/architecture/specs/akm-0.9.0-bundle-adapter-spec.md +0 -501
  485. package/docs/architecture/specs/akm-0.9.0-ref-grammar-decision.md +0 -291
  486. package/docs/architecture/specs/akm-format-neutral-bundle-workspace-spec.md +0 -2059
  487. package/docs/architecture/specs/di-seams-plan.md +0 -539
  488. package/docs/architecture/specs/driver-protocol-keep-or-cut.md +0 -275
  489. package/docs/architecture/specs/fact-asset-type.md +0 -142
  490. package/docs/architecture/specs/improve-collapse-churn-detector-design.md +0 -634
  491. package/docs/architecture/specs/reviews/task-workflow-unification/README.md +0 -72
  492. package/docs/architecture/specs/reviews/task-workflow-unification/findings-consolidated.md +0 -152
  493. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-alternatives.md +0 -352
  494. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-bunqueue.md +0 -300
  495. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-openworkflow.md +0 -402
  496. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-1.md +0 -758
  497. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-2.md +0 -645
  498. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-3.md +0 -602
  499. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-adversarial.md +0 -27
  500. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-architecture.md +0 -27
  501. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-coverage.md +0 -74
  502. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-security.md +0 -15
  503. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-adversarial.md +0 -132
  504. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-verification.md +0 -147
  505. package/docs/architecture/specs/stash-conventions-code-spec.md +0 -275
  506. package/docs/architecture/specs/stash-organization-conventions.md +0 -377
  507. package/docs/architecture/specs/task-workflow-format-unification.md +0 -721
  508. package/docs/architecture/specs/workflow-engine-buy-vs-build.md +0 -231
  509. package/docs/architecture/specs/workflow-format-unification.md +0 -432
  510. package/docs/architecture/testing/manual-testing-checklist.md +0 -3411
  511. package/docs/architecture/testing/okf-v0.2-conformance-runbook.md +0 -924
  512. package/docs/architecture/testing/task-workflow-manual-tests.md +0 -1720
  513. package/docs/guides/claude-code-vs-akm-workflows.md +0 -29
  514. package/docs/guides/knowledge-management.md +0 -18
  515. package/docs/maintainers/curate-workmap.md +0 -335
  516. package/docs/migration/v0.5-to-v0.6.md +0 -759
  517. package/docs/migration/v0.7-to-v0.8.md +0 -1378
  518. package/docs/plans/0.9.2-agent-command-workflow-plan.md +0 -423
@@ -1,1052 +0,0 @@
1
- // This Source Code Form is subject to the terms of the Mozilla Public
2
- // License, v. 2.0. If a copy of the MPL was not distributed with this
3
- // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- /**
5
- * OpenCode SDK agent runner (migrated from `agent/sdk-runner.ts`, #564).
6
- *
7
- * Uses the embedded `@opencode-ai/sdk` instead of `Bun.spawn` for dispatch, so
8
- * no agent CLI argv is ever constructed — but the SDK is not self-contained:
9
- * {@link createManagedOpencode} spawns `opencode serve` and talks HTTP to it,
10
- * so the `opencode` binary must be on PATH (which is why setup's
11
- * `detectHarness` gates this harness on that probe). The user provides an
12
- * OpenAI-compatible endpoint (or inherits from the selected fallback LLM
13
- * engine) for the SDK.
14
- *
15
- * This is the runtime surface of the {@link OpencodeSdkHarness} (`id =
16
- * 'opencode-sdk'`). It is the dispatch path for SDK runner specs; it exposes
17
- * no native session logs of its own (`capabilities.sessionLogs = false`).
18
- *
19
- * ## Two OpenCode majors, one runner (#1049)
20
- *
21
- * This file owns what both majors share: the managed `opencode serve` child
22
- * (spawn, port, registry, cleanup, {@link closeServer}), the child environment,
23
- * per-workspace isolation, deadlines, abort handling and result shaping. The
24
- * per-major wire protocol is an adapter behind `./wire.ts`, chosen once per
25
- * dispatch from the major the engine's binary reports
26
- * (`../opencode/version.ts`); the major is part of the server registry key.
27
- * Where the sections below name an OpenCode 1 wire shape (`query.directory`,
28
- * the prompt body), `v2-adapter.ts` has the OpenCode 2 equivalent.
29
- *
30
- * ## Sessions are kept (#1100)
31
- *
32
- * One session is created per dispatch and is NEVER deleted: it stays in
33
- * OpenCode's history (transcript for debugging, input for session capture),
34
- * as it does for `opencode run`, and its id is returned as
35
- * `AgentRunResult.sessionId`. A dispatch akm gives up on (timeout or caller
36
- * abort) is aborted server-side so the server stops calling the model, but the
37
- * session is still kept. The server process itself is closed independently
38
- * (see the registry notes below).
39
- *
40
- * ## Per-call cwd and env (redesign addendum R2, open seam decision 1)
41
- *
42
- * The plan left one decision open: per-call cwd/env forwarding vs a server
43
- * keyed by `(cwd, envKeysHash)`. Reading the SDK settled it as a SPLIT — the
44
- * two halves have different API realities (verified against
45
- * `@opencode-ai/sdk` 1.2.20):
46
- *
47
- * - **cwd is PER-CALL.** `session.create` / `session.prompt` /
48
- * `session.abort` all accept a `query.directory` parameter that scopes
49
- * the session's working directory, so a single server can host sessions
50
- * in any number of working directories. {@link RunAgentOptions.cwd} is
51
- * forwarded as `query: { directory }` on every session call — no
52
- * per-cwd server processes, no server-key explosion for worktree
53
- * isolation (which mints a fresh directory per unit attempt).
54
- *
55
- * - **env is PER-SERVER (keyed registry).** The SDK exposes NO per-call or
56
- * per-session env surface; the only way env reaches tool child processes
57
- * is the `opencode serve` process environment. {@link getOrStartServer}
58
- * snapshots and materializes the complete child environment for each
59
- * dispatch, passes it directly to the managed spawn (never mutating
60
- * `process.env`), and hashes that exact canonical environment for registry
61
- * identity. Identical child material shares one server; any material
62
- * difference receives a separate server.
63
- *
64
- * This is what removed the workflow engine's `env_unsupported` hard-fail for
65
- * the sdk runner: injection genuinely reaches the child, because tool
66
- * subprocesses (bash etc.) inherit the server process environment.
67
- *
68
- * Registry hygiene (peer-review fixes):
69
- *
70
- * - **Ports.** `createOpencodeServer` binds a FIXED default port (4096),
71
- * so coexisting registry entries would contend for the same bind. Every
72
- * entry is started on its own reserved OS-assigned port (see
73
- * {@link startServer}).
74
- * - **Shutdown.** {@link closeServer} closes resolved servers
75
- * SYNCHRONOUSLY — it runs from `process.once('exit')`, where Bun never
76
- * drains microtasks, so a `.then()`-based close would orphan every
77
- * `opencode serve` child.
78
- *
79
- * Process-lifecycle note (owner finding 4): a cached `opencode serve` child is
80
- * a live OS handle that keeps Bun's event loop OPEN, so a one-shot CLI never
81
- * becomes idle and `process.once('exit')` never fires — the exit hook alone
82
- * cannot free a process the child is keeping alive (a deadlock that hangs the
83
- * caller after an otherwise-successful run). The registry is therefore drained
84
- * PROACTIVELY at the end of a dispatching command: the workflow engine calls
85
- * `disposeDispatchResources()` (→ {@link closeServer}) in composition-root and
86
- * workflow `finally` blocks.
87
- * The `process.once('exit')` hook stays as the last-resort backstop for paths
88
- * that never reach that drain.
89
- *
90
- * ## Managed server spawn (owner finding 4, live-harness follow-up)
91
- *
92
- * Draining the registry is necessary but NOT sufficient with the SDK's own
93
- * `createOpencodeServer`: its `close()` merely sends SIGTERM and it never
94
- * `unref()`s the child or its stdio pipes, so akm's event loop stays pinned
95
- * until the child ACTUALLY exits — and a real `opencode serve` (a live HTTP
96
- * server with provider children) can outlive SIGTERM long enough to hang the
97
- * caller indefinitely. {@link createManagedOpencode} therefore owns the spawn
98
- * (the SDK package is used only for its client).
99
- *
100
- * Owning the spawn changes the LIFECYCLE, not the REQUIREMENT: the SDK's
101
- * `createOpencodeServer` is itself a `spawn("opencode", ["serve", ...])`, and
102
- * the package declares `"dependencies": {}`, so `opencode` must be on PATH
103
- * either way. akm's version differs only in how it supervises the child:
104
- *
105
- * - after the URL handshake, the child and its stdio are `unref()`ed /
106
- * destroyed, so the handle can never hold akm open;
107
- * - `close()` sends SIGTERM and arms a bounded grace timer
108
- * ({@link SERVER_KILL_GRACE_MS}) that escalates to SIGKILL and is cleared
109
- * on cooperative exit, so stubborn children cannot survive parent exit and
110
- * stale timers cannot signal a reused PID;
111
- * - the spawn receives the immutable per-dispatch environment directly, so
112
- * asynchronous factory setup cannot race a temporary process-wide overlay.
113
- */
114
- import { spawn } from "node:child_process";
115
- import { createHash } from "node:crypto";
116
- import { COMMON_SPAWN_ENV_PASSTHROUGH, OPENCODE_ENV_PASSTHROUGH, spawnEnvNamesFor } from "../../../core/spawn-env.js";
117
- import { DEFAULT_AGENT_TIMEOUT_MS } from "../../agent/config.js";
118
- import { opencodeInferenceConfig } from "../opencode/model-config.js";
119
- import { MODEL_WORK_OPENCODE_AGENT, modelWorkOpencodeConfig, modelWorkPluginEnv } from "../opencode/model-work-agent.js";
120
- import { detectOpencodeMajor } from "../opencode/version.js";
121
- import { V1_ADAPTER } from "./v1-adapter.js";
122
- import { V2_ADAPTER } from "./v2-adapter.js";
123
- import { parseReadiness, } from "./wire.js";
124
- // Server registry — one server per complete server-material signature. Caller
125
- // deadlines race the shared promise independently; they never become startup
126
- // configuration inherited by later callers.
127
- const _servers = new Map();
128
- // Resolved servers by registry key, mirrored from `_servers` as each start
129
- // promise settles. This exists so closeServer() can close started servers
130
- // SYNCHRONOUSLY: it is wired to `process.once('exit')`, and Bun does not
131
- // drain microtasks scheduled inside 'exit' handlers, so a `.then()`-based
132
- // close never runs there and would orphan every `opencode serve` child.
133
- const _resolvedServers = new Map();
134
- // Listen ports handed to non-default registry entries (see startServer) —
135
- // tracked so two coexisting servers in this process can never be assigned
136
- // the same port.
137
- const _serverPorts = new Map();
138
- // Serializes probe-and-reserve so concurrent starts cannot claim one port.
139
- let _portAllocationTail = Promise.resolve();
140
- /** The port `createOpencodeServer` binds when none is passed (SDK 1.2.20). */
141
- const DEFAULT_SDK_PORT = 4096;
142
- // Test override: when set, every call uses this server (all keys) and no real
143
- // server is ever started.
144
- let _testServer = null;
145
- // Test seam replacing the real `createOpencode` import (see __setServerFactory).
146
- let _serverFactory = null;
147
- let _exitHookInstalled = false;
148
- /**
149
- * True once `process.on("exit")` has fired. Close paths consult it because an
150
- * exit handler runs after the event loop has stopped: no timer scheduled there
151
- * will ever fire, so any cleanup that needs to happen must happen inline.
152
- */
153
- let _processExiting = false;
154
- /**
155
- * Test-only seam: inject a fake {@link SdkServer} so `runOpencodeSdk` can be
156
- * exercised without the real `@opencode-ai/sdk` (which would spin up a server).
157
- * Pass `null` to clear. NOT part of the public runtime API — used only to
158
- * assert the #564 bug fixes (systemPrompt/tools forwarding + timeout). The
159
- * leading underscores mark it as internal.
160
- */
161
- export function __setTestServer(server) {
162
- _testServer = server;
163
- }
164
- /**
165
- * Test-only seam: replace the `createOpencode` factory so the env-keyed
166
- * server registry (module doc, *Per-call cwd and env*) can be exercised
167
- * without the real SDK. The complete child environment is supplied as
168
- * `options.env`, so async factories observe the same immutable values as the
169
- * production spawn. Pass `null` to clear.
170
- */
171
- export function __setServerFactory(factory) {
172
- _serverFactory = factory;
173
- }
174
- /**
175
- * Close every started OpenCode SDK server and reset the registry (and any
176
- * injected test server). Used by tests for clean teardown between runs and
177
- * wired to `process.once('exit')` — which is why resolved servers MUST be
178
- * closed synchronously here: Bun never drains microtasks scheduled inside
179
- * 'exit' handlers, so a promise-based close would silently orphan the
180
- * `opencode serve` children (leaking processes AND keeping their ports
181
- * bound for the next invocation).
182
- */
183
- export async function closeServer() {
184
- const closes = [];
185
- for (const [key, entry] of _servers) {
186
- const resolved = _resolvedServers.get(key);
187
- if (resolved) {
188
- // Synchronous close — safe from the 'exit' hook.
189
- try {
190
- closes.push(Promise.resolve(resolved.server.close()));
191
- }
192
- catch {
193
- /* ignore */
194
- }
195
- }
196
- else {
197
- // Still starting: cancel the real managed spawn immediately. A custom
198
- // factory may ignore the signal; the registry settlement handler closes
199
- // any late result without awaiting it and pinning shutdown indefinitely.
200
- entry.controller.abort();
201
- }
202
- }
203
- _servers.clear();
204
- _resolvedServers.clear();
205
- _serverPorts.clear();
206
- try {
207
- if (_testServer)
208
- closes.push(Promise.resolve(_testServer.server.close()));
209
- }
210
- catch {
211
- /* ignore */
212
- }
213
- _testServer = null;
214
- await Promise.allSettled(closes);
215
- }
216
- /**
217
- * Convert an `AgentDispatchRequest.tools` policy into the SDK's tool-allowlist
218
- * shape (`{ [toolName]: boolean }`).
219
- *
220
- * #564 bug fix (2): the tool list was previously dropped entirely. The CLI
221
- * builder passes tools as a comma-separated `--allowedTools` flag; the SDK
222
- * instead wants a per-tool boolean map. A list/comma-string of tool names is
223
- * treated as an allowlist (each name → true). A structured policy object whose
224
- * values are already booleans is forwarded as-is.
225
- *
226
- * Returns `undefined` when there is nothing to forward, so an absent policy
227
- * leaves the SDK's own defaults untouched (behaviour-preserving for callers
228
- * that pass no tools).
229
- */
230
- function toolsToSdkAllowlist(tools) {
231
- if (tools === undefined || tools === null)
232
- return undefined;
233
- const names = [];
234
- if (typeof tools === "string") {
235
- names.push(...tools
236
- .split(",")
237
- .map((t) => t.trim())
238
- .filter(Boolean));
239
- }
240
- else if (Array.isArray(tools)) {
241
- for (const t of tools) {
242
- if (typeof t === "string" && t.trim())
243
- names.push(t.trim());
244
- }
245
- }
246
- else if (typeof tools === "object") {
247
- // Structured policy: forward boolean entries directly.
248
- const out = {};
249
- for (const [k, v] of Object.entries(tools)) {
250
- if (typeof v === "boolean")
251
- out[k] = v;
252
- }
253
- return Object.keys(out).length > 0 ? out : undefined;
254
- }
255
- if (names.length === 0)
256
- return undefined;
257
- const out = {};
258
- for (const n of names)
259
- out[n] = true;
260
- return out;
261
- }
262
- /** The inference a fallback LLM connection carries, which opencode can take. */
263
- function fallbackInference(llmConfig) {
264
- const out = {};
265
- for (const key of ["temperature", "maxTokens", "contextLength", "enableThinking", "reasoningEffort"]) {
266
- if (llmConfig?.[key] !== undefined)
267
- out[key] = llmConfig[key];
268
- }
269
- return out;
270
- }
271
- /**
272
- * Assemble the OpenCode SDK server config from the profile + LLM fallback.
273
- * Pure and exported for tests. `profile.model` is already exact because model
274
- * aliases resolve once before harness lowering. A server for model work also
275
- * defines the confined model-work agent (`../opencode/model-work-agent`).
276
- *
277
- * The model the config routes through `akm-custom` is declared in full, with
278
- * the dispatch's inference (`../opencode/model-config`): the fallback LLM
279
- * engine's, under `requestInference`, the request's own. For model work the
280
- * options go on the confined agent instead of the model, which needs no model
281
- * named: it runs whichever model opencode picks. A model the user's own opencode
282
- * config provides carries none: set inference there.
283
- */
284
- export function buildSdkConfig(profile, llmConfig, modelWork = false, requestInference) {
285
- const endpoint = llmConfig?.endpoint;
286
- const apiKey = llmConfig?.apiKey;
287
- const profileModel = profile.model;
288
- const model = profileModel ?? llmConfig?.model;
289
- const inference = requestInference === null ? undefined : { ...fallbackInference(llmConfig), ...requestInference };
290
- const { entry, agentOptions } = opencodeInferenceConfig(inference, modelWork);
291
- const sdkConfig = {};
292
- if (model)
293
- sdkConfig.model = model;
294
- if (endpoint || apiKey) {
295
- // The first path segment selects the OpenCode provider. Model IDs may
296
- // themselves contain slashes, but still belong to this custom endpoint.
297
- const modelId = model?.startsWith("akm-custom/") ? model.slice("akm-custom/".length) : model;
298
- // Configure a custom OpenAI-compatible provider. OpenCode registers only
299
- // the models a custom provider lists, so the routed model is declared
300
- // (#1015: without it every dispatch failed with ProviderModelNotFoundError).
301
- sdkConfig.provider = {
302
- "akm-custom": {
303
- npm: "@ai-sdk/openai-compatible",
304
- options: {
305
- baseURL: canonicalProviderBase(endpoint) ?? undefined,
306
- ...(apiKey ? { apiKey } : {}),
307
- },
308
- ...(modelId ? { models: { [modelId]: entry } } : {}),
309
- },
310
- };
311
- if (modelId)
312
- sdkConfig.model = `akm-custom/${modelId}`;
313
- }
314
- return modelWork ? { ...sdkConfig, ...modelWorkOpencodeConfig(agentOptions) } : sdkConfig;
315
- }
316
- /** Digest the OpenCode major, the executable and the exact environment received by the child. */
317
- function serverRegistryKey(adapter, profile, env) {
318
- // The major is part of the identity: a v1 and a v2 server never share an entry.
319
- const material = { major: adapter.major, bin: profile.bin, env };
320
- return createHash("sha256")
321
- .update(JSON.stringify(canonicalize(material)))
322
- .digest("hex");
323
- }
324
- /**
325
- * @internal Exact environment allowlist used to start the OpenCode SDK server:
326
- * the common baseline, the OpenCode names the `opencode` CLI profile passes
327
- * (`OPENCODE_ENV_PASSTHROUGH`: API key, config path, XDG base directories), and
328
- * the profile's own names. They are the server's, not the profile's: there is no
329
- * built-in `opencode-sdk` profile, so its `envPassthrough` is empty.
330
- */
331
- export function opencodeSdkServerEnvironmentNames(profile) {
332
- return [
333
- ...new Set([
334
- ...spawnEnvNamesFor(COMMON_SPAWN_ENV_PASSTHROUGH),
335
- ...OPENCODE_ENV_PASSTHROUGH,
336
- ...(profile.envPassthrough ?? []),
337
- ]),
338
- ];
339
- }
340
- function buildServerEnv(profile, config, bindings, envSource, modelWork) {
341
- const env = {};
342
- for (const key of opencodeSdkServerEnvironmentNames(profile)) {
343
- const value = envSource[key];
344
- if (value !== undefined)
345
- env[key] = value;
346
- }
347
- for (const [key, value] of Object.entries(bindings ?? {}))
348
- env[key] = value;
349
- if (modelWork)
350
- Object.assign(env, modelWorkPluginEnv());
351
- env.OPENCODE_CONFIG_CONTENT = JSON.stringify(config);
352
- return env;
353
- }
354
- function canonicalProviderBase(endpoint) {
355
- if (!endpoint)
356
- return null;
357
- try {
358
- const url = new URL(endpoint);
359
- url.pathname = url.pathname.replace(/\/chat\/completions$/, "").replace(/\/$/, "");
360
- return url.toString().replace(/\/$/, "");
361
- }
362
- catch {
363
- return endpoint.replace(/\/chat\/completions$/, "").replace(/\/$/, "");
364
- }
365
- }
366
- function canonicalize(value) {
367
- if (Array.isArray(value))
368
- return value.map(canonicalize);
369
- if (value && typeof value === "object") {
370
- return Object.fromEntries(Object.entries(value)
371
- .sort(([a], [b]) => a.localeCompare(b))
372
- .map(([key, child]) => [key, canonicalize(child)]));
373
- }
374
- return value;
375
- }
376
- /**
377
- * Ask the OS for a currently-free localhost port (bind :0, read the assigned
378
- * port, release it). Skips the SDK's fixed default port and any port already
379
- * handed to another registry entry in this process, so coexisting servers
380
- * never contend. The probe-then-use gap is the standard free-port race —
381
- * acceptable here because the failure mode is a clean `spawn_failed` on the
382
- * next dispatch, not corruption.
383
- */
384
- async function allocateFreePort(registryKey) {
385
- let release;
386
- const previous = _portAllocationTail;
387
- _portAllocationTail = new Promise((resolve) => {
388
- release = resolve;
389
- });
390
- await previous;
391
- try {
392
- const { createServer } = await import("node:net");
393
- const taken = new Set(_serverPorts.values());
394
- for (let attempt = 0; attempt < 10; attempt++) {
395
- const port = await new Promise((resolve, reject) => {
396
- const probe = createServer();
397
- probe.unref();
398
- probe.on("error", reject);
399
- probe.listen(0, "127.0.0.1", () => {
400
- const address = probe.address();
401
- probe.close(() => {
402
- if (address && typeof address === "object")
403
- resolve(address.port);
404
- else
405
- reject(new Error("could not read the probe socket's port"));
406
- });
407
- });
408
- });
409
- if (port !== DEFAULT_SDK_PORT && !taken.has(port)) {
410
- _serverPorts.set(registryKey, port);
411
- return port;
412
- }
413
- }
414
- throw new Error("could not allocate a free port for the OpenCode SDK server");
415
- }
416
- finally {
417
- release();
418
- }
419
- }
420
- /** Grace between SIGTERM and SIGKILL when closing a managed server child. */
421
- const SERVER_KILL_GRACE_MS = 2_000;
422
- // Test seam: override the argv used to spawn the server child ("opencode"
423
- // plus serve flags by default) so the managed-spawn lifecycle (handshake,
424
- // unref, SIGTERM→SIGKILL escalation) is testable without the real binary.
425
- let _serveCommand = null;
426
- /** Test-only seam: replace the `opencode serve` argv. Pass `null` to clear. */
427
- export function __setServeCommand(argv) {
428
- _serveCommand = argv;
429
- }
430
- /**
431
- * Spawn-owning replacement for the SDK's `createOpencode` (module doc,
432
- * *Managed server spawn*). Mirrors `createOpencodeServer`'s contract — the
433
- * `spawn` receives the factory's immutable environment directly,
434
- * `OPENCODE_CONFIG_CONTENT` carries the config, the handshake parses the
435
- * "opencode server listening on <url>" line — but manages the child so its
436
- * handle can never pin akm's event loop:
437
- *
438
- * - handshake success → stdio destroyed, listeners dropped, `proc.unref()`;
439
- * - `close()` → SIGTERM now, SIGKILL after an unref'ed grace timer;
440
- * - handshake failure → the child is killed and unref'ed before rejecting.
441
- */
442
- async function createManagedOpencode(options) {
443
- const { adapter } = options;
444
- const port = options.port ?? DEFAULT_SDK_PORT;
445
- const argv = _serveCommand ?? adapter.serveArgv(options.bin ?? "opencode", port);
446
- // The adapter may add to the child's environment (V2: the server password akm chose).
447
- const authorization = adapter.authorize();
448
- const proc = spawn(argv[0], argv.slice(1), {
449
- env: { ...options.env, ...authorization.env },
450
- stdio: ["ignore", "pipe", "pipe"],
451
- });
452
- let closeStarted = false;
453
- let closeEscalation;
454
- const childExited = () => proc.exitCode !== null || proc.signalCode !== null;
455
- const clearCloseEscalation = () => {
456
- if (closeEscalation !== undefined) {
457
- clearTimeout(closeEscalation);
458
- closeEscalation = undefined;
459
- }
460
- };
461
- const closeManaged = () => {
462
- if (closeStarted)
463
- return;
464
- closeStarted = true;
465
- if (childExited())
466
- return;
467
- proc.once("exit", clearCloseEscalation);
468
- try {
469
- proc.kill("SIGTERM");
470
- }
471
- catch {
472
- proc.off("exit", clearCloseEscalation);
473
- return;
474
- }
475
- if (childExited())
476
- return;
477
- if (_processExiting) {
478
- // During `process.on("exit")` the event loop is finished, so a timer
479
- // scheduled here would never fire and the SIGKILL escalation could not
480
- // happen at all — a server child that ignores SIGTERM outlived akm.
481
- // Exit handlers must be synchronous, so escalate immediately instead of
482
- // waiting out a grace period we have no way to wait for.
483
- try {
484
- proc.kill("SIGKILL");
485
- }
486
- catch {
487
- /* already dead */
488
- }
489
- return;
490
- }
491
- closeEscalation = setTimeout(() => {
492
- closeEscalation = undefined;
493
- if (childExited())
494
- return;
495
- try {
496
- proc.kill("SIGKILL");
497
- }
498
- catch {
499
- /* already dead */
500
- }
501
- }, SERVER_KILL_GRACE_MS);
502
- };
503
- const url = await new Promise((resolve, reject) => {
504
- let output = "";
505
- let settled = false;
506
- const cleanupStartup = () => {
507
- proc.stdout?.off("data", onStdoutData);
508
- proc.stderr?.off("data", onStderrData);
509
- proc.off("exit", onExit);
510
- proc.off("error", onError);
511
- options.startupSignal.removeEventListener("abort", onAbort);
512
- };
513
- const fail = (err) => {
514
- if (settled)
515
- return;
516
- settled = true;
517
- cleanupStartup();
518
- closeManaged();
519
- proc.stdout?.destroy();
520
- proc.stderr?.destroy();
521
- proc.unref();
522
- reject(err);
523
- };
524
- const succeed = (serverUrl) => {
525
- if (settled)
526
- return;
527
- settled = true;
528
- cleanupStartup();
529
- resolve(serverUrl);
530
- };
531
- const onStdoutData = (chunk) => {
532
- output += chunk.toString();
533
- for (const line of output.split("\n")) {
534
- const verdict = parseReadiness(line, adapter.major);
535
- if (!verdict)
536
- continue;
537
- if ("error" in verdict)
538
- fail(new Error(verdict.error));
539
- else
540
- succeed(verdict.url);
541
- return;
542
- }
543
- };
544
- const onStderrData = (chunk) => {
545
- output += chunk.toString();
546
- };
547
- const onExit = (code, signal) => {
548
- const status = code !== null ? `code ${code}` : `signal ${signal ?? "unknown"}`;
549
- fail(new Error(`OpenCode server exited with ${status}${output.trim() ? `\nServer output: ${output}` : ""}`));
550
- };
551
- const onError = (err) => {
552
- fail(err instanceof Error ? err : new Error(String(err)));
553
- };
554
- const onAbort = () => fail(new Error("OpenCode server startup cancelled because no callers are waiting"));
555
- proc.stdout?.on("data", onStdoutData);
556
- proc.stderr?.on("data", onStderrData);
557
- proc.on("exit", onExit);
558
- proc.on("error", onError);
559
- if (options.startupSignal.aborted)
560
- onAbort();
561
- else
562
- options.startupSignal.addEventListener("abort", onAbort, { once: true });
563
- });
564
- // Handshake done: from here on the child must never hold akm open. Its
565
- // lifetime is managed explicitly (closeServer → closeManaged), not by the
566
- // event loop. Destroying the pipes also releases their loop handles.
567
- proc.stdout?.destroy();
568
- proc.stderr?.destroy();
569
- proc.unref();
570
- try {
571
- return { client: await adapter.connect(url, authorization.credentials), server: { close: closeManaged } };
572
- }
573
- catch (err) {
574
- closeManaged();
575
- throw err;
576
- }
577
- }
578
- async function startServer(adapter, profile, sdkConfig, env, registryKey, startupSignal) {
579
- const factory = _serverFactory ?? createManagedOpencode;
580
- const options = {
581
- adapter,
582
- bin: profile.bin,
583
- ...(Object.keys(sdkConfig).length > 0 ? { config: sdkConfig } : {}),
584
- env,
585
- startupSignal,
586
- };
587
- // Every cached server receives a separately reserved port. This avoids both
588
- // inter-entry contention and collisions with an unrelated process on 4096.
589
- options.port = await allocateFreePort(registryKey);
590
- if (startupSignal.aborted)
591
- throw new Error("OpenCode server startup cancelled because no callers are waiting");
592
- // Installed before the spawn, so a child that is still starting up is covered too.
593
- if (!_exitHookInstalled) {
594
- _exitHookInstalled = true;
595
- process.once("exit", () => {
596
- _processExiting = true;
597
- void closeServer();
598
- });
599
- // A command with no SIGINT/SIGTERM handler of its own dies on the signal without running `exit` hooks, which
600
- // orphaned every cached server. Close them and re-raise, so the default termination still happens. A command that
601
- // handles the signal itself (`workflow run`, `improve`, `index`, task runs) unwinds through its own cleanup and is
602
- // left alone.
603
- for (const signal of ["SIGINT", "SIGTERM"]) {
604
- if (process.listenerCount(signal) > 0)
605
- continue;
606
- process.once(signal, () => {
607
- _processExiting = true;
608
- void closeServer();
609
- process.kill(process.pid, signal);
610
- });
611
- }
612
- }
613
- const server = await factory(options);
614
- if (!server)
615
- throw new Error("Failed to initialise OpenCode SDK server.");
616
- return server;
617
- }
618
- /**
619
- * Get (or lazily start) the server for this call's complete server material.
620
- * Concurrent callers of the same key share one
621
- * start (the registry stores the in-flight promise). A failed start is
622
- * evicted so the next call can retry instead of caching the error forever.
623
- */
624
- function getOrStartServer(adapter, profile, llmConfig, env, envSource = process.env, modelWork = false, inference) {
625
- if (_testServer)
626
- return { promise: Promise.resolve(_testServer), release() { } };
627
- const sdkConfig = buildSdkConfig(profile, llmConfig, modelWork, inference);
628
- const serverEnv = buildServerEnv(profile, sdkConfig, env, envSource, modelWork);
629
- const key = serverRegistryKey(adapter, profile, serverEnv);
630
- let entry = _servers.get(key);
631
- if (!entry) {
632
- const controller = new AbortController();
633
- entry = {
634
- promise: startServer(adapter, profile, sdkConfig, serverEnv, key, controller.signal),
635
- controller,
636
- waiters: 0,
637
- };
638
- const started = entry;
639
- _servers.set(key, started);
640
- started.promise.then((server) => {
641
- if (_servers.get(key) === started) {
642
- started.server = server;
643
- _resolvedServers.set(key, server);
644
- }
645
- else {
646
- void server.server.close();
647
- }
648
- }, () => {
649
- if (_servers.get(key) === started) {
650
- _servers.delete(key);
651
- _serverPorts.delete(key);
652
- }
653
- });
654
- }
655
- entry.waiters++;
656
- let released = false;
657
- return {
658
- promise: entry.promise,
659
- release() {
660
- if (released)
661
- return;
662
- released = true;
663
- entry.waiters--;
664
- if (entry.waiters === 0 && !entry.server && _servers.get(key) === entry) {
665
- _servers.delete(key);
666
- _serverPorts.delete(key);
667
- entry.controller.abort();
668
- }
669
- },
670
- };
671
- }
672
- const SDK_OPERATION_TIMED_OUT = Symbol("opencode-sdk-operation-timeout");
673
- const SDK_OPERATION_ABORTED = Symbol("opencode-sdk-operation-aborted");
674
- async function raceSdkOperation(operation, opts) {
675
- let timer;
676
- let onAbort;
677
- const racers = [operation];
678
- if (opts.timeoutMs !== null) {
679
- racers.push(new Promise((resolve) => {
680
- timer = opts.setTimeoutFn(() => resolve(SDK_OPERATION_TIMED_OUT), opts.timeoutMs ?? 0);
681
- if (typeof timer !== "number")
682
- timer.unref?.();
683
- }));
684
- }
685
- if (opts.signal) {
686
- racers.push(new Promise((resolve) => {
687
- onAbort = () => resolve(SDK_OPERATION_ABORTED);
688
- if (opts.signal?.aborted)
689
- onAbort();
690
- else
691
- opts.signal?.addEventListener("abort", onAbort, { once: true });
692
- }));
693
- }
694
- try {
695
- return racers.length === 1 ? await operation : await Promise.race(racers);
696
- }
697
- finally {
698
- if (timer !== undefined)
699
- opts.clearTimeoutFn(timer);
700
- if (opts.signal && onAbort)
701
- opts.signal.removeEventListener("abort", onAbort);
702
- }
703
- }
704
- function errorText(err) {
705
- return err instanceof Error ? err.message : String(err);
706
- }
707
- /**
708
- * Answer every OpenCode permission request of the dispatch's session (and of
709
- * the sub-sessions it spawns) with "reject", once each. This is what a
710
- * non-interactive `opencode run` does ("auto-rejecting"); left unanswered, a
711
- * tool call that needs permission (OpenCode's default `external_directory:
712
- * ask`) stays `running` and the prompt never settles. A rejection reaches the
713
- * model as a tool error it can work around. Each request and answer is
714
- * appended to `notes`. The same stream reports OpenCode's retries (#1108):
715
- * each is noted, and one caused by a usage limit, or due after `deadline`,
716
- * ends the run through `stopForRetry` instead of a silent wait. `ready`
717
- * settles once the subscription is open and never rejects; `stop` closes it.
718
- * The wire format (event shapes, the reject call) is the adapter's.
719
- */
720
- function rejectPermissionRequests(adapter, client, sessionId, spec, notes, deadline, stopForRetry) {
721
- const controller = new AbortController();
722
- const stop = () => controller.abort();
723
- if (adapter.canWatch?.(client) === false)
724
- return { stop };
725
- const sessions = new Set([sessionId]);
726
- const answered = new Set();
727
- const consume = async (stream) => {
728
- for await (const raw of stream) {
729
- const event = adapter.decodeEvent(raw);
730
- if (!event)
731
- continue;
732
- if (event.kind === "session") {
733
- if (event.parentId !== undefined && sessions.has(event.parentId))
734
- sessions.add(event.id);
735
- continue;
736
- }
737
- if (!sessions.has(event.sessionId))
738
- continue;
739
- if (event.kind === "retry") {
740
- const retryAt = event.next === undefined ? "unknown time" : new Date(event.next).toISOString();
741
- notes.push(`OpenCode retry attempt ${event.attempt ?? "unknown"}${event.provider ? ` (${event.provider})` : ""}: ${event.message}; retrying at ${retryAt}`);
742
- if (event.accountLimit) {
743
- stopForRetry(`OpenCode provider limit stopped this run${event.provider ? ` (${event.provider})` : ""}: ${event.message}; reset at ${retryAt}`);
744
- }
745
- else if (event.next !== undefined && deadline !== null && event.next > deadline) {
746
- stopForRetry(`OpenCode's next retry (${retryAt}) is after this run's deadline: ${event.message}`);
747
- }
748
- continue;
749
- }
750
- if (answered.has(event.requestId))
751
- continue;
752
- answered.add(event.requestId);
753
- notes.push(`permission requested: ${event.description}; auto-rejecting`);
754
- adapter.rejectPermission(client, event, spec).catch((err) => {
755
- notes.push(`permission reply for ${event.requestId} failed: ${errorText(err)}`);
756
- });
757
- }
758
- };
759
- const ready = adapter
760
- .subscribe(client, spec, controller.signal)
761
- .then((stream) => {
762
- void consume(stream).catch((err) => {
763
- if (!controller.signal.aborted)
764
- notes.push(`permission event stream ended: ${errorText(err)}`);
765
- });
766
- })
767
- .catch((err) => {
768
- notes.push(`permission requests will not be answered: event subscription failed: ${errorText(err)}`);
769
- });
770
- return { ready, stop };
771
- }
772
- const ADAPTERS = { 1: V1_ADAPTER, 2: V2_ADAPTER };
773
- /** The wire adapter for the OpenCode major the engine's binary reports. */
774
- function adapterFor(profile) {
775
- return ADAPTERS[detectOpencodeMajor(profile.bin).major];
776
- }
777
- function abortedBeforeSdkStart(profile) {
778
- return {
779
- ok: false,
780
- stdout: "",
781
- stderr: "",
782
- durationMs: 0,
783
- exitCode: null,
784
- reason: "aborted",
785
- error: `opencode-sdk agent "${profile.name}" not started: caller signal already aborted`,
786
- };
787
- }
788
- export async function runOpencodeSdk(profile, prompt, opts = {}, llmConfig) {
789
- const start = Date.now();
790
- const timeoutMs = opts.timeoutMs !== undefined ? opts.timeoutMs : DEFAULT_AGENT_TIMEOUT_MS;
791
- const deadline = timeoutMs === null ? null : start + timeoutMs;
792
- const remainingTimeoutMs = () => (deadline === null ? null : Math.max(0, deadline - Date.now()));
793
- const setTimeoutImpl = opts.setTimeoutFn ?? setTimeout;
794
- const clearTimeoutImpl = opts.clearTimeoutFn ?? clearTimeout;
795
- if (opts.signal?.aborted)
796
- return abortedBeforeSdkStart(profile);
797
- const modelWork = opts.dispatch?.modelWork === true;
798
- const adapter = adapterFor(profile);
799
- let client;
800
- if (_testServer) {
801
- client = _testServer.client;
802
- }
803
- else {
804
- const startupHandle = getOrStartServer(adapter, profile, llmConfig, opts.env, opts.envSource, modelWork, opts.dispatch?.inference);
805
- try {
806
- const startup = await raceSdkOperation(startupHandle.promise, {
807
- timeoutMs: remainingTimeoutMs(),
808
- setTimeoutFn: setTimeoutImpl,
809
- clearTimeoutFn: clearTimeoutImpl,
810
- signal: opts.signal,
811
- });
812
- if (startup === SDK_OPERATION_ABORTED) {
813
- return {
814
- ok: false,
815
- stdout: "",
816
- stderr: "",
817
- durationMs: Date.now() - start,
818
- exitCode: null,
819
- reason: "aborted",
820
- error: `opencode-sdk agent "${profile.name}" aborted by caller signal during server startup`,
821
- };
822
- }
823
- if (startup === SDK_OPERATION_TIMED_OUT) {
824
- return {
825
- ok: false,
826
- stdout: "",
827
- stderr: "",
828
- durationMs: Date.now() - start,
829
- exitCode: null,
830
- reason: "timeout",
831
- error: `opencode-sdk agent "${profile.name}" timed out during server startup after ${timeoutMs}ms`,
832
- };
833
- }
834
- client = startup.client;
835
- }
836
- catch (e) {
837
- return {
838
- ok: false,
839
- stdout: "",
840
- stderr: String(e),
841
- durationMs: Date.now() - start,
842
- exitCode: 1,
843
- reason: "spawn_failed",
844
- error: String(e),
845
- };
846
- }
847
- finally {
848
- startupHandle.release();
849
- }
850
- }
851
- // #564 bug fix (3): enforce a hard timeout like the CLI path (runAgent).
852
- // Previously runOpencodeSdk() awaited SDK calls with no timeout, so a stalled
853
- // local-model endpoint or wedged server could block the caller indefinitely.
854
- // The same absolute deadline covers server startup, session creation, and
855
- // prompting. null disables every dispatch timer.
856
- // Per-call working directory (module doc): the adapter scopes the session to
857
- // it, so worktree-isolated units run in their own checkout without a
858
- // per-cwd server.
859
- // Forward the exact native agent selector, systemPrompt, and tools from the abstract
860
- // dispatch request. Both were once accepted on AgentDispatchRequest but silently dropped
861
- // on the SDK path (#564). Model work runs the confined agent its server config defines;
862
- // its tools are that agent's.
863
- const dispatch = opts.dispatch;
864
- const agent = modelWork ? MODEL_WORK_OPENCODE_AGENT : dispatch?.agent;
865
- const system = dispatch?.systemPrompt;
866
- const tools = toolsToSdkAllowlist(dispatch?.tools);
867
- const spec = {
868
- ...(opts.cwd ? { directory: opts.cwd } : {}),
869
- ...(agent ? { agent } : {}),
870
- ...(system ? { system } : {}),
871
- ...(tools ? { tools } : {}),
872
- };
873
- // One session per call — do NOT reuse (history accumulates, token costs grow).
874
- // Session creation is startup plumbing, so failures map to spawn_failed rather
875
- // than bubbling out as a generic workflow dispatch exception.
876
- const callerSignal = opts.signal;
877
- const runController = new AbortController();
878
- const abortForCaller = () => runController.abort();
879
- if (callerSignal?.aborted)
880
- abortForCaller();
881
- else
882
- callerSignal?.addEventListener("abort", abortForCaller, { once: true });
883
- const abortSignal = runController.signal;
884
- let sessionId;
885
- try {
886
- const created = await raceSdkOperation(adapter.createSession(client, spec), {
887
- timeoutMs: remainingTimeoutMs(),
888
- setTimeoutFn: setTimeoutImpl,
889
- clearTimeoutFn: clearTimeoutImpl,
890
- signal: abortSignal,
891
- });
892
- if (created === SDK_OPERATION_ABORTED) {
893
- return {
894
- ok: false,
895
- stdout: "",
896
- stderr: "",
897
- durationMs: Date.now() - start,
898
- exitCode: null,
899
- reason: "aborted",
900
- error: `opencode-sdk agent "${profile.name}" aborted by caller signal`,
901
- };
902
- }
903
- if (created === SDK_OPERATION_TIMED_OUT) {
904
- return {
905
- ok: false,
906
- stdout: "",
907
- stderr: "",
908
- durationMs: Date.now() - start,
909
- exitCode: null,
910
- reason: "timeout",
911
- error: `opencode-sdk agent "${profile.name}" timed out creating a session after ${timeoutMs}ms`,
912
- };
913
- }
914
- sessionId = created;
915
- }
916
- catch (err) {
917
- return {
918
- ok: false,
919
- stdout: "",
920
- stderr: errorText(err),
921
- durationMs: Date.now() - start,
922
- exitCode: 1,
923
- reason: "spawn_failed",
924
- error: errorText(err),
925
- };
926
- }
927
- if (!sessionId) {
928
- return {
929
- ok: false,
930
- stdout: "",
931
- stderr: "Failed to create session",
932
- durationMs: Date.now() - start,
933
- exitCode: 1,
934
- reason: "spawn_failed",
935
- error: "Failed to create OpenCode session",
936
- };
937
- }
938
- let result;
939
- // Subscribed before the prompt so no request can be missed; closed in `finally`.
940
- const permissionNotes = [];
941
- let retryFailure;
942
- const permissions = rejectPermissionRequests(adapter, client, sessionId, spec, permissionNotes, deadline, (failure) => {
943
- retryFailure ??= failure;
944
- runController.abort();
945
- });
946
- try {
947
- // Bounded like every other step; if it times out the prompt race below does too.
948
- if (permissions.ready) {
949
- await raceSdkOperation(permissions.ready, {
950
- timeoutMs: remainingTimeoutMs(),
951
- setTimeoutFn: setTimeoutImpl,
952
- clearTimeoutFn: clearTimeoutImpl,
953
- signal: abortSignal,
954
- });
955
- }
956
- const prompted = await raceSdkOperation(adapter.prompt(client, sessionId, prompt, spec, abortSignal), {
957
- timeoutMs: remainingTimeoutMs(),
958
- setTimeoutFn: setTimeoutImpl,
959
- clearTimeoutFn: clearTimeoutImpl,
960
- signal: abortSignal,
961
- });
962
- // A session the dispatch stops early is aborted on the server too.
963
- if (prompted === SDK_OPERATION_ABORTED || prompted === SDK_OPERATION_TIMED_OUT) {
964
- adapter.abort(client, sessionId, spec);
965
- }
966
- if (prompted === SDK_OPERATION_ABORTED && retryFailure) {
967
- result = {
968
- ok: false,
969
- stdout: "",
970
- stderr: retryFailure,
971
- durationMs: Date.now() - start,
972
- exitCode: 1,
973
- reason: "llm_rate_limit",
974
- error: retryFailure,
975
- sessionId,
976
- };
977
- }
978
- else if (prompted === SDK_OPERATION_ABORTED) {
979
- result = {
980
- ok: false,
981
- stdout: "",
982
- stderr: "",
983
- durationMs: Date.now() - start,
984
- exitCode: null,
985
- reason: "aborted",
986
- error: `opencode-sdk agent "${profile.name}" aborted by caller signal`,
987
- sessionId,
988
- };
989
- }
990
- else if (prompted === SDK_OPERATION_TIMED_OUT) {
991
- result = {
992
- ok: false,
993
- stdout: "",
994
- stderr: "",
995
- durationMs: Date.now() - start,
996
- exitCode: null,
997
- reason: "timeout",
998
- error: `opencode-sdk agent "${profile.name}" timed out after ${timeoutMs}ms`,
999
- sessionId,
1000
- };
1001
- }
1002
- else {
1003
- const { text: stdout, usage, failure } = prompted;
1004
- if (failure) {
1005
- result = {
1006
- ok: false,
1007
- stdout,
1008
- stderr: failure.message,
1009
- durationMs: Date.now() - start,
1010
- exitCode: failure.reason === "aborted" ? null : 1,
1011
- reason: failure.reason,
1012
- error: failure.message,
1013
- sessionId,
1014
- ...(usage ? { usage } : {}),
1015
- };
1016
- }
1017
- else {
1018
- result = {
1019
- ok: true,
1020
- stdout,
1021
- stderr: "",
1022
- durationMs: Date.now() - start,
1023
- exitCode: 0,
1024
- sessionId,
1025
- ...(usage ? { usage } : {}),
1026
- };
1027
- }
1028
- }
1029
- }
1030
- catch (err) {
1031
- result = {
1032
- ok: false,
1033
- stdout: "",
1034
- stderr: errorText(err),
1035
- durationMs: Date.now() - start,
1036
- exitCode: 1,
1037
- reason: "non_zero_exit",
1038
- error: errorText(err),
1039
- sessionId,
1040
- };
1041
- }
1042
- finally {
1043
- permissions.stop();
1044
- callerSignal?.removeEventListener("abort", abortForCaller);
1045
- // The dispatch is over: cancel any request still in flight.
1046
- runController.abort();
1047
- }
1048
- if (permissionNotes.length > 0) {
1049
- result.stderr = [result.stderr, ...permissionNotes].filter(Boolean).join("\n");
1050
- }
1051
- return result;
1052
- }