@desplega.ai/agent-swarm 1.158.0 → 1.160.0

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 (378) hide show
  1. package/README.md +1 -1
  2. package/dist/{acp-adapter-xaq4n6pc.js → acp-adapter-2mjwcsfh.js} +124 -27
  3. package/dist/{actions-p9hz5yat.js → actions-e43rxskr.js} +35 -30
  4. package/dist/{anthropic-messages-51pz26qe.js → anthropic-messages-5qx80j9k.js} +106 -16
  5. package/dist/{app-g26wdcz6.js → app-mc7xfagn.js} +12 -10
  6. package/dist/{assistant-wydfqkb3.js → assistant-sedkzv3b.js} +42 -31
  7. package/dist/{azure-openai-responses-8gksqtmz.js → azure-openai-responses-nfb21z9a.js} +13 -14
  8. package/dist/{boot-reembed-se8zmdcp.js → boot-reembed-cqz9swn5.js} +13 -12
  9. package/dist/{boot-reembed-g5ckg9yb.js → boot-reembed-m3656j2n.js} +14 -13
  10. package/dist/{boot-scrub-logs-wfxqan61.js → boot-scrub-logs-xbxf5d7w.js} +11 -9
  11. package/dist/boot-seeds-gsetbs5y.js +28 -0
  12. package/dist/{claude-adapter-tv2x4f36.js → claude-adapter-dc6289mc.js} +28 -23
  13. package/dist/{claude-managed-adapter-41b2s7fd.js → claude-managed-adapter-naaw51me.js} +4 -3
  14. package/dist/{claude-managed-setup-60g8aazx.js → claude-managed-setup-eacvchx0.js} +12 -3
  15. package/dist/claude-sdk-session-bqp2xvym.js +29722 -0
  16. package/dist/{cli-987hg0aa.js → cli-0jwe9034.js} +2 -2
  17. package/dist/{cli-vj6h9kc2.js → cli-0m7wct0d.js} +59 -1
  18. package/dist/{cli-c07r9a2m.js → cli-0q3avq3s.js} +12 -11
  19. package/dist/{cli-19jenss3.js → cli-0wgxmwf6.js} +31 -10
  20. package/dist/cli-0x01kwrd.js +491 -0
  21. package/dist/cli-0xqde53b.js +176 -0
  22. package/dist/cli-10nhj6xq.js +8355 -0
  23. package/dist/{cli-tt9k8r35.js → cli-12tszpk9.js} +479 -910
  24. package/dist/{cli-5b6b62tz.js → cli-13hw4z91.js} +1 -1
  25. package/dist/{cli-cqt19wg6.js → cli-1a2j50m0.js} +11 -2
  26. package/dist/{cli-xberxa1r.js → cli-25fcq4b4.js} +2 -2
  27. package/dist/{cli-npssxgaz.js → cli-2pdaysab.js} +52 -2
  28. package/dist/cli-3qhyh31j.js +166 -0
  29. package/dist/cli-447dt1h3.js +3828 -0
  30. package/dist/cli-4derpgyk.js +115 -0
  31. package/dist/{cli-00w2c66p.js → cli-523679eh.js} +13 -2
  32. package/dist/{cli-y95wp3tg.js → cli-58e8yr0d.js} +1 -1
  33. package/dist/{cli-24k806sc.js → cli-5nbmpy8w.js} +3 -3
  34. package/dist/{cli-53sz8gkz.js → cli-6cs6wzxt.js} +1 -1
  35. package/dist/cli-741yb0xx.js +1027 -0
  36. package/dist/cli-74h9hscv.js +69 -0
  37. package/dist/cli-7cxw8b60.js +14910 -0
  38. package/dist/{cli-xhaeg2nc.js → cli-7e398j1h.js} +1 -1
  39. package/dist/cli-7kyk9jr3.js +22 -0
  40. package/dist/cli-89s4ngw3.js +183 -0
  41. package/dist/{cli-9b13ggd1.js → cli-89v6kyq7.js} +1 -0
  42. package/dist/{cli-ftfvggzx.js → cli-9hsgwz4w.js} +1045 -581
  43. package/dist/{cli-rqz8qbw7.js → cli-a8dvhzrt.js} +59 -20
  44. package/dist/{cli-j57qkrpq.js → cli-azhgzetg.js} +395 -56
  45. package/dist/cli-br0qdrnp.js +2150 -0
  46. package/dist/{cli-rj19hc34.js → cli-btfv8y88.js} +1 -1
  47. package/dist/{cli-8qmme6vd.js → cli-c8a36vcc.js} +2 -2
  48. package/dist/cli-cpvsaqsw.js +333 -0
  49. package/dist/cli-d5czme6m.js +17805 -0
  50. package/dist/{cli-h38x0qhg.js → cli-et8m9xsv.js} +4 -3
  51. package/dist/{cli-x24pshtq.js → cli-evvtk35y.js} +6 -6
  52. package/dist/{cli-7s3syk22.js → cli-f8gy6r2r.js} +5 -5
  53. package/dist/{cli-zt1227k1.js → cli-fkqhbg8g.js} +5 -3
  54. package/dist/{cli-9httdpyq.js → cli-g2whhdrf.js} +1 -1
  55. package/dist/cli-gt4hxb8p.js +99 -0
  56. package/dist/{cli-s8eet254.js → cli-jajnsm8q.js} +37184 -53838
  57. package/dist/{cli-7gtvvpbh.js → cli-jayjpjmb.js} +21 -12
  58. package/dist/{cli-68w09sjw.js → cli-jyk0ynw2.js} +27 -89
  59. package/dist/{cli-aehm1rbq.js → cli-jz4w45qt.js} +1152 -61
  60. package/dist/cli-kmcpfwn2.js +333856 -0
  61. package/dist/{cli-4198qc9v.js → cli-ktmyrtpn.js} +19 -3
  62. package/dist/{cli-j55n64b6.js → cli-mfe5btp9.js} +505 -189
  63. package/dist/{cli-q67y4wzm.js → cli-phsaj4xq.js} +24 -2
  64. package/dist/{cli-1hkrch3k.js → cli-pm1rst0h.js} +46 -7
  65. package/dist/{cli-pw9rj5t9.js → cli-pqetb83f.js} +1 -176
  66. package/dist/{cli-ggm92wcn.js → cli-ptr8h7kp.js} +20 -1
  67. package/dist/{cli-s8s499xy.js → cli-qbksc292.js} +3 -2
  68. package/dist/{cli-47m6dkd3.js → cli-qydf22qq.js} +597 -346
  69. package/dist/{cli-3gmcwfyx.js → cli-r1mxd1xh.js} +13 -6
  70. package/dist/cli-rb9m6rpe.js +4980 -0
  71. package/dist/cli-rykfmgwg.js +102 -0
  72. package/dist/{cli-5d10vd2m.js → cli-shkxm1vn.js} +6 -5
  73. package/dist/{cli-eetrvv91.js → cli-t3p77qk1.js} +1 -1
  74. package/dist/cli-tfky2838.js +756 -0
  75. package/dist/{cli-g8djp6qe.js → cli-tmqt7dpj.js} +40 -0
  76. package/dist/{cli-fgqxwrpv.js → cli-tpq83z89.js} +1631 -127
  77. package/dist/cli-trc24wk2.js +143 -0
  78. package/dist/{cli-aky8tctm.js → cli-tzx2ke1v.js} +3 -3
  79. package/dist/cli-v7fn5gms.js +1425 -0
  80. package/dist/{cli-wscdvcqz.js → cli-vctdqs5h.js} +1 -1
  81. package/dist/{cli-cw9zck8j.js → cli-xjcywfsf.js} +8 -4
  82. package/dist/{cli-k0g1sjzm.js → cli-ybgv4kw2.js} +18 -11
  83. package/dist/{cli-rjehvrts.js → cli-ydmcgve1.js} +8 -8
  84. package/dist/{cli-vhjwh6cz.js → cli-ygwm91nt.js} +1 -1
  85. package/dist/{cli-mcnhgnn1.js → cli-ykk5jaax.js} +184 -26
  86. package/dist/{cli-de1396j2.js → cli-ywybs5as.js} +7 -1
  87. package/dist/cli-zpep9q5e.js +491 -0
  88. package/dist/cli.js +41 -32
  89. package/dist/cloudflare-workers-ai-system-one-k6fbxw9d.js +46 -0
  90. package/dist/{codex-adapter-cnm8wk11.js → codex-adapter-nmxcatww.js} +26 -17
  91. package/dist/{codex-hook-9hptmdhj.js → codex-hook-20me6byw.js} +2 -2
  92. package/dist/{codex-login-x3jk4etn.js → codex-login-w6fz4xbz.js} +50 -5
  93. package/dist/{codex-session-runner-gfg547zj.js → codex-session-runner-90aa80ev.js} +24 -17
  94. package/dist/{commands-e3bn0p75.js → commands-8wcq1kn7.js} +11 -9
  95. package/dist/{db-03v9kzr2.js → db-j40kc4se.js} +25 -11
  96. package/dist/{devin-adapter-8vgkwh29.js → devin-adapter-dpsv6hd9.js} +1 -1
  97. package/dist/{dispatcher-nrmg8yht.js → dispatcher-4zqy768d.js} +34 -29
  98. package/dist/{dsh-adapter-k03nfqa6.js → dsh-adapter-mpnzgztj.js} +3 -3
  99. package/dist/{e2b-93x1k0j5.js → e2b-ez07znam.js} +4 -3
  100. package/dist/execute-6e7qeyk4.js +1017 -0
  101. package/dist/{google-generative-ai-bxmv50nc.js → google-generative-ai-k6n5nbpp.js} +6 -5
  102. package/dist/{google-vertex-809af514.js → google-vertex-44xxtcvp.js} +6 -5
  103. package/dist/{handlers-aqcedbyb.js → handlers-kjdce1w5.js} +37 -32
  104. package/dist/{hook-ybvmrmhq.js → hook-czx13n12.js} +19 -15
  105. package/dist/{hook-7vkskkre.js → hook-r2t4bqfg.js} +18 -14
  106. package/dist/{http-2eqa8t24.js → http-3bbb19be.js} +900 -145
  107. package/dist/{index-f6cryxwe.js → index-4a5njnm1.js} +2 -59
  108. package/dist/{index-x71bvq0p.js → index-668me3f0.js} +35 -30
  109. package/dist/{index-c6zsv1bd.js → index-7aj368tw.js} +13 -12
  110. package/dist/{index-1yks3qcr.js → index-fg0dsp6c.js} +34 -29
  111. package/dist/{index-chgdkq36.js → index-kkv84jq9.js} +34 -29
  112. package/dist/{index-a3vn96n8.js → index-zg7vp46x.js} +198 -54
  113. package/dist/{keepalive-vtkf5n99.js → keepalive-yra8dy57.js} +13 -11
  114. package/dist/{lead-h3sp357x.js → lead-va0pad2c.js} +54 -49
  115. package/dist/llama-cpp-classify-tgj35k8d.js +335 -0
  116. package/dist/{maintenance-jzeqtk3q.js → maintenance-hqewtmyg.js} +14 -13
  117. package/dist/{mistral-conversations-zx0kn554.js → mistral-conversations-pc3ntnxt.js} +14 -17
  118. package/dist/{oauth-refresh-sweep-qp3dzmdp.js → oauth-refresh-sweep-7rdt1m9n.js} +12 -10
  119. package/dist/{onboard-tmsp0qbz.js → onboard-j6ms6d0x.js} +6 -5
  120. package/dist/{openai-codex-responses-4vjqzvjp.js → openai-codex-responses-bbancrr0.js} +21 -8
  121. package/dist/{openai-completions-9hjrqbw8.js → openai-completions-jpaj5g2b.js} +9 -12
  122. package/dist/{openai-responses-7b1pn0xt.js → openai-responses-200k1wx4.js} +23 -18
  123. package/dist/{opencode-adapter-x6x2da1z.js → opencode-adapter-89rabkw2.js} +8 -7
  124. package/dist/{openrouter-images-9f9ek4rw.js → openrouter-images-8yx3487g.js} +5 -7
  125. package/dist/{otel-impl-9mz73q65.js → otel-impl-a792rjz0.js} +1 -1
  126. package/dist/{pi-messages-txpjj39q.js → pi-messages-vxdt8v7j.js} +2 -1
  127. package/dist/{pi-mono-adapter-m6dvv0dz.js → pi-mono-adapter-g7qzvr3v.js} +295 -70
  128. package/dist/pricing-refresh-wx0tp7cb.js +39 -0
  129. package/dist/quickjs-r1pq2107.wasm +0 -0
  130. package/dist/{rbac-roles-c9wqz83x.js → rbac-roles-8tzawacv.js} +12 -10
  131. package/dist/{render-v2-gryrh9h8.js → render-v2-mm7zswb8.js} +12 -10
  132. package/dist/runtime-zyssgse0.js +24 -0
  133. package/dist/{setup-q9720bt6.js → setup-8y70ys6t.js} +6 -5
  134. package/dist/typesafe-system-one-2ef30pec.js +27 -0
  135. package/dist/{virtual-modules-3htpgn8x.js → virtual-modules-6nmk7h3n.js} +22 -13
  136. package/dist/{work-objects-b76x7deb.js → work-objects-fg6xkb0n.js} +37 -32
  137. package/dist/{worker-6vrxbv41.js → worker-wmwhxsph.js} +54 -49
  138. package/openapi.json +1314 -91
  139. package/package.json +12 -11
  140. package/src/be/db/agents.ts +20 -0
  141. package/src/be/db/api-keys.ts +70 -0
  142. package/src/be/db/tasks/human-free.ts +442 -0
  143. package/src/be/db/tasks/read.ts +56 -1
  144. package/src/be/db/tasks/write.ts +107 -11
  145. package/src/be/db.ts +274 -188
  146. package/src/be/harness-model-support.ts +182 -0
  147. package/src/be/human-free-drain.ts +67 -0
  148. package/src/be/memory/providers/sqlite-store.ts +51 -15
  149. package/src/be/migrations/182_task_human_free_flag.sql +63 -0
  150. package/src/be/migrations/183_human_free_reclassify_queue.sql +25 -0
  151. package/src/be/migrations/184_model_catalog.sql +120 -0
  152. package/src/be/migrations/185_api_key_auth_failures.sql +6 -0
  153. package/src/be/migrations/186_api_key_seat_mismatch.sql +8 -0
  154. package/src/be/model-catalog-store.ts +644 -0
  155. package/src/be/model-tier-keys.ts +46 -0
  156. package/src/be/model-tier-resolution.ts +534 -0
  157. package/src/be/model-validation.ts +188 -0
  158. package/src/be/models-catalog.ts +61 -7
  159. package/src/be/modelsdev-cache.json +82 -0
  160. package/src/be/modelsdev-cache.ts +2 -0
  161. package/src/be/pricing-refresh.ts +154 -12
  162. package/src/be/scripts/typecheck.ts +15 -0
  163. package/src/be/seed/runner.ts +18 -12
  164. package/src/be/seed/types.ts +1 -0
  165. package/src/be/seed-scripts/catalog/task-failure-audit.ts +61 -9
  166. package/src/be/swarm-config-guard.ts +8 -0
  167. package/src/cli.tsx +10 -0
  168. package/src/commands/claude-managed-setup.ts +3 -1
  169. package/src/commands/codex-login.ts +67 -0
  170. package/src/commands/credential-outcome-report.ts +191 -0
  171. package/src/commands/rate-limit-outcome.ts +34 -4
  172. package/src/commands/runner.ts +357 -186
  173. package/src/commands/seat-mismatch-report.ts +46 -0
  174. package/src/extensions/apply-task-create.ts +27 -1
  175. package/src/extensions/contract-types.generated.ts +1 -1
  176. package/src/heartbeat/heartbeat.ts +79 -17
  177. package/src/heartbeat/templates.ts +2 -2
  178. package/src/http/agents.ts +62 -1
  179. package/src/http/api-keys.ts +140 -2
  180. package/src/http/codex-oauth-keep-warm.ts +3 -5
  181. package/src/http/index.ts +8 -0
  182. package/src/http/memory.ts +12 -5
  183. package/src/http/models-catalog.ts +357 -8
  184. package/src/http/poll.ts +54 -3
  185. package/src/http/schedules.ts +51 -6
  186. package/src/http/script-runs.ts +12 -0
  187. package/src/http/session-data.ts +2 -5
  188. package/src/http/stats.ts +6 -8
  189. package/src/http/status.ts +15 -1
  190. package/src/http/tasks.ts +90 -7
  191. package/src/http/usage-cache.ts +56 -15
  192. package/src/http/workflows.ts +46 -8
  193. package/src/mcp-client/http-client.ts +5 -0
  194. package/src/memory/recall-query.ts +74 -0
  195. package/src/prompts/base-prompt.ts +15 -4
  196. package/src/prompts/session-templates.ts +23 -1
  197. package/src/providers/acp-adapter.ts +1 -0
  198. package/src/providers/claude-adapter.ts +14 -5
  199. package/src/providers/claude-managed-adapter.ts +1 -0
  200. package/src/providers/claude-managed-models.ts +47 -161
  201. package/src/providers/claude-sdk-session.ts +3 -1
  202. package/src/providers/codex-adapter.ts +14 -14
  203. package/src/providers/codex-models.ts +65 -198
  204. package/src/providers/devin-adapter.ts +1 -0
  205. package/src/providers/dsh-adapter.ts +2 -1
  206. package/src/providers/harness-version.ts +83 -7
  207. package/src/providers/opencode-adapter.ts +53 -1
  208. package/src/providers/pi-binary-root/src/extensions/codemode/worker.ts +11 -0
  209. package/src/providers/pi-codemode-runtime.ts +17 -0
  210. package/src/providers/pi-mono-adapter.ts +361 -83
  211. package/src/providers/pi-mono-extension.ts +16 -1
  212. package/src/providers/reasoning-effort.ts +44 -140
  213. package/src/providers/types.ts +8 -0
  214. package/src/rbac/legacy-policy.ts +43 -0
  215. package/src/rbac/permissions.ts +15 -0
  216. package/src/scripts-runtime/sdk-allowlist.ts +4 -0
  217. package/src/scripts-runtime/types/stdlib.d.ts +15 -0
  218. package/src/scripts-runtime/types/swarm-extension.d.ts +3 -0
  219. package/src/scripts-runtime/types/swarm-sdk.d.ts +15 -0
  220. package/src/server.ts +6 -0
  221. package/src/slack/assistant.ts +13 -0
  222. package/src/slack/message-text.ts +56 -2
  223. package/src/slack/render-v2.ts +87 -37
  224. package/src/slack/session-status.ts +541 -0
  225. package/src/slack/thread-buffer.ts +10 -2
  226. package/src/tasks/sibling-awareness.ts +3 -2
  227. package/src/telemetry.ts +5 -4
  228. package/src/templates.d.ts +6 -0
  229. package/src/tests/agents-list-model-display.test.ts +4 -6
  230. package/src/tests/api-key-auth-failure-route.test.ts +142 -0
  231. package/src/tests/api-key-tracking.test.ts +368 -1
  232. package/src/tests/base-prompt.test.ts +43 -2
  233. package/src/tests/claude-adapter.test.ts +68 -0
  234. package/src/tests/claude-managed-adapter.test.ts +12 -18
  235. package/src/tests/codex-adapter.test.ts +2 -1
  236. package/src/tests/codex-auth-failure.test.ts +52 -0
  237. package/src/tests/codex-gpt6-release.test.ts +66 -5
  238. package/src/tests/codex-login.test.ts +116 -0
  239. package/src/tests/context-window.test.ts +7 -5
  240. package/src/tests/credential-exhaustion-policy.test.ts +118 -3
  241. package/src/tests/credential-outcome-report.test.ts +171 -0
  242. package/src/tests/credential-status-api.test.ts +103 -2
  243. package/src/tests/db-extraction-contract.test.ts +1 -0
  244. package/src/tests/db-task-read-contract.test.ts +10 -1
  245. package/src/tests/db-task-write-contract.test.ts +535 -2
  246. package/src/tests/docs-index-footprint.test.js +3 -3
  247. package/src/tests/dokploy-deploy.test.ts +315 -0
  248. package/src/tests/dsh-adapter.test.ts +1 -1
  249. package/src/tests/explicit-model-validation.test.ts +583 -0
  250. package/src/tests/extensions-pre-task.test.ts +73 -0
  251. package/src/tests/fetch-resolved-env.test.ts +46 -0
  252. package/src/tests/hang-watchdog-worker.ts +47 -0
  253. package/src/tests/hang-watchdog.test.ts +45 -0
  254. package/src/tests/hang-watchdog.ts +30 -0
  255. package/src/tests/harness-model-guard.test.ts +123 -0
  256. package/src/tests/harness-model-support.test.ts +374 -0
  257. package/src/tests/harness-version.test.ts +70 -3
  258. package/src/tests/heartbeat-checklist-config.test.ts +71 -0
  259. package/src/tests/heartbeat-checklist.test.ts +4 -2
  260. package/src/tests/heartbeat-orphan-resume.test.ts +48 -0
  261. package/src/tests/http-task-write-ownership.test.ts +172 -0
  262. package/src/tests/human-free-flag.test.ts +279 -0
  263. package/src/tests/human-free-oracle.ts +57 -0
  264. package/src/tests/human-free-reclassify.test.ts +669 -0
  265. package/src/tests/memory-fts-populate.test.ts +139 -0
  266. package/src/tests/memory-http-recall-gating.test.ts +125 -1
  267. package/src/tests/migration-046-budgets.test.ts +3 -3
  268. package/src/tests/model-catalog-migration.test.ts +134 -0
  269. package/src/tests/model-catalog-table.test.ts +249 -0
  270. package/src/tests/model-catalog-write-access.test.ts +387 -0
  271. package/src/tests/model-rate-limit-windows.test.ts +19 -0
  272. package/src/tests/model-tier-resolution.test.ts +611 -0
  273. package/src/tests/models-catalog.test.ts +1 -1
  274. package/src/tests/multi-runtime-enabled-stats.test.ts +65 -1
  275. package/src/tests/opencode-adapter.test.ts +157 -2
  276. package/src/tests/package-publish.test.ts +137 -36
  277. package/src/tests/pi-codemode.test.ts +246 -0
  278. package/src/tests/pi-mono-adapter.test.ts +3 -0
  279. package/src/tests/pi-native-mcp.test.ts +193 -0
  280. package/src/tests/pi-tool-exposure.test.ts +374 -0
  281. package/src/tests/preload.ts +3 -0
  282. package/src/tests/pricing-routes.test.ts +2 -2
  283. package/src/tests/prompt-template-session.test.ts +5 -3
  284. package/src/tests/rate-limit-event.test.ts +65 -0
  285. package/src/tests/rate-limit-outcome.test.ts +90 -0
  286. package/src/tests/rbac-engine.test.ts +77 -3
  287. package/src/tests/reasoning-effort.test.ts +109 -0
  288. package/src/tests/refresh-modelsdev-carryforward.test.ts +5 -5
  289. package/src/tests/runner-key-completion-outcome.test.ts +112 -0
  290. package/src/tests/runner-key-outcome-late-reset.test.ts +224 -0
  291. package/src/tests/runner-registration-retry.test.ts +149 -0
  292. package/src/tests/runtime-model-catalog.test.ts +86 -0
  293. package/src/tests/script-connections.test.ts +9 -0
  294. package/src/tests/seat-mismatch-report.test.ts +85 -0
  295. package/src/tests/seed-scripts.test.ts +138 -0
  296. package/src/tests/seed.test.ts +25 -0
  297. package/src/tests/session-costs.test.ts +14 -11
  298. package/src/tests/slack-message-text.test.ts +101 -0
  299. package/src/tests/slack-render-v2.test.ts +96 -0
  300. package/src/tests/slack-session-status.test.ts +626 -0
  301. package/src/tests/slack-thread-buffer-context.test.ts +93 -0
  302. package/src/tests/status.test.ts +29 -0
  303. package/src/tests/steering-promotion.test.ts +51 -0
  304. package/src/tests/store-progress-ownership.test.ts +136 -0
  305. package/src/tests/subscription-plans.test.ts +28 -0
  306. package/src/tests/task-cascade-fail.test.ts +148 -2
  307. package/src/tests/task-citations.test.ts +3 -2
  308. package/src/tests/tool-annotations.test.ts +1 -1
  309. package/src/tests/usage-cache.test.ts +166 -0
  310. package/src/tests/workflow-agent-task.test.ts +30 -0
  311. package/src/tests/workflow-executors.test.ts +5 -2
  312. package/src/tests/workflow-system-one-decision-review.test.ts +1021 -0
  313. package/src/tests/workflow-system-one-decision-save-warnings.test.ts +365 -0
  314. package/src/tests/workflow-system-one-decision.test.ts +2063 -0
  315. package/src/tests/workflow-triggers-v2.test.ts +320 -0
  316. package/src/tools/model-catalog/index.ts +137 -0
  317. package/src/tools/schedules/create-schedule.ts +17 -2
  318. package/src/tools/schedules/patch-schedule.ts +31 -1
  319. package/src/tools/schedules/update-schedule.ts +31 -1
  320. package/src/tools/send-task.ts +18 -1
  321. package/src/tools/store-progress.ts +49 -4
  322. package/src/tools/task-action.ts +14 -1
  323. package/src/tools/tool-config.ts +3 -1
  324. package/src/tools/workflows/create-workflow.ts +17 -4
  325. package/src/tools/workflows/patch-workflow-node.ts +17 -7
  326. package/src/tools/workflows/patch-workflow.ts +9 -2
  327. package/src/tools/workflows/update-workflow.ts +17 -4
  328. package/src/types.ts +41 -1
  329. package/src/utils/codex-auth-failure.ts +34 -0
  330. package/src/utils/context-window.ts +47 -53
  331. package/src/utils/credentials.ts +72 -30
  332. package/src/utils/error-tracker.ts +80 -0
  333. package/src/utils/harness-cli-version.ts +115 -0
  334. package/src/utils/harness-model-error.ts +33 -0
  335. package/src/utils/model-rate-limit-windows.ts +12 -0
  336. package/src/utils/runtime-model-catalog.ts +152 -0
  337. package/src/utils/spawn-model-guard.ts +43 -0
  338. package/src/utils/subscription-plans.ts +22 -0
  339. package/src/workflows/approval-resolution.ts +39 -0
  340. package/src/workflows/definition.ts +7 -0
  341. package/src/workflows/engine.ts +69 -1
  342. package/src/workflows/executors/agent-task.ts +16 -6
  343. package/src/workflows/executors/base.ts +53 -0
  344. package/src/workflows/executors/human-in-the-loop.ts +8 -8
  345. package/src/workflows/executors/index.ts +1 -0
  346. package/src/workflows/executors/registry.ts +2 -0
  347. package/src/workflows/executors/system-one-decision.ts +1232 -0
  348. package/src/workflows/executors/system-one-providers.ts +301 -0
  349. package/src/workflows/executors/workflow-llm.ts +13 -1
  350. package/src/workflows/model-validation.ts +45 -0
  351. package/src/workflows/patch-definition.ts +5 -0
  352. package/src/workflows/readiness.ts +86 -0
  353. package/src/workflows/recovery.ts +12 -12
  354. package/src/workflows/resume.ts +23 -9
  355. package/src/workflows/triggers.ts +85 -0
  356. package/templates/schedules/daily-blocker-digest/content.md +4 -5
  357. package/templates/skills/agent-browser/SKILL.md +6 -5
  358. package/templates/skills/agent-browser/content.md +6 -5
  359. package/templates/skills/heartbeat-runbook/config.json +1 -1
  360. package/templates/skills/heartbeat-runbook/content.md +2 -2
  361. package/templates/skills/swarm-scripts/SKILL.md +1 -1
  362. package/templates/skills/swarm-scripts/content.md +1 -1
  363. package/templates/skills/workflow-iterate/content.md +82 -1
  364. package/templates/workflows/claude-code-changelog-watch/content.md +2 -0
  365. package/templates/workflows/pr-review-status-sweep/content.md +2 -0
  366. package/dist/boot-seeds-s3q01xj3.js +0 -26
  367. package/dist/claude-sdk-session-t442qz6f.js +0 -39108
  368. package/dist/cli-0xjkj1j7.js +0 -61
  369. package/dist/cli-20cj37vt.js +0 -335561
  370. package/dist/cli-2djtr51b.js +0 -48
  371. package/dist/cli-65hjmev5.js +0 -1141
  372. package/dist/cli-aw1jwp1r.js +0 -14075
  373. package/dist/cli-h967nb6r.js +0 -6603
  374. package/dist/cli-vapyk8sw.js +0 -20
  375. package/dist/cli-ws6rqycm.js +0 -247
  376. package/dist/pricing-refresh-v1cbr9te.js +0 -27
  377. package/src/providers/modelsdev-reasoning.json +0 -6453
  378. package/dist/{index-f24115ge.js → index-2wsdsnmm.js} +3 -3
@@ -1,139 +1,73 @@
1
1
  /**
2
- * Codex API-addressable models, verified from https://developers.openai.com/api/docs/models
3
- * and https://developers.openai.com/api/docs/deprecations as of 2026-09-23.
2
+ * Codex model facts, read from the model catalog (model-catalog phase 4).
4
3
  *
5
- * NOTE: `gpt-5.3-codex-spark` is intentionally excluded. It is a ChatGPT Pro
6
- * research preview and is NOT API-addressable via the Codex SDK at launch.
7
- * Including it here would cause runtime errors if selected via MODEL_OVERRIDE.
4
+ * There is no hand-maintained Codex allowlist, context-window table or price
5
+ * table any more: every lookup reads the runtime catalog
6
+ * (`src/utils/runtime-model-catalog.ts` — the API's `model_catalog` + overlay
7
+ * rows, pulled over HTTP; the vendored models.dev snapshot offline). A new
8
+ * OpenAI model is therefore listed, windowed and priced as soon as it lands
9
+ * in `model_catalog`, with no code change and no redeploy.
8
10
  *
9
- * Bump this file when the CLI / SDK adds new models. Kept separate from the
10
- * adapter so the onboarding UI and model selector can import it without
11
- * pulling in the SDK.
11
+ * Which OpenAI SKUs count as Codex-drivable is a rule, not a list:
12
+ * `isHarnessCatalogModel("codex", ...)` in `@desplega/model-catalog`.
13
+ *
14
+ * Kept separate from the adapter so the onboarding UI and model selector can
15
+ * import it without pulling in the SDK.
12
16
  */
13
- import modelsDevCache from "../be/modelsdev-cache.json";
17
+ import { harnessModelIds } from "@desplega/model-catalog";
18
+ import { DEFAULT_MODEL_TIER_MAP } from "../types";
19
+ import { runtimeCatalogModel, runtimeCatalogSection } from "../utils/runtime-model-catalog";
20
+
21
+ /** Codex models from the catalog, newest first. Drives selectors and pricing tests. */
22
+ export function listCodexModels(): string[] {
23
+ return harnessModelIds("codex", runtimeCatalogSection("openai"));
24
+ }
14
25
 
15
26
  /**
16
- * List of Codex models we know about (drives the onboarding model selector,
17
- * the pricing table, and the context-window map). The resolver does NOT
18
- * constrain inputs to this list — it passes unknown strings through to the
19
- * SDK, so new OpenAI models work without a code change.
27
+ * The baseline default when neither MODEL_OVERRIDE nor task.model is set:
28
+ * the codex `regular` tier default (operators move it with
29
+ * `MODEL_TIER_CODEX_REGULAR`, resolved server-side at claim time).
20
30
  */
21
- export const CODEX_MODELS = [
22
- "gpt-6-astra", // most capable model for complex reasoning, coding, and agentic work
23
- "gpt-6-sol", // complex coding and agentic workflows
24
- "gpt-6-luna", // focused, high-volume tasks
25
- "gpt-5.6-sol", // frontier GPT-5.6 tier for complex reasoning/coding
26
- "gpt-5.6-terra", // balanced GPT-5.6 tier
27
- "gpt-5.6-luna", // fast/cheap GPT-5.6 tier for high-volume workloads
28
- "gpt-5.5", // previous frontier coding/professional-work model
29
- "gpt-5.4", // previous mainline reasoning model w/ frontier coding
30
- "gpt-5.4-mini", // faster/cheaper
31
- "gpt-5.3-codex", // coding-specialized legacy model
32
- "gpt-5.2-codex", // legacy — scheduled for retirement, see openai deprecations page
33
- ] as const;
34
-
35
- export type CodexModel = (typeof CODEX_MODELS)[number];
36
-
37
- /** The baseline default when neither MODEL_OVERRIDE nor task.model is set. */
38
- export const CODEX_DEFAULT_MODEL: CodexModel = "gpt-5.6-terra";
31
+ export const CODEX_DEFAULT_MODEL: string = DEFAULT_MODEL_TIER_MAP.codex.regular;
39
32
 
40
33
  /**
41
- * Map claude-style shortnames (that flow through MODEL_OVERRIDE / task.model)
42
- * to Codex equivalents. Mirrors `pi-mono-adapter.ts:71-75` shortnames map so
43
- * a task authored for Claude works unchanged when pointed at a Codex worker.
34
+ * Claude-style shortnames (that flow through MODEL_OVERRIDE / task.model)
35
+ * map to the codex tier with the same intent, so a task authored for Claude
36
+ * works unchanged when pointed at a Codex worker.
44
37
  */
45
- const CLAUDE_SHORTNAMES: Record<string, CodexModel> = {
46
- fable: "gpt-5.6-sol",
47
- opus: "gpt-5.6-sol",
48
- sonnet: "gpt-5.6-terra",
49
- haiku: "gpt-5.6-luna",
50
- };
38
+ const CLAUDE_SHORTNAME_TIER = {
39
+ fable: "ultra",
40
+ opus: "smart",
41
+ sonnet: "regular",
42
+ haiku: "smol",
43
+ } as const;
51
44
 
52
45
  /**
53
46
  * Resolve a model string (shortname or full Codex model id) into the literal
54
47
  * id we hand to the Codex SDK. Behavior:
55
48
  * - empty/undefined → `CODEX_DEFAULT_MODEL`
56
- * - claude shortname (opus/sonnet/haiku) → mapped Codex id
49
+ * - claude shortname (opus/sonnet/haiku/fable) → codex tier default
57
50
  * - anything else → passthrough (lowercased), so new OpenAI models work
58
51
  * without a code change. The SDK is the source of truth for validity.
59
52
  */
60
53
  export function resolveCodexModel(modelStr: string | undefined): string {
61
54
  if (!modelStr) return CODEX_DEFAULT_MODEL;
62
55
  const normalized = modelStr.toLowerCase();
63
- return CLAUDE_SHORTNAMES[normalized] ?? normalized;
64
- }
65
-
66
- interface ModelsDevOpenAiModel {
67
- limit?: {
68
- context?: number;
69
- };
70
- cost?: {
71
- input?: number;
72
- cache_read?: number;
73
- output?: number;
74
- };
56
+ const tier = CLAUDE_SHORTNAME_TIER[normalized as keyof typeof CLAUDE_SHORTNAME_TIER];
57
+ return tier ? DEFAULT_MODEL_TIER_MAP.codex[tier] : normalized;
75
58
  }
76
59
 
77
- const MODELSDEV_OPENAI_MODELS =
78
- (
79
- modelsDevCache as {
80
- openai?: {
81
- models?: Record<string, ModelsDevOpenAiModel>;
82
- };
83
- }
84
- ).openai?.models ?? {};
85
-
86
- const FALLBACK_CODEX_MODEL_CONTEXT_WINDOWS: Record<CodexModel, number> = {
87
- "gpt-6-astra": 1_050_000,
88
- "gpt-6-sol": 1_050_000,
89
- "gpt-6-luna": 1_050_000,
90
- "gpt-5.6-sol": 1_050_000,
91
- "gpt-5.6-terra": 1_050_000,
92
- "gpt-5.6-luna": 1_050_000,
93
- "gpt-5.5": 1_050_000,
94
- "gpt-5.4": 1_050_000,
95
- "gpt-5.4-mini": 400_000,
96
- "gpt-5.3-codex": 400_000,
97
- "gpt-5.2-codex": 200_000,
98
- };
99
-
100
- /**
101
- * Per-model approximate context window (tokens). The Codex SDK does not
102
- * expose these at runtime, so we read the vendored models.dev cache used by
103
- * the pricing seeder and fall back only for legacy/incomplete cache entries.
104
- * The values are used by the `context_usage` percent calculation inside
105
- * `CodexSession`.
106
- */
107
- export const CODEX_MODEL_CONTEXT_WINDOWS: Record<CodexModel, number> = Object.fromEntries(
108
- CODEX_MODELS.map((model) => [
109
- model,
110
- MODELSDEV_OPENAI_MODELS[model]?.limit?.context ?? FALLBACK_CODEX_MODEL_CONTEXT_WINDOWS[model],
111
- ]),
112
- ) as Record<CodexModel, number>;
60
+ const UNKNOWN_CONTEXT_WINDOW = 200_000;
113
61
 
114
62
  /**
115
- * Return the context window in tokens for a given Codex model. Unknown models
116
- * (passthrough strings) get the 200k default — keeps `context_usage` finite
117
- * even on a model id we haven't catalogued yet.
63
+ * Context window in tokens for a Codex model, from the catalog. Unknown
64
+ * models (passthrough strings) get 200k — keeps `context_usage` finite.
118
65
  */
119
66
  export function getCodexContextWindow(model: string): number {
120
- return CODEX_MODEL_CONTEXT_WINDOWS[model as CodexModel] ?? 200_000;
67
+ const context = runtimeCatalogModel("openai", model)?.limit?.context;
68
+ return typeof context === "number" && context > 0 ? context : UNKNOWN_CONTEXT_WINDOW;
121
69
  }
122
70
 
123
- /**
124
- * Per-model pricing in USD per million tokens, sourced from the vendored
125
- * models.dev cache (`src/be/modelsdev-cache.json`). The fallback below mirrors
126
- * that snapshot for known models and covers legacy models that models.dev no
127
- * longer lists.
128
- *
129
- * The Codex SDK does NOT report dollar cost in `Usage`, so this map is what
130
- * powers `totalCostUsd` on the `result` event. Refresh models.dev whenever
131
- * OpenAI changes pricing or adds new models.
132
- *
133
- * `gpt-5.2-codex` is not on the current pricing page (legacy / retired); it
134
- * inherits the `gpt-5.3-codex` rate as a best-effort fallback so old tasks
135
- * pinned to it still report a non-zero cost instead of silently $0.
136
- */
137
71
  export interface CodexModelPricing {
138
72
  /** USD per million input tokens (uncached). */
139
73
  inputPerMillion: number;
@@ -144,99 +78,33 @@ export interface CodexModelPricing {
144
78
  }
145
79
 
146
80
  /**
147
- * Advisory only: the canonical price is the API server's recompute against
148
- * the runtime-refreshed pricing table. `agentswarm.cost.drift.usd` is the
149
- * watchdog for divergence between this worker-local fallback and that table.
150
- * Exported so tests can pin the fallback values directly — through
151
- * `computeCodexCostUsd` the models.dev snapshot always wins, leaving this
152
- * table unreachable when both agree.
81
+ * Per-model pricing from the catalog (USD per million tokens). Advisory only:
82
+ * the canonical price is the API server's recompute against the pricing
83
+ * table (`agentswarm.cost.drift.usd` watches the divergence). A catalog row
84
+ * without a cache-read price bills cached input at 10% of input, OpenAI's
85
+ * standard discount.
153
86
  */
154
- export const FALLBACK_CODEX_MODEL_PRICING: Record<CodexModel, CodexModelPricing> = {
155
- "gpt-6-astra": {
156
- inputPerMillion: 10.0,
157
- cachedInputPerMillion: 1.0,
158
- outputPerMillion: 50.0,
159
- },
160
- "gpt-6-sol": {
161
- inputPerMillion: 2.0,
162
- cachedInputPerMillion: 0.2,
163
- outputPerMillion: 10.0,
164
- },
165
- "gpt-6-luna": {
166
- inputPerMillion: 0.1,
167
- cachedInputPerMillion: 0.01,
168
- outputPerMillion: 0.5,
169
- },
170
- "gpt-5.6-sol": {
171
- inputPerMillion: 4.0,
172
- cachedInputPerMillion: 0.4,
173
- outputPerMillion: 20.0,
174
- },
175
- "gpt-5.6-terra": {
176
- inputPerMillion: 2.0,
177
- cachedInputPerMillion: 0.2,
178
- outputPerMillion: 12.0,
179
- },
180
- "gpt-5.6-luna": {
181
- inputPerMillion: 0.2,
182
- cachedInputPerMillion: 0.02,
183
- outputPerMillion: 1.2,
184
- },
185
- "gpt-5.5": {
186
- inputPerMillion: 5.0,
187
- cachedInputPerMillion: 0.5,
188
- outputPerMillion: 30.0,
189
- },
190
- "gpt-5.4": {
191
- inputPerMillion: 2.5,
192
- cachedInputPerMillion: 0.25,
193
- outputPerMillion: 15.0,
194
- },
195
- "gpt-5.4-mini": {
196
- inputPerMillion: 0.75,
197
- cachedInputPerMillion: 0.075,
198
- outputPerMillion: 4.5,
199
- },
200
- "gpt-5.3-codex": {
201
- inputPerMillion: 1.75,
202
- cachedInputPerMillion: 0.175,
203
- outputPerMillion: 14.0,
204
- },
205
- // Legacy — not on the current pricing page; inherit from gpt-5.3-codex.
206
- "gpt-5.2-codex": {
207
- inputPerMillion: 1.75,
208
- cachedInputPerMillion: 0.175,
209
- outputPerMillion: 14.0,
210
- },
211
- };
212
-
213
- function getModelsDevCodexPricing(model: CodexModel): CodexModelPricing | undefined {
214
- const cost = MODELSDEV_OPENAI_MODELS[model]?.cost;
215
- if (
216
- typeof cost?.input !== "number" ||
217
- typeof cost.cache_read !== "number" ||
218
- typeof cost.output !== "number"
219
- ) {
220
- return undefined;
221
- }
87
+ export function getCodexModelPricing(model: string): CodexModelPricing | undefined {
88
+ const cost = runtimeCatalogModel("openai", model)?.cost;
89
+ if (typeof cost?.input !== "number" || typeof cost.output !== "number") return undefined;
222
90
  return {
223
91
  inputPerMillion: cost.input,
224
- cachedInputPerMillion: cost.cache_read,
92
+ cachedInputPerMillion: typeof cost.cache_read === "number" ? cost.cache_read : cost.input / 10,
225
93
  outputPerMillion: cost.output,
226
94
  };
227
95
  }
228
96
 
229
- export const CODEX_MODEL_PRICING: Record<CodexModel, CodexModelPricing> = Object.fromEntries(
230
- CODEX_MODELS.map((model) => [
231
- model,
232
- getModelsDevCodexPricing(model) ?? FALLBACK_CODEX_MODEL_PRICING[model],
233
- ]),
234
- ) as Record<CodexModel, CodexModelPricing>;
97
+ /** Priced Codex models, keyed by id (every `listCodexModels()` entry the catalog prices). */
98
+ export function codexModelPricingTable(): Record<string, CodexModelPricing> {
99
+ const table: Record<string, CodexModelPricing> = {};
100
+ for (const model of listCodexModels()) {
101
+ const pricing = getCodexModelPricing(model);
102
+ if (pricing) table[model] = pricing;
103
+ }
104
+ return table;
105
+ }
235
106
 
236
- /**
237
- * Phase 6 — one-warning-per-process tracking so unknown models log once
238
- * instead of spamming the worker log on every turn.
239
- */
107
+ /** One warning per model per process, so unpriced models don't spam the log. */
240
108
  const _warnedUnknownCodexModels = new Set<string>();
241
109
 
242
110
  /**
@@ -245,10 +113,9 @@ const _warnedUnknownCodexModels = new Set<string>();
245
113
  * + uncached), so we subtract `cached_input_tokens` before billing the
246
114
  * uncached portion at the full rate.
247
115
  *
248
- * Phase 6: returns 0 for unknown models AND logs a one-time warning, so an
249
- * operator running `MODEL_OVERRIDE=gpt-future-2027` notices that the worker
250
- * is silently dropping cost. The server-side recompute path (Phase 2) tags
251
- * such rows `costSource='unpriced'`, which surfaces as a yellow UI badge.
116
+ * Returns 0 for models the catalog does not price AND logs a one-time
117
+ * warning. The server-side recompute tags such rows `costSource='unpriced'`,
118
+ * which surfaces as a yellow UI badge.
252
119
  */
253
120
  export function computeCodexCostUsd(
254
121
  model: string,
@@ -256,13 +123,13 @@ export function computeCodexCostUsd(
256
123
  cachedInputTokens: number,
257
124
  outputTokens: number,
258
125
  ): number {
259
- const pricing = CODEX_MODEL_PRICING[model as CodexModel];
126
+ const pricing = getCodexModelPricing(model);
260
127
  if (!pricing) {
261
128
  if (!_warnedUnknownCodexModels.has(model)) {
262
129
  _warnedUnknownCodexModels.add(model);
263
130
  console.warn(
264
131
  `[codex] unpriced model ${JSON.stringify(model)} — adapter cost will report $0; ` +
265
- "server-side recompute will tag costSource='unpriced' if the pricing table has no rows.",
132
+ "add it to the model catalog (refresh or overlay row). Server-side recompute will tag costSource='unpriced' if the pricing table has no rows.",
266
133
  );
267
134
  }
268
135
  return 0;
@@ -836,6 +836,7 @@ export class DevinAdapter implements ProviderAdapter {
836
836
  // enumerate the installed skills.
837
837
  return {
838
838
  hasMcp,
839
+ hasToolSearch: false,
839
840
  nativeSkillDiscovery: false,
840
841
  hasLocalEnvironment: false,
841
842
  steerModes: ["queue"],
@@ -21,7 +21,7 @@ import type {
21
21
  } from "./types";
22
22
 
23
23
  // npm's `latest` is older and lacks the JSON/stdin contract used here.
24
- export const DSH_PACKAGE = "@deepseek-ai/dsh@0.1.7-alpha.2";
24
+ export const DSH_PACKAGE = "@deepseek-ai/dsh@0.2.0-rc.2";
25
25
 
26
26
  export function checkDshCredentials(env: Record<string, string | undefined>): CredStatus {
27
27
  return env.DEEPSEEK_API_KEY?.trim() || env.OPENROUTER_API_KEY?.trim()
@@ -163,6 +163,7 @@ export class DshAdapter implements ProviderAdapter {
163
163
  readonly name = "dsh";
164
164
  readonly traits: ProviderTraits = {
165
165
  hasMcp: false,
166
+ hasToolSearch: false,
166
167
  nativeSkillDiscovery: false,
167
168
  hasLocalEnvironment: true,
168
169
  steerModes: [],
@@ -1,10 +1,14 @@
1
1
  import { spawnSync } from "node:child_process";
2
+ import { existsSync, readFileSync, realpathSync } from "node:fs";
3
+ import { delimiter, dirname, join } from "node:path";
2
4
 
3
- type PackageJson = { version?: unknown };
5
+ type PackageJson = { name?: unknown; version?: unknown };
4
6
 
5
7
  type ReadPkgVersionOptions = {
6
8
  requirePackageJson?: (specifier: string) => PackageJson;
7
9
  spawn?: typeof spawnSync;
10
+ /** Version of the package that owns `command` on PATH, or undefined. No process is spawned. */
11
+ readInstalledVersion?: (command: string, packageName: string) => string | undefined;
8
12
  };
9
13
 
10
14
  const cliVersionCommands: Record<string, { command: string; args: string[] }> = {
@@ -12,6 +16,9 @@ const cliVersionCommands: Record<string, { command: string; args: string[] }> =
12
16
  "@opencode-ai/sdk": { command: "opencode", args: ["--version"] },
13
17
  };
14
18
 
19
+ // The CLI does not change under a running process, so spawn it at most once.
20
+ const spawnedCliVersions = new Map<string, string | undefined>();
21
+
15
22
  function normalizeVersion(value: unknown): string | undefined {
16
23
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
17
24
  }
@@ -20,19 +27,87 @@ function parseCliVersion(output: string): string | undefined {
20
27
  return output.match(/\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?/)?.[0];
21
28
  }
22
29
 
23
- function readCliVersion(packageName: string, spawn: typeof spawnSync): string | undefined {
24
- const command = cliVersionCommands[packageName];
25
- if (!command) return undefined;
30
+ function findOnPath(command: string): string | undefined {
31
+ for (const dir of (process.env.PATH ?? "").split(delimiter)) {
32
+ if (!dir) continue;
33
+ const candidate = join(dir, command);
34
+ if (existsSync(candidate)) return candidate;
35
+ }
36
+ return undefined;
37
+ }
38
+
39
+ /**
40
+ * Reads the version of the npm package a CLI shim belongs to by walking up from the shim's real
41
+ * path to the package.json that names `packageName`. `pi --version` prints exactly that file's
42
+ * `version`, so this gives the same answer without starting a process.
43
+ */
44
+ export function readInstalledCliPackageVersion(
45
+ command: string,
46
+ packageName: string,
47
+ ): string | undefined {
48
+ const binPath = findOnPath(command);
49
+ if (!binPath) return undefined;
50
+
51
+ let dir: string;
52
+ try {
53
+ dir = dirname(realpathSync(binPath));
54
+ } catch {
55
+ return undefined;
56
+ }
57
+
58
+ for (;;) {
59
+ try {
60
+ const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8")) as PackageJson;
61
+ if (pkg.name === packageName) return normalizeVersion(pkg.version);
62
+ } catch {
63
+ // No package.json at this level, or not valid JSON: keep walking up.
64
+ }
65
+ const parent = dirname(dir);
66
+ if (parent === dir) return undefined;
67
+ dir = parent;
68
+ }
69
+ }
26
70
 
71
+ function spawnCliVersion(
72
+ packageName: string,
73
+ command: { command: string; args: string[] },
74
+ spawn: typeof spawnSync,
75
+ ): string | undefined {
76
+ // Only the real spawnSync is cached; an injected one (tests) must run every call.
77
+ const cacheable = spawn === spawnSync;
78
+ if (cacheable && spawnedCliVersions.has(packageName)) return spawnedCliVersions.get(packageName);
79
+
80
+ let version: string | undefined;
27
81
  try {
28
82
  const result = spawn(command.command, command.args, {
29
83
  encoding: "utf8",
30
84
  stdio: ["ignore", "pipe", "pipe"],
85
+ timeout: 5_000,
86
+ killSignal: "SIGKILL",
31
87
  });
32
- return parseCliVersion(`${result.stdout ?? ""}\n${result.stderr ?? ""}`);
88
+ version = parseCliVersion(`${result.stdout ?? ""}\n${result.stderr ?? ""}`);
33
89
  } catch {
34
- return undefined;
90
+ version = undefined;
35
91
  }
92
+ if (cacheable) spawnedCliVersions.set(packageName, version);
93
+ return version;
94
+ }
95
+
96
+ function readCliVersion(
97
+ packageName: string,
98
+ spawn: typeof spawnSync,
99
+ readInstalledVersion: NonNullable<ReadPkgVersionOptions["readInstalledVersion"]>,
100
+ ): string | undefined {
101
+ const command = cliVersionCommands[packageName];
102
+ if (!command) return undefined;
103
+
104
+ // Prefer reading the installed package: a synchronous spawn blocks this thread for the whole
105
+ // child run, and on Bun 1.4.0 a `pi --version` spawnSync intermittently never returns (the child
106
+ // exits and stays a zombie while the parent spins), which wedged CI test workers for 20 minutes.
107
+ const installed = readInstalledVersion(command.command, packageName);
108
+ if (installed) return installed;
109
+
110
+ return spawnCliVersion(packageName, command, spawn);
36
111
  }
37
112
 
38
113
  export function readPkgVersion(
@@ -40,9 +115,10 @@ export function readPkgVersion(
40
115
  {
41
116
  requirePackageJson = (specifier) => require(specifier) as PackageJson,
42
117
  spawn = spawnSync,
118
+ readInstalledVersion = readInstalledCliPackageVersion,
43
119
  }: ReadPkgVersionOptions = {},
44
120
  ): string | undefined {
45
- const cliVersion = readCliVersion(packageName, spawn);
121
+ const cliVersion = readCliVersion(packageName, spawn, readInstalledVersion);
46
122
  if (cliVersion) return cliVersion;
47
123
 
48
124
  try {
@@ -10,7 +10,7 @@
10
10
 
11
11
  import { existsSync, mkdirSync } from "node:fs";
12
12
  import { join } from "node:path";
13
- import type { AssistantMessage, Config, Event as OpencodeEvent } from "@opencode-ai/sdk";
13
+ import type { AssistantMessage, Config, Event as OpencodeEvent, TextPart } from "@opencode-ai/sdk";
14
14
  import { createOpencode } from "@opencode-ai/sdk";
15
15
  import {
16
16
  CONTEXT_FORMULA,
@@ -301,6 +301,13 @@ export class OpencodeSession implements ProviderSession {
301
301
  // Keep the latest snapshot per message so those replays do not double-count.
302
302
  private finalizedMessages = new Map<string, FinalizedMessageUsage>();
303
303
  private missingMessageIdCounter = 0;
304
+ // Assistant text for the runner's final-message capture (`trackAssistantText`),
305
+ // which is how codex tasks with an outputSchema get their final message
306
+ // validated. Text parts stream in around the message's own updates, so buffer
307
+ // the latest snapshot per part and emit per message once its role is known.
308
+ private textParts = new Map<string, Map<string, string>>();
309
+ private assistantMessageIds = new Set<string>();
310
+ private emittedText = new Map<string, string>();
304
311
  private startedAt = Date.now();
305
312
  private model: string;
306
313
  private agentId: string;
@@ -462,6 +469,7 @@ export class OpencodeSession implements ProviderSession {
462
469
  case "message.updated": {
463
470
  const msg = ev.properties.info;
464
471
  if (!isAssistantMessage(msg) || msg.sessionID !== this._sessionId) break;
472
+ if (typeof msg.id === "string") this.assistantMessageIds.add(msg.id);
465
473
  // Phase 9 fix: opencode fires `message.updated` repeatedly during a single
466
474
  // assistant turn (streaming text deltas, tool transitions, etc.) and only
467
475
  // populates `tokens`/`cost` on the FINAL update once `time.completed` is
@@ -487,6 +495,7 @@ export class OpencodeSession implements ProviderSession {
487
495
  cacheReadTokens: msg.tokens?.cache?.read ?? 0,
488
496
  cacheWriteTokens: msg.tokens?.cache?.write ?? 0,
489
497
  });
498
+ this.emitAssistantText(msg.id);
490
499
  if (!this.model && msg.modelID) this.model = msg.modelID;
491
500
 
492
501
  // Emit context_usage so the runner can POST /api/tasks/:id/context
@@ -521,6 +530,12 @@ export class OpencodeSession implements ProviderSession {
521
530
  }
522
531
 
523
532
  case "message.part.updated": {
533
+ // Text parts are matched to this session by their own `sessionID`
534
+ // (checked in `recordTextPart`), not by the tool branch's check below.
535
+ if (ev.properties.part?.type === "text") {
536
+ this.recordTextPart(ev.properties.part);
537
+ break;
538
+ }
524
539
  // Bridge opencode's part.state lifecycle to swarm's tool_start/tool_end
525
540
  // so the dashboard's Activity timeline mirrors what other providers
526
541
  // emit. We fire tool_start the first time we see a tool part (any
@@ -612,6 +627,36 @@ export class OpencodeSession implements ProviderSession {
612
627
  }
613
628
  }
614
629
 
630
+ /** Keep the latest snapshot of a text part (`part.text` is cumulative). */
631
+ private recordTextPart(part: TextPart): void {
632
+ // Synthetic and ignored parts are opencode-injected context, not model output.
633
+ if (part.sessionID !== this._sessionId || part.synthetic || part.ignored) return;
634
+ if (typeof part.messageID !== "string" || typeof part.id !== "string") return;
635
+ let parts = this.textParts.get(part.messageID);
636
+ if (!parts) {
637
+ parts = new Map();
638
+ this.textParts.set(part.messageID, parts);
639
+ }
640
+ parts.set(part.id, part.text ?? "");
641
+ // A part can land after its message was finalized; re-emit the fuller text.
642
+ if (this.finalizedMessages.has(part.messageID)) this.emitAssistantText(part.messageID);
643
+ }
644
+
645
+ /**
646
+ * Emit an assistant message's text as a normal `message` event. Only assistant
647
+ * messages qualify, and unchanged text is not re-emitted (opencode replays
648
+ * finalized snapshots). The runner keeps the last non-empty one.
649
+ */
650
+ private emitAssistantText(messageId: string): void {
651
+ if (!this.assistantMessageIds.has(messageId)) return;
652
+ const parts = this.textParts.get(messageId);
653
+ if (!parts) return;
654
+ const text = [...parts.values()].join("").trim();
655
+ if (!text || this.emittedText.get(messageId) === text) return;
656
+ this.emittedText.set(messageId, text);
657
+ this.emit({ type: "message", role: "assistant", content: text, messageId });
658
+ }
659
+
615
660
  private emitError(message: string): void {
616
661
  const errorEvent: ProviderEvent = { type: "error", message };
617
662
  for (const l of this.listeners) l(errorEvent);
@@ -719,6 +764,7 @@ export class OpencodeAdapter implements ProviderAdapter {
719
764
 
720
765
  readonly traits: ProviderTraits = {
721
766
  hasMcp: true,
767
+ hasToolSearch: false,
722
768
  // Same inline-resolver pattern as codex (`resolveSlashSkillPrompt`) — no
723
769
  // ambient skill awareness, so the system prompt enumerates them.
724
770
  nativeSkillDiscovery: false,
@@ -760,6 +806,12 @@ export class OpencodeAdapter implements ProviderAdapter {
760
806
  headers: {
761
807
  Authorization: `Bearer ${config.apiKey}`,
762
808
  "X-Agent-ID": config.agentId,
809
+ // Per-task identity, same as claude/codex. Without it `store-progress`
810
+ // has no default task, so the model must hand-type a UUID and can
811
+ // copy a sibling's or parent's id from the prompt. The config file
812
+ // and `opencode serve` are already per task, so this is not shared.
813
+ "X-Source-Task-Id": taskId,
814
+ ...(config.contextKey ? { "X-Context-Key": config.contextKey } : {}),
763
815
  ...(runtimeInstanceId ? { "X-Runtime-Instance-ID": runtimeInstanceId } : {}),
764
816
  },
765
817
  },
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Codemode sandbox worker for the compiled agent-swarm binary.
3
+ *
4
+ * In a Bun binary, pi's `getCodemodeWorkerUrl()` loads the worker from
5
+ * `./src/extensions/codemode/worker.js` next to the executable. The build
6
+ * passes this file as an extra entrypoint with
7
+ * `--root ./src/providers/pi-binary-root`, which embeds it at that path.
8
+ * Keep the Dockerfile.worker compile line and both package.json
9
+ * build:binary scripts in sync.
10
+ */
11
+ import "@earendil-works/pi-codemode/worker";
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Point pi's codemode at the QuickJS wasm that `bun build --compile` embeds.
3
+ *
4
+ * pi resolves the wasm with `createRequire(import.meta.url).resolve(...)`
5
+ * unless an embedded path was set, and that resolve fails inside a compiled
6
+ * binary's `$bunfs`. pi's own Bun binary sets the path in its entry
7
+ * (dist/bun/runtime-setup.js); our binary has a different entry, so this
8
+ * module does the same. Outside a binary, the import is the file on disk.
9
+ *
10
+ * `setEmbeddedQuickJSWasmPath` is not in the package `exports` map, so the
11
+ * import goes through the file path. It is the same module instance pi's
12
+ * codemode reads, and tsc fails if pi moves or drops it.
13
+ */
14
+ import quickjsWasmPath from "quickjs-wasi/quickjs.wasm" with { type: "file" };
15
+ import { setEmbeddedQuickJSWasmPath } from "../../node_modules/@earendil-works/pi-coding-agent/dist/config.js";
16
+
17
+ setEmbeddedQuickJSWasmPath(quickjsWasmPath);