@desplega.ai/agent-swarm 1.164.1 → 1.166.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (328) hide show
  1. package/README.md +1 -1
  2. package/dist/acp-adapter-yzfxn8vj.js +23 -0
  3. package/dist/{actions-hp2h32dx.js → actions-qz3m692w.js} +24 -28
  4. package/dist/{amp-adapter-zfd7djyx.js → amp-adapter-j1xbmsqz.js} +7 -7
  5. package/dist/{app-w2d26h4f.js → app-43vrdmv8.js} +9 -10
  6. package/dist/{assistant-45pc4en7.js → assistant-ertffmka.js} +25 -29
  7. package/dist/{boot-reembed-k1q2s4fc.js → boot-reembed-998jtpdg.js} +12 -15
  8. package/dist/{boot-reembed-6rhcpsjf.js → boot-reembed-s7k7c6v2.js} +13 -15
  9. package/dist/boot-scrub-sweep-nepsk4t1.js +211 -0
  10. package/dist/{boot-seeds-ex93edx7.js → boot-seeds-vvzs1zhk.js} +12 -12
  11. package/dist/{claude-adapter-hcxd0szm.js → claude-adapter-2zvmekrp.js} +15 -17
  12. package/dist/{claude-managed-adapter-fz7xps2r.js → claude-managed-adapter-s49wbk8t.js} +5 -5
  13. package/dist/{claude-managed-setup-xx6f15h9.js → claude-managed-setup-cvhqq8qn.js} +4 -4
  14. package/dist/{claude-sdk-session-6nb9raem.js → claude-sdk-session-kkbxtw5q.js} +2624 -2606
  15. package/dist/{cli-etbrt8x1.js → cli-0nhdwxd5.js} +4 -4
  16. package/dist/{cli-jeefgppq.js → cli-0qjcsxgz.js} +7 -7
  17. package/dist/{cli-6behs2bt.js → cli-32bca7nr.js} +1 -1
  18. package/dist/{cli-gt8v96qx.js → cli-3ddybbbx.js} +4 -4
  19. package/dist/{acp-adapter-rxvek4ym.js → cli-3qn4r334.js} +317 -35
  20. package/dist/{cli-1d3w4xm3.js → cli-4r1sjfc4.js} +14 -6
  21. package/dist/{cli-691f2rxm.js → cli-66zh26sp.js} +68 -16
  22. package/dist/{cli-et8wdyqh.js → cli-68r1j4sg.js} +9 -9
  23. package/dist/{cli-xb7kyanp.js → cli-8kgvmkak.js} +3 -3
  24. package/dist/{cli-fdpkw8vk.js → cli-8t229cmr.js} +8 -8
  25. package/dist/{cli-gc54qc0p.js → cli-aqcey741.js} +1 -1
  26. package/dist/{cli-4q4cmr4t.js → cli-cpfmqz3n.js} +8 -8
  27. package/dist/{cli-a9bw0xxt.js → cli-df56tqdw.js} +997 -408
  28. package/dist/{cli-n7h1kqpf.js → cli-dmtrxqhg.js} +6 -1
  29. package/dist/{cli-xfb7333f.js → cli-dw2q9f7g.js} +5 -5
  30. package/dist/{cli-4drstbb9.js → cli-ecknp7p1.js} +81 -23
  31. package/dist/{cli-v0pvvhfq.js → cli-efnd2kce.js} +36 -1
  32. package/dist/{cli-1wetf34q.js → cli-f2n17zhp.js} +1 -1
  33. package/dist/{cli-2c8eky5y.js → cli-feh3dkah.js} +1 -1
  34. package/dist/cli-fn697bnt.js +126 -0
  35. package/dist/{cli-v5pr2fgt.js → cli-gt8azyet.js} +3 -3
  36. package/dist/{cli-dbszf3rw.js → cli-h3x613ze.js} +4 -4
  37. package/dist/{cli-nwhb8y2e.js → cli-hf5s9pgf.js} +1 -1
  38. package/dist/cli-j2jbr8af.js +516 -0
  39. package/dist/{cli-agjzafns.js → cli-jhm6hzjn.js} +593 -10
  40. package/dist/{cli-j0tmc908.js → cli-jvj6a0cv.js} +484 -477
  41. package/dist/{cli-xxz89c2n.js → cli-mb75wpxf.js} +1 -1
  42. package/dist/{cli-07s39g1n.js → cli-mv2520nm.js} +1 -1
  43. package/dist/{cli-v90vfwyb.js → cli-mwegs4af.js} +5 -5
  44. package/dist/{cli-bz1gbac9.js → cli-p4ztb80h.js} +2 -2
  45. package/dist/{cli-1wd1n2ya.js → cli-rfphkzh5.js} +1 -1
  46. package/dist/{cli-976r1zqx.js → cli-rggdq8wq.js} +5 -5
  47. package/dist/{cli-3ajw5v5v.js → cli-rsy933yz.js} +6 -6
  48. package/dist/{cli-yj790e19.js → cli-tadfgd0t.js} +8 -1
  49. package/dist/{cli-cn53e7vc.js → cli-ttbsse3f.js} +6 -1
  50. package/dist/{cli-dyqsrqcf.js → cli-v2a8x45d.js} +4 -4
  51. package/dist/{cli-wbvnn6tk.js → cli-v6vv7dy8.js} +91 -9
  52. package/dist/{cli-z2ap2c0c.js → cli-vzc73wet.js} +90 -55
  53. package/dist/cli-wt7ccy4r.js +2654 -0
  54. package/dist/{cli-4cn19jvn.js → cli-x7kpr72a.js} +3 -3
  55. package/dist/{cli-003785v9.js → cli-xvc3q9br.js} +1 -1
  56. package/dist/{cli-v0e7jpr5.js → cli-xvdgasgy.js} +13 -4
  57. package/dist/{cli-asqeg4ve.js → cli-y59h2pk9.js} +2 -2
  58. package/dist/{cli-0wn3ypqv.js → cli-yshaazs1.js} +14121 -3478
  59. package/dist/{cli-wg8yjqtw.js → cli-ysnyhws9.js} +16 -3
  60. package/dist/cli-zp67qg5x.js +92 -0
  61. package/dist/{cli-g3qgrzck.js → cli-zsh093hk.js} +71 -3
  62. package/dist/{cli-ma3zvh39.js → cli-zy7vwsne.js} +6066 -5829
  63. package/dist/cli.js +79 -26
  64. package/dist/{codex-adapter-pcxdn10v.js → codex-adapter-dw1wxqt8.js} +10 -10
  65. package/dist/{codex-hook-b1fb7c2k.js → codex-hook-d2rkvf39.js} +1 -1
  66. package/dist/{codex-login-059nm145.js → codex-login-terbdsqq.js} +2 -2
  67. package/dist/{codex-session-runner-xzwwddp8.js → codex-session-runner-3cgb8y1g.js} +10 -10
  68. package/dist/{commands-98at8mkn.js → commands-mhxam6z2.js} +9 -9
  69. package/dist/{cursor-adapter-dkg6qnkp.js → cursor-adapter-2qda3st7.js} +6 -6
  70. package/dist/{db-01g8wtvw.js → db-sjks418k.js} +11 -9
  71. package/dist/{devin-adapter-qmttt2fz.js → devin-adapter-bffjj5n4.js} +2 -2
  72. package/dist/{dispatcher-nn2k1wxd.js → dispatcher-1es759rn.js} +22 -26
  73. package/dist/{dsh-adapter-w4d4vwdq.js → dsh-adapter-bek4cyw9.js} +6 -6
  74. package/dist/{e2b-9e4g2hvt.js → e2b-nd50eccg.js} +1 -1
  75. package/dist/{event-bus-z7v2t26e.js → event-bus-vnfthafg.js} +2 -2
  76. package/dist/grok-adapter-ar9cw9jg.js +29 -0
  77. package/dist/{handlers-b0kqsbg8.js → handlers-1gkbhktr.js} +25 -29
  78. package/dist/{hook-sap7v1aj.js → hook-ds2zxq9v.js} +16 -18
  79. package/dist/{hook-pz50gqep.js → hook-vxa3tcsy.js} +10 -12
  80. package/dist/{http-ncr8rmd0.js → http-3h21z9md.js} +679 -227
  81. package/dist/{index-c4aqcyz1.js → index-9b65j1m5.js} +11 -11
  82. package/dist/{index-r46s01nq.js → index-bn6cy8ps.js} +22 -26
  83. package/dist/{index-38kqdt3e.js → index-dkfxpkmm.js} +23 -27
  84. package/dist/{index-630kj5vd.js → index-f8xnqrye.js} +55 -43
  85. package/dist/{index-fdk1t4d6.js → index-n0rxa4ca.js} +11962 -9900
  86. package/dist/{index-9nrj7797.js → index-n99fycq7.js} +22 -26
  87. package/dist/{keepalive-31613r2h.js → keepalive-3xspa9jw.js} +14 -11
  88. package/dist/{lead-mx59tr5s.js → lead-6qphc124.js} +43 -43
  89. package/dist/{maintenance-55ff39ca.js → maintenance-0teem1bb.js} +10 -12
  90. package/dist/{node-f0ykebbv.js → node-9sg7ajby.js} +25 -7
  91. package/dist/{oauth-refresh-sweep-anma954w.js → oauth-refresh-sweep-sh9q25xp.js} +13 -10
  92. package/dist/{onboard-b2fy8ftb.js → onboard-54qmnqxe.js} +4 -3
  93. package/dist/{opencode-adapter-p1s92tmy.js → opencode-adapter-wjfa2wvd.js} +6 -6
  94. package/dist/{otel-impl-9brvt7xv.js → otel-impl-ycqzp2a1.js} +2 -2
  95. package/dist/{pi-mono-adapter-kz4rrwvd.js → pi-mono-adapter-e77e9zst.js} +6 -6
  96. package/dist/{pricing-refresh-7w8mbmva.js → pricing-refresh-atnkngqs.js} +10 -10
  97. package/dist/{rbac-roles-ejp7qwvb.js → rbac-roles-9pjphh9q.js} +10 -10
  98. package/dist/{rbac-roles-nhk4r7bc.js → rbac-roles-j78wdksm.js} +10 -10
  99. package/dist/{render-v2-2gzt8skq.js → render-v2-pxjs49fx.js} +9 -10
  100. package/dist/{setup-5et8t412.js → setup-ymej0xtx.js} +2 -2
  101. package/dist/{work-objects-tb0er7qa.js → work-objects-3nh4ymzb.js} +26 -30
  102. package/dist/{worker-62cv075d.js → worker-qa9fkz5s.js} +43 -43
  103. package/dist/{x-vbxb31jr.js → x-ma4gnfgy.js} +2 -2
  104. package/openapi.json +457 -31
  105. package/package.json +14 -6
  106. package/plugin/commands/create-pr.md +7 -1
  107. package/plugin/pi-skills/create-pr/SKILL.md +7 -1
  108. package/src/agentmail/app.ts +5 -4
  109. package/src/be/automation-preflight.ts +11 -9
  110. package/src/be/boot-scrub-sweep.ts +302 -0
  111. package/src/be/connector-codes.ts +81 -0
  112. package/src/be/crypto/key-bootstrap.ts +1 -1
  113. package/src/be/db/approvals.ts +15 -1
  114. package/src/be/db/codex-oauth-device-flows.ts +40 -0
  115. package/src/be/db/config-secrets.ts +31 -0
  116. package/src/be/db/runtime.ts +4 -0
  117. package/src/be/db/script-runs.ts +15 -0
  118. package/src/be/db/tasks/write.ts +1 -1
  119. package/src/be/db-queries/mcp-oauth.ts +5 -0
  120. package/src/be/db-queries/oauth.ts +77 -0
  121. package/src/be/db.ts +162 -59
  122. package/src/be/events.ts +17 -1
  123. package/src/be/memory/index-content.ts +2 -2
  124. package/src/be/memory/providers/sqlite-store.ts +63 -11
  125. package/src/be/memory/types.ts +14 -0
  126. package/src/be/migrations/197_connector_codes.sql +23 -0
  127. package/src/be/migrations/198_permission_audit_guest.sql +33 -0
  128. package/src/be/migrations/200_backfill_auto_memory_expiry.sql +20 -0
  129. package/src/be/migrations/201_workflow_replay_columns.sql +10 -0
  130. package/src/be/migrations/202_codex_oauth_device_flows.sql +24 -0
  131. package/src/be/model-tier-resolution.ts +2 -1
  132. package/src/be/model-validation.ts +1 -1
  133. package/src/be/pricing-normalize.ts +3 -0
  134. package/src/be/rbac-audit.ts +2 -1
  135. package/src/be/scripts/db.ts +28 -4
  136. package/src/be/scripts/source-secrets.ts +37 -0
  137. package/src/be/scripts/typecheck.ts +23 -2
  138. package/src/be/scrub-json.ts +32 -0
  139. package/src/be/sealed-json.ts +59 -0
  140. package/src/be/secret-registry.ts +131 -0
  141. package/src/be/seed-pricing.ts +14 -0
  142. package/src/be/users.ts +7 -1
  143. package/src/be/workflow-replay.ts +77 -0
  144. package/src/cli.tsx +7 -0
  145. package/src/commands/provider-credentials.ts +46 -8
  146. package/src/commands/runner.ts +2 -10
  147. package/src/extensions/contract-types.generated.ts +1 -1
  148. package/src/github/app.ts +4 -4
  149. package/src/heartbeat/heartbeat.ts +20 -8
  150. package/src/hooks/pr-body-guard.ts +1 -1
  151. package/src/http/agents.ts +1 -0
  152. package/src/http/approval-requests.ts +1 -0
  153. package/src/http/apps.ts +3 -0
  154. package/src/http/auth.ts +4 -1
  155. package/src/http/codex-oauth-device.ts +8 -10
  156. package/src/http/codex-oauth-keep-warm.ts +2 -2
  157. package/src/http/config-values.ts +59 -0
  158. package/src/http/config.ts +5 -0
  159. package/src/http/db-query-bounded.ts +3 -0
  160. package/src/http/db-query-shared.ts +107 -15
  161. package/src/http/db-query.ts +10 -2
  162. package/src/http/extensions.ts +1 -0
  163. package/src/http/index.ts +23 -6
  164. package/src/http/integrations.ts +1 -38
  165. package/src/http/ip-rate-limit.ts +82 -0
  166. package/src/http/kv.ts +38 -4
  167. package/src/http/mcp-servers.ts +1 -0
  168. package/src/http/memory.ts +9 -0
  169. package/src/http/models-catalog.ts +2 -0
  170. package/src/http/operator-actor.ts +4 -0
  171. package/src/http/page-proxy.ts +22 -0
  172. package/src/http/pages.ts +4 -2
  173. package/src/http/realtime.ts +1 -1
  174. package/src/http/request-principal.ts +14 -1
  175. package/src/http/script-connections.ts +8 -0
  176. package/src/http/script-runs.ts +12 -4
  177. package/src/http/scripts.ts +15 -0
  178. package/src/http/session-cost-recompute.ts +3 -2
  179. package/src/http/tasks.ts +9 -0
  180. package/src/http/users.ts +226 -0
  181. package/src/http/workflows.ts +3 -2
  182. package/src/http.ts +1 -0
  183. package/src/integrations/kapso/client.ts +3 -1
  184. package/src/jira/app.ts +2 -2
  185. package/src/kv-view.ts +324 -0
  186. package/src/linear/app.ts +2 -2
  187. package/src/oauth/mcp-wrapper.ts +1 -1
  188. package/src/prompts/memories.ts +42 -1
  189. package/src/providers/acp-adapter.ts +85 -24
  190. package/src/providers/acp-target-catalog.ts +54 -1
  191. package/src/providers/acp-targets.ts +358 -4
  192. package/src/providers/amp-adapter.ts +1 -1
  193. package/src/providers/cursor-adapter.ts +1 -1
  194. package/src/providers/grok-adapter.ts +186 -0
  195. package/src/providers/index.ts +5 -1
  196. package/src/providers/pricing-sources.md +6 -0
  197. package/src/providers/reasoning-effort.ts +17 -1
  198. package/src/providers/types.ts +2 -1
  199. package/src/rbac/can.ts +4 -3
  200. package/src/rbac/legacy-policy.ts +3 -3
  201. package/src/rbac/types.ts +6 -0
  202. package/src/script-workflows/supervisor.ts +11 -2
  203. package/src/scripts-runtime/eval-harness.ts +2 -12
  204. package/src/scripts-runtime/executors/quickjs-bundle.ts +152 -0
  205. package/src/scripts-runtime/executors/quickjs-prelude.ts +285 -0
  206. package/src/scripts-runtime/executors/quickjs-runner.ts +580 -0
  207. package/src/scripts-runtime/executors/quickjs-worker.ts +81 -0
  208. package/src/scripts-runtime/executors/quickjs.ts +196 -0
  209. package/src/scripts-runtime/executors/registry.ts +2 -0
  210. package/src/scripts-runtime/executors/sourcemap.ts +79 -0
  211. package/src/scripts-runtime/script-error-hints.ts +14 -0
  212. package/src/scripts-runtime/swarm-sdk.ts +9 -2
  213. package/src/scripts-runtime/types/stdlib.d.ts +27 -8
  214. package/src/scripts-runtime/types/swarm-extension.d.ts +3 -0
  215. package/src/scripts-runtime/types/swarm-sdk.d.ts +27 -8
  216. package/src/server-user.ts +2 -20
  217. package/src/server.ts +3 -1
  218. package/src/slack/app.ts +4 -0
  219. package/src/slack/config.ts +3 -1
  220. package/src/slack/egress-scrub.ts +87 -0
  221. package/src/tests/acp-adapter.test.ts +191 -2
  222. package/src/tests/boot-scrub-sweep.test.ts +390 -0
  223. package/src/tests/budget-user-scope.test.ts +7 -21
  224. package/src/tests/check-pr-body.test.ts +149 -6
  225. package/src/tests/check-text-columns.test.ts +145 -0
  226. package/src/tests/claude-adapter-binary.test.ts +1 -1
  227. package/src/tests/claude-sdk-process.fixture.ts +13 -2
  228. package/src/tests/codex-oauth-device.test.ts +23 -37
  229. package/src/tests/codex-oauth-keep-warm.test.ts +29 -0
  230. package/src/tests/console-scrub.test.ts +141 -0
  231. package/src/tests/credential-check.test.ts +90 -0
  232. package/src/tests/db-query-denylist.test.ts +202 -0
  233. package/src/tests/db-task-read-contract.test.ts +1 -0
  234. package/src/tests/db-task-write-contract.test.ts +1 -0
  235. package/src/tests/docs-index-footprint.test.js +3 -3
  236. package/src/tests/entrypoint-acp-target-binary.test.ts +44 -1
  237. package/src/tests/events-scrub.test.ts +88 -0
  238. package/src/tests/explicit-model-validation.test.ts +81 -0
  239. package/src/tests/extensions-pre-task.test.ts +33 -1
  240. package/src/tests/fixtures/grok/acp-updates-recorded.jsonl +53 -0
  241. package/src/tests/fixtures/grok/prompt-response-meta.json +36 -0
  242. package/src/tests/fixtures/secret-corpus.ts +320 -0
  243. package/src/tests/grok-adapter.test.ts +647 -0
  244. package/src/tests/heartbeat-checklist.test.ts +44 -0
  245. package/src/tests/integration-disable-flags.test.ts +156 -0
  246. package/src/tests/kv-http.test.ts +34 -0
  247. package/src/tests/kv-tool.test.ts +274 -1
  248. package/src/tests/kv-view.test.ts +147 -0
  249. package/src/tests/mcp-user-route.test.ts +257 -15
  250. package/src/tests/memory-injection-outcome.test.ts +143 -0
  251. package/src/tests/model-tier-resolution.test.ts +76 -3
  252. package/src/tests/page-guest-session.test.ts +424 -0
  253. package/src/tests/page-proxy.test.ts +1 -1
  254. package/src/tests/page-viewer-auth.test.ts +22 -1
  255. package/src/tests/pr-body-leaks.test.ts +157 -2
  256. package/src/tests/preload.ts +10 -0
  257. package/src/tests/provider-registration.test.ts +3 -1
  258. package/src/tests/rbac-audit.test.ts +32 -0
  259. package/src/tests/rbac-mcp-admission-e2e.test.ts +4 -62
  260. package/src/tests/script-connections.test.ts +9 -0
  261. package/src/tests/script-executor-conformance.test.ts +3 -0
  262. package/src/tests/script-executor-quickjs.test.ts +442 -0
  263. package/src/tests/script-executor-registry.test.ts +6 -1
  264. package/src/tests/script-runs-http.test.ts +126 -1
  265. package/src/tests/script-runs-scrub.test.ts +275 -0
  266. package/src/tests/scripts-http.test.ts +82 -1
  267. package/src/tests/scripts-mcp-e2e.test.ts +42 -6
  268. package/src/tests/scripts-runtime.test.ts +37 -0
  269. package/src/tests/scrubbed-text-12e.test.ts +289 -0
  270. package/src/tests/sealed-key-guard.test.ts +166 -0
  271. package/src/tests/secret-canary-sweep.test.ts +958 -0
  272. package/src/tests/secret-registry.test.ts +355 -0
  273. package/src/tests/secret-rules.test.ts +230 -0
  274. package/src/tests/secret-scrubber.test.ts +317 -1
  275. package/src/tests/session-costs-golden.test.ts +17 -0
  276. package/src/tests/slack-egress-scrub.test.ts +216 -0
  277. package/src/tests/swarm-tool-result-gate.test.ts +143 -30
  278. package/src/tests/task-tools-ctx.test.ts +3 -0
  279. package/src/tests/user-token-routes.test.ts +342 -3
  280. package/src/tests/workflow-replay.test.ts +518 -0
  281. package/src/tests/workflow-scrub.test.ts +242 -0
  282. package/src/tests/workflow-system-one-decision-providers.test.ts +737 -0
  283. package/src/tools/db-query.ts +1 -1
  284. package/src/tools/inject-learning.ts +1 -1
  285. package/src/tools/kv/kv-get.ts +136 -6
  286. package/src/tools/send-task.ts +61 -17
  287. package/src/tools/utils.ts +186 -43
  288. package/src/tools/workflows/get-workflow-run.ts +3 -2
  289. package/src/types.ts +16 -1
  290. package/src/utils/console-scrub-install.ts +5 -0
  291. package/src/utils/console-scrub.ts +57 -0
  292. package/src/utils/credentials.ts +8 -0
  293. package/src/utils/page-session.ts +6 -2
  294. package/src/utils/pr-body-leaks.ts +149 -10
  295. package/src/utils/request-auth-context.ts +2 -0
  296. package/src/utils/secret-rules.generated.ts +2055 -0
  297. package/src/utils/secret-scrubber.ts +662 -36
  298. package/src/workflows/engine.ts +39 -4
  299. package/src/workflows/executors/notify.ts +6 -1
  300. package/src/workflows/executors/system-one-decision.ts +31 -12
  301. package/src/workflows/executors/system-one-openai-wire.ts +168 -0
  302. package/src/workflows/executors/system-one-providers.ts +112 -11
  303. package/src/workflows/executors/system-one-wire.ts +79 -0
  304. package/src/workflows/recovery.ts +10 -1
  305. package/src/workflows/resume.ts +62 -3
  306. package/src/workflows/retry-poller.ts +3 -0
  307. package/templates/ai-toolbox.manifest.json +2 -2
  308. package/templates/skills/agent-browser/SKILL.md +5 -5
  309. package/templates/skills/agent-browser/config.json +1 -1
  310. package/templates/skills/agent-browser/content.md +4 -4
  311. package/templates/skills/artifacts/content.md +17 -7
  312. package/templates/skills/jira-interaction/config.json +1 -1
  313. package/templates/skills/jira-interaction/content.md +56 -205
  314. package/templates/skills/linear-interaction/config.json +1 -1
  315. package/templates/skills/linear-interaction/content.md +31 -50
  316. package/templates/skills/pages/content.md +8 -4
  317. package/templates/skills/qa/content.md +2 -2
  318. package/templates/skills/workflow-iterate/content.md +4 -4
  319. package/tsconfig.json +3 -3
  320. package/dist/boot-scrub-logs-t8xg7wd7.js +0 -99
  321. package/dist/cli-0a7r5331.js +0 -168
  322. package/dist/cli-93rxw9yd.js +0 -951
  323. package/dist/cli-cvk6yzw5.js +0 -40
  324. package/dist/cli-dgnj0zcc.js +0 -40
  325. package/dist/cli-qt5wzq6v.js +0 -256
  326. package/dist/cli-v7fqehqd.js +0 -9281
  327. package/dist/cli-vbr859rz.js +0 -572
  328. package/src/be/boot-scrub-logs.ts +0 -132
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Small in-memory per-IP token bucket for unauthenticated routes. State is
3
+ * per process and resets on restart, which is fine for defense-in-depth on
4
+ * routes whose credential is already unguessable.
5
+ */
6
+
7
+ import type { IncomingMessage } from "node:http";
8
+
9
+ /** Bucket count that triggers a prune of idle buckets. */
10
+ const PRUNE_THRESHOLD = 10_000;
11
+ /** Minimum time between prunes, so a flood of new IPs cannot force a scan per request. */
12
+ const PRUNE_INTERVAL_MS = 60_000;
13
+ /** Hard cap on tracked buckets. New keys past it evict the oldest-inserted bucket in O(1). */
14
+ const MAX_BUCKETS = 50_000;
15
+
16
+ /** Loopback, RFC1918, link-local and IPv6 unique-local addresses (IPv4-mapped forms included). */
17
+ function isPrivateAddress(address: string): boolean {
18
+ const ip = address.startsWith("::ffff:") ? address.slice(7) : address;
19
+ if (ip === "::1" || ip.startsWith("127.") || ip.startsWith("10.")) return true;
20
+ if (ip.startsWith("192.168.") || ip.startsWith("169.254.")) return true;
21
+ const match = /^172\.(\d+)\./.exec(ip);
22
+ if (match && Number(match[1]) >= 16 && Number(match[1]) <= 31) return true;
23
+ const lower = ip.toLowerCase();
24
+ return lower.startsWith("fc") || lower.startsWith("fd") || lower.startsWith("fe80:");
25
+ }
26
+
27
+ /**
28
+ * Client IP for rate limiting. The rightmost `X-Forwarded-For` hop (the
29
+ * address the nearest proxy saw) is trusted only when the socket peer is a
30
+ * private address, i.e. a reverse proxy on the same host or network. A client
31
+ * that reaches the API directly is keyed on its socket address, so a spoofed
32
+ * header cannot buy it a fresh bucket.
33
+ */
34
+ export function clientIp(req: IncomingMessage): string {
35
+ const peer = req.socket.remoteAddress ?? "unknown";
36
+ if (!isPrivateAddress(peer)) return peer;
37
+ const raw = req.headers["x-forwarded-for"];
38
+ const header = Array.isArray(raw) ? raw[raw.length - 1] : raw;
39
+ const hop = header?.split(",").pop()?.trim();
40
+ return hop || peer;
41
+ }
42
+
43
+ export function createIpRateLimiter(options: { capacity: number; refillPerMs: number }) {
44
+ const buckets = new Map<string, { tokens: number; updatedAt: number }>();
45
+ let lastPruneAt = 0;
46
+
47
+ function refill(bucket: { tokens: number; updatedAt: number }, now: number): void {
48
+ const elapsed = now - bucket.updatedAt;
49
+ bucket.tokens = Math.min(options.capacity, bucket.tokens + elapsed * options.refillPerMs);
50
+ bucket.updatedAt = now;
51
+ }
52
+
53
+ return {
54
+ /** Take one token for `ip`. Returns false when the bucket is empty. */
55
+ take(ip: string, now = Date.now()): boolean {
56
+ if (buckets.size > PRUNE_THRESHOLD && now - lastPruneAt >= PRUNE_INTERVAL_MS) {
57
+ lastPruneAt = now;
58
+ for (const [key, bucket] of buckets) {
59
+ refill(bucket, now);
60
+ if (bucket.tokens >= options.capacity) buckets.delete(key);
61
+ }
62
+ }
63
+ let bucket = buckets.get(ip);
64
+ if (!bucket) {
65
+ if (buckets.size >= MAX_BUCKETS) {
66
+ const oldest = buckets.keys().next().value;
67
+ if (oldest !== undefined) buckets.delete(oldest);
68
+ }
69
+ bucket = { tokens: options.capacity, updatedAt: now };
70
+ buckets.set(ip, bucket);
71
+ }
72
+ refill(bucket, now);
73
+ if (bucket.tokens < 1) return false;
74
+ bucket.tokens -= 1;
75
+ return true;
76
+ },
77
+ reset(): void {
78
+ buckets.clear();
79
+ lastPruneAt = 0;
80
+ },
81
+ };
82
+ }
package/src/http/kv.ts CHANGED
@@ -13,6 +13,7 @@ import {
13
13
  } from "../be/db";
14
14
  import { mcpOverflowAuthError } from "../kv-overflow";
15
15
  import { reservedNamespaceError, reservedRoomKeyError } from "../kv-reserved-namespaces";
16
+ import { hasKvViewArgs, resolveKvView } from "../kv-view";
16
17
  import { can } from "../rbac";
17
18
  import { agentContextKey, pageContextKey } from "../tasks/context-key";
18
19
  import { KvEntrySchema, KvKeySchema, KvNamespaceSchema, KvValueTypeSchema } from "../types";
@@ -69,12 +70,33 @@ const kvListQuerySchema = z.object({
69
70
  offset: z.coerce.number().int().nonnegative().optional(),
70
71
  });
71
72
 
73
+ // Targeted read of one entry: dot path into a JSON value + offset/limit page.
74
+ // Unbounded by design: these routes back ctx.swarm.kv_get inside the script
75
+ // sandbox; the model-facing kv-get MCP tool applies its own size cap.
76
+ const kvGetQuerySchema = z.object({
77
+ path: z.string().max(1024).optional(),
78
+ offset: z.coerce.number().int().nonnegative().optional(),
79
+ limit: z.coerce.number().int().positive().optional(),
80
+ });
81
+
82
+ const kvViewSchema = z.object({
83
+ path: z.string(),
84
+ type: z.enum(["object", "array", "string", "number", "boolean", "null"]),
85
+ total: z.number().int().nonnegative().optional(),
86
+ offset: z.number().int().nonnegative().optional(),
87
+ returned: z.number().int().nonnegative().optional(),
88
+ nextOffset: z.number().int().nonnegative().nullable().optional(),
89
+ });
90
+
91
+ // With path/offset/limit, `value` is the resolved slice and `view` describes it.
92
+ const kvEntryViewSchema = KvEntrySchema.extend({ view: kvViewSchema.optional() });
93
+
72
94
  // ─── Routes ──────────────────────────────────────────────────────────────────
73
95
 
74
96
  const RESPONSES_GET = {
75
- 200: { description: "KV entry", schema: KvEntrySchema },
97
+ 200: { description: "KV entry (or a path/offset/limit view of it)", schema: kvEntryViewSchema },
76
98
  404: { description: "KV entry not found or expired" },
77
- 400: { description: "Validation error or unresolvable namespace" },
99
+ 400: { description: "Validation error, unresolvable namespace, or invalid view path" },
78
100
  } as const;
79
101
 
80
102
  const RESPONSES_PUT = {
@@ -106,6 +128,7 @@ const getKvHeader = route({
106
128
  summary: "Get a KV entry by key (namespace resolved from request headers)",
107
129
  tags: ["KV"],
108
130
  params: z.object({ key: KvKeySchema }),
131
+ query: kvGetQuerySchema,
109
132
  responses: RESPONSES_GET,
110
133
  });
111
134
 
@@ -167,6 +190,7 @@ const getKvExplicit = route({
167
190
  summary: "Get a KV entry by explicit namespace + key",
168
191
  tags: ["KV"],
169
192
  params: z.object({ namespace: KvNamespaceSchema, key: KvKeySchema }),
193
+ query: kvGetQuerySchema,
170
194
  responses: RESPONSES_GET,
171
195
  });
172
196
 
@@ -471,7 +495,7 @@ export async function handleKv(
471
495
  if (!ns) return true;
472
496
  const key = decodeKvSegment(res, parsed.params.key, "key");
473
497
  if (!key) return true;
474
- return sendGet(req, res, ns, key, getKvExplicit.respond);
498
+ return sendGet(req, res, ns, key, parsed.query, getKvExplicit.respond);
475
499
  }
476
500
  if (putKvExplicit.match(req.method, pathSegments)) {
477
501
  if (enforceContentLengthCap(req, res, MAX_KV_BODY_BYTES) === BODY_TOO_LARGE) return true;
@@ -511,7 +535,7 @@ export async function handleKv(
511
535
  }
512
536
  const key = decodeKvSegment(res, parsed.params.key, "key");
513
537
  if (!key) return true;
514
- return sendGet(req, res, ns, key, getKvHeader.respond);
538
+ return sendGet(req, res, ns, key, parsed.query, getKvHeader.respond);
515
539
  }
516
540
  if (putKvHeader.match(req.method, pathSegments)) {
517
541
  if (enforceContentLengthCap(req, res, MAX_KV_BODY_BYTES) === BODY_TOO_LARGE) return true;
@@ -638,6 +662,7 @@ async function sendGet(
638
662
  res: ServerResponse,
639
663
  namespace: string,
640
664
  key: string,
665
+ query: z.infer<typeof kvGetQuerySchema>,
641
666
  respond: GetKvRespond,
642
667
  ): Promise<boolean> {
643
668
  const authErr = authorizeRead(namespace, await buildAuthCtx(req));
@@ -650,6 +675,15 @@ async function sendGet(
650
675
  jsonError(res, "not found", 404);
651
676
  return true;
652
677
  }
678
+ if (hasKvViewArgs(query)) {
679
+ const view = resolveKvView(entry.value, query);
680
+ if (!view.ok) {
681
+ jsonError(res, view.error, 400);
682
+ return true;
683
+ }
684
+ respond(res, 200, { ...entry, value: view.value, view: view.view });
685
+ return true;
686
+ }
653
687
  respond(res, 200, entry);
654
688
  return true;
655
689
  }
@@ -253,6 +253,7 @@ async function mcpServerPrincipal(req: IncomingMessage): Promise<RbacPrincipal>
253
253
  const auth = getRequestAuth(req);
254
254
  if (auth?.kind === "operator") return { kind: "operator" };
255
255
  if (auth?.kind === "user") return { kind: "user", userId: auth.userId };
256
+ if (auth?.kind === "guest") return { kind: "guest" };
256
257
  return { kind: "agent", agentId: "", isLead: false };
257
258
  }
258
259
 
@@ -43,6 +43,7 @@ import { can, type RbacPrincipal } from "../rbac";
43
43
  import { AgentMemorySchema, AgentMemoryScopeSchema, AgentMemorySourceSchema } from "../types";
44
44
  import { getRequestAuth } from "../utils/request-auth-context";
45
45
  import { scrubSecrets } from "../utils/secret-scrubber";
46
+ import { rejectGuest } from "./request-principal";
46
47
  import { route } from "./route-def";
47
48
  import { jsonError, parseQueryParams } from "./utils";
48
49
 
@@ -108,6 +109,8 @@ const MemorySearchResultItemSchema = z.object({
108
109
  scope: AgentMemoryScopeSchema,
109
110
  tags: z.array(z.string()),
110
111
  accessCount: z.number(),
112
+ summary: z.string().nullable().optional(),
113
+ createdAt: z.string().optional(),
111
114
  });
112
115
 
113
116
  const searchMemory = route({
@@ -787,6 +790,7 @@ async function ingestPrincipal(
787
790
  ): Promise<RbacPrincipal> {
788
791
  const auth = getRequestAuth(req);
789
792
  if (auth?.kind === "user") return { kind: "user", userId: auth.userId };
793
+ if (auth?.kind === "guest") return { kind: "guest" };
790
794
  const agentId = auth?.kind === "agent" ? auth.agentId : agentIdHeader;
791
795
  if (agentId) {
792
796
  const agent = await getAgentById(agentId);
@@ -810,6 +814,9 @@ export async function handleMemory(
810
814
  ): Promise<boolean> {
811
815
  // Page memory operations use the owner's scope. Authentication and audit retain the signed viewer.
812
816
  const myAgentId = getRequestAuth(req)?.page?.executionAgentId ?? callerAgentId;
817
+ if (pathSegments[0] === "api" && pathSegments[1] === "memory" && rejectGuest(req, res)) {
818
+ return true;
819
+ }
813
820
  if (indexMemory.match(req.method, pathSegments)) {
814
821
  const parsed = await indexMemory.parse(req, res, pathSegments, new URLSearchParams());
815
822
  if (!parsed) return true;
@@ -996,6 +1003,8 @@ export async function handleMemory(
996
1003
  scope: r.scope,
997
1004
  tags: r.tags,
998
1005
  accessCount: (r.accessCount ?? 0) + (consumedIdSet.has(r.id) ? 1 : 0),
1006
+ summary: r.summary,
1007
+ createdAt: r.createdAt,
999
1008
  })),
1000
1009
  });
1001
1010
  } catch (err) {
@@ -275,6 +275,8 @@ async function ensureCatalogWriter(
275
275
  let principal: RbacPrincipal;
276
276
  if (auth?.kind === "user") {
277
277
  principal = { kind: "user", userId: auth.userId };
278
+ } else if (auth?.kind === "guest") {
279
+ principal = { kind: "guest" };
278
280
  } else if (agentId) {
279
281
  // An X-Agent-ID wins over the shared API key: a worker holds that key, so treating it as the
280
282
  // operator would let any worker skip the verb's lead-only rule.
@@ -48,6 +48,10 @@ export function getOperatorActor(req: IncomingMessage, res: ServerResponse): Ide
48
48
  if (auth?.kind === "operator") {
49
49
  return { kind: "operator", id: auth.fingerprint };
50
50
  }
51
+ if (auth?.kind === "guest") {
52
+ jsonError(res, "Forbidden", 403);
53
+ return null;
54
+ }
51
55
 
52
56
  const rawKey = extractBearer(req);
53
57
  const swarmKey = getApiKey();
@@ -149,6 +149,22 @@ function isSafeProxySuffix(suffix: string): boolean {
149
149
  return true;
150
150
  }
151
151
 
152
+ /**
153
+ * What a guest page session (a password-page cookie with no signed-in user) may
154
+ * reach. Everything else is refused before it is forwarded:
155
+ * - `GET pages/<its own id>`: the page reads its own record.
156
+ * - `kv`, `kv/*`: the page's key-value store. The KV handler pins every such
157
+ * request to `task:page:<own id>` through the `X-Page-Id` header set below.
158
+ * Exported for tests.
159
+ */
160
+ export function isGuestAllowedProxyPath(method: string, suffix: string, pageId: string): boolean {
161
+ const segments = suffix.split("/").map((seg) => decodeURIComponent(seg));
162
+ if (segments[0] === "kv") return true;
163
+ return (
164
+ method === "GET" && segments.length === 2 && segments[0] === "pages" && segments[1] === pageId
165
+ );
166
+ }
167
+
152
168
  /**
153
169
  * Handle `/@swarm/api/*` requests. Returns `true` if the request was handled
154
170
  * (response sent), `false` if not — caller continues to the next handler.
@@ -194,6 +210,12 @@ export async function handlePageProxy(req: IncomingMessage, res: ServerResponse)
194
210
  return true;
195
211
  }
196
212
 
213
+ // A password-page session carries no user, so only the allowlist above is reachable.
214
+ if (!payload.uid && !payload.op && !isGuestAllowedProxyPath(method, suffix, page.id)) {
215
+ jsonError(res, "forbidden", 403);
216
+ return true;
217
+ }
218
+
197
219
  // ─── Rewrite + forward ─────────────────────────────────────────────────────
198
220
  // `/@swarm/api/me` → `/api/me`. Preserve query string.
199
221
  //
package/src/http/pages.ts CHANGED
@@ -660,7 +660,9 @@ export async function handlePages(
660
660
  }
661
661
  const favoriteScope = (await resolveHttpFavoriteOwner(req, myAgentId))?.scope;
662
662
  const [decorated] = await withFavoriteFlags([page], { favoriteScope, itemType: "page" });
663
- getPageRoute.respond(res, 200, withShareUrls(decorated ?? page));
663
+ const body = withShareUrls(decorated ?? page);
664
+ if (getRequestAuth(req)?.kind === "guest") delete body.passwordHash;
665
+ getPageRoute.respond(res, 200, body);
664
666
  return true;
665
667
  }
666
668
 
@@ -808,7 +810,7 @@ export async function handlePages(
808
810
  auth?.kind === "user"
809
811
  ? { uid: auth.userId, name: auth.user.name }
810
812
  : auth?.kind === "operator"
811
- ? { name: `guest-${randomUUID().slice(0, 8)}` }
813
+ ? { name: `guest-${randomUUID().slice(0, 8)}`, op: true as const }
812
814
  : undefined;
813
815
  const cookie = await issuePageSessionCookie(page.id, {
814
816
  dev: isDevRequest(req),
@@ -53,7 +53,7 @@ export async function handleRealtimeTicket(
53
53
  const parsed = await realtimeTicketRoute.parse(req, res, pathSegments, queryParams);
54
54
  if (!parsed) return true;
55
55
  const auth = getRequestAuth(req);
56
- if (!auth || auth.kind === "agent") {
56
+ if (!auth || auth.kind === "agent" || auth.kind === "guest") {
57
57
  jsonError(res, "Comb presence requires dashboard authentication", 403);
58
58
  return true;
59
59
  }
@@ -1,7 +1,8 @@
1
- import type { IncomingMessage } from "node:http";
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
2
  import { getAgentById } from "../be/db";
3
3
  import type { RbacPrincipal } from "../rbac";
4
4
  import { getRequestAuth } from "../utils/request-auth-context";
5
+ import { jsonError } from "./utils";
5
6
 
6
7
  /**
7
8
  * The RBAC principal of an HTTP request, for a handler-side `can()` check.
@@ -16,6 +17,7 @@ export async function requestPrincipal(
16
17
  const auth = getRequestAuth(req);
17
18
  if (auth?.kind === "operator") return { kind: "operator" };
18
19
  if (auth?.kind === "user") return { kind: "user", userId: auth.userId };
20
+ if (auth?.kind === "guest") return { kind: "guest" };
19
21
  if (!myAgentId) return null;
20
22
  const agent = await getAgentById(myAgentId);
21
23
  return { kind: "agent", agentId: myAgentId, isLead: agent?.isLead ?? false };
@@ -34,8 +36,19 @@ export async function agentFirstPrincipal(
34
36
  ): Promise<RbacPrincipal> {
35
37
  const auth = getRequestAuth(req);
36
38
  if (auth?.kind === "user") return { kind: "user", userId: auth.userId };
39
+ if (auth?.kind === "guest") return { kind: "guest" };
37
40
  const agentId = auth?.kind === "agent" ? auth.agentId : myAgentId;
38
41
  if (!agentId) return { kind: "operator" };
39
42
  const agent = await getAgentById(agentId);
40
43
  return { kind: "agent", agentId, isLead: agent?.isLead === true };
41
44
  }
45
+
46
+ /**
47
+ * Refuse a guest page session on a handler that has no per-route guest rule.
48
+ * Writes a 403 and returns true when the request is a guest's.
49
+ */
50
+ export function rejectGuest(req: IncomingMessage, res: ServerResponse): boolean {
51
+ if (getRequestAuth(req)?.kind !== "guest") return false;
52
+ jsonError(res, "Forbidden", 403);
53
+ return true;
54
+ }
@@ -1047,6 +1047,10 @@ async function ensureConnectionAdmin(
1047
1047
  ): Promise<boolean> {
1048
1048
  const auth = getRequestAuth(req);
1049
1049
  if (auth?.kind === "operator" || auth?.kind === "user") return true;
1050
+ if (auth?.kind === "guest") {
1051
+ jsonError(res, "Forbidden", 403);
1052
+ return false;
1053
+ }
1050
1054
 
1051
1055
  const callerAgentId = agentId ?? singleHeader(req, "x-agent-id");
1052
1056
  const agent = callerAgentId ? await getAgentById(callerAgentId) : undefined;
@@ -1081,6 +1085,10 @@ async function ensureVerbAdmin(
1081
1085
  ): Promise<boolean> {
1082
1086
  const auth = getRequestAuth(req);
1083
1087
  if (auth?.kind === "operator" || auth?.kind === "user") return true;
1088
+ if (auth?.kind === "guest") {
1089
+ jsonError(res, "Forbidden", 403);
1090
+ return false;
1091
+ }
1084
1092
 
1085
1093
  const callerAgentId = agentId ?? singleHeader(req, "x-agent-id");
1086
1094
  const agent = callerAgentId ? await getAgentById(callerAgentId) : undefined;
@@ -23,6 +23,7 @@ import {
23
23
  upsertScriptRunJournalStep,
24
24
  } from "../be/db";
25
25
  import { explicitModelErrorForAgent } from "../be/model-validation";
26
+ import { refuseSourceWithSecrets } from "../be/scripts/source-secrets";
26
27
  import { lintWorkflowLabels } from "../script-workflows/label-lint";
27
28
  import { scriptRunMaxAgentTasks, scriptRunMaxSteps } from "../script-workflows/limits";
28
29
  import {
@@ -39,14 +40,15 @@ import {
39
40
  ScriptRunStatusSchema,
40
41
  TERMINAL_SCRIPT_RUN_STATUSES,
41
42
  } from "../types";
42
- import { getAppUrl } from "../utils/constants";
43
+ import { getAppUrl, getMcpBaseUrl } from "../utils/constants";
44
+ import { scrubSecrets } from "../utils/secret-scrubber";
43
45
  import {
44
46
  executeRawLlm,
45
47
  RawLlmConfigSchema,
46
48
  RawLlmOutputSchema,
47
49
  } from "../workflows/executors/raw-llm";
48
50
  import { route } from "./route-def";
49
- import { deriveApiBaseUrl, json, jsonError } from "./utils";
51
+ import { json, jsonError } from "./utils";
50
52
 
51
53
  const DEFAULT_SCRIPT_RUN_CONCURRENCY_CAP = 10;
52
54
 
@@ -369,6 +371,12 @@ export async function handleScriptRuns(
369
371
  const agent = await requireAgent(res, agentId);
370
372
  if (!agent) return true;
371
373
 
374
+ const secretRefusal = refuseSourceWithSecrets(parsed.body.source);
375
+ if (secretRefusal) {
376
+ json(res, secretRefusal, 400);
377
+ return true;
378
+ }
379
+
372
380
  const lint = lintWorkflowLabels(parsed.body.source);
373
381
  if (!lint.ok) {
374
382
  json(
@@ -418,7 +426,7 @@ export async function handleScriptRuns(
418
426
  const { run, existing } = creation;
419
427
 
420
428
  if (!existing && parsed.body.background) {
421
- startScriptRunProcess(run, deriveApiBaseUrl(req), bearerToken(req)).catch(async (err) => {
429
+ startScriptRunProcess(run, getMcpBaseUrl(), bearerToken(req)).catch(async (err) => {
422
430
  await updateScriptRun(run.id, {
423
431
  status: "failed",
424
432
  pid: null,
@@ -545,7 +553,7 @@ export async function handleScriptRuns(
545
553
  config: parsed.body.config ?? {},
546
554
  status: parsed.body.status,
547
555
  result: parsed.body.result,
548
- error: parsed.body.error,
556
+ error: parsed.body.error === undefined ? undefined : scrubSecrets(parsed.body.error),
549
557
  durationMs: parsed.body.durationMs,
550
558
  });
551
559
  return assertRunWithinLimits(run.id);
@@ -32,6 +32,7 @@ import {
32
32
  } from "../be/scripts/db";
33
33
  import { searchScripts } from "../be/scripts/embeddings";
34
34
  import { extractArgsJsonSchema } from "../be/scripts/extract-schema";
35
+ import { refuseSourceWithSecrets } from "../be/scripts/source-secrets";
35
36
  import {
36
37
  scriptSdkTypesWithGeneratedApis,
37
38
  scriptStdlibTypesWithGeneratedApis,
@@ -649,6 +650,12 @@ export async function handleScripts(
649
650
  }
650
651
  }
651
652
 
653
+ const secretRefusal = refuseSourceWithSecrets(parsed.body.source);
654
+ if (secretRefusal) {
655
+ json(res, secretRefusal, 400);
656
+ return true;
657
+ }
658
+
652
659
  const typecheck = await typecheckScript(parsed.body.source, { agentId: agent.id });
653
660
  if (!typecheck.ok) {
654
661
  json(
@@ -716,6 +723,14 @@ export async function handleScripts(
716
723
  req.headers["x-runtime-instance-id"],
717
724
  );
718
725
 
726
+ // Inline source is saved as a scratch script and recorded on the run row:
727
+ // refuse an embedded secret before it executes or persists.
728
+ const secretRefusal = parsed.body.source ? refuseSourceWithSecrets(parsed.body.source) : null;
729
+ if (secretRefusal) {
730
+ json(res, secretRefusal, 400);
731
+ return true;
732
+ }
733
+
719
734
  let source = parsed.body.source;
720
735
  let fsMode = parsed.body.fsMode;
721
736
  let namedScript: ScriptRecord | null = null;
@@ -185,8 +185,9 @@ export async function recomputeSessionCost(
185
185
  // Amp's own recorded cost (`amp threads usage`) is what it billed, including
186
186
  // subagent threads the token counts miss. The adapter sends it only when
187
187
  // every request was billed through Amp, so it wins over the token price,
188
- // which still fills the per-model breakdown.
189
- if (input.provider === "amp" && input.harnessCostUsd > 0) {
188
+ // which still fills the per-model breakdown. Grok's `costUsdTicks` is the
189
+ // same kind of report: what xAI billed, long-context tiers included.
190
+ if ((input.provider === "amp" || input.provider === "grok") && input.harnessCostUsd > 0) {
190
191
  const priced = await recomputeSessionCost({ ...input, harnessCostUsd: 0 }, lookupRate);
191
192
  return {
192
193
  totalCostUsd: input.harnessCostUsd,
package/src/http/tasks.ts CHANGED
@@ -75,6 +75,7 @@ import {
75
75
  import { ensure } from "../utils/business-use";
76
76
  import { getRequestAuth } from "../utils/request-auth-context";
77
77
  import { scrubSecrets } from "../utils/secret-scrubber";
78
+ import { rejectGuest } from "./request-principal";
78
79
  import { route } from "./route-def";
79
80
  import { jsonError, parseBody } from "./utils";
80
81
 
@@ -713,6 +714,8 @@ async function canSteerTask(
713
714
  } else if (auth?.kind === "user") {
714
715
  principal = { kind: "user", userId: auth.userId };
715
716
  verb = "task.steer.own";
717
+ } else if (auth?.kind === "guest") {
718
+ return false;
716
719
  } else {
717
720
  if (!myAgentId) return false;
718
721
  const agent = await getAgentById(myAgentId);
@@ -750,6 +753,8 @@ async function canActOnOwnTask(
750
753
  principal = { kind: "operator" };
751
754
  } else if (auth?.kind === "user") {
752
755
  principal = { kind: "user", userId: auth.userId };
756
+ } else if (auth?.kind === "guest") {
757
+ return false;
753
758
  } else {
754
759
  if (!myAgentId) return false;
755
760
  const agent = await getAgentById(myAgentId);
@@ -772,6 +777,7 @@ async function resolveTaskWritePrincipal(
772
777
  ): Promise<RbacPrincipal> {
773
778
  const auth = getRequestAuth(req);
774
779
  if (auth?.kind === "user") return { kind: "user", userId: auth.userId };
780
+ if (auth?.kind === "guest") return { kind: "guest" };
775
781
  const agentId = auth?.kind === "agent" ? auth.agentId : myAgentId;
776
782
  if (!agentId) return { kind: "operator" };
777
783
  const agent = await getAgentById(agentId);
@@ -787,6 +793,9 @@ export async function handleTasks(
787
793
  queryParams: URLSearchParams,
788
794
  myAgentId: string | undefined,
789
795
  ): Promise<boolean> {
796
+ if (pathSegments[0] === "api" && pathSegments[1] === "tasks" && rejectGuest(req, res)) {
797
+ return true;
798
+ }
790
799
  if (listTasks.match(req.method, pathSegments)) {
791
800
  const parsed = await listTasks.parse(req, res, pathSegments, queryParams);
792
801
  if (!parsed) return true;