@desplega.ai/agent-swarm 1.131.1 → 1.134.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 (360) hide show
  1. package/README.md +3 -3
  2. package/dist/{actions-jgcy477k.js → actions-7kast243.js} +20 -18
  3. package/dist/{anthropic-messages-rjmd2e7n.js → anthropic-messages-706yad38.js} +33 -17
  4. package/dist/app-0g84t8wf.js +28 -0
  5. package/dist/{artifact-jysj3wrg.js → artifact-dje5ke4h.js} +37 -131
  6. package/dist/{assistant-f2vkk1hj.js → assistant-ac4htcky.js} +25 -22
  7. package/dist/{azure-openai-responses-c0yjmbfd.js → azure-openai-responses-be59fbsz.js} +13 -13
  8. package/dist/{boot-reembed-q7mp8hcr.js → boot-reembed-b1rcqn85.js} +13 -11
  9. package/dist/{boot-reembed-xmw8wcx6.js → boot-reembed-kw2541c4.js} +14 -12
  10. package/dist/{boot-scrub-logs-dsqrv79n.js → boot-scrub-logs-ja29t92g.js} +11 -9
  11. package/dist/{claude-adapter-q6x7yspx.js → claude-adapter-5cg1fv8s.js} +33 -29
  12. package/dist/{claude-managed-adapter-wsg37bt8.js → claude-managed-adapter-9z9sdytx.js} +9 -9
  13. package/dist/{claude-managed-setup-52z4mq9w.js → claude-managed-setup-3zqp51cr.js} +5 -5
  14. package/dist/{claude-1ex232fc.js → claude-wqn3dtca.js} +1 -1
  15. package/dist/{cli-4wgngmwv.js → cli-00w4hbam.js} +1 -1
  16. package/dist/{cli-er3a4bb0.js → cli-15429qdw.js} +6 -1
  17. package/dist/{cli-e5dabkx0.js → cli-1cwhyvta.js} +4 -4
  18. package/dist/{cli-zsspvvm2.js → cli-1ybr6vw2.js} +237 -237
  19. package/dist/{cli-xpxde7rm.js → cli-2wv4kj0e.js} +60 -31
  20. package/dist/cli-34w2584r.js +530 -0
  21. package/dist/{cli-se2p6rkb.js → cli-38taxznr.js} +6 -6
  22. package/dist/{cli-9sbhdpvb.js → cli-3cgfyfzm.js} +14 -14
  23. package/dist/{cli-5n6e5yfn.js → cli-3zmdzfjf.js} +79 -74
  24. package/dist/{cli-3ty0t082.js → cli-43x36mty.js} +197 -8
  25. package/dist/{cli-ycrnynpw.js → cli-480nehnh.js} +29 -22
  26. package/dist/{cli-0258203w.js → cli-492e7ap5.js} +140 -20
  27. package/dist/{cli-3fj60etf.js → cli-497b685t.js} +3 -3
  28. package/dist/{cli-pxfxkx5m.js → cli-4bap00k0.js} +1 -1
  29. package/dist/{cli-w2wxd56v.js → cli-4bc8vmhm.js} +848 -770
  30. package/dist/{cli-aca5pz9f.js → cli-589599p9.js} +1 -1
  31. package/dist/{cli-t930g0xh.js → cli-5tnr64pm.js} +1 -1
  32. package/dist/{cli-jxg3s9ey.js → cli-65d93ves.js} +7 -7
  33. package/dist/{cli-6dpytdr2.js → cli-6x363v1z.js} +2 -2
  34. package/dist/{cli-dxak0fqd.js → cli-787ph852.js} +11 -11
  35. package/dist/{cli-793jvbsh.js → cli-7bv8nckk.js} +16 -11
  36. package/dist/{cli-zyxs93y9.js → cli-7jcc9y1r.js} +3 -3
  37. package/dist/{cli-f1am9szv.js → cli-87t2gstf.js} +1 -1
  38. package/dist/{cli-9aryf9tv.js → cli-95m51ztz.js} +1 -1
  39. package/dist/{cli-4w4fmq9x.js → cli-9w49enmh.js} +2 -2
  40. package/dist/{cli-v69vk5h0.js → cli-9y5b02vr.js} +13 -13
  41. package/dist/{cli-x23me2mg.js → cli-a1rrkrb4.js} +1 -1
  42. package/dist/{cli-psrqtw2m.js → cli-a3pkfpqs.js} +2 -2
  43. package/dist/{cli-h0rgxd24.js → cli-a4ajmfvp.js} +6 -6
  44. package/dist/{cli-ygrbmjja.js → cli-aw8bb6sf.js} +6 -6
  45. package/dist/{cli-g74vpjp2.js → cli-brm3et47.js} +55 -22
  46. package/dist/{cli-czkae1fp.js → cli-c5ddbdfj.js} +9 -9
  47. package/dist/{cli-114fvtmj.js → cli-c79a9zrc.js} +1 -1
  48. package/dist/{cli-49n9f49x.js → cli-dx3dz0ms.js} +3 -3
  49. package/dist/{cli-6dp3ydxa.js → cli-e07exmsb.js} +18 -14
  50. package/dist/cli-et1bbwqf.js +13 -0
  51. package/dist/{cli-yazx6hap.js → cli-f3s9dry0.js} +14 -14
  52. package/dist/{cli-t8d349cy.js → cli-f4wj0s3m.js} +3 -3
  53. package/dist/{cli-d6n0thxw.js → cli-f64gdfxc.js} +1 -1
  54. package/dist/{cli-pwfsqc2x.js → cli-fyd58xgb.js} +55 -55
  55. package/dist/{cli-7dm2zqy2.js → cli-g0j14czv.js} +2 -2
  56. package/dist/{cli-b0p7rfnd.js → cli-g2p9bd1z.js} +14 -14
  57. package/dist/{cli-z905tvmm.js → cli-g814jbst.js} +2 -2
  58. package/dist/{cli-5dsbcjjw.js → cli-ghexzd2d.js} +970 -970
  59. package/dist/{cli-jvv0g8bh.js → cli-gpt9kf01.js} +15 -15
  60. package/dist/{cli-rgrfzg8z.js → cli-grt6qv91.js} +380 -380
  61. package/dist/{cli-wz9115x0.js → cli-gtafvdy0.js} +8 -8
  62. package/dist/{cli-q5dzzak0.js → cli-gy07xk4s.js} +2 -2
  63. package/dist/{cli-sy7jk3tx.js → cli-h0djenzm.js} +2 -2
  64. package/dist/{cli-nrkkknat.js → cli-h37gbjbf.js} +2 -281
  65. package/dist/{cli-jevgrb4w.js → cli-h39xxnq6.js} +1200 -491
  66. package/dist/{cli-gz9sbyk3.js → cli-jepmgn7r.js} +1 -1
  67. package/dist/{cli-ccf9vy7q.js → cli-jmvk6et8.js} +7 -7
  68. package/dist/{cli-2rk0cx7s.js → cli-jxj92gkt.js} +51 -41
  69. package/dist/{cli-bqw0mt1n.js → cli-k46t4664.js} +18 -3
  70. package/dist/{cli-50vbt00w.js → cli-m07313tr.js} +1153 -696
  71. package/dist/{cli-4j2mrzag.js → cli-mkq1rk7b.js} +1 -1
  72. package/dist/{cli-1z7048dy.js → cli-mnm7w46e.js} +1 -1
  73. package/dist/{cli-kd1ck4rx.js → cli-mp9sg4cg.js} +14 -8
  74. package/dist/{cli-jbcva0xj.js → cli-mrammd8c.js} +60 -35
  75. package/dist/cli-nanaxrz6.js +27 -0
  76. package/dist/{cli-p9swy5t3.js → cli-pq2ee5ja.js} +8 -6
  77. package/dist/{cli-2wadx7p2.js → cli-pt43pdp1.js} +2 -2
  78. package/dist/{cli-7sgrxg9a.js → cli-q6d0vvey.js} +4 -4
  79. package/dist/{cli-k9qkf1hw.js → cli-qf9kpsve.js} +9 -9
  80. package/dist/cli-qh4sqfws.js +15 -0
  81. package/dist/{cli-e15jhafb.js → cli-qntkxysp.js} +14 -14
  82. package/dist/{cli-sajgpkqg.js → cli-qs9hhsvb.js} +1723 -325
  83. package/dist/{cli-z2zcxes1.js → cli-qt5bt3vk.js} +1 -1
  84. package/dist/{cli-wx82tw1k.js → cli-qtekrty1.js} +3 -3
  85. package/dist/{cli-g1re8r05.js → cli-rh6f3s9n.js} +3 -3
  86. package/dist/{cli-n0krnc6d.js → cli-rzhcrw2p.js} +2 -2
  87. package/dist/{cli-gecy3z2s.js → cli-snw62ze5.js} +4 -4
  88. package/dist/{cli-n6h8qt8n.js → cli-snz3p4yf.js} +15 -10
  89. package/dist/{cli-r6kqqbj0.js → cli-tkf8n6cw.js} +11 -11
  90. package/dist/{cli-4rkfg1wx.js → cli-tkrr24mz.js} +14 -9
  91. package/dist/{cli-9dr5qzc0.js → cli-tv9f1kyb.js} +54 -43
  92. package/dist/{cli-gm9cbhbq.js → cli-vtz6yj3v.js} +28 -9
  93. package/dist/{cli-2f07m0xr.js → cli-wf86d5t7.js} +1 -1
  94. package/dist/{cli-7zd05xb4.js → cli-whrvct19.js} +45 -45
  95. package/dist/{cli-ap1czx8f.js → cli-wrrea8xm.js} +3 -3
  96. package/dist/{cli-57khth04.js → cli-xmyqv00z.js} +120 -4
  97. package/dist/{cli-cwy5hqbq.js → cli-yjmk7tzc.js} +1 -1
  98. package/dist/{cli-6pcz9h15.js → cli-zykjrwcx.js} +180 -394
  99. package/dist/cli.js +45 -45
  100. package/dist/{codex-adapter-htjkn22b.js → codex-adapter-hsq6mz3m.js} +21 -19
  101. package/dist/{codex-hook-hq54t9ft.js → codex-hook-8c500gq8.js} +8 -7
  102. package/dist/{codex-login-z6yhbkmk.js → codex-login-x7603481.js} +3 -3
  103. package/dist/{codex-session-runner-wjfc7smz.js → codex-session-runner-c1x3x143.js} +15 -13
  104. package/dist/{commands-xhrfwqsk.js → commands-rwtddtst.js} +11 -9
  105. package/dist/{db-anvwb22f.js → db-j2b355vz.js} +458 -444
  106. package/dist/{devin-adapter-v1sarqtw.js → devin-adapter-8qmhqpy3.js} +3 -3
  107. package/dist/{devtools-2y1r7yqk.js → devtools-j2rnj8w7.js} +4 -4
  108. package/dist/{e2b-jdkwemv2.js → e2b-znpx78eg.js} +16 -16
  109. package/dist/{es5-vjgtkgp6.js → es5-fmqrjzy3.js} +2 -2
  110. package/dist/{event-bus-axn823e9.js → event-bus-n8sb35pw.js} +4 -4
  111. package/dist/{getMachineId-bsd-6tc9wxdj.js → getMachineId-bsd-wpzbhkm6.js} +4 -4
  112. package/dist/{getMachineId-darwin-sjt9sgnc.js → getMachineId-darwin-45zabjy0.js} +4 -4
  113. package/dist/{getMachineId-linux-v5n18y9p.js → getMachineId-linux-warnp5pd.js} +3 -3
  114. package/dist/{getMachineId-unsupported-yrjyqw3d.js → getMachineId-unsupported-5spent2a.js} +3 -3
  115. package/dist/{getMachineId-win-w3r8063c.js → getMachineId-win-v4bvcw7a.js} +4 -4
  116. package/dist/{google-generative-ai-hysrd8qx.js → google-generative-ai-b0wzss55.js} +17 -14
  117. package/dist/{google-vertex-p5arrga0.js → google-vertex-e8q4dpya.js} +18 -15
  118. package/dist/{handlers-b9kbc621.js → handlers-hchgrxkh.js} +32 -29
  119. package/dist/{hook-xkvn6c5h.js → hook-p49egqc0.js} +66 -52
  120. package/dist/hook-spb00w76.js +34 -0
  121. package/dist/{http-9xh6ta4r.js → http-nh14kkms.js} +663 -181
  122. package/dist/index-1qthq7vc.js +6 -0
  123. package/dist/{index-0g68f0p6.js → index-20xqmn3v.js} +3 -3
  124. package/dist/{index-vdkr5z2w.js → index-3e03k6e4.js} +114 -114
  125. package/dist/{index-ywds4812.js → index-4cbyhq2g.js} +43 -41
  126. package/dist/index-67pzqfrb.js +36 -0
  127. package/dist/{index-bccrkdkn.js → index-7m8yxkw5.js} +16 -16
  128. package/dist/{index-eaxyqcmj.js → index-dvsk64sm.js} +163 -83
  129. package/dist/{index-dqkpnmqm.js → index-e19s2m8k.js} +18 -18
  130. package/dist/{index-j30k5zxn.js → index-emvxcy98.js} +16 -16
  131. package/dist/{index-7ndw6ecy.js → index-gqnmr60d.js} +9 -9
  132. package/dist/{index-9znhar3x.js → index-j02xvjtd.js} +184 -185
  133. package/dist/{index-1s2442yw.js → index-ns5kksc1.js} +7 -7
  134. package/dist/{index-dpy2hth8.js → index-pdxc8kfh.js} +71 -71
  135. package/dist/{index-z1m7pw0v.js → index-ptcqm8bc.js} +107 -107
  136. package/dist/{index-070v4eyb.js → index-pyp7rexm.js} +5 -6
  137. package/dist/{index-86znjz1p.js → index-shne41sd.js} +13 -13
  138. package/dist/{index-2qn2qndv.js → index-t36q2v4t.js} +183 -183
  139. package/dist/index-tcnvv8w6.js +7 -0
  140. package/dist/index-w91qbfv1.js +7 -0
  141. package/dist/{index-fzc0mwvj.js → index-yqweff5n.js} +32 -32
  142. package/dist/index-yrfphye1.js +39 -0
  143. package/dist/{keepalive-vc7s2aa6.js → keepalive-9k51nqhc.js} +21 -15
  144. package/dist/lead-2xq3dxbz.js +88 -0
  145. package/dist/maintenance-0c0g6nym.js +28 -0
  146. package/dist/mistral-conversations-2njwdwhj.js +717 -0
  147. package/dist/{multipart-parser-qaktc7p8.js → multipart-parser-6gftesw0.js} +2 -2
  148. package/dist/{oauth-refresh-sweep-yecgjpk4.js → oauth-refresh-sweep-273a64r2.js} +14 -12
  149. package/dist/{onboard-xj0f73f9.js → onboard-tsv9wwhz.js} +12 -12
  150. package/dist/{openai-codex-responses-5g27pd6f.js → openai-codex-responses-kt84eq86.js} +26 -26
  151. package/dist/{openai-completions-3vkmfz2a.js → openai-completions-mrypq7qp.js} +17 -16
  152. package/dist/{openai-responses-dg4t7j7a.js → openai-responses-qhrk21ez.js} +20 -17
  153. package/dist/{opencode-adapter-1gm38aba.js → opencode-adapter-rar9676j.js} +13 -11
  154. package/dist/{openrouter-images-g3gm8931.js → openrouter-images-684fysx9.js} +5 -5
  155. package/dist/{otel-impl-nxg0n0b3.js → otel-impl-wp1mfzd2.js} +752 -537
  156. package/dist/{photon_rs-ky7dj5qw.js → photon_rs-8dwt2pzy.js} +581 -581
  157. package/dist/{pi-messages-tqj8q6b9.js → pi-messages-txp9f97r.js} +7 -7
  158. package/dist/{pi-mono-adapter-4g5kby31.js → pi-mono-adapter-yz47kk8e.js} +1910 -1180
  159. package/dist/pricing-refresh-jw1yw2fn.js +22 -0
  160. package/dist/rbac-roles-dg964c8p.js +32 -0
  161. package/dist/{rbac-roles-1hcxdtg7.js → rbac-roles-p53cg5cv.js} +19 -17
  162. package/dist/{registry-02cy9z5z.js → registry-mh12y17v.js} +4 -4
  163. package/dist/render-v2-56vn2rk9.js +36 -0
  164. package/dist/seed-pricing-52xx6z63.js +19 -0
  165. package/dist/{setup-46xtgq3c.js → setup-e8gp79sy.js} +7 -7
  166. package/dist/token-util-zy908s1a.js +6 -0
  167. package/dist/{token-fxkn2zf2.js → token-xg5711x5.js} +4 -4
  168. package/dist/worker-s17f1g46.js +88 -0
  169. package/dist/{x-6mg4kfpe.js → x-xkfh0w0v.js} +5 -5
  170. package/openapi.json +566 -22
  171. package/package.json +6 -6
  172. package/src/be/budget-refusal-notify.ts +14 -7
  173. package/src/be/db-queries/task-artifacts.ts +182 -0
  174. package/src/be/db.ts +834 -171
  175. package/src/be/events.ts +5 -0
  176. package/src/be/migrations/131_session_logs_created_at_index.sql +2 -0
  177. package/src/be/migrations/132_multi_runtime_instances.sql +44 -0
  178. package/src/be/migrations/133_workflow_run_trigger_type.sql +12 -0
  179. package/src/be/migrations/135_backfill_task_pull_request_attachments.sql +228 -0
  180. package/src/be/migrations/136_task_requester_provenance.sql +37 -0
  181. package/src/be/migrations/137_memory_retrieval_composite_index.sql +1 -0
  182. package/src/be/migrations/runner.ts +17 -11
  183. package/src/be/multi-runtime.ts +430 -0
  184. package/src/be/scripts/extract-schema.ts +3 -1
  185. package/src/be/scripts/typecheck.ts +6 -2
  186. package/src/be/seed-prompt-templates.ts +25 -2
  187. package/src/be/seed-skills/index.ts +7 -0
  188. package/src/be/steering.ts +1 -1
  189. package/src/be/swarm-config-guard.ts +18 -0
  190. package/src/commands/credential-wait.ts +54 -0
  191. package/src/commands/onboard/steps/core-credentials.tsx +1 -1
  192. package/src/commands/onboard/steps/custom-templates.tsx +1 -1
  193. package/src/commands/onboard/steps/harness-credentials.tsx +1 -1
  194. package/src/commands/onboard/steps/health-check.tsx +1 -1
  195. package/src/commands/onboard/steps/start.tsx +1 -1
  196. package/src/commands/profile-sync-audit.ts +56 -0
  197. package/src/commands/profile-sync.ts +287 -15
  198. package/src/commands/provider-credentials.ts +46 -19
  199. package/src/commands/runner.ts +199 -33
  200. package/src/commands/templates.ts +22 -0
  201. package/src/github/handlers.ts +55 -9
  202. package/src/heartbeat/heartbeat.ts +109 -17
  203. package/src/hooks/hook.ts +49 -33
  204. package/src/http/active-sessions.ts +11 -1
  205. package/src/http/agents.ts +261 -30
  206. package/src/http/all-routes.ts +1 -0
  207. package/src/http/config.ts +13 -2
  208. package/src/http/core.ts +102 -4
  209. package/src/http/db-query-bounded.ts +313 -0
  210. package/src/http/db-query-shared.ts +249 -0
  211. package/src/http/db-query.ts +54 -42
  212. package/src/http/events.ts +2 -0
  213. package/src/http/index.ts +23 -2
  214. package/src/http/mcp-bridge.ts +37 -3
  215. package/src/http/mcp.ts +12 -0
  216. package/src/http/memory.ts +13 -2
  217. package/src/http/openapi.ts +1 -0
  218. package/src/http/poll.ts +70 -27
  219. package/src/http/prompt-templates.ts +36 -13
  220. package/src/http/route-def.ts +43 -9
  221. package/src/http/schedules.ts +2 -2
  222. package/src/http/scripts.ts +10 -1
  223. package/src/http/session-data.ts +44 -0
  224. package/src/http/tasks.ts +2 -2
  225. package/src/http/users.ts +74 -52
  226. package/src/http/webhooks.ts +5 -2
  227. package/src/http/workflows.ts +1 -4
  228. package/src/integrations/kapso/inbound.ts +1 -1
  229. package/src/jira/outbound.ts +39 -8
  230. package/src/jira/webhook-lifecycle.ts +13 -2
  231. package/src/linear/outbound.ts +43 -10
  232. package/src/oauth/keepalive.ts +13 -2
  233. package/src/prompts/base-prompt.ts +1 -1
  234. package/src/prompts/default-drift.ts +21 -0
  235. package/src/prompts/session-templates.ts +56 -7
  236. package/src/providers/claude-adapter.ts +6 -0
  237. package/src/providers/codex-adapter.ts +6 -0
  238. package/src/providers/opencode-adapter.ts +6 -0
  239. package/src/providers/pi-mono-adapter.ts +8 -0
  240. package/src/providers/pricing-sources.md +24 -0
  241. package/src/queue-stall-alarm.ts +216 -0
  242. package/src/rbac/legacy-policy.ts +3 -0
  243. package/src/rbac/permissions.ts +12 -0
  244. package/src/scheduler/scheduler.ts +7 -0
  245. package/src/script-workflows/executor.ts +2 -1
  246. package/src/scripts-runtime/executors/native.ts +2 -1
  247. package/src/scripts-runtime/executors/types.ts +5 -0
  248. package/src/scripts-runtime/loader.ts +5 -0
  249. package/src/scripts-runtime/sdk-allowlist.ts +3 -0
  250. package/src/scripts-runtime/swarm-config.ts +8 -0
  251. package/src/scripts-runtime/swarm-sdk.ts +7 -0
  252. package/src/scripts-runtime/types/stdlib.d.ts +4 -0
  253. package/src/scripts-runtime/types/swarm-sdk.d.ts +4 -0
  254. package/src/server.ts +6 -0
  255. package/src/slack/channel-join.ts +22 -3
  256. package/src/slack/channel-lifecycle.ts +114 -0
  257. package/src/tests/budget-claim-gate.test.ts +20 -0
  258. package/src/tests/claude-adapter.test.ts +32 -0
  259. package/src/tests/codex-adapter.test.ts +30 -0
  260. package/src/tests/create-task-validation.test.ts +128 -0
  261. package/src/tests/credential-wait.test.ts +67 -1
  262. package/src/tests/db-query.test.ts +711 -2
  263. package/src/tests/heartbeat-checklist.test.ts +28 -0
  264. package/src/tests/http-api-integration.test.ts +81 -0
  265. package/src/tests/http-users.test.ts +166 -1
  266. package/src/tests/identity-field-budget.test.ts +176 -0
  267. package/src/tests/mcp-tools-user.test.ts +34 -0
  268. package/src/tests/mcp-user-route.test.ts +17 -0
  269. package/src/tests/metrics-http.test.ts +44 -0
  270. package/src/tests/migration-failure.test.ts +86 -0
  271. package/src/tests/multi-runtime-registration.test.ts +2810 -0
  272. package/src/tests/openapi-response-contract.test.ts +24 -0
  273. package/src/tests/openapi-route-headers.test.ts +83 -0
  274. package/src/tests/opencode-adapter.test.ts +40 -0
  275. package/src/tests/outbound-listener-rejection.test.ts +138 -0
  276. package/src/tests/pi-mono-adapter.test.ts +98 -0
  277. package/src/tests/preload.ts +2 -0
  278. package/src/tests/profile-sync-warning.test.ts +277 -0
  279. package/src/tests/profile-sync.test.ts +232 -6
  280. package/src/tests/prompt-template-resolver.test.ts +177 -6
  281. package/src/tests/prompt-template-session.test.ts +39 -3
  282. package/src/tests/queue-stall-alarm.test.ts +226 -0
  283. package/src/tests/rbac-charact-slack.test.ts +29 -0
  284. package/src/tests/rbac-engine.test.ts +3 -0
  285. package/src/tests/requester-comms.test.ts +32 -0
  286. package/src/tests/runner-requester-profile.test.ts +38 -0
  287. package/src/tests/schedule-target-type.test.ts +38 -0
  288. package/src/tests/script-connections.test.ts +13 -1
  289. package/src/tests/scripts-runtime-identity.test.ts +375 -0
  290. package/src/tests/scripts-runtime.test.ts +4 -3
  291. package/src/tests/sdk-allowlist.test.ts +47 -0
  292. package/src/tests/send-task-requested-by.test.ts +9 -0
  293. package/src/tests/session-costs.test.ts +506 -1
  294. package/src/tests/shutdown-error-scrubbing.test.ts +92 -0
  295. package/src/tests/slack-channel-lifecycle.test.ts +241 -0
  296. package/src/tests/slack-render-v2.test.ts +43 -0
  297. package/src/tests/steering-core.test.ts +28 -0
  298. package/src/tests/swarm-config-reserved-keys.test.ts +22 -0
  299. package/src/tests/task-artifacts.test.ts +386 -0
  300. package/src/tests/task-attachments-schema.test.ts +17 -1
  301. package/src/tests/task-completion-idempotency.test.ts +57 -0
  302. package/src/tests/task-pull-request-backfill-migration.test.ts +321 -0
  303. package/src/tests/tool-annotations.test.ts +4 -0
  304. package/src/tests/update-profile-api.test.ts +67 -39
  305. package/src/tests/update-profile-auth.test.ts +48 -4
  306. package/src/tests/user-attribution-migration.test.ts +54 -0
  307. package/src/tests/user-identity.test.ts +72 -0
  308. package/src/tests/workflow-async-v2.test.ts +39 -1
  309. package/src/tests/workflow-http-v2.test.ts +3 -2
  310. package/src/tests/workflow-mcp-trigger-schema.test.ts +52 -3
  311. package/src/tests/workflow-retry-v2.test.ts +45 -1
  312. package/src/tools/db-query.ts +18 -8
  313. package/src/tools/get-task-details.ts +24 -3
  314. package/src/tools/manage-user.ts +36 -3
  315. package/src/tools/poll-task.ts +93 -14
  316. package/src/tools/script-common.ts +5 -0
  317. package/src/tools/send-task.ts +1 -4
  318. package/src/tools/slack-archive-channel.ts +70 -0
  319. package/src/tools/slack-create-channel.ts +74 -0
  320. package/src/tools/slack-invite-to-channel.ts +78 -0
  321. package/src/tools/store-progress.ts +13 -2
  322. package/src/tools/swarm-config/delete-config.ts +9 -0
  323. package/src/tools/swarm-config/set-config.ts +3 -2
  324. package/src/tools/task-action.ts +30 -0
  325. package/src/tools/task-tool-ctx.ts +8 -1
  326. package/src/tools/tool-config.ts +4 -1
  327. package/src/tools/update-profile.ts +16 -21
  328. package/src/tools/utils.ts +5 -0
  329. package/src/tools/workflows/trigger-workflow.ts +7 -1
  330. package/src/types.ts +165 -1
  331. package/src/utils/github-pull-request.ts +76 -0
  332. package/src/utils/identity-field-budget.ts +73 -0
  333. package/src/utils/multi-runtime.ts +20 -0
  334. package/src/utils/requester-comms.ts +24 -0
  335. package/src/workflows/engine.ts +22 -2
  336. package/src/workflows/resume.ts +19 -11
  337. package/src/workflows/retry-poller.ts +1 -0
  338. package/src/workflows/triggers.ts +4 -0
  339. package/templates/skills/db-query-guidance/config.json +14 -0
  340. package/templates/skills/db-query-guidance/content.md +57 -0
  341. package/dist/app-5da78bd4.js +0 -26
  342. package/dist/cli-3te4jdkm.js +0 -225
  343. package/dist/cli-bmze986d.js +0 -7
  344. package/dist/cli-kh3ba2zf.js +0 -607
  345. package/dist/hook-cgk4wxmx.js +0 -31
  346. package/dist/index-25wjsdv2.js +0 -34
  347. package/dist/index-76at2ymp.js +0 -7
  348. package/dist/index-hase6zbe.js +0 -6
  349. package/dist/index-vg4snay4.js +0 -36
  350. package/dist/index-vy8edt27.js +0 -7
  351. package/dist/lead-r2yqfmx7.js +0 -85
  352. package/dist/maintenance-mw70k468.js +0 -26
  353. package/dist/mistral-conversations-81fwav0j.js +0 -30578
  354. package/dist/otel-nqa12wf3.js +0 -3149
  355. package/dist/pricing-refresh-2cn6effr.js +0 -20
  356. package/dist/rbac-roles-vynday5g.js +0 -30
  357. package/dist/render-v2-tc0bh8kk.js +0 -34
  358. package/dist/seed-pricing-8kceb43f.js +0 -17
  359. package/dist/token-util-pqyc8kng.js +0 -6
  360. package/dist/worker-qrbz6hmv.js +0 -85
@@ -3,7 +3,7 @@ import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
5
  import * as z from "zod";
6
- import { getAgentById, updateAgentName, updateAgentProfile } from "@/be/db";
6
+ import { getAgentById, updateAgentProfile } from "@/be/db";
7
7
  import { can } from "@/rbac";
8
8
  import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
9
9
  import { type Agent, AgentAvatarSchema, AgentStatusSchema, ProviderNameSchema } from "@/types";
@@ -110,7 +110,7 @@ export const registerUpdateProfileTool = (server: McpServer) => {
110
110
  .max(MAX_PROFILE_FILE_LENGTH)
111
111
  .optional()
112
112
  .describe(
113
- "Personal CLAUDE.md content. Loaded on session start and synced back on session end. Use for persistent notes and instructions.",
113
+ "Personal CLAUDE.md content. Loaded on session start and synced back on session end. Above 20,000 characters, updates may only keep or reduce the stored size; move durable overflow into memory.",
114
114
  ),
115
115
  soulMd: z
116
116
  .string()
@@ -118,7 +118,7 @@ export const registerUpdateProfileTool = (server: McpServer) => {
118
118
  .max(MAX_PROFILE_FILE_LENGTH)
119
119
  .optional()
120
120
  .describe(
121
- "Soul content: persona and behavioral directives. Updates both DB and /workspace/SOUL.md. Must be at least 200 characters to prevent accidental corruption.",
121
+ "Soul content: persona and behavioral directives. Updates both DB and /workspace/SOUL.md. Must be at least 200 characters; above 10,000 characters, updates may only keep or reduce the stored size.",
122
122
  ),
123
123
  identityMd: z
124
124
  .string()
@@ -126,7 +126,7 @@ export const registerUpdateProfileTool = (server: McpServer) => {
126
126
  .max(MAX_PROFILE_FILE_LENGTH)
127
127
  .optional()
128
128
  .describe(
129
- "Identity content: expertise and working style. Updates both DB and /workspace/IDENTITY.md. Must be at least 200 characters to prevent accidental corruption.",
129
+ "Identity content: expertise and working style. Updates both DB and /workspace/IDENTITY.md. Must be at least 200 characters; above 10,000 characters, updates may only keep or reduce the stored size.",
130
130
  ),
131
131
  setupScript: z
132
132
  .string()
@@ -140,7 +140,7 @@ export const registerUpdateProfileTool = (server: McpServer) => {
140
140
  .max(MAX_PROFILE_FILE_LENGTH)
141
141
  .optional()
142
142
  .describe(
143
- "Environment-specific operational knowledge. Repos, services, SSH hosts, APIs, device names — anything specific to your setup. Synced to /workspace/TOOLS.md.",
143
+ "Environment-specific operational knowledge. Synced to /workspace/TOOLS.md. Above 20,000 characters, updates may only keep or reduce the stored size; move durable overflow into memory.",
144
144
  ),
145
145
  heartbeatMd: z
146
146
  .string()
@@ -250,21 +250,14 @@ export const registerUpdateProfileTool = (server: McpServer) => {
250
250
  }
251
251
  }
252
252
 
253
- // Update name if provided
254
- if (name !== undefined) {
255
- agent = updateAgentName(targetAgentId, name);
256
- if (!agent) {
257
- return toolErr("Target agent not found.", {
258
- data: { yourAgentId: requestInfo.agentId },
259
- });
260
- }
261
- }
262
-
263
- // Update profile fields if provided. `avatar` is spread in only when
264
- // present so `null` (reset) stays distinguishable from "not provided".
253
+ // Apply name and profile fields in one DB transaction so validation or
254
+ // update failures cannot leave a partially-applied combined payload.
255
+ // `avatar` is spread in only when present so `null` (reset) stays
256
+ // distinguishable from "not provided".
265
257
  agent = updateAgentProfile(
266
258
  targetAgentId,
267
259
  {
260
+ name,
268
261
  description,
269
262
  role,
270
263
  capabilities,
@@ -282,6 +275,12 @@ export const registerUpdateProfileTool = (server: McpServer) => {
282
275
  },
283
276
  );
284
277
 
278
+ // Do not perform audit or workspace-file side effects when the target
279
+ // disappeared before the transactional update could find it.
280
+ if (!agent) {
281
+ return toolErr("Agent not found.", { data: { yourAgentId: requestInfo.agentId } });
282
+ }
283
+
285
284
  if (setupScript !== undefined && previousSetupScript !== undefined) {
286
285
  try {
287
286
  const diff = await computeSetupScriptDiff(previousSetupScript, setupScript);
@@ -348,10 +347,6 @@ export const registerUpdateProfileTool = (server: McpServer) => {
348
347
  }
349
348
  }
350
349
 
351
- if (!agent) {
352
- return toolErr("Agent not found.", { data: { yourAgentId: requestInfo.agentId } });
353
- }
354
-
355
350
  const updatedFields: string[] = [];
356
351
  if (name !== undefined) updatedFields.push("name");
357
352
  if (description !== undefined) updatedFields.push("description");
@@ -32,6 +32,8 @@ export function markScriptSdkRequestOrigin<T extends object>(meta: T): T {
32
32
  export type RequestInfo = {
33
33
  sessionId: string | undefined;
34
34
  agentId: string | undefined;
35
+ /** Calling worker process, when multi-runtime mode is on. */
36
+ runtimeInstanceId: string | undefined;
35
37
  sourceTaskId: string | undefined;
36
38
  contextKey: string | undefined;
37
39
  callOrigin: "mcp" | "script-sdk";
@@ -41,6 +43,8 @@ export const getRequestInfo = (req: Meta): RequestInfo => {
41
43
  const agentIdHeader = req.requestInfo?.headers?.["x-agent-id"];
42
44
  const sourceTaskIdHeader = req.requestInfo?.headers?.["x-source-task-id"];
43
45
  const contextKeyHeader = req.requestInfo?.headers?.["x-context-key"];
46
+ const runtimeHeader = req.requestInfo?.headers?.["x-runtime-instance-id"];
47
+ const runtimeInstanceId = Array.isArray(runtimeHeader) ? runtimeHeader[0] : runtimeHeader;
44
48
 
45
49
  let agentId: string | undefined;
46
50
  if (Array.isArray(agentIdHeader)) {
@@ -66,6 +70,7 @@ export const getRequestInfo = (req: Meta): RequestInfo => {
66
70
  return {
67
71
  sessionId: req.sessionId || undefined,
68
72
  agentId,
73
+ runtimeInstanceId: typeof runtimeInstanceId === "string" ? runtimeInstanceId : undefined,
69
74
  sourceTaskId,
70
75
  contextKey,
71
76
  callOrigin: scriptSdkRequestOrigins.has(req) ? "script-sdk" : "mcp",
@@ -1,5 +1,6 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { z } from "zod";
3
+ import { resolveTaskAuditUserId } from "@/be/audit-user";
3
4
  import { getWorkflow, getWorkflowRun } from "@/be/db";
4
5
  import { createToolRegistrar, swarmToolOutputSchema, toolErr, toolOk } from "@/tools/utils";
5
6
  import { getExecutorRegistry, startWorkflowExecution } from "@/workflows";
@@ -28,7 +29,7 @@ export const registerTriggerWorkflowTool = (server: McpServer) => {
28
29
  triggerSchema: z.record(z.string(), z.unknown()).optional(),
29
30
  }),
30
31
  },
31
- async ({ id, triggerData }) => {
32
+ async ({ id, triggerData }, requestInfo) => {
32
33
  try {
33
34
  const workflow = getWorkflow(id);
34
35
  if (!workflow) {
@@ -41,6 +42,11 @@ export const registerTriggerWorkflowTool = (server: McpServer) => {
41
42
  workflow,
42
43
  triggerData ?? {},
43
44
  getExecutorRegistry(),
45
+ {
46
+ triggerType: "manual",
47
+ requestedByUserId:
48
+ resolveTaskAuditUserId(requestInfo.sourceTaskId, requestInfo.agentId) ?? undefined,
49
+ },
44
50
  );
45
51
 
46
52
  // Check if the run was skipped due to cooldown
package/src/types.ts CHANGED
@@ -633,6 +633,102 @@ export const AgentTaskSchema = z
633
633
  })
634
634
  .openapi("AgentTask");
635
635
 
636
+ // ─── Create-task input contract ──────────────────────────────────────────────
637
+ //
638
+ // Runtime schema for `createTaskExtended` options (src/be/db.ts). Every task
639
+ // write — REST POST /api/tasks, MCP send-task, the scripts bridge, webhooks,
640
+ // the scheduler, and internal callers — funnels through that function, so
641
+ // this schema is the single enforcement point for task-input shapes. It
642
+ // rejects, never coerces: a type mismatch or out-of-range value throws, and
643
+ // only an ABSENT field falls back to the `??` defaults at the INSERT bind
644
+ // site. Deliberately NOT an `.openapi()` component: it is an internal write
645
+ // contract, not a response shape.
646
+ export const CreateTaskOptionsSchema = z.object({
647
+ key: z.string().min(1).optional(),
648
+ agentId: z.string().nullable().optional(),
649
+ creatorAgentId: z.string().optional(),
650
+ source: AgentTaskSourceSchema.optional(),
651
+ taskType: z.string().max(50).optional(),
652
+ tags: z.array(z.string()).optional(),
653
+ priority: z.number().int().min(0).max(100).optional(),
654
+ dependsOn: z.array(z.string()).optional(),
655
+ offeredTo: z.string().optional(),
656
+ /** Explicitly set initial status. */
657
+ status: z.enum(["backlog", "unassigned"]).optional(),
658
+ slackChannelId: z.string().optional(),
659
+ slackThreadTs: z.string().optional(),
660
+ /** Exact Slack message that directly triggered this task; never inherited. */
661
+ slackTriggerMessageTs: z.string().optional(),
662
+ slackUserId: z.string().optional(),
663
+ /**
664
+ * Opt out of the residual Slack/contextKey normalization (see the
665
+ * "Residual-mismatch guard" comment near the INSERT in src/be/db.ts): a
666
+ * deliberate cross-channel/thread dispatch (e.g. `send-task`'s
667
+ * `overrideSlackContext: true`) sets this so its explicit slackChannelId/
668
+ * slackThreadTs survive even when they disagree with a slack-family
669
+ * `contextKey`/parent. Trusted callers that don't set this get normalized —
670
+ * this boundary must not let a caller silently persist a mismatch.
671
+ */
672
+ overrideSlackContext: z.boolean().optional(),
673
+ vcsProvider: z.enum(["github", "gitlab"]).optional(),
674
+ vcsRepo: z.string().optional(),
675
+ vcsEventType: z.string().optional(),
676
+ vcsNumber: z.number().int().optional(),
677
+ vcsCommentId: z.number().int().optional(),
678
+ vcsAuthor: z.string().optional(),
679
+ vcsUrl: z.string().optional(),
680
+ vcsInstallationId: z.number().int().optional(),
681
+ vcsNodeId: z.string().optional(),
682
+ agentmailInboxId: z.string().optional(),
683
+ agentmailMessageId: z.string().optional(),
684
+ agentmailThreadId: z.string().optional(),
685
+ mentionMessageId: z.string().optional(),
686
+ mentionChannelId: z.string().optional(),
687
+ dir: z.string().optional(),
688
+ parentTaskId: z.string().optional(),
689
+ model: z.string().optional(),
690
+ modelTier: ModelTierSchema.optional(),
691
+ effort: ReasoningEffortSchema.optional(),
692
+ scheduleId: z.string().optional(),
693
+ workflowRunId: z.string().optional(),
694
+ workflowRunStepId: z.string().optional(),
695
+ sourceTaskId: z.string().optional(),
696
+ /**
697
+ * Optional JSON Schema the agent's final output must conform to.
698
+ *
699
+ * Enforced via the MCP `store-progress` tool (validated in
700
+ * `src/tools/store-progress.ts`). NOT enforced when the task runs on
701
+ * default-mode Devin (no MCP) — see runbooks/harness-providers.md
702
+ * ("Per-task outputSchema support"). Callers reading `task.output` for
703
+ * a schema'd task should be defensive about JSON parsing.
704
+ */
705
+ outputSchema: z.record(z.string(), z.unknown()).optional(),
706
+ /**
707
+ * When a `parentTaskId` is set, the child inherits the parent's `outputSchema`
708
+ * by default. Set this to `false` to opt out — used by control-plane children
709
+ * (e.g. the Lead `reroute-decision` task) that must inherit Slack/VCS context
710
+ * from the parent but must NOT be forced to satisfy the original work's output
711
+ * contract on completion (which would block the control task — DES-523).
712
+ */
713
+ inheritParentOutputSchema: z.boolean().optional(),
714
+ followUpConfig: FollowUpConfigSchema.optional(),
715
+ requestedByUserId: z.string().optional(),
716
+ contextKey: z.string().optional(),
717
+ /**
718
+ * Internal control-plane escape hatch for continuations that MUST coexist
719
+ * with their active Linear parent (for example promoted steering). General
720
+ * task creation keeps tracker-context dedup enabled.
721
+ */
722
+ bypassTrackerContextDedup: z.boolean().optional(),
723
+ /**
724
+ * Routing-affinity snapshot gating pool eligibility (see
725
+ * `isAgentEligibleForTask`). Inherited from the parent (via `parentTaskId`)
726
+ * when not explicitly set — same treatment as `vcsRepo`/`contextKey`.
727
+ */
728
+ routingAffinity: RoutingAffinitySchema.optional(),
729
+ });
730
+ export type CreateTaskOptions = z.infer<typeof CreateTaskOptionsSchema>;
731
+
636
732
  // ============================================================================
637
733
  // Task Attachments (Phase 1 — pointer-based artifacts)
638
734
  // ============================================================================
@@ -645,11 +741,19 @@ export const AgentTaskSchema = z
645
741
  export const TaskAttachmentKindSchema = z.enum(["agent-fs", "url", "shared-fs", "page"]);
646
742
  export type TaskAttachmentKind = z.infer<typeof TaskAttachmentKindSchema>;
647
743
 
744
+ export const SERVER_GENERATED_ATTACHMENT_CAPABILITY = "_agentSwarmGeneratedBy";
745
+
746
+ const attachmentCapabilitiesSchema = z
747
+ .record(z.string(), z.unknown())
748
+ .refine((capabilities) => !(SERVER_GENERATED_ATTACHMENT_CAPABILITY in capabilities), {
749
+ message: `${SERVER_GENERATED_ATTACHMENT_CAPABILITY} is reserved for server-generated attachments`,
750
+ });
751
+
648
752
  const attachmentCommonFields = {
649
753
  name: z.string().min(1).describe("Display name for the attachment."),
650
754
  providerId: z.string().min(1).optional(),
651
755
  providerKey: z.string().min(1).optional(),
652
- capabilities: z.record(z.string(), z.unknown()).optional(),
756
+ capabilities: attachmentCapabilitiesSchema.optional(),
653
757
  mimeType: z.string().optional(),
654
758
  sizeBytes: z.number().int().min(0).optional(),
655
759
  sha256: z.string().optional(),
@@ -755,6 +859,22 @@ export const UserSchema = z
755
859
 
756
860
  export type User = z.infer<typeof UserSchema>;
757
861
 
862
+ /**
863
+ * Structured communication preferences read from `users.metadata.comms`.
864
+ * All fields are free-form strings; values render verbatim into the requester
865
+ * profile prompt. Written merge-safely via the `comms` field on
866
+ * PATCH /api/users/{id} and the manage-user tool.
867
+ */
868
+ export const UserCommsPrefsSchema = z
869
+ .object({
870
+ tone: z.string().optional(),
871
+ language: z.string().optional(),
872
+ verbosity: z.string().optional(),
873
+ })
874
+ .openapi("UserCommsPrefs");
875
+
876
+ export type UserCommsPrefs = z.infer<typeof UserCommsPrefsSchema>;
877
+
758
878
  /**
759
879
  * Identity event types — mirrored in lockstep with the CHECK constraint on
760
880
  * `user_identity_events.eventType` in migration 064. Drift breaks helper
@@ -1338,6 +1458,8 @@ export const EventNameSchema = z.enum([
1338
1458
  "system.boot",
1339
1459
  "system.migration",
1340
1460
  "system.error",
1461
+ "system.profile_sync_rejected",
1462
+ "system.profile_sync_reconciled",
1341
1463
  // Script catalog events
1342
1464
  "script.global_upsert",
1343
1465
  // Schedule events
@@ -1563,6 +1685,8 @@ export const ActiveSessionSchema = z
1563
1685
  taskDescription: z.string().nullable(),
1564
1686
  runnerSessionId: z.string().nullable(),
1565
1687
  providerSessionId: z.string().nullable(),
1688
+ /** Runtime process that owns this session; null for single-runtime rows. */
1689
+ runtimeInstanceId: z.string().nullable().optional(),
1566
1690
  startedAt: z.iso.datetime(),
1567
1691
  lastHeartbeatAt: z.iso.datetime(),
1568
1692
  })
@@ -1570,6 +1694,45 @@ export const ActiveSessionSchema = z
1570
1694
 
1571
1695
  export type ActiveSession = z.infer<typeof ActiveSessionSchema>;
1572
1696
 
1697
+ // ============================================================================
1698
+ // Runtime Instance Types (multi-runtime worker tracking)
1699
+ // ============================================================================
1700
+
1701
+ /**
1702
+ * Lifecycle of a runtime instance. Zod is the source of truth for the allowed
1703
+ * values — the SQL column has no CHECK constraint (migration 132) so adding a
1704
+ * state doesn't require a table rebuild.
1705
+ */
1706
+ export const RuntimeInstanceStatusSchema = z.enum(["active", "offline"]);
1707
+
1708
+ /**
1709
+ * One worker process serving a logical agent. The `agents` row (AGENT_ID /
1710
+ * X-Agent-ID) keeps durable identity and the logical maxTasks policy; a
1711
+ * runtime instance carries only process-scoped state: liveness and its own
1712
+ * reported execution capacity. A logical agent may be served by 0..N runtime
1713
+ * instances; rows exist only for multi-runtime registrations
1714
+ * (MULTI_RUNTIME_ENABLED).
1715
+ */
1716
+ export const RuntimeInstanceSchema = z
1717
+ .object({
1718
+ id: z.string(),
1719
+ agentId: z.string(),
1720
+ status: RuntimeInstanceStatusSchema,
1721
+ /** Runtime-local concurrent task capacity, self-reported at registration. */
1722
+ reportedSlots: z.number().int(),
1723
+ /** Process-local credential readiness; null when never reported. */
1724
+ credentialReady: z.boolean().nullable().optional(),
1725
+ /** Provider-neutral metadata; unused in the initial slice. */
1726
+ metadata: z.record(z.string(), z.unknown()).nullable(),
1727
+ lastSeenAt: z.iso.datetime(),
1728
+ createdAt: z.iso.datetime(),
1729
+ updatedAt: z.iso.datetime(),
1730
+ })
1731
+ .openapi("RuntimeInstance");
1732
+
1733
+ export type RuntimeInstanceStatus = z.infer<typeof RuntimeInstanceStatusSchema>;
1734
+ export type RuntimeInstance = z.infer<typeof RuntimeInstanceSchema>;
1735
+
1573
1736
  // ============================================================================
1574
1737
  // Workflow Engine Types
1575
1738
  // ============================================================================
@@ -2238,6 +2401,7 @@ export const WorkflowRunSchema = z
2238
2401
  triggerData: z.unknown().optional(),
2239
2402
  context: z.record(z.string(), z.unknown()).optional(),
2240
2403
  error: z.string().optional(),
2404
+ createdBy: z.string().optional(),
2241
2405
  startedAt: z.string(),
2242
2406
  lastUpdatedAt: z.string(),
2243
2407
  finishedAt: z.string().optional(),
@@ -0,0 +1,76 @@
1
+ export interface GitHubPullRequestUrl {
2
+ url: string;
3
+ owner: string;
4
+ repo: string;
5
+ number: number;
6
+ }
7
+
8
+ const CANDIDATE_TOKEN_RE = /[^\s()[\]{}<>"'`]+/g;
9
+ const TRAILING_PROSE_PUNCTUATION_RE = /[)\]},.;:!]+$/;
10
+ const GITHUB_PATH_SEGMENT_RE = /^[\w.-]+$/;
11
+
12
+ function parseGitHubPullRequestToken(token: string): GitHubPullRequestUrl | null {
13
+ const candidate = token.replace(TRAILING_PROSE_PUNCTUATION_RE, "");
14
+ const lowerCandidate = candidate.toLowerCase();
15
+ if (
16
+ !lowerCandidate.startsWith("https://github.com/") &&
17
+ !lowerCandidate.startsWith("http://github.com/") &&
18
+ !lowerCandidate.startsWith("github.com/")
19
+ ) {
20
+ return null;
21
+ }
22
+ const withScheme = /^https?:\/\//i.test(candidate) ? candidate : `https://${candidate}`;
23
+
24
+ let parsed: URL;
25
+ try {
26
+ parsed = new URL(withScheme);
27
+ } catch {
28
+ return null;
29
+ }
30
+ if (
31
+ parsed.host.toLowerCase() !== "github.com" ||
32
+ parsed.username.length > 0 ||
33
+ parsed.password.length > 0
34
+ ) {
35
+ return null;
36
+ }
37
+
38
+ const [owner, repo, pull, numberText] = parsed.pathname.split("/").filter(Boolean);
39
+ if (
40
+ !owner ||
41
+ !repo ||
42
+ pull?.toLowerCase() !== "pull" ||
43
+ !numberText ||
44
+ !GITHUB_PATH_SEGMENT_RE.test(owner) ||
45
+ !GITHUB_PATH_SEGMENT_RE.test(repo) ||
46
+ !/^\d+$/.test(numberText)
47
+ ) {
48
+ return null;
49
+ }
50
+
51
+ return {
52
+ url: `https://github.com/${owner}/${repo}/pull/${numberText}`,
53
+ owner,
54
+ repo,
55
+ number: Number(numberText),
56
+ };
57
+ }
58
+
59
+ /** Extract distinct canonical GitHub pull-request URLs from free text. */
60
+ export function extractGitHubPullRequestUrls(
61
+ text: string | null | undefined,
62
+ ): GitHubPullRequestUrl[] {
63
+ if (!text) return [];
64
+
65
+ const results: GitHubPullRequestUrl[] = [];
66
+ const seen = new Set<string>();
67
+ for (const match of text.matchAll(CANDIDATE_TOKEN_RE)) {
68
+ const pullRequest = parseGitHubPullRequestToken(match[0]);
69
+ if (!pullRequest) continue;
70
+ const dedupeKey = pullRequest.url.toLowerCase();
71
+ if (seen.has(dedupeKey)) continue;
72
+ seen.add(dedupeKey);
73
+ results.push(pullRequest);
74
+ }
75
+ return results;
76
+ }
@@ -0,0 +1,73 @@
1
+ import { BOOTSTRAP_MAX_CHARS } from "../prompts/base-prompt";
2
+
3
+ export const SOUL_MD_MAX_CHARS = 10_000;
4
+ export const IDENTITY_MD_MAX_CHARS = 10_000;
5
+
6
+ export const IDENTITY_FIELD_BUDGETS = {
7
+ soulMd: SOUL_MD_MAX_CHARS,
8
+ identityMd: IDENTITY_MD_MAX_CHARS,
9
+ claudeMd: BOOTSTRAP_MAX_CHARS,
10
+ toolsMd: BOOTSTRAP_MAX_CHARS,
11
+ } as const;
12
+
13
+ export type BudgetedIdentityField = keyof typeof IDENTITY_FIELD_BUDGETS;
14
+
15
+ export interface IdentityFieldBudgetRejection {
16
+ field: BudgetedIdentityField;
17
+ dbSize: number;
18
+ diskSize: number;
19
+ budget: number;
20
+ delta: number;
21
+ reason: string;
22
+ }
23
+
24
+ export type IdentityFieldBudgetResult =
25
+ | { ok: true }
26
+ | ({ ok: false } & IdentityFieldBudgetRejection);
27
+
28
+ export function checkIdentityFieldBudget({
29
+ field,
30
+ currentValue,
31
+ nextValue,
32
+ }: {
33
+ field: BudgetedIdentityField;
34
+ currentValue: string;
35
+ nextValue: string;
36
+ }): IdentityFieldBudgetResult {
37
+ const budget = IDENTITY_FIELD_BUDGETS[field];
38
+ if (nextValue.length <= budget || nextValue.length <= currentValue.length) {
39
+ return { ok: true };
40
+ }
41
+
42
+ const delta = nextValue.length - currentValue.length;
43
+ const remediation =
44
+ field === "toolsMd"
45
+ ? ` Content past the ${budget}-character cap is already silently dropped at read time, so shrinking that tail loses nothing sessions currently receive. Move durable content into memories and keep pointers to it in this field.`
46
+ : field === "claudeMd"
47
+ ? ` The tail past the ${budget}-character cap is dropped from the base prompt and only reaches harnesses with a native CLAUDE.md loader. Move durable content into memories and keep pointers to it in this field.`
48
+ : " Move durable content into memories and keep pointers to it in this field.";
49
+
50
+ return {
51
+ ok: false,
52
+ field,
53
+ dbSize: currentValue.length,
54
+ diskSize: nextValue.length,
55
+ budget,
56
+ delta,
57
+ reason:
58
+ `Update rejected for ${field}: current size ${currentValue.length} characters, ` +
59
+ `budget ${budget} characters, delta ${delta >= 0 ? "+" : ""}${delta} characters.` +
60
+ remediation,
61
+ };
62
+ }
63
+
64
+ export class IdentityFieldBudgetError extends Error {
65
+ constructor(
66
+ readonly rejection: IdentityFieldBudgetRejection,
67
+ readonly dbHash: string,
68
+ readonly diskHash: string,
69
+ ) {
70
+ super(rejection.reason);
71
+ this.name = "IdentityFieldBudgetError";
72
+ }
73
+ }
@@ -0,0 +1,20 @@
1
+ import { parseEnvFlag } from "./env-flag";
2
+
3
+ /**
4
+ * Opt-in, default off: allows one logical agent to be served by several
5
+ * worker processes. Lives outside `src/be` so worker-side code can import it
6
+ * without crossing the DB boundary.
7
+ */
8
+ export function isMultiRuntimeEnabled(env: NodeJS.ProcessEnv = process.env): boolean {
9
+ return parseEnvFlag(env.MULTI_RUNTIME_ENABLED, false);
10
+ }
11
+
12
+ /**
13
+ * Per-boot runtime identity the runner stamps into the process environment
14
+ * before any provider session exists. Adapters read it here to send the
15
+ * X-Runtime-Instance-ID header on the swarm MCP session — runtime identity is
16
+ * process/auth context, never a tool argument.
17
+ */
18
+ export function swarmRuntimeInstanceId(env: NodeJS.ProcessEnv = process.env): string | undefined {
19
+ return env.SWARM_RUNTIME_INSTANCE_ID || undefined;
20
+ }
@@ -0,0 +1,24 @@
1
+ import type { User, UserCommsPrefs } from "../types";
2
+
3
+ function pickString(value: unknown): string | undefined {
4
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : undefined;
5
+ }
6
+
7
+ /**
8
+ * Extract structured communication preferences from `users.metadata.comms`.
9
+ * `metadata` is a free-form JSON blob with no write-side validation, so only
10
+ * non-empty string fields survive; returns undefined when nothing usable is set.
11
+ */
12
+ export function getUserCommsPrefs(
13
+ user: Pick<User, "metadata"> | null | undefined,
14
+ ): UserCommsPrefs | undefined {
15
+ const comms = user?.metadata?.comms;
16
+ if (!comms || typeof comms !== "object" || Array.isArray(comms)) return undefined;
17
+ const record = comms as Record<string, unknown>;
18
+ const prefs: UserCommsPrefs = {
19
+ tone: pickString(record.tone),
20
+ language: pickString(record.language),
21
+ verbosity: pickString(record.verbosity),
22
+ };
23
+ return prefs.tone || prefs.language || prefs.verbosity ? prefs : undefined;
24
+ }
@@ -76,7 +76,13 @@ export async function startWorkflowExecution(
76
76
  // Cooldown check
77
77
  if (workflow.cooldown && shouldSkipCooldown(workflow.id, workflow.cooldown)) {
78
78
  const runId = crypto.randomUUID();
79
- createWorkflowRun({ id: runId, workflowId: workflow.id, triggerData });
79
+ createWorkflowRun({
80
+ id: runId,
81
+ workflowId: workflow.id,
82
+ triggerType: options.triggerType ?? "manual",
83
+ triggerData,
84
+ createdBy: options.requestedByUserId,
85
+ });
80
86
  updateWorkflowRun(runId, {
81
87
  status: "skipped",
82
88
  error: "cooldown",
@@ -86,7 +92,13 @@ export async function startWorkflowExecution(
86
92
  }
87
93
 
88
94
  const runId = crypto.randomUUID();
89
- createWorkflowRun({ id: runId, workflowId: workflow.id, triggerData });
95
+ createWorkflowRun({
96
+ id: runId,
97
+ workflowId: workflow.id,
98
+ triggerType: options.triggerType ?? "manual",
99
+ triggerData,
100
+ createdBy: options.requestedByUserId,
101
+ });
90
102
  telemetry.workflow("started", {
91
103
  workflowId: workflow.id,
92
104
  nodeCount: workflow.definition.nodes.length,
@@ -160,6 +172,14 @@ export async function walkGraph(
160
172
  secretKeys: Set<string> = new Set(),
161
173
  options: WorkflowExecutionOptions = {},
162
174
  ): Promise<void> {
175
+ // Initial triggers carry the requester in options. Resumes, retries, and
176
+ // recovery start from persisted state, so rehydrate it from the run.
177
+ if (!options.requestedByUserId) {
178
+ options = {
179
+ ...options,
180
+ requestedByUserId: getWorkflowRun(runId)?.createdBy,
181
+ };
182
+ }
163
183
  let nodeExecutionCount = 0;
164
184
  // `run.id` is a context builtin (like `trigger` / `input`) so nodes can reference
165
185
  // their own run — e.g. an audit/receipt node correlating its output with the run