@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
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { afterAll, beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
type BasePromptArgs,
|
|
4
|
+
getBasePrompt,
|
|
5
|
+
getTaskContextPrompt,
|
|
6
|
+
truncateRepoClaudeMd,
|
|
7
|
+
} from "../prompts/base-prompt";
|
|
3
8
|
import { generateDefaultClaudeMd, generateDefaultIdentityMd } from "../prompts/defaults";
|
|
4
9
|
import type { ProviderTraits } from "../providers/types";
|
|
5
10
|
|
|
@@ -377,7 +382,7 @@ describe("getBasePrompt: persona injection", () => {
|
|
|
377
382
|
});
|
|
378
383
|
|
|
379
384
|
// ---------------------------------------------------------------------------
|
|
380
|
-
// 4. Agent notes (CLAUDE.md):
|
|
385
|
+
// 4. Agent notes (CLAUDE.md): local providers, only when edited
|
|
381
386
|
// ---------------------------------------------------------------------------
|
|
382
387
|
|
|
383
388
|
describe("getBasePrompt: agent notes section", () => {
|
|
@@ -385,18 +390,9 @@ describe("getBasePrompt: agent notes section", () => {
|
|
|
385
390
|
const EDITED = "# Agent: Ada\n\nAlways run `bun run tsc:check` before you push.";
|
|
386
391
|
const notesAgent = { name: "Ada", role: "worker" };
|
|
387
392
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
name: "Ada",
|
|
392
|
-
provider: "claude",
|
|
393
|
-
claudeMd: EDITED,
|
|
394
|
-
});
|
|
395
|
-
expect(result).not.toContain(NOTES_HEADER);
|
|
396
|
-
expect(result).not.toContain("Always run `bun run tsc:check` before you push.");
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
for (const provider of ["codex", "opencode", "pi"] as const) {
|
|
393
|
+
// Claude excludes ~/.claude/CLAUDE.md from its memory load
|
|
394
|
+
// (claude-flag-settings.ts), so the system prompt is its only copy.
|
|
395
|
+
for (const provider of ["claude", "codex", "opencode", "pi"] as const) {
|
|
400
396
|
test(`${provider} gets the section when the notes are edited`, async () => {
|
|
401
397
|
const result = await getBasePrompt({
|
|
402
398
|
...minimalArgs,
|
|
@@ -409,6 +405,17 @@ describe("getBasePrompt: agent notes section", () => {
|
|
|
409
405
|
});
|
|
410
406
|
}
|
|
411
407
|
|
|
408
|
+
test("claude truncates notes over 20k characters and points at its own file", async () => {
|
|
409
|
+
const result = await getBasePrompt({
|
|
410
|
+
...minimalArgs,
|
|
411
|
+
name: "Ada",
|
|
412
|
+
provider: "claude",
|
|
413
|
+
claudeMd: "x".repeat(25_000),
|
|
414
|
+
});
|
|
415
|
+
expect(result).toContain("[...truncated, see ~/.claude/CLAUDE.md for full content]");
|
|
416
|
+
expect(result).not.toContain("x".repeat(20_000));
|
|
417
|
+
});
|
|
418
|
+
|
|
412
419
|
test("codex skips notes that equal the generated default", async () => {
|
|
413
420
|
const result = await getBasePrompt({
|
|
414
421
|
...minimalArgs,
|
|
@@ -598,11 +605,15 @@ describe("getBasePrompt: slack section", () => {
|
|
|
598
605
|
|
|
599
606
|
test("a scripts-only worker with a Slack task gets the scripts-only variant only", async () => {
|
|
600
607
|
enableSlack();
|
|
601
|
-
const
|
|
608
|
+
const system = await getBasePrompt({ ...minimalArgs, scriptsOnly: true });
|
|
609
|
+
const result = await getTaskContextPrompt({
|
|
602
610
|
...minimalArgs,
|
|
603
611
|
scriptsOnly: true,
|
|
604
612
|
slackContext: { channelId: "C0SAMPLE", threadTs: "123.456" },
|
|
605
613
|
});
|
|
614
|
+
// The channel id is per task, so the note rides in the task prompt.
|
|
615
|
+
expect(system).not.toContain(SCRIPTS_ONLY_SLACK_HEADER);
|
|
616
|
+
expect(system).not.toContain(SLACK_HEADER);
|
|
606
617
|
expect(result).toContain(SCRIPTS_ONLY_SLACK_HEADER);
|
|
607
618
|
expect(result).toContain("C0SAMPLE");
|
|
608
619
|
expect(result).not.toContain(SLACK_HEADER);
|
|
@@ -610,7 +621,7 @@ describe("getBasePrompt: slack section", () => {
|
|
|
610
621
|
|
|
611
622
|
test("a scripts-only lead with a Slack task gets neither variant", async () => {
|
|
612
623
|
enableSlack();
|
|
613
|
-
const result = await
|
|
624
|
+
const result = await getTaskContextPrompt({
|
|
614
625
|
...minimalArgs,
|
|
615
626
|
role: "lead",
|
|
616
627
|
scriptsOnly: true,
|
|
@@ -786,14 +797,14 @@ describe("getBasePrompt: tools and skills section", () => {
|
|
|
786
797
|
// 10. Repository
|
|
787
798
|
// ---------------------------------------------------------------------------
|
|
788
799
|
|
|
789
|
-
describe("
|
|
800
|
+
describe("getTaskContextPrompt: repository section", () => {
|
|
790
801
|
const CLONE_PATH = "/workspace/repos/my-repo";
|
|
791
802
|
const CLONE_SENTENCE = "This task's repository is cloned locally. `get-repos` returns the path.";
|
|
792
803
|
const CODE_QUALITY_LINE =
|
|
793
804
|
"You MUST use the `code-quality` skill before you push, open a PR, or review one.";
|
|
794
805
|
|
|
795
806
|
test("a local claude worker gets the clone sentence and no clone path literal", async () => {
|
|
796
|
-
const result = await
|
|
807
|
+
const result = await getTaskContextPrompt({
|
|
797
808
|
...minimalArgs,
|
|
798
809
|
traits: localTraits,
|
|
799
810
|
repoContext: { claudeMd: "Repo rules here.", clonePath: CLONE_PATH },
|
|
@@ -804,7 +815,7 @@ describe("getBasePrompt: repository section", () => {
|
|
|
804
815
|
});
|
|
805
816
|
|
|
806
817
|
test("claude does not get the repo CLAUDE.md inlined", async () => {
|
|
807
|
-
const result = await
|
|
818
|
+
const result = await getTaskContextPrompt({
|
|
808
819
|
...minimalArgs,
|
|
809
820
|
provider: "claude",
|
|
810
821
|
traits: localTraits,
|
|
@@ -814,7 +825,7 @@ describe("getBasePrompt: repository section", () => {
|
|
|
814
825
|
});
|
|
815
826
|
|
|
816
827
|
test("opencode inlines the repo CLAUDE.md with the clone path", async () => {
|
|
817
|
-
const result = await
|
|
828
|
+
const result = await getTaskContextPrompt({
|
|
818
829
|
...minimalArgs,
|
|
819
830
|
provider: "opencode",
|
|
820
831
|
traits: localTraits,
|
|
@@ -826,7 +837,7 @@ describe("getBasePrompt: repository section", () => {
|
|
|
826
837
|
|
|
827
838
|
test("opencode truncates a repo CLAUDE.md over 12k with an on-disk pointer", async () => {
|
|
828
839
|
const huge = "x".repeat(30_000);
|
|
829
|
-
const result = await
|
|
840
|
+
const result = await getTaskContextPrompt({
|
|
830
841
|
...minimalArgs,
|
|
831
842
|
provider: "opencode",
|
|
832
843
|
traits: localTraits,
|
|
@@ -838,7 +849,7 @@ describe("getBasePrompt: repository section", () => {
|
|
|
838
849
|
|
|
839
850
|
test("opencode keeps a repo CLAUDE.md under the cap verbatim", async () => {
|
|
840
851
|
const small = "y".repeat(5_000);
|
|
841
|
-
const result = await
|
|
852
|
+
const result = await getTaskContextPrompt({
|
|
842
853
|
...minimalArgs,
|
|
843
854
|
provider: "opencode",
|
|
844
855
|
traits: localTraits,
|
|
@@ -849,7 +860,7 @@ describe("getBasePrompt: repository section", () => {
|
|
|
849
860
|
});
|
|
850
861
|
|
|
851
862
|
test("shows the repo warning when one is set", async () => {
|
|
852
|
-
const result = await
|
|
863
|
+
const result = await getTaskContextPrompt({
|
|
853
864
|
...minimalArgs,
|
|
854
865
|
repoContext: { clonePath: CLONE_PATH, warning: "Repo is stale" },
|
|
855
866
|
});
|
|
@@ -857,7 +868,7 @@ describe("getBasePrompt: repository section", () => {
|
|
|
857
868
|
});
|
|
858
869
|
|
|
859
870
|
test("surfaces auto-stashed work when entries exist", async () => {
|
|
860
|
-
const result = await
|
|
871
|
+
const result = await getTaskContextPrompt({
|
|
861
872
|
...minimalArgs,
|
|
862
873
|
traits: localTraits,
|
|
863
874
|
repoContext: {
|
|
@@ -871,7 +882,7 @@ describe("getBasePrompt: repository section", () => {
|
|
|
871
882
|
});
|
|
872
883
|
|
|
873
884
|
test("says nothing about stashes when the list is empty", async () => {
|
|
874
|
-
const result = await
|
|
885
|
+
const result = await getTaskContextPrompt({
|
|
875
886
|
...minimalArgs,
|
|
876
887
|
traits: localTraits,
|
|
877
888
|
repoContext: { clonePath: CLONE_PATH, autoStashes: [] },
|
|
@@ -880,7 +891,7 @@ describe("getBasePrompt: repository section", () => {
|
|
|
880
891
|
});
|
|
881
892
|
|
|
882
893
|
test("null guidelines tell the agent to ask the lead", async () => {
|
|
883
|
-
const result = await
|
|
894
|
+
const result = await getTaskContextPrompt({
|
|
884
895
|
...minimalArgs,
|
|
885
896
|
repoContext: { clonePath: CLONE_PATH, guidelines: null },
|
|
886
897
|
});
|
|
@@ -889,7 +900,7 @@ describe("getBasePrompt: repository section", () => {
|
|
|
889
900
|
});
|
|
890
901
|
|
|
891
902
|
test("full guidelines render PR checks, merge policy, and review guidance", async () => {
|
|
892
|
-
const result = await
|
|
903
|
+
const result = await getTaskContextPrompt({
|
|
893
904
|
...minimalArgs,
|
|
894
905
|
repoContext: {
|
|
895
906
|
clonePath: CLONE_PATH,
|
|
@@ -911,7 +922,7 @@ describe("getBasePrompt: repository section", () => {
|
|
|
911
922
|
});
|
|
912
923
|
|
|
913
924
|
test("all-empty guidelines render no guidelines block", async () => {
|
|
914
|
-
const result = await
|
|
925
|
+
const result = await getTaskContextPrompt({
|
|
915
926
|
...minimalArgs,
|
|
916
927
|
repoContext: {
|
|
917
928
|
clonePath: CLONE_PATH,
|
|
@@ -923,7 +934,7 @@ describe("getBasePrompt: repository section", () => {
|
|
|
923
934
|
});
|
|
924
935
|
|
|
925
936
|
test("allowMerge true renders the merge policy even with empty arrays", async () => {
|
|
926
|
-
const result = await
|
|
937
|
+
const result = await getTaskContextPrompt({
|
|
927
938
|
...minimalArgs,
|
|
928
939
|
repoContext: {
|
|
929
940
|
clonePath: CLONE_PATH,
|
|
@@ -935,7 +946,7 @@ describe("getBasePrompt: repository section", () => {
|
|
|
935
946
|
});
|
|
936
947
|
|
|
937
948
|
test("a remote worker keeps the guidelines but loses the clone sentence", async () => {
|
|
938
|
-
const result = await
|
|
949
|
+
const result = await getTaskContextPrompt({
|
|
939
950
|
...minimalArgs,
|
|
940
951
|
provider: "devin",
|
|
941
952
|
traits: remoteTraits,
|
|
@@ -957,7 +968,7 @@ describe("getBasePrompt: repository section", () => {
|
|
|
957
968
|
});
|
|
958
969
|
|
|
959
970
|
test("the code-quality pointer is present with MCP", async () => {
|
|
960
|
-
const result = await
|
|
971
|
+
const result = await getTaskContextPrompt({
|
|
961
972
|
...minimalArgs,
|
|
962
973
|
traits: localTraits,
|
|
963
974
|
repoContext: { clonePath: CLONE_PATH },
|
|
@@ -966,7 +977,7 @@ describe("getBasePrompt: repository section", () => {
|
|
|
966
977
|
});
|
|
967
978
|
|
|
968
979
|
test("the code-quality pointer is absent for a remote worker", async () => {
|
|
969
|
-
const result = await
|
|
980
|
+
const result = await getTaskContextPrompt({
|
|
970
981
|
...minimalArgs,
|
|
971
982
|
traits: remoteTraits,
|
|
972
983
|
repoContext: { clonePath: CLONE_PATH },
|
|
@@ -1033,18 +1044,6 @@ describe("getBasePrompt: size budget", () => {
|
|
|
1033
1044
|
claudeMd: big(40_000),
|
|
1034
1045
|
skillsSummary: [{ name: "commit", description: "Create a commit" }],
|
|
1035
1046
|
mcpServers: ["linear"],
|
|
1036
|
-
slackContext: { channelId: "C0SAMPLE" },
|
|
1037
|
-
repoContext: {
|
|
1038
|
-
claudeMd: big(60_000),
|
|
1039
|
-
clonePath: "/workspace/repos/big-repo",
|
|
1040
|
-
warning: "Repo is stale",
|
|
1041
|
-
guidelines: {
|
|
1042
|
-
prChecks: ["bun run lint"],
|
|
1043
|
-
mergeChecks: ["CI green"],
|
|
1044
|
-
allowMerge: true,
|
|
1045
|
-
review: ["Read the runbook"],
|
|
1046
|
-
},
|
|
1047
|
-
},
|
|
1048
1047
|
});
|
|
1049
1048
|
expect(result.length).toBeLessThan(120_000);
|
|
1050
1049
|
});
|
|
@@ -1070,7 +1069,7 @@ describe("getBasePrompt: output hygiene", () => {
|
|
|
1070
1069
|
args: { ...minimalArgs, name: "Ada", traits: remoteTraits, provider: "devin" },
|
|
1071
1070
|
},
|
|
1072
1071
|
{
|
|
1073
|
-
label: "codex worker with skills
|
|
1072
|
+
label: "codex worker with skills",
|
|
1074
1073
|
args: {
|
|
1075
1074
|
...minimalArgs,
|
|
1076
1075
|
name: "Ada",
|
|
@@ -1078,20 +1077,25 @@ describe("getBasePrompt: output hygiene", () => {
|
|
|
1078
1077
|
traits: { ...localTraits, nativeSkillDiscovery: false, steerModes: ["queue"] },
|
|
1079
1078
|
skillsSummary: [{ name: "commit", description: "Create a commit" }],
|
|
1080
1079
|
mcpServers: ["linear"],
|
|
1081
|
-
repoContext: {
|
|
1082
|
-
clonePath: "/workspace/repos/my-repo",
|
|
1083
|
-
guidelines: {
|
|
1084
|
-
prChecks: ["bun run lint"],
|
|
1085
|
-
mergeChecks: [],
|
|
1086
|
-
allowMerge: false,
|
|
1087
|
-
review: [],
|
|
1088
|
-
},
|
|
1089
|
-
},
|
|
1090
1080
|
},
|
|
1091
1081
|
},
|
|
1092
1082
|
{ label: "scripts-only worker", args: { ...minimalArgs, name: "Ada", scriptsOnly: true } },
|
|
1093
1083
|
];
|
|
1094
1084
|
|
|
1085
|
+
test("the task context renders no em dash and no triple newline", async () => {
|
|
1086
|
+
const result = await getTaskContextPrompt({
|
|
1087
|
+
...minimalArgs,
|
|
1088
|
+
provider: "codex",
|
|
1089
|
+
traits: localTraits,
|
|
1090
|
+
repoContext: {
|
|
1091
|
+
clonePath: "/workspace/repos/my-repo",
|
|
1092
|
+
guidelines: { prChecks: ["bun run lint"], mergeChecks: [], allowMerge: false, review: [] },
|
|
1093
|
+
},
|
|
1094
|
+
});
|
|
1095
|
+
expect(result).not.toContain(EM_DASH);
|
|
1096
|
+
expect(result).not.toMatch(/\n{3,}/);
|
|
1097
|
+
});
|
|
1098
|
+
|
|
1095
1099
|
for (const variant of hygieneVariants) {
|
|
1096
1100
|
test(`${variant.label} renders no em dash`, async () => {
|
|
1097
1101
|
const result = await getBasePrompt(variant.args);
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
2
|
+
import { unlink } from "node:fs/promises";
|
|
3
|
+
import { closeDb, createAgent, getDbClient, getTaskById, initDb } from "../be/db";
|
|
4
|
+
import { CHAT_COMMANDS } from "../integrations/chat-sdk/commands";
|
|
5
|
+
import { startChatSdk, stopChatSdk } from "../integrations/chat-sdk/runtime";
|
|
6
|
+
import { readPersistedThreadHistory } from "../integrations/chat-sdk/state";
|
|
7
|
+
import { chatSdkContextKey } from "../tasks/context-key";
|
|
8
|
+
import {
|
|
9
|
+
flushChatBuffers,
|
|
10
|
+
postTelegramUpdate,
|
|
11
|
+
startTelegramHarness,
|
|
12
|
+
type TelegramChat,
|
|
13
|
+
type TelegramHarness,
|
|
14
|
+
telegramUpdate,
|
|
15
|
+
} from "./chat-sdk-harness";
|
|
16
|
+
|
|
17
|
+
const TEST_DB_PATH = "./test-chat-sdk-commands.sqlite";
|
|
18
|
+
|
|
19
|
+
let harness: TelegramHarness;
|
|
20
|
+
|
|
21
|
+
type TaskRow = { id: string; task: string; status: string; parentTaskId: string | null };
|
|
22
|
+
|
|
23
|
+
async function chatTasks(chatId: number): Promise<TaskRow[]> {
|
|
24
|
+
return getDbClient().query<TaskRow>(
|
|
25
|
+
`SELECT id, task, status, parentTaskId FROM agent_tasks
|
|
26
|
+
WHERE source = 'chat' AND contextKey = ? ORDER BY createdAt, rowid`,
|
|
27
|
+
[chatSdkContextKey(`telegram:${chatId}`)],
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function removeDbFiles(): Promise<void> {
|
|
32
|
+
for (const suffix of ["", "-wal", "-shm"]) {
|
|
33
|
+
await unlink(`${TEST_DB_PATH}${suffix}`).catch(() => {});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const dm = (id: number): TelegramChat => ({ id, type: "private" });
|
|
38
|
+
|
|
39
|
+
/** A `/command` update: Telegram marks the command with a `bot_command` entity at offset 0. */
|
|
40
|
+
function commandUpdate(chat: TelegramChat, command: string): Record<string, unknown> {
|
|
41
|
+
return telegramUpdate(chat, command, {
|
|
42
|
+
entities: [{ type: "bot_command", offset: 0, length: command.length }],
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Text of every message the bot sent to `chatId`, in order. */
|
|
47
|
+
function sentTexts(chatId: number): string[] {
|
|
48
|
+
return harness.mock.calls
|
|
49
|
+
.filter((call) => /^send(Rich)?Message$/.test(call.method))
|
|
50
|
+
.filter((call) => String(call.body.chat_id) === String(chatId))
|
|
51
|
+
.map((call) => {
|
|
52
|
+
const rich = call.body.rich_message as { markdown?: string } | undefined;
|
|
53
|
+
return String(call.body.text ?? rich?.markdown ?? "");
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
beforeAll(async () => {
|
|
58
|
+
await removeDbFiles();
|
|
59
|
+
initDb(TEST_DB_PATH);
|
|
60
|
+
await createAgent({ id: crypto.randomUUID(), name: "CmdLead", isLead: true, status: "idle" });
|
|
61
|
+
harness = await startTelegramHarness();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
afterAll(async () => {
|
|
65
|
+
await harness.stop();
|
|
66
|
+
closeDb();
|
|
67
|
+
await removeDbFiles();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe("Chat SDK slash commands", () => {
|
|
71
|
+
test("the command list is published to Telegram at adapter start", () => {
|
|
72
|
+
const call = harness.mock.calls.find((c) => c.method === "setMyCommands");
|
|
73
|
+
expect(call).toBeDefined();
|
|
74
|
+
const names = (call!.body.commands as { command: string }[]).map((c) => c.command);
|
|
75
|
+
expect(names).toEqual(CHAT_COMMANDS.map((c) => c.command));
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("/help answers with the list and creates no task", async () => {
|
|
79
|
+
await postTelegramUpdate(commandUpdate(dm(3001), "/help"));
|
|
80
|
+
const texts = sentTexts(3001);
|
|
81
|
+
expect(texts).toHaveLength(1);
|
|
82
|
+
for (const spec of CHAT_COMMANDS) expect(texts[0]).toContain(`/${spec.command}`);
|
|
83
|
+
await flushChatBuffers();
|
|
84
|
+
expect(await chatTasks(3001)).toHaveLength(0);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("an unknown command gets the list instead of silence", async () => {
|
|
88
|
+
await postTelegramUpdate(commandUpdate(dm(3002), "/deploy"));
|
|
89
|
+
const texts = sentTexts(3002);
|
|
90
|
+
expect(texts).toHaveLength(1);
|
|
91
|
+
expect(texts[0]).toContain("Unknown command /deploy");
|
|
92
|
+
expect(texts[0]).toContain("/help");
|
|
93
|
+
await flushChatBuffers();
|
|
94
|
+
expect(await chatTasks(3002)).toHaveLength(0);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("/status reports the latest task and /cancel cancels the active one", async () => {
|
|
98
|
+
await postTelegramUpdate(commandUpdate(dm(3003), "/status"));
|
|
99
|
+
expect(sentTexts(3003).at(-1)).toContain("No task yet");
|
|
100
|
+
|
|
101
|
+
await postTelegramUpdate(telegramUpdate(dm(3003), "please look into the build"));
|
|
102
|
+
await flushChatBuffers();
|
|
103
|
+
const [task] = await chatTasks(3003);
|
|
104
|
+
expect(task).toBeDefined();
|
|
105
|
+
expect(task!.status).toBe("pending");
|
|
106
|
+
|
|
107
|
+
await postTelegramUpdate(commandUpdate(dm(3003), "/status"));
|
|
108
|
+
const status = sentTexts(3003).at(-1)!;
|
|
109
|
+
expect(status).toContain(task!.id.slice(0, 8));
|
|
110
|
+
expect(status).toContain("pending");
|
|
111
|
+
expect(status).toContain("/cancel");
|
|
112
|
+
|
|
113
|
+
await postTelegramUpdate(commandUpdate(dm(3003), "/cancel"));
|
|
114
|
+
expect(sentTexts(3003).at(-1)).toContain(`Cancelled task ${task!.id.slice(0, 8)}`);
|
|
115
|
+
const cancelled = await getTaskById(task!.id);
|
|
116
|
+
expect(cancelled?.status).toBe("cancelled");
|
|
117
|
+
expect(cancelled?.failureReason).toContain("Cancelled from chat by tester");
|
|
118
|
+
|
|
119
|
+
await postTelegramUpdate(commandUpdate(dm(3003), "/cancel"));
|
|
120
|
+
expect(sentTexts(3003).at(-1)).toContain("No running task");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("/reset starts a new conversation: no parent task and no older context", async () => {
|
|
124
|
+
await postTelegramUpdate(telegramUpdate(dm(3004), "the first request"));
|
|
125
|
+
await flushChatBuffers();
|
|
126
|
+
await postTelegramUpdate(telegramUpdate(dm(3004), "a follow-up"));
|
|
127
|
+
await flushChatBuffers();
|
|
128
|
+
let tasks = await chatTasks(3004);
|
|
129
|
+
expect(tasks).toHaveLength(2);
|
|
130
|
+
expect(tasks[1]!.parentTaskId).toBe(tasks[0]!.id);
|
|
131
|
+
expect(tasks[1]!.task).toContain("<thread_context>");
|
|
132
|
+
expect(tasks[1]!.task).toContain("the first request");
|
|
133
|
+
|
|
134
|
+
// Reset while a task still runs: the mark is set, and the reply says so.
|
|
135
|
+
await postTelegramUpdate(commandUpdate(dm(3004), "/reset"));
|
|
136
|
+
expect(sentTexts(3004).at(-1)).toContain("Context reset");
|
|
137
|
+
expect(sentTexts(3004).at(-1)).toContain("still running");
|
|
138
|
+
|
|
139
|
+
// The usual case: the thread's tasks are done when the user resets.
|
|
140
|
+
await getDbClient().run("UPDATE agent_tasks SET status = 'completed' WHERE contextKey = ?", [
|
|
141
|
+
chatSdkContextKey("telegram:3004"),
|
|
142
|
+
]);
|
|
143
|
+
await postTelegramUpdate(commandUpdate(dm(3004), "/reset"));
|
|
144
|
+
expect(sentTexts(3004).at(-1)).not.toContain("still running");
|
|
145
|
+
// The reset mark is compared to message timestamps with second precision.
|
|
146
|
+
await Bun.sleep(1_100);
|
|
147
|
+
|
|
148
|
+
await postTelegramUpdate(telegramUpdate(dm(3004), "a brand new topic"));
|
|
149
|
+
await flushChatBuffers();
|
|
150
|
+
tasks = await chatTasks(3004);
|
|
151
|
+
expect(tasks).toHaveLength(3);
|
|
152
|
+
expect(tasks[2]!.parentTaskId).toBeNull();
|
|
153
|
+
expect(tasks[2]!.task).not.toContain("<thread_context>");
|
|
154
|
+
expect(tasks[2]!.task).not.toContain("the first request");
|
|
155
|
+
|
|
156
|
+
await postTelegramUpdate(telegramUpdate(dm(3004), "more on the new topic"));
|
|
157
|
+
await flushChatBuffers();
|
|
158
|
+
tasks = await chatTasks(3004);
|
|
159
|
+
expect(tasks).toHaveLength(4);
|
|
160
|
+
expect(tasks[3]!.parentTaskId).toBe(tasks[2]!.id);
|
|
161
|
+
expect(tasks[3]!.task).toContain("a brand new topic");
|
|
162
|
+
expect(tasks[3]!.task).not.toContain("the first request");
|
|
163
|
+
expect(tasks[3]!.task).not.toContain("a follow-up");
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
describe("Chat SDK thread context across an adapter restart", () => {
|
|
168
|
+
test("the SDK persists Telegram history under the pinned key prefix", async () => {
|
|
169
|
+
await postTelegramUpdate(telegramUpdate(dm(3005), "remember this one"));
|
|
170
|
+
await flushChatBuffers();
|
|
171
|
+
const history = await readPersistedThreadHistory("telegram:3005");
|
|
172
|
+
expect(history.map((m) => m.text)).toContain("remember this one");
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test("a message after a restart still carries the earlier thread messages", async () => {
|
|
176
|
+
await postTelegramUpdate(telegramUpdate(dm(3006), "before the restart"));
|
|
177
|
+
await flushChatBuffers();
|
|
178
|
+
|
|
179
|
+
// Same as a config reload or an API restart: the adapter's memory is gone.
|
|
180
|
+
await stopChatSdk();
|
|
181
|
+
await startChatSdk();
|
|
182
|
+
|
|
183
|
+
await postTelegramUpdate(telegramUpdate(dm(3006), "after the restart"));
|
|
184
|
+
await flushChatBuffers();
|
|
185
|
+
const tasks = await chatTasks(3006);
|
|
186
|
+
expect(tasks).toHaveLength(2);
|
|
187
|
+
expect(tasks[1]!.parentTaskId).toBe(tasks[0]!.id);
|
|
188
|
+
expect(tasks[1]!.task).toContain("<thread_context>");
|
|
189
|
+
expect(tasks[1]!.task).toContain("before the restart");
|
|
190
|
+
});
|
|
191
|
+
});
|