@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
@@ -0,0 +1,289 @@
1
+ /**
2
+ * Write-time scrub for memory, channel and inbox messages, approval requests,
3
+ * schedules, task text and agent_log.oldValue. Each test writes a synthetic
4
+ * secret through the real writer, reads the row back from SQLite, and checks
5
+ * that the secret is gone while a redaction marker and the surrounding
6
+ * non-secret text survive.
7
+ */
8
+ import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
9
+ import { unlink } from "node:fs/promises";
10
+ import { recordSchedulePreflightFailure } from "../be/automation-preflight";
11
+ import {
12
+ cancelApprovalRequestById,
13
+ cancelPendingApprovalRequestsForRun,
14
+ closeDb,
15
+ createAgent,
16
+ createApprovalRequest,
17
+ createChannel,
18
+ createInboxMessage,
19
+ createLogEntry,
20
+ createScheduledTask,
21
+ createTask,
22
+ createTaskExtended,
23
+ createWorkflow,
24
+ createWorkflowRun,
25
+ getApprovalRequestById,
26
+ getDbClient,
27
+ initDb,
28
+ markInboxMessageResponded,
29
+ postMessage,
30
+ recordApprovalVotes,
31
+ resolveApprovalRequest,
32
+ updateScheduledTask,
33
+ } from "../be/db";
34
+ import { SqliteMemoryStore } from "../be/memory/providers/sqlite-store";
35
+ import { isSealedJson } from "../be/sealed-json";
36
+ import { registerVolatileSecret } from "../utils/secret-scrubber";
37
+ import { type SyntheticSecret, syntheticSecret } from "./synthetic-secret-helpers";
38
+
39
+ const TEST_DB_PATH = "./test-scrubbed-text-12e.sqlite";
40
+ const agentId = "aaaaaaaa-0000-4000-8000-000000000e12";
41
+
42
+ let secret: SyntheticSecret;
43
+
44
+ async function removeDbFiles(): Promise<void> {
45
+ for (const suffix of ["", "-wal", "-shm"]) {
46
+ try {
47
+ await unlink(TEST_DB_PATH + suffix);
48
+ } catch {}
49
+ }
50
+ }
51
+
52
+ /** Secret absent, a redaction marker present, and the context kept. */
53
+ function expectScrubbed(value: string | null | undefined, context: string): void {
54
+ expect(value).toBeTruthy();
55
+ expect(value).not.toContain(secret.value);
56
+ expect(value).toContain("[REDACTED:");
57
+ expect(value).toContain(context);
58
+ }
59
+
60
+ async function column(table: string, col: string, id: string): Promise<string | null> {
61
+ const row = await getDbClient().get<Record<string, string | null>>(
62
+ `SELECT "${col}" AS v FROM ${table} WHERE id = ?`,
63
+ [id],
64
+ );
65
+ return row?.v ?? null;
66
+ }
67
+
68
+ beforeAll(async () => {
69
+ await removeDbFiles();
70
+ initDb(TEST_DB_PATH);
71
+ await createAgent({ id: agentId, name: "Scrub 12e Agent", isLead: false, status: "idle" });
72
+ secret = syntheticSecret("scrub12e");
73
+ });
74
+
75
+ // The test preload clears volatile secrets after every test, so register per test.
76
+ beforeEach(() => registerVolatileSecret(secret.value, secret.name));
77
+
78
+ afterAll(async () => {
79
+ secret.cleanup();
80
+ closeDb();
81
+ await removeDbFiles();
82
+ });
83
+
84
+ describe("agent_memory", () => {
85
+ test("store scrubs name, content, summary, tags, version row and FTS row", async () => {
86
+ const store = new SqliteMemoryStore();
87
+ const memory = await store.store({
88
+ agentId,
89
+ scope: "agent",
90
+ name: `deploy note ${secret.value}`,
91
+ content: `the deploy token is ${secret.value} for staging`,
92
+ summary: `summary ${secret.value}`,
93
+ tags: [`tag ${secret.value}`, "plain"],
94
+ source: "manual",
95
+ });
96
+ // Callers embed the returned content, so it must be the scrubbed text.
97
+ expectScrubbed(memory.content, "for staging");
98
+ expectScrubbed(await column("agent_memory", "name", memory.id), "deploy note");
99
+ expectScrubbed(await column("agent_memory", "content", memory.id), "for staging");
100
+ expectScrubbed(await column("agent_memory", "summary", memory.id), "summary");
101
+ const tags = await column("agent_memory", "tags", memory.id);
102
+ expectScrubbed(tags, "plain");
103
+ expect(JSON.parse(tags!)[0]).toContain("tag ");
104
+ const version = await getDbClient().get<{ content: string }>(
105
+ "SELECT content FROM agent_memory_version WHERE memory_id = ?",
106
+ [memory.id],
107
+ );
108
+ expectScrubbed(version?.content, "for staging");
109
+ const fts = await getDbClient().get<{ content: string }>(
110
+ "SELECT content FROM memory_fts WHERE memory_id = ?",
111
+ [memory.id],
112
+ );
113
+ if (fts) expectScrubbed(fts.content, "for staging");
114
+ });
115
+
116
+ test("edit scrubs the new content", async () => {
117
+ const store = new SqliteMemoryStore();
118
+ const memory = await store.store({
119
+ agentId,
120
+ scope: "agent",
121
+ name: "editable",
122
+ content: "clean body",
123
+ source: "manual",
124
+ });
125
+ const result = await store.edit({
126
+ id: memory.id,
127
+ mode: "replace",
128
+ content: `edited body ${secret.value} end`,
129
+ intent: "edit with secret",
130
+ changedByAgentId: agentId,
131
+ });
132
+ expect(result.changed).toBe(true);
133
+ expectScrubbed(result.memory.content, "edited body");
134
+ expectScrubbed(await column("agent_memory", "content", memory.id), "edited body");
135
+ });
136
+ });
137
+
138
+ describe("agent_tasks.task and agent_log.oldValue", () => {
139
+ test("createTaskExtended scrubs the task text", async () => {
140
+ const task = await createTaskExtended(`run the job with ${secret.value} please`, { agentId });
141
+ expectScrubbed(await column("agent_tasks", "task", task.id), "run the job");
142
+ });
143
+
144
+ test("createTask scrubs the task text", async () => {
145
+ const task = await createTask(agentId, `legacy create ${secret.value} please`);
146
+ expectScrubbed(await column("agent_tasks", "task", task.id), "legacy create");
147
+ });
148
+
149
+ test("createLogEntry scrubs oldValue", async () => {
150
+ const log = await createLogEntry({
151
+ eventType: "task_status_change",
152
+ agentId,
153
+ oldValue: `previous ${secret.value}`,
154
+ newValue: "next",
155
+ });
156
+ expectScrubbed(await column("agent_log", "oldValue", log.id), "previous");
157
+ });
158
+ });
159
+
160
+ describe("channel and inbox messages", () => {
161
+ test("postMessage scrubs content", async () => {
162
+ const channel = await createChannel(`scrub-12e-${crypto.randomUUID().slice(0, 8)}`);
163
+ const message = await postMessage(channel.id, agentId, `hello ${secret.value} channel`);
164
+ expectScrubbed(await column("channel_messages", "content", message.id), "channel");
165
+ });
166
+
167
+ test("createInboxMessage and markInboxMessageResponded scrub free text", async () => {
168
+ const inbox = await createInboxMessage(agentId, `inbox body ${secret.value}`, {
169
+ matchedText: `matched ${secret.value}`,
170
+ });
171
+ expectScrubbed(await column("inbox_messages", "content", inbox.id), "inbox body");
172
+ expectScrubbed(await column("inbox_messages", "matchedText", inbox.id), "matched");
173
+ await markInboxMessageResponded(inbox.id, `reply ${secret.value}`);
174
+ expectScrubbed(await column("inbox_messages", "responseText", inbox.id), "reply");
175
+ });
176
+ });
177
+
178
+ describe("scheduled_tasks", () => {
179
+ test("create and update scrub description, taskTemplate and lastErrorMessage", async () => {
180
+ const schedule = await createScheduledTask({
181
+ name: `scrub-12e-${crypto.randomUUID().slice(0, 8)}`,
182
+ intervalMs: 60_000,
183
+ description: `desc ${secret.value}`,
184
+ taskTemplate: `call the api with ${secret.value}`,
185
+ });
186
+ expectScrubbed(await column("scheduled_tasks", "description", schedule.id), "desc");
187
+ expectScrubbed(await column("scheduled_tasks", "taskTemplate", schedule.id), "call the api");
188
+
189
+ await updateScheduledTask(schedule.id, {
190
+ description: `new desc ${secret.value}`,
191
+ taskTemplate: `new template ${secret.value}`,
192
+ lastErrorMessage: `boom ${secret.value}`,
193
+ });
194
+ expectScrubbed(await column("scheduled_tasks", "description", schedule.id), "new desc");
195
+ expectScrubbed(await column("scheduled_tasks", "taskTemplate", schedule.id), "new template");
196
+ expectScrubbed(await column("scheduled_tasks", "lastErrorMessage", schedule.id), "boom");
197
+ });
198
+
199
+ test("preflight failure scrubs the message and still dedups the same day", async () => {
200
+ const schedule = await createScheduledTask({
201
+ name: `scrub-12e-${crypto.randomUUID().slice(0, 8)}`,
202
+ intervalMs: 60_000,
203
+ taskTemplate: "work",
204
+ });
205
+ const now = new Date();
206
+ expect(await recordSchedulePreflightFailure(schedule.id, `missing ${secret.value}`, now)).toBe(
207
+ true,
208
+ );
209
+ expectScrubbed(await column("scheduled_tasks", "lastErrorMessage", schedule.id), "missing");
210
+ // Same raw reason the same day: the scrubbed compare still matches.
211
+ expect(await recordSchedulePreflightFailure(schedule.id, `missing ${secret.value}`, now)).toBe(
212
+ false,
213
+ );
214
+ });
215
+ });
216
+
217
+ describe("approval_requests", () => {
218
+ test("create, vote and resolve scrub title, questions, approvals and resolutionReason, seal responses", async () => {
219
+ const id = crypto.randomUUID();
220
+ await createApprovalRequest({
221
+ id,
222
+ title: `approve ${secret.value} deploy`,
223
+ questions: [{ id: "q1", type: "text", label: `paste ${secret.value} here` }],
224
+ approvers: { users: [], policy: "any" },
225
+ });
226
+ expectScrubbed(await column("approval_requests", "title", id), "deploy");
227
+ const questions = await column("approval_requests", "questions", id);
228
+ expectScrubbed(questions, "paste");
229
+ expect(() => JSON.parse(questions!)).not.toThrow();
230
+
231
+ const vote = {
232
+ responder: "operator",
233
+ approved: true,
234
+ responses: { q1: `note ${secret.value}` },
235
+ respondedAt: new Date().toISOString(),
236
+ };
237
+ expect(await recordApprovalVotes(id, [vote])).toBe(true);
238
+ const votes = await column("approval_requests", "approvals", id);
239
+ expectScrubbed(votes, "note");
240
+ expect(() => JSON.parse(votes!)).not.toThrow();
241
+
242
+ const responses = { q1: `note ${secret.value}` };
243
+ await resolveApprovalRequest(id, {
244
+ status: "approved",
245
+ responses,
246
+ approvals: [vote],
247
+ resolutionReason: `because ${secret.value}`,
248
+ });
249
+ expectScrubbed(await column("approval_requests", "resolutionReason", id), "because");
250
+ expectScrubbed(await column("approval_requests", "approvals", id), "note");
251
+
252
+ // Responses resume the workflow on recovery, so they stay byte-exact for
253
+ // the reader and hold no plaintext at rest.
254
+ const stored = await column("approval_requests", "responses", id);
255
+ expect(isSealedJson(stored ?? "")).toBe(true);
256
+ expect(stored).not.toContain(secret.value);
257
+ expect(stored).not.toContain("note");
258
+ expect((await getApprovalRequestById(id))?.responses).toEqual(responses);
259
+ });
260
+
261
+ test("cancel paths scrub the reason", async () => {
262
+ const single = crypto.randomUUID();
263
+ await createApprovalRequest({
264
+ id: single,
265
+ title: "single",
266
+ questions: [],
267
+ approvers: { users: [], policy: "any" },
268
+ });
269
+ await cancelApprovalRequestById(single, { reason: `stop ${secret.value}`, resolvedBy: null });
270
+ expectScrubbed(await column("approval_requests", "resolutionReason", single), "stop");
271
+
272
+ const workflow = await createWorkflow({
273
+ name: `scrub-12e-${crypto.randomUUID().slice(0, 8)}`,
274
+ definition: { nodes: [] },
275
+ });
276
+ const runId = crypto.randomUUID();
277
+ await createWorkflowRun({ id: runId, workflowId: workflow.id });
278
+ const inRun = crypto.randomUUID();
279
+ await createApprovalRequest({
280
+ id: inRun,
281
+ title: "in run",
282
+ questions: [],
283
+ approvers: { users: [], policy: "any" },
284
+ workflowRunId: runId,
285
+ });
286
+ await cancelPendingApprovalRequestsForRun(runId, `run cancelled ${secret.value}`);
287
+ expectScrubbed(await column("approval_requests", "resolutionReason", inRun), "run cancelled");
288
+ });
289
+ });
@@ -0,0 +1,166 @@
1
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
+ import { existsSync, mkdtempSync, rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { __resetEncryptionKeyForTests, resolveEncryptionKey } from "../be/crypto";
6
+ import {
7
+ closeDb,
8
+ createApprovalRequest,
9
+ createScriptRun,
10
+ createWorkflow,
11
+ createWorkflowRun,
12
+ getDbClient,
13
+ getScriptRunExecutionArgs,
14
+ initDb,
15
+ resolveApprovalRequest,
16
+ updateWorkflowRun,
17
+ upsertScriptRunJournalStep,
18
+ } from "../be/db";
19
+ import { SEALED_REPLAY_COLUMNS } from "../be/sealed-json";
20
+
21
+ /**
22
+ * A real close/reopen with the key gone: a DB whose only encrypted data is
23
+ * sealed replay state must refuse to boot rather than write a replacement
24
+ * key that can never open it.
25
+ */
26
+
27
+ const templateGlobals = globalThis as typeof globalThis & {
28
+ __testMigrationTemplate?: Uint8Array;
29
+ };
30
+
31
+ let savedTemplate: Uint8Array | undefined;
32
+ let savedKey: string | undefined;
33
+ let savedKeyFile: string | undefined;
34
+ let dir: string;
35
+ let dbPath: string;
36
+ let keyFile: string;
37
+
38
+ beforeEach(() => {
39
+ closeDb();
40
+ savedTemplate = templateGlobals.__testMigrationTemplate;
41
+ templateGlobals.__testMigrationTemplate = undefined;
42
+ savedKey = process.env.SECRETS_ENCRYPTION_KEY;
43
+ savedKeyFile = process.env.SECRETS_ENCRYPTION_KEY_FILE;
44
+ delete process.env.SECRETS_ENCRYPTION_KEY;
45
+ delete process.env.SECRETS_ENCRYPTION_KEY_FILE;
46
+ __resetEncryptionKeyForTests();
47
+ dir = mkdtempSync(join(tmpdir(), "sealed-key-guard-"));
48
+ dbPath = join(dir, "swarm.sqlite");
49
+ keyFile = join(dir, ".encryption-key");
50
+ });
51
+
52
+ afterEach(() => {
53
+ closeDb();
54
+ templateGlobals.__testMigrationTemplate = savedTemplate;
55
+ if (savedKey !== undefined) process.env.SECRETS_ENCRYPTION_KEY = savedKey;
56
+ if (savedKeyFile !== undefined) process.env.SECRETS_ENCRYPTION_KEY_FILE = savedKeyFile;
57
+ __resetEncryptionKeyForTests();
58
+ resolveEncryptionKey(":memory:");
59
+ rmSync(dir, { recursive: true, force: true });
60
+ });
61
+
62
+ /** Close, forget the cached key, and delete the generated key file. */
63
+ function restartWithoutKey(): void {
64
+ closeDb();
65
+ __resetEncryptionKeyForTests();
66
+ rmSync(keyFile, { force: true });
67
+ }
68
+
69
+ describe("boot key guard counts sealed replay values as encrypted data", () => {
70
+ test("every sealed column is listed", () => {
71
+ expect(SEALED_REPLAY_COLUMNS).toEqual(
72
+ expect.arrayContaining([
73
+ { table: "script_runs", column: "args" },
74
+ { table: "script_run_journal", column: "result" },
75
+ { table: "workflow_run_steps", column: "output_replay" },
76
+ { table: "workflow_runs", column: "context_replay" },
77
+ { table: "approval_requests", column: "responses" },
78
+ ]),
79
+ );
80
+ });
81
+
82
+ test("control: a DB with no encrypted data boots and generates a key", () => {
83
+ initDb(dbPath);
84
+ expect(existsSync(keyFile)).toBe(true);
85
+ restartWithoutKey();
86
+ initDb(dbPath);
87
+ expect(existsSync(keyFile)).toBe(true);
88
+ });
89
+
90
+ test("sealed durable-run args: boot refuses and writes no key", async () => {
91
+ initDb(dbPath);
92
+ expect(existsSync(keyFile)).toBe(true);
93
+ const id = crypto.randomUUID();
94
+ await createScriptRun({
95
+ id,
96
+ agentId: crypto.randomUUID(),
97
+ source: "export default () => 1",
98
+ args: { n: 7 },
99
+ });
100
+ expect(await getScriptRunExecutionArgs(id)).toEqual({ n: 7 });
101
+
102
+ restartWithoutKey();
103
+ expect(() => initDb(dbPath)).toThrow(/sealed replay values/);
104
+ expect(existsSync(keyFile)).toBe(false);
105
+ });
106
+
107
+ test("sealed workflow run context: boot refuses and writes no key", async () => {
108
+ initDb(dbPath);
109
+ const workflow = await createWorkflow({
110
+ name: `sealed-key-guard-${crypto.randomUUID()}`,
111
+ definition: { nodes: [] },
112
+ });
113
+ const runId = crypto.randomUUID();
114
+ await createWorkflowRun({ id: runId, workflowId: workflow.id });
115
+ await updateWorkflowRun(runId, { context: { trigger: { n: 7 } } });
116
+
117
+ restartWithoutKey();
118
+ expect(() => initDb(dbPath)).toThrow(/sealed replay values/);
119
+ expect(existsSync(keyFile)).toBe(false);
120
+ });
121
+
122
+ test("sealed approval responses: boot refuses and writes no key", async () => {
123
+ initDb(dbPath);
124
+ const id = crypto.randomUUID();
125
+ await createApprovalRequest({
126
+ id,
127
+ title: "sealed-key-guard",
128
+ questions: [{ id: "ok", type: "approval", label: "Proceed?" }],
129
+ approvers: { users: [] },
130
+ });
131
+ const resolved = await resolveApprovalRequest(id, {
132
+ status: "approved",
133
+ responses: { ok: { approved: true } },
134
+ });
135
+ expect(resolved?.status).toBe("approved");
136
+
137
+ restartWithoutKey();
138
+ expect(() => initDb(dbPath)).toThrow(/sealed replay values/);
139
+ expect(existsSync(keyFile)).toBe(false);
140
+ });
141
+
142
+ test("sealed journal result: boot refuses and writes no key", async () => {
143
+ initDb(dbPath);
144
+ const runId = crypto.randomUUID();
145
+ await createScriptRun({
146
+ id: runId,
147
+ agentId: crypto.randomUUID(),
148
+ source: "export default () => 1",
149
+ args: null,
150
+ });
151
+ await upsertScriptRunJournalStep({
152
+ runId,
153
+ stepKey: "step-1",
154
+ stepType: "swarm",
155
+ config: {},
156
+ status: "completed",
157
+ result: { ok: true },
158
+ });
159
+ // Leave the journal result as the only sealed value (pre-sealing args shape).
160
+ await getDbClient().run("UPDATE script_runs SET args = 'null' WHERE id = ?", [runId]);
161
+
162
+ restartWithoutKey();
163
+ expect(() => initDb(dbPath)).toThrow(/sealed replay values/);
164
+ expect(existsSync(keyFile)).toBe(false);
165
+ });
166
+ });