@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.
- package/README.md +1 -1
- package/dist/{acp-adapter-fpecx1g1.js → acp-adapter-sj2g0m84.js} +18 -17
- package/dist/{actions-pw87a1a8.js → actions-fegk44gr.js} +65 -35
- package/dist/{amp-adapter-8jem2bc0.js → amp-adapter-ad8ft89x.js} +8 -7
- package/dist/{anthropic-messages-8ee2f22q.js → anthropic-messages-r6czkjv1.js} +7 -7
- package/dist/{app-wmf0m843.js → app-kfvgbvzy.js} +19 -13
- package/dist/{assistant-emp1e108.js → assistant-z7zdy9ja.js} +65 -38
- package/dist/{boot-reembed-1de4f4aj.js → boot-reembed-5c2z0mbe.js} +11 -9
- package/dist/{boot-reembed-4ymn9atn.js → boot-reembed-j8xa181e.js} +11 -9
- package/dist/{boot-scrub-sweep-adhk1s8c.js → boot-scrub-sweep-hnkn3rgt.js} +12 -10
- package/dist/{boot-seeds-8q637aew.js → boot-seeds-kwjq4d2z.js} +15 -13
- package/dist/{claude-adapter-t26cv8a4.js → claude-adapter-9vmvpvgd.js} +20 -19
- package/dist/{claude-managed-adapter-xgxhqrt2.js → claude-managed-adapter-k9qae05j.js} +7 -5
- package/dist/{claude-managed-setup-bnmjarjc.js → claude-managed-setup-q54zx5q2.js} +6 -4
- package/dist/{claude-sdk-session-gsfsh662.js → claude-sdk-session-9jye6cxr.js} +12698 -11995
- package/dist/{cli-k46r9hmk.js → cli-03gedhng.js} +16 -14
- package/dist/{cli-kb4ehzx5.js → cli-1e4b6bzh.js} +7 -7
- package/dist/{cli-d603qf86.js → cli-29jc5q7w.js} +10 -8
- package/dist/cli-2m1ktfzz.js +17795 -0
- package/dist/{cli-8vs9trd4.js → cli-3dj8k4vy.js} +1 -1
- package/dist/{cli-zxm9p3x4.js → cli-3fbmmawm.js} +5 -5
- package/dist/{cli-gmdajfw5.js → cli-4jxm2qtz.js} +4987 -1708
- package/dist/{cli-tadfgd0t.js → cli-5hkm7whv.js} +33 -4
- package/dist/{cli-6jb2rf77.js → cli-5t4f29ez.js} +3 -3
- package/dist/{cli-7a0sde2x.js → cli-6cagzc96.js} +9 -0
- package/dist/cli-6cvfxs8r.js +47 -0
- package/dist/{cli-pj6x9qce.js → cli-6jyj59nb.js} +2 -2
- package/dist/{cli-388egyf6.js → cli-6razj6t4.js} +2 -2
- package/dist/{cli-qsztpfry.js → cli-74ahz61x.js} +2 -2
- package/dist/{cli-4nz8p1p5.js → cli-7x0g4sn1.js} +15474 -16082
- package/dist/{cli-yatxqynm.js → cli-9ha6hs2d.js} +1 -290
- package/dist/{cli-81r1wjyv.js → cli-9nfx44eh.js} +1 -1
- package/dist/cli-9traj3dn.js +638 -0
- package/dist/{cli-smcrrmhy.js → cli-agzebbd5.js} +9 -9
- package/dist/{cli-bf7485pm.js → cli-apx87jne.js} +2 -2
- package/dist/{cli-ztrv9y21.js → cli-bzszjf2p.js} +3689 -1081
- package/dist/{cli-c5km5znr.js → cli-c8r3kzzb.js} +1 -1
- package/dist/{cli-hsem16hb.js → cli-cgtksawx.js} +7 -4
- package/dist/{cli-db3m6zdt.js → cli-d2qr7aj4.js} +1 -1
- package/dist/{cli-1hgat6pa.js → cli-d5f9ed5c.js} +5 -5
- package/dist/cli-e3g5mbkz.js +776 -0
- package/dist/{cli-7jykv7ef.js → cli-e6mpjhed.js} +1 -1
- package/dist/{cli-j6kpj9az.js → cli-ee9gtz70.js} +1553 -68
- package/dist/{cli-e0a9sk00.js → cli-eejsgvm4.js} +4 -4
- package/dist/{cli-tgpfnm6r.js → cli-ef1hg1wv.js} +518 -59
- package/dist/cli-f527ce74.js +781 -0
- package/dist/{cli-ppy4nnnh.js → cli-f7kr7svz.js} +2 -2
- package/dist/cli-hjwmhcg1.js +10622 -0
- package/dist/{cli-2bw3w47j.js → cli-hnhtk6cj.js} +1 -1
- package/dist/{cli-ywm1azg9.js → cli-hnmhtzvh.js} +6 -4
- package/dist/{cli-4fnb6884.js → cli-hvqbtna4.js} +7 -1259
- package/dist/{cli-pstq9888.js → cli-j0pfptdp.js} +220 -103
- package/dist/cli-jta2eyzc.js +619 -0
- package/dist/{cli-esnpehf5.js → cli-k382tshb.js} +12 -13
- package/dist/cli-k3dp5tpf.js +263 -0
- package/dist/{cli-wd0hz6ah.js → cli-kbrt8gqa.js} +9 -91
- package/dist/cli-kceah1eh.js +92 -0
- package/dist/{cli-prrt9fa0.js → cli-ke896fcf.js} +10 -10
- package/dist/{cli-nv4zd0b9.js → cli-kt6zqg0e.js} +6 -6
- package/dist/{cli-g6t9texm.js → cli-m50t4309.js} +5 -18
- package/dist/cli-n2wq4j7m.js +6985 -0
- package/dist/{cli-c2pq2rjb.js → cli-n3hzg89b.js} +46 -15
- package/dist/{cli-tmhsxhb8.js → cli-n5vwg842.js} +23 -6
- package/dist/{cli-5rv1j9ga.js → cli-ne236afy.js} +53 -7
- package/dist/cli-p0sg24zp.js +16 -0
- package/dist/{cli-d161g69q.js → cli-p1x85d2t.js} +50 -2
- package/dist/{cli-7pz3b7x5.js → cli-przpmdgr.js} +25 -0
- package/dist/{cli-g4f5pj26.js → cli-q0ke1yww.js} +111 -25
- package/dist/{cli-e3pmyt39.js → cli-q50942rs.js} +1 -1
- package/dist/{cli-r44t9wqc.js → cli-r106725k.js} +2 -2
- package/dist/{cli-gj81e47t.js → cli-r2pnvj8g.js} +2 -2
- package/dist/{cli-r8smj8b8.js → cli-r62a35ea.js} +19 -3
- package/dist/{cli-cd0hv9nh.js → cli-ra0xzggj.js} +2 -2
- package/dist/cli-rhs9pz33.js +294 -0
- package/dist/{cli-nyhb1r6x.js → cli-s2sp957d.js} +52 -29
- package/dist/{cli-1x7da0de.js → cli-s4setq91.js} +8 -8
- package/dist/{cli-xczpdp9e.js → cli-se8w6wny.js} +6 -6
- package/dist/cli-skxvvpap.js +10140 -0
- package/dist/{cli-014vns2y.js → cli-stfs0k5s.js} +208 -525
- package/dist/{cli-0p3enk35.js → cli-tqaazemc.js} +9 -1
- package/dist/{cli-cf69s3ew.js → cli-tr4sxp68.js} +3 -3
- package/dist/{cli-8hzfb6xz.js → cli-tsde1hdr.js} +15 -15
- package/dist/{cli-cceskcvt.js → cli-txqb9s38.js} +8 -8
- package/dist/{cli-aspcaef3.js → cli-v1yb0rqb.js} +6 -6
- package/dist/{cli-j1pmc07z.js → cli-vatyhshy.js} +268 -40
- package/dist/{cli-04emfe2q.js → cli-vdach5a9.js} +5 -5
- package/dist/{cli-7avy4v8w.js → cli-w1q7h2r2.js} +5 -3
- package/dist/{cli-rcp5aqdw.js → cli-w7m81tb7.js} +1 -1
- package/dist/{cli-hwwvy36w.js → cli-wayw93nt.js} +3 -3
- package/dist/cli-wmgrkscj.js +23254 -0
- package/dist/{cli-gp1rtyvk.js → cli-z5jjpaxw.js} +29 -6
- package/dist/{cli-451ks6aw.js → cli-zkpks3r3.js} +1 -1
- package/dist/cli.js +32 -48
- package/dist/{codex-adapter-m4tgdysy.js → codex-adapter-y81sssk0.js} +16 -15
- package/dist/{codex-login-841kv5fk.js → codex-login-f2rcfpp9.js} +3 -2
- package/dist/{codex-session-runner-ymwzm6ea.js → codex-session-runner-8vx4j6a9.js} +16 -15
- package/dist/{commands-n0hshcvs.js → commands-kcpvpcrr.js} +21 -10
- package/dist/{controller-hq5ggpcp.js → controller-ywrmt6yd.js} +5 -4
- package/dist/{controllers-connect-y07cqcvy.js → controllers-connect-kwpvj3rf.js} +45 -38
- package/dist/{cursor-adapter-q8pgv5qa.js → cursor-adapter-cehv1qns.js} +7 -6
- package/dist/{db-hkqvrdcb.js → db-k8ankj20.js} +13 -11
- package/dist/{db-4w7tmt1g.js → db-ww5d7jq3.js} +29 -9
- package/dist/{devin-adapter-xgtj680j.js → devin-adapter-x1022qb1.js} +3 -2
- package/dist/{dispatcher-5sd69y14.js → dispatcher-e8gq5m68.js} +41 -34
- package/dist/{docker-f503k9p0.js → docker-ff6y1190.js} +3 -2
- package/dist/{dsh-adapter-2h8m4cab.js → dsh-adapter-3epjm3af.js} +7 -6
- package/dist/{e2b-66xra4az.js → e2b-8ygwhg0f.js} +4 -3
- package/dist/{e2b-xwhyj8cw.js → e2b-swhnpw3n.js} +4 -3
- package/dist/{event-bus-vjtv82gm.js → event-bus-6axnngdj.js} +3 -2
- package/dist/external-channel-h3359cwn.js +52 -0
- package/dist/{google-generative-ai-7pavrbv7.js → google-generative-ai-g8db7x0f.js} +5 -3
- package/dist/{google-vertex-3f1jk41f.js → google-vertex-r2692q0r.js} +5 -3
- package/dist/{grok-adapter-8m9yqzbq.js → grok-adapter-0d4x7gkx.js} +19 -18
- package/dist/{handlers-s20xr10r.js → handlers-an1gzv21.js} +44 -37
- package/dist/{heartbeat-3k31q4g2.js → heartbeat-6dakeb61.js} +46 -39
- package/dist/{hook-gdn6a2fn.js → hook-6v6cv6d5.js} +12 -24
- package/dist/{hook-ygwerrdh.js → hook-sa5xye4y.js} +13 -12
- package/dist/{http-jdteeg81.js → http-r7f3eky0.js} +1598 -279
- package/dist/index-52j1aja4.js +3465 -0
- package/dist/{index-e0snym4t.js → index-7ep4gzdk.js} +41 -34
- package/dist/index-9gw58n4k.js +193 -0
- package/dist/index-9j87mcm8.js +56305 -0
- package/dist/index-bsmcresk.js +79424 -0
- package/dist/index-mh85qhec.js +475 -0
- package/dist/{index-vm3bm9t7.js → index-rmpw9a8q.js} +41 -34
- package/dist/{index-6ksewd5y.js → index-s2wxgzr0.js} +1015 -732
- package/dist/{index-tsszt7g9.js → index-v1v6n7hg.js} +46 -39
- package/dist/index-we27kd31.js +12810 -0
- package/dist/{index-ffqzqgwb.js → index-zk96aq1k.js} +11 -9
- package/dist/{keepalive-jekc896t.js → keepalive-be9vwy5h.js} +13 -11
- package/dist/{lead-4k9s9szx.js → lead-0861cwrf.js} +64 -57
- package/dist/{maintenance-shazch4n.js → maintenance-mzzgc8bn.js} +11 -9
- package/dist/node-c9we95xf.js +1 -1
- package/dist/{node-fkajaq96.js → node-mn1zs7an.js} +2 -1
- package/dist/{oauth-refresh-sweep-5gb36kn8.js → oauth-refresh-sweep-cpdnfwdj.js} +12 -10
- package/dist/{onboard-ydrbnvww.js → onboard-fh8qs4r3.js} +11 -3
- package/dist/{opencode-adapter-5zh0he20.js → opencode-adapter-nm42wwj8.js} +19 -14
- package/dist/{otel-impl-6pf5cv86.js → otel-impl-x6c1c9g4.js} +248 -869
- package/dist/{pi-mono-adapter-ef9wwx9m.js → pi-mono-adapter-9n2ya240.js} +245 -107
- package/dist/{pricing-refresh-s52nsa5q.js → pricing-refresh-qb0rv0ap.js} +13 -11
- package/dist/{rbac-roles-aq3msh4e.js → rbac-roles-1gzdwbh1.js} +12 -10
- package/dist/{rbac-roles-q5gb5e8v.js → rbac-roles-3mraey52.js} +13 -11
- package/dist/{render-v2-pd1rg54q.js → render-v2-agpa45zz.js} +19 -13
- package/dist/{resume-gw4a2c6v.js → resume-x0ydy9s9.js} +27 -21
- package/dist/{runtime-qfry88v2.js → runtime-sa2vr16g.js} +2 -2
- package/dist/{setup-jda1ksdh.js → setup-as9ga0d6.js} +2 -2
- package/dist/{virtual-modules-b3ga2m65.js → virtual-modules-9gkxqrap.js} +7 -6
- package/dist/{work-objects-tb7ycye8.js → work-objects-y5g1y7ev.js} +49 -37
- package/dist/{worker-njjccz5d.js → worker-860c3e38.js} +64 -57
- package/dist/{x-qfmygqqv.js → x-m477fr0h.js} +3 -2
- package/openapi.json +951 -356
- package/package.json +12 -13
- package/src/apps/catalog.generated.json +1309 -53
- package/src/apps/page-validator.ts +93 -31
- package/src/azure-devops/api.ts +94 -18
- package/src/azure-devops/auth.ts +32 -0
- package/src/azure-devops/boards-sync.ts +708 -0
- package/src/azure-devops/handlers.ts +52 -8
- package/src/azure-devops/html.ts +26 -0
- package/src/azure-devops/index.ts +15 -0
- package/src/azure-devops/templates.ts +86 -0
- package/src/azure-devops/types.ts +65 -1
- package/src/be/config-refs.ts +60 -0
- package/src/be/db/page-versions.ts +15 -0
- package/src/be/db/runtime.ts +35 -8
- package/src/be/db/skill-name-resolution.ts +36 -0
- package/src/be/db/tasks/read.ts +230 -0
- package/src/be/db/tasks/write.ts +7 -0
- package/src/be/db.ts +74 -13
- package/src/be/migrations/213_script_connections_sql.sql +79 -0
- package/src/be/migrations/215_swarm_config_export_to_env.sql +34 -0
- package/src/be/migrations/216_task_resolved_effort.sql +5 -0
- package/src/be/migrations/217_chat_sdk_state.sql +104 -0
- package/src/be/model-catalog-store.ts +5 -1
- package/src/be/model-tier-resolution.ts +12 -1
- package/src/be/model-validation.ts +5 -2
- package/src/be/models-catalog.ts +26 -1
- package/src/be/modelsdev-cache.json +198 -63
- package/src/be/modelsdev-cache.ts +9 -1
- package/src/be/script-connections.ts +47 -8
- package/src/be/script-credential-broker.ts +39 -2
- package/src/be/scripts/run-saved.ts +1 -0
- package/src/be/scripts/typecheck.ts +10 -8
- package/src/be/seed/runner.ts +13 -1
- package/src/be/seed/state-db.ts +5 -0
- package/src/be/seed/types.ts +6 -0
- package/src/be/seed-pricing.ts +19 -2
- package/src/be/seed-scripts/catalog/ops-catalog-audit.inline.ts +1 -1
- package/src/be/seed-scripts/catalog/ops-catalog-audit.ts +1 -1
- package/src/be/seed-skills/bundled-files.generated.json +10 -18
- package/src/be/seed-skills/index.ts +107 -3
- package/src/be/sql-connections.ts +118 -0
- package/src/be/swarm-config-guard.ts +53 -0
- package/src/cli.tsx +0 -16
- package/src/commands/onboard/non-interactive.ts +4 -2
- package/src/commands/profile-sync.ts +5 -3
- package/src/commands/provider-credentials.ts +52 -6
- package/src/commands/runner.ts +196 -56
- package/src/e2b/env.ts +5 -0
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/extensions/contract.ts +45 -0
- package/src/extensions/dispatcher.ts +7 -0
- package/src/extensions/post-bridge.ts +40 -0
- package/src/github/handlers.ts +11 -4
- package/src/heartbeat/templates.ts +8 -5
- package/src/hooks/hook.ts +0 -26
- package/src/http/all-routes.ts +0 -1
- package/src/http/config.ts +36 -17
- package/src/http/core.ts +4 -0
- package/src/http/index.ts +14 -2
- package/src/http/integrations.ts +88 -1
- package/src/http/mcp-oauth.ts +6 -13
- package/src/http/mcp.ts +15 -5
- package/src/http/page-proxy.ts +2 -4
- package/src/http/pages-public.ts +6 -5
- package/src/http/pages.ts +5 -14
- package/src/http/poll.ts +28 -68
- package/src/http/requested-by.ts +54 -0
- package/src/http/route-def.ts +7 -6
- package/src/http/script-connection-proxy.ts +51 -4
- package/src/http/script-connections.ts +5 -2
- package/src/http/scripts.ts +1 -0
- package/src/http/session-cost-recompute.ts +12 -2
- package/src/http/session-data.ts +4 -0
- package/src/http/stats.ts +1 -30
- package/src/http/tasks.ts +61 -5
- package/src/http/utils.ts +14 -0
- package/src/http/webhooks.ts +183 -2
- package/src/http/x.ts +1 -0
- package/src/integrations/chat-sdk/adapters.ts +129 -0
- package/src/integrations/chat-sdk/attachments.ts +232 -0
- package/src/integrations/chat-sdk/background.ts +36 -0
- package/src/integrations/chat-sdk/commands.ts +194 -0
- package/src/integrations/chat-sdk/config.ts +198 -0
- package/src/integrations/chat-sdk/discord-api.ts +63 -0
- package/src/integrations/chat-sdk/discord-gateway.ts +185 -0
- package/src/integrations/chat-sdk/handlers.ts +28 -0
- package/src/integrations/chat-sdk/inbound.ts +426 -0
- package/src/integrations/chat-sdk/limits.ts +48 -0
- package/src/integrations/chat-sdk/logger.ts +81 -0
- package/src/integrations/chat-sdk/outbound.ts +123 -0
- package/src/integrations/chat-sdk/relay.ts +172 -0
- package/src/integrations/chat-sdk/router.ts +169 -0
- package/src/integrations/chat-sdk/runtime.ts +351 -0
- package/src/integrations/chat-sdk/schema.ts +245 -0
- package/src/integrations/chat-sdk/state.ts +243 -0
- package/src/integrations/chat-sdk/status.ts +244 -0
- package/src/integrations/chat-sdk/steering.ts +58 -0
- package/src/integrations/chat-sdk/teams-origin.ts +77 -0
- package/src/integrations/chat-sdk/telegram-api.ts +56 -0
- package/src/integrations/chat-sdk/templates.ts +16 -0
- package/src/integrations/chat-sdk/test-hooks.ts +37 -0
- package/src/integrations/chat-sdk/thread-buffer.ts +100 -0
- package/src/integrations/chat-sdk/typing.ts +65 -0
- package/src/jira/adf.ts +3 -1
- package/src/kv-reserved-namespaces.ts +2 -0
- package/src/oauth/mcp-wrapper.ts +10 -6
- package/src/otel-impl.ts +2 -1
- package/src/pages/version.ts +4 -5
- package/src/prompts/base-prompt.ts +111 -45
- package/src/providers/acp-target-catalog.ts +6 -0
- package/src/providers/acp-targets.ts +56 -3
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/claude-adapter.ts +27 -2
- package/src/providers/claude-flag-settings.ts +133 -0
- package/src/providers/claude-managed-adapter.ts +1 -1
- package/src/providers/claude-sdk-session.ts +7 -0
- package/src/providers/claude-session-events.ts +6 -1
- package/src/providers/dsh-adapter.ts +1 -1
- package/src/providers/opencode-adapter.ts +268 -37
- package/src/providers/opencode-billing.ts +86 -0
- package/src/providers/opencode-swarm-plugin.ts +51 -0
- package/src/providers/pi-mono-adapter.ts +398 -153
- package/src/providers/pricing-sources.md +37 -3
- package/src/providers/reasoning-effort.ts +27 -0
- package/src/providers/types.ts +11 -2
- package/src/rbac/legacy-policy.ts +8 -2
- package/src/rbac/permissions.ts +21 -0
- package/src/scheduler/scheduler.ts +4 -2
- package/src/scripts-runtime/api-client.ts +15 -1
- package/src/scripts-runtime/api-types.ts +9 -1
- package/src/scripts-runtime/ctx.ts +1 -1
- package/src/scripts-runtime/loader.ts +19 -7
- package/src/scripts-runtime/mcp-client.ts +30 -19
- package/src/scripts-runtime/sdk-allowlist.ts +8 -6
- package/src/scripts-runtime/swarm-sdk.ts +0 -11
- package/src/scripts-runtime/types/stdlib.d.ts +27 -15
- package/src/scripts-runtime/types/swarm-extension.d.ts +63 -1
- package/src/scripts-runtime/types/swarm-sdk.d.ts +27 -15
- package/src/server.ts +52 -18
- package/src/slack/actions.ts +29 -1
- package/src/slack/app.ts +14 -2
- package/src/slack/assistant.ts +36 -2
- package/src/slack/channel-join.ts +3 -16
- package/src/slack/commands.ts +9 -1
- package/src/slack/event-dedup.ts +1 -1
- package/src/slack/external-channel-egress.ts +113 -0
- package/src/slack/external-channel.ts +309 -0
- package/src/slack/handlers.ts +61 -0
- package/src/slack/render-v2.ts +40 -47
- package/src/slack/work-objects.ts +5 -0
- package/src/tasks/additive-buffer.ts +4 -2
- package/src/tasks/context-key.ts +94 -2
- package/src/tasks/model-pin.ts +67 -0
- package/src/tasks/worker-follow-up.ts +4 -31
- package/src/telemetry.ts +2 -1
- package/src/tests/acp-adapter.test.ts +42 -0
- package/src/tests/api-drain-worker-e2e.test.ts +1 -6
- package/src/tests/apps-catalog-feedback.test.ts +310 -0
- package/src/tests/apps-catalog-inputs.test.ts +70 -0
- package/src/tests/apps-catalog-table.test.ts +113 -0
- package/src/tests/azure-devops-boards-sync.test.ts +584 -0
- package/src/tests/azure-devops-html.test.ts +13 -0
- package/src/tests/azure-devops-webhook-malformed.test.ts +4 -0
- package/src/tests/base-prompt.test.ts +58 -54
- package/src/tests/chat-sdk-commands.test.ts +191 -0
- package/src/tests/chat-sdk-config.test.ts +334 -0
- package/src/tests/chat-sdk-discord-api.test.ts +68 -0
- package/src/tests/chat-sdk-discord-gateway.test.ts +252 -0
- package/src/tests/chat-sdk-discord-webhook.test.ts +126 -0
- package/src/tests/chat-sdk-fields-sync.test.ts +50 -0
- package/src/tests/chat-sdk-harness.ts +146 -0
- package/src/tests/chat-sdk-inbound.test.ts +363 -0
- package/src/tests/chat-sdk-relay.test.ts +470 -0
- package/src/tests/chat-sdk-reload.test.ts +128 -0
- package/src/tests/chat-sdk-shutdown.test.ts +220 -0
- package/src/tests/chat-sdk-slack.test.ts +268 -0
- package/src/tests/chat-sdk-state.test.ts +163 -0
- package/src/tests/chat-sdk-status-route.test.ts +382 -0
- package/src/tests/chat-sdk-teams.test.ts +277 -0
- package/src/tests/chat-sdk-tools.test.ts +520 -0
- package/src/tests/chat-sdk-typing.test.ts +120 -0
- package/src/tests/chat-sdk-webhook-telegram.test.ts +200 -0
- package/src/tests/claude-adapter-binary.test.ts +144 -0
- package/src/tests/claude-flag-settings.test.ts +114 -0
- package/src/tests/claude-managed-adapter.test.ts +1 -1
- package/src/tests/claude-sdk-lifecycle.test.ts +148 -1
- package/src/tests/claude-sdk-process.fixture.ts +7 -0
- package/src/tests/claude-thinking-tokens.test.ts +57 -0
- package/src/tests/claude-worker-parity.test.ts +2 -7
- package/src/tests/config-env-export-chat-sdk.test.ts +11 -0
- package/src/tests/config-refs.test.ts +66 -0
- package/src/tests/context-key.test.ts +51 -0
- package/src/tests/cors-allowlist.test.ts +25 -0
- package/src/tests/create-page-tool.test.ts +18 -0
- package/src/tests/credential-check.test.ts +139 -0
- package/src/tests/credentials.test.ts +21 -0
- package/src/tests/db-runtime.test.ts +61 -0
- package/src/tests/db-task-read-contract.test.ts +20 -0
- package/src/tests/db-task-write-contract.test.ts +10 -0
- package/src/tests/deferred-task-wake.test.ts +52 -4
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/dsh-adapter.test.ts +1 -1
- package/src/tests/enabled-capabilities.test.ts +20 -0
- package/src/tests/entrypoint-api-readiness.test.ts +14 -8
- package/src/tests/entrypoint-config-env-export.test.ts +5 -2
- package/src/tests/entrypoint-export-to-env.test.ts +78 -0
- package/src/tests/explicit-model-validation.test.ts +105 -1
- package/src/tests/extensions-example-route-chat-channel.test.ts +154 -0
- package/src/tests/extensions-post-bridge.test.ts +51 -0
- package/src/tests/fetch-resolved-env.test.ts +128 -0
- package/src/tests/fixtures/azure-boards/workitem.commented.json +89 -0
- package/src/tests/fixtures/azure-boards/workitem.created.json +81 -0
- package/src/tests/fixtures/azure-boards/workitem.deleted.json +70 -0
- package/src/tests/fixtures/azure-boards/workitem.restored.json +82 -0
- package/src/tests/fixtures/azure-boards/workitem.updated.json +130 -0
- package/src/tests/fixtures/catalog-feedback-definition.json.txt +860 -0
- package/src/tests/fixtures/catalog-inputs-definition.json.txt +910 -0
- package/src/tests/fixtures/catalog-table-definition.json.txt +468 -0
- package/src/tests/fixtures/chat-sdk/telegram-private-text.json +10 -0
- package/src/tests/fixtures/extensions/route-chat-channel-to-agent/hooks.ts +27 -0
- package/src/tests/fixtures/extensions/route-chat-channel-to-agent/manifest.json +9 -0
- package/src/tests/fixtures/openai-compatible-registry-extension.child.ts +146 -0
- package/src/tests/fixtures/runner-identity-refresh.ts +3 -0
- package/src/tests/fixtures/runner-legacy-resume-prompt.ts +122 -0
- package/src/tests/github-handlers.test.ts +35 -0
- package/src/tests/harness-provider-resolution.test.ts +12 -0
- package/src/tests/heartbeat-checklist.test.ts +3 -2
- package/src/tests/hook-runtime-close.test.ts +0 -2
- package/src/tests/http-api-integration.test.ts +0 -25
- package/src/tests/migration-046-budgets.test.ts +17 -16
- package/src/tests/migration-217-chat-sdk-state.test.ts +113 -0
- package/src/tests/model-tier-resolution.test.ts +93 -0
- package/src/tests/models-catalog.test.ts +42 -1
- package/src/tests/node-env-runtime.test.ts +98 -0
- package/src/tests/openai-compatible-providers.test.ts +134 -0
- package/src/tests/openai-compatible-registry-extension.test.ts +42 -0
- package/src/tests/opencode-adapter.test.ts +348 -3
- package/src/tests/opencode-billing.test.ts +28 -0
- package/src/tests/opencode-helmcode-sdk.test.ts +201 -0
- package/src/tests/opencode-shell-env.test.ts +145 -0
- package/src/tests/openrouter-base-url.test.ts +101 -151
- package/src/tests/page-proxy.test.ts +1 -1
- package/src/tests/pages-browser-sdk.test.ts +76 -0
- package/src/tests/pages-feedback.test.ts +1 -1
- package/src/tests/paused-tasks-requested-by.test.ts +102 -0
- package/src/tests/pi-custom-provider-fake-server.test.ts +442 -0
- package/src/tests/pi-mono-adapter.test.ts +385 -5
- package/src/tests/pi-shell-env.test.ts +170 -0
- package/src/tests/prompt-template-remaining.test.ts +35 -0
- package/src/tests/rbac-engine.test.ts +11 -3
- package/src/tests/runner-exit-after-tasks.test.ts +0 -4
- package/src/tests/runner-identity-refresh.test.ts +3 -0
- package/src/tests/runner-legacy-resume-prompt.test.ts +64 -0
- package/src/tests/script-connections-sql.test.ts +675 -0
- package/src/tests/script-connections.test.ts +7 -1
- package/src/tests/scripts-http.test.ts +201 -1
- package/src/tests/scripts-only-gating.test.ts +2 -3
- package/src/tests/scripts-runtime.test.ts +58 -40
- package/src/tests/secret-scrubber.test.ts +86 -0
- package/src/tests/seed-skills-bundled-files.test.ts +45 -53
- package/src/tests/seed-skills-retire.test.ts +182 -0
- package/src/tests/send-task-requester-gate.test.ts +58 -0
- package/src/tests/session-cost-recompute-unit.test.ts +94 -0
- package/src/tests/skill-get-name-resolution.test.ts +137 -0
- package/src/tests/slack-channel-join.test.ts +4 -1
- package/src/tests/slack-external-channel-config.test.ts +65 -0
- package/src/tests/slack-external-channel-egress.test.ts +237 -0
- package/src/tests/slack-external-channel-guard.test.ts +280 -0
- package/src/tests/slack-external-channel-tools.test.ts +173 -0
- package/src/tests/slack-external-channel.test.ts +370 -0
- package/src/tests/slack-render-v2.test.ts +86 -10
- package/src/tests/sonnet-5-5-registry.test.ts +9 -1
- package/src/tests/spec-base-url.test.ts +16 -1
- package/src/tests/swarm-config-export-to-env.test.ts +267 -0
- package/src/tests/swarm-config-reserved-keys.test.ts +16 -0
- package/src/tests/swarm-diff.test.ts +1 -1
- package/src/tests/task-model-effort-rerun.test.ts +164 -0
- package/src/tests/task-session-prompts.test.ts +132 -0
- package/src/tests/task-tool-manifest.test.ts +14 -0
- package/src/tests/task-tools-ctx.test.ts +2 -0
- package/src/tests/tool-annotations.test.ts +6 -6
- package/src/tests/ui-logs-parser.test.ts +43 -0
- package/src/tests/used-by-route.test.ts +6 -0
- package/src/tests/vendored-openapi.test.ts +16 -1
- package/src/tests/vercel-ai-gateway.test.ts +149 -0
- package/src/tests/workflow-swarm-script.test.ts +38 -0
- package/src/tests/workflow-wait-filter.test.ts +5 -3
- package/src/tools/chat-access.ts +41 -0
- package/src/tools/chat-open-dm.ts +59 -0
- package/src/tools/chat-post.ts +74 -0
- package/src/tools/chat-read-thread.ts +100 -0
- package/src/tools/chat-reply.ts +75 -0
- package/src/tools/create-page.ts +3 -13
- package/src/tools/defer-task.ts +17 -0
- package/src/tools/model-catalog/index.ts +2 -1
- package/src/tools/register-chat-channel.ts +139 -0
- package/src/tools/send-task.ts +24 -0
- package/src/tools/skills/skill-get.ts +17 -9
- package/src/tools/slack-post.ts +6 -0
- package/src/tools/slack-read.ts +8 -0
- package/src/tools/slack-reply.ts +6 -0
- package/src/tools/slack-start-thread.ts +6 -0
- package/src/tools/slack-update.ts +6 -0
- package/src/tools/slack-upload-file.ts +6 -0
- package/src/tools/swarm-config/get-config.ts +2 -1
- package/src/tools/swarm-config/list-config.ts +2 -1
- package/src/tools/swarm-config/set-config.ts +16 -3
- package/src/tools/tool-config.ts +8 -6
- package/src/types.ts +12 -2
- package/src/utils/config-env-export.ts +74 -0
- package/src/utils/credentials.test.ts +92 -0
- package/src/utils/credentials.ts +52 -4
- package/src/utils/harness-provider.ts +15 -6
- package/src/utils/node-env.ts +24 -0
- package/src/utils/runtime-model-catalog.ts +11 -1
- package/src/utils/secret-scrubber.ts +71 -2
- package/src/utils/task-tool-manifest.ts +19 -2
- package/src/workflows/executors/swarm-script.ts +1 -0
- package/templates/skills/agent-browser/SKILL.md +1 -1
- package/templates/skills/agent-browser/content.md +1 -1
- package/templates/skills/apps/SKILL.md +749 -0
- package/templates/skills/apps/content.md +60 -22
- package/templates/skills/chat-sdk/config.json +14 -0
- package/templates/skills/chat-sdk/content.md +84 -0
- package/templates/skills/composio-gmail/content.md +7 -0
- package/templates/skills/composio-google-docs/content.md +1 -1
- package/templates/skills/email-domain-health/config.json +14 -0
- package/templates/skills/email-domain-health/content.md +131 -0
- package/templates/skills/kv-storage/content.md +2 -2
- package/templates/skills/pages/content.md +8 -10
- package/templates/skills/qa-loom/config.json +14 -0
- package/templates/skills/qa-loom/content.md +187 -0
- package/templates/skills/qa-loom/files/build-loom.mjs +221 -0
- package/templates/skills/qa-loom/files/run-qa.sh +57 -0
- package/templates/skills/slack-interaction/content.md +4 -0
- package/templates/skills/swarm-extensions/content.md +2 -0
- package/templates/skills/swarm-scripts/SKILL.md +4 -2
- package/templates/skills/swarm-scripts/config.json +1 -1
- package/templates/skills/swarm-scripts/content.md +4 -2
- package/templates/skills/workflow-iterate/content.md +38 -0
- package/dist/artifact-eczsknc3.js +0 -3843
- package/dist/cli-9xsmdpdt.js +0 -56377
- package/dist/cli-bd957dgb.js +0 -153
- package/dist/cli-q51yprfq.js +0 -1104
- package/dist/cli-t1ze64h3.js +0 -811
- package/dist/index-9ch1n761.js +0 -1102
- package/src/artifact-sdk/index.ts +0 -2
- package/src/artifact-sdk/localtunnel.d.ts +0 -20
- package/src/artifact-sdk/port.ts +0 -12
- package/src/artifact-sdk/server.ts +0 -183
- package/src/artifact-sdk/tunnel.ts +0 -19
- package/src/commands/artifact.ts +0 -249
- package/src/http/ecosystem.ts +0 -77
- package/src/tests/artifact-commands.test.ts +0 -92
- package/src/tests/artifact-sdk.test.ts +0 -793
- package/src/tests/register-service-security.test.ts +0 -194
- package/src/tools/list-services.ts +0 -107
- package/src/tools/register-service.ts +0 -177
- package/src/tools/unregister-service.ts +0 -73
- package/src/tools/update-service-status.ts +0 -96
- package/templates/skills/artifacts/config.json +0 -14
- package/templates/skills/artifacts/content.md +0 -344
- package/templates/skills/artifacts/files/examples/approval-flow.ts +0 -34
- package/templates/skills/artifacts/files/examples/hono-dashboard.ts +0 -31
- package/templates/skills/artifacts/files/examples/multi-artifact.ts +0 -20
- package/templates/skills/artifacts/files/examples/static-report.sh +0 -17
- package/dist/{azure-openai-responses-ap1gwxgr.js → azure-openai-responses-ygfreq4t.js} +5 -5
- package/dist/{cli-5rqrj7q0.js → cli-pw7ebjdt.js} +0 -0
- package/dist/{openai-codex-responses-33qhkkdy.js → openai-codex-responses-3bk0grnr.js} +4 -4
- package/dist/{openai-completions-s88xm3ns.js → openai-completions-5d4jpakc.js} +9 -9
- package/dist/{openai-responses-7xd2f2e2.js → openai-responses-krb51f59.js} +5 -5
- package/dist/{pi-messages-xszk613n.js → pi-messages-x9gzynm1.js} +3 -3
- /package/src/{artifact-sdk → pages}/browser-sdk.ts +0 -0
- /package/src/{artifact-sdk → pages}/feedback-overlay.ts +0 -0
|
@@ -16,10 +16,11 @@ import {
|
|
|
16
16
|
} from "@/scripts-runtime/credential-broker";
|
|
17
17
|
import { refreshSecretScrubberCache } from "@/utils/secret-scrubber";
|
|
18
18
|
import { listMcpServerTools } from "./mcp-proxy";
|
|
19
|
+
import { assertPostgresDsn } from "./sql-connections";
|
|
19
20
|
import { readVendoredOpenapiSpec } from "./vendored-openapi";
|
|
20
21
|
|
|
21
22
|
export type ScriptConnectionScope = "global" | "agent" | "repo";
|
|
22
|
-
export type ScriptConnectionKind = "raw" | "openapi" | "mcp" | "graphql";
|
|
23
|
+
export type ScriptConnectionKind = "raw" | "openapi" | "mcp" | "graphql" | "sql";
|
|
23
24
|
export type ScriptConnectionBaseUrlSource = "user" | "spec";
|
|
24
25
|
export type ScriptConnectionAuthType = "none" | "bearer" | "header" | "query" | "oauth";
|
|
25
26
|
|
|
@@ -109,6 +110,13 @@ export const ConnectionAuthInputSchema = z.discriminatedUnion("type", [
|
|
|
109
110
|
}),
|
|
110
111
|
]);
|
|
111
112
|
|
|
113
|
+
/** Postgres DSN for a `sql` connection: an inline secret or an existing swarm_config key. */
|
|
114
|
+
export const SqlDsnInputSchema = z
|
|
115
|
+
.object({ secret: authSecretSchema, configKey: authConfigKeySchema })
|
|
116
|
+
.refine((dsn) => (dsn.secret === undefined) !== (dsn.configKey === undefined), {
|
|
117
|
+
message: "Provide exactly one of `secret` or `configKey` for the SQL DSN.",
|
|
118
|
+
});
|
|
119
|
+
|
|
112
120
|
export type ScriptConnectionBaseUrlMismatch = {
|
|
113
121
|
specUrl: string;
|
|
114
122
|
effectiveUrl: string;
|
|
@@ -1370,7 +1378,9 @@ function assertConfigKeyNotReserved(auth: ConnectionAuthInput | undefined): void
|
|
|
1370
1378
|
*/
|
|
1371
1379
|
function derivedInlineSecretKey(connection: ScriptConnectionRecord | null): string | null {
|
|
1372
1380
|
if (!connection) return null;
|
|
1373
|
-
|
|
1381
|
+
// sql rows keep auth_type 'none' but own their DSN key in auth_config_key.
|
|
1382
|
+
if (connection.authType === "oauth") return null;
|
|
1383
|
+
if (connection.authType === "none" && connection.kind !== "sql") return null;
|
|
1374
1384
|
const key = connection.authConfigKey;
|
|
1375
1385
|
return key && DERIVED_INLINE_SECRET_KEY.test(key) ? key : null;
|
|
1376
1386
|
}
|
|
@@ -1565,6 +1575,7 @@ export async function upsertScriptConnection(data: {
|
|
|
1565
1575
|
openapiSpecEtag?: string | null;
|
|
1566
1576
|
openapiSpecFetchedAt?: string | null;
|
|
1567
1577
|
mcpServerId?: string | null;
|
|
1578
|
+
sqlDsn?: z.infer<typeof SqlDsnInputSchema>;
|
|
1568
1579
|
enabled?: boolean;
|
|
1569
1580
|
agentId?: string;
|
|
1570
1581
|
userId?: string | null;
|
|
@@ -1608,6 +1619,9 @@ export async function upsertScriptConnection(data: {
|
|
|
1608
1619
|
"MCP connections resolve auth through their MCP server; `auth` is not supported.",
|
|
1609
1620
|
);
|
|
1610
1621
|
}
|
|
1622
|
+
if (data.kind === "sql" && data.auth && data.auth.type !== "none") {
|
|
1623
|
+
throw new Error("SQL connections authenticate through their DSN; `auth` is not supported.");
|
|
1624
|
+
}
|
|
1611
1625
|
// Keep the reserved derived-credential namespaces exclusive so a user-supplied
|
|
1612
1626
|
// configKey can never collide with (or later be mistaken for) a connection's
|
|
1613
1627
|
// own derived inline secret. Checked on user input only — reconstruction of an
|
|
@@ -1638,7 +1652,7 @@ export async function upsertScriptConnection(data: {
|
|
|
1638
1652
|
// binding, e.g. raw-fetch egress) with no `auth`.
|
|
1639
1653
|
const explicitLegacyBinding = data.auth === undefined && data.credentialBindingId !== undefined;
|
|
1640
1654
|
const authIntent: ConnectionAuthInput =
|
|
1641
|
-
data.kind === "mcp" || explicitLegacyBinding
|
|
1655
|
+
data.kind === "mcp" || data.kind === "sql" || explicitLegacyBinding
|
|
1642
1656
|
? { type: "none" }
|
|
1643
1657
|
: (data.auth ?? reconstructAuthFromConnection(existingConnection));
|
|
1644
1658
|
let derived: DerivedConnectionBinding | null = null;
|
|
@@ -1795,6 +1809,26 @@ export async function upsertScriptConnection(data: {
|
|
|
1795
1809
|
generatedAt = now;
|
|
1796
1810
|
}
|
|
1797
1811
|
|
|
1812
|
+
// The DSN key rides auth_config_key; the DSN itself never enters these artifacts.
|
|
1813
|
+
let sqlDsnKey: string | null = null;
|
|
1814
|
+
let sqlSecretWrite: DerivedConnectionBinding["secretWrite"] = null;
|
|
1815
|
+
if (data.kind === "sql") {
|
|
1816
|
+
if (data.sqlDsn?.secret !== undefined) {
|
|
1817
|
+
assertPostgresDsn(data.sqlDsn.secret);
|
|
1818
|
+
sqlDsnKey = `connection.${normalizedSlug}.secret`;
|
|
1819
|
+
sqlSecretWrite = { scope, scopeId, key: sqlDsnKey, value: data.sqlDsn.secret };
|
|
1820
|
+
} else if (data.sqlDsn?.configKey) {
|
|
1821
|
+
assertConfigKeyNotReserved({ type: "bearer", configKey: data.sqlDsn.configKey });
|
|
1822
|
+
sqlDsnKey = data.sqlDsn.configKey;
|
|
1823
|
+
} else {
|
|
1824
|
+
sqlDsnKey = existingConnection?.kind === "sql" ? existingConnection.authConfigKey : null;
|
|
1825
|
+
}
|
|
1826
|
+
if (!sqlDsnKey) throw new Error("SQL connections require `dsn.secret` or `dsn.configKey`.");
|
|
1827
|
+
generatedRuntimeJson = JSON.stringify({ slug: normalizedSlug, kind: "sql", connectionId });
|
|
1828
|
+
generatedTypes = `export interface ${pascal(normalizedSlug)}Api {\n /** One read-only statement; pass values via params ($1, $2, ...). */\n query<T = Record<string, JsonValue>>(sql: string, params?: JsonValue[]): Promise<{ rows: T[]; rowCount: number; truncated: boolean }>;\n}`;
|
|
1829
|
+
generatedAt = now;
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1798
1832
|
if (data.kind !== "graphql") {
|
|
1799
1833
|
allowedHosts =
|
|
1800
1834
|
data.allowedHosts ?? (effectiveBaseUrl ? [new URL(effectiveBaseUrl).hostname] : []);
|
|
@@ -1808,7 +1842,8 @@ export async function upsertScriptConnection(data: {
|
|
|
1808
1842
|
// metadata-only rename reconstructs the old derived key as an explicit
|
|
1809
1843
|
// configKey and keeps referencing it, so it must NOT be deleted.
|
|
1810
1844
|
const oldDerivedSecretKey = derivedInlineSecretKey(existingConnection);
|
|
1811
|
-
const newReferencedConfigKey =
|
|
1845
|
+
const newReferencedConfigKey =
|
|
1846
|
+
derived?.configKey ?? explicitBinding?.configKey ?? sqlDsnKey ?? null;
|
|
1812
1847
|
let supersededSecretDeleted = false;
|
|
1813
1848
|
|
|
1814
1849
|
// Connection row and its auto-managed binding form a reference cycle
|
|
@@ -1822,6 +1857,7 @@ export async function upsertScriptConnection(data: {
|
|
|
1822
1857
|
if (derived?.secretWrite) {
|
|
1823
1858
|
await upsertSwarmConfig({ ...derived.secretWrite, isSecret: true });
|
|
1824
1859
|
}
|
|
1860
|
+
if (sqlSecretWrite) await upsertSwarmConfig({ ...sqlSecretWrite, isSecret: true });
|
|
1825
1861
|
|
|
1826
1862
|
// Drop a superseded derived inline secret (see oldDerivedSecretKey above).
|
|
1827
1863
|
// Skip when the connection's binding still references that same key (same
|
|
@@ -1874,6 +1910,7 @@ export async function upsertScriptConnection(data: {
|
|
|
1874
1910
|
: (existingConnection?.credentialBindingId ?? null);
|
|
1875
1911
|
authCols = NONE_AUTH_COLUMNS;
|
|
1876
1912
|
}
|
|
1913
|
+
if (sqlDsnKey) authCols = { ...NONE_AUTH_COLUMNS, authConfigKey: sqlDsnKey };
|
|
1877
1914
|
|
|
1878
1915
|
const params = [
|
|
1879
1916
|
normalizedSlug,
|
|
@@ -1951,7 +1988,9 @@ export async function upsertScriptConnection(data: {
|
|
|
1951
1988
|
});
|
|
1952
1989
|
|
|
1953
1990
|
const row = await writeConnectionAndBinding();
|
|
1954
|
-
if (derived?.secretWrite || supersededSecretDeleted)
|
|
1991
|
+
if (derived?.secretWrite || sqlSecretWrite || supersededSecretDeleted) {
|
|
1992
|
+
refreshSecretScrubberCache();
|
|
1993
|
+
}
|
|
1955
1994
|
return connectionFromRow(row);
|
|
1956
1995
|
}
|
|
1957
1996
|
|
|
@@ -2117,7 +2156,7 @@ export function getScriptApiConnectionDescriptors(
|
|
|
2117
2156
|
context: { agentId?: string; repoId?: string } = {},
|
|
2118
2157
|
): ScriptApiConnectionDescriptor[] {
|
|
2119
2158
|
return listScriptConnections(context)
|
|
2120
|
-
.filter((connection) =>
|
|
2159
|
+
.filter((connection) => ["openapi", "graphql", "sql"].includes(connection.kind))
|
|
2121
2160
|
.map((connection) => {
|
|
2122
2161
|
if (!connection.generatedRuntimeJson) return null;
|
|
2123
2162
|
try {
|
|
@@ -2145,8 +2184,8 @@ export function getScriptMcpConnectionDescriptors(
|
|
|
2145
2184
|
}
|
|
2146
2185
|
|
|
2147
2186
|
export function getScriptApiTypes(context: { agentId?: string; repoId?: string } = {}): string {
|
|
2148
|
-
const connections = listScriptConnections(context).filter(
|
|
2149
|
-
|
|
2187
|
+
const connections = listScriptConnections(context).filter((connection) =>
|
|
2188
|
+
["openapi", "graphql", "sql"].includes(connection.kind),
|
|
2150
2189
|
);
|
|
2151
2190
|
const blocks = connections
|
|
2152
2191
|
.filter((connection) => connection.generatedTypes)
|
|
@@ -11,6 +11,7 @@ import { registerVolatileSecret } from "@/utils/secret-scrubber";
|
|
|
11
11
|
import { getResolvedConfig } from "./db";
|
|
12
12
|
import { resolveOAuthBindingToken } from "./oauth-credential-bindings";
|
|
13
13
|
import { listRelationalCredentialBindings } from "./script-connections";
|
|
14
|
+
import { isApiOnlyConfigKey, isInternalConfigKey } from "./swarm-config-guard";
|
|
14
15
|
|
|
15
16
|
// Relational-only credential binding store. The legacy SCRIPT_CREDENTIAL_BINDINGS
|
|
16
17
|
// swarm-config JSON blob is retired (migrated to relational rows at boot), so
|
|
@@ -22,6 +23,32 @@ class RelationalCredentialBindingStore implements CredentialBindingStore {
|
|
|
22
23
|
}
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
export type ScriptUserConfig = Record<string, { value: string; isSecret: boolean }>;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The values behind `ctx.swarm.config.get(KEY)`: the invoking agent's resolved
|
|
30
|
+
* config (agent over global, plus repo when given), minus the keys the
|
|
31
|
+
* `/api/config/resolved` route never hands out. A script's own SDK bearer
|
|
32
|
+
* already reads every one of these through `ctx.swarm.config_get`, so this
|
|
33
|
+
* widens nothing; it only makes the documented accessor return them. Secret
|
|
34
|
+
* values are registered with the scrubber so they never leave in run output.
|
|
35
|
+
*/
|
|
36
|
+
function toScriptUserConfig(configs: Awaited<ReturnType<typeof getResolvedConfig>>) {
|
|
37
|
+
const exposed = configs.filter(
|
|
38
|
+
(config) => !isApiOnlyConfigKey(config.key) && !isInternalConfigKey(config.key),
|
|
39
|
+
);
|
|
40
|
+
for (const config of exposed) {
|
|
41
|
+
if (config.isSecret && config.value) {
|
|
42
|
+
registerVolatileSecret(config.value, `config:${config.key}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// fromEntries defines own properties, so a `__proto__` key survives
|
|
46
|
+
// (assigning it into `{}` would hit the prototype setter instead).
|
|
47
|
+
return Object.fromEntries(
|
|
48
|
+
exposed.map((config) => [config.key, { value: config.value, isSecret: config.isSecret }]),
|
|
49
|
+
) as ScriptUserConfig;
|
|
50
|
+
}
|
|
51
|
+
|
|
25
52
|
/**
|
|
26
53
|
* Resolve credential bindings for a script run, partitioning OAuth bindings
|
|
27
54
|
* whose refresh failed into `failedBindings` (surfaced to the sandbox so the
|
|
@@ -32,8 +59,14 @@ class RelationalCredentialBindingStore implements CredentialBindingStore {
|
|
|
32
59
|
export async function buildScriptCredentialBindingsWithFailures(input: {
|
|
33
60
|
agentId?: string;
|
|
34
61
|
repoId?: string;
|
|
35
|
-
}): Promise<{
|
|
62
|
+
}): Promise<{
|
|
63
|
+
egressSecrets: EgressSecretEntry[];
|
|
64
|
+
failedBindings: FailedCredentialBinding[];
|
|
65
|
+
userConfig: ScriptUserConfig;
|
|
66
|
+
}> {
|
|
36
67
|
const resolvedConfigs = await getResolvedConfig(input.agentId, input.repoId);
|
|
68
|
+
// Before the binding loop below, so a binding's own scrubber label wins.
|
|
69
|
+
const userConfig = toScriptUserConfig(resolvedConfigs);
|
|
37
70
|
const configMap = new Map(resolvedConfigs.map((config) => [config.key, config.value]));
|
|
38
71
|
const broker = new CredentialBroker(
|
|
39
72
|
new RelationalCredentialBindingStore(),
|
|
@@ -49,7 +82,11 @@ export async function buildScriptCredentialBindingsWithFailures(input: {
|
|
|
49
82
|
for (const binding of resolved) {
|
|
50
83
|
registerVolatileSecret(binding.value, binding.configKey);
|
|
51
84
|
}
|
|
52
|
-
return {
|
|
85
|
+
return {
|
|
86
|
+
egressSecrets: resolved,
|
|
87
|
+
failedBindings: failed,
|
|
88
|
+
userConfig,
|
|
89
|
+
};
|
|
53
90
|
}
|
|
54
91
|
|
|
55
92
|
export async function buildScriptCredentialBindings(input: {
|
|
@@ -52,6 +52,7 @@ export async function runSavedScriptAsAgent(
|
|
|
52
52
|
agentId: args.agentId,
|
|
53
53
|
egressSecrets: credentials.egressSecrets,
|
|
54
54
|
failedBindings: credentials.failedBindings,
|
|
55
|
+
userConfig: credentials.userConfig,
|
|
55
56
|
apiConnections: getScriptApiConnectionDescriptors({ agentId: args.agentId }),
|
|
56
57
|
mcpConnections: getScriptMcpConnectionDescriptors({ agentId: args.agentId }),
|
|
57
58
|
...(args.timeoutMs ? { timeoutMs: args.timeoutMs } : {}),
|
|
@@ -235,10 +235,10 @@ export interface SwarmSdk {
|
|
|
235
235
|
// --- slack ---
|
|
236
236
|
slack_read(args?: { inboxMessageId?: string; taskId?: string; channelId?: string; threadTs?: string; limit?: number; includeFiles?: boolean }): Promise<unknown>;
|
|
237
237
|
slack_listChannels(args?: { types?: Array<"public" | "private" | "dm" | "mpim">; limit?: number }): Promise<unknown>;
|
|
238
|
+
// --- chat sdk ---
|
|
239
|
+
chat_readThread(args: { threadId: string; limit?: number }): Promise<unknown>;
|
|
238
240
|
// --- messaging ---
|
|
239
241
|
message_read(args?: { channel?: string; limit?: number; since?: string; unreadOnly?: boolean; mentionsOnly?: boolean; markAsRead?: boolean }): Promise<unknown>;
|
|
240
|
-
// --- services ---
|
|
241
|
-
service_list(args?: { agentId?: string; name?: string; status?: "starting" | "healthy" | "unhealthy" | "stopped"; includeOwn?: boolean }): Promise<unknown>;
|
|
242
242
|
// --- context / profiles ---
|
|
243
243
|
context_history(args?: { agentId?: string; field?: "soulMd" | "identityMd" | "toolsMd" | "claudeMd" | "setupScript"; limit?: number }): Promise<unknown>;
|
|
244
244
|
context_diff(args: { versionId: string; compareToVersionId?: string }): Promise<unknown>;
|
|
@@ -281,7 +281,7 @@ export interface SwarmSdk {
|
|
|
281
281
|
task_action(args: Record<string, unknown>): Promise<unknown>;
|
|
282
282
|
|
|
283
283
|
// --- write: config ---
|
|
284
|
-
config_set(args: { key: string; value: unknown; scope?: "global" | "agent" | "repo"; scopeId?: string; isSecret?: boolean }): Promise<unknown>;
|
|
284
|
+
config_set(args: { key: string; value: unknown; scope?: "global" | "agent" | "repo"; scopeId?: string; isSecret?: boolean; exportToEnv?: boolean }): Promise<unknown>;
|
|
285
285
|
config_delete(args: { id: string }): Promise<unknown>;
|
|
286
286
|
|
|
287
287
|
// --- write: model catalog ---
|
|
@@ -311,17 +311,19 @@ export interface SwarmSdk {
|
|
|
311
311
|
slack_delete(args: { channelId: string; messageTs: string }): Promise<unknown>;
|
|
312
312
|
slack_update(args: { channelId: string; messageTs: string; message: string }): Promise<unknown>;
|
|
313
313
|
|
|
314
|
+
// --- write: chat sdk ---
|
|
315
|
+
chat_reply(args: { text: string }): Promise<unknown>;
|
|
316
|
+
chat_post(args: { adapter: "telegram" | "slack" | "discord" | "teams"; threadId?: string; channelId?: string; text: string }): Promise<unknown>;
|
|
317
|
+
chat_openDm(args: { adapter: "telegram" | "slack" | "discord" | "teams"; userId: string }): Promise<unknown>;
|
|
318
|
+
chat_registerChannel(args: { adapter: "telegram" | "slack" | "discord" | "teams"; channelId: string; agentId?: string; workflowId?: string; name?: string }): Promise<unknown>;
|
|
319
|
+
chat_unregisterChannel(args: { adapter: "telegram" | "slack" | "discord" | "teams"; channelId: string }): Promise<unknown>;
|
|
320
|
+
|
|
314
321
|
// --- write: messaging (internal) ---
|
|
315
322
|
message_post(args: { channel?: string; content: string; to?: string }): Promise<unknown>;
|
|
316
323
|
|
|
317
324
|
// --- write: profiles ---
|
|
318
325
|
profile_update(args: Record<string, unknown>): Promise<unknown>;
|
|
319
326
|
|
|
320
|
-
// --- write: services ---
|
|
321
|
-
service_register(args: Record<string, unknown>): Promise<unknown>;
|
|
322
|
-
service_unregister(args: { name: string }): Promise<unknown>;
|
|
323
|
-
service_updateStatus(args: { name: string; status: "starting" | "healthy" | "unhealthy" | "stopped" }): Promise<unknown>;
|
|
324
|
-
|
|
325
327
|
// --- write: schedules ---
|
|
326
328
|
schedule_create(args: Record<string, unknown>): Promise<unknown>;
|
|
327
329
|
schedule_update(args: Record<string, unknown>): Promise<unknown>;
|
package/src/be/seed/runner.ts
CHANGED
|
@@ -22,6 +22,7 @@ export async function runSeeder(seeder: Seeder, opts?: SeederRunOptions): Promis
|
|
|
22
22
|
updated: 0,
|
|
23
23
|
skippedUnchanged: 0,
|
|
24
24
|
skippedUserModified: 0,
|
|
25
|
+
retired: 0,
|
|
25
26
|
failed: [],
|
|
26
27
|
};
|
|
27
28
|
|
|
@@ -77,11 +78,22 @@ export async function runSeeder(seeder: Seeder, opts?: SeederRunOptions): Promis
|
|
|
77
78
|
}
|
|
78
79
|
}
|
|
79
80
|
|
|
81
|
+
if (seeder.retire) {
|
|
82
|
+
try {
|
|
83
|
+
result.retired = (await seeder.retire()).length;
|
|
84
|
+
} catch (err) {
|
|
85
|
+
result.failed.push({
|
|
86
|
+
key: "(retire)",
|
|
87
|
+
error: err instanceof Error ? err.message : String(err),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
80
92
|
if (!opts?.quiet) {
|
|
81
93
|
console.log(
|
|
82
94
|
`[seed:${result.kind}] created=${result.created} updated=${result.updated} ` +
|
|
83
95
|
`unchanged=${result.skippedUnchanged} preserved=${result.skippedUserModified} ` +
|
|
84
|
-
`failed=${result.failed.length}`,
|
|
96
|
+
`retired=${result.retired} failed=${result.failed.length}`,
|
|
85
97
|
);
|
|
86
98
|
for (const f of result.failed) {
|
|
87
99
|
console.error(`[seed:${result.kind}] FAILED ${f.key}: ${f.error}`);
|
package/src/be/seed/state-db.ts
CHANGED
|
@@ -36,3 +36,8 @@ export async function recordSeedState(
|
|
|
36
36
|
[kind, key, seededHash, new Date().toISOString()],
|
|
37
37
|
);
|
|
38
38
|
}
|
|
39
|
+
|
|
40
|
+
/** Forget `(kind, key)`, e.g. after the seeder retires the entity it seeded. */
|
|
41
|
+
export async function deleteSeedState(kind: string, key: string): Promise<void> {
|
|
42
|
+
await getDbClient().run("DELETE FROM seed_state WHERE kind = ? AND key = ?", [kind, key]);
|
|
43
|
+
}
|
package/src/be/seed/types.ts
CHANGED
|
@@ -53,6 +53,11 @@ export interface Seeder<TItem extends SeedItem = SeedItem> {
|
|
|
53
53
|
upstreamHash(item: TItem): string | null | Promise<string | null>;
|
|
54
54
|
/** Create or update the upstream entity so it matches the source definition. */
|
|
55
55
|
apply(item: TItem, action: "create" | "update", opts?: SeederRunOptions): void | Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Optional: remove upstream entities for keys the source no longer ships.
|
|
58
|
+
* Must leave user-modified entities in place. Returns the removed keys.
|
|
59
|
+
*/
|
|
60
|
+
retire?(): Promise<string[]>;
|
|
56
61
|
}
|
|
57
62
|
|
|
58
63
|
export type SeederResult = {
|
|
@@ -61,5 +66,6 @@ export type SeederResult = {
|
|
|
61
66
|
updated: number;
|
|
62
67
|
skippedUnchanged: number;
|
|
63
68
|
skippedUserModified: number;
|
|
69
|
+
retired: number;
|
|
64
70
|
failed: Array<{ key: string; error: string }>;
|
|
65
71
|
};
|
package/src/be/seed-pricing.ts
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
* admin route (`POST /api/pricing`) — we don't overwrite seed rows.
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
+
import { openAICompatibleProviderIds } from "@desplega/model-catalog";
|
|
22
23
|
import type { PricingProvider, PricingTokenClass } from "../types";
|
|
23
24
|
import { getDb } from "./db";
|
|
24
25
|
import {
|
|
@@ -98,8 +99,9 @@ const ANTHROPIC_SHORTNAME_TO_MODELSDEV: Record<string, string> = {
|
|
|
98
99
|
const ANTHROPIC_SHORTNAME_PRICING_FALLBACKS: Record<string, ModelsDevCostBlock> = {
|
|
99
100
|
// https://platform.claude.com/docs/en/about-claude/pricing (verified 2026-09-22)
|
|
100
101
|
"claude-opus-5-5": { input: 4, output: 20, cache_read: 0.2, cache_write: 5 },
|
|
101
|
-
// https://platform.claude.com/docs/en/about-claude/pricing (verified 2026-
|
|
102
|
-
|
|
102
|
+
// https://platform.claude.com/docs/en/about-claude/pricing (verified 2026-10-15):
|
|
103
|
+
// cache hits on Sonnet 5.5 cost 0.05x input, not the usual 0.1x.
|
|
104
|
+
"claude-sonnet-5-5": { input: 2, output: 10, cache_read: 0.1, cache_write: 2.5 },
|
|
103
105
|
"claude-fable-5-1": { input: 10, output: 50, cache_read: 0.25, cache_write: 12.5 },
|
|
104
106
|
"claude-mythos-5-1": { input: 10, output: 50, cache_read: 0.25, cache_write: 12.5 },
|
|
105
107
|
};
|
|
@@ -367,6 +369,21 @@ export function buildModelsDevSeedRows(cache: ModelsDevCache): PricingSeedRow[]
|
|
|
367
369
|
}
|
|
368
370
|
}
|
|
369
371
|
|
|
372
|
+
// ---- OpenAI-compatible registry providers ($0 flat-rate subscriptions) --
|
|
373
|
+
// pi and opencode run these as `<provider>/<id>`. The models.dev `nan`
|
|
374
|
+
// section and the registry-synthesized `helmcode` section
|
|
375
|
+
// (`withRegistryCatalogSections`) carry cost {0, 0}, so the sessions price
|
|
376
|
+
// as `pricing-table` at $0 instead of `unpriced`. The provider prefix stays
|
|
377
|
+
// in the key: `nan/qwen3.6` and `helmcode/qwen3.6` are separate rows.
|
|
378
|
+
for (const provider of openAICompatibleProviderIds()) {
|
|
379
|
+
for (const [id, model] of Object.entries(cache[provider]?.models ?? {})) {
|
|
380
|
+
if (!model?.cost) continue;
|
|
381
|
+
const key = `${provider}/${id}`;
|
|
382
|
+
add(key, projectCostBlock("pi", key, model.cost));
|
|
383
|
+
add(key, projectCostBlock("opencode", key, model.cost));
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
370
387
|
return collapseNormalizedIds(sourced);
|
|
371
388
|
}
|
|
372
389
|
|
|
@@ -38,7 +38,7 @@ const CONTRADICTORY_RE =
|
|
|
38
38
|
/\b(do not|don't|never)\b[\s\S]{0,240}\b(always|must|required)\b|\b(always|must|required)\b[\s\S]{0,240}\b(do not|don't|never)\b/i;
|
|
39
39
|
|
|
40
40
|
const CODE_REGISTRY_EVENTS =
|
|
41
|
-
"agentmail.email.followup agentmail.email.mapped_lead agentmail.email.mapped_worker agentmail.email.no_agent agentmail.email.unmapped common.command_suggestions.github_comment_issue common.command_suggestions.github_comment_pr common.command_suggestions.github_issue common.command_suggestions.github_pr common.command_suggestions.gitlab_issue common.command_suggestions.gitlab_mr common.delegation_instruction common.delegation_instruction.gitlab github.check_run.failed github.check_suite.failed github.comment.mentioned github.issue.assigned github.issue.labeled github.issue.mentioned github.pull_request.assigned github.pull_request.closed github.pull_request.labeled github.pull_request.mentioned github.pull_request.review_requested github.pull_request.review_submitted github.pull_request.synchronize github.workflow_run.failed gitlab.comment.mentioned gitlab.issue.assigned gitlab.merge_request.opened gitlab.pipeline.failed heartbeat.boot-triage heartbeat.checklist jira.issue.assigned jira.issue.commented jira.issue.followup kapso.message.received linear.issue.assigned linear.issue.followup linear.issue.reassigned slack.assistant.greeting slack.assistant.offline slack.assistant.suggested_prompts slack.message.attachment_failed slack.message.thread_context system.agent.communication system.agent.lead system.agent.memory system.agent.memory.remote system.agent.outputs system.agent.outputs.no_agent_fs system.agent.repository system.agent.role system.agent.secrets system.agent.slack system.agent.tools_skills system.agent.worker system.agent.worker.remote system.agent.workspace system.agent.workspace.remote system.session.lead system.session.lead.managed system.session.worker system.session.worker.managed system.session.worker.remote task.budget.refused task.requester.profile task.worker.completed task.worker.failed".split(
|
|
41
|
+
"agentmail.email.followup agentmail.email.mapped_lead agentmail.email.mapped_worker agentmail.email.no_agent agentmail.email.unmapped chat.message.thread_context common.command_suggestions.github_comment_issue common.command_suggestions.github_comment_pr common.command_suggestions.github_issue common.command_suggestions.github_pr common.command_suggestions.gitlab_issue common.command_suggestions.gitlab_mr common.delegation_instruction common.delegation_instruction.gitlab github.check_run.failed github.check_suite.failed github.comment.mentioned github.issue.assigned github.issue.labeled github.issue.mentioned github.pull_request.assigned github.pull_request.closed github.pull_request.labeled github.pull_request.mentioned github.pull_request.review_requested github.pull_request.review_submitted github.pull_request.synchronize github.workflow_run.failed gitlab.comment.mentioned gitlab.issue.assigned gitlab.merge_request.opened gitlab.pipeline.failed heartbeat.boot-triage heartbeat.checklist jira.issue.assigned jira.issue.commented jira.issue.followup kapso.message.received linear.issue.assigned linear.issue.followup linear.issue.reassigned slack.assistant.greeting slack.assistant.offline slack.assistant.suggested_prompts slack.message.attachment_failed slack.message.thread_context system.agent.communication system.agent.lead system.agent.memory system.agent.memory.remote system.agent.outputs system.agent.outputs.no_agent_fs system.agent.repository system.agent.role system.agent.secrets system.agent.slack system.agent.tools_skills system.agent.worker system.agent.worker.remote system.agent.workspace system.agent.workspace.remote system.session.lead system.session.lead.managed system.session.worker system.session.worker.managed system.session.worker.remote task.budget.refused task.requester.profile task.worker.completed task.worker.failed".split(
|
|
42
42
|
" ",
|
|
43
43
|
);
|
|
44
44
|
|
|
@@ -38,7 +38,7 @@ const CONTRADICTORY_RE =
|
|
|
38
38
|
/\b(do not|don't|never)\b[\s\S]{0,240}\b(always|must|required)\b|\b(always|must|required)\b[\s\S]{0,240}\b(do not|don't|never)\b/i;
|
|
39
39
|
|
|
40
40
|
const CODE_REGISTRY_EVENTS =
|
|
41
|
-
"agentmail.email.followup agentmail.email.mapped_lead agentmail.email.mapped_worker agentmail.email.no_agent agentmail.email.unmapped common.command_suggestions.github_comment_issue common.command_suggestions.github_comment_pr common.command_suggestions.github_issue common.command_suggestions.github_pr common.command_suggestions.gitlab_issue common.command_suggestions.gitlab_mr common.delegation_instruction common.delegation_instruction.gitlab github.check_run.failed github.check_suite.failed github.comment.mentioned github.issue.assigned github.issue.labeled github.issue.mentioned github.pull_request.assigned github.pull_request.closed github.pull_request.labeled github.pull_request.mentioned github.pull_request.review_requested github.pull_request.review_submitted github.pull_request.synchronize github.workflow_run.failed gitlab.comment.mentioned gitlab.issue.assigned gitlab.merge_request.opened gitlab.pipeline.failed heartbeat.boot-triage heartbeat.checklist jira.issue.assigned jira.issue.commented jira.issue.followup kapso.message.received linear.issue.assigned linear.issue.followup linear.issue.reassigned slack.assistant.greeting slack.assistant.offline slack.assistant.suggested_prompts slack.message.attachment_failed slack.message.thread_context system.agent.communication system.agent.lead system.agent.memory system.agent.memory.remote system.agent.outputs system.agent.outputs.no_agent_fs system.agent.repository system.agent.role system.agent.secrets system.agent.slack system.agent.tools_skills system.agent.worker system.agent.worker.remote system.agent.workspace system.agent.workspace.remote system.session.lead system.session.lead.managed system.session.worker system.session.worker.managed system.session.worker.remote task.budget.refused task.requester.profile task.worker.completed task.worker.failed".split(
|
|
41
|
+
"agentmail.email.followup agentmail.email.mapped_lead agentmail.email.mapped_worker agentmail.email.no_agent agentmail.email.unmapped chat.message.thread_context common.command_suggestions.github_comment_issue common.command_suggestions.github_comment_pr common.command_suggestions.github_issue common.command_suggestions.github_pr common.command_suggestions.gitlab_issue common.command_suggestions.gitlab_mr common.delegation_instruction common.delegation_instruction.gitlab github.check_run.failed github.check_suite.failed github.comment.mentioned github.issue.assigned github.issue.labeled github.issue.mentioned github.pull_request.assigned github.pull_request.closed github.pull_request.labeled github.pull_request.mentioned github.pull_request.review_requested github.pull_request.review_submitted github.pull_request.synchronize github.workflow_run.failed gitlab.comment.mentioned gitlab.issue.assigned gitlab.merge_request.opened gitlab.pipeline.failed heartbeat.boot-triage heartbeat.checklist jira.issue.assigned jira.issue.commented jira.issue.followup kapso.message.received linear.issue.assigned linear.issue.followup linear.issue.reassigned slack.assistant.greeting slack.assistant.offline slack.assistant.suggested_prompts slack.message.attachment_failed slack.message.thread_context system.agent.communication system.agent.lead system.agent.memory system.agent.memory.remote system.agent.outputs system.agent.outputs.no_agent_fs system.agent.repository system.agent.role system.agent.secrets system.agent.slack system.agent.tools_skills system.agent.worker system.agent.worker.remote system.agent.workspace system.agent.workspace.remote system.session.lead system.session.lead.managed system.session.worker system.session.worker.managed system.session.worker.remote task.budget.refused task.requester.profile task.worker.completed task.worker.failed".split(
|
|
42
42
|
" ",
|
|
43
43
|
);
|
|
44
44
|
|
|
@@ -1,22 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
"artifacts": [
|
|
3
|
-
{
|
|
4
|
-
"path": "examples/approval-flow.ts",
|
|
5
|
-
"content": "import { createArtifactServer } from \"../../src/artifact-sdk\";\nimport { Hono } from \"hono\";\n\nconst app = new Hono();\napp.get(\"/\", (c) =>\n c.html(`\n<!DOCTYPE html>\n<html>\n<head><title>Approval Required</title></head>\n<body>\n <h1>PR #42 — Review Required</h1>\n <p>Agent wants to merge this PR. Please review.</p>\n <button id=\"approve\">Approve</button>\n <button id=\"reject\">Reject</button>\n <script src=\"/@swarm/sdk.js\"></script>\n <script>\n const swarm = new SwarmSDK();\n document.getElementById('approve').onclick = async () => {\n await swarm.createTask({ task: 'Merge PR #42 — human approved' });\n document.body.innerHTML = '<h1>Approved! Task created.</h1>';\n };\n document.getElementById('reject').onclick = async () => {\n await swarm.createTask({ task: 'PR #42 rejected by human — needs changes' });\n document.body.innerHTML = '<h1>Rejected. Agent notified.</h1>';\n };\n </script>\n</body>\n</html>\n`),\n);\n\nconst server = createArtifactServer({ name: \"approval-pr-42\", app });\nawait server.start();\nconsole.log(`Approval artifact at: ${server.url}`);\n"
|
|
6
|
-
},
|
|
7
|
-
{
|
|
8
|
-
"path": "examples/hono-dashboard.ts",
|
|
9
|
-
"content": "import { createArtifactServer } from \"../../src/artifact-sdk\";\nimport { Hono } from \"hono\";\n\nconst app = new Hono();\n\napp.get(\"/\", (c) =>\n c.html(`\n<!DOCTYPE html>\n<html>\n<head><title>Dashboard</title></head>\n<body>\n <h1>Agent Dashboard</h1>\n <div id=\"agents\"></div>\n <script src=\"/@swarm/sdk.js\"></script>\n <script>\n const swarm = new SwarmSDK();\n swarm.getSwarm().then(agents => {\n document.getElementById('agents').innerHTML =\n '<ul>' + agents.map(a => '<li>' + a.name + ' (' + a.status + ')</li>').join('') + '</ul>';\n });\n </script>\n</body>\n</html>\n`),\n);\n\napp.get(\"/api/status\", (c) => c.json({ status: \"ok\", timestamp: Date.now() }));\n\nconst server = createArtifactServer({ name: \"dashboard\", app });\nawait server.start();\nconsole.log(`Dashboard live at: ${server.url}`);\n"
|
|
10
|
-
},
|
|
11
|
-
{
|
|
12
|
-
"path": "examples/multi-artifact.ts",
|
|
13
|
-
"content": "import { createArtifactServer } from \"../../src/artifact-sdk\";\nimport { Hono } from \"hono\";\n\n// Artifact 1: Status page\nconst statusApp = new Hono();\nstatusApp.get(\"/\", (c) => c.html(\"<h1>Status: All systems operational</h1>\"));\n\n// Artifact 2: Data viewer\nconst dataApp = new Hono();\ndataApp.get(\"/\", (c) => c.json({ items: [1, 2, 3], generated: new Date().toISOString() }));\n\nconst status = createArtifactServer({ name: \"status\", app: statusApp });\nconst data = createArtifactServer({ name: \"data\", app: dataApp });\n\nawait status.start();\nawait data.start();\n\nconsole.log(`Status page: ${status.url}`);\nconsole.log(`Data viewer: ${data.url}`);\nconsole.log(`Both artifacts running on different ports (${status.port}, ${data.port})`);\n"
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"path": "examples/static-report.sh",
|
|
17
|
-
"content": "#!/bin/bash\n# Serve a static HTML report as an artifact\nARTIFACT_DIR=\"/workspace/personal/artifacts/my-report\"\nmkdir -p \"$ARTIFACT_DIR\"\n\ncat > \"$ARTIFACT_DIR/index.html\" << 'HTML'\n<!DOCTYPE html>\n<html>\n<head><title>Agent Report</title></head>\n<body>\n <h1>Analysis Report</h1>\n <p>Generated by agent</p>\n</body>\n</html>\nHTML\n\nagent-swarm artifact serve \"$ARTIFACT_DIR\" --name \"my-report\"\n"
|
|
18
|
-
}
|
|
19
|
-
],
|
|
20
2
|
"brainstorming": [
|
|
21
3
|
{
|
|
22
4
|
"path": "template.md",
|
|
@@ -77,6 +59,16 @@
|
|
|
77
59
|
"content": "---\ndate: [ISO format]\nauthor: [Name]\ntopic: \"[Feature/Bug/Scenario being validated]\"\ntags: [qa, relevant-tags]\nstatus: [in-progress|pass|fail|blocked]\nsource_plan: [optional — path to plan that motivated this QA]\nsource_verification: [optional — path to verification report]\nrelated_pr: [optional — PR URL or number]\nenvironment: [optional — local|staging|production]\nlast_updated: [YYYY-MM-DD]\nlast_updated_by: [Name]\n---\n\n# [Feature/Scenario] — QA Report\n\n## Context\n[What is being validated and why. Link to plan, PR, or issue.]\n\n## Scope\n### In Scope\n- [What this QA covers]\n\n### Out of Scope\n- [What this QA does NOT cover]\n\n## Test Cases\n\n### TC-1: [Test Case Name]\n**Steps:**\n1. [Step 1]\n2. [Step 2]\n\n**Expected Result:** [What should happen]\n**Actual Result:** [What actually happened]\n**Status:** pass | fail | blocked | skipped\n\n### TC-2: [Test Case Name]\n[Same structure]\n\n## Edge Cases & Exploratory Testing\n- [Findings from exploratory testing]\n\n## Evidence\n\n### Screenshots\n- \n\n### Videos\n- [Video: Description](url)\n\n### Logs & Output\n```\n[Relevant log output]\n```\n\n### External Links\n- [Sentry Issue](url)\n- [Grafana Dashboard](url)\n- [CI/CD Run](url)\n- [PR](url)\n\n## Issues Found\n- [ ] [Issue description — severity: critical|major|minor]\n\n## Verdict\n**Status**: PASS | FAIL | BLOCKED\n**Summary**: [1-2 sentence summary of QA outcome]\n\n## Appendix\n\n- **Plan**: `thoughts/.../plans/YYYY-MM-DD-topic.md`\n- **Verification**: `thoughts/.../plans/YYYY-MM-DD-verification-report.md`\n- **Related documents**: [links]\n- **Notes**: [follow-up QA, derail items, environment caveats]\n"
|
|
78
60
|
}
|
|
79
61
|
],
|
|
62
|
+
"qa-loom": [
|
|
63
|
+
{
|
|
64
|
+
"path": "build-loom.mjs",
|
|
65
|
+
"content": "// build-loom.mjs: steps.json + raw recording -> narrated qa-loom.mp4 (+ qa-loom.ass, work/).\n// Usage: node build-loom.mjs <steps.json> <raw.webm> <outDir> (bun works too)\n// Env: ELEVENLABS_API_KEY (required), SPEED=1.33, VOICE=21m00Tcm4TlvDq8ikWAM,\n// MODEL=eleven_turbo_v2_5, FONT=\"DejaVu Sans\"\n// Needs: Node 18+ (global fetch, top-level await) and ffmpeg built with libass (`ass` filter).\nimport { execFileSync } from \"node:child_process\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\n\nconst [stepsPath, raw, outArg] = process.argv.slice(2);\nif (!stepsPath || !raw || !outArg) {\n throw new Error(\"usage: node build-loom.mjs <steps.json> <raw.webm> <outDir>\");\n}\nconst out = resolve(outArg);\nconst SPEED = Number(process.env.SPEED ?? 1.33);\nconst VOICE = process.env.VOICE ?? \"21m00Tcm4TlvDq8ikWAM\";\nconst MODEL = process.env.MODEL ?? \"eleven_turbo_v2_5\";\nconst FONT = process.env.FONT ?? \"DejaVu Sans\";\nconst KEY = process.env.ELEVENLABS_API_KEY;\nif (!KEY) throw new Error(\"ELEVENLABS_API_KEY is not set\");\nmkdirSync(join(out, \"work\"), { recursive: true });\nconst W = (f) => join(out, \"work\", f);\n\n// -loglevel error keeps ffmpeg's banner and progress output out of the logs.\nconst ff = (...args) => {\n try {\n execFileSync(\"ffmpeg\", [\"-y\", \"-hide_banner\", \"-loglevel\", \"error\", ...args], {\n stdio: [\"ignore\", \"ignore\", \"pipe\"],\n });\n } catch (e) {\n throw new Error(`ffmpeg failed: ${String(e.stderr).slice(0, 400)}`);\n }\n};\nconst ENC = [\n \"-c:v\",\n \"libx264\",\n \"-preset\",\n \"veryfast\",\n \"-crf\",\n \"23\",\n \"-pix_fmt\",\n \"yuv420p\",\n \"-r\",\n \"30\",\n \"-video_track_timescale\",\n \"15360\",\n];\n// Keep the aspect ratio: letterbox or pillarbox into 1920x1080, never stretch (mobile viewports).\nconst FIT =\n \"scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2:color=white\";\n\nconst { steps } = JSON.parse(readFileSync(stepsPath, \"utf8\"));\nconst concat = [];\nconst cues = [];\nconst audio = [];\nlet T = 0; // running output timeline, in seconds\n\nfor (const [i, s] of steps.entries()) {\n const visualAt = s.visualAt ?? s.start;\n const segLen = (s.end - s.start) / SPEED;\n const v = (visualAt - s.start) / SPEED; // voice and subtitles start when the screen renders\n\n // 1. TTS with character timestamps, one request per step. Cached per narration text,\n // so a re-run after a style tweak costs no ElevenLabs characters.\n const mp3 = W(`audio-${i + 1}.mp3`);\n const alPath = W(`audio-${i + 1}.json`);\n let cached = existsSync(alPath) ? JSON.parse(readFileSync(alPath, \"utf8\")) : null;\n if (cached?.text !== s.narration) {\n const res = await fetch(\n `https://api.elevenlabs.io/v1/text-to-speech/${VOICE}/with-timestamps`,\n {\n method: \"POST\",\n headers: { \"xi-api-key\": KEY, \"content-type\": \"application/json\" },\n body: JSON.stringify({ text: s.narration, model_id: MODEL }),\n },\n );\n if (!res.ok) throw new Error(`ElevenLabs ${res.status}: ${(await res.text()).slice(0, 200)}`);\n const tts = await res.json();\n // audio_base64 is large: write it straight to disk, never print it.\n writeFileSync(mp3, Buffer.from(tts.audio_base64, \"base64\"));\n cached = { text: s.narration, alignment: tts.alignment };\n writeFileSync(alPath, JSON.stringify(cached));\n }\n const al = cached.alignment;\n const aDur = al.character_end_times_seconds.at(-1);\n\n // 2. Sped-up segment. Freeze the last frame when the narration outlasts it.\n const seg = W(`seg-${i + 1}.mp4`);\n ff(\n \"-ss\",\n String(s.start),\n \"-t\",\n String(s.end - s.start),\n \"-i\",\n raw,\n \"-an\",\n \"-vf\",\n `setpts=(PTS-STARTPTS)/${SPEED},${FIT},fps=30`,\n ...ENC,\n seg,\n );\n concat.push(seg);\n const need = v + aDur + 0.35;\n let len = segLen;\n if (need > segLen) {\n const png = W(`last-${i + 1}.png`);\n ff(\"-sseof\", \"-0.2\", \"-i\", seg, \"-update\", \"1\", \"-frames:v\", \"1\", png);\n const pad = W(`pad-${i + 1}.mp4`);\n const padLen = (need - segLen).toFixed(3);\n ff(\"-loop\", \"1\", \"-framerate\", \"30\", \"-t\", padLen, \"-i\", png, \"-vf\", FIT, ...ENC, pad);\n concat.push(pad);\n len = need;\n }\n audio.push({ mp3, delayMs: Math.round((T + v) * 1000) });\n\n // 3. Subtitle cues from the same alignment, so voice and subtitles cannot drift.\n // At most 6 words per cue, break on punctuation, merge orphans under 3 words.\n const words = [];\n let cur = null;\n al.characters.forEach((ch, k) => {\n if (/\\s/.test(ch)) {\n if (cur) words.push(cur);\n cur = null;\n return;\n }\n if (!cur) cur = { text: \"\", t0: al.character_start_times_seconds[k] };\n cur.text += ch;\n cur.t1 = al.character_end_times_seconds[k];\n });\n if (cur) words.push(cur);\n const groups = [];\n let g = [];\n for (const w of words) {\n g.push(w);\n if (g.length >= 6 || /[.,;:!?]$/.test(w.text)) {\n groups.push(g);\n g = [];\n }\n }\n if (g.length) groups.push(g);\n for (let k = groups.length - 1; k > 0; k--) {\n if (groups[k].length < 3) {\n groups[k - 1].push(...groups[k]);\n groups.splice(k, 1);\n }\n }\n for (const grp of groups) {\n cues.push({\n t0: T + v + grp[0].t0,\n t1: T + v + grp.at(-1).t1 + 0.1,\n text: grp.map((w) => w.text).join(\" \"),\n });\n }\n T += len;\n}\n\n// 4. Concat with the concat demuxer. The concat filter in one big filtergraph can\n// truncate the video stream.\nwriteFileSync(W(\"concat.txt\"), concat.map((f) => `file '${f}'`).join(\"\\n\"));\nff(\"-f\", \"concat\", \"-safe\", \"0\", \"-i\", W(\"concat.txt\"), \"-c\", \"copy\", W(\"video.mp4\"));\n\n// 5. ASS subtitles: one small line, bottom centre, dark box. libass ignores the\n// BackColour alpha with BorderStyle=3, so the box is solid #303030.\nconst ts = (t) => {\n const cs = Math.round(t * 100);\n const h = Math.floor(cs / 360000);\n const m = String(Math.floor(cs / 6000) % 60).padStart(2, \"0\");\n const sec = String(Math.floor(cs / 100) % 60).padStart(2, \"0\");\n return `${h}:${m}:${sec}.${String(cs % 100).padStart(2, \"0\")}`;\n};\nfor (let k = 0; k < cues.length - 1; k++) cues[k].t1 = Math.min(cues[k].t1, cues[k + 1].t0);\nconst dialogue = cues\n .map((c) => `Dialogue: 0,${ts(c.t0)},${ts(c.t1)},Default,,0,0,0,,${c.text}`)\n .join(\"\\n\");\nconst ass = `[Script Info]\nScriptType: v4.00+\nPlayResX: 1920\nPlayResY: 1080\n\n[V4+ Styles]\nFormat: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding\nStyle: Default,${FONT},38,&H00FFFFFF,&H00FFFFFF,&H303030,&H00000000,0,0,0,0,100,100,0,0,3,2,0,2,40,40,30,1\n\n[Events]\nFormat: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n${dialogue}\n`;\nconst assPath = join(out, \"qa-loom.ass\");\nwriteFileSync(assPath, ass);\n\n// 6. Mux: delay each step's audio to where its screen renders, mix, burn subtitles.\n// normalize=0 stops amix from dividing the volume by the number of inputs.\nconst inputs = audio.flatMap((a) => [\"-i\", a.mp3]);\nconst delays = audio.map((a, k) => `[${k + 1}:a]adelay=${a.delayMs}:all=1[a${k}]`).join(\";\");\nconst labels = audio.map((_, k) => `[a${k}]`).join(\"\");\nconst mix = `${labels}amix=inputs=${audio.length}:normalize=0:duration=longest[a]`;\nff(\n \"-i\",\n W(\"video.mp4\"),\n ...inputs,\n \"-filter_complex\",\n `${delays};${mix};[0:v]ass=${assPath}[v]`,\n \"-map\",\n \"[v]\",\n \"-map\",\n \"[a]\",\n ...ENC,\n \"-c:a\",\n \"aac\",\n \"-b:a\",\n \"128k\",\n \"-movflags\",\n \"+faststart\",\n \"-t\",\n T.toFixed(3),\n join(out, \"qa-loom.mp4\"),\n);\nconst chars = steps.reduce((n, s) => n + s.narration.length, 0);\nconsole.log(\n JSON.stringify({ durationS: +T.toFixed(2), steps: steps.length, cues: cues.length, chars }),\n);\n"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"path": "run-qa.sh",
|
|
69
|
+
"content": "#!/usr/bin/env bash\n# run-qa.sh: record a UI QA pass with agent-browser and write steps.json.\n# Every step offset is in seconds from the moment recording starts.\n#\n# Usage: BASE_URL=http://localhost:5274 OUT=./loom bash run-qa.sh\n# Needs: agent-browser (with `record`), jq, GNU date (%N). No bc: time math runs in jq.\n#\n# Replace the two example steps with the steps that prove your change.\nset -euo pipefail\nBASE_URL=${BASE_URL:-http://localhost:5274}\nOUT=${OUT:-./loom}\nmkdir -p \"$OUT\"\nLOG=\"$OUT/steps.ndjson\"\n: >\"$LOG\"\n\nnow() { date +%s.%N; }\n# act: call right before the action fires. It marks the step start.\nact() { STEP_START=$(now); }\n# step <action> <observed> <verdict> <narration>: call AFTER the resulting screen has\n# rendered (after an `agent-browser wait <selector>`). That moment becomes visualAt.\nstep() {\n jq -nc --arg a \"$1\" --arg o \"$2\" --arg v \"$3\" --arg n \"$4\" \\\n --arg t0 \"$T0\" --arg s \"$STEP_START\" --arg va \"$(now)\" \\\n '{action:$a, observed:$o, verdict:$v, narration:$n,\n start:(($s|tonumber)-($t0|tonumber)), visualAt:(($va|tonumber)-($t0|tonumber))}' >>\"$LOG\"\n}\n\n# Log in and seed data BEFORE recording starts.\nagent-browser set viewport 1920 1080 >/dev/null\nagent-browser open \"$BASE_URL\" >/dev/null\nagent-browser wait 1500 >/dev/null\nagent-browser record start \"$OUT/raw.webm\" --cursor --fps 30 >/dev/null\nT0=$(now)\n\nact\nagent-browser reload >/dev/null\nagent-browser wait h1 >/dev/null\nstep \"Load home page\" \"Heading and nav render\" pass \"The home page loads with its heading and nav.\"\nagent-browser wait 800 >/dev/null # dwell so the viewer sees the result before the next action\n\nact\nagent-browser open \"$BASE_URL/settings\" >/dev/null\nagent-browser wait h1 >/dev/null\nstep \"Open settings\" \"Settings page renders\" pass \"The settings page opens.\"\nagent-browser wait 800 >/dev/null\n\nEND=$(jq -n --arg a \"$(now)\" --arg b \"$T0\" '($a|tonumber)-($b|tonumber)')\nagent-browser record stop >/dev/null\n\n# Each step ends where the next one starts; the last one ends at record stop.\njq -s --argjson end \"$END\" '\n {steps: ([to_entries[] | .value + {idx: (.key + 1)}]\n | . as $s\n | [range(0; length) as $i\n | $s[$i] + {end: (if $i + 1 < ($s | length) then $s[$i + 1].start else $end end)}])}\n' \"$LOG\" >\"$OUT/steps.json\"\njq -c '.steps[] | {idx, start, visualAt, end, verdict}' \"$OUT/steps.json\"\n"
|
|
70
|
+
}
|
|
71
|
+
],
|
|
80
72
|
"questioning": [
|
|
81
73
|
{
|
|
82
74
|
"path": "template.md",
|