akm-cli 0.10.261013-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 (477) hide show
  1. package/CHANGELOG.md +494 -3
  2. package/README.md +15 -0
  3. package/SECURITY.md +9 -8
  4. package/STABILITY.md +45 -49
  5. package/dist/assets/hints/cli-hints-full.md +15 -12
  6. package/dist/assets/opencode/akm-plugin.js +177 -0
  7. package/dist/assets/stash-skeleton/README.md +1 -1
  8. package/dist/assets/tasks/core/improve.yml +1 -1
  9. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +1 -1
  10. package/dist/assets/ui/config.js +27 -10
  11. package/dist/assets/workflows/workflow-template.md +2 -2
  12. package/dist/cli/clack.js +3 -1
  13. package/dist/cli/selection.js +33 -0
  14. package/dist/cli/shared.js +14 -1
  15. package/dist/cli/unknown-flags.js +6 -37
  16. package/dist/cli.js +69 -48
  17. package/dist/commands/agent/agent-dispatch.js +17 -25
  18. package/dist/commands/agent/contribute-cli.js +9 -8
  19. package/dist/commands/command/command-cli.js +4 -8
  20. package/dist/commands/command/command-execution.js +25 -39
  21. package/dist/commands/completions.js +1 -1
  22. package/dist/commands/config-cli.js +14 -5
  23. package/dist/commands/env/child-env.js +12 -14
  24. package/dist/commands/env/env-cli.js +2 -2
  25. package/dist/commands/env/secret-cli.js +2 -2
  26. package/dist/commands/health/checks.js +10 -849
  27. package/dist/commands/health/connections-and-harnesses-check.js +109 -0
  28. package/dist/commands/health/egress.js +3 -3
  29. package/dist/commands/health/html-report.js +9 -10
  30. package/dist/commands/health/plugin-staleness.js +18 -11
  31. package/dist/commands/health/report-view-model.js +16 -12
  32. package/dist/commands/health/semantic-search-check.js +85 -0
  33. package/dist/commands/health/type-directory-check.js +0 -3
  34. package/dist/commands/health.js +30 -42
  35. package/dist/commands/improve/consolidate/pair-pass.js +10 -25
  36. package/dist/commands/improve/consolidate.js +10 -15
  37. package/dist/commands/improve/distill.js +5 -15
  38. package/dist/commands/improve/eligibility.js +1 -1
  39. package/dist/commands/improve/execution.js +37 -40
  40. package/dist/commands/improve/extract-cli.js +6 -11
  41. package/dist/commands/improve/extract-prompt.js +2 -2
  42. package/dist/commands/improve/extract.js +67 -97
  43. package/dist/commands/improve/improve-cli.js +22 -128
  44. package/dist/commands/improve/improve-report.js +1 -1
  45. package/dist/commands/improve/improve-strategies.js +25 -108
  46. package/dist/commands/improve/improve.js +10 -14
  47. package/dist/commands/improve/loop-stages.js +3 -0
  48. package/dist/commands/improve/memory/memory-improve.js +1 -1
  49. package/dist/commands/improve/preparation.js +8 -13
  50. package/dist/commands/improve/reflect.js +203 -332
  51. package/dist/commands/improve/retrieval-scope.js +1 -1
  52. package/dist/commands/improve/session-asset.js +3 -2
  53. package/dist/commands/improve/stage.js +87 -115
  54. package/dist/commands/lint/base-linter.js +3 -3
  55. package/dist/commands/lint/index.js +2 -2
  56. package/dist/commands/models-cli.js +103 -63
  57. package/dist/commands/proposal/drain.js +26 -39
  58. package/dist/commands/proposal/proposal-cli.js +14 -18
  59. package/dist/commands/proposal/propose-cli.js +4 -2
  60. package/dist/commands/proposal/propose.js +37 -64
  61. package/dist/commands/proposal/repository.js +17 -17
  62. package/dist/commands/proposal/validators/proposal-validators.js +2 -2
  63. package/dist/commands/read/curate.js +10 -6
  64. package/dist/commands/read/registry-search.js +2 -48
  65. package/dist/commands/read/search-cli.js +1 -8
  66. package/dist/commands/read/search.js +5 -6
  67. package/dist/commands/read/show.js +2 -4
  68. package/dist/commands/remember.js +25 -40
  69. package/dist/commands/sources/bundle-config-ops.js +2 -2
  70. package/dist/commands/sources/info.js +12 -1
  71. package/dist/commands/sources/init.js +1 -1
  72. package/dist/commands/sources/installed-stashes.js +2 -2
  73. package/dist/commands/sources/migration-help.js +1 -1
  74. package/dist/commands/sources/plugin-upgrade.js +8 -15
  75. package/dist/commands/sources/schema-repair.js +4 -8
  76. package/dist/commands/sources/source-add.js +2 -2
  77. package/dist/commands/sources/source-manage.js +4 -4
  78. package/dist/commands/targets.js +175 -0
  79. package/dist/commands/tasks/explain.js +103 -68
  80. package/dist/commands/tasks/tasks-cli.js +9 -11
  81. package/dist/commands/tasks/tasks.js +37 -41
  82. package/dist/commands/ui/api.js +11 -3
  83. package/dist/commands/ui/redact.js +1 -27
  84. package/dist/commands/ui/workspace-actions.js +1 -1
  85. package/dist/commands/workflow/plan.js +1 -1
  86. package/dist/commands/workflow-cli.js +3 -4
  87. package/dist/core/adapter/adapters/agent-skills-adapter.js +10 -11
  88. package/dist/core/adapter/adapters/akm-adapter.js +33 -38
  89. package/dist/core/adapter/adapters/akm-lint.js +7 -8
  90. package/dist/core/adapter/adapters/akm-task-adapter.js +3 -3
  91. package/dist/core/adapter/adapters/akm-workflow-adapter.js +5 -5
  92. package/dist/core/adapter/adapters/claude-adapter.js +1 -1
  93. package/dist/core/adapter/adapters/dotenv-adapter.js +3 -3
  94. package/dist/core/adapter/adapters/generic-files-adapter.js +5 -5
  95. package/dist/core/adapter/adapters/index.js +3 -4
  96. package/dist/core/adapter/adapters/llm-wiki-adapter.js +12 -14
  97. package/dist/core/adapter/adapters/okf-adapter.js +8 -10
  98. package/dist/core/adapter/adapters/opencode-adapter.js +1 -1
  99. package/dist/core/adapter/adapters/shared.js +3 -3
  100. package/dist/core/adapter/adapters/tool-dir-shared.js +7 -25
  101. package/dist/core/adapter/adapters/website-snapshot-adapter.js +3 -3
  102. package/dist/core/adapter/detect-adapter.js +1 -1
  103. package/dist/core/adapter/execution-source.js +13 -35
  104. package/dist/core/adapter/registry.js +1 -1
  105. package/dist/core/adapter/validate-context.js +2 -4
  106. package/dist/core/asset/asset-placement.js +1 -1
  107. package/dist/core/asset/asset-ref.js +3 -3
  108. package/dist/core/closest-match.js +37 -0
  109. package/dist/core/common.js +33 -5
  110. package/dist/core/config/config-io.js +26 -18
  111. package/dist/core/config/config-schema.js +19 -106
  112. package/dist/core/config/config-sources.js +3 -3
  113. package/dist/core/config/config-types.js +4 -4
  114. package/dist/core/config/config-walker.js +50 -77
  115. package/dist/core/config/config.js +385 -93
  116. package/dist/core/config/convert-engine-references.js +114 -0
  117. package/dist/core/config/convert-engines-shared.js +40 -0
  118. package/dist/core/config/convert-engines.js +300 -0
  119. package/dist/core/config/convert-models-overlay.js +137 -0
  120. package/dist/core/config/credentials.js +304 -0
  121. package/dist/core/config/engine-semantics.js +3 -3
  122. package/dist/core/config/migrate-keys.js +222 -0
  123. package/dist/core/config/model-aliases.js +37 -0
  124. package/dist/core/config/models-file.js +34 -0
  125. package/dist/core/config/retired-engines.js +53 -0
  126. package/dist/core/config/schema/connections.js +41 -0
  127. package/dist/core/config/schema/embedding.js +2 -2
  128. package/dist/core/config/schema/harnesses.js +41 -0
  129. package/dist/core/config/schema/improve-processes.js +9 -28
  130. package/dist/core/config/schema/improve.js +36 -1
  131. package/dist/core/config/schema/index-config.js +18 -20
  132. package/dist/core/config/schema/primitives.js +41 -22
  133. package/dist/core/config/schema/search.js +6 -11
  134. package/dist/core/config/schema/sources-bundles.js +7 -7
  135. package/dist/core/config/schema/workflow.js +2 -6
  136. package/dist/core/config/unusable-entries.js +66 -0
  137. package/dist/core/content-safety.js +3 -6
  138. package/dist/core/endpoint-url.js +47 -0
  139. package/dist/core/errors.js +9 -15
  140. package/dist/core/extra-params.js +3 -3
  141. package/dist/core/improve-result.js +8 -9
  142. package/dist/core/json-schema.js +1 -1
  143. package/dist/core/lesson-lint.js +1 -1
  144. package/dist/core/llm-inference.js +80 -0
  145. package/dist/core/parse.js +2 -2
  146. package/dist/core/paths.js +32 -4
  147. package/dist/core/recognition-util.js +8 -10
  148. package/dist/core/redaction.js +78 -52
  149. package/dist/core/state/migrations.js +17 -22
  150. package/dist/core/structured.js +2 -68
  151. package/dist/core/subprocess.js +63 -22
  152. package/dist/core/type-presentation.js +10 -14
  153. package/dist/execution/input-contract.js +3 -6
  154. package/dist/execution/notices.js +49 -0
  155. package/dist/execution/resolved-request.js +24 -40
  156. package/dist/execution/source.js +21 -45
  157. package/dist/execution/target-ref.js +2 -2
  158. package/dist/indexer/bundle-identity-guard.js +6 -5
  159. package/dist/indexer/ensure-index.js +1 -1
  160. package/dist/indexer/index-writer-lock.js +1 -1
  161. package/dist/indexer/indexer.js +19 -6
  162. package/dist/indexer/installations.js +11 -14
  163. package/dist/indexer/materialize-embeddings.js +20 -16
  164. package/dist/indexer/passes/memory-inference.js +21 -50
  165. package/dist/indexer/passes/metadata.js +5 -5
  166. package/dist/indexer/scan/doc-to-entry.js +1 -1
  167. package/dist/indexer/scan/drain-dir.js +6 -8
  168. package/dist/indexer/search/db-search.js +9 -7
  169. package/dist/indexer/search/search-source.js +40 -22
  170. package/dist/indexer/usage/usage-events.js +2 -3
  171. package/dist/indexer/walk/index-context.js +2 -1
  172. package/dist/indexer/walk/matchers.js +3 -11
  173. package/dist/indexer/walk/walker.js +14 -4
  174. package/dist/integrations/agent/akm-read-tools.js +81 -0
  175. package/dist/integrations/agent/config.js +1 -1
  176. package/dist/integrations/agent/detect.js +32 -36
  177. package/dist/integrations/agent/execution.js +130 -428
  178. package/dist/integrations/agent/index.js +1 -1
  179. package/dist/integrations/agent/profiles.js +24 -28
  180. package/dist/integrations/agent/prompts.js +17 -30
  181. package/dist/integrations/agent/request-lowering.js +67 -101
  182. package/dist/integrations/agent/resolution.js +332 -0
  183. package/dist/integrations/agent/runner-dispatch.js +154 -178
  184. package/dist/integrations/agent/runner.js +20 -72
  185. package/dist/integrations/agent/runtime-port.js +4 -0
  186. package/dist/integrations/agent/runtime.js +10 -0
  187. package/dist/integrations/agent/spawn.js +65 -190
  188. package/dist/integrations/harnesses/aider/index.js +19 -39
  189. package/dist/integrations/harnesses/amazonq/index.js +17 -32
  190. package/dist/integrations/harnesses/claude/index.js +59 -28
  191. package/dist/integrations/harnesses/claude/session-log.js +7 -5
  192. package/dist/integrations/harnesses/codex/index.js +19 -31
  193. package/dist/integrations/harnesses/copilot/index.js +17 -35
  194. package/dist/integrations/harnesses/gemini/index.js +18 -34
  195. package/dist/integrations/harnesses/harness.js +36 -0
  196. package/dist/integrations/harnesses/index.js +20 -51
  197. package/dist/integrations/harnesses/opencode/agent-builder.js +132 -100
  198. package/dist/integrations/harnesses/opencode/index.js +11 -33
  199. package/dist/integrations/harnesses/opencode/roots.js +50 -0
  200. package/dist/integrations/harnesses/opencode/runtime/binary.js +206 -0
  201. package/dist/integrations/harnesses/opencode/runtime/command.js +152 -0
  202. package/dist/integrations/harnesses/opencode/runtime/index.js +177 -0
  203. package/dist/integrations/harnesses/opencode/runtime/tar.js +107 -0
  204. package/dist/integrations/harnesses/opencode/session-log-v1.js +15 -7
  205. package/dist/integrations/harnesses/opencode/session-log-v2.js +72 -48
  206. package/dist/integrations/harnesses/opencode/session-log.js +58 -137
  207. package/dist/integrations/harnesses/opencode/version.js +34 -53
  208. package/dist/integrations/harnesses/openhands/index.js +13 -37
  209. package/dist/integrations/harnesses/pi/index.js +21 -32
  210. package/dist/integrations/lockfile.js +1 -1
  211. package/dist/integrations/session-logs/index.js +11 -31
  212. package/dist/llm/client.js +93 -111
  213. package/dist/llm/embedder.js +19 -34
  214. package/dist/llm/embedders/connection.js +31 -0
  215. package/dist/llm/embedders/local-model-files.js +56 -0
  216. package/dist/llm/embedders/local.js +146 -115
  217. package/dist/llm/embedders/remote.js +26 -24
  218. package/dist/llm/embedders/types.js +1 -1
  219. package/dist/llm/index-passes.js +33 -39
  220. package/dist/llm/memory-infer.js +67 -77
  221. package/dist/llm/model-list.js +35 -0
  222. package/dist/llm/rerank-client.js +8 -14
  223. package/dist/llm/structured-call.js +45 -99
  224. package/dist/llm/structured.js +60 -0
  225. package/dist/output/renderers.js +2 -7
  226. package/dist/output/shapes/helpers.js +18 -42
  227. package/dist/output/shapes/passthrough.js +1 -0
  228. package/dist/output/shapes.js +1 -7
  229. package/dist/output/text/command-format.js +29 -37
  230. package/dist/output/text/health-format.js +8 -2
  231. package/dist/output/text/helpers.js +1 -1
  232. package/dist/output/text/migrate.js +12 -0
  233. package/dist/output/text/models.js +3 -2
  234. package/dist/output/text/registry-commands.js +1 -2
  235. package/dist/output/text/show-format.js +0 -2
  236. package/dist/output/text/targets-table.js +70 -0
  237. package/dist/output/text/workflow-format.js +7 -8
  238. package/dist/output/text.js +1 -1
  239. package/dist/registry/providers/skills-sh.js +1 -28
  240. package/dist/registry/providers/static-index.js +1 -71
  241. package/dist/runtime.js +6 -2
  242. package/dist/scripts/akm-migrate-node.js +5280 -4263
  243. package/dist/scripts/akm-migrate.js +5280 -4263
  244. package/dist/setup/detect.js +28 -273
  245. package/dist/setup/endpoint.js +36 -0
  246. package/dist/setup/semantic-assets.js +13 -19
  247. package/dist/setup/setup-flags.js +77 -0
  248. package/dist/setup/setup-plan.js +144 -0
  249. package/dist/setup/setup.js +134 -358
  250. package/dist/setup/steps/connection-shared.js +49 -101
  251. package/dist/setup/steps/connection.js +121 -524
  252. package/dist/setup/steps/embeddings.js +151 -0
  253. package/dist/setup/steps/harnesses.js +118 -0
  254. package/dist/setup/steps/platforms.js +2 -81
  255. package/dist/setup/steps.js +10 -0
  256. package/dist/setup/summary.js +67 -0
  257. package/dist/sources/freshness.js +16 -21
  258. package/dist/sources/providers/git-provider.js +2 -9
  259. package/dist/sources/providers/provider-utils.js +1 -5
  260. package/dist/sources/snapshot-fetchers/robots.js +11 -12
  261. package/dist/sources/snapshot-fetchers/website-ingest.js +8 -15
  262. package/dist/standalone/onnxruntime-library.js +52 -0
  263. package/dist/storage/repositories/improve-ledger-repository.js +1 -1
  264. package/dist/storage/repositories/improve-runs-repository.js +0 -13
  265. package/dist/storage/repositories/index-entries-repository.js +2 -2
  266. package/dist/storage/repositories/workflow-runs-repository.js +8 -15
  267. package/dist/storage/sqlite-read-snapshot.js +33 -29
  268. package/dist/tasks/log-redaction.js +7 -119
  269. package/dist/tasks/prepare/prepare-support.js +5 -5
  270. package/dist/tasks/prepare/prepare.js +5 -5
  271. package/dist/tasks/prepare/script-capture.js +3 -5
  272. package/dist/tasks/run/attempt-lifecycle.js +17 -35
  273. package/dist/tasks/run/load-task.js +8 -13
  274. package/dist/tasks/run/run-command-task.js +15 -10
  275. package/dist/tasks/run/run-native-task.js +8 -13
  276. package/dist/tasks/run/run-task.js +5 -8
  277. package/dist/tasks/run/run-workflow-task.js +12 -19
  278. package/dist/tasks/run/task-history.js +9 -17
  279. package/dist/tasks/run/task-log.js +1 -5
  280. package/dist/tasks/scheduler-invocation.js +13 -18
  281. package/dist/tasks/source/bounded-document.js +31 -76
  282. package/dist/tasks/source/project-v4.js +5 -3
  283. package/dist/tasks/source/task-input-diagnostics.js +3 -4
  284. package/dist/tasks/source/task-source-v4.js +47 -68
  285. package/dist/tasks/source-v3.js +2 -5
  286. package/dist/tasks/task-run-reserved-flags.js +1 -2
  287. package/dist/workflows/authoring/authoring.js +2 -7
  288. package/dist/workflows/concurrency-policy.js +4 -4
  289. package/dist/workflows/exec/child-workflow.js +0 -2
  290. package/dist/workflows/exec/dispatch-redaction.js +14 -83
  291. package/dist/workflows/exec/environment.js +2 -3
  292. package/dist/workflows/exec/exec-unit.js +10 -59
  293. package/dist/workflows/exec/frozen-judge.js +10 -26
  294. package/dist/workflows/exec/native-executor.js +56 -45
  295. package/dist/workflows/exec/run-workflow.js +7 -24
  296. package/dist/workflows/exec/step-work.js +9 -14
  297. package/dist/workflows/exec/unit-dispatch.js +6 -13
  298. package/dist/workflows/freeze/freeze.js +14 -32
  299. package/dist/workflows/freeze/step-values.js +5 -11
  300. package/dist/workflows/freeze/targets/command.js +3 -6
  301. package/dist/workflows/freeze/task-bindings.js +0 -1
  302. package/dist/workflows/parser.js +44 -34
  303. package/dist/workflows/plan.js +3 -1
  304. package/dist/workflows/renderer.js +6 -5
  305. package/dist/workflows/resource-limits.js +2 -2
  306. package/dist/workflows/runtime/run-plan.js +2 -1
  307. package/dist/workflows/runtime/runs.js +14 -9
  308. package/docs/README.md +10 -16
  309. package/docs/architecture/README.md +2 -37
  310. package/docs/architecture/adapters.md +2 -2
  311. package/docs/architecture/akm-core-principles.md +256 -118
  312. package/docs/architecture/architecture.md +282 -98
  313. package/docs/architecture/improvement.md +8 -2
  314. package/docs/architecture/internals/classification.md +1 -3
  315. package/docs/architecture/internals/fresh-host-rebuild-runbook.md +7 -6
  316. package/docs/architecture/internals/health-advisories.md +35 -74
  317. package/docs/architecture/internals/improve-workflow.md +27 -26
  318. package/docs/architecture/internals/indexing.md +3 -3
  319. package/docs/architecture/internals/storage-locations.md +45 -12
  320. package/docs/architecture/persisted-data-compat.md +9 -6
  321. package/docs/architecture/specs/okf-support.md +9 -11
  322. package/docs/architecture/specs/ref.md +7 -9
  323. package/docs/architecture/testing/testing-workflow.md +164 -42
  324. package/docs/architecture/workflow-engine.md +17 -14
  325. package/docs/guides/README.md +0 -1
  326. package/docs/guides/author-bundles.md +1 -3
  327. package/docs/guides/author-workflows.md +61 -73
  328. package/docs/guides/bundles.md +4 -5
  329. package/docs/guides/discover-and-load.md +1 -1
  330. package/docs/guides/getting-started.md +49 -3
  331. package/docs/guides/improve-the-library.md +10 -4
  332. package/docs/guides/recipes/headless-install.md +11 -16
  333. package/docs/guides/run-workflows.md +6 -9
  334. package/docs/guides/scheduling.md +2 -2
  335. package/docs/guides/use-with-any-agent.md +20 -22
  336. package/docs/integration/bundling-akm.md +19 -9
  337. package/docs/maintainers/README.md +0 -3
  338. package/docs/migration/README.md +10 -3
  339. package/docs/migration/release-notes/0.10.0.md +81 -16
  340. package/docs/migration/release-notes/0.6.0.md +1 -1
  341. package/docs/migration/release-notes/0.7.0.md +1 -1
  342. package/docs/migration/release-notes/0.9.0.md +1 -1
  343. package/docs/migration/release-notes/README.md +1 -1
  344. package/docs/migration/v0.9-to-v0.10.md +295 -59
  345. package/docs/migration/v0.9.1-to-v0.9.2.md +6 -5
  346. package/docs/posts/config-v2-lessons-16.md +1 -1
  347. package/docs/posts/llm-mode-reflect-13.md +1 -1
  348. package/docs/posts/release-0.6.0.md +11 -11
  349. package/docs/posts/release-0.8.0.md +4 -4
  350. package/docs/posts/workflows-vaults-09.md +1 -1
  351. package/docs/reference/README.md +1 -1
  352. package/docs/reference/asset-types.md +2 -2
  353. package/docs/reference/cli.md +356 -279
  354. package/docs/reference/configuration.md +569 -557
  355. package/docs/reference/data-and-telemetry.md +8 -5
  356. package/docs/reference/env-and-secrets.md +11 -5
  357. package/docs/reference/refs.md +3 -20
  358. package/docs/reference/registry.md +6 -27
  359. package/docs/reference/supported-formats.md +1 -1
  360. package/docs/reference/tasks.md +11 -8
  361. package/docs/reference/ui-api.md +11 -6
  362. package/docs/reference/workflow-schema.md +106 -134
  363. package/docs/reference/workflows.md +4 -6
  364. package/package.json +9 -11
  365. package/schemas/akm-config.json +1696 -2922
  366. package/schemas/akm-task.json +2 -0
  367. package/schemas/akm-workflow.json +36 -9
  368. package/ROADMAP.md +0 -112
  369. package/dist/assets/models.json +0 -35
  370. package/dist/commands/health/engine-usage.js +0 -45
  371. package/dist/core/config/schema/engines.js +0 -160
  372. package/dist/core/config/schema/execution.js +0 -23
  373. package/dist/core/spawn-env.js +0 -268
  374. package/dist/integrations/agent/builder-shared.js +0 -33
  375. package/dist/integrations/agent/builders.js +0 -43
  376. package/dist/integrations/agent/engine-fallback.js +0 -59
  377. package/dist/integrations/agent/engine-resolution.js +0 -335
  378. package/dist/integrations/agent/model-map.js +0 -475
  379. package/dist/integrations/harnesses/aider/agent-builder.js +0 -107
  380. package/dist/integrations/harnesses/aider/result-extractor.js +0 -53
  381. package/dist/integrations/harnesses/amazonq/agent-builder.js +0 -138
  382. package/dist/integrations/harnesses/amazonq/result-extractor.js +0 -48
  383. package/dist/integrations/harnesses/claude/agent-builder.js +0 -106
  384. package/dist/integrations/harnesses/claude/config-import.js +0 -67
  385. package/dist/integrations/harnesses/claude/result-extractor.js +0 -67
  386. package/dist/integrations/harnesses/codex/agent-builder.js +0 -150
  387. package/dist/integrations/harnesses/codex/result-extractor.js +0 -110
  388. package/dist/integrations/harnesses/copilot/agent-builder.js +0 -117
  389. package/dist/integrations/harnesses/copilot/result-extractor.js +0 -178
  390. package/dist/integrations/harnesses/gemini/agent-builder.js +0 -114
  391. package/dist/integrations/harnesses/gemini/result-extractor.js +0 -149
  392. package/dist/integrations/harnesses/opencode/config-import.js +0 -149
  393. package/dist/integrations/harnesses/opencode/model-config.js +0 -33
  394. package/dist/integrations/harnesses/opencode/model-work-agent.js +0 -115
  395. package/dist/integrations/harnesses/opencode/session-log-types.js +0 -12
  396. package/dist/integrations/harnesses/opencode-sdk/index.js +0 -44
  397. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +0 -1052
  398. package/dist/integrations/harnesses/opencode-sdk/v1-adapter.js +0 -199
  399. package/dist/integrations/harnesses/opencode-sdk/v2-adapter.js +0 -254
  400. package/dist/integrations/harnesses/opencode-sdk/wire.js +0 -66
  401. package/dist/integrations/harnesses/openhands/agent-builder.js +0 -120
  402. package/dist/integrations/harnesses/openhands/result-extractor.js +0 -135
  403. package/dist/integrations/harnesses/pi/agent-builder.js +0 -89
  404. package/dist/integrations/harnesses/pi/result-extractor.js +0 -168
  405. package/dist/integrations/harnesses/shared.js +0 -18
  406. package/dist/integrations/harnesses/types.js +0 -55
  407. package/dist/llm/feature-gate.js +0 -115
  408. package/dist/output/shapes/models-list.js +0 -16
  409. package/dist/output/shapes/registry-search.js +0 -10
  410. package/dist/setup/detected-engines.js +0 -136
  411. package/dist/setup/engine-config.js +0 -97
  412. package/dist/setup/harness-config-import.js +0 -47
  413. package/dist/setup/providers.js +0 -14
  414. package/dist/setup/steps/semantic.js +0 -39
  415. package/dist/workflows/exec/lowering-notices.js +0 -23
  416. package/docs/architecture/0.10-process-rules.md +0 -832
  417. package/docs/architecture/akm-architecture-decision-history.md +0 -2055
  418. package/docs/architecture/comparisons/claude-code-vs-akm-workflows-full.md +0 -544
  419. package/docs/architecture/decisions/0001-no-interpolation-attached-structured-context.md +0 -72
  420. package/docs/architecture/decisions/0002-unit-reuse-and-input-hash-scope.md +0 -142
  421. package/docs/architecture/decisions/0003-child-env-allowlist-and-provenance.md +0 -187
  422. package/docs/architecture/decisions/0004-task-input-contract-and-flag-coercion.md +0 -97
  423. package/docs/architecture/decisions/0005-task-result-vocabulary-and-legacy-read-mapping.md +0 -108
  424. package/docs/architecture/decisions/0006-task-source-version-routing.md +0 -124
  425. package/docs/architecture/decisions/0007-workflow-composition-bounds.md +0 -83
  426. package/docs/architecture/decisions/0008-task-binding-normalization.md +0 -88
  427. package/docs/architecture/decisions/0009-child-run-publication-column-parity.md +0 -80
  428. package/docs/architecture/decisions/0010-driver-protocol-cut.md +0 -36
  429. package/docs/architecture/decisions/0011-engine-run-loop-invariants.md +0 -125
  430. package/docs/architecture/decisions/README.md +0 -62
  431. package/docs/architecture/internals/functional-contract-patterns.md +0 -340
  432. package/docs/architecture/internals/registry-network-boundary.md +0 -57
  433. package/docs/architecture/internals/search.md +0 -248
  434. package/docs/architecture/reviews/env-secret-access.md +0 -319
  435. package/docs/architecture/runtime-boundary-design.md +0 -153
  436. package/docs/architecture/specs/0.9.0-close-out-plan.md +0 -361
  437. package/docs/architecture/specs/0.9.0-decisions.md +0 -532
  438. package/docs/architecture/specs/0.9.0-docs-code-drift-register.md +0 -745
  439. package/docs/architecture/specs/0.9.0-open-items-register.md +0 -135
  440. package/docs/architecture/specs/0.9.0-public-api-issue-backlog.md +0 -740
  441. package/docs/architecture/specs/0.9.0-release-surface-review.md +0 -636
  442. package/docs/architecture/specs/agent-command-engine-model-design.md +0 -513
  443. package/docs/architecture/specs/akm-0.9.0-bundle-adapter-spec.md +0 -501
  444. package/docs/architecture/specs/akm-0.9.0-ref-grammar-decision.md +0 -291
  445. package/docs/architecture/specs/akm-format-neutral-bundle-workspace-spec.md +0 -2059
  446. package/docs/architecture/specs/di-seams-plan.md +0 -539
  447. package/docs/architecture/specs/driver-protocol-keep-or-cut.md +0 -275
  448. package/docs/architecture/specs/fact-asset-type.md +0 -142
  449. package/docs/architecture/specs/improve-collapse-churn-detector-design.md +0 -634
  450. package/docs/architecture/specs/reviews/task-workflow-unification/README.md +0 -72
  451. package/docs/architecture/specs/reviews/task-workflow-unification/findings-consolidated.md +0 -152
  452. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-alternatives.md +0 -352
  453. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-bunqueue.md +0 -300
  454. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-openworkflow.md +0 -402
  455. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-1.md +0 -758
  456. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-2.md +0 -645
  457. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-3.md +0 -602
  458. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-adversarial.md +0 -27
  459. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-architecture.md +0 -27
  460. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-coverage.md +0 -74
  461. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-security.md +0 -15
  462. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-adversarial.md +0 -132
  463. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-verification.md +0 -147
  464. package/docs/architecture/specs/stash-conventions-code-spec.md +0 -275
  465. package/docs/architecture/specs/stash-organization-conventions.md +0 -377
  466. package/docs/architecture/specs/task-workflow-format-unification.md +0 -721
  467. package/docs/architecture/specs/workflow-engine-buy-vs-build.md +0 -231
  468. package/docs/architecture/specs/workflow-format-unification.md +0 -432
  469. package/docs/architecture/testing/manual-testing-checklist.md +0 -3379
  470. package/docs/architecture/testing/okf-v0.2-conformance-runbook.md +0 -924
  471. package/docs/architecture/testing/task-workflow-manual-tests.md +0 -1702
  472. package/docs/guides/claude-code-vs-akm-workflows.md +0 -29
  473. package/docs/guides/knowledge-management.md +0 -18
  474. package/docs/maintainers/curate-workmap.md +0 -335
  475. package/docs/migration/v0.5-to-v0.6.md +0 -759
  476. package/docs/migration/v0.7-to-v0.8.md +0 -1378
  477. 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
- }