@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
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
// Claim-time resolution of model profiles: a task's own `profile`, the agent's
|
|
2
|
+
// default (MODEL_PROFILE), and how both sit in the precedence next to a raw
|
|
3
|
+
// `model` and a `modelTier`. Drives the real `/api/poll` handler.
|
|
4
|
+
|
|
5
|
+
import { afterAll, beforeAll, beforeEach, describe, expect, test } from "bun:test";
|
|
6
|
+
import { unlink } from "node:fs/promises";
|
|
7
|
+
import {
|
|
8
|
+
closeDb,
|
|
9
|
+
createAgent,
|
|
10
|
+
createTaskExtended,
|
|
11
|
+
getDbClient,
|
|
12
|
+
getTaskById,
|
|
13
|
+
initDb,
|
|
14
|
+
upsertSwarmConfig,
|
|
15
|
+
} from "../be/db";
|
|
16
|
+
import { createModelProfile, getAgentDefaultProfileName } from "../be/db/model-profiles";
|
|
17
|
+
import { recordClaimModelResolution } from "../be/model-tier-resolution";
|
|
18
|
+
import { handlePoll } from "../http/poll";
|
|
19
|
+
import { taskModelPin } from "../tasks/model-pin";
|
|
20
|
+
import { inheritedModelSelection } from "../tasks/worker-follow-up";
|
|
21
|
+
|
|
22
|
+
const TEST_DB_PATH = "./test-profile-claim.sqlite";
|
|
23
|
+
|
|
24
|
+
async function removeDbFiles(path: string): Promise<void> {
|
|
25
|
+
for (const suffix of ["", "-wal", "-shm"]) {
|
|
26
|
+
try {
|
|
27
|
+
await unlink(path + suffix);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type Trigger = { type: string; taskId: string; task: Record<string, unknown> };
|
|
35
|
+
|
|
36
|
+
async function callPoll(agentId: string): Promise<Trigger | null> {
|
|
37
|
+
let bodyStr = "";
|
|
38
|
+
const req = {
|
|
39
|
+
method: "GET",
|
|
40
|
+
url: "/api/poll",
|
|
41
|
+
headers: { "x-agent-id": agentId },
|
|
42
|
+
} as unknown as Parameters<typeof handlePoll>[0];
|
|
43
|
+
const res = {
|
|
44
|
+
setHeader() {},
|
|
45
|
+
writeHead() {},
|
|
46
|
+
end(body?: string) {
|
|
47
|
+
bodyStr = body ?? "";
|
|
48
|
+
},
|
|
49
|
+
} as unknown as Parameters<typeof handlePoll>[1];
|
|
50
|
+
await handlePoll(req, res, ["api", "poll"], new URLSearchParams(), agentId);
|
|
51
|
+
return (JSON.parse(bodyStr) as { trigger: Trigger | null }).trigger;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function claimRecord(id: string) {
|
|
55
|
+
return await getDbClient().get<{
|
|
56
|
+
resolvedModel: string | null;
|
|
57
|
+
modelSource: string | null;
|
|
58
|
+
resolvedProfile: string | null;
|
|
59
|
+
}>("SELECT resolvedModel, modelSource, resolvedProfile FROM agent_tasks WHERE id = ?", [id]);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function worker(name: string, harnessProvider: "claude" | "codex", maxTasks = 1) {
|
|
63
|
+
return await createAgent({ name, isLead: false, status: "idle", maxTasks, harnessProvider });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function setDefault(agentId: string, profile: string): Promise<void> {
|
|
67
|
+
await upsertSwarmConfig({
|
|
68
|
+
scope: "agent",
|
|
69
|
+
scopeId: agentId,
|
|
70
|
+
key: "MODEL_PROFILE",
|
|
71
|
+
value: profile,
|
|
72
|
+
exportToEnv: false,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
beforeAll(async () => {
|
|
77
|
+
await removeDbFiles(TEST_DB_PATH);
|
|
78
|
+
initDb(TEST_DB_PATH);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
afterAll(async () => {
|
|
82
|
+
closeDb();
|
|
83
|
+
await removeDbFiles(TEST_DB_PATH);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
beforeEach(async () => {
|
|
87
|
+
const client = getDbClient();
|
|
88
|
+
// References first: migration 220's triggers refuse deleting a named profile.
|
|
89
|
+
await client.run("DELETE FROM agent_tasks");
|
|
90
|
+
await client.run("DELETE FROM swarm_config WHERE scope IN ('agent', 'repo')");
|
|
91
|
+
await client.run("DELETE FROM swarm_repos");
|
|
92
|
+
await client.run("DELETE FROM agents");
|
|
93
|
+
await client.run("DELETE FROM model_profiles");
|
|
94
|
+
await createModelProfile({
|
|
95
|
+
name: "claude-opus-high",
|
|
96
|
+
harness: "claude",
|
|
97
|
+
model: "opus",
|
|
98
|
+
effort: "high",
|
|
99
|
+
});
|
|
100
|
+
await createModelProfile({ name: "claude-haiku", harness: "claude", model: "haiku" });
|
|
101
|
+
await createModelProfile({
|
|
102
|
+
name: "codex-sol",
|
|
103
|
+
harness: "codex",
|
|
104
|
+
model: "gpt-5.6-sol",
|
|
105
|
+
effort: "xhigh",
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe("a task's own profile", () => {
|
|
110
|
+
test("claim stamps the profile and hands its model and effort to the worker", async () => {
|
|
111
|
+
const w = await worker("p-task", "claude");
|
|
112
|
+
const task = await createTaskExtended("work", { agentId: w.id, profile: "claude-opus-high" });
|
|
113
|
+
const trigger = await callPoll(w.id);
|
|
114
|
+
|
|
115
|
+
expect(trigger?.task).toMatchObject({
|
|
116
|
+
resolvedModel: "opus",
|
|
117
|
+
modelSource: "profile:task",
|
|
118
|
+
resolvedProfile: "claude-opus-high",
|
|
119
|
+
effort: "high",
|
|
120
|
+
});
|
|
121
|
+
expect(await claimRecord(task.id)).toEqual({
|
|
122
|
+
resolvedModel: "opus",
|
|
123
|
+
modelSource: "profile:task",
|
|
124
|
+
resolvedProfile: "claude-opus-high",
|
|
125
|
+
});
|
|
126
|
+
// The profile's effort rides on the trigger only; the row keeps the creator's ask.
|
|
127
|
+
expect((await getTaskById(task.id))?.effort).toBeUndefined();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("the task's own effort wins over the profile's", async () => {
|
|
131
|
+
const w = await worker("p-effort", "claude");
|
|
132
|
+
await createTaskExtended("work", { agentId: w.id, profile: "claude-opus-high", effort: "low" });
|
|
133
|
+
expect((await callPoll(w.id))?.task.effort).toBe("low");
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("a profile beats the agent default", async () => {
|
|
137
|
+
const w = await worker("p-beats-default", "claude");
|
|
138
|
+
await setDefault(w.id, "claude-haiku");
|
|
139
|
+
const task = await createTaskExtended("work", { agentId: w.id, profile: "claude-opus-high" });
|
|
140
|
+
await callPoll(w.id);
|
|
141
|
+
expect((await claimRecord(task.id))?.resolvedProfile).toBe("claude-opus-high");
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("a directed task with another harness's profile fails fast without a spawn", async () => {
|
|
145
|
+
const w = await worker("p-mismatch", "claude");
|
|
146
|
+
await createTaskExtended("work", { agentId: w.id, profile: "codex-sol" });
|
|
147
|
+
const trigger = await callPoll(w.id);
|
|
148
|
+
expect(String(trigger?.task.modelUnsupported)).toContain("[model-harness-mismatch]");
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test("a task naming a missing profile fails fast with a tagged reason", async () => {
|
|
152
|
+
// Migration 220's triggers keep such a row from existing; the claim still
|
|
153
|
+
// refuses one rather than run a model nobody picked.
|
|
154
|
+
const w = await worker("p-missing", "claude");
|
|
155
|
+
const task = await createTaskExtended("work", { agentId: w.id });
|
|
156
|
+
const fields = await recordClaimModelResolution(
|
|
157
|
+
{ id: task.id, profile: "claude-gone" },
|
|
158
|
+
{ id: w.id, harnessProvider: "claude" },
|
|
159
|
+
);
|
|
160
|
+
expect(String(fields.modelUnsupported)).toContain("[model-profile-missing]");
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test("a pool task waits for a worker on the profile's harness", async () => {
|
|
164
|
+
const codex = await worker("p-pool-codex", "codex");
|
|
165
|
+
const claude = await worker("p-pool-claude", "claude");
|
|
166
|
+
const task = await createTaskExtended("pool work", { profile: "claude-opus-high" });
|
|
167
|
+
|
|
168
|
+
expect(await callPoll(codex.id)).toBeNull();
|
|
169
|
+
const trigger = await callPoll(claude.id);
|
|
170
|
+
expect(trigger?.taskId).toBe(task.id);
|
|
171
|
+
expect(trigger?.task.resolvedProfile).toBe("claude-opus-high");
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
describe("the agent's default profile", () => {
|
|
176
|
+
test("applies to a task with no model and no tier", async () => {
|
|
177
|
+
const w = await worker("d-applies", "codex");
|
|
178
|
+
await setDefault(w.id, "codex-sol");
|
|
179
|
+
const task = await createTaskExtended("work", { agentId: w.id });
|
|
180
|
+
const trigger = await callPoll(w.id);
|
|
181
|
+
expect(trigger?.task).toMatchObject({
|
|
182
|
+
resolvedModel: "gpt-5.6-sol",
|
|
183
|
+
modelSource: "profile:agent-default",
|
|
184
|
+
resolvedProfile: "codex-sol",
|
|
185
|
+
effort: "xhigh",
|
|
186
|
+
});
|
|
187
|
+
expect((await claimRecord(task.id))?.modelSource).toBe("profile:agent-default");
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test("a raw task model and a task tier both win over it", async () => {
|
|
191
|
+
const w = await worker("d-loses", "claude", 2);
|
|
192
|
+
await setDefault(w.id, "claude-opus-high");
|
|
193
|
+
const pinned = await createTaskExtended("pinned", { agentId: w.id, model: "sonnet" });
|
|
194
|
+
const tiered = await createTaskExtended("tiered", { agentId: w.id, modelTier: "smol" });
|
|
195
|
+
await callPoll(w.id);
|
|
196
|
+
await callPoll(w.id);
|
|
197
|
+
expect(await claimRecord(pinned.id)).toEqual({
|
|
198
|
+
resolvedModel: "sonnet",
|
|
199
|
+
modelSource: "model",
|
|
200
|
+
resolvedProfile: null,
|
|
201
|
+
});
|
|
202
|
+
expect(await claimRecord(tiered.id)).toEqual({
|
|
203
|
+
resolvedModel: "haiku",
|
|
204
|
+
modelSource: "tier-default",
|
|
205
|
+
resolvedProfile: null,
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test("a default for another harness is ignored; a missing one cannot be stored", async () => {
|
|
210
|
+
const w = await worker("d-ignored", "claude", 2);
|
|
211
|
+
await setDefault(w.id, "codex-sol");
|
|
212
|
+
const first = await createTaskExtended("work", { agentId: w.id });
|
|
213
|
+
const trigger = await callPoll(w.id);
|
|
214
|
+
expect(trigger?.task.modelUnsupported).toBeUndefined();
|
|
215
|
+
expect(trigger?.task.resolvedModel).toBeUndefined();
|
|
216
|
+
expect(await claimRecord(first.id)).toEqual({
|
|
217
|
+
resolvedModel: null,
|
|
218
|
+
modelSource: null,
|
|
219
|
+
resolvedProfile: null,
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
await expect(setDefault(w.id, "gone")).rejects.toThrow("model-profile-missing");
|
|
223
|
+
expect(await getAgentDefaultProfileName(w.id)).toBe("codex-sol");
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
describe("re-runs", () => {
|
|
228
|
+
test("a profile-filled model is not a pin, so a re-claim resolves the profile again", async () => {
|
|
229
|
+
const w = await worker("r-rerun", "claude");
|
|
230
|
+
await setDefault(w.id, "claude-opus-high");
|
|
231
|
+
const task = await createTaskExtended("work", { agentId: w.id });
|
|
232
|
+
await callPoll(w.id);
|
|
233
|
+
// The session report writes the model it ran into `model`.
|
|
234
|
+
await getDbClient().run(
|
|
235
|
+
"UPDATE agent_tasks SET model = 'opus', claudeSessionId = 's1', status = 'pending' WHERE id = ?",
|
|
236
|
+
[task.id],
|
|
237
|
+
);
|
|
238
|
+
const ran = await getTaskById(task.id);
|
|
239
|
+
expect(ran && taskModelPin(ran)).toBeUndefined();
|
|
240
|
+
|
|
241
|
+
// The default changes between runs; the re-claim follows it.
|
|
242
|
+
await setDefault(w.id, "claude-haiku");
|
|
243
|
+
const trigger = await callPoll(w.id);
|
|
244
|
+
expect(trigger?.task).toMatchObject({
|
|
245
|
+
resolvedModel: "haiku",
|
|
246
|
+
resolvedProfile: "claude-haiku",
|
|
247
|
+
});
|
|
248
|
+
// Neither the previous run's model (no pin) nor its profile effort carries over.
|
|
249
|
+
expect(trigger?.task.model).toBeUndefined();
|
|
250
|
+
expect(trigger?.task.effort).toBeUndefined();
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
test("a resume carries the task's own profile, not an agent default", async () => {
|
|
254
|
+
const a = await worker("r-resume-own", "claude");
|
|
255
|
+
const b = await worker("r-resume-default", "claude");
|
|
256
|
+
await setDefault(a.id, "claude-opus-high");
|
|
257
|
+
await setDefault(b.id, "claude-opus-high");
|
|
258
|
+
const own = await createTaskExtended("work", { agentId: a.id, profile: "claude-haiku" });
|
|
259
|
+
const viaDefault = await createTaskExtended("work", { agentId: b.id });
|
|
260
|
+
await callPoll(a.id);
|
|
261
|
+
await callPoll(b.id);
|
|
262
|
+
|
|
263
|
+
const ownRow = await getTaskById(own.id);
|
|
264
|
+
const defaultRow = await getTaskById(viaDefault.id);
|
|
265
|
+
expect(ownRow && inheritedModelSelection(ownRow)).toEqual({ profile: "claude-haiku" });
|
|
266
|
+
expect(defaultRow?.resolvedProfile).toBe("claude-opus-high");
|
|
267
|
+
expect(defaultRow && inheritedModelSelection(defaultRow)).toEqual({});
|
|
268
|
+
});
|
|
269
|
+
});
|
|
@@ -12,6 +12,7 @@ import "../heartbeat/templates";
|
|
|
12
12
|
import "../tools/templates";
|
|
13
13
|
import "../commands/templates";
|
|
14
14
|
import "../slack/templates";
|
|
15
|
+
import "../integrations/chat-sdk/templates";
|
|
15
16
|
import "../prompts/session-templates";
|
|
16
17
|
|
|
17
18
|
const TEST_DB_PATH = "./test-prompt-remaining.sqlite";
|
|
@@ -33,6 +34,7 @@ async function ensureTemplatesRegistered(): Promise<void> {
|
|
|
33
34
|
await import(`../tools/templates?t=${ts}`);
|
|
34
35
|
await import(`../commands/templates?t=${ts}`);
|
|
35
36
|
await import(`../slack/templates?t=${ts}`);
|
|
37
|
+
await import(`../integrations/chat-sdk/templates?t=${ts}`);
|
|
36
38
|
await import(`../prompts/session-templates?t=${ts}`);
|
|
37
39
|
}
|
|
38
40
|
|
|
@@ -115,6 +117,22 @@ describe("template registration — all sources", () => {
|
|
|
115
117
|
expect(eventTypes).toContain("heartbeat.checklist");
|
|
116
118
|
});
|
|
117
119
|
|
|
120
|
+
test("Heartbeat checklist puts the live status after the standing orders and instructions", () => {
|
|
121
|
+
// The status changes every tick; the orders and instructions usually do
|
|
122
|
+
// not, so they lead the text and the status goes last.
|
|
123
|
+
const { text } = resolveTemplate("heartbeat.checklist", {
|
|
124
|
+
system_status: "STATUS_MARKER",
|
|
125
|
+
heartbeat_content: "ORDERS_MARKER",
|
|
126
|
+
});
|
|
127
|
+
const orders = text.indexOf("ORDERS_MARKER");
|
|
128
|
+
const instructions = text.indexOf("## Instructions");
|
|
129
|
+
const status = text.indexOf("## Current System Status");
|
|
130
|
+
expect(orders).toBeGreaterThan(-1);
|
|
131
|
+
expect(instructions).toBeGreaterThan(orders);
|
|
132
|
+
expect(status).toBeGreaterThan(instructions);
|
|
133
|
+
expect(text.trimEnd()).toEndWith("STATUS_MARKER");
|
|
134
|
+
});
|
|
135
|
+
|
|
118
136
|
test("Task lifecycle templates are registered (3 task_lifecycle)", () => {
|
|
119
137
|
const all = getAllTemplateDefinitions();
|
|
120
138
|
const eventTypes = all.map((d) => d.eventType);
|
|
@@ -143,6 +161,11 @@ describe("template registration — all sources", () => {
|
|
|
143
161
|
expect(eventTypes).toContain("slack.assistant.offline");
|
|
144
162
|
expect(eventTypes).toContain("slack.message.thread_context");
|
|
145
163
|
});
|
|
164
|
+
|
|
165
|
+
test("Chat SDK templates are registered (1 system)", () => {
|
|
166
|
+
const eventTypes = getAllTemplateDefinitions().map((d) => d.eventType);
|
|
167
|
+
expect(eventTypes).toContain("chat.message.thread_context");
|
|
168
|
+
});
|
|
146
169
|
});
|
|
147
170
|
|
|
148
171
|
// ============================================================================
|
|
@@ -608,4 +631,16 @@ Alice: Hello
|
|
|
608
631
|
|
|
609
632
|
expect(result.text).toBe(expected);
|
|
610
633
|
});
|
|
634
|
+
|
|
635
|
+
test("chat.message.thread_context produces the same block as Slack", () => {
|
|
636
|
+
const result = resolveTemplate("chat.message.thread_context", {
|
|
637
|
+
thread_messages: "Alice: Hello\n[Agent]: Hi there!",
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
expect(result.skipped).toBe(false);
|
|
641
|
+
expect(result.text).toBe(`<thread_context>
|
|
642
|
+
Alice: Hello
|
|
643
|
+
[Agent]: Hi there!
|
|
644
|
+
</thread_context>`);
|
|
645
|
+
});
|
|
611
646
|
});
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
checkQuickJSCompat,
|
|
4
|
+
type QuickJSCompatRule,
|
|
5
|
+
quickJSCompatWarnings,
|
|
6
|
+
} from "../scripts-runtime/quickjs-compat";
|
|
7
|
+
|
|
8
|
+
const wrap = (body: string) => `export default async (args, ctx) => {\n${body}\n};`;
|
|
9
|
+
|
|
10
|
+
// One positive (flagged) and one negative (clean, but close) fixture per rule.
|
|
11
|
+
const FIXTURES: Array<{ rule: QuickJSCompatRule; flagged: string; clean: string }> = [
|
|
12
|
+
{
|
|
13
|
+
rule: "bunGlobal",
|
|
14
|
+
flagged: wrap("return Bun.hash('x');"),
|
|
15
|
+
clean: wrap("const Bun = { hash: (s) => s }; return typeof globalThis.Bun;"),
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
rule: "process",
|
|
19
|
+
flagged: wrap("return process.env.HOME;"),
|
|
20
|
+
clean: wrap("const config = { process: 1 }; return config.process + args.process;"),
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
rule: "fsStdlib",
|
|
24
|
+
flagged: wrap("return ctx.stdlib.glob('*.ts');"),
|
|
25
|
+
clean: wrap("return ctx.stdlib.fetchJson('https://example.com');"),
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
rule: "require",
|
|
29
|
+
flagged: wrap("const fs = require('fs'); return fs;"),
|
|
30
|
+
clean: wrap("const require = (name) => name; return require('fs');"),
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
rule: "unsupportedGlobal",
|
|
34
|
+
flagged: wrap("return Buffer.from('aGk=', 'base64').toString();"),
|
|
35
|
+
clean: wrap("const bytes: Buffer | null = null; return { Buffer: bytes };"),
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
rule: "cryptoSubtle",
|
|
39
|
+
flagged: wrap("return crypto.subtle.digest('SHA-256', new Uint8Array());"),
|
|
40
|
+
clean: wrap("return crypto.randomUUID();"),
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
rule: "responseBody",
|
|
44
|
+
flagged: wrap("const res = await fetch('https://example.com'); return res.arrayBuffer();"),
|
|
45
|
+
clean: wrap("const res = await fetch('https://example.com'); return res.text();"),
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
rule: "localeFormat",
|
|
49
|
+
flagged: wrap("return new Date().toLocaleString('en-GB', { timeZone: 'Europe/Madrid' });"),
|
|
50
|
+
clean: wrap("return new Date().toISOString();"),
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
rule: "hostCredential",
|
|
54
|
+
flagged: wrap("return ctx.stdlib.Redacted.value(ctx.swarm.config.apiKey);"),
|
|
55
|
+
clean: wrap("return ctx.stdlib.Redacted.value(ctx.swarm.config.agentId);"),
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
rule: "privateHost",
|
|
59
|
+
flagged: wrap("return ctx.stdlib.fetchJson('http://intranet:8080/status');"),
|
|
60
|
+
clean: wrap("return ctx.stdlib.fetchJson('https://api.example.com/status');"),
|
|
61
|
+
},
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
describe("checkQuickJSCompat", () => {
|
|
65
|
+
for (const { rule, flagged, clean } of FIXTURES) {
|
|
66
|
+
test(`${rule}: flags the positive fixture`, () => {
|
|
67
|
+
const report = checkQuickJSCompat(flagged);
|
|
68
|
+
expect(report.compatible).toBe(false);
|
|
69
|
+
expect(report.findings.map((finding) => finding.rule)).toContain(rule);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test(`${rule}: passes the negative fixture`, () => {
|
|
73
|
+
expect(checkQuickJSCompat(clean)).toEqual({ compatible: true, findings: [] });
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
test("workspaceRw: flags the fsMode, not the source", () => {
|
|
78
|
+
const source = wrap("return 'workspace-rw';");
|
|
79
|
+
expect(checkQuickJSCompat(source, { fsMode: "workspace-rw" }).findings).toEqual([
|
|
80
|
+
{ rule: "workspaceRw", message: "fsMode workspace-rw is not available under quickjs" },
|
|
81
|
+
]);
|
|
82
|
+
expect(checkQuickJSCompat(source, { fsMode: "none" }).compatible).toBe(true);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("fsStdlib also catches stdlib imports and destructuring", () => {
|
|
86
|
+
const imported = `import { glob } from "stdlib";\n${wrap("return glob('*');")}`;
|
|
87
|
+
const destructured = wrap("const { grep } = ctx.stdlib; return grep('x', '.');");
|
|
88
|
+
expect(checkQuickJSCompat(imported).findings[0]?.rule).toBe("fsStdlib");
|
|
89
|
+
expect(checkQuickJSCompat(destructured).findings[0]?.rule).toBe("fsStdlib");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("globals read off globalThis count, typeof feature checks do not", () => {
|
|
93
|
+
expect(checkQuickJSCompat(wrap("return globalThis.process.env;")).findings[0]?.rule).toBe(
|
|
94
|
+
"process",
|
|
95
|
+
);
|
|
96
|
+
expect(
|
|
97
|
+
checkQuickJSCompat(wrap("return (globalThis as any).Bun.version;")).findings[0]?.rule,
|
|
98
|
+
).toBe("bunGlobal");
|
|
99
|
+
expect(checkQuickJSCompat(wrap("return typeof process === 'undefined';")).compatible).toBe(
|
|
100
|
+
true,
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("the shimmed web globals are compatible", () => {
|
|
105
|
+
const source = wrap(`const url = new URL("/x?a=1", "https://example.com");
|
|
106
|
+
url.searchParams.set("b", "2");
|
|
107
|
+
const bytes = new TextEncoder().encode(url.href);
|
|
108
|
+
const id = setInterval(() => {}, 10);
|
|
109
|
+
clearInterval(id);
|
|
110
|
+
await fetch(url, { method: "POST", body: bytes });
|
|
111
|
+
return [new TextDecoder().decode(bytes), crypto.randomUUID(), crypto.getRandomValues(new Uint8Array(4))];`);
|
|
112
|
+
expect(checkQuickJSCompat(source)).toEqual({ compatible: true, findings: [] });
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("bindings resolve by lexical scope", () => {
|
|
116
|
+
// A local Buffer parameter in one function does not hide a global Buffer in another.
|
|
117
|
+
const shadowed = `function decode(Buffer: { from(x: string): string }) { return Buffer.from("x"); }
|
|
118
|
+
export default async () => [decode({ from: (x) => x }), Buffer.from("aGk=", "base64").toString()];`;
|
|
119
|
+
expect(checkQuickJSCompat(shadowed).findings).toEqual([
|
|
120
|
+
{ rule: "unsupportedGlobal", message: "Buffer is not available under quickjs", line: 2 },
|
|
121
|
+
]);
|
|
122
|
+
// Block scope too, and a shorthand property reads the binding it names.
|
|
123
|
+
const block = wrap("{ const process = 1; } return { process };");
|
|
124
|
+
expect(checkQuickJSCompat(block).findings[0]?.rule).toBe("process");
|
|
125
|
+
// Every use resolves locally: clean.
|
|
126
|
+
const local = `function decode(Buffer: { from(x: string): string }) { return Buffer.from("x"); }
|
|
127
|
+
export default async () => { const process = { env: {} }; return [decode({ from: (x) => x }), process.env]; };`;
|
|
128
|
+
expect(checkQuickJSCompat(local)).toEqual({ compatible: true, findings: [] });
|
|
129
|
+
// A declare-only binding defines nothing at run time, so the global still counts.
|
|
130
|
+
const ambient = `declare const Buffer: any;\n${wrap("return Buffer.from('x');")}`;
|
|
131
|
+
expect(checkQuickJSCompat(ambient).findings[0]?.rule).toBe("unsupportedGlobal");
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("atob and btoa are shimmed; AbortSignal stays reported", () => {
|
|
135
|
+
expect(checkQuickJSCompat(wrap("return atob(btoa('hi'));"))).toEqual({
|
|
136
|
+
compatible: true,
|
|
137
|
+
findings: [],
|
|
138
|
+
});
|
|
139
|
+
expect(
|
|
140
|
+
checkQuickJSCompat(
|
|
141
|
+
wrap("return fetch('https://example.com', { signal: AbortSignal.timeout(5) });"),
|
|
142
|
+
).findings[0]?.message,
|
|
143
|
+
).toBe("AbortSignal is not available under quickjs");
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("privateHost: literal fetch URLs to single-label or private hosts", () => {
|
|
147
|
+
const hosts = (body: string) =>
|
|
148
|
+
checkQuickJSCompat(wrap(body)).findings.filter((finding) => finding.rule === "privateHost");
|
|
149
|
+
const flagged = [
|
|
150
|
+
"fetch('http://api:3013/health')",
|
|
151
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: script source under test
|
|
152
|
+
"fetch(`http://localhost:8080/${args.path}`)",
|
|
153
|
+
"globalThis.fetch('http://10.0.0.5/x')",
|
|
154
|
+
"ctx.stdlib.fetch('http://192.168.1.10/')",
|
|
155
|
+
"ctx.stdlib.fetchJson('http://[::1]:9000/')",
|
|
156
|
+
"ctx.stdlib.fetchJson('http://0x7f.1/')",
|
|
157
|
+
"fetch('http://[fd00::1]/')",
|
|
158
|
+
"fetch('http://[::ffff:10.1.2.3]/')",
|
|
159
|
+
"fetch('https://svc.localhost/')",
|
|
160
|
+
"const { fetchJson } = ctx.stdlib; await fetchJson('http://intranet/')",
|
|
161
|
+
];
|
|
162
|
+
for (const call of flagged) expect(hosts(`return ${call};`)).toHaveLength(1);
|
|
163
|
+
const clean = [
|
|
164
|
+
"fetch('https://example.com/x')",
|
|
165
|
+
"fetch('https://8.8.8.8/')",
|
|
166
|
+
"fetch('http://[2001:db8::1]/')",
|
|
167
|
+
"fetch(args.url)",
|
|
168
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: script source under test
|
|
169
|
+
"fetch(`http://${args.host}/x`)",
|
|
170
|
+
"fetch('relative/path')",
|
|
171
|
+
"ctx.api.github.fetch('http://intranet/')",
|
|
172
|
+
"console.log('http://intranet/')",
|
|
173
|
+
];
|
|
174
|
+
for (const call of clean) expect(hosts(`return ${call};`)).toEqual([]);
|
|
175
|
+
// A const that holds the base URL fixes the host too; a let or a cut-off name does not.
|
|
176
|
+
const constBase = [
|
|
177
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: script source under test
|
|
178
|
+
"const API = 'http://api:3013'; return fetch(`${API}/health`);",
|
|
179
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: script source under test
|
|
180
|
+
"const API = 'http://api:3013'; return fetch(`${API}${args.path}`);",
|
|
181
|
+
"const API = 'http://api:3013'; return ctx.stdlib.fetch(API + '/x');",
|
|
182
|
+
"const API = `http://10.1.2.3`; return fetch(API);",
|
|
183
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: script source under test
|
|
184
|
+
"const API = 'http://api:3013'; const rows = `${API}/rows/${args.id}`; return fetch(`${rows}?limit=5`);",
|
|
185
|
+
];
|
|
186
|
+
for (const body of constBase) expect(hosts(body)).toHaveLength(1);
|
|
187
|
+
const dynamicBase = [
|
|
188
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: script source under test
|
|
189
|
+
"let API = 'http://api:3013'; API = args.base; return fetch(`${API}/x`);",
|
|
190
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: script source under test
|
|
191
|
+
"return fetch(`http://intra${args.suffix}/x`);",
|
|
192
|
+
"return fetch('http://intra' + args.suffix);",
|
|
193
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: script source under test
|
|
194
|
+
"const API = 'https://api.example.com'; return fetch(`${API}/x`);",
|
|
195
|
+
];
|
|
196
|
+
for (const body of dynamicBase) expect(hosts(body)).toEqual([]);
|
|
197
|
+
expect(hosts("return fetch('http://api:3013/health');")[0]?.message).toBe(
|
|
198
|
+
"fetch to api:3013 (single-label host) depends on the egress policy: quickjs refuses private hosts unless the operator allows them",
|
|
199
|
+
);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("reports each rule and name once, with its first line", () => {
|
|
203
|
+
const report = checkQuickJSCompat(wrap("Bun.sleep(1);\nBun.sleep(2);\nreturn process.pid;"));
|
|
204
|
+
expect(report.findings).toEqual([
|
|
205
|
+
{ rule: "bunGlobal", message: "Bun.* is not available under quickjs", line: 2 },
|
|
206
|
+
{ rule: "process", message: "process is not available under quickjs", line: 4 },
|
|
207
|
+
]);
|
|
208
|
+
expect(quickJSCompatWarnings(report, "actions.sync")).toEqual([
|
|
209
|
+
"actions.sync: quickjs compat (bunGlobal, line 2): Bun.* is not available under quickjs",
|
|
210
|
+
"actions.sync: quickjs compat (process, line 4): process is not available under quickjs",
|
|
211
|
+
]);
|
|
212
|
+
});
|
|
213
|
+
});
|
|
@@ -49,6 +49,10 @@ const LEAD_ONLY_VERBS: PermissionVerb[] = [
|
|
|
49
49
|
"memory.write.consolidated",
|
|
50
50
|
"channel.delete",
|
|
51
51
|
"integration.kapso.manage",
|
|
52
|
+
"integration.chat.manage",
|
|
53
|
+
"integration.chat.post",
|
|
54
|
+
"integration.chat.read",
|
|
55
|
+
"integration.chat.dm",
|
|
52
56
|
"integration.slack.post",
|
|
53
57
|
"integration.slack.read",
|
|
54
58
|
"integration.slack.thread.start",
|
|
@@ -103,6 +107,9 @@ const OPERATOR_OR_USER_VERBS: PermissionVerb[] = [
|
|
|
103
107
|
|
|
104
108
|
const LEAD_OR_OPERATOR_VERBS: PermissionVerb[] = ["models.catalog.write"];
|
|
105
109
|
|
|
110
|
+
// The shared API key alone: dashboard users are denied too.
|
|
111
|
+
const API_KEY_OPERATOR_VERBS: PermissionVerb[] = ["models.profiles.write"];
|
|
112
|
+
|
|
106
113
|
const AGENT_OR_OPERATOR_VERBS: PermissionVerb[] = ["models.harness-support.write"];
|
|
107
114
|
|
|
108
115
|
const LEAD_OR_RESOURCE_OWNER_VERBS: PermissionVerb[] = [
|
|
@@ -140,6 +147,7 @@ const REQUESTER_OWNS_TASK_VERBS: PermissionVerb[] = [
|
|
|
140
147
|
const COMPOSITE_VERBS: PermissionVerb[] = [
|
|
141
148
|
"memory.delete.any",
|
|
142
149
|
"task.fs.mutate",
|
|
150
|
+
"task.parent.attach",
|
|
143
151
|
"task.progress.write",
|
|
144
152
|
];
|
|
145
153
|
|
|
@@ -206,6 +214,7 @@ describe("verb-group partition", () => {
|
|
|
206
214
|
...LEAD_OR_OPERATOR_OR_USER_VERBS,
|
|
207
215
|
...OPERATOR_OR_USER_VERBS,
|
|
208
216
|
...LEAD_OR_OPERATOR_VERBS,
|
|
217
|
+
...API_KEY_OPERATOR_VERBS,
|
|
209
218
|
...AGENT_OR_OPERATOR_VERBS,
|
|
210
219
|
];
|
|
211
220
|
expect(new Set(grouped).size).toBe(grouped.length);
|
|
@@ -320,6 +329,23 @@ describe("agent-or-operator verbs", () => {
|
|
|
320
329
|
}
|
|
321
330
|
});
|
|
322
331
|
|
|
332
|
+
describe("api-key operator verbs", () => {
|
|
333
|
+
const expected: Expected = {
|
|
334
|
+
lead: false,
|
|
335
|
+
worker: false,
|
|
336
|
+
ownerWorker: false,
|
|
337
|
+
creatorWorker: false,
|
|
338
|
+
userRequester: false,
|
|
339
|
+
foreignUser: false,
|
|
340
|
+
operator: true,
|
|
341
|
+
};
|
|
342
|
+
for (const verb of API_KEY_OPERATOR_VERBS) {
|
|
343
|
+
test(`${verb}: only the operator allowed`, () => {
|
|
344
|
+
expectDecisions(verb, { kind: "none" }, expected);
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
323
349
|
describe("lead-or-operator verbs", () => {
|
|
324
350
|
const expected: Expected = {
|
|
325
351
|
lead: true,
|
|
@@ -570,9 +596,12 @@ describe("memory.delete.any composite (human, lead for swarm, owner for agent sc
|
|
|
570
596
|
});
|
|
571
597
|
});
|
|
572
598
|
|
|
573
|
-
describe("task.fs.mutate composite (operator OR user OR lead OR assignee OR creator)", () => {
|
|
574
|
-
test(
|
|
575
|
-
|
|
599
|
+
describe("task.fs.mutate and task.parent.attach composite (operator OR user OR lead OR assignee OR creator)", () => {
|
|
600
|
+
test.each([
|
|
601
|
+
"task.fs.mutate",
|
|
602
|
+
"task.parent.attach",
|
|
603
|
+
] as const)("%s: only an unrelated worker agent is denied", (verb) => {
|
|
604
|
+
expectDecisions(verb, TASK_RESOURCE, {
|
|
576
605
|
lead: true,
|
|
577
606
|
worker: false,
|
|
578
607
|
ownerWorker: true, // assignee (task.agentId)
|
|
@@ -122,8 +122,6 @@ test(
|
|
|
122
122
|
const tempDir = `/tmp/runner-exit-shutdown-${crypto.randomUUID()}`;
|
|
123
123
|
await Bun.$`mkdir -p ${tempDir}`.quiet();
|
|
124
124
|
try {
|
|
125
|
-
await Bun.write(`${tempDir}/pm2`, '#!/bin/sh\nprintf "%s" "$1" > "$SHUTDOWN_TEST_PM2_LOG"\n');
|
|
126
|
-
await Bun.$`chmod +x ${tempDir}/pm2`.quiet();
|
|
127
125
|
await Bun.write(
|
|
128
126
|
`${tempDir}/runner.ts`,
|
|
129
127
|
`
|
|
@@ -141,14 +139,12 @@ test(
|
|
|
141
139
|
...process.env,
|
|
142
140
|
PATH: tempDir,
|
|
143
141
|
ANONYMIZED_TELEMETRY: "false",
|
|
144
|
-
SHUTDOWN_TEST_PM2_LOG: `${tempDir}/pm2.log`,
|
|
145
142
|
},
|
|
146
143
|
}),
|
|
147
144
|
"runner shutdown",
|
|
148
145
|
);
|
|
149
146
|
expect(result.stdout).toContain("shutdown callback");
|
|
150
147
|
expect(result.stdout).not.toContain("unexpected return");
|
|
151
|
-
expect(await Bun.file(`${tempDir}/pm2.log`).text()).toBe("save");
|
|
152
148
|
expect(requests).toHaveLength(1);
|
|
153
149
|
expect(requests[0]?.path).toBe("/close");
|
|
154
150
|
expect(requests[0]?.headers["x-agent-id"]).toBe(agentId);
|