@desplega.ai/agent-swarm 1.165.0 → 1.167.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 (296) hide show
  1. package/dist/acp-adapter-zpvy6321.js +34 -0
  2. package/dist/{actions-jna0b3wm.js → actions-fq2zrwtf.js} +30 -34
  3. package/dist/{amp-adapter-0eve81pz.js → amp-adapter-5gbk7501.js} +7 -7
  4. package/dist/{anthropic-messages-hst62ws9.js → anthropic-messages-13yn502s.js} +4 -4
  5. package/dist/{app-75hm5cfa.js → app-nctetn1e.js} +11 -11
  6. package/dist/{assistant-yzd51j33.js → assistant-0aq8gjvj.js} +31 -35
  7. package/dist/{azure-openai-responses-mh8tvf78.js → azure-openai-responses-9st34pfz.js} +5 -5
  8. package/dist/{boot-reembed-z158xq32.js → boot-reembed-1ze5853n.js} +14 -16
  9. package/dist/{boot-reembed-2ex2mcsh.js → boot-reembed-fmp5ww69.js} +15 -16
  10. package/dist/boot-scrub-sweep-cbp8xd4e.js +212 -0
  11. package/dist/{boot-seeds-2bmnh18p.js → boot-seeds-22tx8a53.js} +13 -12
  12. package/dist/{claude-adapter-jbj1mycz.js → claude-adapter-0jhgeh1d.js} +25 -25
  13. package/dist/{claude-managed-adapter-17ggz09t.js → claude-managed-adapter-tzwxy803.js} +5 -5
  14. package/dist/{claude-managed-setup-ptm1nzm2.js → claude-managed-setup-nrhfx5b0.js} +5 -5
  15. package/dist/{claude-sdk-session-9re1g7r2.js → claude-sdk-session-wh77rh83.js} +2632 -2614
  16. package/dist/{cli-zsh093hk.js → cli-014vns2y.js} +63 -1
  17. package/dist/{cli-qvfk83fy.js → cli-13xwcqvj.js} +2 -2
  18. package/dist/{cli-8gpbwy09.js → cli-1yrcfa5s.js} +5 -5
  19. package/dist/{cli-xtgtyafv.js → cli-577bxkf6.js} +212 -451
  20. package/dist/{cli-ma3zvh39.js → cli-5qkcdv8c.js} +6118 -5874
  21. package/dist/{cli-cwxhbf43.js → cli-5t5my0h8.js} +85 -2
  22. package/dist/{cli-zvckv750.js → cli-69egj654.js} +2 -2
  23. package/dist/{cli-9xb89xca.js → cli-6bpvv8h4.js} +9 -9
  24. package/dist/{cli-90v942q1.js → cli-6qxmjpem.js} +8 -8
  25. package/dist/{cli-9866bd07.js → cli-6vfz44sr.js} +1 -1
  26. package/dist/{cli-32bca7nr.js → cli-7avy4v8w.js} +1 -1
  27. package/dist/{cli-xb3c6aat.js → cli-7mhy3ths.js} +21 -18
  28. package/dist/{cli-q38n4bq0.js → cli-95zs84qw.js} +3 -3
  29. package/dist/cli-a35hzdve.js +33 -0
  30. package/dist/{cli-x3x0qwky.js → cli-bxqez1p0.js} +51 -23
  31. package/dist/{cli-prt9emct.js → cli-c2tpw9ck.js} +3 -3
  32. package/dist/{cli-0n4fasz0.js → cli-c8r99akr.js} +16 -16
  33. package/dist/{cli-5anb0ev3.js → cli-cexctgn5.js} +89 -26
  34. package/dist/{cli-bns5c8k9.js → cli-cf2vgn9d.js} +105 -91
  35. package/dist/{cli-ng97fjq7.js → cli-cmxkbzzs.js} +33 -7
  36. package/dist/{cli-nexwh8zg.js → cli-g7k7zbcz.js} +12 -5
  37. package/dist/{cli-mmzwwptd.js → cli-hf5s9pgf.js} +1 -1
  38. package/dist/{cli-nvvfd8bq.js → cli-jbzbr36n.js} +4091 -3793
  39. package/dist/{cli-zn2v3t8v.js → cli-jvmtxqtc.js} +27 -15
  40. package/dist/{cli-pkke6geq.js → cli-km4x66j0.js} +83 -65
  41. package/dist/cli-kw0jcfje.js +516 -0
  42. package/dist/{cli-28smfvbn.js → cli-m7vmeepg.js} +32 -19
  43. package/dist/{cli-hpryr6em.js → cli-mb75wpxf.js} +1 -1
  44. package/dist/{cli-qdv8kgz7.js → cli-mkdsy1nq.js} +14018 -3333
  45. package/dist/{cli-b0f70z24.js → cli-mn8qkd8v.js} +1 -1
  46. package/dist/{cli-420bdgw0.js → cli-mv2520nm.js} +1 -1
  47. package/dist/{cli-n9ehc28t.js → cli-mxry91sa.js} +31 -10
  48. package/dist/{cli-wfajtmf7.js → cli-mxvnnv5q.js} +596 -13
  49. package/dist/cli-nkqjj42q.js +104 -0
  50. package/dist/{cli-e7t4snpa.js → cli-p8rt8spp.js} +5 -5
  51. package/dist/{cli-gh5yqzpe.js → cli-pkj1w46r.js} +12 -7
  52. package/dist/cli-q1vnz8wv.js +96 -0
  53. package/dist/{cli-fj86xr3c.js → cli-qd97v6z2.js} +2 -2
  54. package/dist/{cli-ba66wyhy.js → cli-qzmzwegq.js} +60 -20
  55. package/dist/{cli-5b2fjjzh.js → cli-r7eeddkq.js} +14 -9
  56. package/dist/{cli-8ssjez7e.js → cli-rhy7z1rp.js} +22 -11
  57. package/dist/{cli-4mevd1hf.js → cli-s9y81s7j.js} +5 -5
  58. package/dist/{cli-59jn5twa.js → cli-tc5zec7v.js} +7 -7
  59. package/dist/{cli-ttbsse3f.js → cli-tmhsxhb8.js} +1 -1
  60. package/dist/{cli-k6xx3pve.js → cli-tx2zvn23.js} +4 -4
  61. package/dist/{cli-pjxnr1jn.js → cli-ty4bzkqr.js} +5 -5
  62. package/dist/{cli-nfygw434.js → cli-v1pwxreq.js} +55 -52
  63. package/dist/{cli-ftry7975.js → cli-v1w7pbd8.js} +3 -2
  64. package/dist/{cli-10zmp1p7.js → cli-vr5avem2.js} +27 -32
  65. package/dist/{cli-bzr0ftht.js → cli-wnb9b2g3.js} +19 -0
  66. package/dist/cli-wt7ccy4r.js +2654 -0
  67. package/dist/{cli-50eg13rj.js → cli-wydz3feg.js} +549 -321
  68. package/dist/{cli-krqm5f11.js → cli-xezn9nkh.js} +16 -13
  69. package/dist/{cli-8kt0bmgx.js → cli-xf713zz0.js} +2 -2
  70. package/dist/{cli-x7afk5wm.js → cli-xvc3q9br.js} +1 -1
  71. package/dist/{cli-9s3h836g.js → cli-xvdgasgy.js} +1 -1
  72. package/dist/{cli-j0pdrmwg.js → cli-y0kjpfm9.js} +3 -3
  73. package/dist/{cli-6zcfw0x7.js → cli-y195hv1a.js} +1 -1
  74. package/dist/{cli-amwhs8h2.js → cli-ykt8f7va.js} +22 -9
  75. package/dist/{cli-1kzfvd1z.js → cli-ymmc1zxg.js} +15 -11
  76. package/dist/{cli-xnzrzets.js → cli-ypgba1a8.js} +2 -2
  77. package/dist/{cli-bdv39kgj.js → cli-z1dqhc9y.js} +31 -20
  78. package/dist/{cli-fw6vdcq5.js → cli-zp67qg5x.js} +34 -1
  79. package/dist/{cli-qbksc292.js → cli-zy39d433.js} +2 -0
  80. package/dist/cli.js +36 -32
  81. package/dist/{cloudflare-workers-ai-system-one-tt484t61.js → cloudflare-workers-ai-system-one-sta45jf9.js} +7 -5
  82. package/dist/{codex-adapter-3qnxshje.js → codex-adapter-cgesnjkr.js} +20 -20
  83. package/dist/{codex-login-brkxhhdg.js → codex-login-g1cyt5zm.js} +2 -2
  84. package/dist/{codex-session-runner-612wnapp.js → codex-session-runner-8kd7ddhr.js} +20 -20
  85. package/dist/{commands-2ybfcxkj.js → commands-h6zaaxym.js} +11 -10
  86. package/dist/{cursor-adapter-p3jgdehv.js → cursor-adapter-v8mm6bp5.js} +7 -7
  87. package/dist/{db-f4ans0jj.js → db-qg3myn53.js} +13 -10
  88. package/dist/{devin-adapter-dhzt01ff.js → devin-adapter-bffjj5n4.js} +2 -2
  89. package/dist/{dispatcher-pk182apd.js → dispatcher-7vnwj9qw.js} +28 -32
  90. package/dist/{dsh-adapter-7twaeps1.js → dsh-adapter-tz6hfp5r.js} +7 -7
  91. package/dist/{e2b-y8t3jzyz.js → e2b-nd50eccg.js} +1 -1
  92. package/dist/{easter-egg-3d-mtpnw06s.js → easter-egg-3d-66zxervv.js} +2 -2
  93. package/dist/{event-bus-w7ac4n3c.js → event-bus-vnfthafg.js} +2 -2
  94. package/dist/{execute-s4ccjq3n.js → execute-h4jgevyw.js} +61 -11
  95. package/dist/{google-generative-ai-d6nphg3q.js → google-generative-ai-b8zxrpax.js} +5 -5
  96. package/dist/{google-vertex-cpshkref.js → google-vertex-nrdd697t.js} +5 -5
  97. package/dist/grok-adapter-jdt2dz2s.js +39 -0
  98. package/dist/{handlers-g2wnr4t9.js → handlers-2qtkpncr.js} +31 -35
  99. package/dist/{hook-f7jt9vnm.js → hook-51yfcgsv.js} +48 -39
  100. package/dist/{hook-bbdpnkb4.js → hook-qnbdgwvd.js} +15 -17
  101. package/dist/{http-w40q4w94.js → http-jsmtag08.js} +536 -231
  102. package/dist/{index-056j5kzn.js → index-2t9kesn1.js} +13 -12
  103. package/dist/{index-cj1r9q83.js → index-bt8wkv1w.js} +29 -33
  104. package/dist/{index-7n70jwbb.js → index-cx1sb9bq.js} +42 -35
  105. package/dist/{index-9bfssf54.js → index-ekywyv2v.js} +28 -32
  106. package/dist/{index-fdk1t4d6.js → index-n0rxa4ca.js} +11962 -9900
  107. package/dist/{index-k8xc083e.js → index-t4g06w93.js} +28 -32
  108. package/dist/{keepalive-1tj10tf4.js → keepalive-4aezx4bj.js} +16 -12
  109. package/dist/{lead-st0bg7v4.js → lead-r44bkpm5.js} +52 -56
  110. package/dist/{llama-cpp-classify-tgj35k8d.js → llama-cpp-classify-6erb7wwv.js} +3 -1
  111. package/dist/{maintenance-nz71zats.js → maintenance-grz5jrzh.js} +12 -13
  112. package/dist/{mistral-conversations-8445ddjn.js → mistral-conversations-059y51zt.js} +4 -4
  113. package/dist/{node-f0ykebbv.js → node-qm9eb6z0.js} +25 -7
  114. package/dist/{oauth-refresh-sweep-aefzwvvq.js → oauth-refresh-sweep-wm4ctjfj.js} +15 -11
  115. package/dist/{onboard-kgpr9mbn.js → onboard-v60zgqf0.js} +2 -2
  116. package/dist/{openai-codex-responses-9ynm88zy.js → openai-codex-responses-jzxkta9b.js} +8 -7
  117. package/dist/{openai-completions-nhqwjmaw.js → openai-completions-663yncfq.js} +4 -4
  118. package/dist/openai-decisions-vj9nkpf8.js +162 -0
  119. package/dist/{openai-responses-xx1c4kgm.js → openai-responses-m3g43rrt.js} +5 -5
  120. package/dist/{opencode-adapter-0c8e70dk.js → opencode-adapter-yjyqynzf.js} +8 -8
  121. package/dist/{openrouter-images-8yx3487g.js → openrouter-images-kr60n6b9.js} +1 -1
  122. package/dist/{otel-impl-x8zp4y5a.js → otel-impl-ghh4dyf8.js} +2 -2
  123. package/dist/{pi-messages-vxdt8v7j.js → pi-messages-xszk613n.js} +1 -1
  124. package/dist/{pi-mono-adapter-t280ah5c.js → pi-mono-adapter-0eg8kw2c.js} +69 -52
  125. package/dist/{pricing-refresh-9e0q7cwk.js → pricing-refresh-mjrqvnvw.js} +12 -11
  126. package/dist/{rbac-roles-790j3ga8.js → rbac-roles-axv4d74x.js} +11 -10
  127. package/dist/{rbac-roles-kcp6zge2.js → rbac-roles-fd5tskj9.js} +11 -10
  128. package/dist/{render-v2-tch4nefj.js → render-v2-k50j33qd.js} +11 -11
  129. package/dist/{runtime-v2t1r79e.js → runtime-h9ha1mq6.js} +4 -4
  130. package/dist/{setup-rcm303ns.js → setup-4syh0pd2.js} +2 -2
  131. package/dist/{typesafe-system-one-datfr82p.js → typesafe-system-one-e988vty7.js} +7 -5
  132. package/dist/{virtual-modules-wm8mxdxx.js → virtual-modules-ftr9kwe8.js} +11 -11
  133. package/dist/{work-objects-ctg7t70w.js → work-objects-jcdbg8rr.js} +32 -36
  134. package/dist/{worker-5dp0xm55.js → worker-x8j8c9sx.js} +52 -56
  135. package/dist/{x-v3yw66zs.js → x-ma4gnfgy.js} +2 -2
  136. package/openapi.json +5 -2
  137. package/package.json +15 -11
  138. package/src/be/automation-preflight.ts +9 -4
  139. package/src/be/boot-scrub-sweep.ts +302 -0
  140. package/src/be/crypto/key-bootstrap.ts +1 -1
  141. package/src/be/db/approvals.ts +15 -1
  142. package/src/be/db/codex-oauth-device-flows.ts +40 -0
  143. package/src/be/db/config-secrets.ts +31 -0
  144. package/src/be/db/runtime.ts +4 -0
  145. package/src/be/db/script-runs.ts +15 -0
  146. package/src/be/db/tasks/write.ts +1 -1
  147. package/src/be/db-queries/mcp-oauth.ts +5 -0
  148. package/src/be/db-queries/oauth.ts +77 -0
  149. package/src/be/db.ts +162 -59
  150. package/src/be/events.ts +17 -1
  151. package/src/be/memory/index-content.ts +2 -2
  152. package/src/be/memory/providers/openai-embedding.ts +7 -2
  153. package/src/be/memory/providers/sqlite-store.ts +63 -11
  154. package/src/be/memory/raters/llm-summarizer.ts +7 -2
  155. package/src/be/memory/types.ts +14 -0
  156. package/src/be/migrations/201_workflow_replay_columns.sql +10 -0
  157. package/src/be/migrations/202_codex_oauth_device_flows.sql +24 -0
  158. package/src/be/modelsdev-cache.json +61 -0
  159. package/src/be/scripts/db.ts +28 -4
  160. package/src/be/scripts/source-secrets.ts +37 -0
  161. package/src/be/scrub-json.ts +32 -0
  162. package/src/be/sealed-json.ts +59 -0
  163. package/src/be/secret-registry.ts +131 -0
  164. package/src/be/seed-pricing.ts +84 -53
  165. package/src/be/swarm-config-guard.ts +13 -0
  166. package/src/be/workflow-replay.ts +77 -0
  167. package/src/commands/provider-credentials.ts +28 -5
  168. package/src/commands/runner.ts +7 -12
  169. package/src/heartbeat/heartbeat.ts +91 -19
  170. package/src/hooks/hook.ts +72 -27
  171. package/src/http/codex-oauth-device.ts +8 -10
  172. package/src/http/db-query-bounded.ts +3 -0
  173. package/src/http/db-query-shared.ts +107 -15
  174. package/src/http/db-query.ts +10 -2
  175. package/src/http/index.ts +23 -6
  176. package/src/http/mcp.ts +5 -0
  177. package/src/http/onboarding.ts +2 -0
  178. package/src/http/script-runs.ts +9 -1
  179. package/src/http/scripts.ts +15 -0
  180. package/src/http/workflows.ts +3 -2
  181. package/src/oauth/mcp-wrapper.ts +1 -1
  182. package/src/providers/acp-adapter.ts +25 -11
  183. package/src/providers/acp-target-catalog.ts +35 -1
  184. package/src/providers/acp-targets.ts +78 -7
  185. package/src/providers/amp-adapter.ts +1 -1
  186. package/src/providers/claude-adapter.ts +26 -0
  187. package/src/providers/codex-adapter.ts +30 -3
  188. package/src/providers/codex-oauth/auth-json.ts +21 -4
  189. package/src/providers/codex-oauth/types.ts +2 -0
  190. package/src/providers/cursor-adapter.ts +1 -1
  191. package/src/providers/dsh-adapter.ts +10 -1
  192. package/src/providers/grok-adapter.ts +18 -2
  193. package/src/providers/opencode-adapter.ts +21 -5
  194. package/src/providers/pi-mono-extension.ts +56 -25
  195. package/src/providers/pricing-sources.md +2 -0
  196. package/src/script-workflows/supervisor.ts +11 -2
  197. package/src/scripts-runtime/eval-harness.ts +2 -12
  198. package/src/scripts-runtime/executors/quickjs-bundle.ts +152 -0
  199. package/src/scripts-runtime/executors/quickjs-prelude.ts +285 -0
  200. package/src/scripts-runtime/executors/quickjs-runner.ts +580 -0
  201. package/src/scripts-runtime/executors/quickjs-worker.ts +81 -0
  202. package/src/scripts-runtime/executors/quickjs.ts +196 -0
  203. package/src/scripts-runtime/executors/registry.ts +2 -0
  204. package/src/scripts-runtime/executors/sourcemap.ts +79 -0
  205. package/src/scripts-runtime/script-error-hints.ts +14 -0
  206. package/src/server.ts +66 -4
  207. package/src/tests/acp-adapter.test.ts +277 -12
  208. package/src/tests/boot-scrub-sweep.test.ts +390 -0
  209. package/src/tests/check-text-columns.test.ts +145 -0
  210. package/src/tests/ci-unit-test-report.test.ts +73 -0
  211. package/src/tests/claude-default-route.test.ts +75 -0
  212. package/src/tests/claude-hook-output.test.ts +233 -0
  213. package/src/tests/claude-sdk-process.fixture.ts +13 -2
  214. package/src/tests/codex-adapter-pool-auth-revalidate.test.ts +4 -2
  215. package/src/tests/codex-adapter.test.ts +166 -26
  216. package/src/tests/codex-oauth-auth-timestamps.test.ts +76 -0
  217. package/src/tests/codex-oauth-device.test.ts +23 -37
  218. package/src/tests/codex-oauth.test.ts +4 -1
  219. package/src/tests/context-window.test.ts +2 -2
  220. package/src/tests/credential-check.test.ts +51 -1
  221. package/src/tests/db-query-denylist.test.ts +202 -0
  222. package/src/tests/db-task-read-contract.test.ts +1 -0
  223. package/src/tests/db-task-write-contract.test.ts +1 -0
  224. package/src/tests/dsh-adapter.test.ts +23 -1
  225. package/src/tests/enabled-tools.test.ts +269 -0
  226. package/src/tests/entrypoint-acp-target-binary.test.ts +31 -1
  227. package/src/tests/entrypoint-codex-oauth-seed.test.ts +12 -0
  228. package/src/tests/events-scrub.test.ts +88 -0
  229. package/src/tests/fixtures/secret-corpus.ts +320 -0
  230. package/src/tests/grok-adapter.test.ts +13 -0
  231. package/src/tests/haiku-5-5-pricing.test.ts +38 -0
  232. package/src/tests/heartbeat-checklist.test.ts +158 -1
  233. package/src/tests/onboarding.test.ts +44 -0
  234. package/src/tests/opencode-adapter.test.ts +66 -3
  235. package/src/tests/opencode-plugin.test.ts +37 -0
  236. package/src/tests/openrouter-attribution-call-sites.test.ts +204 -0
  237. package/src/tests/openrouter-base-url.test.ts +30 -39
  238. package/src/tests/pi-mono-extension.test.ts +161 -2
  239. package/src/tests/pricing-refresh.test.ts +70 -1
  240. package/src/tests/reasoning-effort.test.ts +3 -1
  241. package/src/tests/script-connections.test.ts +5 -0
  242. package/src/tests/script-executor-conformance.test.ts +3 -0
  243. package/src/tests/script-executor-quickjs.test.ts +442 -0
  244. package/src/tests/script-executor-registry.test.ts +6 -1
  245. package/src/tests/script-runs-http.test.ts +88 -1
  246. package/src/tests/script-runs-scrub.test.ts +275 -0
  247. package/src/tests/scripts-http.test.ts +82 -1
  248. package/src/tests/scrubbed-text-12e.test.ts +289 -0
  249. package/src/tests/sealed-key-guard.test.ts +166 -0
  250. package/src/tests/secret-canary-sweep.test.ts +958 -0
  251. package/src/tests/secret-registry.test.ts +355 -0
  252. package/src/tests/secret-rules.test.ts +230 -0
  253. package/src/tests/secret-scrubber.test.ts +3 -2
  254. package/src/tests/telemetry-trigger.test.ts +12 -1
  255. package/src/tests/workflow-llm.test.ts +14 -0
  256. package/src/tests/workflow-replay.test.ts +518 -0
  257. package/src/tests/workflow-scrub.test.ts +242 -0
  258. package/src/tests/workflow-system-one-decision-providers.test.ts +33 -0
  259. package/src/tools/db-query.ts +1 -1
  260. package/src/tools/inject-learning.ts +1 -1
  261. package/src/tools/request-human-input.ts +7 -2
  262. package/src/tools/workflows/get-workflow-run.ts +3 -2
  263. package/src/types.ts +1 -1
  264. package/src/utils/enabled-tools.ts +27 -0
  265. package/src/utils/internal-ai/complete-structured.ts +11 -3
  266. package/src/utils/openrouter-base-url.ts +23 -0
  267. package/src/utils/secret-rules.generated.ts +2055 -0
  268. package/src/utils/secret-scrubber.ts +363 -29
  269. package/src/workflows/engine.ts +39 -4
  270. package/src/workflows/executors/raw-llm.ts +1 -0
  271. package/src/workflows/executors/system-one-decision.ts +2 -0
  272. package/src/workflows/executors/validate.ts +1 -0
  273. package/src/workflows/executors/workflow-llm.ts +9 -2
  274. package/src/workflows/recovery.ts +10 -1
  275. package/src/workflows/resume.ts +62 -3
  276. package/src/workflows/retry-poller.ts +3 -0
  277. package/templates/skills/agent-browser/SKILL.md +2 -0
  278. package/templates/skills/agent-browser/content.md +2 -0
  279. package/templates/skills/code-quality/content.md +1 -0
  280. package/templates/skills/heartbeat-runbook/content.md +1 -1
  281. package/templates/skills/work-on-task/SKILL.md +4 -0
  282. package/templates/skills/work-on-task/content.md +4 -0
  283. package/templates/skills/workflow-iterate/content.md +9 -0
  284. package/dist/acp-adapter-w6zws6me.js +0 -23
  285. package/dist/boot-scrub-logs-246yn076.js +0 -99
  286. package/dist/cli-3a4m1aws.js +0 -572
  287. package/dist/cli-3dpj7atn.js +0 -11
  288. package/dist/cli-a87b04k8.js +0 -951
  289. package/dist/cli-cvk6yzw5.js +0 -40
  290. package/dist/cli-gvcgmx69.js +0 -183
  291. package/dist/cli-h1spv8v4.js +0 -427
  292. package/dist/cli-jqt9hrnz.js +0 -168
  293. package/dist/cli-yhmwmv0g.js +0 -9281
  294. package/dist/grok-adapter-yt9pab3k.js +0 -29
  295. package/src/be/boot-scrub-logs.ts +0 -132
  296. package/dist/{codex-hook-d2rkvf39.js → codex-hook-15dgc4cb.js} +1 -1
package/src/be/db.ts CHANGED
@@ -149,7 +149,7 @@ import { activeModelBlock, type ModelFamily } from "../utils/model-rate-limit-wi
149
149
  import { getCurrentRequestUserId } from "../utils/request-auth-context";
150
150
  import {
151
151
  registerSensitiveKeyName,
152
- registerVolatileSecret,
152
+ type ScrubbedText,
153
153
  scrubSecrets,
154
154
  } from "../utils/secret-scrubber";
155
155
  import {
@@ -178,6 +178,7 @@ import {
178
178
  type ApprovalRequestListFilters,
179
179
  type ApprovalVote,
180
180
  approvalRequestListClause,
181
+ scrubApprovalVotesJson,
181
182
  } from "./db/approvals";
182
183
  import {
183
184
  computeContentHash,
@@ -197,9 +198,13 @@ import {
197
198
  rowToAgentTaskSummary,
198
199
  } from "./db/tasks/read";
199
200
  import { configureTaskWriteDependencies, failTask } from "./db/tasks/write";
201
+ import { scrubJsonValue } from "./scrub-json";
202
+ import { openSealedJson, sealedJsonForDisplay, sealJson } from "./sealed-json";
203
+ import { configSecretName, registerStoredSecret } from "./secret-registry";
200
204
  import { promotePendingSteeringForTask } from "./steering";
201
205
  import { isInternalConfigKey, isReservedConfigKey, reservedKeyError } from "./swarm-config-guard";
202
206
  import { emitTaskStarted } from "./task-lifecycle-events";
207
+ import { defineWorkflowPayload, type WorkflowPayloadView } from "./workflow-replay";
203
208
 
204
209
  export {
205
210
  refreshActiveSessionOnActivity,
@@ -257,6 +262,7 @@ export {
257
262
  getContextVersionHistory,
258
263
  getLatestContextVersion,
259
264
  } from "./db/context-versions";
265
+ export { getScriptRunExecutionArgs } from "./db/script-runs";
260
266
 
261
267
  configureAgentDependencies({
262
268
  createLogEntry: (entry) => createLogEntry(entry),
@@ -2182,7 +2188,7 @@ export async function createLogEntry(entry: {
2182
2188
  entry.eventType,
2183
2189
  entry.agentId ?? null,
2184
2190
  entry.taskId ?? null,
2185
- entry.oldValue ?? null,
2191
+ entry.oldValue ? scrubSecrets(entry.oldValue) : null,
2186
2192
  entry.newValue ? scrubSecrets(entry.newValue) : null,
2187
2193
  metaJson ? scrubSecrets(metaJson) : null,
2188
2194
  ],
@@ -2811,7 +2817,8 @@ export async function createTaskExtended(
2811
2817
  assetKey,
2812
2818
  options?.agentId ?? null,
2813
2819
  options?.creatorAgentId ?? null,
2814
- task,
2820
+ // Free text from every task source; scrub at the single INSERT.
2821
+ scrubSecrets(task),
2815
2822
  status,
2816
2823
  options?.source ?? "mcp",
2817
2824
  options?.routingReason ?? null,
@@ -3883,7 +3890,11 @@ export async function postMessage(
3883
3890
 
3884
3891
  // Detect /task prefix - only create tasks when explicitly requested
3885
3892
  const isTaskMessage = content.trimStart().startsWith("/task ");
3886
- const messageContent = isTaskMessage ? content.replace(/^\s*\/task\s+/, "") : content;
3893
+ // Scrubbed once here: the row, the task description built from it and the
3894
+ // appended task-link UPDATE below all derive from this value.
3895
+ const messageContent = scrubSecrets(
3896
+ isTaskMessage ? content.replace(/^\s*\/task\s+/, "") : content,
3897
+ );
3887
3898
 
3888
3899
  const row = await getDbClient().get<ChannelMessageRow>(
3889
3900
  `INSERT INTO channel_messages (id, channelId, agentId, content, replyToId, mentions, createdAt)
@@ -4597,7 +4608,7 @@ export async function createSessionLogs(logs: {
4597
4608
  lines: string[];
4598
4609
  }): Promise<void> {
4599
4610
  // Bounded batches, one short transaction each (reference shape:
4600
- // src/be/boot-scrub-logs.ts): worker log batches have unbounded line
4611
+ // src/be/boot-scrub-sweep.ts): worker log batches have unbounded line
4601
4612
  // counts, and a single transaction across thousands of scrub+INSERT
4602
4613
  // iterations would hold the global write lock — and the event loop — for
4603
4614
  // the whole sweep. The regex scrub runs outside the transaction so only
@@ -5552,12 +5563,12 @@ export async function createInboxMessage(
5552
5563
  [
5553
5564
  id,
5554
5565
  agentId,
5555
- content,
5566
+ scrubSecrets(content),
5556
5567
  options?.source ?? "slack",
5557
5568
  options?.slackChannelId ?? null,
5558
5569
  options?.slackThreadTs ?? null,
5559
5570
  options?.slackUserId ?? null,
5560
- options?.matchedText ?? null,
5571
+ options?.matchedText == null ? null : scrubSecrets(options.matchedText),
5561
5572
  now,
5562
5573
  now,
5563
5574
  ],
@@ -5633,7 +5644,7 @@ export async function markInboxMessageResponded(
5633
5644
  const now = new Date().toISOString();
5634
5645
  const row = await getDbClient().get<InboxMessageRow>(
5635
5646
  "UPDATE inbox_messages SET status = 'responded', responseText = ?, lastUpdatedAt = ? WHERE id = ? AND status IN ('unread', 'processing') RETURNING *",
5636
- [responseText, now, id],
5647
+ [scrubSecrets(responseText), now, id],
5637
5648
  );
5638
5649
  return row ? rowToInboxMessage(row) : null;
5639
5650
  }
@@ -6030,10 +6041,10 @@ export async function createScheduledTask(data: CreateScheduledTaskData): Promis
6030
6041
  id,
6031
6042
  normalizeAssetKey(data.key ?? defaultAssetKey("schedule", id)),
6032
6043
  data.name,
6033
- data.description ?? null,
6044
+ data.description == null ? null : scrubSecrets(data.description),
6034
6045
  data.cronExpression ?? null,
6035
6046
  data.intervalMs ?? null,
6036
- data.taskTemplate ?? null,
6047
+ data.taskTemplate == null ? null : scrubSecrets(data.taskTemplate),
6037
6048
  data.taskType ?? null,
6038
6049
  JSON.stringify(data.tags ?? []),
6039
6050
  data.priority ?? 50,
@@ -6116,7 +6127,7 @@ export async function updateScheduledTask(
6116
6127
  }
6117
6128
  if (data.description !== undefined) {
6118
6129
  updates.push("description = ?");
6119
- params.push(data.description);
6130
+ params.push(data.description == null ? null : scrubSecrets(data.description));
6120
6131
  }
6121
6132
  if (data.cronExpression !== undefined) {
6122
6133
  updates.push("cronExpression = ?");
@@ -6128,7 +6139,7 @@ export async function updateScheduledTask(
6128
6139
  }
6129
6140
  if (data.taskTemplate !== undefined) {
6130
6141
  updates.push("taskTemplate = ?");
6131
- params.push(data.taskTemplate);
6142
+ params.push(data.taskTemplate == null ? null : scrubSecrets(data.taskTemplate));
6132
6143
  }
6133
6144
  if (data.taskType !== undefined) {
6134
6145
  updates.push("taskType = ?");
@@ -6172,7 +6183,7 @@ export async function updateScheduledTask(
6172
6183
  }
6173
6184
  if (data.lastErrorMessage !== undefined) {
6174
6185
  updates.push("lastErrorMessage = ?");
6175
- params.push(data.lastErrorMessage);
6186
+ params.push(data.lastErrorMessage == null ? null : scrubSecrets(data.lastErrorMessage));
6176
6187
  }
6177
6188
  if (data.model !== undefined) {
6178
6189
  updates.push("model = ?");
@@ -6614,7 +6625,7 @@ export async function upsertSwarmConfig(data: {
6614
6625
  }
6615
6626
 
6616
6627
  if (config.isSecret) {
6617
- registerVolatileSecret(config.value, `config:${config.key}`);
6628
+ registerStoredSecret(config.value, configSecretName(config.key));
6618
6629
  registerSensitiveKeyName(config.key);
6619
6630
  }
6620
6631
 
@@ -7655,6 +7666,8 @@ type WorkflowRunRow = {
7655
7666
  triggerData: string | null;
7656
7667
  /** Absent from list rows: see `WORKFLOW_RUN_SUMMARY_COLUMNS`. */
7657
7668
  context?: string | null;
7669
+ /** Sealed exact `context` (migration 201). Absent from list rows. */
7670
+ context_replay?: string | null;
7658
7671
  error: string | null;
7659
7672
  created_by: string | null;
7660
7673
  startedAt: string;
@@ -7662,19 +7675,28 @@ type WorkflowRunRow = {
7662
7675
  finishedAt: string | null;
7663
7676
  };
7664
7677
 
7665
- function rowToWorkflowRun(row: WorkflowRunRow): WorkflowRun {
7666
- return {
7678
+ /**
7679
+ * `view` picks the `context` copy: `replay` (default) is the exact sealed value
7680
+ * the engine resumes from; `display` is the scrubbed column for API, MCP and UI
7681
+ * responses. See `src/be/workflow-replay.ts`.
7682
+ */
7683
+ function rowToWorkflowRun(row: WorkflowRunRow, view: WorkflowPayloadView = "replay"): WorkflowRun {
7684
+ const run: WorkflowRun = {
7667
7685
  id: row.id,
7668
7686
  workflowId: row.workflowId,
7669
7687
  status: row.status as WorkflowRunStatus,
7670
7688
  triggerData: row.triggerData ? JSON.parse(row.triggerData) : undefined,
7671
- context: row.context ? (JSON.parse(row.context) as Record<string, unknown>) : undefined,
7689
+ context: undefined,
7672
7690
  error: row.error ?? undefined,
7673
7691
  createdBy: row.created_by ?? undefined,
7674
7692
  startedAt: normalizeDateRequired(row.startedAt),
7675
7693
  lastUpdatedAt: normalizeDateRequired(row.lastUpdatedAt),
7676
7694
  finishedAt: normalizeDate(row.finishedAt) ?? undefined,
7677
7695
  };
7696
+ return defineWorkflowPayload(run, "context", row.context, row.context_replay, view, {
7697
+ runId: row.id,
7698
+ target: "its context",
7699
+ });
7678
7700
  }
7679
7701
 
7680
7702
  export async function createWorkflowRun(data: {
@@ -7692,7 +7714,7 @@ export async function createWorkflowRun(data: {
7692
7714
  data.workflowId,
7693
7715
  data.triggerType ?? "manual",
7694
7716
  now,
7695
- data.triggerData ? JSON.stringify(data.triggerData) : null,
7717
+ data.triggerData ? scrubJsonValue(data.triggerData) : null,
7696
7718
  data.createdBy ?? null,
7697
7719
  ],
7698
7720
  );
@@ -7700,11 +7722,14 @@ export async function createWorkflowRun(data: {
7700
7722
  return rowToWorkflowRun(row);
7701
7723
  }
7702
7724
 
7703
- export async function getWorkflowRun(id: string): Promise<WorkflowRun | null> {
7725
+ export async function getWorkflowRun(
7726
+ id: string,
7727
+ view: WorkflowPayloadView = "replay",
7728
+ ): Promise<WorkflowRun | null> {
7704
7729
  const row = await getDbClient().get<WorkflowRunRow>("SELECT * FROM workflow_runs WHERE id = ?", [
7705
7730
  id,
7706
7731
  ]);
7707
- return row ? rowToWorkflowRun(row) : null;
7732
+ return row ? rowToWorkflowRun(row, view) : null;
7708
7733
  }
7709
7734
 
7710
7735
  function emitWorkflowTerminalTelemetry(run: WorkflowRun): void {
@@ -7746,12 +7771,15 @@ export async function updateWorkflowRun(
7746
7771
  params.push(data.status);
7747
7772
  }
7748
7773
  if (data.context !== undefined) {
7749
- updates.push("context = ?");
7750
- params.push(JSON.stringify(data.context));
7774
+ // Resume/retry/recovery rebuild the live ctx (resolved `secret.*` inputs
7775
+ // included, which are never re-resolved) from the sealed exact copy; the
7776
+ // scrubbed column serves display and SQL filters.
7777
+ updates.push("context = ?", "context_replay = ?");
7778
+ params.push(scrubJsonValue(data.context), sealJson(data.context));
7751
7779
  }
7752
7780
  if (data.error !== undefined) {
7753
7781
  updates.push("error = ?");
7754
- params.push(data.error);
7782
+ params.push(data.error === null ? null : scrubSecrets(data.error));
7755
7783
  }
7756
7784
  if (data.finishedAt !== undefined) {
7757
7785
  updates.push("finishedAt = ?");
@@ -7833,7 +7861,8 @@ export async function listWorkflowRuns(
7833
7861
  ORDER BY startedAt DESC, id DESC${pagination}`,
7834
7862
  params,
7835
7863
  );
7836
- return rows.map(rowToWorkflowRun);
7864
+ // List rows are display-only (runs API, list-workflow-runs tool).
7865
+ return rows.map((row) => rowToWorkflowRun(row, "display"));
7837
7866
  }
7838
7867
 
7839
7868
  export async function countWorkflowRuns(
@@ -7886,6 +7915,8 @@ type WorkflowRunStepRow = {
7886
7915
  status: string;
7887
7916
  input: string | null;
7888
7917
  output: string | null;
7918
+ /** Sealed exact `output` (migration 201). */
7919
+ output_replay: string | null;
7889
7920
  error: string | null;
7890
7921
  startedAt: string;
7891
7922
  finishedAt: string | null;
@@ -7897,15 +7928,19 @@ type WorkflowRunStepRow = {
7897
7928
  nextPort: string | null;
7898
7929
  };
7899
7930
 
7900
- function rowToWorkflowRunStep(row: WorkflowRunStepRow): WorkflowRunStep {
7901
- return {
7931
+ /** `view` picks the `output` copy, as in `rowToWorkflowRun`. */
7932
+ function rowToWorkflowRunStep(
7933
+ row: WorkflowRunStepRow,
7934
+ view: WorkflowPayloadView = "replay",
7935
+ ): WorkflowRunStep {
7936
+ const step: WorkflowRunStep = {
7902
7937
  id: row.id,
7903
7938
  runId: row.runId,
7904
7939
  nodeId: row.nodeId,
7905
7940
  nodeType: row.nodeType,
7906
7941
  status: row.status as WorkflowRunStepStatus,
7907
7942
  input: row.input ? JSON.parse(row.input) : undefined,
7908
- output: row.output ? JSON.parse(row.output) : undefined,
7943
+ output: undefined,
7909
7944
  error: row.error ?? undefined,
7910
7945
  startedAt: normalizeDateRequired(row.startedAt),
7911
7946
  finishedAt: normalizeDate(row.finishedAt) ?? undefined,
@@ -7916,6 +7951,10 @@ function rowToWorkflowRunStep(row: WorkflowRunStepRow): WorkflowRunStep {
7916
7951
  diagnostics: row.diagnostics ?? undefined,
7917
7952
  nextPort: row.nextPort ?? undefined,
7918
7953
  };
7954
+ return defineWorkflowPayload(step, "output", row.output, row.output_replay, view, {
7955
+ runId: row.runId,
7956
+ target: `the output of step ${row.id} (node ${row.nodeId})`,
7957
+ });
7919
7958
  }
7920
7959
 
7921
7960
  export async function createWorkflowRunStep(data: {
@@ -7936,7 +7975,7 @@ export async function createWorkflowRunStep(data: {
7936
7975
  data.nodeId,
7937
7976
  data.nodeType,
7938
7977
  now,
7939
- data.input ? JSON.stringify(data.input) : null,
7978
+ data.input ? scrubJsonValue(data.input) : null,
7940
7979
  data.idempotencyKey ?? null,
7941
7980
  ],
7942
7981
  );
@@ -7974,12 +8013,14 @@ export async function updateWorkflowRunStep(
7974
8013
  params.push(data.status);
7975
8014
  }
7976
8015
  if (data.output !== undefined) {
7977
- updates.push("output = ?");
7978
- params.push(JSON.stringify(data.output));
8016
+ // Scrubbed for display; dedup, convergence and recovery replay the sealed
8017
+ // exact copy into downstream nodes.
8018
+ updates.push("output = ?", "output_replay = ?");
8019
+ params.push(scrubJsonValue(data.output), sealJson(data.output));
7979
8020
  }
7980
8021
  if (data.error !== undefined) {
7981
8022
  updates.push("error = ?");
7982
- params.push(data.error);
8023
+ params.push(data.error === null ? null : scrubSecrets(data.error));
7983
8024
  }
7984
8025
  if (data.finishedAt !== undefined) {
7985
8026
  updates.push("finishedAt = ?");
@@ -8003,7 +8044,7 @@ export async function updateWorkflowRunStep(
8003
8044
  }
8004
8045
  if (data.diagnostics !== undefined) {
8005
8046
  updates.push("diagnostics = ?");
8006
- params.push(data.diagnostics);
8047
+ params.push(scrubSecrets(data.diagnostics));
8007
8048
  }
8008
8049
  if (data.nextPort !== undefined) {
8009
8050
  updates.push("nextPort = ?");
@@ -8024,12 +8065,15 @@ export async function updateWorkflowRunStep(
8024
8065
  return row ? rowToWorkflowRunStep(row) : null;
8025
8066
  }
8026
8067
 
8027
- export async function getWorkflowRunStepsByRunId(runId: string): Promise<WorkflowRunStep[]> {
8068
+ export async function getWorkflowRunStepsByRunId(
8069
+ runId: string,
8070
+ view: WorkflowPayloadView = "replay",
8071
+ ): Promise<WorkflowRunStep[]> {
8028
8072
  const rows = await getDbClient().query<WorkflowRunStepRow>(
8029
8073
  "SELECT * FROM workflow_run_steps WHERE runId = ? ORDER BY startedAt ASC",
8030
8074
  [runId],
8031
8075
  );
8032
- return rows.map(rowToWorkflowRunStep);
8076
+ return rows.map((row) => rowToWorkflowRunStep(row, view));
8033
8077
  }
8034
8078
 
8035
8079
  // --- Stuck Workflow Run Recovery ---
@@ -8097,7 +8141,7 @@ export async function getRetryableSteps(): Promise<WorkflowRunStep[]> {
8097
8141
  ORDER BY s.nextRetryAt ASC`,
8098
8142
  [now],
8099
8143
  );
8100
- return rows.map(rowToWorkflowRunStep);
8144
+ return rows.map((row) => rowToWorkflowRunStep(row));
8101
8145
  }
8102
8146
 
8103
8147
  export async function getCompletedStepNodeIds(runId: string): Promise<string[]> {
@@ -9526,7 +9570,8 @@ function rowToApprovalRequest(row: ApprovalRequestRow): ApprovalRequest {
9526
9570
  sourceTaskId: row.sourceTaskId,
9527
9571
  approvers: JSON.parse(row.approvers),
9528
9572
  status: row.status as ApprovalRequest["status"],
9529
- responses: row.responses ? JSON.parse(row.responses) : null,
9573
+ // Sealed at rest: workflow recovery and HITL resume read the exact answer.
9574
+ responses: row.responses ? openSealedJson(row.responses) : null,
9530
9575
  approvals: row.approvals ? JSON.parse(row.approvals) : null,
9531
9576
  resolvedBy: row.resolvedBy,
9532
9577
  resolvedAt: normalizeDate(row.resolvedAt),
@@ -9582,14 +9627,14 @@ export async function createApprovalRequest(data: {
9582
9627
  RETURNING *`,
9583
9628
  [
9584
9629
  data.id,
9585
- data.title,
9586
- JSON.stringify(data.questions),
9630
+ scrubSecrets(data.title),
9631
+ scrubJsonValue(data.questions),
9587
9632
  data.workflowRunId ?? null,
9588
9633
  data.workflowRunStepId ?? null,
9589
9634
  data.sourceTaskId ?? null,
9590
9635
  JSON.stringify(data.approvers),
9591
9636
  status,
9592
- resolutionReason,
9637
+ resolutionReason == null ? null : scrubSecrets(resolutionReason),
9593
9638
  status === "cancelled" ? now : null,
9594
9639
  data.timeoutSeconds ?? null,
9595
9640
  expiresAt,
@@ -9651,10 +9696,12 @@ export async function resolveApprovalRequest(
9651
9696
  RETURNING *`,
9652
9697
  [
9653
9698
  data.status,
9654
- data.responses ? JSON.stringify(data.responses) : null,
9655
- data.approvals ? JSON.stringify(data.approvals) : null,
9699
+ // Sealed, not scrubbed: recovery resumes the workflow from this answer,
9700
+ // so it must stay byte-exact. Votes are diagnostic and get redacted.
9701
+ data.responses ? sealJson(data.responses) : null,
9702
+ data.approvals ? scrubApprovalVotesJson(data.approvals) : null,
9656
9703
  data.resolvedBy ?? null,
9657
- data.resolutionReason ?? null,
9704
+ data.resolutionReason == null ? null : scrubSecrets(data.resolutionReason),
9658
9705
  now,
9659
9706
  now,
9660
9707
  id,
@@ -9675,7 +9722,7 @@ export async function cancelApprovalRequestById(
9675
9722
  SET status = 'cancelled', resolutionReason = ?, resolvedBy = ?, resolvedAt = ?, updatedAt = ?
9676
9723
  WHERE id = ? AND status = 'pending'
9677
9724
  RETURNING *`,
9678
- [data.reason, data.resolvedBy, now, now, id],
9725
+ [scrubSecrets(data.reason), data.resolvedBy, now, now, id],
9679
9726
  );
9680
9727
  return row ? rowToApprovalRequest(row) : null;
9681
9728
  }
@@ -9690,7 +9737,7 @@ export async function cancelPendingApprovalRequestsForRun(
9690
9737
  SET status = 'cancelled', resolutionReason = ?, resolvedAt = ?, updatedAt = ?
9691
9738
  WHERE workflowRunId = ? AND status = 'pending'
9692
9739
  RETURNING *`,
9693
- [reason, now, now, workflowRunId],
9740
+ [scrubSecrets(reason), now, now, workflowRunId],
9694
9741
  );
9695
9742
  return rows.map(rowToApprovalRequest);
9696
9743
  }
@@ -9838,7 +9885,7 @@ export interface StuckApprovalRun {
9838
9885
  }
9839
9886
 
9840
9887
  export async function getStuckApprovalRuns(): Promise<StuckApprovalRun[]> {
9841
- return getDbClient().query<StuckApprovalRun>(
9888
+ const rows = await getDbClient().query<StuckApprovalRun>(
9842
9889
  `SELECT
9843
9890
  wr.id as runId,
9844
9891
  wrs.id as stepId,
@@ -9855,6 +9902,11 @@ export async function getStuckApprovalRuns(): Promise<StuckApprovalRun[]> {
9855
9902
  AND (ar.status IN ('approved', 'rejected', 'timeout')
9856
9903
  OR (ar.status = 'pending' AND ar.expiresAt IS NOT NULL AND ar.expiresAt < strftime('%Y-%m-%dT%H:%M:%fZ', 'now')))`,
9857
9904
  );
9905
+ return rows.map((row) => ({
9906
+ ...row,
9907
+ approvalResponses:
9908
+ row.approvalResponses === null ? null : JSON.stringify(openSealedJson(row.approvalResponses)),
9909
+ }));
9858
9910
  }
9859
9911
 
9860
9912
  export async function getApprovalRequestByStepId(stepId: string): Promise<ApprovalRequest | null> {
@@ -12259,6 +12311,33 @@ export async function deleteUser(id: string, replacementUserId?: string): Promis
12259
12311
  // Workflow context is persisted JSON rather than a relational column, but
12260
12312
  // it exposes the same requester identity to downstream interpolation. Keep
12261
12313
  // it consistent with workflow_runs.created_by inside this transaction.
12314
+ // The sealed replay copy is what a resume reads, so rewrite it first, while
12315
+ // the scrubbed column still matches the filter.
12316
+ const sealedContexts = await tx.query<{ id: string; context_replay: string }>(
12317
+ `SELECT id, context_replay FROM workflow_runs
12318
+ WHERE context_replay IS NOT NULL
12319
+ AND json_valid(context)
12320
+ AND json_extract(context, '$.swarm.requestedByUserId') = ?`,
12321
+ [id],
12322
+ );
12323
+ for (const row of sealedContexts) {
12324
+ let context: Record<string, unknown>;
12325
+ try {
12326
+ context = openSealedJson(row.context_replay) as Record<string, unknown>;
12327
+ } catch {
12328
+ // Unreadable replay state fails the run on resume
12329
+ // (WorkflowReplayStateError), so the stale id is never replayed.
12330
+ continue;
12331
+ }
12332
+ const swarm = { ...(context.swarm as Record<string, unknown> | undefined) };
12333
+ if (swarm.requestedByUserId !== id) continue;
12334
+ if (replacementUserId) swarm.requestedByUserId = replacementUserId;
12335
+ else delete swarm.requestedByUserId;
12336
+ await tx.run("UPDATE workflow_runs SET context_replay = ? WHERE id = ?", [
12337
+ sealJson({ ...context, swarm }),
12338
+ row.id,
12339
+ ]);
12340
+ }
12262
12341
  if (replacementUserId) {
12263
12342
  await tx.run(
12264
12343
  `UPDATE workflow_runs
@@ -13833,7 +13912,9 @@ function rowToScriptRun(row: ScriptRunRow): ScriptRun {
13833
13912
  agentId: row.agentId,
13834
13913
  scriptName: row.scriptName ?? undefined,
13835
13914
  source: row.source,
13836
- args: JSON.parse(row.args),
13915
+ // Durable-run args are sealed replay state; callers get the redacted view.
13916
+ // The supervisor reads the exact value via getScriptRunExecutionArgs.
13917
+ args: sealedJsonForDisplay(row.args),
13837
13918
  kind: row.kind as ScriptRunKind,
13838
13919
  status: row.status as ScriptRunStatus,
13839
13920
  pid: row.pid ?? undefined,
@@ -13894,7 +13975,10 @@ export async function createScriptRun(data: {
13894
13975
  data.agentId,
13895
13976
  data.scriptName ?? null,
13896
13977
  data.source,
13897
- JSON.stringify(data.args ?? null),
13978
+ // Sealed, not scrubbed: this is the run's execution input. The supervisor
13979
+ // launches (and relaunches after a restart or pause) from it, so a
13980
+ // redacted value would change what the script receives.
13981
+ sealJson(data.args ?? null),
13898
13982
  data.idempotencyKey ?? null,
13899
13983
  data.requestedByUserId ?? null,
13900
13984
  data.createdBy ?? null,
@@ -13916,7 +14000,7 @@ export async function recordInlineScriptRun(data: {
13916
14000
  scriptName?: string;
13917
14001
  status: "completed" | "failed";
13918
14002
  output?: unknown;
13919
- error?: string;
14003
+ error?: ScrubbedText;
13920
14004
  startedAt: string;
13921
14005
  finishedAt: string;
13922
14006
  requestedByUserId?: string;
@@ -13924,6 +14008,10 @@ export async function recordInlineScriptRun(data: {
13924
14008
  /** Set when this run originated from an external API endpoint (POST /api/x/script/<id>). */
13925
14009
  apiEndpointId?: string | null;
13926
14010
  }): Promise<ScriptRun> {
14011
+ // The run already executed, so args and output are a record, not an input:
14012
+ // redact them here so every caller is covered.
14013
+ const args = scrubJsonValue(data.args ?? null);
14014
+ const output = data.output === undefined ? null : scrubJsonValue(data.output);
13927
14015
  const row = await getDbClient().get<ScriptRunRow>(
13928
14016
  `INSERT INTO script_runs
13929
14017
  (id, agentId, scriptName, source, args, kind, status, output, error,
@@ -13935,9 +14023,9 @@ export async function recordInlineScriptRun(data: {
13935
14023
  data.agentId,
13936
14024
  data.scriptName ?? null,
13937
14025
  data.source,
13938
- JSON.stringify(data.args ?? null),
14026
+ args,
13939
14027
  data.status,
13940
- data.output === undefined ? null : JSON.stringify(data.output),
14028
+ output,
13941
14029
  data.error ?? null,
13942
14030
  data.startedAt,
13943
14031
  data.finishedAt,
@@ -14076,13 +14164,16 @@ function scriptRunUpdateSets(patch: ScriptRunPatch): {
14076
14164
  sets.push("finishedAt = ?");
14077
14165
  vals.push(patch.finishedAt);
14078
14166
  }
14167
+ // Every script_runs UPDATE builds its SET list here, so this is the
14168
+ // chokepoint for the output and error columns. Output is a record of the
14169
+ // finished run (nothing re-reads it as input), so redaction is safe.
14079
14170
  if ("output" in patch) {
14080
14171
  sets.push("output = ?");
14081
- vals.push(patch.output === undefined ? null : JSON.stringify(patch.output));
14172
+ vals.push(patch.output === undefined ? null : scrubJsonValue(patch.output));
14082
14173
  }
14083
14174
  if (patch.error !== undefined) {
14084
14175
  sets.push("error = ?");
14085
- vals.push(patch.error);
14176
+ vals.push(patch.error === null ? null : scrubSecrets(patch.error));
14086
14177
  }
14087
14178
  if (patch.lastHeartbeatAt !== undefined) {
14088
14179
  sets.push("last_heartbeat_at = ?");
@@ -14177,7 +14268,11 @@ type ScriptRunJournalRow = {
14177
14268
  updated_by: string | null;
14178
14269
  };
14179
14270
 
14180
- function rowToScriptRunJournalEntry(row: ScriptRunJournalRow): ScriptRunJournalEntry {
14271
+ function rowToScriptRunJournalEntry(
14272
+ row: ScriptRunJournalRow,
14273
+ view: "display" | "replay" = "display",
14274
+ ): ScriptRunJournalEntry {
14275
+ const open = view === "replay" ? openSealedJson : sealedJsonForDisplay;
14181
14276
  return {
14182
14277
  id: row.id,
14183
14278
  runId: row.runId,
@@ -14185,7 +14280,7 @@ function rowToScriptRunJournalEntry(row: ScriptRunJournalRow): ScriptRunJournalE
14185
14280
  stepType: row.stepType,
14186
14281
  config: JSON.parse(row.config),
14187
14282
  status: row.status as "completed" | "failed",
14188
- result: parseJsonColumn(row.result),
14283
+ result: row.result === null ? undefined : open(row.result),
14189
14284
  error: row.error ?? undefined,
14190
14285
  startedAt: row.startedAt,
14191
14286
  completedAt: row.completedAt ?? undefined,
@@ -14193,6 +14288,10 @@ function rowToScriptRunJournalEntry(row: ScriptRunJournalRow): ScriptRunJournalE
14193
14288
  };
14194
14289
  }
14195
14290
 
14291
+ /**
14292
+ * One journal step with its exact `result`, for the harness replay route only.
14293
+ * Everything that shows a journal to a person uses listScriptRunJournalSteps.
14294
+ */
14196
14295
  export async function getScriptRunJournalStep(
14197
14296
  runId: string,
14198
14297
  stepKey: string,
@@ -14201,7 +14300,7 @@ export async function getScriptRunJournalStep(
14201
14300
  "SELECT * FROM script_run_journal WHERE runId = ? AND stepKey = ?",
14202
14301
  [runId, stepKey],
14203
14302
  );
14204
- return row ? rowToScriptRunJournalEntry(row) : null;
14303
+ return row ? rowToScriptRunJournalEntry(row, "replay") : null;
14205
14304
  }
14206
14305
 
14207
14306
  export async function upsertScriptRunJournalStep(data: {
@@ -14211,9 +14310,13 @@ export async function upsertScriptRunJournalStep(data: {
14211
14310
  config: unknown;
14212
14311
  status: "completed" | "failed";
14213
14312
  result?: unknown;
14214
- error?: string;
14313
+ error?: ScrubbedText;
14215
14314
  durationMs?: number;
14216
14315
  }): Promise<void> {
14316
+ // `config` is diagnostic only (the step GET never returns it), so it is
14317
+ // redacted. `result` is sealed instead: the harness replays it verbatim as
14318
+ // the step's return value on resume, so redaction would change what later
14319
+ // steps see.
14217
14320
  await getDbClient().run(
14218
14321
  `INSERT OR IGNORE INTO script_run_journal
14219
14322
  (id, runId, stepKey, stepType, config, status, result, error, durationMs, completedAt)
@@ -14223,9 +14326,9 @@ export async function upsertScriptRunJournalStep(data: {
14223
14326
  data.runId,
14224
14327
  data.stepKey,
14225
14328
  data.stepType,
14226
- JSON.stringify(data.config ?? {}),
14329
+ scrubJsonValue(data.config ?? {}),
14227
14330
  data.status,
14228
- data.result !== undefined ? JSON.stringify(data.result) : null,
14331
+ data.result !== undefined ? sealJson(data.result) : null,
14229
14332
  data.error ?? null,
14230
14333
  data.durationMs ?? null,
14231
14334
  ],
@@ -14237,7 +14340,7 @@ export async function listScriptRunJournalSteps(runId: string): Promise<ScriptRu
14237
14340
  "SELECT * FROM script_run_journal WHERE runId = ? ORDER BY startedAt ASC",
14238
14341
  [runId],
14239
14342
  );
14240
- return rows.map(rowToScriptRunJournalEntry);
14343
+ return rows.map((row) => rowToScriptRunJournalEntry(row));
14241
14344
  }
14242
14345
 
14243
14346
  export async function countScriptRunJournalSteps(runId: string): Promise<number> {
package/src/be/events.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import type { EventCategory, EventName, EventSource, EventStatus, SwarmEvent } from "../types";
2
+ import { type ScrubbedText, scrubObject, scrubSecrets } from "../utils/secret-scrubber";
2
3
  import { getDbClient } from "./db";
3
4
  import type { DbExecutor } from "./db-client";
5
+ import { breaksJsonValidity } from "./scrub-json";
4
6
  import { recordSkillInvocation } from "./skill-invocations";
5
7
 
6
8
  // -- Events --
@@ -59,6 +61,20 @@ export interface CreateEventInput {
59
61
  data?: Record<string, unknown>;
60
62
  }
61
63
 
64
+ /**
65
+ * Serialize an event payload for `events.data` with secrets redacted. Scrubs
66
+ * the serialized string; if a redaction breaks JSON validity (e.g. a marker
67
+ * swallowed a closing quote), falls back to scrubbing each leaf string so the
68
+ * column always parses for `rowToSwarmEvent`.
69
+ */
70
+ function serializeEventData(data: Record<string, unknown>): ScrubbedText {
71
+ const raw = JSON.stringify(data);
72
+ const scrubbed = scrubSecrets(raw);
73
+ if (!breaksJsonValidity(raw, scrubbed)) return scrubbed;
74
+ // Every leaf string went through scrubSecrets, so the brand holds.
75
+ return JSON.stringify(scrubObject(data)) as ScrubbedText;
76
+ }
77
+
62
78
  async function insertEvent(db: DbExecutor, id: string, input: CreateEventInput): Promise<void> {
63
79
  await db.run(INSERT_EVENT_SQL, [
64
80
  id,
@@ -72,7 +88,7 @@ async function insertEvent(db: DbExecutor, id: string, input: CreateEventInput):
72
88
  input.parentEventId ?? null,
73
89
  input.numericValue ?? null,
74
90
  input.durationMs ?? null,
75
- input.data ? JSON.stringify(input.data) : null,
91
+ input.data ? serializeEventData(input.data) : null,
76
92
  ]);
77
93
  // `skill.invoke` also feeds the per-skill counter and invocation history.
78
94
  await recordSkillInvocation(db, input, id);
@@ -90,7 +90,7 @@ export async function indexMemoryContent(
90
90
  intent: "re-index memory source path",
91
91
  changedByAgentId: agentId,
92
92
  });
93
- const embedding = await provider.embed(contentChunks[0]!.content);
93
+ const embedding = await provider.embed(result.memory.content);
94
94
  if (embedding) await store.updateEmbedding(result.memory.id, embedding, provider.name);
95
95
  try {
96
96
  // Re-index of an existing memory: prune stale content-derived links.
@@ -143,7 +143,7 @@ export async function indexMemoryContent(
143
143
  // Async batch embed (fire and forget)
144
144
  (async () => {
145
145
  try {
146
- const embeddings = await provider.embedBatch(contentChunks.map((c) => c.content));
146
+ const embeddings = await provider.embedBatch(memories.map((m) => m.content));
147
147
  for (let i = 0; i < embeddings.length; i++) {
148
148
  if (embeddings[i]) {
149
149
  await store.updateEmbedding(memories[i]!.id, embeddings[i]!, provider.name);