@desplega.ai/agent-swarm 1.173.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{acp-adapter-ccm9xrep.js → acp-adapter-kr4abrsk.js} +17 -16
- package/dist/{actions-sgeyv08k.js → actions-eh4g6w2r.js} +39 -34
- package/dist/{amp-adapter-gpxp3kc9.js → amp-adapter-7h7wdt8q.js} +8 -7
- package/dist/{app-km9qnv2f.js → app-7k8s9w5p.js} +18 -14
- package/dist/{assistant-5r8tcvvd.js → assistant-wswt2y7p.js} +40 -35
- package/dist/{boot-reembed-16rv9843.js → boot-reembed-9apm9qw6.js} +10 -9
- package/dist/{boot-reembed-swwzdgd7.js → boot-reembed-whzbdthj.js} +10 -9
- package/dist/{boot-scrub-sweep-zkr6vkqb.js → boot-scrub-sweep-ndzmvmgt.js} +11 -10
- package/dist/{boot-seeds-d8sp49t2.js → boot-seeds-e9sta0yt.js} +16 -13
- package/dist/{claude-adapter-yzzzh8jp.js → claude-adapter-4mqd3r6r.js} +19 -18
- package/dist/{claude-managed-adapter-xgxhqrt2.js → claude-managed-adapter-9wswb52m.js} +7 -5
- package/dist/{claude-managed-setup-bnmjarjc.js → claude-managed-setup-tpe1effx.js} +6 -4
- package/dist/{claude-sdk-session-4whxere4.js → claude-sdk-session-q49ghmh9.js} +12402 -11850
- package/dist/{cli-ne7smj1b.js → cli-01xvj1rq.js} +10 -10
- package/dist/{cli-nwrdx840.js → cli-035vekcw.js} +3 -3
- package/dist/{cli-9xf7fdtp.js → cli-0pd8hzhc.js} +29 -6
- package/dist/{cli-tmhsxhb8.js → cli-0veb92a3.js} +28 -6
- package/dist/{cli-c3rk1v0x.js → cli-10jesr25.js} +3 -3
- package/dist/{cli-6thk4jz3.js → cli-1y7yywmj.js} +6 -6
- package/dist/{cli-6r2f7edk.js → cli-20krdhz5.js} +47 -19
- package/dist/{cli-rfs4vddv.js → cli-29rja109.js} +39 -20
- package/dist/cli-2m1ktfzz.js +17795 -0
- package/dist/{cli-na2pxe4f.js → cli-2pwa0q4d.js} +15 -4
- package/dist/{cli-7a93cv8c.js → cli-4mengw46.js} +441 -43
- package/dist/{cli-94b48jhk.js → cli-5twdke5x.js} +16 -14
- package/dist/{cli-7a0sde2x.js → cli-6cagzc96.js} +9 -0
- package/dist/cli-6cvfxs8r.js +47 -0
- package/dist/{cli-w1apjecb.js → cli-711vwbxn.js} +2 -2
- package/dist/{cli-r4fqfpwf.js → cli-78vv7446.js} +50 -27
- package/dist/{cli-srqq3gc5.js → cli-7n6s5man.js} +5 -5
- package/dist/{cli-m89t9jdf.js → cli-7x0g4sn1.js} +15416 -16024
- package/dist/{cli-2bw3w47j.js → cli-8wwxw8a3.js} +1 -1
- package/dist/{cli-yatxqynm.js → cli-9ha6hs2d.js} +1 -290
- package/dist/{cli-ywm1azg9.js → cli-9jzdj90z.js} +8 -4
- package/dist/cli-9traj3dn.js +638 -0
- package/dist/{cli-smcrrmhy.js → cli-agzebbd5.js} +9 -9
- package/dist/{cli-c5km5znr.js → cli-c8r3kzzb.js} +1 -1
- package/dist/cli-cvv9k0hh.js +10143 -0
- package/dist/{cli-anzdk0z4.js → cli-d9286m9h.js} +2 -2
- package/dist/cli-e3g5mbkz.js +776 -0
- package/dist/cli-f527ce74.js +781 -0
- package/dist/{cli-gmdajfw5.js → cli-gxzkg1de.js} +5020 -1729
- package/dist/{cli-451ks6aw.js → cli-h1arqn31.js} +1 -1
- package/dist/cli-hjwmhcg1.js +10622 -0
- package/dist/{cli-jpc4r224.js → cli-hqr88ew6.js} +19 -3
- package/dist/{cli-h7k0xtry.js → cli-hvqbtna4.js} +4 -1256
- package/dist/{cli-cd0hv9nh.js → cli-j67mh2vg.js} +2 -2
- package/dist/cli-jta2eyzc.js +619 -0
- package/dist/{cli-wd0hz6ah.js → cli-kbrt8gqa.js} +9 -91
- package/dist/cli-kceah1eh.js +92 -0
- package/dist/{cli-d603qf86.js → cli-ksphgh48.js} +10 -8
- package/dist/{cli-3nxdmjrc.js → cli-mgz4nbgg.js} +4 -4
- package/dist/{cli-saqyage0.js → cli-mz5as5ha.js} +179 -27
- package/dist/cli-n2wq4j7m.js +6985 -0
- package/dist/{cli-ppy4nnnh.js → cli-n8ndw8j7.js} +2 -2
- package/dist/{cli-1w0vj3gw.js → cli-naqzr17j.js} +7 -7
- package/dist/{cli-0a7mm3zp.js → cli-nm1p8jfr.js} +25 -21
- package/dist/{cli-wrh08zjd.js → cli-p3zbc1rw.js} +5 -18
- package/dist/{cli-hsem16hb.js → cli-pe05f8yk.js} +29 -6
- package/dist/{cli-5wpw4rab.js → cli-qmzsbkvy.js} +5 -5
- package/dist/{cli-bv2etw2y.js → cli-qnm4e9e4.js} +99 -590
- package/dist/{cli-1x7da0de.js → cli-qqy4q48b.js} +8 -8
- package/dist/{cli-r44t9wqc.js → cli-r106725k.js} +2 -2
- package/dist/{cli-5er5gjbm.js → cli-r35rw5de.js} +15 -15
- package/dist/{cli-v3zhnxsb.js → cli-r610p9bs.js} +2 -2
- package/dist/{cli-7jykv7ef.js → cli-rc6pf5kv.js} +1 -1
- package/dist/{cli-x119kdba.js → cli-rdxm77k3.js} +1 -1
- package/dist/cli-rhs9pz33.js +294 -0
- package/dist/{cli-0p3enk35.js → cli-s69nfyeh.js} +9 -1
- package/dist/{cli-0kxf81jj.js → cli-sawv63e8.js} +2 -2
- package/dist/{cli-e3pmyt39.js → cli-sg19gbf0.js} +1 -1
- package/dist/{cli-d161g69q.js → cli-shk7r5cs.js} +57 -6
- package/dist/{cli-7pz3b7x5.js → cli-sksetb2q.js} +36 -1
- package/dist/{cli-014vns2y.js → cli-stfs0k5s.js} +208 -525
- package/dist/{cli-db3m6zdt.js → cli-t02z59f2.js} +1 -1
- package/dist/{cli-b07hr3bt.js → cli-txqb9s38.js} +2 -2
- package/dist/{cli-s8z6x4b4.js → cli-v7g551te.js} +225 -113
- package/dist/{cli-rfz2ve55.js → cli-vgqzp4em.js} +3341 -955
- package/dist/{cli-7avy4v8w.js → cli-w1q7h2r2.js} +5 -3
- package/dist/{cli-rcp5aqdw.js → cli-w7m81tb7.js} +1 -1
- package/dist/cli-wfnemr1t.js +86 -0
- package/dist/cli-wmgrkscj.js +23254 -0
- package/dist/{cli-qm1448f9.js → cli-wms6x9db.js} +8 -8
- package/dist/{cli-tadfgd0t.js → cli-x1r545x8.js} +33 -4
- package/dist/{cli-xc7p7s3g.js → cli-xczsgshn.js} +6 -6
- package/dist/{cli-fmhj1ehj.js → cli-y3167wt1.js} +882 -271
- package/dist/{cli-r0byd61h.js → cli-yjmqpxt3.js} +17 -16
- package/dist/{cli-81r1wjyv.js → cli-z780ec7c.js} +1 -1
- package/dist/{cli-phcqp1b9.js → cli-zm9t2vde.js} +2 -2
- package/dist/cli.js +34 -49
- package/dist/{codex-adapter-r3dn4fp5.js → codex-adapter-4z16v9n0.js} +15 -14
- package/dist/{codex-login-841kv5fk.js → codex-login-pkepy2j1.js} +3 -2
- package/dist/{codex-session-runner-fwt793h4.js → codex-session-runner-rvms9y2x.js} +15 -14
- package/dist/{commands-z8fq9qna.js → commands-2a9rtyat.js} +11 -10
- package/dist/{controller-hq5ggpcp.js → controller-3k3madbn.js} +5 -4
- package/dist/{controllers-connect-3mf2btyj.js → controllers-connect-v9yn8zb9.js} +42 -37
- package/dist/{cursor-adapter-q8pgv5qa.js → cursor-adapter-98gbhyzn.js} +7 -6
- package/dist/{db-qfpq4bt7.js → db-eywwsnjw.js} +30 -9
- package/dist/{db-yyhqqrbf.js → db-ncypk9np.js} +12 -11
- package/dist/{devin-adapter-xgtj680j.js → devin-adapter-zeq8b9gz.js} +3 -2
- package/dist/{dispatcher-x0x7qfcq.js → dispatcher-w94kc575.js} +38 -33
- package/dist/{docker-f503k9p0.js → docker-b1e4cqab.js} +3 -2
- package/dist/{dsh-adapter-34etvb5m.js → dsh-adapter-47ztej36.js} +7 -6
- package/dist/{e2b-66xra4az.js → e2b-bbt8ey45.js} +4 -3
- package/dist/{e2b-xwhyj8cw.js → e2b-dp804sen.js} +4 -3
- package/dist/{event-bus-vjtv82gm.js → event-bus-j2jdg3qb.js} +3 -2
- package/dist/{external-channel-h1fjecwe.js → external-channel-zrkg8ar9.js} +11 -10
- package/dist/{google-generative-ai-szdxbpqg.js → google-generative-ai-g8db7x0f.js} +4 -2
- package/dist/{google-vertex-qzw3p36d.js → google-vertex-r2692q0r.js} +4 -2
- package/dist/{grok-adapter-e7mr287e.js → grok-adapter-s9ncp9vy.js} +18 -17
- package/dist/{handlers-znb9n7d4.js → handlers-kqftvjgw.js} +41 -36
- package/dist/{heartbeat-bm4qkn8d.js → heartbeat-9q96p8v9.js} +43 -38
- package/dist/{hook-254r4mza.js → hook-56qe10dp.js} +11 -23
- package/dist/{hook-xaa3q80s.js → hook-yy6rw42k.js} +12 -11
- package/dist/{http-3dgrgp8y.js → http-r2dnb8f8.js} +2411 -275
- package/dist/{index-fx3jqq1e.js → index-3gtmkhc6.js} +38 -33
- package/dist/{index-bdsqa799.js → index-3k5hkz7r.js} +43 -38
- package/dist/index-52j1aja4.js +3465 -0
- package/dist/{index-g2byveez.js → index-5e101240.js} +38 -33
- package/dist/{index-2ybk7khy.js → index-5wyfjc2c.js} +845 -683
- package/dist/index-9gw58n4k.js +193 -0
- package/dist/index-9j87mcm8.js +56305 -0
- package/dist/index-bsmcresk.js +79424 -0
- package/dist/index-mh85qhec.js +475 -0
- package/dist/{index-jd7sejm2.js → index-vxvhxxbn.js} +10 -9
- package/dist/index-we27kd31.js +12810 -0
- package/dist/{keepalive-6ywhpja1.js → keepalive-psj6j54c.js} +12 -11
- package/dist/{lead-cw45n21s.js → lead-hajz4688.js} +61 -56
- package/dist/{maintenance-ss1a1ge9.js → maintenance-ps02sb5m.js} +10 -9
- package/dist/{node-fkajaq96.js → node-mwb5r697.js} +2 -1
- package/dist/{oauth-refresh-sweep-3c6f8vyy.js → oauth-refresh-sweep-0284h93b.js} +11 -10
- package/dist/{onboard-8hzdhc1k.js → onboard-rbf6bg01.js} +11 -3
- package/dist/{opencode-adapter-qbchbcfz.js → opencode-adapter-13dcatgq.js} +18 -13
- package/dist/{otel-impl-e6mnvc4v.js → otel-impl-zvqcbhp7.js} +242 -868
- package/dist/{pi-mono-adapter-3c9g9snh.js → pi-mono-adapter-xxy47q4v.js} +244 -106
- package/dist/{pricing-refresh-gedkjdw0.js → pricing-refresh-kefx9v3t.js} +12 -11
- package/dist/{rbac-roles-dt4vh1yy.js → rbac-roles-3m36rxm2.js} +11 -10
- package/dist/{rbac-roles-eszdg6pm.js → rbac-roles-z76xv0hq.js} +12 -11
- package/dist/{render-v2-907cy6sa.js → render-v2-wz73bs25.js} +18 -14
- package/dist/{resume-42stcaz0.js → resume-k4wzge8w.js} +25 -21
- package/dist/{runtime-qfry88v2.js → runtime-sa2vr16g.js} +2 -2
- package/dist/{setup-adbc1c10.js → setup-2fp2pypp.js} +2 -2
- package/dist/{virtual-modules-rtmt494q.js → virtual-modules-9gkxqrap.js} +6 -5
- package/dist/{work-objects-wpj8ck95.js → work-objects-6k0ywz3m.js} +41 -36
- package/dist/{worker-bzdra9rf.js → worker-5v3rxsdh.js} +61 -56
- package/dist/{x-qfmygqqv.js → x-4mtdeym1.js} +3 -2
- package/openapi.json +1279 -329
- package/package.json +15 -15
- package/src/agentmail/handlers.ts +21 -11
- package/src/apps/definition.ts +16 -0
- package/src/azure-devops/api.ts +52 -2
- package/src/azure-devops/boards-sync.ts +50 -20
- package/src/azure-devops/index.ts +1 -0
- package/src/azure-devops/outbound.ts +95 -0
- package/src/be/boot-seeds.ts +11 -0
- package/src/be/config-refs.ts +60 -0
- package/src/be/db/model-profiles.ts +315 -0
- package/src/be/db/page-versions.ts +15 -0
- package/src/be/db/runtime.ts +35 -8
- package/src/be/db/skill-name-resolution.ts +36 -0
- package/src/be/db/tasks/read.ts +237 -0
- package/src/be/db/tasks/write.ts +7 -0
- package/src/be/db.ts +87 -36
- package/src/be/migrations/215_swarm_config_export_to_env.sql +34 -0
- package/src/be/migrations/216_task_resolved_effort.sql +5 -0
- package/src/be/migrations/217_chat_sdk_state.sql +104 -0
- package/src/be/migrations/219_relabel_pi_context_compactions.sql +22 -0
- package/src/be/migrations/220_model_profiles.sql +137 -0
- package/src/be/model-catalog-store.ts +5 -1
- package/src/be/model-tier-resolution.ts +155 -10
- package/src/be/model-validation.ts +12 -2
- package/src/be/models-catalog.ts +26 -1
- package/src/be/modelsdev-cache.json +198 -63
- package/src/be/modelsdev-cache.ts +9 -1
- package/src/be/script-credential-broker.ts +39 -2
- package/src/be/scripts/run-saved.ts +1 -0
- package/src/be/scripts/typecheck.ts +22 -8
- package/src/be/secret-registry.ts +6 -2
- package/src/be/seed/runner.ts +13 -1
- package/src/be/seed/state-db.ts +5 -0
- package/src/be/seed/types.ts +6 -0
- package/src/be/seed-model-profiles.ts +41 -0
- package/src/be/seed-pricing.ts +19 -2
- package/src/be/seed-scripts/catalog/heartbeat-jev-gate.ts +368 -0
- package/src/be/seed-scripts/catalog/ops-catalog-audit.inline.ts +1 -1
- package/src/be/seed-scripts/catalog/ops-catalog-audit.ts +1 -1
- package/src/be/seed-scripts/index.ts +9 -0
- package/src/be/seed-skills/bundled-files.generated.json +0 -18
- package/src/be/seed-skills/index.ts +104 -3
- package/src/be/swarm-config-guard.ts +53 -0
- package/src/cli.tsx +0 -16
- package/src/commands/onboard/non-interactive.ts +4 -2
- package/src/commands/provider-credentials.ts +52 -6
- package/src/commands/runner.ts +193 -71
- package/src/e2b/env.ts +5 -0
- package/src/extensions/catalog.generated.json +17 -0
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/extensions/contract.ts +45 -0
- package/src/extensions/dispatcher.ts +7 -0
- package/src/extensions/loader.ts +10 -5
- package/src/extensions/post-bridge.ts +40 -0
- package/src/github/handlers.ts +11 -4
- package/src/heartbeat/heartbeat.ts +2 -1
- package/src/heartbeat/templates.ts +8 -5
- package/src/hooks/hook.ts +0 -26
- package/src/http/agents.ts +49 -2
- package/src/http/all-routes.ts +1 -1
- package/src/http/apps.ts +30 -3
- package/src/http/config.ts +62 -19
- package/src/http/core.ts +8 -0
- package/src/http/index.ts +25 -3
- package/src/http/integrations.ts +88 -1
- package/src/http/mcp.ts +15 -5
- package/src/http/model-profiles.ts +303 -0
- package/src/http/page-proxy.ts +2 -4
- package/src/http/pages-public.ts +13 -7
- package/src/http/pages.ts +3 -13
- package/src/http/poll.ts +42 -71
- package/src/http/requested-by.ts +54 -0
- package/src/http/script-runs.ts +1 -1
- package/src/http/scripts.ts +26 -2
- package/src/http/stats.ts +1 -30
- package/src/http/tasks.ts +79 -5
- package/src/http/utils.ts +14 -0
- package/src/http/webhooks.ts +160 -3
- package/src/http/x.ts +1 -0
- package/src/integrations/chat-sdk/adapters.ts +129 -0
- package/src/integrations/chat-sdk/attachments.ts +232 -0
- package/src/integrations/chat-sdk/background.ts +36 -0
- package/src/integrations/chat-sdk/commands.ts +194 -0
- package/src/integrations/chat-sdk/config.ts +198 -0
- package/src/integrations/chat-sdk/discord-api.ts +63 -0
- package/src/integrations/chat-sdk/discord-gateway.ts +185 -0
- package/src/integrations/chat-sdk/handlers.ts +28 -0
- package/src/integrations/chat-sdk/inbound.ts +426 -0
- package/src/integrations/chat-sdk/limits.ts +48 -0
- package/src/integrations/chat-sdk/logger.ts +81 -0
- package/src/integrations/chat-sdk/outbound.ts +123 -0
- package/src/integrations/chat-sdk/relay.ts +172 -0
- package/src/integrations/chat-sdk/router.ts +169 -0
- package/src/integrations/chat-sdk/runtime.ts +351 -0
- package/src/integrations/chat-sdk/schema.ts +245 -0
- package/src/integrations/chat-sdk/state.ts +243 -0
- package/src/integrations/chat-sdk/status.ts +244 -0
- package/src/integrations/chat-sdk/steering.ts +58 -0
- package/src/integrations/chat-sdk/teams-origin.ts +77 -0
- package/src/integrations/chat-sdk/telegram-api.ts +56 -0
- package/src/integrations/chat-sdk/templates.ts +16 -0
- package/src/integrations/chat-sdk/test-hooks.ts +37 -0
- package/src/integrations/chat-sdk/thread-buffer.ts +100 -0
- package/src/integrations/chat-sdk/typing.ts +65 -0
- package/src/{artifact-sdk → pages}/feedback-overlay.ts +65 -6
- package/src/pages/version.ts +4 -5
- package/src/prompts/base-prompt.ts +111 -45
- package/src/providers/acp-target-catalog.ts +6 -0
- package/src/providers/acp-targets.ts +56 -3
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/claude-adapter.ts +1 -1
- package/src/providers/claude-flag-settings.ts +17 -5
- package/src/providers/claude-managed-adapter.ts +1 -1
- package/src/providers/claude-session-events.ts +6 -1
- package/src/providers/dsh-adapter.ts +1 -1
- package/src/providers/opencode-adapter.ts +243 -26
- package/src/providers/opencode-swarm-plugin.ts +31 -0
- package/src/providers/pi-mono-adapter.ts +398 -153
- package/src/providers/pi-mono-extension.ts +17 -3
- package/src/providers/pricing-sources.md +33 -2
- package/src/providers/reasoning-effort.ts +37 -0
- package/src/providers/types.ts +6 -2
- package/src/rbac/legacy-policy.ts +16 -2
- package/src/rbac/permissions.ts +26 -0
- package/src/scheduler/deferred-task-waits.ts +18 -5
- package/src/scheduler/scheduler.ts +11 -3
- package/src/scripts-runtime/executors/quickjs-prelude.ts +75 -6
- package/src/scripts-runtime/executors/quickjs-runner.ts +85 -8
- package/src/scripts-runtime/executors/quickjs-web-shims.ts +1041 -0
- package/src/scripts-runtime/extract-args-schema.ts +9 -8
- package/src/scripts-runtime/loader.ts +19 -7
- package/src/scripts-runtime/quickjs-compat.ts +464 -0
- package/src/scripts-runtime/sdk-allowlist.ts +12 -6
- package/src/scripts-runtime/swarm-sdk.ts +0 -11
- package/src/scripts-runtime/types/stdlib.d.ts +39 -15
- package/src/scripts-runtime/types/swarm-extension.d.ts +66 -1
- package/src/scripts-runtime/types/swarm-sdk.d.ts +39 -15
- package/src/server.ts +62 -19
- package/src/slack/blocks.ts +4 -2
- package/src/slack/event-dedup.ts +1 -1
- package/src/slack/reaction-shortcode.ts +14 -0
- package/src/slack/render-v2.ts +12 -7
- package/src/slack/responses.ts +5 -4
- package/src/slack/thread-buffer.ts +41 -22
- package/src/tasks/additive-buffer.ts +4 -2
- package/src/tasks/context-key.ts +60 -1
- package/src/tasks/model-pin.ts +67 -0
- package/src/tasks/worker-follow-up.ts +15 -33
- package/src/tests/acp-adapter.test.ts +42 -0
- package/src/tests/agentmail-filters.test.ts +47 -0
- package/src/tests/api-drain-worker-e2e.test.ts +1 -6
- package/src/tests/apps-spike2.test.ts +35 -0
- package/src/tests/azure-devops-outbound.test.ts +420 -0
- package/src/tests/base-prompt.test.ts +58 -54
- package/src/tests/chat-sdk-commands.test.ts +191 -0
- package/src/tests/chat-sdk-config.test.ts +334 -0
- package/src/tests/chat-sdk-discord-api.test.ts +68 -0
- package/src/tests/chat-sdk-discord-gateway.test.ts +252 -0
- package/src/tests/chat-sdk-discord-webhook.test.ts +126 -0
- package/src/tests/chat-sdk-fields-sync.test.ts +50 -0
- package/src/tests/chat-sdk-harness.ts +146 -0
- package/src/tests/chat-sdk-inbound.test.ts +363 -0
- package/src/tests/chat-sdk-relay.test.ts +470 -0
- package/src/tests/chat-sdk-reload.test.ts +128 -0
- package/src/tests/chat-sdk-shutdown.test.ts +220 -0
- package/src/tests/chat-sdk-slack.test.ts +268 -0
- package/src/tests/chat-sdk-state.test.ts +163 -0
- package/src/tests/chat-sdk-status-route.test.ts +382 -0
- package/src/tests/chat-sdk-teams.test.ts +277 -0
- package/src/tests/chat-sdk-tools.test.ts +520 -0
- package/src/tests/chat-sdk-typing.test.ts +120 -0
- package/src/tests/chat-sdk-webhook-telegram.test.ts +200 -0
- package/src/tests/claude-adapter-binary.test.ts +9 -4
- package/src/tests/claude-flag-settings.test.ts +11 -3
- package/src/tests/claude-managed-adapter.test.ts +1 -1
- package/src/tests/claude-sdk-lifecycle.test.ts +9 -3
- package/src/tests/claude-thinking-tokens.test.ts +57 -0
- package/src/tests/claude-worker-parity.test.ts +2 -7
- package/src/tests/config-env-export-chat-sdk.test.ts +11 -0
- package/src/tests/config-refs.test.ts +66 -0
- package/src/tests/context-key.test.ts +19 -0
- package/src/tests/cors-allowlist.test.ts +25 -0
- package/src/tests/create-page-tool.test.ts +18 -0
- package/src/tests/credential-check.test.ts +139 -0
- package/src/tests/credentials.test.ts +21 -0
- package/src/tests/db-runtime.test.ts +61 -0
- package/src/tests/db-task-read-contract.test.ts +22 -0
- package/src/tests/db-task-write-contract.test.ts +11 -0
- package/src/tests/deferred-task-wake.test.ts +78 -1
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/dsh-adapter.test.ts +1 -1
- package/src/tests/enabled-capabilities.test.ts +20 -0
- package/src/tests/entrypoint-api-readiness.test.ts +14 -8
- package/src/tests/entrypoint-config-env-export.test.ts +5 -2
- package/src/tests/entrypoint-export-to-env.test.ts +78 -0
- package/src/tests/env-flag.test.ts +1 -0
- package/src/tests/explicit-model-validation.test.ts +105 -1
- package/src/tests/extensions-catalog.test.ts +1 -0
- package/src/tests/extensions-example-comms-adherence-gate.test.ts +413 -0
- package/src/tests/extensions-example-route-chat-channel.test.ts +154 -0
- package/src/tests/extensions-loader.test.ts +15 -0
- package/src/tests/extensions-post-bridge.test.ts +51 -0
- package/src/tests/fetch-resolved-env.test.ts +128 -0
- package/src/tests/fixtures/chat-sdk/telegram-private-text.json +10 -0
- package/src/tests/fixtures/extensions/route-chat-channel-to-agent/hooks.ts +27 -0
- package/src/tests/fixtures/extensions/route-chat-channel-to-agent/manifest.json +9 -0
- package/src/tests/fixtures/openai-compatible-registry-extension.child.ts +146 -0
- package/src/tests/fixtures/runner-legacy-resume-prompt.ts +122 -0
- package/src/tests/github-handlers.test.ts +35 -0
- package/src/tests/harness-provider-resolution.test.ts +12 -0
- package/src/tests/heartbeat-checklist.test.ts +3 -2
- package/src/tests/heartbeat-jev-gate-credentials.test.ts +168 -0
- package/src/tests/heartbeat-jev-gate.test.ts +385 -0
- package/src/tests/hook-runtime-close.test.ts +0 -2
- package/src/tests/http-api-integration.test.ts +0 -25
- package/src/tests/migration-046-budgets.test.ts +17 -16
- package/src/tests/migration-217-chat-sdk-state.test.ts +113 -0
- package/src/tests/model-profile-backcompat.test.ts +327 -0
- package/src/tests/model-profile-providers.test.ts +302 -0
- package/src/tests/model-profile-runtime-secrets.test.ts +223 -0
- package/src/tests/model-profiles-api.test.ts +614 -0
- package/src/tests/model-tier-resolution.test.ts +93 -0
- package/src/tests/models-catalog.test.ts +42 -1
- package/src/tests/openai-compatible-providers.test.ts +134 -0
- package/src/tests/openai-compatible-registry-extension.test.ts +42 -0
- package/src/tests/opencode-adapter.test.ts +216 -2
- package/src/tests/opencode-helmcode-sdk.test.ts +201 -0
- package/src/tests/opencode-shell-env.test.ts +145 -0
- package/src/tests/openrouter-base-url.test.ts +101 -151
- package/src/tests/page-proxy.test.ts +1 -1
- package/src/tests/pages-browser-sdk.test.ts +76 -0
- package/src/tests/pages-feedback-keys.test.ts +121 -0
- package/src/tests/pages-feedback.test.ts +4 -1
- package/src/tests/paused-tasks-requested-by.test.ts +102 -0
- package/src/tests/pi-context-compaction.test.ts +193 -0
- package/src/tests/pi-custom-provider-fake-server.test.ts +442 -0
- package/src/tests/pi-mono-adapter.test.ts +385 -5
- package/src/tests/pi-shell-env.test.ts +170 -0
- package/src/tests/profile-claim.test.ts +269 -0
- package/src/tests/prompt-template-remaining.test.ts +35 -0
- package/src/tests/quickjs-compat.test.ts +213 -0
- package/src/tests/rbac-engine.test.ts +32 -3
- package/src/tests/runner-exit-after-tasks.test.ts +0 -4
- package/src/tests/runner-legacy-resume-prompt.test.ts +64 -0
- package/src/tests/script-connections.test.ts +6 -0
- package/src/tests/script-executor-conformance.test.ts +252 -1
- package/src/tests/script-executor-quickjs.test.ts +96 -0
- package/src/tests/script-runs-http.test.ts +89 -0
- package/src/tests/scripts-http.test.ts +217 -1
- package/src/tests/scripts-only-gating.test.ts +2 -3
- package/src/tests/scripts-runtime.test.ts +58 -40
- package/src/tests/secret-scrubber.test.ts +96 -0
- package/src/tests/seed-model-profiles.test.ts +174 -0
- package/src/tests/seed-scripts.test.ts +2 -2
- package/src/tests/seed-skills-bundled-files.test.ts +45 -53
- package/src/tests/seed-skills-retire.test.ts +182 -0
- package/src/tests/send-task-requester-gate.test.ts +58 -0
- package/src/tests/session-cost-recompute-unit.test.ts +46 -0
- package/src/tests/skill-get-name-resolution.test.ts +137 -0
- package/src/tests/skill-invocations.test.ts +4 -2
- package/src/tests/slack-blocks.test.ts +36 -0
- package/src/tests/slack-inline-output.test.ts +14 -0
- package/src/tests/slack-reaction-config.test.ts +20 -0
- package/src/tests/slack-render-v2.test.ts +37 -0
- package/src/tests/slack-thread-buffer.test.ts +98 -1
- package/src/tests/sonnet-5-5-registry.test.ts +9 -1
- package/src/tests/swarm-config-export-to-env.test.ts +267 -0
- package/src/tests/swarm-config-reserved-keys.test.ts +16 -0
- package/src/tests/swarm-diff.test.ts +1 -1
- package/src/tests/task-model-effort-rerun.test.ts +164 -0
- package/src/tests/task-session-prompts.test.ts +132 -0
- package/src/tests/task-tool-manifest.test.ts +14 -0
- package/src/tests/task-tools-ctx.test.ts +2 -0
- package/src/tests/tool-annotations.test.ts +6 -6
- package/src/tests/ui-logs-parser.test.ts +43 -0
- package/src/tests/used-by-route.test.ts +6 -0
- package/src/tests/vercel-ai-gateway.test.ts +149 -0
- package/src/tests/workflow-llm.test.ts +61 -0
- package/src/tests/workflow-swarm-script.test.ts +38 -0
- package/src/tests/workflow-wait-filter.test.ts +5 -3
- package/src/tools/chat-access.ts +41 -0
- package/src/tools/chat-open-dm.ts +59 -0
- package/src/tools/chat-post.ts +74 -0
- package/src/tools/chat-read-thread.ts +100 -0
- package/src/tools/chat-reply.ts +75 -0
- package/src/tools/create-page.ts +3 -13
- package/src/tools/model-catalog/index.ts +2 -1
- package/src/tools/model-profiles/list-model-profiles.ts +54 -0
- package/src/tools/register-chat-channel.ts +139 -0
- package/src/tools/request-human-input.ts +11 -3
- package/src/tools/script-apis.ts +5 -1
- package/src/tools/script-upsert.ts +4 -1
- package/src/tools/send-task.ts +48 -1
- package/src/tools/skills/skill-get.ts +17 -9
- package/src/tools/swarm-config/get-config.ts +2 -1
- package/src/tools/swarm-config/list-config.ts +2 -1
- package/src/tools/swarm-config/set-config.ts +16 -3
- package/src/tools/system-one-decide.ts +104 -0
- package/src/tools/task-action.ts +22 -1
- package/src/tools/tool-config.ts +14 -6
- package/src/types.ts +48 -3
- package/src/utils/config-env-export.ts +74 -0
- package/src/utils/credentials.test.ts +92 -0
- package/src/utils/credentials.ts +52 -4
- package/src/utils/harness-provider.ts +15 -6
- package/src/utils/runtime-model-catalog.ts +11 -1
- package/src/utils/secret-scrubber.ts +86 -10
- package/src/utils/task-tool-manifest.ts +19 -2
- package/src/utils/vcs-repo-match.ts +29 -0
- package/src/workflows/executors/raw-llm.ts +8 -3
- package/src/workflows/executors/swarm-script.ts +1 -0
- package/src/workflows/executors/system-one-decision.ts +21 -11
- package/src/workflows/executors/workflow-llm.ts +22 -0
- package/src/workflows/index.ts +21 -12
- package/templates/extensions/README.md +1 -0
- package/templates/extensions/comms-adherence-gate/README.md +88 -0
- package/templates/extensions/comms-adherence-gate/hooks.ts +489 -0
- package/templates/extensions/comms-adherence-gate/manifest.json +8 -0
- package/templates/skills/agent-browser/SKILL.md +1 -1
- package/templates/skills/agent-browser/content.md +1 -1
- package/templates/skills/apps/SKILL.md +8 -1
- package/templates/skills/apps/content.md +8 -1
- package/templates/skills/chat-sdk/config.json +14 -0
- package/templates/skills/chat-sdk/content.md +84 -0
- package/templates/skills/composio-gmail/content.md +7 -0
- package/templates/skills/composio-google-docs/content.md +1 -1
- package/templates/skills/email-domain-health/config.json +14 -0
- package/templates/skills/email-domain-health/content.md +131 -0
- package/templates/skills/kv-storage/content.md +2 -2
- package/templates/skills/pages/content.md +8 -10
- package/templates/skills/swarm-extensions/content.md +2 -0
- package/templates/skills/swarm-scripts/SKILL.md +4 -2
- package/templates/skills/swarm-scripts/config.json +1 -1
- package/templates/skills/swarm-scripts/content.md +4 -2
- package/templates/skills/workflow-iterate/content.md +38 -0
- package/dist/artifact-eczsknc3.js +0 -3843
- package/dist/cli-42qhyvwx.js +0 -33
- package/dist/cli-bd957dgb.js +0 -153
- package/dist/cli-q51yprfq.js +0 -1104
- package/dist/cli-t1ze64h3.js +0 -811
- package/dist/cli-tcc4wd5d.js +0 -56471
- package/dist/index-9ch1n761.js +0 -1102
- package/src/artifact-sdk/index.ts +0 -2
- package/src/artifact-sdk/localtunnel.d.ts +0 -20
- package/src/artifact-sdk/port.ts +0 -12
- package/src/artifact-sdk/server.ts +0 -183
- package/src/artifact-sdk/tunnel.ts +0 -19
- package/src/commands/artifact.ts +0 -249
- package/src/http/ecosystem.ts +0 -77
- package/src/tests/artifact-commands.test.ts +0 -92
- package/src/tests/artifact-sdk.test.ts +0 -793
- package/src/tests/register-service-security.test.ts +0 -194
- package/src/tools/list-services.ts +0 -107
- package/src/tools/register-service.ts +0 -177
- package/src/tools/unregister-service.ts +0 -73
- package/src/tools/update-service-status.ts +0 -96
- package/templates/skills/artifacts/config.json +0 -14
- package/templates/skills/artifacts/content.md +0 -344
- package/templates/skills/artifacts/files/examples/approval-flow.ts +0 -34
- package/templates/skills/artifacts/files/examples/hono-dashboard.ts +0 -31
- package/templates/skills/artifacts/files/examples/multi-artifact.ts +0 -20
- package/templates/skills/artifacts/files/examples/static-report.sh +0 -17
- /package/src/{artifact-sdk → pages}/browser-sdk.ts +0 -0
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { describe, expect, mock, test } from "bun:test";
|
|
2
|
+
import { OPENAI_COMPATIBLE_PROVIDERS } from "@desplega/model-catalog";
|
|
2
3
|
import {
|
|
3
4
|
buildCredStatusReport,
|
|
4
5
|
CREDENTIAL_PROVIDER_CHECKERS,
|
|
6
|
+
checkOpenAICompatibleProvider,
|
|
5
7
|
checkProviderCredentials,
|
|
6
8
|
isBedrockMode,
|
|
7
9
|
isCredCheckDisabled,
|
|
@@ -652,6 +654,37 @@ describe("checkOpencodeCredentials", () => {
|
|
|
652
654
|
).toBe(true);
|
|
653
655
|
});
|
|
654
656
|
|
|
657
|
+
test("strict: nan/* needs NAN_API_KEY or its HELMCODE_API_NAN alias", () => {
|
|
658
|
+
const env = { MODEL_OVERRIDE: "nan/glm5.3-flash", OPENROUTER_API_KEY: "x" };
|
|
659
|
+
const missing = checkOpencodeCredentials(env, { homeDir: HOME, fs: noFiles });
|
|
660
|
+
expect(missing.ready).toBe(false);
|
|
661
|
+
expect(missing.missing).toContain("NAN_API_KEY");
|
|
662
|
+
for (const name of ["NAN_API_KEY", "HELMCODE_API_NAN"]) {
|
|
663
|
+
expect(
|
|
664
|
+
checkOpencodeCredentials({ ...env, [name]: "x" }, { homeDir: HOME, fs: noFiles }).ready,
|
|
665
|
+
).toBe(true);
|
|
666
|
+
}
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
test("strict: helmcode/* needs HELMCODE_API_KEY, not the NaN alias", () => {
|
|
670
|
+
const env = { MODEL_OVERRIDE: "helmcode/glm5.3", HELMCODE_API_NAN: "x" };
|
|
671
|
+
const missing = checkOpencodeCredentials(env, { homeDir: HOME, fs: noFiles });
|
|
672
|
+
expect(missing.ready).toBe(false);
|
|
673
|
+
expect(missing.missing).toContain("HELMCODE_API_KEY");
|
|
674
|
+
expect(
|
|
675
|
+
checkOpencodeCredentials({ ...env, HELMCODE_API_KEY: "x" }, { homeDir: HOME, fs: noFiles })
|
|
676
|
+
.ready,
|
|
677
|
+
).toBe(true);
|
|
678
|
+
});
|
|
679
|
+
|
|
680
|
+
test("permissive: a NaN or Helmcode key alone is enough", () => {
|
|
681
|
+
for (const name of ["NAN_API_KEY", "HELMCODE_API_NAN", "HELMCODE_API_KEY"]) {
|
|
682
|
+
expect(checkOpencodeCredentials({ [name]: "x" }, { homeDir: HOME, fs: noFiles }).ready).toBe(
|
|
683
|
+
true,
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
});
|
|
687
|
+
|
|
655
688
|
test("not ready when nothing is set", () => {
|
|
656
689
|
const status = checkOpencodeCredentials({}, { homeDir: HOME, fs: noFiles });
|
|
657
690
|
expect(status.ready).toBe(false);
|
|
@@ -923,6 +956,112 @@ describe("REQUIRED_CRED_VARS_BY_PROVIDER", () => {
|
|
|
923
956
|
// acp is credential-free by design: the target process owns its own auth.
|
|
924
957
|
expect(REQUIRED_CRED_VARS_BY_PROVIDER.acp).toEqual([]);
|
|
925
958
|
});
|
|
959
|
+
|
|
960
|
+
test("pi and opencode list the NaN (both names) and Helmcode key vars", () => {
|
|
961
|
+
for (const p of ["pi", "opencode"] as const) {
|
|
962
|
+
expect(REQUIRED_CRED_VARS_BY_PROVIDER[p]).toEqual(
|
|
963
|
+
expect.arrayContaining(["NAN_API_KEY", "HELMCODE_API_NAN", "HELMCODE_API_KEY"]),
|
|
964
|
+
);
|
|
965
|
+
}
|
|
966
|
+
});
|
|
967
|
+
});
|
|
968
|
+
|
|
969
|
+
describe("NaN and Helmcode live probes", () => {
|
|
970
|
+
const envReady = { ready: true, missing: [], satisfiedBy: "env" as const };
|
|
971
|
+
|
|
972
|
+
function fakeProvider(status: number) {
|
|
973
|
+
const seen: Array<{ path: string; authorization: string | null }> = [];
|
|
974
|
+
const server = Bun.serve({
|
|
975
|
+
port: 0,
|
|
976
|
+
fetch(req) {
|
|
977
|
+
seen.push({
|
|
978
|
+
path: new URL(req.url).pathname,
|
|
979
|
+
authorization: req.headers.get("authorization"),
|
|
980
|
+
});
|
|
981
|
+
return new Response(status === 200 ? '{"data":[]}' : '{"error":"Invalid API key."}', {
|
|
982
|
+
status,
|
|
983
|
+
});
|
|
984
|
+
},
|
|
985
|
+
});
|
|
986
|
+
return { server, seen, baseUrl: `${server.url.toString().replace(/\/$/, "")}/v1` };
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
for (const [name, probe] of [
|
|
990
|
+
[
|
|
991
|
+
"nan",
|
|
992
|
+
(key: string, url: string) =>
|
|
993
|
+
checkOpenAICompatibleProvider(OPENAI_COMPATIBLE_PROVIDERS.nan, key, url),
|
|
994
|
+
],
|
|
995
|
+
[
|
|
996
|
+
"helmcode",
|
|
997
|
+
(key: string, url: string) =>
|
|
998
|
+
checkOpenAICompatibleProvider(OPENAI_COMPATIBLE_PROVIDERS.helmcode, key, url),
|
|
999
|
+
],
|
|
1000
|
+
] as const) {
|
|
1001
|
+
test(`${name} probe lists models with a Bearer key at the injected base URL`, async () => {
|
|
1002
|
+
const ok = fakeProvider(200);
|
|
1003
|
+
const bad = fakeProvider(401);
|
|
1004
|
+
try {
|
|
1005
|
+
expect(await probe("fixture-key", `${ok.baseUrl}/`)).toMatchObject({ ok: true });
|
|
1006
|
+
expect(ok.seen).toEqual([{ path: "/v1/models", authorization: "Bearer fixture-key" }]);
|
|
1007
|
+
const failure = await probe("fixture-key", bad.baseUrl);
|
|
1008
|
+
expect(failure.ok).toBe(false);
|
|
1009
|
+
expect(failure.error).toContain("HTTP 401");
|
|
1010
|
+
} finally {
|
|
1011
|
+
ok.server.stop(true);
|
|
1012
|
+
bad.server.stop(true);
|
|
1013
|
+
}
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
for (const provider of ["pi", "opencode"] as const) {
|
|
1018
|
+
for (const [env, url] of [
|
|
1019
|
+
[{ NAN_API_KEY: "fixture-nan" }, "https://api.nan.builders/v1/models"],
|
|
1020
|
+
[{ HELMCODE_API_NAN: "fixture-nan" }, "https://api.nan.builders/v1/models"],
|
|
1021
|
+
[{ HELMCODE_API_KEY: "fixture-helm" }, "https://api.helmcode.com/v1/models"],
|
|
1022
|
+
// `<ID>_BASE_URL` moves the probe with the provider.
|
|
1023
|
+
[
|
|
1024
|
+
{ NAN_API_KEY: "fixture-nan", NAN_BASE_URL: "https://nan-gw.example.test/v1/" },
|
|
1025
|
+
"https://nan-gw.example.test/v1/models",
|
|
1026
|
+
],
|
|
1027
|
+
] as const) {
|
|
1028
|
+
test(`${provider}: ${Object.keys(env).join("+")} is probed at ${url}`, async () => {
|
|
1029
|
+
const realFetch = globalThis.fetch;
|
|
1030
|
+
const fetchMock = mock(async (_url: string | URL | Request, _init?: RequestInit) => {
|
|
1031
|
+
return new Response("{}", { status: 200 });
|
|
1032
|
+
});
|
|
1033
|
+
globalThis.fetch = fetchMock as typeof fetch;
|
|
1034
|
+
try {
|
|
1035
|
+
const result = await validateProviderCredentials(provider, { ...env }, envReady);
|
|
1036
|
+
expect(result.ok).toBe(true);
|
|
1037
|
+
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
1038
|
+
expect(String(fetchMock.mock.calls[0]?.[0])).toBe(url);
|
|
1039
|
+
const headers = fetchMock.mock.calls[0]?.[1]?.headers as Record<string, string>;
|
|
1040
|
+
expect(headers.Authorization).toBe(`Bearer ${Object.values(env)[0]}`);
|
|
1041
|
+
} finally {
|
|
1042
|
+
globalThis.fetch = realFetch;
|
|
1043
|
+
}
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
test(`${provider}: an OpenRouter key keeps the probe ahead of a NaN key`, async () => {
|
|
1048
|
+
const realFetch = globalThis.fetch;
|
|
1049
|
+
const fetchMock = mock(async (_url: string | URL | Request, _init?: RequestInit) => {
|
|
1050
|
+
return new Response("{}", { status: 200 });
|
|
1051
|
+
});
|
|
1052
|
+
globalThis.fetch = fetchMock as typeof fetch;
|
|
1053
|
+
try {
|
|
1054
|
+
await validateProviderCredentials(
|
|
1055
|
+
provider,
|
|
1056
|
+
{ OPENROUTER_API_KEY: "fixture-or", NAN_API_KEY: "fixture-nan" },
|
|
1057
|
+
envReady,
|
|
1058
|
+
);
|
|
1059
|
+
expect(String(fetchMock.mock.calls[0]?.[0])).not.toContain("nan.builders");
|
|
1060
|
+
} finally {
|
|
1061
|
+
globalThis.fetch = realFetch;
|
|
1062
|
+
}
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
926
1065
|
});
|
|
927
1066
|
|
|
928
1067
|
// ─── Migration 055: report composition + opt-out ────────────────────────────
|
|
@@ -84,4 +84,25 @@ describe("validateOpencodeCredentials", () => {
|
|
|
84
84
|
validateOpencodeCredentials({ OPENROUTER_API_KEY: "sk-or", ANTHROPIC_API_KEY: "sk-ant" }),
|
|
85
85
|
).toBe("openrouter_api_key");
|
|
86
86
|
});
|
|
87
|
+
|
|
88
|
+
test("accepts a NaN key under either name", () => {
|
|
89
|
+
expect(validateOpencodeCredentials({ NAN_API_KEY: "example-nan" })).toBe("nan_api_key");
|
|
90
|
+
expect(validateOpencodeCredentials({ HELMCODE_API_NAN: "example-nan" })).toBe("nan_api_key");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("accepts a Helmcode key", () => {
|
|
94
|
+
expect(validateOpencodeCredentials({ HELMCODE_API_KEY: "example-helm" })).toBe(
|
|
95
|
+
"helmcode_api_key",
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("keeps OpenRouter ahead of NaN and Helmcode", () => {
|
|
100
|
+
expect(
|
|
101
|
+
validateOpencodeCredentials({
|
|
102
|
+
OPENROUTER_API_KEY: "sk-or",
|
|
103
|
+
NAN_API_KEY: "example-nan",
|
|
104
|
+
HELMCODE_API_KEY: "example-helm",
|
|
105
|
+
}),
|
|
106
|
+
).toBe("openrouter_api_key");
|
|
107
|
+
});
|
|
87
108
|
});
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
1
2
|
import { afterEach, describe, expect, test } from "bun:test";
|
|
3
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
2
6
|
import * as facade from "../be/db";
|
|
3
7
|
import * as runtime from "../be/db/runtime";
|
|
8
|
+
import { CHILD_PROCESS_TEST_BUDGET_MS, expectChildOk, runChild } from "./test-proc";
|
|
4
9
|
|
|
5
10
|
const runtimeExports = [
|
|
6
11
|
"__resetSqliteVecExtensionPathCacheForTests",
|
|
@@ -67,4 +72,60 @@ describe("database runtime facade", () => {
|
|
|
67
72
|
globals.__testMigrationTemplate = template;
|
|
68
73
|
}
|
|
69
74
|
});
|
|
75
|
+
|
|
76
|
+
// rbac-admission-e2e restarted its server on an existing DB and the server
|
|
77
|
+
// died with "Encryption key not resolved yet": boot hit SQLITE_BUSY before
|
|
78
|
+
// resolving the key, and the db-client's BUSY retry reused the half-booted
|
|
79
|
+
// handle instead of booting again. Child processes boot cold (no test
|
|
80
|
+
// template) and leave no in-process connection blocking the lock below.
|
|
81
|
+
test(
|
|
82
|
+
"a cold boot that hits SQLITE_BUSY boots again before serving queries",
|
|
83
|
+
async () => {
|
|
84
|
+
const dir = mkdtempSync(join(tmpdir(), "db-runtime-busy-"));
|
|
85
|
+
const dbPath = join(dir, "boot.sqlite");
|
|
86
|
+
const child = (source: string) =>
|
|
87
|
+
runChild([process.execPath, "--eval", source], {
|
|
88
|
+
cwd: new URL("../../", import.meta.url).pathname,
|
|
89
|
+
env: { ...process.env, DATABASE_PATH: dbPath, AGENT_SWARM_TELEMETRY_DISABLED: "1" },
|
|
90
|
+
});
|
|
91
|
+
try {
|
|
92
|
+
expectChildOk(
|
|
93
|
+
await child(`
|
|
94
|
+
const db = await import("./src/be/db");
|
|
95
|
+
db.initDb(process.env.DATABASE_PATH);
|
|
96
|
+
await db.upsertSwarmConfig({ scope: "global", key: "BOOT_BUSY_SECRET", value: "boot-busy-value", isSecret: true });
|
|
97
|
+
db.closeDb();
|
|
98
|
+
`),
|
|
99
|
+
"seed",
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
const holder = new Database(dbPath);
|
|
103
|
+
holder.run("PRAGMA locking_mode = EXCLUSIVE");
|
|
104
|
+
holder.run("BEGIN EXCLUSIVE");
|
|
105
|
+
let released = false;
|
|
106
|
+
const releaseLock = () => {
|
|
107
|
+
if (released) return;
|
|
108
|
+
released = true;
|
|
109
|
+
holder.run("COMMIT");
|
|
110
|
+
holder.close();
|
|
111
|
+
};
|
|
112
|
+
const release = setTimeout(releaseLock, 500);
|
|
113
|
+
try {
|
|
114
|
+
const read = await child(`
|
|
115
|
+
const db = await import("./src/be/db");
|
|
116
|
+
const configs = await db.getInjectableGlobalConfigs();
|
|
117
|
+
console.log("VALUE=" + configs.find((c) => c.key === "BOOT_BUSY_SECRET")?.value);
|
|
118
|
+
`);
|
|
119
|
+
expect(read.stderr).not.toContain("Encryption key not resolved yet");
|
|
120
|
+
expect(read.stdout).toContain("VALUE=boot-busy-value");
|
|
121
|
+
} finally {
|
|
122
|
+
clearTimeout(release);
|
|
123
|
+
releaseLock();
|
|
124
|
+
}
|
|
125
|
+
} finally {
|
|
126
|
+
rmSync(dir, { recursive: true, force: true });
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
CHILD_PROCESS_TEST_BUDGET_MS * 2,
|
|
130
|
+
);
|
|
70
131
|
});
|
|
@@ -72,6 +72,7 @@ type:McpServerFilters
|
|
|
72
72
|
type:McpServerInsert
|
|
73
73
|
type:MentionPreview
|
|
74
74
|
type:PageListOptions
|
|
75
|
+
type:PendingChatRelay
|
|
75
76
|
type:PlanSource
|
|
76
77
|
type:RecordBudgetRefusalNotificationInput
|
|
77
78
|
type:ScheduledTaskFilters
|
|
@@ -109,6 +110,7 @@ value:NOT_EXTENSION_AGENT_SQL
|
|
|
109
110
|
value:ReservedAgentRoleError
|
|
110
111
|
value:SESSION_ACTIVITY_REFRESH_MIN_INTERVAL_MS
|
|
111
112
|
value:SKILL_FILE_LIMITS
|
|
113
|
+
value:SUPERSEDED_RESUME_REPAIR_WINDOW_MS
|
|
112
114
|
value:UNATTRIBUTED_USER_ID
|
|
113
115
|
value:__resetSqliteVecExtensionPathCacheForTests
|
|
114
116
|
value:abandonSlackOutcomeDelivery
|
|
@@ -237,6 +239,7 @@ value:getAgentMcpServers
|
|
|
237
239
|
value:getAgentSkills
|
|
238
240
|
value:getAgentStatusCounts
|
|
239
241
|
value:getAgentWithTasks
|
|
242
|
+
value:getAgentWorkingOnContextKey
|
|
240
243
|
value:getAgentWorkingOnThread
|
|
241
244
|
value:getAllAgentMailInboxMappings
|
|
242
245
|
value:getAllAgents
|
|
@@ -298,8 +301,10 @@ value:getKv
|
|
|
298
301
|
value:getLastReadAt
|
|
299
302
|
value:getLastRunStart
|
|
300
303
|
value:getLastSuccessfulRun
|
|
304
|
+
value:getLatestActiveTaskByContextKey
|
|
301
305
|
value:getLatestActiveTaskInThread
|
|
302
306
|
value:getLatestContextVersion
|
|
307
|
+
value:getLatestLeadTaskByContextKey
|
|
303
308
|
value:getLatestLeadTaskInThread
|
|
304
309
|
value:getLatestPageBySlug
|
|
305
310
|
value:getLatestScriptRunStepTaskByContextKey
|
|
@@ -329,6 +334,7 @@ value:getPageVersion
|
|
|
329
334
|
value:getPageVersions
|
|
330
335
|
value:getPausedTasksForAgent
|
|
331
336
|
value:getPendingApprovalVoteState
|
|
337
|
+
value:getPendingChatRelayTasks
|
|
332
338
|
value:getPendingEventWaitNames
|
|
333
339
|
value:getPendingSlackRelayTasks
|
|
334
340
|
value:getPendingSteeringForAgent
|
|
@@ -426,6 +432,7 @@ value:getWorkflowRunStepsByRunId
|
|
|
426
432
|
value:getWorkflowVersion
|
|
427
433
|
value:getWorkflowVersions
|
|
428
434
|
value:getWorkflowsByScheduleId
|
|
435
|
+
value:giveUpStaleChatRelays
|
|
429
436
|
value:hasBudgetRefusalNotificationToday
|
|
430
437
|
value:hasCapacity
|
|
431
438
|
value:hasFirstCompletedTask
|
|
@@ -477,6 +484,9 @@ value:listUserFavorites
|
|
|
477
484
|
value:listWorkflowRuns
|
|
478
485
|
value:listWorkflowRunsPage
|
|
479
486
|
value:listWorkflows
|
|
487
|
+
value:markChatRelayAttempted
|
|
488
|
+
value:markChatRelayDelivered
|
|
489
|
+
value:markChatRelayGivenUp
|
|
480
490
|
value:markFinalizedSlackRelaysDelivered
|
|
481
491
|
value:markInboxMessageDelegated
|
|
482
492
|
value:markInboxMessageRead
|
|
@@ -505,6 +515,7 @@ value:promoteDraftTask
|
|
|
505
515
|
value:reassociateSessionLogs
|
|
506
516
|
value:recordApprovalVotes
|
|
507
517
|
value:recordBudgetRefusalNotification
|
|
518
|
+
value:recordChatRelayError
|
|
508
519
|
value:recordInlineScriptRun
|
|
509
520
|
value:recordKeyAuthFailure
|
|
510
521
|
value:recordKeyRateLimitWindows
|
|
@@ -598,24 +609,30 @@ value:upsertSwarmConfig
|
|
|
598
609
|
value:withFavoriteFlags`.split("\n");
|
|
599
610
|
const selected = `type:ExistingTrackerContextWork
|
|
600
611
|
type:ExistingTrackerContextWorkReason
|
|
612
|
+
type:PendingChatRelay
|
|
601
613
|
type:TaskFilters
|
|
614
|
+
value:SUPERSEDED_RESUME_REPAIR_WINDOW_MS
|
|
602
615
|
value:findCompletedTaskInThread
|
|
603
616
|
value:findExistingLinearTrackerContextWork
|
|
604
617
|
value:findRecentCancelledTaskInThread
|
|
605
618
|
value:findTaskByGitHub
|
|
606
619
|
value:findTaskByVcs
|
|
607
620
|
value:getAgentCurrentTask
|
|
621
|
+
value:getAgentWorkingOnContextKey
|
|
608
622
|
value:getAgentWorkingOnThread
|
|
609
623
|
value:getAllTasks
|
|
610
624
|
value:getChildTasks
|
|
611
625
|
value:getCompletedSlackTasks
|
|
612
626
|
value:getInProgressSlackTasks
|
|
613
627
|
value:getInProgressTasksByContextKey
|
|
628
|
+
value:getLatestActiveTaskByContextKey
|
|
614
629
|
value:getLatestActiveTaskInThread
|
|
630
|
+
value:getLatestLeadTaskByContextKey
|
|
615
631
|
value:getLatestLeadTaskInThread
|
|
616
632
|
value:getLatestScriptRunStepTaskByContextKey
|
|
617
633
|
value:getLatestTaskByContextKey
|
|
618
634
|
value:getMostRecentTaskInThread
|
|
635
|
+
value:getPendingChatRelayTasks
|
|
619
636
|
value:getPendingSlackRelayTasks
|
|
620
637
|
value:getRecentlyFinishedWorkerTasks
|
|
621
638
|
value:getSlackTasksMissingTree
|
|
@@ -626,13 +643,18 @@ value:getTaskStats
|
|
|
626
643
|
value:getTasksByAgentId
|
|
627
644
|
value:getTasksByStatus
|
|
628
645
|
value:getTasksCount
|
|
646
|
+
value:giveUpStaleChatRelays
|
|
629
647
|
value:hasNonTerminalRerouteDecisionChild
|
|
630
648
|
value:hasNonTerminalResumeChild
|
|
631
649
|
value:isLinearTrackerContextKey
|
|
650
|
+
value:markChatRelayAttempted
|
|
651
|
+
value:markChatRelayDelivered
|
|
652
|
+
value:markChatRelayGivenUp
|
|
632
653
|
value:markFinalizedSlackRelaysDelivered
|
|
633
654
|
value:markSlackRelayAttempted
|
|
634
655
|
value:markSlackRelayDelivered
|
|
635
656
|
value:markTasksNotified
|
|
657
|
+
value:recordChatRelayError
|
|
636
658
|
value:resetTasksNotified`.split("\n");
|
|
637
659
|
const internal = [
|
|
638
660
|
"type:AgentTaskRow",
|
|
@@ -95,6 +95,7 @@ type:McpServerFilters
|
|
|
95
95
|
type:McpServerInsert
|
|
96
96
|
type:MentionPreview
|
|
97
97
|
type:PageListOptions
|
|
98
|
+
type:PendingChatRelay
|
|
98
99
|
type:PlanSource
|
|
99
100
|
type:RecordBudgetRefusalNotificationInput
|
|
100
101
|
type:ScheduledTaskFilters
|
|
@@ -132,6 +133,7 @@ value:NOT_EXTENSION_AGENT_SQL
|
|
|
132
133
|
value:ReservedAgentRoleError
|
|
133
134
|
value:SESSION_ACTIVITY_REFRESH_MIN_INTERVAL_MS
|
|
134
135
|
value:SKILL_FILE_LIMITS
|
|
136
|
+
value:SUPERSEDED_RESUME_REPAIR_WINDOW_MS
|
|
135
137
|
value:UNATTRIBUTED_USER_ID
|
|
136
138
|
value:__resetSqliteVecExtensionPathCacheForTests
|
|
137
139
|
value:abandonSlackOutcomeDelivery
|
|
@@ -260,6 +262,7 @@ value:getAgentMcpServers
|
|
|
260
262
|
value:getAgentSkills
|
|
261
263
|
value:getAgentStatusCounts
|
|
262
264
|
value:getAgentWithTasks
|
|
265
|
+
value:getAgentWorkingOnContextKey
|
|
263
266
|
value:getAgentWorkingOnThread
|
|
264
267
|
value:getAllAgentMailInboxMappings
|
|
265
268
|
value:getAllAgents
|
|
@@ -321,8 +324,10 @@ value:getKv
|
|
|
321
324
|
value:getLastReadAt
|
|
322
325
|
value:getLastRunStart
|
|
323
326
|
value:getLastSuccessfulRun
|
|
327
|
+
value:getLatestActiveTaskByContextKey
|
|
324
328
|
value:getLatestActiveTaskInThread
|
|
325
329
|
value:getLatestContextVersion
|
|
330
|
+
value:getLatestLeadTaskByContextKey
|
|
326
331
|
value:getLatestLeadTaskInThread
|
|
327
332
|
value:getLatestPageBySlug
|
|
328
333
|
value:getLatestScriptRunStepTaskByContextKey
|
|
@@ -352,6 +357,7 @@ value:getPageVersion
|
|
|
352
357
|
value:getPageVersions
|
|
353
358
|
value:getPausedTasksForAgent
|
|
354
359
|
value:getPendingApprovalVoteState
|
|
360
|
+
value:getPendingChatRelayTasks
|
|
355
361
|
value:getPendingEventWaitNames
|
|
356
362
|
value:getPendingSlackRelayTasks
|
|
357
363
|
value:getPendingSteeringForAgent
|
|
@@ -449,6 +455,7 @@ value:getWorkflowRunStepsByRunId
|
|
|
449
455
|
value:getWorkflowVersion
|
|
450
456
|
value:getWorkflowVersions
|
|
451
457
|
value:getWorkflowsByScheduleId
|
|
458
|
+
value:giveUpStaleChatRelays
|
|
452
459
|
value:hasBudgetRefusalNotificationToday
|
|
453
460
|
value:hasCapacity
|
|
454
461
|
value:hasFirstCompletedTask
|
|
@@ -500,6 +507,9 @@ value:listUserFavorites
|
|
|
500
507
|
value:listWorkflowRuns
|
|
501
508
|
value:listWorkflowRunsPage
|
|
502
509
|
value:listWorkflows
|
|
510
|
+
value:markChatRelayAttempted
|
|
511
|
+
value:markChatRelayDelivered
|
|
512
|
+
value:markChatRelayGivenUp
|
|
503
513
|
value:markFinalizedSlackRelaysDelivered
|
|
504
514
|
value:markInboxMessageDelegated
|
|
505
515
|
value:markInboxMessageRead
|
|
@@ -528,6 +538,7 @@ value:promoteDraftTask
|
|
|
528
538
|
value:reassociateSessionLogs
|
|
529
539
|
value:recordApprovalVotes
|
|
530
540
|
value:recordBudgetRefusalNotification
|
|
541
|
+
value:recordChatRelayError
|
|
531
542
|
value:recordInlineScriptRun
|
|
532
543
|
value:recordKeyAuthFailure
|
|
533
544
|
value:recordKeyRateLimitWindows
|
|
@@ -21,6 +21,10 @@ import {
|
|
|
21
21
|
supersedeTask,
|
|
22
22
|
updateScheduledTask,
|
|
23
23
|
} from "../be/db";
|
|
24
|
+
import { installExtension, setExtensionState } from "../be/extensions/db";
|
|
25
|
+
import { listRegistered, registerLoaded, unregister } from "../extensions/dispatcher";
|
|
26
|
+
import { ensureExtensionAgent } from "../extensions/identity";
|
|
27
|
+
import { loadExtension } from "../extensions/loader";
|
|
24
28
|
import {
|
|
25
29
|
dispatchDeferredTaskWait,
|
|
26
30
|
initDeferredTaskWaits,
|
|
@@ -32,6 +36,7 @@ import { dispatchScheduleTarget, executeSchedule } from "../scheduler/scheduler"
|
|
|
32
36
|
import { createResumeFollowUp } from "../tasks/worker-follow-up";
|
|
33
37
|
import { registerDeferTaskTool } from "../tools/defer-task";
|
|
34
38
|
import type { AgentTask } from "../types";
|
|
39
|
+
import { loadBundleFixture } from "./fixtures/extensions/load";
|
|
35
40
|
|
|
36
41
|
type Result = { structuredContent: { success: boolean; message: string; scheduleId?: string } };
|
|
37
42
|
type Tool = {
|
|
@@ -69,7 +74,13 @@ beforeAll(async () => {
|
|
|
69
74
|
"defer-task"
|
|
70
75
|
]!;
|
|
71
76
|
});
|
|
72
|
-
afterEach(() =>
|
|
77
|
+
afterEach(async () => {
|
|
78
|
+
stopDeferredTaskWaits();
|
|
79
|
+
for (const loaded of listRegistered()) {
|
|
80
|
+
unregister(loaded.record.id);
|
|
81
|
+
await loaded.dispose();
|
|
82
|
+
}
|
|
83
|
+
});
|
|
73
84
|
afterAll(async () => {
|
|
74
85
|
closeDb();
|
|
75
86
|
templateGlobals.__testMigrationTemplate = savedTemplate;
|
|
@@ -172,6 +183,72 @@ async function multiFixture(mode?: "all" | "any", event: TaskWakeEvent = "settle
|
|
|
172
183
|
}
|
|
173
184
|
|
|
174
185
|
describe("defer-task wakeOn", () => {
|
|
186
|
+
async function ceilingHook(action: string) {
|
|
187
|
+
const bundle = await loadBundleFixture("minimal");
|
|
188
|
+
bundle.manifest = { ...bundle.manifest, name: `ceiling-${crypto.randomUUID()}` };
|
|
189
|
+
bundle.files["hooks.ts"] = `
|
|
190
|
+
import { block, modify } from "swarm-extension";
|
|
191
|
+
export default (api) => {
|
|
192
|
+
api.on("pre.task.create", () => ${action});
|
|
193
|
+
};
|
|
194
|
+
`;
|
|
195
|
+
const installed = await installExtension(bundle);
|
|
196
|
+
const record = await setExtensionState(installed.extension.id, {
|
|
197
|
+
agentId: await ensureExtensionAgent(installed.extension.name),
|
|
198
|
+
});
|
|
199
|
+
const loaded = await loadExtension({ record: record!, ...bundle });
|
|
200
|
+
registerLoaded(loaded);
|
|
201
|
+
return loaded;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
test("ceiling honours pre.task.create blocks and leaves the wait pending", async () => {
|
|
205
|
+
const { schedule } = await fixture();
|
|
206
|
+
await ceilingHook('block("ceiling denied")');
|
|
207
|
+
await executeSchedule(schedule);
|
|
208
|
+
expect(await children(schedule.id)).toHaveLength(0);
|
|
209
|
+
expect(await waitState(schedule.id)).toMatchObject({ status: "pending", childTaskId: null });
|
|
210
|
+
expect(await getScheduledTaskById(schedule.id)).toMatchObject({
|
|
211
|
+
consecutiveErrors: 1,
|
|
212
|
+
lastErrorMessage: expect.stringContaining("ceiling denied"),
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
test("ceiling applies pre.task.create payload modifications", async () => {
|
|
217
|
+
const { schedule } = await fixture();
|
|
218
|
+
await ceilingHook('modify({ description: "modified ceiling wake" })');
|
|
219
|
+
await executeSchedule(schedule);
|
|
220
|
+
const found = await children(schedule.id);
|
|
221
|
+
expect(found).toHaveLength(1);
|
|
222
|
+
expect(found[0]!.task).toBe("modified ceiling wake");
|
|
223
|
+
expect(await waitState(schedule.id)).toMatchObject({ status: "fired", firedBy: "ceiling" });
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
test("a ceiling moved during pre-hook preparation cannot fire the stale occurrence", async () => {
|
|
227
|
+
const { schedule } = await fixture();
|
|
228
|
+
const nextRunAt = new Date(Date.now() + 120_000).toISOString();
|
|
229
|
+
const loaded = await ceilingHook('modify({ description: "stale preparation" })');
|
|
230
|
+
const handler = loaded.handlers[0]!;
|
|
231
|
+
const original = handler.handler;
|
|
232
|
+
handler.handler = async (event, ctx) => {
|
|
233
|
+
await updateScheduledTask(schedule.id, { nextRunAt });
|
|
234
|
+
return original(event, ctx);
|
|
235
|
+
};
|
|
236
|
+
await executeSchedule(schedule);
|
|
237
|
+
expect(await children(schedule.id)).toHaveLength(0);
|
|
238
|
+
expect(await waitState(schedule.id)).toMatchObject({ status: "pending" });
|
|
239
|
+
expect(await getScheduledTaskById(schedule.id)).toMatchObject({ enabled: true, nextRunAt });
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
test("a stale ceiling snapshot cannot fire after re-defer moves nextRunAt", async () => {
|
|
243
|
+
const { schedule } = await fixture();
|
|
244
|
+
const nextRunAt = new Date(Date.now() + 120_000).toISOString();
|
|
245
|
+
await updateScheduledTask(schedule.id, { nextRunAt });
|
|
246
|
+
await executeSchedule(schedule);
|
|
247
|
+
expect(await children(schedule.id)).toHaveLength(0);
|
|
248
|
+
expect(await waitState(schedule.id)).toMatchObject({ status: "pending" });
|
|
249
|
+
expect(await getScheduledTaskById(schedule.id)).toMatchObject({ enabled: true, nextRunAt });
|
|
250
|
+
});
|
|
251
|
+
|
|
175
252
|
test.each([
|
|
176
253
|
"completed",
|
|
177
254
|
"failed",
|
|
@@ -78,7 +78,7 @@ describe("docs API index footprint", () => {
|
|
|
78
78
|
expect(tasks).toContain('"path":"/api/task-templates","method":"get"');
|
|
79
79
|
});
|
|
80
80
|
|
|
81
|
-
test("OpenAPI and generated pages keep all
|
|
81
|
+
test("OpenAPI and generated pages keep all 425 operations", async () => {
|
|
82
82
|
const spec = await Bun.file(resolve(repoRoot, "openapi.json")).json();
|
|
83
83
|
const operationKeys = [];
|
|
84
84
|
const tags = new Set();
|
|
@@ -103,8 +103,8 @@ describe("docs API index footprint", () => {
|
|
|
103
103
|
expect(spec.paths["/api/agents/{id}/runtime"]?.get).toBeDefined();
|
|
104
104
|
|
|
105
105
|
const generatedKeys = await generatedOperationKeys();
|
|
106
|
-
expect(operationKeys).toHaveLength(
|
|
107
|
-
expect(generatedKeys).toHaveLength(
|
|
106
|
+
expect(operationKeys).toHaveLength(430);
|
|
107
|
+
expect(generatedKeys).toHaveLength(430);
|
|
108
108
|
expect(new Set(generatedKeys)).toEqual(new Set(operationKeys));
|
|
109
109
|
});
|
|
110
110
|
|
|
@@ -376,7 +376,7 @@ describe("dsh harness", () => {
|
|
|
376
376
|
await rename(join(config.cwd, "dsh"), join(config.cwd, "npx"));
|
|
377
377
|
config.env = { ...config.env, DSH_BINARY: "", PATH: config.cwd };
|
|
378
378
|
await expect(new DshAdapter().createSession(config)).rejects.toThrow(
|
|
379
|
-
"dsh CLI not found. Install @deepseek-ai/dsh@0.2.1-alpha.
|
|
379
|
+
"dsh CLI not found. Install @deepseek-ai/dsh@0.2.1-alpha.5",
|
|
380
380
|
);
|
|
381
381
|
expect(await Bun.file(join(config.cwd, "invocation.json")).exists()).toBe(false);
|
|
382
382
|
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { afterEach, expect, test } from "bun:test";
|
|
2
|
+
import { ALL_CAPABILITIES, getEnabledCapabilities, hasCapability } from "../server";
|
|
3
|
+
|
|
4
|
+
const originalCapabilities = process.env.CAPABILITIES;
|
|
5
|
+
|
|
6
|
+
afterEach(() => {
|
|
7
|
+
if (originalCapabilities === undefined) delete process.env.CAPABILITIES;
|
|
8
|
+
else process.env.CAPABILITIES = originalCapabilities;
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
// `services` was a real capability until the PM2 service registry was removed.
|
|
12
|
+
// A deployment that still lists it must keep registering agents: the register
|
|
13
|
+
// response validates enabledCapabilities against ALL_CAPABILITIES.
|
|
14
|
+
test("enabledCapabilities drops retired and free-form names", () => {
|
|
15
|
+
process.env.CAPABILITIES = "core,services,artifacts,pages";
|
|
16
|
+
|
|
17
|
+
expect(ALL_CAPABILITIES as readonly string[]).not.toContain("services");
|
|
18
|
+
expect(getEnabledCapabilities()).toEqual(["core", "pages"]);
|
|
19
|
+
expect(hasCapability("pages")).toBe(true);
|
|
20
|
+
});
|
|
@@ -221,28 +221,34 @@ describe("wait_for_api_ready: immediate success", () => {
|
|
|
221
221
|
|
|
222
222
|
describe("wait_for_api_ready: transient failure then success", () => {
|
|
223
223
|
test("retries roughly once per second until the server recovers", async () => {
|
|
224
|
-
|
|
224
|
+
// A per-test path prefix: an ephemeral port can be reused from another test
|
|
225
|
+
// process whose poller still hits /health, and those stray requests used
|
|
226
|
+
// to count as retries (3 requests in 1033 ms on CI).
|
|
227
|
+
const prefix = `/${crypto.randomUUID()}`;
|
|
228
|
+
const requestTimes: number[] = [];
|
|
225
229
|
server = Bun.serve({
|
|
226
230
|
port: 0,
|
|
227
231
|
hostname: "127.0.0.1",
|
|
228
232
|
fetch(req) {
|
|
229
233
|
const url = new URL(req.url);
|
|
230
|
-
if (url.pathname !==
|
|
231
|
-
|
|
232
|
-
if (
|
|
234
|
+
if (url.pathname !== `${prefix}/health`) return new Response("not found", { status: 404 });
|
|
235
|
+
requestTimes.push(performance.now());
|
|
236
|
+
if (requestTimes.length < 3) return new Response("unavailable", { status: 503 });
|
|
233
237
|
return new Response("ok", { status: 200 });
|
|
234
238
|
},
|
|
235
239
|
});
|
|
236
240
|
|
|
237
|
-
const result = await runWaitForApiReady(server.url.
|
|
241
|
+
const result = await runWaitForApiReady(`${server.url.origin}${prefix}/`, {
|
|
238
242
|
WORKER_API_READY_TIMEOUT_SECONDS: "10",
|
|
239
243
|
});
|
|
240
244
|
|
|
241
245
|
expect(result.exitCode).toBe(0);
|
|
242
|
-
expect(
|
|
246
|
+
expect(requestTimes).toHaveLength(3);
|
|
243
247
|
expect(result.stdout).toContain("API ready at");
|
|
244
|
-
//
|
|
245
|
-
|
|
248
|
+
// Each retry waits out `sleep 1` after the previous response, so the
|
|
249
|
+
// server-side gap cannot shrink under load: paced, not a hot loop.
|
|
250
|
+
expect(requestTimes[1]! - requestTimes[0]!).toBeGreaterThanOrEqual(900);
|
|
251
|
+
expect(requestTimes[2]! - requestTimes[1]!).toBeGreaterThanOrEqual(900);
|
|
246
252
|
expect(result.durationMs).toBeLessThan(15000);
|
|
247
253
|
}, 20000);
|
|
248
254
|
});
|
|
@@ -93,7 +93,10 @@ function extractExportFilter(lineMarker: string): string {
|
|
|
93
93
|
const line = script.split("\n").find((l) => l.includes(lineMarker) && l.includes("jq -r '"));
|
|
94
94
|
if (!line) throw new Error(`Could not locate the jq line containing ${lineMarker}`);
|
|
95
95
|
const start = line.indexOf("jq -r '") + "jq -r '".length;
|
|
96
|
-
|
|
96
|
+
// Inline programs read /tmp/swarm_config.json; the swarm_config_env_lines
|
|
97
|
+
// function reads its "$1" argument.
|
|
98
|
+
const ends = ["' /tmp/swarm_config.json", '\' "$1"'].map((tail) => line.indexOf(tail, start));
|
|
99
|
+
const end = ends.find((index) => index !== -1) ?? -1;
|
|
97
100
|
if (end === -1) throw new Error(`Could not locate the end of the jq program for ${lineMarker}`);
|
|
98
101
|
return line.slice(start, end);
|
|
99
102
|
}
|
|
@@ -127,7 +130,7 @@ describe("entrypoint config env export: real jq filter", () => {
|
|
|
127
130
|
test(
|
|
128
131
|
"never exports codex_oauth or codex_oauth_<n>, nor the runner-resolved keys",
|
|
129
132
|
async () => {
|
|
130
|
-
const lines = await runJq(extractExportFilter("
|
|
133
|
+
const lines = await runJq(extractExportFilter("(.value | @sh)"), configs);
|
|
131
134
|
const keys = lines.map((l) => l.slice(0, l.indexOf("=")));
|
|
132
135
|
expect(keys).toEqual(["NORMAL", "codex_oauth_extra"]);
|
|
133
136
|
expect(lines.join("\n")).not.toContain("rt.secret");
|