@desplega.ai/agent-swarm 1.172.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{acp-adapter-fpecx1g1.js → acp-adapter-sj2g0m84.js} +18 -17
- package/dist/{actions-pw87a1a8.js → actions-fegk44gr.js} +65 -35
- package/dist/{amp-adapter-8jem2bc0.js → amp-adapter-ad8ft89x.js} +8 -7
- package/dist/{anthropic-messages-8ee2f22q.js → anthropic-messages-r6czkjv1.js} +7 -7
- package/dist/{app-wmf0m843.js → app-kfvgbvzy.js} +19 -13
- package/dist/{assistant-emp1e108.js → assistant-z7zdy9ja.js} +65 -38
- package/dist/{boot-reembed-1de4f4aj.js → boot-reembed-5c2z0mbe.js} +11 -9
- package/dist/{boot-reembed-4ymn9atn.js → boot-reembed-j8xa181e.js} +11 -9
- package/dist/{boot-scrub-sweep-adhk1s8c.js → boot-scrub-sweep-hnkn3rgt.js} +12 -10
- package/dist/{boot-seeds-8q637aew.js → boot-seeds-kwjq4d2z.js} +15 -13
- package/dist/{claude-adapter-t26cv8a4.js → claude-adapter-9vmvpvgd.js} +20 -19
- package/dist/{claude-managed-adapter-xgxhqrt2.js → claude-managed-adapter-k9qae05j.js} +7 -5
- package/dist/{claude-managed-setup-bnmjarjc.js → claude-managed-setup-q54zx5q2.js} +6 -4
- package/dist/{claude-sdk-session-gsfsh662.js → claude-sdk-session-9jye6cxr.js} +12698 -11995
- package/dist/{cli-k46r9hmk.js → cli-03gedhng.js} +16 -14
- package/dist/{cli-kb4ehzx5.js → cli-1e4b6bzh.js} +7 -7
- package/dist/{cli-d603qf86.js → cli-29jc5q7w.js} +10 -8
- package/dist/cli-2m1ktfzz.js +17795 -0
- package/dist/{cli-8vs9trd4.js → cli-3dj8k4vy.js} +1 -1
- package/dist/{cli-zxm9p3x4.js → cli-3fbmmawm.js} +5 -5
- package/dist/{cli-gmdajfw5.js → cli-4jxm2qtz.js} +4987 -1708
- package/dist/{cli-tadfgd0t.js → cli-5hkm7whv.js} +33 -4
- package/dist/{cli-6jb2rf77.js → cli-5t4f29ez.js} +3 -3
- package/dist/{cli-7a0sde2x.js → cli-6cagzc96.js} +9 -0
- package/dist/cli-6cvfxs8r.js +47 -0
- package/dist/{cli-pj6x9qce.js → cli-6jyj59nb.js} +2 -2
- package/dist/{cli-388egyf6.js → cli-6razj6t4.js} +2 -2
- package/dist/{cli-qsztpfry.js → cli-74ahz61x.js} +2 -2
- package/dist/{cli-4nz8p1p5.js → cli-7x0g4sn1.js} +15474 -16082
- package/dist/{cli-yatxqynm.js → cli-9ha6hs2d.js} +1 -290
- package/dist/{cli-81r1wjyv.js → cli-9nfx44eh.js} +1 -1
- package/dist/cli-9traj3dn.js +638 -0
- package/dist/{cli-smcrrmhy.js → cli-agzebbd5.js} +9 -9
- package/dist/{cli-bf7485pm.js → cli-apx87jne.js} +2 -2
- package/dist/{cli-ztrv9y21.js → cli-bzszjf2p.js} +3689 -1081
- package/dist/{cli-c5km5znr.js → cli-c8r3kzzb.js} +1 -1
- package/dist/{cli-hsem16hb.js → cli-cgtksawx.js} +7 -4
- package/dist/{cli-db3m6zdt.js → cli-d2qr7aj4.js} +1 -1
- package/dist/{cli-1hgat6pa.js → cli-d5f9ed5c.js} +5 -5
- package/dist/cli-e3g5mbkz.js +776 -0
- package/dist/{cli-7jykv7ef.js → cli-e6mpjhed.js} +1 -1
- package/dist/{cli-j6kpj9az.js → cli-ee9gtz70.js} +1553 -68
- package/dist/{cli-e0a9sk00.js → cli-eejsgvm4.js} +4 -4
- package/dist/{cli-tgpfnm6r.js → cli-ef1hg1wv.js} +518 -59
- package/dist/cli-f527ce74.js +781 -0
- package/dist/{cli-ppy4nnnh.js → cli-f7kr7svz.js} +2 -2
- package/dist/cli-hjwmhcg1.js +10622 -0
- package/dist/{cli-2bw3w47j.js → cli-hnhtk6cj.js} +1 -1
- package/dist/{cli-ywm1azg9.js → cli-hnmhtzvh.js} +6 -4
- package/dist/{cli-4fnb6884.js → cli-hvqbtna4.js} +7 -1259
- package/dist/{cli-pstq9888.js → cli-j0pfptdp.js} +220 -103
- package/dist/cli-jta2eyzc.js +619 -0
- package/dist/{cli-esnpehf5.js → cli-k382tshb.js} +12 -13
- package/dist/cli-k3dp5tpf.js +263 -0
- package/dist/{cli-wd0hz6ah.js → cli-kbrt8gqa.js} +9 -91
- package/dist/cli-kceah1eh.js +92 -0
- package/dist/{cli-prrt9fa0.js → cli-ke896fcf.js} +10 -10
- package/dist/{cli-nv4zd0b9.js → cli-kt6zqg0e.js} +6 -6
- package/dist/{cli-g6t9texm.js → cli-m50t4309.js} +5 -18
- package/dist/cli-n2wq4j7m.js +6985 -0
- package/dist/{cli-c2pq2rjb.js → cli-n3hzg89b.js} +46 -15
- package/dist/{cli-tmhsxhb8.js → cli-n5vwg842.js} +23 -6
- package/dist/{cli-5rv1j9ga.js → cli-ne236afy.js} +53 -7
- package/dist/cli-p0sg24zp.js +16 -0
- package/dist/{cli-d161g69q.js → cli-p1x85d2t.js} +50 -2
- package/dist/{cli-7pz3b7x5.js → cli-przpmdgr.js} +25 -0
- package/dist/{cli-g4f5pj26.js → cli-q0ke1yww.js} +111 -25
- package/dist/{cli-e3pmyt39.js → cli-q50942rs.js} +1 -1
- package/dist/{cli-r44t9wqc.js → cli-r106725k.js} +2 -2
- package/dist/{cli-gj81e47t.js → cli-r2pnvj8g.js} +2 -2
- package/dist/{cli-r8smj8b8.js → cli-r62a35ea.js} +19 -3
- package/dist/{cli-cd0hv9nh.js → cli-ra0xzggj.js} +2 -2
- package/dist/cli-rhs9pz33.js +294 -0
- package/dist/{cli-nyhb1r6x.js → cli-s2sp957d.js} +52 -29
- package/dist/{cli-1x7da0de.js → cli-s4setq91.js} +8 -8
- package/dist/{cli-xczpdp9e.js → cli-se8w6wny.js} +6 -6
- package/dist/cli-skxvvpap.js +10140 -0
- package/dist/{cli-014vns2y.js → cli-stfs0k5s.js} +208 -525
- package/dist/{cli-0p3enk35.js → cli-tqaazemc.js} +9 -1
- package/dist/{cli-cf69s3ew.js → cli-tr4sxp68.js} +3 -3
- package/dist/{cli-8hzfb6xz.js → cli-tsde1hdr.js} +15 -15
- package/dist/{cli-cceskcvt.js → cli-txqb9s38.js} +8 -8
- package/dist/{cli-aspcaef3.js → cli-v1yb0rqb.js} +6 -6
- package/dist/{cli-j1pmc07z.js → cli-vatyhshy.js} +268 -40
- package/dist/{cli-04emfe2q.js → cli-vdach5a9.js} +5 -5
- package/dist/{cli-7avy4v8w.js → cli-w1q7h2r2.js} +5 -3
- package/dist/{cli-rcp5aqdw.js → cli-w7m81tb7.js} +1 -1
- package/dist/{cli-hwwvy36w.js → cli-wayw93nt.js} +3 -3
- package/dist/cli-wmgrkscj.js +23254 -0
- package/dist/{cli-gp1rtyvk.js → cli-z5jjpaxw.js} +29 -6
- package/dist/{cli-451ks6aw.js → cli-zkpks3r3.js} +1 -1
- package/dist/cli.js +32 -48
- package/dist/{codex-adapter-m4tgdysy.js → codex-adapter-y81sssk0.js} +16 -15
- package/dist/{codex-login-841kv5fk.js → codex-login-f2rcfpp9.js} +3 -2
- package/dist/{codex-session-runner-ymwzm6ea.js → codex-session-runner-8vx4j6a9.js} +16 -15
- package/dist/{commands-n0hshcvs.js → commands-kcpvpcrr.js} +21 -10
- package/dist/{controller-hq5ggpcp.js → controller-ywrmt6yd.js} +5 -4
- package/dist/{controllers-connect-y07cqcvy.js → controllers-connect-kwpvj3rf.js} +45 -38
- package/dist/{cursor-adapter-q8pgv5qa.js → cursor-adapter-cehv1qns.js} +7 -6
- package/dist/{db-hkqvrdcb.js → db-k8ankj20.js} +13 -11
- package/dist/{db-4w7tmt1g.js → db-ww5d7jq3.js} +29 -9
- package/dist/{devin-adapter-xgtj680j.js → devin-adapter-x1022qb1.js} +3 -2
- package/dist/{dispatcher-5sd69y14.js → dispatcher-e8gq5m68.js} +41 -34
- package/dist/{docker-f503k9p0.js → docker-ff6y1190.js} +3 -2
- package/dist/{dsh-adapter-2h8m4cab.js → dsh-adapter-3epjm3af.js} +7 -6
- package/dist/{e2b-66xra4az.js → e2b-8ygwhg0f.js} +4 -3
- package/dist/{e2b-xwhyj8cw.js → e2b-swhnpw3n.js} +4 -3
- package/dist/{event-bus-vjtv82gm.js → event-bus-6axnngdj.js} +3 -2
- package/dist/external-channel-h3359cwn.js +52 -0
- package/dist/{google-generative-ai-7pavrbv7.js → google-generative-ai-g8db7x0f.js} +5 -3
- package/dist/{google-vertex-3f1jk41f.js → google-vertex-r2692q0r.js} +5 -3
- package/dist/{grok-adapter-8m9yqzbq.js → grok-adapter-0d4x7gkx.js} +19 -18
- package/dist/{handlers-s20xr10r.js → handlers-an1gzv21.js} +44 -37
- package/dist/{heartbeat-3k31q4g2.js → heartbeat-6dakeb61.js} +46 -39
- package/dist/{hook-gdn6a2fn.js → hook-6v6cv6d5.js} +12 -24
- package/dist/{hook-ygwerrdh.js → hook-sa5xye4y.js} +13 -12
- package/dist/{http-jdteeg81.js → http-r7f3eky0.js} +1598 -279
- package/dist/index-52j1aja4.js +3465 -0
- package/dist/{index-e0snym4t.js → index-7ep4gzdk.js} +41 -34
- 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-vm3bm9t7.js → index-rmpw9a8q.js} +41 -34
- package/dist/{index-6ksewd5y.js → index-s2wxgzr0.js} +1015 -732
- package/dist/{index-tsszt7g9.js → index-v1v6n7hg.js} +46 -39
- package/dist/index-we27kd31.js +12810 -0
- package/dist/{index-ffqzqgwb.js → index-zk96aq1k.js} +11 -9
- package/dist/{keepalive-jekc896t.js → keepalive-be9vwy5h.js} +13 -11
- package/dist/{lead-4k9s9szx.js → lead-0861cwrf.js} +64 -57
- package/dist/{maintenance-shazch4n.js → maintenance-mzzgc8bn.js} +11 -9
- package/dist/node-c9we95xf.js +1 -1
- package/dist/{node-fkajaq96.js → node-mn1zs7an.js} +2 -1
- package/dist/{oauth-refresh-sweep-5gb36kn8.js → oauth-refresh-sweep-cpdnfwdj.js} +12 -10
- package/dist/{onboard-ydrbnvww.js → onboard-fh8qs4r3.js} +11 -3
- package/dist/{opencode-adapter-5zh0he20.js → opencode-adapter-nm42wwj8.js} +19 -14
- package/dist/{otel-impl-6pf5cv86.js → otel-impl-x6c1c9g4.js} +248 -869
- package/dist/{pi-mono-adapter-ef9wwx9m.js → pi-mono-adapter-9n2ya240.js} +245 -107
- package/dist/{pricing-refresh-s52nsa5q.js → pricing-refresh-qb0rv0ap.js} +13 -11
- package/dist/{rbac-roles-aq3msh4e.js → rbac-roles-1gzdwbh1.js} +12 -10
- package/dist/{rbac-roles-q5gb5e8v.js → rbac-roles-3mraey52.js} +13 -11
- package/dist/{render-v2-pd1rg54q.js → render-v2-agpa45zz.js} +19 -13
- package/dist/{resume-gw4a2c6v.js → resume-x0ydy9s9.js} +27 -21
- package/dist/{runtime-qfry88v2.js → runtime-sa2vr16g.js} +2 -2
- package/dist/{setup-jda1ksdh.js → setup-as9ga0d6.js} +2 -2
- package/dist/{virtual-modules-b3ga2m65.js → virtual-modules-9gkxqrap.js} +7 -6
- package/dist/{work-objects-tb7ycye8.js → work-objects-y5g1y7ev.js} +49 -37
- package/dist/{worker-njjccz5d.js → worker-860c3e38.js} +64 -57
- package/dist/{x-qfmygqqv.js → x-m477fr0h.js} +3 -2
- package/openapi.json +951 -356
- package/package.json +12 -13
- package/src/apps/catalog.generated.json +1309 -53
- package/src/apps/page-validator.ts +93 -31
- package/src/azure-devops/api.ts +94 -18
- package/src/azure-devops/auth.ts +32 -0
- package/src/azure-devops/boards-sync.ts +708 -0
- package/src/azure-devops/handlers.ts +52 -8
- package/src/azure-devops/html.ts +26 -0
- package/src/azure-devops/index.ts +15 -0
- package/src/azure-devops/templates.ts +86 -0
- package/src/azure-devops/types.ts +65 -1
- package/src/be/config-refs.ts +60 -0
- package/src/be/db/page-versions.ts +15 -0
- package/src/be/db/runtime.ts +35 -8
- package/src/be/db/skill-name-resolution.ts +36 -0
- package/src/be/db/tasks/read.ts +230 -0
- package/src/be/db/tasks/write.ts +7 -0
- package/src/be/db.ts +74 -13
- package/src/be/migrations/213_script_connections_sql.sql +79 -0
- package/src/be/migrations/215_swarm_config_export_to_env.sql +34 -0
- package/src/be/migrations/216_task_resolved_effort.sql +5 -0
- package/src/be/migrations/217_chat_sdk_state.sql +104 -0
- package/src/be/model-catalog-store.ts +5 -1
- package/src/be/model-tier-resolution.ts +12 -1
- package/src/be/model-validation.ts +5 -2
- package/src/be/models-catalog.ts +26 -1
- package/src/be/modelsdev-cache.json +198 -63
- package/src/be/modelsdev-cache.ts +9 -1
- package/src/be/script-connections.ts +47 -8
- package/src/be/script-credential-broker.ts +39 -2
- package/src/be/scripts/run-saved.ts +1 -0
- package/src/be/scripts/typecheck.ts +10 -8
- package/src/be/seed/runner.ts +13 -1
- package/src/be/seed/state-db.ts +5 -0
- package/src/be/seed/types.ts +6 -0
- package/src/be/seed-pricing.ts +19 -2
- package/src/be/seed-scripts/catalog/ops-catalog-audit.inline.ts +1 -1
- package/src/be/seed-scripts/catalog/ops-catalog-audit.ts +1 -1
- package/src/be/seed-skills/bundled-files.generated.json +10 -18
- package/src/be/seed-skills/index.ts +107 -3
- package/src/be/sql-connections.ts +118 -0
- 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/profile-sync.ts +5 -3
- package/src/commands/provider-credentials.ts +52 -6
- package/src/commands/runner.ts +196 -56
- package/src/e2b/env.ts +5 -0
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/extensions/contract.ts +45 -0
- package/src/extensions/dispatcher.ts +7 -0
- package/src/extensions/post-bridge.ts +40 -0
- package/src/github/handlers.ts +11 -4
- package/src/heartbeat/templates.ts +8 -5
- package/src/hooks/hook.ts +0 -26
- package/src/http/all-routes.ts +0 -1
- package/src/http/config.ts +36 -17
- package/src/http/core.ts +4 -0
- package/src/http/index.ts +14 -2
- package/src/http/integrations.ts +88 -1
- package/src/http/mcp-oauth.ts +6 -13
- package/src/http/mcp.ts +15 -5
- package/src/http/page-proxy.ts +2 -4
- package/src/http/pages-public.ts +6 -5
- package/src/http/pages.ts +5 -14
- package/src/http/poll.ts +28 -68
- package/src/http/requested-by.ts +54 -0
- package/src/http/route-def.ts +7 -6
- package/src/http/script-connection-proxy.ts +51 -4
- package/src/http/script-connections.ts +5 -2
- package/src/http/scripts.ts +1 -0
- package/src/http/session-cost-recompute.ts +12 -2
- package/src/http/session-data.ts +4 -0
- package/src/http/stats.ts +1 -30
- package/src/http/tasks.ts +61 -5
- package/src/http/utils.ts +14 -0
- package/src/http/webhooks.ts +183 -2
- 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/jira/adf.ts +3 -1
- package/src/kv-reserved-namespaces.ts +2 -0
- package/src/oauth/mcp-wrapper.ts +10 -6
- package/src/otel-impl.ts +2 -1
- 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 +27 -2
- package/src/providers/claude-flag-settings.ts +133 -0
- package/src/providers/claude-managed-adapter.ts +1 -1
- package/src/providers/claude-sdk-session.ts +7 -0
- package/src/providers/claude-session-events.ts +6 -1
- package/src/providers/dsh-adapter.ts +1 -1
- package/src/providers/opencode-adapter.ts +268 -37
- package/src/providers/opencode-billing.ts +86 -0
- package/src/providers/opencode-swarm-plugin.ts +51 -0
- package/src/providers/pi-mono-adapter.ts +398 -153
- package/src/providers/pricing-sources.md +37 -3
- package/src/providers/reasoning-effort.ts +27 -0
- package/src/providers/types.ts +11 -2
- package/src/rbac/legacy-policy.ts +8 -2
- package/src/rbac/permissions.ts +21 -0
- package/src/scheduler/scheduler.ts +4 -2
- package/src/scripts-runtime/api-client.ts +15 -1
- package/src/scripts-runtime/api-types.ts +9 -1
- package/src/scripts-runtime/ctx.ts +1 -1
- package/src/scripts-runtime/loader.ts +19 -7
- package/src/scripts-runtime/mcp-client.ts +30 -19
- package/src/scripts-runtime/sdk-allowlist.ts +8 -6
- package/src/scripts-runtime/swarm-sdk.ts +0 -11
- package/src/scripts-runtime/types/stdlib.d.ts +27 -15
- package/src/scripts-runtime/types/swarm-extension.d.ts +63 -1
- package/src/scripts-runtime/types/swarm-sdk.d.ts +27 -15
- package/src/server.ts +52 -18
- package/src/slack/actions.ts +29 -1
- package/src/slack/app.ts +14 -2
- package/src/slack/assistant.ts +36 -2
- package/src/slack/channel-join.ts +3 -16
- package/src/slack/commands.ts +9 -1
- package/src/slack/event-dedup.ts +1 -1
- package/src/slack/external-channel-egress.ts +113 -0
- package/src/slack/external-channel.ts +309 -0
- package/src/slack/handlers.ts +61 -0
- package/src/slack/render-v2.ts +40 -47
- package/src/slack/work-objects.ts +5 -0
- package/src/tasks/additive-buffer.ts +4 -2
- package/src/tasks/context-key.ts +94 -2
- package/src/tasks/model-pin.ts +67 -0
- package/src/tasks/worker-follow-up.ts +4 -31
- package/src/telemetry.ts +2 -1
- package/src/tests/acp-adapter.test.ts +42 -0
- package/src/tests/api-drain-worker-e2e.test.ts +1 -6
- package/src/tests/apps-catalog-feedback.test.ts +310 -0
- package/src/tests/apps-catalog-inputs.test.ts +70 -0
- package/src/tests/apps-catalog-table.test.ts +113 -0
- package/src/tests/azure-devops-boards-sync.test.ts +584 -0
- package/src/tests/azure-devops-html.test.ts +13 -0
- package/src/tests/azure-devops-webhook-malformed.test.ts +4 -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 +144 -0
- package/src/tests/claude-flag-settings.test.ts +114 -0
- package/src/tests/claude-managed-adapter.test.ts +1 -1
- package/src/tests/claude-sdk-lifecycle.test.ts +148 -1
- package/src/tests/claude-sdk-process.fixture.ts +7 -0
- 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 +51 -0
- package/src/tests/cors-allowlist.test.ts +25 -0
- package/src/tests/create-page-tool.test.ts +18 -0
- package/src/tests/credential-check.test.ts +139 -0
- package/src/tests/credentials.test.ts +21 -0
- package/src/tests/db-runtime.test.ts +61 -0
- package/src/tests/db-task-read-contract.test.ts +20 -0
- package/src/tests/db-task-write-contract.test.ts +10 -0
- package/src/tests/deferred-task-wake.test.ts +52 -4
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/dsh-adapter.test.ts +1 -1
- package/src/tests/enabled-capabilities.test.ts +20 -0
- package/src/tests/entrypoint-api-readiness.test.ts +14 -8
- package/src/tests/entrypoint-config-env-export.test.ts +5 -2
- package/src/tests/entrypoint-export-to-env.test.ts +78 -0
- package/src/tests/explicit-model-validation.test.ts +105 -1
- package/src/tests/extensions-example-route-chat-channel.test.ts +154 -0
- package/src/tests/extensions-post-bridge.test.ts +51 -0
- package/src/tests/fetch-resolved-env.test.ts +128 -0
- package/src/tests/fixtures/azure-boards/workitem.commented.json +89 -0
- package/src/tests/fixtures/azure-boards/workitem.created.json +81 -0
- package/src/tests/fixtures/azure-boards/workitem.deleted.json +70 -0
- package/src/tests/fixtures/azure-boards/workitem.restored.json +82 -0
- package/src/tests/fixtures/azure-boards/workitem.updated.json +130 -0
- package/src/tests/fixtures/catalog-feedback-definition.json.txt +860 -0
- package/src/tests/fixtures/catalog-inputs-definition.json.txt +910 -0
- package/src/tests/fixtures/catalog-table-definition.json.txt +468 -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-identity-refresh.ts +3 -0
- package/src/tests/fixtures/runner-legacy-resume-prompt.ts +122 -0
- package/src/tests/github-handlers.test.ts +35 -0
- package/src/tests/harness-provider-resolution.test.ts +12 -0
- package/src/tests/heartbeat-checklist.test.ts +3 -2
- package/src/tests/hook-runtime-close.test.ts +0 -2
- package/src/tests/http-api-integration.test.ts +0 -25
- package/src/tests/migration-046-budgets.test.ts +17 -16
- package/src/tests/migration-217-chat-sdk-state.test.ts +113 -0
- package/src/tests/model-tier-resolution.test.ts +93 -0
- package/src/tests/models-catalog.test.ts +42 -1
- package/src/tests/node-env-runtime.test.ts +98 -0
- 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 +348 -3
- package/src/tests/opencode-billing.test.ts +28 -0
- package/src/tests/opencode-helmcode-sdk.test.ts +201 -0
- package/src/tests/opencode-shell-env.test.ts +145 -0
- package/src/tests/openrouter-base-url.test.ts +101 -151
- package/src/tests/page-proxy.test.ts +1 -1
- package/src/tests/pages-browser-sdk.test.ts +76 -0
- package/src/tests/pages-feedback.test.ts +1 -1
- package/src/tests/paused-tasks-requested-by.test.ts +102 -0
- package/src/tests/pi-custom-provider-fake-server.test.ts +442 -0
- package/src/tests/pi-mono-adapter.test.ts +385 -5
- package/src/tests/pi-shell-env.test.ts +170 -0
- package/src/tests/prompt-template-remaining.test.ts +35 -0
- package/src/tests/rbac-engine.test.ts +11 -3
- package/src/tests/runner-exit-after-tasks.test.ts +0 -4
- package/src/tests/runner-identity-refresh.test.ts +3 -0
- package/src/tests/runner-legacy-resume-prompt.test.ts +64 -0
- package/src/tests/script-connections-sql.test.ts +675 -0
- package/src/tests/script-connections.test.ts +7 -1
- package/src/tests/scripts-http.test.ts +201 -1
- package/src/tests/scripts-only-gating.test.ts +2 -3
- package/src/tests/scripts-runtime.test.ts +58 -40
- package/src/tests/secret-scrubber.test.ts +86 -0
- package/src/tests/seed-skills-bundled-files.test.ts +45 -53
- package/src/tests/seed-skills-retire.test.ts +182 -0
- package/src/tests/send-task-requester-gate.test.ts +58 -0
- package/src/tests/session-cost-recompute-unit.test.ts +94 -0
- package/src/tests/skill-get-name-resolution.test.ts +137 -0
- package/src/tests/slack-channel-join.test.ts +4 -1
- package/src/tests/slack-external-channel-config.test.ts +65 -0
- package/src/tests/slack-external-channel-egress.test.ts +237 -0
- package/src/tests/slack-external-channel-guard.test.ts +280 -0
- package/src/tests/slack-external-channel-tools.test.ts +173 -0
- package/src/tests/slack-external-channel.test.ts +370 -0
- package/src/tests/slack-render-v2.test.ts +86 -10
- package/src/tests/sonnet-5-5-registry.test.ts +9 -1
- package/src/tests/spec-base-url.test.ts +16 -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/vendored-openapi.test.ts +16 -1
- package/src/tests/vercel-ai-gateway.test.ts +149 -0
- package/src/tests/workflow-swarm-script.test.ts +38 -0
- package/src/tests/workflow-wait-filter.test.ts +5 -3
- package/src/tools/chat-access.ts +41 -0
- package/src/tools/chat-open-dm.ts +59 -0
- package/src/tools/chat-post.ts +74 -0
- package/src/tools/chat-read-thread.ts +100 -0
- package/src/tools/chat-reply.ts +75 -0
- package/src/tools/create-page.ts +3 -13
- package/src/tools/defer-task.ts +17 -0
- package/src/tools/model-catalog/index.ts +2 -1
- package/src/tools/register-chat-channel.ts +139 -0
- package/src/tools/send-task.ts +24 -0
- package/src/tools/skills/skill-get.ts +17 -9
- package/src/tools/slack-post.ts +6 -0
- package/src/tools/slack-read.ts +8 -0
- package/src/tools/slack-reply.ts +6 -0
- package/src/tools/slack-start-thread.ts +6 -0
- package/src/tools/slack-update.ts +6 -0
- package/src/tools/slack-upload-file.ts +6 -0
- package/src/tools/swarm-config/get-config.ts +2 -1
- package/src/tools/swarm-config/list-config.ts +2 -1
- package/src/tools/swarm-config/set-config.ts +16 -3
- package/src/tools/tool-config.ts +8 -6
- package/src/types.ts +12 -2
- package/src/utils/config-env-export.ts +74 -0
- package/src/utils/credentials.test.ts +92 -0
- package/src/utils/credentials.ts +52 -4
- package/src/utils/harness-provider.ts +15 -6
- package/src/utils/node-env.ts +24 -0
- package/src/utils/runtime-model-catalog.ts +11 -1
- package/src/utils/secret-scrubber.ts +71 -2
- package/src/utils/task-tool-manifest.ts +19 -2
- package/src/workflows/executors/swarm-script.ts +1 -0
- package/templates/skills/agent-browser/SKILL.md +1 -1
- package/templates/skills/agent-browser/content.md +1 -1
- package/templates/skills/apps/SKILL.md +749 -0
- package/templates/skills/apps/content.md +60 -22
- 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/qa-loom/config.json +14 -0
- package/templates/skills/qa-loom/content.md +187 -0
- package/templates/skills/qa-loom/files/build-loom.mjs +221 -0
- package/templates/skills/qa-loom/files/run-qa.sh +57 -0
- package/templates/skills/slack-interaction/content.md +4 -0
- 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-9xsmdpdt.js +0 -56377
- package/dist/cli-bd957dgb.js +0 -153
- package/dist/cli-q51yprfq.js +0 -1104
- package/dist/cli-t1ze64h3.js +0 -811
- 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/dist/{azure-openai-responses-ap1gwxgr.js → azure-openai-responses-ygfreq4t.js} +5 -5
- package/dist/{cli-5rqrj7q0.js → cli-pw7ebjdt.js} +0 -0
- package/dist/{openai-codex-responses-33qhkkdy.js → openai-codex-responses-3bk0grnr.js} +4 -4
- package/dist/{openai-completions-s88xm3ns.js → openai-completions-5d4jpakc.js} +9 -9
- package/dist/{openai-responses-7xd2f2e2.js → openai-responses-krb51f59.js} +5 -5
- package/dist/{pi-messages-xszk613n.js → pi-messages-x9gzynm1.js} +3 -3
- /package/src/{artifact-sdk → pages}/browser-sdk.ts +0 -0
- /package/src/{artifact-sdk → pages}/feedback-overlay.ts +0 -0
package/src/http/mcp.ts
CHANGED
|
@@ -7,7 +7,12 @@ import { createServer } from "@/server";
|
|
|
7
7
|
import { parseEnvFlag } from "@/utils/env-flag";
|
|
8
8
|
import { getRequestAuth } from "@/utils/request-auth-context";
|
|
9
9
|
import { resolveScriptsOnlyMode } from "@/utils/scripts-only-mode";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
CHAT_TASK_ONLY_TOOLS,
|
|
12
|
+
isChatSdkTask,
|
|
13
|
+
parseTaskToolManifest,
|
|
14
|
+
selectTaskTools,
|
|
15
|
+
} from "@/utils/task-tool-manifest";
|
|
11
16
|
|
|
12
17
|
export type McpTransportActivity = Record<string, number>;
|
|
13
18
|
export type McpSessionAgents = Record<string, string>;
|
|
@@ -218,18 +223,22 @@ export async function handleMcp(
|
|
|
218
223
|
true,
|
|
219
224
|
);
|
|
220
225
|
let preloadedTools: string[] = [];
|
|
226
|
+
const sourceTaskId = headerValue(req.headers["x-source-task-id"]);
|
|
227
|
+
const sourceTask = sourceTaskId ? await getTaskById(sourceTaskId) : undefined;
|
|
228
|
+
// A session bound to a non-chat task never sees chat-reply. Without the
|
|
229
|
+
// header the tool stays, and resolves the agent's current task itself.
|
|
230
|
+
const excludedTools =
|
|
231
|
+
sourceTask && !isChatSdkTask(sourceTask) ? [...CHAT_TASK_ONLY_TOOLS] : [];
|
|
221
232
|
if (preloadEnabled) {
|
|
222
|
-
const taskId = headerValue(req.headers["x-source-task-id"]);
|
|
223
|
-
const task = taskId ? await getTaskById(taskId) : undefined;
|
|
224
233
|
// Never select a manifest using another agent's task. Session-token
|
|
225
234
|
// identity is also checked above, before any MCP session is created.
|
|
226
|
-
if (
|
|
235
|
+
if (sourceTask?.agentId === agentId) {
|
|
227
236
|
const manifestValue =
|
|
228
237
|
configs.find((config) => config.key === "TASK_TOOL_MANIFESTS")?.value ??
|
|
229
238
|
process.env.TASK_TOOL_MANIFESTS ??
|
|
230
239
|
"{}";
|
|
231
240
|
try {
|
|
232
|
-
preloadedTools = selectTaskTools(parseTaskToolManifest(manifestValue),
|
|
241
|
+
preloadedTools = selectTaskTools(parseTaskToolManifest(manifestValue), sourceTask);
|
|
233
242
|
} catch {
|
|
234
243
|
// An invalid deployment value must not prevent tool discovery.
|
|
235
244
|
// Avoid logging its contents, which may contain misfiled secrets.
|
|
@@ -239,6 +248,7 @@ export async function handleMcp(
|
|
|
239
248
|
}
|
|
240
249
|
const server = await createServer({
|
|
241
250
|
preloadedTools,
|
|
251
|
+
excludedTools,
|
|
242
252
|
// Agent row, then global row, then deployment env. A blank agent row
|
|
243
253
|
// resolves to "unset" for that agent.
|
|
244
254
|
enabledTools:
|
package/src/http/page-proxy.ts
CHANGED
|
@@ -6,10 +6,8 @@ import { route } from "./route-def";
|
|
|
6
6
|
import { jsonError } from "./utils";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* `/@swarm/api/*` proxy.
|
|
10
|
-
*
|
|
11
|
-
* authenticates via the page-session cookie instead of basic-auth + a per-
|
|
12
|
-
* artifact tunnel.
|
|
9
|
+
* `/@swarm/api/*` proxy. Lives on the MAIN API server and authenticates via
|
|
10
|
+
* the page-session cookie.
|
|
13
11
|
*
|
|
14
12
|
* Flow:
|
|
15
13
|
* 1. Browser hits `/@swarm/api/<rest>` from inside an iframe of `/p/:id`.
|
package/src/http/pages-public.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Scope of THIS module (step-3):
|
|
9
9
|
* - `auth_mode === 'public'`: ungated. HTML responses inline-inject the
|
|
10
|
-
* `BROWSER_SDK_JS` constant from `src/
|
|
10
|
+
* `BROWSER_SDK_JS` constant from `src/pages/browser-sdk.ts` (reused
|
|
11
11
|
* verbatim — no token-injection hook on the client). SVG responses serve
|
|
12
12
|
* raw bytes with a restrictive CSP and no injected scripts. JSON responses
|
|
13
13
|
* 302-redirect to the SPA `/pages/:id` route (the JSON renderer lives
|
|
@@ -23,11 +23,12 @@
|
|
|
23
23
|
*/
|
|
24
24
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
25
25
|
import { z } from "zod";
|
|
26
|
-
import { BROWSER_SDK_JS, SWARM_UI_JS } from "../artifact-sdk/browser-sdk";
|
|
27
|
-
import { feedbackOverlayScripts, isPageFeedbackRequested } from "../artifact-sdk/feedback-overlay";
|
|
28
26
|
import { getPage, incrementPageViewCount } from "../be/db";
|
|
27
|
+
import { BROWSER_SDK_JS, SWARM_UI_JS } from "../pages/browser-sdk";
|
|
28
|
+
import { feedbackOverlayScripts, isPageFeedbackRequested } from "../pages/feedback-overlay";
|
|
29
29
|
import { type Page, PageSchema } from "../types";
|
|
30
30
|
import { getAppUrl, getConfiguredAppUrls } from "../utils/constants";
|
|
31
|
+
import { getNodeEnv } from "../utils/node-env";
|
|
31
32
|
import { extractAndVerifyCookie, issuePageSessionCookie } from "../utils/page-session";
|
|
32
33
|
import { scrubSecrets } from "../utils/secret-scrubber";
|
|
33
34
|
import { route } from "./route-def";
|
|
@@ -318,7 +319,7 @@ function buildCsp(): string {
|
|
|
318
319
|
// between Vite ports / portless dev doesn't require restarting the API.
|
|
319
320
|
const configured = getConfiguredAppUrls();
|
|
320
321
|
const devFallbacks =
|
|
321
|
-
|
|
322
|
+
getNodeEnv() === "production"
|
|
322
323
|
? []
|
|
323
324
|
: [
|
|
324
325
|
"http://localhost:5274",
|
|
@@ -459,7 +460,7 @@ function extractPasswordCandidate(
|
|
|
459
460
|
* `SameSite=Lax` (dev) vs `SameSite=None; Secure` (prod).
|
|
460
461
|
*/
|
|
461
462
|
function isLocalhostRequest(req: IncomingMessage): boolean {
|
|
462
|
-
if (
|
|
463
|
+
if (getNodeEnv() === "production") return false;
|
|
463
464
|
// Only emit `SameSite=Lax` (no Secure) when the request comes from the
|
|
464
465
|
// SAME http://localhost origin as the API — Lax cookies don't travel on
|
|
465
466
|
// cross-site fetches, so portless `*.localhost` setups (SPA on https
|
package/src/http/pages.ts
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
updatePage,
|
|
20
20
|
withFavoriteFlags,
|
|
21
21
|
} from "../be/db";
|
|
22
|
+
import { getPageEditCounter } from "../be/db/page-versions";
|
|
22
23
|
import { snapshotPage } from "../pages/version";
|
|
23
24
|
import { resolveTemplate } from "../prompts/resolver";
|
|
24
25
|
// Side-effect import: registers `task.page.feedback`.
|
|
@@ -34,6 +35,7 @@ import {
|
|
|
34
35
|
PageVersionSchema,
|
|
35
36
|
} from "../types";
|
|
36
37
|
import { getAppUrl, getPublicMcpBaseUrl } from "../utils/constants";
|
|
38
|
+
import { getNodeEnv } from "../utils/node-env";
|
|
37
39
|
import { issuePageSessionCookie } from "../utils/page-session";
|
|
38
40
|
import { getRequestAuth } from "../utils/request-auth-context";
|
|
39
41
|
import { resolveHttpFavoriteOwner } from "./favorite-owner";
|
|
@@ -226,7 +228,7 @@ const updatePageRoute = route({
|
|
|
226
228
|
/**
|
|
227
229
|
* POST /api/pages/:id/feedback — turn the element comments collected by the
|
|
228
230
|
* in-page feedback overlay (`?__swarm-feedback`, see
|
|
229
|
-
* `src/
|
|
231
|
+
* `src/pages/feedback-overlay.ts`) into one task for the lead. In the
|
|
230
232
|
* dashboard the SPA calls it with its bearer for the page it shows. A page
|
|
231
233
|
* opened directly reaches it through the `/@swarm/api/*` page proxy, which
|
|
232
234
|
* pins `X-Page-Id` to the cookie's page, so a page can only file feedback on
|
|
@@ -481,17 +483,6 @@ function withShareUrls<T extends { id: string; slug: string }>(
|
|
|
481
483
|
};
|
|
482
484
|
}
|
|
483
485
|
|
|
484
|
-
/**
|
|
485
|
-
* Compute the page's "edit counter" — `MAX(page_versions.version) + 1`. Means
|
|
486
|
-
* "this is the N-th edit since the page was created". After the first PUT the
|
|
487
|
-
* value is 2 (one snapshot row → version 1 → counter becomes 2). This is the
|
|
488
|
-
* value POST and PUT return as `version` on the wire.
|
|
489
|
-
*/
|
|
490
|
-
async function pageEditCounter(pageId: string): Promise<number> {
|
|
491
|
-
const versions = await getPageVersions(pageId);
|
|
492
|
-
return versions.length > 0 ? versions[0]!.version + 1 : 1;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
486
|
/**
|
|
496
487
|
* Viewer and page-supplied text sits inside a `<page_feedback>` block in the
|
|
497
488
|
* task prompt. Defuse any tag of that name so the text cannot close the block
|
|
@@ -536,7 +527,7 @@ function formatFeedbackComments(comments: z.infer<typeof PageFeedbackCommentSche
|
|
|
536
527
|
}
|
|
537
528
|
|
|
538
529
|
function isDevRequest(req: IncomingMessage): boolean {
|
|
539
|
-
if (
|
|
530
|
+
if (getNodeEnv() === "production") return false;
|
|
540
531
|
// We want `SameSite=Lax` only when the request itself comes from the same
|
|
541
532
|
// local-`http://localhost` origin as the API — same-site loads tolerate
|
|
542
533
|
// Lax without Secure. Anything else (including portless `*.localhost`
|
|
@@ -842,7 +833,7 @@ export async function handlePages(
|
|
|
842
833
|
updatePageRoute.respond(res, 200, {
|
|
843
834
|
id: updated.id,
|
|
844
835
|
key: updated.key,
|
|
845
|
-
version: await
|
|
836
|
+
version: await getPageEditCounter(updated.id),
|
|
846
837
|
});
|
|
847
838
|
return true;
|
|
848
839
|
}
|
package/src/http/poll.ts
CHANGED
|
@@ -19,7 +19,6 @@ import {
|
|
|
19
19
|
getTaskAttachments,
|
|
20
20
|
getTaskById,
|
|
21
21
|
getUnassignedTaskIdsForAgent,
|
|
22
|
-
getUserById,
|
|
23
22
|
hasCapacity,
|
|
24
23
|
isExtensionAgent,
|
|
25
24
|
recordBudgetRefusalNotification,
|
|
@@ -27,7 +26,6 @@ import {
|
|
|
27
26
|
updateAgentStatusFromCapacity,
|
|
28
27
|
upsertChannelActivityCursor,
|
|
29
28
|
} from "../be/db";
|
|
30
|
-
import { renderIdentity, resolveIdentity } from "../be/identity";
|
|
31
29
|
import {
|
|
32
30
|
parseModelTierOverridesHeader,
|
|
33
31
|
recordClaimModelResolution,
|
|
@@ -37,19 +35,14 @@ import { poolTaskRunsOnHarness } from "../be/model-validation";
|
|
|
37
35
|
import { getRuntimeInstanceById, touchRuntimeInstance } from "../be/multi-runtime";
|
|
38
36
|
import { hasCapability } from "../server";
|
|
39
37
|
import { fetchChannelActivity } from "../slack/channel-activity";
|
|
38
|
+
import { type TaskModelPinFields, taskModelPin } from "../tasks/model-pin";
|
|
40
39
|
import { emitTaskTelemetry, resolveTriggerSurface } from "../telemetry-trigger";
|
|
41
|
-
import {
|
|
42
|
-
AgentTaskSchema,
|
|
43
|
-
BudgetRefusedTriggerSchema,
|
|
44
|
-
TaskAttachmentSchema,
|
|
45
|
-
UserCommsPrefsSchema,
|
|
46
|
-
UserSchema,
|
|
47
|
-
} from "../types";
|
|
40
|
+
import { AgentTaskSchema, BudgetRefusedTriggerSchema, TaskAttachmentSchema } from "../types";
|
|
48
41
|
import { ensure } from "../utils/business-use";
|
|
49
42
|
import { isLeadChannelMonitoringEnabled } from "../utils/lead-monitor-channels";
|
|
50
43
|
import { isMultiRuntimeEnabled } from "../utils/multi-runtime";
|
|
51
|
-
import { getUserCommsPrefs } from "../utils/requester-comms";
|
|
52
44
|
import { isRuntimePoolsEnabled } from "../utils/runtime-pools-flag";
|
|
45
|
+
import { buildTaskRequestedBy, TaskRequestedBySchema } from "./requested-by";
|
|
53
46
|
import { route, runtimeInstanceHeader } from "./route-def";
|
|
54
47
|
import { jsonError } from "./utils";
|
|
55
48
|
|
|
@@ -100,27 +93,11 @@ const PollTriggerAttachmentSchema = TaskAttachmentSchema.pick({
|
|
|
100
93
|
sizeBytes: true,
|
|
101
94
|
});
|
|
102
95
|
|
|
103
|
-
// Requester projection sent on `task_assigned` triggers — either the resolved
|
|
104
|
-
// user's identity fields, or (when no `requestedByUserId` is recorded) just a
|
|
105
|
-
// rendered `name` for the UNKNOWN-identity sentinel.
|
|
106
|
-
const PollRequestedBySchema = UserSchema.pick({
|
|
107
|
-
id: true,
|
|
108
|
-
name: true,
|
|
109
|
-
email: true,
|
|
110
|
-
role: true,
|
|
111
|
-
notes: true,
|
|
112
|
-
}).extend({
|
|
113
|
-
// Absent for the UNKNOWN-identity sentinel (Slack-only requester, no users row).
|
|
114
|
-
id: z.string().optional(),
|
|
115
|
-
// Structured communication preferences from `users.metadata.comms`.
|
|
116
|
-
comms: UserCommsPrefsSchema.optional(),
|
|
117
|
-
});
|
|
118
|
-
|
|
119
96
|
const PollTaskOfferedTriggerSchema = z.object({
|
|
120
97
|
type: z.literal("task_offered"),
|
|
121
98
|
taskId: z.string(),
|
|
122
99
|
task: AgentTaskSchema,
|
|
123
|
-
requestedBy:
|
|
100
|
+
requestedBy: TaskRequestedBySchema.optional(),
|
|
124
101
|
});
|
|
125
102
|
|
|
126
103
|
const PollTaskAssignedTriggerSchema = z.object({
|
|
@@ -132,7 +109,7 @@ const PollTaskAssignedTriggerSchema = z.object({
|
|
|
132
109
|
task: AgentTaskSchema.extend({
|
|
133
110
|
attachments: z.array(PollTriggerAttachmentSchema),
|
|
134
111
|
}),
|
|
135
|
-
requestedBy:
|
|
112
|
+
requestedBy: TaskRequestedBySchema.optional(),
|
|
136
113
|
});
|
|
137
114
|
|
|
138
115
|
// `budget_refused` reuses the canonical `BudgetRefusedTriggerSchema` entity
|
|
@@ -204,38 +181,6 @@ const pollTriggers = route({
|
|
|
204
181
|
const CHANNEL_ACTIVITY_INTERVAL_MS = 60_000; // Check at most once per 60s
|
|
205
182
|
let lastChannelActivityCheckAt = 0;
|
|
206
183
|
|
|
207
|
-
function getRequesterNotes(notes: string | undefined): string | undefined {
|
|
208
|
-
return typeof notes === "string" && notes.trim().length > 0 ? notes : undefined;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Resolve the requester projection for a task trigger. If the task carries a
|
|
213
|
-
* machine-recorded external id (today: the Slack user field) but no
|
|
214
|
-
* requestedByUserId, render the explicit UNKNOWN sentinel instead of silently
|
|
215
|
-
* omitting the section: never a substituted human (Rule 33 /
|
|
216
|
-
* provenance-or-silence).
|
|
217
|
-
*/
|
|
218
|
-
async function buildTriggerRequestedBy(task: {
|
|
219
|
-
requestedByUserId?: string | null;
|
|
220
|
-
slackUserId?: string | null;
|
|
221
|
-
}): Promise<z.infer<typeof PollRequestedBySchema> | undefined> {
|
|
222
|
-
const user = task.requestedByUserId ? await getUserById(task.requestedByUserId) : undefined;
|
|
223
|
-
if (user) {
|
|
224
|
-
return {
|
|
225
|
-
id: user.id,
|
|
226
|
-
name: user.name,
|
|
227
|
-
email: user.email,
|
|
228
|
-
role: user.role,
|
|
229
|
-
notes: getRequesterNotes(user.notes),
|
|
230
|
-
comms: getUserCommsPrefs(user),
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
if (task.slackUserId) {
|
|
234
|
-
return { name: renderIdentity(await resolveIdentity("slack", task.slackUserId)) };
|
|
235
|
-
}
|
|
236
|
-
return undefined;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
184
|
/**
|
|
240
185
|
* Slim attachment projection for the `task_assigned` poll trigger — just
|
|
241
186
|
* enough (id, name, mimeType, sizeBytes) for the worker to build a one-shot
|
|
@@ -256,16 +201,31 @@ async function attachmentsForTrigger(
|
|
|
256
201
|
/**
|
|
257
202
|
* Claim-time model resolution (runbooks/model-tiers.md). Never blocks a claim:
|
|
258
203
|
* a resolution failure leaves the worker on its own local resolution.
|
|
204
|
+
*
|
|
205
|
+
* The trigger's `model` is the task's own pin, not the column: a re-claimed
|
|
206
|
+
* row carries the model its previous session reported, and the worker treats
|
|
207
|
+
* `task.model` as a pin that beats the agent's current MODEL_OVERRIDE. The
|
|
208
|
+
* previous claim record is overwritten for the same reason.
|
|
259
209
|
*/
|
|
260
210
|
async function claimModelFields(
|
|
261
|
-
task: { id: string
|
|
211
|
+
task: TaskModelPinFields & { id: string },
|
|
262
212
|
agent: Parameters<typeof recordClaimModelResolution>[1],
|
|
263
|
-
): Promise<Awaited<ReturnType<typeof recordClaimModelResolution
|
|
213
|
+
): Promise<Awaited<ReturnType<typeof recordClaimModelResolution>> & { model: string | undefined }> {
|
|
214
|
+
const pin = taskModelPin(task);
|
|
215
|
+
const cleared = {
|
|
216
|
+
model: pin,
|
|
217
|
+
resolvedModel: undefined,
|
|
218
|
+
modelSource: undefined,
|
|
219
|
+
modelAlias: undefined,
|
|
220
|
+
};
|
|
264
221
|
try {
|
|
265
|
-
return
|
|
222
|
+
return {
|
|
223
|
+
...cleared,
|
|
224
|
+
...(await recordClaimModelResolution({ ...task, model: pin }, agent)),
|
|
225
|
+
};
|
|
266
226
|
} catch (error) {
|
|
267
227
|
console.warn(`[/api/poll] model resolution failed for task ${task.id}:`, error);
|
|
268
|
-
return
|
|
228
|
+
return cleared;
|
|
269
229
|
}
|
|
270
230
|
}
|
|
271
231
|
|
|
@@ -422,7 +382,7 @@ export async function handlePoll(
|
|
|
422
382
|
const claimedTask = await claimOfferedTask(firstOfferedTask.id, myAgentId);
|
|
423
383
|
if (claimedTask) {
|
|
424
384
|
await recordRuntimeTask(claimedTask.id);
|
|
425
|
-
const offeredRequestedBy = await
|
|
385
|
+
const offeredRequestedBy = await buildTaskRequestedBy(claimedTask);
|
|
426
386
|
return {
|
|
427
387
|
trigger: {
|
|
428
388
|
type: "task_offered",
|
|
@@ -519,8 +479,8 @@ export async function handlePoll(
|
|
|
519
479
|
});
|
|
520
480
|
|
|
521
481
|
// Resolve requesting user if available (UNKNOWN sentinel handling
|
|
522
|
-
// lives in
|
|
523
|
-
const assignedRequestedBy = await
|
|
482
|
+
// lives in buildTaskRequestedBy).
|
|
483
|
+
const assignedRequestedBy = await buildTaskRequestedBy(pendingTask);
|
|
524
484
|
// The surface that started the whole chain, so the worker can tag
|
|
525
485
|
// its session events with it (workers cannot read the task tree).
|
|
526
486
|
const assignedTriggerSurface = await resolveTriggerSurface(
|
|
@@ -667,7 +627,7 @@ export async function handlePoll(
|
|
|
667
627
|
agentId: myAgentId,
|
|
668
628
|
});
|
|
669
629
|
});
|
|
670
|
-
const claimedRequestedBy = await
|
|
630
|
+
const claimedRequestedBy = await buildTaskRequestedBy(claimed);
|
|
671
631
|
const claimedTriggerSurface = await resolveTriggerSurface(
|
|
672
632
|
claimed.id,
|
|
673
633
|
claimed.source,
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { getUserById } from "../be/db";
|
|
3
|
+
import { renderIdentity, resolveIdentity } from "../be/identity";
|
|
4
|
+
import { UserCommsPrefsSchema, UserSchema } from "../types";
|
|
5
|
+
import { getUserCommsPrefs } from "../utils/requester-comms";
|
|
6
|
+
|
|
7
|
+
// Requester projection sent with a task to the worker that runs it (poll
|
|
8
|
+
// triggers, paused-task resume) — either the resolved user's identity fields,
|
|
9
|
+
// or (when no `requestedByUserId` is recorded) just a rendered `name` for the
|
|
10
|
+
// UNKNOWN-identity sentinel.
|
|
11
|
+
export const TaskRequestedBySchema = UserSchema.pick({
|
|
12
|
+
id: true,
|
|
13
|
+
name: true,
|
|
14
|
+
email: true,
|
|
15
|
+
role: true,
|
|
16
|
+
notes: true,
|
|
17
|
+
}).extend({
|
|
18
|
+
// Absent for the UNKNOWN-identity sentinel (Slack-only requester, no users row).
|
|
19
|
+
id: z.string().optional(),
|
|
20
|
+
// Structured communication preferences from `users.metadata.comms`.
|
|
21
|
+
comms: UserCommsPrefsSchema.optional(),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
function getRequesterNotes(notes: string | undefined): string | undefined {
|
|
25
|
+
return typeof notes === "string" && notes.trim().length > 0 ? notes : undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Resolve the requester projection for a task. If the task carries a
|
|
30
|
+
* machine-recorded external id (today: the Slack user field) but no
|
|
31
|
+
* requestedByUserId, render the explicit UNKNOWN sentinel instead of silently
|
|
32
|
+
* omitting the section: never a substituted human (Rule 33 /
|
|
33
|
+
* provenance-or-silence).
|
|
34
|
+
*/
|
|
35
|
+
export async function buildTaskRequestedBy(task: {
|
|
36
|
+
requestedByUserId?: string | null;
|
|
37
|
+
slackUserId?: string | null;
|
|
38
|
+
}): Promise<z.infer<typeof TaskRequestedBySchema> | undefined> {
|
|
39
|
+
const user = task.requestedByUserId ? await getUserById(task.requestedByUserId) : undefined;
|
|
40
|
+
if (user) {
|
|
41
|
+
return {
|
|
42
|
+
id: user.id,
|
|
43
|
+
name: user.name,
|
|
44
|
+
email: user.email,
|
|
45
|
+
role: user.role,
|
|
46
|
+
notes: getRequesterNotes(user.notes),
|
|
47
|
+
comms: getUserCommsPrefs(user),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (task.slackUserId) {
|
|
51
|
+
return { name: renderIdentity(await resolveIdentity("slack", task.slackUserId)) };
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
package/src/http/route-def.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import type { PermissionVerb } from "../rbac";
|
|
4
|
+
import { getNodeEnv } from "../utils/node-env";
|
|
4
5
|
import { scrubSecrets } from "../utils/secret-scrubber";
|
|
5
6
|
import { jsonError, matchRoute, parseBody, RequestBodyTooLargeError } from "./utils";
|
|
6
7
|
|
|
@@ -310,13 +311,13 @@ export function describeRequestRoute(
|
|
|
310
311
|
* endpoint down outside tests (2026-08-18 incident — a single priority='high'
|
|
311
312
|
* row 500'd every task listing). When the gate is on outside tests
|
|
312
313
|
* (NODE_ENV=development, or VALIDATE_HTTP_RESPONSES=true), `respond()` logs
|
|
313
|
-
* the violation and sends the response anyway.
|
|
314
|
-
*
|
|
315
|
-
*
|
|
314
|
+
* the violation and sends the response anyway. NODE_ENV is read at runtime
|
|
315
|
+
* (`getNodeEnv`), so the compiled binary with NODE_ENV unset leaves the gate
|
|
316
|
+
* off unless VALIDATE_HTTP_RESPONSES=true.
|
|
316
317
|
*/
|
|
317
318
|
const VALIDATE_RESPONSES =
|
|
318
|
-
|
|
319
|
-
|
|
319
|
+
getNodeEnv() === "test" ||
|
|
320
|
+
getNodeEnv() === "development" ||
|
|
320
321
|
process.env.VALIDATE_HTTP_RESPONSES === "true";
|
|
321
322
|
|
|
322
323
|
export function route<
|
|
@@ -343,7 +344,7 @@ export function route<
|
|
|
343
344
|
result.error.issues
|
|
344
345
|
.map((i) => `${i.path.join(".") || "<root>"}: ${i.message}`)
|
|
345
346
|
.join("; ");
|
|
346
|
-
if (
|
|
347
|
+
if (getNodeEnv() === "test") {
|
|
347
348
|
throw new Error(detail);
|
|
348
349
|
}
|
|
349
350
|
console.error(`[route-def] ${scrubSecrets(detail)}`);
|
|
@@ -3,7 +3,9 @@ import { z } from "zod";
|
|
|
3
3
|
import { getAgentById } from "@/be/db";
|
|
4
4
|
import { callMcpServerTool } from "@/be/mcp-proxy";
|
|
5
5
|
import { getScriptConnectionById } from "@/be/script-connections";
|
|
6
|
+
import { resolveSqlDsn, runReadOnlyQuery } from "@/be/sql-connections";
|
|
6
7
|
import { can } from "@/rbac";
|
|
8
|
+
import { scrubSecrets } from "@/utils/secret-scrubber";
|
|
7
9
|
import { route } from "./route-def";
|
|
8
10
|
import { jsonError } from "./utils";
|
|
9
11
|
|
|
@@ -51,6 +53,37 @@ const mcpCallRoute = route({
|
|
|
51
53
|
},
|
|
52
54
|
});
|
|
53
55
|
|
|
56
|
+
const SqlQueryEnvelopeSchema = z.union([
|
|
57
|
+
z.object({
|
|
58
|
+
ok: z.literal(true),
|
|
59
|
+
rows: z.array(z.record(z.string(), z.unknown())),
|
|
60
|
+
rowCount: z.number(),
|
|
61
|
+
truncated: z.boolean(),
|
|
62
|
+
}),
|
|
63
|
+
z.object({ ok: z.literal(false), error: z.string() }),
|
|
64
|
+
]);
|
|
65
|
+
|
|
66
|
+
const sqlQueryRoute = route({
|
|
67
|
+
method: "post",
|
|
68
|
+
path: "/api/script-connections/{id}/sql-query",
|
|
69
|
+
pattern: ["api", "script-connections", null, "sql-query"],
|
|
70
|
+
summary: "Run one read-only query on a SQL script connection",
|
|
71
|
+
tags: ["Script Connections"],
|
|
72
|
+
auth: { apiKey: true, agentId: true },
|
|
73
|
+
rbac: { permission: "script-connection.invoke" },
|
|
74
|
+
params: z.object({ id: z.string().uuid() }),
|
|
75
|
+
body: z.object({
|
|
76
|
+
sql: z.string().min(1).max(100_000),
|
|
77
|
+
params: z.array(z.unknown()).max(100).optional(),
|
|
78
|
+
}),
|
|
79
|
+
responses: {
|
|
80
|
+
200: { description: "Query rows or a scrubbed error", schema: SqlQueryEnvelopeSchema },
|
|
81
|
+
400: { description: "Invalid SQL connection or request" },
|
|
82
|
+
403: { description: "Not allowed to invoke this SQL connection" },
|
|
83
|
+
404: { description: "Script connection or agent not found" },
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
|
|
54
87
|
function connectionScopeMatches(
|
|
55
88
|
connection: { scope: string; scopeId: string | null },
|
|
56
89
|
agentId: string,
|
|
@@ -75,9 +108,12 @@ export async function handleScriptConnectionProxy(
|
|
|
75
108
|
queryParams: URLSearchParams,
|
|
76
109
|
agentId: string | undefined,
|
|
77
110
|
): Promise<boolean> {
|
|
78
|
-
|
|
111
|
+
const isSql = sqlQueryRoute.match(req.method, pathSegments);
|
|
112
|
+
if (!isSql && !mcpCallRoute.match(req.method, pathSegments)) return false;
|
|
79
113
|
|
|
80
|
-
const parsed =
|
|
114
|
+
const parsed = isSql
|
|
115
|
+
? await sqlQueryRoute.parse(req, res, pathSegments, queryParams)
|
|
116
|
+
: await mcpCallRoute.parse(req, res, pathSegments, queryParams);
|
|
81
117
|
if (!parsed) return true;
|
|
82
118
|
|
|
83
119
|
if (!agentId) {
|
|
@@ -111,14 +147,25 @@ export async function handleScriptConnectionProxy(
|
|
|
111
147
|
jsonError(res, "Script connection is disabled", 403);
|
|
112
148
|
return true;
|
|
113
149
|
}
|
|
114
|
-
if (connection.kind !== "mcp") {
|
|
115
|
-
jsonError(res,
|
|
150
|
+
if (connection.kind !== (isSql ? "sql" : "mcp")) {
|
|
151
|
+
jsonError(res, `Script connection is not ${isSql ? "a SQL" : "an MCP"} connection`, 400);
|
|
116
152
|
return true;
|
|
117
153
|
}
|
|
118
154
|
if (!connectionScopeMatches(connection, agentId, syntheticPrincipal)) {
|
|
119
155
|
jsonError(res, "Script connection is not available to this agent", 403);
|
|
120
156
|
return true;
|
|
121
157
|
}
|
|
158
|
+
if ("sql" in parsed.body) {
|
|
159
|
+
try {
|
|
160
|
+
const dsn = await resolveSqlDsn(connection, agentId);
|
|
161
|
+
const result = await runReadOnlyQuery(dsn, parsed.body.sql, parsed.body.params);
|
|
162
|
+
sqlQueryRoute.respond(res, 200, { ok: true, ...result });
|
|
163
|
+
} catch (err) {
|
|
164
|
+
const error = scrubSecrets(err instanceof Error ? err.message : String(err));
|
|
165
|
+
sqlQueryRoute.respond(res, 200, { ok: false, error });
|
|
166
|
+
}
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
122
169
|
if (!connection.mcpServerId) {
|
|
123
170
|
jsonError(res, "Script MCP connection is missing mcpServerId", 400);
|
|
124
171
|
return true;
|
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
type ScriptConnectionKind,
|
|
39
39
|
type ScriptConnectionRecord,
|
|
40
40
|
type ScriptCredentialBindingRecord,
|
|
41
|
+
SqlDsnInputSchema,
|
|
41
42
|
setScriptConnectionEnabled,
|
|
42
43
|
upsertCredentialBinding,
|
|
43
44
|
upsertScriptConnection,
|
|
@@ -80,7 +81,7 @@ const providerSchema = z
|
|
|
80
81
|
.regex(/^[A-Za-z0-9_-]+$/);
|
|
81
82
|
|
|
82
83
|
const scopeSchema = z.enum(["global", "agent", "repo"]);
|
|
83
|
-
const connectionKindSchema = z.enum(["openapi", "graphql", "mcp"]);
|
|
84
|
+
const connectionKindSchema = z.enum(["openapi", "graphql", "mcp", "sql"]);
|
|
84
85
|
|
|
85
86
|
const idParamsSchema = z.object({ id: z.string().uuid() });
|
|
86
87
|
const providerParamsSchema = z.object({ provider: providerSchema });
|
|
@@ -133,6 +134,7 @@ const upsertConnectionBodySchema = z.discriminatedUnion("kind", [
|
|
|
133
134
|
kind: z.literal("mcp"),
|
|
134
135
|
mcpServerId: z.string().uuid(),
|
|
135
136
|
}),
|
|
137
|
+
connectionBaseBodySchema.extend({ kind: z.literal("sql"), dsn: SqlDsnInputSchema.optional() }),
|
|
136
138
|
]);
|
|
137
139
|
|
|
138
140
|
const disableConnectionBodySchema = z.object({ enabled: z.boolean() });
|
|
@@ -232,7 +234,7 @@ const connectionAuthTypeResponseSchema = z.enum(["none", "bearer", "header", "qu
|
|
|
232
234
|
// value (never written via this handler's upsert path, but readable via the
|
|
233
235
|
// list/get routes for rows written elsewhere) — wider than the HTTP-body-only
|
|
234
236
|
// `connectionKindSchema` above.
|
|
235
|
-
const connectionRecordKindSchema = z.enum(["raw", "openapi", "mcp", "graphql"]);
|
|
237
|
+
const connectionRecordKindSchema = z.enum(["raw", "openapi", "mcp", "graphql", "sql"]);
|
|
236
238
|
|
|
237
239
|
const bindingSummarySchema = z.object({
|
|
238
240
|
id: z.string(),
|
|
@@ -2062,6 +2064,7 @@ export async function handleScriptConnections(
|
|
|
2062
2064
|
? existingOpenapiConnection?.openapiSpecFetchedAt
|
|
2063
2065
|
: undefined,
|
|
2064
2066
|
mcpServerId: parsed.body.kind === "mcp" ? parsed.body.mcpServerId : null,
|
|
2067
|
+
sqlDsn: parsed.body.kind === "sql" ? parsed.body.dsn : undefined,
|
|
2065
2068
|
enabled,
|
|
2066
2069
|
agentId,
|
|
2067
2070
|
userId,
|
package/src/http/scripts.ts
CHANGED
|
@@ -765,6 +765,7 @@ export async function handleScripts(
|
|
|
765
765
|
runtimeInstanceId,
|
|
766
766
|
egressSecrets: credentials.egressSecrets,
|
|
767
767
|
failedBindings: credentials.failedBindings,
|
|
768
|
+
userConfig: credentials.userConfig,
|
|
768
769
|
apiConnections: getScriptApiConnectionDescriptors({ agentId: agent.id }),
|
|
769
770
|
mcpConnections: getScriptMcpConnectionDescriptors({ agentId: agent.id }),
|
|
770
771
|
});
|
|
@@ -31,6 +31,8 @@ interface SessionCostRecomputeInput {
|
|
|
31
31
|
provider?: PricingProvider;
|
|
32
32
|
model: string;
|
|
33
33
|
harnessCostUsd: number;
|
|
34
|
+
/** `harnessCostUsd` is the provider's bill, not a token estimate. */
|
|
35
|
+
harnessCostBilled?: boolean;
|
|
34
36
|
inputTokens: number;
|
|
35
37
|
outputTokens: number;
|
|
36
38
|
cacheReadTokens: number;
|
|
@@ -187,8 +189,16 @@ export async function recomputeSessionCost(
|
|
|
187
189
|
// every request was billed through Amp, so it wins over the token price,
|
|
188
190
|
// which still fills the per-model breakdown. Grok's `costUsdTicks` is the
|
|
189
191
|
// same kind of report: what xAI billed, long-context tiers included.
|
|
190
|
-
|
|
191
|
-
|
|
192
|
+
// opencode on OpenRouter sends OpenRouter's `usage.cost` sum, flagged as
|
|
193
|
+
// billed: the routed endpoint can cost many times the list price.
|
|
194
|
+
if (
|
|
195
|
+
(input.provider === "amp" || input.provider === "grok" || input.harnessCostBilled) &&
|
|
196
|
+
input.harnessCostUsd > 0
|
|
197
|
+
) {
|
|
198
|
+
const priced = await recomputeSessionCost(
|
|
199
|
+
{ ...input, harnessCostUsd: 0, harnessCostBilled: false },
|
|
200
|
+
lookupRate,
|
|
201
|
+
);
|
|
192
202
|
return {
|
|
193
203
|
totalCostUsd: input.harnessCostUsd,
|
|
194
204
|
costSource: "harness",
|
package/src/http/session-data.ts
CHANGED
|
@@ -179,6 +179,9 @@ const createSessionCostRoute = route({
|
|
|
179
179
|
sessionId: z.string().min(1),
|
|
180
180
|
agentId: z.string().min(1),
|
|
181
181
|
totalCostUsd: z.number(),
|
|
182
|
+
// The adapter's totalCostUsd is the provider's bill (e.g. OpenRouter's
|
|
183
|
+
// `usage.cost`), so it wins over the pricing table.
|
|
184
|
+
harnessCostBilled: z.boolean().optional(),
|
|
182
185
|
taskId: z.string().optional(),
|
|
183
186
|
// Phase 3: non-negative — the recompute no longer clamps, so negative
|
|
184
187
|
// token counts must be rejected at the wire instead of pricing below $0.
|
|
@@ -397,6 +400,7 @@ export async function handleSessionData(
|
|
|
397
400
|
provider: parsed.body.provider,
|
|
398
401
|
model,
|
|
399
402
|
harnessCostUsd: parsed.body.totalCostUsd,
|
|
403
|
+
harnessCostBilled: parsed.body.harnessCostBilled,
|
|
400
404
|
inputTokens,
|
|
401
405
|
outputTokens,
|
|
402
406
|
cacheReadTokens: cachedInputTokens,
|