@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,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs the real runner boot in a child process up to the legacy paused-task
|
|
3
|
+
* resume, and records the prompts the provider receives for that session.
|
|
4
|
+
*/
|
|
5
|
+
import { mock } from "bun:test";
|
|
6
|
+
import { writeFileSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import type { ProviderSessionConfig } from "../../providers/types";
|
|
9
|
+
|
|
10
|
+
const directory = process.argv[2]!;
|
|
11
|
+
const pausedTaskId = "44444444-4444-4444-8444-444444444444";
|
|
12
|
+
const REQUESTER_SENTINEL = "RESUME_REQUESTER_SENTINEL_7f3a";
|
|
13
|
+
const MISSING_DIR = "/nonexistent/legacy-resume-dir-7f3a";
|
|
14
|
+
|
|
15
|
+
const resolver = await import("../../prompts/resolver");
|
|
16
|
+
mock.module("../../prompts/resolver", () => ({
|
|
17
|
+
...resolver,
|
|
18
|
+
configureHttpResolver() {},
|
|
19
|
+
}));
|
|
20
|
+
const profileSync = await import("../../commands/profile-sync");
|
|
21
|
+
mock.module("../../commands/profile-sync", () => ({
|
|
22
|
+
...profileSync,
|
|
23
|
+
SOUL_MD_PATH: join(directory, "SOUL.md"),
|
|
24
|
+
IDENTITY_MD_PATH: join(directory, "IDENTITY.md"),
|
|
25
|
+
TOOLS_MD_PATH: join(directory, "TOOLS.md"),
|
|
26
|
+
HEARTBEAT_MD_PATH: join(directory, "HEARTBEAT.md"),
|
|
27
|
+
CLAUDE_MD_PATH: join(directory, "CLAUDE.md"),
|
|
28
|
+
WORKSPACE_CLAUDE_MD_PATH: join(directory, "CLAUDE.md"),
|
|
29
|
+
SETUP_SCRIPT_PATH: join(directory, "start-up.sh"),
|
|
30
|
+
IDENTITY_BASELINES_PATH: join(directory, "baselines.json"),
|
|
31
|
+
async readIdentityBaselines() {
|
|
32
|
+
return {};
|
|
33
|
+
},
|
|
34
|
+
async writeProfileFileFromDb() {
|
|
35
|
+
return null;
|
|
36
|
+
},
|
|
37
|
+
async writeIdentityBaselines() {},
|
|
38
|
+
async syncProfileFilesToServer() {},
|
|
39
|
+
async prependProfileSyncRejectionBanner(prompt: string) {
|
|
40
|
+
return { prompt, injected: false };
|
|
41
|
+
},
|
|
42
|
+
}));
|
|
43
|
+
mock.module("../../utils/skills-refresh", () => ({
|
|
44
|
+
async refreshSkillsIfChanged() {
|
|
45
|
+
return { changed: false };
|
|
46
|
+
},
|
|
47
|
+
}));
|
|
48
|
+
const credentials = await import("../../utils/credentials");
|
|
49
|
+
mock.module("../../utils/credentials", () => ({
|
|
50
|
+
...credentials,
|
|
51
|
+
async resolveCredentialPools() {
|
|
52
|
+
return [];
|
|
53
|
+
},
|
|
54
|
+
}));
|
|
55
|
+
|
|
56
|
+
mock.module("../../providers/index", () => ({
|
|
57
|
+
async createProviderAdapter() {
|
|
58
|
+
return {
|
|
59
|
+
name: "pi",
|
|
60
|
+
traits: { hasMcp: true, hasLocalEnvironment: true, nativeSkillDiscovery: false },
|
|
61
|
+
formatCommand(name: string, args: string) {
|
|
62
|
+
return `/${name} ${args}`;
|
|
63
|
+
},
|
|
64
|
+
async createSession(config: ProviderSessionConfig) {
|
|
65
|
+
writeFileSync(
|
|
66
|
+
join(directory, "result.json"),
|
|
67
|
+
JSON.stringify({
|
|
68
|
+
taskId: config.taskId,
|
|
69
|
+
prompt: config.prompt,
|
|
70
|
+
systemPrompt: config.systemPrompt,
|
|
71
|
+
}),
|
|
72
|
+
);
|
|
73
|
+
// runAgent runs forever; the first spawn is the resumed task.
|
|
74
|
+
process.exit(0);
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
},
|
|
78
|
+
}));
|
|
79
|
+
|
|
80
|
+
// All requests terminate here. The paused-tasks endpoint carries the
|
|
81
|
+
// requester projection the API attaches, as on a task_assigned trigger.
|
|
82
|
+
globalThis.fetch = (async (input: string | URL | Request) => {
|
|
83
|
+
const url = new URL(input instanceof Request ? input.url : input.toString());
|
|
84
|
+
if (url.pathname === "/me") {
|
|
85
|
+
return Response.json({ name: "Fixture agent", soulMd: "Fixture soul" });
|
|
86
|
+
}
|
|
87
|
+
if (url.pathname === "/api/paused-tasks") {
|
|
88
|
+
return Response.json({
|
|
89
|
+
tasks: [
|
|
90
|
+
{
|
|
91
|
+
id: pausedTaskId,
|
|
92
|
+
task: "Resume the paused fixture task",
|
|
93
|
+
status: "paused",
|
|
94
|
+
dir: MISSING_DIR,
|
|
95
|
+
attachments: [],
|
|
96
|
+
requestedBy: {
|
|
97
|
+
id: "user-1",
|
|
98
|
+
name: "Fixture Requester",
|
|
99
|
+
email: "requester@example.com",
|
|
100
|
+
role: "CTO",
|
|
101
|
+
notes: REQUESTER_SENTINEL,
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
if (url.pathname === "/api/poll") return new Promise(() => {});
|
|
108
|
+
if (url.pathname === "/api/config/resolved") return Response.json({ configs: [] });
|
|
109
|
+
if (url.pathname === "/api/agents") return Response.json({ enabledCapabilities: [] });
|
|
110
|
+
if (url.pathname === "/cancelled-tasks") return Response.json({ cancelled: [] });
|
|
111
|
+
if (url.pathname.startsWith("/api/tasks/")) {
|
|
112
|
+
return Response.json({ success: true, task: { id: pausedTaskId, status: "in_progress" } });
|
|
113
|
+
}
|
|
114
|
+
return Response.json({ enabled: false, recovered: 0, memories: [], configs: [] });
|
|
115
|
+
}) as typeof fetch;
|
|
116
|
+
|
|
117
|
+
process.env.MAX_CONCURRENT_TASKS = "1";
|
|
118
|
+
const { runAgent } = await import("../../commands/runner");
|
|
119
|
+
await runAgent(
|
|
120
|
+
{ role: "worker", defaultPrompt: "Fixture", metadataType: "worker_metadata" },
|
|
121
|
+
{ logsDir: join(directory, "logs"), systemPrompt: "Additional fixture instructions" },
|
|
122
|
+
);
|
|
@@ -421,3 +421,38 @@ describe("no github email enrichment", () => {
|
|
|
421
421
|
expect(await getKv(UNMAPPED_NAMESPACE, "triage-target:count")).toBeNull();
|
|
422
422
|
});
|
|
423
423
|
});
|
|
424
|
+
|
|
425
|
+
// ── Every no-task comment path logs why ──
|
|
426
|
+
|
|
427
|
+
describe("comment skip reasons", () => {
|
|
428
|
+
async function skippedReasons(events: CommentEvent[]): Promise<string[]> {
|
|
429
|
+
const logSpy = spyOn(console, "log").mockImplementation(() => {});
|
|
430
|
+
try {
|
|
431
|
+
for (const event of events) {
|
|
432
|
+
expect((await handleComment(event, "issue_comment")).created).toBe(false);
|
|
433
|
+
}
|
|
434
|
+
return logSpy.mock.calls
|
|
435
|
+
.map((args) => String(args[0]))
|
|
436
|
+
.filter((line) => line.startsWith("[GitHub] Skipped issue_comment."));
|
|
437
|
+
} finally {
|
|
438
|
+
logSpy.mockRestore();
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
test("edited, unmentioned and duplicate comments each log a reason", async () => {
|
|
443
|
+
const mention = `@${GITHUB_BOT_NAME} please review`;
|
|
444
|
+
const edited = { ...makeCommentEvent("skip-edit", mention), action: "edited" };
|
|
445
|
+
edited.comment = { ...edited.comment, id: 7001 };
|
|
446
|
+
const noMention = makeCommentEvent("skip-plain", "no ping here");
|
|
447
|
+
noMention.comment = { ...noMention.comment, id: 7002 };
|
|
448
|
+
const first = makeCommentEvent("skip-dup", mention);
|
|
449
|
+
first.comment = { ...first.comment, id: 7003 };
|
|
450
|
+
|
|
451
|
+
expect((await handleComment(first, "issue_comment")).created).toBe(true);
|
|
452
|
+
expect(await skippedReasons([edited, noMention, first])).toEqual([
|
|
453
|
+
"[GitHub] Skipped issue_comment.edited by skip-edit on test/repo comment 7001: action is not created",
|
|
454
|
+
"[GitHub] Skipped issue_comment.created by skip-plain on test/repo comment 7002: no bot mention",
|
|
455
|
+
"[GitHub] Skipped issue_comment.created by skip-dup on test/repo comment 7003: duplicate delivery",
|
|
456
|
+
]);
|
|
457
|
+
});
|
|
458
|
+
});
|
|
@@ -158,6 +158,18 @@ describe("resolveHarnessProvider", () => {
|
|
|
158
158
|
test("keeps 'claude' when no credentials are present at all", () => {
|
|
159
159
|
expect(resolveHarnessProvider({}, {})).toBe("claude");
|
|
160
160
|
});
|
|
161
|
+
|
|
162
|
+
test("prefers 'pi' when a NaN or Helmcode key is the only LLM credential", () => {
|
|
163
|
+
expect(resolveHarnessProvider({ NAN_API_KEY: "example-nan" }, {})).toBe("pi");
|
|
164
|
+
expect(resolveHarnessProvider({}, { HELMCODE_API_NAN: "example-nan" })).toBe("pi");
|
|
165
|
+
expect(resolveHarnessProvider({ HELMCODE_API_KEY: "example-helm" }, {})).toBe("pi");
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("keeps 'claude' when a NaN key sits next to an Anthropic credential", () => {
|
|
169
|
+
expect(
|
|
170
|
+
resolveHarnessProvider({ NAN_API_KEY: "example-nan", ANTHROPIC_API_KEY: "example-ant" }, {}),
|
|
171
|
+
).toBe("claude");
|
|
172
|
+
});
|
|
161
173
|
});
|
|
162
174
|
|
|
163
175
|
// ─── validateConfigValue ─────────────────────────────────────────────────────
|
|
@@ -524,7 +524,8 @@ describe("Heartbeat Checklist", () => {
|
|
|
524
524
|
buildCredentials: () => {
|
|
525
525
|
setupStarted.resolve();
|
|
526
526
|
return new Promise((resolve) => {
|
|
527
|
-
releaseSetup = () =>
|
|
527
|
+
releaseSetup = () =>
|
|
528
|
+
resolve({ egressSecrets: [], failedBindings: [], userConfig: {} });
|
|
528
529
|
});
|
|
529
530
|
},
|
|
530
531
|
});
|
|
@@ -561,7 +562,7 @@ describe("Heartbeat Checklist", () => {
|
|
|
561
562
|
{
|
|
562
563
|
buildCredentials: async () => {
|
|
563
564
|
controller.abort();
|
|
564
|
-
return { egressSecrets: [], failedBindings: [] };
|
|
565
|
+
return { egressSecrets: [], failedBindings: [], userConfig: {} };
|
|
565
566
|
},
|
|
566
567
|
},
|
|
567
568
|
);
|
|
@@ -24,8 +24,6 @@ test(
|
|
|
24
24
|
});
|
|
25
25
|
await Bun.$`mkdir -p ${tempDir}`.quiet();
|
|
26
26
|
try {
|
|
27
|
-
await Bun.write(`${tempDir}/pm2`, '#!/bin/sh\nif [ "$1" = jlist ]; then printf "[]"; fi\n');
|
|
28
|
-
await Bun.$`chmod +x ${tempDir}/pm2`.quiet();
|
|
29
27
|
await Bun.write(
|
|
30
28
|
`${tempDir}/.mcp.json`,
|
|
31
29
|
JSON.stringify({
|
|
@@ -1174,13 +1174,6 @@ describe("Stats & Metadata", () => {
|
|
|
1174
1174
|
expect(Array.isArray(body.logs)).toBe(true);
|
|
1175
1175
|
});
|
|
1176
1176
|
|
|
1177
|
-
test("GET /api/services — returns services list", async () => {
|
|
1178
|
-
const { status, body } = await get("/api/services");
|
|
1179
|
-
expect(status).toBe(200);
|
|
1180
|
-
expect(body.services).toBeDefined();
|
|
1181
|
-
expect(Array.isArray(body.services)).toBe(true);
|
|
1182
|
-
});
|
|
1183
|
-
|
|
1184
1177
|
test("GET /api/scheduled-tasks — returns scheduled tasks", async () => {
|
|
1185
1178
|
const { status, body } = await get("/api/scheduled-tasks");
|
|
1186
1179
|
expect(status).toBe(200);
|
|
@@ -1817,24 +1810,6 @@ describe("Memory", () => {
|
|
|
1817
1810
|
});
|
|
1818
1811
|
});
|
|
1819
1812
|
|
|
1820
|
-
// ===========================================================================
|
|
1821
|
-
// 16. Ecosystem
|
|
1822
|
-
// ===========================================================================
|
|
1823
|
-
|
|
1824
|
-
describe("Ecosystem", () => {
|
|
1825
|
-
test("GET /ecosystem — missing agent ID header returns 400", async () => {
|
|
1826
|
-
const { status } = await get("/ecosystem");
|
|
1827
|
-
expect(status).toBe(400);
|
|
1828
|
-
});
|
|
1829
|
-
|
|
1830
|
-
test("GET /ecosystem — returns ecosystem config for agent", async () => {
|
|
1831
|
-
const { status, body } = await get("/ecosystem", { agentId: ids.workerAgent });
|
|
1832
|
-
expect(status).toBe(200);
|
|
1833
|
-
expect(body.apps).toBeDefined();
|
|
1834
|
-
expect(Array.isArray(body.apps)).toBe(true);
|
|
1835
|
-
});
|
|
1836
|
-
});
|
|
1837
|
-
|
|
1838
1813
|
// ===========================================================================
|
|
1839
1814
|
// 17. Close Agent (run near end as it changes agent state)
|
|
1840
1815
|
// ===========================================================================
|
|
@@ -224,25 +224,26 @@ describe("migration 046 — budgets and pricing", () => {
|
|
|
224
224
|
// Derive expected rates from the vendored snapshot instead of literals —
|
|
225
225
|
// Anthropic reprices (sonnet-5 intro rate through 2026-08-31), and this
|
|
226
226
|
// test pins faithful PROJECTION of the snapshot, not a rate freeze.
|
|
227
|
-
|
|
228
|
-
expect(cost?.input).toBeGreaterThan(0);
|
|
229
|
-
const expectedPrices = {
|
|
230
|
-
input: cost?.input,
|
|
231
|
-
cached_input: cost?.cache_read,
|
|
232
|
-
cache_write: cost?.cache_write,
|
|
233
|
-
// Phase 3: Anthropic-billed rows derive the 1h class at 2x base input.
|
|
234
|
-
cache_write_1h: (cost?.input ?? 0) * 2,
|
|
235
|
-
output: cost?.output,
|
|
236
|
-
} as const;
|
|
227
|
+
// The `sonnet` shortname follows the current default, Sonnet 5.5.
|
|
237
228
|
const seededKeys = [
|
|
238
|
-
["claude", "claude-sonnet-5"],
|
|
239
|
-
["claude-managed", "claude-sonnet-5"],
|
|
240
|
-
["claude", "sonnet"],
|
|
241
|
-
["claude-managed", "sonnet"],
|
|
242
|
-
["pi", "sonnet"],
|
|
229
|
+
["claude", "claude-sonnet-5", "claude-sonnet-5"],
|
|
230
|
+
["claude-managed", "claude-sonnet-5", "claude-sonnet-5"],
|
|
231
|
+
["claude", "sonnet", "claude-sonnet-5-5"],
|
|
232
|
+
["claude-managed", "sonnet", "claude-sonnet-5-5"],
|
|
233
|
+
["pi", "sonnet", "claude-sonnet-5-5"],
|
|
243
234
|
] as const;
|
|
244
235
|
|
|
245
|
-
for (const [provider, model] of seededKeys) {
|
|
236
|
+
for (const [provider, model, sourceId] of seededKeys) {
|
|
237
|
+
const cost = loadModelsDevCache()?.anthropic?.models?.[sourceId]?.cost;
|
|
238
|
+
expect(cost?.input).toBeGreaterThan(0);
|
|
239
|
+
const expectedPrices = {
|
|
240
|
+
input: cost?.input,
|
|
241
|
+
cached_input: cost?.cache_read,
|
|
242
|
+
cache_write: cost?.cache_write,
|
|
243
|
+
// Phase 3: Anthropic-billed rows derive the 1h class at 2x base input.
|
|
244
|
+
cache_write_1h: (cost?.input ?? 0) * 2,
|
|
245
|
+
output: cost?.output,
|
|
246
|
+
} as const;
|
|
246
247
|
for (const [tokenClass, price] of Object.entries(expectedPrices)) {
|
|
247
248
|
const row = await client.get<PricingRow>(
|
|
248
249
|
`SELECT * FROM pricing
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { expect, test } from "bun:test";
|
|
3
|
+
|
|
4
|
+
function setup(): Database {
|
|
5
|
+
const db = new Database(":memory:");
|
|
6
|
+
db.exec(
|
|
7
|
+
`CREATE TABLE agents (
|
|
8
|
+
id TEXT PRIMARY KEY,
|
|
9
|
+
isLead INTEGER NOT NULL DEFAULT 0
|
|
10
|
+
)`,
|
|
11
|
+
);
|
|
12
|
+
db.exec("INSERT INTO agents (id, isLead) VALUES ('lead', 1), ('worker', 0)");
|
|
13
|
+
db.exec(
|
|
14
|
+
`CREATE TABLE agent_tasks (
|
|
15
|
+
id TEXT PRIMARY KEY,
|
|
16
|
+
status TEXT NOT NULL,
|
|
17
|
+
source TEXT NOT NULL,
|
|
18
|
+
taskType TEXT,
|
|
19
|
+
contextKey TEXT,
|
|
20
|
+
agentId TEXT
|
|
21
|
+
)`,
|
|
22
|
+
);
|
|
23
|
+
return db;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function obligations(db: Database): string[] {
|
|
27
|
+
return db
|
|
28
|
+
.query<{ task_id: string }, []>("SELECT task_id FROM chat_relay_obligations ORDER BY task_id")
|
|
29
|
+
.all()
|
|
30
|
+
.map((row) => row.task_id);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
test("migration 217 creates the chat state tables and the pending index", async () => {
|
|
34
|
+
const db = setup();
|
|
35
|
+
try {
|
|
36
|
+
db.exec(await Bun.file("src/be/migrations/217_chat_sdk_state.sql").text());
|
|
37
|
+
const tables = db
|
|
38
|
+
.query<{ name: string }, []>("SELECT name FROM sqlite_master WHERE type = 'table'")
|
|
39
|
+
.all()
|
|
40
|
+
.map((row) => row.name);
|
|
41
|
+
for (const table of [
|
|
42
|
+
"chat_state_kv",
|
|
43
|
+
"chat_subscriptions",
|
|
44
|
+
"chat_locks",
|
|
45
|
+
"chat_lists",
|
|
46
|
+
"chat_queue",
|
|
47
|
+
"chat_relay_obligations",
|
|
48
|
+
]) {
|
|
49
|
+
expect(tables).toContain(table);
|
|
50
|
+
}
|
|
51
|
+
const pendingIndex = db
|
|
52
|
+
.query<{ sql: string }, []>(
|
|
53
|
+
"SELECT sql FROM sqlite_master WHERE name = 'idx_chat_relay_obligations_pending'",
|
|
54
|
+
)
|
|
55
|
+
.get();
|
|
56
|
+
expect(pendingIndex?.sql).toContain("WHERE delivered_at IS NULL AND given_up_at IS NULL");
|
|
57
|
+
expect(
|
|
58
|
+
db
|
|
59
|
+
.query<{ name: string }, []>("PRAGMA index_list(chat_queue)")
|
|
60
|
+
.all()
|
|
61
|
+
.map((index) => index.name),
|
|
62
|
+
).toContain("idx_chat_queue_thread");
|
|
63
|
+
expect(db.query("PRAGMA integrity_check").get()).toEqual({ integrity_check: "ok" });
|
|
64
|
+
} finally {
|
|
65
|
+
db.close();
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("migration 217 triggers enqueue a relay obligation only for chat-rooted terminal tasks", async () => {
|
|
70
|
+
const db = setup();
|
|
71
|
+
try {
|
|
72
|
+
db.exec(await Bun.file("src/be/migrations/217_chat_sdk_state.sql").text());
|
|
73
|
+
const insert = db.prepare(
|
|
74
|
+
"INSERT INTO agent_tasks (id, status, source, taskType, contextKey, agentId) VALUES (?, ?, ?, ?, ?, ?)",
|
|
75
|
+
);
|
|
76
|
+
const key = "task:chat-sdk:telegram:555";
|
|
77
|
+
insert.run("chat", "in_progress", "chat", "chat-inbound", key, "lead");
|
|
78
|
+
insert.run("slack", "in_progress", "slack", null, "task:slack:C1:1.2", "lead");
|
|
79
|
+
insert.run("followup", "in_progress", "system", "follow-up", key, "lead");
|
|
80
|
+
insert.run("resume-lead", "in_progress", "system", "resume", key, "lead");
|
|
81
|
+
insert.run("resume-worker", "in_progress", "system", "resume", key, "worker");
|
|
82
|
+
insert.run("resume-unassigned", "in_progress", "system", "resume", key, null);
|
|
83
|
+
insert.run("delegated", "in_progress", "system", "delegated", key, "worker");
|
|
84
|
+
insert.run("upper", "in_progress", "chat", null, "TASK:CHAT-SDK:telegram:555", "lead");
|
|
85
|
+
insert.run("done-at-insert", "completed", "chat", null, "task:chat-sdk:telegram:777", "lead");
|
|
86
|
+
insert.run("resume-at-insert", "completed", "system", "resume", key, "lead");
|
|
87
|
+
|
|
88
|
+
for (const id of [
|
|
89
|
+
"chat",
|
|
90
|
+
"slack",
|
|
91
|
+
"followup",
|
|
92
|
+
"resume-lead",
|
|
93
|
+
"resume-worker",
|
|
94
|
+
"resume-unassigned",
|
|
95
|
+
"delegated",
|
|
96
|
+
"upper",
|
|
97
|
+
]) {
|
|
98
|
+
db.run("UPDATE agent_tasks SET status = 'completed' WHERE id = ?", [id]);
|
|
99
|
+
}
|
|
100
|
+
// A second terminal transition must not add a second row.
|
|
101
|
+
db.run("UPDATE agent_tasks SET status = 'failed' WHERE id = 'chat'");
|
|
102
|
+
|
|
103
|
+
expect(obligations(db)).toEqual([
|
|
104
|
+
"chat",
|
|
105
|
+
"done-at-insert",
|
|
106
|
+
"followup",
|
|
107
|
+
"resume-at-insert",
|
|
108
|
+
"resume-lead",
|
|
109
|
+
]);
|
|
110
|
+
} finally {
|
|
111
|
+
db.close();
|
|
112
|
+
}
|
|
113
|
+
});
|
|
@@ -267,6 +267,99 @@ describe("claim-time resolution via /api/poll", () => {
|
|
|
267
267
|
});
|
|
268
268
|
});
|
|
269
269
|
|
|
270
|
+
// A row that already ran carries that run's model: the session report wrote it
|
|
271
|
+
// into `model`, and the claim wrote `resolvedModel`/`modelSource`. Re-claiming
|
|
272
|
+
// it must resolve from the config in force NOW, not replay the previous run.
|
|
273
|
+
describe("re-claim after a previous run", () => {
|
|
274
|
+
async function markRan(
|
|
275
|
+
taskId: string,
|
|
276
|
+
fields: { model: string; resolvedModel?: string; modelSource?: string },
|
|
277
|
+
): Promise<void> {
|
|
278
|
+
await getDbClient().run(
|
|
279
|
+
`UPDATE agent_tasks SET model = ?, resolvedModel = ?, modelSource = ?,
|
|
280
|
+
claudeSessionId = 'previous-session', status = 'pending' WHERE id = ?`,
|
|
281
|
+
[fields.model, fields.resolvedModel ?? null, fields.modelSource ?? null, taskId],
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
test("an unpinned task drops the previous run's model, so the agent's current config applies", async () => {
|
|
286
|
+
const worker = await createAgent({
|
|
287
|
+
name: "rerun-unpinned",
|
|
288
|
+
isLead: false,
|
|
289
|
+
status: "idle",
|
|
290
|
+
maxTasks: 1,
|
|
291
|
+
harnessProvider: "claude",
|
|
292
|
+
});
|
|
293
|
+
const task = await createTaskExtended("plain work", { agentId: worker.id });
|
|
294
|
+
// First run: no pin, MODEL_OVERRIDE was opus, the session reported it.
|
|
295
|
+
await markRan(task.id, { model: "claude-opus-5-5" });
|
|
296
|
+
|
|
297
|
+
const trigger = await callPoll(worker.id);
|
|
298
|
+
|
|
299
|
+
expect(trigger?.type).toBe("task_assigned");
|
|
300
|
+
// No `model` in the trigger: the worker falls through to MODEL_OVERRIDE.
|
|
301
|
+
expect(trigger?.task.model).toBeUndefined();
|
|
302
|
+
expect(trigger?.task.resolvedModel).toBeUndefined();
|
|
303
|
+
expect(await taskRow(task.id)).toEqual({
|
|
304
|
+
resolvedModel: null,
|
|
305
|
+
modelSource: null,
|
|
306
|
+
modelAlias: null,
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
test("a tier task re-resolves the tier after the tier config changed", async () => {
|
|
311
|
+
const worker = await createAgent({
|
|
312
|
+
name: "rerun-tier",
|
|
313
|
+
isLead: false,
|
|
314
|
+
status: "idle",
|
|
315
|
+
maxTasks: 1,
|
|
316
|
+
harnessProvider: "claude",
|
|
317
|
+
});
|
|
318
|
+
const task = await createTaskExtended("smart work", { agentId: worker.id, modelTier: "smart" });
|
|
319
|
+
await markRan(task.id, {
|
|
320
|
+
model: "claude-opus-5-5",
|
|
321
|
+
resolvedModel: "claude-opus-5-5",
|
|
322
|
+
modelSource: "tier-config",
|
|
323
|
+
});
|
|
324
|
+
process.env.MODEL_TIER_CLAUDE_SMART = "claude-sonnet-5-5";
|
|
325
|
+
|
|
326
|
+
const trigger = await callPoll(worker.id, header({}));
|
|
327
|
+
|
|
328
|
+
expect(trigger?.task.model).toBeUndefined();
|
|
329
|
+
expect(trigger?.task.resolvedModel).toBe("claude-sonnet-5-5");
|
|
330
|
+
expect(await taskRow(task.id)).toEqual({
|
|
331
|
+
resolvedModel: "claude-sonnet-5-5",
|
|
332
|
+
modelSource: "tier-config",
|
|
333
|
+
modelAlias: null,
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
test("a task-pinned model stays pinned on re-claim", async () => {
|
|
338
|
+
const worker = await createAgent({
|
|
339
|
+
name: "rerun-pinned",
|
|
340
|
+
isLead: false,
|
|
341
|
+
status: "idle",
|
|
342
|
+
maxTasks: 1,
|
|
343
|
+
harnessProvider: "claude",
|
|
344
|
+
});
|
|
345
|
+
const task = await createTaskExtended("pinned work", {
|
|
346
|
+
agentId: worker.id,
|
|
347
|
+
model: "claude-sonnet-5-5",
|
|
348
|
+
});
|
|
349
|
+
await markRan(task.id, {
|
|
350
|
+
model: "claude-sonnet-5-5",
|
|
351
|
+
resolvedModel: "claude-sonnet-5-5",
|
|
352
|
+
modelSource: "model",
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
const trigger = await callPoll(worker.id);
|
|
356
|
+
|
|
357
|
+
expect(trigger?.task.model).toBe("claude-sonnet-5-5");
|
|
358
|
+
expect(trigger?.task.resolvedModel).toBe("claude-sonnet-5-5");
|
|
359
|
+
expect((await taskRow(task.id))?.modelSource).toBe("model");
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
|
|
270
363
|
describe("claim-time harness guard", () => {
|
|
271
364
|
test("a directed task pinning an Anthropic model on a Codex worker fails fast with a tagged reason", async () => {
|
|
272
365
|
const worker = await createAgent({
|
|
@@ -11,14 +11,17 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { afterEach, describe, expect, test } from "bun:test";
|
|
14
|
+
import { reasoningLevelsForModel } from "@desplega/model-catalog";
|
|
14
15
|
import {
|
|
15
16
|
buildModelsCatalog,
|
|
17
|
+
type CatalogProviderId,
|
|
16
18
|
getModelsCatalog,
|
|
17
19
|
PINNED_MODELSDEV_ENTRIES,
|
|
18
20
|
resetModelsCatalogForTests,
|
|
19
21
|
updateLiveModelsCatalog,
|
|
20
22
|
} from "../be/models-catalog";
|
|
21
23
|
import type { ModelsDevCache } from "../be/modelsdev-cache";
|
|
24
|
+
import { getContextWindowSize } from "../utils/context-window";
|
|
22
25
|
|
|
23
26
|
afterEach(() => {
|
|
24
27
|
resetModelsCatalogForTests();
|
|
@@ -82,6 +85,14 @@ describe("getModelsCatalog", () => {
|
|
|
82
85
|
expect(Object.keys(result.providers.openrouter?.models ?? {}).length).toBeGreaterThan(0);
|
|
83
86
|
expect(Object.keys(result.providers.anthropic?.models ?? {}).length).toBeGreaterThan(0);
|
|
84
87
|
expect(Object.keys(result.providers.opencode?.models ?? {}).length).toBeGreaterThan(0);
|
|
88
|
+
expect(result.providers.nan?.models["glm5.3-flash"]).toBeDefined();
|
|
89
|
+
expect(Object.keys(result.providers.helmcode?.models ?? {}).sort()).toEqual([
|
|
90
|
+
"deepseek-v4-flash",
|
|
91
|
+
"gemma4",
|
|
92
|
+
"glm5.3",
|
|
93
|
+
"glm5.3-flash",
|
|
94
|
+
"qwen3.6",
|
|
95
|
+
]);
|
|
85
96
|
});
|
|
86
97
|
|
|
87
98
|
test("prefers live data after an update", () => {
|
|
@@ -99,7 +110,7 @@ describe("getModelsCatalog", () => {
|
|
|
99
110
|
const result = getModelsCatalog();
|
|
100
111
|
for (const entryPath of PINNED_MODELSDEV_ENTRIES) {
|
|
101
112
|
const slashIndex = entryPath.indexOf("/");
|
|
102
|
-
const providerId = entryPath.slice(0, slashIndex) as
|
|
113
|
+
const providerId = entryPath.slice(0, slashIndex) as CatalogProviderId;
|
|
103
114
|
const modelId = entryPath.slice(slashIndex + 1);
|
|
104
115
|
expect(result.providers[providerId]?.models[modelId]).toBeDefined();
|
|
105
116
|
}
|
|
@@ -118,3 +129,33 @@ describe("getModelsCatalog", () => {
|
|
|
118
129
|
expect(result.providers.anthropic?.models["claude-mythos-5"]?.name).toBe("Live Mythos");
|
|
119
130
|
});
|
|
120
131
|
});
|
|
132
|
+
|
|
133
|
+
describe("nan and helmcode sections", () => {
|
|
134
|
+
test("the registry-synthesized helmcode section survives a live payload that lacks it", () => {
|
|
135
|
+
// LIVE_CACHE has no helmcode provider: the registry section is refilled.
|
|
136
|
+
updateLiveModelsCatalog(LIVE_CACHE);
|
|
137
|
+
const helmcode = getModelsCatalog().providers.helmcode;
|
|
138
|
+
expect(helmcode?.id).toBe("helmcode");
|
|
139
|
+
expect(helmcode?.name).toBe("Helmcode");
|
|
140
|
+
expect(helmcode?.models["glm5.3"]?.limit?.context).toBe(1_048_576);
|
|
141
|
+
expect(helmcode?.models["qwen3.6"]?.limit?.context).toBe(262_144);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("reasoning levels resolve for helmcode and nan ids on pi and opencode", () => {
|
|
145
|
+
const catalog = getModelsCatalog().providers;
|
|
146
|
+
for (const harness of ["pi", "opencode"]) {
|
|
147
|
+
expect(
|
|
148
|
+
reasoningLevelsForModel(harness, "helmcode/glm5.3-flash", catalog).length,
|
|
149
|
+
).toBeGreaterThan(0);
|
|
150
|
+
expect(reasoningLevelsForModel(harness, "nan/glm5.3-flash", catalog).length).toBeGreaterThan(
|
|
151
|
+
0,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("context windows come from the catalog sections", () => {
|
|
157
|
+
expect(getContextWindowSize("helmcode/glm5.3")).toBe(1_048_576);
|
|
158
|
+
expect(getContextWindowSize("helmcode/gemma4")).toBe(262_144);
|
|
159
|
+
expect(getContextWindowSize("nan/qwen3.6")).toBe(262_144);
|
|
160
|
+
});
|
|
161
|
+
});
|