@desplega.ai/agent-swarm 2.7.0 → 2.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/dist/{acp-adapter-ma1js5n7.js → acp-adapter-8dn5tq56.js} +6 -6
  2. package/dist/{actions-1vhmtvg9.js → actions-3r6gw7dy.js} +18 -18
  3. package/dist/{amp-adapter-sxqqvmdk.js → amp-adapter-wjtq413p.js} +3 -3
  4. package/dist/{anthropic-messages-txs190dd.js → anthropic-messages-n4cby1rz.js} +4 -4
  5. package/dist/{app-mwx1d5a9.js → app-d53yhykk.js} +3 -3
  6. package/dist/{assistant-yx1p6g82.js → assistant-ksrawj3k.js} +20 -20
  7. package/dist/{boot-reembed-7h78vmvv.js → boot-reembed-fxfw17m2.js} +10 -3
  8. package/dist/{boot-reembed-5h37a2c2.js → boot-reembed-k5y5xbj0.js} +15 -8
  9. package/dist/{boot-scrub-sweep-8mawd4gw.js → boot-scrub-sweep-se41j9fd.js} +3 -3
  10. package/dist/{boot-seeds-ca84dhnn.js → boot-seeds-r3kwwv8b.js} +4 -4
  11. package/dist/{claude-adapter-wqt4hfkq.js → claude-adapter-ahmyf231.js} +7 -7
  12. package/dist/{claude-managed-adapter-6fjqtr2z.js → claude-managed-adapter-5knamegc.js} +3 -3
  13. package/dist/{claude-managed-setup-rqv98sea.js → claude-managed-setup-mdc7dy6b.js} +2 -2
  14. package/dist/{claude-sdk-session-f29f3b2x.js → claude-sdk-session-n5wen9zk.js} +8728 -8599
  15. package/dist/{cli-m5e802r7.js → cli-0dqtv2jg.js} +2 -2
  16. package/dist/{cli-e1595bgx.js → cli-0tww7dnz.js} +2 -2
  17. package/dist/{cli-2y7v133v.js → cli-0w4e4v58.js} +7 -7
  18. package/dist/{cli-d20qvpr2.js → cli-2sjm3t4r.js} +1 -1
  19. package/dist/{cli-gf2850q7.js → cli-3cywb44q.js} +18 -1
  20. package/dist/{cli-8wkb09q5.js → cli-3k9jej0c.js} +2 -2
  21. package/dist/{cli-twctnkng.js → cli-3v2cyqb8.js} +3 -3
  22. package/dist/{cli-he9gbdx0.js → cli-5qt9vzmq.js} +39 -20
  23. package/dist/{cli-a6ds2epe.js → cli-5rp1w4cr.js} +1 -1
  24. package/dist/{cli-6srv653c.js → cli-6vwjsetx.js} +4 -4
  25. package/dist/{cli-1hhbzkbb.js → cli-6w2yrrc0.js} +8 -8
  26. package/dist/{cli-f12mr20z.js → cli-75tdydgv.js} +1 -1
  27. package/dist/{cli-j3y0x2n4.js → cli-8feswf2v.js} +43 -33
  28. package/dist/{cli-07q03xm2.js → cli-8wd3mbyj.js} +1 -1
  29. package/dist/{cli-ymjpkxgn.js → cli-9csfthbw.js} +8 -6
  30. package/dist/{cli-f1e0ptjj.js → cli-b41p764a.js} +21 -2
  31. package/dist/{cli-kv7sbc2c.js → cli-c1t533hh.js} +3 -3
  32. package/dist/{cli-54sj64fc.js → cli-c28jxpcv.js} +1 -1
  33. package/dist/{cli-8mbc9za9.js → cli-cb50g9cd.js} +1 -1
  34. package/dist/{cli-c668vvcf.js → cli-d8gaphtr.js} +1 -1
  35. package/dist/{cli-86ehn4s5.js → cli-dgtfysne.js} +2 -2
  36. package/dist/{cli-sjctjr48.js → cli-dscnhn18.js} +1 -1
  37. package/dist/{cli-j0ppywdr.js → cli-dv11pgpv.js} +12 -12
  38. package/dist/{cli-1m9x8dr5.js → cli-ekptgk2m.js} +5 -5
  39. package/dist/{cli-9gsm2rzp.js → cli-f3mzy662.js} +17 -17
  40. package/dist/{cli-s0mgx214.js → cli-fk7k8s9x.js} +2 -2
  41. package/dist/{cli-vbhgkw9h.js → cli-h3e7bhvn.js} +3 -3
  42. package/dist/{cli-yd2nreqw.js → cli-hkxqdmnb.js} +2 -2
  43. package/dist/{cli-8wmj18cz.js → cli-hpkvgdad.js} +7 -4
  44. package/dist/{cli-wqr7cea4.js → cli-hq2v4rr5.js} +37 -9
  45. package/dist/{cli-sgqm2582.js → cli-k1zb7raw.js} +5 -5
  46. package/dist/{cli-4rvr7zfx.js → cli-k4awgvzm.js} +5 -0
  47. package/dist/{cli-ay40dnj3.js → cli-k7d3hvya.js} +2 -2
  48. package/dist/{cli-j9bpmykh.js → cli-kbtdftbw.js} +5 -5
  49. package/dist/{cli-x37r7qwf.js → cli-kj69b9tw.js} +25 -5
  50. package/dist/{cli-gsfk58nw.js → cli-kk9jy4nm.js} +1 -1
  51. package/dist/{cli-39aet655.js → cli-m52t4cta.js} +1 -1
  52. package/dist/{cli-h5jeg46p.js → cli-mdadewqp.js} +5 -5
  53. package/dist/{cli-a89zdqz9.js → cli-nf6k60sn.js} +1 -1
  54. package/dist/{cli-v6shg7na.js → cli-nzdf09js.js} +1 -1
  55. package/dist/{cli-rr7zx19j.js → cli-prcyzecg.js} +12 -12
  56. package/dist/{cli-6a9qfa8b.js → cli-pzafyqtk.js} +118 -48
  57. package/dist/{cli-48gxjmcz.js → cli-qvaa1gjp.js} +87 -24
  58. package/dist/{cli-qj442fws.js → cli-rg4b2g6j.js} +1 -1
  59. package/dist/{cli-aypd09nf.js → cli-rm66vqdz.js} +1 -1
  60. package/dist/{cli-9pshx6rd.js → cli-s504c3w7.js} +1 -1
  61. package/dist/{cli-ptfq202x.js → cli-swkx9jm2.js} +3 -3
  62. package/dist/{cli-m30k9777.js → cli-thsaq4pd.js} +16 -16
  63. package/dist/{cli-60sty27p.js → cli-twjnyvs8.js} +2 -2
  64. package/dist/{cli-rd5m70gj.js → cli-typdpe8g.js} +4 -4
  65. package/dist/{cli-yxbsmx56.js → cli-vg8pwmsd.js} +6 -6
  66. package/dist/{cli-s09j9vp5.js → cli-vhpckzvp.js} +1 -1
  67. package/dist/{cli-dv97kdzv.js → cli-wdxgsdz5.js} +2 -2
  68. package/dist/{cli-7a6p4e65.js → cli-wtjg6je3.js} +1 -1
  69. package/dist/{cli-b3yzyc57.js → cli-x3cngb1h.js} +44 -12
  70. package/dist/{cli-6mgdba42.js → cli-xbz6mcg4.js} +2 -2
  71. package/dist/{cli-2pvzmas1.js → cli-y4tdxsqb.js} +3 -3
  72. package/dist/{cli-f4dbh7a5.js → cli-yqrsp3ny.js} +7 -7
  73. package/dist/cli.js +27 -19
  74. package/dist/{codex-adapter-zk59x2zh.js → codex-adapter-7wepzq15.js} +5 -5
  75. package/dist/{codex-hook-xyjhvxwd.js → codex-hook-pbdh4xhx.js} +2 -2
  76. package/dist/{codex-session-runner-kh6s4j3n.js → codex-session-runner-02sb49e4.js} +5 -5
  77. package/dist/{commands-68et1qv2.js → commands-3rcspxct.js} +2 -2
  78. package/dist/{controller-fyrbnzg4.js → controller-8ar4avd6.js} +3 -3
  79. package/dist/{controllers-connect-nep9h2xv.js → controllers-connect-ajvh9h0v.js} +19 -19
  80. package/dist/{cursor-adapter-yss3pg7t.js → cursor-adapter-gd0sa7bh.js} +1 -1
  81. package/dist/{db-28gjyvxj.js → db-dv9j7ts5.js} +2 -2
  82. package/dist/{db-8ftwvzba.js → db-z26vgdvj.js} +3 -3
  83. package/dist/{dispatcher-hayexmkr.js → dispatcher-8m0htjq8.js} +17 -17
  84. package/dist/{dsh-adapter-46wawff9.js → dsh-adapter-y8gyk130.js} +1 -1
  85. package/dist/{e2b-fn3zacvt.js → e2b-74vg9b7e.js} +1 -1
  86. package/dist/{e2b-222hsajc.js → e2b-jyay8wzn.js} +1 -1
  87. package/dist/{easter-egg-3d-8d1e0h4m.js → easter-egg-3d-28f880xx.js} +1 -1
  88. package/dist/{execute-cjrfxfnm.js → execute-9seb79vh.js} +1 -1
  89. package/dist/{external-channel-xbmxcg09.js → external-channel-smz7jyth.js} +2 -2
  90. package/dist/{getMachineId-bsd-brn0xym3.js → getMachineId-bsd-whfre6gr.js} +3 -3
  91. package/dist/{getMachineId-darwin-t339sfxm.js → getMachineId-darwin-5zjynb19.js} +3 -3
  92. package/dist/{getMachineId-linux-5eq95d8b.js → getMachineId-linux-8mksrrsq.js} +3 -3
  93. package/dist/{getMachineId-unsupported-qavrtc7j.js → getMachineId-unsupported-fxtqsdt2.js} +3 -3
  94. package/dist/{getMachineId-win-q7fe1wx7.js → getMachineId-win-nzfmz857.js} +3 -3
  95. package/dist/{grok-adapter-ag4wks2a.js → grok-adapter-z0nnvtbn.js} +7 -7
  96. package/dist/handlers-pspryh6g.js +41 -0
  97. package/dist/{heartbeat-psfvcgwp.js → heartbeat-f928aqyp.js} +20 -20
  98. package/dist/{hook-cyq4mryn.js → hook-d7c605r4.js} +7 -7
  99. package/dist/{hook-8pz04697.js → hook-q9hv5xf9.js} +5 -5
  100. package/dist/{http-haj1nd6k.js → http-q6j4k989.js} +934 -180
  101. package/dist/{index-z5h22pes.js → index-5nb266jr.js} +17 -17
  102. package/dist/{index-ysd45kgc.js → index-8fxt5eah.js} +10 -10
  103. package/dist/{index-zgvqzjsw.js → index-cekzwhez.js} +5 -5
  104. package/dist/{index-zyq8ct52.js → index-ch0c1xff.js} +17 -17
  105. package/dist/{index-syvhnvtw.js → index-ebnjhxh1.js} +1 -1
  106. package/dist/{index-y1ajx04a.js → index-ed8dy8rn.js} +20 -20
  107. package/dist/{index-jwt2bj6f.js → index-ejqdkkqx.js} +11 -10
  108. package/dist/{index-dr94zbnh.js → index-farm1zsn.js} +32661 -29443
  109. package/dist/{index-q52f47pe.js → index-yn1yxg85.js} +1 -1
  110. package/dist/{keepalive-xdvsamvj.js → keepalive-zzc3hks1.js} +3 -3
  111. package/dist/{lead-hqg57kdp.js → lead-c0pjcaw4.js} +36 -36
  112. package/dist/{maintenance-4y2xzh3a.js → maintenance-kqsxzpzx.js} +1 -1
  113. package/dist/{oauth-refresh-sweep-jd4r7zbs.js → oauth-refresh-sweep-7qt0g55z.js} +2 -2
  114. package/dist/{onboard-18447q9q.js → onboard-k7x13fqg.js} +118 -5
  115. package/dist/{opencode-adapter-26b2frdf.js → opencode-adapter-tqvrtter.js} +5 -5
  116. package/dist/{otel-impl-f9tgr8yw.js → otel-impl-sqkf6k3b.js} +97 -97
  117. package/dist/{pi-mono-adapter-qn6qbga1.js → pi-mono-adapter-m2qnafxm.js} +63 -9
  118. package/dist/{pricing-refresh-dw2tgb53.js → pricing-refresh-wkpwhgvs.js} +3 -3
  119. package/dist/{rbac-roles-s4nwj2m3.js → rbac-roles-dpn0c0wj.js} +7 -4
  120. package/dist/{rbac-roles-aygm2760.js → rbac-roles-wk7xd6a6.js} +2 -2
  121. package/dist/{render-v2-yfvdfr0x.js → render-v2-ex2m4g2k.js} +3 -3
  122. package/dist/{resume-1bmvjbf3.js → resume-725g9b0b.js} +7 -7
  123. package/dist/{runtime-f7tpzedy.js → runtime-kajas15g.js} +1 -1
  124. package/dist/{session-access-r5garej6.js → session-access-0j2ezjje.js} +8 -4
  125. package/dist/{session-source-backfill-424wzv0r.js → session-source-backfill-62202awe.js} +2 -2
  126. package/dist/{setup-8tygvxkq.js → setup-9sj11ax6.js} +1 -1
  127. package/dist/{virtual-modules-xsw323b3.js → virtual-modules-c6fcq6zr.js} +2 -2
  128. package/dist/{work-objects-ggnghpgf.js → work-objects-0y07495y.js} +20 -20
  129. package/dist/{worker-sqjd1nqf.js → worker-d4q9sqbc.js} +36 -36
  130. package/openapi.json +1070 -9
  131. package/package.json +3 -3
  132. package/src/be/connector-codes.ts +65 -22
  133. package/src/be/memory/boot-reembed.ts +4 -1
  134. package/src/be/memory/constants.ts +6 -0
  135. package/src/be/memory/embedding-recovery.ts +48 -0
  136. package/src/be/memory/index-content.ts +5 -2
  137. package/src/be/memory/providers/openai-embedding.ts +65 -14
  138. package/src/be/memory/task-learning.ts +110 -8
  139. package/src/be/memory/types.ts +13 -2
  140. package/src/be/migrations/246_connector_code_kind.sql +11 -0
  141. package/src/be/onboarding.ts +125 -15
  142. package/src/be/rbac-roles.ts +4 -1
  143. package/src/be/scripts/embeddings.ts +2 -2
  144. package/src/be/seed/agent-fs-provision.ts +75 -14
  145. package/src/be/swarm-config-guard.ts +3 -0
  146. package/src/cli.tsx +9 -0
  147. package/src/comb/config.ts +6 -1
  148. package/src/commands/onboard/agent-fs-version.ts +5 -0
  149. package/src/commands/onboard/compose-generator.ts +112 -1
  150. package/src/commands/onboard/env-generator.ts +9 -0
  151. package/src/commands/onboard/tei-image.ts +12 -0
  152. package/src/commands/onboard/types.ts +7 -0
  153. package/src/commands/onboard.tsx +11 -1
  154. package/src/commands/runner.ts +14 -1
  155. package/src/http/agentfs-proxy.ts +369 -0
  156. package/src/http/all-routes.ts +1 -0
  157. package/src/http/codex-oauth-device.ts +8 -1
  158. package/src/http/fs.ts +64 -10
  159. package/src/http/inbox-state.ts +27 -1
  160. package/src/http/index.ts +4 -1
  161. package/src/http/memory.ts +7 -4
  162. package/src/http/onboarding.ts +139 -11
  163. package/src/http/session-access.ts +24 -0
  164. package/src/http/sessions.ts +10 -6
  165. package/src/http/tasks.ts +85 -11
  166. package/src/http/telemetry-ui.ts +1 -1
  167. package/src/http/user-roles.ts +4 -1
  168. package/src/http/users.ts +272 -3
  169. package/src/http/utils.ts +22 -3
  170. package/src/prompts/base-prompt.ts +24 -9
  171. package/src/prompts/session-templates.ts +26 -2
  172. package/src/providers/amp-adapter.ts +1 -1
  173. package/src/providers/codex-adapter.ts +6 -1
  174. package/src/providers/cursor-adapter.ts +1 -1
  175. package/src/providers/pi-mono-adapter.ts +58 -3
  176. package/src/rbac/legacy-policy.ts +1 -0
  177. package/src/rbac/permissions.ts +4 -0
  178. package/src/tests/agent-fs-provision-seeder.test.ts +261 -92
  179. package/src/tests/agentfs-proxy.test.ts +1106 -0
  180. package/src/tests/base-prompt.test.ts +69 -2
  181. package/src/tests/codex-adapter.test.ts +46 -8
  182. package/src/tests/codex-oauth-device.test.ts +12 -1
  183. package/src/tests/comb-config-guard.test.ts +20 -0
  184. package/src/tests/comb-links.test.ts +79 -1
  185. package/src/tests/comb-status.test.ts +44 -0
  186. package/src/tests/docs-index-footprint.test.js +3 -3
  187. package/src/tests/fixtures/onboard/baseline-states.ts +68 -0
  188. package/src/tests/fixtures/onboard/content.compose.yml.txt +190 -0
  189. package/src/tests/fixtures/onboard/content.env.txt +29 -0
  190. package/src/tests/fixtures/onboard/dev.compose.yml.txt +155 -0
  191. package/src/tests/fixtures/onboard/dev.env.txt +28 -0
  192. package/src/tests/fixtures/onboard/full-all.compose.yml.txt +582 -0
  193. package/src/tests/fixtures/onboard/full-all.env.txt +59 -0
  194. package/src/tests/fixtures/onboard/full.compose.yml.txt +435 -0
  195. package/src/tests/fixtures/onboard/full.env.txt +36 -0
  196. package/src/tests/fixtures/onboard/research.compose.yml.txt +155 -0
  197. package/src/tests/fixtures/onboard/research.env.txt +28 -0
  198. package/src/tests/fixtures/onboard/solo.compose.yml.txt +85 -0
  199. package/src/tests/fixtures/onboard/solo.env.txt +26 -0
  200. package/src/tests/http-log-scrubbing.test.ts +34 -1
  201. package/src/tests/http-task-write-ownership.test.ts +5 -1
  202. package/src/tests/invites-http.test.ts +464 -0
  203. package/src/tests/memory-embedding-input-cap.test.ts +122 -1
  204. package/src/tests/memory-embedding-recovery.test.ts +137 -0
  205. package/src/tests/onboard-compose.test.ts +142 -0
  206. package/src/tests/onboard-env.test.ts +24 -0
  207. package/src/tests/onboard-non-interactive.test.ts +29 -0
  208. package/src/tests/onboard-tei-image.test.ts +36 -0
  209. package/src/tests/onboarding.test.ts +451 -4
  210. package/src/tests/openai-embedding-provider.test.ts +105 -2
  211. package/src/tests/pi-mono-session-cost-on-abort.test.ts +270 -0
  212. package/src/tests/preload-env.test.ts +4 -0
  213. package/src/tests/preload.ts +4 -0
  214. package/src/tests/prompt-template-session.test.ts +19 -3
  215. package/src/tests/rbac-admission-e2e.test.ts +22 -6
  216. package/src/tests/rbac-admission.test.ts +342 -4
  217. package/src/tests/rbac-engine.test.ts +1 -0
  218. package/src/tests/rbac-roles.test.ts +9 -1
  219. package/src/tests/send-task-requester-gate.test.ts +64 -3
  220. package/src/tests/sessions-move.test.ts +39 -1
  221. package/src/tests/store-progress-learning.test.ts +95 -2
  222. package/src/tests/whoami-route.test.ts +41 -0
  223. package/src/tools/inject-learning.ts +1 -1
  224. package/src/tools/memory-edit.ts +1 -1
  225. package/src/tools/memory-search.ts +1 -1
  226. package/src/tools/store-progress.ts +5 -1
  227. package/src/tools/utils.ts +12 -1
  228. package/src/utils/constants.ts +39 -0
  229. package/src/utils/task-attachment-links.ts +15 -3
  230. package/templates/skills/swarm-docs/content.md +1 -1
  231. package/dist/handlers-tzmevqb5.js +0 -41
  232. package/dist/{azure-openai-responses-9kfwhmn7.js → azure-openai-responses-43asa23s.js} +0 -0
  233. package/dist/{node-vxx896fa.js → node-t28cc56b.js} +0 -0
  234. package/dist/{openai-codex-responses-79s8xnbh.js → openai-codex-responses-jhr3sjtm.js} +3 -3
  235. package/dist/{openai-completions-tdbyts0x.js → openai-completions-gs6yn37k.js} +3 -3
  236. /package/dist/{openai-responses-dm3t5vqp.js → openai-responses-jqm9vwwk.js} +0 -0
@@ -34,6 +34,10 @@ const ENV_KEYS = [
34
34
  "SLACK_SIGNING_SECRET",
35
35
  "STEERING_ENABLED",
36
36
  "AGENT_FS_API_URL",
37
+ "AGENT_FS_LINK_TARGET",
38
+ "AGENT_FS_LIVE_URL",
39
+ "APP_URL",
40
+ "DASHBOARD_URL",
37
41
  "SCRIPTS_ONLY_MCP",
38
42
  ] as const;
39
43
 
@@ -495,6 +499,47 @@ describe("getBasePrompt: outputs section", () => {
495
499
  expect(result).not.toContain(NO_AGENT_FS_LINE);
496
500
  });
497
501
 
502
+ test("AGENT_FS_LINK_TARGET=dashboard with APP_URL links people to <APP_URL>/file/~/", async () => {
503
+ process.env.AGENT_FS_API_URL = "http://localhost:8787";
504
+ process.env.AGENT_FS_LINK_TARGET = "dashboard";
505
+ process.env.APP_URL = "http://localhost:5274/";
506
+ const result = await getBasePrompt({ ...minimalArgs, traits: localTraits });
507
+ expect(result).toContain(
508
+ "Give people the live app link `http://localhost:5274/file/~/<org>/<drive>/<path>`.",
509
+ );
510
+ expect(result).not.toContain("AGENT_FS_LIVE_URL}/file/~/");
511
+ });
512
+
513
+ test("AGENT_FS_LINK_TARGET=dashboard without APP_URL falls back to the live base", async () => {
514
+ process.env.AGENT_FS_API_URL = "http://localhost:8787";
515
+ process.env.AGENT_FS_LINK_TARGET = "dashboard";
516
+ process.env.AGENT_FS_LIVE_URL = "https://live.example.test/";
517
+ const result = await getBasePrompt({ ...minimalArgs, traits: localTraits });
518
+ expect(result).toContain(
519
+ "Give people the live app link `https://live.example.test/file/~/<org>/<drive>/<path>`.",
520
+ );
521
+ });
522
+
523
+ test("AGENT_FS_LINK_TARGET=live uses the default live host", async () => {
524
+ process.env.AGENT_FS_API_URL = "http://localhost:8787";
525
+ process.env.AGENT_FS_LINK_TARGET = "live";
526
+ process.env.APP_URL = "http://localhost:5274";
527
+ const result = await getBasePrompt({ ...minimalArgs, traits: localTraits });
528
+ expect(result).toContain(
529
+ "Give people the live app link `https://live.agent-fs.dev/file/~/<org>/<drive>/<path>`.",
530
+ );
531
+ });
532
+
533
+ test("an invalid AGENT_FS_LINK_TARGET keeps the exact old sentence", async () => {
534
+ process.env.AGENT_FS_API_URL = "http://localhost:8787";
535
+ process.env.AGENT_FS_LINK_TARGET = "Dashboard2";
536
+ process.env.APP_URL = "http://localhost:5274";
537
+ const result = await getBasePrompt({ ...minimalArgs, traits: localTraits });
538
+ expect(result).toContain(
539
+ `Give people the live app link \`\${AGENT_FS_LIVE_URL}/file/~/<org>/<drive>/<path>\`. Make an \`agent-fs share-create\` link only when the requester asks for one or the reader has no agent-fs login.`,
540
+ );
541
+ });
542
+
498
543
  test("uses the no_agent_fs variant when AGENT_FS_API_URL is unset", async () => {
499
544
  const result = await getBasePrompt({ ...minimalArgs, traits: localTraits });
500
545
  expect(result).toContain("## Outputs");
@@ -1193,9 +1238,7 @@ describe("getBasePrompt: harness tool discovery", () => {
1193
1238
 
1194
1239
  test.each([
1195
1240
  "pi",
1196
- "codex",
1197
1241
  "claude-managed",
1198
- "opencode",
1199
1242
  "acp",
1200
1243
  ])("%s without tool search gets direct-tool guidance", async (provider) => {
1201
1244
  const result = await getBasePrompt({
@@ -1220,6 +1263,30 @@ describe("getBasePrompt: harness tool discovery", () => {
1220
1263
  expect(result).toContain('import { store_progress } from "agent-swarm"');
1221
1264
  });
1222
1265
 
1266
+ test("codex gets exec guidance with the normalized tool name", async () => {
1267
+ const result = await getBasePrompt({
1268
+ ...minimalArgs,
1269
+ provider: "codex",
1270
+ traits: { ...localTraits, hasToolSearch: false },
1271
+ });
1272
+ expect(result).not.toContain(deferred);
1273
+ expect(result).not.toContain("already in your tool list");
1274
+ expect(result).toContain("await tools.mcp__agent_swarm__store_progress({ ... })");
1275
+ expect(result).toContain("`ALL_TOOLS`");
1276
+ });
1277
+
1278
+ test("opencode gets execute guidance with the bracketed tool path", async () => {
1279
+ const result = await getBasePrompt({
1280
+ ...minimalArgs,
1281
+ provider: "opencode",
1282
+ traits: { ...localTraits, hasToolSearch: false },
1283
+ });
1284
+ expect(result).not.toContain(deferred);
1285
+ expect(result).not.toContain("already in your tool list");
1286
+ expect(result).toContain('await tools.swarm["store-progress"]({ ... })');
1287
+ expect(result).toContain("`search({ query })`");
1288
+ });
1289
+
1223
1290
  test("omitted tool-search capability defaults to direct tools", async () => {
1224
1291
  const result = await getBasePrompt({ ...minimalArgs, traits: localTraits });
1225
1292
  expect(result).not.toContain(deferred);
@@ -34,6 +34,7 @@ import {
34
34
  resolveCodexModel,
35
35
  } from "../providers/codex-models";
36
36
  import type { ProviderEvent, ProviderResult, ProviderSessionConfig } from "../providers/types";
37
+ import { isCodexAuthFailureReason } from "../utils/codex-auth-failure";
37
38
  import { HELD_NOTE_TEXT, holdTemplateRender } from "./fixtures/held-template-render";
38
39
 
39
40
  /**
@@ -1177,23 +1178,38 @@ describe("CodexSession event mapping", () => {
1177
1178
  expect(result.failureReason).toContain("[rate-limit]");
1178
1179
  });
1179
1180
 
1180
- test("turn.failed with auth error rewrites + sets errorCategory=authentication_failed", async () => {
1181
+ test.each([
1182
+ undefined,
1183
+ 0,
1184
+ 2,
1185
+ ])("turn.failed auth error identifies pool slot %p", async (codexSlot) => {
1186
+ const raw = "Request failed: HTTP 401 Unauthorized — Invalid API key provided.";
1181
1187
  const events: ThreadEvent[] = [
1182
1188
  { type: "thread.started", thread_id: "thread-auth" },
1183
1189
  { type: "turn.started" },
1184
1190
  {
1185
1191
  type: "turn.failed",
1186
- error: { message: "Request failed: HTTP 401 Unauthorized — Invalid API key provided." },
1192
+ error: { message: raw },
1187
1193
  },
1188
1194
  ];
1189
1195
 
1190
1196
  const { emitted, result } = await runSessionWithFakeThread(
1191
1197
  events,
1192
- testConfig({ logFile: join(tmpLogDir, "auth.log"), cwd: "" }),
1198
+ testConfig({
1199
+ logFile: join(tmpLogDir, "auth.log"),
1200
+ cwd: "",
1201
+ codexSlot,
1202
+ apiUrl: "",
1203
+ apiKey: "",
1204
+ }),
1193
1205
  );
1194
1206
 
1207
+ const expected =
1208
+ codexSlot === undefined
1209
+ ? `[auth-error] Codex authentication failed — check OPENAI_API_KEY or ChatGPT login. Original error: ${raw}`
1210
+ : `[auth-error] Codex pool slot ${codexSlot} authentication failed — re-run agent-swarm codex-login --slot ${codexSlot}. Original error: ${raw}`;
1195
1211
  const errorEvent = emitted.find((e) => e.type === "error");
1196
- expect(errorEvent?.type === "error" && errorEvent.message).toContain("[auth-error]");
1212
+ expect(errorEvent?.type === "error" && errorEvent.message).toBe(expected);
1197
1213
 
1198
1214
  const resultEvent = emitted.findLast((e) => e.type === "result");
1199
1215
  expect(resultEvent?.type === "result" && resultEvent.errorCategory).toBe(
@@ -1201,7 +1217,8 @@ describe("CodexSession event mapping", () => {
1201
1217
  );
1202
1218
 
1203
1219
  expect(result.isError).toBe(true);
1204
- expect(result.failureReason).toContain("[auth-error]");
1220
+ expect(result.failureReason).toBe(expected);
1221
+ expect(isCodexAuthFailureReason(result.failureReason)).toBe(true);
1205
1222
  });
1206
1223
 
1207
1224
  test("abort() resolves the session with cancelled result", async () => {
@@ -2517,7 +2534,9 @@ describe("CodexSession — rate-limit error preservation", () => {
2517
2534
  expect(resetMs).toBeGreaterThan(Date.now());
2518
2535
  });
2519
2536
 
2520
- test("revoked-while-valid OAuth pool failure ('Failed to refresh token') classifies as [auth-error]/authentication_failed", async () => {
2537
+ test.each([
2538
+ 0, 2,
2539
+ ])("revoked-while-valid OAuth failure identifies pool slot %i after transport exit", async (codexSlot) => {
2521
2540
  // A credential revoked server-side while its access token is still
2522
2541
  // unexpired never enters getValidCodexOAuth (the token looks fresh, it
2523
2542
  // fast-returns) — the spawned Codex CLI self-refreshes straight from the
@@ -2534,13 +2553,32 @@ describe("CodexSession — rate-limit error preservation", () => {
2534
2553
  ];
2535
2554
  const sdkThrow = new Error("Codex Exec exited with code 1: Reading prompt from stdin");
2536
2555
 
2537
- const { result } = await runSessionWithThrowingThread(
2556
+ const { emitted, result } = await runSessionWithThrowingThread(
2538
2557
  events,
2539
2558
  sdkThrow,
2540
- testConfig({ logFile: join(tmpLogDir, "authfail-preserve.log"), cwd: "" }),
2559
+ testConfig({
2560
+ logFile: join(tmpLogDir, "authfail-preserve.log"),
2561
+ cwd: "",
2562
+ codexSlot,
2563
+ apiUrl: "",
2564
+ apiKey: "",
2565
+ }),
2541
2566
  );
2542
2567
 
2543
2568
  expect(result.failureReason).toMatch(/\[auth-error\]/);
2569
+ expect(result.failureReason).toContain(`pool slot ${codexSlot}`);
2570
+ expect(result.failureReason).toContain(`agent-swarm codex-login --slot ${codexSlot}`);
2571
+ expect(result.failureReason).toContain(refreshFailMsg);
2572
+ expect(isCodexAuthFailureReason(result.failureReason)).toBe(true);
2573
+ const errorEvents = emitted.filter((e) => e.type === "error");
2574
+ expect(errorEvents.length).toBeGreaterThan(0);
2575
+ for (const event of errorEvents) {
2576
+ expect(event.message).toBe(result.failureReason);
2577
+ }
2578
+ const resultEvent = emitted.findLast((e) => e.type === "result");
2579
+ expect(resultEvent?.type === "result" && resultEvent.errorCategory).toBe(
2580
+ "authentication_failed",
2581
+ );
2544
2582
  expect(result.isError).toBe(true);
2545
2583
  });
2546
2584
 
@@ -1,7 +1,14 @@
1
1
  import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test } from "bun:test";
2
2
  import { createServer, type Server } from "node:http";
3
3
  import { decryptSecret, encryptSecret, getEncryptionKey } from "../be/crypto";
4
- import { closeDb, getDbClient, getSwarmConfigs, initDb, upsertSwarmConfig } from "../be/db";
4
+ import {
5
+ closeDb,
6
+ getDbClient,
7
+ getKeyStatuses,
8
+ getSwarmConfigs,
9
+ initDb,
10
+ upsertSwarmConfig,
11
+ } from "../be/db";
5
12
  import { getCodexDeviceFlow, upsertCodexDeviceFlow } from "../be/db/codex-oauth-device-flows";
6
13
  import type { OnboardingState } from "../be/onboarding";
7
14
  import { handleCodexOAuthDevice } from "../http/codex-oauth-device";
@@ -92,6 +99,7 @@ afterAll(async () => {
92
99
  beforeEach(async () => {
93
100
  await getDbClient().run("DELETE FROM codex_oauth_device_flows");
94
101
  await getDbClient().run("DELETE FROM swarm_config");
102
+ await getDbClient().run("DELETE FROM api_key_status");
95
103
  fetchImpl = async () => {
96
104
  throw new Error("Unexpected fetch");
97
105
  };
@@ -231,6 +239,9 @@ describe("Codex device HTTP routes", () => {
231
239
  const onboarding = await getSwarmConfigs({ scope: "global", key: "onboarding_state" });
232
240
  const state = JSON.parse(onboarding[0]!.value) as OnboardingState;
233
241
  expect(state.steps.ai).toMatchObject({ status: "done", method: "codex_device" });
242
+ // The sign-in itself checks the account: the account list says "Signed in".
243
+ const statuses = await getKeyStatuses("CODEX_OAUTH", "global", null);
244
+ expect(statuses.map((s) => [s.keySuffix, s.keyIndex])).toEqual([["-full", 1]]);
234
245
  });
235
246
 
236
247
  test("throttles upstream polling to the requested interval", async () => {
@@ -11,6 +11,15 @@ describe("Comb config keys", () => {
11
11
  }
12
12
  });
13
13
 
14
+ test("AGENT_FS_PROXY accepts boolean literals only", () => {
15
+ for (const value of ["true", "false", "1", "0", " TRUE "]) {
16
+ expect(validateConfigValue("AGENT_FS_PROXY", value)).toBeNull();
17
+ }
18
+ for (const value of ["yes", "on", ""]) {
19
+ expect(validateConfigValue("AGENT_FS_PROXY", value)).toContain("Invalid AGENT_FS_PROXY");
20
+ }
21
+ });
22
+
14
23
  test("AGENT_FS_PUBLIC_URL accepts blank or an http(s) base URL", () => {
15
24
  for (const value of ["", "http://localhost:7433", "https://agent-fs.example.com/base"]) {
16
25
  expect(validateConfigValue("AGENT_FS_PUBLIC_URL", value)).toBeNull();
@@ -28,6 +37,17 @@ describe("Comb config keys", () => {
28
37
  }
29
38
  });
30
39
 
40
+ test("AGENT_FS_LINK_TARGET accepts dashboard, live, or blank", () => {
41
+ for (const value of ["dashboard", "live", "", " live "]) {
42
+ expect(validateConfigValue("AGENT_FS_LINK_TARGET", value)).toBeNull();
43
+ }
44
+ for (const value of ["Dashboard2", "comb", true]) {
45
+ expect(validateConfigValue("AGENT_FS_LINK_TARGET", value)).toContain(
46
+ "Invalid AGENT_FS_LINK_TARGET",
47
+ );
48
+ }
49
+ });
50
+
31
51
  test("OPENROUTER_BASE_URL keeps its rules after sharing the URL validator", () => {
32
52
  expect(validateConfigValue("OPENROUTER_BASE_URL", "")).toBeNull();
33
53
  expect(validateConfigValue("OPENROUTER_BASE_URL", "https://api.example.com/v1")).toBeNull();
@@ -1,7 +1,12 @@
1
1
  import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
2
  import { buildCitationUrl } from "../be/task-citations";
3
3
  import type { TaskAttachment } from "../types";
4
- import { buildAgentFsLiveUrl, buildCombFileUrl } from "../utils/constants";
4
+ import {
5
+ buildAgentFsLiveUrl,
6
+ buildCombFileUrl,
7
+ readAgentFsLinkTarget,
8
+ resolveAgentFsLinkBase,
9
+ } from "../utils/constants";
5
10
  import { taskAttachmentDisplayUrl } from "../utils/task-attachment-links";
6
11
 
7
12
  const ENV_KEYS = [
@@ -10,6 +15,7 @@ const ENV_KEYS = [
10
15
  "DASHBOARD_URL",
11
16
  "AGENT_FS_API_URL",
12
17
  "AGENT_FS_LIVE_URL",
18
+ "AGENT_FS_LINK_TARGET",
13
19
  "AGENT_FS_DEFAULT_ORG_ID",
14
20
  "AGENT_FS_DEFAULT_DRIVE_ID",
15
21
  ] as const;
@@ -160,3 +166,75 @@ describe("agent-fs citations", () => {
160
166
  ).toBe(COMB);
161
167
  });
162
168
  });
169
+
170
+ describe("AGENT_FS_LINK_TARGET", () => {
171
+ test("unset: no target, and attachments keep the automatic choice", () => {
172
+ expect(readAgentFsLinkTarget()).toBeNull();
173
+ expect(resolveAgentFsLinkBase("attachment")).toBeNull();
174
+ expect(resolveAgentFsLinkBase("prompt")).toBeNull();
175
+ expect(taskAttachmentDisplayUrl(agentFsAttachment())).toBe(LIVE);
176
+ process.env.COMB_ENABLED = "true";
177
+ process.env.APP_URL = "http://localhost:5274";
178
+ expect(taskAttachmentDisplayUrl(agentFsAttachment())).toBe(COMB);
179
+ });
180
+
181
+ test("live on a Comb + APP_URL install: the live URL", () => {
182
+ process.env.COMB_ENABLED = "true";
183
+ process.env.APP_URL = "http://localhost:5274";
184
+ process.env.AGENT_FS_LINK_TARGET = " Live ";
185
+ expect(readAgentFsLinkTarget()).toBe("live");
186
+ expect(taskAttachmentDisplayUrl(agentFsAttachment())).toBe(LIVE);
187
+ expect(resolveAgentFsLinkBase("prompt")).toBe("https://live.example.test");
188
+ });
189
+
190
+ test("dashboard with Comb on and APP_URL: the dashboard URL", () => {
191
+ process.env.COMB_ENABLED = "true";
192
+ process.env.APP_URL = "http://localhost:5274/";
193
+ process.env.AGENT_FS_LINK_TARGET = "dashboard";
194
+ expect(taskAttachmentDisplayUrl(agentFsAttachment())).toBe(COMB);
195
+ expect(resolveAgentFsLinkBase("prompt")).toBe("http://localhost:5274");
196
+ });
197
+
198
+ test("dashboard with Comb off: attachments use the live URL, the prompt still uses APP_URL", () => {
199
+ process.env.APP_URL = "http://localhost:5274";
200
+ process.env.AGENT_FS_LINK_TARGET = "dashboard";
201
+ expect(taskAttachmentDisplayUrl(agentFsAttachment())).toBe(LIVE);
202
+ expect(resolveAgentFsLinkBase("attachment")).toBe("https://live.example.test");
203
+ expect(resolveAgentFsLinkBase("prompt")).toBe("http://localhost:5274");
204
+ });
205
+
206
+ test("dashboard without APP_URL: the live URL on both surfaces", () => {
207
+ process.env.COMB_ENABLED = "true";
208
+ process.env.AGENT_FS_LINK_TARGET = "dashboard";
209
+ expect(taskAttachmentDisplayUrl(agentFsAttachment())).toBe(LIVE);
210
+ expect(resolveAgentFsLinkBase("prompt")).toBe("https://live.example.test");
211
+ });
212
+
213
+ test("an invalid or blank value counts as unset", () => {
214
+ process.env.COMB_ENABLED = "true";
215
+ process.env.APP_URL = "http://localhost:5274";
216
+ for (const value of ["Dashboard2", "comb", "", " "]) {
217
+ process.env.AGENT_FS_LINK_TARGET = value;
218
+ expect(readAgentFsLinkTarget()).toBeNull();
219
+ expect(resolveAgentFsLinkBase("prompt")).toBeNull();
220
+ expect(taskAttachmentDisplayUrl(agentFsAttachment())).toBe(COMB);
221
+ }
222
+ });
223
+
224
+ test("comb: false still links to the live URL", () => {
225
+ process.env.COMB_ENABLED = "true";
226
+ process.env.APP_URL = "http://localhost:5274";
227
+ process.env.AGENT_FS_LINK_TARGET = "dashboard";
228
+ expect(taskAttachmentDisplayUrl(agentFsAttachment(), { comb: false })).toBe(LIVE);
229
+ });
230
+
231
+ test("a bad path still falls back to agent-fs:<path>", () => {
232
+ process.env.COMB_ENABLED = "true";
233
+ process.env.APP_URL = "http://localhost:5274";
234
+ process.env.AGENT_FS_LINK_TARGET = "dashboard";
235
+ expect(taskAttachmentDisplayUrl(agentFsAttachment({ path: "../x" }))).toBe("agent-fs:../x");
236
+ expect(
237
+ taskAttachmentDisplayUrl(agentFsAttachment({ orgId: undefined, driveId: undefined })),
238
+ ).toBe("agent-fs:comb-qa/notes.md");
239
+ });
240
+ });
@@ -15,6 +15,9 @@ const ENV_KEYS = [
15
15
  "AGENT_FS_DEFAULT_DRIVE_ID",
16
16
  "API_AGENT_FS_API_KEY",
17
17
  "AGENT_FS_API_KEY",
18
+ "AGENT_FS_PROXY",
19
+ "PUBLIC_MCP_BASE_URL",
20
+ "MCP_BASE_URL",
18
21
  ];
19
22
  const savedEnv = new Map<string, string | undefined>();
20
23
 
@@ -92,6 +95,47 @@ describe("/status agent_fs.comb", () => {
92
95
  expect((await comb()).api_url).toBe("https://files.example.com");
93
96
  });
94
97
 
98
+ test("AGENT_FS_PROXY on and a blank public URL: api_url is <PUBLIC_MCP_BASE_URL>/agentfs", async () => {
99
+ process.env.COMB_ENABLED = "true";
100
+ process.env.AGENT_FS_API_URL = "http://agent-fs:7433";
101
+ process.env.MCP_BASE_URL = "http://swarm-api:3013";
102
+ process.env.PUBLIC_MCP_BASE_URL = "https://swarm.example.com/";
103
+ for (const value of ["true", " TRUE ", "1"]) {
104
+ process.env.AGENT_FS_PROXY = value;
105
+ expect((await comb()).api_url).toBe("https://swarm.example.com/agentfs");
106
+ }
107
+ process.env.AGENT_FS_PUBLIC_URL = " ";
108
+ expect((await comb()).api_url).toBe("https://swarm.example.com/agentfs");
109
+ // No public URL: falls back to MCP_BASE_URL.
110
+ delete process.env.PUBLIC_MCP_BASE_URL;
111
+ expect((await comb()).api_url).toBe("http://swarm-api:3013/agentfs");
112
+ });
113
+
114
+ test("AGENT_FS_PROXY on: an explicit AGENT_FS_PUBLIC_URL still wins", async () => {
115
+ process.env.COMB_ENABLED = "true";
116
+ process.env.AGENT_FS_API_URL = "http://agent-fs:7433";
117
+ process.env.PUBLIC_MCP_BASE_URL = "https://swarm.example.com";
118
+ process.env.AGENT_FS_PROXY = "true";
119
+ process.env.AGENT_FS_PUBLIC_URL = "https://files.example.com/";
120
+ expect((await comb()).api_url).toBe("https://files.example.com");
121
+ });
122
+
123
+ test("AGENT_FS_PROXY off or without AGENT_FS_API_URL: api_url unchanged", async () => {
124
+ process.env.COMB_ENABLED = "true";
125
+ process.env.AGENT_FS_API_URL = "http://agent-fs:7433";
126
+ process.env.PUBLIC_MCP_BASE_URL = "https://swarm.example.com";
127
+ for (const value of [undefined, "false", "0", ""]) {
128
+ if (value === undefined) delete process.env.AGENT_FS_PROXY;
129
+ else process.env.AGENT_FS_PROXY = value;
130
+ expect((await comb()).api_url).toBe("http://agent-fs:7433");
131
+ }
132
+ process.env.AGENT_FS_PROXY = "true";
133
+ delete process.env.AGENT_FS_API_URL;
134
+ const result = await comb();
135
+ expect(result.enabled).toBe(false);
136
+ expect(result.api_url).toBeNull();
137
+ });
138
+
95
139
  test("ids come from AGENT_FS_DEFAULT_*, and live_url from AGENT_FS_LIVE_URL", async () => {
96
140
  process.env.COMB_ENABLED = "true";
97
141
  process.env.AGENT_FS_API_URL = "http://agent-fs:7433";
@@ -78,7 +78,7 @@ describe("docs API index footprint", () => {
78
78
  expect(tasks).toContain('"path":"/api/task-templates","method":"get"');
79
79
  });
80
80
 
81
- test("OpenAPI and generated pages keep all 456 operations", async () => {
81
+ test("OpenAPI and generated pages keep all 466 operations", async () => {
82
82
  const spec = await Bun.file(resolve(repoRoot, "openapi.json")).json();
83
83
  const operationKeys = [];
84
84
  const tags = new Set();
@@ -103,8 +103,8 @@ describe("docs API index footprint", () => {
103
103
  expect(spec.paths["/api/agents/{id}/runtime"]?.get).toBeDefined();
104
104
 
105
105
  const generatedKeys = await generatedOperationKeys();
106
- expect(operationKeys).toHaveLength(456);
107
- expect(generatedKeys).toHaveLength(456);
106
+ expect(operationKeys).toHaveLength(466);
107
+ expect(generatedKeys).toHaveLength(466);
108
108
  expect(new Set(generatedKeys)).toEqual(new Set(operationKeys));
109
109
  });
110
110
 
@@ -0,0 +1,68 @@
1
+ import { getPresetById } from "../../../commands/onboard/presets.ts";
2
+ import { INITIAL_STATE, type OnboardState } from "../../../commands/onboard/types.ts";
3
+
4
+ /**
5
+ * Deterministic onboard states, one per unattended preset plus one variant
6
+ * with every integration, Bedrock (profile) and Podman. The fixtures next to
7
+ * this file hold the compose and .env these states rendered before the
8
+ * agent-fs bundle existed, so flag-off output stays byte-identical.
9
+ */
10
+ export const BASELINE_PRESET_IDS = ["full", "dev", "content", "research", "solo"] as const;
11
+
12
+ function agentIdsFor(state: Pick<OnboardState, "services">): Record<string, string> {
13
+ const ids: Record<string, string> = {};
14
+ let n = 0;
15
+ const next = () => `00000000-0000-4000-8000-${String(++n).padStart(12, "0")}`;
16
+ for (const svc of state.services) {
17
+ if (svc.count === 1) {
18
+ ids[svc.isLead ? "lead" : `worker-${svc.role}`] = next();
19
+ } else {
20
+ for (let i = 1; i <= svc.count; i++) ids[`worker-${svc.role}-${i}`] = next();
21
+ }
22
+ }
23
+ return ids;
24
+ }
25
+
26
+ export function baselineState(presetId: string, variant: "default" | "all" = "default") {
27
+ const preset = getPresetById(presetId);
28
+ if (!preset) throw new Error(`unknown preset ${presetId}`);
29
+ const base: OnboardState = {
30
+ ...INITIAL_STATE,
31
+ nonInteractive: true,
32
+ presetId: preset.id,
33
+ services: preset.services,
34
+ apiKey: "example-test-api-key",
35
+ claudeOAuthToken: "example-test-oauth",
36
+ outputDir: "/tmp/onboard-fixture",
37
+ };
38
+ base.agentIds = agentIdsFor(base);
39
+ if (variant === "default") return base;
40
+ return {
41
+ ...base,
42
+ provider: "bedrock",
43
+ harness: "pi",
44
+ awsRegion: "us-east-1",
45
+ awsProfile: "example-profile",
46
+ modelOverride: "example-model",
47
+ containerEngine: "podman",
48
+ pullPolicy: "missing",
49
+ apiPort: 3999,
50
+ maxConcurrentTasks: 3,
51
+ integrations: { github: true, slack: true, gitlab: true, sentry: true },
52
+ githubToken: "example-gh",
53
+ githubEmail: "dev@example.com",
54
+ githubName: "Example Dev",
55
+ slackBotToken: "example-slack-bot",
56
+ slackSigningSecret: "example-slack-signing",
57
+ slackMode: "http",
58
+ gitlabToken: "example-gitlab",
59
+ gitlabEmail: "dev@example.com",
60
+ sentryToken: "example-sentry",
61
+ sentryOrg: "example-org",
62
+ } satisfies OnboardState;
63
+ }
64
+
65
+ export const BASELINE_CASES: { name: string; state: OnboardState }[] = [
66
+ ...BASELINE_PRESET_IDS.map((id) => ({ name: id, state: baselineState(id) })),
67
+ { name: "full-all", state: baselineState("full", "all") },
68
+ ];