@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,269 @@
1
+ // Claim-time resolution of model profiles: a task's own `profile`, the agent's
2
+ // default (MODEL_PROFILE), and how both sit in the precedence next to a raw
3
+ // `model` and a `modelTier`. Drives the real `/api/poll` handler.
4
+
5
+ import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
6
+ import { unlink } from "node:fs/promises";
7
+ import {
8
+ closeDb,
9
+ createAgent,
10
+ createTaskExtended,
11
+ getDbClient,
12
+ getTaskById,
13
+ initDb,
14
+ upsertSwarmConfig,
15
+ } from "../be/db";
16
+ import { createModelProfile, getAgentDefaultProfileName } from "../be/db/model-profiles";
17
+ import { recordClaimModelResolution } from "../be/model-tier-resolution";
18
+ import { handlePoll } from "../http/poll";
19
+ import { taskModelPin } from "../tasks/model-pin";
20
+ import { inheritedModelSelection } from "../tasks/worker-follow-up";
21
+
22
+ const TEST_DB_PATH = "./test-profile-claim.sqlite";
23
+
24
+ async function removeDbFiles(path: string): Promise<void> {
25
+ for (const suffix of ["", "-wal", "-shm"]) {
26
+ try {
27
+ await unlink(path + suffix);
28
+ } catch (error) {
29
+ if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
30
+ }
31
+ }
32
+ }
33
+
34
+ type Trigger = { type: string; taskId: string; task: Record<string, unknown> };
35
+
36
+ async function callPoll(agentId: string): Promise<Trigger | null> {
37
+ let bodyStr = "";
38
+ const req = {
39
+ method: "GET",
40
+ url: "/api/poll",
41
+ headers: { "x-agent-id": agentId },
42
+ } as unknown as Parameters<typeof handlePoll>[0];
43
+ const res = {
44
+ setHeader() {},
45
+ writeHead() {},
46
+ end(body?: string) {
47
+ bodyStr = body ?? "";
48
+ },
49
+ } as unknown as Parameters<typeof handlePoll>[1];
50
+ await handlePoll(req, res, ["api", "poll"], new URLSearchParams(), agentId);
51
+ return (JSON.parse(bodyStr) as { trigger: Trigger | null }).trigger;
52
+ }
53
+
54
+ async function claimRecord(id: string) {
55
+ return await getDbClient().get<{
56
+ resolvedModel: string | null;
57
+ modelSource: string | null;
58
+ resolvedProfile: string | null;
59
+ }>("SELECT resolvedModel, modelSource, resolvedProfile FROM agent_tasks WHERE id = ?", [id]);
60
+ }
61
+
62
+ async function worker(name: string, harnessProvider: "claude" | "codex", maxTasks = 1) {
63
+ return await createAgent({ name, isLead: false, status: "idle", maxTasks, harnessProvider });
64
+ }
65
+
66
+ async function setDefault(agentId: string, profile: string): Promise<void> {
67
+ await upsertSwarmConfig({
68
+ scope: "agent",
69
+ scopeId: agentId,
70
+ key: "MODEL_PROFILE",
71
+ value: profile,
72
+ exportToEnv: false,
73
+ });
74
+ }
75
+
76
+ beforeAll(async () => {
77
+ await removeDbFiles(TEST_DB_PATH);
78
+ initDb(TEST_DB_PATH);
79
+ });
80
+
81
+ afterAll(async () => {
82
+ closeDb();
83
+ await removeDbFiles(TEST_DB_PATH);
84
+ });
85
+
86
+ beforeEach(async () => {
87
+ const client = getDbClient();
88
+ // References first: migration 220's triggers refuse deleting a named profile.
89
+ await client.run("DELETE FROM agent_tasks");
90
+ await client.run("DELETE FROM swarm_config WHERE scope IN ('agent', 'repo')");
91
+ await client.run("DELETE FROM swarm_repos");
92
+ await client.run("DELETE FROM agents");
93
+ await client.run("DELETE FROM model_profiles");
94
+ await createModelProfile({
95
+ name: "claude-opus-high",
96
+ harness: "claude",
97
+ model: "opus",
98
+ effort: "high",
99
+ });
100
+ await createModelProfile({ name: "claude-haiku", harness: "claude", model: "haiku" });
101
+ await createModelProfile({
102
+ name: "codex-sol",
103
+ harness: "codex",
104
+ model: "gpt-5.6-sol",
105
+ effort: "xhigh",
106
+ });
107
+ });
108
+
109
+ describe("a task's own profile", () => {
110
+ test("claim stamps the profile and hands its model and effort to the worker", async () => {
111
+ const w = await worker("p-task", "claude");
112
+ const task = await createTaskExtended("work", { agentId: w.id, profile: "claude-opus-high" });
113
+ const trigger = await callPoll(w.id);
114
+
115
+ expect(trigger?.task).toMatchObject({
116
+ resolvedModel: "opus",
117
+ modelSource: "profile:task",
118
+ resolvedProfile: "claude-opus-high",
119
+ effort: "high",
120
+ });
121
+ expect(await claimRecord(task.id)).toEqual({
122
+ resolvedModel: "opus",
123
+ modelSource: "profile:task",
124
+ resolvedProfile: "claude-opus-high",
125
+ });
126
+ // The profile's effort rides on the trigger only; the row keeps the creator's ask.
127
+ expect((await getTaskById(task.id))?.effort).toBeUndefined();
128
+ });
129
+
130
+ test("the task's own effort wins over the profile's", async () => {
131
+ const w = await worker("p-effort", "claude");
132
+ await createTaskExtended("work", { agentId: w.id, profile: "claude-opus-high", effort: "low" });
133
+ expect((await callPoll(w.id))?.task.effort).toBe("low");
134
+ });
135
+
136
+ test("a profile beats the agent default", async () => {
137
+ const w = await worker("p-beats-default", "claude");
138
+ await setDefault(w.id, "claude-haiku");
139
+ const task = await createTaskExtended("work", { agentId: w.id, profile: "claude-opus-high" });
140
+ await callPoll(w.id);
141
+ expect((await claimRecord(task.id))?.resolvedProfile).toBe("claude-opus-high");
142
+ });
143
+
144
+ test("a directed task with another harness's profile fails fast without a spawn", async () => {
145
+ const w = await worker("p-mismatch", "claude");
146
+ await createTaskExtended("work", { agentId: w.id, profile: "codex-sol" });
147
+ const trigger = await callPoll(w.id);
148
+ expect(String(trigger?.task.modelUnsupported)).toContain("[model-harness-mismatch]");
149
+ });
150
+
151
+ test("a task naming a missing profile fails fast with a tagged reason", async () => {
152
+ // Migration 220's triggers keep such a row from existing; the claim still
153
+ // refuses one rather than run a model nobody picked.
154
+ const w = await worker("p-missing", "claude");
155
+ const task = await createTaskExtended("work", { agentId: w.id });
156
+ const fields = await recordClaimModelResolution(
157
+ { id: task.id, profile: "claude-gone" },
158
+ { id: w.id, harnessProvider: "claude" },
159
+ );
160
+ expect(String(fields.modelUnsupported)).toContain("[model-profile-missing]");
161
+ });
162
+
163
+ test("a pool task waits for a worker on the profile's harness", async () => {
164
+ const codex = await worker("p-pool-codex", "codex");
165
+ const claude = await worker("p-pool-claude", "claude");
166
+ const task = await createTaskExtended("pool work", { profile: "claude-opus-high" });
167
+
168
+ expect(await callPoll(codex.id)).toBeNull();
169
+ const trigger = await callPoll(claude.id);
170
+ expect(trigger?.taskId).toBe(task.id);
171
+ expect(trigger?.task.resolvedProfile).toBe("claude-opus-high");
172
+ });
173
+ });
174
+
175
+ describe("the agent's default profile", () => {
176
+ test("applies to a task with no model and no tier", async () => {
177
+ const w = await worker("d-applies", "codex");
178
+ await setDefault(w.id, "codex-sol");
179
+ const task = await createTaskExtended("work", { agentId: w.id });
180
+ const trigger = await callPoll(w.id);
181
+ expect(trigger?.task).toMatchObject({
182
+ resolvedModel: "gpt-5.6-sol",
183
+ modelSource: "profile:agent-default",
184
+ resolvedProfile: "codex-sol",
185
+ effort: "xhigh",
186
+ });
187
+ expect((await claimRecord(task.id))?.modelSource).toBe("profile:agent-default");
188
+ });
189
+
190
+ test("a raw task model and a task tier both win over it", async () => {
191
+ const w = await worker("d-loses", "claude", 2);
192
+ await setDefault(w.id, "claude-opus-high");
193
+ const pinned = await createTaskExtended("pinned", { agentId: w.id, model: "sonnet" });
194
+ const tiered = await createTaskExtended("tiered", { agentId: w.id, modelTier: "smol" });
195
+ await callPoll(w.id);
196
+ await callPoll(w.id);
197
+ expect(await claimRecord(pinned.id)).toEqual({
198
+ resolvedModel: "sonnet",
199
+ modelSource: "model",
200
+ resolvedProfile: null,
201
+ });
202
+ expect(await claimRecord(tiered.id)).toEqual({
203
+ resolvedModel: "haiku",
204
+ modelSource: "tier-default",
205
+ resolvedProfile: null,
206
+ });
207
+ });
208
+
209
+ test("a default for another harness is ignored; a missing one cannot be stored", async () => {
210
+ const w = await worker("d-ignored", "claude", 2);
211
+ await setDefault(w.id, "codex-sol");
212
+ const first = await createTaskExtended("work", { agentId: w.id });
213
+ const trigger = await callPoll(w.id);
214
+ expect(trigger?.task.modelUnsupported).toBeUndefined();
215
+ expect(trigger?.task.resolvedModel).toBeUndefined();
216
+ expect(await claimRecord(first.id)).toEqual({
217
+ resolvedModel: null,
218
+ modelSource: null,
219
+ resolvedProfile: null,
220
+ });
221
+
222
+ await expect(setDefault(w.id, "gone")).rejects.toThrow("model-profile-missing");
223
+ expect(await getAgentDefaultProfileName(w.id)).toBe("codex-sol");
224
+ });
225
+ });
226
+
227
+ describe("re-runs", () => {
228
+ test("a profile-filled model is not a pin, so a re-claim resolves the profile again", async () => {
229
+ const w = await worker("r-rerun", "claude");
230
+ await setDefault(w.id, "claude-opus-high");
231
+ const task = await createTaskExtended("work", { agentId: w.id });
232
+ await callPoll(w.id);
233
+ // The session report writes the model it ran into `model`.
234
+ await getDbClient().run(
235
+ "UPDATE agent_tasks SET model = 'opus', claudeSessionId = 's1', status = 'pending' WHERE id = ?",
236
+ [task.id],
237
+ );
238
+ const ran = await getTaskById(task.id);
239
+ expect(ran && taskModelPin(ran)).toBeUndefined();
240
+
241
+ // The default changes between runs; the re-claim follows it.
242
+ await setDefault(w.id, "claude-haiku");
243
+ const trigger = await callPoll(w.id);
244
+ expect(trigger?.task).toMatchObject({
245
+ resolvedModel: "haiku",
246
+ resolvedProfile: "claude-haiku",
247
+ });
248
+ // Neither the previous run's model (no pin) nor its profile effort carries over.
249
+ expect(trigger?.task.model).toBeUndefined();
250
+ expect(trigger?.task.effort).toBeUndefined();
251
+ });
252
+
253
+ test("a resume carries the task's own profile, not an agent default", async () => {
254
+ const a = await worker("r-resume-own", "claude");
255
+ const b = await worker("r-resume-default", "claude");
256
+ await setDefault(a.id, "claude-opus-high");
257
+ await setDefault(b.id, "claude-opus-high");
258
+ const own = await createTaskExtended("work", { agentId: a.id, profile: "claude-haiku" });
259
+ const viaDefault = await createTaskExtended("work", { agentId: b.id });
260
+ await callPoll(a.id);
261
+ await callPoll(b.id);
262
+
263
+ const ownRow = await getTaskById(own.id);
264
+ const defaultRow = await getTaskById(viaDefault.id);
265
+ expect(ownRow && inheritedModelSelection(ownRow)).toEqual({ profile: "claude-haiku" });
266
+ expect(defaultRow?.resolvedProfile).toBe("claude-opus-high");
267
+ expect(defaultRow && inheritedModelSelection(defaultRow)).toEqual({});
268
+ });
269
+ });
@@ -12,6 +12,7 @@ import "../heartbeat/templates";
12
12
  import "../tools/templates";
13
13
  import "../commands/templates";
14
14
  import "../slack/templates";
15
+ import "../integrations/chat-sdk/templates";
15
16
  import "../prompts/session-templates";
16
17
 
17
18
  const TEST_DB_PATH = "./test-prompt-remaining.sqlite";
@@ -33,6 +34,7 @@ async function ensureTemplatesRegistered(): Promise<void> {
33
34
  await import(`../tools/templates?t=${ts}`);
34
35
  await import(`../commands/templates?t=${ts}`);
35
36
  await import(`../slack/templates?t=${ts}`);
37
+ await import(`../integrations/chat-sdk/templates?t=${ts}`);
36
38
  await import(`../prompts/session-templates?t=${ts}`);
37
39
  }
38
40
 
@@ -115,6 +117,22 @@ describe("template registration — all sources", () => {
115
117
  expect(eventTypes).toContain("heartbeat.checklist");
116
118
  });
117
119
 
120
+ test("Heartbeat checklist puts the live status after the standing orders and instructions", () => {
121
+ // The status changes every tick; the orders and instructions usually do
122
+ // not, so they lead the text and the status goes last.
123
+ const { text } = resolveTemplate("heartbeat.checklist", {
124
+ system_status: "STATUS_MARKER",
125
+ heartbeat_content: "ORDERS_MARKER",
126
+ });
127
+ const orders = text.indexOf("ORDERS_MARKER");
128
+ const instructions = text.indexOf("## Instructions");
129
+ const status = text.indexOf("## Current System Status");
130
+ expect(orders).toBeGreaterThan(-1);
131
+ expect(instructions).toBeGreaterThan(orders);
132
+ expect(status).toBeGreaterThan(instructions);
133
+ expect(text.trimEnd()).toEndWith("STATUS_MARKER");
134
+ });
135
+
118
136
  test("Task lifecycle templates are registered (3 task_lifecycle)", () => {
119
137
  const all = getAllTemplateDefinitions();
120
138
  const eventTypes = all.map((d) => d.eventType);
@@ -143,6 +161,11 @@ describe("template registration — all sources", () => {
143
161
  expect(eventTypes).toContain("slack.assistant.offline");
144
162
  expect(eventTypes).toContain("slack.message.thread_context");
145
163
  });
164
+
165
+ test("Chat SDK templates are registered (1 system)", () => {
166
+ const eventTypes = getAllTemplateDefinitions().map((d) => d.eventType);
167
+ expect(eventTypes).toContain("chat.message.thread_context");
168
+ });
146
169
  });
147
170
 
148
171
  // ============================================================================
@@ -608,4 +631,16 @@ Alice: Hello
608
631
 
609
632
  expect(result.text).toBe(expected);
610
633
  });
634
+
635
+ test("chat.message.thread_context produces the same block as Slack", () => {
636
+ const result = resolveTemplate("chat.message.thread_context", {
637
+ thread_messages: "Alice: Hello\n[Agent]: Hi there!",
638
+ });
639
+
640
+ expect(result.skipped).toBe(false);
641
+ expect(result.text).toBe(`<thread_context>
642
+ Alice: Hello
643
+ [Agent]: Hi there!
644
+ </thread_context>`);
645
+ });
611
646
  });
@@ -0,0 +1,213 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import {
3
+ checkQuickJSCompat,
4
+ type QuickJSCompatRule,
5
+ quickJSCompatWarnings,
6
+ } from "../scripts-runtime/quickjs-compat";
7
+
8
+ const wrap = (body: string) => `export default async (args, ctx) => {\n${body}\n};`;
9
+
10
+ // One positive (flagged) and one negative (clean, but close) fixture per rule.
11
+ const FIXTURES: Array<{ rule: QuickJSCompatRule; flagged: string; clean: string }> = [
12
+ {
13
+ rule: "bunGlobal",
14
+ flagged: wrap("return Bun.hash('x');"),
15
+ clean: wrap("const Bun = { hash: (s) => s }; return typeof globalThis.Bun;"),
16
+ },
17
+ {
18
+ rule: "process",
19
+ flagged: wrap("return process.env.HOME;"),
20
+ clean: wrap("const config = { process: 1 }; return config.process + args.process;"),
21
+ },
22
+ {
23
+ rule: "fsStdlib",
24
+ flagged: wrap("return ctx.stdlib.glob('*.ts');"),
25
+ clean: wrap("return ctx.stdlib.fetchJson('https://example.com');"),
26
+ },
27
+ {
28
+ rule: "require",
29
+ flagged: wrap("const fs = require('fs'); return fs;"),
30
+ clean: wrap("const require = (name) => name; return require('fs');"),
31
+ },
32
+ {
33
+ rule: "unsupportedGlobal",
34
+ flagged: wrap("return Buffer.from('aGk=', 'base64').toString();"),
35
+ clean: wrap("const bytes: Buffer | null = null; return { Buffer: bytes };"),
36
+ },
37
+ {
38
+ rule: "cryptoSubtle",
39
+ flagged: wrap("return crypto.subtle.digest('SHA-256', new Uint8Array());"),
40
+ clean: wrap("return crypto.randomUUID();"),
41
+ },
42
+ {
43
+ rule: "responseBody",
44
+ flagged: wrap("const res = await fetch('https://example.com'); return res.arrayBuffer();"),
45
+ clean: wrap("const res = await fetch('https://example.com'); return res.text();"),
46
+ },
47
+ {
48
+ rule: "localeFormat",
49
+ flagged: wrap("return new Date().toLocaleString('en-GB', { timeZone: 'Europe/Madrid' });"),
50
+ clean: wrap("return new Date().toISOString();"),
51
+ },
52
+ {
53
+ rule: "hostCredential",
54
+ flagged: wrap("return ctx.stdlib.Redacted.value(ctx.swarm.config.apiKey);"),
55
+ clean: wrap("return ctx.stdlib.Redacted.value(ctx.swarm.config.agentId);"),
56
+ },
57
+ {
58
+ rule: "privateHost",
59
+ flagged: wrap("return ctx.stdlib.fetchJson('http://intranet:8080/status');"),
60
+ clean: wrap("return ctx.stdlib.fetchJson('https://api.example.com/status');"),
61
+ },
62
+ ];
63
+
64
+ describe("checkQuickJSCompat", () => {
65
+ for (const { rule, flagged, clean } of FIXTURES) {
66
+ test(`${rule}: flags the positive fixture`, () => {
67
+ const report = checkQuickJSCompat(flagged);
68
+ expect(report.compatible).toBe(false);
69
+ expect(report.findings.map((finding) => finding.rule)).toContain(rule);
70
+ });
71
+
72
+ test(`${rule}: passes the negative fixture`, () => {
73
+ expect(checkQuickJSCompat(clean)).toEqual({ compatible: true, findings: [] });
74
+ });
75
+ }
76
+
77
+ test("workspaceRw: flags the fsMode, not the source", () => {
78
+ const source = wrap("return 'workspace-rw';");
79
+ expect(checkQuickJSCompat(source, { fsMode: "workspace-rw" }).findings).toEqual([
80
+ { rule: "workspaceRw", message: "fsMode workspace-rw is not available under quickjs" },
81
+ ]);
82
+ expect(checkQuickJSCompat(source, { fsMode: "none" }).compatible).toBe(true);
83
+ });
84
+
85
+ test("fsStdlib also catches stdlib imports and destructuring", () => {
86
+ const imported = `import { glob } from "stdlib";\n${wrap("return glob('*');")}`;
87
+ const destructured = wrap("const { grep } = ctx.stdlib; return grep('x', '.');");
88
+ expect(checkQuickJSCompat(imported).findings[0]?.rule).toBe("fsStdlib");
89
+ expect(checkQuickJSCompat(destructured).findings[0]?.rule).toBe("fsStdlib");
90
+ });
91
+
92
+ test("globals read off globalThis count, typeof feature checks do not", () => {
93
+ expect(checkQuickJSCompat(wrap("return globalThis.process.env;")).findings[0]?.rule).toBe(
94
+ "process",
95
+ );
96
+ expect(
97
+ checkQuickJSCompat(wrap("return (globalThis as any).Bun.version;")).findings[0]?.rule,
98
+ ).toBe("bunGlobal");
99
+ expect(checkQuickJSCompat(wrap("return typeof process === 'undefined';")).compatible).toBe(
100
+ true,
101
+ );
102
+ });
103
+
104
+ test("the shimmed web globals are compatible", () => {
105
+ const source = wrap(`const url = new URL("/x?a=1", "https://example.com");
106
+ url.searchParams.set("b", "2");
107
+ const bytes = new TextEncoder().encode(url.href);
108
+ const id = setInterval(() => {}, 10);
109
+ clearInterval(id);
110
+ await fetch(url, { method: "POST", body: bytes });
111
+ return [new TextDecoder().decode(bytes), crypto.randomUUID(), crypto.getRandomValues(new Uint8Array(4))];`);
112
+ expect(checkQuickJSCompat(source)).toEqual({ compatible: true, findings: [] });
113
+ });
114
+
115
+ test("bindings resolve by lexical scope", () => {
116
+ // A local Buffer parameter in one function does not hide a global Buffer in another.
117
+ const shadowed = `function decode(Buffer: { from(x: string): string }) { return Buffer.from("x"); }
118
+ export default async () => [decode({ from: (x) => x }), Buffer.from("aGk=", "base64").toString()];`;
119
+ expect(checkQuickJSCompat(shadowed).findings).toEqual([
120
+ { rule: "unsupportedGlobal", message: "Buffer is not available under quickjs", line: 2 },
121
+ ]);
122
+ // Block scope too, and a shorthand property reads the binding it names.
123
+ const block = wrap("{ const process = 1; } return { process };");
124
+ expect(checkQuickJSCompat(block).findings[0]?.rule).toBe("process");
125
+ // Every use resolves locally: clean.
126
+ const local = `function decode(Buffer: { from(x: string): string }) { return Buffer.from("x"); }
127
+ export default async () => { const process = { env: {} }; return [decode({ from: (x) => x }), process.env]; };`;
128
+ expect(checkQuickJSCompat(local)).toEqual({ compatible: true, findings: [] });
129
+ // A declare-only binding defines nothing at run time, so the global still counts.
130
+ const ambient = `declare const Buffer: any;\n${wrap("return Buffer.from('x');")}`;
131
+ expect(checkQuickJSCompat(ambient).findings[0]?.rule).toBe("unsupportedGlobal");
132
+ });
133
+
134
+ test("atob and btoa are shimmed; AbortSignal stays reported", () => {
135
+ expect(checkQuickJSCompat(wrap("return atob(btoa('hi'));"))).toEqual({
136
+ compatible: true,
137
+ findings: [],
138
+ });
139
+ expect(
140
+ checkQuickJSCompat(
141
+ wrap("return fetch('https://example.com', { signal: AbortSignal.timeout(5) });"),
142
+ ).findings[0]?.message,
143
+ ).toBe("AbortSignal is not available under quickjs");
144
+ });
145
+
146
+ test("privateHost: literal fetch URLs to single-label or private hosts", () => {
147
+ const hosts = (body: string) =>
148
+ checkQuickJSCompat(wrap(body)).findings.filter((finding) => finding.rule === "privateHost");
149
+ const flagged = [
150
+ "fetch('http://api:3013/health')",
151
+ // biome-ignore lint/suspicious/noTemplateCurlyInString: script source under test
152
+ "fetch(`http://localhost:8080/${args.path}`)",
153
+ "globalThis.fetch('http://10.0.0.5/x')",
154
+ "ctx.stdlib.fetch('http://192.168.1.10/')",
155
+ "ctx.stdlib.fetchJson('http://[::1]:9000/')",
156
+ "ctx.stdlib.fetchJson('http://0x7f.1/')",
157
+ "fetch('http://[fd00::1]/')",
158
+ "fetch('http://[::ffff:10.1.2.3]/')",
159
+ "fetch('https://svc.localhost/')",
160
+ "const { fetchJson } = ctx.stdlib; await fetchJson('http://intranet/')",
161
+ ];
162
+ for (const call of flagged) expect(hosts(`return ${call};`)).toHaveLength(1);
163
+ const clean = [
164
+ "fetch('https://example.com/x')",
165
+ "fetch('https://8.8.8.8/')",
166
+ "fetch('http://[2001:db8::1]/')",
167
+ "fetch(args.url)",
168
+ // biome-ignore lint/suspicious/noTemplateCurlyInString: script source under test
169
+ "fetch(`http://${args.host}/x`)",
170
+ "fetch('relative/path')",
171
+ "ctx.api.github.fetch('http://intranet/')",
172
+ "console.log('http://intranet/')",
173
+ ];
174
+ for (const call of clean) expect(hosts(`return ${call};`)).toEqual([]);
175
+ // A const that holds the base URL fixes the host too; a let or a cut-off name does not.
176
+ const constBase = [
177
+ // biome-ignore lint/suspicious/noTemplateCurlyInString: script source under test
178
+ "const API = 'http://api:3013'; return fetch(`${API}/health`);",
179
+ // biome-ignore lint/suspicious/noTemplateCurlyInString: script source under test
180
+ "const API = 'http://api:3013'; return fetch(`${API}${args.path}`);",
181
+ "const API = 'http://api:3013'; return ctx.stdlib.fetch(API + '/x');",
182
+ "const API = `http://10.1.2.3`; return fetch(API);",
183
+ // biome-ignore lint/suspicious/noTemplateCurlyInString: script source under test
184
+ "const API = 'http://api:3013'; const rows = `${API}/rows/${args.id}`; return fetch(`${rows}?limit=5`);",
185
+ ];
186
+ for (const body of constBase) expect(hosts(body)).toHaveLength(1);
187
+ const dynamicBase = [
188
+ // biome-ignore lint/suspicious/noTemplateCurlyInString: script source under test
189
+ "let API = 'http://api:3013'; API = args.base; return fetch(`${API}/x`);",
190
+ // biome-ignore lint/suspicious/noTemplateCurlyInString: script source under test
191
+ "return fetch(`http://intra${args.suffix}/x`);",
192
+ "return fetch('http://intra' + args.suffix);",
193
+ // biome-ignore lint/suspicious/noTemplateCurlyInString: script source under test
194
+ "const API = 'https://api.example.com'; return fetch(`${API}/x`);",
195
+ ];
196
+ for (const body of dynamicBase) expect(hosts(body)).toEqual([]);
197
+ expect(hosts("return fetch('http://api:3013/health');")[0]?.message).toBe(
198
+ "fetch to api:3013 (single-label host) depends on the egress policy: quickjs refuses private hosts unless the operator allows them",
199
+ );
200
+ });
201
+
202
+ test("reports each rule and name once, with its first line", () => {
203
+ const report = checkQuickJSCompat(wrap("Bun.sleep(1);\nBun.sleep(2);\nreturn process.pid;"));
204
+ expect(report.findings).toEqual([
205
+ { rule: "bunGlobal", message: "Bun.* is not available under quickjs", line: 2 },
206
+ { rule: "process", message: "process is not available under quickjs", line: 4 },
207
+ ]);
208
+ expect(quickJSCompatWarnings(report, "actions.sync")).toEqual([
209
+ "actions.sync: quickjs compat (bunGlobal, line 2): Bun.* is not available under quickjs",
210
+ "actions.sync: quickjs compat (process, line 4): process is not available under quickjs",
211
+ ]);
212
+ });
213
+ });
@@ -49,6 +49,10 @@ const LEAD_ONLY_VERBS: PermissionVerb[] = [
49
49
  "memory.write.consolidated",
50
50
  "channel.delete",
51
51
  "integration.kapso.manage",
52
+ "integration.chat.manage",
53
+ "integration.chat.post",
54
+ "integration.chat.read",
55
+ "integration.chat.dm",
52
56
  "integration.slack.post",
53
57
  "integration.slack.read",
54
58
  "integration.slack.thread.start",
@@ -103,6 +107,9 @@ const OPERATOR_OR_USER_VERBS: PermissionVerb[] = [
103
107
 
104
108
  const LEAD_OR_OPERATOR_VERBS: PermissionVerb[] = ["models.catalog.write"];
105
109
 
110
+ // The shared API key alone: dashboard users are denied too.
111
+ const API_KEY_OPERATOR_VERBS: PermissionVerb[] = ["models.profiles.write"];
112
+
106
113
  const AGENT_OR_OPERATOR_VERBS: PermissionVerb[] = ["models.harness-support.write"];
107
114
 
108
115
  const LEAD_OR_RESOURCE_OWNER_VERBS: PermissionVerb[] = [
@@ -140,6 +147,7 @@ const REQUESTER_OWNS_TASK_VERBS: PermissionVerb[] = [
140
147
  const COMPOSITE_VERBS: PermissionVerb[] = [
141
148
  "memory.delete.any",
142
149
  "task.fs.mutate",
150
+ "task.parent.attach",
143
151
  "task.progress.write",
144
152
  ];
145
153
 
@@ -206,6 +214,7 @@ describe("verb-group partition", () => {
206
214
  ...LEAD_OR_OPERATOR_OR_USER_VERBS,
207
215
  ...OPERATOR_OR_USER_VERBS,
208
216
  ...LEAD_OR_OPERATOR_VERBS,
217
+ ...API_KEY_OPERATOR_VERBS,
209
218
  ...AGENT_OR_OPERATOR_VERBS,
210
219
  ];
211
220
  expect(new Set(grouped).size).toBe(grouped.length);
@@ -320,6 +329,23 @@ describe("agent-or-operator verbs", () => {
320
329
  }
321
330
  });
322
331
 
332
+ describe("api-key operator verbs", () => {
333
+ const expected: Expected = {
334
+ lead: false,
335
+ worker: false,
336
+ ownerWorker: false,
337
+ creatorWorker: false,
338
+ userRequester: false,
339
+ foreignUser: false,
340
+ operator: true,
341
+ };
342
+ for (const verb of API_KEY_OPERATOR_VERBS) {
343
+ test(`${verb}: only the operator allowed`, () => {
344
+ expectDecisions(verb, { kind: "none" }, expected);
345
+ });
346
+ }
347
+ });
348
+
323
349
  describe("lead-or-operator verbs", () => {
324
350
  const expected: Expected = {
325
351
  lead: true,
@@ -570,9 +596,12 @@ describe("memory.delete.any composite (human, lead for swarm, owner for agent sc
570
596
  });
571
597
  });
572
598
 
573
- describe("task.fs.mutate composite (operator OR user OR lead OR assignee OR creator)", () => {
574
- test("only an unrelated worker agent is denied", () => {
575
- expectDecisions("task.fs.mutate", TASK_RESOURCE, {
599
+ describe("task.fs.mutate and task.parent.attach composite (operator OR user OR lead OR assignee OR creator)", () => {
600
+ test.each([
601
+ "task.fs.mutate",
602
+ "task.parent.attach",
603
+ ] as const)("%s: only an unrelated worker agent is denied", (verb) => {
604
+ expectDecisions(verb, TASK_RESOURCE, {
576
605
  lead: true,
577
606
  worker: false,
578
607
  ownerWorker: true, // assignee (task.agentId)
@@ -122,8 +122,6 @@ test(
122
122
  const tempDir = `/tmp/runner-exit-shutdown-${crypto.randomUUID()}`;
123
123
  await Bun.$`mkdir -p ${tempDir}`.quiet();
124
124
  try {
125
- await Bun.write(`${tempDir}/pm2`, '#!/bin/sh\nprintf "%s" "$1" > "$SHUTDOWN_TEST_PM2_LOG"\n');
126
- await Bun.$`chmod +x ${tempDir}/pm2`.quiet();
127
125
  await Bun.write(
128
126
  `${tempDir}/runner.ts`,
129
127
  `
@@ -141,14 +139,12 @@ test(
141
139
  ...process.env,
142
140
  PATH: tempDir,
143
141
  ANONYMIZED_TELEMETRY: "false",
144
- SHUTDOWN_TEST_PM2_LOG: `${tempDir}/pm2.log`,
145
142
  },
146
143
  }),
147
144
  "runner shutdown",
148
145
  );
149
146
  expect(result.stdout).toContain("shutdown callback");
150
147
  expect(result.stdout).not.toContain("unexpected return");
151
- expect(await Bun.file(`${tempDir}/pm2.log`).text()).toBe("save");
152
148
  expect(requests).toHaveLength(1);
153
149
  expect(requests[0]?.path).toBe("/close");
154
150
  expect(requests[0]?.headers["x-agent-id"]).toBe(agentId);