@desplega.ai/agent-swarm 2.6.0 → 2.7.1

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 (324) hide show
  1. package/README.md +2 -0
  2. package/dist/{acp-adapter-dgfakfpw.js → acp-adapter-8dn5tq56.js} +6 -6
  3. package/dist/{actions-fbqma8aa.js → actions-3r6gw7dy.js} +18 -18
  4. package/dist/{amp-adapter-en0jy6hs.js → amp-adapter-wjtq413p.js} +3 -3
  5. package/dist/{anthropic-messages-txs190dd.js → anthropic-messages-n4cby1rz.js} +4 -4
  6. package/dist/{app-k8a5cgpp.js → app-d53yhykk.js} +3 -3
  7. package/dist/{assistant-a16433gr.js → assistant-ksrawj3k.js} +20 -20
  8. package/dist/{boot-reembed-6c1sjfnr.js → boot-reembed-fxfw17m2.js} +11 -3
  9. package/dist/{boot-reembed-f4nn561n.js → boot-reembed-k5y5xbj0.js} +15 -8
  10. package/dist/{boot-scrub-sweep-d4w9gajf.js → boot-scrub-sweep-se41j9fd.js} +3 -3
  11. package/dist/{boot-seeds-9bmkspyw.js → boot-seeds-r3kwwv8b.js} +4 -4
  12. package/dist/{claude-adapter-nys1kjxa.js → claude-adapter-ahmyf231.js} +8 -8
  13. package/dist/{claude-managed-adapter-55207973.js → claude-managed-adapter-5knamegc.js} +3 -3
  14. package/dist/{claude-managed-setup-3cm35gey.js → claude-managed-setup-mdc7dy6b.js} +3 -3
  15. package/dist/{claude-sdk-session-nw5yd8cp.js → claude-sdk-session-n5wen9zk.js} +8729 -8600
  16. package/dist/{cli-683y0b3a.js → cli-0dqtv2jg.js} +2 -2
  17. package/dist/{cli-a34pr3w1.js → cli-0tww7dnz.js} +2 -2
  18. package/dist/{cli-w2656ka1.js → cli-0w4e4v58.js} +7 -7
  19. package/dist/{cli-5sckpqcx.js → cli-2sjm3t4r.js} +1 -1
  20. package/dist/{cli-gf2850q7.js → cli-3cywb44q.js} +18 -1
  21. package/dist/{cli-8wkb09q5.js → cli-3k9jej0c.js} +2 -2
  22. package/dist/{cli-3aqd4jaa.js → cli-3v2cyqb8.js} +3 -3
  23. package/dist/{cli-emhjcj2y.js → cli-5qt9vzmq.js} +89 -20
  24. package/dist/{cli-abkm71vr.js → cli-5rp1w4cr.js} +1 -1
  25. package/dist/{cli-gf95q3bm.js → cli-6vwjsetx.js} +4 -4
  26. package/dist/{cli-k21gx5ht.js → cli-6w2yrrc0.js} +10 -10
  27. package/dist/{cli-m70bajzr.js → cli-75tdydgv.js} +1 -1
  28. package/dist/{cli-5rj83bam.js → cli-8feswf2v.js} +47 -36
  29. package/dist/{cli-sxgtf5fw.js → cli-8wd3mbyj.js} +1 -1
  30. package/dist/{cli-ca6banwz.js → cli-9csfthbw.js} +10 -8
  31. package/dist/{cli-dqygeeh5.js → cli-b41p764a.js} +31 -3
  32. package/dist/{cli-m5a9m0ak.js → cli-c1t533hh.js} +3 -3
  33. package/dist/{cli-2sjhpsns.js → cli-c28jxpcv.js} +1 -1
  34. package/dist/{cli-bqztfyve.js → cli-cb50g9cd.js} +1 -1
  35. package/dist/{cli-ag2yrnt5.js → cli-d8gaphtr.js} +2 -2
  36. package/dist/{cli-9abdgn5t.js → cli-dgtfysne.js} +3 -3
  37. package/dist/{cli-nq677csy.js → cli-dscnhn18.js} +1 -1
  38. package/dist/{cli-3fwqy71j.js → cli-dv11pgpv.js} +39 -32
  39. package/dist/{cli-v49fkghp.js → cli-ekptgk2m.js} +6 -6
  40. package/dist/{cli-c2habvsf.js → cli-f3mzy662.js} +17 -17
  41. package/dist/{cli-zqne1qcj.js → cli-fk7k8s9x.js} +2 -2
  42. package/dist/{cli-1tem6ns9.js → cli-gt4h8cxw.js} +3 -2
  43. package/dist/{cli-vbhgkw9h.js → cli-h3e7bhvn.js} +3 -3
  44. package/dist/{cli-ffvsj2qw.js → cli-hkxqdmnb.js} +3 -3
  45. package/dist/{cli-a2rr0fmg.js → cli-hpkvgdad.js} +7 -4
  46. package/dist/{cli-art7c1zh.js → cli-hq2v4rr5.js} +37 -9
  47. package/dist/{cli-sqma6vw1.js → cli-k1zb7raw.js} +5 -5
  48. package/dist/{cli-4rvr7zfx.js → cli-k4awgvzm.js} +5 -0
  49. package/dist/{cli-3ndmpxxv.js → cli-k7d3hvya.js} +2 -2
  50. package/dist/{cli-3gfvmzht.js → cli-kbtdftbw.js} +74 -26
  51. package/dist/{cli-hfeb68z4.js → cli-kj69b9tw.js} +34 -6
  52. package/dist/{cli-az25fn5f.js → cli-kk9jy4nm.js} +2 -2
  53. package/dist/{cli-39aet655.js → cli-m52t4cta.js} +1 -1
  54. package/dist/{cli-cz37jqss.js → cli-mdadewqp.js} +5 -5
  55. package/dist/{cli-esakngh8.js → cli-nf6k60sn.js} +1 -1
  56. package/dist/{cli-j6rktc91.js → cli-nzdf09js.js} +1 -1
  57. package/dist/{cli-rr7zx19j.js → cli-prcyzecg.js} +12 -12
  58. package/dist/{cli-4hxrf0p4.js → cli-pzafyqtk.js} +393 -62
  59. package/dist/{cli-g8ka5j4j.js → cli-qvaa1gjp.js} +173 -52
  60. package/dist/{cli-d2dpf95n.js → cli-rg4b2g6j.js} +1 -1
  61. package/dist/{cli-kf4jree9.js → cli-rm66vqdz.js} +1 -1
  62. package/dist/{cli-4rf0f5vg.js → cli-s504c3w7.js} +5 -3
  63. package/dist/{cli-nn6vx2mq.js → cli-swkx9jm2.js} +7 -5
  64. package/dist/{cli-g20jjwt4.js → cli-thsaq4pd.js} +16 -16
  65. package/dist/{cli-aj8djyqt.js → cli-twjnyvs8.js} +2 -2
  66. package/dist/{cli-03gg9pd1.js → cli-typdpe8g.js} +4 -4
  67. package/dist/{cli-mtnj4fwa.js → cli-vg8pwmsd.js} +6 -6
  68. package/dist/{cli-gnsb1zxa.js → cli-vhpckzvp.js} +1 -1
  69. package/dist/{cli-7enjwya9.js → cli-vz78s5wm.js} +1 -1
  70. package/dist/{cli-dv97kdzv.js → cli-wdxgsdz5.js} +2 -2
  71. package/dist/{cli-7a6p4e65.js → cli-wtjg6je3.js} +1 -1
  72. package/dist/{cli-6y51ryva.js → cli-x3cngb1h.js} +44 -12
  73. package/dist/{cli-n7asmce6.js → cli-xbz6mcg4.js} +3 -3
  74. package/dist/{cli-24j1jeeb.js → cli-y4tdxsqb.js} +3 -3
  75. package/dist/{cli-mvjz5an3.js → cli-yqrsp3ny.js} +7 -7
  76. package/dist/cli.js +31 -21
  77. package/dist/{codex-adapter-d95q4qsn.js → codex-adapter-7wepzq15.js} +6 -6
  78. package/dist/{codex-hook-brpnycn5.js → codex-hook-pbdh4xhx.js} +2 -2
  79. package/dist/{codex-session-runner-gdwd8h0c.js → codex-session-runner-02sb49e4.js} +6 -6
  80. package/dist/{commands-jqmgk54b.js → commands-3rcspxct.js} +2 -2
  81. package/dist/{controller-z3jq589f.js → controller-8ar4avd6.js} +3 -3
  82. package/dist/{controllers-connect-r2za39ef.js → controllers-connect-ajvh9h0v.js} +19 -19
  83. package/dist/{cursor-adapter-4mr4jgkw.js → cursor-adapter-gd0sa7bh.js} +1 -1
  84. package/dist/{db-w36wwb5g.js → db-dv9j7ts5.js} +2 -2
  85. package/dist/{db-ppv2tze7.js → db-z26vgdvj.js} +3 -3
  86. package/dist/{dispatcher-pv5h77vf.js → dispatcher-8m0htjq8.js} +17 -17
  87. package/dist/{dsh-adapter-d4pdpb3r.js → dsh-adapter-y8gyk130.js} +1 -1
  88. package/dist/{e2b-fn3zacvt.js → e2b-74vg9b7e.js} +1 -1
  89. package/dist/{e2b-222hsajc.js → e2b-jyay8wzn.js} +1 -1
  90. package/dist/{easter-egg-3d-4sp4dvmc.js → easter-egg-3d-28f880xx.js} +1 -1
  91. package/dist/{execute-jev839y1.js → execute-9seb79vh.js} +1 -1
  92. package/dist/{external-channel-y6nkzhnn.js → external-channel-smz7jyth.js} +2 -2
  93. package/dist/{getMachineId-bsd-brn0xym3.js → getMachineId-bsd-whfre6gr.js} +3 -3
  94. package/dist/{getMachineId-darwin-t339sfxm.js → getMachineId-darwin-5zjynb19.js} +3 -3
  95. package/dist/{getMachineId-linux-5eq95d8b.js → getMachineId-linux-8mksrrsq.js} +3 -3
  96. package/dist/{getMachineId-unsupported-qavrtc7j.js → getMachineId-unsupported-fxtqsdt2.js} +3 -3
  97. package/dist/{getMachineId-win-q7fe1wx7.js → getMachineId-win-nzfmz857.js} +3 -3
  98. package/dist/{grok-adapter-t08b9ntm.js → grok-adapter-z0nnvtbn.js} +7 -7
  99. package/dist/handlers-pspryh6g.js +41 -0
  100. package/dist/{heartbeat-vn31xwdk.js → heartbeat-f928aqyp.js} +20 -20
  101. package/dist/{hook-cpjs8q51.js → hook-d7c605r4.js} +7 -7
  102. package/dist/{hook-6d0zbvvt.js → hook-q9hv5xf9.js} +5 -5
  103. package/dist/{http-9c6medda.js → http-q6j4k989.js} +1000 -251
  104. package/dist/{index-k0yt1tvq.js → index-5nb266jr.js} +17 -17
  105. package/dist/{index-ysd45kgc.js → index-8fxt5eah.js} +10 -10
  106. package/dist/{index-zgvqzjsw.js → index-cekzwhez.js} +5 -5
  107. package/dist/{index-38t35h0n.js → index-ch0c1xff.js} +17 -17
  108. package/dist/{index-pkma1yf8.js → index-ebnjhxh1.js} +1 -1
  109. package/dist/{index-yqgbfgaj.js → index-ed8dy8rn.js} +20 -20
  110. package/dist/{index-qhjf2htm.js → index-ejqdkkqx.js} +262 -96
  111. package/dist/{index-dr94zbnh.js → index-farm1zsn.js} +32661 -29443
  112. package/dist/{index-g6k9tctj.js → index-yn1yxg85.js} +1 -1
  113. package/dist/{keepalive-em0117bp.js → keepalive-zzc3hks1.js} +3 -3
  114. package/dist/lead-c0pjcaw4.js +79 -0
  115. package/dist/{maintenance-kbme3qy9.js → maintenance-kqsxzpzx.js} +1 -1
  116. package/dist/{oauth-refresh-sweep-xysj4yse.js → oauth-refresh-sweep-7qt0g55z.js} +8 -3
  117. package/dist/{onboard-p6q0vz97.js → onboard-k7x13fqg.js} +121 -8
  118. package/dist/{opencode-adapter-esv3k9yn.js → opencode-adapter-tqvrtter.js} +5 -5
  119. package/dist/{otel-impl-54qhqh1z.js → otel-impl-sqkf6k3b.js} +97 -97
  120. package/dist/{pi-mono-adapter-mymbwhj0.js → pi-mono-adapter-m2qnafxm.js} +89 -14
  121. package/dist/{pricing-refresh-yb75wv33.js → pricing-refresh-wkpwhgvs.js} +3 -3
  122. package/dist/{rbac-roles-dexhc9x8.js → rbac-roles-dpn0c0wj.js} +7 -4
  123. package/dist/{rbac-roles-1vp2mw4p.js → rbac-roles-wk7xd6a6.js} +2 -2
  124. package/dist/{render-v2-tjay5hhf.js → render-v2-ex2m4g2k.js} +3 -3
  125. package/dist/{resume-8ayf37g5.js → resume-725g9b0b.js} +7 -7
  126. package/dist/{runtime-hqe5cqcd.js → runtime-kajas15g.js} +1 -1
  127. package/dist/{session-access-fdhp987s.js → session-access-0j2ezjje.js} +8 -4
  128. package/dist/{session-source-backfill-qgtc86z1.js → session-source-backfill-62202awe.js} +2 -2
  129. package/dist/{setup-4rrmqcq3.js → setup-9sj11ax6.js} +1 -1
  130. package/dist/{virtual-modules-52km00d6.js → virtual-modules-c6fcq6zr.js} +2 -2
  131. package/dist/{work-objects-p28fgd70.js → work-objects-0y07495y.js} +20 -20
  132. package/dist/worker-d4q9sqbc.js +79 -0
  133. package/openapi.json +1536 -407
  134. package/package.json +7 -5
  135. package/src/be/automation-preflight.ts +2 -0
  136. package/src/be/connector-codes.ts +65 -22
  137. package/src/be/db/api-keys.ts +9 -0
  138. package/src/be/db/workflow-runs.ts +36 -5
  139. package/src/be/db.ts +80 -22
  140. package/src/be/memory/boot-reembed.ts +4 -1
  141. package/src/be/memory/constants.ts +6 -0
  142. package/src/be/memory/embedding-recovery.ts +48 -0
  143. package/src/be/memory/index-content.ts +5 -2
  144. package/src/be/memory/providers/openai-embedding.ts +65 -14
  145. package/src/be/memory/task-learning.ts +257 -0
  146. package/src/be/memory/types.ts +13 -2
  147. package/src/be/migrations/245_script_executor.sql +3 -0
  148. package/src/be/migrations/246_connector_code_kind.sql +11 -0
  149. package/src/be/oauth-refresh-sweep.ts +2 -1
  150. package/src/be/onboarding.ts +125 -15
  151. package/src/be/rbac-roles.ts +4 -1
  152. package/src/be/scripts/boot-reembed.ts +2 -0
  153. package/src/be/scripts/db.ts +14 -4
  154. package/src/be/scripts/embeddings.ts +4 -2
  155. package/src/be/scripts/run-saved.ts +1 -0
  156. package/src/be/scripts/typecheck.ts +9 -3
  157. package/src/be/seed/agent-fs-provision.ts +75 -14
  158. package/src/be/seed-skills/index.ts +7 -0
  159. package/src/be/swarm-config-guard.ts +3 -0
  160. package/src/cli.tsx +9 -0
  161. package/src/comb/config.ts +6 -1
  162. package/src/commands/onboard/agent-fs-version.ts +5 -0
  163. package/src/commands/onboard/compose-generator.ts +117 -4
  164. package/src/commands/onboard/env-generator.ts +9 -0
  165. package/src/commands/onboard/tei-image.ts +12 -0
  166. package/src/commands/onboard/types.ts +7 -0
  167. package/src/commands/onboard.tsx +11 -1
  168. package/src/commands/provider-credentials.ts +2 -1
  169. package/src/commands/runner.ts +14 -1
  170. package/src/heartbeat/heartbeat.ts +14 -1
  171. package/src/heartbeat/script-runner-probe.ts +47 -0
  172. package/src/http/agentfs-proxy.ts +369 -0
  173. package/src/http/all-routes.ts +1 -0
  174. package/src/http/api-keys.ts +24 -9
  175. package/src/http/codex-oauth-device.ts +8 -1
  176. package/src/http/fs.ts +64 -10
  177. package/src/http/idempotency-key.ts +1 -1
  178. package/src/http/inbox-state.ts +27 -1
  179. package/src/http/index.ts +7 -4
  180. package/src/http/memory.ts +7 -4
  181. package/src/http/onboarding.ts +139 -11
  182. package/src/http/pages-public.ts +38 -5
  183. package/src/http/scripts.ts +27 -0
  184. package/src/http/session-access.ts +24 -0
  185. package/src/http/sessions.ts +10 -6
  186. package/src/http/tasks.ts +85 -11
  187. package/src/http/telemetry-ui.ts +1 -1
  188. package/src/http/user-roles.ts +4 -1
  189. package/src/http/users.ts +272 -3
  190. package/src/http/utils.ts +22 -3
  191. package/src/http/workflows.ts +10 -0
  192. package/src/http/x.ts +5 -0
  193. package/src/prompts/base-prompt.ts +45 -10
  194. package/src/prompts/session-templates.ts +43 -5
  195. package/src/providers/amp-adapter.ts +1 -1
  196. package/src/providers/codex-adapter.ts +6 -1
  197. package/src/providers/cursor-adapter.ts +1 -1
  198. package/src/providers/dsh-adapter.ts +1 -1
  199. package/src/providers/pi-mono-adapter.ts +99 -14
  200. package/src/rbac/legacy-policy.ts +1 -0
  201. package/src/rbac/permissions.ts +4 -0
  202. package/src/scheduler/scheduler.ts +2 -1
  203. package/src/scripts-runtime/eval-harness.ts +8 -1
  204. package/src/scripts-runtime/executors/native.ts +22 -16
  205. package/src/scripts-runtime/executors/quickjs-prelude.ts +103 -1
  206. package/src/scripts-runtime/executors/quickjs-runner.ts +2 -1
  207. package/src/scripts-runtime/executors/quickjs.ts +7 -0
  208. package/src/scripts-runtime/executors/registry.ts +32 -0
  209. package/src/scripts-runtime/executors/types.ts +7 -0
  210. package/src/scripts-runtime/host-validated-args.ts +133 -0
  211. package/src/scripts-runtime/loader.ts +30 -3
  212. package/src/scripts-runtime/swarm-sdk.ts +9 -2
  213. package/src/scripts-runtime/types/stdlib.d.ts +9 -1
  214. package/src/scripts-runtime/types/swarm-sdk.d.ts +9 -1
  215. package/src/tests/agent-fs-provision-seeder.test.ts +261 -92
  216. package/src/tests/agentfs-proxy.test.ts +1106 -0
  217. package/src/tests/api-key-status-by-suffix.test.ts +242 -0
  218. package/src/tests/base-prompt.test.ts +139 -4
  219. package/src/tests/codex-adapter.test.ts +46 -8
  220. package/src/tests/codex-oauth-device.test.ts +12 -1
  221. package/src/tests/comb-config-guard.test.ts +20 -0
  222. package/src/tests/comb-links.test.ts +79 -1
  223. package/src/tests/comb-status.test.ts +44 -0
  224. package/src/tests/community-linear-delivery-loop.test.ts +2099 -0
  225. package/src/tests/credential-check.test.ts +4 -2
  226. package/src/tests/docs-index-footprint.test.js +3 -3
  227. package/src/tests/docs-index.test.ts +135 -0
  228. package/src/tests/dsh-adapter.test.ts +1 -1
  229. package/src/tests/fetch-resolved-env.test.ts +16 -2
  230. package/src/tests/fixtures/onboard/baseline-states.ts +68 -0
  231. package/src/tests/fixtures/onboard/content.compose.yml.txt +190 -0
  232. package/src/tests/fixtures/onboard/content.env.txt +29 -0
  233. package/src/tests/fixtures/onboard/dev.compose.yml.txt +155 -0
  234. package/src/tests/fixtures/onboard/dev.env.txt +28 -0
  235. package/src/tests/fixtures/onboard/full-all.compose.yml.txt +582 -0
  236. package/src/tests/fixtures/onboard/full-all.env.txt +59 -0
  237. package/src/tests/fixtures/onboard/full.compose.yml.txt +435 -0
  238. package/src/tests/fixtures/onboard/full.env.txt +36 -0
  239. package/src/tests/fixtures/onboard/research.compose.yml.txt +155 -0
  240. package/src/tests/fixtures/onboard/research.env.txt +28 -0
  241. package/src/tests/fixtures/onboard/solo.compose.yml.txt +85 -0
  242. package/src/tests/fixtures/onboard/solo.env.txt +26 -0
  243. package/src/tests/heartbeat-checklist.test.ts +89 -0
  244. package/src/tests/heartbeat-script-runner-probe.test.ts +83 -0
  245. package/src/tests/http-log-scrubbing.test.ts +34 -1
  246. package/src/tests/http-task-write-ownership.test.ts +5 -1
  247. package/src/tests/invites-http.test.ts +464 -0
  248. package/src/tests/memory-embedding-input-cap.test.ts +122 -1
  249. package/src/tests/memory-embedding-recovery.test.ts +137 -0
  250. package/src/tests/oauth-refresh-sweep.test.ts +34 -1
  251. package/src/tests/onboard-compose.test.ts +162 -0
  252. package/src/tests/onboard-env.test.ts +24 -0
  253. package/src/tests/onboard-non-interactive.test.ts +29 -0
  254. package/src/tests/onboard-tei-image.test.ts +36 -0
  255. package/src/tests/onboarding.test.ts +451 -4
  256. package/src/tests/openai-embedding-provider.test.ts +105 -2
  257. package/src/tests/opencode-adapter.test.ts +2 -1
  258. package/src/tests/pages-frame-ancestors.test.ts +162 -0
  259. package/src/tests/pi-codemode.test.ts +2 -1
  260. package/src/tests/pi-mono-adapter.test.ts +54 -0
  261. package/src/tests/pi-mono-session-cost-on-abort.test.ts +270 -0
  262. package/src/tests/pi-native-mcp.test.ts +13 -1
  263. package/src/tests/pi-tool-exposure.test.ts +8 -8
  264. package/src/tests/preload-env.test.ts +4 -0
  265. package/src/tests/preload.ts +4 -0
  266. package/src/tests/probe-tool-schema-tokens.test.ts +144 -0
  267. package/src/tests/prompt-template-session.test.ts +20 -3
  268. package/src/tests/rbac-admission-e2e.test.ts +22 -6
  269. package/src/tests/rbac-admission.test.ts +342 -4
  270. package/src/tests/rbac-engine.test.ts +1 -0
  271. package/src/tests/rbac-roles.test.ts +9 -1
  272. package/src/tests/request-composition-derive.test.ts +329 -0
  273. package/src/tests/script-connections.test.ts +2 -0
  274. package/src/tests/script-executor-conformance.test.ts +36 -1
  275. package/src/tests/script-executor-quickjs-pool.test.ts +50 -0
  276. package/src/tests/script-executor-quickjs.test.ts +140 -0
  277. package/src/tests/script-host-validated-args-native.test.ts +196 -0
  278. package/src/tests/script-per-script-executor.test.ts +415 -0
  279. package/src/tests/scripts-external-api.test.ts +45 -0
  280. package/src/tests/sdk-allowlist.test.ts +3 -2
  281. package/src/tests/self-improvement.test.ts +4 -4
  282. package/src/tests/send-task-requester-gate.test.ts +64 -3
  283. package/src/tests/sessions-move.test.ts +39 -1
  284. package/src/tests/store-progress-learning.test.ts +381 -0
  285. package/src/tests/ui-logs-parser.test.ts +180 -0
  286. package/src/tests/whoami-route.test.ts +41 -0
  287. package/src/tests/workflow-engine-v2.test.ts +116 -1
  288. package/src/tests/workflow-mcp-trigger-schema.test.ts +209 -0
  289. package/src/tests/workflow-run-finished-event.test.ts +263 -0
  290. package/src/tests/workflow-swarm-script.test.ts +34 -0
  291. package/src/tools/inject-learning.ts +1 -1
  292. package/src/tools/memory-edit.ts +1 -1
  293. package/src/tools/memory-search.ts +1 -1
  294. package/src/tools/script-common.ts +1 -0
  295. package/src/tools/script-run.ts +7 -0
  296. package/src/tools/script-upsert.ts +7 -0
  297. package/src/tools/store-progress.ts +71 -1
  298. package/src/tools/utils.ts +12 -1
  299. package/src/tools/workflows/trigger-workflow.ts +104 -10
  300. package/src/types.ts +12 -1
  301. package/src/utils/constants.ts +39 -0
  302. package/src/utils/credentials.ts +5 -2
  303. package/src/utils/task-attachment-links.ts +15 -3
  304. package/src/workflows/engine.ts +18 -16
  305. package/src/workflows/executors/swarm-script.ts +5 -0
  306. package/src/workflows/foreach-driver.ts +5 -11
  307. package/src/workflows/node-timeout.ts +30 -0
  308. package/templates/community/linear-delivery-loop/PLAYBOOK.md +323 -0
  309. package/templates/community/linear-delivery-loop/README.md +38 -0
  310. package/templates/community/linear-delivery-loop/config.example.json +31 -0
  311. package/templates/community/linear-delivery-loop/lead-prompt.md +34 -0
  312. package/templates/community/linear-delivery-loop/schedule.json +10 -0
  313. package/templates/community/linear-delivery-loop/scripts/linear-delivery-setup.ts +148 -0
  314. package/templates/community/linear-delivery-loop/scripts/linear-delivery-tick.ts +1781 -0
  315. package/templates/skills/swarm-docs/config.json +14 -0
  316. package/templates/skills/swarm-docs/content.md +144 -0
  317. package/dist/handlers-eqsnmv95.js +0 -41
  318. package/dist/lead-23e7z59t.js +0 -79
  319. package/dist/worker-e3w7vye1.js +0 -79
  320. package/dist/{azure-openai-responses-9kfwhmn7.js → azure-openai-responses-43asa23s.js} +0 -0
  321. package/dist/{node-vxx896fa.js → node-t28cc56b.js} +0 -0
  322. package/dist/{openai-codex-responses-79s8xnbh.js → openai-codex-responses-jhr3sjtm.js} +3 -3
  323. package/dist/{openai-completions-tdbyts0x.js → openai-completions-gs6yn37k.js} +3 -3
  324. /package/dist/{openai-responses-dm3t5vqp.js → openai-responses-jqm9vwwk.js} +0 -0
@@ -288,12 +288,15 @@ import {
288
288
  emitBudgetRefusalSideEffects,
289
289
  workflowModelErrors,
290
290
  patchWorkflowDefinition,
291
+ idempotencyKeyHeader,
292
+ readIdempotencyKey,
293
+ replayAcceptedOperation,
291
294
  resolveScriptsOnlyMode,
292
295
  ALL_CAPABILITIES,
293
296
  hasCapability,
294
297
  getEnabledCapabilities,
295
298
  createServer
296
- } from "./cli-4hxrf0p4.js";
299
+ } from "./cli-pzafyqtk.js";
297
300
  import {
298
301
  chatThreadOfContextKey,
299
302
  agentContextKey,
@@ -335,7 +338,9 @@ import {
335
338
  init_asset_key_audit,
336
339
  CredentialBindingSchema,
337
340
  placeholderForConfigKey,
341
+ isPrivateAddress,
338
342
  assertUrlSafe,
343
+ init_egress_guard,
339
344
  init_credential_broker,
340
345
  encryptSecret,
341
346
  decryptSecret,
@@ -403,6 +408,7 @@ import {
403
408
  getAllTasks2,
404
409
  getTasksCount2,
405
410
  getTaskStats2,
411
+ getInProgressTasksByContextKey2,
406
412
  getLatestScriptRunStepTaskByContextKey2,
407
413
  init_read,
408
414
  MODEL_PROFILE_CONFIG_KEY,
@@ -454,7 +460,6 @@ import {
454
460
  updateTaskProgress2,
455
461
  IdempotencyKeyTakenError,
456
462
  workflowRequestFingerprint,
457
- findAcceptedWorkflowOperation,
458
463
  assertWorkflowIdempotencyKeyUnused,
459
464
  recordAcceptedWorkflowOperation,
460
465
  init_workflow_idempotency,
@@ -521,6 +526,7 @@ import {
521
526
  searchMemoryCandidates,
522
527
  init_search_candidates,
523
528
  getEmbeddingProvider2,
529
+ resetEmbeddingProvider2,
524
530
  getMemoryStore2,
525
531
  init_memory,
526
532
  searchScripts,
@@ -763,7 +769,7 @@ import {
763
769
  countScriptRunJournalSteps2,
764
770
  countScriptRunJournalAgentTaskSteps2,
765
771
  init_db3
766
- } from "./cli-g8ka5j4j.js";
772
+ } from "./cli-qvaa1gjp.js";
767
773
  import {
768
774
  PRIMARY_ROLE_NAMES2,
769
775
  getUserGrant2,
@@ -774,22 +780,22 @@ import {
774
780
  assertNotLastAdmin2,
775
781
  setUserRoles2,
776
782
  init_rbac_roles
777
- } from "./cli-a2rr0fmg.js";
778
- import"./cli-03gg9pd1.js";
783
+ } from "./cli-hpkvgdad.js";
784
+ import"./cli-typdpe8g.js";
779
785
  import {
780
786
  normalizeModelKey
781
- } from "./cli-5sckpqcx.js";
787
+ } from "./cli-2sjm3t4r.js";
782
788
  import {
783
789
  upsertModelCatalogOverlay,
784
790
  deleteModelCatalogOverlay,
785
791
  loadModelsCatalog,
786
792
  requestModelCatalogRefresh2
787
- } from "./cli-aj8djyqt.js";
793
+ } from "./cli-twjnyvs8.js";
788
794
  import {
789
795
  AMP_ESTIMATE_MODELS,
790
796
  ampBillsCacheWrites,
791
797
  resolveAmpModel
792
- } from "./cli-bqztfyve.js";
798
+ } from "./cli-cb50g9cd.js";
793
799
  import {
794
800
  workflowEventBus2,
795
801
  init_event_bus
@@ -829,7 +835,7 @@ import {
829
835
  generateEdges,
830
836
  definitionNodeIds,
831
837
  validateDefinition
832
- } from "./cli-3fwqy71j.js";
838
+ } from "./cli-dv11pgpv.js";
833
839
  import {
834
840
  require_dist2,
835
841
  isInvalidClientError,
@@ -889,8 +895,10 @@ import {
889
895
  buildSandboxedCommand,
890
896
  sandboxSpawnEnv,
891
897
  readStreamCapped,
898
+ isExecutorAllowedByDeployment,
892
899
  MIN_SCRIPT_WALL_CLOCK_MS,
893
900
  MAX_SCRIPT_WALL_CLOCK_MS,
901
+ HOST_VALIDATED_ARGS,
894
902
  require_typescript2,
895
903
  checkImportAllowlist,
896
904
  runScriptWithRunCredential,
@@ -909,14 +917,14 @@ import {
909
917
  typecheckWithAmbient,
910
918
  typecheckScript,
911
919
  parseSkillContent
912
- } from "./cli-3gfvmzht.js";
920
+ } from "./cli-kbtdftbw.js";
913
921
  import {
914
922
  buildAuthorizationUrl,
915
923
  exchangeAuthorizationCode,
916
924
  oauthAppRowToProviderConfig,
917
925
  forceRefreshAuthorizationOrThrow,
918
926
  forceRefreshTokenOrThrow
919
- } from "./cli-kf4jree9.js";
927
+ } from "./cli-rm66vqdz.js";
920
928
  import {
921
929
  signalProcessGroup,
922
930
  detachedProcessGroup,
@@ -949,9 +957,9 @@ import {
949
957
  getSlackApp2,
950
958
  startSlackApp2,
951
959
  stopSlackApp2
952
- } from "./cli-g20jjwt4.js";
953
- import"./cli-gnsb1zxa.js";
954
- import"./cli-v49fkghp.js";
960
+ } from "./cli-thsaq4pd.js";
961
+ import"./cli-vhpckzvp.js";
962
+ import"./cli-ekptgk2m.js";
955
963
  import {
956
964
  _fetchHolder,
957
965
  CLIENT_ID,
@@ -968,7 +976,7 @@ import {
968
976
  import {
969
977
  retryFailedRun2,
970
978
  cancelWorkflowRun2
971
- } from "./cli-3aqd4jaa.js";
979
+ } from "./cli-3v2cyqb8.js";
972
980
  import {
973
981
  duplicateGuardedQueryKey,
974
982
  warnIfCorsAllowsAnyOrigin,
@@ -978,6 +986,7 @@ import {
978
986
  parseQueryParams,
979
987
  getPathSegments,
980
988
  wireHttpSpanLifecycle,
989
+ redactShareTokenInPath,
981
990
  safeRequestUrlForLog,
982
991
  agentWithCapacity,
983
992
  RequestBodyTooLargeError,
@@ -1007,13 +1016,15 @@ import {
1007
1016
  readableByFor2,
1008
1017
  SESSION_WRITE_DENIED2,
1009
1018
  hasSessionWrite2,
1019
+ NOT_YOUR_TASK_DENIED2,
1020
+ deniesNonOwnerTaskWrite2,
1010
1021
  denyParentAttach2,
1011
1022
  runtimeInstanceHeader,
1012
1023
  bodyTask,
1013
1024
  bodyListTask,
1014
1025
  describeRequestRoute,
1015
1026
  route
1016
- } from "./cli-art7c1zh.js";
1027
+ } from "./cli-hq2v4rr5.js";
1017
1028
  import {
1018
1029
  FilesError,
1019
1030
  normalizeFilesError,
@@ -1041,7 +1052,7 @@ import {
1041
1052
  resolvePoolDelivery2,
1042
1053
  createPoolAgent2,
1043
1054
  init_db
1044
- } from "./cli-gf95q3bm.js";
1055
+ } from "./cli-6vwjsetx.js";
1045
1056
  import {
1046
1057
  RATING_MODEL_MAX_LENGTH,
1047
1058
  REFERENCES_SOURCE_MAX_LENGTH,
@@ -1051,14 +1062,17 @@ import"./cli-737pyqxr.js";
1051
1062
  import {
1052
1063
  startPoolRouting,
1053
1064
  stopPoolRouting,
1065
+ INVITE_CODE_TTL_MS,
1054
1066
  DEFAULT_CONNECTOR_CODE_LABEL,
1067
+ DEFAULT_INVITE_CODE_LABEL,
1055
1068
  createConnectorCode,
1056
1069
  exchangeConnectorCode,
1070
+ deleteUnusedInviteCodes,
1057
1071
  checkHeartbeatChecklist2,
1058
1072
  runHeartbeatSweep2,
1059
1073
  stopHeartbeat2
1060
- } from "./cli-emhjcj2y.js";
1061
- import"./cli-d2dpf95n.js";
1074
+ } from "./cli-5qt9vzmq.js";
1075
+ import"./cli-rg4b2g6j.js";
1062
1076
  import {
1063
1077
  import_websocket,
1064
1078
  import_websocket_server
@@ -1067,7 +1081,7 @@ import {
1067
1081
  runtimeSupervisorStatus,
1068
1082
  startRuntimeSupervisor,
1069
1083
  stopRuntimeSupervisor
1070
- } from "./cli-c2habvsf.js";
1084
+ } from "./cli-f3mzy662.js";
1071
1085
  import"./cli-ns59dc6r.js";
1072
1086
  import {
1073
1087
  ACP_TARGET_IDS
@@ -1078,7 +1092,7 @@ import {
1078
1092
  } from "./cli-c081bj3b.js";
1079
1093
  import {
1080
1094
  MAX_RATE_LIMIT_RESET_MS
1081
- } from "./cli-7enjwya9.js";
1095
+ } from "./cli-vz78s5wm.js";
1082
1096
  import {
1083
1097
  isLeadChannelMonitoringEnabled,
1084
1098
  ORG_IDENTITY_KEYS,
@@ -1088,14 +1102,14 @@ import {
1088
1102
  isUnknownModelError,
1089
1103
  checkProviderPoolKey,
1090
1104
  writeSkillsToFilesystem
1091
- } from "./cli-n7asmce6.js";
1105
+ } from "./cli-xbz6mcg4.js";
1092
1106
  import {
1093
1107
  inviteEmailToSharedOrg,
1094
1108
  ensureAgentFsCredentialsForAgent
1095
- } from "./cli-6y51ryva.js";
1109
+ } from "./cli-x3cngb1h.js";
1096
1110
  import {
1097
1111
  Anthropic
1098
- } from "./cli-rr7zx19j.js";
1112
+ } from "./cli-prcyzecg.js";
1099
1113
  import {
1100
1114
  isCodexOAuthConfigKey
1101
1115
  } from "./cli-j4bwnpkm.js";
@@ -1112,6 +1126,7 @@ import {
1112
1126
  registerSessionTokenConnection,
1113
1127
  init_session_token_connections,
1114
1128
  findUserById,
1129
+ findUserByEmail,
1115
1130
  getUserIdentities,
1116
1131
  listUserEvents,
1117
1132
  listUserTokens,
@@ -1132,7 +1147,7 @@ import {
1132
1147
  revokeAgentTokens,
1133
1148
  fingerprintApiKey,
1134
1149
  init_users
1135
- } from "./cli-esakngh8.js";
1150
+ } from "./cli-nf6k60sn.js";
1136
1151
  import"./cli-v41kn3cd.js";
1137
1152
  import"./cli-wgtk7xxr.js";
1138
1153
  import"./cli-1f0gp9vz.js";
@@ -1147,7 +1162,7 @@ import {
1147
1162
  clearAuditSink,
1148
1163
  can,
1149
1164
  init_rbac
1150
- } from "./cli-4rvr7zfx.js";
1165
+ } from "./cli-k4awgvzm.js";
1151
1166
  import {
1152
1167
  ASSET_KEY_MAX_LENGTH,
1153
1168
  normalizeAssetKey,
@@ -1265,6 +1280,7 @@ import {
1265
1280
  PromptTemplateHistorySchema,
1266
1281
  ScriptScopeSchema,
1267
1282
  ScriptFsModeSchema,
1283
+ ScriptExecutorNameSchema,
1268
1284
  ScriptRecordSchema,
1269
1285
  ScriptVersionRecordSchema,
1270
1286
  ExtensionManifestSchema,
@@ -1297,12 +1313,12 @@ import {
1297
1313
  KvKeySchema,
1298
1314
  KvEntrySchema,
1299
1315
  init_types
1300
- } from "./cli-4rf0f5vg.js";
1316
+ } from "./cli-s504c3w7.js";
1301
1317
  import {
1302
1318
  IDENTITY_FIELD_BUDGETS,
1303
1319
  IdentityFieldBudgetError,
1304
1320
  init_identity_field_budget
1305
- } from "./cli-hfeb68z4.js";
1321
+ } from "./cli-kj69b9tw.js";
1306
1322
  import {
1307
1323
  isSteeringEnabled2,
1308
1324
  init_steering_enabled
@@ -1325,7 +1341,7 @@ import {
1325
1341
  splitCredentialPool,
1326
1342
  selectCredential,
1327
1343
  init_credentials
1328
- } from "./cli-1tem6ns9.js";
1344
+ } from "./cli-gt4h8cxw.js";
1329
1345
  import {
1330
1346
  getOpenRouterAttributionHeaders,
1331
1347
  init_openrouter_base_url
@@ -1339,7 +1355,7 @@ import {
1339
1355
  resolveTemplate,
1340
1356
  init_resolver,
1341
1357
  init_session_templates
1342
- } from "./cli-dqygeeh5.js";
1358
+ } from "./cli-b41p764a.js";
1343
1359
  import {
1344
1360
  init_zod
1345
1361
  } from "./cli-4chz1hye.js";
@@ -1374,25 +1390,28 @@ import {
1374
1390
  import {
1375
1391
  init_package,
1376
1392
  package_default
1377
- } from "./cli-nn6vx2mq.js";
1378
- import {
1379
- realtimeBus,
1380
- init_bus
1381
- } from "./cli-syfdvhkc.js";
1393
+ } from "./cli-swkx9jm2.js";
1382
1394
  import {
1383
1395
  MAX_PROFILE_FILE_LENGTH,
1396
+ DEFAULT_APP_URL,
1384
1397
  getConfiguredAppUrls,
1385
1398
  getAppUrl,
1386
1399
  getMcpBaseUrl,
1387
1400
  getPublicMcpBaseUrl,
1388
1401
  agentFsFileRoute,
1402
+ isCombEnabled,
1403
+ isAgentFsProxyEnabled,
1389
1404
  init_constants2
1390
- } from "./cli-gf2850q7.js";
1405
+ } from "./cli-3cywb44q.js";
1391
1406
  import {
1392
1407
  parseEnvFlag,
1393
1408
  isEnvFlagEnabled,
1394
1409
  init_env_flag
1395
1410
  } from "./cli-cebvbc9t.js";
1411
+ import {
1412
+ realtimeBus,
1413
+ init_bus
1414
+ } from "./cli-syfdvhkc.js";
1396
1415
  import {
1397
1416
  getApiKey,
1398
1417
  init_api_key
@@ -4278,6 +4297,353 @@ async function handleActiveSessions(req, res, pathSegments, queryParams, myAgent
4278
4297
  return false;
4279
4298
  }
4280
4299
 
4300
+ // src/http/agentfs-proxy.ts
4301
+ init_api_key();
4302
+ init_constants2();
4303
+ import { Readable } from "node:stream";
4304
+
4305
+ // src/http/ip-rate-limit.ts
4306
+ var PRUNE_THRESHOLD = 1e4;
4307
+ var PRUNE_INTERVAL_MS = 60000;
4308
+ var MAX_BUCKETS = 50000;
4309
+ function isPrivateAddress2(address) {
4310
+ const ip = address.startsWith("::ffff:") ? address.slice(7) : address;
4311
+ if (ip === "::1" || ip.startsWith("127.") || ip.startsWith("10."))
4312
+ return true;
4313
+ if (ip.startsWith("192.168.") || ip.startsWith("169.254."))
4314
+ return true;
4315
+ const match = /^172\.(\d+)\./.exec(ip);
4316
+ if (match && Number(match[1]) >= 16 && Number(match[1]) <= 31)
4317
+ return true;
4318
+ const lower = ip.toLowerCase();
4319
+ return lower.startsWith("fc") || lower.startsWith("fd") || lower.startsWith("fe80:");
4320
+ }
4321
+ function clientIp(req) {
4322
+ const peer = req.socket.remoteAddress ?? "unknown";
4323
+ if (!isPrivateAddress2(peer))
4324
+ return peer;
4325
+ const raw = req.headers["x-forwarded-for"];
4326
+ const header = Array.isArray(raw) ? raw[raw.length - 1] : raw;
4327
+ const hop = header?.split(",").pop()?.trim();
4328
+ return hop || peer;
4329
+ }
4330
+ function createIpRateLimiter(options) {
4331
+ const buckets = new Map;
4332
+ let lastPruneAt = 0;
4333
+ function refill(bucket, now) {
4334
+ const elapsed = now - bucket.updatedAt;
4335
+ bucket.tokens = Math.min(options.capacity, bucket.tokens + elapsed * options.refillPerMs);
4336
+ bucket.updatedAt = now;
4337
+ }
4338
+ return {
4339
+ take(ip, now = Date.now()) {
4340
+ if (buckets.size > PRUNE_THRESHOLD && now - lastPruneAt >= PRUNE_INTERVAL_MS) {
4341
+ lastPruneAt = now;
4342
+ for (const [key, bucket] of buckets) {
4343
+ refill(bucket, now);
4344
+ if (bucket.tokens >= options.capacity)
4345
+ buckets.delete(key);
4346
+ }
4347
+ }
4348
+ let bucket = buckets.get(ip);
4349
+ if (!bucket) {
4350
+ if (buckets.size >= MAX_BUCKETS) {
4351
+ const oldest = buckets.keys().next().value;
4352
+ if (oldest !== undefined)
4353
+ buckets.delete(oldest);
4354
+ }
4355
+ bucket = { tokens: options.capacity, updatedAt: now };
4356
+ buckets.set(ip, bucket);
4357
+ }
4358
+ refill(bucket, now);
4359
+ if (bucket.tokens < 1)
4360
+ return false;
4361
+ bucket.tokens -= 1;
4362
+ return true;
4363
+ },
4364
+ reset() {
4365
+ buckets.clear();
4366
+ lastPruneAt = 0;
4367
+ }
4368
+ };
4369
+ }
4370
+
4371
+ // src/http/agentfs-proxy.ts
4372
+ var API_PREFIX = "/agentfs";
4373
+ var PUBLIC_PREFIXES = ["/share/", "/site/"];
4374
+ function upstreamBase() {
4375
+ const v = process.env.AGENT_FS_API_URL?.trim().replace(/\/+$/, "");
4376
+ return v || null;
4377
+ }
4378
+ var REQUEST_HEADER_ALLOWLIST = [
4379
+ "content-type",
4380
+ "content-length",
4381
+ "accept",
4382
+ "if-match",
4383
+ "if-none-match",
4384
+ "range",
4385
+ "x-agent-fs-message",
4386
+ "x-agent-fs-message-encoding"
4387
+ ];
4388
+ var AGENT_FS_BEARER = /^Bearer\s+af_\S+$/i;
4389
+ var RESPONSE_HEADER_ALLOWLIST = new Set([
4390
+ "content-type",
4391
+ "content-length",
4392
+ "content-disposition",
4393
+ "content-range",
4394
+ "accept-ranges",
4395
+ "etag",
4396
+ "last-modified",
4397
+ "cache-control",
4398
+ "location",
4399
+ "retry-after",
4400
+ "content-security-policy",
4401
+ "x-content-type-options",
4402
+ "x-frame-options",
4403
+ "referrer-policy",
4404
+ "x-robots-tag",
4405
+ "cross-origin-resource-policy"
4406
+ ]);
4407
+ var EXPOSED_HEADERS = ["X-Agent-FS-Version", "X-Agent-FS-Content-Hash", "Content-Disposition"];
4408
+ var UNGATED = "agent-fs enforces its own af_ auth; register needs a swarm credential; swarm credentials are never forwarded";
4409
+ var OK_200 = {
4410
+ description: "Upstream agent-fs response, streamed verbatim",
4411
+ unstructured: "Body is streamed from agent-fs; shape and content-type vary per upstream route"
4412
+ };
4413
+ for (const method of ["get", "post", "put", "delete", "patch"]) {
4414
+ route({
4415
+ method,
4416
+ path: "/agentfs/{path}",
4417
+ pattern: ["agentfs", null],
4418
+ exact: false,
4419
+ summary: "Streaming pass-through proxy to agent-fs (enabled by AGENT_FS_PROXY)",
4420
+ tags: ["Comb"],
4421
+ responses: {
4422
+ 200: OK_200,
4423
+ 401: {
4424
+ description: "Returned by agent-fs when credentials are missing or invalid, or by the swarm when POST /agentfs/auth/register has no swarm credential"
4425
+ },
4426
+ 403: {
4427
+ description: "POST /agentfs/auth/register with a user token for an email that is not the user's"
4428
+ },
4429
+ 413: { description: "POST /agentfs/auth/register body over 16 KB" },
4430
+ 429: { description: "Too many registrations from this IP (POST /agentfs/auth/register)" },
4431
+ 502: { description: "agent-fs is unreachable" }
4432
+ },
4433
+ auth: { apiKey: false },
4434
+ ...method === "get" ? {} : { rbac: { ungated: UNGATED } }
4435
+ });
4436
+ }
4437
+ for (const prefix of ["share", "site"]) {
4438
+ route({
4439
+ method: "get",
4440
+ path: `/${prefix}/{path}`,
4441
+ pattern: [prefix, null],
4442
+ exact: false,
4443
+ summary: `Public agent-fs ${prefix} page, served through the API (enabled by AGENT_FS_PROXY)`,
4444
+ tags: ["Comb"],
4445
+ responses: {
4446
+ 200: {
4447
+ description: `agent-fs /${prefix} response, streamed verbatim`,
4448
+ unstructured: "HTML page or raw file streamed from agent-fs; content-type varies"
4449
+ },
4450
+ 404: { description: "Unknown or expired link (from agent-fs), or the proxy is off" },
4451
+ 502: { description: "agent-fs is unreachable" }
4452
+ },
4453
+ auth: { apiKey: false }
4454
+ });
4455
+ }
4456
+ var registerRateLimiter = createIpRateLimiter({ capacity: 5, refillPerMs: 5 / 60000 });
4457
+ var REGISTER_BODY_MAX_BYTES = 16 * 1024;
4458
+ async function checkRegister(req, res) {
4459
+ const auth = await resolveHttpRequestAuth(req, getApiKey());
4460
+ if (!auth || auth.page || auth.kind !== "operator" && auth.kind !== "user") {
4461
+ jsonError(res, "Creating an agent-fs account here needs a swarm API key or user token", 401);
4462
+ return null;
4463
+ }
4464
+ let body;
4465
+ try {
4466
+ body = await parseBody(req, REGISTER_BODY_MAX_BYTES);
4467
+ } catch (err) {
4468
+ if (err instanceof RequestBodyTooLargeError)
4469
+ jsonError(res, "Request body too large", 413);
4470
+ else
4471
+ jsonError(res, "Invalid JSON body", 400);
4472
+ return null;
4473
+ }
4474
+ if (!body || typeof body !== "object" || Array.isArray(body)) {
4475
+ jsonError(res, "Invalid JSON body", 400);
4476
+ return null;
4477
+ }
4478
+ if (auth.kind === "user") {
4479
+ const email = body.email;
4480
+ const own = [auth.user.email, ...auth.user.emailAliases].filter((e) => typeof e === "string").map((e) => e.trim().toLowerCase());
4481
+ if (typeof email !== "string" || !own.includes(email.trim().toLowerCase())) {
4482
+ jsonError(res, "You can create an agent-fs account only for your own email", 403);
4483
+ return null;
4484
+ }
4485
+ }
4486
+ return JSON.stringify(body);
4487
+ }
4488
+ function routedPath(pathOnly) {
4489
+ let decoded = pathOnly;
4490
+ try {
4491
+ decoded = decodeURIComponent(pathOnly);
4492
+ } catch {}
4493
+ return decoded.replace(/\/{2,}/g, "/").replace(/\/+$/, "");
4494
+ }
4495
+ function isNonCanonical(pathOnly) {
4496
+ let decoded = pathOnly;
4497
+ try {
4498
+ decoded = decodeURIComponent(pathOnly);
4499
+ } catch {}
4500
+ for (const p of [pathOnly, decoded]) {
4501
+ if (p.includes("\\"))
4502
+ return true;
4503
+ if (p.split("/").some((s) => s === "." || s === ".."))
4504
+ return true;
4505
+ }
4506
+ return pathOnly.split("/").some((s) => /^(\.|%2e){1,2}$/i.test(s));
4507
+ }
4508
+ async function handleAgentFsProxy(req, res) {
4509
+ const url = req.url ?? "";
4510
+ const isApi = url.startsWith(`${API_PREFIX}/`);
4511
+ const isPublicPage = !isApi && PUBLIC_PREFIXES.some((p) => url.startsWith(p));
4512
+ if (!isApi && !isPublicPage)
4513
+ return false;
4514
+ const base = upstreamBase();
4515
+ if (!base || !isAgentFsProxyEnabled())
4516
+ return false;
4517
+ const method = req.method ?? "GET";
4518
+ const allowed = isApi ? ["GET", "POST", "PUT", "DELETE", "PATCH"] : ["GET"];
4519
+ if (!allowed.includes(method))
4520
+ return false;
4521
+ const upstreamPath = isApi ? url.slice(API_PREFIX.length) : url;
4522
+ const queryStart = upstreamPath.indexOf("?");
4523
+ const pathOnly = queryStart === -1 ? upstreamPath : upstreamPath.slice(0, queryStart);
4524
+ if (isNonCanonical(pathOnly)) {
4525
+ jsonError(res, "Invalid path", 400);
4526
+ return true;
4527
+ }
4528
+ const upstreamUrl = `${base}${upstreamPath}`;
4529
+ let sentPath;
4530
+ try {
4531
+ const basePath = new URL(base).pathname.replace(/\/+$/, "");
4532
+ sentPath = new URL(upstreamUrl).pathname.slice(basePath.length);
4533
+ } catch {
4534
+ jsonError(res, "Invalid path", 400);
4535
+ return true;
4536
+ }
4537
+ const isRegister = isApi && method === "POST" && routedPath(sentPath) === "/auth/register";
4538
+ if (isRegister && !registerRateLimiter.take(clientIp(req))) {
4539
+ res.setHeader("Retry-After", "12");
4540
+ jsonError(res, "rate_limited", 429);
4541
+ return true;
4542
+ }
4543
+ const registerBody = isRegister ? await checkRegister(req, res) : null;
4544
+ if (isRegister && registerBody === null)
4545
+ return true;
4546
+ const headers = new Headers;
4547
+ const authorization = req.headers.authorization?.trim();
4548
+ if (registerBody === null && authorization && AGENT_FS_BEARER.test(authorization) && authorization.replace(/^bearer\s+/i, "") !== getApiKey()) {
4549
+ headers.set("authorization", authorization);
4550
+ }
4551
+ for (const name of REQUEST_HEADER_ALLOWLIST) {
4552
+ const v = req.headers[name];
4553
+ if (typeof v === "string")
4554
+ headers.set(name, v);
4555
+ }
4556
+ if (registerBody !== null) {
4557
+ headers.delete("content-length");
4558
+ headers.set("content-type", "application/json");
4559
+ }
4560
+ const ac = new AbortController;
4561
+ res.on("close", () => {
4562
+ if (!res.writableEnded)
4563
+ ac.abort();
4564
+ });
4565
+ const hasBody = method !== "GET" && method !== "DELETE";
4566
+ let upstream;
4567
+ try {
4568
+ const init = {
4569
+ method,
4570
+ headers,
4571
+ redirect: "manual",
4572
+ signal: ac.signal
4573
+ };
4574
+ if (registerBody !== null) {
4575
+ init.body = registerBody;
4576
+ } else if (hasBody) {
4577
+ init.body = Readable.toWeb(req);
4578
+ init.duplex = "half";
4579
+ }
4580
+ upstream = await fetch(upstreamUrl, init);
4581
+ } catch {
4582
+ if (!res.headersSent && !res.destroyed)
4583
+ jsonError(res, "agent-fs upstream unavailable", 502);
4584
+ else
4585
+ res.destroy();
4586
+ return true;
4587
+ }
4588
+ if (isPublicPage) {
4589
+ for (const name of res.getHeaderNames()) {
4590
+ if (name.startsWith("access-control-"))
4591
+ res.removeHeader(name);
4592
+ }
4593
+ }
4594
+ const encoded = upstream.headers.has("content-encoding");
4595
+ upstream.headers.forEach((value, name) => {
4596
+ const n = name.toLowerCase();
4597
+ const keep = RESPONSE_HEADER_ALLOWLIST.has(n) || n.startsWith("x-agent-fs-") || isPublicPage && n.startsWith("access-control-");
4598
+ if (!keep)
4599
+ return;
4600
+ if (encoded && n === "content-length")
4601
+ return;
4602
+ res.setHeader(name, value);
4603
+ });
4604
+ if (isApi) {
4605
+ const expose = res.getHeader("Access-Control-Expose-Headers");
4606
+ if (typeof expose === "string" && expose !== "*") {
4607
+ res.setHeader("Access-Control-Expose-Headers", `${expose}, ${EXPOSED_HEADERS.join(", ")}`);
4608
+ }
4609
+ }
4610
+ res.writeHead(upstream.status);
4611
+ if (!upstream.body) {
4612
+ res.end();
4613
+ return true;
4614
+ }
4615
+ await pump(upstream.body, res);
4616
+ return true;
4617
+ }
4618
+ async function pump(body, res) {
4619
+ const reader = body.getReader();
4620
+ const closed = new Promise((resolve) => {
4621
+ if (res.destroyed)
4622
+ resolve();
4623
+ else
4624
+ res.once("close", () => resolve());
4625
+ });
4626
+ try {
4627
+ for (;; ) {
4628
+ if (res.destroyed)
4629
+ break;
4630
+ const { done, value } = await reader.read();
4631
+ if (done) {
4632
+ res.end();
4633
+ break;
4634
+ }
4635
+ if (!res.write(value)) {
4636
+ await Promise.race([new Promise((resolve) => res.once("drain", resolve)), closed]);
4637
+ }
4638
+ }
4639
+ } catch {
4640
+ res.destroy();
4641
+ } finally {
4642
+ reader.cancel().catch(() => {});
4643
+ }
4644
+ await closed;
4645
+ }
4646
+
4281
4647
  // src/http/agents.ts
4282
4648
  init_src2();
4283
4649
  init_zod();
@@ -5651,7 +6017,11 @@ var getAvailable = route({
5651
6017
  totalKeys: exports_coerce.number().int().min(1),
5652
6018
  scope: string2().optional(),
5653
6019
  scopeId: string2().optional(),
5654
- model: _enum2(["fable", "opus", "sonnet", "haiku"]).optional()
6020
+ model: _enum2(["fable", "opus", "sonnet", "haiku"]).optional(),
6021
+ keySuffixes: string2().transform((value) => value.split(",")).optional()
6022
+ }).refine((query) => !query.keySuffixes || query.keySuffixes.length === query.totalKeys, {
6023
+ message: "keySuffixes must list exactly totalKeys suffixes",
6024
+ path: ["keySuffixes"]
5655
6025
  }),
5656
6026
  responses: {
5657
6027
  200: {
@@ -5986,9 +6356,9 @@ async function handleApiKeys(req, res, pathSegments, queryParams) {
5986
6356
  const parsed = await getAvailable.parse(req, res, pathSegments, queryParams);
5987
6357
  if (!parsed)
5988
6358
  return true;
5989
- const { keyType, totalKeys, scope, scopeId, model } = parsed.query;
6359
+ const { keyType, totalKeys, scope, scopeId, model, keySuffixes } = parsed.query;
5990
6360
  try {
5991
- const result = await getAvailableKeyIndices2(keyType, totalKeys, scope, scopeId ?? null, model);
6361
+ const result = await getAvailableKeyIndices2(keyType, totalKeys, scope, scopeId ?? null, model, keySuffixes);
5992
6362
  getAvailable.respond(res, 200, {
5993
6363
  success: true,
5994
6364
  availableIndices: result.availableIndices,
@@ -8374,6 +8744,7 @@ var OnboardingStepIdSchema = _enum2([
8374
8744
  "agents",
8375
8745
  "memory",
8376
8746
  "integrations",
8747
+ "files",
8377
8748
  "first_task"
8378
8749
  ]);
8379
8750
  var OnboardingErrorClassSchema = _enum2([
@@ -8409,6 +8780,14 @@ var OnboardingMemoryPresetSchema = _enum2([
8409
8780
  "custom",
8410
8781
  "existing"
8411
8782
  ]);
8783
+ var OnboardingMemoryProviderSchema = _enum2([
8784
+ "local",
8785
+ "openai",
8786
+ "openrouter",
8787
+ "vercel",
8788
+ "azure",
8789
+ "custom"
8790
+ ]);
8412
8791
  var OnboardingIntegrationMethodSchema = _enum2([
8413
8792
  "slack",
8414
8793
  "github",
@@ -8417,6 +8796,7 @@ var OnboardingIntegrationMethodSchema = _enum2([
8417
8796
  "linear_oauth",
8418
8797
  "jira_oauth"
8419
8798
  ]);
8799
+ var OnboardingFilesMethodSchema = _enum2(["created", "connected"]);
8420
8800
  var OnboardingFirstTaskMethodSchema = _enum2(["suggestion", "free_form"]);
8421
8801
  var OnboardingStepMethodSchema = union2([
8422
8802
  OnboardingConnectMethodSchema,
@@ -8425,6 +8805,7 @@ var OnboardingStepMethodSchema = union2([
8425
8805
  OnboardingAgentsMethodSchema,
8426
8806
  OnboardingMemoryPresetSchema,
8427
8807
  OnboardingIntegrationMethodSchema,
8808
+ OnboardingFilesMethodSchema,
8428
8809
  OnboardingFirstTaskMethodSchema
8429
8810
  ]);
8430
8811
  function onboardingStepStateSchema(method) {
@@ -8446,6 +8827,7 @@ var OnboardingAiStepStateSchema = onboardingStepStateSchema(OnboardingAiMethodSc
8446
8827
  var OnboardingAgentsStepStateSchema = onboardingStepStateSchema(OnboardingAgentsMethodSchema);
8447
8828
  var OnboardingMemoryStepStateSchema = onboardingStepStateSchema(OnboardingMemoryPresetSchema);
8448
8829
  var OnboardingIntegrationStepStateSchema = onboardingStepStateSchema(OnboardingIntegrationMethodSchema);
8830
+ var OnboardingFilesStepStateSchema = onboardingStepStateSchema(OnboardingFilesMethodSchema);
8449
8831
  var OnboardingFirstTaskStepStateSchema = onboardingStepStateSchema(OnboardingFirstTaskMethodSchema);
8450
8832
  var OnboardingStateSchema = object2({
8451
8833
  version: literal2(1),
@@ -8463,6 +8845,7 @@ var OnboardingStateSchema = object2({
8463
8845
  agents: OnboardingAgentsStepStateSchema,
8464
8846
  memory: OnboardingMemoryStepStateSchema,
8465
8847
  integrations: OnboardingIntegrationStepStateSchema,
8848
+ files: OnboardingFilesStepStateSchema,
8466
8849
  first_task: OnboardingFirstTaskStepStateSchema
8467
8850
  })
8468
8851
  });
@@ -8477,7 +8860,11 @@ var OnboardingSignalsSchema = object2({
8477
8860
  })),
8478
8861
  embeddings: object2({
8479
8862
  configured: boolean(),
8480
- dimensions: number2().int().positive()
8863
+ dimensions: number2().int().positive(),
8864
+ model: string2().nullable(),
8865
+ local: boolean(),
8866
+ provider: OnboardingMemoryProviderSchema.nullable(),
8867
+ localDefault: object2({ model: string2() }).nullable()
8481
8868
  }),
8482
8869
  integrations: object2({
8483
8870
  slack: boolean(),
@@ -8491,7 +8878,8 @@ var OnboardingSignalsSchema = object2({
8491
8878
  leadsOnline: number2().int().nonnegative(),
8492
8879
  workersOnline: number2().int().nonnegative()
8493
8880
  }),
8494
- firstTask: object2({ id: string2(), status: string2() }).nullable()
8881
+ firstTask: object2({ id: string2(), status: string2() }).nullable(),
8882
+ files: object2({ enabled: boolean() })
8495
8883
  });
8496
8884
  var OnboardingResponseSchema = object2({
8497
8885
  state: OnboardingStateSchema,
@@ -8524,9 +8912,14 @@ var OnboardingActionSchema = union2([
8524
8912
  step: literal2("integrations"),
8525
8913
  method: OnboardingIntegrationMethodSchema
8526
8914
  }).strict(),
8915
+ object2({
8916
+ action: literal2("complete"),
8917
+ step: literal2("files"),
8918
+ method: OnboardingFilesMethodSchema
8919
+ }).strict(),
8527
8920
  object2({
8528
8921
  action: literal2("skip"),
8529
- step: _enum2(["name", "ai", "agents", "memory", "integrations", "first_task"])
8922
+ step: _enum2(["name", "ai", "agents", "memory", "integrations", "files", "first_task"])
8530
8923
  }).strict(),
8531
8924
  object2({
8532
8925
  action: literal2("fail"),
@@ -8555,8 +8948,12 @@ var OnboardingMemoryRequestSchema = object2({
8555
8948
  baseUrl: OnboardingHttpUrlSchema.optional(),
8556
8949
  model: string2().trim().min(1).optional(),
8557
8950
  apiKey: string2().min(1).optional(),
8558
- reuseKey: _enum2(["OPENAI_API_KEY", "OPENROUTER_API_KEY"]).optional()
8559
- }).strict();
8951
+ reuseKey: _enum2(["OPENAI_API_KEY", "OPENROUTER_API_KEY"]).optional(),
8952
+ restoreDefault: boolean().optional()
8953
+ }).strict().refine((body) => !body.restoreDefault || body.preset === "existing", {
8954
+ message: "restoreDefault needs preset existing",
8955
+ path: ["restoreDefault"]
8956
+ });
8560
8957
  var OnboardingMemoryResponseSchema = object2({
8561
8958
  ok: boolean(),
8562
8959
  dimensions: number2().int().positive().optional(),
@@ -8584,6 +8981,7 @@ function freshState(now, autoCompleted) {
8584
8981
  agents: emptyStep(),
8585
8982
  memory: emptyStep(),
8586
8983
  integrations: emptyStep(),
8984
+ files: emptyStep(),
8587
8985
  first_task: emptyStep()
8588
8986
  }
8589
8987
  };
@@ -8742,6 +9140,14 @@ function deriveState(state, signals, funnelEvents) {
8742
9140
  }
8743
9141
  return changed;
8744
9142
  }
9143
+ function presentState(state, signals) {
9144
+ if (signals.files.enabled || state.steps.files.status !== "todo")
9145
+ return state;
9146
+ return {
9147
+ ...state,
9148
+ steps: { ...state.steps, files: { ...state.steps.files, status: "skipped" } }
9149
+ };
9150
+ }
8745
9151
 
8746
9152
  class OnboardingTaskNotFoundError extends Error {
8747
9153
  }
@@ -8811,7 +9217,7 @@ async function readOrUpdateOnboarding(loadSignals, action) {
8811
9217
  readOnly: true
8812
9218
  });
8813
9219
  if (!read.dirty)
8814
- return { state: read.state, signals: read.signals };
9220
+ return { state: presentState(read.state, read.signals), signals: read.signals };
8815
9221
  }
8816
9222
  return await client.transaction(async () => {
8817
9223
  const events = [];
@@ -8826,20 +9232,30 @@ async function readOrUpdateOnboarding(loadSignals, action) {
8826
9232
  if (dirty)
8827
9233
  await persistState(state);
8828
9234
  queueTelemetry(events);
8829
- return { state, signals };
9235
+ return { state: presentState(state, signals), signals };
8830
9236
  });
8831
9237
  }
8832
- async function updateOnboardingMemory(preset, result, saveConfig) {
9238
+ var EMBEDDING_PREFIX_KEYS = [
9239
+ "EMBEDDING_QUERY_PREFIX",
9240
+ "EMBEDDING_DOCUMENT_PREFIX"
9241
+ ];
9242
+ var EMBEDDING_CONFIG_KEYS = [
9243
+ "EMBEDDING_API_BASE_URL",
9244
+ "EMBEDDING_MODEL",
9245
+ "EMBEDDING_API_KEY",
9246
+ ...EMBEDDING_PREFIX_KEYS
9247
+ ];
9248
+ async function updateOnboardingMemory(preset, result, change) {
8833
9249
  return await getDbClient2().transaction(async () => {
8834
9250
  const events = [];
8835
9251
  const loaded = await loadState(events);
8836
9252
  let dirty = loaded.dirty;
8837
9253
  const eventsForState = loaded.state.autoCompleted ? [] : events;
8838
- if (saveConfig) {
9254
+ if (change?.kind === "save") {
8839
9255
  const configValues = [
8840
- ["EMBEDDING_API_BASE_URL", saveConfig.baseUrl],
8841
- ["EMBEDDING_MODEL", saveConfig.model],
8842
- ...saveConfig.apiKey ? [["EMBEDDING_API_KEY", saveConfig.apiKey]] : []
9256
+ ["EMBEDDING_API_BASE_URL", change.baseUrl],
9257
+ ["EMBEDDING_MODEL", change.model],
9258
+ ...change.apiKey ? [["EMBEDDING_API_KEY", change.apiKey]] : []
8843
9259
  ];
8844
9260
  for (const [key, value] of configValues) {
8845
9261
  const validationError = validateConfigValue(key, value);
@@ -8849,17 +9265,26 @@ async function updateOnboardingMemory(preset, result, saveConfig) {
8849
9265
  await upsertSwarmConfig2({
8850
9266
  scope: "global",
8851
9267
  key: "EMBEDDING_API_BASE_URL",
8852
- value: saveConfig.baseUrl
9268
+ value: change.baseUrl
8853
9269
  });
8854
- await upsertSwarmConfig2({ scope: "global", key: "EMBEDDING_MODEL", value: saveConfig.model });
8855
- if (saveConfig.apiKey) {
9270
+ await upsertSwarmConfig2({ scope: "global", key: "EMBEDDING_MODEL", value: change.model });
9271
+ if (change.apiKey) {
8856
9272
  await upsertSwarmConfig2({
8857
9273
  scope: "global",
8858
9274
  key: "EMBEDDING_API_KEY",
8859
- value: saveConfig.apiKey,
9275
+ value: change.apiKey,
8860
9276
  isSecret: true
8861
9277
  });
8862
9278
  }
9279
+ for (const { key, value } of change.prefixes) {
9280
+ if (value === null)
9281
+ await deleteSwarmConfigByKey2("global", null, key);
9282
+ else
9283
+ await upsertSwarmConfig2({ scope: "global", key, value });
9284
+ }
9285
+ } else if (change?.kind === "restore") {
9286
+ for (const key of EMBEDDING_CONFIG_KEYS)
9287
+ await deleteSwarmConfigByKey2("global", null, key);
8863
9288
  }
8864
9289
  if (result.ok) {
8865
9290
  dirty = completeStep(loaded.state, "memory", preset, false, eventsForState) || dirty;
@@ -8869,7 +9294,7 @@ async function updateOnboardingMemory(preset, result, saveConfig) {
8869
9294
  if (dirty)
8870
9295
  await persistState(loaded.state);
8871
9296
  queueTelemetry(events);
8872
- return saveConfig !== undefined;
9297
+ return change !== undefined;
8873
9298
  });
8874
9299
  }
8875
9300
  async function updateOnboardingAiFromCodexDevice(result) {
@@ -9143,6 +9568,9 @@ async function completeFlow(flowId, credentials) {
9143
9568
  isSecret: true,
9144
9569
  description: `Codex ChatGPT OAuth credentials slot ${slot} (stored by dashboard device login)`
9145
9570
  });
9571
+ const keySuffix = deriveCodexKeySuffix(credentials.access, credentials.accountId);
9572
+ const checked = [{ keyIndex: slot, keySuffix, ok: true }];
9573
+ await recordKeyValidation2("CODEX_OAUTH", checked, "global", null, { retireAbsent: false });
9146
9574
  await updateOnboardingAiFromCodexDevice({ status: "complete" });
9147
9575
  state.status = "complete";
9148
9576
  state.slot = slot;
@@ -14031,6 +14459,7 @@ ${block}`, new Date().toISOString(), taskId]);
14031
14459
  }
14032
14460
 
14033
14461
  // src/http/fs.ts
14462
+ init_rbac_roles();
14034
14463
  init_rbac();
14035
14464
  init_types();
14036
14465
 
@@ -14076,7 +14505,11 @@ var agentFsCredentialStateSchema = object2({
14076
14505
  });
14077
14506
  var agentFsInviteStateSchema = object2({
14078
14507
  orgId: string2(),
14079
- invited: boolean()
14508
+ invited: boolean().describe("False only when the email already holds the requested role."),
14509
+ status: _enum2(["already_member", "added", "pending"]).describe("already_member: no change. added: an existing agent-fs account joined the org. pending: the email has no account, so agent-fs created an invite the person must accept."),
14510
+ inviteId: string2().optional().describe("Pending invite id (status pending)."),
14511
+ expiresAt: string2().optional().describe("ISO time the accept link expires (status pending)."),
14512
+ acceptUrl: string2().optional().describe("Link the invitee opens to accept (status pending). agent-fs sends no email, so hand it to the person. Absent when agent-fs has no app URL configured.")
14080
14513
  });
14081
14514
  var signedUrlResponseSchema = object2({
14082
14515
  url: string2(),
@@ -14116,21 +14549,23 @@ var inviteMemberRoute = route({
14116
14549
  path: "/api/fs/members/invite",
14117
14550
  pattern: ["api", "fs", "members", "invite"],
14118
14551
  summary: "Invite an external member into the agent-fs shared org",
14119
- description: "The API server performs the invite with its own bootstrap credentials (which are API-only and never served over HTTP), provisioning the shared org/drive first when needed. Intended for the cloud control plane's Connect-to-Drive flow. No keys are returned; the invitee obtains their own key via agent-fs registration.",
14552
+ description: "The API server performs the invite with its own bootstrap credentials (which are API-only and never served over HTTP), provisioning the shared org/drive first when needed. Intended for the cloud control plane's Connect-to-Drive flow. An email with an agent-fs account is added directly (status added). An email without one gets a pending invite (status pending) with an acceptUrl when agent-fs returns one; agent-fs sends no email, so the caller must hand the link to the invitee. No keys are returned; the invitee obtains their own key via agent-fs registration.",
14120
14553
  tags: ["FS"],
14121
14554
  body: object2({
14122
14555
  email: email(),
14123
14556
  role: _enum2(["viewer", "editor", "admin"]).default("editor")
14124
14557
  }),
14125
14558
  responses: {
14126
- 200: { description: "Invite state ({ orgId, invited })", schema: agentFsInviteStateSchema },
14559
+ 200: {
14560
+ description: "Invite state ({ orgId, invited, status, acceptUrl? })",
14561
+ schema: agentFsInviteStateSchema
14562
+ },
14127
14563
  400: { description: "Invalid body" },
14564
+ 403: { description: "A non-admin user token named an email other than its own" },
14128
14565
  500: { description: "Provisioning or invite failed" }
14129
14566
  },
14130
14567
  auth: { apiKey: true },
14131
- rbac: {
14132
- ungated: "Tenant API key is the org-owner credential; the invite runs under the API server's own bootstrap identity, no per-agent principal applies."
14133
- }
14568
+ rbac: { permission: "fs.join.own" }
14134
14569
  });
14135
14570
  var listTaskFilesRoute = route({
14136
14571
  method: "get",
@@ -14229,6 +14664,27 @@ var deleteTaskFileRoute = route({
14229
14664
  auth: { apiKey: true, agentId: true },
14230
14665
  rbac: { permission: "task.fs.mutate" }
14231
14666
  });
14667
+ async function resolveInvite(req, body) {
14668
+ const auth = getRequestAuth(req);
14669
+ if (auth?.kind !== "user" || !isRbacEnabled())
14670
+ return body;
14671
+ const grant = await getUserGrant2(auth.userId);
14672
+ if (grant.grantsAll)
14673
+ return body;
14674
+ const decision = can({
14675
+ principal: { kind: "user", userId: auth.userId },
14676
+ verb: "fs.join.own",
14677
+ resource: { kind: "none" },
14678
+ source: "http"
14679
+ });
14680
+ if (!decision.allow)
14681
+ return { denied: decision.reason };
14682
+ const ownEmail = auth.user.email?.trim();
14683
+ if (!ownEmail || ownEmail.toLowerCase() !== body.email.trim().toLowerCase()) {
14684
+ return { denied: "You can only join the shared drive with your own account email" };
14685
+ }
14686
+ return { email: ownEmail, role: grant.verbs.has("task.fs.mutate") ? "editor" : "viewer" };
14687
+ }
14232
14688
  async function handleFs(req, res, pathSegments, queryParams, myAgentId) {
14233
14689
  if (ensureAgentCredentialsRoute.match(req.method, pathSegments)) {
14234
14690
  const parsed = await ensureAgentCredentialsRoute.parse(req, res, pathSegments, queryParams);
@@ -14253,8 +14709,13 @@ async function handleFs(req, res, pathSegments, queryParams, myAgentId) {
14253
14709
  const parsed = await inviteMemberRoute.parse(req, res, pathSegments, queryParams);
14254
14710
  if (!parsed)
14255
14711
  return true;
14712
+ const invite = await resolveInvite(req, parsed.body);
14713
+ if ("denied" in invite) {
14714
+ jsonError(res, invite.denied, 403);
14715
+ return true;
14716
+ }
14256
14717
  try {
14257
- const result = await inviteEmailToSharedOrg(parsed.body.email, parsed.body.role);
14718
+ const result = await inviteEmailToSharedOrg(invite.email, invite.role);
14258
14719
  inviteMemberRoute.respond(res, 200, result);
14259
14720
  } catch (error) {
14260
14721
  const message = scrubSecrets(error instanceof Error ? error.message : String(error));
@@ -14591,7 +15052,10 @@ async function handleHeartbeat(req, res, pathSegments) {
14591
15052
  // src/http/inbox-state.ts
14592
15053
  init_zod();
14593
15054
  init_db3();
15055
+ init_rbac_roles();
15056
+ init_rbac();
14594
15057
  init_types();
15058
+ init_request_auth_context();
14595
15059
  var listState = route({
14596
15060
  method: "get",
14597
15061
  path: "/api/inbox-state",
@@ -14634,9 +15098,11 @@ var upsertState = route({
14634
15098
  schema: object2({ item: InboxItemStateSchema })
14635
15099
  },
14636
15100
  400: { description: "Validation error" },
14637
- 401: { description: "Unauthorized" }
15101
+ 401: { description: "Unauthorized" },
15102
+ 403: { description: "A non-admin user token wrote another user's state" }
14638
15103
  },
14639
- auth: { apiKey: true }
15104
+ auth: { apiKey: true },
15105
+ rbac: { permission: "favorite.write.own" }
14640
15106
  });
14641
15107
  var bulkUpsertState = route({
14642
15108
  method: "patch",
@@ -14654,11 +15120,19 @@ var bulkUpsertState = route({
14654
15120
  schema: object2({ items: array2(InboxItemStateSchema) })
14655
15121
  },
14656
15122
  400: { description: "Validation error" },
14657
- 401: { description: "Unauthorized" }
15123
+ 401: { description: "Unauthorized" },
15124
+ 403: { description: "A non-admin user token wrote another user's state" }
14658
15125
  },
14659
15126
  auth: { apiKey: true },
14660
- rbac: { ungated: "per-user inbox UI state, same posture as PATCH /api/inbox-state" }
15127
+ rbac: { permission: "favorite.write.own" }
14661
15128
  });
15129
+ var OTHER_USER_STATE_DENIED = "You can only change your own inbox state";
15130
+ async function writesOtherUsersState(req, userId) {
15131
+ const auth = getRequestAuth(req);
15132
+ if (auth?.kind !== "user" || auth.userId === userId || !isRbacEnabled())
15133
+ return false;
15134
+ return !(await getUserGrant2(auth.userId)).grantsAll;
15135
+ }
14662
15136
  async function handleInboxState(req, res, pathSegments, queryParams) {
14663
15137
  if (listState.match(req.method, pathSegments)) {
14664
15138
  const parsed = await listState.parse(req, res, pathSegments, queryParams);
@@ -14676,6 +15150,10 @@ async function handleInboxState(req, res, pathSegments, queryParams) {
14676
15150
  const parsed = await bulkUpsertState.parse(req, res, pathSegments, queryParams);
14677
15151
  if (!parsed)
14678
15152
  return true;
15153
+ if (await writesOtherUsersState(req, parsed.body.userId)) {
15154
+ jsonError(res, OTHER_USER_STATE_DENIED, 403);
15155
+ return true;
15156
+ }
14679
15157
  try {
14680
15158
  const items = await getDbClient2().transaction(async () => {
14681
15159
  const rows = [];
@@ -14694,6 +15172,10 @@ async function handleInboxState(req, res, pathSegments, queryParams) {
14694
15172
  const parsed = await upsertState.parse(req, res, pathSegments, queryParams);
14695
15173
  if (!parsed)
14696
15174
  return true;
15175
+ if (await writesOtherUsersState(req, parsed.body.userId)) {
15176
+ jsonError(res, OTHER_USER_STATE_DENIED, 403);
15177
+ return true;
15178
+ }
14697
15179
  try {
14698
15180
  const item = await upsertInboxState2({
14699
15181
  userId: parsed.body.userId,
@@ -15815,7 +16297,7 @@ import { randomUUID as randomUUID2 } from "node:crypto";
15815
16297
  // node_modules/@hono/node-server/dist/index.mjs
15816
16298
  import { Http2ServerRequest as Http2ServerRequest2, constants as h2constants } from "http2";
15817
16299
  import { Http2ServerRequest } from "http2";
15818
- import { Readable } from "stream";
16300
+ import { Readable as Readable2 } from "stream";
15819
16301
  import crypto2 from "crypto";
15820
16302
  var RequestError = class extends Error {
15821
16303
  constructor(message, options) {
@@ -15882,7 +16364,7 @@ var newRequestFromIncoming = (method, url, headers, incoming, abortController) =
15882
16364
  init.body = new ReadableStream({
15883
16365
  async pull(controller) {
15884
16366
  try {
15885
- reader ||= Readable.toWeb(incoming).getReader();
16367
+ reader ||= Readable2.toWeb(incoming).getReader();
15886
16368
  const { done, value } = await reader.read();
15887
16369
  if (done) {
15888
16370
  controller.close();
@@ -15895,7 +16377,7 @@ var newRequestFromIncoming = (method, url, headers, incoming, abortController) =
15895
16377
  }
15896
16378
  });
15897
16379
  } else {
15898
- init.body = Readable.toWeb(incoming);
16380
+ init.body = Readable2.toWeb(incoming);
15899
16381
  }
15900
16382
  }
15901
16383
  return new Request2(url, init);
@@ -19795,7 +20277,7 @@ async function handleMemory(req, res, pathSegments, callerAgentId) {
19795
20277
  try {
19796
20278
  const provider = getEmbeddingProvider2();
19797
20279
  const store = getMemoryStore2();
19798
- const queryEmbedding = await provider.embed(query);
20280
+ const queryEmbedding = await provider.embed(query, { kind: "query" });
19799
20281
  const candidateLimit = Math.min(limit, 20) * CANDIDATE_SET_MULTIPLIER;
19800
20282
  const candidates = await searchMemoryCandidates(store, queryEmbedding ?? new Float32Array(0), myAgentId, {
19801
20283
  scope,
@@ -19876,7 +20358,7 @@ async function handleMemory(req, res, pathSegments, callerAgentId) {
19876
20358
  try {
19877
20359
  if (query && query.trim().length > 0) {
19878
20360
  const provider = getEmbeddingProvider2();
19879
- const queryEmbedding = await provider.embed(query.trim());
20361
+ const queryEmbedding = await provider.embed(query.trim(), { kind: "query" });
19880
20362
  const candidateLimit = Math.min(4096, Math.max(offset + pageLimit, pageLimit) * CANDIDATE_SET_MULTIPLIER);
19881
20363
  let candidates = await searchMemoryCandidates(store, queryEmbedding ?? new Float32Array(0), viewer.agentId, {
19882
20364
  scope,
@@ -20091,7 +20573,7 @@ async function handleMemory(req, res, pathSegments, callerAgentId) {
20091
20573
  });
20092
20574
  if (result.changed) {
20093
20575
  const provider = getEmbeddingProvider2();
20094
- const embedding = await provider.embed(result.memory.content);
20576
+ const embedding = await provider.embed(result.memory.content, { kind: "document" });
20095
20577
  if (embedding)
20096
20578
  await store.updateEmbedding(result.memory.id, embedding, provider.name);
20097
20579
  try {
@@ -20169,7 +20651,7 @@ async function handleMemory(req, res, pathSegments, callerAgentId) {
20169
20651
  for (let i = 0;i < memories.length; i += batchSize) {
20170
20652
  const batch = memories.slice(i, i + batchSize);
20171
20653
  try {
20172
- const embeddings = await provider.embedBatch(batch.map((m) => m.content));
20654
+ const embeddings = await provider.embedBatch(batch.map((m) => m.content), { kind: "document" });
20173
20655
  for (let j = 0;j < embeddings.length; j++) {
20174
20656
  if (embeddings[j]) {
20175
20657
  await store.updateEmbedding(batch[j].id, embeddings[j], provider.name);
@@ -21900,7 +22382,9 @@ init_db3();
21900
22382
  init_memory();
21901
22383
  init_constants();
21902
22384
  init_types();
22385
+ init_constants2();
21903
22386
  init_credentials();
22387
+ init_egress_guard();
21904
22388
  init_openrouter_base_url();
21905
22389
 
21906
22390
  // src/http/status.ts
@@ -22492,6 +22976,58 @@ async function handleStatus(req, res, pathSegments, queryParams) {
22492
22976
  }
22493
22977
 
22494
22978
  // src/http/onboarding.ts
22979
+ function isLocalEndpoint(baseUrl) {
22980
+ if (!baseUrl?.trim())
22981
+ return false;
22982
+ let host;
22983
+ try {
22984
+ host = new URL(baseUrl.trim()).hostname.toLowerCase().replace(/\.$/, "");
22985
+ } catch {
22986
+ return false;
22987
+ }
22988
+ if (!host)
22989
+ return false;
22990
+ if (host === "localhost" || host.endsWith(".localhost"))
22991
+ return true;
22992
+ if (isPrivateAddress(host))
22993
+ return true;
22994
+ return !host.includes(".") && !host.includes(":");
22995
+ }
22996
+ var PRESET_HOSTS = [
22997
+ ["openai", (host) => host === "api.openai.com"],
22998
+ ["openrouter", (host) => host === "openrouter.ai"],
22999
+ ["vercel", (host) => host === "ai-gateway.vercel.sh"],
23000
+ [
23001
+ "azure",
23002
+ (host) => [".services.ai.azure.com", ".openai.azure.com", ".cognitiveservices.azure.com"].some((suffix) => host.endsWith(suffix))
23003
+ ]
23004
+ ];
23005
+ function embeddingsProvider(baseUrl) {
23006
+ if (isLocalEndpoint(baseUrl))
23007
+ return "local";
23008
+ if (!baseUrl?.trim())
23009
+ return "openai";
23010
+ let host;
23011
+ try {
23012
+ host = new URL(baseUrl.trim()).hostname.toLowerCase().replace(/\.$/, "");
23013
+ } catch {
23014
+ return "custom";
23015
+ }
23016
+ return PRESET_HOSTS.find(([, matches]) => matches(host))?.[0] ?? "custom";
23017
+ }
23018
+ function deploymentEmbeddings() {
23019
+ return {
23020
+ baseUrl: getDeploymentEnvValue("EMBEDDING_API_BASE_URL"),
23021
+ model: getDeploymentEnvValue("EMBEDDING_MODEL") ?? "text-embedding-3-small",
23022
+ apiKey: getDeploymentEnvValue("EMBEDDING_API_KEY") ?? process.env.OPENAI_API_KEY
23023
+ };
23024
+ }
23025
+ function localDefault() {
23026
+ const deployment = deploymentEmbeddings();
23027
+ if (!isLocalEndpoint(deployment.baseUrl) || !deployment.apiKey)
23028
+ return null;
23029
+ return { model: deployment.model };
23030
+ }
22495
23031
  async function buildSignals(state) {
22496
23032
  const providers = [];
22497
23033
  for (const entry of await getAgentHarnessProviders2()) {
@@ -22512,11 +23048,16 @@ async function buildSignals(state) {
22512
23048
  const integrations = await getAutomationSetupStates();
22513
23049
  const agentCounts = await getReadyAgentCounts2();
22514
23050
  const firstTask = state.firstTaskId ? await getTaskById2(state.firstTaskId) : null;
23051
+ const embeddingsOn = getEmbeddingProvider2().isConfigured();
22515
23052
  return {
22516
23053
  providers,
22517
23054
  embeddings: {
22518
- configured: getEmbeddingProvider2().isConfigured(),
22519
- dimensions: EMBEDDING_DIMENSIONS
23055
+ configured: embeddingsOn,
23056
+ dimensions: EMBEDDING_DIMENSIONS,
23057
+ model: embeddingsOn ? process.env.EMBEDDING_MODEL ?? "text-embedding-3-small" : null,
23058
+ local: embeddingsOn && isLocalEndpoint(process.env.EMBEDDING_API_BASE_URL),
23059
+ provider: embeddingsOn ? embeddingsProvider(process.env.EMBEDDING_API_BASE_URL) : null,
23060
+ localDefault: localDefault()
22520
23061
  },
22521
23062
  integrations: {
22522
23063
  slack: integrations.slack === "configured",
@@ -22530,7 +23071,8 @@ async function buildSignals(state) {
22530
23071
  leadsOnline: agentCounts.leads,
22531
23072
  workersOnline: agentCounts.workers
22532
23073
  },
22533
- firstTask: firstTask ? { id: firstTask.id, status: firstTask.status } : null
23074
+ firstTask: firstTask ? { id: firstTask.id, status: firstTask.status } : null,
23075
+ files: { enabled: isCombEnabled() }
22534
23076
  };
22535
23077
  }
22536
23078
  var getOnboarding = route({
@@ -22627,19 +23169,34 @@ function memoryErrorMessage(errorClass, status) {
22627
23169
  return `The endpoint returned an error${statusSuffix}.`;
22628
23170
  }
22629
23171
  }
22630
- async function updateMemoryState(preset, result, saveConfig) {
22631
- const configSaved = await updateOnboardingMemory(preset, result, saveConfig);
22632
- if (configSaved)
22633
- scheduleIntegrationsReload();
23172
+ async function updateMemoryState(preset, result, change) {
23173
+ const configChanged = await updateOnboardingMemory(preset, result, change);
23174
+ if (!configChanged)
23175
+ return;
23176
+ if (change?.kind === "restore") {
23177
+ await loadGlobalConfigsIntoEnv(true);
23178
+ resetEmbeddingProvider2();
23179
+ }
23180
+ scheduleIntegrationsReload();
23181
+ }
23182
+ function prefixChanges(model) {
23183
+ const sameModel = model === getDeploymentEnvValue("EMBEDDING_MODEL");
23184
+ return EMBEDDING_PREFIX_KEYS.filter((key) => getDeploymentEnvValue(key)).map((key) => ({
23185
+ key,
23186
+ value: sameModel ? null : ""
23187
+ }));
22634
23188
  }
22635
23189
  async function handleMemoryProbe(body) {
22636
23190
  const explicitKey = body.apiKey;
22637
23191
  const preset = body.preset === "openai" || body.preset === "openrouter" || body.preset === "vercel" ? MEMORY_PRESETS[body.preset] : undefined;
22638
- const baseUrl = body.preset === "existing" ? process.env.EMBEDDING_API_BASE_URL : body.baseUrl ?? preset?.baseUrl ?? process.env.EMBEDDING_API_BASE_URL ?? "https://api.openai.com/v1";
22639
- const model = body.preset === "existing" ? process.env.EMBEDDING_MODEL ?? "text-embedding-3-small" : body.model ?? preset?.model ?? process.env.EMBEDDING_MODEL ?? "text-embedding-3-small";
23192
+ const restore = body.preset === "existing" && body.restoreDefault === true;
23193
+ const deployment = restore ? deploymentEmbeddings() : null;
23194
+ const baseUrl = body.preset === "existing" ? deployment ? deployment.baseUrl : process.env.EMBEDDING_API_BASE_URL : body.baseUrl ?? preset?.baseUrl ?? process.env.EMBEDDING_API_BASE_URL ?? "https://api.openai.com/v1";
23195
+ const model = body.preset === "existing" ? deployment?.model ?? process.env.EMBEDDING_MODEL ?? "text-embedding-3-small" : body.model ?? preset?.model ?? process.env.EMBEDDING_MODEL ?? "text-embedding-3-small";
23196
+ const configuredEndpoint = body.preset === "existing" && !!baseUrl && baseUrl === getDeploymentEnvValue("EMBEDDING_API_BASE_URL");
22640
23197
  let endpoint;
22641
23198
  try {
22642
- endpoint = assertUrlSafe(baseUrl ?? "", publicEndpointSsrfOptions());
23199
+ endpoint = configuredEndpoint ? new URL(baseUrl ?? "") : assertUrlSafe(baseUrl ?? "", publicEndpointSsrfOptions());
22643
23200
  } catch {
22644
23201
  return {
22645
23202
  ok: false,
@@ -22651,7 +23208,7 @@ async function handleMemoryProbe(body) {
22651
23208
  const reuseHost = body.reuseKey === "OPENAI_API_KEY" ? "api.openai.com" : body.reuseKey === "OPENROUTER_API_KEY" ? "openrouter.ai" : undefined;
22652
23209
  const canReuseKey = reuseHost === undefined || endpoint.hostname === reuseHost;
22653
23210
  const reusedKey = !explicitKey && body.reuseKey && canReuseKey ? process.env[body.reuseKey] : undefined;
22654
- const implicitKey = body.preset === "existing" ? process.env.EMBEDDING_API_KEY ?? process.env.OPENAI_API_KEY : undefined;
23211
+ const implicitKey = body.preset === "existing" ? deployment ? deployment.apiKey : process.env.EMBEDDING_API_KEY ?? process.env.OPENAI_API_KEY : undefined;
22655
23212
  const apiKey = splitCredentialPool(explicitKey ?? reusedKey ?? implicitKey ?? "")[0];
22656
23213
  if (!apiKey) {
22657
23214
  return {
@@ -22689,10 +23246,12 @@ async function handleMemoryProbe(body) {
22689
23246
  };
22690
23247
  }
22691
23248
  const keyToSave = explicitKey || reusedKey ? apiKey : undefined;
22692
- await updateMemoryState(body.preset, { ok: true }, body.preset === "existing" ? undefined : {
23249
+ await updateMemoryState(body.preset, { ok: true }, body.preset === "existing" ? restore ? { kind: "restore" } : undefined : {
23250
+ kind: "save",
22693
23251
  baseUrl: baseUrl ?? "https://api.openai.com/v1",
22694
23252
  model,
22695
- apiKey: keyToSave
23253
+ apiKey: keyToSave,
23254
+ prefixes: prefixChanges(model)
22696
23255
  });
22697
23256
  return { ok: true, dimensions, latencyMs };
22698
23257
  } catch (error) {
@@ -22971,6 +23530,7 @@ init_request_auth_context();
22971
23530
  init_zod();
22972
23531
  init_db3();
22973
23532
  init_model_profiles();
23533
+ init_rbac_roles();
22974
23534
  init_steering();
22975
23535
  init_users();
22976
23536
  init_rbac();
@@ -23147,6 +23707,7 @@ var createTask = route({
23147
23707
  path: "/api/tasks",
23148
23708
  pattern: ["api", "tasks"],
23149
23709
  session: bodyTask("parentTaskId"),
23710
+ rbac: { permission: "task.create.own" },
23150
23711
  summary: "Create a new task",
23151
23712
  tags: ["Tasks"],
23152
23713
  body: object2({
@@ -23253,6 +23814,7 @@ var cancelTaskRoute = route({
23253
23814
  path: "/api/tasks/{id}/cancel",
23254
23815
  pattern: ["api", "tasks", null, "cancel"],
23255
23816
  session: { taskParam: "id" },
23817
+ rbac: { permission: "task.cancel.own" },
23256
23818
  summary: "Cancel a pending or in-progress task",
23257
23819
  tags: ["Tasks"],
23258
23820
  params: object2({ id: string2() }),
@@ -23503,6 +24065,7 @@ var pauseTaskRoute = route({
23503
24065
  path: "/api/tasks/{id}/pause",
23504
24066
  pattern: ["api", "tasks", null, "pause"],
23505
24067
  session: { taskParam: "id" },
24068
+ rbac: { permission: "task.action.own" },
23506
24069
  summary: "Pause an in-progress task",
23507
24070
  tags: ["Tasks"],
23508
24071
  params: object2({ id: string2() }),
@@ -23518,6 +24081,7 @@ var resumeTaskRoute = route({
23518
24081
  path: "/api/tasks/{id}/resume",
23519
24082
  pattern: ["api", "tasks", null, "resume"],
23520
24083
  session: { taskParam: "id" },
24084
+ rbac: { permission: "task.action.own" },
23521
24085
  summary: "Resume a paused task",
23522
24086
  tags: ["Tasks"],
23523
24087
  params: object2({ id: string2() }),
@@ -23587,9 +24151,7 @@ var updateTaskTitleRoute = route({
23587
24151
  404: { description: "Task not found" }
23588
24152
  },
23589
24153
  auth: { apiKey: true },
23590
- rbac: {
23591
- ungated: "mirrors the pre-RBAC PATCH /api/tasks/{id}/vcs sibling route: bearer auth only"
23592
- }
24154
+ rbac: { permission: "task.action.own" }
23593
24155
  });
23594
24156
  async function canSteerTask(req, myAgentId, task, sessionAccess = "none") {
23595
24157
  const auth = getRequestAuth(req);
@@ -23659,6 +24221,34 @@ async function resolveTaskWritePrincipal(req, myAgentId) {
23659
24221
  const agent = await getAgentById2(agentId);
23660
24222
  return { kind: "agent", agentId, isLead: agent?.isLead === true };
23661
24223
  }
24224
+ async function mayBindChatThread(principal) {
24225
+ if (principal.kind === "operator")
24226
+ return true;
24227
+ if (principal.kind === "user") {
24228
+ if (!isRbacEnabled())
24229
+ return true;
24230
+ const grant = await getUserGrant2(principal.userId);
24231
+ return grant.grantsAll || grant.verbs.has("integration.chat.post");
24232
+ }
24233
+ return can({
24234
+ principal,
24235
+ verb: "integration.chat.post",
24236
+ resource: { kind: "none" },
24237
+ source: "http"
24238
+ }).allow;
24239
+ }
24240
+ async function deniesForeignContext(req, contextKey) {
24241
+ const auth = getRequestAuth(req);
24242
+ if (auth?.kind !== "user" || !isRbacEnabled())
24243
+ return false;
24244
+ if ((await getUserGrant2(auth.userId)).grantsAll)
24245
+ return false;
24246
+ for (const task of await getInProgressTasksByContextKey2(contextKey)) {
24247
+ if (await deniesNonOwnerTaskWrite2(req, task))
24248
+ return true;
24249
+ }
24250
+ return false;
24251
+ }
23662
24252
  async function resolveTaskRequester(req, myAgentId, bodyUserId, trustedUserId) {
23663
24253
  const trusted = trustedUserId === undefined ? await resolveHttpAuditUserId(req, myAgentId) : trustedUserId;
23664
24254
  if (trusted)
@@ -23774,8 +24364,12 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
23774
24364
  if (parentTask && await denyParentAttach2(req, res, myAgentId, parentTask, principal)) {
23775
24365
  return true;
23776
24366
  }
23777
- if (principal.kind === "agent" && parsed.body.contextKey && parsed.body.contextKey !== parentTask?.contextKey && chatThreadOfContextKey(parsed.body.contextKey) && !can({ principal, verb: "integration.chat.post", resource: { kind: "none" }, source: "http" }).allow) {
23778
- jsonError(res, "Permission denied: only a lead can bind a task to a chat thread.", 403);
24367
+ if (parsed.body.contextKey && parsed.body.contextKey !== parentTask?.contextKey && chatThreadOfContextKey(parsed.body.contextKey) && !await mayBindChatThread(principal)) {
24368
+ jsonError(res, "Permission denied: only a lead or an admin can bind a task to a chat thread.", 403);
24369
+ return true;
24370
+ }
24371
+ if (parsed.body.contextKey && parsed.body.contextKey !== parentTask?.contextKey && await deniesForeignContext(req, parsed.body.contextKey)) {
24372
+ jsonError(res, "You don't have access to the tasks in this context.", 403);
23779
24373
  return true;
23780
24374
  }
23781
24375
  let assetKey;
@@ -23895,6 +24489,10 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
23895
24489
  jsonError(res, "Task not found", 404);
23896
24490
  return true;
23897
24491
  }
24492
+ if (await deniesNonOwnerTaskWrite2(req, task)) {
24493
+ jsonError(res, NOT_YOUR_TASK_DENIED2, 403);
24494
+ return true;
24495
+ }
23898
24496
  const principal = await resolveTaskWritePrincipal(req, myAgentId);
23899
24497
  if (principal.kind === "agent" && !can({
23900
24498
  principal,
@@ -24386,6 +24984,10 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
24386
24984
  jsonError(res, "Task not found", 404);
24387
24985
  return true;
24388
24986
  }
24987
+ if (await deniesNonOwnerTaskWrite2(req, task)) {
24988
+ jsonError(res, NOT_YOUR_TASK_DENIED2, 403);
24989
+ return true;
24990
+ }
24389
24991
  if (myAgentId && task.agentId !== myAgentId) {
24390
24992
  jsonError(res, "Task belongs to another agent", 403);
24391
24993
  return true;
@@ -24446,6 +25048,15 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
24446
25048
  const parsed = await updateTaskTitleRoute.parse(req, res, pathSegments, queryParams);
24447
25049
  if (!parsed)
24448
25050
  return true;
25051
+ const existing = await getTaskById2(parsed.params.id);
25052
+ if (!existing) {
25053
+ jsonError(res, "Task not found", 404);
25054
+ return true;
25055
+ }
25056
+ if (await deniesNonOwnerTaskWrite2(req, existing)) {
25057
+ jsonError(res, NOT_YOUR_TASK_DENIED2, 403);
25058
+ return true;
25059
+ }
24449
25060
  const task = await updateTaskTitle2(parsed.params.id, parsed.body.title);
24450
25061
  if (!task) {
24451
25062
  jsonError(res, "Task not found", 404);
@@ -24463,6 +25074,10 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
24463
25074
  jsonError(res, "Task not found", 404);
24464
25075
  return true;
24465
25076
  }
25077
+ if (await deniesNonOwnerTaskWrite2(req, task)) {
25078
+ jsonError(res, NOT_YOUR_TASK_DENIED2, 403);
25079
+ return true;
25080
+ }
24466
25081
  if (myAgentId && task.agentId !== myAgentId) {
24467
25082
  jsonError(res, "Task belongs to another agent", 403);
24468
25083
  return true;
@@ -26840,7 +27455,26 @@ var LOCAL_PAGE_APP_URL = "http://localhost:5274";
26840
27455
  function getAppBaseUrl2() {
26841
27456
  return getAppUrl(LOCAL_PAGE_APP_URL);
26842
27457
  }
26843
- function buildCsp() {
27458
+ var FRAME_ANCESTOR_SOURCE_RE = /^(?:https?:\/\/)?(?:\*\.)?[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)*(?::(?:\d{1,5}|\*))?$/i;
27459
+ var cachedFrameAncestors = null;
27460
+ function getPageFrameAncestors() {
27461
+ const raw = process.env.PAGES_FRAME_ANCESTORS ?? "";
27462
+ if (cachedFrameAncestors?.raw === raw)
27463
+ return cachedFrameAncestors.origins;
27464
+ const origins = [];
27465
+ for (const entry of raw.split(/[\s,]+/)) {
27466
+ const value = entry.replace(/\/+$/, "");
27467
+ if (!value)
27468
+ continue;
27469
+ if (FRAME_ANCESTOR_SOURCE_RE.test(value))
27470
+ origins.push(value);
27471
+ else
27472
+ console.warn(`[pages] ignoring invalid PAGES_FRAME_ANCESTORS entry: ${entry}`);
27473
+ }
27474
+ cachedFrameAncestors = { raw, origins };
27475
+ return origins;
27476
+ }
27477
+ function buildCsp(page) {
26844
27478
  const configured = getConfiguredAppUrls();
26845
27479
  const devFallbacks = getNodeEnv() === "production" ? [] : [
26846
27480
  "http://localhost:5274",
@@ -26850,7 +27484,8 @@ function buildCsp() {
26850
27484
  "https://*.localhost",
26851
27485
  "http://*.localhost"
26852
27486
  ];
26853
- const ancestors = Array.from(new Set([...configured, ...devFallbacks]));
27487
+ const embedders = page.authMode === "public" ? getPageFrameAncestors() : [];
27488
+ const ancestors = Array.from(new Set([...configured, ...devFallbacks, ...embedders]));
26854
27489
  return [
26855
27490
  "default-src 'self'",
26856
27491
  "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.tailwindcss.com https://cdn.jsdelivr.net https://unpkg.com",
@@ -27016,7 +27651,7 @@ async function handlePagesPublic(req, res, pathSegments, queryParams) {
27016
27651
  const headers = {
27017
27652
  "Content-Type": "text/html; charset=utf-8",
27018
27653
  "Cache-Control": "no-store",
27019
- "Content-Security-Policy": buildCsp(),
27654
+ "Content-Security-Policy": buildCsp(page),
27020
27655
  "X-Content-Type-Options": "nosniff"
27021
27656
  };
27022
27657
  if (inlineSetCookie.length > 0)
@@ -27040,7 +27675,7 @@ async function handlePagesPublic(req, res, pathSegments, queryParams) {
27040
27675
  const headers = {
27041
27676
  "Content-Type": "text/html; charset=utf-8",
27042
27677
  "Cache-Control": "no-store",
27043
- "Content-Security-Policy": buildCsp(),
27678
+ "Content-Security-Policy": buildCsp(page),
27044
27679
  "X-Content-Type-Options": "nosniff"
27045
27680
  };
27046
27681
  if (inlineSetCookie.length > 0)
@@ -32852,7 +33487,8 @@ var upsertBodySchema = object2({
32852
33487
  description: string2().default(""),
32853
33488
  intent: string2().default(""),
32854
33489
  scope: ScriptScopeSchema.default("agent"),
32855
- fsMode: ScriptFsModeSchema.default("none")
33490
+ fsMode: ScriptFsModeSchema.default("none"),
33491
+ executor: ScriptExecutorNameSchema.nullable().optional()
32856
33492
  });
32857
33493
  var runBodySchema = object2({
32858
33494
  name: scriptNameSchema.optional(),
@@ -32861,6 +33497,7 @@ var runBodySchema = object2({
32861
33497
  intent: string2().default(""),
32862
33498
  scope: ScriptScopeSchema.optional(),
32863
33499
  fsMode: ScriptFsModeSchema.default("none"),
33500
+ executor: ScriptExecutorNameSchema.nullable().optional(),
32864
33501
  idempotencyKey: string2().max(200).optional()
32865
33502
  }).refine((body) => Boolean(body.name) !== Boolean(body.source), {
32866
33503
  message: "Provide exactly one of name or source"
@@ -33309,6 +33946,10 @@ async function handleScripts(req, res, pathSegments, queryParams, agentId) {
33309
33946
  return true;
33310
33947
  }
33311
33948
  }
33949
+ if (parsed.body.executor && !isExecutorAllowedByDeployment(parsed.body.executor)) {
33950
+ jsonError(res, 'Executor "native" is not permitted when deployment SCRIPT_EXECUTOR="quickjs"', 400);
33951
+ return true;
33952
+ }
33312
33953
  const secretRefusal = refuseSourceWithSecrets(parsed.body.source);
33313
33954
  if (secretRefusal) {
33314
33955
  json(res, secretRefusal, 400);
@@ -33336,6 +33977,7 @@ async function handleScripts(req, res, pathSegments, queryParams, agentId) {
33336
33977
  signatureJson: signatureJsonFor(parsed.body.source),
33337
33978
  argsJsonSchema,
33338
33979
  fsMode: parsed.body.fsMode,
33980
+ executor: parsed.body.executor,
33339
33981
  agentId: agent.id,
33340
33982
  isScratch: false,
33341
33983
  typeChecked: true,
@@ -33373,6 +34015,7 @@ async function handleScripts(req, res, pathSegments, queryParams, agentId) {
33373
34015
  }
33374
34016
  let source = parsed.body.source;
33375
34017
  let fsMode = parsed.body.fsMode;
34018
+ let executor = parsed.body.executor ?? null;
33376
34019
  let namedScript = null;
33377
34020
  if (parsed.body.name) {
33378
34021
  const script = await resolveScript(parsed.body.name, agent.id, parsed.body.scope);
@@ -33383,11 +34026,16 @@ async function handleScripts(req, res, pathSegments, queryParams, agentId) {
33383
34026
  namedScript = script;
33384
34027
  source = script.source;
33385
34028
  fsMode = script.fsMode;
34029
+ executor = parsed.body.executor !== undefined ? parsed.body.executor : script.executor;
33386
34030
  }
33387
34031
  if (fsMode === "workspace-rw") {
33388
34032
  jsonError(res, "workspace-rw scripts are not supported by /api/scripts/run in v1", 501);
33389
34033
  return true;
33390
34034
  }
34035
+ if (executor && !isExecutorAllowedByDeployment(executor)) {
34036
+ jsonError(res, 'Executor "native" is not permitted when deployment SCRIPT_EXECUTOR="quickjs"', 400);
34037
+ return true;
34038
+ }
33391
34039
  const startedAt = new Date().toISOString();
33392
34040
  const runStartTouch = namedScript?.isScratch ? await touchScratchScriptLastUsed(namedScript.id) : null;
33393
34041
  const credentials = await buildScriptCredentialBindingsWithFailures({ agentId: agent.id });
@@ -33395,6 +34043,7 @@ async function handleScripts(req, res, pathSegments, queryParams, agentId) {
33395
34043
  source,
33396
34044
  args: parsed.body.args,
33397
34045
  fsMode,
34046
+ executor,
33398
34047
  agentId: agent.id,
33399
34048
  runtimeInstanceId,
33400
34049
  egressSecrets: credentials.egressSecrets,
@@ -33523,6 +34172,7 @@ async function handleScripts(req, res, pathSegments, queryParams, agentId) {
33523
34172
  isScratch: script.isScratch,
33524
34173
  typeChecked: script.typeChecked,
33525
34174
  fsMode: script.fsMode,
34175
+ executor: script.executor,
33526
34176
  createdByAgentId: script.createdByAgentId,
33527
34177
  createdAt: script.createdAt,
33528
34178
  updatedAt: script.updatedAt
@@ -34775,7 +35425,7 @@ var triageSessionListRoute = route({
34775
35425
  401: { description: "Unauthorized" }
34776
35426
  },
34777
35427
  auth: { apiKey: true },
34778
- rbac: { ungated: "per-user inbox UI state, same posture as PATCH /api/inbox-state" }
35428
+ rbac: { permission: "favorite.write.own" }
34779
35429
  });
34780
35430
  var SESSION_MOVE_LIMIT = 200;
34781
35431
  var SESSION_MOVE_OWNER_ONLY = "Only the session owner can move this group";
@@ -34784,9 +35434,7 @@ var SessionMoveResultSchema = object2({
34784
35434
  moved: array2(string2()),
34785
35435
  skipped: array2(object2({ rootTaskId: string2(), reason: string2() }))
34786
35436
  });
34787
- var sessionMoveRbac = {
34788
- ungated: "per-session access, per-task task.fs.mutate, and asset-key write checks in the handler, as PATCH /api/assets/task/{id}/key"
34789
- };
35437
+ var sessionMoveRbac = { permission: "task.fs.mutate" };
34790
35438
  var sessionMoveResponses = {
34791
35439
  400: { description: "Invalid namespace or request" },
34792
35440
  403: { description: "Personal destination not authorized" },
@@ -34858,6 +35506,8 @@ async function checkSessionMove(rootTaskId, actor, req, myAgentId, options = {})
34858
35506
  return { reason: SESSION_MOVE_UNAVAILABLE };
34859
35507
  if (!hasSessionAccess(access, "write"))
34860
35508
  return { reason: "Session write access required" };
35509
+ if (await deniesNonOwnerTaskWrite2(req, root))
35510
+ return { reason: NOT_YOUR_TASK_DENIED2 };
34861
35511
  }
34862
35512
  if (root.parentTaskId)
34863
35513
  return { reason: "Task is not a session root" };
@@ -36945,7 +37595,7 @@ var uiEvent = route({
36945
37595
  401: { description: "Unauthorized" },
36946
37596
  403: { description: "Guest page sessions cannot send events" }
36947
37597
  },
36948
- rbac: { ungated: "any authenticated dashboard user; event name allowlisted" }
37598
+ rbac: { permission: "favorite.write.own" }
36949
37599
  });
36950
37600
  var enableTelemetry = route({
36951
37601
  method: "post",
@@ -37869,76 +38519,11 @@ init_zod();
37869
38519
  init_db3();
37870
38520
  init_rbac_roles();
37871
38521
  init_users();
38522
+ init_rbac();
37872
38523
  init_types();
38524
+ init_constants2();
38525
+ init_egress_guard();
37873
38526
  init_request_auth_context();
37874
-
37875
- // src/http/ip-rate-limit.ts
37876
- var PRUNE_THRESHOLD = 1e4;
37877
- var PRUNE_INTERVAL_MS = 60000;
37878
- var MAX_BUCKETS = 50000;
37879
- function isPrivateAddress(address) {
37880
- const ip = address.startsWith("::ffff:") ? address.slice(7) : address;
37881
- if (ip === "::1" || ip.startsWith("127.") || ip.startsWith("10."))
37882
- return true;
37883
- if (ip.startsWith("192.168.") || ip.startsWith("169.254."))
37884
- return true;
37885
- const match = /^172\.(\d+)\./.exec(ip);
37886
- if (match && Number(match[1]) >= 16 && Number(match[1]) <= 31)
37887
- return true;
37888
- const lower = ip.toLowerCase();
37889
- return lower.startsWith("fc") || lower.startsWith("fd") || lower.startsWith("fe80:");
37890
- }
37891
- function clientIp(req) {
37892
- const peer = req.socket.remoteAddress ?? "unknown";
37893
- if (!isPrivateAddress(peer))
37894
- return peer;
37895
- const raw = req.headers["x-forwarded-for"];
37896
- const header = Array.isArray(raw) ? raw[raw.length - 1] : raw;
37897
- const hop = header?.split(",").pop()?.trim();
37898
- return hop || peer;
37899
- }
37900
- function createIpRateLimiter(options) {
37901
- const buckets = new Map;
37902
- let lastPruneAt = 0;
37903
- function refill(bucket, now) {
37904
- const elapsed = now - bucket.updatedAt;
37905
- bucket.tokens = Math.min(options.capacity, bucket.tokens + elapsed * options.refillPerMs);
37906
- bucket.updatedAt = now;
37907
- }
37908
- return {
37909
- take(ip, now = Date.now()) {
37910
- if (buckets.size > PRUNE_THRESHOLD && now - lastPruneAt >= PRUNE_INTERVAL_MS) {
37911
- lastPruneAt = now;
37912
- for (const [key, bucket] of buckets) {
37913
- refill(bucket, now);
37914
- if (bucket.tokens >= options.capacity)
37915
- buckets.delete(key);
37916
- }
37917
- }
37918
- let bucket = buckets.get(ip);
37919
- if (!bucket) {
37920
- if (buckets.size >= MAX_BUCKETS) {
37921
- const oldest = buckets.keys().next().value;
37922
- if (oldest !== undefined)
37923
- buckets.delete(oldest);
37924
- }
37925
- bucket = { tokens: options.capacity, updatedAt: now };
37926
- buckets.set(ip, bucket);
37927
- }
37928
- refill(bucket, now);
37929
- if (bucket.tokens < 1)
37930
- return false;
37931
- bucket.tokens -= 1;
37932
- return true;
37933
- },
37934
- reset() {
37935
- buckets.clear();
37936
- lastPruneAt = 0;
37937
- }
37938
- };
37939
- }
37940
-
37941
- // src/http/users.ts
37942
38527
  async function composeUser(userId, recentEventLimit = 5) {
37943
38528
  const user = await getUserById2(userId);
37944
38529
  if (!user)
@@ -38010,7 +38595,12 @@ var whoamiRoute = route({
38010
38595
  description: "Authenticated principal",
38011
38596
  schema: object2({
38012
38597
  kind: _enum2(["operator", "user"]),
38013
- user: union2([UserSchema, _null()])
38598
+ user: union2([UserSchema, _null()]),
38599
+ grants: object2({
38600
+ grantsAll: boolean(),
38601
+ verbs: array2(string2()),
38602
+ roles: array2(string2())
38603
+ }).optional()
38014
38604
  })
38015
38605
  },
38016
38606
  401: { description: "Unauthorized" }
@@ -38259,6 +38849,72 @@ var exchangeConnectorCodeRoute = route({
38259
38849
  ungated: "the single-use code is the credential; the minted token carries the user's own grant and is admitted on use"
38260
38850
  }
38261
38851
  });
38852
+ var INVITE_ROLES = ["requester", "viewer", "admin"];
38853
+ var createInviteRoute = route({
38854
+ method: "post",
38855
+ path: "/api/invites",
38856
+ pattern: ["api", "invites"],
38857
+ summary: "Invite a teammate with a one-time API link",
38858
+ description: "Creates an invited user with the given role (requester by default) and returns a 7-day single-use API link. The API redirects to the current APP_URL dashboard with the code in the URL fragment. The browser exchanges the code at POST /api/invites/exchange for a personal MCP token. Re-inviting an invited user replaces the role and link. Active or suspended users return 409 user_exists. No email is sent.",
38859
+ tags: ["Users"],
38860
+ body: object2({
38861
+ email: email(),
38862
+ name: string2().trim().min(1).max(200).optional(),
38863
+ role: _enum2(INVITE_ROLES).default("requester")
38864
+ }),
38865
+ responses: {
38866
+ 201: {
38867
+ description: "The invited user, API link, dashboard URL, expiry and API reachability",
38868
+ schema: object2({
38869
+ user: ComposedUserSchema.nullable(),
38870
+ inviteUrl: string2(),
38871
+ appUrl: string2(),
38872
+ expiresAt: string2(),
38873
+ apiReachable: _enum2(["public", "local"])
38874
+ })
38875
+ },
38876
+ 401: { description: "Unauthorized" },
38877
+ 403: { description: "Not allowed to manage roles" },
38878
+ 409: { description: "The email belongs to an active or suspended user" }
38879
+ },
38880
+ auth: { apiKey: true },
38881
+ rbac: { permission: "user.role.manage" }
38882
+ });
38883
+ var openInviteRoute = route({
38884
+ method: "get",
38885
+ path: "/invite/{code}",
38886
+ pattern: ["invite", null],
38887
+ summary: "Open an invite in the configured dashboard",
38888
+ description: "Redirects to the current APP_URL dashboard with the code in the URL fragment. Does not validate or consume the invite.",
38889
+ tags: ["Users"],
38890
+ params: object2({ code: string2() }),
38891
+ responses: {
38892
+ 302: { description: "Redirect to the dashboard", unstructured: "Dashboard redirect" },
38893
+ 404: { description: "Malformed code (code_invalid)" }
38894
+ },
38895
+ auth: { apiKey: false }
38896
+ });
38897
+ var exchangeInviteCodeRoute = route({
38898
+ method: "post",
38899
+ path: "/api/invites/exchange",
38900
+ pattern: ["api", "invites", "exchange"],
38901
+ summary: "Exchange an invite code for the invited user's MCP token",
38902
+ description: "Unauthenticated: the single-use invite code is the credential. Rate limited per IP. Activates the invited user. Unknown, expired, used and connector codes, and codes for users who are no longer invited, all return 404 code_invalid.",
38903
+ tags: ["Users"],
38904
+ body: object2({ code: string2().min(1).max(256) }),
38905
+ maxBodyBytes: 1024,
38906
+ responses: {
38907
+ 200: {
38908
+ description: "Minted MCP token for the invited user",
38909
+ schema: object2({ token: string2(), userId: string2() })
38910
+ },
38911
+ 404: { description: "Code unknown, malformed, expired, already used or not an invite" },
38912
+ 413: { description: "Request body too large" },
38913
+ 429: { description: "Rate limited" }
38914
+ },
38915
+ auth: { apiKey: false },
38916
+ rbac: { ungated: "the one-time invite code is the credential" }
38917
+ });
38262
38918
  var connectorDiscoveryRoute = route({
38263
38919
  method: "get",
38264
38920
  path: "/api/connector/discovery",
@@ -38439,6 +39095,26 @@ var connectorExchangeRateLimiter = createIpRateLimiter({
38439
39095
  capacity: 10,
38440
39096
  refillPerMs: 10 / 60000
38441
39097
  });
39098
+ var inviteExchangeRateLimiter = createIpRateLimiter({
39099
+ capacity: 10,
39100
+ refillPerMs: 10 / 60000
39101
+ });
39102
+ function apiReachability(apiUrl) {
39103
+ let host;
39104
+ try {
39105
+ host = assertUrlSafe(apiUrl, { allowInsecure: true }).hostname.toLowerCase();
39106
+ } catch {
39107
+ return "local";
39108
+ }
39109
+ if (!host.includes(".") && !host.includes(":"))
39110
+ return "local";
39111
+ if (/\.(local|internal|lan|home\.arpa)\.?$/.test(host))
39112
+ return "local";
39113
+ return "public";
39114
+ }
39115
+
39116
+ class InviteUserExistsError extends Error {
39117
+ }
38442
39118
  async function handleUsers(req, res, pathSegments, queryParams) {
38443
39119
  if (whoamiRoute.match(req.method, pathSegments)) {
38444
39120
  const parsed = await whoamiRoute.parse(req, res, pathSegments, queryParams);
@@ -38446,9 +39122,26 @@ async function handleUsers(req, res, pathSegments, queryParams) {
38446
39122
  return true;
38447
39123
  const auth = getRequestAuth(req);
38448
39124
  if (auth?.kind === "user") {
38449
- whoamiRoute.respond(res, 200, { kind: "user", user: auth.user });
39125
+ const [grant, roles] = await Promise.all([
39126
+ getUserGrant2(auth.user.id),
39127
+ listUserRoles2(auth.user.id)
39128
+ ]);
39129
+ const grantsAll = !isRbacEnabled() || grant.grantsAll;
39130
+ whoamiRoute.respond(res, 200, {
39131
+ kind: "user",
39132
+ user: auth.user,
39133
+ grants: {
39134
+ grantsAll,
39135
+ verbs: grantsAll ? [] : [...grant.verbs].sort(),
39136
+ roles: roles.map((role) => role.name)
39137
+ }
39138
+ });
38450
39139
  } else {
38451
- whoamiRoute.respond(res, 200, { kind: "operator", user: null });
39140
+ whoamiRoute.respond(res, 200, {
39141
+ kind: "operator",
39142
+ user: null,
39143
+ grants: { grantsAll: true, verbs: [], roles: [] }
39144
+ });
38452
39145
  }
38453
39146
  return true;
38454
39147
  }
@@ -38652,6 +39345,110 @@ async function handleUsers(req, res, pathSegments, queryParams) {
38652
39345
  }
38653
39346
  return true;
38654
39347
  }
39348
+ if (createInviteRoute.match(req.method, pathSegments)) {
39349
+ const parsed = await createInviteRoute.parse(req, res, pathSegments, queryParams);
39350
+ if (!parsed)
39351
+ return true;
39352
+ if (!await ensureRoleManager(req, res))
39353
+ return true;
39354
+ const actor = getOperatorActor(req, res);
39355
+ if (!actor)
39356
+ return true;
39357
+ const { email: email2, name, role } = parsed.body;
39358
+ const actorUserId = actor.kind === "user" ? actor.id : null;
39359
+ let invited;
39360
+ try {
39361
+ invited = await getDbClient2().transaction(async () => {
39362
+ const existing = await findUserByEmail(email2);
39363
+ if (existing && existing.status !== "invited")
39364
+ throw new InviteUserExistsError;
39365
+ let userId;
39366
+ if (existing) {
39367
+ userId = existing.id;
39368
+ await deleteUnusedInviteCodes(userId);
39369
+ } else {
39370
+ const user = await createUser2({
39371
+ name: name ?? (email2.split("@")[0] || email2),
39372
+ email: email2,
39373
+ status: "invited"
39374
+ });
39375
+ userId = user.id;
39376
+ await recordIdentityEvent(userId, "identity_added", actor, null, {
39377
+ email: email2,
39378
+ source: "invite"
39379
+ });
39380
+ }
39381
+ const { before, after } = await setUserRoles2(userId, [role], actorUserId);
39382
+ if (before.join(",") !== after.join(",")) {
39383
+ await recordIdentityEvent(userId, "profile_changed", actor, { rbacRoles: before }, { rbacRoles: after });
39384
+ }
39385
+ const { code, expiresAt } = await createConnectorCode(userId, DEFAULT_INVITE_CODE_LABEL, actor, new Date, { kind: "invite", ttlMs: INVITE_CODE_TTL_MS });
39386
+ return { userId, code, expiresAt };
39387
+ });
39388
+ } catch (err) {
39389
+ if (err instanceof InviteUserExistsError) {
39390
+ jsonError(res, "user_exists", 409);
39391
+ return true;
39392
+ }
39393
+ jsonError(res, "Failed to create invite", 500);
39394
+ return true;
39395
+ }
39396
+ const apiUrl = await resolveMcpBaseUrl();
39397
+ const appUrl = await resolveAppUrl() ?? DEFAULT_APP_URL;
39398
+ const inviteUrl = `${apiUrl}/invite/${invited.code}`;
39399
+ createInviteRoute.respond(res, 201, {
39400
+ user: await composeUser(invited.userId),
39401
+ inviteUrl,
39402
+ appUrl,
39403
+ expiresAt: invited.expiresAt,
39404
+ apiReachable: apiReachability(apiUrl)
39405
+ });
39406
+ return true;
39407
+ }
39408
+ if (openInviteRoute.match(req.method, pathSegments)) {
39409
+ const parsed = await openInviteRoute.parse(req, res, pathSegments, queryParams);
39410
+ if (!parsed)
39411
+ return true;
39412
+ const { code } = parsed.params;
39413
+ if (!/^[A-Za-z0-9_-]{16,256}$/.test(code)) {
39414
+ jsonError(res, "code_invalid", 404);
39415
+ return true;
39416
+ }
39417
+ const apiUrl = await resolveMcpBaseUrl();
39418
+ const appUrl = await resolveAppUrl() ?? DEFAULT_APP_URL;
39419
+ res.writeHead(302, {
39420
+ Location: `${appUrl}/join?swarm=${encodeURIComponent(apiUrl)}#code=${code}`,
39421
+ "Cache-Control": "no-store",
39422
+ "Referrer-Policy": "no-referrer"
39423
+ });
39424
+ res.end();
39425
+ return true;
39426
+ }
39427
+ if (exchangeInviteCodeRoute.match(req.method, pathSegments)) {
39428
+ if (!inviteExchangeRateLimiter.take(clientIp(req))) {
39429
+ res.setHeader("Retry-After", "6");
39430
+ jsonError(res, "rate_limited", 429);
39431
+ return true;
39432
+ }
39433
+ const parsed = await exchangeInviteCodeRoute.parse(req, res, pathSegments, queryParams);
39434
+ if (!parsed)
39435
+ return true;
39436
+ if (!CONNECTOR_CODE_PATTERN.test(parsed.body.code)) {
39437
+ jsonError(res, "code_invalid", 404);
39438
+ return true;
39439
+ }
39440
+ try {
39441
+ const result = await exchangeConnectorCode(parsed.body.code, { kind: "invite" });
39442
+ if (!result) {
39443
+ jsonError(res, "code_invalid", 404);
39444
+ return true;
39445
+ }
39446
+ exchangeInviteCodeRoute.respond(res, 200, result);
39447
+ } catch {
39448
+ jsonError(res, "Failed to exchange code", 500);
39449
+ }
39450
+ return true;
39451
+ }
38655
39452
  if (revokeUserMcpTokenRoute.match(req.method, pathSegments)) {
38656
39453
  const parsed = await revokeUserMcpTokenRoute.parse(req, res, pathSegments, queryParams);
38657
39454
  if (!parsed)
@@ -40006,63 +40803,6 @@ init_db3();
40006
40803
  init_workflow_idempotency();
40007
40804
  init_workflow_runs();
40008
40805
  init_types();
40009
-
40010
- // src/http/idempotency-key.ts
40011
- init_zod();
40012
- init_workflow_idempotency();
40013
- init_request_auth_context();
40014
- var IDEMPOTENCY_KEY_PATTERN = /^[\x21-\x7E]{1,255}$/;
40015
- var IDEMPOTENT_REPLAYED_HEADER = "Idempotent-Replayed";
40016
- var idempotencyKeyHeader = (accepted) => object2({
40017
- "Idempotency-Key": string2().regex(IDEMPOTENCY_KEY_PATTERN).optional().describe("Optional client-generated key, 1-255 visible ASCII characters (a UUID works). " + `The first request the server accepts under a key ${accepted}; a retry with the same key ` + "and an equivalent body gets that request's status and body back (with " + `\`${IDEMPOTENT_REPLAYED_HEADER}: true\`) and changes nothing. The key is scoped to the ` + "caller's identity and this operation; it is not a credential. The same key with a " + "different body is a 409. A request rejected before it is accepted (validation, auth, " + "not found, disabled) does not use up the key. Accepted keys do not expire.")
40018
- });
40019
- function idempotencyPrincipal(req, myAgentId) {
40020
- const auth = getRequestAuth(req);
40021
- switch (auth?.kind) {
40022
- case "user":
40023
- return `user:${auth.userId}`;
40024
- case "agent":
40025
- return `agent:${auth.agentId}`;
40026
- case "controller":
40027
- return `controller:${auth.controllerId}`;
40028
- case "guest":
40029
- return null;
40030
- default:
40031
- return myAgentId ? `agent:${myAgentId}` : "operator";
40032
- }
40033
- }
40034
- function readIdempotencyKey(req, res, myAgentId) {
40035
- const raw = req.headers["idempotency-key"];
40036
- if (raw === undefined)
40037
- return;
40038
- const key = Array.isArray(raw) ? raw.join(", ") : raw;
40039
- if (!IDEMPOTENCY_KEY_PATTERN.test(key)) {
40040
- jsonError(res, "Idempotency-Key must be 1-255 visible ASCII characters", 400);
40041
- return null;
40042
- }
40043
- const principal = idempotencyPrincipal(req, myAgentId);
40044
- if (!principal) {
40045
- jsonError(res, "Idempotency-Key needs an identified caller", 400);
40046
- return null;
40047
- }
40048
- return { principal, key };
40049
- }
40050
- async function replayAcceptedOperation(res, claim, send) {
40051
- const accepted = await findAcceptedWorkflowOperation(claim);
40052
- if (!accepted)
40053
- return false;
40054
- if (accepted.fingerprint !== claim.fingerprint) {
40055
- jsonError(res, "Idempotency-Key was already used for a different request", 409);
40056
- } else if (accepted.resourceDeletedAt !== null) {
40057
- jsonError(res, "The workflow this Idempotency-Key created or triggered was deleted; send a new key to start over", 410);
40058
- } else {
40059
- res.setHeader(IDEMPOTENT_REPLAYED_HEADER, "true");
40060
- send(accepted.body);
40061
- }
40062
- return true;
40063
- }
40064
-
40065
- // src/http/workflows.ts
40066
40806
  var WorkflowWithFavoriteSchema = WorkflowSchema.extend({ favorite: boolean() });
40067
40807
  var WorkflowSaveResponseSchema = WorkflowSchema.extend({
40068
40808
  warnings: array2(string2()).optional()
@@ -40256,6 +40996,9 @@ var triggerWorkflowRoute = route({
40256
40996
  description: "With an `Idempotency-Key`, a retry returns the accepted run's `runId` and `skipped` without starting, resuming or waiting for anything, even after the workflow was disabled or edited and whatever state the run is in now.",
40257
40997
  tags: ["Workflows"],
40258
40998
  params: object2({ id: string2() }),
40999
+ query: object2({
41000
+ wait: _enum2(["true", "false"]).optional().describe("Default true: respond after the run's instant steps ran, when it finishes or parks at its first async step. " + "false: respond as soon as the run is committed and walk it in the background.")
41001
+ }),
40259
41002
  headers: idempotencyKeyHeader("starts one run"),
40260
41003
  responses: {
40261
41004
  201: {
@@ -40854,6 +41597,7 @@ async function handleWorkflows(req, res, pathSegments, queryParams, myAgentId) {
40854
41597
  runId = await startWorkflowExecution2(workflow, body, getExecutorRegistry2(), {
40855
41598
  triggerType: "api",
40856
41599
  requestedByUserId: await resolveHttpAuditUserId(req, myAgentId) ?? undefined,
41600
+ detach: parsed.query.wait === "false",
40857
41601
  accept: claim ? async (run) => {
40858
41602
  const response = { runId: run.runId, skipped: run.skipped };
40859
41603
  await recordAcceptedWorkflowOperation(claim, {
@@ -41098,6 +41842,7 @@ async function handleX(req, res, pathSegments) {
41098
41842
  jsonError(res, "workspace-rw scripts are not supported", 501);
41099
41843
  return true;
41100
41844
  }
41845
+ let hostValidatedArgs = false;
41101
41846
  if (script.argsJsonSchema) {
41102
41847
  try {
41103
41848
  const schema = JSON.parse(script.argsJsonSchema);
@@ -41111,6 +41856,7 @@ async function handleX(req, res, pathSegments) {
41111
41856
  });
41112
41857
  return true;
41113
41858
  }
41859
+ hostValidatedArgs = true;
41114
41860
  } catch {}
41115
41861
  }
41116
41862
  const timeoutMs = resolveTimeoutMs(req);
@@ -41122,7 +41868,9 @@ async function handleX(req, res, pathSegments) {
41122
41868
  const output = await runScriptWithRunCredential({
41123
41869
  source: script.source,
41124
41870
  args: args ?? null,
41871
+ [HOST_VALIDATED_ARGS]: hostValidatedArgs,
41125
41872
  fsMode: "none",
41873
+ executor: script.executor,
41126
41874
  agentId: endpoint.agentId,
41127
41875
  timeoutMs,
41128
41876
  egressSecrets: credentials.egressSecrets,
@@ -41255,7 +42003,7 @@ var httpServer = createHttpServer(async (req, res) => {
41255
42003
  const semconv = httpServerSemconvAttributes(req);
41256
42004
  const span = skipSpan ? null : startSpan2(spanName, {
41257
42005
  "http.request.method": req.method ?? "",
41258
- "url.path": reqPath,
42006
+ "url.path": redactShareTokenInPath(reqPath),
41259
42007
  "url.scheme": semconv["url.scheme"],
41260
42008
  "http.route": httpRoute,
41261
42009
  "server.address": semconv["server.address"],
@@ -41299,6 +42047,7 @@ var httpServer = createHttpServer(async (req, res) => {
41299
42047
  () => handlePoolKeys(req, res, pathSegments, queryParams),
41300
42048
  () => handleConfig(req, res, pathSegments, queryParams),
41301
42049
  () => handleFs(req, res, pathSegments, queryParams, myAgentId),
42050
+ () => handleAgentFsProxy(req, res),
41302
42051
  () => handleComb(req, res, pathSegments, queryParams, myAgentId),
41303
42052
  () => handleKv(req, res, pathSegments, queryParams),
41304
42053
  () => handleRooms(req, res, pathSegments, queryParams),
@@ -41373,7 +42122,7 @@ var httpServer = createHttpServer(async (req, res) => {
41373
42122
  });
41374
42123
  });
41375
42124
  var detachRealtime = attachRealtimeTransport(httpServer);
41376
- var { attachControllerTransport } = await import("./controllers-connect-r2za39ef.js");
42125
+ var { attachControllerTransport } = await import("./controllers-connect-ajvh9h0v.js");
41377
42126
  var detachControllers = attachControllerTransport(httpServer);
41378
42127
  var stopWorkerNudges = startWorkerNudgeListener();
41379
42128
  var stopRoomDeletion = realtimeBus.subscribe("room:namespace-deleted", (namespace) => {
@@ -41402,7 +42151,7 @@ async function shutdown() {
41402
42151
  sessionsProcessed: getServerSessionsProcessed()
41403
42152
  });
41404
42153
  if (hasCapability("scheduling")) {
41405
- const { stopScheduler } = await import("./index-38t35h0n.js");
42154
+ const { stopScheduler } = await import("./index-ch0c1xff.js");
41406
42155
  stopScheduler();
41407
42156
  }
41408
42157
  stopHeartbeat2();
@@ -41413,8 +42162,8 @@ async function shutdown() {
41413
42162
  await stopScriptRunSupervisor();
41414
42163
  await stopSlackApp2();
41415
42164
  await stopChatSdk();
41416
- if (process.env.OAUTH_KEEPALIVE_DISABLE !== "true") {
41417
- const { stopOAuthKeepalive } = await import("./keepalive-em0117bp.js");
42165
+ if (!isEnvFlagEnabled("OAUTH_KEEPALIVE_DISABLE", false)) {
42166
+ const { stopOAuthKeepalive } = await import("./keepalive-zzc3hks1.js");
41418
42167
  await stopOAuthKeepalive();
41419
42168
  }
41420
42169
  stopOAuthPendingGc();
@@ -41494,21 +42243,21 @@ try {
41494
42243
  console.warn("[startup] CAPABILITIES upgrade seed failed (non-fatal):", err);
41495
42244
  }
41496
42245
  try {
41497
- const { ensurePricingSeeded } = await import("./boot-seeds-9bmkspyw.js");
42246
+ const { ensurePricingSeeded } = await import("./boot-seeds-r3kwwv8b.js");
41498
42247
  ensurePricingSeeded();
41499
- const { startPricingRefreshLoop } = await import("./pricing-refresh-yb75wv33.js");
42248
+ const { startPricingRefreshLoop } = await import("./pricing-refresh-wkpwhgvs.js");
41500
42249
  startPricingRefreshLoop();
41501
42250
  } catch (err) {
41502
42251
  console.error("[startup] Failed to seed pricing rows:", err);
41503
42252
  }
41504
42253
  try {
41505
- const { ensureModelProfilesSeeded } = await import("./boot-seeds-9bmkspyw.js");
42254
+ const { ensureModelProfilesSeeded } = await import("./boot-seeds-r3kwwv8b.js");
41506
42255
  await ensureModelProfilesSeeded();
41507
42256
  } catch (err) {
41508
42257
  console.error("[startup] Failed to seed model profiles:", err);
41509
42258
  }
41510
42259
  try {
41511
- const { ensureRbacSeeded } = await import("./boot-seeds-9bmkspyw.js");
42260
+ const { ensureRbacSeeded } = await import("./boot-seeds-r3kwwv8b.js");
41512
42261
  ensureRbacSeeded();
41513
42262
  } catch (err) {
41514
42263
  console.error("[startup] Failed to sync RBAC seed rows:", err);
@@ -41516,7 +42265,7 @@ try {
41516
42265
  throw err;
41517
42266
  }
41518
42267
  try {
41519
- const { runAllSeeders } = await import("./index-qhjf2htm.js");
42268
+ const { runAllSeeders } = await import("./index-ejqdkkqx.js");
41520
42269
  await runAllSeeders({ scriptEmbeddingMode: "skip" });
41521
42270
  } catch (err) {
41522
42271
  console.error("[startup] Failed to seed built-in entities:", err);
@@ -41579,42 +42328,42 @@ httpServer.listen(port, async () => {
41579
42328
  await initWorkflows2();
41580
42329
  await startScriptRunSupervisor(getMcpBaseUrl());
41581
42330
  if (hasCapability("scheduling")) {
41582
- const { startScheduler } = await import("./index-38t35h0n.js");
41583
- const { getExecutorRegistry } = await import("./index-k0yt1tvq.js");
42331
+ const { startScheduler } = await import("./index-ch0c1xff.js");
42332
+ const { getExecutorRegistry } = await import("./index-5nb266jr.js");
41584
42333
  const intervalMs = Number(process.env.SCHEDULER_INTERVAL_MS) || 1e4;
41585
42334
  startScheduler(getExecutorRegistry(), intervalMs, {
41586
42335
  runId: globalState.__runId
41587
42336
  });
41588
42337
  }
41589
42338
  if (!isEnvFlagEnabled("HEARTBEAT_DISABLE", false)) {
41590
- const { startHeartbeat } = await import("./index-yqgbfgaj.js");
42339
+ const { startHeartbeat } = await import("./index-ed8dy8rn.js");
41591
42340
  const heartbeatMs = Number(process.env.HEARTBEAT_INTERVAL_MS) || 90000;
41592
42341
  startHeartbeat(heartbeatMs);
41593
42342
  }
41594
42343
  startPoolRouting();
41595
42344
  startRuntimeSupervisor();
41596
- if (process.env.OAUTH_KEEPALIVE_DISABLE !== "true") {
41597
- const { startOAuthKeepalive } = await import("./keepalive-em0117bp.js");
42345
+ if (!isEnvFlagEnabled("OAUTH_KEEPALIVE_DISABLE", false)) {
42346
+ const { startOAuthKeepalive } = await import("./keepalive-zzc3hks1.js");
41598
42347
  startOAuthKeepalive();
41599
42348
  }
41600
- if (process.env.OAUTH_REFRESH_SWEEP_DISABLE !== "true") {
41601
- const { startOAuthRefreshSweep } = await import("./oauth-refresh-sweep-xysj4yse.js");
42349
+ if (!isEnvFlagEnabled("OAUTH_REFRESH_SWEEP_DISABLE", false)) {
42350
+ const { startOAuthRefreshSweep } = await import("./oauth-refresh-sweep-7qt0g55z.js");
41602
42351
  startOAuthRefreshSweep();
41603
42352
  }
41604
42353
  startOAuthPendingGc();
41605
42354
  await startMemoryGc();
41606
42355
  await startDbRetention();
41607
42356
  startHumanFreeDrain();
41608
- import("./boot-reembed-f4nn561n.js").then(({ runBootReembed }) => runBootReembed()).catch((err) => {
42357
+ import("./boot-reembed-k5y5xbj0.js").then(({ runBootReembed }) => runBootReembed()).catch((err) => {
41609
42358
  console.error("[boot-reembed] startup backfill failed (non-fatal):", err);
41610
42359
  });
41611
- import("./boot-reembed-6c1sjfnr.js").then(({ runBootReembedScripts }) => runBootReembedScripts()).catch((err) => {
42360
+ import("./boot-reembed-fxfw17m2.js").then(({ runBootReembedScripts }) => runBootReembedScripts()).catch((err) => {
41612
42361
  console.error("[boot-reembed-scripts] startup backfill failed (non-fatal):", err);
41613
42362
  });
41614
- import("./boot-scrub-sweep-d4w9gajf.js").then(({ runBootScrubSweep }) => runBootScrubSweep()).catch((err) => {
42363
+ import("./boot-scrub-sweep-se41j9fd.js").then(({ runBootScrubSweep }) => runBootScrubSweep()).catch((err) => {
41615
42364
  console.error("[boot-scrub-sweep] startup scrub failed (non-fatal):", err);
41616
42365
  });
41617
- import("./session-source-backfill-qgtc86z1.js").then(({ runSessionSourceBackfill }) => runSessionSourceBackfill()).catch((err) => {
42366
+ import("./session-source-backfill-62202awe.js").then(({ runSessionSourceBackfill }) => runSessionSourceBackfill()).catch((err) => {
41618
42367
  console.error("[session-source-backfill] startup run failed (non-fatal):", err);
41619
42368
  });
41620
42369
  }).on("error", (err) => {