@desplega.ai/agent-swarm 1.173.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-ccm9xrep.js → acp-adapter-sj2g0m84.js} +17 -16
- package/dist/{actions-sgeyv08k.js → actions-fegk44gr.js} +39 -34
- package/dist/{amp-adapter-gpxp3kc9.js → amp-adapter-ad8ft89x.js} +8 -7
- package/dist/{app-km9qnv2f.js → app-kfvgbvzy.js} +18 -14
- package/dist/{assistant-5r8tcvvd.js → assistant-z7zdy9ja.js} +40 -35
- package/dist/{boot-reembed-swwzdgd7.js → boot-reembed-5c2z0mbe.js} +10 -9
- package/dist/{boot-reembed-16rv9843.js → boot-reembed-j8xa181e.js} +10 -9
- package/dist/{boot-scrub-sweep-zkr6vkqb.js → boot-scrub-sweep-hnkn3rgt.js} +11 -10
- package/dist/{boot-seeds-d8sp49t2.js → boot-seeds-kwjq4d2z.js} +14 -13
- package/dist/{claude-adapter-yzzzh8jp.js → claude-adapter-9vmvpvgd.js} +19 -18
- 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-4whxere4.js → claude-sdk-session-9jye6cxr.js} +11881 -11472
- package/dist/{cli-94b48jhk.js → cli-03gedhng.js} +16 -14
- package/dist/{cli-1w0vj3gw.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-x119kdba.js → cli-3dj8k4vy.js} +1 -1
- package/dist/{cli-srqq3gc5.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-42qhyvwx.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-0kxf81jj.js → cli-6jyj59nb.js} +2 -2
- package/dist/{cli-w1apjecb.js → cli-6razj6t4.js} +2 -2
- package/dist/{cli-anzdk0z4.js → cli-74ahz61x.js} +2 -2
- package/dist/{cli-m89t9jdf.js → cli-7x0g4sn1.js} +15416 -16024
- 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-na2pxe4f.js → cli-apx87jne.js} +2 -2
- package/dist/{cli-rfz2ve55.js → cli-bzszjf2p.js} +2855 -862
- 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-rfs4vddv.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-7a93cv8c.js → cli-ee9gtz70.js} +98 -23
- package/dist/{cli-3nxdmjrc.js → cli-eejsgvm4.js} +4 -4
- package/dist/{cli-fmhj1ehj.js → cli-ef1hg1wv.js} +470 -51
- 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-h7k0xtry.js → cli-hvqbtna4.js} +4 -1256
- package/dist/{cli-s8z6x4b4.js → cli-j0pfptdp.js} +218 -102
- package/dist/cli-jta2eyzc.js +619 -0
- package/dist/{cli-5er5gjbm.js → cli-k382tshb.js} +10 -11
- package/dist/{cli-v3zhnxsb.js → cli-k3dp5tpf.js} +2 -2
- package/dist/{cli-wd0hz6ah.js → cli-kbrt8gqa.js} +9 -91
- package/dist/cli-kceah1eh.js +92 -0
- package/dist/{cli-ne7smj1b.js → cli-ke896fcf.js} +10 -10
- package/dist/{cli-xc7p7s3g.js → cli-kt6zqg0e.js} +6 -6
- package/dist/{cli-wrh08zjd.js → cli-m50t4309.js} +5 -18
- package/dist/cli-n2wq4j7m.js +6985 -0
- package/dist/{cli-bv2etw2y.js → cli-n3hzg89b.js} +46 -15
- package/dist/{cli-tmhsxhb8.js → cli-n5vwg842.js} +23 -6
- package/dist/{cli-qm1448f9.js → cli-ne236afy.js} +8 -8
- package/dist/{cli-d161g69q.js → cli-p1x85d2t.js} +50 -2
- package/dist/{cli-7pz3b7x5.js → cli-przpmdgr.js} +25 -0
- package/dist/{cli-0a7mm3zp.js → cli-q0ke1yww.js} +25 -21
- package/dist/{cli-e3pmyt39.js → cli-q50942rs.js} +1 -1
- package/dist/{cli-r44t9wqc.js → cli-r106725k.js} +2 -2
- package/dist/{cli-phcqp1b9.js → cli-r2pnvj8g.js} +2 -2
- package/dist/{cli-jpc4r224.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-r4fqfpwf.js → cli-s2sp957d.js} +50 -27
- package/dist/{cli-1x7da0de.js → cli-s4setq91.js} +8 -8
- package/dist/{cli-6thk4jz3.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-nwrdx840.js → cli-tr4sxp68.js} +3 -3
- package/dist/{cli-r0byd61h.js → cli-tsde1hdr.js} +15 -15
- package/dist/{cli-b07hr3bt.js → cli-txqb9s38.js} +2 -2
- package/dist/{cli-6r2f7edk.js → cli-v1yb0rqb.js} +6 -6
- package/dist/{cli-saqyage0.js → cli-vatyhshy.js} +179 -27
- package/dist/{cli-5wpw4rab.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-c3rk1v0x.js → cli-wayw93nt.js} +3 -3
- package/dist/cli-wmgrkscj.js +23254 -0
- package/dist/{cli-9xf7fdtp.js → cli-z5jjpaxw.js} +29 -6
- package/dist/{cli-451ks6aw.js → cli-zkpks3r3.js} +1 -1
- package/dist/cli.js +29 -45
- package/dist/{codex-adapter-r3dn4fp5.js → codex-adapter-y81sssk0.js} +15 -14
- package/dist/{codex-login-841kv5fk.js → codex-login-f2rcfpp9.js} +3 -2
- package/dist/{codex-session-runner-fwt793h4.js → codex-session-runner-8vx4j6a9.js} +15 -14
- package/dist/{commands-z8fq9qna.js → commands-kcpvpcrr.js} +11 -10
- package/dist/{controller-hq5ggpcp.js → controller-ywrmt6yd.js} +5 -4
- package/dist/{controllers-connect-3mf2btyj.js → controllers-connect-kwpvj3rf.js} +42 -37
- package/dist/{cursor-adapter-q8pgv5qa.js → cursor-adapter-cehv1qns.js} +7 -6
- package/dist/{db-yyhqqrbf.js → db-k8ankj20.js} +12 -11
- package/dist/{db-qfpq4bt7.js → db-ww5d7jq3.js} +28 -9
- package/dist/{devin-adapter-xgtj680j.js → devin-adapter-x1022qb1.js} +3 -2
- package/dist/{dispatcher-x0x7qfcq.js → dispatcher-e8gq5m68.js} +38 -33
- package/dist/{docker-f503k9p0.js → docker-ff6y1190.js} +3 -2
- package/dist/{dsh-adapter-34etvb5m.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-h1fjecwe.js → external-channel-h3359cwn.js} +11 -10
- package/dist/{google-generative-ai-szdxbpqg.js → google-generative-ai-g8db7x0f.js} +4 -2
- package/dist/{google-vertex-qzw3p36d.js → google-vertex-r2692q0r.js} +4 -2
- package/dist/{grok-adapter-e7mr287e.js → grok-adapter-0d4x7gkx.js} +18 -17
- package/dist/{handlers-znb9n7d4.js → handlers-an1gzv21.js} +41 -36
- package/dist/{heartbeat-bm4qkn8d.js → heartbeat-6dakeb61.js} +43 -38
- package/dist/{hook-254r4mza.js → hook-6v6cv6d5.js} +11 -23
- package/dist/{hook-xaa3q80s.js → hook-sa5xye4y.js} +12 -11
- package/dist/{http-3dgrgp8y.js → http-r7f3eky0.js} +1499 -251
- package/dist/index-52j1aja4.js +3465 -0
- package/dist/{index-fx3jqq1e.js → index-7ep4gzdk.js} +38 -33
- 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-g2byveez.js → index-rmpw9a8q.js} +38 -33
- package/dist/{index-2ybk7khy.js → index-s2wxgzr0.js} +468 -683
- package/dist/{index-bdsqa799.js → index-v1v6n7hg.js} +43 -38
- package/dist/index-we27kd31.js +12810 -0
- package/dist/{index-jd7sejm2.js → index-zk96aq1k.js} +10 -9
- package/dist/{keepalive-6ywhpja1.js → keepalive-be9vwy5h.js} +12 -11
- package/dist/{lead-cw45n21s.js → lead-0861cwrf.js} +61 -56
- package/dist/{maintenance-ss1a1ge9.js → maintenance-mzzgc8bn.js} +10 -9
- package/dist/{node-fkajaq96.js → node-mn1zs7an.js} +2 -1
- package/dist/{oauth-refresh-sweep-3c6f8vyy.js → oauth-refresh-sweep-cpdnfwdj.js} +11 -10
- package/dist/{onboard-8hzdhc1k.js → onboard-fh8qs4r3.js} +11 -3
- package/dist/{opencode-adapter-qbchbcfz.js → opencode-adapter-nm42wwj8.js} +18 -13
- package/dist/{otel-impl-e6mnvc4v.js → otel-impl-x6c1c9g4.js} +242 -868
- package/dist/{pi-mono-adapter-3c9g9snh.js → pi-mono-adapter-9n2ya240.js} +244 -106
- package/dist/{pricing-refresh-gedkjdw0.js → pricing-refresh-qb0rv0ap.js} +12 -11
- package/dist/{rbac-roles-dt4vh1yy.js → rbac-roles-1gzdwbh1.js} +11 -10
- package/dist/{rbac-roles-eszdg6pm.js → rbac-roles-3mraey52.js} +12 -11
- package/dist/{render-v2-907cy6sa.js → render-v2-agpa45zz.js} +18 -14
- package/dist/{resume-42stcaz0.js → resume-x0ydy9s9.js} +25 -21
- package/dist/{runtime-qfry88v2.js → runtime-sa2vr16g.js} +2 -2
- package/dist/{setup-adbc1c10.js → setup-as9ga0d6.js} +2 -2
- package/dist/{virtual-modules-rtmt494q.js → virtual-modules-9gkxqrap.js} +6 -5
- package/dist/{work-objects-wpj8ck95.js → work-objects-y5g1y7ev.js} +41 -36
- package/dist/{worker-bzdra9rf.js → worker-860c3e38.js} +61 -56
- package/dist/{x-qfmygqqv.js → x-m477fr0h.js} +3 -2
- package/openapi.json +523 -344
- package/package.json +10 -11
- 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 +52 -7
- 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-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 +0 -18
- package/src/be/seed-skills/index.ts +104 -3
- package/src/be/swarm-config-guard.ts +52 -0
- package/src/cli.tsx +0 -16
- package/src/commands/onboard/non-interactive.ts +4 -2
- package/src/commands/provider-credentials.ts +52 -6
- package/src/commands/runner.ts +190 -54
- 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.ts +15 -5
- package/src/http/page-proxy.ts +2 -4
- package/src/http/pages-public.ts +3 -3
- package/src/http/pages.ts +3 -13
- package/src/http/poll.ts +28 -68
- package/src/http/requested-by.ts +54 -0
- package/src/http/scripts.ts +1 -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 +159 -1
- 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/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 +55 -2
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/claude-adapter.ts +1 -1
- package/src/providers/claude-flag-settings.ts +17 -5
- package/src/providers/claude-managed-adapter.ts +1 -1
- package/src/providers/claude-session-events.ts +6 -1
- package/src/providers/dsh-adapter.ts +1 -1
- package/src/providers/opencode-adapter.ts +243 -26
- package/src/providers/opencode-swarm-plugin.ts +31 -0
- package/src/providers/pi-mono-adapter.ts +398 -153
- package/src/providers/pricing-sources.md +33 -2
- package/src/providers/reasoning-effort.ts +27 -0
- package/src/providers/types.ts +6 -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/loader.ts +19 -7
- 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/event-dedup.ts +1 -1
- package/src/tasks/additive-buffer.ts +4 -2
- package/src/tasks/context-key.ts +60 -1
- package/src/tasks/model-pin.ts +67 -0
- package/src/tasks/worker-follow-up.ts +4 -31
- package/src/tests/acp-adapter.test.ts +42 -0
- package/src/tests/api-drain-worker-e2e.test.ts +1 -6
- 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 +9 -4
- package/src/tests/claude-flag-settings.test.ts +11 -3
- package/src/tests/claude-managed-adapter.test.ts +1 -1
- package/src/tests/claude-sdk-lifecycle.test.ts +9 -3
- 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 +19 -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/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/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-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/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 +216 -2
- 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-legacy-resume-prompt.test.ts +64 -0
- package/src/tests/script-connections.test.ts +2 -0
- 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 +46 -0
- package/src/tests/skill-get-name-resolution.test.ts +137 -0
- package/src/tests/sonnet-5-5-registry.test.ts +9 -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/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/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/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/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 +8 -1
- package/templates/skills/apps/content.md +8 -1
- 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/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-bd957dgb.js +0 -153
- package/dist/cli-q51yprfq.js +0 -1104
- package/dist/cli-t1ze64h3.js +0 -811
- package/dist/cli-tcc4wd5d.js +0 -56471
- 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/src/{artifact-sdk → pages}/browser-sdk.ts +0 -0
- /package/src/{artifact-sdk → pages}/feedback-overlay.ts +0 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import {
|
|
2
|
+
afterAll,
|
|
3
|
+
afterEach,
|
|
4
|
+
beforeAll,
|
|
5
|
+
beforeEach,
|
|
6
|
+
describe,
|
|
7
|
+
expect,
|
|
8
|
+
spyOn,
|
|
9
|
+
test,
|
|
10
|
+
} from "bun:test";
|
|
11
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
12
|
+
import { tmpdir } from "node:os";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import { OpenCode } from "@opencode/sdk";
|
|
15
|
+
import { fetchResolvedEnv } from "../commands/runner";
|
|
16
|
+
import { createOpencodeSwarmPlugin } from "../providers/opencode-swarm-plugin";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* OpenCode runs in-process, so its shell tool would spawn with the worker's
|
|
20
|
+
* process.env. The swarm plugin swaps in the env the runner resolved for the
|
|
21
|
+
* task: a row admitted after boot (exportToEnv flipped on) must reach the
|
|
22
|
+
* shell, and a row revoked after boot (flipped off) must leave it, even though
|
|
23
|
+
* process.env still holds the boot value.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
type ConfigRow = { key: string; value: string; isSecret: boolean; exportToEnv: boolean };
|
|
27
|
+
|
|
28
|
+
const SHELL_TIMEOUT_MS = 15_000;
|
|
29
|
+
|
|
30
|
+
let server: ReturnType<typeof Bun.serve>;
|
|
31
|
+
let apiUrl = "";
|
|
32
|
+
let rows: ConfigRow[] = [];
|
|
33
|
+
let directory = "";
|
|
34
|
+
|
|
35
|
+
beforeAll(() => {
|
|
36
|
+
server = Bun.serve({
|
|
37
|
+
port: 0,
|
|
38
|
+
fetch: () => Response.json({ configs: rows }),
|
|
39
|
+
});
|
|
40
|
+
apiUrl = `http://localhost:${server.port}`;
|
|
41
|
+
directory = mkdtempSync(join(tmpdir(), "opencode-shell-env-"));
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
afterAll(() => {
|
|
45
|
+
server.stop(true);
|
|
46
|
+
rmSync(directory, { recursive: true, force: true });
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
type Host = Awaited<ReturnType<typeof OpenCode.create>>;
|
|
50
|
+
|
|
51
|
+
async function hostWithEnv(env: Record<string, string | undefined>): Promise<Host> {
|
|
52
|
+
const plugin = createOpencodeSwarmPlugin({
|
|
53
|
+
apiUrl: "http://127.0.0.1:9",
|
|
54
|
+
apiKey: "",
|
|
55
|
+
agentId: "agent-1",
|
|
56
|
+
taskId: "",
|
|
57
|
+
isLead: false,
|
|
58
|
+
env,
|
|
59
|
+
});
|
|
60
|
+
return OpenCode.create({ plugins: [plugin], log: { level: "error", emit: () => {} } });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Runs one command as a session shell call, the path the agent's shell tool
|
|
65
|
+
* takes (`host.shell.create` skips plugin hooks), and returns its output.
|
|
66
|
+
*/
|
|
67
|
+
async function shell(host: Host, command: string): Promise<string> {
|
|
68
|
+
const session = await host.sessions.create({ location: { directory } });
|
|
69
|
+
const outFile = join(directory, `out-${crypto.randomUUID()}.txt`);
|
|
70
|
+
await host.sessions.shell({
|
|
71
|
+
sessionID: session.id,
|
|
72
|
+
command: `{ ${command}; } > ${outFile}.tmp 2>&1; mv ${outFile}.tmp ${outFile}`,
|
|
73
|
+
});
|
|
74
|
+
const deadline = Date.now() + SHELL_TIMEOUT_MS;
|
|
75
|
+
while (!(await Bun.file(outFile).exists())) {
|
|
76
|
+
if (Date.now() > deadline) throw new Error("shell did not exit");
|
|
77
|
+
await Bun.sleep(25);
|
|
78
|
+
}
|
|
79
|
+
return (await Bun.file(outFile).text()).trim();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
describe("OpenCode shell env (real OpenCode host)", () => {
|
|
83
|
+
const envKeys = ["SHELL_REVOKED_CANARY", "SHELL_ADMITTED_CANARY"] as const;
|
|
84
|
+
const saved: Record<string, string | undefined> = {};
|
|
85
|
+
let log: ReturnType<typeof spyOn>;
|
|
86
|
+
|
|
87
|
+
beforeEach(() => {
|
|
88
|
+
for (const key of envKeys) saved[key] = process.env[key];
|
|
89
|
+
log = spyOn(console, "log").mockImplementation(() => {});
|
|
90
|
+
});
|
|
91
|
+
afterEach(() => {
|
|
92
|
+
log.mockRestore();
|
|
93
|
+
for (const key of envKeys) {
|
|
94
|
+
if (saved[key] === undefined) delete process.env[key];
|
|
95
|
+
else process.env[key] = saved[key];
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test(
|
|
100
|
+
"admission and revocation after boot reach the shell",
|
|
101
|
+
async () => {
|
|
102
|
+
process.env.SHELL_REVOKED_CANARY = "boot-revoked";
|
|
103
|
+
delete process.env.SHELL_ADMITTED_CANARY;
|
|
104
|
+
rows = [
|
|
105
|
+
{ key: "SHELL_REVOKED_CANARY", value: "row-revoked", isSecret: true, exportToEnv: false },
|
|
106
|
+
{ key: "SHELL_ADMITTED_CANARY", value: "row-admitted", isSecret: true, exportToEnv: true },
|
|
107
|
+
];
|
|
108
|
+
const { env } = await fetchResolvedEnv(apiUrl, "key", "agent-1", process.env);
|
|
109
|
+
const host = await hostWithEnv(env);
|
|
110
|
+
try {
|
|
111
|
+
const out = await shell(
|
|
112
|
+
host,
|
|
113
|
+
'echo "admitted=$SHELL_ADMITTED_CANARY revoked=$SHELL_REVOKED_CANARY term=$OPENCODE_TERMINAL."; tr "\\0" "\\n" < /proc/$$/environ | grep -c "^SHELL_REVOKED_CANARY=" || true',
|
|
114
|
+
);
|
|
115
|
+
expect(out).toContain("admitted=row-admitted revoked= term=1.");
|
|
116
|
+
expect(out.split("\n").at(-1)).toBe("0");
|
|
117
|
+
} finally {
|
|
118
|
+
await host.close();
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
SHELL_TIMEOUT_MS * 2,
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
test(
|
|
125
|
+
"concurrent hosts keep their own env",
|
|
126
|
+
async () => {
|
|
127
|
+
const [first, second] = await Promise.all([
|
|
128
|
+
hostWithEnv({ ...process.env, SHELL_ADMITTED_CANARY: "first" }),
|
|
129
|
+
hostWithEnv({ ...process.env, SHELL_ADMITTED_CANARY: undefined }),
|
|
130
|
+
]);
|
|
131
|
+
try {
|
|
132
|
+
const [a, b] = await Promise.all([
|
|
133
|
+
shell(first, 'echo "v=$SHELL_ADMITTED_CANARY"'),
|
|
134
|
+
shell(second, 'echo "v=$SHELL_ADMITTED_CANARY"'),
|
|
135
|
+
]);
|
|
136
|
+
expect(a).toBe("v=first");
|
|
137
|
+
expect(b).toBe("v=");
|
|
138
|
+
expect(process.env.SHELL_ADMITTED_CANARY).toBeUndefined();
|
|
139
|
+
} finally {
|
|
140
|
+
await Promise.all([first.close(), second.close()]);
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
SHELL_TIMEOUT_MS * 2,
|
|
144
|
+
);
|
|
145
|
+
});
|
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
* OPENROUTER_BASE_URL gateway routing — unit tests.
|
|
3
3
|
*
|
|
4
4
|
* Covers the shared resolver (`src/utils/openrouter-base-url.ts`), the pi
|
|
5
|
-
* models.json
|
|
6
|
-
* its composition through pi-coding-agent's ModelRuntime, and
|
|
5
|
+
* per-session models.json composer (`writeSessionModelsJson`) for every shared
|
|
6
|
+
* starting state, its composition through pi-coding-agent's ModelRuntime, and
|
|
7
|
+
* attribution host gating.
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
|
-
import {
|
|
10
|
-
import { mkdtempSync, rmSync } from "node:fs";
|
|
10
|
+
import { describe, expect, test } from "bun:test";
|
|
11
|
+
import { mkdtempSync, readdirSync, readFileSync, rmSync } from "node:fs";
|
|
11
12
|
import { tmpdir } from "node:os";
|
|
12
13
|
import { join } from "node:path";
|
|
13
14
|
import { ModelRuntime } from "@earendil-works/pi-coding-agent";
|
|
14
|
-
import {
|
|
15
|
+
import { writeSessionModelsJson } from "../providers/pi-mono-adapter";
|
|
15
16
|
import {
|
|
16
17
|
DEFAULT_OPENROUTER_BASE_URL,
|
|
17
18
|
getOpenRouterAttributionHeaders,
|
|
@@ -47,162 +48,111 @@ describe("getOpenRouterBaseUrl", () => {
|
|
|
47
48
|
});
|
|
48
49
|
});
|
|
49
50
|
|
|
50
|
-
describe("
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
51
|
+
describe("writeSessionModelsJson — OpenRouter gateway (pi)", () => {
|
|
52
|
+
const MARKER = ".agent-swarm-openrouter-override.json";
|
|
53
|
+
const LEGACY = "https://legacy-gateway.example/v1";
|
|
54
|
+
const USER = "https://user.example/v1";
|
|
55
|
+
const userOpenRouter = { baseUrl: USER, compat: { supportsTools: true } };
|
|
56
|
+
const custom = { baseUrl: "https://custom.example/v1", api: "openai-completions" };
|
|
57
|
+
|
|
58
|
+
/** Starting states of the shared agent dir; the marker ones are left by earlier versions. */
|
|
59
|
+
const states: Record<string, Record<string, string>> = {
|
|
60
|
+
"no models.json": {},
|
|
61
|
+
"hand-authored baseUrl": {
|
|
62
|
+
"models.json": JSON.stringify({ providers: { openrouter: userOpenRouter, custom } }),
|
|
63
|
+
},
|
|
64
|
+
"legacy override over a user baseUrl": {
|
|
65
|
+
"models.json": JSON.stringify({
|
|
66
|
+
providers: { openrouter: { ...userOpenRouter, baseUrl: LEGACY }, custom },
|
|
67
|
+
}),
|
|
68
|
+
[MARKER]: JSON.stringify({ baseUrl: LEGACY, previous: USER }),
|
|
69
|
+
},
|
|
70
|
+
"legacy override alone": {
|
|
71
|
+
"models.json": JSON.stringify({ providers: { openrouter: { baseUrl: LEGACY } } }),
|
|
72
|
+
[MARKER]: JSON.stringify({ baseUrl: LEGACY }),
|
|
73
|
+
},
|
|
74
|
+
"legacy marker, baseUrl edited by hand since": {
|
|
75
|
+
"models.json": JSON.stringify({ providers: { openrouter: { baseUrl: USER } } }),
|
|
76
|
+
[MARKER]: JSON.stringify({ baseUrl: LEGACY }),
|
|
77
|
+
},
|
|
78
|
+
"corrupt models.json": { "models.json": "{not json" },
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// The openrouter block the session's runtime loads, per state, for the
|
|
82
|
+
// default gateway and for OPENROUTER_BASE_URL=GATEWAY. `null`: no copy, and
|
|
83
|
+
// the session loads the shared file as it is.
|
|
84
|
+
const expected: Record<string, [defaultGateway: unknown, gateway: unknown]> = {
|
|
85
|
+
"no models.json": [null, { baseUrl: GATEWAY }],
|
|
86
|
+
"hand-authored baseUrl": [null, { ...userOpenRouter, baseUrl: GATEWAY }],
|
|
87
|
+
"legacy override over a user baseUrl": [
|
|
88
|
+
userOpenRouter,
|
|
89
|
+
{ ...userOpenRouter, baseUrl: GATEWAY },
|
|
90
|
+
],
|
|
91
|
+
"legacy override alone": [undefined, { baseUrl: GATEWAY }],
|
|
92
|
+
"legacy marker, baseUrl edited by hand since": [{ baseUrl: USER }, { baseUrl: GATEWAY }],
|
|
93
|
+
"corrupt models.json": [null, { baseUrl: GATEWAY }],
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
function snapshot(dir: string): Record<string, string> {
|
|
97
|
+
return Object.fromEntries(
|
|
98
|
+
readdirSync(dir)
|
|
99
|
+
.sort()
|
|
100
|
+
.map((name) => [name, readFileSync(join(dir, name), "utf-8")]),
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
for (const [state, files] of Object.entries(states)) {
|
|
105
|
+
for (const [i, env] of [{}, { OPENROUTER_BASE_URL: GATEWAY }].entries()) {
|
|
106
|
+
const label = i === 0 ? "default gateway" : "session gateway";
|
|
107
|
+
test(`${state}, ${label}: composed privately, shared dir untouched`, async () => {
|
|
108
|
+
const dir = mkdtempSync(join(tmpdir(), "pi-or-session-"));
|
|
109
|
+
try {
|
|
110
|
+
for (const [name, text] of Object.entries(files)) await Bun.write(join(dir, name), text);
|
|
111
|
+
const before = snapshot(dir);
|
|
112
|
+
const session = await writeSessionModelsJson(dir, env);
|
|
113
|
+
try {
|
|
114
|
+
const want = expected[state]?.[i];
|
|
115
|
+
if (want === null) {
|
|
116
|
+
expect(session.modelsPath).toBeUndefined();
|
|
117
|
+
} else {
|
|
118
|
+
expect(session.modelsPath).toBeDefined();
|
|
119
|
+
expect(session.modelsPath?.startsWith(dir)).toBe(false);
|
|
120
|
+
const copy = JSON.parse(readFileSync(session.modelsPath as string, "utf-8"));
|
|
121
|
+
expect(copy.providers?.openrouter).toEqual(want);
|
|
122
|
+
if (files["models.json"]?.includes("custom.example")) {
|
|
123
|
+
expect(copy.providers.custom).toEqual(custom);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
} finally {
|
|
127
|
+
await session.dispose();
|
|
128
|
+
}
|
|
129
|
+
expect(snapshot(dir)).toEqual(before);
|
|
130
|
+
} finally {
|
|
131
|
+
rmSync(dir, { recursive: true, force: true });
|
|
132
|
+
}
|
|
117
133
|
});
|
|
118
|
-
} finally {
|
|
119
|
-
rmSync(dir, { recursive: true, force: true });
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
test("revert leaves a user-edited baseUrl alone (marker mismatch) and drops the marker", async () => {
|
|
124
|
-
const dir = mkdtempSync(join(tmpdir(), "pi-or-user-edit-"));
|
|
125
|
-
try {
|
|
126
|
-
await ensureOpenRouterModelsOverride(dir, { OPENROUTER_BASE_URL: GATEWAY });
|
|
127
|
-
// User hand-edits the file after we wrote it.
|
|
128
|
-
await Bun.write(
|
|
129
|
-
join(dir, "models.json"),
|
|
130
|
-
JSON.stringify({ providers: { openrouter: { baseUrl: "https://mine.example/v1" } } }),
|
|
131
|
-
);
|
|
132
|
-
await ensureOpenRouterModelsOverride(dir, {});
|
|
133
|
-
const kept = JSON.parse(await Bun.file(join(dir, "models.json")).text());
|
|
134
|
-
expect(kept.providers.openrouter.baseUrl).toBe("https://mine.example/v1");
|
|
135
|
-
expect(await Bun.file(join(dir, ".agent-swarm-openrouter-override.json")).exists()).toBe(
|
|
136
|
-
false,
|
|
137
|
-
);
|
|
138
|
-
} finally {
|
|
139
|
-
rmSync(dir, { recursive: true, force: true });
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
test("default env never touches a hand-authored models.json (no marker)", async () => {
|
|
144
|
-
const dir = mkdtempSync(join(tmpdir(), "pi-or-hands-off-"));
|
|
145
|
-
try {
|
|
146
|
-
const userConfig = {
|
|
147
|
-
providers: { openrouter: { baseUrl: "https://mine.example/v1" } },
|
|
148
|
-
};
|
|
149
|
-
await Bun.write(join(dir, "models.json"), JSON.stringify(userConfig));
|
|
150
|
-
await ensureOpenRouterModelsOverride(dir, {});
|
|
151
|
-
expect(JSON.parse(await Bun.file(join(dir, "models.json")).text())).toEqual(userConfig);
|
|
152
|
-
} finally {
|
|
153
|
-
rmSync(dir, { recursive: true, force: true });
|
|
154
134
|
}
|
|
155
|
-
}
|
|
135
|
+
}
|
|
156
136
|
|
|
157
|
-
test("
|
|
158
|
-
|
|
137
|
+
test("ModelRuntime composes the override onto built-in OpenRouter models", async () => {
|
|
138
|
+
// The whole point of the lever: built-in model IDs resolved through the
|
|
139
|
+
// runtime's composed store carry the gateway baseUrl.
|
|
140
|
+
const dir = mkdtempSync(join(tmpdir(), "pi-or-runtime-"));
|
|
141
|
+
const session = await writeSessionModelsJson(dir, { OPENROUTER_BASE_URL: GATEWAY });
|
|
159
142
|
try {
|
|
160
|
-
await
|
|
161
|
-
join(dir, "
|
|
162
|
-
|
|
163
|
-
providers: {
|
|
164
|
-
openrouter: { baseUrl: "https://old.example/v1", compat: { supportsTools: true } },
|
|
165
|
-
custom: { baseUrl: "https://custom.example/v1", api: "openai-completions" },
|
|
166
|
-
},
|
|
167
|
-
}),
|
|
168
|
-
);
|
|
169
|
-
await ensureOpenRouterModelsOverride(dir, { OPENROUTER_BASE_URL: GATEWAY });
|
|
170
|
-
const written = JSON.parse(await Bun.file(join(dir, "models.json")).text());
|
|
171
|
-
expect(written.providers.openrouter.baseUrl).toBe(GATEWAY);
|
|
172
|
-
expect(written.providers.openrouter.compat).toEqual({ supportsTools: true });
|
|
173
|
-
expect(written.providers.custom).toEqual({
|
|
174
|
-
baseUrl: "https://custom.example/v1",
|
|
175
|
-
api: "openai-completions",
|
|
143
|
+
const runtime = await ModelRuntime.create({
|
|
144
|
+
authPath: join(dir, "auth.json"),
|
|
145
|
+
modelsPath: session.modelsPath,
|
|
176
146
|
});
|
|
147
|
+
expect(runtime.getError()).toBeUndefined();
|
|
148
|
+
const model = runtime.getModel("openrouter", "anthropic/claude-sonnet-5");
|
|
149
|
+
expect(model?.baseUrl).toBe(GATEWAY);
|
|
177
150
|
} finally {
|
|
151
|
+
await session.dispose();
|
|
178
152
|
rmSync(dir, { recursive: true, force: true });
|
|
179
153
|
}
|
|
180
154
|
});
|
|
181
155
|
|
|
182
|
-
test("overwrites a corrupt models.json with just the override", async () => {
|
|
183
|
-
const dir = mkdtempSync(join(tmpdir(), "pi-or-corrupt-"));
|
|
184
|
-
try {
|
|
185
|
-
await Bun.write(join(dir, "models.json"), "{not json");
|
|
186
|
-
await ensureOpenRouterModelsOverride(dir, { OPENROUTER_BASE_URL: GATEWAY });
|
|
187
|
-
const written = JSON.parse(await Bun.file(join(dir, "models.json")).text());
|
|
188
|
-
expect(written).toEqual({ providers: { openrouter: { baseUrl: GATEWAY } } });
|
|
189
|
-
} finally {
|
|
190
|
-
rmSync(dir, { recursive: true, force: true });
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
test("ModelRuntime composes the override onto built-in OpenRouter models", async () => {
|
|
195
|
-
// The whole point of the lever: built-in model IDs resolved through the
|
|
196
|
-
// runtime's composed store carry the gateway baseUrl.
|
|
197
|
-
const runtime = await ModelRuntime.create({
|
|
198
|
-
authPath: join(agentDir, "auth.json"),
|
|
199
|
-
modelsPath: join(agentDir, "models.json"),
|
|
200
|
-
});
|
|
201
|
-
expect(runtime.getError()).toBeUndefined();
|
|
202
|
-
const model = runtime.getModel("openrouter", "anthropic/claude-sonnet-5");
|
|
203
|
-
expect(model?.baseUrl).toBe(GATEWAY);
|
|
204
|
-
});
|
|
205
|
-
|
|
206
156
|
test("ModelRuntime keeps the default baseUrl when no models.json exists", async () => {
|
|
207
157
|
const dir = mkdtempSync(join(tmpdir(), "pi-or-default-"));
|
|
208
158
|
try {
|
|
@@ -15,7 +15,7 @@ import { unlink } from "node:fs/promises";
|
|
|
15
15
|
import net from "node:net";
|
|
16
16
|
import { runInNewContext } from "node:vm";
|
|
17
17
|
import type { Subprocess } from "bun";
|
|
18
|
-
import { BROWSER_SDK_JS } from "../
|
|
18
|
+
import { BROWSER_SDK_JS } from "../pages/browser-sdk";
|
|
19
19
|
import { signPageSession, verifyPageSession } from "../utils/page-session";
|
|
20
20
|
import { getFreePort, SERVER_BOOT_HOOK_TIMEOUT_MS, waitForServer } from "./test-net";
|
|
21
21
|
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { describe, expect, mock, test } from "bun:test";
|
|
2
|
+
import { BROWSER_SDK_JS } from "../pages/browser-sdk";
|
|
3
|
+
|
|
4
|
+
// ─── Browser SDK tests ─────────────────────────────────────────────────
|
|
5
|
+
|
|
6
|
+
describe("BROWSER_SDK_JS", () => {
|
|
7
|
+
test("is a non-empty string", () => {
|
|
8
|
+
expect(typeof BROWSER_SDK_JS).toBe("string");
|
|
9
|
+
expect(BROWSER_SDK_JS.length).toBeGreaterThan(100);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test("contains SwarmSDK class", () => {
|
|
13
|
+
expect(BROWSER_SDK_JS).toContain("class SwarmSDK");
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("is syntactically valid JavaScript", () => {
|
|
17
|
+
expect(() => new Function(BROWSER_SDK_JS)).not.toThrow();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("exposes the eight canonical domains", () => {
|
|
21
|
+
const expectedDomains = [
|
|
22
|
+
"this.tasks",
|
|
23
|
+
"this.agents",
|
|
24
|
+
"this.events",
|
|
25
|
+
"this.memory",
|
|
26
|
+
"this.repos",
|
|
27
|
+
"this.schedules",
|
|
28
|
+
"this.approvalRequests",
|
|
29
|
+
"this.assets",
|
|
30
|
+
];
|
|
31
|
+
for (const domain of expectedDomains) {
|
|
32
|
+
expect(BROWSER_SDK_JS).toContain(domain);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("removed domains are NOT exposed (messages, services, slack)", () => {
|
|
37
|
+
const removed = ["this.messages", "this.services", "this.slack", "postMessage", "readMessages"];
|
|
38
|
+
for (const r of removed) {
|
|
39
|
+
expect(BROWSER_SDK_JS).not.toContain(r);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("assigns SwarmSDK class + swarmSdk singleton to window", () => {
|
|
44
|
+
expect(BROWSER_SDK_JS).toContain("window.SwarmSDK = SwarmSDK");
|
|
45
|
+
expect(BROWSER_SDK_JS).toContain("window.swarmSdk = new SwarmSDK()");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("routes calls through the /@swarm/api/* proxy", () => {
|
|
49
|
+
expect(BROWSER_SDK_JS).toContain("const base = '/@swarm/api'");
|
|
50
|
+
// Sentinel endpoints — one per domain.
|
|
51
|
+
expect(BROWSER_SDK_JS).toContain("'/tasks'");
|
|
52
|
+
expect(BROWSER_SDK_JS).toContain("'/agents'");
|
|
53
|
+
expect(BROWSER_SDK_JS).toContain("'/events'");
|
|
54
|
+
expect(BROWSER_SDK_JS).toContain("'/memory/search'");
|
|
55
|
+
expect(BROWSER_SDK_JS).toContain("'/repos'");
|
|
56
|
+
expect(BROWSER_SDK_JS).toContain("'/schedules'");
|
|
57
|
+
expect(BROWSER_SDK_JS).toContain("'/approval-requests'");
|
|
58
|
+
expect(BROWSER_SDK_JS).toContain("'/assets'");
|
|
59
|
+
expect(BROWSER_SDK_JS).toContain("'/assets/key-audit'");
|
|
60
|
+
expect(BROWSER_SDK_JS).toContain("'/assets/mappings'");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("does not fetch on SDK load or construction", () => {
|
|
64
|
+
const fetchMock = mock(() => Promise.resolve(Response.json({})));
|
|
65
|
+
const window = {};
|
|
66
|
+
const loadSdk = new Function("window", "fetch", `${BROWSER_SDK_JS}\nreturn window.SwarmSDK;`);
|
|
67
|
+
|
|
68
|
+
const SwarmSDK = loadSdk(window, fetchMock);
|
|
69
|
+
expect(window).toHaveProperty("swarmSdk");
|
|
70
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
71
|
+
|
|
72
|
+
new SwarmSDK();
|
|
73
|
+
new SwarmSDK();
|
|
74
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -12,11 +12,11 @@ import {
|
|
|
12
12
|
type Server,
|
|
13
13
|
type ServerResponse,
|
|
14
14
|
} from "node:http";
|
|
15
|
-
import { feedbackOverlayScripts, isPageFeedbackRequested } from "../artifact-sdk/feedback-overlay";
|
|
16
15
|
import { closeDb, createAgent, getTaskById, initDb } from "../be/db";
|
|
17
16
|
import { handlePages } from "../http/pages";
|
|
18
17
|
import { handlePagesPublic } from "../http/pages-public";
|
|
19
18
|
import { getPathSegments, parseQueryParams } from "../http/utils";
|
|
19
|
+
import { feedbackOverlayScripts, isPageFeedbackRequested } from "../pages/feedback-overlay";
|
|
20
20
|
import { listenOnFreePort } from "./test-net";
|
|
21
21
|
|
|
22
22
|
const TEST_DB_PATH = "./test-pages-feedback.sqlite";
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
2
|
+
import { unlink } from "node:fs/promises";
|
|
3
|
+
import {
|
|
4
|
+
closeDb,
|
|
5
|
+
createAgent,
|
|
6
|
+
createTaskExtended,
|
|
7
|
+
createUser,
|
|
8
|
+
initDb,
|
|
9
|
+
pauseTask,
|
|
10
|
+
startTask,
|
|
11
|
+
} from "../be/db";
|
|
12
|
+
import { handleTasks } from "../http/tasks";
|
|
13
|
+
|
|
14
|
+
const TEST_DB_PATH = "./test-paused-tasks-requested-by.sqlite";
|
|
15
|
+
const AGENT_ID = "50000000-0000-4000-8000-000000000001";
|
|
16
|
+
|
|
17
|
+
async function removeDbFiles(path: string): Promise<void> {
|
|
18
|
+
for (const suffix of ["", "-wal", "-shm"]) {
|
|
19
|
+
try {
|
|
20
|
+
await unlink(path + suffix);
|
|
21
|
+
} catch (error) {
|
|
22
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function getPausedTasks(): Promise<{
|
|
28
|
+
status: number;
|
|
29
|
+
body: { tasks: Array<{ id: string; requestedBy?: Record<string, unknown> }> };
|
|
30
|
+
}> {
|
|
31
|
+
let status = 200;
|
|
32
|
+
let bodyStr = "";
|
|
33
|
+
const req = {
|
|
34
|
+
method: "GET",
|
|
35
|
+
url: "/api/paused-tasks",
|
|
36
|
+
headers: { "x-agent-id": AGENT_ID },
|
|
37
|
+
} as unknown as Parameters<typeof handleTasks>[0];
|
|
38
|
+
const res = {
|
|
39
|
+
setHeader() {},
|
|
40
|
+
writeHead(code: number) {
|
|
41
|
+
status = code;
|
|
42
|
+
},
|
|
43
|
+
end(body?: string) {
|
|
44
|
+
bodyStr = body ?? "";
|
|
45
|
+
},
|
|
46
|
+
} as unknown as Parameters<typeof handleTasks>[1];
|
|
47
|
+
const handled = await handleTasks(
|
|
48
|
+
req,
|
|
49
|
+
res,
|
|
50
|
+
["api", "paused-tasks"],
|
|
51
|
+
new URLSearchParams(),
|
|
52
|
+
AGENT_ID,
|
|
53
|
+
);
|
|
54
|
+
if (!handled) throw new Error("handleTasks did not handle the request");
|
|
55
|
+
return { status, body: JSON.parse(bodyStr) };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
beforeAll(async () => {
|
|
59
|
+
await removeDbFiles(TEST_DB_PATH);
|
|
60
|
+
initDb(TEST_DB_PATH);
|
|
61
|
+
await createAgent({ id: AGENT_ID, name: "Paused Worker", isLead: false, status: "idle" });
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
afterAll(async () => {
|
|
65
|
+
closeDb();
|
|
66
|
+
await removeDbFiles(TEST_DB_PATH);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe("GET /api/paused-tasks requester projection", () => {
|
|
70
|
+
test("carries the same requestedBy a task_assigned trigger carries", async () => {
|
|
71
|
+
const user = await createUser({
|
|
72
|
+
name: "Paused Requester",
|
|
73
|
+
email: "paused@example.com",
|
|
74
|
+
role: "CTO",
|
|
75
|
+
notes: "Paused requester notes",
|
|
76
|
+
});
|
|
77
|
+
const withUser = await createTaskExtended("Paused task with requester", {
|
|
78
|
+
agentId: AGENT_ID,
|
|
79
|
+
requestedByUserId: user.id,
|
|
80
|
+
});
|
|
81
|
+
const withoutUser = await createTaskExtended("Paused task without requester", {
|
|
82
|
+
agentId: AGENT_ID,
|
|
83
|
+
});
|
|
84
|
+
for (const task of [withUser, withoutUser]) {
|
|
85
|
+
await startTask(task.id);
|
|
86
|
+
expect(await pauseTask(task.id)).not.toBeNull();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const { status, body } = await getPausedTasks();
|
|
90
|
+
expect(status).toBe(200);
|
|
91
|
+
const byId = new Map(body.tasks.map((task) => [task.id, task]));
|
|
92
|
+
expect(byId.get(withUser.id)?.requestedBy).toMatchObject({
|
|
93
|
+
id: user.id,
|
|
94
|
+
name: "Paused Requester",
|
|
95
|
+
email: "paused@example.com",
|
|
96
|
+
role: "CTO",
|
|
97
|
+
notes: "Paused requester notes",
|
|
98
|
+
});
|
|
99
|
+
expect(byId.get(withoutUser.id)).toBeDefined();
|
|
100
|
+
expect(byId.get(withoutUser.id)?.requestedBy).toBeUndefined();
|
|
101
|
+
});
|
|
102
|
+
});
|