@desplega.ai/agent-swarm 2.4.4 → 2.5.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 (382) hide show
  1. package/README.md +4 -2
  2. package/dist/{acp-adapter-thq04adh.js → acp-adapter-11gp67nr.js} +8 -6
  3. package/dist/{actions-j6ywh6x5.js → actions-8p2kze8j.js} +24 -22
  4. package/dist/{amp-adapter-k8b5xmj8.js → amp-adapter-z4qvhacj.js} +2 -2
  5. package/dist/{anthropic-messages-g6pqmqk4.js → anthropic-messages-txs190dd.js} +17 -20
  6. package/dist/{app-rb7vc02s.js → app-0qp0a2gk.js} +3 -3
  7. package/dist/{assistant-ftta1zjf.js → assistant-jedd3ktm.js} +27 -25
  8. package/dist/{azure-openai-responses-9dbmj346.js → azure-openai-responses-9kfwhmn7.js} +18 -13
  9. package/dist/{boot-reembed-xns7h5h9.js → boot-reembed-4xwq5tg0.js} +1 -1
  10. package/dist/{boot-reembed-srp6v6af.js → boot-reembed-n4wq5dd2.js} +1 -1
  11. package/dist/{boot-scrub-sweep-3qqrh2mz.js → boot-scrub-sweep-69h90xcq.js} +4 -4
  12. package/dist/{boot-seeds-9ks8hce7.js → boot-seeds-3vn0m9sg.js} +4 -4
  13. package/dist/{claude-adapter-k5ksyres.js → claude-adapter-221mvad5.js} +8 -6
  14. package/dist/{claude-managed-adapter-h2y889ge.js → claude-managed-adapter-gt6pa35d.js} +2 -2
  15. package/dist/{claude-managed-setup-n5a5eqks.js → claude-managed-setup-c3hp36ph.js} +4 -4
  16. package/dist/{claude-sdk-session-2j3j4kwn.js → claude-sdk-session-8thjsk5e.js} +9729 -9628
  17. package/dist/{cli-b9fcxxev.js → cli-007cnhm1.js} +50 -4
  18. package/dist/{cli-j4rqdf2n.js → cli-03vpq45j.js} +2 -2
  19. package/dist/{cli-amyhm3gj.js → cli-0y6f0ckq.js} +1 -1
  20. package/dist/{cli-4dg55wsk.js → cli-0z3vjrpm.js} +1 -1
  21. package/dist/{cli-pccfmtb5.js → cli-179xcyky.js} +11 -11
  22. package/dist/{cli-3r8samv3.js → cli-1f0gp9vz.js} +3 -2
  23. package/dist/{cli-hj3debgw.js → cli-1pr672hz.js} +3 -3
  24. package/dist/{cli-0bqteszy.js → cli-2nf3b08t.js} +328 -25
  25. package/dist/{cli-t7cyef5g.js → cli-36j4jnk3.js} +4 -4
  26. package/dist/{cli-er83cjbn.js → cli-3qv8naq8.js} +527 -231
  27. package/dist/cli-3zwye9sn.js +1193 -0
  28. package/dist/{cli-60cgh1rv.js → cli-459fk97x.js} +7 -7
  29. package/dist/{cli-xj0re5tm.js → cli-4w24f7z3.js} +1 -1
  30. package/dist/{cli-v3yr2gbg.js → cli-5kzk3472.js} +2198 -968
  31. package/dist/{cli-h7gc0b4r.js → cli-5m84zd2k.js} +55 -18
  32. package/dist/{cli-w6nadkh4.js → cli-5rvg1ddk.js} +5 -5
  33. package/dist/{cli-dvkmp9bt.js → cli-6ctwrf1z.js} +20 -10
  34. package/dist/{cli-9qtjfd60.js → cli-6rg2jn2y.js} +3 -2
  35. package/dist/{cli-2v045xyf.js → cli-7bwtc6yx.js} +3 -3
  36. package/dist/{cli-3cgngyd8.js → cli-7g40g8hq.js} +35 -17
  37. package/dist/{cli-q6r7t299.js → cli-92mqenkc.js} +2379 -154
  38. package/dist/{cli-cynvc9fj.js → cli-9ce2xw98.js} +8 -8
  39. package/dist/{cli-gqvh8d8a.js → cli-bgdtv62r.js} +36 -22
  40. package/dist/{cli-wxbn3yfx.js → cli-cz5nmdxt.js} +2 -2
  41. package/dist/{cli-wt645v7q.js → cli-dabehxhp.js} +1 -1
  42. package/dist/{cli-vfha21c9.js → cli-e5bxnjps.js} +2 -2
  43. package/dist/{cli-p20r8w42.js → cli-fcc5qe3f.js} +763 -268
  44. package/dist/{cli-1ephgwnq.js → cli-fes51ya2.js} +3 -3
  45. package/dist/{cli-1d70crsy.js → cli-ftmq4nvk.js} +2 -2
  46. package/dist/{cli-qkhc5aqy.js → cli-fy1jyyym.js} +13 -13
  47. package/dist/{cli-s95dknxh.js → cli-g1cw7xtt.js} +1 -1
  48. package/dist/{cli-qje9k2nh.js → cli-g9ypd5en.js} +12 -2
  49. package/dist/{cli-74c1wpm0.js → cli-gpv60278.js} +1 -1
  50. package/dist/{cli-pemywhfq.js → cli-ha9rmwgq.js} +2 -1
  51. package/dist/{cli-bnz0r3kc.js → cli-j1eghqfm.js} +5 -5
  52. package/dist/{cli-2zqpkvtt.js → cli-jpxsrxcb.js} +1 -1
  53. package/dist/{cli-ejs3m1sx.js → cli-kxpfx2xr.js} +1 -1
  54. package/dist/{cli-37yb7307.js → cli-mbfj11fq.js} +1 -1
  55. package/dist/{cli-pnp2j5zg.js → cli-mmhj8vd7.js} +2 -2
  56. package/dist/{cli-xf234ws8.js → cli-n4c8e3s5.js} +2 -0
  57. package/dist/{cli-a0kcty51.js → cli-n6kmqngw.js} +2 -2
  58. package/dist/{cli-te6b5wvg.js → cli-nnvjb17s.js} +3 -3
  59. package/dist/{cli-1dmcq2eh.js → cli-nzfnt2gs.js} +1 -1
  60. package/dist/{cli-625tb7aq.js → cli-p859hcc8.js} +2 -2
  61. package/dist/{cli-smvcw9z5.js → cli-pasyvjmm.js} +1 -1
  62. package/dist/cli-pkaqk57w.js +128 -0
  63. package/dist/{cli-8nyhf7za.js → cli-qqzmjxkf.js} +129 -105
  64. package/dist/{cli-zrwpctp4.js → cli-r25qpaxt.js} +161 -63
  65. package/dist/{cli-nc35pxb7.js → cli-rh5smt8t.js} +19 -4
  66. package/dist/{cli-qwkcwjdn.js → cli-rycdaj0a.js} +1 -1
  67. package/dist/{cli-esxerzg7.js → cli-s4sntwt4.js} +2 -2
  68. package/dist/{cli-b8k7epj1.js → cli-segs7hx9.js} +8 -8
  69. package/dist/{cli-36ckw0wx.js → cli-stxdrqs2.js} +2 -2
  70. package/dist/{cli-vdm20wej.js → cli-sy8f9705.js} +4 -4
  71. package/dist/{cli-1m17dc9d.js → cli-ta8bd7th.js} +2 -2
  72. package/dist/{cli-2y91wnbh.js → cli-we0ra057.js} +222 -10
  73. package/dist/{cli-55qmbp6z.js → cli-x21dqn18.js} +5 -5
  74. package/dist/{cli-dqb4n629.js → cli-x6vf4sad.js} +11 -11
  75. package/dist/{cli-d2mb5s8a.js → cli-x77gen2p.js} +680 -440
  76. package/dist/{cli-vq97g1jq.js → cli-xawhpgc9.js} +95 -195
  77. package/dist/{cli-82d3nkc3.js → cli-xd4eqtr2.js} +2 -2
  78. package/dist/{cli-9fhkkpj5.js → cli-xt768r5m.js} +2 -2
  79. package/dist/{cli-zrk6jxv8.js → cli-y4x8ww6s.js} +2 -2
  80. package/dist/{cli-xc27fwzg.js → cli-y9jvxfhk.js} +3 -3
  81. package/dist/{cli-44159k28.js → cli-z63a269p.js} +2 -2
  82. package/dist/{cli-sm8rses9.js → cli-zfdp01hn.js} +8 -5
  83. package/dist/cli.js +31 -29
  84. package/dist/{cloudflare-workers-ai-system-one-ch2wxfcp.js → cloudflare-workers-ai-system-one-43v9qkjk.js} +7 -2
  85. package/dist/{codex-adapter-968xwfy7.js → codex-adapter-tcg1q3jk.js} +6 -4
  86. package/dist/{codex-login-m819c41r.js → codex-login-rn7c5gxt.js} +1 -1
  87. package/dist/{codex-session-runner-ht4z99fa.js → codex-session-runner-xfx4wd9x.js} +7 -5
  88. package/dist/{commands-pcymaeg9.js → commands-ew83nj70.js} +2 -2
  89. package/dist/{controller-fpjyk46r.js → controller-gnq1k1mf.js} +4 -4
  90. package/dist/{controllers-connect-mwzx1fh6.js → controllers-connect-tw1dan9r.js} +26 -24
  91. package/dist/{cursor-adapter-cp4s2e2f.js → cursor-adapter-yq5xgfcg.js} +1 -1
  92. package/dist/{db-xsjg9jqq.js → db-7gbc53fd.js} +14 -2
  93. package/dist/{db-sgd12bt9.js → db-t86xrnes.js} +3 -3
  94. package/dist/{devin-adapter-zgqadvnm.js → devin-adapter-4ycy4vrf.js} +1 -1
  95. package/dist/{dispatcher-x0jfw2c9.js → dispatcher-64bdyhps.js} +23 -21
  96. package/dist/{docker-fkgtr75t.js → docker-nfy2gc8t.js} +2 -2
  97. package/dist/{dsh-adapter-jrq7ma7h.js → dsh-adapter-tvhc4k2d.js} +1 -1
  98. package/dist/{e2b-16d6ch39.js → e2b-9gkdhm3c.js} +2 -2
  99. package/dist/{e2b-zn0b4w5w.js → e2b-cjtf8k1z.js} +2 -2
  100. package/dist/{easter-egg-3d-5cgynaja.js → easter-egg-3d-e4qv8dxj.js} +1 -1
  101. package/dist/{event-bus-9vpk8gmq.js → event-bus-3z78ep5n.js} +1 -1
  102. package/dist/{execute-t4e6m3rw.js → execute-0xpz8950.js} +85 -23
  103. package/dist/{external-channel-8njj4h3b.js → external-channel-e9nw24zv.js} +2 -2
  104. package/dist/{google-generative-ai-bmq8gkf3.js → google-generative-ai-mk9vcpe1.js} +15 -15
  105. package/dist/{google-vertex-b1rvp52s.js → google-vertex-q580kz8k.js} +20 -20
  106. package/dist/{grok-adapter-kkr97v2f.js → grok-adapter-v82srhrx.js} +9 -7
  107. package/dist/handlers-c9ajzhbn.js +41 -0
  108. package/dist/{heartbeat-wd03zhn0.js → heartbeat-2vqs1319.js} +26 -24
  109. package/dist/{hook-xzj2ts2p.js → hook-01nk13wx.js} +7 -5
  110. package/dist/{hook-f0vghj42.js → hook-jzpheegg.js} +9 -7
  111. package/dist/{http-4hwxv62p.js → http-fc1k6ec8.js} +859 -252
  112. package/dist/index-07ef7nkm.js +42 -0
  113. package/dist/{index-g6tbb6ve.js → index-31ceyf42.js} +1 -1
  114. package/dist/index-7yts8n9s.js +43 -0
  115. package/dist/{index-c4ykd0f1.js → index-dr94zbnh.js} +13348 -11611
  116. package/dist/index-q3t40ryv.js +46 -0
  117. package/dist/{index-ww2vznkp.js → index-sepja052.js} +1 -1
  118. package/dist/{index-97q4xn03.js → index-zdycv3ja.js} +135 -79
  119. package/dist/{index-g216q6jr.js → index-zsyx51ap.js} +1 -4
  120. package/dist/{keepalive-hrh7xnr9.js → keepalive-5nh2rhmr.js} +4 -4
  121. package/dist/lead-aayq64jn.js +79 -0
  122. package/dist/{llama-cpp-classify-vv43gswy.js → llama-cpp-classify-ee7vmd7q.js} +4 -1
  123. package/dist/{maintenance-e8wnwnqx.js → maintenance-hjhzdt3k.js} +1 -1
  124. package/dist/{mistral-conversations-1medfwv7.js → mistral-conversations-sd0xf1tn.js} +13 -13
  125. package/dist/{node-3v3f08f7.js → node-jzjj7r9x.js} +10 -9
  126. package/dist/{oauth-refresh-sweep-zm0yj4sj.js → oauth-refresh-sweep-y3n1knmd.js} +3 -3
  127. package/dist/{onboard-dtwcq603.js → onboard-1zq3m7db.js} +2 -2
  128. package/dist/{openai-codex-responses-de9pvs4d.js → openai-codex-responses-79s8xnbh.js} +29 -23
  129. package/dist/{openai-completions-mtqge3nf.js → openai-completions-tdbyts0x.js} +21 -20
  130. package/dist/{openai-decisions-ygk4kkxd.js → openai-decisions-bqhnjyfz.js} +2 -1
  131. package/dist/{openai-responses-dkyng71g.js → openai-responses-dm3t5vqp.js} +26 -17
  132. package/dist/{opencode-adapter-x4tqzhkh.js → opencode-adapter-e56086m1.js} +7 -5
  133. package/dist/{openrouter-images-wvv7camj.js → openrouter-images-ssttr1y4.js} +6 -3
  134. package/dist/{otel-impl-w3fbhpn0.js → otel-impl-h9yyza47.js} +2 -2
  135. package/dist/{pi-messages-nhh1pkz2.js → pi-messages-3nf8bq1a.js} +6 -4
  136. package/dist/{pi-mono-adapter-mcfkn9vf.js → pi-mono-adapter-tzps8f38.js} +9 -37
  137. package/dist/{pricing-refresh-j38dehfs.js → pricing-refresh-nevhat8h.js} +3 -3
  138. package/dist/{rbac-roles-ae8yttq4.js → rbac-roles-4y6ngvzq.js} +1 -1
  139. package/dist/{rbac-roles-ejxx9b35.js → rbac-roles-exbg8xb6.js} +2 -2
  140. package/dist/{registry-rxw10xzg.js → registry-ajhexe44.js} +1 -1
  141. package/dist/{render-v2-n16n0phe.js → render-v2-cfsh13z3.js} +3 -3
  142. package/dist/{resume-z0fgjc38.js → resume-5s2t0dbs.js} +10 -8
  143. package/dist/{runtime-xe3aqy4x.js → runtime-bt5pkpm1.js} +1 -1
  144. package/dist/{session-access-cv3mq6mc.js → session-access-dnbyp3cn.js} +6 -4
  145. package/dist/{setup-mafhj3yt.js → setup-1wj1y77j.js} +1 -1
  146. package/dist/{typesafe-system-one-2hgpayzy.js → typesafe-system-one-bre38hvs.js} +4 -2
  147. package/dist/{virtual-modules-4r3zkc20.js → virtual-modules-mnfbh5mr.js} +8 -2
  148. package/dist/{work-objects-8b86mgjp.js → work-objects-7mg6216g.js} +26 -24
  149. package/dist/worker-nwb789mg.js +79 -0
  150. package/dist/{x-y45mthwn.js → x-pbpezr0s.js} +2 -2
  151. package/openapi.json +793 -6
  152. package/package.json +11 -11
  153. package/src/apps/catalog.generated.json +206 -4
  154. package/src/apps/page-validator.ts +13 -1
  155. package/src/assets/key.ts +17 -0
  156. package/src/azure-devops/handlers.ts +1 -1
  157. package/src/be/chunking.ts +42 -9
  158. package/src/be/db/approvals.ts +18 -0
  159. package/src/be/db/session-moves.ts +164 -0
  160. package/src/be/db/session-tree.ts +384 -0
  161. package/src/be/db/tasks/draft-text.ts +15 -0
  162. package/src/be/db/tasks/read.ts +54 -5
  163. package/src/be/db/workflow-runs.ts +96 -0
  164. package/src/be/db.ts +85 -237
  165. package/src/be/memory/index-content.ts +36 -17
  166. package/src/be/memory/providers/openai-embedding.ts +115 -61
  167. package/src/be/memory/providers/sqlite-store.ts +69 -23
  168. package/src/be/memory/reranker.ts +31 -1
  169. package/src/be/memory/search-candidates.ts +47 -0
  170. package/src/be/memory/types.ts +14 -0
  171. package/src/be/onboarding.ts +3 -0
  172. package/src/be/scripts/typecheck.ts +2 -0
  173. package/src/be/seed-scripts/catalog/heartbeat-jev-gate.ts +106 -54
  174. package/src/be/seed-scripts/index.ts +2 -2
  175. package/src/be/skill-sync.ts +43 -2
  176. package/src/be/swarm-config-guard.ts +25 -1
  177. package/src/be/task-attachment-store.ts +30 -0
  178. package/src/bitbucket/api.ts +81 -0
  179. package/src/bitbucket/auth.ts +64 -0
  180. package/src/bitbucket/handlers.ts +434 -0
  181. package/src/bitbucket/index.ts +28 -0
  182. package/src/bitbucket/templates.ts +134 -0
  183. package/src/bitbucket/types.ts +65 -0
  184. package/src/commands/runner.ts +55 -3
  185. package/src/e2b/env.ts +1 -0
  186. package/src/extensions/catalog.generated.json +4 -4
  187. package/src/extensions/contract-types.generated.ts +1 -1
  188. package/src/extensions/contract.ts +9 -2
  189. package/src/extensions/post-bridge.ts +1 -0
  190. package/src/fs/provider.ts +13 -1
  191. package/src/github/handlers.ts +6 -6
  192. package/src/gitlab/handlers.ts +5 -5
  193. package/src/heartbeat/heartbeat.ts +31 -11
  194. package/src/http/assets.ts +14 -2
  195. package/src/http/core.ts +12 -0
  196. package/src/http/fs.ts +54 -34
  197. package/src/http/index.ts +4 -0
  198. package/src/http/mcp-body.ts +37 -0
  199. package/src/http/mcp-user.ts +3 -5
  200. package/src/http/mcp.ts +6 -7
  201. package/src/http/memory.ts +26 -15
  202. package/src/http/onboarding.ts +1 -0
  203. package/src/http/page-proxy.ts +61 -5
  204. package/src/http/pages-public.ts +27 -44
  205. package/src/http/pages.ts +4 -4
  206. package/src/http/script-runs.ts +2 -0
  207. package/src/http/sessions.ts +366 -12
  208. package/src/http/skills.ts +48 -1
  209. package/src/http/tasks.ts +1 -1
  210. package/src/http/users.ts +13 -1
  211. package/src/http/webhooks.ts +141 -0
  212. package/src/integrations/chat-sdk/adapters.ts +24 -4
  213. package/src/integrations/chat-sdk/attachments.ts +90 -12
  214. package/src/integrations/chat-sdk/facts.ts +138 -0
  215. package/src/integrations/chat-sdk/inbound.ts +156 -8
  216. package/src/integrations/chat-sdk/limits.ts +24 -0
  217. package/src/integrations/chat-sdk/outbound-files.ts +186 -0
  218. package/src/integrations/chat-sdk/outbound.ts +272 -5
  219. package/src/integrations/chat-sdk/relay.ts +4 -1
  220. package/src/integrations/chat-sdk/router.ts +28 -2
  221. package/src/integrations/chat-sdk/steering.ts +9 -0
  222. package/src/integrations/chat-sdk/templates.ts +14 -0
  223. package/src/integrations/chat-sdk/thread-buffer.ts +4 -0
  224. package/src/mcp-limits.ts +20 -0
  225. package/src/pages/browser-sdk.ts +8 -3
  226. package/src/pages/feedback-overlay.ts +1 -1
  227. package/src/providers/acp-targets.ts +1 -1
  228. package/src/providers/amp-adapter.ts +1 -1
  229. package/src/providers/codex-adapter.ts +106 -119
  230. package/src/providers/codex-agents-md.ts +35 -229
  231. package/src/providers/cursor-adapter.ts +1 -1
  232. package/src/providers/pi-mono-adapter.ts +5 -53
  233. package/src/realtime/transport.ts +1 -2
  234. package/src/script-workflows/workflow-ctx.ts +1 -0
  235. package/src/scripts-runtime/types/stdlib.d.ts +2 -0
  236. package/src/scripts-runtime/types/swarm-extension.d.ts +14 -4
  237. package/src/scripts-runtime/types/swarm-sdk.d.ts +2 -0
  238. package/src/server-user.ts +2 -0
  239. package/src/server.ts +2 -0
  240. package/src/slack/assistant.ts +2 -2
  241. package/src/slack/files.ts +2 -0
  242. package/src/slack/handlers.test.ts +158 -0
  243. package/src/slack/handlers.ts +23 -32
  244. package/src/slack/inbound-files.ts +39 -6
  245. package/src/slack/thread-buffer.ts +2 -1
  246. package/src/tasks/context-key.ts +61 -1
  247. package/src/tasks/task-terminal-effects.ts +21 -17
  248. package/src/tests/apps-card-grid.test.ts +197 -0
  249. package/src/tests/apps-catalog-additions.test.ts +120 -0
  250. package/src/tests/apps-tree.test.ts +129 -0
  251. package/src/tests/bitbucket-auth.test.ts +156 -0
  252. package/src/tests/bitbucket-handlers.test.ts +425 -0
  253. package/src/tests/bitbucket-webhook.test.ts +220 -0
  254. package/src/tests/chat-sdk-attachments-reply.test.ts +52 -0
  255. package/src/tests/chat-sdk-config.test.ts +11 -0
  256. package/src/tests/chat-sdk-facts.test.ts +23 -0
  257. package/src/tests/chat-sdk-inbound.test.ts +597 -4
  258. package/src/tests/chat-sdk-outbound-files.test.ts +652 -0
  259. package/src/tests/chat-sdk-telegram-markdown.test.ts +92 -0
  260. package/src/tests/codex-adapter.test.ts +167 -242
  261. package/src/tests/config-api-only-keys.test.ts +9 -0
  262. package/src/tests/context-key.test.ts +50 -0
  263. package/src/tests/core-auth.test.ts +5 -0
  264. package/src/tests/cors-allowlist.test.ts +1 -1
  265. package/src/tests/db-task-read-contract.test.ts +7 -0
  266. package/src/tests/db-task-write-contract.test.ts +6 -0
  267. package/src/tests/docs-index-footprint.test.js +3 -3
  268. package/src/tests/extensions-example-slack-reply-gate.test.ts +262 -91
  269. package/src/tests/fixtures/bitbucket/pullrequest-comment-created-inline.json +174 -0
  270. package/src/tests/fixtures/bitbucket/pullrequest-comment-created.json +170 -0
  271. package/src/tests/fixtures/bitbucket/pullrequest-created-mention.json +132 -0
  272. package/src/tests/fixtures/bitbucket/pullrequest-created-reviewer.json +140 -0
  273. package/src/tests/fixtures/bitbucket/pullrequest-fulfilled.json +134 -0
  274. package/src/tests/fixtures/bitbucket/pullrequest-rejected.json +133 -0
  275. package/src/tests/generate-mcp-docs.test.ts +101 -0
  276. package/src/tests/gitlab-handlers.test.ts +3 -3
  277. package/src/tests/gitlab-vcs-db.test.ts +4 -4
  278. package/src/tests/heartbeat-checklist.test.ts +33 -0
  279. package/src/tests/heartbeat-jev-gate-credentials.test.ts +44 -0
  280. package/src/tests/heartbeat-jev-gate.test.ts +242 -1
  281. package/src/tests/integration-disable-flags.test.ts +36 -0
  282. package/src/tests/kv-page-proxy.test.ts +91 -5
  283. package/src/tests/launch-password-rejection.test.ts +2 -2
  284. package/src/tests/mcp-input-caps.test.ts +294 -0
  285. package/src/tests/mcp-tools.test.ts +1 -1
  286. package/src/tests/memory-chunked-search-starvation.test.ts +200 -0
  287. package/src/tests/memory-embedding-input-cap.test.ts +114 -0
  288. package/src/tests/memory.test.ts +14 -0
  289. package/src/tests/onboarding.test.ts +12 -0
  290. package/src/tests/page-guest-session.test.ts +19 -8
  291. package/src/tests/page-proxy-authed.test.ts +1 -1
  292. package/src/tests/page-proxy.test.ts +17 -10
  293. package/src/tests/page-session.test.ts +102 -1
  294. package/src/tests/pages-authed-mode.test.ts +82 -12
  295. package/src/tests/pages-password-mode.test.ts +5 -5
  296. package/src/tests/pi-codemode.test.ts +8 -2
  297. package/src/tests/preload-env.test.ts +57 -0
  298. package/src/tests/preload.ts +21 -0
  299. package/src/tests/prompt-template-remaining.test.ts +38 -0
  300. package/src/tests/realtime-transport.test.ts +13 -7
  301. package/src/tests/run-root-coverage.test.ts +39 -0
  302. package/src/tests/runner-bitbucket-vcs.test.ts +191 -0
  303. package/src/tests/runner-skills-refresh.test.ts +58 -0
  304. package/src/tests/script-runs-http.test.ts +84 -0
  305. package/src/tests/scripts-http.test.ts +10 -1
  306. package/src/tests/secret-scrubber.test.ts +15 -0
  307. package/src/tests/sessions-move.test.ts +576 -0
  308. package/src/tests/sessions-tree.test.ts +493 -0
  309. package/src/tests/skill-files-http.test.ts +50 -0
  310. package/src/tests/skills-bundle.test.ts +99 -0
  311. package/src/tests/swarm-config-export-to-env.test.ts +23 -3
  312. package/src/tests/task-audio-transcription.test.ts +299 -0
  313. package/src/tests/task-completion-memory-chunking.test.ts +434 -0
  314. package/src/tests/task-terminal-secret-scrub.test.ts +6 -1
  315. package/src/tests/transcription.test.ts +299 -0
  316. package/src/tests/vcs-bitbucket.test.ts +91 -0
  317. package/src/tests/vcs-provider-isolation.test.ts +123 -0
  318. package/src/tests/vcs-provider.test.ts +16 -1
  319. package/src/tests/vcs-tracking.test.ts +3 -3
  320. package/src/tests/workflow-foreach-bodies.test.ts +637 -0
  321. package/src/tests/workflow-foreach-concurrency.test.ts +342 -0
  322. package/src/tests/workflow-foreach-retry.test.ts +537 -0
  323. package/src/tests/workflow-foreach.test.ts +44 -23
  324. package/src/tests/workflow-step-claims.test.ts +6 -6
  325. package/src/tests/workflow-system-one-decision-providers.test.ts +98 -1
  326. package/src/tests/workflow-system-one-decision.test.ts +2 -1
  327. package/src/tests/workflow-tla-races.test.ts +2 -2
  328. package/src/tools/chat-access.ts +10 -0
  329. package/src/tools/chat-post.ts +31 -11
  330. package/src/tools/chat-reply.ts +32 -10
  331. package/src/tools/memory-search.ts +3 -1
  332. package/src/tools/script-delete.ts +1 -1
  333. package/src/tools/system-one-decide.ts +17 -4
  334. package/src/tools/workflows/create-workflow.ts +5 -3
  335. package/src/types.ts +9 -2
  336. package/src/utils/config-env-export.ts +4 -1
  337. package/src/utils/page-session.ts +133 -20
  338. package/src/utils/secret-scrubber.ts +4 -0
  339. package/src/utils/skills-refresh.ts +83 -43
  340. package/src/utils/transcription.ts +246 -0
  341. package/src/vcs/bitbucket.ts +58 -0
  342. package/src/vcs/index.ts +2 -0
  343. package/src/vcs/types.ts +2 -2
  344. package/src/workflows/approval-resolution.ts +4 -1
  345. package/src/workflows/checkpoint.ts +9 -1
  346. package/src/workflows/constants.ts +3 -0
  347. package/src/workflows/definition.ts +48 -17
  348. package/src/workflows/engine.ts +25 -15
  349. package/src/workflows/executors/foreach.ts +45 -99
  350. package/src/workflows/executors/system-one-decision.ts +28 -7
  351. package/src/workflows/executors/system-one-providers.ts +39 -4
  352. package/src/workflows/foreach-body.ts +146 -0
  353. package/src/workflows/foreach-driver.ts +552 -0
  354. package/src/workflows/foreach-join.ts +206 -7
  355. package/src/workflows/node-timeout.ts +21 -0
  356. package/src/workflows/recovery.ts +68 -3
  357. package/src/workflows/resume.ts +221 -43
  358. package/src/workflows/retry-poller.ts +39 -24
  359. package/src/workflows/step-input.ts +59 -0
  360. package/src/workflows/task-step-routing.ts +114 -56
  361. package/templates/extensions/slack-reply-gate/README.md +24 -22
  362. package/templates/extensions/slack-reply-gate/hooks.ts +146 -95
  363. package/templates/extensions/slack-reply-gate/manifest.json +2 -2
  364. package/templates/skills/apps/SKILL.md +28 -6
  365. package/templates/skills/apps/content.md +28 -6
  366. package/templates/skills/chat-sdk/content.md +6 -2
  367. package/templates/skills/code-quality/content.md +2 -1
  368. package/templates/skills/heartbeat-runbook/content.md +5 -5
  369. package/templates/skills/pages/content.md +6 -4
  370. package/templates/skills/slack-interaction/content.md +5 -0
  371. package/templates/skills/swarm-scripts/SKILL.md +2 -2
  372. package/templates/skills/swarm-scripts/content.md +2 -2
  373. package/templates/skills/work-on-task/SKILL.md +1 -1
  374. package/templates/skills/work-on-task/content.md +1 -1
  375. package/dist/cli-51y09c6p.js +0 -2657
  376. package/dist/cli-yw91rnmw.js +0 -13
  377. package/dist/handlers-0pvee9nw.js +0 -39
  378. package/dist/index-gdh8jrdm.js +0 -43
  379. package/dist/index-hg0x2cdv.js +0 -40
  380. package/dist/index-thm0y6a8.js +0 -41
  381. package/dist/lead-ee8vazz2.js +0 -77
  382. package/dist/worker-16s75hm5.js +0 -77
@@ -13,11 +13,15 @@ export const argsSchema = z.object({
13
13
  .min(0)
14
14
  .max(1)
15
15
  .optional()
16
- .describe("Quiet when Jev's p(needs action) is below this (default 0.5)"),
16
+ .describe(
17
+ "Quiet when the model's p(needs action) is below this (default: the provider's measured threshold, 0.5 for Jev, 0.2 for Clef)",
18
+ ),
17
19
  provider: z
18
- .enum(["auto", "typesafe", "openrouter"])
20
+ .enum(["auto", "typesafe", "openrouter", "cloudflare"])
19
21
  .optional()
20
- .describe("Jev host; auto prefers TypeSafe (default auto)"),
22
+ .describe(
23
+ "Decision host: typesafe or openrouter (Jev), cloudflare (Clef). auto tries them in that order (default auto)",
24
+ ),
21
25
  timeZone: z
22
26
  .string()
23
27
  .optional()
@@ -28,25 +32,56 @@ export const argsSchema = z.object({
28
32
  .min(1_000)
29
33
  .max(45_000)
30
34
  .optional()
31
- .describe("Abandon the Jev call after this many ms (default 25000); the gate deadline is 60 s"),
35
+ .describe(
36
+ "Abandon the decision call after this many ms (default 25000); the gate deadline is 60 s",
37
+ ),
32
38
  });
33
39
 
34
40
  const KV_NAMESPACE = "heartbeat-jev-gate";
35
41
  const KV_KEY = "setup";
36
- // Measured per decision in the 2026-10-15 heartbeat replay (229 states).
37
- const COST_PER_DECISION = "$0.00004";
42
+ // Jev's cost, measured per decision in the 2026-10-15 heartbeat replay (229 states).
43
+ const JEV_COST_PER_DECISION = "$0.00004";
44
+ // Clef replayed on the same 229 states and labels (2026-10-20): 0.2 is the highest
45
+ // threshold that misses none of the 9 ticks where the Lead acted (57 quiet ticks;
46
+ // Jev at 0.5 has 100). Above 0.21 Clef starts missing real asks.
47
+ const CLEF_THRESHOLD = 0.2;
38
48
 
39
- // Jev is called through `ctx.swarm.systemOne_decide`: the API server reads the
40
- // key and sends the request, so no key ever enters this script's realm. The
49
+ // The model is called through `ctx.swarm.systemOne_decide`: the API server reads
50
+ // the key and sends the request, so no key ever enters this script's realm. The
41
51
  // server also refuses OpenRouter when the effective OPENROUTER_BASE_URL (stored
42
- // config or deployment env) points at a gateway.
52
+ // config or deployment env) points at a gateway; `auto` then tries the next
53
+ // usable provider, since this script cannot read the deployment env.
54
+ //
55
+ // `credentials` lists what must be set: every group needs one of its keys.
56
+ // `threshold` is the default the provider's own replay measured (see `CLEF_THRESHOLD`).
43
57
  const PROVIDERS = {
44
- typesafe: { keyName: "TYPESAFE_API_KEY", model: "jev-latest" },
45
- openrouter: { keyName: "OPENROUTER_API_KEY", model: "~typesafe/jev-latest" },
58
+ typesafe: {
59
+ model: "jev-latest",
60
+ family: "jev",
61
+ threshold: 0.5,
62
+ credentials: [["TYPESAFE_API_KEY"]],
63
+ setup: "`TYPESAFE_API_KEY` (Jev on TypeSafe)",
64
+ },
65
+ openrouter: {
66
+ model: "~typesafe/jev-latest",
67
+ family: "jev",
68
+ threshold: 0.5,
69
+ credentials: [["OPENROUTER_API_KEY"]],
70
+ setup: "`OPENROUTER_API_KEY` (Jev on OpenRouter)",
71
+ },
72
+ cloudflare: {
73
+ model: "clef",
74
+ family: "clef",
75
+ threshold: CLEF_THRESHOLD,
76
+ credentials: [["CLOUDFLARE_API_TOKEN", "CLOUDFLARE_API_KEY"], ["CLOUDFLARE_ACCOUNT_ID"]],
77
+ setup:
78
+ "`CLOUDFLARE_API_TOKEN` (or `CLOUDFLARE_API_KEY`) as a secret plus the `CLOUDFLARE_ACCOUNT_ID` config value (Clef on Cloudflare Workers AI)",
79
+ },
46
80
  } as const;
47
81
  type ProviderId = keyof typeof PROVIDERS;
82
+ const AUTO_ORDER: ProviderId[] = ["typesafe", "openrouter", "cloudflare"];
48
83
 
49
- // Worded exactly as in the replay that set the 0.5 threshold; changing it voids that result.
84
+ // Worded exactly as in the replays that set the thresholds; changing it voids those results.
50
85
  const QUESTIONS = {
51
86
  act: {
52
87
  type: "noul",
@@ -229,21 +264,25 @@ async function buildState(ctx: any, leadId: string, nowMs: number, timeZone: str
229
264
  };
230
265
  }
231
266
 
232
- function setupPrompt(keys: string[]) {
267
+ function setupPrompt(candidates: ProviderId[]) {
268
+ const options = candidates.map((id) => `- ${PROVIDERS[id].setup}`);
233
269
  return {
234
- title: "Heartbeat Jev gate: add a Jev credential or decline",
270
+ title: "Heartbeat gate: add a decision model credential or decline",
235
271
  detached: true,
236
272
  questions: [
237
273
  {
238
274
  id: "enable",
239
275
  type: "approval",
240
- label: "Enable the Jev heartbeat gate?",
276
+ label: "Enable the heartbeat checklist gate?",
241
277
  description: [
242
- "Before each recurring heartbeat checklist, the `heartbeat-jev-gate` script sends a short summary of swarm state (about 1,500 characters: new failures, due tracked items, task and agent counts) to Jev, a small decision model by TypeSafe, and asks whether the Lead needs to act. With `HEARTBEAT_CHECKLIST_GATE_MODE=enforce`, quiet ticks skip the Lead's checklist task.",
278
+ "Before each recurring heartbeat checklist, the `heartbeat-jev-gate` script sends a short summary of swarm state (about 1,500 characters: new failures, due tracked items, task and agent counts) to a small SystemOne decision model (Jev by TypeSafe, or Clef on Cloudflare Workers AI) and asks whether the Lead needs to act. With `HEARTBEAT_CHECKLIST_GATE_MODE=enforce`, quiet ticks skip the Lead's checklist task.",
243
279
  "",
244
- `Cost: about ${COST_PER_DECISION} per decision, one decision per checklist tick.`,
280
+ `Cost: one decision per checklist tick, about ${JEV_COST_PER_DECISION} each with Jev.`,
245
281
  "",
246
- `No Jev credential is configured. To enable, approve and add one of ${keys.map((k) => `\`${k}\``).join(" or ")} as a global secret on the Secrets page (Settings > Secrets). Do not paste the key here. The next tick picks it up.`,
282
+ "No credential for the gate is configured. To enable, approve and add one of these as a global value on the Secrets page (Settings > Secrets):",
283
+ ...options,
284
+ "",
285
+ "Do not paste a key here. The next tick picks it up.",
247
286
  "",
248
287
  "Reject to decline. The gate then stays inactive, with no further prompts, until a key is added.",
249
288
  ].join("\n"),
@@ -253,9 +292,10 @@ function setupPrompt(keys: string[]) {
253
292
  }
254
293
 
255
294
  /**
256
- * Heartbeat checklist gate backed by Jev: asks Jev whether a recurring checklist
257
- * tick needs the Lead and returns { quiet, reason, summary }. With no Jev
258
- * credential it asks a human once, then stays a no-op until a key is added.
295
+ * Heartbeat checklist gate backed by a SystemOne decision model (Jev or Clef):
296
+ * asks whether a recurring checklist tick needs the Lead and returns
297
+ * { quiet, reason, summary }. With no credential it asks a human once, then
298
+ * stays a no-op until a key is added.
259
299
  */
260
300
  export default async function heartbeatJevGate(args: any, ctx: any) {
261
301
  const parsed = argsSchema.safeParse(args ?? {});
@@ -276,25 +316,21 @@ export default async function heartbeatJevGate(args: any, ctx: any) {
276
316
  };
277
317
 
278
318
  const cfgThreshold = Number.parseFloat(cfg("HEARTBEAT_JEV_GATE_THRESHOLD"));
279
- const threshold =
280
- a.threshold ?? (cfgThreshold >= 0 && cfgThreshold <= 1 ? cfgThreshold : 0.5);
281
319
  const providerPref = a.provider ?? (cfg("HEARTBEAT_JEV_GATE_PROVIDER").toLowerCase() || "auto");
282
320
  const timeZone = a.timeZone ?? (cfg("HEARTBEAT_JEV_GATE_TIMEZONE") || "UTC");
283
321
 
284
- const usable: Record<ProviderId, boolean> = {
285
- typesafe: cfg(PROVIDERS.typesafe.keyName) !== "",
286
- openrouter: cfg(PROVIDERS.openrouter.keyName) !== "",
287
- };
322
+ const usable = (id: ProviderId) =>
323
+ PROVIDERS[id].credentials.every((group) => group.some((key) => cfg(key) !== ""));
288
324
  const candidates: ProviderId[] =
289
- providerPref === "typesafe"
290
- ? ["typesafe"]
291
- : providerPref === "openrouter"
292
- ? ["openrouter"]
293
- : ["typesafe", "openrouter"];
294
- const provider = candidates.find((id) => usable[id]);
325
+ Object.hasOwn(PROVIDERS, providerPref) ? [providerPref as ProviderId] : AUTO_ORDER;
326
+ const usableCandidates = candidates.filter(usable);
295
327
 
296
- if (!provider) {
297
- const keys = candidates.map((id) => PROVIDERS[id].keyName);
328
+ if (usableCandidates.length === 0) {
329
+ const keys = candidates.map((id) =>
330
+ PROVIDERS[id].credentials
331
+ .map((group) => (group.length > 1 ? `(${group.join(" or ")})` : group[0]))
332
+ .join(" + "),
333
+ );
298
334
  const inactive = (state: string, extra: Record<string, unknown> = {}) => ({
299
335
  quiet: false,
300
336
  reason: `jev-gate inactive: ${state}`.slice(0, 300),
@@ -305,7 +341,7 @@ export default async function heartbeatJevGate(args: any, ctx: any) {
305
341
  let setup: SetupState | null = typeof stored === "string" ? JSON.parse(stored) : (stored ?? null);
306
342
 
307
343
  if (!setup) {
308
- const prompt = setupPrompt(keys);
344
+ const prompt = setupPrompt(candidates);
309
345
  if (dryRun) {
310
346
  const state = await buildState(ctx, leadId, Date.now(), timeZone);
311
347
  return inactive("no credential, would ask a human", { prompt, state });
@@ -349,35 +385,51 @@ export default async function heartbeatJevGate(args: any, ctx: any) {
349
385
  return inactive(why, { setup });
350
386
  }
351
387
 
352
- const target = PROVIDERS[provider];
353
388
  const state = await buildState(ctx, leadId, Date.now(), timeZone);
354
389
 
355
- // The server enforces the time budget. No retries, as in the replay.
356
- const res: any = await ctx.swarm.systemOne_decide({
357
- provider,
358
- model: target.model,
359
- state,
360
- questions: QUESTIONS,
361
- timeoutMs: a.jevTimeoutMs ?? 25_000,
362
- maxRetries: 0,
363
- });
364
- const body: any = res?.data ?? res;
365
- if (res?.success === false || body?.success === false) {
366
- throw new Error(`jev ${provider}: ${String(body?.message ?? body?.error ?? `HTTP ${res?.status}`).slice(0, 250)}`);
390
+ // The server enforces the time budget. No retries, as in the replay. A provider
391
+ // this deployment refuses (OpenRouter behind a gateway) sends nothing, so the
392
+ // next usable one is tried; only an explicit choice or the last one throws.
393
+ let provider = usableCandidates[0] as ProviderId;
394
+ let body: any;
395
+ const skipped: string[] = [];
396
+ for (const [i, id] of usableCandidates.entries()) {
397
+ provider = id;
398
+ const res: any = await ctx.swarm.systemOne_decide({
399
+ provider: id,
400
+ model: PROVIDERS[id].model,
401
+ state,
402
+ questions: QUESTIONS,
403
+ timeoutMs: a.jevTimeoutMs ?? 25_000,
404
+ maxRetries: 0,
405
+ });
406
+ body = res?.data ?? res;
407
+ if (res?.success !== false && body?.success !== false) break;
408
+ const why = String(body?.message ?? body?.error ?? `HTTP ${res?.status}`).slice(0, 250);
409
+ if (body?.providerUnavailable === true && i < usableCandidates.length - 1) {
410
+ skipped.push(`${id}: ${why}`);
411
+ continue;
412
+ }
413
+ throw new Error(`${PROVIDERS[id].family} ${id}: ${why}`);
367
414
  }
415
+ const target = PROVIDERS[provider];
416
+ // A configured threshold applies to every provider; unset, each uses its measured one.
417
+ const threshold =
418
+ a.threshold ?? (cfgThreshold >= 0 && cfgThreshold <= 1 ? cfgThreshold : target.threshold);
368
419
  const p = body?.answers?.act?.noul;
369
420
  if (typeof p !== "number" || !Number.isFinite(p)) {
370
- throw new Error(`jev ${provider}: response has no numeric answers.act.noul`);
421
+ throw new Error(`${target.family} ${provider}: response has no numeric answers.act.noul`);
371
422
  }
372
423
 
373
424
  const pText = p.toFixed(3);
374
425
  const quiet = p < threshold;
375
426
  const verdict: Record<string, unknown> = {
376
427
  quiet,
377
- reason: `jev p(needs action)=${pText} ${quiet ? "<" : ">="} ${threshold} via ${provider}`,
428
+ reason: `${target.family} p(needs action)=${pText} ${quiet ? "<" : ">="} ${threshold} via ${provider}`,
378
429
  };
379
430
  if (!quiet) {
380
- const lines = [`Jev p(needs action) = ${pText} (threshold ${threshold}, ${provider}).`];
431
+ const model = target.family === "jev" ? "Jev" : "Clef";
432
+ const lines = [`${model} p(needs action) = ${pText} (threshold ${threshold}, ${provider}).`];
381
433
  const failures = state.new_failures_since_previous_heartbeat;
382
434
  const tracked = state.tracked_items_mentioning_today_or_past_delete_date;
383
435
  if (Array.isArray(failures)) {
@@ -387,9 +439,9 @@ export default async function heartbeatJevGate(args: any, ctx: any) {
387
439
  lines.push("", "Tracked items due today or past their DELETE date:", ...tracked.map((t) => `- ${t.replace(/^[-*] /, "")}`));
388
440
  }
389
441
  if (!Array.isArray(failures) && !Array.isArray(tracked)) {
390
- lines.push("", "No new failures or due tracked items; Jev flagged the overall state.");
442
+ lines.push("", `No new failures or due tracked items; ${model} flagged the overall state.`);
391
443
  }
392
444
  verdict.summary = lines.join("\n").slice(0, 4000);
393
445
  }
394
- return dryRun ? { ...verdict, dryRun: true, provider, p, threshold, state } : verdict;
446
+ return dryRun ? { ...verdict, dryRun: true, provider, skipped, p, threshold, state } : verdict;
395
447
  }
@@ -116,9 +116,9 @@ export const SEED_SCRIPTS: SeedScript[] = [
116
116
  {
117
117
  name: "heartbeat-jev-gate",
118
118
  description:
119
- "Heartbeat checklist gate backed by Jev (TypeSafe or OpenRouter decisions API): scores a compact swarm state and returns {quiet, reason, summary} for HEARTBEAT_CHECKLIST_GATE_SCRIPT.",
119
+ "Heartbeat checklist gate backed by a SystemOne decision model (Jev on TypeSafe or OpenRouter, Clef on Cloudflare Workers AI): scores a compact swarm state and returns {quiet, reason, summary} for HEARTBEAT_CHECKLIST_GATE_SCRIPT.",
120
120
  intent:
121
- "Skip quiet Lead heartbeat checklist ticks with a cheap Jev decision; with no Jev credential it asks a human once, then stays a no-op until a key is added.",
121
+ "Skip quiet Lead heartbeat checklist ticks with a cheap Jev or Clef decision; with no credential it asks a human once, then stays a no-op until a key is added.",
122
122
  source: asText(heartbeatJevGateSrc),
123
123
  },
124
124
  {
@@ -10,6 +10,7 @@
10
10
  */
11
11
 
12
12
  import { homedir } from "node:os";
13
+ import type { SkillFile, SkillWithInstallInfo } from "../types";
13
14
  import {
14
15
  type SkillFsEntry,
15
16
  type SkillHarnessTarget,
@@ -71,8 +72,7 @@ export interface SkillsSignature {
71
72
  * uninstall, toggle, or skill-update mutates at least one of them. Output is
72
73
  * deterministic and contains no timestamps beyond per-row mutation fields.
73
74
  */
74
- export async function computeAgentSkillsSignature(agentId: string): Promise<SkillsSignature> {
75
- const skills = await getAgentSkills(agentId);
75
+ export function computeSkillsSignature(skills: readonly SkillWithInstallInfo[]): SkillsSignature {
76
76
  const sorted = [...skills].sort((a, b) => a.id.localeCompare(b.id));
77
77
  const canonical = JSON.stringify(
78
78
  sorted.map((s) => [
@@ -89,3 +89,44 @@ export async function computeAgentSkillsSignature(agentId: string): Promise<Skil
89
89
  const hash = new Bun.CryptoHasher("sha256").update(canonical).digest("hex");
90
90
  return { hash, count: sorted.length };
91
91
  }
92
+
93
+ /**
94
+ * Fetch the agent's skills and sign them.
95
+ *
96
+ * Callers that already hold the rows (the bundle endpoint) must use
97
+ * `computeSkillsSignature` on those exact rows instead of calling this: a
98
+ * second read can observe a newer edit and pair a fresh hash with stale
99
+ * payload bytes, which makes later signature probes skip the refresh forever.
100
+ */
101
+ export async function computeAgentSkillsSignature(agentId: string): Promise<SkillsSignature> {
102
+ return computeSkillsSignature(await getAgentSkills(agentId));
103
+ }
104
+
105
+ export interface SkillsBundle {
106
+ skills: (SkillWithInstallInfo & { files: SkillFile[] })[];
107
+ total: number;
108
+ signature: string;
109
+ }
110
+
111
+ /**
112
+ * Build the worker boot payload: every installed skill plus its bundled files,
113
+ * signed over the rows this same call returned.
114
+ *
115
+ * The readers are injected so the ordering the fix depends on is testable: the
116
+ * signature is taken before the first `readFiles`, so an edit that lands while
117
+ * the files are being read cannot pair stale bytes with a newer hash.
118
+ */
119
+ export async function buildSkillsBundle(
120
+ readSkills: () => Promise<SkillWithInstallInfo[]>,
121
+ readFiles: (skillId: string) => Promise<SkillFile[]>,
122
+ ): Promise<SkillsBundle> {
123
+ const skills = await readSkills();
124
+ const signature = computeSkillsSignature(skills).hash;
125
+ const entries = await Promise.all(
126
+ skills.map(async (skill) => ({
127
+ ...skill,
128
+ files: skill.isComplex ? await readFiles(skill.id) : [],
129
+ })),
130
+ );
131
+ return { skills: entries, total: entries.length, signature };
132
+ }
@@ -68,7 +68,11 @@ const RESERVED_KEYS = new Set([
68
68
  // the agent-fs bootstrap admin key would let a compromised worker administer the
69
69
  // shared org. The API materializes these into its own process.env at boot via
70
70
  // getInjectableGlobalConfigs, so no HTTP consumer legitimately needs them.
71
- const API_ONLY_CONFIG_KEYS = new Set(["API_AGENT_FS_API_KEY", "SLACK_SIGNING_SECRET"]);
71
+ const API_ONLY_CONFIG_KEYS = new Set([
72
+ "API_AGENT_FS_API_KEY",
73
+ "SLACK_SIGNING_SECRET",
74
+ "TRANSCRIPTION_API_KEY",
75
+ ]);
72
76
 
73
77
  export function isApiOnlyConfigKey(key: string): boolean {
74
78
  return API_ONLY_CONFIG_KEYS.has(key);
@@ -160,6 +164,20 @@ export const APPS_JSON_COLUMN_MAX_BYTES_BOUNDS = { min: 1_024, max: 16_777_216 }
160
164
  */
161
165
  export const CODEX_OAUTH_KEEP_WARM_MAX_AGE_HOURS_BOUNDS = { min: 1, max: 216 } as const;
162
166
 
167
+ /**
168
+ * Decision hosts `SYSTEM_ONE_DEFAULT_PROVIDER` may name: `SYSTEM_ONE_PROVIDER_IDS`
169
+ * in `src/workflows/executors/system-one-providers.ts`, listed here because that
170
+ * module's credential readers would pull the LLM stack into every `db.ts` import.
171
+ * A test keeps the two lists equal.
172
+ */
173
+ export const SYSTEM_ONE_DEFAULT_PROVIDER_OPTIONS = [
174
+ "typesafe",
175
+ "openrouter",
176
+ "laya",
177
+ "openai",
178
+ "cloudflare",
179
+ ] as const;
180
+
163
181
  /** Build `{ KEY: validator }` entries accepting only boolean literals. */
164
182
  function booleanValidators(keys: string[]): Record<string, ConfigValidator> {
165
183
  const message = (key: string) =>
@@ -399,6 +417,7 @@ const VALIDATED_KEYS: Record<string, ConfigValidator> = {
399
417
  },
400
418
  ...enumValidator("CLAUDE_TRANSPORT", ["cli", "sdk"]),
401
419
  ...enumValidator("SCRIPT_EXECUTOR", ["native", "quickjs"]),
420
+ ...enumValidator("TRANSCRIPTION_PROVIDER", ["openai", "elevenlabs", "groq"]),
402
421
  SCRIPT_QUICKJS_POOL_SIZE: validateQuickJSPoolSize,
403
422
  ...booleanValidators(["SCRIPT_EGRESS_ALLOW_PRIVATE"]),
404
423
  SCRIPT_EGRESS_ALLOWLIST: (value) =>
@@ -407,6 +426,7 @@ const VALIDATED_KEYS: Record<string, ConfigValidator> = {
407
426
  // model's weekly window (Fable/Opus/Sonnet). fallback: legacy random pick.
408
427
  ...enumValidator("MODEL_WINDOW_EXHAUSTED_POLICY", ["fail", "fallback"]),
409
428
  ...enumValidator("HEARTBEAT_CHECKLIST_GATE_MODE", ["shadow", "enforce"]),
429
+ ...enumValidator("SYSTEM_ONE_DEFAULT_PROVIDER", [...SYSTEM_ONE_DEFAULT_PROVIDER_OPTIONS]),
410
430
  // Codex credits-exhausted cooldown (ms). Permissive on range here (positive
411
431
  // integer) — the worker clamps to [5m, 7d] via resolveCodexCreditsExhaustedCooldownMs.
412
432
  CODEX_CREDITS_EXHAUSTED_COOLDOWN_MS: (value) => {
@@ -479,6 +499,7 @@ const VALIDATED_KEYS: Record<string, ConfigValidator> = {
479
499
  "GITHUB_DISABLE",
480
500
  "GITLAB_DISABLE",
481
501
  "AZURE_DEVOPS_DISABLE",
502
+ "BITBUCKET_DISABLE",
482
503
  "LINEAR_DISABLE",
483
504
  "JIRA_DISABLE",
484
505
  "AGENTMAIL_DISABLE",
@@ -582,6 +603,9 @@ const VALIDATED_KEYS: Record<string, ConfigValidator> = {
582
603
  // default in thread-buffer.ts; above 10 minutes a
583
604
  // reply waits longer than any operator expects.
584
605
  ...boundedIntegerValidators(["CHAT_SDK_BUFFER_MS"], 1, 600_000),
606
+ // Minutes a Chat SDK thread keeps its owner after the owner's task ends.
607
+ // 0 keeps it only while the task is open; a day is the longest useful window.
608
+ ...boundedIntegerValidators(["CHAT_SDK_OWNER_WINDOW_MIN"], 0, 1440),
585
609
  // Below ~100 tokens the preamble can't fit a useful summary; above 20000
586
610
  // (~80k chars) it risks the SIGTERM-143 context-saturation failure mode
587
611
  // the cap exists to prevent (see context-preamble.ts).
@@ -68,3 +68,33 @@ export async function recordTaskAttachmentUpload(
68
68
  throw error;
69
69
  }
70
70
  }
71
+
72
+ // Resolve against the row's ACTUAL stored key + its own org/drive, rather than
73
+ // reconstructing `tasks/<taskId>/<name>`. Attachments written to agent-fs via the
74
+ // CLI live at arbitrary paths (`misc/…`, `smoke/…`); reconstruction mis-resolves
75
+ // them, which previously caused downloads to 404 and deletes to orphan the real
76
+ // file. New uploads have no stored key yet at scope-build time and keep the
77
+ // forward-looking layout.
78
+ export function scopeFromAttachment(attachment: TaskAttachment): FileScope {
79
+ const storedKey = attachment.providerKey ?? attachment.path ?? "";
80
+ return {
81
+ taskId: attachment.taskId,
82
+ name: attachment.name,
83
+ key: storedKey.trim() ? storedKey : undefined,
84
+ orgId: attachment.orgId,
85
+ driveId: attachment.driveId,
86
+ };
87
+ }
88
+
89
+ // Which file-storage provider (if any) actually holds this attachment's bytes.
90
+ // Only agent-fs objects and local-fs uploads are provider-backed; `shared-fs`
91
+ // worker-volume pointers, `url`, and `page` attachments have no provider object,
92
+ // so they must never be fetched or deleted through a provider (doing so would
93
+ // mis-resolve and, on delete, orphan the real target). Note local-fs uploads are
94
+ // recorded with kind "shared-fs" but an explicit local-fs providerId, so key off
95
+ // both signals rather than kind alone.
96
+ export function backingProviderId(attachment: TaskAttachment): string | null {
97
+ if (attachment.kind === "agent-fs") return attachment.providerId ?? "agent-fs";
98
+ if (attachment.providerId === "local-fs") return "local-fs";
99
+ return null;
100
+ }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Bitbucket Cloud REST interactions — resolving the bot identity behind the
3
+ * token and posting pull request comments. Uses raw fetch, no SDK.
4
+ */
5
+
6
+ import { scrubSecrets } from "../utils/secret-scrubber";
7
+ import { getBitbucketToken } from "./auth";
8
+
9
+ export const BITBUCKET_API = "https://api.bitbucket.org/2.0";
10
+
11
+ function headers(): Record<string, string> {
12
+ const token = getBitbucketToken();
13
+ if (!token) throw new Error("[Bitbucket] No API token configured");
14
+ return {
15
+ Authorization: `Bearer ${token}`,
16
+ "Content-Type": "application/json",
17
+ Accept: "application/json",
18
+ };
19
+ }
20
+
21
+ let cachedBotAccountId: string | null = null;
22
+
23
+ /**
24
+ * Atlassian `account_id` of the bot. A picker @mention is stored as
25
+ * `@{<account_id>}` in the comment's raw content, so this is what mention
26
+ * detection matches. Uses BITBUCKET_BOT_ACCOUNT_ID when set, otherwise the
27
+ * user that owns the token (`GET /2.0/user`), cached. Returns null when
28
+ * neither is available; callers then fall back to plain-text `@name` matching.
29
+ */
30
+ export async function resolveBitbucketBotAccountId(): Promise<string | null> {
31
+ const fromEnv = process.env.BITBUCKET_BOT_ACCOUNT_ID?.trim();
32
+ if (fromEnv) return fromEnv;
33
+ if (cachedBotAccountId) return cachedBotAccountId;
34
+ if (!getBitbucketToken()) return null;
35
+ try {
36
+ // Redirects are refused so the token never follows a response to another host.
37
+ const resp = await fetch(`${BITBUCKET_API}/user`, { headers: headers(), redirect: "error" });
38
+ if (!resp.ok) {
39
+ console.error(`[Bitbucket] GET /user returned ${resp.status}`);
40
+ return null;
41
+ }
42
+ const data = (await resp.json()) as { account_id?: string };
43
+ cachedBotAccountId = data.account_id?.trim() || null;
44
+ return cachedBotAccountId;
45
+ } catch (err) {
46
+ console.error(`[Bitbucket] Failed to resolve bot identity: ${scrubSecrets(String(err))}`);
47
+ return null;
48
+ }
49
+ }
50
+
51
+ /** Reset the cached bot identity (tests, config reloads). */
52
+ export function resetBitbucketBotCache(): void {
53
+ cachedBotAccountId = null;
54
+ }
55
+
56
+ /** Post a comment on a pull request. `fullName` is `<workspace>/<repo_slug>`. */
57
+ export async function postBitbucketPullRequestComment(
58
+ fullName: string,
59
+ pullRequestId: number,
60
+ raw: string,
61
+ ): Promise<void> {
62
+ const [workspace, repoSlug] = fullName.split("/");
63
+ if (!workspace || !repoSlug) throw new Error(`[Bitbucket] Invalid repository: ${fullName}`);
64
+ const url =
65
+ `${BITBUCKET_API}/repositories/${encodeURIComponent(workspace)}/` +
66
+ `${encodeURIComponent(repoSlug)}/pullrequests/${pullRequestId}/comments`;
67
+ const resp = await fetch(url, {
68
+ method: "POST",
69
+ headers: headers(),
70
+ redirect: "error",
71
+ body: JSON.stringify({ content: { raw } }),
72
+ });
73
+ if (!resp.ok) {
74
+ const text = await resp.text().catch(() => "");
75
+ throw new Error(
76
+ scrubSecrets(
77
+ `[Bitbucket] Comment on ${fullName}#${pullRequestId} failed: ${resp.status} ${text}`,
78
+ ),
79
+ );
80
+ }
81
+ }
@@ -0,0 +1,64 @@
1
+ import { isEnvFlagEnabled } from "../utils/env-flag";
2
+ import { verifyHmacSignature } from "../workflows/triggers";
3
+
4
+ /**
5
+ * Bitbucket Cloud Authentication & Webhook Verification
6
+ *
7
+ * Webhooks are signed with the webhook's secret: `X-Hub-Signature` carries
8
+ * `sha256=<hex HMAC-SHA256 of the raw body>`, checked against
9
+ * BITBUCKET_WEBHOOK_SECRET. API calls send BITBUCKET_TOKEN (an Atlassian API
10
+ * token or a workspace access token) as a Bearer token.
11
+ */
12
+
13
+ let initialized = false;
14
+ let webhookSecret: string | null = null;
15
+ let apiToken: string | null = null;
16
+
17
+ export function isBitbucketEnabled(): boolean {
18
+ return !!process.env.BITBUCKET_WEBHOOK_SECRET && !isEnvFlagEnabled("BITBUCKET_DISABLE", false);
19
+ }
20
+
21
+ export function initBitbucket(): void {
22
+ if (initialized) return;
23
+ if (!isBitbucketEnabled()) {
24
+ console.log("[Bitbucket] Integration disabled (BITBUCKET_WEBHOOK_SECRET not set)");
25
+ return;
26
+ }
27
+
28
+ webhookSecret = process.env.BITBUCKET_WEBHOOK_SECRET!;
29
+ apiToken = process.env.BITBUCKET_TOKEN ?? null;
30
+ initialized = true;
31
+
32
+ console.log(`[Bitbucket] Integration initialized (API token: ${apiToken ? "set" : "not set"})`);
33
+ }
34
+
35
+ /**
36
+ * Verify a Bitbucket webhook delivery from its raw body and `X-Hub-Signature`
37
+ * header. Only the `sha256=<hex>` form Bitbucket sends is accepted.
38
+ */
39
+ export function verifyBitbucketWebhook(rawBody: string, signatureHeader: string | undefined) {
40
+ if (!webhookSecret) return false;
41
+ if (!signatureHeader?.startsWith("sha256=")) return false;
42
+ return verifyHmacSignature(webhookSecret, rawBody, signatureHeader);
43
+ }
44
+
45
+ /** Get the Bitbucket token for API calls. */
46
+ export function getBitbucketToken(): string | null {
47
+ return apiToken;
48
+ }
49
+
50
+ /**
51
+ * Bot name for plain-text `@name` mentions. Read on every call: a stored
52
+ * swarm_config value reaches process.env after this module loads, and again
53
+ * on each config reload.
54
+ */
55
+ export function getBitbucketBotName(): string {
56
+ return process.env.BITBUCKET_BOT_NAME || "agent-swarm-bot";
57
+ }
58
+
59
+ /** Reset state for testing and config reloads. */
60
+ export function resetBitbucket(): void {
61
+ initialized = false;
62
+ webhookSecret = null;
63
+ apiToken = null;
64
+ }