@desplega.ai/agent-swarm 1.173.0 → 2.1.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 (511) hide show
  1. package/README.md +1 -1
  2. package/dist/{acp-adapter-ccm9xrep.js → acp-adapter-kr4abrsk.js} +17 -16
  3. package/dist/{actions-sgeyv08k.js → actions-eh4g6w2r.js} +39 -34
  4. package/dist/{amp-adapter-gpxp3kc9.js → amp-adapter-7h7wdt8q.js} +8 -7
  5. package/dist/{app-km9qnv2f.js → app-7k8s9w5p.js} +18 -14
  6. package/dist/{assistant-5r8tcvvd.js → assistant-wswt2y7p.js} +40 -35
  7. package/dist/{boot-reembed-16rv9843.js → boot-reembed-9apm9qw6.js} +10 -9
  8. package/dist/{boot-reembed-swwzdgd7.js → boot-reembed-whzbdthj.js} +10 -9
  9. package/dist/{boot-scrub-sweep-zkr6vkqb.js → boot-scrub-sweep-ndzmvmgt.js} +11 -10
  10. package/dist/{boot-seeds-d8sp49t2.js → boot-seeds-e9sta0yt.js} +16 -13
  11. package/dist/{claude-adapter-yzzzh8jp.js → claude-adapter-4mqd3r6r.js} +19 -18
  12. package/dist/{claude-managed-adapter-xgxhqrt2.js → claude-managed-adapter-9wswb52m.js} +7 -5
  13. package/dist/{claude-managed-setup-bnmjarjc.js → claude-managed-setup-tpe1effx.js} +6 -4
  14. package/dist/{claude-sdk-session-4whxere4.js → claude-sdk-session-q49ghmh9.js} +12402 -11850
  15. package/dist/{cli-ne7smj1b.js → cli-01xvj1rq.js} +10 -10
  16. package/dist/{cli-nwrdx840.js → cli-035vekcw.js} +3 -3
  17. package/dist/{cli-9xf7fdtp.js → cli-0pd8hzhc.js} +29 -6
  18. package/dist/{cli-tmhsxhb8.js → cli-0veb92a3.js} +28 -6
  19. package/dist/{cli-c3rk1v0x.js → cli-10jesr25.js} +3 -3
  20. package/dist/{cli-6thk4jz3.js → cli-1y7yywmj.js} +6 -6
  21. package/dist/{cli-6r2f7edk.js → cli-20krdhz5.js} +47 -19
  22. package/dist/{cli-rfs4vddv.js → cli-29rja109.js} +39 -20
  23. package/dist/cli-2m1ktfzz.js +17795 -0
  24. package/dist/{cli-na2pxe4f.js → cli-2pwa0q4d.js} +15 -4
  25. package/dist/{cli-7a93cv8c.js → cli-4mengw46.js} +441 -43
  26. package/dist/{cli-94b48jhk.js → cli-5twdke5x.js} +16 -14
  27. package/dist/{cli-7a0sde2x.js → cli-6cagzc96.js} +9 -0
  28. package/dist/cli-6cvfxs8r.js +47 -0
  29. package/dist/{cli-w1apjecb.js → cli-711vwbxn.js} +2 -2
  30. package/dist/{cli-r4fqfpwf.js → cli-78vv7446.js} +50 -27
  31. package/dist/{cli-srqq3gc5.js → cli-7n6s5man.js} +5 -5
  32. package/dist/{cli-m89t9jdf.js → cli-7x0g4sn1.js} +15416 -16024
  33. package/dist/{cli-2bw3w47j.js → cli-8wwxw8a3.js} +1 -1
  34. package/dist/{cli-yatxqynm.js → cli-9ha6hs2d.js} +1 -290
  35. package/dist/{cli-ywm1azg9.js → cli-9jzdj90z.js} +8 -4
  36. package/dist/cli-9traj3dn.js +638 -0
  37. package/dist/{cli-smcrrmhy.js → cli-agzebbd5.js} +9 -9
  38. package/dist/{cli-c5km5znr.js → cli-c8r3kzzb.js} +1 -1
  39. package/dist/cli-cvv9k0hh.js +10143 -0
  40. package/dist/{cli-anzdk0z4.js → cli-d9286m9h.js} +2 -2
  41. package/dist/cli-e3g5mbkz.js +776 -0
  42. package/dist/cli-f527ce74.js +781 -0
  43. package/dist/{cli-gmdajfw5.js → cli-gxzkg1de.js} +5020 -1729
  44. package/dist/{cli-451ks6aw.js → cli-h1arqn31.js} +1 -1
  45. package/dist/cli-hjwmhcg1.js +10622 -0
  46. package/dist/{cli-jpc4r224.js → cli-hqr88ew6.js} +19 -3
  47. package/dist/{cli-h7k0xtry.js → cli-hvqbtna4.js} +4 -1256
  48. package/dist/{cli-cd0hv9nh.js → cli-j67mh2vg.js} +2 -2
  49. package/dist/cli-jta2eyzc.js +619 -0
  50. package/dist/{cli-wd0hz6ah.js → cli-kbrt8gqa.js} +9 -91
  51. package/dist/cli-kceah1eh.js +92 -0
  52. package/dist/{cli-d603qf86.js → cli-ksphgh48.js} +10 -8
  53. package/dist/{cli-3nxdmjrc.js → cli-mgz4nbgg.js} +4 -4
  54. package/dist/{cli-saqyage0.js → cli-mz5as5ha.js} +179 -27
  55. package/dist/cli-n2wq4j7m.js +6985 -0
  56. package/dist/{cli-ppy4nnnh.js → cli-n8ndw8j7.js} +2 -2
  57. package/dist/{cli-1w0vj3gw.js → cli-naqzr17j.js} +7 -7
  58. package/dist/{cli-0a7mm3zp.js → cli-nm1p8jfr.js} +25 -21
  59. package/dist/{cli-wrh08zjd.js → cli-p3zbc1rw.js} +5 -18
  60. package/dist/{cli-hsem16hb.js → cli-pe05f8yk.js} +29 -6
  61. package/dist/{cli-5wpw4rab.js → cli-qmzsbkvy.js} +5 -5
  62. package/dist/{cli-bv2etw2y.js → cli-qnm4e9e4.js} +99 -590
  63. package/dist/{cli-1x7da0de.js → cli-qqy4q48b.js} +8 -8
  64. package/dist/{cli-r44t9wqc.js → cli-r106725k.js} +2 -2
  65. package/dist/{cli-5er5gjbm.js → cli-r35rw5de.js} +15 -15
  66. package/dist/{cli-v3zhnxsb.js → cli-r610p9bs.js} +2 -2
  67. package/dist/{cli-7jykv7ef.js → cli-rc6pf5kv.js} +1 -1
  68. package/dist/{cli-x119kdba.js → cli-rdxm77k3.js} +1 -1
  69. package/dist/cli-rhs9pz33.js +294 -0
  70. package/dist/{cli-0p3enk35.js → cli-s69nfyeh.js} +9 -1
  71. package/dist/{cli-0kxf81jj.js → cli-sawv63e8.js} +2 -2
  72. package/dist/{cli-e3pmyt39.js → cli-sg19gbf0.js} +1 -1
  73. package/dist/{cli-d161g69q.js → cli-shk7r5cs.js} +57 -6
  74. package/dist/{cli-7pz3b7x5.js → cli-sksetb2q.js} +36 -1
  75. package/dist/{cli-014vns2y.js → cli-stfs0k5s.js} +208 -525
  76. package/dist/{cli-db3m6zdt.js → cli-t02z59f2.js} +1 -1
  77. package/dist/{cli-b07hr3bt.js → cli-txqb9s38.js} +2 -2
  78. package/dist/{cli-s8z6x4b4.js → cli-v7g551te.js} +225 -113
  79. package/dist/{cli-rfz2ve55.js → cli-vgqzp4em.js} +3341 -955
  80. package/dist/{cli-7avy4v8w.js → cli-w1q7h2r2.js} +5 -3
  81. package/dist/{cli-rcp5aqdw.js → cli-w7m81tb7.js} +1 -1
  82. package/dist/cli-wfnemr1t.js +86 -0
  83. package/dist/cli-wmgrkscj.js +23254 -0
  84. package/dist/{cli-qm1448f9.js → cli-wms6x9db.js} +8 -8
  85. package/dist/{cli-tadfgd0t.js → cli-x1r545x8.js} +33 -4
  86. package/dist/{cli-xc7p7s3g.js → cli-xczsgshn.js} +6 -6
  87. package/dist/{cli-fmhj1ehj.js → cli-y3167wt1.js} +882 -271
  88. package/dist/{cli-r0byd61h.js → cli-yjmqpxt3.js} +17 -16
  89. package/dist/{cli-81r1wjyv.js → cli-z780ec7c.js} +1 -1
  90. package/dist/{cli-phcqp1b9.js → cli-zm9t2vde.js} +2 -2
  91. package/dist/cli.js +34 -49
  92. package/dist/{codex-adapter-r3dn4fp5.js → codex-adapter-4z16v9n0.js} +15 -14
  93. package/dist/{codex-login-841kv5fk.js → codex-login-pkepy2j1.js} +3 -2
  94. package/dist/{codex-session-runner-fwt793h4.js → codex-session-runner-rvms9y2x.js} +15 -14
  95. package/dist/{commands-z8fq9qna.js → commands-2a9rtyat.js} +11 -10
  96. package/dist/{controller-hq5ggpcp.js → controller-3k3madbn.js} +5 -4
  97. package/dist/{controllers-connect-3mf2btyj.js → controllers-connect-v9yn8zb9.js} +42 -37
  98. package/dist/{cursor-adapter-q8pgv5qa.js → cursor-adapter-98gbhyzn.js} +7 -6
  99. package/dist/{db-qfpq4bt7.js → db-eywwsnjw.js} +30 -9
  100. package/dist/{db-yyhqqrbf.js → db-ncypk9np.js} +12 -11
  101. package/dist/{devin-adapter-xgtj680j.js → devin-adapter-zeq8b9gz.js} +3 -2
  102. package/dist/{dispatcher-x0x7qfcq.js → dispatcher-w94kc575.js} +38 -33
  103. package/dist/{docker-f503k9p0.js → docker-b1e4cqab.js} +3 -2
  104. package/dist/{dsh-adapter-34etvb5m.js → dsh-adapter-47ztej36.js} +7 -6
  105. package/dist/{e2b-66xra4az.js → e2b-bbt8ey45.js} +4 -3
  106. package/dist/{e2b-xwhyj8cw.js → e2b-dp804sen.js} +4 -3
  107. package/dist/{event-bus-vjtv82gm.js → event-bus-j2jdg3qb.js} +3 -2
  108. package/dist/{external-channel-h1fjecwe.js → external-channel-zrkg8ar9.js} +11 -10
  109. package/dist/{google-generative-ai-szdxbpqg.js → google-generative-ai-g8db7x0f.js} +4 -2
  110. package/dist/{google-vertex-qzw3p36d.js → google-vertex-r2692q0r.js} +4 -2
  111. package/dist/{grok-adapter-e7mr287e.js → grok-adapter-s9ncp9vy.js} +18 -17
  112. package/dist/{handlers-znb9n7d4.js → handlers-kqftvjgw.js} +41 -36
  113. package/dist/{heartbeat-bm4qkn8d.js → heartbeat-9q96p8v9.js} +43 -38
  114. package/dist/{hook-254r4mza.js → hook-56qe10dp.js} +11 -23
  115. package/dist/{hook-xaa3q80s.js → hook-yy6rw42k.js} +12 -11
  116. package/dist/{http-3dgrgp8y.js → http-r2dnb8f8.js} +2411 -275
  117. package/dist/{index-fx3jqq1e.js → index-3gtmkhc6.js} +38 -33
  118. package/dist/{index-bdsqa799.js → index-3k5hkz7r.js} +43 -38
  119. package/dist/index-52j1aja4.js +3465 -0
  120. package/dist/{index-g2byveez.js → index-5e101240.js} +38 -33
  121. package/dist/{index-2ybk7khy.js → index-5wyfjc2c.js} +845 -683
  122. package/dist/index-9gw58n4k.js +193 -0
  123. package/dist/index-9j87mcm8.js +56305 -0
  124. package/dist/index-bsmcresk.js +79424 -0
  125. package/dist/index-mh85qhec.js +475 -0
  126. package/dist/{index-jd7sejm2.js → index-vxvhxxbn.js} +10 -9
  127. package/dist/index-we27kd31.js +12810 -0
  128. package/dist/{keepalive-6ywhpja1.js → keepalive-psj6j54c.js} +12 -11
  129. package/dist/{lead-cw45n21s.js → lead-hajz4688.js} +61 -56
  130. package/dist/{maintenance-ss1a1ge9.js → maintenance-ps02sb5m.js} +10 -9
  131. package/dist/{node-fkajaq96.js → node-mwb5r697.js} +2 -1
  132. package/dist/{oauth-refresh-sweep-3c6f8vyy.js → oauth-refresh-sweep-0284h93b.js} +11 -10
  133. package/dist/{onboard-8hzdhc1k.js → onboard-rbf6bg01.js} +11 -3
  134. package/dist/{opencode-adapter-qbchbcfz.js → opencode-adapter-13dcatgq.js} +18 -13
  135. package/dist/{otel-impl-e6mnvc4v.js → otel-impl-zvqcbhp7.js} +242 -868
  136. package/dist/{pi-mono-adapter-3c9g9snh.js → pi-mono-adapter-xxy47q4v.js} +244 -106
  137. package/dist/{pricing-refresh-gedkjdw0.js → pricing-refresh-kefx9v3t.js} +12 -11
  138. package/dist/{rbac-roles-dt4vh1yy.js → rbac-roles-3m36rxm2.js} +11 -10
  139. package/dist/{rbac-roles-eszdg6pm.js → rbac-roles-z76xv0hq.js} +12 -11
  140. package/dist/{render-v2-907cy6sa.js → render-v2-wz73bs25.js} +18 -14
  141. package/dist/{resume-42stcaz0.js → resume-k4wzge8w.js} +25 -21
  142. package/dist/{runtime-qfry88v2.js → runtime-sa2vr16g.js} +2 -2
  143. package/dist/{setup-adbc1c10.js → setup-2fp2pypp.js} +2 -2
  144. package/dist/{virtual-modules-rtmt494q.js → virtual-modules-9gkxqrap.js} +6 -5
  145. package/dist/{work-objects-wpj8ck95.js → work-objects-6k0ywz3m.js} +41 -36
  146. package/dist/{worker-bzdra9rf.js → worker-5v3rxsdh.js} +61 -56
  147. package/dist/{x-qfmygqqv.js → x-4mtdeym1.js} +3 -2
  148. package/openapi.json +1279 -329
  149. package/package.json +15 -15
  150. package/src/agentmail/handlers.ts +21 -11
  151. package/src/apps/definition.ts +16 -0
  152. package/src/azure-devops/api.ts +52 -2
  153. package/src/azure-devops/boards-sync.ts +50 -20
  154. package/src/azure-devops/index.ts +1 -0
  155. package/src/azure-devops/outbound.ts +95 -0
  156. package/src/be/boot-seeds.ts +11 -0
  157. package/src/be/config-refs.ts +60 -0
  158. package/src/be/db/model-profiles.ts +315 -0
  159. package/src/be/db/page-versions.ts +15 -0
  160. package/src/be/db/runtime.ts +35 -8
  161. package/src/be/db/skill-name-resolution.ts +36 -0
  162. package/src/be/db/tasks/read.ts +237 -0
  163. package/src/be/db/tasks/write.ts +7 -0
  164. package/src/be/db.ts +87 -36
  165. package/src/be/migrations/215_swarm_config_export_to_env.sql +34 -0
  166. package/src/be/migrations/216_task_resolved_effort.sql +5 -0
  167. package/src/be/migrations/217_chat_sdk_state.sql +104 -0
  168. package/src/be/migrations/219_relabel_pi_context_compactions.sql +22 -0
  169. package/src/be/migrations/220_model_profiles.sql +137 -0
  170. package/src/be/model-catalog-store.ts +5 -1
  171. package/src/be/model-tier-resolution.ts +155 -10
  172. package/src/be/model-validation.ts +12 -2
  173. package/src/be/models-catalog.ts +26 -1
  174. package/src/be/modelsdev-cache.json +198 -63
  175. package/src/be/modelsdev-cache.ts +9 -1
  176. package/src/be/script-credential-broker.ts +39 -2
  177. package/src/be/scripts/run-saved.ts +1 -0
  178. package/src/be/scripts/typecheck.ts +22 -8
  179. package/src/be/secret-registry.ts +6 -2
  180. package/src/be/seed/runner.ts +13 -1
  181. package/src/be/seed/state-db.ts +5 -0
  182. package/src/be/seed/types.ts +6 -0
  183. package/src/be/seed-model-profiles.ts +41 -0
  184. package/src/be/seed-pricing.ts +19 -2
  185. package/src/be/seed-scripts/catalog/heartbeat-jev-gate.ts +368 -0
  186. package/src/be/seed-scripts/catalog/ops-catalog-audit.inline.ts +1 -1
  187. package/src/be/seed-scripts/catalog/ops-catalog-audit.ts +1 -1
  188. package/src/be/seed-scripts/index.ts +9 -0
  189. package/src/be/seed-skills/bundled-files.generated.json +0 -18
  190. package/src/be/seed-skills/index.ts +104 -3
  191. package/src/be/swarm-config-guard.ts +53 -0
  192. package/src/cli.tsx +0 -16
  193. package/src/commands/onboard/non-interactive.ts +4 -2
  194. package/src/commands/provider-credentials.ts +52 -6
  195. package/src/commands/runner.ts +193 -71
  196. package/src/e2b/env.ts +5 -0
  197. package/src/extensions/catalog.generated.json +17 -0
  198. package/src/extensions/contract-types.generated.ts +1 -1
  199. package/src/extensions/contract.ts +45 -0
  200. package/src/extensions/dispatcher.ts +7 -0
  201. package/src/extensions/loader.ts +10 -5
  202. package/src/extensions/post-bridge.ts +40 -0
  203. package/src/github/handlers.ts +11 -4
  204. package/src/heartbeat/heartbeat.ts +2 -1
  205. package/src/heartbeat/templates.ts +8 -5
  206. package/src/hooks/hook.ts +0 -26
  207. package/src/http/agents.ts +49 -2
  208. package/src/http/all-routes.ts +1 -1
  209. package/src/http/apps.ts +30 -3
  210. package/src/http/config.ts +62 -19
  211. package/src/http/core.ts +8 -0
  212. package/src/http/index.ts +25 -3
  213. package/src/http/integrations.ts +88 -1
  214. package/src/http/mcp.ts +15 -5
  215. package/src/http/model-profiles.ts +303 -0
  216. package/src/http/page-proxy.ts +2 -4
  217. package/src/http/pages-public.ts +13 -7
  218. package/src/http/pages.ts +3 -13
  219. package/src/http/poll.ts +42 -71
  220. package/src/http/requested-by.ts +54 -0
  221. package/src/http/script-runs.ts +1 -1
  222. package/src/http/scripts.ts +26 -2
  223. package/src/http/stats.ts +1 -30
  224. package/src/http/tasks.ts +79 -5
  225. package/src/http/utils.ts +14 -0
  226. package/src/http/webhooks.ts +160 -3
  227. package/src/http/x.ts +1 -0
  228. package/src/integrations/chat-sdk/adapters.ts +129 -0
  229. package/src/integrations/chat-sdk/attachments.ts +232 -0
  230. package/src/integrations/chat-sdk/background.ts +36 -0
  231. package/src/integrations/chat-sdk/commands.ts +194 -0
  232. package/src/integrations/chat-sdk/config.ts +198 -0
  233. package/src/integrations/chat-sdk/discord-api.ts +63 -0
  234. package/src/integrations/chat-sdk/discord-gateway.ts +185 -0
  235. package/src/integrations/chat-sdk/handlers.ts +28 -0
  236. package/src/integrations/chat-sdk/inbound.ts +426 -0
  237. package/src/integrations/chat-sdk/limits.ts +48 -0
  238. package/src/integrations/chat-sdk/logger.ts +81 -0
  239. package/src/integrations/chat-sdk/outbound.ts +123 -0
  240. package/src/integrations/chat-sdk/relay.ts +172 -0
  241. package/src/integrations/chat-sdk/router.ts +169 -0
  242. package/src/integrations/chat-sdk/runtime.ts +351 -0
  243. package/src/integrations/chat-sdk/schema.ts +245 -0
  244. package/src/integrations/chat-sdk/state.ts +243 -0
  245. package/src/integrations/chat-sdk/status.ts +244 -0
  246. package/src/integrations/chat-sdk/steering.ts +58 -0
  247. package/src/integrations/chat-sdk/teams-origin.ts +77 -0
  248. package/src/integrations/chat-sdk/telegram-api.ts +56 -0
  249. package/src/integrations/chat-sdk/templates.ts +16 -0
  250. package/src/integrations/chat-sdk/test-hooks.ts +37 -0
  251. package/src/integrations/chat-sdk/thread-buffer.ts +100 -0
  252. package/src/integrations/chat-sdk/typing.ts +65 -0
  253. package/src/{artifact-sdk → pages}/feedback-overlay.ts +65 -6
  254. package/src/pages/version.ts +4 -5
  255. package/src/prompts/base-prompt.ts +111 -45
  256. package/src/providers/acp-target-catalog.ts +6 -0
  257. package/src/providers/acp-targets.ts +56 -3
  258. package/src/providers/amp-adapter.ts +1 -1
  259. package/src/providers/claude-adapter.ts +1 -1
  260. package/src/providers/claude-flag-settings.ts +17 -5
  261. package/src/providers/claude-managed-adapter.ts +1 -1
  262. package/src/providers/claude-session-events.ts +6 -1
  263. package/src/providers/dsh-adapter.ts +1 -1
  264. package/src/providers/opencode-adapter.ts +243 -26
  265. package/src/providers/opencode-swarm-plugin.ts +31 -0
  266. package/src/providers/pi-mono-adapter.ts +398 -153
  267. package/src/providers/pi-mono-extension.ts +17 -3
  268. package/src/providers/pricing-sources.md +33 -2
  269. package/src/providers/reasoning-effort.ts +37 -0
  270. package/src/providers/types.ts +6 -2
  271. package/src/rbac/legacy-policy.ts +16 -2
  272. package/src/rbac/permissions.ts +26 -0
  273. package/src/scheduler/deferred-task-waits.ts +18 -5
  274. package/src/scheduler/scheduler.ts +11 -3
  275. package/src/scripts-runtime/executors/quickjs-prelude.ts +75 -6
  276. package/src/scripts-runtime/executors/quickjs-runner.ts +85 -8
  277. package/src/scripts-runtime/executors/quickjs-web-shims.ts +1041 -0
  278. package/src/scripts-runtime/extract-args-schema.ts +9 -8
  279. package/src/scripts-runtime/loader.ts +19 -7
  280. package/src/scripts-runtime/quickjs-compat.ts +464 -0
  281. package/src/scripts-runtime/sdk-allowlist.ts +12 -6
  282. package/src/scripts-runtime/swarm-sdk.ts +0 -11
  283. package/src/scripts-runtime/types/stdlib.d.ts +39 -15
  284. package/src/scripts-runtime/types/swarm-extension.d.ts +66 -1
  285. package/src/scripts-runtime/types/swarm-sdk.d.ts +39 -15
  286. package/src/server.ts +62 -19
  287. package/src/slack/blocks.ts +4 -2
  288. package/src/slack/event-dedup.ts +1 -1
  289. package/src/slack/reaction-shortcode.ts +14 -0
  290. package/src/slack/render-v2.ts +12 -7
  291. package/src/slack/responses.ts +5 -4
  292. package/src/slack/thread-buffer.ts +41 -22
  293. package/src/tasks/additive-buffer.ts +4 -2
  294. package/src/tasks/context-key.ts +60 -1
  295. package/src/tasks/model-pin.ts +67 -0
  296. package/src/tasks/worker-follow-up.ts +15 -33
  297. package/src/tests/acp-adapter.test.ts +42 -0
  298. package/src/tests/agentmail-filters.test.ts +47 -0
  299. package/src/tests/api-drain-worker-e2e.test.ts +1 -6
  300. package/src/tests/apps-spike2.test.ts +35 -0
  301. package/src/tests/azure-devops-outbound.test.ts +420 -0
  302. package/src/tests/base-prompt.test.ts +58 -54
  303. package/src/tests/chat-sdk-commands.test.ts +191 -0
  304. package/src/tests/chat-sdk-config.test.ts +334 -0
  305. package/src/tests/chat-sdk-discord-api.test.ts +68 -0
  306. package/src/tests/chat-sdk-discord-gateway.test.ts +252 -0
  307. package/src/tests/chat-sdk-discord-webhook.test.ts +126 -0
  308. package/src/tests/chat-sdk-fields-sync.test.ts +50 -0
  309. package/src/tests/chat-sdk-harness.ts +146 -0
  310. package/src/tests/chat-sdk-inbound.test.ts +363 -0
  311. package/src/tests/chat-sdk-relay.test.ts +470 -0
  312. package/src/tests/chat-sdk-reload.test.ts +128 -0
  313. package/src/tests/chat-sdk-shutdown.test.ts +220 -0
  314. package/src/tests/chat-sdk-slack.test.ts +268 -0
  315. package/src/tests/chat-sdk-state.test.ts +163 -0
  316. package/src/tests/chat-sdk-status-route.test.ts +382 -0
  317. package/src/tests/chat-sdk-teams.test.ts +277 -0
  318. package/src/tests/chat-sdk-tools.test.ts +520 -0
  319. package/src/tests/chat-sdk-typing.test.ts +120 -0
  320. package/src/tests/chat-sdk-webhook-telegram.test.ts +200 -0
  321. package/src/tests/claude-adapter-binary.test.ts +9 -4
  322. package/src/tests/claude-flag-settings.test.ts +11 -3
  323. package/src/tests/claude-managed-adapter.test.ts +1 -1
  324. package/src/tests/claude-sdk-lifecycle.test.ts +9 -3
  325. package/src/tests/claude-thinking-tokens.test.ts +57 -0
  326. package/src/tests/claude-worker-parity.test.ts +2 -7
  327. package/src/tests/config-env-export-chat-sdk.test.ts +11 -0
  328. package/src/tests/config-refs.test.ts +66 -0
  329. package/src/tests/context-key.test.ts +19 -0
  330. package/src/tests/cors-allowlist.test.ts +25 -0
  331. package/src/tests/create-page-tool.test.ts +18 -0
  332. package/src/tests/credential-check.test.ts +139 -0
  333. package/src/tests/credentials.test.ts +21 -0
  334. package/src/tests/db-runtime.test.ts +61 -0
  335. package/src/tests/db-task-read-contract.test.ts +22 -0
  336. package/src/tests/db-task-write-contract.test.ts +11 -0
  337. package/src/tests/deferred-task-wake.test.ts +78 -1
  338. package/src/tests/docs-index-footprint.test.js +3 -3
  339. package/src/tests/dsh-adapter.test.ts +1 -1
  340. package/src/tests/enabled-capabilities.test.ts +20 -0
  341. package/src/tests/entrypoint-api-readiness.test.ts +14 -8
  342. package/src/tests/entrypoint-config-env-export.test.ts +5 -2
  343. package/src/tests/entrypoint-export-to-env.test.ts +78 -0
  344. package/src/tests/env-flag.test.ts +1 -0
  345. package/src/tests/explicit-model-validation.test.ts +105 -1
  346. package/src/tests/extensions-catalog.test.ts +1 -0
  347. package/src/tests/extensions-example-comms-adherence-gate.test.ts +413 -0
  348. package/src/tests/extensions-example-route-chat-channel.test.ts +154 -0
  349. package/src/tests/extensions-loader.test.ts +15 -0
  350. package/src/tests/extensions-post-bridge.test.ts +51 -0
  351. package/src/tests/fetch-resolved-env.test.ts +128 -0
  352. package/src/tests/fixtures/chat-sdk/telegram-private-text.json +10 -0
  353. package/src/tests/fixtures/extensions/route-chat-channel-to-agent/hooks.ts +27 -0
  354. package/src/tests/fixtures/extensions/route-chat-channel-to-agent/manifest.json +9 -0
  355. package/src/tests/fixtures/openai-compatible-registry-extension.child.ts +146 -0
  356. package/src/tests/fixtures/runner-legacy-resume-prompt.ts +122 -0
  357. package/src/tests/github-handlers.test.ts +35 -0
  358. package/src/tests/harness-provider-resolution.test.ts +12 -0
  359. package/src/tests/heartbeat-checklist.test.ts +3 -2
  360. package/src/tests/heartbeat-jev-gate-credentials.test.ts +168 -0
  361. package/src/tests/heartbeat-jev-gate.test.ts +385 -0
  362. package/src/tests/hook-runtime-close.test.ts +0 -2
  363. package/src/tests/http-api-integration.test.ts +0 -25
  364. package/src/tests/migration-046-budgets.test.ts +17 -16
  365. package/src/tests/migration-217-chat-sdk-state.test.ts +113 -0
  366. package/src/tests/model-profile-backcompat.test.ts +327 -0
  367. package/src/tests/model-profile-providers.test.ts +302 -0
  368. package/src/tests/model-profile-runtime-secrets.test.ts +223 -0
  369. package/src/tests/model-profiles-api.test.ts +614 -0
  370. package/src/tests/model-tier-resolution.test.ts +93 -0
  371. package/src/tests/models-catalog.test.ts +42 -1
  372. package/src/tests/openai-compatible-providers.test.ts +134 -0
  373. package/src/tests/openai-compatible-registry-extension.test.ts +42 -0
  374. package/src/tests/opencode-adapter.test.ts +216 -2
  375. package/src/tests/opencode-helmcode-sdk.test.ts +201 -0
  376. package/src/tests/opencode-shell-env.test.ts +145 -0
  377. package/src/tests/openrouter-base-url.test.ts +101 -151
  378. package/src/tests/page-proxy.test.ts +1 -1
  379. package/src/tests/pages-browser-sdk.test.ts +76 -0
  380. package/src/tests/pages-feedback-keys.test.ts +121 -0
  381. package/src/tests/pages-feedback.test.ts +4 -1
  382. package/src/tests/paused-tasks-requested-by.test.ts +102 -0
  383. package/src/tests/pi-context-compaction.test.ts +193 -0
  384. package/src/tests/pi-custom-provider-fake-server.test.ts +442 -0
  385. package/src/tests/pi-mono-adapter.test.ts +385 -5
  386. package/src/tests/pi-shell-env.test.ts +170 -0
  387. package/src/tests/profile-claim.test.ts +269 -0
  388. package/src/tests/prompt-template-remaining.test.ts +35 -0
  389. package/src/tests/quickjs-compat.test.ts +213 -0
  390. package/src/tests/rbac-engine.test.ts +32 -3
  391. package/src/tests/runner-exit-after-tasks.test.ts +0 -4
  392. package/src/tests/runner-legacy-resume-prompt.test.ts +64 -0
  393. package/src/tests/script-connections.test.ts +6 -0
  394. package/src/tests/script-executor-conformance.test.ts +252 -1
  395. package/src/tests/script-executor-quickjs.test.ts +96 -0
  396. package/src/tests/script-runs-http.test.ts +89 -0
  397. package/src/tests/scripts-http.test.ts +217 -1
  398. package/src/tests/scripts-only-gating.test.ts +2 -3
  399. package/src/tests/scripts-runtime.test.ts +58 -40
  400. package/src/tests/secret-scrubber.test.ts +96 -0
  401. package/src/tests/seed-model-profiles.test.ts +174 -0
  402. package/src/tests/seed-scripts.test.ts +2 -2
  403. package/src/tests/seed-skills-bundled-files.test.ts +45 -53
  404. package/src/tests/seed-skills-retire.test.ts +182 -0
  405. package/src/tests/send-task-requester-gate.test.ts +58 -0
  406. package/src/tests/session-cost-recompute-unit.test.ts +46 -0
  407. package/src/tests/skill-get-name-resolution.test.ts +137 -0
  408. package/src/tests/skill-invocations.test.ts +4 -2
  409. package/src/tests/slack-blocks.test.ts +36 -0
  410. package/src/tests/slack-inline-output.test.ts +14 -0
  411. package/src/tests/slack-reaction-config.test.ts +20 -0
  412. package/src/tests/slack-render-v2.test.ts +37 -0
  413. package/src/tests/slack-thread-buffer.test.ts +98 -1
  414. package/src/tests/sonnet-5-5-registry.test.ts +9 -1
  415. package/src/tests/swarm-config-export-to-env.test.ts +267 -0
  416. package/src/tests/swarm-config-reserved-keys.test.ts +16 -0
  417. package/src/tests/swarm-diff.test.ts +1 -1
  418. package/src/tests/task-model-effort-rerun.test.ts +164 -0
  419. package/src/tests/task-session-prompts.test.ts +132 -0
  420. package/src/tests/task-tool-manifest.test.ts +14 -0
  421. package/src/tests/task-tools-ctx.test.ts +2 -0
  422. package/src/tests/tool-annotations.test.ts +6 -6
  423. package/src/tests/ui-logs-parser.test.ts +43 -0
  424. package/src/tests/used-by-route.test.ts +6 -0
  425. package/src/tests/vercel-ai-gateway.test.ts +149 -0
  426. package/src/tests/workflow-llm.test.ts +61 -0
  427. package/src/tests/workflow-swarm-script.test.ts +38 -0
  428. package/src/tests/workflow-wait-filter.test.ts +5 -3
  429. package/src/tools/chat-access.ts +41 -0
  430. package/src/tools/chat-open-dm.ts +59 -0
  431. package/src/tools/chat-post.ts +74 -0
  432. package/src/tools/chat-read-thread.ts +100 -0
  433. package/src/tools/chat-reply.ts +75 -0
  434. package/src/tools/create-page.ts +3 -13
  435. package/src/tools/model-catalog/index.ts +2 -1
  436. package/src/tools/model-profiles/list-model-profiles.ts +54 -0
  437. package/src/tools/register-chat-channel.ts +139 -0
  438. package/src/tools/request-human-input.ts +11 -3
  439. package/src/tools/script-apis.ts +5 -1
  440. package/src/tools/script-upsert.ts +4 -1
  441. package/src/tools/send-task.ts +48 -1
  442. package/src/tools/skills/skill-get.ts +17 -9
  443. package/src/tools/swarm-config/get-config.ts +2 -1
  444. package/src/tools/swarm-config/list-config.ts +2 -1
  445. package/src/tools/swarm-config/set-config.ts +16 -3
  446. package/src/tools/system-one-decide.ts +104 -0
  447. package/src/tools/task-action.ts +22 -1
  448. package/src/tools/tool-config.ts +14 -6
  449. package/src/types.ts +48 -3
  450. package/src/utils/config-env-export.ts +74 -0
  451. package/src/utils/credentials.test.ts +92 -0
  452. package/src/utils/credentials.ts +52 -4
  453. package/src/utils/harness-provider.ts +15 -6
  454. package/src/utils/runtime-model-catalog.ts +11 -1
  455. package/src/utils/secret-scrubber.ts +86 -10
  456. package/src/utils/task-tool-manifest.ts +19 -2
  457. package/src/utils/vcs-repo-match.ts +29 -0
  458. package/src/workflows/executors/raw-llm.ts +8 -3
  459. package/src/workflows/executors/swarm-script.ts +1 -0
  460. package/src/workflows/executors/system-one-decision.ts +21 -11
  461. package/src/workflows/executors/workflow-llm.ts +22 -0
  462. package/src/workflows/index.ts +21 -12
  463. package/templates/extensions/README.md +1 -0
  464. package/templates/extensions/comms-adherence-gate/README.md +88 -0
  465. package/templates/extensions/comms-adherence-gate/hooks.ts +489 -0
  466. package/templates/extensions/comms-adherence-gate/manifest.json +8 -0
  467. package/templates/skills/agent-browser/SKILL.md +1 -1
  468. package/templates/skills/agent-browser/content.md +1 -1
  469. package/templates/skills/apps/SKILL.md +8 -1
  470. package/templates/skills/apps/content.md +8 -1
  471. package/templates/skills/chat-sdk/config.json +14 -0
  472. package/templates/skills/chat-sdk/content.md +84 -0
  473. package/templates/skills/composio-gmail/content.md +7 -0
  474. package/templates/skills/composio-google-docs/content.md +1 -1
  475. package/templates/skills/email-domain-health/config.json +14 -0
  476. package/templates/skills/email-domain-health/content.md +131 -0
  477. package/templates/skills/kv-storage/content.md +2 -2
  478. package/templates/skills/pages/content.md +8 -10
  479. package/templates/skills/swarm-extensions/content.md +2 -0
  480. package/templates/skills/swarm-scripts/SKILL.md +4 -2
  481. package/templates/skills/swarm-scripts/config.json +1 -1
  482. package/templates/skills/swarm-scripts/content.md +4 -2
  483. package/templates/skills/workflow-iterate/content.md +38 -0
  484. package/dist/artifact-eczsknc3.js +0 -3843
  485. package/dist/cli-42qhyvwx.js +0 -33
  486. package/dist/cli-bd957dgb.js +0 -153
  487. package/dist/cli-q51yprfq.js +0 -1104
  488. package/dist/cli-t1ze64h3.js +0 -811
  489. package/dist/cli-tcc4wd5d.js +0 -56471
  490. package/dist/index-9ch1n761.js +0 -1102
  491. package/src/artifact-sdk/index.ts +0 -2
  492. package/src/artifact-sdk/localtunnel.d.ts +0 -20
  493. package/src/artifact-sdk/port.ts +0 -12
  494. package/src/artifact-sdk/server.ts +0 -183
  495. package/src/artifact-sdk/tunnel.ts +0 -19
  496. package/src/commands/artifact.ts +0 -249
  497. package/src/http/ecosystem.ts +0 -77
  498. package/src/tests/artifact-commands.test.ts +0 -92
  499. package/src/tests/artifact-sdk.test.ts +0 -793
  500. package/src/tests/register-service-security.test.ts +0 -194
  501. package/src/tools/list-services.ts +0 -107
  502. package/src/tools/register-service.ts +0 -177
  503. package/src/tools/unregister-service.ts +0 -73
  504. package/src/tools/update-service-status.ts +0 -96
  505. package/templates/skills/artifacts/config.json +0 -14
  506. package/templates/skills/artifacts/content.md +0 -344
  507. package/templates/skills/artifacts/files/examples/approval-flow.ts +0 -34
  508. package/templates/skills/artifacts/files/examples/hono-dashboard.ts +0 -31
  509. package/templates/skills/artifacts/files/examples/multi-artifact.ts +0 -20
  510. package/templates/skills/artifacts/files/examples/static-report.sh +0 -17
  511. /package/src/{artifact-sdk → pages}/browser-sdk.ts +0 -0
@@ -0,0 +1,164 @@
1
+ // What a task records about the model and effort it ran on, and what a re-run
2
+ // of the same row carries over. Regression for: an agent's model/effort changed
3
+ // in the dashboard, and the next run of a task kept the previous values.
4
+
5
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
6
+ import { unlink } from "node:fs/promises";
7
+ import {
8
+ closeDb,
9
+ createAgent,
10
+ createTaskExtended,
11
+ getTaskById,
12
+ initDb,
13
+ updateTaskClaudeSessionId,
14
+ } from "../be/db";
15
+ import { effectiveReasoningEffort } from "../providers/reasoning-effort";
16
+ import { resumedTaskModelSelection, taskModelPin } from "../tasks/model-pin";
17
+
18
+ const TEST_DB_PATH = "./test-task-model-effort-rerun.sqlite";
19
+
20
+ async function removeDbFiles(path: string): Promise<void> {
21
+ for (const suffix of ["", "-wal", "-shm"]) {
22
+ try {
23
+ await unlink(path + suffix);
24
+ } catch (error) {
25
+ if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
26
+ }
27
+ }
28
+ }
29
+
30
+ beforeAll(async () => {
31
+ await removeDbFiles(TEST_DB_PATH);
32
+ initDb(TEST_DB_PATH);
33
+ });
34
+
35
+ afterAll(async () => {
36
+ closeDb();
37
+ await removeDbFiles(TEST_DB_PATH);
38
+ });
39
+
40
+ describe("taskModelPin", () => {
41
+ test("a model reported by a previous session is not a pin", () => {
42
+ expect(taskModelPin({ model: "claude-opus-5-5", claudeSessionId: "s1" })).toBeUndefined();
43
+ });
44
+
45
+ test("a model a tier layer chose is not a pin", () => {
46
+ expect(
47
+ taskModelPin({
48
+ model: "claude-opus-5-5",
49
+ modelTier: "smart",
50
+ resolvedModel: "claude-opus-5-5",
51
+ modelSource: "tier-default",
52
+ }),
53
+ ).toBeUndefined();
54
+ });
55
+
56
+ test("the creator's model on an unclaimed row is the pin", () => {
57
+ expect(taskModelPin({ model: "claude-sonnet-5-5" })).toBe("claude-sonnet-5-5");
58
+ });
59
+
60
+ test("a claimed pin keeps its alias", () => {
61
+ expect(
62
+ taskModelPin({
63
+ model: "claude-opus-5-5",
64
+ modelSource: "model",
65
+ modelAlias: "latest:anthropic/opus",
66
+ claudeSessionId: "s1",
67
+ }),
68
+ ).toBe("latest:anthropic/opus");
69
+ });
70
+ });
71
+
72
+ describe("resumedTaskModelSelection", () => {
73
+ test("a paused unpinned task resumes without the previous run's model or claim record", () => {
74
+ expect(
75
+ resumedTaskModelSelection({
76
+ model: "claude-opus-5-5",
77
+ resolvedModel: "claude-opus-5-5",
78
+ modelSource: "tier-config",
79
+ modelTier: "smart",
80
+ claudeSessionId: "s1",
81
+ }),
82
+ ).toEqual({ model: undefined, resolvedModel: undefined, modelSource: undefined });
83
+ });
84
+
85
+ test("a paused pinned task resumes on its pin", () => {
86
+ expect(
87
+ resumedTaskModelSelection({
88
+ model: "claude-sonnet-5-5",
89
+ resolvedModel: "claude-sonnet-5-5",
90
+ modelSource: "model",
91
+ claudeSessionId: "s1",
92
+ }),
93
+ ).toEqual({
94
+ model: "claude-sonnet-5-5",
95
+ resolvedModel: "claude-sonnet-5-5",
96
+ modelSource: "model",
97
+ });
98
+ });
99
+ });
100
+
101
+ describe("effectiveReasoningEffort", () => {
102
+ test("unset effort records nothing", () => {
103
+ expect(effectiveReasoningEffort("claude", "claude-opus-5-5", undefined)).toBeNull();
104
+ });
105
+
106
+ test("harnesses without effort support record nothing", () => {
107
+ expect(effectiveReasoningEffort("devin", "", "high")).toBeNull();
108
+ });
109
+
110
+ test("a level the model cannot take is dropped, the same as the adapter does", () => {
111
+ expect(effectiveReasoningEffort("claude", "not-a-reasoning-model", "high")).toBeNull();
112
+ });
113
+ });
114
+
115
+ describe("session report records the effort", () => {
116
+ test("the effort the session ran at lands on resolvedEffort, and a re-run replaces it", async () => {
117
+ const agent = await createAgent({ name: "effort-rerun", isLead: false, status: "idle" });
118
+ const task = await createTaskExtended("work", { agentId: agent.id });
119
+
120
+ // First run: the agent's REASONING_EFFORT_OVERRIDE was high.
121
+ await updateTaskClaudeSessionId(
122
+ task.id,
123
+ "run-1",
124
+ "claude",
125
+ undefined,
126
+ "claude-opus-5-5",
127
+ undefined,
128
+ undefined,
129
+ "high",
130
+ );
131
+ let row = await getTaskById(task.id);
132
+ expect(row?.resolvedEffort).toBe("high");
133
+ // The requested-effort column stays the creator's value, not a pin.
134
+ expect(row?.effort).toBeUndefined();
135
+
136
+ // Effort changed to low in the dashboard; the next run records low.
137
+ await updateTaskClaudeSessionId(
138
+ task.id,
139
+ "run-2",
140
+ "claude",
141
+ undefined,
142
+ "claude-opus-5-5",
143
+ undefined,
144
+ undefined,
145
+ "low",
146
+ );
147
+ row = await getTaskById(task.id);
148
+ expect(row?.resolvedEffort).toBe("low");
149
+
150
+ // Effort set back to Auto: the harness default applied, nothing to show.
151
+ await updateTaskClaudeSessionId(
152
+ task.id,
153
+ "run-3",
154
+ "claude",
155
+ undefined,
156
+ "claude-opus-5-5",
157
+ undefined,
158
+ undefined,
159
+ null,
160
+ );
161
+ row = await getTaskById(task.id);
162
+ expect(row?.resolvedEffort).toBeUndefined();
163
+ });
164
+ });
@@ -0,0 +1,132 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import {
3
+ buildRequesterProfilePrompt,
4
+ buildTaskSessionPrompts,
5
+ type TaskSessionContext,
6
+ } from "../commands/runner";
7
+ import { type BasePromptArgs, getTaskContextPrompt } from "../prompts/base-prompt";
8
+ import type { ProviderName } from "../types";
9
+
10
+ // The provider caches the system prompt as a prefix, so any per-task byte in it
11
+ // re-bills the whole prompt on the first call of every session. These tests pin
12
+ // the split: one agent, different tasks, requesters and repos, same system
13
+ // prompt; the per-task context still reaches the model, in the task prompt.
14
+
15
+ const agentArgs = (
16
+ provider: ProviderName,
17
+ extra: Partial<BasePromptArgs> = {},
18
+ ): BasePromptArgs => ({
19
+ role: "worker",
20
+ agentId: "11111111-2222-3333-4444-555555555555",
21
+ name: "Ada",
22
+ provider,
23
+ traits: { hasMcp: true, hasLocalEnvironment: true, nativeSkillDiscovery: provider !== "codex" },
24
+ soulMd: "You are Ada.",
25
+ claudeMd: "# Agent: Ada\n\nAlways run the tests before you push.",
26
+ skillsSummary: [{ name: "commit", description: "Create a commit" }],
27
+ mcpServers: ["linear"],
28
+ ...extra,
29
+ });
30
+
31
+ const sessionA: TaskSessionContext = {
32
+ repoContext: {
33
+ clonePath: "/workspace/personal/repos/alpha",
34
+ claudeMd: "Alpha repo rules.",
35
+ warning: "Alpha is behind origin",
36
+ autoStashes: [{ ref: "stash@{0}", message: "WIP alpha" }],
37
+ guidelines: {
38
+ prChecks: ["bun run lint"],
39
+ mergeChecks: ["CI green"],
40
+ allowMerge: false,
41
+ review: ["Check the db boundary"],
42
+ },
43
+ },
44
+ slackContext: { channelId: "C0ALPHA", threadTs: "111.222" },
45
+ requestedBy: {
46
+ name: "Taras",
47
+ role: "CTO",
48
+ notes: "Lead with the answer.",
49
+ comms: { verbosity: "terse" },
50
+ },
51
+ cwdWarning: 'Note: The task requested working directory "/nope" but it does not exist.',
52
+ };
53
+
54
+ const sessionB: TaskSessionContext = {
55
+ repoContext: { clonePath: "/workspace/personal/repos/beta", guidelines: null },
56
+ requestedBy: { name: "Eze", role: "CEO", comms: { language: "Spanish" } },
57
+ };
58
+
59
+ // A heartbeat: no repository, no requester, no working directory.
60
+ const sessionC: TaskSessionContext = {};
61
+
62
+ async function build(args: BasePromptArgs, context: TaskSessionContext, taskPrompt: string) {
63
+ return buildTaskSessionPrompts({
64
+ basePromptArgs: args,
65
+ additionalSystemPrompt: "Operator note: be careful.",
66
+ taskPrompt,
67
+ context,
68
+ });
69
+ }
70
+
71
+ describe("buildTaskSessionPrompts", () => {
72
+ for (const provider of ["claude", "codex", "opencode", "pi"] as const) {
73
+ test(`${provider}: the system prompt is byte-identical across tasks, requesters and repos`, async () => {
74
+ const a = await build(agentArgs(provider), sessionA, "/work-on-task aaa\n\nTask: alpha");
75
+ const b = await build(agentArgs(provider), sessionB, "/work-on-task bbb\n\nTask: beta");
76
+ const c = await build(agentArgs(provider), sessionC, "/work-on-task ccc\n\nTask: heartbeat");
77
+
78
+ expect(a.systemPrompt).toBe(b.systemPrompt);
79
+ expect(a.systemPrompt).toBe(c.systemPrompt);
80
+ expect(a.systemPrompt).toEndWith("\n\nOperator note: be careful.");
81
+ for (const perTask of ["## Repository", "## Requester Profile", "Taras", "Eze", "/nope"]) {
82
+ expect(a.systemPrompt).not.toContain(perTask);
83
+ }
84
+ });
85
+ }
86
+
87
+ test("a scripts-only worker keeps the system prompt stable across Slack and non-Slack tasks", async () => {
88
+ const keys = ["SLACK_DISABLE", "SLACK_BOT_TOKEN", "SLACK_APP_TOKEN"] as const;
89
+ const saved = keys.map((key) => [key, process.env[key]] as const);
90
+ delete process.env.SLACK_DISABLE;
91
+ process.env.SLACK_BOT_TOKEN = "xoxb-test-token";
92
+ process.env.SLACK_APP_TOKEN = "xapp-test-token";
93
+ try {
94
+ const args = agentArgs("claude", { scriptsOnly: true });
95
+ const a = await build(args, sessionA, "task a");
96
+ const c = await build(args, sessionC, "task c");
97
+ expect(a.systemPrompt).toBe(c.systemPrompt);
98
+ expect(a.systemPrompt).not.toContain("C0ALPHA");
99
+ expect(a.prompt).toContain("## Slack (scripts-only)");
100
+ expect(a.prompt).toContain("C0ALPHA");
101
+ } finally {
102
+ for (const [key, value] of saved) {
103
+ if (value === undefined) delete process.env[key];
104
+ else process.env[key] = value;
105
+ }
106
+ }
107
+ });
108
+
109
+ test("the per-task context reaches the task prompt with the same text, after the task", async () => {
110
+ const args = agentArgs("opencode");
111
+ const taskPrompt = "/work-on-task aaa\n\nTask: alpha";
112
+ const { prompt } = await build(args, sessionA, taskPrompt);
113
+
114
+ const repoBlock = await getTaskContextPrompt({ ...args, repoContext: sessionA.repoContext });
115
+ const profile = await buildRequesterProfilePrompt(sessionA.requestedBy);
116
+ expect(repoBlock).toContain("## Repository");
117
+ expect(repoBlock).toContain("Alpha repo rules.");
118
+ expect(profile).toContain("This task was requested by Taras (CTO).");
119
+
120
+ expect(prompt).toStartWith(`${taskPrompt}\n\n`);
121
+ expect(prompt).toContain(repoBlock);
122
+ expect(prompt).toContain(profile);
123
+ expect(prompt).toEndWith(sessionA.cwdWarning ?? "");
124
+ expect(prompt.indexOf(repoBlock)).toBeLessThan(prompt.indexOf(profile));
125
+ expect(prompt).not.toMatch(/\n{3,}/);
126
+ });
127
+
128
+ test("a task with no per-task context keeps its prompt unchanged", async () => {
129
+ const { prompt } = await build(agentArgs("claude"), sessionC, "/work-on-task ccc");
130
+ expect(prompt).toBe("/work-on-task ccc");
131
+ });
132
+ });
@@ -27,6 +27,20 @@ describe("task tool manifests", () => {
27
27
  ]);
28
28
  });
29
29
 
30
+ test("chat-reply only preloads for a task carrying a chat-sdk contextKey", () => {
31
+ const manifest = parseTaskToolManifest('{"taskTypes":{"review":["chat-reply","script-run"]}}');
32
+ expect(selectTaskTools(manifest, { taskType: "review" })).toEqual(["script-run"]);
33
+ expect(
34
+ selectTaskTools(manifest, { taskType: "review", contextKey: "task:slack:C123:1700.1" }),
35
+ ).toEqual(["script-run"]);
36
+ expect(
37
+ selectTaskTools(manifest, {
38
+ taskType: "review",
39
+ contextKey: "task:chat-sdk:telegram:555001",
40
+ }),
41
+ ).toEqual(["chat-reply", "script-run"]);
42
+ });
43
+
30
44
  test("config writes reject unknown tools, invalid shapes, and oversized selections", () => {
31
45
  for (const value of [
32
46
  "not json",
@@ -137,8 +137,10 @@ describe("task tool ctx", () => {
137
137
  });
138
138
  await updateAgentProfile(lead.id, { capabilities: ["merge"] });
139
139
  await updateAgentProfile(underprivilegedLead.id, { capabilities: ["typescript"] });
140
+ // The sender created the parent, so it may continue it (task.parent.attach).
140
141
  const parent = await createTaskExtended("privileged parent", {
141
142
  agentId: lead.id,
143
+ creatorAgentId: sender.id,
142
144
  routingAffinity: { capabilities: ["merge"], leadOnly: true },
143
145
  });
144
146
 
@@ -116,7 +116,6 @@ describe("Tool Annotations & Classification", () => {
116
116
  "delete-config",
117
117
  "delete-workflow",
118
118
  "delete-page",
119
- "unregister-service",
120
119
  "slack-delete",
121
120
  "slack-archive-channel",
122
121
  ];
@@ -135,7 +134,6 @@ describe("Tool Annotations & Classification", () => {
135
134
  "get-tasks",
136
135
  "get-config",
137
136
  "list-channels",
138
- "list-services",
139
137
  "list-schedules",
140
138
  "list-config",
141
139
  "memory-get",
@@ -350,7 +348,7 @@ describe("Tool Annotations & Classification", () => {
350
348
  expect(missing).toEqual([]);
351
349
  });
352
350
 
353
- test("service tools are all deferred", () => {
351
+ test("the removed PM2 service registry tools stay gone", () => {
354
352
  const serviceTools = [
355
353
  "register-service",
356
354
  "unregister-service",
@@ -358,7 +356,8 @@ describe("Tool Annotations & Classification", () => {
358
356
  "update-service-status",
359
357
  ];
360
358
  for (const tool of serviceTools) {
361
- expect(DEFERRED_TOOLS.has(tool)).toBe(true);
359
+ expect(DEFERRED_TOOLS.has(tool)).toBe(false);
360
+ expect(tools[tool]).toBeUndefined();
362
361
  }
363
362
  });
364
363
 
@@ -376,9 +375,10 @@ describe("Tool Annotations & Classification", () => {
376
375
  // We expect all tools to be registered when all capabilities are enabled
377
376
  // (this suite forces ALL_CAPABILITIES, including default-disabled ones).
378
377
  // Includes 11 skill tools, 7 MCP server tools, reusable script tools, and the
379
- // native Kapso/WhatsApp tools (register/unregister number + send/reply message).
378
+ // native Kapso/WhatsApp tools (register/unregister number + send/reply message),
379
+ // and the six Chat SDK tools (chat-* + register/unregister-chat-channel).
380
380
  expect(count).toBeGreaterThanOrEqual(45);
381
- expect(count).toBeLessThanOrEqual(157);
381
+ expect(count).toBeLessThanOrEqual(163);
382
382
  });
383
383
 
384
384
  test("core tools are fewer than deferred tools", () => {
@@ -1154,6 +1154,49 @@ describe("ui logs parser", () => {
1154
1154
  ]);
1155
1155
  });
1156
1156
 
1157
+ test("keeps a Claude sub-agent's parent_tool_use_id on its messages", () => {
1158
+ const messages = parseSessionLogs([
1159
+ log("spawn", "claude", 1, {
1160
+ type: "assistant",
1161
+ message: {
1162
+ role: "assistant",
1163
+ content: [{ type: "tool_use", id: "toolu_agent_1", name: "Agent", input: {} }],
1164
+ },
1165
+ }),
1166
+ log("child-call", "claude", 2, {
1167
+ type: "assistant",
1168
+ parent_tool_use_id: "toolu_agent_1",
1169
+ message: {
1170
+ role: "assistant",
1171
+ content: [
1172
+ { type: "thinking", thinking: "Look at the fixtures." },
1173
+ { type: "tool_use", id: "toolu_read_1", name: "Read", input: { file_path: "a.ts" } },
1174
+ ],
1175
+ },
1176
+ }),
1177
+ log("child-result", "claude", 3, {
1178
+ type: "user",
1179
+ parent_tool_use_id: "toolu_agent_1",
1180
+ message: {
1181
+ role: "user",
1182
+ content: [{ type: "tool_result", tool_use_id: "toolu_read_1", content: "ok" }],
1183
+ },
1184
+ }),
1185
+ log("main-text", "claude", 4, {
1186
+ type: "assistant",
1187
+ parent_tool_use_id: null,
1188
+ message: { role: "assistant", content: [{ type: "text", text: "Done." }] },
1189
+ }),
1190
+ ]);
1191
+
1192
+ expect(messages.map((m) => [m.id, m.parentToolUseId])).toEqual([
1193
+ ["spawn", undefined],
1194
+ ["child-call", "toolu_agent_1"],
1195
+ ["child-result", "toolu_agent_1"],
1196
+ ["main-text", undefined],
1197
+ ]);
1198
+ });
1199
+
1157
1200
  test("extracts a completed Claude foreground Agent run from its paired result", () => {
1158
1201
  const runs = extractSubagentRuns([
1159
1202
  log("spawn", "claude", 1, {
@@ -61,10 +61,14 @@ const configValue = async (key: string) =>
61
61
 
62
62
  describe("POST /api/used-by", () => {
63
63
  const originalFetch = globalThis.fetch;
64
+ const savedEnvWebsite = process.env.SWARM_ORG_WEBSITE;
65
+ const savedEnvName = process.env.SWARM_ORG_NAME;
64
66
  let proxied: Array<{ url: string; body: Record<string, unknown> }>;
65
67
  let proxyStatus: number;
66
68
 
67
69
  beforeAll(async () => {
70
+ delete process.env.SWARM_ORG_WEBSITE;
71
+ delete process.env.SWARM_ORG_NAME;
68
72
  await removeDbFiles();
69
73
  initDb(TEST_DB_PATH);
70
74
  await createAgent({ id: WORKER_ID, name: "Worker", isLead: false, status: "idle" });
@@ -75,6 +79,8 @@ describe("POST /api/used-by", () => {
75
79
  });
76
80
 
77
81
  afterAll(async () => {
82
+ if (savedEnvWebsite !== undefined) process.env.SWARM_ORG_WEBSITE = savedEnvWebsite;
83
+ if (savedEnvName !== undefined) process.env.SWARM_ORG_NAME = savedEnvName;
78
84
  globalThis.fetch = originalFetch;
79
85
  closeDb();
80
86
  await removeDbFiles();
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Vercel AI Gateway (`vercel/<vendor>/<model>`) on pi and opencode: the full
3
+ * chat catalog, and the one endpoint no env var or hand-written block can move.
4
+ */
5
+ import { afterEach, describe, expect, test } from "bun:test";
6
+ import { mkdtemp, rm } from "node:fs/promises";
7
+ import { tmpdir } from "node:os";
8
+ import { join } from "node:path";
9
+ import {
10
+ isAgentChatModel,
11
+ OPENAI_COMPATIBLE_PROVIDERS,
12
+ openAICompatibleBaseUrlVars,
13
+ providerBaseUrl,
14
+ providerForModel,
15
+ withRegistryCatalogSections,
16
+ } from "@desplega/model-catalog";
17
+ import { buildCatalogEntries } from "../be/model-catalog-store";
18
+ import { isKnownCatalogModel } from "../be/model-validation";
19
+ import { buildModelsCatalog } from "../be/models-catalog";
20
+ import { loadModelsDevCache, type ModelsDevCache } from "../be/modelsdev-cache";
21
+ import snapshot from "../be/modelsdev-cache.json";
22
+ import { applyProviderKeySplice, type OpencodeConfig } from "../providers/opencode-adapter";
23
+ import { customProviderModelsBlock, writeSessionModelsJson } from "../providers/pi-mono-adapter";
24
+
25
+ const VERCEL = OPENAI_COMPATIBLE_PROVIDERS.vercel;
26
+ const ENDPOINT = "https://ai-gateway.vercel.sh/v1";
27
+ const WRONG = "https://ai-gateway.vercel.sh";
28
+ const raw = snapshot as unknown as ModelsDevCache;
29
+ const rawVercel = (raw.vercel?.models ?? {}) as Record<
30
+ string,
31
+ Parameters<typeof isAgentChatModel>[0]
32
+ >;
33
+ const chatIds = Object.keys(rawVercel).filter((id) => isAgentChatModel(rawVercel[id] ?? {}));
34
+
35
+ describe("Vercel AI Gateway catalog", () => {
36
+ test("the snapshot carries non-chat rows, so the filter is exercised", () => {
37
+ expect(rawVercel["voyage/voyage-code-3"]).toBeDefined();
38
+ expect(chatIds.length).toBeGreaterThan(200);
39
+ expect(chatIds.length).toBeLessThan(Object.keys(rawVercel).length);
40
+ });
41
+
42
+ test("pi lists every AI Gateway chat model, and no embedding or video model", () => {
43
+ const block = customProviderModelsBlock(VERCEL);
44
+ const ids = block.models.map((m) => m.id as string);
45
+ expect(ids.sort()).toEqual([...chatIds].sort());
46
+ expect(ids).toContain("zai/glm-5.3");
47
+ expect(ids).toContain("moonshotai/kimi-k3");
48
+ expect(ids).toContain("anthropic/claude-sonnet-4.6");
49
+ expect(ids).not.toContain("voyage/voyage-code-3");
50
+ expect(ids).not.toContain("klingai/kling-v3.0-t2v");
51
+ const glm = block.models.find((m) => m.id === "zai/glm-5.3");
52
+ expect(glm).toMatchObject({ contextWindow: 1000000, maxTokens: 1000000, reasoning: true });
53
+ expect(block).toMatchObject({
54
+ baseUrl: ENDPOINT,
55
+ api: "openai-completions",
56
+ apiKey: "$AI_GATEWAY_API_KEY",
57
+ });
58
+ });
59
+
60
+ test("picker, live and validation catalogs drop non-chat Vercel rows", () => {
61
+ const merged = withRegistryCatalogSections(raw);
62
+ expect(Object.keys(merged.vercel?.models ?? {}).sort()).toEqual([...chatIds].sort());
63
+ // OpenRouter is not a registry provider and keeps its full section.
64
+ expect(Object.keys(merged.openrouter?.models ?? {}).length).toBe(
65
+ Object.keys(raw.openrouter?.models ?? {}).length,
66
+ );
67
+
68
+ const live = buildCatalogEntries(raw, 0).filter((e) => e.provider === "vercel");
69
+ expect(live.map((e) => e.modelId).sort()).toEqual([...chatIds].sort());
70
+
71
+ const cache = loadModelsDevCache();
72
+ if (!cache) throw new Error("snapshot missing");
73
+ const catalog = buildModelsCatalog(cache) as Parameters<typeof isKnownCatalogModel>[1];
74
+ expect(isKnownCatalogModel("vercel/zai/glm-5.3", catalog)).toBe(true);
75
+ expect(isKnownCatalogModel("vercel/anthropic/claude-sonnet-4.6", catalog)).toBe(true);
76
+ expect(isKnownCatalogModel("vercel/voyage/voyage-code-3", catalog)).toBe(false);
77
+ expect(providerForModel("vercel/moonshotai/kimi-k3")?.id).toBe("vercel");
78
+ });
79
+ });
80
+
81
+ describe("Vercel AI Gateway endpoint is fixed", () => {
82
+ const dirs: string[] = [];
83
+ afterEach(async () => {
84
+ await Promise.all(dirs.splice(0).map((d) => rm(d, { recursive: true, force: true })));
85
+ });
86
+
87
+ test("VERCEL_BASE_URL is never read or forwarded", () => {
88
+ expect(VERCEL.baseUrl).toBe(ENDPOINT);
89
+ expect(providerBaseUrl(VERCEL, { VERCEL_BASE_URL: WRONG })).toBe(ENDPOINT);
90
+ expect(openAICompatibleBaseUrlVars()).not.toContain("VERCEL_BASE_URL");
91
+ expect(openAICompatibleBaseUrlVars()).toContain("NAN_BASE_URL");
92
+ });
93
+
94
+ test("opencode gets the key and no baseURL, even over a configured one", () => {
95
+ const key = crypto.randomUUID();
96
+ const config: OpencodeConfig = {
97
+ model: "vercel/zai/glm-5.3",
98
+ provider: { vercel: { options: { baseURL: WRONG } } },
99
+ };
100
+ const picked = applyProviderKeySplice(config, config.model, {
101
+ AI_GATEWAY_API_KEY: key,
102
+ VERCEL_BASE_URL: WRONG,
103
+ });
104
+ expect(picked?.key).toBe(key);
105
+ expect(config.provider?.vercel).toEqual({ env: [], options: { apiKey: key } });
106
+ });
107
+
108
+ test("pi gets the Chat Completions endpoint over VERCEL_BASE_URL and a hand-written block", async () => {
109
+ const agentDir = await mkdtemp(join(tmpdir(), "pi-vercel-"));
110
+ dirs.push(agentDir);
111
+ await Bun.write(
112
+ join(agentDir, "models.json"),
113
+ JSON.stringify({
114
+ providers: { vercel: { baseUrl: WRONG, api: "anthropic-messages", headers: { a: "b" } } },
115
+ }),
116
+ );
117
+ const env = { AI_GATEWAY_API_KEY: "k", VERCEL_BASE_URL: WRONG };
118
+ const session = await writeSessionModelsJson(agentDir, env, "vercel/vendor/brand-new-model");
119
+ try {
120
+ expect(session.modelsPath).toBeDefined();
121
+ const written = await Bun.file(session.modelsPath as string).json();
122
+ const block = written.providers.vercel;
123
+ expect(block.baseUrl).toBe(ENDPOINT);
124
+ expect(block.api).toBe("openai-completions");
125
+ expect(block.headers).toEqual({ a: "b" });
126
+ const ids = (block.models as { id: string }[]).map((m) => m.id);
127
+ expect(ids).toContain("zai/glm-5.3");
128
+ // An id the catalog does not know yet still resolves (bare entry).
129
+ expect(ids).toContain("vendor/brand-new-model");
130
+ } finally {
131
+ await session.dispose();
132
+ }
133
+ });
134
+
135
+ test("pi without a shared block also lands on the endpoint", async () => {
136
+ const agentDir = await mkdtemp(join(tmpdir(), "pi-vercel-"));
137
+ dirs.push(agentDir);
138
+ const session = await writeSessionModelsJson(agentDir, {
139
+ AI_GATEWAY_API_KEY: "k",
140
+ VERCEL_BASE_URL: WRONG,
141
+ });
142
+ try {
143
+ const written = await Bun.file(session.modelsPath as string).json();
144
+ expect(written.providers.vercel.baseUrl).toBe(ENDPOINT);
145
+ } finally {
146
+ await session.dispose();
147
+ }
148
+ });
149
+ });
@@ -77,6 +77,67 @@ describe("resolveWorkflowLlmConfig", () => {
77
77
  expect(config.model).toBe("gpt-5.4");
78
78
  });
79
79
 
80
+ test("routes a registry-provider model to that provider, not OpenRouter", async () => {
81
+ const config = await resolveWorkflowLlmConfig("nan/qwen3.8-flash", {
82
+ OPENROUTER_API_KEY: "sk-or-test",
83
+ NAN_API_KEY: "nan-test",
84
+ });
85
+
86
+ expect(config).toEqual({
87
+ apiKey: "nan-test",
88
+ baseURL: "https://api.nan.builders/v1",
89
+ headers: {},
90
+ model: "qwen3.8-flash",
91
+ });
92
+ });
93
+
94
+ test("reads a registry provider's alias key and base URL override", async () => {
95
+ const config = await resolveWorkflowLlmConfig("nan/deepseek-v4-flash", {
96
+ HELMCODE_API_NAN: "nan-alias",
97
+ NAN_BASE_URL: "https://nan.example.test/v1/",
98
+ });
99
+
100
+ expect(config.apiKey).toBe("nan-alias");
101
+ expect(config.baseURL).toBe("https://nan.example.test/v1");
102
+ expect(config.model).toBe("deepseek-v4-flash");
103
+ });
104
+
105
+ test("hands the SDK one key from a canonical NaN key pool", async () => {
106
+ const pool = ["nan-pool-a", "nan-pool-b", "nan-pool-c"];
107
+ for (let i = 0; i < 20; i++) {
108
+ const config = await resolveWorkflowLlmConfig("nan/qwen3.8-flash", {
109
+ NAN_API_KEY: ` ${pool.join(", ")} `,
110
+ });
111
+ expect(pool).toContain(config.apiKey);
112
+ }
113
+ });
114
+
115
+ test("hands the SDK one key from an alias NaN key pool", async () => {
116
+ const pool = ["nan-alias-a", "nan-alias-b"];
117
+ for (let i = 0; i < 20; i++) {
118
+ const config = await resolveWorkflowLlmConfig("nan/deepseek-v4-flash", {
119
+ HELMCODE_API_NAN: pool.join(","),
120
+ });
121
+ expect(pool).toContain(config.apiKey);
122
+ }
123
+ });
124
+
125
+ test("fails a registry-provider model without its key instead of falling back to OpenRouter", async () => {
126
+ await expect(
127
+ resolveWorkflowLlmConfig("nan/qwen3.8-flash", { OPENROUTER_API_KEY: "sk-or-test" }),
128
+ ).rejects.toThrow("No NaN credential found for nan/qwen3.8-flash. Set NAN_API_KEY.");
129
+ });
130
+
131
+ test("keeps OpenRouter vendor ids on OpenRouter", async () => {
132
+ const config = await resolveWorkflowLlmConfig("deepseek/deepseek-v4.1-flash", {
133
+ OPENROUTER_API_KEY: "sk-or-test",
134
+ NAN_API_KEY: "nan-test",
135
+ });
136
+
137
+ expect(config.apiKey).toBe("sk-or-test");
138
+ expect(config.model).toBe("deepseek/deepseek-v4.1-flash");
139
+ });
140
+
80
141
  test("rejects credential kinds without an OpenAI-compatible endpoint", async () => {
81
142
  await expect(
82
143
  resolveWorkflowLlmConfig(undefined, { ANTHROPIC_API_KEY: "example-sk-ant-test" }),