@desplega.ai/agent-swarm 1.173.0 → 2.1.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-kr4abrsk.js} +17 -16
- package/dist/{actions-sgeyv08k.js → actions-eh4g6w2r.js} +39 -34
- package/dist/{amp-adapter-gpxp3kc9.js → amp-adapter-7h7wdt8q.js} +8 -7
- package/dist/{app-km9qnv2f.js → app-7k8s9w5p.js} +18 -14
- package/dist/{assistant-5r8tcvvd.js → assistant-wswt2y7p.js} +40 -35
- package/dist/{boot-reembed-16rv9843.js → boot-reembed-9apm9qw6.js} +10 -9
- package/dist/{boot-reembed-swwzdgd7.js → boot-reembed-whzbdthj.js} +10 -9
- package/dist/{boot-scrub-sweep-zkr6vkqb.js → boot-scrub-sweep-ndzmvmgt.js} +11 -10
- package/dist/{boot-seeds-d8sp49t2.js → boot-seeds-e9sta0yt.js} +16 -13
- package/dist/{claude-adapter-yzzzh8jp.js → claude-adapter-4mqd3r6r.js} +19 -18
- package/dist/{claude-managed-adapter-xgxhqrt2.js → claude-managed-adapter-9wswb52m.js} +7 -5
- package/dist/{claude-managed-setup-bnmjarjc.js → claude-managed-setup-tpe1effx.js} +6 -4
- package/dist/{claude-sdk-session-4whxere4.js → claude-sdk-session-q49ghmh9.js} +12402 -11850
- package/dist/{cli-ne7smj1b.js → cli-01xvj1rq.js} +10 -10
- package/dist/{cli-nwrdx840.js → cli-035vekcw.js} +3 -3
- package/dist/{cli-9xf7fdtp.js → cli-0pd8hzhc.js} +29 -6
- package/dist/{cli-tmhsxhb8.js → cli-0veb92a3.js} +28 -6
- package/dist/{cli-c3rk1v0x.js → cli-10jesr25.js} +3 -3
- package/dist/{cli-6thk4jz3.js → cli-1y7yywmj.js} +6 -6
- package/dist/{cli-6r2f7edk.js → cli-20krdhz5.js} +47 -19
- package/dist/{cli-rfs4vddv.js → cli-29rja109.js} +39 -20
- package/dist/cli-2m1ktfzz.js +17795 -0
- package/dist/{cli-na2pxe4f.js → cli-2pwa0q4d.js} +15 -4
- package/dist/{cli-7a93cv8c.js → cli-4mengw46.js} +441 -43
- package/dist/{cli-94b48jhk.js → cli-5twdke5x.js} +16 -14
- package/dist/{cli-7a0sde2x.js → cli-6cagzc96.js} +9 -0
- package/dist/cli-6cvfxs8r.js +47 -0
- package/dist/{cli-w1apjecb.js → cli-711vwbxn.js} +2 -2
- package/dist/{cli-r4fqfpwf.js → cli-78vv7446.js} +50 -27
- package/dist/{cli-srqq3gc5.js → cli-7n6s5man.js} +5 -5
- package/dist/{cli-m89t9jdf.js → cli-7x0g4sn1.js} +15416 -16024
- package/dist/{cli-2bw3w47j.js → cli-8wwxw8a3.js} +1 -1
- package/dist/{cli-yatxqynm.js → cli-9ha6hs2d.js} +1 -290
- package/dist/{cli-ywm1azg9.js → cli-9jzdj90z.js} +8 -4
- package/dist/cli-9traj3dn.js +638 -0
- package/dist/{cli-smcrrmhy.js → cli-agzebbd5.js} +9 -9
- package/dist/{cli-c5km5znr.js → cli-c8r3kzzb.js} +1 -1
- package/dist/cli-cvv9k0hh.js +10143 -0
- package/dist/{cli-anzdk0z4.js → cli-d9286m9h.js} +2 -2
- package/dist/cli-e3g5mbkz.js +776 -0
- package/dist/cli-f527ce74.js +781 -0
- package/dist/{cli-gmdajfw5.js → cli-gxzkg1de.js} +5020 -1729
- package/dist/{cli-451ks6aw.js → cli-h1arqn31.js} +1 -1
- package/dist/cli-hjwmhcg1.js +10622 -0
- package/dist/{cli-jpc4r224.js → cli-hqr88ew6.js} +19 -3
- package/dist/{cli-h7k0xtry.js → cli-hvqbtna4.js} +4 -1256
- package/dist/{cli-cd0hv9nh.js → cli-j67mh2vg.js} +2 -2
- package/dist/cli-jta2eyzc.js +619 -0
- package/dist/{cli-wd0hz6ah.js → cli-kbrt8gqa.js} +9 -91
- package/dist/cli-kceah1eh.js +92 -0
- package/dist/{cli-d603qf86.js → cli-ksphgh48.js} +10 -8
- package/dist/{cli-3nxdmjrc.js → cli-mgz4nbgg.js} +4 -4
- package/dist/{cli-saqyage0.js → cli-mz5as5ha.js} +179 -27
- package/dist/cli-n2wq4j7m.js +6985 -0
- package/dist/{cli-ppy4nnnh.js → cli-n8ndw8j7.js} +2 -2
- package/dist/{cli-1w0vj3gw.js → cli-naqzr17j.js} +7 -7
- package/dist/{cli-0a7mm3zp.js → cli-nm1p8jfr.js} +25 -21
- package/dist/{cli-wrh08zjd.js → cli-p3zbc1rw.js} +5 -18
- package/dist/{cli-hsem16hb.js → cli-pe05f8yk.js} +29 -6
- package/dist/{cli-5wpw4rab.js → cli-qmzsbkvy.js} +5 -5
- package/dist/{cli-bv2etw2y.js → cli-qnm4e9e4.js} +99 -590
- package/dist/{cli-1x7da0de.js → cli-qqy4q48b.js} +8 -8
- package/dist/{cli-r44t9wqc.js → cli-r106725k.js} +2 -2
- package/dist/{cli-5er5gjbm.js → cli-r35rw5de.js} +15 -15
- package/dist/{cli-v3zhnxsb.js → cli-r610p9bs.js} +2 -2
- package/dist/{cli-7jykv7ef.js → cli-rc6pf5kv.js} +1 -1
- package/dist/{cli-x119kdba.js → cli-rdxm77k3.js} +1 -1
- package/dist/cli-rhs9pz33.js +294 -0
- package/dist/{cli-0p3enk35.js → cli-s69nfyeh.js} +9 -1
- package/dist/{cli-0kxf81jj.js → cli-sawv63e8.js} +2 -2
- package/dist/{cli-e3pmyt39.js → cli-sg19gbf0.js} +1 -1
- package/dist/{cli-d161g69q.js → cli-shk7r5cs.js} +57 -6
- package/dist/{cli-7pz3b7x5.js → cli-sksetb2q.js} +36 -1
- package/dist/{cli-014vns2y.js → cli-stfs0k5s.js} +208 -525
- package/dist/{cli-db3m6zdt.js → cli-t02z59f2.js} +1 -1
- package/dist/{cli-b07hr3bt.js → cli-txqb9s38.js} +2 -2
- package/dist/{cli-s8z6x4b4.js → cli-v7g551te.js} +225 -113
- package/dist/{cli-rfz2ve55.js → cli-vgqzp4em.js} +3341 -955
- package/dist/{cli-7avy4v8w.js → cli-w1q7h2r2.js} +5 -3
- package/dist/{cli-rcp5aqdw.js → cli-w7m81tb7.js} +1 -1
- package/dist/cli-wfnemr1t.js +86 -0
- package/dist/cli-wmgrkscj.js +23254 -0
- package/dist/{cli-qm1448f9.js → cli-wms6x9db.js} +8 -8
- package/dist/{cli-tadfgd0t.js → cli-x1r545x8.js} +33 -4
- package/dist/{cli-xc7p7s3g.js → cli-xczsgshn.js} +6 -6
- package/dist/{cli-fmhj1ehj.js → cli-y3167wt1.js} +882 -271
- package/dist/{cli-r0byd61h.js → cli-yjmqpxt3.js} +17 -16
- package/dist/{cli-81r1wjyv.js → cli-z780ec7c.js} +1 -1
- package/dist/{cli-phcqp1b9.js → cli-zm9t2vde.js} +2 -2
- package/dist/cli.js +34 -49
- package/dist/{codex-adapter-r3dn4fp5.js → codex-adapter-4z16v9n0.js} +15 -14
- package/dist/{codex-login-841kv5fk.js → codex-login-pkepy2j1.js} +3 -2
- package/dist/{codex-session-runner-fwt793h4.js → codex-session-runner-rvms9y2x.js} +15 -14
- package/dist/{commands-z8fq9qna.js → commands-2a9rtyat.js} +11 -10
- package/dist/{controller-hq5ggpcp.js → controller-3k3madbn.js} +5 -4
- package/dist/{controllers-connect-3mf2btyj.js → controllers-connect-v9yn8zb9.js} +42 -37
- package/dist/{cursor-adapter-q8pgv5qa.js → cursor-adapter-98gbhyzn.js} +7 -6
- package/dist/{db-qfpq4bt7.js → db-eywwsnjw.js} +30 -9
- package/dist/{db-yyhqqrbf.js → db-ncypk9np.js} +12 -11
- package/dist/{devin-adapter-xgtj680j.js → devin-adapter-zeq8b9gz.js} +3 -2
- package/dist/{dispatcher-x0x7qfcq.js → dispatcher-w94kc575.js} +38 -33
- package/dist/{docker-f503k9p0.js → docker-b1e4cqab.js} +3 -2
- package/dist/{dsh-adapter-34etvb5m.js → dsh-adapter-47ztej36.js} +7 -6
- package/dist/{e2b-66xra4az.js → e2b-bbt8ey45.js} +4 -3
- package/dist/{e2b-xwhyj8cw.js → e2b-dp804sen.js} +4 -3
- package/dist/{event-bus-vjtv82gm.js → event-bus-j2jdg3qb.js} +3 -2
- package/dist/{external-channel-h1fjecwe.js → external-channel-zrkg8ar9.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-s9ncp9vy.js} +18 -17
- package/dist/{handlers-znb9n7d4.js → handlers-kqftvjgw.js} +41 -36
- package/dist/{heartbeat-bm4qkn8d.js → heartbeat-9q96p8v9.js} +43 -38
- package/dist/{hook-254r4mza.js → hook-56qe10dp.js} +11 -23
- package/dist/{hook-xaa3q80s.js → hook-yy6rw42k.js} +12 -11
- package/dist/{http-3dgrgp8y.js → http-r2dnb8f8.js} +2411 -275
- package/dist/{index-fx3jqq1e.js → index-3gtmkhc6.js} +38 -33
- package/dist/{index-bdsqa799.js → index-3k5hkz7r.js} +43 -38
- package/dist/index-52j1aja4.js +3465 -0
- package/dist/{index-g2byveez.js → index-5e101240.js} +38 -33
- package/dist/{index-2ybk7khy.js → index-5wyfjc2c.js} +845 -683
- 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-jd7sejm2.js → index-vxvhxxbn.js} +10 -9
- package/dist/index-we27kd31.js +12810 -0
- package/dist/{keepalive-6ywhpja1.js → keepalive-psj6j54c.js} +12 -11
- package/dist/{lead-cw45n21s.js → lead-hajz4688.js} +61 -56
- package/dist/{maintenance-ss1a1ge9.js → maintenance-ps02sb5m.js} +10 -9
- package/dist/{node-fkajaq96.js → node-mwb5r697.js} +2 -1
- package/dist/{oauth-refresh-sweep-3c6f8vyy.js → oauth-refresh-sweep-0284h93b.js} +11 -10
- package/dist/{onboard-8hzdhc1k.js → onboard-rbf6bg01.js} +11 -3
- package/dist/{opencode-adapter-qbchbcfz.js → opencode-adapter-13dcatgq.js} +18 -13
- package/dist/{otel-impl-e6mnvc4v.js → otel-impl-zvqcbhp7.js} +242 -868
- package/dist/{pi-mono-adapter-3c9g9snh.js → pi-mono-adapter-xxy47q4v.js} +244 -106
- package/dist/{pricing-refresh-gedkjdw0.js → pricing-refresh-kefx9v3t.js} +12 -11
- package/dist/{rbac-roles-dt4vh1yy.js → rbac-roles-3m36rxm2.js} +11 -10
- package/dist/{rbac-roles-eszdg6pm.js → rbac-roles-z76xv0hq.js} +12 -11
- package/dist/{render-v2-907cy6sa.js → render-v2-wz73bs25.js} +18 -14
- package/dist/{resume-42stcaz0.js → resume-k4wzge8w.js} +25 -21
- package/dist/{runtime-qfry88v2.js → runtime-sa2vr16g.js} +2 -2
- package/dist/{setup-adbc1c10.js → setup-2fp2pypp.js} +2 -2
- package/dist/{virtual-modules-rtmt494q.js → virtual-modules-9gkxqrap.js} +6 -5
- package/dist/{work-objects-wpj8ck95.js → work-objects-6k0ywz3m.js} +41 -36
- package/dist/{worker-bzdra9rf.js → worker-5v3rxsdh.js} +61 -56
- package/dist/{x-qfmygqqv.js → x-4mtdeym1.js} +3 -2
- package/openapi.json +1279 -329
- package/package.json +15 -15
- package/src/agentmail/handlers.ts +21 -11
- package/src/apps/definition.ts +16 -0
- package/src/azure-devops/api.ts +52 -2
- package/src/azure-devops/boards-sync.ts +50 -20
- package/src/azure-devops/index.ts +1 -0
- package/src/azure-devops/outbound.ts +95 -0
- package/src/be/boot-seeds.ts +11 -0
- package/src/be/config-refs.ts +60 -0
- package/src/be/db/model-profiles.ts +315 -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 +237 -0
- package/src/be/db/tasks/write.ts +7 -0
- package/src/be/db.ts +87 -36
- 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/migrations/219_relabel_pi_context_compactions.sql +22 -0
- package/src/be/migrations/220_model_profiles.sql +137 -0
- package/src/be/model-catalog-store.ts +5 -1
- package/src/be/model-tier-resolution.ts +155 -10
- package/src/be/model-validation.ts +12 -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 +22 -8
- package/src/be/secret-registry.ts +6 -2
- 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-model-profiles.ts +41 -0
- package/src/be/seed-pricing.ts +19 -2
- package/src/be/seed-scripts/catalog/heartbeat-jev-gate.ts +368 -0
- 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-scripts/index.ts +9 -0
- 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 +53 -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 +193 -71
- package/src/e2b/env.ts +5 -0
- package/src/extensions/catalog.generated.json +17 -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/loader.ts +10 -5
- package/src/extensions/post-bridge.ts +40 -0
- package/src/github/handlers.ts +11 -4
- package/src/heartbeat/heartbeat.ts +2 -1
- package/src/heartbeat/templates.ts +8 -5
- package/src/hooks/hook.ts +0 -26
- package/src/http/agents.ts +49 -2
- package/src/http/all-routes.ts +1 -1
- package/src/http/apps.ts +30 -3
- package/src/http/config.ts +62 -19
- package/src/http/core.ts +8 -0
- package/src/http/index.ts +25 -3
- package/src/http/integrations.ts +88 -1
- package/src/http/mcp.ts +15 -5
- package/src/http/model-profiles.ts +303 -0
- package/src/http/page-proxy.ts +2 -4
- package/src/http/pages-public.ts +13 -7
- package/src/http/pages.ts +3 -13
- package/src/http/poll.ts +42 -71
- package/src/http/requested-by.ts +54 -0
- package/src/http/script-runs.ts +1 -1
- package/src/http/scripts.ts +26 -2
- package/src/http/stats.ts +1 -30
- package/src/http/tasks.ts +79 -5
- package/src/http/utils.ts +14 -0
- package/src/http/webhooks.ts +160 -3
- 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/{artifact-sdk → pages}/feedback-overlay.ts +65 -6
- 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 +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/pi-mono-extension.ts +17 -3
- package/src/providers/pricing-sources.md +33 -2
- package/src/providers/reasoning-effort.ts +37 -0
- package/src/providers/types.ts +6 -2
- package/src/rbac/legacy-policy.ts +16 -2
- package/src/rbac/permissions.ts +26 -0
- package/src/scheduler/deferred-task-waits.ts +18 -5
- package/src/scheduler/scheduler.ts +11 -3
- package/src/scripts-runtime/executors/quickjs-prelude.ts +75 -6
- package/src/scripts-runtime/executors/quickjs-runner.ts +85 -8
- package/src/scripts-runtime/executors/quickjs-web-shims.ts +1041 -0
- package/src/scripts-runtime/extract-args-schema.ts +9 -8
- package/src/scripts-runtime/loader.ts +19 -7
- package/src/scripts-runtime/quickjs-compat.ts +464 -0
- package/src/scripts-runtime/sdk-allowlist.ts +12 -6
- package/src/scripts-runtime/swarm-sdk.ts +0 -11
- package/src/scripts-runtime/types/stdlib.d.ts +39 -15
- package/src/scripts-runtime/types/swarm-extension.d.ts +66 -1
- package/src/scripts-runtime/types/swarm-sdk.d.ts +39 -15
- package/src/server.ts +62 -19
- package/src/slack/blocks.ts +4 -2
- package/src/slack/event-dedup.ts +1 -1
- package/src/slack/reaction-shortcode.ts +14 -0
- package/src/slack/render-v2.ts +12 -7
- package/src/slack/responses.ts +5 -4
- package/src/slack/thread-buffer.ts +41 -22
- 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 +15 -33
- package/src/tests/acp-adapter.test.ts +42 -0
- package/src/tests/agentmail-filters.test.ts +47 -0
- package/src/tests/api-drain-worker-e2e.test.ts +1 -6
- package/src/tests/apps-spike2.test.ts +35 -0
- package/src/tests/azure-devops-outbound.test.ts +420 -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 +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 +22 -0
- package/src/tests/db-task-write-contract.test.ts +11 -0
- package/src/tests/deferred-task-wake.test.ts +78 -1
- 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/env-flag.test.ts +1 -0
- package/src/tests/explicit-model-validation.test.ts +105 -1
- package/src/tests/extensions-catalog.test.ts +1 -0
- package/src/tests/extensions-example-comms-adherence-gate.test.ts +413 -0
- package/src/tests/extensions-example-route-chat-channel.test.ts +154 -0
- package/src/tests/extensions-loader.test.ts +15 -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/heartbeat-jev-gate-credentials.test.ts +168 -0
- package/src/tests/heartbeat-jev-gate.test.ts +385 -0
- 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-profile-backcompat.test.ts +327 -0
- package/src/tests/model-profile-providers.test.ts +302 -0
- package/src/tests/model-profile-runtime-secrets.test.ts +223 -0
- package/src/tests/model-profiles-api.test.ts +614 -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-keys.test.ts +121 -0
- package/src/tests/pages-feedback.test.ts +4 -1
- package/src/tests/paused-tasks-requested-by.test.ts +102 -0
- package/src/tests/pi-context-compaction.test.ts +193 -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/profile-claim.test.ts +269 -0
- package/src/tests/prompt-template-remaining.test.ts +35 -0
- package/src/tests/quickjs-compat.test.ts +213 -0
- package/src/tests/rbac-engine.test.ts +32 -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 +6 -0
- package/src/tests/script-executor-conformance.test.ts +252 -1
- package/src/tests/script-executor-quickjs.test.ts +96 -0
- package/src/tests/script-runs-http.test.ts +89 -0
- package/src/tests/scripts-http.test.ts +217 -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 +96 -0
- package/src/tests/seed-model-profiles.test.ts +174 -0
- package/src/tests/seed-scripts.test.ts +2 -2
- 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/skill-invocations.test.ts +4 -2
- package/src/tests/slack-blocks.test.ts +36 -0
- package/src/tests/slack-inline-output.test.ts +14 -0
- package/src/tests/slack-reaction-config.test.ts +20 -0
- package/src/tests/slack-render-v2.test.ts +37 -0
- package/src/tests/slack-thread-buffer.test.ts +98 -1
- 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-llm.test.ts +61 -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/model-profiles/list-model-profiles.ts +54 -0
- package/src/tools/register-chat-channel.ts +139 -0
- package/src/tools/request-human-input.ts +11 -3
- package/src/tools/script-apis.ts +5 -1
- package/src/tools/script-upsert.ts +4 -1
- package/src/tools/send-task.ts +48 -1
- 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/system-one-decide.ts +104 -0
- package/src/tools/task-action.ts +22 -1
- package/src/tools/tool-config.ts +14 -6
- package/src/types.ts +48 -3
- 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 +86 -10
- package/src/utils/task-tool-manifest.ts +19 -2
- package/src/utils/vcs-repo-match.ts +29 -0
- package/src/workflows/executors/raw-llm.ts +8 -3
- package/src/workflows/executors/swarm-script.ts +1 -0
- package/src/workflows/executors/system-one-decision.ts +21 -11
- package/src/workflows/executors/workflow-llm.ts +22 -0
- package/src/workflows/index.ts +21 -12
- package/templates/extensions/README.md +1 -0
- package/templates/extensions/comms-adherence-gate/README.md +88 -0
- package/templates/extensions/comms-adherence-gate/hooks.ts +489 -0
- package/templates/extensions/comms-adherence-gate/manifest.json +8 -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-42qhyvwx.js +0 -33
- 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
|
@@ -3,7 +3,7 @@ import { unlink } from "node:fs/promises";
|
|
|
3
3
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
4
4
|
import { Readable } from "node:stream";
|
|
5
5
|
import { createApp } from "../apps/store";
|
|
6
|
-
import { closeDb, createAgent, getDbClient, initDb } from "../be/db";
|
|
6
|
+
import { closeDb, createAgent, getDbClient, initDb, upsertSwarmConfig } from "../be/db";
|
|
7
7
|
import { getScript, listScripts } from "../be/scripts/db";
|
|
8
8
|
import { setScriptEmbeddingProviderForTests } from "../be/scripts/embeddings";
|
|
9
9
|
import { configSecretName, registerStoredSecret } from "../be/secret-registry";
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
} from "../utils/secret-scrubber";
|
|
17
17
|
import { SKIP_SANDBOX_SPAWN_TESTS } from "./sandbox-spawn-test-helpers";
|
|
18
18
|
import { randomToken } from "./synthetic-secret-helpers";
|
|
19
|
+
import { CHILD_PROCESS_TEST_BUDGET_MS } from "./test-proc";
|
|
19
20
|
|
|
20
21
|
const skip = test.skipIf(SKIP_SANDBOX_SPAWN_TESTS);
|
|
21
22
|
|
|
@@ -241,6 +242,22 @@ describe("/api/scripts HTTP", () => {
|
|
|
241
242
|
expect(await second.json()).toEqual({ name: "double", version: 2, contentDeduped: false });
|
|
242
243
|
});
|
|
243
244
|
|
|
245
|
+
test("upsert saves a quickjs-incompatible script and returns advisory warnings", async () => {
|
|
246
|
+
const res = await upsert({
|
|
247
|
+
name: "uses-process",
|
|
248
|
+
// The script typings omit process, but a cast still typechecks.
|
|
249
|
+
source:
|
|
250
|
+
"export default async (): Promise<string> => (globalThis as any).process.env.HOME ?? '';",
|
|
251
|
+
});
|
|
252
|
+
expect(res.status).toBe(200);
|
|
253
|
+
expect(await res.json()).toEqual({
|
|
254
|
+
name: "uses-process",
|
|
255
|
+
version: 1,
|
|
256
|
+
contentDeduped: false,
|
|
257
|
+
warnings: ["quickjs compat (process, line 1): process is not available under quickjs"],
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
244
261
|
test("upsert typecheck failures return diagnostics and do not write rows", async () => {
|
|
245
262
|
const res = await upsert({
|
|
246
263
|
name: "bad-types",
|
|
@@ -737,3 +754,202 @@ describe("script source with an embedded secret is refused at write", () => {
|
|
|
737
754
|
expect(response.status).toBe(200);
|
|
738
755
|
});
|
|
739
756
|
});
|
|
757
|
+
|
|
758
|
+
// Regression: ctx.swarm.config.get used to return undefined for every key,
|
|
759
|
+
// because no run entry point filled the payload's user config. These go
|
|
760
|
+
// through the real POST /api/scripts/run route, not an injected payload.
|
|
761
|
+
describe("ctx.swarm.config.get through POST /api/scripts/run", () => {
|
|
762
|
+
const secretValue = `cfg-secret-${randomToken(32)}`;
|
|
763
|
+
// Under the global scrubber's 12-char floor, so only the run's own
|
|
764
|
+
// output boundary can redact it.
|
|
765
|
+
const shortSecretValue = randomToken(10);
|
|
766
|
+
const readerSource = `
|
|
767
|
+
import type { ScriptContext } from "swarm-sdk";
|
|
768
|
+
export default async (_args: unknown, ctx: ScriptContext) => {
|
|
769
|
+
const read = (key: string) => {
|
|
770
|
+
const value = ctx.swarm.config.get(key);
|
|
771
|
+
return value === undefined ? null : ctx.stdlib.Redacted.value(value);
|
|
772
|
+
};
|
|
773
|
+
const secret = ctx.swarm.config.get("SCRIPT_CFG_SECRET");
|
|
774
|
+
console.log("secret:", secret === undefined ? "missing" : ctx.stdlib.Redacted.value(secret));
|
|
775
|
+
return {
|
|
776
|
+
plain: read("SCRIPT_CFG_PLAIN"),
|
|
777
|
+
override: read("SCRIPT_CFG_OVERRIDE"),
|
|
778
|
+
// Compare by shape: the literal value in source would be refused at write.
|
|
779
|
+
secretMatches:
|
|
780
|
+
secret !== undefined &&
|
|
781
|
+
ctx.stdlib.Redacted.value(secret).startsWith("cfg-secret-") &&
|
|
782
|
+
ctx.stdlib.Redacted.value(secret).length === ${secretValue.length},
|
|
783
|
+
secretIsSecret: secret !== undefined && ctx.stdlib.Redacted.isSecret(secret),
|
|
784
|
+
secretStringified: String(secret),
|
|
785
|
+
missingIsUndefined: ctx.swarm.config.get("SCRIPT_CFG_NOT_SET") === undefined,
|
|
786
|
+
fallback: read("SCRIPT_CFG_NOT_SET") ?? "fallback",
|
|
787
|
+
apiOnly: read("SLACK_SIGNING_SECRET"),
|
|
788
|
+
proto: read("__proto__"),
|
|
789
|
+
};
|
|
790
|
+
};`;
|
|
791
|
+
|
|
792
|
+
type Reader = {
|
|
793
|
+
plain: string | null;
|
|
794
|
+
override: string | null;
|
|
795
|
+
secretMatches: boolean;
|
|
796
|
+
secretIsSecret: boolean;
|
|
797
|
+
secretStringified: string;
|
|
798
|
+
missingIsUndefined: boolean;
|
|
799
|
+
fallback: string;
|
|
800
|
+
apiOnly: string | null;
|
|
801
|
+
proto: string | null;
|
|
802
|
+
};
|
|
803
|
+
|
|
804
|
+
beforeAll(async () => {
|
|
805
|
+
await upsertSwarmConfig({ scope: "global", key: "SCRIPT_CFG_PLAIN", value: "plain-global" });
|
|
806
|
+
await upsertSwarmConfig({ scope: "global", key: "SCRIPT_CFG_OVERRIDE", value: "from-global" });
|
|
807
|
+
await upsertSwarmConfig({
|
|
808
|
+
scope: "agent",
|
|
809
|
+
scopeId: workerId,
|
|
810
|
+
key: "SCRIPT_CFG_OVERRIDE",
|
|
811
|
+
value: "from-worker",
|
|
812
|
+
});
|
|
813
|
+
await upsertSwarmConfig({
|
|
814
|
+
scope: "global",
|
|
815
|
+
key: "SCRIPT_CFG_SECRET",
|
|
816
|
+
value: secretValue,
|
|
817
|
+
isSecret: true,
|
|
818
|
+
});
|
|
819
|
+
await upsertSwarmConfig({
|
|
820
|
+
scope: "global",
|
|
821
|
+
key: "SCRIPT_CFG_SHORT_SECRET",
|
|
822
|
+
value: shortSecretValue,
|
|
823
|
+
isSecret: true,
|
|
824
|
+
});
|
|
825
|
+
await upsertSwarmConfig({ scope: "global", key: "__proto__", value: "proto-global" });
|
|
826
|
+
await upsertSwarmConfig({
|
|
827
|
+
scope: "global",
|
|
828
|
+
key: "SLACK_SIGNING_SECRET",
|
|
829
|
+
value: `signing-${randomToken(24)}`,
|
|
830
|
+
isSecret: true,
|
|
831
|
+
});
|
|
832
|
+
});
|
|
833
|
+
|
|
834
|
+
afterAll(async () => {
|
|
835
|
+
await getDbClient().run(
|
|
836
|
+
"DELETE FROM swarm_config WHERE key LIKE 'SCRIPT_CFG_%' OR key IN ('SLACK_SIGNING_SECRET', '__proto__')",
|
|
837
|
+
);
|
|
838
|
+
clearVolatileSecretsForTesting();
|
|
839
|
+
});
|
|
840
|
+
|
|
841
|
+
async function run(body: Record<string, unknown>, agentId: string) {
|
|
842
|
+
const response = await dispatch("/api/scripts/run", {
|
|
843
|
+
method: "POST",
|
|
844
|
+
agentId,
|
|
845
|
+
body: JSON.stringify(body),
|
|
846
|
+
});
|
|
847
|
+
expect(response.status).toBe(200);
|
|
848
|
+
return (await response.json()) as { result: Reader; stdout: string; error?: string };
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
skip(
|
|
852
|
+
"inline run reads existing keys, agent scope over global, and undefined for a missing key",
|
|
853
|
+
async () => {
|
|
854
|
+
const out = await run({ source: readerSource, intent: "config get regression" }, workerId);
|
|
855
|
+
expect(out.error).toBeUndefined();
|
|
856
|
+
expect(out.result).toEqual({
|
|
857
|
+
plain: "plain-global",
|
|
858
|
+
override: "from-worker",
|
|
859
|
+
secretMatches: true,
|
|
860
|
+
secretIsSecret: true,
|
|
861
|
+
secretStringified: "<redacted>",
|
|
862
|
+
missingIsUndefined: true,
|
|
863
|
+
fallback: "fallback",
|
|
864
|
+
apiOnly: null,
|
|
865
|
+
proto: "proto-global",
|
|
866
|
+
});
|
|
867
|
+
// The unwrapped secret is scrubbed from everything the run returns.
|
|
868
|
+
expect(out.stdout).toContain("secret:");
|
|
869
|
+
expect(JSON.stringify(out)).not.toContain(secretValue);
|
|
870
|
+
},
|
|
871
|
+
CHILD_PROCESS_TEST_BUDGET_MS,
|
|
872
|
+
);
|
|
873
|
+
|
|
874
|
+
skip(
|
|
875
|
+
"named run resolves config for the invoking agent",
|
|
876
|
+
async () => {
|
|
877
|
+
expect((await upsert({ name: "config-reader", source: readerSource }, leadId)).status).toBe(
|
|
878
|
+
200,
|
|
879
|
+
);
|
|
880
|
+
const out = await run({ name: "config-reader" }, leadId);
|
|
881
|
+
expect(out.error).toBeUndefined();
|
|
882
|
+
// The worker's agent-scope override does not leak to another agent.
|
|
883
|
+
expect(out.result.override).toBe("from-global");
|
|
884
|
+
expect(out.result.plain).toBe("plain-global");
|
|
885
|
+
expect(out.result.secretMatches).toBe(true);
|
|
886
|
+
expect(out.result.missingIsUndefined).toBe(true);
|
|
887
|
+
},
|
|
888
|
+
CHILD_PROCESS_TEST_BUDGET_MS,
|
|
889
|
+
);
|
|
890
|
+
|
|
891
|
+
skip(
|
|
892
|
+
"a short secret the run read stays out of stdout, result and the stored run row",
|
|
893
|
+
async () => {
|
|
894
|
+
// No `secret:` label in the log line: that prefix triggers structural
|
|
895
|
+
// masking and would hide a missing value-level redaction.
|
|
896
|
+
const source = `
|
|
897
|
+
import type { ScriptContext } from "swarm-sdk";
|
|
898
|
+
export default async (_args: unknown, ctx: ScriptContext) => {
|
|
899
|
+
const raw = ctx.stdlib.Redacted.value(ctx.swarm.config.get("SCRIPT_CFG_SHORT_SECRET")!);
|
|
900
|
+
console.log("read:", raw);
|
|
901
|
+
return { leaked: raw, length: raw.length, [raw]: "as-key" };
|
|
902
|
+
};`;
|
|
903
|
+
const response = await dispatch("/api/scripts/run", {
|
|
904
|
+
method: "POST",
|
|
905
|
+
agentId: workerId,
|
|
906
|
+
body: JSON.stringify({ source, intent: "short config secret redaction" }),
|
|
907
|
+
});
|
|
908
|
+
expect(response.status).toBe(200);
|
|
909
|
+
const out = (await response.json()) as {
|
|
910
|
+
result: Record<string, unknown>;
|
|
911
|
+
stdout: string;
|
|
912
|
+
error?: string;
|
|
913
|
+
};
|
|
914
|
+
expect(out.error).toBeUndefined();
|
|
915
|
+
// The script really read the value: only its rendering is redacted.
|
|
916
|
+
expect(out.result.length).toBe(shortSecretValue.length);
|
|
917
|
+
expect(out.stdout).toContain("read: [REDACTED:config:SCRIPT_CFG_SHORT_SECRET]");
|
|
918
|
+
expect(out.stdout).not.toContain(shortSecretValue);
|
|
919
|
+
expect(out.result.leaked).toBe("[REDACTED:config:SCRIPT_CFG_SHORT_SECRET]");
|
|
920
|
+
expect(JSON.stringify(out.result)).not.toContain(shortSecretValue);
|
|
921
|
+
|
|
922
|
+
const row = await getDbClient().get<{ output: string | null }>(
|
|
923
|
+
"SELECT output FROM script_runs WHERE agentId = ? AND source = ?",
|
|
924
|
+
[workerId, source],
|
|
925
|
+
);
|
|
926
|
+
expect(row?.output).toBeTruthy();
|
|
927
|
+
expect(row?.output).toContain("[REDACTED:config:SCRIPT_CFG_SHORT_SECRET]");
|
|
928
|
+
expect(row?.output).not.toContain(shortSecretValue);
|
|
929
|
+
},
|
|
930
|
+
CHILD_PROCESS_TEST_BUDGET_MS,
|
|
931
|
+
);
|
|
932
|
+
|
|
933
|
+
test("an import rejection quoting a short secret stays out of stderr and the stored run row", async () => {
|
|
934
|
+
// The import check returns before any process spawns, so its diagnostic
|
|
935
|
+
// needs the run-local scrub too.
|
|
936
|
+
const source = `import nope from "${shortSecretValue}"; export default async () => nope;`;
|
|
937
|
+
const response = await dispatch("/api/scripts/run", {
|
|
938
|
+
method: "POST",
|
|
939
|
+
agentId: workerId,
|
|
940
|
+
body: JSON.stringify({ source, intent: "short config secret in import diagnostic" }),
|
|
941
|
+
});
|
|
942
|
+
expect(response.status).toBe(200);
|
|
943
|
+
const out = (await response.json()) as { stderr: string; error?: string };
|
|
944
|
+
expect(out.error).toBe("import_violation");
|
|
945
|
+
expect(out.stderr).toContain("[REDACTED:config:SCRIPT_CFG_SHORT_SECRET]");
|
|
946
|
+
expect(out.stderr).not.toContain(shortSecretValue);
|
|
947
|
+
|
|
948
|
+
const row = await getDbClient().get<{ error: string | null }>(
|
|
949
|
+
"SELECT error FROM script_runs WHERE agentId = ? AND source = ?",
|
|
950
|
+
[workerId, source],
|
|
951
|
+
);
|
|
952
|
+
expect(row?.error).toBeTruthy();
|
|
953
|
+
expect(row?.error).not.toContain(shortSecretValue);
|
|
954
|
+
});
|
|
955
|
+
});
|
|
@@ -10,7 +10,7 @@ import type { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/se
|
|
|
10
10
|
import { closeDb, createAgent, getDbClient, initDb, upsertSwarmConfig } from "../be/db";
|
|
11
11
|
import { _resetAutoReloadForTests, flushPendingIntegrationsReload } from "../http/core";
|
|
12
12
|
import { handleMcp } from "../http/mcp";
|
|
13
|
-
import { getBasePrompt } from "../prompts/base-prompt";
|
|
13
|
+
import { getBasePrompt, getTaskContextPrompt } from "../prompts/base-prompt";
|
|
14
14
|
import { createServer } from "../server";
|
|
15
15
|
import { resolveScriptsOnlyMode } from "../utils/scripts-only-mode";
|
|
16
16
|
import { listenOnFreePort } from "./test-net";
|
|
@@ -371,9 +371,8 @@ describe("scripts-only prompt gating", () => {
|
|
|
371
371
|
});
|
|
372
372
|
|
|
373
373
|
test("uses the scripts-only Slack variant for Slack-originated tasks", async () => {
|
|
374
|
-
const prompt = await
|
|
374
|
+
const prompt = await getTaskContextPrompt({
|
|
375
375
|
role: "worker",
|
|
376
|
-
agentId: "scripts-only-slack-agent",
|
|
377
376
|
scriptsOnly: true,
|
|
378
377
|
slackContext: { channelId: "C123", threadTs: "123.456" },
|
|
379
378
|
});
|
|
@@ -96,54 +96,72 @@ describe("runScript", () => {
|
|
|
96
96
|
expect(output.result).toEqual({ status: 200, parsed: { ok: true } });
|
|
97
97
|
});
|
|
98
98
|
|
|
99
|
-
spawnTest(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
99
|
+
spawnTest(
|
|
100
|
+
"ctx.swarm bridge respects URL overrides and PORT fallback",
|
|
101
|
+
async () => {
|
|
102
|
+
const entries = new Map<string, unknown>();
|
|
103
|
+
const agentId = crypto.randomUUID();
|
|
104
|
+
const server = Bun.serve({
|
|
105
|
+
hostname: "localhost",
|
|
106
|
+
port: 0,
|
|
107
|
+
async fetch(req) {
|
|
108
|
+
expect(req.headers.get("authorization")).toBe(
|
|
109
|
+
"Bearer example-runtime-test-secret-1234567890",
|
|
110
|
+
);
|
|
111
|
+
expect(req.headers.get("x-agent-id")).toBe(agentId);
|
|
112
|
+
|
|
113
|
+
const url = new URL(req.url);
|
|
114
|
+
if (req.method === "PUT" && url.pathname.startsWith("/api/kv/")) {
|
|
115
|
+
const key = decodeURIComponent(url.pathname.slice("/api/kv/".length));
|
|
116
|
+
const body = (await req.json()) as { value: unknown };
|
|
117
|
+
entries.set(key, body.value);
|
|
118
|
+
return Response.json({ key, value: body.value });
|
|
119
|
+
}
|
|
120
|
+
if (req.method === "GET" && url.pathname.startsWith("/api/kv/")) {
|
|
121
|
+
const key = decodeURIComponent(url.pathname.slice("/api/kv/".length));
|
|
122
|
+
return Response.json({ key, value: entries.get(key) ?? null });
|
|
123
|
+
}
|
|
124
|
+
return Response.json({ error: "not found" }, { status: 404 });
|
|
125
|
+
},
|
|
126
|
+
});
|
|
108
127
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
return Response.json({ error: "not found" }, { status: 404 });
|
|
121
|
-
},
|
|
122
|
-
});
|
|
128
|
+
try {
|
|
129
|
+
const baseUrl = `http://localhost:${server.port}`;
|
|
130
|
+
for (const urlSource of ["input", "MCP_BASE_URL", "PORT"] as const) {
|
|
131
|
+
delete process.env.PORT;
|
|
132
|
+
process.env.MCP_BASE_URL = "http://unused.invalid";
|
|
133
|
+
if (urlSource === "MCP_BASE_URL") {
|
|
134
|
+
process.env.MCP_BASE_URL = baseUrl;
|
|
135
|
+
} else if (urlSource === "PORT") {
|
|
136
|
+
delete process.env.MCP_BASE_URL;
|
|
137
|
+
process.env.PORT = String(server.port);
|
|
138
|
+
}
|
|
123
139
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
source: `
|
|
140
|
+
const output = await runScript({
|
|
141
|
+
agentId,
|
|
142
|
+
mcpBaseUrl: urlSource === "input" ? baseUrl : undefined,
|
|
143
|
+
resources,
|
|
144
|
+
source: `
|
|
130
145
|
export default async (_args, ctx) => {
|
|
131
146
|
await ctx.swarm.kv_set({ key: "bridge-smoke", value: { ok: true } });
|
|
132
147
|
return await ctx.swarm.kv_get({ key: "bridge-smoke" });
|
|
133
148
|
};
|
|
134
149
|
`,
|
|
135
|
-
|
|
150
|
+
});
|
|
136
151
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
152
|
+
expect(output.error, `${urlSource}: ${output.stderr}`).toBeUndefined();
|
|
153
|
+
expect(output.result, urlSource).toEqual({
|
|
154
|
+
success: true,
|
|
155
|
+
status: 200,
|
|
156
|
+
data: { key: "bridge-smoke", value: { ok: true } },
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
} finally {
|
|
160
|
+
server.stop(true);
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
CHILD_PROCESS_TEST_BUDGET_MS,
|
|
164
|
+
);
|
|
147
165
|
|
|
148
166
|
spawnTest("ctx.swarm.kv_get forwards path/offset/limit as a view query", async () => {
|
|
149
167
|
const seen: string[] = [];
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
+
import { openAICompatibleKeyVars } from "@desplega/model-catalog";
|
|
2
3
|
import {
|
|
3
4
|
clearVolatileSecretsForTesting,
|
|
5
|
+
createValueScrubber,
|
|
4
6
|
isSensitiveKey,
|
|
5
7
|
refreshSecretScrubberCache,
|
|
8
|
+
registerEnvSecrets,
|
|
6
9
|
registerSensitiveKeyName,
|
|
7
10
|
registerVolatileSecret,
|
|
11
|
+
SCRUBBER_RULES_VERSION,
|
|
8
12
|
scrubObject,
|
|
9
13
|
scrubSecrets,
|
|
10
14
|
unregisterVolatileSecret,
|
|
@@ -77,6 +81,33 @@ describe("scrubSecrets — env-based replacement", () => {
|
|
|
77
81
|
expect(out).toBe("key=[REDACTED:FOO_SERVICE_API_KEY] tail");
|
|
78
82
|
});
|
|
79
83
|
|
|
84
|
+
test("redacts NaN and Helmcode keys, including the suffix-less HELMCODE_API_NAN alias", () => {
|
|
85
|
+
const values = {
|
|
86
|
+
NAN_API_KEY: `example-nan-${randomToken(24)}`,
|
|
87
|
+
HELMCODE_API_NAN: `example-nanalias-${randomToken(24)}`,
|
|
88
|
+
HELMCODE_API_KEY: `example-helm-${randomToken(24)}`,
|
|
89
|
+
};
|
|
90
|
+
for (const [name, value] of Object.entries(values)) process.env[name] = value;
|
|
91
|
+
refreshSecretScrubberCache();
|
|
92
|
+
for (const [name, value] of Object.entries(values)) {
|
|
93
|
+
expect(isSensitiveKey(name)).toBe(true);
|
|
94
|
+
expect(scrubSecrets(`Bearer ${value} tail`)).toBe(`Bearer [REDACTED:${name}] tail`);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("the registry key vars are pinned to SCRUBBER_RULES_VERSION", () => {
|
|
99
|
+
// Every registry key var is an exact sensitive key, so a new registry
|
|
100
|
+
// entry changes the rules: bump SCRUBBER_RULES_VERSION (the boot sweep
|
|
101
|
+
// re-redacts stored rows once per version), then update this pin.
|
|
102
|
+
expect({
|
|
103
|
+
version: SCRUBBER_RULES_VERSION,
|
|
104
|
+
keyVars: [...openAICompatibleKeyVars()].sort(),
|
|
105
|
+
}).toEqual({
|
|
106
|
+
version: 8,
|
|
107
|
+
keyVars: ["AI_GATEWAY_API_KEY", "HELMCODE_API_KEY", "HELMCODE_API_NAN", "NAN_API_KEY"],
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
80
111
|
test("redacts any key with _TOKEN suffix", () => {
|
|
81
112
|
process.env.WEIRD_SERVICE_TOKEN = "example-weirdserviceTOKENvalue_1234567890";
|
|
82
113
|
refreshSecretScrubberCache();
|
|
@@ -390,6 +421,33 @@ describe("registerVolatileSecret", () => {
|
|
|
390
421
|
expect(out).not.toContain(secret);
|
|
391
422
|
});
|
|
392
423
|
|
|
424
|
+
test("registerEnvSecrets covers a per-task env that never reached process.env", () => {
|
|
425
|
+
const picked = `opaque${randomToken(20)}`;
|
|
426
|
+
const member = `opaque${randomToken(20)}`;
|
|
427
|
+
const env = {
|
|
428
|
+
HELMCODE_API_KEY: picked,
|
|
429
|
+
HELMCODE_API_NAN: `${member},opaque${randomToken(20)}`,
|
|
430
|
+
HELMCODE_BASE_URL: "http://127.0.0.1:9/v1",
|
|
431
|
+
};
|
|
432
|
+
expect(process.env.HELMCODE_API_KEY).not.toBe(picked);
|
|
433
|
+
expect(scrubSecrets(`echo ${picked}`)).toContain(picked);
|
|
434
|
+
registerEnvSecrets(env);
|
|
435
|
+
expect(scrubSecrets(`echo ${picked}`)).toBe("echo [REDACTED:HELMCODE_API_KEY]");
|
|
436
|
+
expect(scrubSecrets(`echo ${member}`)).toBe("echo [REDACTED:HELMCODE_API_NAN]");
|
|
437
|
+
// A non-secret key stays readable.
|
|
438
|
+
expect(scrubSecrets("http://127.0.0.1:9/v1")).toBe("http://127.0.0.1:9/v1");
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
test("padded singleton values redact their trimmed form", () => {
|
|
442
|
+
const fromEnv = `opaque${randomToken(20)}`;
|
|
443
|
+
const fromMap = `opaque${randomToken(20)}`;
|
|
444
|
+
process.env.PADDED_API_KEY = ` ${fromEnv}\n`;
|
|
445
|
+
refreshSecretScrubberCache();
|
|
446
|
+
registerEnvSecrets({ HELMCODE_API_NAN: ` ${fromMap} ` });
|
|
447
|
+
expect(scrubSecrets(`key: ${fromEnv}`)).toBe("key: [REDACTED:PADDED_API_KEY]");
|
|
448
|
+
expect(scrubSecrets(`key: ${fromMap}`)).toBe("key: [REDACTED:HELMCODE_API_NAN]");
|
|
449
|
+
});
|
|
450
|
+
|
|
393
451
|
test("ignores values shorter than the minimum length", () => {
|
|
394
452
|
registerVolatileSecret("short", "TOO_SHORT");
|
|
395
453
|
const out = scrubSecrets("contains short somewhere");
|
|
@@ -889,3 +947,41 @@ describe("scrubSecrets — over-redaction guards", () => {
|
|
|
889
947
|
{ retry: 2 },
|
|
890
948
|
);
|
|
891
949
|
});
|
|
950
|
+
|
|
951
|
+
describe("createValueScrubber (per-run exact values)", () => {
|
|
952
|
+
test("redacts values under the global floor without registering them swarm-wide", () => {
|
|
953
|
+
const short = randomToken(5);
|
|
954
|
+
const scrub = createValueScrubber([{ value: short, name: "config:SHORT" }]);
|
|
955
|
+
expect(scrub.text(`got ${short}!`)).toBe("got [REDACTED:config:SHORT]!");
|
|
956
|
+
expect(scrubSecrets(`got ${short}!`)).toBe(`got ${short}!`);
|
|
957
|
+
});
|
|
958
|
+
|
|
959
|
+
test("a one-char value is redacted everywhere in the run's output", () => {
|
|
960
|
+
const scrub = createValueScrubber([{ value: "x", name: "config:ONE" }]);
|
|
961
|
+
expect(scrub.text("x+x")).toBe("[REDACTED:config:ONE]+[REDACTED:config:ONE]");
|
|
962
|
+
});
|
|
963
|
+
|
|
964
|
+
test("one pass, longest first: a marker is never rewritten by another value", () => {
|
|
965
|
+
const scrub = createValueScrubber([
|
|
966
|
+
{ value: "RED", name: "config:A" },
|
|
967
|
+
{ value: "REDACT", name: "config:B" },
|
|
968
|
+
]);
|
|
969
|
+
expect(scrub.text("RED REDACT")).toBe("[REDACTED:config:A] [REDACTED:config:B]");
|
|
970
|
+
});
|
|
971
|
+
|
|
972
|
+
test("covers JSON-escaped forms, object keys and nested values", () => {
|
|
973
|
+
const value = 'a"b';
|
|
974
|
+
const scrub = createValueScrubber([{ value, name: "config:Q" }]);
|
|
975
|
+
expect(scrub.text(JSON.stringify({ v: value }))).toBe('{"v":"[REDACTED:config:Q]"}');
|
|
976
|
+
expect(scrub.object({ [value]: [value, 1, null] })).toEqual({
|
|
977
|
+
"[REDACTED:config:Q]": ["[REDACTED:config:Q]", 1, null],
|
|
978
|
+
});
|
|
979
|
+
});
|
|
980
|
+
|
|
981
|
+
test("no values and empty values are a no-op", () => {
|
|
982
|
+
const scrub = createValueScrubber([{ value: "", name: "config:EMPTY" }]);
|
|
983
|
+
expect(scrub.text("unchanged")).toBe("unchanged");
|
|
984
|
+
const obj = { a: 1 };
|
|
985
|
+
expect(scrub.object(obj)).toBe(obj);
|
|
986
|
+
});
|
|
987
|
+
});
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// The shipped `<harness>-<tier>` seeds, and migration 220's profiles made from
|
|
2
|
+
// agents' MODEL_OVERRIDE + REASONING_EFFORT_OVERRIDE.
|
|
3
|
+
|
|
4
|
+
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
5
|
+
import { readFileSync } from "node:fs";
|
|
6
|
+
import { unlink } from "node:fs/promises";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import {
|
|
9
|
+
closeDb,
|
|
10
|
+
createAgent,
|
|
11
|
+
getDb,
|
|
12
|
+
getDbClient,
|
|
13
|
+
getSwarmConfigs,
|
|
14
|
+
initDb,
|
|
15
|
+
upsertSwarmConfig,
|
|
16
|
+
} from "../be/db";
|
|
17
|
+
import { getModelProfileByName, listModelProfiles } from "../be/db/model-profiles";
|
|
18
|
+
import { recordClaimModelResolution } from "../be/model-tier-resolution";
|
|
19
|
+
import { seedModelProfiles } from "../be/seed-model-profiles";
|
|
20
|
+
import { DEFAULT_MODEL_TIER_MAP, type MODEL_TIERS, type ProviderName } from "../types";
|
|
21
|
+
|
|
22
|
+
const TEST_DB_PATH = "./test-seed-model-profiles.sqlite";
|
|
23
|
+
|
|
24
|
+
async function removeDbFiles(path: string): Promise<void> {
|
|
25
|
+
for (const suffix of ["", "-wal", "-shm"]) {
|
|
26
|
+
try {
|
|
27
|
+
await unlink(path + suffix);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The part of migration 220 that turns agent overrides into profiles. */
|
|
35
|
+
function agentOverrideMigrationSql(): string {
|
|
36
|
+
const sql = readFileSync(
|
|
37
|
+
join(import.meta.dir, "../be/migrations/220_model_profiles.sql"),
|
|
38
|
+
"utf8",
|
|
39
|
+
);
|
|
40
|
+
return sql.slice(sql.indexOf("CREATE TEMP TABLE _model_profile_overrides"));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
beforeAll(async () => {
|
|
44
|
+
await removeDbFiles(TEST_DB_PATH);
|
|
45
|
+
initDb(TEST_DB_PATH);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
afterAll(async () => {
|
|
49
|
+
closeDb();
|
|
50
|
+
await removeDbFiles(TEST_DB_PATH);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
beforeEach(async () => {
|
|
54
|
+
const client = getDbClient();
|
|
55
|
+
// References first: migration 220's triggers refuse deleting a named profile.
|
|
56
|
+
await client.run("DELETE FROM agent_tasks");
|
|
57
|
+
await client.run("DELETE FROM swarm_config");
|
|
58
|
+
await client.run("DELETE FROM swarm_repos");
|
|
59
|
+
await client.run("DELETE FROM agents");
|
|
60
|
+
await client.run("DELETE FROM model_profiles");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe("tier seeds", () => {
|
|
64
|
+
test("one profile per harness and tier, copied from the tier map; none for acp", async () => {
|
|
65
|
+
expect(await seedModelProfiles()).toBe(40);
|
|
66
|
+
const profiles = await listModelProfiles();
|
|
67
|
+
expect(profiles).toHaveLength(40);
|
|
68
|
+
expect(profiles.some((p) => p.harness === "acp")).toBe(false);
|
|
69
|
+
for (const profile of profiles) {
|
|
70
|
+
const tier = profile.seededFrom?.replace("tier:", "") as (typeof MODEL_TIERS)[number];
|
|
71
|
+
expect(profile.name).toBe(`${profile.harness}-${tier}`);
|
|
72
|
+
expect(profile.model).toBe(DEFAULT_MODEL_TIER_MAP[profile.harness as ProviderName][tier]);
|
|
73
|
+
expect(profile.effort).toBeNull();
|
|
74
|
+
}
|
|
75
|
+
expect((await getModelProfileByName("claude-smart"))?.model).toBe("opus");
|
|
76
|
+
expect((await getModelProfileByName("codex-ultra"))?.model).toBe("gpt-5.6-sol");
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("a global MODEL_TIER row overrides the seed's model; reseeding never overwrites", async () => {
|
|
80
|
+
await upsertSwarmConfig({
|
|
81
|
+
scope: "global",
|
|
82
|
+
key: "MODEL_TIER_CLAUDE_SMART",
|
|
83
|
+
value: "claude-opus-5-5",
|
|
84
|
+
});
|
|
85
|
+
await seedModelProfiles();
|
|
86
|
+
expect((await getModelProfileByName("claude-smart"))?.model).toBe("claude-opus-5-5");
|
|
87
|
+
|
|
88
|
+
await getDbClient().run(
|
|
89
|
+
"UPDATE model_profiles SET model = 'edited' WHERE name = 'claude-regular'",
|
|
90
|
+
);
|
|
91
|
+
expect(await seedModelProfiles()).toBe(0);
|
|
92
|
+
expect((await getModelProfileByName("claude-regular"))?.model).toBe("edited");
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe("migration 220: profiles from agent overrides", () => {
|
|
97
|
+
async function agentWith(
|
|
98
|
+
name: string,
|
|
99
|
+
harnessProvider: ProviderName,
|
|
100
|
+
model?: string,
|
|
101
|
+
effort?: string,
|
|
102
|
+
): Promise<string> {
|
|
103
|
+
const agent = await createAgent({ name, isLead: false, status: "idle", harnessProvider });
|
|
104
|
+
if (model) {
|
|
105
|
+
await upsertSwarmConfig({
|
|
106
|
+
scope: "agent",
|
|
107
|
+
scopeId: agent.id,
|
|
108
|
+
key: "MODEL_OVERRIDE",
|
|
109
|
+
value: model,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
if (effort) {
|
|
113
|
+
await upsertSwarmConfig({
|
|
114
|
+
scope: "agent",
|
|
115
|
+
scopeId: agent.id,
|
|
116
|
+
key: "REASONING_EFFORT_OVERRIDE",
|
|
117
|
+
value: effort,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
return agent.id;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
test("dedupes by (harness, model, effort) and makes each the agent's default", async () => {
|
|
124
|
+
const a = await agentWith("m-a", "claude", "claude-opus-5-5", "high");
|
|
125
|
+
const b = await agentWith("m-b", "claude", "claude-opus-5-5", "high");
|
|
126
|
+
const c = await agentWith("m-c", "codex", "gpt-6.1-sol", "xhigh");
|
|
127
|
+
const d = await agentWith("m-d", "claude", "openrouter/vendor/model:free");
|
|
128
|
+
const skipped = [
|
|
129
|
+
await agentWith("m-acp", "acp", "some-acp-model"),
|
|
130
|
+
await agentWith("m-alias", "claude", "latest:anthropic/opus"),
|
|
131
|
+
await agentWith("m-none", "claude"),
|
|
132
|
+
];
|
|
133
|
+
|
|
134
|
+
getDb().exec(agentOverrideMigrationSql());
|
|
135
|
+
|
|
136
|
+
const profiles = await listModelProfiles();
|
|
137
|
+
expect(profiles.map((p) => [p.name, p.harness, p.model, p.effort])).toEqual([
|
|
138
|
+
["claude-openrouter-vendor-model-free", "claude", "openrouter/vendor/model:free", null],
|
|
139
|
+
["claude-opus-5-5-high", "claude", "claude-opus-5-5", "high"],
|
|
140
|
+
["codex-gpt-6.1-sol-xhigh", "codex", "gpt-6.1-sol", "xhigh"],
|
|
141
|
+
]);
|
|
142
|
+
expect(profiles.every((p) => p.seededFrom === "agent-config" && p.allowCustomModel)).toBe(true);
|
|
143
|
+
|
|
144
|
+
const defaults = await getSwarmConfigs({ scope: "agent", key: "MODEL_PROFILE" });
|
|
145
|
+
expect(Object.fromEntries(defaults.map((row) => [row.scopeId, row.value]))).toEqual({
|
|
146
|
+
[a]: "claude-opus-5-5-high",
|
|
147
|
+
[b]: "claude-opus-5-5-high",
|
|
148
|
+
[c]: "codex-gpt-6.1-sol-xhigh",
|
|
149
|
+
[d]: "claude-openrouter-vendor-model-free",
|
|
150
|
+
});
|
|
151
|
+
expect(defaults.every((row) => row.exportToEnv === false)).toBe(true);
|
|
152
|
+
expect(defaults.some((row) => skipped.includes(row.scopeId ?? ""))).toBe(false);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("an overridden agent's untiered task resolves to the same model and effort as before", async () => {
|
|
156
|
+
const id = await agentWith("m-same", "codex", "gpt-6.1-sol", "xhigh");
|
|
157
|
+
getDb().exec(agentOverrideMigrationSql());
|
|
158
|
+
const task = await getDbClient().get<{ id: string }>(
|
|
159
|
+
`INSERT INTO agent_tasks (id, agentId, task, status, source, createdAt, lastUpdatedAt)
|
|
160
|
+
VALUES ('t-same', ?, 'work', 'pending', 'mcp', '2026-01-01', '2026-01-01') RETURNING id`,
|
|
161
|
+
[id],
|
|
162
|
+
);
|
|
163
|
+
const fields = await recordClaimModelResolution(
|
|
164
|
+
{ id: task?.id ?? "" },
|
|
165
|
+
{ id, harnessProvider: "codex" },
|
|
166
|
+
);
|
|
167
|
+
expect(fields).toMatchObject({
|
|
168
|
+
resolvedModel: "gpt-6.1-sol",
|
|
169
|
+
modelSource: "profile:agent-default",
|
|
170
|
+
resolvedProfile: "codex-gpt-6.1-sol-xhigh",
|
|
171
|
+
effort: "xhigh",
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
});
|