@desplega.ai/agent-swarm 1.172.0 → 2.0.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 (525) hide show
  1. package/README.md +1 -1
  2. package/dist/{acp-adapter-fpecx1g1.js → acp-adapter-sj2g0m84.js} +18 -17
  3. package/dist/{actions-pw87a1a8.js → actions-fegk44gr.js} +65 -35
  4. package/dist/{amp-adapter-8jem2bc0.js → amp-adapter-ad8ft89x.js} +8 -7
  5. package/dist/{anthropic-messages-8ee2f22q.js → anthropic-messages-r6czkjv1.js} +7 -7
  6. package/dist/{app-wmf0m843.js → app-kfvgbvzy.js} +19 -13
  7. package/dist/{assistant-emp1e108.js → assistant-z7zdy9ja.js} +65 -38
  8. package/dist/{boot-reembed-1de4f4aj.js → boot-reembed-5c2z0mbe.js} +11 -9
  9. package/dist/{boot-reembed-4ymn9atn.js → boot-reembed-j8xa181e.js} +11 -9
  10. package/dist/{boot-scrub-sweep-adhk1s8c.js → boot-scrub-sweep-hnkn3rgt.js} +12 -10
  11. package/dist/{boot-seeds-8q637aew.js → boot-seeds-kwjq4d2z.js} +15 -13
  12. package/dist/{claude-adapter-t26cv8a4.js → claude-adapter-9vmvpvgd.js} +20 -19
  13. package/dist/{claude-managed-adapter-xgxhqrt2.js → claude-managed-adapter-k9qae05j.js} +7 -5
  14. package/dist/{claude-managed-setup-bnmjarjc.js → claude-managed-setup-q54zx5q2.js} +6 -4
  15. package/dist/{claude-sdk-session-gsfsh662.js → claude-sdk-session-9jye6cxr.js} +12698 -11995
  16. package/dist/{cli-k46r9hmk.js → cli-03gedhng.js} +16 -14
  17. package/dist/{cli-kb4ehzx5.js → cli-1e4b6bzh.js} +7 -7
  18. package/dist/{cli-d603qf86.js → cli-29jc5q7w.js} +10 -8
  19. package/dist/cli-2m1ktfzz.js +17795 -0
  20. package/dist/{cli-8vs9trd4.js → cli-3dj8k4vy.js} +1 -1
  21. package/dist/{cli-zxm9p3x4.js → cli-3fbmmawm.js} +5 -5
  22. package/dist/{cli-gmdajfw5.js → cli-4jxm2qtz.js} +4987 -1708
  23. package/dist/{cli-tadfgd0t.js → cli-5hkm7whv.js} +33 -4
  24. package/dist/{cli-6jb2rf77.js → cli-5t4f29ez.js} +3 -3
  25. package/dist/{cli-7a0sde2x.js → cli-6cagzc96.js} +9 -0
  26. package/dist/cli-6cvfxs8r.js +47 -0
  27. package/dist/{cli-pj6x9qce.js → cli-6jyj59nb.js} +2 -2
  28. package/dist/{cli-388egyf6.js → cli-6razj6t4.js} +2 -2
  29. package/dist/{cli-qsztpfry.js → cli-74ahz61x.js} +2 -2
  30. package/dist/{cli-4nz8p1p5.js → cli-7x0g4sn1.js} +15474 -16082
  31. package/dist/{cli-yatxqynm.js → cli-9ha6hs2d.js} +1 -290
  32. package/dist/{cli-81r1wjyv.js → cli-9nfx44eh.js} +1 -1
  33. package/dist/cli-9traj3dn.js +638 -0
  34. package/dist/{cli-smcrrmhy.js → cli-agzebbd5.js} +9 -9
  35. package/dist/{cli-bf7485pm.js → cli-apx87jne.js} +2 -2
  36. package/dist/{cli-ztrv9y21.js → cli-bzszjf2p.js} +3689 -1081
  37. package/dist/{cli-c5km5znr.js → cli-c8r3kzzb.js} +1 -1
  38. package/dist/{cli-hsem16hb.js → cli-cgtksawx.js} +7 -4
  39. package/dist/{cli-db3m6zdt.js → cli-d2qr7aj4.js} +1 -1
  40. package/dist/{cli-1hgat6pa.js → cli-d5f9ed5c.js} +5 -5
  41. package/dist/cli-e3g5mbkz.js +776 -0
  42. package/dist/{cli-7jykv7ef.js → cli-e6mpjhed.js} +1 -1
  43. package/dist/{cli-j6kpj9az.js → cli-ee9gtz70.js} +1553 -68
  44. package/dist/{cli-e0a9sk00.js → cli-eejsgvm4.js} +4 -4
  45. package/dist/{cli-tgpfnm6r.js → cli-ef1hg1wv.js} +518 -59
  46. package/dist/cli-f527ce74.js +781 -0
  47. package/dist/{cli-ppy4nnnh.js → cli-f7kr7svz.js} +2 -2
  48. package/dist/cli-hjwmhcg1.js +10622 -0
  49. package/dist/{cli-2bw3w47j.js → cli-hnhtk6cj.js} +1 -1
  50. package/dist/{cli-ywm1azg9.js → cli-hnmhtzvh.js} +6 -4
  51. package/dist/{cli-4fnb6884.js → cli-hvqbtna4.js} +7 -1259
  52. package/dist/{cli-pstq9888.js → cli-j0pfptdp.js} +220 -103
  53. package/dist/cli-jta2eyzc.js +619 -0
  54. package/dist/{cli-esnpehf5.js → cli-k382tshb.js} +12 -13
  55. package/dist/cli-k3dp5tpf.js +263 -0
  56. package/dist/{cli-wd0hz6ah.js → cli-kbrt8gqa.js} +9 -91
  57. package/dist/cli-kceah1eh.js +92 -0
  58. package/dist/{cli-prrt9fa0.js → cli-ke896fcf.js} +10 -10
  59. package/dist/{cli-nv4zd0b9.js → cli-kt6zqg0e.js} +6 -6
  60. package/dist/{cli-g6t9texm.js → cli-m50t4309.js} +5 -18
  61. package/dist/cli-n2wq4j7m.js +6985 -0
  62. package/dist/{cli-c2pq2rjb.js → cli-n3hzg89b.js} +46 -15
  63. package/dist/{cli-tmhsxhb8.js → cli-n5vwg842.js} +23 -6
  64. package/dist/{cli-5rv1j9ga.js → cli-ne236afy.js} +53 -7
  65. package/dist/cli-p0sg24zp.js +16 -0
  66. package/dist/{cli-d161g69q.js → cli-p1x85d2t.js} +50 -2
  67. package/dist/{cli-7pz3b7x5.js → cli-przpmdgr.js} +25 -0
  68. package/dist/{cli-g4f5pj26.js → cli-q0ke1yww.js} +111 -25
  69. package/dist/{cli-e3pmyt39.js → cli-q50942rs.js} +1 -1
  70. package/dist/{cli-r44t9wqc.js → cli-r106725k.js} +2 -2
  71. package/dist/{cli-gj81e47t.js → cli-r2pnvj8g.js} +2 -2
  72. package/dist/{cli-r8smj8b8.js → cli-r62a35ea.js} +19 -3
  73. package/dist/{cli-cd0hv9nh.js → cli-ra0xzggj.js} +2 -2
  74. package/dist/cli-rhs9pz33.js +294 -0
  75. package/dist/{cli-nyhb1r6x.js → cli-s2sp957d.js} +52 -29
  76. package/dist/{cli-1x7da0de.js → cli-s4setq91.js} +8 -8
  77. package/dist/{cli-xczpdp9e.js → cli-se8w6wny.js} +6 -6
  78. package/dist/cli-skxvvpap.js +10140 -0
  79. package/dist/{cli-014vns2y.js → cli-stfs0k5s.js} +208 -525
  80. package/dist/{cli-0p3enk35.js → cli-tqaazemc.js} +9 -1
  81. package/dist/{cli-cf69s3ew.js → cli-tr4sxp68.js} +3 -3
  82. package/dist/{cli-8hzfb6xz.js → cli-tsde1hdr.js} +15 -15
  83. package/dist/{cli-cceskcvt.js → cli-txqb9s38.js} +8 -8
  84. package/dist/{cli-aspcaef3.js → cli-v1yb0rqb.js} +6 -6
  85. package/dist/{cli-j1pmc07z.js → cli-vatyhshy.js} +268 -40
  86. package/dist/{cli-04emfe2q.js → cli-vdach5a9.js} +5 -5
  87. package/dist/{cli-7avy4v8w.js → cli-w1q7h2r2.js} +5 -3
  88. package/dist/{cli-rcp5aqdw.js → cli-w7m81tb7.js} +1 -1
  89. package/dist/{cli-hwwvy36w.js → cli-wayw93nt.js} +3 -3
  90. package/dist/cli-wmgrkscj.js +23254 -0
  91. package/dist/{cli-gp1rtyvk.js → cli-z5jjpaxw.js} +29 -6
  92. package/dist/{cli-451ks6aw.js → cli-zkpks3r3.js} +1 -1
  93. package/dist/cli.js +32 -48
  94. package/dist/{codex-adapter-m4tgdysy.js → codex-adapter-y81sssk0.js} +16 -15
  95. package/dist/{codex-login-841kv5fk.js → codex-login-f2rcfpp9.js} +3 -2
  96. package/dist/{codex-session-runner-ymwzm6ea.js → codex-session-runner-8vx4j6a9.js} +16 -15
  97. package/dist/{commands-n0hshcvs.js → commands-kcpvpcrr.js} +21 -10
  98. package/dist/{controller-hq5ggpcp.js → controller-ywrmt6yd.js} +5 -4
  99. package/dist/{controllers-connect-y07cqcvy.js → controllers-connect-kwpvj3rf.js} +45 -38
  100. package/dist/{cursor-adapter-q8pgv5qa.js → cursor-adapter-cehv1qns.js} +7 -6
  101. package/dist/{db-hkqvrdcb.js → db-k8ankj20.js} +13 -11
  102. package/dist/{db-4w7tmt1g.js → db-ww5d7jq3.js} +29 -9
  103. package/dist/{devin-adapter-xgtj680j.js → devin-adapter-x1022qb1.js} +3 -2
  104. package/dist/{dispatcher-5sd69y14.js → dispatcher-e8gq5m68.js} +41 -34
  105. package/dist/{docker-f503k9p0.js → docker-ff6y1190.js} +3 -2
  106. package/dist/{dsh-adapter-2h8m4cab.js → dsh-adapter-3epjm3af.js} +7 -6
  107. package/dist/{e2b-66xra4az.js → e2b-8ygwhg0f.js} +4 -3
  108. package/dist/{e2b-xwhyj8cw.js → e2b-swhnpw3n.js} +4 -3
  109. package/dist/{event-bus-vjtv82gm.js → event-bus-6axnngdj.js} +3 -2
  110. package/dist/external-channel-h3359cwn.js +52 -0
  111. package/dist/{google-generative-ai-7pavrbv7.js → google-generative-ai-g8db7x0f.js} +5 -3
  112. package/dist/{google-vertex-3f1jk41f.js → google-vertex-r2692q0r.js} +5 -3
  113. package/dist/{grok-adapter-8m9yqzbq.js → grok-adapter-0d4x7gkx.js} +19 -18
  114. package/dist/{handlers-s20xr10r.js → handlers-an1gzv21.js} +44 -37
  115. package/dist/{heartbeat-3k31q4g2.js → heartbeat-6dakeb61.js} +46 -39
  116. package/dist/{hook-gdn6a2fn.js → hook-6v6cv6d5.js} +12 -24
  117. package/dist/{hook-ygwerrdh.js → hook-sa5xye4y.js} +13 -12
  118. package/dist/{http-jdteeg81.js → http-r7f3eky0.js} +1598 -279
  119. package/dist/index-52j1aja4.js +3465 -0
  120. package/dist/{index-e0snym4t.js → index-7ep4gzdk.js} +41 -34
  121. package/dist/index-9gw58n4k.js +193 -0
  122. package/dist/index-9j87mcm8.js +56305 -0
  123. package/dist/index-bsmcresk.js +79424 -0
  124. package/dist/index-mh85qhec.js +475 -0
  125. package/dist/{index-vm3bm9t7.js → index-rmpw9a8q.js} +41 -34
  126. package/dist/{index-6ksewd5y.js → index-s2wxgzr0.js} +1015 -732
  127. package/dist/{index-tsszt7g9.js → index-v1v6n7hg.js} +46 -39
  128. package/dist/index-we27kd31.js +12810 -0
  129. package/dist/{index-ffqzqgwb.js → index-zk96aq1k.js} +11 -9
  130. package/dist/{keepalive-jekc896t.js → keepalive-be9vwy5h.js} +13 -11
  131. package/dist/{lead-4k9s9szx.js → lead-0861cwrf.js} +64 -57
  132. package/dist/{maintenance-shazch4n.js → maintenance-mzzgc8bn.js} +11 -9
  133. package/dist/node-c9we95xf.js +1 -1
  134. package/dist/{node-fkajaq96.js → node-mn1zs7an.js} +2 -1
  135. package/dist/{oauth-refresh-sweep-5gb36kn8.js → oauth-refresh-sweep-cpdnfwdj.js} +12 -10
  136. package/dist/{onboard-ydrbnvww.js → onboard-fh8qs4r3.js} +11 -3
  137. package/dist/{opencode-adapter-5zh0he20.js → opencode-adapter-nm42wwj8.js} +19 -14
  138. package/dist/{otel-impl-6pf5cv86.js → otel-impl-x6c1c9g4.js} +248 -869
  139. package/dist/{pi-mono-adapter-ef9wwx9m.js → pi-mono-adapter-9n2ya240.js} +245 -107
  140. package/dist/{pricing-refresh-s52nsa5q.js → pricing-refresh-qb0rv0ap.js} +13 -11
  141. package/dist/{rbac-roles-aq3msh4e.js → rbac-roles-1gzdwbh1.js} +12 -10
  142. package/dist/{rbac-roles-q5gb5e8v.js → rbac-roles-3mraey52.js} +13 -11
  143. package/dist/{render-v2-pd1rg54q.js → render-v2-agpa45zz.js} +19 -13
  144. package/dist/{resume-gw4a2c6v.js → resume-x0ydy9s9.js} +27 -21
  145. package/dist/{runtime-qfry88v2.js → runtime-sa2vr16g.js} +2 -2
  146. package/dist/{setup-jda1ksdh.js → setup-as9ga0d6.js} +2 -2
  147. package/dist/{virtual-modules-b3ga2m65.js → virtual-modules-9gkxqrap.js} +7 -6
  148. package/dist/{work-objects-tb7ycye8.js → work-objects-y5g1y7ev.js} +49 -37
  149. package/dist/{worker-njjccz5d.js → worker-860c3e38.js} +64 -57
  150. package/dist/{x-qfmygqqv.js → x-m477fr0h.js} +3 -2
  151. package/openapi.json +951 -356
  152. package/package.json +12 -13
  153. package/src/apps/catalog.generated.json +1309 -53
  154. package/src/apps/page-validator.ts +93 -31
  155. package/src/azure-devops/api.ts +94 -18
  156. package/src/azure-devops/auth.ts +32 -0
  157. package/src/azure-devops/boards-sync.ts +708 -0
  158. package/src/azure-devops/handlers.ts +52 -8
  159. package/src/azure-devops/html.ts +26 -0
  160. package/src/azure-devops/index.ts +15 -0
  161. package/src/azure-devops/templates.ts +86 -0
  162. package/src/azure-devops/types.ts +65 -1
  163. package/src/be/config-refs.ts +60 -0
  164. package/src/be/db/page-versions.ts +15 -0
  165. package/src/be/db/runtime.ts +35 -8
  166. package/src/be/db/skill-name-resolution.ts +36 -0
  167. package/src/be/db/tasks/read.ts +230 -0
  168. package/src/be/db/tasks/write.ts +7 -0
  169. package/src/be/db.ts +74 -13
  170. package/src/be/migrations/213_script_connections_sql.sql +79 -0
  171. package/src/be/migrations/215_swarm_config_export_to_env.sql +34 -0
  172. package/src/be/migrations/216_task_resolved_effort.sql +5 -0
  173. package/src/be/migrations/217_chat_sdk_state.sql +104 -0
  174. package/src/be/model-catalog-store.ts +5 -1
  175. package/src/be/model-tier-resolution.ts +12 -1
  176. package/src/be/model-validation.ts +5 -2
  177. package/src/be/models-catalog.ts +26 -1
  178. package/src/be/modelsdev-cache.json +198 -63
  179. package/src/be/modelsdev-cache.ts +9 -1
  180. package/src/be/script-connections.ts +47 -8
  181. package/src/be/script-credential-broker.ts +39 -2
  182. package/src/be/scripts/run-saved.ts +1 -0
  183. package/src/be/scripts/typecheck.ts +10 -8
  184. package/src/be/seed/runner.ts +13 -1
  185. package/src/be/seed/state-db.ts +5 -0
  186. package/src/be/seed/types.ts +6 -0
  187. package/src/be/seed-pricing.ts +19 -2
  188. package/src/be/seed-scripts/catalog/ops-catalog-audit.inline.ts +1 -1
  189. package/src/be/seed-scripts/catalog/ops-catalog-audit.ts +1 -1
  190. package/src/be/seed-skills/bundled-files.generated.json +10 -18
  191. package/src/be/seed-skills/index.ts +107 -3
  192. package/src/be/sql-connections.ts +118 -0
  193. package/src/be/swarm-config-guard.ts +53 -0
  194. package/src/cli.tsx +0 -16
  195. package/src/commands/onboard/non-interactive.ts +4 -2
  196. package/src/commands/profile-sync.ts +5 -3
  197. package/src/commands/provider-credentials.ts +52 -6
  198. package/src/commands/runner.ts +196 -56
  199. package/src/e2b/env.ts +5 -0
  200. package/src/extensions/contract-types.generated.ts +1 -1
  201. package/src/extensions/contract.ts +45 -0
  202. package/src/extensions/dispatcher.ts +7 -0
  203. package/src/extensions/post-bridge.ts +40 -0
  204. package/src/github/handlers.ts +11 -4
  205. package/src/heartbeat/templates.ts +8 -5
  206. package/src/hooks/hook.ts +0 -26
  207. package/src/http/all-routes.ts +0 -1
  208. package/src/http/config.ts +36 -17
  209. package/src/http/core.ts +4 -0
  210. package/src/http/index.ts +14 -2
  211. package/src/http/integrations.ts +88 -1
  212. package/src/http/mcp-oauth.ts +6 -13
  213. package/src/http/mcp.ts +15 -5
  214. package/src/http/page-proxy.ts +2 -4
  215. package/src/http/pages-public.ts +6 -5
  216. package/src/http/pages.ts +5 -14
  217. package/src/http/poll.ts +28 -68
  218. package/src/http/requested-by.ts +54 -0
  219. package/src/http/route-def.ts +7 -6
  220. package/src/http/script-connection-proxy.ts +51 -4
  221. package/src/http/script-connections.ts +5 -2
  222. package/src/http/scripts.ts +1 -0
  223. package/src/http/session-cost-recompute.ts +12 -2
  224. package/src/http/session-data.ts +4 -0
  225. package/src/http/stats.ts +1 -30
  226. package/src/http/tasks.ts +61 -5
  227. package/src/http/utils.ts +14 -0
  228. package/src/http/webhooks.ts +183 -2
  229. package/src/http/x.ts +1 -0
  230. package/src/integrations/chat-sdk/adapters.ts +129 -0
  231. package/src/integrations/chat-sdk/attachments.ts +232 -0
  232. package/src/integrations/chat-sdk/background.ts +36 -0
  233. package/src/integrations/chat-sdk/commands.ts +194 -0
  234. package/src/integrations/chat-sdk/config.ts +198 -0
  235. package/src/integrations/chat-sdk/discord-api.ts +63 -0
  236. package/src/integrations/chat-sdk/discord-gateway.ts +185 -0
  237. package/src/integrations/chat-sdk/handlers.ts +28 -0
  238. package/src/integrations/chat-sdk/inbound.ts +426 -0
  239. package/src/integrations/chat-sdk/limits.ts +48 -0
  240. package/src/integrations/chat-sdk/logger.ts +81 -0
  241. package/src/integrations/chat-sdk/outbound.ts +123 -0
  242. package/src/integrations/chat-sdk/relay.ts +172 -0
  243. package/src/integrations/chat-sdk/router.ts +169 -0
  244. package/src/integrations/chat-sdk/runtime.ts +351 -0
  245. package/src/integrations/chat-sdk/schema.ts +245 -0
  246. package/src/integrations/chat-sdk/state.ts +243 -0
  247. package/src/integrations/chat-sdk/status.ts +244 -0
  248. package/src/integrations/chat-sdk/steering.ts +58 -0
  249. package/src/integrations/chat-sdk/teams-origin.ts +77 -0
  250. package/src/integrations/chat-sdk/telegram-api.ts +56 -0
  251. package/src/integrations/chat-sdk/templates.ts +16 -0
  252. package/src/integrations/chat-sdk/test-hooks.ts +37 -0
  253. package/src/integrations/chat-sdk/thread-buffer.ts +100 -0
  254. package/src/integrations/chat-sdk/typing.ts +65 -0
  255. package/src/jira/adf.ts +3 -1
  256. package/src/kv-reserved-namespaces.ts +2 -0
  257. package/src/oauth/mcp-wrapper.ts +10 -6
  258. package/src/otel-impl.ts +2 -1
  259. package/src/pages/version.ts +4 -5
  260. package/src/prompts/base-prompt.ts +111 -45
  261. package/src/providers/acp-target-catalog.ts +6 -0
  262. package/src/providers/acp-targets.ts +56 -3
  263. package/src/providers/amp-adapter.ts +1 -1
  264. package/src/providers/claude-adapter.ts +27 -2
  265. package/src/providers/claude-flag-settings.ts +133 -0
  266. package/src/providers/claude-managed-adapter.ts +1 -1
  267. package/src/providers/claude-sdk-session.ts +7 -0
  268. package/src/providers/claude-session-events.ts +6 -1
  269. package/src/providers/dsh-adapter.ts +1 -1
  270. package/src/providers/opencode-adapter.ts +268 -37
  271. package/src/providers/opencode-billing.ts +86 -0
  272. package/src/providers/opencode-swarm-plugin.ts +51 -0
  273. package/src/providers/pi-mono-adapter.ts +398 -153
  274. package/src/providers/pricing-sources.md +37 -3
  275. package/src/providers/reasoning-effort.ts +27 -0
  276. package/src/providers/types.ts +11 -2
  277. package/src/rbac/legacy-policy.ts +8 -2
  278. package/src/rbac/permissions.ts +21 -0
  279. package/src/scheduler/scheduler.ts +4 -2
  280. package/src/scripts-runtime/api-client.ts +15 -1
  281. package/src/scripts-runtime/api-types.ts +9 -1
  282. package/src/scripts-runtime/ctx.ts +1 -1
  283. package/src/scripts-runtime/loader.ts +19 -7
  284. package/src/scripts-runtime/mcp-client.ts +30 -19
  285. package/src/scripts-runtime/sdk-allowlist.ts +8 -6
  286. package/src/scripts-runtime/swarm-sdk.ts +0 -11
  287. package/src/scripts-runtime/types/stdlib.d.ts +27 -15
  288. package/src/scripts-runtime/types/swarm-extension.d.ts +63 -1
  289. package/src/scripts-runtime/types/swarm-sdk.d.ts +27 -15
  290. package/src/server.ts +52 -18
  291. package/src/slack/actions.ts +29 -1
  292. package/src/slack/app.ts +14 -2
  293. package/src/slack/assistant.ts +36 -2
  294. package/src/slack/channel-join.ts +3 -16
  295. package/src/slack/commands.ts +9 -1
  296. package/src/slack/event-dedup.ts +1 -1
  297. package/src/slack/external-channel-egress.ts +113 -0
  298. package/src/slack/external-channel.ts +309 -0
  299. package/src/slack/handlers.ts +61 -0
  300. package/src/slack/render-v2.ts +40 -47
  301. package/src/slack/work-objects.ts +5 -0
  302. package/src/tasks/additive-buffer.ts +4 -2
  303. package/src/tasks/context-key.ts +94 -2
  304. package/src/tasks/model-pin.ts +67 -0
  305. package/src/tasks/worker-follow-up.ts +4 -31
  306. package/src/telemetry.ts +2 -1
  307. package/src/tests/acp-adapter.test.ts +42 -0
  308. package/src/tests/api-drain-worker-e2e.test.ts +1 -6
  309. package/src/tests/apps-catalog-feedback.test.ts +310 -0
  310. package/src/tests/apps-catalog-inputs.test.ts +70 -0
  311. package/src/tests/apps-catalog-table.test.ts +113 -0
  312. package/src/tests/azure-devops-boards-sync.test.ts +584 -0
  313. package/src/tests/azure-devops-html.test.ts +13 -0
  314. package/src/tests/azure-devops-webhook-malformed.test.ts +4 -0
  315. package/src/tests/base-prompt.test.ts +58 -54
  316. package/src/tests/chat-sdk-commands.test.ts +191 -0
  317. package/src/tests/chat-sdk-config.test.ts +334 -0
  318. package/src/tests/chat-sdk-discord-api.test.ts +68 -0
  319. package/src/tests/chat-sdk-discord-gateway.test.ts +252 -0
  320. package/src/tests/chat-sdk-discord-webhook.test.ts +126 -0
  321. package/src/tests/chat-sdk-fields-sync.test.ts +50 -0
  322. package/src/tests/chat-sdk-harness.ts +146 -0
  323. package/src/tests/chat-sdk-inbound.test.ts +363 -0
  324. package/src/tests/chat-sdk-relay.test.ts +470 -0
  325. package/src/tests/chat-sdk-reload.test.ts +128 -0
  326. package/src/tests/chat-sdk-shutdown.test.ts +220 -0
  327. package/src/tests/chat-sdk-slack.test.ts +268 -0
  328. package/src/tests/chat-sdk-state.test.ts +163 -0
  329. package/src/tests/chat-sdk-status-route.test.ts +382 -0
  330. package/src/tests/chat-sdk-teams.test.ts +277 -0
  331. package/src/tests/chat-sdk-tools.test.ts +520 -0
  332. package/src/tests/chat-sdk-typing.test.ts +120 -0
  333. package/src/tests/chat-sdk-webhook-telegram.test.ts +200 -0
  334. package/src/tests/claude-adapter-binary.test.ts +144 -0
  335. package/src/tests/claude-flag-settings.test.ts +114 -0
  336. package/src/tests/claude-managed-adapter.test.ts +1 -1
  337. package/src/tests/claude-sdk-lifecycle.test.ts +148 -1
  338. package/src/tests/claude-sdk-process.fixture.ts +7 -0
  339. package/src/tests/claude-thinking-tokens.test.ts +57 -0
  340. package/src/tests/claude-worker-parity.test.ts +2 -7
  341. package/src/tests/config-env-export-chat-sdk.test.ts +11 -0
  342. package/src/tests/config-refs.test.ts +66 -0
  343. package/src/tests/context-key.test.ts +51 -0
  344. package/src/tests/cors-allowlist.test.ts +25 -0
  345. package/src/tests/create-page-tool.test.ts +18 -0
  346. package/src/tests/credential-check.test.ts +139 -0
  347. package/src/tests/credentials.test.ts +21 -0
  348. package/src/tests/db-runtime.test.ts +61 -0
  349. package/src/tests/db-task-read-contract.test.ts +20 -0
  350. package/src/tests/db-task-write-contract.test.ts +10 -0
  351. package/src/tests/deferred-task-wake.test.ts +52 -4
  352. package/src/tests/docs-index-footprint.test.js +3 -3
  353. package/src/tests/dsh-adapter.test.ts +1 -1
  354. package/src/tests/enabled-capabilities.test.ts +20 -0
  355. package/src/tests/entrypoint-api-readiness.test.ts +14 -8
  356. package/src/tests/entrypoint-config-env-export.test.ts +5 -2
  357. package/src/tests/entrypoint-export-to-env.test.ts +78 -0
  358. package/src/tests/explicit-model-validation.test.ts +105 -1
  359. package/src/tests/extensions-example-route-chat-channel.test.ts +154 -0
  360. package/src/tests/extensions-post-bridge.test.ts +51 -0
  361. package/src/tests/fetch-resolved-env.test.ts +128 -0
  362. package/src/tests/fixtures/azure-boards/workitem.commented.json +89 -0
  363. package/src/tests/fixtures/azure-boards/workitem.created.json +81 -0
  364. package/src/tests/fixtures/azure-boards/workitem.deleted.json +70 -0
  365. package/src/tests/fixtures/azure-boards/workitem.restored.json +82 -0
  366. package/src/tests/fixtures/azure-boards/workitem.updated.json +130 -0
  367. package/src/tests/fixtures/catalog-feedback-definition.json.txt +860 -0
  368. package/src/tests/fixtures/catalog-inputs-definition.json.txt +910 -0
  369. package/src/tests/fixtures/catalog-table-definition.json.txt +468 -0
  370. package/src/tests/fixtures/chat-sdk/telegram-private-text.json +10 -0
  371. package/src/tests/fixtures/extensions/route-chat-channel-to-agent/hooks.ts +27 -0
  372. package/src/tests/fixtures/extensions/route-chat-channel-to-agent/manifest.json +9 -0
  373. package/src/tests/fixtures/openai-compatible-registry-extension.child.ts +146 -0
  374. package/src/tests/fixtures/runner-identity-refresh.ts +3 -0
  375. package/src/tests/fixtures/runner-legacy-resume-prompt.ts +122 -0
  376. package/src/tests/github-handlers.test.ts +35 -0
  377. package/src/tests/harness-provider-resolution.test.ts +12 -0
  378. package/src/tests/heartbeat-checklist.test.ts +3 -2
  379. package/src/tests/hook-runtime-close.test.ts +0 -2
  380. package/src/tests/http-api-integration.test.ts +0 -25
  381. package/src/tests/migration-046-budgets.test.ts +17 -16
  382. package/src/tests/migration-217-chat-sdk-state.test.ts +113 -0
  383. package/src/tests/model-tier-resolution.test.ts +93 -0
  384. package/src/tests/models-catalog.test.ts +42 -1
  385. package/src/tests/node-env-runtime.test.ts +98 -0
  386. package/src/tests/openai-compatible-providers.test.ts +134 -0
  387. package/src/tests/openai-compatible-registry-extension.test.ts +42 -0
  388. package/src/tests/opencode-adapter.test.ts +348 -3
  389. package/src/tests/opencode-billing.test.ts +28 -0
  390. package/src/tests/opencode-helmcode-sdk.test.ts +201 -0
  391. package/src/tests/opencode-shell-env.test.ts +145 -0
  392. package/src/tests/openrouter-base-url.test.ts +101 -151
  393. package/src/tests/page-proxy.test.ts +1 -1
  394. package/src/tests/pages-browser-sdk.test.ts +76 -0
  395. package/src/tests/pages-feedback.test.ts +1 -1
  396. package/src/tests/paused-tasks-requested-by.test.ts +102 -0
  397. package/src/tests/pi-custom-provider-fake-server.test.ts +442 -0
  398. package/src/tests/pi-mono-adapter.test.ts +385 -5
  399. package/src/tests/pi-shell-env.test.ts +170 -0
  400. package/src/tests/prompt-template-remaining.test.ts +35 -0
  401. package/src/tests/rbac-engine.test.ts +11 -3
  402. package/src/tests/runner-exit-after-tasks.test.ts +0 -4
  403. package/src/tests/runner-identity-refresh.test.ts +3 -0
  404. package/src/tests/runner-legacy-resume-prompt.test.ts +64 -0
  405. package/src/tests/script-connections-sql.test.ts +675 -0
  406. package/src/tests/script-connections.test.ts +7 -1
  407. package/src/tests/scripts-http.test.ts +201 -1
  408. package/src/tests/scripts-only-gating.test.ts +2 -3
  409. package/src/tests/scripts-runtime.test.ts +58 -40
  410. package/src/tests/secret-scrubber.test.ts +86 -0
  411. package/src/tests/seed-skills-bundled-files.test.ts +45 -53
  412. package/src/tests/seed-skills-retire.test.ts +182 -0
  413. package/src/tests/send-task-requester-gate.test.ts +58 -0
  414. package/src/tests/session-cost-recompute-unit.test.ts +94 -0
  415. package/src/tests/skill-get-name-resolution.test.ts +137 -0
  416. package/src/tests/slack-channel-join.test.ts +4 -1
  417. package/src/tests/slack-external-channel-config.test.ts +65 -0
  418. package/src/tests/slack-external-channel-egress.test.ts +237 -0
  419. package/src/tests/slack-external-channel-guard.test.ts +280 -0
  420. package/src/tests/slack-external-channel-tools.test.ts +173 -0
  421. package/src/tests/slack-external-channel.test.ts +370 -0
  422. package/src/tests/slack-render-v2.test.ts +86 -10
  423. package/src/tests/sonnet-5-5-registry.test.ts +9 -1
  424. package/src/tests/spec-base-url.test.ts +16 -1
  425. package/src/tests/swarm-config-export-to-env.test.ts +267 -0
  426. package/src/tests/swarm-config-reserved-keys.test.ts +16 -0
  427. package/src/tests/swarm-diff.test.ts +1 -1
  428. package/src/tests/task-model-effort-rerun.test.ts +164 -0
  429. package/src/tests/task-session-prompts.test.ts +132 -0
  430. package/src/tests/task-tool-manifest.test.ts +14 -0
  431. package/src/tests/task-tools-ctx.test.ts +2 -0
  432. package/src/tests/tool-annotations.test.ts +6 -6
  433. package/src/tests/ui-logs-parser.test.ts +43 -0
  434. package/src/tests/used-by-route.test.ts +6 -0
  435. package/src/tests/vendored-openapi.test.ts +16 -1
  436. package/src/tests/vercel-ai-gateway.test.ts +149 -0
  437. package/src/tests/workflow-swarm-script.test.ts +38 -0
  438. package/src/tests/workflow-wait-filter.test.ts +5 -3
  439. package/src/tools/chat-access.ts +41 -0
  440. package/src/tools/chat-open-dm.ts +59 -0
  441. package/src/tools/chat-post.ts +74 -0
  442. package/src/tools/chat-read-thread.ts +100 -0
  443. package/src/tools/chat-reply.ts +75 -0
  444. package/src/tools/create-page.ts +3 -13
  445. package/src/tools/defer-task.ts +17 -0
  446. package/src/tools/model-catalog/index.ts +2 -1
  447. package/src/tools/register-chat-channel.ts +139 -0
  448. package/src/tools/send-task.ts +24 -0
  449. package/src/tools/skills/skill-get.ts +17 -9
  450. package/src/tools/slack-post.ts +6 -0
  451. package/src/tools/slack-read.ts +8 -0
  452. package/src/tools/slack-reply.ts +6 -0
  453. package/src/tools/slack-start-thread.ts +6 -0
  454. package/src/tools/slack-update.ts +6 -0
  455. package/src/tools/slack-upload-file.ts +6 -0
  456. package/src/tools/swarm-config/get-config.ts +2 -1
  457. package/src/tools/swarm-config/list-config.ts +2 -1
  458. package/src/tools/swarm-config/set-config.ts +16 -3
  459. package/src/tools/tool-config.ts +8 -6
  460. package/src/types.ts +12 -2
  461. package/src/utils/config-env-export.ts +74 -0
  462. package/src/utils/credentials.test.ts +92 -0
  463. package/src/utils/credentials.ts +52 -4
  464. package/src/utils/harness-provider.ts +15 -6
  465. package/src/utils/node-env.ts +24 -0
  466. package/src/utils/runtime-model-catalog.ts +11 -1
  467. package/src/utils/secret-scrubber.ts +71 -2
  468. package/src/utils/task-tool-manifest.ts +19 -2
  469. package/src/workflows/executors/swarm-script.ts +1 -0
  470. package/templates/skills/agent-browser/SKILL.md +1 -1
  471. package/templates/skills/agent-browser/content.md +1 -1
  472. package/templates/skills/apps/SKILL.md +749 -0
  473. package/templates/skills/apps/content.md +60 -22
  474. package/templates/skills/chat-sdk/config.json +14 -0
  475. package/templates/skills/chat-sdk/content.md +84 -0
  476. package/templates/skills/composio-gmail/content.md +7 -0
  477. package/templates/skills/composio-google-docs/content.md +1 -1
  478. package/templates/skills/email-domain-health/config.json +14 -0
  479. package/templates/skills/email-domain-health/content.md +131 -0
  480. package/templates/skills/kv-storage/content.md +2 -2
  481. package/templates/skills/pages/content.md +8 -10
  482. package/templates/skills/qa-loom/config.json +14 -0
  483. package/templates/skills/qa-loom/content.md +187 -0
  484. package/templates/skills/qa-loom/files/build-loom.mjs +221 -0
  485. package/templates/skills/qa-loom/files/run-qa.sh +57 -0
  486. package/templates/skills/slack-interaction/content.md +4 -0
  487. package/templates/skills/swarm-extensions/content.md +2 -0
  488. package/templates/skills/swarm-scripts/SKILL.md +4 -2
  489. package/templates/skills/swarm-scripts/config.json +1 -1
  490. package/templates/skills/swarm-scripts/content.md +4 -2
  491. package/templates/skills/workflow-iterate/content.md +38 -0
  492. package/dist/artifact-eczsknc3.js +0 -3843
  493. package/dist/cli-9xsmdpdt.js +0 -56377
  494. package/dist/cli-bd957dgb.js +0 -153
  495. package/dist/cli-q51yprfq.js +0 -1104
  496. package/dist/cli-t1ze64h3.js +0 -811
  497. package/dist/index-9ch1n761.js +0 -1102
  498. package/src/artifact-sdk/index.ts +0 -2
  499. package/src/artifact-sdk/localtunnel.d.ts +0 -20
  500. package/src/artifact-sdk/port.ts +0 -12
  501. package/src/artifact-sdk/server.ts +0 -183
  502. package/src/artifact-sdk/tunnel.ts +0 -19
  503. package/src/commands/artifact.ts +0 -249
  504. package/src/http/ecosystem.ts +0 -77
  505. package/src/tests/artifact-commands.test.ts +0 -92
  506. package/src/tests/artifact-sdk.test.ts +0 -793
  507. package/src/tests/register-service-security.test.ts +0 -194
  508. package/src/tools/list-services.ts +0 -107
  509. package/src/tools/register-service.ts +0 -177
  510. package/src/tools/unregister-service.ts +0 -73
  511. package/src/tools/update-service-status.ts +0 -96
  512. package/templates/skills/artifacts/config.json +0 -14
  513. package/templates/skills/artifacts/content.md +0 -344
  514. package/templates/skills/artifacts/files/examples/approval-flow.ts +0 -34
  515. package/templates/skills/artifacts/files/examples/hono-dashboard.ts +0 -31
  516. package/templates/skills/artifacts/files/examples/multi-artifact.ts +0 -20
  517. package/templates/skills/artifacts/files/examples/static-report.sh +0 -17
  518. package/dist/{azure-openai-responses-ap1gwxgr.js → azure-openai-responses-ygfreq4t.js} +5 -5
  519. package/dist/{cli-5rqrj7q0.js → cli-pw7ebjdt.js} +0 -0
  520. package/dist/{openai-codex-responses-33qhkkdy.js → openai-codex-responses-3bk0grnr.js} +4 -4
  521. package/dist/{openai-completions-s88xm3ns.js → openai-completions-5d4jpakc.js} +9 -9
  522. package/dist/{openai-responses-7xd2f2e2.js → openai-responses-krb51f59.js} +5 -5
  523. package/dist/{pi-messages-xszk613n.js → pi-messages-x9gzynm1.js} +3 -3
  524. /package/src/{artifact-sdk → pages}/browser-sdk.ts +0 -0
  525. /package/src/{artifact-sdk → pages}/feedback-overlay.ts +0 -0
@@ -1,6 +1,6 @@
1
1
  # Swarm Apps
2
2
 
3
- Swarm Apps are persistent, agent-authored internal applications: a schema-backed row store, named queries, custom script/task actions, and a validated json-render interface served at `/apps/<id>`. Use an app when people need to view and change live records; use `create_page` for a static snapshot and `artifacts` for custom server logic or a UI outside this catalog.
3
+ Swarm Apps are persistent, agent-authored internal applications: a schema-backed row store, named queries, custom script/task actions, and a validated json-render interface served at `/apps/<id>`. Use an app when people need to view and change live records; use `create_page` for a static snapshot.
4
4
 
5
5
  > **Capability gate**: app tools are available when the server's `CAPABILITIES` includes `pages` (for example `CAPABILITIES=core,task-pool,pages`). There is no separate `apps` capability. If these tools are absent from your MCP list, this is why.
6
6
 
@@ -425,50 +425,71 @@ Drawer variant: declare a route param on the containing page, set the Drawer `pr
425
425
 
426
426
  Param `kind` is `string` by default, or `number` / `boolean`; `required` defaults to false. The names `mode`, `apiUrl`, `apiKey`, `email`, and `name` are reserved. `/apps/<id>` renders `defaultPage`; `/apps/<id>/p/<page>?issueId=42` is the shareable deep link for another page. Browser navigation is client-side, so app state and polled data stay warm across page changes.
427
427
 
428
- Within each page, `root` names one entry in its non-empty `elements` map. Elements are a flat, single-parent tree: ids are map keys, `children` contains ids (not nested elements), all elements must be reachable from `root`, and cycles, missing children, or shared children are invalid. Element and Form/UI ids are page-local. Element keys are `type`, `props`, `children`, `on`, `visible`, `repeat`, and `watch`; only components with a `default` child slot accept `children` (Stack, Grid, Split, Tabs, Container, Card, and Drawer). `watch` maps state paths to an action step or chain.
428
+ Within each page, `root` names one entry in its non-empty `elements` map. Elements are a flat, single-parent tree: ids are map keys, `children` contains ids (not nested elements), all elements must be reachable from `root`, and cycles, missing children, or shared children are invalid. Element and Form/UI ids are page-local. Element keys are `type`, `props`, `children`, `on`, `visible`, `repeat`, and `watch`; only components with a `default` child slot accept `children` (Stack, Grid, Split, Tabs, Container, Card, Section, and Drawer). `watch` maps state paths to an action step or chain.
429
429
 
430
- ## Component catalog (all 23)
430
+ ## Component catalog (all 33)
431
431
 
432
432
  Props reject unknown keys. A `{"$state":"..."}` binding may replace a literal prop value at any depth.
433
433
 
434
+ Icons: every `icon` prop (and the `Icon` block) takes one name from a curated lucide set: `plus`, `refresh-cw`, `search`, `filter`, `download`, `upload`, `external-link`, `copy`, `check`, `x`, `trash-2`, `pencil`, `eye`, `calendar`, `clock`, `user`, `users`, `bot`, `git-pull-request`, `git-branch`, `github`, `slack`, `mail`, `link`, `file-text`, `folder`, `database`, `zap`, `sparkles`, `flag`, `star`, `tag`, `alert-triangle`, `info`, `circle-check`, `trending-up`, `trending-down`, `dollar-sign`, `activity`, `rocket`. Any other name is rejected.
435
+
436
+ Tones: Badge, Metric and Progress `tone` take `neutral`, `success`, `active`, `error`, `info`, `pending`, `warning`, or `paused`.
437
+
434
438
  | Component | Required props | Optional props / values |
435
439
  |---|---|---|
436
- | `Stack` | none | Primary layout; `direction: "column", "row"`, `gap`, `padding: "none", "xs", "sm", "md", "lg", "xl"`; `align: "start", "center", "end", "stretch"`, `justify: "start", "center", "end", "between"`, `wrap`; has the `default` child slot. |
440
+ | `Stack` | none | Primary layout; `direction: "column", "row"`, `gap`, `padding: "none", "xs", "sm", "md", "lg", "xl"`; `align: "start", "center", "end", "stretch"`, `justify: "start", "center", "end", "between"`, `wrap`; `motion: "rise"` (default), `"fade"`, or `"none"` (see Motion below); has the `default` child slot. |
437
441
  | `Grid` | none | Responsive `columns`: integer 1 through 6, or `{ base, sm, md, lg }` counts 1 through 6; `gap` uses the Stack spacing values; has the `default` child slot. |
438
442
  | `Split` | none | `ratio: "1-1", "1-2", "2-1", "1-3", "3-1"`, `gap`, `collapseBelow: "sm", "md", "lg"`, `reverse`; has the positional `default` child slot. |
439
443
  | `Divider` | none | `label`; no children. |
440
- | `Tabs` | `id`, `tabs` | `tabs` entries are `{ key, label? }`; `defaultTab`; has the positional `default` child slot. |
444
+ | `Tabs` | `id`, `tabs` | `tabs` entries are `{ key, label?, icon?, count? }` (`count` is a number shown as a small pill); `defaultTab`; `variant: "line"` (default) or `"segmented"` (a compact segmented control, same state and warm panels); has the positional `default` child slot. |
441
445
  | `Container` | none | Legacy layout primitive; prefer Stack. `direction: "row", "column"`, `gap: "none", "sm", "md", "lg"`; has the `default` child slot. |
442
446
  | `Card` | none | `title`, `description`; has the `default` child slot. |
443
447
  | `Heading` | `text` | `level: "h1", "h2", or "h3"`. |
444
448
  | `Text` | `content` | `tone: "default" or "muted"`. |
445
449
  | `Markdown` | `content` | Rendered Markdown for help, instructions, and rich prose; no children. |
446
- | `SearchInput` | `id` | `placeholder`, `label`; writes debounced text to `/ui/<id>/value`; no children. |
447
- | `Select` | `id`, `options` | `options` are strings or `{ value, label? }`; `placeholder`, `label`, `clearable`; writes a string or null to `/ui/<id>/value`; no children. |
448
- | `Button` | `label` | `variant: "default", "secondary", "outline", "ghost", "destructive", or "destructive-outline"` (use `destructive-outline` for standalone deletes); `disabled` (constant or bound boolean); `busyWith: "<actionName>"` disables and spins while that custom action's `/actions/<name>/status` is `"running"`; dispatch with element-level `on.press`. |
449
- | `Metric` | `label`, `value` | `value` is string or number; bind `loading` to `/queries/<name>/loading` for a skeleton while the query loads. |
450
- | `Alert` | `message` | `title`, `tone: "info", "success", "warning", or "error"`. |
451
- | `Badge` | `text` | `text` is string or number; `tone: "neutral", "success", "active", "error", "info", "pending", "warning", or "paused"`. |
452
- | `Table` | `columns` | `data`, `loading`, `error`, `emptyMessage`, `rowActions`, `search`, `filters`; see below. |
450
+ | `SearchInput` | `id` | `placeholder`, `label`, `shortcut` (one key, usually `"/"`: shows a keycap and focuses the field); writes debounced text to `/ui/<id>/value`; no children. |
451
+ | `Select` | `id`, `options` | `options` are strings or `{ value, label?, icon?, hint? }`; `placeholder`, `label`, `clearable`, `searchable` (type to filter; on by default past 8 options), `searchPlaceholder`, `multiple`; writes a string or null to `/ui/<id>/value`, or with `multiple` a string array (empty = nothing picked); no children. |
452
+ | `Chips` | `id`, `options` | Toggle chips for quick filters. `options` are strings or `{ value, label?, icon? }`; `multiple`; `label` (group name for screen readers). Writes a string or null to `/ui/<id>/value`, or a string array with `multiple`; pressing the selected chip clears it. |
453
+ | `Button` | `label` | `variant: "default", "secondary", "outline", "ghost", "destructive", or "destructive-outline"` (use `destructive-outline` for standalone deletes); `disabled` (constant or bound boolean); `busyWith: "<actionName>"` swaps the label for a spinner while that custom action's `/actions/<name>/status` is `"running"` (presses are ignored), then shows a check and `successLabel` for about 1.5s when it turns `"ok"`; `icon`, `iconPosition: "start" or "end"`; `size: "sm", "default", or "lg"`; `shortcut` (one key, shown as a keycap, presses the button); `hint` (tooltip); dispatch with element-level `on.press`. |
454
+ | `Metric` | `label`, `value` | The dashboard stat tile. `value` is string or number (empty renders an em dash); bind `loading` to `/queries/<name>/loading` for a skeleton while the query loads; `icon` with `tone` tinting its tile; `detail` (a line under the value); `trend: { direction: "up", "down", or "flat", label }`; `info` (explanation tooltip next to the label). |
455
+ | `Alert` | `message` | `title`, `tone: "info", "success", "warning", or "error"`, `icon` (overrides the tone icon), `actionLabel` (a button inside the callout that emits `press`; wire it with `on.press`). |
456
+ | `Badge` | `text` | `text` is string or number; `tone`; `icon` replaces the dot; `variant: "tag"` (default uppercase chip), `"status"` (sentence-case pill), `"count"` (numeric pill), or `"beta"` (the BETA tag); `status` (a lifecycle value renders the dashboard status chip); `hint` (tooltip; the beta tag's explanation). |
457
+ | `Icon` | `name` | `size: "sm", "md", or "lg"`, `tone: "default", "muted", "success", "warning", "error", or "info"`, `label` (accessible name; decorative without it). |
458
+ | `Progress` | `value` | `max` (default 100), `tone`, `label`, `variant: "bar"` (default) or `"ring"` (the dashboard progress ring; ignores `tone`), `showValue` (default true: percent for a bar, `value / max` for a ring). |
459
+ | `EmptyState` | `title` | `icon` (default inbox), `body`, `actionLabel` (a button that emits `press`; wire it with `on.press`). |
460
+ | `CopyButton` | `value` | `value` is string or number; `label` is the visible text, icon-only without it. Renders nothing for an empty value. |
461
+ | `Kbd` | `keys` | Keycaps, one per string (`["⌘", "K"]`). Display only. |
462
+ | `Skeleton` | `shape` | `shape: "line", "block", "rows", "metric", or "card"`; `count` (1-20: lines, rows, tiles, or cards); `height` (px, `block` only, default 128). Show it with `visible` bound to a query's `loading`. |
463
+ | `Spinner` | none | `variant: "ring", "ellipsis", or "bars"` (default: the dashboard spinner), `size: "sm" or "md"`, `label` (visible text and accessible name). |
464
+ | `AvatarStack` | `users` | `users` are display names (initials avatars); `max` (default 4) before a `+N` count. Hover lists every name. |
465
+ | `Section` | `title` | `description`, `collapsible`, `defaultOpen` (default true); has the `default` child slot. |
466
+ | `Table` | `columns` | `data`, `loading`, `error`, `emptyMessage`, `emptyState`, `rowActions`, `onRowClick`, `selectedId`, `rowId`, `mobile`, `mobileFields`, `search`, `filters`; see below. |
453
467
  | `Form` | `id`, `fields`, `onSubmit` | `title`, `submitLabel`; see below. |
454
- | `Drawer` | `param` | `title`, `description`, `side: "right" or "left"`, `size: "sm", "md", "lg", or "xl"`; has the `default` child slot. |
455
- | `DetailList` | `fields` | `data`, `emptyMessage`, `columns: 1 or 2`, `loading` (bind `/queries/<name>/loading` for skeleton fields while loading); renders one record without edit controls. |
468
+ | `Drawer` | `param` | `title`, `description`, `side: "right" or "left"`, `size: "sm", "md" (default), "lg", or "xl"` (about 480/660/900/1010px at 1440px; grows with the viewport up to 640/960/1280/1600px; full width on phones), `actions` (sticky footer buttons; see below); has the `default` child slot. |
469
+ | `DetailList` | `fields` | `data`, `emptyMessage`, `columns: 1 or 2`, `loading` (bind `/queries/<name>/loading` for skeleton fields while loading); renders one record without edit controls; see below for field kinds. |
456
470
  | `Kanban` | `columnField`, `columns`, `titleField` | `data`, `loading`, `cardFields`, `idField` (default `id`), `onMove`, `onCardClick`; see below. |
457
471
  | `Calendar` | `startField`, `titleField` | `data`, `loading`, `endField`, `idField`, `onSelect`; read-only month grid; see below. |
458
472
  | `ContributionGraph` | none | `data`, `loading`, `dateField` (default `date`), `countField`, `days` (7-730, default 365); read-only activity heatmap. |
459
473
 
460
474
  Table details:
461
475
 
462
- - `columns[]`: `{ key, label?, kind?, tones?: {value: badgeTone}, width?: number, pinned?: "left" or "right" }`. `kind` is `text`, `string`, `enum`, `number`, `boolean`, `date`, or `badge`; `string` and `enum` render as text. Pin id/actions columns so they survive horizontal scroll on narrow viewports.
476
+ - `columns[]`: `{ key, label?, kind?, tones?: {value: badgeTone}, width?: number, pinned?: "left" or "right", align?: "start" or "end", truncate?: "end" or "middle", icon?, tooltip?: "<row field>", max?: number, onChange?: chain }`. `kind` is `text`, `string`, `enum`, `number`, `boolean`, `date`, `badge`, `status`, `user`, `agent`, `relative`, `duration`, `progress`, `copy`, `switch`, or `link`; `string` and `enum` render as text. Pin id/actions columns so they survive horizontal scroll on narrow viewports.
477
+ - `status`: lifecycle values (`pending`, `in_progress`, `running`, `completed`, `failed`, `cancelled`, `paused`, `reviewing`, ...) render the dashboard status chip with its icon; other values use `tones` like `badge`.
478
+ - `user` / `agent`: avatar plus name. `relative` (and `date`): smart time, exact time on hover. `duration`: milliseconds as `1m 23s`. `progress`: a bar against `max` (default 100). `copy`: the value plus a copy button. `link`: an http(s) URL opening in a new tab (other values render as text).
479
+ - `switch`: a toggle. `onChange` runs with `$row` = the row with this column already set to the new value, e.g. `[{ "action": "app.mutate", "params": { "model": "m", "op": "update", "rowId": { "$row": "id" }, "values": { "enabled": { "$row": "enabled" } } } }]`. Without `onChange` the switch is read-only.
480
+ - Numbers, durations and progress right-align by default. `truncate: "middle"` keeps both ends of ids and paths. `icon` shows in the header; `tooltip` names a row field shown on hover.
481
+ - `onRowClick`: action chain with `$row` / `$rowIndex`, run when a row is clicked (or Enter on a focused cell). Pair it with `app.navigate` that sets a Drawer param, and bind `selectedId` to `{ "$state": "/route/params/<param>" }` so the open row stays highlighted. `rowId` names the id field (default `id`).
482
+ - Phones (below 768px): the table renders as a card list. `mobileFields: { title, subtitle?, meta?: string or string[], status? }` picks the columns; by default the first text column is the title and the first `status`/`badge` column the chip. `mobile: "scroll"` keeps the grid; it is the default when the table has `rowActions` but no `onRowClick`.
483
+ - `emptyState: { icon?, title, body? }` replaces the plain empty message. A search or filter that hides every row shows its own "No rows match" state.
463
484
  - `pagination`: boolean; defaults to auto-enabling past 200 rows (set `false` to force one scroll region). `density: "compact"` tightens row height for dense readouts.
464
- - `rowActions[]`: `{ label, variant?, confirm?, actions }`. Variants add `destructive-outline` to the Button variants. `destructive` and `destructive-outline` confirm by default; customize with `{ "title": ..., "description": ..., "confirmLabel": ... }`, or use a bare `confirm` string as the dialog description (use `confirm: false` only for a reversible action).
485
+ - `rowActions[]`: `{ label, variant?, icon?, placement?: "inline" or "menu", confirm?, actions }`. `placement: "menu"` moves the action into the row's More menu. Variants add `destructive-outline` to the Button variants. `destructive` and `destructive-outline` confirm by default; customize with `{ "title": ..., "description": ..., "confirmLabel": ... }`, or use a bare `confirm` string as the dialog description (use `confirm: false` only for a reversible action).
465
486
  - `search`: optional string; case-insensitive substring matching across the listed string and number columns. Bind a SearchInput value for client-side search.
466
- - `filters`: optional record of per-column string, number, boolean, or null values. Null, empty, or absent values disable one filter. Bind Select values for client-side equality filters.
487
+ - `filters`: optional record of per-column string, number, boolean, null, or array values. An array means "the cell is one of these" (what a `multiple` Chips or Select writes). Null, empty strings, empty arrays, or absent values disable one filter. Bind Select or Chips values for client-side filters.
467
488
 
468
489
  Kanban details:
469
490
 
470
491
  - One card per `data` row, grouped by the row's `columnField` value. `columns[]` are plain strings or `{ id, label? }`, in display order; rows matching no column are hidden.
471
- - `cardFields[]` uses the DetailList field shape (`{ key, label?, kind?, tones? }`) for extra lines under the title.
492
+ - `cardFields[]` uses the DetailList field shape (`{ key, label?, kind?, tones? }`, same kinds) for extra lines under the title.
472
493
  - `onMove` is an action chain run when a card is dropped in another column. `$row` is the moved row with `columnField` already set to the new column: `[{ "action": "app.mutate", "params": { "model": "deal", "op": "update", "rowId": { "$row": "id" }, "values": { "stage": { "$row": "stage" } } } }]`. Without `onMove`, cards cannot be dragged.
473
494
  - `onCardClick` is an action chain with `$row`; pair it with `app.navigate` and a `Drawer` to open a card.
474
495
 
@@ -490,10 +511,19 @@ Form details:
490
511
  Drawer details:
491
512
 
492
513
  - `param` must name a param declared on the containing page. The drawer is open exactly when `/route/params/<param>` has a non-empty value, so its open state survives refresh and is shareable. Its children mount only while open; closing it clears that param with history replacement.
514
+ - `actions[]`: `{ label, variant?, icon?, confirm?, actions }`, rendered as buttons in a sticky footer under the scrolling body. Confirmation follows Table `rowActions`: destructive variants confirm by default, and a chain with an `app.mutate` delete always confirms. Chains have no `$row`; address the open record with `{ "$state": "/route/params/<param>" }`, for example `{ "action": "app.mutate", "params": { "model": "order", "op": "delete", "rowId": { "$state": "/route/params/panel" } } }`. Prev/next record navigation is not available.
493
515
 
494
516
  DetailList details:
495
517
 
496
- - `fields[]`: `{ key, label?, kind?, tones? }`. Kinds reuse Table formatting (`text`, `string`, `number`, `boolean`, `date`, `badge`, `enum`) and add `code` for monospace raw or JSON values. Bind `data` to one record, commonly `/queries/<detailQuery>/data/0`.
518
+ - `fields[]`: `{ key, label?, kind?, tones? }`. Kinds reuse Table formatting (`text`, `string`, `number`, `boolean`, `date`, `badge`, `enum`) and the Table cell renderers (`status`: lifecycle chip or `tones` pill; `user`: avatar plus name; `relative`: smart time, exact on hover; `copy`: value plus copy button), and add `code` for a monospace block and `json` for a collapsible JSON tree (objects, or a string holding JSON). Bind `data` to one record, commonly `/queries/<detailQuery>/data/0`.
519
+
520
+ Motion details:
521
+
522
+ - Every `Stack` fades its direct children in and lifts them 4px over 200ms on first paint, staggered 30ms (capped at the 10th child). It never replays on the 5s data poll. A Stack nested inside another one does not add its own entrance. `motion: "fade"` drops the lift; `motion: "none"` turns it off. Viewers with reduced motion get a 150ms fade.
523
+
524
+ Shortcut details:
525
+
526
+ - `Button.shortcut` and `SearchInput.shortcut` are one key. Letters and digits match without Shift; symbols such as `/` and `?` match as typed. They do nothing while focus is in a text field, while Ctrl, Cmd, or Alt is held, or while a dialog or menu is open (unless the control is inside it, such as a Button in an open Drawer).
497
527
 
498
528
  ## Layout & interactivity
499
529
 
@@ -501,7 +531,7 @@ Use `Stack` as the primary page and section layout; `Container` is the legacy tw
501
531
 
502
532
  `Split` and `Tabs` children are positional. For Split, `children[0]` is the first pane, `children[1]` is the second, and extra children append inside the second pane. Below `collapseBelow`, panes stack; `reverse` changes only that narrow-layout stacking order. For Tabs, `children[i]` is the body for `tabs[i]`; keep both arrays in the same order and with the same count. Inactive tab children stay mounted but hidden, so Tables keep polling. Use `Divider` to separate sections and `Markdown` for richer explanatory content.
503
533
 
504
- SearchInput and Select are client-side controls: each needs a literal `id` and writes state under `/ui/<id>/value`; Tabs writes its active key to `/ui/<id>/tab`. Bind those values into a Table's `search` and `filters` props. This filters already-polled rows locally; it does not alter or re-run the named query.
534
+ SearchInput, Select, and Chips are client-side controls: each needs a literal `id` and writes state under `/ui/<id>/value`; Tabs writes its active key to `/ui/<id>/tab`. Bind those values into a Table's `search` and `filters` props. This filters already-polled rows locally; it does not alter or re-run the named query.
505
535
 
506
536
  ```json
507
537
  {
@@ -550,7 +580,7 @@ Valid `$state` roots are:
550
580
 
551
581
  - `/queries/<declaredQuery>/...`
552
582
  - `/forms/<formId>/...`, where a `Form` element has that literal `props.id`
553
- - `/ui/<id>/...`, where a SearchInput, Select, or Tabs element has that literal `props.id` (SearchInput and Select use `/value`; Tabs uses `/tab`)
583
+ - `/ui/<id>/...`, where a SearchInput, Select, Chips, or Tabs element has that literal `props.id` (SearchInput, Select, and Chips use `/value`; Tabs uses `/tab`)
554
584
  - `/actions/<declaredAction>/...`
555
585
  - `/route/page` for the active page name
556
586
  - `/route/params/<declaredParam>` for a param declared on the current page
@@ -572,10 +602,11 @@ Each sentinel object must contain exactly the single key shown.
572
602
 
573
603
  An action-chain step is `{ "action": "<type>", "params": {...} }`. Available action types:
574
604
 
575
- - `app.mutate`: `{ model, op, rowId?, values?, formId? }`, where `op` is `"create"`, `"update"`, or `"delete"`. `update`/`delete` require `rowId` (usually `{ "$row": "id" }`); literal `values` keys must be model columns. Successful mutation refetches all queries on that model.
605
+ - `app.mutate`: `{ model, op, rowId?, values?, formId? }`, where `op` is `"create"`, `"update"`, or `"delete"`. `update`/`delete` require `rowId` (usually `{ "$row": "id" }`, or `{ "$state": "/route/params/<param>" }` in a Drawer footer); literal `values` keys must be model columns. Successful mutation refetches all queries on that model.
576
606
  - `app.refresh`: `{ query? }`; omit `query` to refetch all, or name a declared query.
577
607
  - `app.action`: `{ name, input? }`; `name` must be declared in definition `actions`. For a task-kind row action, pass whole-row context with `input: { issue: { "$row": "" } }`.
578
608
  - `app.navigate`: `{ page, params? }`; `page` must be declared, supplied keys must be params of the target page, and every required target param must be supplied. `params` replace the current route params wholesale and may contain action sentinels. Navigation pushes history, so browser Back returns to the previous page/params; only `mode` is preserved automatically.
609
+ - `ui.toast`: `{ message, description?, tone?, undo? }`, where `tone` is `"success"`, `"error"`, `"info"` (default), or `"warning"`. `undo: { label?, actions }` adds an Undo button (default label `Undo`) that runs `actions` as a chain; its steps are validated like any chain. Put it after the step it confirms, e.g. a row delete followed by `{ "action": "ui.toast", "params": { "message": "Order deleted", "undo": { "actions": [{ "action": "app.mutate", "params": { "model": "order", "op": "create", "values": { "title": { "$row": "title" } } } }] } } }`. Undo params resolve `$state` when Undo is pressed; `$row` / `$form` resolve when the toast is shown.
579
610
  - `swarm.sdk`: `{ sdk, args? }`; invokes a catalog-supported Swarm browser SDK method with the viewer's bearer.
580
611
  - `swarm.call`: `{ method, endpoint: "/api/...", body? }`, where `method` is `"GET"`, `"POST"`, `"PUT"`, `"DELETE"`, or `"PATCH"`; raw authenticated API call.
581
612
 
@@ -704,3 +735,10 @@ Create this with `app-upsert`:
704
735
  ```
705
736
 
706
737
  The tool returns `{ appId, url: "/apps/<id>" }`. Open that URL to verify the live UI, then continue with the read-patch-validate loop above.
738
+
739
+ ## View and screenshot a live app
740
+
741
+ - An app renders at `<dashboard>/apps/<id>`. Two view modes are set by query string: `?mode=full` (full-viewport overlay with a slim header) and `?mode=chromeless` (only the rendered page, the embed surface). `app.navigate` keeps only `?mode`.
742
+ - Logged-in browser: a fresh dashboard has no connection and redirects to /setup. Seed the localStorage key `agent-swarm-connections` with `{connections:[{id,name,apiUrl,apiKey}],activeId}`. Build the value from an env var and pipe it to `agent-browser eval --stdin`, so the key never reaches a command line. Reload, then pick a user in the "Who are you?" dialog.
743
+ - Apps scroll inside an inner canvas, so a full-page screenshot captures only the viewport. Scroll the canvas or raise the viewport height. For mobile, set a 375px-wide viewport before the screenshot.
744
+ - App definitions: `GET /api/apps` and `GET /api/apps/<id>` with the API key as Bearer.
@@ -0,0 +1,14 @@
1
+ {
2
+ "kind": "skill",
3
+ "name": "chat-sdk",
4
+ "displayName": "Chat SDK",
5
+ "slug": "chat-sdk",
6
+ "title": "Chat SDK",
7
+ "description": "Chat threads from Telegram, Slack (Chat SDK), Discord, and Teams. Use when a task carries a task:chat-sdk contextKey or a user asked through Telegram, Slack, Discord, or Teams. Covers what the swarm posts for you, `chat-reply`, markdown and length per platform, attachments, `chat-read-thread` limits, and channel routes with `register-chat-channel`.",
8
+ "version": "1.0.0",
9
+ "category": "skills",
10
+ "placeholders": [],
11
+ "runAllSeedersCandidate": true,
12
+ "systemDefault": false,
13
+ "tags": ["chat", "telegram", "discord", "teams", "slack", "communication"]
14
+ }
@@ -0,0 +1,84 @@
1
+ # Chat SDK
2
+
3
+ The swarm receives messages from Telegram, Slack, Discord, and Microsoft Teams through the Chat SDK channel. A person mentions the bot or sends it a direct message, and the swarm creates a task for it. This skill tells you how to work on such a task.
4
+
5
+ ## How you know you are on a chat task
6
+
7
+ - The task has `source: "chat"`, or it is a lead follow-up of one.
8
+ - The task `contextKey` starts with `task:chat-sdk:`. The full form is `task:chat-sdk:<adapter>:<encoded thread id>`, for example `task:chat-sdk:telegram:555001`.
9
+ - The task text holds the user's message. Later replies in the same thread arrive as follow-up tasks with the same `contextKey`, or as steering messages on your running task.
10
+ - The user has four slash commands that never reach you as a task: `/help`, `/status`, `/cancel` and `/reset`. `/cancel` ends the thread's active tasks, yours included, with status `cancelled`. After `/reset` the next task has no parent task and no earlier thread context, so treat it as a new conversation.
11
+
12
+ Use `get-task-details` to read the `contextKey` when you are not sure.
13
+
14
+ ## Reply with `chat-reply`
15
+
16
+ `chat-reply { text }` posts to the thread your current task came from. It finds the thread from your task, so you do not pass ids.
17
+
18
+ - Use it to ask the user a question you cannot answer yourself. Ask once, with all your questions together.
19
+ - Use it for one short progress note when work takes more than a few minutes, such as "Started. This takes about 10 minutes."
20
+ - Do not use it for receipts ("Got it", "On it"), and do not send many progress notes.
21
+ - It works only on a task with a `task:chat-sdk:` contextKey. A session bound to a non-chat task does not see `chat-reply` at all, because the tool is left out of its tool list.
22
+
23
+ ## The final summary is posted automatically
24
+
25
+ When your task ends, the swarm posts the task output to the thread:
26
+
27
+ - Completed: your `store-progress` output.
28
+ - Failed: a "Task failed." line, then the failure reason.
29
+ - Cancelled: a "Task cancelled." line, then the reason when there is one.
30
+
31
+ Do not post your final answer with `chat-reply` as well. The user would get it twice. Put the answer in the `store-progress` output and stop.
32
+
33
+ The post is durable. If the platform is down, the swarm retries with backoff for up to 24 hours.
34
+
35
+ ## Markdown per platform
36
+
37
+ Write plain, short markdown: bold, italic, inline code, code blocks, bullet lists, and links. Avoid tables, nested lists, and HTML.
38
+
39
+ | Platform | What renders | Limit per message |
40
+ |---|---|---|
41
+ | Telegram | Bold, italic, code, code blocks, links, lists. No tables. | 4096 characters |
42
+ | Discord | Bold, italic, code, code blocks, lists, headings, links. No tables. | 2000 characters |
43
+ | Slack | Converted to Slack mrkdwn: bold, italic, code, lists, links. Tables do not render well. | 4000 characters |
44
+ | Teams | Basic markdown: bold, italic, code, lists, links. | 20000 characters |
45
+
46
+ The swarm splits longer text into several messages at line breaks. Long answers are hard to read in chat, so keep the answer short and put long material in an artifact or page, then post the link.
47
+
48
+ ## Attachments arrive as task attachments
49
+
50
+ Files and images the user sends in the thread are stored as attachments of the task. The task text names them. Download them with the `download-task-attachment` skill. You cannot send files back to the thread in this version. Share a link instead.
51
+
52
+ ## `chat-read-thread` limits per platform
53
+
54
+ `chat-read-thread { threadId, limit? }` returns the most recent messages of a thread, oldest first. The thread id is the adapter name plus the URI-decoded remainder of the contextKey. Telegram ids have no escaping, so `telegram:555001` stays as it is. Slack, Discord, and Teams ids contain `%3A` in the contextKey, so do not build the id by hand. `chat-reply` resolves the thread itself. For `chat-read-thread`, use the thread id returned by `chat-reply`, `chat-post`, or `chat-open-dm`. You can read the thread of your current task. Any other thread needs lead privileges.
55
+
56
+ - Telegram has no history API. The SDK keeps the Telegram thread history it saw in the swarm database, capped at 100 messages and 7 days.
57
+ - Teams needs the `Chat.Read.*` Graph permission on the bot. Without it, the call fails.
58
+ - Slack and Discord return the platform history.
59
+
60
+ The task text already holds the earlier swarm messages of the thread, so read the thread only when you need more.
61
+
62
+ ## Other tools
63
+
64
+ - `chat-post { adapter, threadId | channelId, text }` posts outside your task's thread. Lead only. Use it only when the task asks you to message another thread or channel.
65
+ - `chat-open-dm { adapter, userId }` returns the direct-message thread id for a user. Lead only. Then post to it with `chat-post`.
66
+
67
+ ## Leads: `register-chat-channel`
68
+
69
+ By default every message addressed to the swarm goes to the lead. A lead can route one channel to a fixed agent or workflow:
70
+
71
+ ```
72
+ register-chat-channel { adapter: "telegram", channelId: "telegram:-100123", agentId: "<agent id>" }
73
+ register-chat-channel { adapter: "slack", channelId: "slack:C123", workflowId: "<workflow id>" }
74
+ unregister-chat-channel { adapter: "telegram", channelId: "telegram:-100123" }
75
+ ```
76
+
77
+ Give exactly one of `agentId` or `workflowId`. A `swarm#<agentId>` token in a message still wins over the route. A workflow mapping fires only for a bot mention or a DM; a plain reply in the subscribed thread does not start another run. Only the lead can call these tools.
78
+
79
+ ## Etiquette
80
+
81
+ - Keep messages short. Chat is read on phones.
82
+ - Never post secrets, tokens, internal URLs with credentials, or private data from other users. The swarm scrubs known secrets, but do not depend on it.
83
+ - When you ask a question in a group, start with the user's name so they see it.
84
+ - One question message is better than several.
@@ -62,6 +62,13 @@ agent-swarm x composio POST /tools/execute/GMAIL_SEND_EMAIL \
62
62
  - `recipient_email` is the primary; add more via `extra_recipients` / `cc` / `bcc`.
63
63
  - **Sending is a write action** — only do it when the task explicitly asks.
64
64
 
65
+ ## Sender (From) and footer
66
+
67
+ - A draft defaults to the connected account's primary address. Before you create drafts, list the account's verified send-as aliases and set From to the alias the requester prefers (requester profile or brief). Take the footer and signature domain from the same source.
68
+ - Never set From to an alias that is not verified. Gmail rewrites it to the primary address.
69
+ - After you create the drafts, check From as well as To and Subject on each one.
70
+ - To fix existing drafts, update each draft in place with the alias. Draft ids stay the same, so there is no need to delete and recreate.
71
+
65
72
  ## Reply in a thread
66
73
 
67
74
  ```bash
@@ -67,7 +67,7 @@ agent-swarm x composio POST /tools/execute/GOOGLEDOCS_CREATE_DOCUMENT_MARKDOWN \
67
67
  2. Write the converted content to your own agent-fs namespace with a short
68
68
  provenance header naming the source document and import method.
69
69
  3. Resolve the destination org and drive from the environment or
70
- `agent-fs stat <path> --json`; the `artifacts` skill documents the canonical
70
+ `agent-fs stat <path> --json`; the `agent-fs` skill documents the canonical
71
71
  storage and share-link workflow. Never copy org or drive IDs from an example.
72
72
  4. Read the stored file back and assert several markers: the title, a middle
73
73
  phrase, and the final sentence. A successful byte-count response alone does
@@ -0,0 +1,14 @@
1
+ {
2
+ "kind": "skill",
3
+ "name": "email-domain-health",
4
+ "displayName": "Email Domain Health",
5
+ "slug": "email-domain-health",
6
+ "title": "Email Domain Health",
7
+ "description": "Build a scheduled email domain health monitor: ask the user for domains, providers, test inbox, schedule and alert rules, then build a checks script (SPF, DKIM, DMARC, MX, provider records, blocklists with positive controls), a daily test send with score capture, bounce rate, run history in a Swarm App, and alerts only on a new red or a score drop. Orchestrates the swarm-scripts, apps, scheduling and composio-gmail skills.",
8
+ "version": "1.0.0",
9
+ "category": "skills",
10
+ "placeholders": [],
11
+ "runAllSeedersCandidate": true,
12
+ "systemDefault": true,
13
+ "tags": ["email", "deliverability", "dns", "monitoring", "apps"]
14
+ }
@@ -0,0 +1,131 @@
1
+ # Email Domain Health
2
+
3
+ Build a monitor that tells a person, every working day, whether mail from their
4
+ domains authenticates, lands, and stays off blocklists. It has four parts: one
5
+ saved script (checks + test send), a Swarm App with run history, one or two
6
+ schedules, and an alert that fires only when something gets worse.
7
+
8
+ This skill decides **what** to build. The mechanics live in other skills; read
9
+ them, do not re-derive them:
10
+
11
+ - [[swarm-scripts]]: authoring, `script-upsert`, scope, connections, secrets.
12
+ - [[apps]]: models, named queries, pages, row writes.
13
+ - [[scheduling]]: `targetType: "script"` schedules, cron and timezone.
14
+ - [[composio-gmail]] (or the user's own mail integration): the test send and
15
+ the bounce search.
16
+
17
+ Full design notes and pitfalls: the "Email domain health" playbook on the docs
18
+ site.
19
+
20
+ ## 1. Ask for requirements first
21
+
22
+ Ask before building (one batched question, see [[ask-user]]). Do not guess any
23
+ of these; a wrong selector or From address makes every later check wrong.
24
+
25
+ | Ask | Why it matters |
26
+ |---|---|
27
+ | Domains to monitor, and which one is the envelope/bounce domain | One check set per domain; SPF is judged on the envelope domain. |
28
+ | Sending providers per domain (Google Workspace, Microsoft 365, an ESP such as Resend, SES, Postmark) and each DKIM selector | DKIM is looked up at `<selector>._domainkey.<domain>`; there is no way to list selectors. |
29
+ | The From address for the test send, and the integration that sends it (Composio Gmail, SMTP, ESP API) | The test must leave through the real path, alias included. |
30
+ | Test inbox: mail-tester (free tier has a small daily cap) or an inbox the user owns | Decides how the score is captured. |
31
+ | Schedule and timezone | Default: weekdays, morning, user's timezone. |
32
+ | Alert channel and thresholds (score floor, bounce rate) | Defaults: score < 6/10, bounces >= 5% on >= 10 sends. |
33
+ | History retention | Default 90 days of runs. |
34
+
35
+ Read the answers back in one short message before you write code.
36
+
37
+ ## 2. Build the checks script
38
+
39
+ One saved script with modes `send`, `check`, and optionally `backfill`. Keep
40
+ domains, selectors, thresholds and ids in a config block at the top or in
41
+ `args`, never scattered through the code.
42
+
43
+ **DNS over HTTPS, two resolvers.** Query every record on two public DoH
44
+ resolvers (for example dns.google and Cloudflare's JSON endpoint).
45
+ NOERROR and NXDOMAIN are answers; SERVFAIL, REFUSED and timeouts are not.
46
+ Same verdict on both: use it. Different verdicts: `warn` ("resolvers
47
+ disagree", often propagation). One failed: use the other, note it. Both
48
+ failed: `unknown`.
49
+
50
+ **Checks per domain:**
51
+
52
+ - SPF: exactly one `v=spf1` record, includes each provider, total DNS lookups
53
+ <= 10 (recurse `include:`/`redirect=`; warn at 9), ends `~all` or `-all`.
54
+ - DKIM: TXT at each selector (follow a CNAME), non-empty `p=` (empty means
55
+ revoked), key length.
56
+ - DMARC: exactly one `v=DMARC1` at `_dmarc.<domain>`, a `p=` tag, `rua=` (warn
57
+ without it).
58
+ - MX: present and pointing at the expected provider.
59
+ - Provider records the ESP documents (bounce subdomain SPF and MX, DKIM
60
+ CNAMEs).
61
+ - Blocklists: domain lists such as Spamhaus DBL, SURBL, URIBL (details below).
62
+ - Test-send result: score plus the SPF, DKIM and DMARC verdicts from the
63
+ receiver's headers.
64
+ - Bounce rate: sent vs bounced in the last 24h from the sending mailbox,
65
+ excluding test sends.
66
+
67
+ **Verdicts:** `ok`, `warn`, `red`, `unknown`. Overall = worst. `unknown` means
68
+ "could not read", and it is **never** reported as clean.
69
+
70
+ **Controls, not hope.** Every negative result needs a positive control in the
71
+ same run:
72
+
73
+ - Blocklists: before trusting a list, query its documented test entry
74
+ (`dbltest.com` on `dbl.spamhaus.org`, `test.surbl.org` on `multi.surbl.org`,
75
+ `test.uribl.com` on `multi.uribl.com`) on each resolver. Use the list only on
76
+ a resolver where the test entry answers "listed". Refusal codes
77
+ (`127.255.255.x` from Spamhaus, `127.0.0.1` from SURBL and URIBL) mean the
78
+ resolver is blocked: `unknown`.
79
+ - Bounce rate: a whole-mailbox 24h count of 0 means the search failed:
80
+ `unknown`, not "0 bounces".
81
+ - Test result page without a score yet: `unknown` for the first hour after the
82
+ send, then `red`.
83
+
84
+ Return a `controls.ok` flag with the result so a run that read nothing cannot
85
+ pass as green.
86
+
87
+ **Send mode.** Send one realistic message (the same template and links real
88
+ mail uses) to a fresh test address, through the user's integration. Read the
89
+ sent message back and confirm the From header kept the address the user asked
90
+ for. Store the pending test id in KV with a per-day key so a retry never
91
+ sends twice.
92
+
93
+ ## 3. Build the app
94
+
95
+ Use [[apps]]. Minimum models:
96
+
97
+ - `run`: one row per run (time, overall, red/warn/unknown counts, score,
98
+ bounces, sent).
99
+ - `check`: one row per check per run (history).
100
+ - `current`: one row per `domain|check`, updated in place, with `since` = when
101
+ the status last changed.
102
+
103
+ Optional per-domain views (`domainRun`, `domainCheck`) make tabs per domain
104
+ cheap to render. Page: one tab per domain (problems first, raw record on
105
+ hover), a score trend, and a collapsed "How it works". The script writes rows
106
+ through the row endpoints; prune `run` and `check` rows older than the
107
+ retention window.
108
+
109
+ ## 4. Schedule it
110
+
111
+ Use [[scheduling]] with `targetType: "script"`, not an agent task that says
112
+ "run the script". Two schedules: `send`, then `check` 10 to 15 minutes later
113
+ in the user's timezone, so the test inbox has the message before the check
114
+ reads it.
115
+
116
+ ## 5. Alert only on change
117
+
118
+ Compare with the previous `current` rows. Alert when a check **turns** red, or
119
+ the score drops below the floor after being above it. Never alert on a
120
+ steady-state warn or an unknown. The message lists each new red with its raw
121
+ detail and links the app. If the script's identity cannot post to the chosen
122
+ channel, have it hand the message to an agent that can.
123
+
124
+ ## Done when
125
+
126
+ - A manual `check` run returns every check, `controls.ok: true`, and writes
127
+ rows the app renders.
128
+ - Every blocklist shows either a passing control or `unknown`, never a bare
129
+ "not listed".
130
+ - One full scheduled send + check cycle has run and the score landed in `run`.
131
+ - You reported the app link, script name and scope, and schedule ids.
@@ -287,8 +287,8 @@ value so you can detect a clobber.
287
287
  ## See also
288
288
 
289
289
  - The **`pages`** skill — companion skill for authed pages and `swarmSdk.kv`.
290
- - The **`artifacts`** skill — for anything too big or too binary for KV.
290
+ - The **`agent-fs`** skill — for anything too big or too binary for KV.
291
291
  - `src/be/migrations/061_kv_store.sql` — schema (`kv_entries`)
292
292
  - `src/http/kv.ts` — REST handler + namespace resolution
293
293
  - `src/tools/kv/*` — MCP tool registrars
294
- - `src/artifact-sdk/browser-sdk.ts` — `swarmSdk.kv` for pages
294
+ - `src/pages/browser-sdk.ts` — `swarmSdk.kv` for pages
@@ -1,8 +1,8 @@
1
1
  # Pages — DB-backed Static Artifacts
2
2
 
3
3
  DB-backed static content (HTML or JSON) served by the API directly. Cheap,
4
- versioned, share-able by URL. The lighter-weight cousin of `artifacts` —
5
- no PM2, no tunnels, no port allocation, no `services` registry row.
4
+ versioned, share-able by URL. No server process, no tunnel, no port
5
+ allocation.
6
6
 
7
7
  A page should be a clean human-facing artifact, not a raw dump with a URL. This
8
8
  skill covers both halves: **how to publish** a page, and **the taste baseline**
@@ -13,7 +13,7 @@ every page should meet.
13
13
  > (e.g. `CAPABILITIES=core,task-pool,pages`). If the tool is missing from
14
14
  > your MCP list, this is why.
15
15
 
16
- ## When to use Pages vs Artifacts
16
+ ## When to use Pages vs Apps
17
17
 
18
18
  | You need… | Use |
19
19
  |---|---|
@@ -21,12 +21,11 @@ every page should meet.
21
21
  | A JSON status payload + a few buttons that call swarm APIs | **Pages** (`contentType: 'application/json'`) |
22
22
  | To share an output via a URL with no server logic | **Pages** |
23
23
  | Analysis that should be linkable and bookmarkable | **Pages** |
24
- | Custom routes, websockets, server-side logic | The **`artifacts`** skill |
25
- | File uploads or per-request computation | The **`artifacts`** skill |
24
+ | Persistent records, queries, and server-side actions | The **`apps`** skill |
25
+ | Large or binary files (PNG, MP4, exports) | The **`agent-fs`** skill |
26
26
 
27
27
  Rule of thumb: if the content is a snapshot (you can write the full HTML/JSON
28
- in a single call), use pages. If the content is a *running program*, use
29
- artifacts.
28
+ in a single call), use pages. If it needs stored data and actions, use apps.
30
29
 
31
30
  Do **not** use pages for:
32
31
 
@@ -533,9 +532,8 @@ human-facing summary.
533
532
 
534
533
  ## See Also
535
534
 
536
- - The **`artifacts`** skill — durable storage (agent-fs, shared workspace) plus
537
- full custom Hono apps with PM2 + a tunneled subdomain. Use it for interactive
538
- servers, not static content.
535
+ - The **`apps`** skill — schema-backed records, named queries, and actions.
536
+ - The **`agent-fs`** skill — durable file storage and share links.
539
537
  - The **`kv-storage`** skill — page-scoped state via `swarmSdk.kv`.
540
538
  - `runbooks/secret-scrubbing.md` — egress scrubbing details.
541
539
  - SPA listing: `${APP_URL}/pages` — the same `APP_URL` used for share links above.
@@ -0,0 +1,14 @@
1
+ {
2
+ "category": "skills",
3
+ "description": "Narrated QA loom for UI work: record a UI QA pass with agent-browser and a timestamped step log, voice it with ElevenLabs, burn one-line subtitles with ffmpeg, and embed the narrated mp4 (plus an optional GIF preview) in the PR as GitHub user attachments. Use on UI-changing PRs and UI QA passes. Layers on the `qa` and `agent-browser` skills. Needs ELEVENLABS_API_KEY, ffmpeg with libass, and agent-browser.",
4
+ "displayName": "QA Loom",
5
+ "kind": "skill",
6
+ "name": "qa-loom",
7
+ "placeholders": [],
8
+ "runAllSeedersCandidate": true,
9
+ "slug": "qa-loom",
10
+ "systemDefault": true,
11
+ "tags": ["qa", "video", "screen-recording", "evidence", "elevenlabs"],
12
+ "title": "QA Loom",
13
+ "version": "1.0.0"
14
+ }