@desplega.ai/agent-swarm 1.173.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{acp-adapter-ccm9xrep.js → acp-adapter-kr4abrsk.js} +17 -16
- package/dist/{actions-sgeyv08k.js → actions-eh4g6w2r.js} +39 -34
- package/dist/{amp-adapter-gpxp3kc9.js → amp-adapter-7h7wdt8q.js} +8 -7
- package/dist/{app-km9qnv2f.js → app-7k8s9w5p.js} +18 -14
- package/dist/{assistant-5r8tcvvd.js → assistant-wswt2y7p.js} +40 -35
- package/dist/{boot-reembed-16rv9843.js → boot-reembed-9apm9qw6.js} +10 -9
- package/dist/{boot-reembed-swwzdgd7.js → boot-reembed-whzbdthj.js} +10 -9
- package/dist/{boot-scrub-sweep-zkr6vkqb.js → boot-scrub-sweep-ndzmvmgt.js} +11 -10
- package/dist/{boot-seeds-d8sp49t2.js → boot-seeds-e9sta0yt.js} +16 -13
- package/dist/{claude-adapter-yzzzh8jp.js → claude-adapter-4mqd3r6r.js} +19 -18
- package/dist/{claude-managed-adapter-xgxhqrt2.js → claude-managed-adapter-9wswb52m.js} +7 -5
- package/dist/{claude-managed-setup-bnmjarjc.js → claude-managed-setup-tpe1effx.js} +6 -4
- package/dist/{claude-sdk-session-4whxere4.js → claude-sdk-session-q49ghmh9.js} +12402 -11850
- package/dist/{cli-ne7smj1b.js → cli-01xvj1rq.js} +10 -10
- package/dist/{cli-nwrdx840.js → cli-035vekcw.js} +3 -3
- package/dist/{cli-9xf7fdtp.js → cli-0pd8hzhc.js} +29 -6
- package/dist/{cli-tmhsxhb8.js → cli-0veb92a3.js} +28 -6
- package/dist/{cli-c3rk1v0x.js → cli-10jesr25.js} +3 -3
- package/dist/{cli-6thk4jz3.js → cli-1y7yywmj.js} +6 -6
- package/dist/{cli-6r2f7edk.js → cli-20krdhz5.js} +47 -19
- package/dist/{cli-rfs4vddv.js → cli-29rja109.js} +39 -20
- package/dist/cli-2m1ktfzz.js +17795 -0
- package/dist/{cli-na2pxe4f.js → cli-2pwa0q4d.js} +15 -4
- package/dist/{cli-7a93cv8c.js → cli-4mengw46.js} +441 -43
- package/dist/{cli-94b48jhk.js → cli-5twdke5x.js} +16 -14
- package/dist/{cli-7a0sde2x.js → cli-6cagzc96.js} +9 -0
- package/dist/cli-6cvfxs8r.js +47 -0
- package/dist/{cli-w1apjecb.js → cli-711vwbxn.js} +2 -2
- package/dist/{cli-r4fqfpwf.js → cli-78vv7446.js} +50 -27
- package/dist/{cli-srqq3gc5.js → cli-7n6s5man.js} +5 -5
- package/dist/{cli-m89t9jdf.js → cli-7x0g4sn1.js} +15416 -16024
- package/dist/{cli-2bw3w47j.js → cli-8wwxw8a3.js} +1 -1
- package/dist/{cli-yatxqynm.js → cli-9ha6hs2d.js} +1 -290
- package/dist/{cli-ywm1azg9.js → cli-9jzdj90z.js} +8 -4
- package/dist/cli-9traj3dn.js +638 -0
- package/dist/{cli-smcrrmhy.js → cli-agzebbd5.js} +9 -9
- package/dist/{cli-c5km5znr.js → cli-c8r3kzzb.js} +1 -1
- package/dist/cli-cvv9k0hh.js +10143 -0
- package/dist/{cli-anzdk0z4.js → cli-d9286m9h.js} +2 -2
- package/dist/cli-e3g5mbkz.js +776 -0
- package/dist/cli-f527ce74.js +781 -0
- package/dist/{cli-gmdajfw5.js → cli-gxzkg1de.js} +5020 -1729
- package/dist/{cli-451ks6aw.js → cli-h1arqn31.js} +1 -1
- package/dist/cli-hjwmhcg1.js +10622 -0
- package/dist/{cli-jpc4r224.js → cli-hqr88ew6.js} +19 -3
- package/dist/{cli-h7k0xtry.js → cli-hvqbtna4.js} +4 -1256
- package/dist/{cli-cd0hv9nh.js → cli-j67mh2vg.js} +2 -2
- package/dist/cli-jta2eyzc.js +619 -0
- package/dist/{cli-wd0hz6ah.js → cli-kbrt8gqa.js} +9 -91
- package/dist/cli-kceah1eh.js +92 -0
- package/dist/{cli-d603qf86.js → cli-ksphgh48.js} +10 -8
- package/dist/{cli-3nxdmjrc.js → cli-mgz4nbgg.js} +4 -4
- package/dist/{cli-saqyage0.js → cli-mz5as5ha.js} +179 -27
- package/dist/cli-n2wq4j7m.js +6985 -0
- package/dist/{cli-ppy4nnnh.js → cli-n8ndw8j7.js} +2 -2
- package/dist/{cli-1w0vj3gw.js → cli-naqzr17j.js} +7 -7
- package/dist/{cli-0a7mm3zp.js → cli-nm1p8jfr.js} +25 -21
- package/dist/{cli-wrh08zjd.js → cli-p3zbc1rw.js} +5 -18
- package/dist/{cli-hsem16hb.js → cli-pe05f8yk.js} +29 -6
- package/dist/{cli-5wpw4rab.js → cli-qmzsbkvy.js} +5 -5
- package/dist/{cli-bv2etw2y.js → cli-qnm4e9e4.js} +99 -590
- package/dist/{cli-1x7da0de.js → cli-qqy4q48b.js} +8 -8
- package/dist/{cli-r44t9wqc.js → cli-r106725k.js} +2 -2
- package/dist/{cli-5er5gjbm.js → cli-r35rw5de.js} +15 -15
- package/dist/{cli-v3zhnxsb.js → cli-r610p9bs.js} +2 -2
- package/dist/{cli-7jykv7ef.js → cli-rc6pf5kv.js} +1 -1
- package/dist/{cli-x119kdba.js → cli-rdxm77k3.js} +1 -1
- package/dist/cli-rhs9pz33.js +294 -0
- package/dist/{cli-0p3enk35.js → cli-s69nfyeh.js} +9 -1
- package/dist/{cli-0kxf81jj.js → cli-sawv63e8.js} +2 -2
- package/dist/{cli-e3pmyt39.js → cli-sg19gbf0.js} +1 -1
- package/dist/{cli-d161g69q.js → cli-shk7r5cs.js} +57 -6
- package/dist/{cli-7pz3b7x5.js → cli-sksetb2q.js} +36 -1
- package/dist/{cli-014vns2y.js → cli-stfs0k5s.js} +208 -525
- package/dist/{cli-db3m6zdt.js → cli-t02z59f2.js} +1 -1
- package/dist/{cli-b07hr3bt.js → cli-txqb9s38.js} +2 -2
- package/dist/{cli-s8z6x4b4.js → cli-v7g551te.js} +225 -113
- package/dist/{cli-rfz2ve55.js → cli-vgqzp4em.js} +3341 -955
- package/dist/{cli-7avy4v8w.js → cli-w1q7h2r2.js} +5 -3
- package/dist/{cli-rcp5aqdw.js → cli-w7m81tb7.js} +1 -1
- package/dist/cli-wfnemr1t.js +86 -0
- package/dist/cli-wmgrkscj.js +23254 -0
- package/dist/{cli-qm1448f9.js → cli-wms6x9db.js} +8 -8
- package/dist/{cli-tadfgd0t.js → cli-x1r545x8.js} +33 -4
- package/dist/{cli-xc7p7s3g.js → cli-xczsgshn.js} +6 -6
- package/dist/{cli-fmhj1ehj.js → cli-y3167wt1.js} +882 -271
- package/dist/{cli-r0byd61h.js → cli-yjmqpxt3.js} +17 -16
- package/dist/{cli-81r1wjyv.js → cli-z780ec7c.js} +1 -1
- package/dist/{cli-phcqp1b9.js → cli-zm9t2vde.js} +2 -2
- package/dist/cli.js +34 -49
- package/dist/{codex-adapter-r3dn4fp5.js → codex-adapter-4z16v9n0.js} +15 -14
- package/dist/{codex-login-841kv5fk.js → codex-login-pkepy2j1.js} +3 -2
- package/dist/{codex-session-runner-fwt793h4.js → codex-session-runner-rvms9y2x.js} +15 -14
- package/dist/{commands-z8fq9qna.js → commands-2a9rtyat.js} +11 -10
- package/dist/{controller-hq5ggpcp.js → controller-3k3madbn.js} +5 -4
- package/dist/{controllers-connect-3mf2btyj.js → controllers-connect-v9yn8zb9.js} +42 -37
- package/dist/{cursor-adapter-q8pgv5qa.js → cursor-adapter-98gbhyzn.js} +7 -6
- package/dist/{db-qfpq4bt7.js → db-eywwsnjw.js} +30 -9
- package/dist/{db-yyhqqrbf.js → db-ncypk9np.js} +12 -11
- package/dist/{devin-adapter-xgtj680j.js → devin-adapter-zeq8b9gz.js} +3 -2
- package/dist/{dispatcher-x0x7qfcq.js → dispatcher-w94kc575.js} +38 -33
- package/dist/{docker-f503k9p0.js → docker-b1e4cqab.js} +3 -2
- package/dist/{dsh-adapter-34etvb5m.js → dsh-adapter-47ztej36.js} +7 -6
- package/dist/{e2b-66xra4az.js → e2b-bbt8ey45.js} +4 -3
- package/dist/{e2b-xwhyj8cw.js → e2b-dp804sen.js} +4 -3
- package/dist/{event-bus-vjtv82gm.js → event-bus-j2jdg3qb.js} +3 -2
- package/dist/{external-channel-h1fjecwe.js → external-channel-zrkg8ar9.js} +11 -10
- package/dist/{google-generative-ai-szdxbpqg.js → google-generative-ai-g8db7x0f.js} +4 -2
- package/dist/{google-vertex-qzw3p36d.js → google-vertex-r2692q0r.js} +4 -2
- package/dist/{grok-adapter-e7mr287e.js → grok-adapter-s9ncp9vy.js} +18 -17
- package/dist/{handlers-znb9n7d4.js → handlers-kqftvjgw.js} +41 -36
- package/dist/{heartbeat-bm4qkn8d.js → heartbeat-9q96p8v9.js} +43 -38
- package/dist/{hook-254r4mza.js → hook-56qe10dp.js} +11 -23
- package/dist/{hook-xaa3q80s.js → hook-yy6rw42k.js} +12 -11
- package/dist/{http-3dgrgp8y.js → http-r2dnb8f8.js} +2411 -275
- package/dist/{index-fx3jqq1e.js → index-3gtmkhc6.js} +38 -33
- package/dist/{index-bdsqa799.js → index-3k5hkz7r.js} +43 -38
- package/dist/index-52j1aja4.js +3465 -0
- package/dist/{index-g2byveez.js → index-5e101240.js} +38 -33
- package/dist/{index-2ybk7khy.js → index-5wyfjc2c.js} +845 -683
- package/dist/index-9gw58n4k.js +193 -0
- package/dist/index-9j87mcm8.js +56305 -0
- package/dist/index-bsmcresk.js +79424 -0
- package/dist/index-mh85qhec.js +475 -0
- package/dist/{index-jd7sejm2.js → index-vxvhxxbn.js} +10 -9
- package/dist/index-we27kd31.js +12810 -0
- package/dist/{keepalive-6ywhpja1.js → keepalive-psj6j54c.js} +12 -11
- package/dist/{lead-cw45n21s.js → lead-hajz4688.js} +61 -56
- package/dist/{maintenance-ss1a1ge9.js → maintenance-ps02sb5m.js} +10 -9
- package/dist/{node-fkajaq96.js → node-mwb5r697.js} +2 -1
- package/dist/{oauth-refresh-sweep-3c6f8vyy.js → oauth-refresh-sweep-0284h93b.js} +11 -10
- package/dist/{onboard-8hzdhc1k.js → onboard-rbf6bg01.js} +11 -3
- package/dist/{opencode-adapter-qbchbcfz.js → opencode-adapter-13dcatgq.js} +18 -13
- package/dist/{otel-impl-e6mnvc4v.js → otel-impl-zvqcbhp7.js} +242 -868
- package/dist/{pi-mono-adapter-3c9g9snh.js → pi-mono-adapter-xxy47q4v.js} +244 -106
- package/dist/{pricing-refresh-gedkjdw0.js → pricing-refresh-kefx9v3t.js} +12 -11
- package/dist/{rbac-roles-dt4vh1yy.js → rbac-roles-3m36rxm2.js} +11 -10
- package/dist/{rbac-roles-eszdg6pm.js → rbac-roles-z76xv0hq.js} +12 -11
- package/dist/{render-v2-907cy6sa.js → render-v2-wz73bs25.js} +18 -14
- package/dist/{resume-42stcaz0.js → resume-k4wzge8w.js} +25 -21
- package/dist/{runtime-qfry88v2.js → runtime-sa2vr16g.js} +2 -2
- package/dist/{setup-adbc1c10.js → setup-2fp2pypp.js} +2 -2
- package/dist/{virtual-modules-rtmt494q.js → virtual-modules-9gkxqrap.js} +6 -5
- package/dist/{work-objects-wpj8ck95.js → work-objects-6k0ywz3m.js} +41 -36
- package/dist/{worker-bzdra9rf.js → worker-5v3rxsdh.js} +61 -56
- package/dist/{x-qfmygqqv.js → x-4mtdeym1.js} +3 -2
- package/openapi.json +1279 -329
- package/package.json +15 -15
- package/src/agentmail/handlers.ts +21 -11
- package/src/apps/definition.ts +16 -0
- package/src/azure-devops/api.ts +52 -2
- package/src/azure-devops/boards-sync.ts +50 -20
- package/src/azure-devops/index.ts +1 -0
- package/src/azure-devops/outbound.ts +95 -0
- package/src/be/boot-seeds.ts +11 -0
- package/src/be/config-refs.ts +60 -0
- package/src/be/db/model-profiles.ts +315 -0
- package/src/be/db/page-versions.ts +15 -0
- package/src/be/db/runtime.ts +35 -8
- package/src/be/db/skill-name-resolution.ts +36 -0
- package/src/be/db/tasks/read.ts +237 -0
- package/src/be/db/tasks/write.ts +7 -0
- package/src/be/db.ts +87 -36
- package/src/be/migrations/215_swarm_config_export_to_env.sql +34 -0
- package/src/be/migrations/216_task_resolved_effort.sql +5 -0
- package/src/be/migrations/217_chat_sdk_state.sql +104 -0
- package/src/be/migrations/219_relabel_pi_context_compactions.sql +22 -0
- package/src/be/migrations/220_model_profiles.sql +137 -0
- package/src/be/model-catalog-store.ts +5 -1
- package/src/be/model-tier-resolution.ts +155 -10
- package/src/be/model-validation.ts +12 -2
- package/src/be/models-catalog.ts +26 -1
- package/src/be/modelsdev-cache.json +198 -63
- package/src/be/modelsdev-cache.ts +9 -1
- package/src/be/script-credential-broker.ts +39 -2
- package/src/be/scripts/run-saved.ts +1 -0
- package/src/be/scripts/typecheck.ts +22 -8
- package/src/be/secret-registry.ts +6 -2
- package/src/be/seed/runner.ts +13 -1
- package/src/be/seed/state-db.ts +5 -0
- package/src/be/seed/types.ts +6 -0
- package/src/be/seed-model-profiles.ts +41 -0
- package/src/be/seed-pricing.ts +19 -2
- package/src/be/seed-scripts/catalog/heartbeat-jev-gate.ts +368 -0
- package/src/be/seed-scripts/catalog/ops-catalog-audit.inline.ts +1 -1
- package/src/be/seed-scripts/catalog/ops-catalog-audit.ts +1 -1
- package/src/be/seed-scripts/index.ts +9 -0
- package/src/be/seed-skills/bundled-files.generated.json +0 -18
- package/src/be/seed-skills/index.ts +104 -3
- package/src/be/swarm-config-guard.ts +53 -0
- package/src/cli.tsx +0 -16
- package/src/commands/onboard/non-interactive.ts +4 -2
- package/src/commands/provider-credentials.ts +52 -6
- package/src/commands/runner.ts +193 -71
- package/src/e2b/env.ts +5 -0
- package/src/extensions/catalog.generated.json +17 -0
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/extensions/contract.ts +45 -0
- package/src/extensions/dispatcher.ts +7 -0
- package/src/extensions/loader.ts +10 -5
- package/src/extensions/post-bridge.ts +40 -0
- package/src/github/handlers.ts +11 -4
- package/src/heartbeat/heartbeat.ts +2 -1
- package/src/heartbeat/templates.ts +8 -5
- package/src/hooks/hook.ts +0 -26
- package/src/http/agents.ts +49 -2
- package/src/http/all-routes.ts +1 -1
- package/src/http/apps.ts +30 -3
- package/src/http/config.ts +62 -19
- package/src/http/core.ts +8 -0
- package/src/http/index.ts +25 -3
- package/src/http/integrations.ts +88 -1
- package/src/http/mcp.ts +15 -5
- package/src/http/model-profiles.ts +303 -0
- package/src/http/page-proxy.ts +2 -4
- package/src/http/pages-public.ts +13 -7
- package/src/http/pages.ts +3 -13
- package/src/http/poll.ts +42 -71
- package/src/http/requested-by.ts +54 -0
- package/src/http/script-runs.ts +1 -1
- package/src/http/scripts.ts +26 -2
- package/src/http/stats.ts +1 -30
- package/src/http/tasks.ts +79 -5
- package/src/http/utils.ts +14 -0
- package/src/http/webhooks.ts +160 -3
- package/src/http/x.ts +1 -0
- package/src/integrations/chat-sdk/adapters.ts +129 -0
- package/src/integrations/chat-sdk/attachments.ts +232 -0
- package/src/integrations/chat-sdk/background.ts +36 -0
- package/src/integrations/chat-sdk/commands.ts +194 -0
- package/src/integrations/chat-sdk/config.ts +198 -0
- package/src/integrations/chat-sdk/discord-api.ts +63 -0
- package/src/integrations/chat-sdk/discord-gateway.ts +185 -0
- package/src/integrations/chat-sdk/handlers.ts +28 -0
- package/src/integrations/chat-sdk/inbound.ts +426 -0
- package/src/integrations/chat-sdk/limits.ts +48 -0
- package/src/integrations/chat-sdk/logger.ts +81 -0
- package/src/integrations/chat-sdk/outbound.ts +123 -0
- package/src/integrations/chat-sdk/relay.ts +172 -0
- package/src/integrations/chat-sdk/router.ts +169 -0
- package/src/integrations/chat-sdk/runtime.ts +351 -0
- package/src/integrations/chat-sdk/schema.ts +245 -0
- package/src/integrations/chat-sdk/state.ts +243 -0
- package/src/integrations/chat-sdk/status.ts +244 -0
- package/src/integrations/chat-sdk/steering.ts +58 -0
- package/src/integrations/chat-sdk/teams-origin.ts +77 -0
- package/src/integrations/chat-sdk/telegram-api.ts +56 -0
- package/src/integrations/chat-sdk/templates.ts +16 -0
- package/src/integrations/chat-sdk/test-hooks.ts +37 -0
- package/src/integrations/chat-sdk/thread-buffer.ts +100 -0
- package/src/integrations/chat-sdk/typing.ts +65 -0
- package/src/{artifact-sdk → pages}/feedback-overlay.ts +65 -6
- package/src/pages/version.ts +4 -5
- package/src/prompts/base-prompt.ts +111 -45
- package/src/providers/acp-target-catalog.ts +6 -0
- package/src/providers/acp-targets.ts +56 -3
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/claude-adapter.ts +1 -1
- package/src/providers/claude-flag-settings.ts +17 -5
- package/src/providers/claude-managed-adapter.ts +1 -1
- package/src/providers/claude-session-events.ts +6 -1
- package/src/providers/dsh-adapter.ts +1 -1
- package/src/providers/opencode-adapter.ts +243 -26
- package/src/providers/opencode-swarm-plugin.ts +31 -0
- package/src/providers/pi-mono-adapter.ts +398 -153
- package/src/providers/pi-mono-extension.ts +17 -3
- package/src/providers/pricing-sources.md +33 -2
- package/src/providers/reasoning-effort.ts +37 -0
- package/src/providers/types.ts +6 -2
- package/src/rbac/legacy-policy.ts +16 -2
- package/src/rbac/permissions.ts +26 -0
- package/src/scheduler/deferred-task-waits.ts +18 -5
- package/src/scheduler/scheduler.ts +11 -3
- package/src/scripts-runtime/executors/quickjs-prelude.ts +75 -6
- package/src/scripts-runtime/executors/quickjs-runner.ts +85 -8
- package/src/scripts-runtime/executors/quickjs-web-shims.ts +1041 -0
- package/src/scripts-runtime/extract-args-schema.ts +9 -8
- package/src/scripts-runtime/loader.ts +19 -7
- package/src/scripts-runtime/quickjs-compat.ts +464 -0
- package/src/scripts-runtime/sdk-allowlist.ts +12 -6
- package/src/scripts-runtime/swarm-sdk.ts +0 -11
- package/src/scripts-runtime/types/stdlib.d.ts +39 -15
- package/src/scripts-runtime/types/swarm-extension.d.ts +66 -1
- package/src/scripts-runtime/types/swarm-sdk.d.ts +39 -15
- package/src/server.ts +62 -19
- package/src/slack/blocks.ts +4 -2
- package/src/slack/event-dedup.ts +1 -1
- package/src/slack/reaction-shortcode.ts +14 -0
- package/src/slack/render-v2.ts +12 -7
- package/src/slack/responses.ts +5 -4
- package/src/slack/thread-buffer.ts +41 -22
- package/src/tasks/additive-buffer.ts +4 -2
- package/src/tasks/context-key.ts +60 -1
- package/src/tasks/model-pin.ts +67 -0
- package/src/tasks/worker-follow-up.ts +15 -33
- package/src/tests/acp-adapter.test.ts +42 -0
- package/src/tests/agentmail-filters.test.ts +47 -0
- package/src/tests/api-drain-worker-e2e.test.ts +1 -6
- package/src/tests/apps-spike2.test.ts +35 -0
- package/src/tests/azure-devops-outbound.test.ts +420 -0
- package/src/tests/base-prompt.test.ts +58 -54
- package/src/tests/chat-sdk-commands.test.ts +191 -0
- package/src/tests/chat-sdk-config.test.ts +334 -0
- package/src/tests/chat-sdk-discord-api.test.ts +68 -0
- package/src/tests/chat-sdk-discord-gateway.test.ts +252 -0
- package/src/tests/chat-sdk-discord-webhook.test.ts +126 -0
- package/src/tests/chat-sdk-fields-sync.test.ts +50 -0
- package/src/tests/chat-sdk-harness.ts +146 -0
- package/src/tests/chat-sdk-inbound.test.ts +363 -0
- package/src/tests/chat-sdk-relay.test.ts +470 -0
- package/src/tests/chat-sdk-reload.test.ts +128 -0
- package/src/tests/chat-sdk-shutdown.test.ts +220 -0
- package/src/tests/chat-sdk-slack.test.ts +268 -0
- package/src/tests/chat-sdk-state.test.ts +163 -0
- package/src/tests/chat-sdk-status-route.test.ts +382 -0
- package/src/tests/chat-sdk-teams.test.ts +277 -0
- package/src/tests/chat-sdk-tools.test.ts +520 -0
- package/src/tests/chat-sdk-typing.test.ts +120 -0
- package/src/tests/chat-sdk-webhook-telegram.test.ts +200 -0
- package/src/tests/claude-adapter-binary.test.ts +9 -4
- package/src/tests/claude-flag-settings.test.ts +11 -3
- package/src/tests/claude-managed-adapter.test.ts +1 -1
- package/src/tests/claude-sdk-lifecycle.test.ts +9 -3
- package/src/tests/claude-thinking-tokens.test.ts +57 -0
- package/src/tests/claude-worker-parity.test.ts +2 -7
- package/src/tests/config-env-export-chat-sdk.test.ts +11 -0
- package/src/tests/config-refs.test.ts +66 -0
- package/src/tests/context-key.test.ts +19 -0
- package/src/tests/cors-allowlist.test.ts +25 -0
- package/src/tests/create-page-tool.test.ts +18 -0
- package/src/tests/credential-check.test.ts +139 -0
- package/src/tests/credentials.test.ts +21 -0
- package/src/tests/db-runtime.test.ts +61 -0
- package/src/tests/db-task-read-contract.test.ts +22 -0
- package/src/tests/db-task-write-contract.test.ts +11 -0
- package/src/tests/deferred-task-wake.test.ts +78 -1
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/dsh-adapter.test.ts +1 -1
- package/src/tests/enabled-capabilities.test.ts +20 -0
- package/src/tests/entrypoint-api-readiness.test.ts +14 -8
- package/src/tests/entrypoint-config-env-export.test.ts +5 -2
- package/src/tests/entrypoint-export-to-env.test.ts +78 -0
- package/src/tests/env-flag.test.ts +1 -0
- package/src/tests/explicit-model-validation.test.ts +105 -1
- package/src/tests/extensions-catalog.test.ts +1 -0
- package/src/tests/extensions-example-comms-adherence-gate.test.ts +413 -0
- package/src/tests/extensions-example-route-chat-channel.test.ts +154 -0
- package/src/tests/extensions-loader.test.ts +15 -0
- package/src/tests/extensions-post-bridge.test.ts +51 -0
- package/src/tests/fetch-resolved-env.test.ts +128 -0
- package/src/tests/fixtures/chat-sdk/telegram-private-text.json +10 -0
- package/src/tests/fixtures/extensions/route-chat-channel-to-agent/hooks.ts +27 -0
- package/src/tests/fixtures/extensions/route-chat-channel-to-agent/manifest.json +9 -0
- package/src/tests/fixtures/openai-compatible-registry-extension.child.ts +146 -0
- package/src/tests/fixtures/runner-legacy-resume-prompt.ts +122 -0
- package/src/tests/github-handlers.test.ts +35 -0
- package/src/tests/harness-provider-resolution.test.ts +12 -0
- package/src/tests/heartbeat-checklist.test.ts +3 -2
- package/src/tests/heartbeat-jev-gate-credentials.test.ts +168 -0
- package/src/tests/heartbeat-jev-gate.test.ts +385 -0
- package/src/tests/hook-runtime-close.test.ts +0 -2
- package/src/tests/http-api-integration.test.ts +0 -25
- package/src/tests/migration-046-budgets.test.ts +17 -16
- package/src/tests/migration-217-chat-sdk-state.test.ts +113 -0
- package/src/tests/model-profile-backcompat.test.ts +327 -0
- package/src/tests/model-profile-providers.test.ts +302 -0
- package/src/tests/model-profile-runtime-secrets.test.ts +223 -0
- package/src/tests/model-profiles-api.test.ts +614 -0
- package/src/tests/model-tier-resolution.test.ts +93 -0
- package/src/tests/models-catalog.test.ts +42 -1
- package/src/tests/openai-compatible-providers.test.ts +134 -0
- package/src/tests/openai-compatible-registry-extension.test.ts +42 -0
- package/src/tests/opencode-adapter.test.ts +216 -2
- package/src/tests/opencode-helmcode-sdk.test.ts +201 -0
- package/src/tests/opencode-shell-env.test.ts +145 -0
- package/src/tests/openrouter-base-url.test.ts +101 -151
- package/src/tests/page-proxy.test.ts +1 -1
- package/src/tests/pages-browser-sdk.test.ts +76 -0
- package/src/tests/pages-feedback-keys.test.ts +121 -0
- package/src/tests/pages-feedback.test.ts +4 -1
- package/src/tests/paused-tasks-requested-by.test.ts +102 -0
- package/src/tests/pi-context-compaction.test.ts +193 -0
- package/src/tests/pi-custom-provider-fake-server.test.ts +442 -0
- package/src/tests/pi-mono-adapter.test.ts +385 -5
- package/src/tests/pi-shell-env.test.ts +170 -0
- package/src/tests/profile-claim.test.ts +269 -0
- package/src/tests/prompt-template-remaining.test.ts +35 -0
- package/src/tests/quickjs-compat.test.ts +213 -0
- package/src/tests/rbac-engine.test.ts +32 -3
- package/src/tests/runner-exit-after-tasks.test.ts +0 -4
- package/src/tests/runner-legacy-resume-prompt.test.ts +64 -0
- package/src/tests/script-connections.test.ts +6 -0
- package/src/tests/script-executor-conformance.test.ts +252 -1
- package/src/tests/script-executor-quickjs.test.ts +96 -0
- package/src/tests/script-runs-http.test.ts +89 -0
- package/src/tests/scripts-http.test.ts +217 -1
- package/src/tests/scripts-only-gating.test.ts +2 -3
- package/src/tests/scripts-runtime.test.ts +58 -40
- package/src/tests/secret-scrubber.test.ts +96 -0
- package/src/tests/seed-model-profiles.test.ts +174 -0
- package/src/tests/seed-scripts.test.ts +2 -2
- package/src/tests/seed-skills-bundled-files.test.ts +45 -53
- package/src/tests/seed-skills-retire.test.ts +182 -0
- package/src/tests/send-task-requester-gate.test.ts +58 -0
- package/src/tests/session-cost-recompute-unit.test.ts +46 -0
- package/src/tests/skill-get-name-resolution.test.ts +137 -0
- package/src/tests/skill-invocations.test.ts +4 -2
- package/src/tests/slack-blocks.test.ts +36 -0
- package/src/tests/slack-inline-output.test.ts +14 -0
- package/src/tests/slack-reaction-config.test.ts +20 -0
- package/src/tests/slack-render-v2.test.ts +37 -0
- package/src/tests/slack-thread-buffer.test.ts +98 -1
- package/src/tests/sonnet-5-5-registry.test.ts +9 -1
- package/src/tests/swarm-config-export-to-env.test.ts +267 -0
- package/src/tests/swarm-config-reserved-keys.test.ts +16 -0
- package/src/tests/swarm-diff.test.ts +1 -1
- package/src/tests/task-model-effort-rerun.test.ts +164 -0
- package/src/tests/task-session-prompts.test.ts +132 -0
- package/src/tests/task-tool-manifest.test.ts +14 -0
- package/src/tests/task-tools-ctx.test.ts +2 -0
- package/src/tests/tool-annotations.test.ts +6 -6
- package/src/tests/ui-logs-parser.test.ts +43 -0
- package/src/tests/used-by-route.test.ts +6 -0
- package/src/tests/vercel-ai-gateway.test.ts +149 -0
- package/src/tests/workflow-llm.test.ts +61 -0
- package/src/tests/workflow-swarm-script.test.ts +38 -0
- package/src/tests/workflow-wait-filter.test.ts +5 -3
- package/src/tools/chat-access.ts +41 -0
- package/src/tools/chat-open-dm.ts +59 -0
- package/src/tools/chat-post.ts +74 -0
- package/src/tools/chat-read-thread.ts +100 -0
- package/src/tools/chat-reply.ts +75 -0
- package/src/tools/create-page.ts +3 -13
- package/src/tools/model-catalog/index.ts +2 -1
- package/src/tools/model-profiles/list-model-profiles.ts +54 -0
- package/src/tools/register-chat-channel.ts +139 -0
- package/src/tools/request-human-input.ts +11 -3
- package/src/tools/script-apis.ts +5 -1
- package/src/tools/script-upsert.ts +4 -1
- package/src/tools/send-task.ts +48 -1
- package/src/tools/skills/skill-get.ts +17 -9
- package/src/tools/swarm-config/get-config.ts +2 -1
- package/src/tools/swarm-config/list-config.ts +2 -1
- package/src/tools/swarm-config/set-config.ts +16 -3
- package/src/tools/system-one-decide.ts +104 -0
- package/src/tools/task-action.ts +22 -1
- package/src/tools/tool-config.ts +14 -6
- package/src/types.ts +48 -3
- package/src/utils/config-env-export.ts +74 -0
- package/src/utils/credentials.test.ts +92 -0
- package/src/utils/credentials.ts +52 -4
- package/src/utils/harness-provider.ts +15 -6
- package/src/utils/runtime-model-catalog.ts +11 -1
- package/src/utils/secret-scrubber.ts +86 -10
- package/src/utils/task-tool-manifest.ts +19 -2
- package/src/utils/vcs-repo-match.ts +29 -0
- package/src/workflows/executors/raw-llm.ts +8 -3
- package/src/workflows/executors/swarm-script.ts +1 -0
- package/src/workflows/executors/system-one-decision.ts +21 -11
- package/src/workflows/executors/workflow-llm.ts +22 -0
- package/src/workflows/index.ts +21 -12
- package/templates/extensions/README.md +1 -0
- package/templates/extensions/comms-adherence-gate/README.md +88 -0
- package/templates/extensions/comms-adherence-gate/hooks.ts +489 -0
- package/templates/extensions/comms-adherence-gate/manifest.json +8 -0
- package/templates/skills/agent-browser/SKILL.md +1 -1
- package/templates/skills/agent-browser/content.md +1 -1
- package/templates/skills/apps/SKILL.md +8 -1
- package/templates/skills/apps/content.md +8 -1
- package/templates/skills/chat-sdk/config.json +14 -0
- package/templates/skills/chat-sdk/content.md +84 -0
- package/templates/skills/composio-gmail/content.md +7 -0
- package/templates/skills/composio-google-docs/content.md +1 -1
- package/templates/skills/email-domain-health/config.json +14 -0
- package/templates/skills/email-domain-health/content.md +131 -0
- package/templates/skills/kv-storage/content.md +2 -2
- package/templates/skills/pages/content.md +8 -10
- package/templates/skills/swarm-extensions/content.md +2 -0
- package/templates/skills/swarm-scripts/SKILL.md +4 -2
- package/templates/skills/swarm-scripts/config.json +1 -1
- package/templates/skills/swarm-scripts/content.md +4 -2
- package/templates/skills/workflow-iterate/content.md +38 -0
- package/dist/artifact-eczsknc3.js +0 -3843
- package/dist/cli-42qhyvwx.js +0 -33
- package/dist/cli-bd957dgb.js +0 -153
- package/dist/cli-q51yprfq.js +0 -1104
- package/dist/cli-t1ze64h3.js +0 -811
- package/dist/cli-tcc4wd5d.js +0 -56471
- package/dist/index-9ch1n761.js +0 -1102
- package/src/artifact-sdk/index.ts +0 -2
- package/src/artifact-sdk/localtunnel.d.ts +0 -20
- package/src/artifact-sdk/port.ts +0 -12
- package/src/artifact-sdk/server.ts +0 -183
- package/src/artifact-sdk/tunnel.ts +0 -19
- package/src/commands/artifact.ts +0 -249
- package/src/http/ecosystem.ts +0 -77
- package/src/tests/artifact-commands.test.ts +0 -92
- package/src/tests/artifact-sdk.test.ts +0 -793
- package/src/tests/register-service-security.test.ts +0 -194
- package/src/tools/list-services.ts +0 -107
- package/src/tools/register-service.ts +0 -177
- package/src/tools/unregister-service.ts +0 -73
- package/src/tools/update-service-status.ts +0 -96
- package/templates/skills/artifacts/config.json +0 -14
- package/templates/skills/artifacts/content.md +0 -344
- package/templates/skills/artifacts/files/examples/approval-flow.ts +0 -34
- package/templates/skills/artifacts/files/examples/hono-dashboard.ts +0 -31
- package/templates/skills/artifacts/files/examples/multi-artifact.ts +0 -20
- package/templates/skills/artifacts/files/examples/static-report.sh +0 -17
- /package/src/{artifact-sdk → pages}/browser-sdk.ts +0 -0
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
BROWSER_SDK_JS,
|
|
3
|
-
SWARM_UI_JS
|
|
4
|
-
} from "./cli-t1ze64h3.js";
|
|
5
1
|
import {
|
|
6
2
|
SIMILARITY_THRESHOLD,
|
|
7
3
|
isLeadChannelMonitoringEnabled,
|
|
@@ -11,7 +7,7 @@ import {
|
|
|
11
7
|
} from "./cli-hz99wfxp.js";
|
|
12
8
|
import {
|
|
13
9
|
ACP_TARGET_IDS
|
|
14
|
-
} from "./cli-
|
|
10
|
+
} from "./cli-6cagzc96.js";
|
|
15
11
|
import {
|
|
16
12
|
isCodexOAuthConfigKey
|
|
17
13
|
} from "./cli-bqjfjex4.js";
|
|
@@ -23,12 +19,13 @@ import {
|
|
|
23
19
|
MAX_RATE_LIMIT_RESET_MS
|
|
24
20
|
} from "./cli-2pdaysab.js";
|
|
25
21
|
import {
|
|
26
|
-
reasoningCapability
|
|
27
|
-
|
|
22
|
+
reasoningCapability,
|
|
23
|
+
supportedReasoningLevels
|
|
24
|
+
} from "./cli-0veb92a3.js";
|
|
28
25
|
import {
|
|
29
26
|
ensureAgentFsCredentialsForAgent,
|
|
30
27
|
inviteEmailToSharedOrg
|
|
31
|
-
} from "./cli-
|
|
28
|
+
} from "./cli-zm9t2vde.js";
|
|
32
29
|
import"./cli-6zx22mv6.js";
|
|
33
30
|
import {
|
|
34
31
|
DEFAULT_CONNECTOR_CODE_LABEL,
|
|
@@ -39,16 +36,17 @@ import {
|
|
|
39
36
|
startPoolRouting,
|
|
40
37
|
stopHeartbeat,
|
|
41
38
|
stopPoolRouting
|
|
42
|
-
} from "./cli-
|
|
39
|
+
} from "./cli-yjmqpxt3.js";
|
|
43
40
|
import {
|
|
44
41
|
Anthropic
|
|
45
|
-
} from "./cli-
|
|
42
|
+
} from "./cli-gxzkg1de.js";
|
|
46
43
|
import {
|
|
47
44
|
runtimeSupervisorStatus,
|
|
48
45
|
startRuntimeSupervisor,
|
|
49
46
|
stopRuntimeSupervisor
|
|
50
|
-
} from "./cli-
|
|
51
|
-
import"./cli-
|
|
47
|
+
} from "./cli-01xvj1rq.js";
|
|
48
|
+
import"./cli-n8ndw8j7.js";
|
|
49
|
+
import"./cli-knd276ed.js";
|
|
52
50
|
import {
|
|
53
51
|
AGENT_MAX_TASKS_CONFIG_KEY,
|
|
54
52
|
ALL_CAPABILITIES,
|
|
@@ -75,6 +73,7 @@ import {
|
|
|
75
73
|
SDK_ALLOWLIST,
|
|
76
74
|
SUPPORTED_PROTOCOL_VERSIONS,
|
|
77
75
|
TaskCreationBlockedError,
|
|
76
|
+
TelegramBotApiError,
|
|
78
77
|
WebhookError,
|
|
79
78
|
activateVersion,
|
|
80
79
|
agentRanModel,
|
|
@@ -88,6 +87,7 @@ import {
|
|
|
88
87
|
authorizeAssetKeyWrite,
|
|
89
88
|
authorizeRoomNamespace,
|
|
90
89
|
calculateNextRun,
|
|
90
|
+
callTelegramBotApi,
|
|
91
91
|
canClaim,
|
|
92
92
|
canReadMemory,
|
|
93
93
|
cancelApprovalRequest,
|
|
@@ -124,6 +124,7 @@ import {
|
|
|
124
124
|
enforceContentLengthCap,
|
|
125
125
|
enqueueAdmissionRow,
|
|
126
126
|
enqueueAuditRow,
|
|
127
|
+
errorMessage,
|
|
127
128
|
estimateTokens,
|
|
128
129
|
executeRawLlm,
|
|
129
130
|
executeReadOnlyQuery,
|
|
@@ -133,6 +134,9 @@ import {
|
|
|
133
134
|
extractAndVerifyCookie,
|
|
134
135
|
flushAuditBuffer,
|
|
135
136
|
getAgentHarnessCliVersion,
|
|
137
|
+
getChatInstance,
|
|
138
|
+
getChatSdkConfig,
|
|
139
|
+
getChatSdkHealth,
|
|
136
140
|
getCombConfig,
|
|
137
141
|
getEnabledCapabilities,
|
|
138
142
|
getEventCountsFiltered,
|
|
@@ -147,6 +151,7 @@ import {
|
|
|
147
151
|
getLinksForMemory,
|
|
148
152
|
getMemoryChunks,
|
|
149
153
|
getOAuthPreset,
|
|
154
|
+
getPageEditCounter,
|
|
150
155
|
getPathSegments,
|
|
151
156
|
getPosteriorsByIds,
|
|
152
157
|
getRoom,
|
|
@@ -198,6 +203,7 @@ import {
|
|
|
198
203
|
initAgentMail,
|
|
199
204
|
initApiTelemetry,
|
|
200
205
|
initAzureDevOps,
|
|
206
|
+
initAzureDevOpsOutbound,
|
|
201
207
|
initGitHub,
|
|
202
208
|
initGitLab,
|
|
203
209
|
initJira,
|
|
@@ -207,6 +213,9 @@ import {
|
|
|
207
213
|
isAgentMailEnabled,
|
|
208
214
|
isApiDraining,
|
|
209
215
|
isAzureDevOpsEnabled,
|
|
216
|
+
isChatAdapterConfigured,
|
|
217
|
+
isChatSdkEnabled,
|
|
218
|
+
isChatSdkStopping,
|
|
210
219
|
isGitHubEnabled,
|
|
211
220
|
isGitLabEnabled,
|
|
212
221
|
isInboxAllowed,
|
|
@@ -218,6 +227,7 @@ import {
|
|
|
218
227
|
isLinearEnabled,
|
|
219
228
|
isOriginAllowedForCredentials,
|
|
220
229
|
isSenderAllowed,
|
|
230
|
+
isTrustedTeamsServiceUrl,
|
|
221
231
|
issuePageSessionCookie,
|
|
222
232
|
json,
|
|
223
233
|
jsonError,
|
|
@@ -241,7 +251,6 @@ import {
|
|
|
241
251
|
parseCookieHeader,
|
|
242
252
|
parseModelTierOverridesHeader,
|
|
243
253
|
parseQueryParams,
|
|
244
|
-
parseSkillContent,
|
|
245
254
|
patchWorkflowDefinition,
|
|
246
255
|
poolTaskRunsOnHarness,
|
|
247
256
|
posteriorMean,
|
|
@@ -267,6 +276,7 @@ import {
|
|
|
267
276
|
reservedRoomKeyError,
|
|
268
277
|
resetAgentMaxTasksMirror,
|
|
269
278
|
resetRoom,
|
|
279
|
+
resolveAdapterApiUrl,
|
|
270
280
|
resolveHttpAuditUserId,
|
|
271
281
|
resolveHttpFavoriteOwner,
|
|
272
282
|
resolveHttpRequestAuth,
|
|
@@ -301,17 +311,21 @@ import {
|
|
|
301
311
|
snapshotPage,
|
|
302
312
|
startAuditGc,
|
|
303
313
|
startAuditWriter,
|
|
314
|
+
startChatSdk,
|
|
304
315
|
steerTaskHandler,
|
|
305
316
|
steerTaskOutputSchema,
|
|
306
317
|
steerTaskUserInputSchema,
|
|
307
318
|
stopAuditGc,
|
|
308
319
|
stopAuditWriter,
|
|
320
|
+
stopChatSdk,
|
|
309
321
|
stopExtensionRuntime,
|
|
310
322
|
stripSiblingBlock,
|
|
311
323
|
sweepRooms,
|
|
312
324
|
syncTelemetryEnabled,
|
|
325
|
+
taskModelPin,
|
|
313
326
|
toolErr,
|
|
314
327
|
touchRuntimeInstance,
|
|
328
|
+
trackBackground,
|
|
315
329
|
triggerSchemaErrorResponse,
|
|
316
330
|
uninstallExtension,
|
|
317
331
|
updateExtensionMeta,
|
|
@@ -332,13 +346,13 @@ import {
|
|
|
332
346
|
wireHttpSpanLifecycle,
|
|
333
347
|
workflowModelErrors,
|
|
334
348
|
writeUnhandledError
|
|
335
|
-
} from "./cli-
|
|
336
|
-
import"./cli-
|
|
349
|
+
} from "./cli-vgqzp4em.js";
|
|
350
|
+
import"./cli-h1arqn31.js";
|
|
337
351
|
import {
|
|
338
352
|
AMP_ESTIMATE_MODELS,
|
|
339
353
|
ampBillsCacheWrites,
|
|
340
354
|
resolveAmpModel
|
|
341
|
-
} from "./cli-
|
|
355
|
+
} from "./cli-8wwxw8a3.js";
|
|
342
356
|
import {
|
|
343
357
|
RATING_MODEL_MAX_LENGTH,
|
|
344
358
|
REFERENCES_SOURCE_MAX_LENGTH,
|
|
@@ -347,16 +361,12 @@ import {
|
|
|
347
361
|
import {
|
|
348
362
|
isMultiRuntimeEnabled
|
|
349
363
|
} from "./cli-re8z5p8p.js";
|
|
350
|
-
import"./cli-42qhyvwx.js";
|
|
351
364
|
import {
|
|
352
365
|
deleteModelCatalogOverlay,
|
|
353
366
|
loadModelsCatalog,
|
|
354
367
|
requestModelCatalogRefresh,
|
|
355
368
|
upsertModelCatalogOverlay
|
|
356
|
-
} from "./cli-
|
|
357
|
-
import {
|
|
358
|
-
normalizeModelKey
|
|
359
|
-
} from "./cli-jpc4r224.js";
|
|
369
|
+
} from "./cli-0pd8hzhc.js";
|
|
360
370
|
import {
|
|
361
371
|
ConnectionAuthInputSchema,
|
|
362
372
|
MAX_SCRIPT_WALL_CLOCK_MS,
|
|
@@ -373,6 +383,7 @@ import {
|
|
|
373
383
|
buildScriptCredentialBindingsWithFailures,
|
|
374
384
|
callMcpServerTool,
|
|
375
385
|
checkImportAllowlist,
|
|
386
|
+
checkQuickJSCompat,
|
|
376
387
|
collectScriptReferences,
|
|
377
388
|
computeExpiresAt,
|
|
378
389
|
connectionAuthInputFromFlat,
|
|
@@ -411,7 +422,9 @@ import {
|
|
|
411
422
|
mcpOAuthSsrfOptions,
|
|
412
423
|
normalizeTokenEndpointAuthMethod,
|
|
413
424
|
oauthAppToProviderConfig,
|
|
425
|
+
parseSkillContent,
|
|
414
426
|
publicEndpointSsrfOptions,
|
|
427
|
+
quickJSCompatWarnings,
|
|
415
428
|
readStreamCapped,
|
|
416
429
|
refreshMcpToken,
|
|
417
430
|
refreshScriptConnection,
|
|
@@ -434,7 +447,7 @@ import {
|
|
|
434
447
|
upsertCredentialBinding,
|
|
435
448
|
upsertMcpOAuthToken,
|
|
436
449
|
upsertScriptConnection
|
|
437
|
-
} from "./cli-
|
|
450
|
+
} from "./cli-4mengw46.js";
|
|
438
451
|
import"./cli-epan13p1.js";
|
|
439
452
|
import {
|
|
440
453
|
detachedProcessGroup,
|
|
@@ -449,7 +462,12 @@ import {
|
|
|
449
462
|
forceRefreshAuthorizationOrThrow,
|
|
450
463
|
forceRefreshTokenOrThrow,
|
|
451
464
|
oauthAppRowToProviderConfig
|
|
452
|
-
} from "./cli-
|
|
465
|
+
} from "./cli-711vwbxn.js";
|
|
466
|
+
import"./cli-wfnemr1t.js";
|
|
467
|
+
import {
|
|
468
|
+
normalizeModelKey
|
|
469
|
+
} from "./cli-hqr88ew6.js";
|
|
470
|
+
import"./cli-6cvfxs8r.js";
|
|
453
471
|
import {
|
|
454
472
|
AgentFsProvider,
|
|
455
473
|
FilesError,
|
|
@@ -468,14 +486,14 @@ import {
|
|
|
468
486
|
startWorkflowExecution,
|
|
469
487
|
validateJsonSchema,
|
|
470
488
|
workflowSaveWarnings
|
|
471
|
-
} from "./cli-
|
|
489
|
+
} from "./cli-naqzr17j.js";
|
|
472
490
|
import {
|
|
473
491
|
createApprovalFollowUpTask,
|
|
474
492
|
definitionNodeIds,
|
|
475
493
|
generateEdges,
|
|
476
494
|
validateDefinition
|
|
477
|
-
} from "./cli-
|
|
478
|
-
import"./cli-
|
|
495
|
+
} from "./cli-20krdhz5.js";
|
|
496
|
+
import"./cli-1y7yywmj.js";
|
|
479
497
|
import {
|
|
480
498
|
CLIENT_ID,
|
|
481
499
|
MAX_CODEX_OAUTH_SLOT,
|
|
@@ -486,10 +504,10 @@ import {
|
|
|
486
504
|
getAccountId,
|
|
487
505
|
getValidCodexOAuth,
|
|
488
506
|
loadAllCodexOAuthSlots
|
|
489
|
-
} from "./cli-
|
|
490
|
-
import"./cli-b07hr3bt.js";
|
|
491
|
-
import"./cli-h6vtvt5c.js";
|
|
507
|
+
} from "./cli-rc6pf5kv.js";
|
|
492
508
|
import"./cli-2x0e561b.js";
|
|
509
|
+
import"./cli-txqb9s38.js";
|
|
510
|
+
import"./cli-h6vtvt5c.js";
|
|
493
511
|
import"./cli-fad8m16k.js";
|
|
494
512
|
import"./cli-bsakbbkm.js";
|
|
495
513
|
import"./cli-zxg80zs9.js";
|
|
@@ -509,18 +527,22 @@ import {
|
|
|
509
527
|
recordDbRetentionStatement,
|
|
510
528
|
recordDbRetentionSweep,
|
|
511
529
|
recordSessionCost,
|
|
512
|
-
require_content_type,
|
|
513
530
|
startSlackApp,
|
|
514
531
|
startSpan,
|
|
515
532
|
stopSlackApp,
|
|
516
533
|
withRemoteContext,
|
|
517
534
|
withSpan,
|
|
518
535
|
withSpanContext
|
|
519
|
-
} from "./cli-
|
|
520
|
-
import"./cli-
|
|
536
|
+
} from "./cli-cvv9k0hh.js";
|
|
537
|
+
import"./cli-n2wq4j7m.js";
|
|
538
|
+
import {
|
|
539
|
+
require_content_type
|
|
540
|
+
} from "./cli-wmgrkscj.js";
|
|
521
541
|
import"./cli-3er6gcsn.js";
|
|
522
|
-
import"./cli-
|
|
523
|
-
import"./cli-
|
|
542
|
+
import"./cli-hjwmhcg1.js";
|
|
543
|
+
import"./cli-rhs9pz33.js";
|
|
544
|
+
import"./cli-9ha6hs2d.js";
|
|
545
|
+
import"./cli-r610p9bs.js";
|
|
524
546
|
import {
|
|
525
547
|
InvalidRuntimeTokenError,
|
|
526
548
|
RuntimePoolError,
|
|
@@ -559,11 +581,11 @@ import {
|
|
|
559
581
|
unlinkIdentity,
|
|
560
582
|
updateRuntimeController,
|
|
561
583
|
updateRuntimePool
|
|
562
|
-
} from "./cli-
|
|
563
|
-
import"./cli-
|
|
584
|
+
} from "./cli-mgz4nbgg.js";
|
|
585
|
+
import"./cli-j67mh2vg.js";
|
|
564
586
|
import {
|
|
565
587
|
bedrockProviderModule
|
|
566
|
-
} from "./cli-
|
|
588
|
+
} from "./cli-hvqbtna4.js";
|
|
567
589
|
import"./cli-phre6gz0.js";
|
|
568
590
|
import"./cli-eh1ytktb.js";
|
|
569
591
|
import"./cli-txgcknmy.js";
|
|
@@ -578,7 +600,7 @@ import"./cli-1m2e086m.js";
|
|
|
578
600
|
import"./cli-btbzvq95.js";
|
|
579
601
|
import"./cli-p7z6q2m6.js";
|
|
580
602
|
import"./cli-wnb9b2g3.js";
|
|
581
|
-
import"./cli-
|
|
603
|
+
import"./cli-f527ce74.js";
|
|
582
604
|
import {
|
|
583
605
|
import_websocket,
|
|
584
606
|
import_websocket_server
|
|
@@ -587,7 +609,7 @@ import"./cli-pzxba7gw.js";
|
|
|
587
609
|
import {
|
|
588
610
|
getUserGrant,
|
|
589
611
|
init_rbac_roles
|
|
590
|
-
} from "./cli-
|
|
612
|
+
} from "./cli-d9286m9h.js";
|
|
591
613
|
import {
|
|
592
614
|
LEGACY_RULES,
|
|
593
615
|
can,
|
|
@@ -596,10 +618,12 @@ import {
|
|
|
596
618
|
init_rbac,
|
|
597
619
|
isRbacEnabled,
|
|
598
620
|
setAuditSink
|
|
599
|
-
} from "./cli-
|
|
621
|
+
} from "./cli-sksetb2q.js";
|
|
600
622
|
import {
|
|
601
623
|
API_DRAINING_HEADER,
|
|
602
624
|
CANDIDATE_SET_MULTIPLIER,
|
|
625
|
+
CHAT_SDK_ADAPTER_NAMES,
|
|
626
|
+
CHAT_TASK_ONLY_TOOLS,
|
|
603
627
|
CODEX_AUTH_WATCH_NAMESPACE,
|
|
604
628
|
CredentialBindingSchema,
|
|
605
629
|
DB_RETENTION_TUNING_BOUNDS,
|
|
@@ -610,6 +634,7 @@ import {
|
|
|
610
634
|
KvTypeCollisionError,
|
|
611
635
|
MAX_DB_RETENTION_DAYS,
|
|
612
636
|
MEMORY_KEY_MAX_LENGTH,
|
|
637
|
+
MODEL_PROFILE_CONFIG_KEY,
|
|
613
638
|
NOT_EXTENSION_AGENT_SQL,
|
|
614
639
|
OpenAI,
|
|
615
640
|
ReservedAgentRoleError,
|
|
@@ -626,6 +651,7 @@ import {
|
|
|
626
651
|
cancelTask,
|
|
627
652
|
catalog_default,
|
|
628
653
|
channelTopic,
|
|
654
|
+
chatThreadOfContextKey,
|
|
629
655
|
checkoutPromptTemplate,
|
|
630
656
|
claimKv,
|
|
631
657
|
claimMentions,
|
|
@@ -656,6 +682,7 @@ import {
|
|
|
656
682
|
createLogEntry,
|
|
657
683
|
createMcpServer,
|
|
658
684
|
createMetric,
|
|
685
|
+
createModelProfile,
|
|
659
686
|
createOAuthApp,
|
|
660
687
|
createPage,
|
|
661
688
|
createScheduledTask,
|
|
@@ -677,6 +704,7 @@ import {
|
|
|
677
704
|
deleteKv,
|
|
678
705
|
deleteMcpServer,
|
|
679
706
|
deleteMetric,
|
|
707
|
+
deleteModelProfile,
|
|
680
708
|
deleteOAuthTokens,
|
|
681
709
|
deletePage,
|
|
682
710
|
deletePricingRow,
|
|
@@ -715,7 +743,6 @@ import {
|
|
|
715
743
|
getAllChannelActivityCursors,
|
|
716
744
|
getAllLogs,
|
|
717
745
|
getAllPricingRows,
|
|
718
|
-
getAllServices,
|
|
719
746
|
getAllSessionCosts,
|
|
720
747
|
getAllTasks,
|
|
721
748
|
getAllUsers,
|
|
@@ -752,6 +779,7 @@ import {
|
|
|
752
779
|
getMetric,
|
|
753
780
|
getMetricVersion,
|
|
754
781
|
getMetricVersions,
|
|
782
|
+
getModelProfileByName,
|
|
755
783
|
getOAuthApp,
|
|
756
784
|
getOAuthAppById,
|
|
757
785
|
getOAuthTokens,
|
|
@@ -787,7 +815,6 @@ import {
|
|
|
787
815
|
getScriptRunByIdempotencyKey,
|
|
788
816
|
getScriptRunExecutionArgs,
|
|
789
817
|
getScriptRunJournalStep,
|
|
790
|
-
getServicesByAgentId,
|
|
791
818
|
getSessionCostSummary,
|
|
792
819
|
getSessionCostsByAgentId,
|
|
793
820
|
getSessionCostsByTaskId,
|
|
@@ -842,6 +869,7 @@ import {
|
|
|
842
869
|
init_human_free,
|
|
843
870
|
init_key_paths,
|
|
844
871
|
init_memory,
|
|
872
|
+
init_model_profiles,
|
|
845
873
|
init_oauth,
|
|
846
874
|
init_openai,
|
|
847
875
|
init_org_website,
|
|
@@ -850,6 +878,7 @@ import {
|
|
|
850
878
|
init_reranker,
|
|
851
879
|
init_runtime,
|
|
852
880
|
init_runtime_pools_flag,
|
|
881
|
+
init_schema,
|
|
853
882
|
init_secret_registry,
|
|
854
883
|
init_steering,
|
|
855
884
|
init_swarm_config_guard,
|
|
@@ -866,6 +895,9 @@ import {
|
|
|
866
895
|
installMcpServer,
|
|
867
896
|
installSkill,
|
|
868
897
|
internalConfigKeyError,
|
|
898
|
+
isAdapterEntryEnabled,
|
|
899
|
+
isApiOnlyConfigKey,
|
|
900
|
+
isChatSdkTask,
|
|
869
901
|
isExtensionAgent,
|
|
870
902
|
isFilledOrgLogoUrl,
|
|
871
903
|
isInternalConfigKey,
|
|
@@ -886,6 +918,7 @@ import {
|
|
|
886
918
|
listKv,
|
|
887
919
|
listMcpServers,
|
|
888
920
|
listMetricsByAgent,
|
|
921
|
+
listModelProfiles,
|
|
889
922
|
listPagesByAgent,
|
|
890
923
|
listRecentSessions,
|
|
891
924
|
listScriptApisForScript,
|
|
@@ -905,6 +938,8 @@ import {
|
|
|
905
938
|
markSteeringHandled,
|
|
906
939
|
markSteeringUndeliverable,
|
|
907
940
|
maskSecrets,
|
|
941
|
+
modelProfileRefError,
|
|
942
|
+
modelProfileReferences,
|
|
908
943
|
moveAssetKey,
|
|
909
944
|
normalizeDate,
|
|
910
945
|
normalizeOrgWebsite,
|
|
@@ -954,6 +989,7 @@ import {
|
|
|
954
989
|
startTask,
|
|
955
990
|
supersedeTask,
|
|
956
991
|
sweepExpiredKv,
|
|
992
|
+
taskProfileError,
|
|
957
993
|
telemetry,
|
|
958
994
|
touchScratchScriptLastUsed,
|
|
959
995
|
trackRelayed,
|
|
@@ -972,6 +1008,7 @@ import {
|
|
|
972
1008
|
updateAgentStatusFromCapacity,
|
|
973
1009
|
updateAuthorizationIdentity,
|
|
974
1010
|
updateMetric,
|
|
1011
|
+
updateModelProfile,
|
|
975
1012
|
updateOAuthAppById,
|
|
976
1013
|
updatePage,
|
|
977
1014
|
updateScheduledTask,
|
|
@@ -998,9 +1035,10 @@ import {
|
|
|
998
1035
|
upsertSkillFiles,
|
|
999
1036
|
upsertSwarmConfig,
|
|
1000
1037
|
validateConfigValue,
|
|
1038
|
+
validateExportToEnv,
|
|
1001
1039
|
withFavoriteFlags
|
|
1002
|
-
} from "./cli-
|
|
1003
|
-
import"./cli-
|
|
1040
|
+
} from "./cli-y3167wt1.js";
|
|
1041
|
+
import"./cli-9jzdj90z.js";
|
|
1004
1042
|
import {
|
|
1005
1043
|
ActiveSessionSchema,
|
|
1006
1044
|
AgentAcpStatusSchema,
|
|
@@ -1059,6 +1097,8 @@ import {
|
|
|
1059
1097
|
MetricSchema,
|
|
1060
1098
|
MetricVersionSchema,
|
|
1061
1099
|
MetricWidgetSchema,
|
|
1100
|
+
ModelProfileNameSchema,
|
|
1101
|
+
ModelProfileSchema,
|
|
1062
1102
|
ModelTierSchema,
|
|
1063
1103
|
OnUnsupportedSchema,
|
|
1064
1104
|
PageAuthModeSchema,
|
|
@@ -1096,7 +1136,6 @@ import {
|
|
|
1096
1136
|
ScriptRunStatusSchema,
|
|
1097
1137
|
ScriptScopeSchema,
|
|
1098
1138
|
ScriptVersionRecordSchema,
|
|
1099
|
-
ServiceSchema,
|
|
1100
1139
|
SessionCostModelBreakdownSchema,
|
|
1101
1140
|
SessionCostSchema,
|
|
1102
1141
|
SessionLogSchema,
|
|
@@ -1136,22 +1175,22 @@ import {
|
|
|
1136
1175
|
isSafeBundlePath,
|
|
1137
1176
|
isTerminalTaskStatus,
|
|
1138
1177
|
splitLegacyModelAlias
|
|
1139
|
-
} from "./cli-
|
|
1178
|
+
} from "./cli-pe05f8yk.js";
|
|
1140
1179
|
import {
|
|
1141
1180
|
IDENTITY_FIELD_BUDGETS,
|
|
1142
1181
|
IdentityFieldBudgetError,
|
|
1143
1182
|
init_identity_field_budget
|
|
1144
|
-
} from "./cli-
|
|
1145
|
-
import {
|
|
1146
|
-
init_steering_enabled,
|
|
1147
|
-
isSteeringEnabled
|
|
1148
|
-
} from "./cli-7mm2r1kp.js";
|
|
1183
|
+
} from "./cli-78vv7446.js";
|
|
1149
1184
|
import {
|
|
1150
1185
|
getSlackConfiguration,
|
|
1151
1186
|
init_config
|
|
1152
1187
|
} from "./cli-dmtrxqhg.js";
|
|
1188
|
+
import {
|
|
1189
|
+
init_steering_enabled,
|
|
1190
|
+
isSteeringEnabled
|
|
1191
|
+
} from "./cli-7mm2r1kp.js";
|
|
1192
|
+
import"./cli-x1r545x8.js";
|
|
1153
1193
|
import"./cli-mvzyxwg3.js";
|
|
1154
|
-
import"./cli-tadfgd0t.js";
|
|
1155
1194
|
import {
|
|
1156
1195
|
getAllTemplateDefinitions,
|
|
1157
1196
|
getTemplateDefinition,
|
|
@@ -1178,10 +1217,7 @@ import {
|
|
|
1178
1217
|
getOpenRouterAttributionHeaders,
|
|
1179
1218
|
init_openrouter_base_url
|
|
1180
1219
|
} from "./cli-a35hzdve.js";
|
|
1181
|
-
import
|
|
1182
|
-
init_src,
|
|
1183
|
-
isAlias
|
|
1184
|
-
} from "./cli-014vns2y.js";
|
|
1220
|
+
import"./cli-stfs0k5s.js";
|
|
1185
1221
|
import {
|
|
1186
1222
|
exports_zod,
|
|
1187
1223
|
init_zod
|
|
@@ -1202,17 +1238,17 @@ import {
|
|
|
1202
1238
|
import {
|
|
1203
1239
|
init_package,
|
|
1204
1240
|
package_default
|
|
1205
|
-
} from "./cli-
|
|
1241
|
+
} from "./cli-r35rw5de.js";
|
|
1206
1242
|
import {
|
|
1207
1243
|
init_event_bus,
|
|
1208
1244
|
workflowEventBus
|
|
1209
|
-
} from "./cli-
|
|
1245
|
+
} from "./cli-z780ec7c.js";
|
|
1210
1246
|
import {
|
|
1211
1247
|
init_bus,
|
|
1212
1248
|
realtimeBus
|
|
1213
1249
|
} from "./cli-zf8jjwex.js";
|
|
1214
1250
|
import"./cli-h8w3fg83.js";
|
|
1215
|
-
import"./cli-
|
|
1251
|
+
import"./cli-t02z59f2.js";
|
|
1216
1252
|
import {
|
|
1217
1253
|
MAX_PROFILE_FILE_LENGTH,
|
|
1218
1254
|
agentFsFileRoute,
|
|
@@ -1228,7 +1264,7 @@ import {
|
|
|
1228
1264
|
parseEnvFlag
|
|
1229
1265
|
} from "./cli-ej6gbxfb.js";
|
|
1230
1266
|
import"./cli-1srmzpr3.js";
|
|
1231
|
-
import"./cli-
|
|
1267
|
+
import"./cli-s69nfyeh.js";
|
|
1232
1268
|
import {
|
|
1233
1269
|
getApiKey,
|
|
1234
1270
|
init_api_key
|
|
@@ -1239,7 +1275,11 @@ import {
|
|
|
1239
1275
|
registerVolatileSecret,
|
|
1240
1276
|
scrubObject,
|
|
1241
1277
|
scrubSecrets
|
|
1242
|
-
} from "./cli-
|
|
1278
|
+
} from "./cli-shk7r5cs.js";
|
|
1279
|
+
import {
|
|
1280
|
+
init_src,
|
|
1281
|
+
isAlias
|
|
1282
|
+
} from "./cli-e3g5mbkz.js";
|
|
1243
1283
|
import {
|
|
1244
1284
|
__require,
|
|
1245
1285
|
__toESM
|
|
@@ -4024,6 +4064,7 @@ async function handleActiveSessions(req, res, pathSegments, queryParams, myAgent
|
|
|
4024
4064
|
init_src();
|
|
4025
4065
|
init_zod();
|
|
4026
4066
|
init_db3();
|
|
4067
|
+
init_model_profiles();
|
|
4027
4068
|
init_db2();
|
|
4028
4069
|
init_telemetry();
|
|
4029
4070
|
init_types();
|
|
@@ -4171,7 +4212,7 @@ var updateAgentRuntimeRoute = route({
|
|
|
4171
4212
|
path: "/api/agents/{id}/runtime",
|
|
4172
4213
|
pattern: ["api", "agents", null, "runtime"],
|
|
4173
4214
|
summary: "Update an agent's runtime harness and default model",
|
|
4174
|
-
description: "Updates `agents.harness_provider` and agent-scoped runtime config. The settings apply to future provider sessions. For `model`, `reasoning_effort`, and `claude.transport`: omit the field to leave it unchanged, send `null` to clear the corresponding override, or send a value to set it. A `model` the model catalog does not list is a 400 unless `allow_custom_model` is true. A `latest:` alias is not accepted here (aliases resolve for a task's model and for `MODEL_TIER_*` values); `reasoning_effort` must be one the harness and model support.",
|
|
4215
|
+
description: "Updates `agents.harness_provider` and agent-scoped runtime config. The settings apply to future provider sessions. For `model`, `reasoning_effort`, and `claude.transport`: omit the field to leave it unchanged, send `null` to clear the corresponding override, or send a value to set it. A `model` the model catalog does not list is a 400 unless `allow_custom_model` is true. A `latest:` alias is not accepted here (aliases resolve for a task's model and for `MODEL_TIER_*` values); `reasoning_effort` must be one the harness and model support. `profile` sets the agent's default model profile (same tri-state): it must exist and run on `harness_provider`, and it cannot be sent with `model`. Sending `model`, or changing the model or effort override by any path, clears the default profile.",
|
|
4175
4216
|
tags: ["Agents"],
|
|
4176
4217
|
params: exports_external.object({ id: exports_external.string() }),
|
|
4177
4218
|
query: exports_external.object({ repoId: exports_external.string().optional() }),
|
|
@@ -4180,6 +4221,7 @@ var updateAgentRuntimeRoute = route({
|
|
|
4180
4221
|
model: exports_external.string().trim().min(1).nullable().optional(),
|
|
4181
4222
|
allow_custom_model: exports_external.boolean().optional().default(false),
|
|
4182
4223
|
reasoning_effort: ReasoningEffortSchema.nullable().optional(),
|
|
4224
|
+
profile: exports_external.string().trim().min(1).nullable().optional(),
|
|
4183
4225
|
acp: AcpRuntimeConfigSchema.optional(),
|
|
4184
4226
|
claude: ClaudeRuntimeConfigSchema.optional()
|
|
4185
4227
|
}),
|
|
@@ -4824,7 +4866,22 @@ async function handleAgentsRest(req, res, pathSegments, queryParams, _myAgentId)
|
|
|
4824
4866
|
const parsed = await updateAgentRuntimeRoute.parse(req, res, pathSegments, queryParams);
|
|
4825
4867
|
if (!parsed)
|
|
4826
4868
|
return true;
|
|
4827
|
-
const { harness_provider, model, allow_custom_model, reasoning_effort, acp, claude } = parsed.body;
|
|
4869
|
+
const { harness_provider, model, allow_custom_model, reasoning_effort, profile, acp, claude } = parsed.body;
|
|
4870
|
+
if (typeof profile === "string") {
|
|
4871
|
+
if (model !== undefined) {
|
|
4872
|
+
jsonError(res, "Send `profile` or `model`, not both", 400);
|
|
4873
|
+
return true;
|
|
4874
|
+
}
|
|
4875
|
+
const picked = await getModelProfileByName(profile);
|
|
4876
|
+
if (!picked) {
|
|
4877
|
+
jsonError(res, `Unknown model profile "${profile}"`, 400);
|
|
4878
|
+
return true;
|
|
4879
|
+
}
|
|
4880
|
+
if (picked.harness !== harness_provider) {
|
|
4881
|
+
jsonError(res, `Model profile "${profile}" runs on the ${picked.harness} harness, not ${harness_provider}`, 400);
|
|
4882
|
+
return true;
|
|
4883
|
+
}
|
|
4884
|
+
}
|
|
4828
4885
|
if (acp && harness_provider !== "acp") {
|
|
4829
4886
|
jsonError(res, "ACP configuration requires harness_provider=acp", 400);
|
|
4830
4887
|
return true;
|
|
@@ -4901,6 +4958,18 @@ async function handleAgentsRest(req, res, pathSegments, queryParams, _myAgentId)
|
|
|
4901
4958
|
description: "Set via PATCH /api/agents/{id}/runtime"
|
|
4902
4959
|
});
|
|
4903
4960
|
}
|
|
4961
|
+
if (model !== undefined || profile === null) {
|
|
4962
|
+
await deleteSwarmConfigByKey("agent", parsed.params.id, MODEL_PROFILE_CONFIG_KEY);
|
|
4963
|
+
} else if (profile !== undefined) {
|
|
4964
|
+
await upsertSwarmConfig({
|
|
4965
|
+
scope: "agent",
|
|
4966
|
+
scopeId: parsed.params.id,
|
|
4967
|
+
key: MODEL_PROFILE_CONFIG_KEY,
|
|
4968
|
+
value: profile,
|
|
4969
|
+
description: "Default model profile, set via PATCH /api/agents/{id}/runtime",
|
|
4970
|
+
exportToEnv: false
|
|
4971
|
+
});
|
|
4972
|
+
}
|
|
4904
4973
|
if (claude?.transport === null) {
|
|
4905
4974
|
await deleteSwarmConfigByKey("agent", parsed.params.id, "CLAUDE_TRANSPORT");
|
|
4906
4975
|
} else if (claude?.transport !== undefined) {
|
|
@@ -4987,6 +5056,10 @@ async function handleAgentsRest(req, res, pathSegments, queryParams, _myAgentId)
|
|
|
4987
5056
|
jsonError(res, error.message, 400);
|
|
4988
5057
|
return true;
|
|
4989
5058
|
}
|
|
5059
|
+
if (modelProfileRefError(error) === "missing") {
|
|
5060
|
+
jsonError(res, `Unknown model profile "${profile}"`, 400);
|
|
5061
|
+
return true;
|
|
5062
|
+
}
|
|
4990
5063
|
throw error;
|
|
4991
5064
|
}
|
|
4992
5065
|
if (!agent) {
|
|
@@ -7487,6 +7560,7 @@ init_secret_scrubber();
|
|
|
7487
7560
|
// src/http/config.ts
|
|
7488
7561
|
init_zod();
|
|
7489
7562
|
init_db3();
|
|
7563
|
+
init_model_profiles();
|
|
7490
7564
|
|
|
7491
7565
|
// src/be/org-profile.ts
|
|
7492
7566
|
init_org_website();
|
|
@@ -7522,14 +7596,23 @@ init_request_auth_context();
|
|
|
7522
7596
|
init_secret_scrubber();
|
|
7523
7597
|
var MAX_ENV_PRESENCE_KEYS = 200;
|
|
7524
7598
|
var SECRETS_FORCE_MASK_NOTE = " (secret values masked: this token cannot read secrets in this scope)";
|
|
7525
|
-
var API_ONLY_CONFIG_KEYS = new Set(["API_AGENT_FS_API_KEY", "SLACK_SIGNING_SECRET"]);
|
|
7526
7599
|
function stripApiOnlyKeys(configs) {
|
|
7527
|
-
return configs.filter((config) => !
|
|
7600
|
+
return configs.filter((config) => !isApiOnlyConfigKey(config.key) && !isInternalConfigKey(config.key));
|
|
7528
7601
|
}
|
|
7529
7602
|
function singleHeader(req, name) {
|
|
7530
7603
|
const raw = req.headers[name];
|
|
7531
7604
|
return Array.isArray(raw) ? raw[0] : raw;
|
|
7532
7605
|
}
|
|
7606
|
+
function readsRepoSecrets(auth, agent) {
|
|
7607
|
+
if (auth?.kind !== "agent" || !auth.runtimeId)
|
|
7608
|
+
return true;
|
|
7609
|
+
return can({
|
|
7610
|
+
principal: { kind: "agent", agentId: auth.agentId, isLead: agent?.isLead ?? false },
|
|
7611
|
+
verb: "config.read.secrets",
|
|
7612
|
+
resource: { kind: "none" },
|
|
7613
|
+
source: "http"
|
|
7614
|
+
}).allow;
|
|
7615
|
+
}
|
|
7533
7616
|
async function requestSecretReadScope(req, resolvedScope) {
|
|
7534
7617
|
const auth = getRequestAuth(req);
|
|
7535
7618
|
if (auth?.kind === "operator")
|
|
@@ -7566,6 +7649,9 @@ async function resolveSecretsRead(req, includeSecrets, resolvedScope) {
|
|
|
7566
7649
|
};
|
|
7567
7650
|
}
|
|
7568
7651
|
async function ensureConfigAdmin(req, res, verb) {
|
|
7652
|
+
return ensureHttpPermission(req, res, verb, verb === "config.write.any" ? "Writing swarm config requires the lead agent" : "Deleting swarm config requires the lead agent");
|
|
7653
|
+
}
|
|
7654
|
+
async function ensureHttpPermission(req, res, verb, forbiddenMessage) {
|
|
7569
7655
|
const auth = getRequestAuth(req);
|
|
7570
7656
|
if (auth?.kind === "operator" || auth?.kind === "user")
|
|
7571
7657
|
return true;
|
|
@@ -7582,7 +7668,7 @@ async function ensureConfigAdmin(req, res, verb) {
|
|
|
7582
7668
|
source: "http"
|
|
7583
7669
|
});
|
|
7584
7670
|
if (!decision.allow) {
|
|
7585
|
-
jsonError(res,
|
|
7671
|
+
jsonError(res, forbiddenMessage, 403);
|
|
7586
7672
|
return false;
|
|
7587
7673
|
}
|
|
7588
7674
|
return true;
|
|
@@ -7700,7 +7786,8 @@ var upsertConfig = route({
|
|
|
7700
7786
|
value: exports_external.unknown(),
|
|
7701
7787
|
isSecret: exports_external.boolean().optional(),
|
|
7702
7788
|
envPath: exports_external.string().nullish(),
|
|
7703
|
-
description: exports_external.string().nullish()
|
|
7789
|
+
description: exports_external.string().nullish(),
|
|
7790
|
+
exportToEnv: exports_external.boolean().optional().describe("Export this row into agent process env (default true on create, false for a few server-side secrets; omitted on update keeps the stored flag). Rejected as true for keys only the API server reads.")
|
|
7704
7791
|
}),
|
|
7705
7792
|
responses: {
|
|
7706
7793
|
200: { description: "Config entry upserted", schema: SwarmConfigSchema },
|
|
@@ -7839,7 +7926,7 @@ async function handleConfig(req, res, pathSegments, queryParams) {
|
|
|
7839
7926
|
return true;
|
|
7840
7927
|
if (!await ensureConfigAdmin(req, res, "config.write.any"))
|
|
7841
7928
|
return true;
|
|
7842
|
-
const { scope, scopeId, key, value, isSecret, envPath, description } = parsed.body;
|
|
7929
|
+
const { scope, scopeId, key, value, isSecret, envPath, description, exportToEnv } = parsed.body;
|
|
7843
7930
|
if (scope === "global" && scopeId) {
|
|
7844
7931
|
jsonError(res, "Global scope must not have scopeId", 400);
|
|
7845
7932
|
return true;
|
|
@@ -7856,7 +7943,7 @@ async function handleConfig(req, res, pathSegments, queryParams) {
|
|
|
7856
7943
|
jsonError(res, internalConfigKeyError(key).message, 400);
|
|
7857
7944
|
return true;
|
|
7858
7945
|
}
|
|
7859
|
-
const validationError = validateConfigValue(key, value);
|
|
7946
|
+
const validationError = validateConfigValue(key, value) ?? validateExportToEnv(key, exportToEnv);
|
|
7860
7947
|
if (validationError) {
|
|
7861
7948
|
jsonError(res, validationError, 400);
|
|
7862
7949
|
return true;
|
|
@@ -7870,7 +7957,8 @@ async function handleConfig(req, res, pathSegments, queryParams) {
|
|
|
7870
7957
|
value: String(value),
|
|
7871
7958
|
isSecret: isSecret || false,
|
|
7872
7959
|
envPath: envPath || null,
|
|
7873
|
-
description: description || null
|
|
7960
|
+
description: description || null,
|
|
7961
|
+
exportToEnv
|
|
7874
7962
|
});
|
|
7875
7963
|
if (scope === "global" && key === "SWARM_ORG_WEBSITE") {
|
|
7876
7964
|
await fillOrgLogoFromWebsite(String(value));
|
|
@@ -7880,7 +7968,11 @@ async function handleConfig(req, res, pathSegments, queryParams) {
|
|
|
7880
7968
|
}
|
|
7881
7969
|
const result = includeSecrets || !config.isSecret ? config : maskSecrets([config])[0];
|
|
7882
7970
|
upsertConfig.respond(res, 200, result);
|
|
7883
|
-
} catch (
|
|
7971
|
+
} catch (error) {
|
|
7972
|
+
if (modelProfileRefError(error) === "missing") {
|
|
7973
|
+
jsonError(res, `Unknown model profile "${String(value)}"`, 400);
|
|
7974
|
+
return true;
|
|
7975
|
+
}
|
|
7884
7976
|
jsonError(res, "Failed to upsert config", 500);
|
|
7885
7977
|
}
|
|
7886
7978
|
return true;
|
|
@@ -8428,7 +8520,7 @@ async function sendReviewBatch(input, deps = {}) {
|
|
|
8428
8520
|
const target = targets[index];
|
|
8429
8521
|
const isRepair = index >= wonIds.length;
|
|
8430
8522
|
if (reply.status === "rejected") {
|
|
8431
|
-
console.warn(scrubSecrets(`[comb] sent reply failed comment=${target.id} task=${target.taskId}: ${
|
|
8523
|
+
console.warn(scrubSecrets(`[comb] sent reply failed comment=${target.id} task=${target.taskId}: ${errorMessage2(reply.reason)}`));
|
|
8432
8524
|
if (isRepair)
|
|
8433
8525
|
skipped.push(alreadySent(target.id, target.taskId));
|
|
8434
8526
|
} else if (isRepair) {
|
|
@@ -8459,7 +8551,7 @@ async function readComment(agentFs, drive, versionOf, id) {
|
|
|
8459
8551
|
} catch (error) {
|
|
8460
8552
|
if (error instanceof FilesError && error.code === "NotFound")
|
|
8461
8553
|
return { id, thread: null };
|
|
8462
|
-
throw new ReviewBatchError(502, scrubSecrets(`agent-fs could not read comment ${id}: ${
|
|
8554
|
+
throw new ReviewBatchError(502, scrubSecrets(`agent-fs could not read comment ${id}: ${errorMessage2(error)}`));
|
|
8463
8555
|
}
|
|
8464
8556
|
const { comment } = thread;
|
|
8465
8557
|
const version = comment.fileVersion ?? (comment.parentId ? undefined : await versionOf(comment));
|
|
@@ -8519,7 +8611,7 @@ async function settleClaims(ids, claim, next) {
|
|
|
8519
8611
|
settled.forEach((result, index) => {
|
|
8520
8612
|
if (result.status === "fulfilled" && result.value)
|
|
8521
8613
|
return;
|
|
8522
|
-
const why = result.status === "rejected" ?
|
|
8614
|
+
const why = result.status === "rejected" ? errorMessage2(result.reason) : "another send holds the claim";
|
|
8523
8615
|
console.warn(scrubSecrets(`[comb] could not ${next ? "record" : "release"} the claim on comment=${ids[index]}: ${why}`));
|
|
8524
8616
|
});
|
|
8525
8617
|
}
|
|
@@ -8621,7 +8713,7 @@ async function requesterLabel(userId) {
|
|
|
8621
8713
|
return "an operator (swarm API key)";
|
|
8622
8714
|
return user.email ? `${user.name} <${user.email}>` : user.name;
|
|
8623
8715
|
}
|
|
8624
|
-
function
|
|
8716
|
+
function errorMessage2(error) {
|
|
8625
8717
|
return error instanceof Error ? error.message : String(error);
|
|
8626
8718
|
}
|
|
8627
8719
|
|
|
@@ -8828,64 +8920,6 @@ async function handleContext(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
8828
8920
|
return false;
|
|
8829
8921
|
}
|
|
8830
8922
|
|
|
8831
|
-
// src/http/ecosystem.ts
|
|
8832
|
-
init_zod();
|
|
8833
|
-
init_db3();
|
|
8834
|
-
var EcosystemAppSchema = exports_external.object({
|
|
8835
|
-
name: exports_external.string(),
|
|
8836
|
-
script: exports_external.string(),
|
|
8837
|
-
cwd: exports_external.string().optional(),
|
|
8838
|
-
interpreter: exports_external.string().optional(),
|
|
8839
|
-
args: exports_external.array(exports_external.string()).optional(),
|
|
8840
|
-
env: exports_external.record(exports_external.string(), exports_external.string()).optional()
|
|
8841
|
-
});
|
|
8842
|
-
var getEcosystem = route({
|
|
8843
|
-
method: "get",
|
|
8844
|
-
path: "/ecosystem",
|
|
8845
|
-
pattern: ["ecosystem"],
|
|
8846
|
-
summary: "Get PM2 ecosystem config for agent services",
|
|
8847
|
-
tags: ["Ecosystem"],
|
|
8848
|
-
auth: { apiKey: true, agentId: true },
|
|
8849
|
-
responses: {
|
|
8850
|
-
200: {
|
|
8851
|
-
description: "PM2 ecosystem config",
|
|
8852
|
-
schema: exports_external.object({ apps: exports_external.array(EcosystemAppSchema) })
|
|
8853
|
-
},
|
|
8854
|
-
400: { description: "Missing X-Agent-ID" }
|
|
8855
|
-
}
|
|
8856
|
-
});
|
|
8857
|
-
async function handleEcosystem(req, res, pathSegments, myAgentId) {
|
|
8858
|
-
if (getEcosystem.match(req.method, pathSegments)) {
|
|
8859
|
-
if (!myAgentId) {
|
|
8860
|
-
jsonError(res, "Missing X-Agent-ID header", 400);
|
|
8861
|
-
return true;
|
|
8862
|
-
}
|
|
8863
|
-
const services = await getServicesByAgentId(myAgentId);
|
|
8864
|
-
const ecosystem = {
|
|
8865
|
-
apps: services.filter((s) => s.script).map((s) => {
|
|
8866
|
-
const app = {
|
|
8867
|
-
name: s.name,
|
|
8868
|
-
script: s.script
|
|
8869
|
-
};
|
|
8870
|
-
if (s.cwd)
|
|
8871
|
-
app.cwd = s.cwd;
|
|
8872
|
-
if (s.interpreter)
|
|
8873
|
-
app.interpreter = s.interpreter;
|
|
8874
|
-
if (s.args && s.args.length > 0)
|
|
8875
|
-
app.args = s.args;
|
|
8876
|
-
if (s.env && Object.keys(s.env).length > 0)
|
|
8877
|
-
app.env = s.env;
|
|
8878
|
-
if (s.port)
|
|
8879
|
-
app.env = { ...app.env || {}, PORT: String(s.port) };
|
|
8880
|
-
return app;
|
|
8881
|
-
})
|
|
8882
|
-
};
|
|
8883
|
-
getEcosystem.respond(res, 200, ecosystem);
|
|
8884
|
-
return true;
|
|
8885
|
-
}
|
|
8886
|
-
return false;
|
|
8887
|
-
}
|
|
8888
|
-
|
|
8889
8923
|
// src/http/events.ts
|
|
8890
8924
|
init_zod();
|
|
8891
8925
|
init_types();
|
|
@@ -9107,6 +9141,7 @@ declare module "swarm-extension" {
|
|
|
9107
9141
|
| "linear"
|
|
9108
9142
|
| "jira"
|
|
9109
9143
|
| "comb"
|
|
9144
|
+
| "chat"
|
|
9110
9145
|
| undefined;
|
|
9111
9146
|
routingReason?:
|
|
9112
9147
|
| "skill"
|
|
@@ -9147,6 +9182,7 @@ declare module "swarm-extension" {
|
|
|
9147
9182
|
model?: string | undefined;
|
|
9148
9183
|
modelTier?: "smol" | "regular" | "smart" | "ultra" | undefined;
|
|
9149
9184
|
effort?: "off" | "low" | "medium" | "high" | "xhigh" | "max" | undefined;
|
|
9185
|
+
profile?: string | undefined;
|
|
9150
9186
|
scheduleId?: string | undefined;
|
|
9151
9187
|
workflowRunId?: string | undefined;
|
|
9152
9188
|
workflowRunStepId?: string | undefined;
|
|
@@ -9220,7 +9256,8 @@ declare module "swarm-extension" {
|
|
|
9220
9256
|
| "system"
|
|
9221
9257
|
| "linear"
|
|
9222
9258
|
| "jira"
|
|
9223
|
-
| "comb"
|
|
9259
|
+
| "comb"
|
|
9260
|
+
| "chat";
|
|
9224
9261
|
tags: string[];
|
|
9225
9262
|
priority: number;
|
|
9226
9263
|
dependsOn: string[];
|
|
@@ -9279,6 +9316,9 @@ declare module "swarm-extension" {
|
|
|
9279
9316
|
resolvedModel?: string | undefined;
|
|
9280
9317
|
modelSource?: string | undefined;
|
|
9281
9318
|
modelAlias?: string | undefined;
|
|
9319
|
+
resolvedEffort?: "off" | "low" | "medium" | "high" | "xhigh" | "max" | undefined;
|
|
9320
|
+
profile?: string | undefined;
|
|
9321
|
+
resolvedProfile?: string | undefined;
|
|
9282
9322
|
scheduleId?: string | undefined;
|
|
9283
9323
|
workflowRunId?: string | null | undefined;
|
|
9284
9324
|
workflowRunStepId?: string | null | undefined;
|
|
@@ -9555,6 +9595,43 @@ declare module "swarm-extension" {
|
|
|
9555
9595
|
kind: "broadcast";
|
|
9556
9596
|
};
|
|
9557
9597
|
}
|
|
9598
|
+
/** A Chat SDK message after its thread buffer flushed, before routing. */
|
|
9599
|
+
export interface ChatRouteEvent {
|
|
9600
|
+
/** Adapter name, such as \`telegram\` or \`slack\`. */
|
|
9601
|
+
adapter: string;
|
|
9602
|
+
channelId: string;
|
|
9603
|
+
/** The Chat SDK thread id, prefixed with the adapter name. */
|
|
9604
|
+
threadId: string;
|
|
9605
|
+
/** The first buffered sender. */
|
|
9606
|
+
userId: string;
|
|
9607
|
+
userName?: string;
|
|
9608
|
+
/** The buffered messages, joined. */
|
|
9609
|
+
text: string;
|
|
9610
|
+
botMentioned: boolean;
|
|
9611
|
+
isDirectMessage: boolean;
|
|
9612
|
+
/** Set when earlier swarm tasks exist in this thread. */
|
|
9613
|
+
threadContext?: {
|
|
9614
|
+
adapter: string;
|
|
9615
|
+
threadId: string;
|
|
9616
|
+
};
|
|
9617
|
+
}
|
|
9618
|
+
export interface ChatRouteModify {
|
|
9619
|
+
target:
|
|
9620
|
+
| {
|
|
9621
|
+
kind: "agent";
|
|
9622
|
+
agentId: string;
|
|
9623
|
+
}
|
|
9624
|
+
| {
|
|
9625
|
+
kind: "lead";
|
|
9626
|
+
}
|
|
9627
|
+
| {
|
|
9628
|
+
kind: "broadcast";
|
|
9629
|
+
}
|
|
9630
|
+
| {
|
|
9631
|
+
kind: "workflow";
|
|
9632
|
+
workflowId: string;
|
|
9633
|
+
};
|
|
9634
|
+
}
|
|
9558
9635
|
export type HeartbeatClassification = "no-session" | "stale-session" | "fresh-stalled";
|
|
9559
9636
|
export type HeartbeatAction = "supersede-resume" | "fail" | "record";
|
|
9560
9637
|
export interface HeartbeatRemediateEvent {
|
|
@@ -9599,6 +9676,18 @@ declare module "swarm-extension" {
|
|
|
9599
9676
|
threadTs?: string;
|
|
9600
9677
|
taskId?: string;
|
|
9601
9678
|
}
|
|
9679
|
+
/** A Chat SDK message the swarm received or sent. */
|
|
9680
|
+
export interface ChatMessageEvent {
|
|
9681
|
+
adapter: string;
|
|
9682
|
+
channelId: string;
|
|
9683
|
+
threadId: string;
|
|
9684
|
+
/** The sender, for an inbound message. */
|
|
9685
|
+
userId?: string;
|
|
9686
|
+
text: string;
|
|
9687
|
+
direction: "inbound" | "outbound";
|
|
9688
|
+
taskId?: string;
|
|
9689
|
+
messageId?: string;
|
|
9690
|
+
}
|
|
9602
9691
|
/**
|
|
9603
9692
|
* A person answered an approval request. Fires for workflow and standalone
|
|
9604
9693
|
* requests. Timeouts and cancellations do not emit it today.
|
|
@@ -9707,6 +9796,11 @@ declare module "swarm-extension" {
|
|
|
9707
9796
|
modify: SlackRouteModify;
|
|
9708
9797
|
result: PreResult<SlackRouteModify>;
|
|
9709
9798
|
};
|
|
9799
|
+
"pre.chat.route": {
|
|
9800
|
+
event: ChatRouteEvent;
|
|
9801
|
+
modify: ChatRouteModify;
|
|
9802
|
+
result: PreResult<ChatRouteModify>;
|
|
9803
|
+
};
|
|
9710
9804
|
"pre.heartbeat.remediate": {
|
|
9711
9805
|
event: HeartbeatRemediateEvent;
|
|
9712
9806
|
modify: HeartbeatRemediateModify;
|
|
@@ -9752,6 +9846,11 @@ declare module "swarm-extension" {
|
|
|
9752
9846
|
modify: never;
|
|
9753
9847
|
result: void;
|
|
9754
9848
|
};
|
|
9849
|
+
"post.chat.message": {
|
|
9850
|
+
event: ChatMessageEvent;
|
|
9851
|
+
modify: never;
|
|
9852
|
+
result: void;
|
|
9853
|
+
};
|
|
9755
9854
|
"post.approval.resolved": {
|
|
9756
9855
|
event: ApprovalResolvedEvent;
|
|
9757
9856
|
modify: never;
|
|
@@ -9899,6 +9998,512 @@ void _configIsValid;
|
|
|
9899
9998
|
|
|
9900
9999
|
// src/extensions/catalog.generated.json
|
|
9901
10000
|
var catalog_generated_default = {
|
|
10001
|
+
"comms-adherence-gate": {
|
|
10002
|
+
manifestFile: "manifest.json",
|
|
10003
|
+
manifest: {
|
|
10004
|
+
$schema: "../manifest.schema.json",
|
|
10005
|
+
name: "comms-adherence-gate",
|
|
10006
|
+
description: "Judges every Slack-facing store-progress completion against the requester's comms preferences and latest request with Jev (TypeSafe or OpenRouter), and in enforce blocks a non-adherent completion once per task",
|
|
10007
|
+
version: "1.5.0",
|
|
10008
|
+
runtime: "api",
|
|
10009
|
+
assets: {
|
|
10010
|
+
hooks: "hooks.ts"
|
|
10011
|
+
}
|
|
10012
|
+
},
|
|
10013
|
+
files: {
|
|
10014
|
+
"hooks.ts": `import { type ApiCtx, block, type SwarmExtension } from "swarm-extension";
|
|
10015
|
+
import { z } from "zod";
|
|
10016
|
+
|
|
10017
|
+
/**
|
|
10018
|
+
* comms-adherence-gate
|
|
10019
|
+
*
|
|
10020
|
+
* When an agent calls \`store-progress\` with status "completed" on a task that
|
|
10021
|
+
* reaches a human in Slack, judge the \`output\` against the requesting user's
|
|
10022
|
+
* stored comms preferences (users.metadata.$.comms) with Jev, and against what
|
|
10023
|
+
* that user actually asked for in their latest message. An explicit request
|
|
10024
|
+
* ("tell me a long joke", "explain in detail") outranks the stored default, so
|
|
10025
|
+
* a requested long answer is not a verbosity failure.
|
|
10026
|
+
*
|
|
10027
|
+
* Transport (config.provider): Jev is served by TypeSafe directly and by
|
|
10028
|
+
* OpenRouter's Decisions endpoint. Both take the same {model, state, questions}
|
|
10029
|
+
* body and return the same \`answers\` shape. \`auto\` uses TYPESAFE_API_KEY when
|
|
10030
|
+
* set, else OPENROUTER_API_KEY.
|
|
10031
|
+
*
|
|
10032
|
+
* modes (config.mode):
|
|
10033
|
+
* off -> never evaluate
|
|
10034
|
+
* shadow -> evaluate + record, never block (default)
|
|
10035
|
+
* enforce -> record, and block the completion up to maxBlocksPerTask times
|
|
10036
|
+
* per task with actionable feedback
|
|
10037
|
+
*
|
|
10038
|
+
* Fails OPEN on every error: a judge outage must never wall the swarm.
|
|
10039
|
+
* Every verdict lands in KV namespace \`ext:comms-gate\`.
|
|
10040
|
+
*/
|
|
10041
|
+
|
|
10042
|
+
export const config = z.object({
|
|
10043
|
+
mode: z.enum(["off", "shadow", "enforce"]).default("shadow"),
|
|
10044
|
+
/** A dimension scoring below this fails. */
|
|
10045
|
+
threshold: z.number().min(0).max(1).default(0.5),
|
|
10046
|
+
timeoutMs: z.number().int().positive().default(4000),
|
|
10047
|
+
maxBlocksPerTask: z.number().int().min(0).default(1),
|
|
10048
|
+
provider: z.enum(["auto", "typesafe", "openrouter"]).default("auto"),
|
|
10049
|
+
/** Jev model on the TypeSafe transport. */
|
|
10050
|
+
model: z.string().default("jev-latest"),
|
|
10051
|
+
/** Jev model on the OpenRouter transport. */
|
|
10052
|
+
openrouterModel: z.string().default("typesafe/jev-1.13"),
|
|
10053
|
+
/** Explicit TypeSafe key; otherwise TYPESAFE_API_KEY from the API process env. */
|
|
10054
|
+
apiKey: z.string().optional(),
|
|
10055
|
+
});
|
|
10056
|
+
|
|
10057
|
+
type Cfg = z.infer<typeof config>;
|
|
10058
|
+
type Swarm = ApiCtx["swarm"];
|
|
10059
|
+
|
|
10060
|
+
const NS = "ext:comms-gate";
|
|
10061
|
+
const TYPESAFE_ENDPOINT = "https://api.typesafe.ai/v1/systemone";
|
|
10062
|
+
const OPENROUTER_ENDPOINT = "https://openrouter.ai/api/alpha/decisions";
|
|
10063
|
+
const UUID_RE = /^[0-9a-fA-F-]{36}$/;
|
|
10064
|
+
// The dispatcher cancels a handler after 5 s; leave room for the DB read and KV writes.
|
|
10065
|
+
const MAX_TIMEOUT_MS = 4000;
|
|
10066
|
+
|
|
10067
|
+
export const cfgOf = (raw: unknown): Cfg => {
|
|
10068
|
+
const parsed = config.safeParse(raw ?? {});
|
|
10069
|
+
return parsed.success ? parsed.data : config.parse({});
|
|
10070
|
+
};
|
|
10071
|
+
|
|
10072
|
+
// kv keys accept [a-zA-Z0-9._:/%-]
|
|
10073
|
+
const safeKey = (raw: string | undefined | null): string =>
|
|
10074
|
+
(raw && raw.length > 0 ? raw : "unknown").replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 120);
|
|
10075
|
+
|
|
10076
|
+
/** SDK envelopes: {success, data:{...}} or the bare payload. */
|
|
10077
|
+
type Envelope = {
|
|
10078
|
+
data?: { rows?: unknown[]; value?: unknown };
|
|
10079
|
+
rows?: unknown[];
|
|
10080
|
+
value?: unknown;
|
|
10081
|
+
} | null;
|
|
10082
|
+
|
|
10083
|
+
const cell = (row: unknown, i: number, key: string): unknown =>
|
|
10084
|
+
Array.isArray(row) ? row[i] : (row as Record<string, unknown>)?.[key];
|
|
10085
|
+
|
|
10086
|
+
/**
|
|
10087
|
+
* The newest human message sits at the END of a Slack task: the
|
|
10088
|
+
* \`<thread_context>\` blob comes first and can run to thousands of chars, so a
|
|
10089
|
+
* leading slice of \`agent_tasks.task\` shows the judge everything except the
|
|
10090
|
+
* actual request. Take the tail, then the part after the last \`</thread_context>\`.
|
|
10091
|
+
*/
|
|
10092
|
+
const CTX_END = "</thread_context>";
|
|
10093
|
+
export const latestAsk = (tail: string): string => {
|
|
10094
|
+
const end = tail.lastIndexOf(CTX_END);
|
|
10095
|
+
let out = (end >= 0 ? tail.slice(end + CTX_END.length) : tail)
|
|
10096
|
+
.replace(/\\[Thread follow-up[^\\]]*\\]/g, "")
|
|
10097
|
+
.trim();
|
|
10098
|
+
// Some tasks end with the context block; fall back to the last messages in it.
|
|
10099
|
+
if (out.length < 8) out = (end >= 0 ? tail.slice(0, end) : tail).trim().slice(-800);
|
|
10100
|
+
return out.slice(0, 900);
|
|
10101
|
+
};
|
|
10102
|
+
|
|
10103
|
+
// Preferences are the DEFAULT. A direct request outranks them.
|
|
10104
|
+
const JUDGING_RULES = [
|
|
10105
|
+
"The requester's explicit instruction in latest_request OUTRANKS requester_preferences. Preferences describe the DEFAULT; they never override a direct request.",
|
|
10106
|
+
"If latest_request asks for something long, detailed, creative, a story, a joke, a full explanation or a document, then length and structure serving that request are CORRECT and must NOT be penalized.",
|
|
10107
|
+
"Judge whether agent_message serves what latest_request actually asked for, written in the requester's manner. Do not judge it against the default preference when the request overrides it.",
|
|
10108
|
+
];
|
|
10109
|
+
|
|
10110
|
+
type Prefs = { verbosity?: unknown; tone?: unknown; language?: unknown } & Record<string, unknown>;
|
|
10111
|
+
|
|
10112
|
+
/** One \`noul\` question per stated preference the gate knows how to judge. */
|
|
10113
|
+
export const buildQuestions = (prefs: Prefs): Record<string, unknown> => {
|
|
10114
|
+
const questions: Record<string, unknown> = {};
|
|
10115
|
+
if (prefs.verbosity) {
|
|
10116
|
+
questions.verbosity = {
|
|
10117
|
+
type: "noul",
|
|
10118
|
+
instructions:
|
|
10119
|
+
"Does agent_message respect the requester's verbosity preference, AS OVERRIDDEN by any explicit instruction in latest_request? Apply judging_rules.",
|
|
10120
|
+
criteria: {
|
|
10121
|
+
true: "Length and structure fit what latest_request asked for (or the stated preference when the request does not override it), and it leads with the result.",
|
|
10122
|
+
false:
|
|
10123
|
+
"Too long for what was asked, or it opens with preamble, recaps the request, narrates the process, or ends with an offer to elaborate. Length explicitly requested in latest_request is NOT a failure.",
|
|
10124
|
+
},
|
|
10125
|
+
};
|
|
10126
|
+
}
|
|
10127
|
+
if (prefs.tone) {
|
|
10128
|
+
questions.tone = {
|
|
10129
|
+
type: "noul",
|
|
10130
|
+
instructions:
|
|
10131
|
+
"Does agent_message match the requester's stated tone preference, as overridden by any explicit instruction in latest_request? Apply judging_rules.",
|
|
10132
|
+
criteria: {
|
|
10133
|
+
true: "The tone matches what the requester asked for.",
|
|
10134
|
+
false:
|
|
10135
|
+
"The tone conflicts with it (for example hedging, flattery or filler when the requester asked for direct and plain). A voice appropriate to an explicitly requested creative or narrative form is NOT a failure.",
|
|
10136
|
+
},
|
|
10137
|
+
};
|
|
10138
|
+
}
|
|
10139
|
+
if (prefs.language) {
|
|
10140
|
+
questions.language = {
|
|
10141
|
+
type: "noul",
|
|
10142
|
+
instructions: "Is agent_message written in the requester's stated language?",
|
|
10143
|
+
criteria: {
|
|
10144
|
+
true: "Written in the stated language.",
|
|
10145
|
+
false: "Written in a different language.",
|
|
10146
|
+
},
|
|
10147
|
+
};
|
|
10148
|
+
}
|
|
10149
|
+
return questions;
|
|
10150
|
+
};
|
|
10151
|
+
|
|
10152
|
+
const secretCache = new Map<string, { value: string; at: number }>();
|
|
10153
|
+
const KEY_TTL_MS = 5 * 60 * 1000;
|
|
10154
|
+
const usable = (v: string): boolean => Boolean(v) && !v.startsWith("[REDACTED");
|
|
10155
|
+
const envOf = (name: string): string => String(process.env[name] ?? "");
|
|
10156
|
+
|
|
10157
|
+
/**
|
|
10158
|
+
* Resolve a secret: explicit value, then the API process env. The API process loads
|
|
10159
|
+
* global swarm_config rows into process.env at boot and on config reload.
|
|
10160
|
+
* ctx.swarm.config_get cannot serve it: the SDK scrubs secrets to "[REDACTED:<name>]".
|
|
10161
|
+
*/
|
|
10162
|
+
const secret = (name: string, explicit?: string): string => {
|
|
10163
|
+
const cached = secretCache.get(name);
|
|
10164
|
+
if (cached && Date.now() - cached.at < KEY_TTL_MS) return cached.value;
|
|
10165
|
+
const value = [String(explicit ?? ""), envOf(name)].find(usable) ?? "";
|
|
10166
|
+
if (usable(value)) secretCache.set(name, { value, at: Date.now() });
|
|
10167
|
+
return value;
|
|
10168
|
+
};
|
|
10169
|
+
|
|
10170
|
+
/** Test hook: forget cached secrets. */
|
|
10171
|
+
export const resetKeyCache = (): void => {
|
|
10172
|
+
secretCache.clear();
|
|
10173
|
+
};
|
|
10174
|
+
|
|
10175
|
+
export type JevTransport = {
|
|
10176
|
+
provider: "typesafe" | "openrouter";
|
|
10177
|
+
endpoint: string;
|
|
10178
|
+
key: string;
|
|
10179
|
+
model: string;
|
|
10180
|
+
};
|
|
10181
|
+
|
|
10182
|
+
/**
|
|
10183
|
+
* Pick the Jev transport. \`auto\` prefers TypeSafe and falls back to OpenRouter.
|
|
10184
|
+
* Throws when the chosen provider has no usable key; the handler fails open.
|
|
10185
|
+
*/
|
|
10186
|
+
export const resolveTransport = (cfg: Cfg): JevTransport => {
|
|
10187
|
+
const typesafe = (): JevTransport | null => {
|
|
10188
|
+
const key = secret("TYPESAFE_API_KEY", cfg.apiKey);
|
|
10189
|
+
return usable(key)
|
|
10190
|
+
? { provider: "typesafe", endpoint: TYPESAFE_ENDPOINT, key, model: cfg.model }
|
|
10191
|
+
: null;
|
|
10192
|
+
};
|
|
10193
|
+
const openrouter = (): JevTransport | null => {
|
|
10194
|
+
const key = secret("OPENROUTER_API_KEY");
|
|
10195
|
+
return usable(key)
|
|
10196
|
+
? { provider: "openrouter", endpoint: OPENROUTER_ENDPOINT, key, model: cfg.openrouterModel }
|
|
10197
|
+
: null;
|
|
10198
|
+
};
|
|
10199
|
+
const transport =
|
|
10200
|
+
cfg.provider === "typesafe"
|
|
10201
|
+
? typesafe()
|
|
10202
|
+
: cfg.provider === "openrouter"
|
|
10203
|
+
? openrouter()
|
|
10204
|
+
: (typesafe() ?? openrouter());
|
|
10205
|
+
if (transport) return transport;
|
|
10206
|
+
throw new Error(
|
|
10207
|
+
cfg.provider === "typesafe"
|
|
10208
|
+
? "no TYPESAFE_API_KEY"
|
|
10209
|
+
: cfg.provider === "openrouter"
|
|
10210
|
+
? "no OPENROUTER_API_KEY"
|
|
10211
|
+
: "no TYPESAFE_API_KEY or OPENROUTER_API_KEY",
|
|
10212
|
+
);
|
|
10213
|
+
};
|
|
10214
|
+
|
|
10215
|
+
/** One Jev call; returns the \`noul\` score per question. Throws on any failure. */
|
|
10216
|
+
export const askJev = async (
|
|
10217
|
+
state: Record<string, unknown>,
|
|
10218
|
+
questions: Record<string, unknown>,
|
|
10219
|
+
transport: JevTransport,
|
|
10220
|
+
cfg: Cfg,
|
|
10221
|
+
outer?: AbortSignal,
|
|
10222
|
+
): Promise<{ scores: Record<string, number>; latencyMs: number }> => {
|
|
10223
|
+
const controller = new AbortController();
|
|
10224
|
+
const timer = setTimeout(() => controller.abort(), Math.min(cfg.timeoutMs, MAX_TIMEOUT_MS));
|
|
10225
|
+
const onOuterAbort = () => controller.abort();
|
|
10226
|
+
outer?.addEventListener("abort", onOuterAbort);
|
|
10227
|
+
const t0 = Date.now();
|
|
10228
|
+
try {
|
|
10229
|
+
const res = await fetch(transport.endpoint, {
|
|
10230
|
+
method: "POST",
|
|
10231
|
+
headers: { "Content-Type": "application/json", Authorization: \`Bearer \${transport.key}\` },
|
|
10232
|
+
body: JSON.stringify({ state, model: transport.model, questions }),
|
|
10233
|
+
signal: controller.signal,
|
|
10234
|
+
});
|
|
10235
|
+
if (!res.ok) throw new Error(\`\${transport.provider} \${res.status}\`);
|
|
10236
|
+
const body = (await res.json()) as { answers?: Record<string, { noul?: unknown }> } | null;
|
|
10237
|
+
const answers = body?.answers;
|
|
10238
|
+
if (!answers || typeof answers !== "object") throw new Error("no answers in response");
|
|
10239
|
+
const scores: Record<string, number> = {};
|
|
10240
|
+
for (const [id, a] of Object.entries(answers)) {
|
|
10241
|
+
if (typeof a?.noul === "number") scores[id] = a.noul;
|
|
10242
|
+
}
|
|
10243
|
+
if (Object.keys(scores).length === 0) throw new Error("no scores in response");
|
|
10244
|
+
return { scores, latencyMs: Date.now() - t0 };
|
|
10245
|
+
} finally {
|
|
10246
|
+
clearTimeout(timer);
|
|
10247
|
+
outer?.removeEventListener("abort", onOuterAbort);
|
|
10248
|
+
}
|
|
10249
|
+
};
|
|
10250
|
+
|
|
10251
|
+
const SELFTEST = "comms-gate-selftest";
|
|
10252
|
+
const SELFTEST_PREFS = {
|
|
10253
|
+
tone: "direct, plain, no hedging",
|
|
10254
|
+
language: "English",
|
|
10255
|
+
verbosity:
|
|
10256
|
+
"very terse - answer in the first sentence, ~400 chars max. No preamble, no recap, no closing offer.",
|
|
10257
|
+
};
|
|
10258
|
+
const SELFTEST_BAD =
|
|
10259
|
+
"Great question! Let me walk you through what I did here. First I looked at the task, then I reviewed the relevant files, and after careful consideration I decided to go with option B. I hope this helps - let me know if you would like me to elaborate further on any of the above!";
|
|
10260
|
+
const SELFTEST_GOOD =
|
|
10261
|
+
"Went with option B: it is the only one that keeps the migration reversible. PR #123.";
|
|
10262
|
+
|
|
10263
|
+
/**
|
|
10264
|
+
* Run the judge on a known-bad (positive control, must score below threshold) and a
|
|
10265
|
+
* known-good (negative control, must score at or above it) message and record both
|
|
10266
|
+
* under \`selftest:last\`. Proves key, egress and KV from inside the API runtime
|
|
10267
|
+
* without touching real traffic.
|
|
10268
|
+
*/
|
|
10269
|
+
const selfTest = async (swarm: Swarm, cfg: Cfg, signal?: AbortSignal): Promise<void> => {
|
|
10270
|
+
const started = Date.now();
|
|
10271
|
+
try {
|
|
10272
|
+
const transport = resolveTransport(cfg);
|
|
10273
|
+
const questions = { verbosity: buildQuestions(SELFTEST_PREFS).verbosity };
|
|
10274
|
+
const judge = (message: string) =>
|
|
10275
|
+
askJev(
|
|
10276
|
+
{ requester_preferences: SELFTEST_PREFS, agent_message: message },
|
|
10277
|
+
questions,
|
|
10278
|
+
transport,
|
|
10279
|
+
cfg,
|
|
10280
|
+
signal,
|
|
10281
|
+
).then((r) => r.scores.verbosity ?? null);
|
|
10282
|
+
const [bad, good] = await Promise.all([judge(SELFTEST_BAD), judge(SELFTEST_GOOD)]);
|
|
10283
|
+
await swarm.kv_set({
|
|
10284
|
+
namespace: NS,
|
|
10285
|
+
key: "selftest:last",
|
|
10286
|
+
value: {
|
|
10287
|
+
at: new Date().toISOString(),
|
|
10288
|
+
provider: transport.provider,
|
|
10289
|
+
threshold: cfg.threshold,
|
|
10290
|
+
positiveControlBadScore: bad,
|
|
10291
|
+
negativeControlGoodScore: good,
|
|
10292
|
+
controlsOk:
|
|
10293
|
+
typeof bad === "number" &&
|
|
10294
|
+
typeof good === "number" &&
|
|
10295
|
+
bad < cfg.threshold &&
|
|
10296
|
+
good >= cfg.threshold,
|
|
10297
|
+
latencyMs: Date.now() - started,
|
|
10298
|
+
},
|
|
10299
|
+
});
|
|
10300
|
+
} catch (err) {
|
|
10301
|
+
await swarm
|
|
10302
|
+
.kv_set({
|
|
10303
|
+
namespace: NS,
|
|
10304
|
+
key: "selftest:last",
|
|
10305
|
+
value: {
|
|
10306
|
+
at: new Date().toISOString(),
|
|
10307
|
+
error: String(err).slice(0, 400),
|
|
10308
|
+
latencyMs: Date.now() - started,
|
|
10309
|
+
},
|
|
10310
|
+
})
|
|
10311
|
+
.catch(() => {});
|
|
10312
|
+
}
|
|
10313
|
+
};
|
|
10314
|
+
|
|
10315
|
+
const TASK_SQL = \`SELECT t.source, t.slackThreadTs, t.agentId, t.requestedByUserId,
|
|
10316
|
+
json_extract(u.metadata, '$.comms') AS comms, u.name,
|
|
10317
|
+
substr(t.task, 1, 400) AS askHead,
|
|
10318
|
+
substr(t.task, -1400) AS askTail
|
|
10319
|
+
FROM agent_tasks t
|
|
10320
|
+
LEFT JOIN users u ON u.id = t.requestedByUserId
|
|
10321
|
+
WHERE t.id = ?\`;
|
|
10322
|
+
|
|
10323
|
+
const LEAD_SQL = "SELECT isLead FROM agents WHERE id = ?";
|
|
10324
|
+
|
|
10325
|
+
/**
|
|
10326
|
+
* Mirrors the \`task.progress.write\` rule store-progress enforces
|
|
10327
|
+
* (src/rbac/legacy-policy.ts): the caller owns the task, the task is
|
|
10328
|
+
* unassigned, or the caller is a lead. Lead status comes from the caller's
|
|
10329
|
+
* agents row, never from tool args. Extension callers return before this.
|
|
10330
|
+
*/
|
|
10331
|
+
const mayWriteProgress = async (
|
|
10332
|
+
swarm: Swarm,
|
|
10333
|
+
callerId: string | undefined,
|
|
10334
|
+
taskAgentId: string,
|
|
10335
|
+
): Promise<boolean> => {
|
|
10336
|
+
if (!callerId) return false;
|
|
10337
|
+
if (!taskAgentId || taskAgentId === callerId) return true;
|
|
10338
|
+
const q = (await swarm.db_query({ sql: LEAD_SQL, params: [callerId] })) as Envelope;
|
|
10339
|
+
const row = (q?.data?.rows ?? q?.rows ?? [])[0];
|
|
10340
|
+
return Number(cell(row, 0, "isLead") ?? 0) === 1;
|
|
10341
|
+
};
|
|
10342
|
+
|
|
10343
|
+
const extension: SwarmExtension = (api) => {
|
|
10344
|
+
api.on("pre.tool.call", async (event, ctx) => {
|
|
10345
|
+
if (event.tool !== "store-progress") return;
|
|
10346
|
+
if (event.requestInfo?.callOrigin === "extension") return;
|
|
10347
|
+
|
|
10348
|
+
const cfg = cfgOf(ctx.config);
|
|
10349
|
+
if (cfg.mode === "off") return;
|
|
10350
|
+
|
|
10351
|
+
const args = (event.args ?? {}) as Record<string, unknown>;
|
|
10352
|
+
|
|
10353
|
+
// Self-test hatch: a progress call whose text starts with the sentinel runs
|
|
10354
|
+
// the controls and records them under \`selftest:last\`.
|
|
10355
|
+
if (args.status !== "completed") {
|
|
10356
|
+
if (typeof args.progress === "string" && args.progress.startsWith(SELFTEST)) {
|
|
10357
|
+
await selfTest(ctx.swarm, cfg, ctx.signal);
|
|
10358
|
+
}
|
|
10359
|
+
return;
|
|
10360
|
+
}
|
|
10361
|
+
|
|
10362
|
+
const output = typeof args.output === "string" ? args.output.trim() : "";
|
|
10363
|
+
if (!output) return;
|
|
10364
|
+
|
|
10365
|
+
const taskId = String(args.taskId ?? event.requestInfo?.sourceTaskId ?? "");
|
|
10366
|
+
if (!UUID_RE.test(taskId)) return;
|
|
10367
|
+
|
|
10368
|
+
try {
|
|
10369
|
+
const q = (await ctx.swarm.db_query({ sql: TASK_SQL, params: [taskId] })) as Envelope;
|
|
10370
|
+
const row = (q?.data?.rows ?? q?.rows ?? [])[0];
|
|
10371
|
+
if (!row) return;
|
|
10372
|
+
|
|
10373
|
+
const source = String(cell(row, 0, "source") ?? "");
|
|
10374
|
+
const slackThreadTs = cell(row, 1, "slackThreadTs");
|
|
10375
|
+
const agentId = String(cell(row, 2, "agentId") ?? "");
|
|
10376
|
+
const requesterId = String(cell(row, 3, "requestedByUserId") ?? "");
|
|
10377
|
+
const commsRaw = cell(row, 4, "comms");
|
|
10378
|
+
const requesterName = String(cell(row, 5, "name") ?? requesterId);
|
|
10379
|
+
const threadOpening = String(cell(row, 6, "askHead") ?? "");
|
|
10380
|
+
const ask = latestAsk(String(cell(row, 7, "askTail") ?? ""));
|
|
10381
|
+
|
|
10382
|
+
// Judge only a completion store-progress will accept, so no agent can ship
|
|
10383
|
+
// another task's thread and preferences to the external judge.
|
|
10384
|
+
if (!(await mayWriteProgress(ctx.swarm, event.requestInfo?.agentId, agentId))) return;
|
|
10385
|
+
|
|
10386
|
+
// Only where the text actually reaches a human in Slack.
|
|
10387
|
+
if (source !== "slack" && !slackThreadTs) return;
|
|
10388
|
+
if (!commsRaw) return; // no stated preferences -> nothing to judge against
|
|
10389
|
+
const prefs = (typeof commsRaw === "string" ? JSON.parse(commsRaw) : commsRaw) as Prefs;
|
|
10390
|
+
if (!prefs || typeof prefs !== "object") return;
|
|
10391
|
+
|
|
10392
|
+
const questions = buildQuestions(prefs);
|
|
10393
|
+
if (Object.keys(questions).length === 0) return;
|
|
10394
|
+
|
|
10395
|
+
const transport = resolveTransport(cfg);
|
|
10396
|
+
const { scores, latencyMs } = await askJev(
|
|
10397
|
+
{
|
|
10398
|
+
role: "You audit an AI agent's final message to the human who requested the work.",
|
|
10399
|
+
requester: requesterName,
|
|
10400
|
+
requester_preferences: prefs,
|
|
10401
|
+
latest_request: ask,
|
|
10402
|
+
thread_opening: threadOpening,
|
|
10403
|
+
judging_rules: JUDGING_RULES,
|
|
10404
|
+
agent_message: output,
|
|
10405
|
+
},
|
|
10406
|
+
questions,
|
|
10407
|
+
transport,
|
|
10408
|
+
cfg,
|
|
10409
|
+
ctx.signal,
|
|
10410
|
+
);
|
|
10411
|
+
|
|
10412
|
+
const failed = Object.entries(scores)
|
|
10413
|
+
.filter(([, v]) => v < cfg.threshold)
|
|
10414
|
+
.map(([k]) => k);
|
|
10415
|
+
|
|
10416
|
+
const verdict = {
|
|
10417
|
+
at: new Date().toISOString(),
|
|
10418
|
+
taskId,
|
|
10419
|
+
agentId,
|
|
10420
|
+
source,
|
|
10421
|
+
requester: requesterName,
|
|
10422
|
+
requesterId,
|
|
10423
|
+
provider: transport.provider,
|
|
10424
|
+
scores,
|
|
10425
|
+
failed,
|
|
10426
|
+
threshold: cfg.threshold,
|
|
10427
|
+
mode: cfg.mode,
|
|
10428
|
+
latencyMs,
|
|
10429
|
+
preferences: prefs,
|
|
10430
|
+
askPreview: ask.slice(0, 400),
|
|
10431
|
+
outputPreview: output.slice(0, 700),
|
|
10432
|
+
};
|
|
10433
|
+
|
|
10434
|
+
await ctx.swarm.kv_incr({ namespace: NS, key: "total" });
|
|
10435
|
+
await ctx.swarm.kv_set({ namespace: NS, key: "last", value: verdict });
|
|
10436
|
+
|
|
10437
|
+
if (failed.length === 0) {
|
|
10438
|
+
await ctx.swarm.kv_incr({ namespace: NS, key: "pass" });
|
|
10439
|
+
return;
|
|
10440
|
+
}
|
|
10441
|
+
|
|
10442
|
+
await ctx.swarm.kv_incr({ namespace: NS, key: "fail" });
|
|
10443
|
+
for (const dim of failed) {
|
|
10444
|
+
await ctx.swarm.kv_incr({ namespace: NS, key: \`dim:\${safeKey(dim)}\` });
|
|
10445
|
+
}
|
|
10446
|
+
|
|
10447
|
+
const blocksKey = \`blocks:\${safeKey(taskId)}\`;
|
|
10448
|
+
// kv_get answers {success, data:{value}}, and {success:false} when absent.
|
|
10449
|
+
const prior = (await ctx.swarm.kv_get({ namespace: NS, key: blocksKey })) as Envelope;
|
|
10450
|
+
const priorBlocks = Number(prior?.data?.value ?? prior?.value ?? 0) || 0;
|
|
10451
|
+
const willBlock = cfg.mode === "enforce" && priorBlocks < cfg.maxBlocksPerTask;
|
|
10452
|
+
|
|
10453
|
+
await ctx.swarm.kv_set({
|
|
10454
|
+
namespace: NS,
|
|
10455
|
+
key: \`fail:\${Date.now()}:\${safeKey(taskId)}\`,
|
|
10456
|
+
value: { ...verdict, action: willBlock ? "blocked" : "recorded" },
|
|
10457
|
+
});
|
|
10458
|
+
|
|
10459
|
+
if (!willBlock) {
|
|
10460
|
+
await ctx.swarm.kv_incr({
|
|
10461
|
+
namespace: NS,
|
|
10462
|
+
key: cfg.mode === "enforce" ? "escaped" : "shadowed",
|
|
10463
|
+
});
|
|
10464
|
+
return;
|
|
10465
|
+
}
|
|
10466
|
+
|
|
10467
|
+
await ctx.swarm.kv_incr({ namespace: NS, key: "blocked" });
|
|
10468
|
+
await ctx.swarm.kv_incr({ namespace: NS, key: blocksKey });
|
|
10469
|
+
|
|
10470
|
+
const detail = failed
|
|
10471
|
+
.map(
|
|
10472
|
+
(d) =>
|
|
10473
|
+
\`\${d} (\${scores[d]!.toFixed(2)} vs \${cfg.threshold}) — requested: \${String(prefs[d] ?? "").slice(0, 220)}\`,
|
|
10474
|
+
)
|
|
10475
|
+
.join("\\n");
|
|
10476
|
+
|
|
10477
|
+
return block(
|
|
10478
|
+
[
|
|
10479
|
+
\`Comms gate: this output does not match \${requesterName}'s stated preferences.\`,
|
|
10480
|
+
detail,
|
|
10481
|
+
"Rewrite the output to match and call store-progress again. Do not change the facts, only how they are said.",
|
|
10482
|
+
].join("\\n"),
|
|
10483
|
+
);
|
|
10484
|
+
} catch (err) {
|
|
10485
|
+
// Fail open, always.
|
|
10486
|
+
try {
|
|
10487
|
+
await ctx.swarm.kv_incr({ namespace: NS, key: "error" });
|
|
10488
|
+
await ctx.swarm.kv_set({
|
|
10489
|
+
namespace: NS,
|
|
10490
|
+
key: "error:last",
|
|
10491
|
+
value: { at: new Date().toISOString(), taskId, error: String(err).slice(0, 400) },
|
|
10492
|
+
});
|
|
10493
|
+
} catch {
|
|
10494
|
+
/* ignore */
|
|
10495
|
+
}
|
|
10496
|
+
ctx.log.warn("comms-adherence-gate: failing open", { error: String(err) });
|
|
10497
|
+
return;
|
|
10498
|
+
}
|
|
10499
|
+
});
|
|
10500
|
+
};
|
|
10501
|
+
|
|
10502
|
+
export default extension;
|
|
10503
|
+
`
|
|
10504
|
+
},
|
|
10505
|
+
readme: '# comms-adherence-gate\n\nAgents finish Slack tasks with a final message a human reads. This extension judges that message against the requester\'s stated comms preferences and their latest request with Jev, [TypeSafe](https://typesafe.ai)\'s decision model. In `enforce` it blocks a non-adherent completion once and tells the agent what to fix.\n\nInstall config:\n\n```json\n{ "mode": "shadow" }\n```\n\nStart in `shadow`, read the verdicts in KV for a few days, then switch to `enforce`.\n\n## Modes\n\n| `mode` | Effect |\n|---|---|\n| `off` | Never evaluates. |\n| `shadow` (default) | Asks Jev and records the verdict. Never blocks. |\n| `enforce` | Asks Jev, records the verdict, and blocks the `store-progress` call when a dimension fails, at most `maxBlocksPerTask` times per task. The agent gets the failed dimensions, their scores and the stated preference, and calls again. |\n\n## What it gates\n\nHook: `pre.tool.call` on `store-progress` with `status: "completed"` and a non-empty `output`. Calls made by extensions are skipped. The task must:\n\n1. Come from Slack (`source = "slack"` or a `slackThreadTs`). Other tasks pass without a Jev call.\n2. Have a requester with stated preferences in `users.metadata.comms`. A requester with none passes. Set them with the `manage-user` tool.\n\n## Decision\n\nThe gate reads the task row and asks one `noul` question per stated preference it knows: `verbosity`, `tone`, `language`. Any other key in `comms` is context only.\n\nJev sees the preferences, the requester\'s latest message (the text after the last `</thread_context>`, from the last 1,400 characters of the task), the first 400 characters of the task, and the output. Its judging rules say an explicit request outranks the stored default. A requested long answer, story or document is not a verbosity failure.\n\nA dimension fails when its score is below `threshold`. Any failed dimension fails the output.\n\n## Jev transport\n\nJev is reachable two ways. Both take the same request and return the same answer.\n\n| `provider` | Endpoint | Key | Model |\n|---|---|---|---|\n| `typesafe` | `POST https://api.typesafe.ai/v1/systemone` | `TYPESAFE_API_KEY` (or `apiKey`) | `model`, default `jev-latest` |\n| `openrouter` | `POST https://openrouter.ai/api/alpha/decisions` | `OPENROUTER_API_KEY` | `openrouterModel`, default `typesafe/jev-1.13` |\n| `auto` (default) | TypeSafe when its key is set, else OpenRouter | | |\n\nEach verdict row records the `provider` that answered.\n\n## Fail open\n\nEvery error lets the completion through: a missing key, an HTTP error, a network error, a timeout, a malformed answer, or a failed DB read. The handler never throws, so an outage cannot auto-disable the extension. Each failure bumps the `error` counter and overwrites `error:last`.\n\n## Self-test\n\nCall `store-progress` with a non-completed status and `progress` starting with `comms-gate-selftest`. The gate judges a known-bad and a known-good message against a terse preference and writes `selftest:last`: both scores, the threshold, the provider, and `controlsOk` (bad below threshold and good at or above it). Real traffic is untouched.\n\n## Config\n\n| Key | Default | Notes |\n|---|---|---|\n| `mode` | `shadow` | `off`, `shadow`, or `enforce` |\n| `threshold` | `0.5` | A dimension scoring below this fails |\n| `timeoutMs` | `4000` | Jev call timeout, capped at 4000 (the dispatcher cancels a handler at 5 s) |\n| `maxBlocksPerTask` | `1` | Blocks per task in `enforce`; after that the gate records and lets it through |\n| `provider` | `auto` | `auto`, `typesafe`, or `openrouter`. See [Jev transport](#jev-transport) |\n| `model` | `jev-latest` | Jev model on TypeSafe |\n| `openrouterModel` | `typesafe/jev-1.13` | Jev model on OpenRouter |\n| `apiKey` | unset | Explicit TypeSafe key. Prefer the `TYPESAFE_API_KEY` secret below |\n\n## Required secrets\n\nRead from the API process environment at call time. Set them as global `swarm_config` secrets: the API process loads global rows into `process.env` at boot and on config reload.\n\n- `TYPESAFE_API_KEY` or `OPENROUTER_API_KEY`: one is required for any evaluation. Under `auto`, TypeSafe wins when both are set. Without either, every completion fails open.\n\nNo key is written to config, KV, logs, or a block reason.\n\n## KV namespace `ext:comms-gate`\n\n- `last`: the latest verdict, with `scores`, `failed`, `provider`, `latencyMs`, the preferences, a 400-character `askPreview` and a 700-character `outputPreview`.\n- `fail:<epochMs>:<taskId>`: one row per failed verdict, with `action` (`blocked` or `recorded`).\n- `blocks:<taskId>`: blocks issued for that task.\n- `error:last`, `selftest:last`.\n- Counters: `total`, `pass`, `fail`, `blocked`, `escaped` (enforce, block budget spent), `shadowed`, `error`, and `dim:<dimension>`.\n\n## Known limits\n\n- Only the last 1,400 characters of the task are searched for the latest request. A very long final message is cut to its last 900 characters.\n- The block budget is per task, so a retried completion after a block goes through once the budget is spent, even if it still fails.\n'
|
|
10506
|
+
},
|
|
9902
10507
|
"deploy-awareness": {
|
|
9903
10508
|
manifestFile: "manifest.json",
|
|
9904
10509
|
manifest: {
|
|
@@ -12808,6 +13413,203 @@ async function handleInboxState(req, res, pathSegments, queryParams) {
|
|
|
12808
13413
|
|
|
12809
13414
|
// src/http/integrations.ts
|
|
12810
13415
|
init_zod();
|
|
13416
|
+
|
|
13417
|
+
// src/integrations/chat-sdk/status.ts
|
|
13418
|
+
init_zod();
|
|
13419
|
+
init_constants();
|
|
13420
|
+
init_env_flag();
|
|
13421
|
+
init_secret_scrubber();
|
|
13422
|
+
|
|
13423
|
+
// src/integrations/chat-sdk/discord-api.ts
|
|
13424
|
+
init_secret_scrubber();
|
|
13425
|
+
var DISCORD_TIMEOUT_MS = 5000;
|
|
13426
|
+
var GUILDS_CACHE_MS = 60000;
|
|
13427
|
+
var MAX_GUILD_NAMES = 10;
|
|
13428
|
+
var cache;
|
|
13429
|
+
async function fetchDiscordGuilds(token, now = Date.now()) {
|
|
13430
|
+
if (cache && cache.token === token && now - cache.at < GUILDS_CACHE_MS)
|
|
13431
|
+
return cache.guilds;
|
|
13432
|
+
let guilds;
|
|
13433
|
+
try {
|
|
13434
|
+
const response = await fetch(`${resolveAdapterApiUrl("discord")}/users/@me/guilds`, {
|
|
13435
|
+
headers: { authorization: `Bot ${token}` },
|
|
13436
|
+
signal: AbortSignal.timeout(DISCORD_TIMEOUT_MS)
|
|
13437
|
+
});
|
|
13438
|
+
if (!response.ok)
|
|
13439
|
+
throw new Error(`HTTP ${response.status}`);
|
|
13440
|
+
const payload = await response.json();
|
|
13441
|
+
if (!Array.isArray(payload))
|
|
13442
|
+
throw new Error("unexpected response shape");
|
|
13443
|
+
const names = payload.map((guild) => guild !== null && typeof guild === "object" && typeof guild.name === "string" ? guild.name : undefined).filter((name) => name !== undefined);
|
|
13444
|
+
guilds = { count: payload.length, names: names.slice(0, MAX_GUILD_NAMES) };
|
|
13445
|
+
} catch (error) {
|
|
13446
|
+
console.warn(scrubSecrets(`[chat-sdk] discord: could not list the bot's servers: ${errorMessage(error).replaceAll(token, "[REDACTED]")}`));
|
|
13447
|
+
}
|
|
13448
|
+
cache = { token, at: now, guilds };
|
|
13449
|
+
return guilds;
|
|
13450
|
+
}
|
|
13451
|
+
|
|
13452
|
+
// src/integrations/chat-sdk/status.ts
|
|
13453
|
+
init_schema();
|
|
13454
|
+
var ChatSdkStatusSchema = exports_external.object({
|
|
13455
|
+
enabled: exports_external.boolean(),
|
|
13456
|
+
slackClash: exports_external.boolean(),
|
|
13457
|
+
error: exports_external.string().optional(),
|
|
13458
|
+
adapters: exports_external.array(exports_external.object({
|
|
13459
|
+
name: exports_external.enum(CHAT_SDK_ADAPTER_NAMES),
|
|
13460
|
+
configured: exports_external.boolean(),
|
|
13461
|
+
enabled: exports_external.boolean(),
|
|
13462
|
+
health: exports_external.enum(["ok", "error", "disabled", "incomplete"]),
|
|
13463
|
+
error: exports_external.string().optional(),
|
|
13464
|
+
webhookUrl: exports_external.string(),
|
|
13465
|
+
mode: exports_external.string().optional(),
|
|
13466
|
+
gateway: exports_external.object({
|
|
13467
|
+
state: exports_external.enum(["running", "backoff", "stopped"]),
|
|
13468
|
+
since: exports_external.string(),
|
|
13469
|
+
restarts: exports_external.number()
|
|
13470
|
+
}).optional(),
|
|
13471
|
+
guilds: exports_external.object({ count: exports_external.number(), names: exports_external.array(exports_external.string()) }).optional()
|
|
13472
|
+
}))
|
|
13473
|
+
});
|
|
13474
|
+
var DEFAULT_MODES = {
|
|
13475
|
+
telegram: "webhook",
|
|
13476
|
+
slack: "webhook"
|
|
13477
|
+
};
|
|
13478
|
+
var NOT_RUNNING = "The adapter is starting, or the last configuration save is still applying. Refresh in a few seconds.";
|
|
13479
|
+
var SLACK_CLASH_CAUSE = "the Slack adapter conflicts with the built-in Slack integration (set SLACK_DISABLE=true)";
|
|
13480
|
+
var MAX_STATUS_ERROR_CHARS = 300;
|
|
13481
|
+
function statusError(message) {
|
|
13482
|
+
return scrubSecrets(message).slice(0, MAX_STATUS_ERROR_CHARS);
|
|
13483
|
+
}
|
|
13484
|
+
function readRawAdapters() {
|
|
13485
|
+
const raw = process.env.CHAT_SDK_ADAPTERS?.trim();
|
|
13486
|
+
if (!raw)
|
|
13487
|
+
return {};
|
|
13488
|
+
try {
|
|
13489
|
+
const parsed = JSON.parse(raw);
|
|
13490
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
|
|
13491
|
+
return {};
|
|
13492
|
+
const out = {};
|
|
13493
|
+
for (const name of CHAT_SDK_ADAPTER_NAMES) {
|
|
13494
|
+
const value = parsed[name];
|
|
13495
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
13496
|
+
out[name] = value;
|
|
13497
|
+
}
|
|
13498
|
+
}
|
|
13499
|
+
return out;
|
|
13500
|
+
} catch {
|
|
13501
|
+
return {};
|
|
13502
|
+
}
|
|
13503
|
+
}
|
|
13504
|
+
function chatSdkWebhookUrl(name) {
|
|
13505
|
+
return `${getPublicMcpBaseUrl()}/api/integrations/chat/${name}/webhook`;
|
|
13506
|
+
}
|
|
13507
|
+
async function discordGuildsFor(config, health) {
|
|
13508
|
+
if (health?.status !== "ok" || health.gateway?.state !== "running")
|
|
13509
|
+
return;
|
|
13510
|
+
const raw = config.adapters.discord?.botToken;
|
|
13511
|
+
const token = typeof raw === "function" ? await raw() : raw;
|
|
13512
|
+
if (typeof token !== "string" || token.length === 0)
|
|
13513
|
+
return;
|
|
13514
|
+
return fetchDiscordGuilds(token);
|
|
13515
|
+
}
|
|
13516
|
+
async function getChatSdkStatus() {
|
|
13517
|
+
const config = await getChatSdkConfig();
|
|
13518
|
+
const raw = readRawAdapters();
|
|
13519
|
+
const runtimeHealth = getChatSdkHealth();
|
|
13520
|
+
const slackClash = raw.slack !== undefined && !isEnvFlagEnabled("SLACK_DISABLE", false);
|
|
13521
|
+
const blockedCause = config.error ?? (config.enabled && slackClash ? SLACK_CLASH_CAUSE : undefined);
|
|
13522
|
+
const discordGuilds = await discordGuildsFor(config, runtimeHealth.discord);
|
|
13523
|
+
const adapters = CHAT_SDK_ADAPTER_NAMES.map((name) => {
|
|
13524
|
+
const configured = raw[name] !== undefined;
|
|
13525
|
+
const enabled = isAdapterEntryEnabled(raw[name]);
|
|
13526
|
+
const current = runtimeHealth[name];
|
|
13527
|
+
const incomplete = config.incomplete?.[name];
|
|
13528
|
+
const rawMode = raw[name]?.mode;
|
|
13529
|
+
const mode = typeof rawMode === "string" ? rawMode : configured ? DEFAULT_MODES[name] : undefined;
|
|
13530
|
+
let health = "disabled";
|
|
13531
|
+
let error;
|
|
13532
|
+
if (current) {
|
|
13533
|
+
health = current.status;
|
|
13534
|
+
const showWarning = current.status === "error" || current.gateway?.state === "backoff";
|
|
13535
|
+
error = current.error ?? (showWarning ? current.lastWarning : undefined);
|
|
13536
|
+
} else if (incomplete) {
|
|
13537
|
+
health = "incomplete";
|
|
13538
|
+
error = incomplete;
|
|
13539
|
+
} else if (configured && !enabled) {
|
|
13540
|
+
error = "Turned off on the card. The configuration is kept.";
|
|
13541
|
+
} else if (config.enabled && configured) {
|
|
13542
|
+
error = blockedCause ? `Not started: ${blockedCause}` : NOT_RUNNING;
|
|
13543
|
+
}
|
|
13544
|
+
return {
|
|
13545
|
+
name,
|
|
13546
|
+
configured,
|
|
13547
|
+
enabled,
|
|
13548
|
+
health,
|
|
13549
|
+
...error ? { error: statusError(error) } : {},
|
|
13550
|
+
webhookUrl: chatSdkWebhookUrl(name),
|
|
13551
|
+
...mode ? { mode } : {},
|
|
13552
|
+
...current?.gateway ? { gateway: current.gateway } : {},
|
|
13553
|
+
...name === "discord" && discordGuilds ? { guilds: discordGuilds } : {}
|
|
13554
|
+
};
|
|
13555
|
+
});
|
|
13556
|
+
return {
|
|
13557
|
+
enabled: config.enabled,
|
|
13558
|
+
slackClash,
|
|
13559
|
+
...config.error ? { error: statusError(config.error) } : {},
|
|
13560
|
+
adapters
|
|
13561
|
+
};
|
|
13562
|
+
}
|
|
13563
|
+
|
|
13564
|
+
class ChatSdkActionError extends Error {
|
|
13565
|
+
status;
|
|
13566
|
+
constructor(message, status) {
|
|
13567
|
+
super(message);
|
|
13568
|
+
this.status = status;
|
|
13569
|
+
this.name = "ChatSdkActionError";
|
|
13570
|
+
}
|
|
13571
|
+
}
|
|
13572
|
+
async function callBotApi(baseUrl, token, method, body) {
|
|
13573
|
+
try {
|
|
13574
|
+
return await callTelegramBotApi(baseUrl, token, method, body);
|
|
13575
|
+
} catch (error) {
|
|
13576
|
+
if (error instanceof TelegramBotApiError)
|
|
13577
|
+
throw new ChatSdkActionError(error.message, 502);
|
|
13578
|
+
throw error;
|
|
13579
|
+
}
|
|
13580
|
+
}
|
|
13581
|
+
async function registerTelegramWebhook() {
|
|
13582
|
+
const config = await getChatSdkConfig();
|
|
13583
|
+
if (!config.enabled) {
|
|
13584
|
+
throw new ChatSdkActionError("The Chat SDK channel is disabled (CHAT_SDK_ENABLED is not true). Enable it and restart the API first", 400);
|
|
13585
|
+
}
|
|
13586
|
+
if (config.error)
|
|
13587
|
+
throw new ChatSdkActionError(statusError(config.error), 400);
|
|
13588
|
+
const telegram = config.adapters.telegram;
|
|
13589
|
+
if (!telegram) {
|
|
13590
|
+
throw new ChatSdkActionError("Telegram is not configured in CHAT_SDK_ADAPTERS", 400);
|
|
13591
|
+
}
|
|
13592
|
+
if (telegram.mode !== "webhook") {
|
|
13593
|
+
throw new ChatSdkActionError("Telegram runs in polling mode; a registered webhook would stop polling", 400);
|
|
13594
|
+
}
|
|
13595
|
+
const secretToken = telegram.secretToken;
|
|
13596
|
+
if (typeof secretToken !== "string" || secretToken.length === 0) {
|
|
13597
|
+
throw new ChatSdkActionError("Telegram webhook mode needs a secretToken", 400);
|
|
13598
|
+
}
|
|
13599
|
+
const rawToken = telegram.botToken;
|
|
13600
|
+
const token = typeof rawToken === "function" ? await rawToken() : rawToken;
|
|
13601
|
+
if (typeof token !== "string" || token.length === 0) {
|
|
13602
|
+
throw new ChatSdkActionError("Telegram botToken is empty", 400);
|
|
13603
|
+
}
|
|
13604
|
+
const baseUrl = resolveAdapterApiUrl("telegram");
|
|
13605
|
+
const webhookUrl = chatSdkWebhookUrl("telegram");
|
|
13606
|
+
await callBotApi(baseUrl, token, "setWebhook", { url: webhookUrl, secret_token: secretToken });
|
|
13607
|
+
const info = await callBotApi(baseUrl, token, "getWebhookInfo", {});
|
|
13608
|
+
if (info === null || typeof info !== "object" || typeof info.url !== "string") {
|
|
13609
|
+
throw new ChatSdkActionError("Telegram getWebhookInfo returned an unexpected result", 502);
|
|
13610
|
+
}
|
|
13611
|
+
return { webhookUrl, webhookInfo: info };
|
|
13612
|
+
}
|
|
12811
13613
|
// slack-manifest.json
|
|
12812
13614
|
var slack_manifest_default = {
|
|
12813
13615
|
display_information: {
|
|
@@ -12983,6 +13785,19 @@ var McpUserConfigSchema = exports_external.object({
|
|
|
12983
13785
|
mcpUserUrl: exports_external.string()
|
|
12984
13786
|
});
|
|
12985
13787
|
var SlackManifestSchema = exports_external.record(exports_external.string(), exports_external.unknown());
|
|
13788
|
+
var TelegramRegisterWebhookSchema = exports_external.object({
|
|
13789
|
+
webhookUrl: exports_external.string(),
|
|
13790
|
+
webhookInfo: exports_external.looseObject({
|
|
13791
|
+
url: exports_external.string(),
|
|
13792
|
+
has_custom_certificate: exports_external.boolean().optional(),
|
|
13793
|
+
pending_update_count: exports_external.number().optional(),
|
|
13794
|
+
last_error_date: exports_external.number().optional(),
|
|
13795
|
+
last_error_message: exports_external.string().optional(),
|
|
13796
|
+
max_connections: exports_external.number().optional(),
|
|
13797
|
+
ip_address: exports_external.string().optional(),
|
|
13798
|
+
allowed_updates: exports_external.array(exports_external.string()).optional()
|
|
13799
|
+
})
|
|
13800
|
+
});
|
|
12986
13801
|
var claudeManagedTestRoute = route({
|
|
12987
13802
|
method: "post",
|
|
12988
13803
|
path: "/api/integrations/claude-managed/test",
|
|
@@ -13021,6 +13836,34 @@ var slackManifestRoute = route({
|
|
|
13021
13836
|
200: { description: "Slack app manifest", schema: SlackManifestSchema }
|
|
13022
13837
|
}
|
|
13023
13838
|
});
|
|
13839
|
+
var chatSdkStatusRoute = route({
|
|
13840
|
+
method: "get",
|
|
13841
|
+
path: "/api/integrations/chat/status",
|
|
13842
|
+
pattern: ["api", "integrations", "chat", "status"],
|
|
13843
|
+
summary: "Chat SDK channel status: enabled flag, Slack clash, per-adapter health and webhook URL.",
|
|
13844
|
+
tags: ["Integrations"],
|
|
13845
|
+
responses: {
|
|
13846
|
+
200: {
|
|
13847
|
+
description: "Per-adapter status. `health` is `disabled` for an adapter that is not running. Never contains secret values.",
|
|
13848
|
+
schema: ChatSdkStatusSchema
|
|
13849
|
+
}
|
|
13850
|
+
}
|
|
13851
|
+
});
|
|
13852
|
+
var chatSdkTelegramRegisterWebhookRoute = route({
|
|
13853
|
+
method: "post",
|
|
13854
|
+
path: "/api/integrations/chat/telegram/register-webhook",
|
|
13855
|
+
pattern: ["api", "integrations", "chat", "telegram", "register-webhook"],
|
|
13856
|
+
summary: "Register this swarm's public Telegram webhook URL and secret token with the Bot API (setWebhook), then return getWebhookInfo.",
|
|
13857
|
+
tags: ["Integrations"],
|
|
13858
|
+
body: exports_external.object({}).optional(),
|
|
13859
|
+
responses: {
|
|
13860
|
+
200: { description: "Webhook registered", schema: TelegramRegisterWebhookSchema },
|
|
13861
|
+
400: { description: "Telegram is not configured for webhook mode" },
|
|
13862
|
+
403: { description: "Only the lead agent or a dashboard user can register the webhook" },
|
|
13863
|
+
502: { description: "The Telegram Bot API rejected the call" }
|
|
13864
|
+
},
|
|
13865
|
+
rbac: { permission: "integration.chat.manage" }
|
|
13866
|
+
});
|
|
13024
13867
|
function createIntegrationsHandler(deps = {}) {
|
|
13025
13868
|
const buildClient = deps.buildClient ?? ((apiKey) => new Anthropic({ apiKey }));
|
|
13026
13869
|
return async function handleIntegrations(req, res, pathSegments, queryParams = new URLSearchParams) {
|
|
@@ -13031,6 +13874,26 @@ function createIntegrationsHandler(deps = {}) {
|
|
|
13031
13874
|
slackManifestRoute.respond(res, 200, buildSlackManifest(parsed.query.name));
|
|
13032
13875
|
return true;
|
|
13033
13876
|
}
|
|
13877
|
+
if (chatSdkStatusRoute.match(req.method, pathSegments)) {
|
|
13878
|
+
chatSdkStatusRoute.respond(res, 200, await getChatSdkStatus());
|
|
13879
|
+
return true;
|
|
13880
|
+
}
|
|
13881
|
+
if (chatSdkTelegramRegisterWebhookRoute.match(req.method, pathSegments)) {
|
|
13882
|
+
const parsed = await chatSdkTelegramRegisterWebhookRoute.parse(req, res, pathSegments, queryParams);
|
|
13883
|
+
if (!parsed)
|
|
13884
|
+
return true;
|
|
13885
|
+
if (!await ensureHttpPermission(req, res, "integration.chat.manage", "Registering the Telegram webhook requires the lead agent")) {
|
|
13886
|
+
return true;
|
|
13887
|
+
}
|
|
13888
|
+
try {
|
|
13889
|
+
chatSdkTelegramRegisterWebhookRoute.respond(res, 200, await registerTelegramWebhook());
|
|
13890
|
+
} catch (error) {
|
|
13891
|
+
if (!(error instanceof ChatSdkActionError))
|
|
13892
|
+
throw error;
|
|
13893
|
+
jsonError(res, error.message, error.status);
|
|
13894
|
+
}
|
|
13895
|
+
return true;
|
|
13896
|
+
}
|
|
13034
13897
|
if (mcpUserConfigRoute.match(req.method, pathSegments)) {
|
|
13035
13898
|
const mcpBaseUrl = await resolveMcpBaseUrl();
|
|
13036
13899
|
mcpUserConfigRoute.respond(res, 200, { mcpBaseUrl, mcpUserUrl: `${mcpBaseUrl}/mcp-user` });
|
|
@@ -13662,7 +14525,7 @@ var toRequestError = (e) => {
|
|
|
13662
14525
|
return new RequestError(e.message, { cause: e });
|
|
13663
14526
|
};
|
|
13664
14527
|
var GlobalRequest = global.Request;
|
|
13665
|
-
var
|
|
14528
|
+
var Request2 = class extends GlobalRequest {
|
|
13666
14529
|
constructor(input, options) {
|
|
13667
14530
|
if (typeof input === "object" && getRequestCache in input) {
|
|
13668
14531
|
input = input[getRequestCache]();
|
|
@@ -13693,7 +14556,7 @@ var newRequestFromIncoming = (method, url, headers2, incoming, abortController)
|
|
|
13693
14556
|
};
|
|
13694
14557
|
if (method === "TRACE") {
|
|
13695
14558
|
init.method = "GET";
|
|
13696
|
-
const req = new
|
|
14559
|
+
const req = new Request2(url, init);
|
|
13697
14560
|
Object.defineProperty(req, "method", {
|
|
13698
14561
|
get() {
|
|
13699
14562
|
return "TRACE";
|
|
@@ -13730,7 +14593,7 @@ var newRequestFromIncoming = (method, url, headers2, incoming, abortController)
|
|
|
13730
14593
|
init.body = Readable.toWeb(incoming);
|
|
13731
14594
|
}
|
|
13732
14595
|
}
|
|
13733
|
-
return new
|
|
14596
|
+
return new Request2(url, init);
|
|
13734
14597
|
};
|
|
13735
14598
|
var getRequestCache = Symbol("getRequestCache");
|
|
13736
14599
|
var requestCache = Symbol("requestCache");
|
|
@@ -13796,7 +14659,7 @@ Object.defineProperty(requestPrototype, Symbol.for("nodejs.util.inspect.custom")
|
|
|
13796
14659
|
return `Request (lightweight) ${inspectFn(props, { ...options, depth: depth == null ? null : depth - 1 })}`;
|
|
13797
14660
|
}
|
|
13798
14661
|
});
|
|
13799
|
-
Object.setPrototypeOf(requestPrototype,
|
|
14662
|
+
Object.setPrototypeOf(requestPrototype, Request2.prototype);
|
|
13800
14663
|
var newRequest = (incoming, defaultHostname) => {
|
|
13801
14664
|
const req = Object.create(requestPrototype);
|
|
13802
14665
|
req[incomingKey] = incoming;
|
|
@@ -13865,12 +14728,12 @@ var Response2 = class _Response {
|
|
|
13865
14728
|
}
|
|
13866
14729
|
}
|
|
13867
14730
|
get headers() {
|
|
13868
|
-
const
|
|
13869
|
-
if (
|
|
13870
|
-
if (!(
|
|
13871
|
-
|
|
14731
|
+
const cache2 = this[cacheKey];
|
|
14732
|
+
if (cache2) {
|
|
14733
|
+
if (!(cache2[2] instanceof Headers)) {
|
|
14734
|
+
cache2[2] = new Headers(cache2[2] || { "content-type": "text/plain; charset=UTF-8" });
|
|
13872
14735
|
}
|
|
13873
|
-
return
|
|
14736
|
+
return cache2[2];
|
|
13874
14737
|
}
|
|
13875
14738
|
return this[getResponseCache]().headers;
|
|
13876
14739
|
}
|
|
@@ -14158,9 +15021,9 @@ var responseViaResponseObject = async (res, outgoing, options = {}) => {
|
|
|
14158
15021
|
};
|
|
14159
15022
|
var getRequestListener = (fetchCallback, options = {}) => {
|
|
14160
15023
|
const autoCleanupIncoming = options.autoCleanupIncoming ?? true;
|
|
14161
|
-
if (options.overrideGlobalObjects !== false && global.Request !==
|
|
15024
|
+
if (options.overrideGlobalObjects !== false && global.Request !== Request2) {
|
|
14162
15025
|
Object.defineProperty(global, "Request", {
|
|
14163
|
-
value:
|
|
15026
|
+
value: Request2
|
|
14164
15027
|
});
|
|
14165
15028
|
Object.defineProperty(global, "Response", {
|
|
14166
15029
|
value: Response2
|
|
@@ -15200,13 +16063,14 @@ async function handleMcp(req, res, transports, sessionActivity = {}, sessionAgen
|
|
|
15200
16063
|
const configValue = configs.find((config) => config.key === "SCRIPTS_ONLY_MCP")?.value;
|
|
15201
16064
|
const preloadEnabled = parseEnvFlag(configs.find((config) => config.key === "TASK_TOOL_PRELOAD_ENABLED")?.value ?? process.env.TASK_TOOL_PRELOAD_ENABLED, true);
|
|
15202
16065
|
let preloadedTools = [];
|
|
16066
|
+
const sourceTaskId = headerValue(req.headers["x-source-task-id"]);
|
|
16067
|
+
const sourceTask = sourceTaskId ? await getTaskById(sourceTaskId) : undefined;
|
|
16068
|
+
const excludedTools = sourceTask && !isChatSdkTask(sourceTask) ? [...CHAT_TASK_ONLY_TOOLS] : [];
|
|
15203
16069
|
if (preloadEnabled) {
|
|
15204
|
-
|
|
15205
|
-
const task = taskId ? await getTaskById(taskId) : undefined;
|
|
15206
|
-
if (task?.agentId === agentId) {
|
|
16070
|
+
if (sourceTask?.agentId === agentId) {
|
|
15207
16071
|
const manifestValue = configs.find((config) => config.key === "TASK_TOOL_MANIFESTS")?.value ?? process.env.TASK_TOOL_MANIFESTS ?? "{}";
|
|
15208
16072
|
try {
|
|
15209
|
-
preloadedTools = selectTaskTools(parseTaskToolManifest(manifestValue),
|
|
16073
|
+
preloadedTools = selectTaskTools(parseTaskToolManifest(manifestValue), sourceTask);
|
|
15210
16074
|
} catch {
|
|
15211
16075
|
console.warn("[MCP] Invalid TASK_TOOL_MANIFESTS; using ordinary tool discovery");
|
|
15212
16076
|
}
|
|
@@ -15214,6 +16078,7 @@ async function handleMcp(req, res, transports, sessionActivity = {}, sessionAgen
|
|
|
15214
16078
|
}
|
|
15215
16079
|
const server = await createServer({
|
|
15216
16080
|
preloadedTools,
|
|
16081
|
+
excludedTools,
|
|
15217
16082
|
enabledTools: configs.find((config) => config.key === "SWARM_ENABLED_TOOLS")?.value ?? process.env.SWARM_ENABLED_TOOLS,
|
|
15218
16083
|
scriptsOnly: resolveScriptsOnlyMode({
|
|
15219
16084
|
env: process.env.SCRIPTS_ONLY_MCP,
|
|
@@ -18615,6 +19480,237 @@ async function handleMetrics(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
18615
19480
|
return false;
|
|
18616
19481
|
}
|
|
18617
19482
|
|
|
19483
|
+
// src/http/model-profiles.ts
|
|
19484
|
+
init_src();
|
|
19485
|
+
init_zod();
|
|
19486
|
+
init_model_profiles();
|
|
19487
|
+
init_rbac();
|
|
19488
|
+
init_types();
|
|
19489
|
+
var listProfiles = route({
|
|
19490
|
+
method: "get",
|
|
19491
|
+
path: "/api/model-profiles",
|
|
19492
|
+
pattern: ["api", "model-profiles"],
|
|
19493
|
+
summary: "List model profiles",
|
|
19494
|
+
description: "Every model profile, ordered by harness then name. Filter by `harness` to see what an agent on that harness can run.",
|
|
19495
|
+
tags: ["Models"],
|
|
19496
|
+
query: exports_external.object({ harness: ProviderNameSchema.optional() }),
|
|
19497
|
+
responses: {
|
|
19498
|
+
200: {
|
|
19499
|
+
description: "Model profiles",
|
|
19500
|
+
schema: exports_external.object({ profiles: exports_external.array(ModelProfileSchema) })
|
|
19501
|
+
}
|
|
19502
|
+
}
|
|
19503
|
+
});
|
|
19504
|
+
var getProfile = route({
|
|
19505
|
+
method: "get",
|
|
19506
|
+
path: "/api/model-profiles/{name}",
|
|
19507
|
+
pattern: ["api", "model-profiles", null],
|
|
19508
|
+
summary: "Get one model profile",
|
|
19509
|
+
tags: ["Models"],
|
|
19510
|
+
params: exports_external.object({ name: exports_external.string() }),
|
|
19511
|
+
responses: {
|
|
19512
|
+
200: { description: "The model profile", schema: ModelProfileSchema },
|
|
19513
|
+
404: { description: "No profile with that name" }
|
|
19514
|
+
}
|
|
19515
|
+
});
|
|
19516
|
+
var createProfile = route({
|
|
19517
|
+
method: "post",
|
|
19518
|
+
path: "/api/model-profiles",
|
|
19519
|
+
pattern: ["api", "model-profiles"],
|
|
19520
|
+
summary: "Create a model profile",
|
|
19521
|
+
description: "A profile runs on one harness. `model` must run on that harness and be in the model catalog unless `allowCustomModel` is true (a gateway id, a fresh launch); a `latest:` alias is accepted and resolves at claim. `effort` must be a level the harness and model support. Operator only (the shared API key).",
|
|
19522
|
+
tags: ["Models"],
|
|
19523
|
+
rbac: { permission: "models.profiles.write" },
|
|
19524
|
+
body: exports_external.object({
|
|
19525
|
+
name: ModelProfileNameSchema,
|
|
19526
|
+
harness: ProviderNameSchema,
|
|
19527
|
+
model: exports_external.string().trim().min(1),
|
|
19528
|
+
effort: ReasoningEffortSchema.nullable().optional(),
|
|
19529
|
+
allowCustomModel: exports_external.boolean().optional().default(false),
|
|
19530
|
+
description: exports_external.string().trim().max(500).nullable().optional()
|
|
19531
|
+
}),
|
|
19532
|
+
responses: {
|
|
19533
|
+
201: { description: "Profile created", schema: ModelProfileSchema },
|
|
19534
|
+
400: { description: "Validation error" },
|
|
19535
|
+
403: { description: "Forbidden" },
|
|
19536
|
+
409: { description: "A profile with that name exists" }
|
|
19537
|
+
}
|
|
19538
|
+
});
|
|
19539
|
+
var updateProfile = route({
|
|
19540
|
+
method: "patch",
|
|
19541
|
+
path: "/api/model-profiles/{name}",
|
|
19542
|
+
pattern: ["api", "model-profiles", null],
|
|
19543
|
+
summary: "Update a model profile",
|
|
19544
|
+
description: "Changes the model, effort, custom-model flag or description; the name and harness are fixed. Omit a field to keep it; `effort: null` clears it. Applies to tasks claimed after the change. Operator only (the shared API key).",
|
|
19545
|
+
tags: ["Models"],
|
|
19546
|
+
rbac: { permission: "models.profiles.write" },
|
|
19547
|
+
params: exports_external.object({ name: exports_external.string() }),
|
|
19548
|
+
body: exports_external.object({
|
|
19549
|
+
model: exports_external.string().trim().min(1).optional(),
|
|
19550
|
+
effort: ReasoningEffortSchema.nullable().optional(),
|
|
19551
|
+
allowCustomModel: exports_external.boolean().optional(),
|
|
19552
|
+
description: exports_external.string().trim().max(500).nullable().optional()
|
|
19553
|
+
}),
|
|
19554
|
+
responses: {
|
|
19555
|
+
200: { description: "Profile updated", schema: ModelProfileSchema },
|
|
19556
|
+
400: { description: "Validation error" },
|
|
19557
|
+
403: { description: "Forbidden" },
|
|
19558
|
+
404: { description: "No profile with that name" }
|
|
19559
|
+
}
|
|
19560
|
+
});
|
|
19561
|
+
var deleteProfile = route({
|
|
19562
|
+
method: "delete",
|
|
19563
|
+
path: "/api/model-profiles/{name}",
|
|
19564
|
+
pattern: ["api", "model-profiles", null],
|
|
19565
|
+
summary: "Delete a model profile",
|
|
19566
|
+
description: "Refused (409) for a shipped `<harness>-<tier>` seed (edit it instead; it would come back at the next boot), and while an agent has the profile as its default or a task holds it (unfinished, or superseded and still owed its resume). Operator only (the shared API key).",
|
|
19567
|
+
tags: ["Models"],
|
|
19568
|
+
rbac: { permission: "models.profiles.write" },
|
|
19569
|
+
params: exports_external.object({ name: exports_external.string() }),
|
|
19570
|
+
responses: {
|
|
19571
|
+
200: { description: "Profile deleted", schema: exports_external.object({ deleted: exports_external.literal(true) }) },
|
|
19572
|
+
403: { description: "Forbidden" },
|
|
19573
|
+
404: { description: "No profile with that name" },
|
|
19574
|
+
409: { description: "The profile is a seed or still in use" }
|
|
19575
|
+
}
|
|
19576
|
+
});
|
|
19577
|
+
async function profileValueError(input) {
|
|
19578
|
+
const modelError = await explicitModelError({
|
|
19579
|
+
model: input.model,
|
|
19580
|
+
allowCustomModel: input.allowCustomModel,
|
|
19581
|
+
harnessProvider: input.harness
|
|
19582
|
+
});
|
|
19583
|
+
if (modelError)
|
|
19584
|
+
return modelError;
|
|
19585
|
+
if (input.effort && !isAlias(input.model) && !supportedReasoningLevels(input.harness, input.model).includes(input.effort)) {
|
|
19586
|
+
return `Effort "${input.effort}" is not supported by ${input.model} on ${input.harness}.`;
|
|
19587
|
+
}
|
|
19588
|
+
return null;
|
|
19589
|
+
}
|
|
19590
|
+
async function canWriteProfiles(req, res, myAgentId) {
|
|
19591
|
+
const decision = can({
|
|
19592
|
+
principal: await agentFirstPrincipal(req, myAgentId),
|
|
19593
|
+
verb: "models.profiles.write",
|
|
19594
|
+
resource: { kind: "none" },
|
|
19595
|
+
source: "http"
|
|
19596
|
+
});
|
|
19597
|
+
if (!decision.allow) {
|
|
19598
|
+
jsonError(res, "Writing model profiles requires the operator", 403);
|
|
19599
|
+
return false;
|
|
19600
|
+
}
|
|
19601
|
+
return true;
|
|
19602
|
+
}
|
|
19603
|
+
async function handleModelProfiles(req, res, pathSegments, queryParams, myAgentId) {
|
|
19604
|
+
if (listProfiles.match(req.method, pathSegments)) {
|
|
19605
|
+
const parsed = await listProfiles.parse(req, res, pathSegments, queryParams);
|
|
19606
|
+
if (!parsed)
|
|
19607
|
+
return true;
|
|
19608
|
+
listProfiles.respond(res, 200, {
|
|
19609
|
+
profiles: await listModelProfiles({ harness: parsed.query.harness })
|
|
19610
|
+
});
|
|
19611
|
+
return true;
|
|
19612
|
+
}
|
|
19613
|
+
if (getProfile.match(req.method, pathSegments)) {
|
|
19614
|
+
const parsed = await getProfile.parse(req, res, pathSegments, queryParams);
|
|
19615
|
+
if (!parsed)
|
|
19616
|
+
return true;
|
|
19617
|
+
const profile = await getModelProfileByName(parsed.params.name);
|
|
19618
|
+
if (!profile) {
|
|
19619
|
+
jsonError(res, "Model profile not found", 404);
|
|
19620
|
+
return true;
|
|
19621
|
+
}
|
|
19622
|
+
getProfile.respond(res, 200, profile);
|
|
19623
|
+
return true;
|
|
19624
|
+
}
|
|
19625
|
+
if (createProfile.match(req.method, pathSegments)) {
|
|
19626
|
+
const parsed = await createProfile.parse(req, res, pathSegments, queryParams);
|
|
19627
|
+
if (!parsed)
|
|
19628
|
+
return true;
|
|
19629
|
+
if (!await canWriteProfiles(req, res, myAgentId))
|
|
19630
|
+
return true;
|
|
19631
|
+
const body = parsed.body;
|
|
19632
|
+
const error = await profileValueError({ ...body, effort: body.effort ?? null });
|
|
19633
|
+
if (error) {
|
|
19634
|
+
jsonError(res, error, 400);
|
|
19635
|
+
return true;
|
|
19636
|
+
}
|
|
19637
|
+
const created = await createModelProfile(body, {
|
|
19638
|
+
userId: await resolveHttpAuditUserId(req, myAgentId)
|
|
19639
|
+
});
|
|
19640
|
+
if (!created) {
|
|
19641
|
+
jsonError(res, `Model profile "${body.name}" already exists`, 409);
|
|
19642
|
+
return true;
|
|
19643
|
+
}
|
|
19644
|
+
createProfile.respond(res, 201, created);
|
|
19645
|
+
return true;
|
|
19646
|
+
}
|
|
19647
|
+
if (updateProfile.match(req.method, pathSegments)) {
|
|
19648
|
+
const parsed = await updateProfile.parse(req, res, pathSegments, queryParams);
|
|
19649
|
+
if (!parsed)
|
|
19650
|
+
return true;
|
|
19651
|
+
if (!await canWriteProfiles(req, res, myAgentId))
|
|
19652
|
+
return true;
|
|
19653
|
+
const current = await getModelProfileByName(parsed.params.name);
|
|
19654
|
+
if (!current) {
|
|
19655
|
+
jsonError(res, "Model profile not found", 404);
|
|
19656
|
+
return true;
|
|
19657
|
+
}
|
|
19658
|
+
const body = parsed.body;
|
|
19659
|
+
const error = await profileValueError({
|
|
19660
|
+
harness: current.harness,
|
|
19661
|
+
model: body.model ?? current.model,
|
|
19662
|
+
effort: body.effort === undefined ? current.effort : body.effort,
|
|
19663
|
+
allowCustomModel: body.allowCustomModel ?? current.allowCustomModel
|
|
19664
|
+
});
|
|
19665
|
+
if (error) {
|
|
19666
|
+
jsonError(res, error, 400);
|
|
19667
|
+
return true;
|
|
19668
|
+
}
|
|
19669
|
+
const updated = await updateModelProfile(current.name, body, {
|
|
19670
|
+
userId: await resolveHttpAuditUserId(req, myAgentId)
|
|
19671
|
+
});
|
|
19672
|
+
if (!updated) {
|
|
19673
|
+
jsonError(res, "Model profile not found", 404);
|
|
19674
|
+
return true;
|
|
19675
|
+
}
|
|
19676
|
+
updateProfile.respond(res, 200, updated);
|
|
19677
|
+
return true;
|
|
19678
|
+
}
|
|
19679
|
+
if (deleteProfile.match(req.method, pathSegments)) {
|
|
19680
|
+
const parsed = await deleteProfile.parse(req, res, pathSegments, queryParams);
|
|
19681
|
+
if (!parsed)
|
|
19682
|
+
return true;
|
|
19683
|
+
if (!await canWriteProfiles(req, res, myAgentId))
|
|
19684
|
+
return true;
|
|
19685
|
+
const current = await getModelProfileByName(parsed.params.name);
|
|
19686
|
+
if (!current) {
|
|
19687
|
+
jsonError(res, "Model profile not found", 404);
|
|
19688
|
+
return true;
|
|
19689
|
+
}
|
|
19690
|
+
if (current.seededFrom?.startsWith("tier:")) {
|
|
19691
|
+
jsonError(res, `"${current.name}" is a shipped tier profile and comes back at the next boot; edit it instead.`, 409);
|
|
19692
|
+
return true;
|
|
19693
|
+
}
|
|
19694
|
+
const refs = await modelProfileReferences(current.name);
|
|
19695
|
+
if (refs.agentIds.length > 0 || refs.openTaskCount > 0) {
|
|
19696
|
+
jsonError(res, `"${current.name}" is in use: default of ${refs.agentIds.length} agent(s), held by ${refs.openTaskCount} unfinished or awaiting-resume task(s).`, 409);
|
|
19697
|
+
return true;
|
|
19698
|
+
}
|
|
19699
|
+
const outcome = await deleteModelProfile(current.name);
|
|
19700
|
+
if (outcome === "in-use") {
|
|
19701
|
+
jsonError(res, `"${current.name}" is in use by a task or an agent default created just now.`, 409);
|
|
19702
|
+
return true;
|
|
19703
|
+
}
|
|
19704
|
+
if (outcome === "missing") {
|
|
19705
|
+
jsonError(res, "Model profile not found", 404);
|
|
19706
|
+
return true;
|
|
19707
|
+
}
|
|
19708
|
+
deleteProfile.respond(res, 200, { deleted: true });
|
|
19709
|
+
return true;
|
|
19710
|
+
}
|
|
19711
|
+
return false;
|
|
19712
|
+
}
|
|
19713
|
+
|
|
18618
19714
|
// src/http/models-catalog.ts
|
|
18619
19715
|
init_zod();
|
|
18620
19716
|
init_db3();
|
|
@@ -20501,12 +21597,51 @@ init_request_auth_context();
|
|
|
20501
21597
|
// src/http/tasks.ts
|
|
20502
21598
|
init_zod();
|
|
20503
21599
|
init_db3();
|
|
21600
|
+
init_model_profiles();
|
|
20504
21601
|
init_steering();
|
|
20505
21602
|
init_users();
|
|
20506
21603
|
init_rbac();
|
|
21604
|
+
init_context_key();
|
|
20507
21605
|
init_types();
|
|
20508
21606
|
init_request_auth_context();
|
|
20509
21607
|
init_secret_scrubber();
|
|
21608
|
+
|
|
21609
|
+
// src/http/requested-by.ts
|
|
21610
|
+
init_zod();
|
|
21611
|
+
init_db3();
|
|
21612
|
+
init_types();
|
|
21613
|
+
var TaskRequestedBySchema = UserSchema.pick({
|
|
21614
|
+
id: true,
|
|
21615
|
+
name: true,
|
|
21616
|
+
email: true,
|
|
21617
|
+
role: true,
|
|
21618
|
+
notes: true
|
|
21619
|
+
}).extend({
|
|
21620
|
+
id: exports_external.string().optional(),
|
|
21621
|
+
comms: UserCommsPrefsSchema.optional()
|
|
21622
|
+
});
|
|
21623
|
+
function getRequesterNotes(notes) {
|
|
21624
|
+
return typeof notes === "string" && notes.trim().length > 0 ? notes : undefined;
|
|
21625
|
+
}
|
|
21626
|
+
async function buildTaskRequestedBy(task) {
|
|
21627
|
+
const user = task.requestedByUserId ? await getUserById(task.requestedByUserId) : undefined;
|
|
21628
|
+
if (user) {
|
|
21629
|
+
return {
|
|
21630
|
+
id: user.id,
|
|
21631
|
+
name: user.name,
|
|
21632
|
+
email: user.email,
|
|
21633
|
+
role: user.role,
|
|
21634
|
+
notes: getRequesterNotes(user.notes),
|
|
21635
|
+
comms: getUserCommsPrefs(user)
|
|
21636
|
+
};
|
|
21637
|
+
}
|
|
21638
|
+
if (task.slackUserId) {
|
|
21639
|
+
return { name: renderIdentity(await resolveIdentity("slack", task.slackUserId)) };
|
|
21640
|
+
}
|
|
21641
|
+
return;
|
|
21642
|
+
}
|
|
21643
|
+
|
|
21644
|
+
// src/http/tasks.ts
|
|
20510
21645
|
var AgentTaskSummarySchema = AgentTaskSchema.pick({
|
|
20511
21646
|
id: true,
|
|
20512
21647
|
key: true,
|
|
@@ -20530,6 +21665,7 @@ var AgentTaskSummarySchema = AgentTaskSchema.pick({
|
|
|
20530
21665
|
modelSource: true,
|
|
20531
21666
|
modelAlias: true,
|
|
20532
21667
|
effort: true,
|
|
21668
|
+
resolvedEffort: true,
|
|
20533
21669
|
provider: true,
|
|
20534
21670
|
requestedByUserId: true,
|
|
20535
21671
|
progress: true,
|
|
@@ -20658,6 +21794,7 @@ var createTask = route({
|
|
|
20658
21794
|
modelTier: ModelTierSchema.optional(),
|
|
20659
21795
|
allowCustomModel: exports_external.boolean().optional(),
|
|
20660
21796
|
effort: ReasoningEffortSchema.optional(),
|
|
21797
|
+
profile: exports_external.string().trim().min(1).optional(),
|
|
20661
21798
|
draft: exports_external.boolean().optional()
|
|
20662
21799
|
}).superRefine((body, ctx) => {
|
|
20663
21800
|
if ((body.agentId !== undefined || body.offeredTo !== undefined) && !body.routingReason) {
|
|
@@ -20673,6 +21810,9 @@ var createTask = route({
|
|
|
20673
21810
|
400: {
|
|
20674
21811
|
description: "Validation error, an unknown `model` (set `allowCustomModel` to store a custom id), or agentId/offeredTo targets an extension identity"
|
|
20675
21812
|
},
|
|
21813
|
+
403: {
|
|
21814
|
+
description: "An agent caller names a parentTaskId it neither owns nor created, or a non-lead agent binds the task to a chat thread"
|
|
21815
|
+
},
|
|
20676
21816
|
422: {
|
|
20677
21817
|
description: "Task creation blocked by an extension",
|
|
20678
21818
|
schema: exports_external.object({
|
|
@@ -20719,6 +21859,7 @@ var updateSession = route({
|
|
|
20719
21859
|
claudeSessionId: exports_external.string().min(1),
|
|
20720
21860
|
provider: ProviderNameSchema.exclude(["devin"]).optional(),
|
|
20721
21861
|
model: exports_external.string().optional(),
|
|
21862
|
+
effort: ReasoningEffortSchema.nullable().optional(),
|
|
20722
21863
|
providerMeta: exports_external.object({ transport: exports_external.enum(["cli", "sdk"]).optional() }).optional(),
|
|
20723
21864
|
harnessVariant: exports_external.string().optional(),
|
|
20724
21865
|
harnessVariantMeta: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
|
|
@@ -20933,7 +22074,10 @@ var listPausedTasks = route({
|
|
|
20933
22074
|
200: {
|
|
20934
22075
|
description: "Paused task list",
|
|
20935
22076
|
schema: exports_external.object({
|
|
20936
|
-
tasks: exports_external.array(AgentTaskSchema.extend({
|
|
22077
|
+
tasks: exports_external.array(AgentTaskSchema.extend({
|
|
22078
|
+
attachments: exports_external.array(TaskAttachmentSchema),
|
|
22079
|
+
requestedBy: TaskRequestedBySchema.optional()
|
|
22080
|
+
}))
|
|
20937
22081
|
})
|
|
20938
22082
|
}
|
|
20939
22083
|
}
|
|
@@ -21192,7 +22336,36 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
21192
22336
|
jsonError(res, modelError, 400);
|
|
21193
22337
|
return true;
|
|
21194
22338
|
}
|
|
22339
|
+
const profileError = await taskProfileError({
|
|
22340
|
+
profile: parsed.body.profile,
|
|
22341
|
+
...splitLegacyModelAlias({ model: parsed.body.model, modelTier: parsed.body.modelTier }),
|
|
22342
|
+
agentId: defaultAgentId
|
|
22343
|
+
});
|
|
22344
|
+
if (profileError) {
|
|
22345
|
+
jsonError(res, profileError, 400);
|
|
22346
|
+
return true;
|
|
22347
|
+
}
|
|
21195
22348
|
const parentTask = parsed.body.parentTaskId ? await getTaskById(parsed.body.parentTaskId) : null;
|
|
22349
|
+
const principal = await resolveTaskWritePrincipal(req, myAgentId);
|
|
22350
|
+
if (parentTask && !can({
|
|
22351
|
+
principal,
|
|
22352
|
+
verb: "task.parent.attach",
|
|
22353
|
+
resource: {
|
|
22354
|
+
kind: "task",
|
|
22355
|
+
taskId: parentTask.id,
|
|
22356
|
+
requestedByUserId: parentTask.requestedByUserId,
|
|
22357
|
+
creatorAgentId: parentTask.creatorAgentId,
|
|
22358
|
+
agentId: parentTask.agentId
|
|
22359
|
+
},
|
|
22360
|
+
source: "http"
|
|
22361
|
+
}).allow) {
|
|
22362
|
+
jsonError(res, `You don't have context for parent task ${parentTask.id}.`, 403);
|
|
22363
|
+
return true;
|
|
22364
|
+
}
|
|
22365
|
+
if (principal.kind === "agent" && parsed.body.contextKey && parsed.body.contextKey !== parentTask?.contextKey && chatThreadOfContextKey(parsed.body.contextKey) && !can({ principal, verb: "integration.chat.post", resource: { kind: "none" }, source: "http" }).allow) {
|
|
22366
|
+
jsonError(res, "Permission denied: only a lead can bind a task to a chat thread.", 403);
|
|
22367
|
+
return true;
|
|
22368
|
+
}
|
|
21196
22369
|
let assetKey;
|
|
21197
22370
|
try {
|
|
21198
22371
|
const inheritedKey = parentTask?.key;
|
|
@@ -21229,7 +22402,8 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
21229
22402
|
model: parsed.body.model,
|
|
21230
22403
|
modelTier: parsed.body.modelTier
|
|
21231
22404
|
}),
|
|
21232
|
-
effort: parsed.body.effort
|
|
22405
|
+
effort: parsed.body.effort,
|
|
22406
|
+
profile: parsed.body.profile
|
|
21233
22407
|
}, { origin: "rest", allowCustomModel: parsed.body.allowCustomModel });
|
|
21234
22408
|
ensure({
|
|
21235
22409
|
id: "created",
|
|
@@ -21256,6 +22430,10 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
21256
22430
|
jsonError(res, error.message, 400);
|
|
21257
22431
|
return true;
|
|
21258
22432
|
}
|
|
22433
|
+
if (modelProfileRefError(error) === "missing") {
|
|
22434
|
+
jsonError(res, `Unknown model profile "${parsed.body.profile}"`, 400);
|
|
22435
|
+
return true;
|
|
22436
|
+
}
|
|
21259
22437
|
console.error("[HTTP] Failed to create task:", error);
|
|
21260
22438
|
jsonError(res, "Failed to create task", 500);
|
|
21261
22439
|
}
|
|
@@ -21287,7 +22465,7 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
21287
22465
|
const parsed = await updateSession.parse(req, res, pathSegments, queryParams);
|
|
21288
22466
|
if (!parsed)
|
|
21289
22467
|
return true;
|
|
21290
|
-
const task = await updateTaskClaudeSessionId(parsed.params.id, parsed.body.claudeSessionId, parsed.body.provider, parsed.body.providerMeta, parsed.body.model, "harnessVariant" in parsed.body ? parsed.body.harnessVariant : undefined, "harnessVariantMeta" in parsed.body ? parsed.body.harnessVariantMeta : undefined);
|
|
22468
|
+
const task = await updateTaskClaudeSessionId(parsed.params.id, parsed.body.claudeSessionId, parsed.body.provider, parsed.body.providerMeta, parsed.body.model, "harnessVariant" in parsed.body ? parsed.body.harnessVariant : undefined, "harnessVariantMeta" in parsed.body ? parsed.body.harnessVariantMeta : undefined, "effort" in parsed.body ? parsed.body.effort : undefined);
|
|
21291
22469
|
if (!task) {
|
|
21292
22470
|
jsonError(res, "Task not found", 404);
|
|
21293
22471
|
return true;
|
|
@@ -21727,10 +22905,14 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
21727
22905
|
return true;
|
|
21728
22906
|
}
|
|
21729
22907
|
const pausedTasks = await getPausedTasksForAgent(myAgentId);
|
|
21730
|
-
const tasks = await Promise.all(pausedTasks.map(async (task) =>
|
|
21731
|
-
|
|
21732
|
-
|
|
21733
|
-
|
|
22908
|
+
const tasks = await Promise.all(pausedTasks.map(async (task) => {
|
|
22909
|
+
const requestedBy = await buildTaskRequestedBy(task);
|
|
22910
|
+
return {
|
|
22911
|
+
...task,
|
|
22912
|
+
attachments: await getTaskAttachments(task.id),
|
|
22913
|
+
...requestedBy && { requestedBy }
|
|
22914
|
+
};
|
|
22915
|
+
}));
|
|
21734
22916
|
listPausedTasks.respond(res, 200, { tasks });
|
|
21735
22917
|
return true;
|
|
21736
22918
|
}
|
|
@@ -22236,10 +23418,6 @@ function withShareUrls(page) {
|
|
|
22236
23418
|
app_url: `${getAppBaseUrl()}/pages/${page.slug}`
|
|
22237
23419
|
};
|
|
22238
23420
|
}
|
|
22239
|
-
async function pageEditCounter(pageId) {
|
|
22240
|
-
const versions = await getPageVersions(pageId);
|
|
22241
|
-
return versions.length > 0 ? versions[0].version + 1 : 1;
|
|
22242
|
-
}
|
|
22243
23421
|
function defuseFeedbackTags(value) {
|
|
22244
23422
|
return value.replace(/<(\/?page_feedback)/gi, "‹$1");
|
|
22245
23423
|
}
|
|
@@ -22513,7 +23691,7 @@ async function handlePages(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
22513
23691
|
updatePageRoute.respond(res, 200, {
|
|
22514
23692
|
id: updated.id,
|
|
22515
23693
|
key: updated.key,
|
|
22516
|
-
version: await
|
|
23694
|
+
version: await getPageEditCounter(updated.id)
|
|
22517
23695
|
});
|
|
22518
23696
|
return true;
|
|
22519
23697
|
}
|
|
@@ -22635,8 +23813,819 @@ ${defuseFeedbackTags(note)}
|
|
|
22635
23813
|
|
|
22636
23814
|
// src/http/pages-public.ts
|
|
22637
23815
|
init_zod();
|
|
23816
|
+
init_db3();
|
|
23817
|
+
|
|
23818
|
+
// src/pages/browser-sdk.ts
|
|
23819
|
+
var BROWSER_SDK_JS = `
|
|
23820
|
+
class SwarmSDK {
|
|
23821
|
+
constructor() {
|
|
23822
|
+
let realtime;
|
|
23823
|
+
const loadRealtime = () => realtime || (realtime = import('/@swarm/realtime.js'));
|
|
23824
|
+
this.room = async (name, options) => (await loadRealtime()).room(name, options);
|
|
23825
|
+
this.channel = async (name) => (await loadRealtime()).channel(name);
|
|
23826
|
+
|
|
23827
|
+
const base = '/@swarm/api';
|
|
23828
|
+
const call = async (method, path, body) => {
|
|
23829
|
+
const init = { method };
|
|
23830
|
+
if (body !== undefined) {
|
|
23831
|
+
init.headers = { 'Content-Type': 'application/json' };
|
|
23832
|
+
init.body = JSON.stringify(body);
|
|
23833
|
+
}
|
|
23834
|
+
const res = await fetch(base + path, init);
|
|
23835
|
+
const text = await res.text();
|
|
23836
|
+
let parsed = null;
|
|
23837
|
+
if (text) {
|
|
23838
|
+
try { parsed = JSON.parse(text); } catch { parsed = text; }
|
|
23839
|
+
}
|
|
23840
|
+
if (!res.ok) {
|
|
23841
|
+
const err = new Error('SwarmSDK ' + method + ' ' + path + ': ' + res.status);
|
|
23842
|
+
err.status = res.status;
|
|
23843
|
+
err.response = parsed;
|
|
23844
|
+
throw err;
|
|
23845
|
+
}
|
|
23846
|
+
return parsed;
|
|
23847
|
+
};
|
|
23848
|
+
const qs = (obj) => {
|
|
23849
|
+
if (!obj) return '';
|
|
23850
|
+
const p = new URLSearchParams();
|
|
23851
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
23852
|
+
if (v === undefined || v === null) continue;
|
|
23853
|
+
p.set(k, String(v));
|
|
23854
|
+
}
|
|
23855
|
+
const s = p.toString();
|
|
23856
|
+
return s ? '?' + s : '';
|
|
23857
|
+
};
|
|
23858
|
+
const enc = encodeURIComponent;
|
|
23859
|
+
|
|
23860
|
+
this.tasks = {
|
|
23861
|
+
create: (body) => call('POST', '/tasks', body),
|
|
23862
|
+
list: (filters) => call('GET', '/tasks' + qs(filters)),
|
|
23863
|
+
get: (id) => call('GET', '/tasks/' + enc(id)),
|
|
23864
|
+
storeProgress: (id, data) => call('POST', '/tasks/' + enc(id) + '/progress', data),
|
|
23865
|
+
};
|
|
23866
|
+
|
|
23867
|
+
this.agents = {
|
|
23868
|
+
list: () => call('GET', '/agents'),
|
|
23869
|
+
get: (id) => call('GET', '/agents/' + enc(id)),
|
|
23870
|
+
};
|
|
23871
|
+
|
|
23872
|
+
this.events = {
|
|
23873
|
+
create: (body) => call('POST', '/events', body),
|
|
23874
|
+
list: (filters) => call('GET', '/events' + qs(filters)),
|
|
23875
|
+
batch: (body) => call('POST', '/events/batch', body),
|
|
23876
|
+
counts: (filters) => call('GET', '/events/counts' + qs(filters)),
|
|
23877
|
+
};
|
|
23878
|
+
|
|
23879
|
+
this.memory = {
|
|
23880
|
+
search: (body) => call('POST', '/memory/search', body),
|
|
23881
|
+
list: (filters) => call('GET', '/memory/list' + qs(filters)),
|
|
23882
|
+
get: (id) => call('GET', '/memory/' + enc(id)),
|
|
23883
|
+
rate: (body) => call('POST', '/memory/rate', body),
|
|
23884
|
+
};
|
|
23885
|
+
|
|
23886
|
+
this.repos = {
|
|
23887
|
+
list: () => call('GET', '/repos'),
|
|
23888
|
+
get: (id) => call('GET', '/repos/' + enc(id)),
|
|
23889
|
+
create: (body) => call('POST', '/repos', body),
|
|
23890
|
+
update: (id, body) => call('PUT', '/repos/' + enc(id), body),
|
|
23891
|
+
delete: (id) => call('DELETE', '/repos/' + enc(id)),
|
|
23892
|
+
};
|
|
23893
|
+
|
|
23894
|
+
this.schedules = {
|
|
23895
|
+
list: () => call('GET', '/schedules'),
|
|
23896
|
+
get: (id) => call('GET', '/schedules/' + enc(id)),
|
|
23897
|
+
create: (body) => call('POST', '/schedules', body),
|
|
23898
|
+
update: (id, body) => call('PUT', '/schedules/' + enc(id), body),
|
|
23899
|
+
delete: (id) => call('DELETE', '/schedules/' + enc(id)),
|
|
23900
|
+
run: (id) => call('POST', '/schedules/' + enc(id) + '/run'),
|
|
23901
|
+
};
|
|
23902
|
+
|
|
23903
|
+
this.approvalRequests = {
|
|
23904
|
+
list: (filters) => call('GET', '/approval-requests' + qs(filters)),
|
|
23905
|
+
get: (id) => call('GET', '/approval-requests/' + enc(id)),
|
|
23906
|
+
create: (body) => call('POST', '/approval-requests', body),
|
|
23907
|
+
respond: (id, body) => call('POST', '/approval-requests/' + enc(id) + '/respond', body),
|
|
23908
|
+
};
|
|
23909
|
+
|
|
23910
|
+
this.assets = {
|
|
23911
|
+
list: (filters) => call('GET', '/assets' + qs(filters)),
|
|
23912
|
+
audit: () => call('GET', '/assets/key-audit'),
|
|
23913
|
+
registerMapping: (body) => call('POST', '/assets/mappings', body),
|
|
23914
|
+
move: (entityType, id, key) => call(
|
|
23915
|
+
'PATCH',
|
|
23916
|
+
'/assets/' + enc(entityType) + '/' + enc(id) + '/key',
|
|
23917
|
+
{ key },
|
|
23918
|
+
),
|
|
23919
|
+
};
|
|
23920
|
+
|
|
23921
|
+
// KV store. The namespace is FORCED by the page-proxy to \`task:page:<id>\`
|
|
23922
|
+
// (it injects X-Page-Id which the kv handler treats as highest priority).
|
|
23923
|
+
// No namespace argument is exposed — pages cannot read/write any other
|
|
23924
|
+
// namespace via this SDK.
|
|
23925
|
+
this.kv = {
|
|
23926
|
+
get: (key) => call('GET', '/kv/' + enc(key)),
|
|
23927
|
+
set: (key, value, opts) => call('PUT', '/kv/' + enc(key), {
|
|
23928
|
+
value,
|
|
23929
|
+
valueType: opts && opts.valueType,
|
|
23930
|
+
expiresInSec: opts && opts.expiresInSec,
|
|
23931
|
+
}),
|
|
23932
|
+
del: (key) => call('DELETE', '/kv/' + enc(key)),
|
|
23933
|
+
incr: (key, by) => call('POST', '/kv/' + enc(key) + '/incr', { by: by == null ? 1 : by }),
|
|
23934
|
+
list: (opts) => call('GET', '/kv' + qs(opts)),
|
|
23935
|
+
};
|
|
23936
|
+
}
|
|
23937
|
+
}
|
|
23938
|
+
|
|
23939
|
+
// Expose BOTH the class (for \`new SwarmSDK()\`) AND a ready-to-use singleton
|
|
23940
|
+
// on \`window.swarmSdk\` so pages can call e.g. \`window.swarmSdk.agents.list()\`
|
|
23941
|
+
// directly without instantiating.
|
|
23942
|
+
window.SwarmSDK = SwarmSDK;
|
|
23943
|
+
window.swarmSdk = new SwarmSDK();
|
|
23944
|
+
`;
|
|
23945
|
+
var SWARM_UI_JS = `
|
|
23946
|
+
(function() {
|
|
23947
|
+
if (typeof window === 'undefined' || !window.customElements) return;
|
|
23948
|
+
if (window.customElements.get('swarm-diff')) return;
|
|
23949
|
+
|
|
23950
|
+
// Keep page-owned presence fields alongside the cursor. Room.presence.set
|
|
23951
|
+
// replaces its payload, so retain each room's latest page data and merge
|
|
23952
|
+
// the reserved __cursor field into it when the cursor moves.
|
|
23953
|
+
var roomPresenceTrackers = new WeakMap();
|
|
23954
|
+
|
|
23955
|
+
function clonePresenceRecord(data) {
|
|
23956
|
+
return data && typeof data === 'object' && !Array.isArray(data)
|
|
23957
|
+
? structuredClone(data)
|
|
23958
|
+
: {};
|
|
23959
|
+
}
|
|
23960
|
+
|
|
23961
|
+
function mergeCursorPresence(data, cursor) {
|
|
23962
|
+
var merged = clonePresenceRecord(data);
|
|
23963
|
+
if (cursor) merged.__cursor = { x: cursor.x, y: cursor.y };
|
|
23964
|
+
else delete merged.__cursor;
|
|
23965
|
+
return merged;
|
|
23966
|
+
}
|
|
23967
|
+
|
|
23968
|
+
function trackRoomPresence(room) {
|
|
23969
|
+
if (!room || !room.presence || typeof room.presence.set !== 'function') return null;
|
|
23970
|
+
var existing = roomPresenceTrackers.get(room);
|
|
23971
|
+
if (existing) return existing;
|
|
23972
|
+
|
|
23973
|
+
var presence = room.presence;
|
|
23974
|
+
var originalSet = presence.set;
|
|
23975
|
+
var pageData = {};
|
|
23976
|
+
var cursor = null;
|
|
23977
|
+
var tracker = {
|
|
23978
|
+
setCursor: function(nextCursor) {
|
|
23979
|
+
cursor = nextCursor;
|
|
23980
|
+
return originalSet.call(presence, mergeCursorPresence(pageData, cursor));
|
|
23981
|
+
},
|
|
23982
|
+
clearCursor: function() {
|
|
23983
|
+
if (!cursor) return;
|
|
23984
|
+
return tracker.setCursor(null);
|
|
23985
|
+
},
|
|
23986
|
+
};
|
|
23987
|
+
|
|
23988
|
+
presence.set = function(data) {
|
|
23989
|
+
pageData = clonePresenceRecord(data);
|
|
23990
|
+
return originalSet.call(presence, cursor ? mergeCursorPresence(pageData, cursor) : data);
|
|
23991
|
+
};
|
|
23992
|
+
roomPresenceTrackers.set(room, tracker);
|
|
23993
|
+
return tracker;
|
|
23994
|
+
}
|
|
23995
|
+
|
|
23996
|
+
// Wrap room creation before page scripts run so presence.set calls made by
|
|
23997
|
+
// page code are tracked even when they happen before <swarm-cursors> connects.
|
|
23998
|
+
var cursorRoomRecords = new Map();
|
|
23999
|
+
var sdkRoomMethods = new WeakMap();
|
|
24000
|
+
var observedRoomClosers = new WeakSet();
|
|
24001
|
+
var originalRoom = null;
|
|
24002
|
+
function roomRecordKey(name) {
|
|
24003
|
+
return name === undefined ? 'default' : name;
|
|
24004
|
+
}
|
|
24005
|
+
|
|
24006
|
+
function observeRoom(record, room) {
|
|
24007
|
+
record.hasResolvedRoom = true;
|
|
24008
|
+
if (!cursorRoomRecords.has(record.name)) cursorRoomRecords.set(record.name, record);
|
|
24009
|
+
trackRoomPresence(room);
|
|
24010
|
+
if (room && typeof room.close === 'function' && !observedRoomClosers.has(room)) {
|
|
24011
|
+
var originalClose = room.close;
|
|
24012
|
+
room.close = function() {
|
|
24013
|
+
if (cursorRoomRecords.get(record.name) === record) cursorRoomRecords.delete(record.name);
|
|
24014
|
+
return originalClose.apply(room, arguments);
|
|
24015
|
+
};
|
|
24016
|
+
observedRoomClosers.add(room);
|
|
24017
|
+
}
|
|
24018
|
+
return room;
|
|
24019
|
+
}
|
|
24020
|
+
|
|
24021
|
+
function observeRoomPromise(record, result) {
|
|
24022
|
+
var observed = Promise.resolve(result).then(function(room) {
|
|
24023
|
+
var resolvedRoom = observeRoom(record, room);
|
|
24024
|
+
record.pageRequests--;
|
|
24025
|
+
record.hasPageUse = true;
|
|
24026
|
+
record.pageUsed = true;
|
|
24027
|
+
record.promise = Promise.resolve(resolvedRoom);
|
|
24028
|
+
return resolvedRoom;
|
|
24029
|
+
}).catch(function(error) {
|
|
24030
|
+
record.pageRequests--;
|
|
24031
|
+
record.pageUsed = record.hasPageUse || record.pageRequests > 0;
|
|
24032
|
+
if (!record.hasResolvedRoom
|
|
24033
|
+
&& record.pageRequests === 0
|
|
24034
|
+
&& cursorRoomRecords.get(record.name) === record)
|
|
24035
|
+
cursorRoomRecords.delete(record.name);
|
|
24036
|
+
if (record.cursorRefs === 0 && record.openedByCursor && !record.pageUsed)
|
|
24037
|
+
closeCursorRoomIfUnused(record);
|
|
24038
|
+
throw error;
|
|
24039
|
+
});
|
|
24040
|
+
record.pageRequests++;
|
|
24041
|
+
record.pageUsed = true;
|
|
24042
|
+
if (!record.promise) record.promise = observed;
|
|
24043
|
+
return observed;
|
|
24044
|
+
}
|
|
24045
|
+
|
|
24046
|
+
function closeCursorRoomIfUnused(record) {
|
|
24047
|
+
if (record.closing || record.cursorRefs || !record.openedByCursor || record.pageUsed) return;
|
|
24048
|
+
record.closing = true;
|
|
24049
|
+
record.promise.then(function(room) {
|
|
24050
|
+
if (record.cursorRefs || record.pageUsed) {
|
|
24051
|
+
record.closing = false;
|
|
24052
|
+
return;
|
|
24053
|
+
}
|
|
24054
|
+
if (cursorRoomRecords.get(record.name) === record) cursorRoomRecords.delete(record.name);
|
|
24055
|
+
if (room && typeof room.close === 'function') return room.close();
|
|
24056
|
+
}).catch(function() {
|
|
24057
|
+
record.closing = false;
|
|
24058
|
+
});
|
|
24059
|
+
}
|
|
24060
|
+
|
|
24061
|
+
function instrumentSdk(sdk) {
|
|
24062
|
+
if (!sdk || typeof sdk.room !== 'function') return null;
|
|
24063
|
+
var existing = sdkRoomMethods.get(sdk);
|
|
24064
|
+
if (existing) return existing;
|
|
24065
|
+
|
|
24066
|
+
var sdkOriginalRoom = sdk.room;
|
|
24067
|
+
var wrappedRoom = function(name, options) {
|
|
24068
|
+
var key = roomRecordKey(name);
|
|
24069
|
+
var record = cursorRoomRecords.get(key);
|
|
24070
|
+
var result = sdkOriginalRoom.call(sdk, name, options);
|
|
24071
|
+
if (!record) {
|
|
24072
|
+
record = {
|
|
24073
|
+
name: key,
|
|
24074
|
+
cursorRefs: 0,
|
|
24075
|
+
openedByCursor: false,
|
|
24076
|
+
pageUsed: false,
|
|
24077
|
+
hasPageUse: false,
|
|
24078
|
+
pageRequests: 0,
|
|
24079
|
+
hasResolvedRoom: false,
|
|
24080
|
+
promise: null,
|
|
24081
|
+
};
|
|
24082
|
+
cursorRoomRecords.set(key, record);
|
|
24083
|
+
}
|
|
24084
|
+
return observeRoomPromise(record, result);
|
|
24085
|
+
};
|
|
24086
|
+
sdk.room = wrappedRoom;
|
|
24087
|
+
var methods = { original: sdkOriginalRoom, wrapped: wrappedRoom };
|
|
24088
|
+
sdkRoomMethods.set(sdk, methods);
|
|
24089
|
+
return methods;
|
|
24090
|
+
}
|
|
24091
|
+
|
|
24092
|
+
if (window.swarmSdk) {
|
|
24093
|
+
var singletonRoomMethods = instrumentSdk(window.swarmSdk);
|
|
24094
|
+
originalRoom = singletonRoomMethods && singletonRoomMethods.original;
|
|
24095
|
+
}
|
|
24096
|
+
if (typeof window.SwarmSDK === 'function') {
|
|
24097
|
+
var OriginalSwarmSDK = window.SwarmSDK;
|
|
24098
|
+
window.SwarmSDK = new Proxy(OriginalSwarmSDK, {
|
|
24099
|
+
construct: function(target, args, newTarget) {
|
|
24100
|
+
var sdk = Reflect.construct(target, args, newTarget);
|
|
24101
|
+
instrumentSdk(sdk);
|
|
24102
|
+
return sdk;
|
|
24103
|
+
},
|
|
24104
|
+
});
|
|
24105
|
+
}
|
|
24106
|
+
|
|
24107
|
+
function acquireCursorRoom(name, options) {
|
|
24108
|
+
var key = roomRecordKey(name);
|
|
24109
|
+
var record = cursorRoomRecords.get(key);
|
|
24110
|
+
if (record) {
|
|
24111
|
+
record.cursorRefs++;
|
|
24112
|
+
return { record: record, promise: record.promise };
|
|
24113
|
+
}
|
|
24114
|
+
|
|
24115
|
+
record = {
|
|
24116
|
+
name: key,
|
|
24117
|
+
cursorRefs: 1,
|
|
24118
|
+
openedByCursor: true,
|
|
24119
|
+
pageUsed: false,
|
|
24120
|
+
hasPageUse: false,
|
|
24121
|
+
pageRequests: 0,
|
|
24122
|
+
hasResolvedRoom: false,
|
|
24123
|
+
closing: false,
|
|
24124
|
+
promise: null,
|
|
24125
|
+
};
|
|
24126
|
+
cursorRoomRecords.set(key, record);
|
|
24127
|
+
record.promise = Promise.resolve().then(function() {
|
|
24128
|
+
return originalRoom(name, options);
|
|
24129
|
+
}).then(function(room) {
|
|
24130
|
+
return observeRoom(record, room);
|
|
24131
|
+
}).catch(function(error) {
|
|
24132
|
+
if (cursorRoomRecords.get(key) === record) cursorRoomRecords.delete(key);
|
|
24133
|
+
throw error;
|
|
24134
|
+
});
|
|
24135
|
+
return { record: record, promise: record.promise };
|
|
24136
|
+
}
|
|
24137
|
+
|
|
24138
|
+
function releaseCursorRoom(record, presence) {
|
|
24139
|
+
if (!record || record.cursorRefs < 1) return;
|
|
24140
|
+
if (record.cursorRefs === 1 && presence) presence.clearCursor();
|
|
24141
|
+
record.cursorRefs--;
|
|
24142
|
+
closeCursorRoomIfUnused(record);
|
|
24143
|
+
}
|
|
24144
|
+
|
|
24145
|
+
var SEV_COLOR = {
|
|
24146
|
+
error: '#ef4444',
|
|
24147
|
+
warn: '#f59e0b',
|
|
24148
|
+
info: '#3b82f6',
|
|
24149
|
+
};
|
|
24150
|
+
|
|
24151
|
+
function esc(s) {
|
|
24152
|
+
return String(s == null ? '' : s)
|
|
24153
|
+
.replace(/&/g, '&')
|
|
24154
|
+
.replace(/</g, '<')
|
|
24155
|
+
.replace(/>/g, '>')
|
|
24156
|
+
.replace(/"/g, '"')
|
|
24157
|
+
.replace(/'/g, ''');
|
|
24158
|
+
}
|
|
24159
|
+
|
|
24160
|
+
function slugifyAttr(s) {
|
|
24161
|
+
return String(s || '').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
|
24162
|
+
}
|
|
24163
|
+
|
|
24164
|
+
function parseHunks(jsonText) {
|
|
24165
|
+
var trimmed = (jsonText || '').trim();
|
|
24166
|
+
if (!trimmed) return [];
|
|
24167
|
+
try {
|
|
24168
|
+
var parsed = JSON.parse(trimmed);
|
|
24169
|
+
if (parsed && Array.isArray(parsed.hunks)) return parsed.hunks;
|
|
24170
|
+
if (Array.isArray(parsed)) return parsed;
|
|
24171
|
+
return [];
|
|
24172
|
+
} catch (e) {
|
|
24173
|
+
console.warn('[swarm-diff] failed to parse JSON body:', e);
|
|
24174
|
+
return [];
|
|
24175
|
+
}
|
|
24176
|
+
}
|
|
24177
|
+
|
|
24178
|
+
function renderAnnotation(ann) {
|
|
24179
|
+
var color = SEV_COLOR[ann && ann.severity] || SEV_COLOR.info;
|
|
24180
|
+
return (
|
|
24181
|
+
'<span class="swarm-diff-annot no-print" '
|
|
24182
|
+
+ 'style="display:inline-block;margin-left:8px;padding:1px 6px;'
|
|
24183
|
+
+ 'border-radius:4px;font-size:11px;font-weight:600;'
|
|
24184
|
+
+ 'background:' + color + '22;color:' + color + ';border:1px solid ' + color + '55;">'
|
|
24185
|
+
+ esc((ann && ann.severity ? ann.severity.toUpperCase() : 'INFO')) + ' · ' + esc(ann && ann.text || '')
|
|
24186
|
+
+ '</span>'
|
|
24187
|
+
);
|
|
24188
|
+
}
|
|
24189
|
+
|
|
24190
|
+
function renderHunk(hunk, hunkIdx, file) {
|
|
24191
|
+
var oldLines = hunk.old_lines || 0;
|
|
24192
|
+
var newLines = hunk.new_lines || 0;
|
|
24193
|
+
var oldStart = hunk.old_start || 0;
|
|
24194
|
+
var newStart = hunk.new_start || 0;
|
|
24195
|
+
var lines = Array.isArray(hunk.lines) ? hunk.lines : [];
|
|
24196
|
+
var annotations = Array.isArray(hunk.annotations) ? hunk.annotations : [];
|
|
24197
|
+
// Index annotations by new-side line number for fast lookup per row.
|
|
24198
|
+
var annByLine = {};
|
|
24199
|
+
for (var i = 0; i < annotations.length; i++) {
|
|
24200
|
+
var a = annotations[i];
|
|
24201
|
+
if (a && typeof a.line === 'number') {
|
|
24202
|
+
if (!annByLine[a.line]) annByLine[a.line] = [];
|
|
24203
|
+
annByLine[a.line].push(a);
|
|
24204
|
+
}
|
|
24205
|
+
}
|
|
24206
|
+
|
|
24207
|
+
var rowsHtml = '';
|
|
24208
|
+
var oldN = oldStart;
|
|
24209
|
+
var newN = newStart;
|
|
24210
|
+
for (var j = 0; j < lines.length; j++) {
|
|
24211
|
+
var line = lines[j] || {};
|
|
24212
|
+
var type = line.type || 'context';
|
|
24213
|
+
var text = line.text == null ? '' : line.text;
|
|
24214
|
+
var bg, oldCell, newCell, sign;
|
|
24215
|
+
if (type === 'add') {
|
|
24216
|
+
bg = 'rgba(34,197,94,0.10)';
|
|
24217
|
+
oldCell = '';
|
|
24218
|
+
newCell = String(newN++);
|
|
24219
|
+
sign = '+';
|
|
24220
|
+
} else if (type === 'del') {
|
|
24221
|
+
bg = 'rgba(239,68,68,0.10)';
|
|
24222
|
+
oldCell = String(oldN++);
|
|
24223
|
+
newCell = '';
|
|
24224
|
+
sign = '-';
|
|
24225
|
+
} else {
|
|
24226
|
+
bg = 'transparent';
|
|
24227
|
+
oldCell = String(oldN++);
|
|
24228
|
+
newCell = String(newN++);
|
|
24229
|
+
sign = ' ';
|
|
24230
|
+
}
|
|
24231
|
+
|
|
24232
|
+
var annHtml = '';
|
|
24233
|
+
var anns = annByLine[Number(newCell)] || annByLine[Number(oldCell)] || [];
|
|
24234
|
+
for (var k = 0; k < anns.length; k++) annHtml += renderAnnotation(anns[k]);
|
|
24235
|
+
|
|
24236
|
+
rowsHtml += (
|
|
24237
|
+
'<tr style="background:' + bg + ';">'
|
|
24238
|
+
+ '<td class="swarm-diff-gutter" style="user-select:none;text-align:right;padding:0 8px;color:#7c8aa6;font-size:12px;width:48px;">' + esc(oldCell) + '</td>'
|
|
24239
|
+
+ '<td class="swarm-diff-gutter" style="user-select:none;text-align:right;padding:0 8px;color:#7c8aa6;font-size:12px;width:48px;">' + esc(newCell) + '</td>'
|
|
24240
|
+
+ '<td class="swarm-diff-sign" style="user-select:none;text-align:center;padding:0 4px;color:#7c8aa6;font-size:12px;width:18px;">' + esc(sign) + '</td>'
|
|
24241
|
+
+ '<td class="swarm-diff-code" style="padding:0 8px;white-space:pre-wrap;word-break:break-word;font-family:\\'Space Mono\\',ui-monospace,monospace;font-size:12px;">' + esc(text) + annHtml + '</td>'
|
|
24242
|
+
+ '</tr>'
|
|
24243
|
+
);
|
|
24244
|
+
}
|
|
24245
|
+
|
|
24246
|
+
var anchorSlug = slugifyAttr((file || 'hunk') + '-' + (oldStart || hunkIdx + 1));
|
|
24247
|
+
var anchorId = 'swarm-diff-' + anchorSlug;
|
|
24248
|
+
var header = (
|
|
24249
|
+
'@@ -' + oldStart + ',' + oldLines + ' +' + newStart + ',' + newLines + ' @@'
|
|
24250
|
+
);
|
|
24251
|
+
|
|
24252
|
+
return (
|
|
24253
|
+
'<a id="' + esc(anchorId) + '" class="swarm-diff-anchor" data-hunk="' + esc(anchorSlug) + '"></a>'
|
|
24254
|
+
+ '<div class="swarm-diff-hunk-header" style="padding:6px 12px;background:rgba(124,138,166,0.10);color:#7c8aa6;font-family:\\'Space Mono\\',ui-monospace,monospace;font-size:11px;border-top:1px solid var(--swarm-border,#22304a);">'
|
|
24255
|
+
+ esc(header)
|
|
24256
|
+
+ '</div>'
|
|
24257
|
+
+ '<table class="swarm-diff-table" style="width:100%;border-collapse:collapse;table-layout:fixed;">'
|
|
24258
|
+
+ '<tbody>' + rowsHtml + '</tbody>'
|
|
24259
|
+
+ '</table>'
|
|
24260
|
+
);
|
|
24261
|
+
}
|
|
24262
|
+
|
|
24263
|
+
function renderDiff(rootEl, diffData) {
|
|
24264
|
+
var hunks = (diffData && Array.isArray(diffData.hunks)) ? diffData.hunks : (Array.isArray(diffData) ? diffData : []);
|
|
24265
|
+
var file = rootEl.getAttribute('file') || '';
|
|
24266
|
+
var baseSha = rootEl.getAttribute('base-sha') || '';
|
|
24267
|
+
var headSha = rootEl.getAttribute('head-sha') || '';
|
|
24268
|
+
|
|
24269
|
+
var shaLine = '';
|
|
24270
|
+
if (baseSha || headSha) {
|
|
24271
|
+
shaLine = '<span class="swarm-diff-sha" style="font-family:\\'Space Mono\\',ui-monospace,monospace;font-size:11px;color:#7c8aa6;">'
|
|
24272
|
+
+ esc(baseSha) + ' → ' + esc(headSha)
|
|
24273
|
+
+ '</span>';
|
|
24274
|
+
}
|
|
24275
|
+
|
|
24276
|
+
var hunksHtml = '';
|
|
24277
|
+
for (var i = 0; i < hunks.length; i++) {
|
|
24278
|
+
hunksHtml += renderHunk(hunks[i] || {}, i, file);
|
|
24279
|
+
}
|
|
24280
|
+
|
|
24281
|
+
rootEl.innerHTML = (
|
|
24282
|
+
'<div class="swarm-diff-root" style="border:1px solid var(--swarm-border,#22304a);border-radius:8px;background:var(--swarm-card,#121826);overflow:hidden;margin:12px 0;break-inside:avoid;">'
|
|
24283
|
+
+ '<div class="swarm-diff-header" style="display:flex;align-items:center;justify-content:space-between;padding:10px 12px;background:rgba(59,130,246,0.10);border-bottom:1px solid var(--swarm-border,#22304a);">'
|
|
24284
|
+
+ '<span class="swarm-diff-file" style="font-family:\\'Space Mono\\',ui-monospace,monospace;font-size:13px;font-weight:700;color:var(--swarm-text,#e6eaf2);">' + esc(file || '(untitled)') + '</span>'
|
|
24285
|
+
+ shaLine
|
|
24286
|
+
+ '</div>'
|
|
24287
|
+
+ hunksHtml
|
|
24288
|
+
+ '</div>'
|
|
24289
|
+
);
|
|
24290
|
+
}
|
|
24291
|
+
|
|
24292
|
+
// Public function form lives on window.swarmUi so callers can render an
|
|
24293
|
+
// arbitrary root element programmatically. The custom element below is just
|
|
24294
|
+
// a declarative wrapper around the same render function.
|
|
24295
|
+
window.swarmUi = window.swarmUi || {};
|
|
24296
|
+
window.swarmUi.renderDiff = renderDiff;
|
|
24297
|
+
|
|
24298
|
+
// Defer the parse-and-render so the HTML parser has time to finish
|
|
24299
|
+
// appending JSON text children. \`connectedCallback\` fires on the opening
|
|
24300
|
+
// tag — \`this.textContent\` is empty until children parse. Without a
|
|
24301
|
+
// defer, every declarative <swarm-diff> renders an empty header and the
|
|
24302
|
+
// JSON text remains visible as orphan children.
|
|
24303
|
+
//
|
|
24304
|
+
// queueMicrotask alone is NOT enough — Chrome's streaming parser drains
|
|
24305
|
+
// microtasks between chunks, so the microtask can run BEFORE the JSON
|
|
24306
|
+
// child is appended. We need to wait for the parser to finish the current
|
|
24307
|
+
// document load, then read textContent.
|
|
24308
|
+
//
|
|
24309
|
+
// * \`document.readyState === 'loading'\` ⇒ parser still streaming →
|
|
24310
|
+
// wait for DOMContentLoaded (fires after all children are parsed).
|
|
24311
|
+
// * otherwise (element was created/inserted dynamically post-load) ⇒
|
|
24312
|
+
// queueMicrotask is fine — DOM is stable, just give the caller a tick.
|
|
24313
|
+
//
|
|
24314
|
+
// Re-entrancy (element moved/reconnected) re-fires connectedCallback so
|
|
24315
|
+
// we re-render against current textContent.
|
|
24316
|
+
class SwarmDiffElement extends HTMLElement {
|
|
24317
|
+
connectedCallback() {
|
|
24318
|
+
var self = this;
|
|
24319
|
+
var doRender = function() {
|
|
24320
|
+
if (!self.isConnected) return;
|
|
24321
|
+
var raw = self.textContent || '';
|
|
24322
|
+
renderDiff(self, { hunks: parseHunks(raw) });
|
|
24323
|
+
// Notify <swarm-diff-jumps> instances so they can pick up new anchors.
|
|
24324
|
+
self.dispatchEvent(new CustomEvent('swarm-diff:rendered', { bubbles: true }));
|
|
24325
|
+
};
|
|
24326
|
+
if (typeof document !== 'undefined' && document.readyState === 'loading') {
|
|
24327
|
+
document.addEventListener('DOMContentLoaded', doRender, { once: true });
|
|
24328
|
+
} else {
|
|
24329
|
+
queueMicrotask(doRender);
|
|
24330
|
+
}
|
|
24331
|
+
}
|
|
24332
|
+
}
|
|
24333
|
+
window.customElements.define('swarm-diff', SwarmDiffElement);
|
|
24334
|
+
|
|
24335
|
+
// Sibling-anchor jump list. Walks subsequent siblings, finds every
|
|
24336
|
+
// <swarm-diff data-hunk=...> anchor, and renders a small list of links.
|
|
24337
|
+
//
|
|
24338
|
+
// Same parse-order hazard as <swarm-diff>: <swarm-diff-jumps> usually
|
|
24339
|
+
// appears in the document BEFORE the <swarm-diff> elements it indexes, so
|
|
24340
|
+
// we also defer to a microtask AND re-render whenever a <swarm-diff> in the
|
|
24341
|
+
// document finishes rendering its anchors.
|
|
24342
|
+
class SwarmDiffJumpsElement extends HTMLElement {
|
|
24343
|
+
connectedCallback() {
|
|
24344
|
+
var self = this;
|
|
24345
|
+
var renderJumps = function() {
|
|
24346
|
+
var anchors = document.querySelectorAll('.swarm-diff-anchor[data-hunk]');
|
|
24347
|
+
if (!anchors.length) {
|
|
24348
|
+
self.innerHTML = '<span class="no-print" style="color:#7c8aa6;font-size:12px;">No hunks yet.</span>';
|
|
24349
|
+
return;
|
|
24350
|
+
}
|
|
24351
|
+
var items = '';
|
|
24352
|
+
for (var i = 0; i < anchors.length; i++) {
|
|
24353
|
+
var a = anchors[i];
|
|
24354
|
+
var slug = a.getAttribute('data-hunk') || ('hunk-' + i);
|
|
24355
|
+
// Hunk title = nearest preceding diff's file attribute if available.
|
|
24356
|
+
var diff = a.closest && a.closest('swarm-diff');
|
|
24357
|
+
var file = (diff && diff.getAttribute('file')) || slug;
|
|
24358
|
+
items += '<li style="margin:0;padding:2px 0;"><a href="#' + esc(a.id) + '" style="color:#3b82f6;text-decoration:none;font-family:\\'Space Mono\\',ui-monospace,monospace;font-size:12px;">' + esc(file) + '</a></li>';
|
|
24359
|
+
}
|
|
24360
|
+
self.innerHTML = (
|
|
24361
|
+
'<nav class="swarm-diff-jumps no-print" style="padding:8px 12px;border:1px dashed var(--swarm-border,#22304a);border-radius:8px;background:rgba(124,138,166,0.05);">'
|
|
24362
|
+
+ '<div style="font-size:10px;text-transform:uppercase;letter-spacing:0.08em;color:#7c8aa6;margin-bottom:4px;">Jump to</div>'
|
|
24363
|
+
+ '<ul style="list-style:none;padding:0;margin:0;">' + items + '</ul>'
|
|
24364
|
+
+ '</nav>'
|
|
24365
|
+
);
|
|
24366
|
+
};
|
|
24367
|
+
// Wait for the parser to finish initial load before first query —
|
|
24368
|
+
// <swarm-diff> elements also defer to DOMContentLoaded so we must
|
|
24369
|
+
// run AFTER they finish rendering their anchors. The event listener
|
|
24370
|
+
// below handles the live-update case.
|
|
24371
|
+
if (typeof document !== 'undefined' && document.readyState === 'loading') {
|
|
24372
|
+
document.addEventListener('DOMContentLoaded', function() { queueMicrotask(renderJumps); }, { once: true });
|
|
24373
|
+
} else {
|
|
24374
|
+
queueMicrotask(renderJumps);
|
|
24375
|
+
}
|
|
24376
|
+
// Re-render whenever a sibling <swarm-diff> finishes its async render.
|
|
24377
|
+
self._onDiffRendered = function() { renderJumps(); };
|
|
24378
|
+
document.addEventListener('swarm-diff:rendered', self._onDiffRendered);
|
|
24379
|
+
}
|
|
24380
|
+
disconnectedCallback() {
|
|
24381
|
+
if (this._onDiffRendered) {
|
|
24382
|
+
document.removeEventListener('swarm-diff:rendered', this._onDiffRendered);
|
|
24383
|
+
}
|
|
24384
|
+
}
|
|
24385
|
+
}
|
|
24386
|
+
window.customElements.define('swarm-diff-jumps', SwarmDiffJumpsElement);
|
|
24387
|
+
|
|
24388
|
+
var CURSOR_IDLE_MS = 3000;
|
|
24389
|
+
var CURSOR_SMOOTHING = 0.28;
|
|
24390
|
+
|
|
24391
|
+
function cursorColor(userId) {
|
|
24392
|
+
var hash = 2166136261;
|
|
24393
|
+
var value = String(userId || '');
|
|
24394
|
+
for (var i = 0; i < value.length; i++) {
|
|
24395
|
+
hash ^= value.charCodeAt(i);
|
|
24396
|
+
hash = Math.imul(hash, 16777619);
|
|
24397
|
+
}
|
|
24398
|
+
return 'hsl(' + ((hash >>> 0) % 360) + ', 72%, 42%)';
|
|
24399
|
+
}
|
|
24400
|
+
|
|
24401
|
+
function documentSize() {
|
|
24402
|
+
var root = document.documentElement || {};
|
|
24403
|
+
return {
|
|
24404
|
+
width: Math.max(1, root.scrollWidth || root.clientWidth || window.innerWidth || 1),
|
|
24405
|
+
height: Math.max(1, root.scrollHeight || root.clientHeight || window.innerHeight || 1),
|
|
24406
|
+
};
|
|
24407
|
+
}
|
|
24408
|
+
|
|
24409
|
+
function cursorMarkup(name, color) {
|
|
24410
|
+
// CursorPointer + CursorBody label structure adapted from Kibo UI (MIT):
|
|
24411
|
+
// https://github.com/shadcnblocks/kibo/blob/main/packages/cursor/index.tsx
|
|
24412
|
+
return '<svg aria-hidden="true" focusable="false" height="20" width="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" style="width:20px;height:20px;flex:none;filter:drop-shadow(0 1px 1px rgba(0,0,0,.2));">'
|
|
24413
|
+
+ '<path d="M19.438 6.716 1.115.05A.832.832 0 0 0 .05 1.116L6.712 19.45a.834.834 0 0 0 1.557.025l3.198-8 7.995-3.2a.833.833 0 0 0 0-1.559h-.024Z" fill="currentColor"></path>'
|
|
24414
|
+
+ '</svg>'
|
|
24415
|
+
+ '<span style="position:relative;margin-left:14px;display:flex;flex-direction:column;white-space:nowrap;border-radius:12px;padding:4px 12px 4px 10px;font-family:system-ui,sans-serif;font-size:12px;font-weight:500;line-height:1.4;background:' + color + ';color:#fff;box-shadow:0 2px 6px rgba(0,0,0,.18);">'
|
|
24416
|
+
+ esc(name || 'Guest')
|
|
24417
|
+
+ '</span>';
|
|
24418
|
+
}
|
|
24419
|
+
|
|
24420
|
+
class SwarmCursorsElement extends HTMLElement {
|
|
24421
|
+
connectedCallback() {
|
|
24422
|
+
if (this._startPromise) return;
|
|
24423
|
+
var roomName = this.getAttribute('room');
|
|
24424
|
+
if (!roomName || !originalRoom) return;
|
|
24425
|
+
|
|
24426
|
+
var self = this;
|
|
24427
|
+
var lifecycle = (this._lifecycle || 0) + 1;
|
|
24428
|
+
this._lifecycle = lifecycle;
|
|
24429
|
+
var begin = function() {
|
|
24430
|
+
self._onDomReady = null;
|
|
24431
|
+
if (!self.isConnected || self._lifecycle !== lifecycle) return;
|
|
24432
|
+
var schemaVersion = self.getAttribute('schema-version');
|
|
24433
|
+
var options = schemaVersion == null ? {} : { schemaVersion: Number(schemaVersion) };
|
|
24434
|
+
var acquired = acquireCursorRoom(roomName, options);
|
|
24435
|
+
self._roomRecord = acquired.record;
|
|
24436
|
+
self._startPromise = acquired.promise.then(function(room) {
|
|
24437
|
+
if (!self.isConnected || self._lifecycle !== lifecycle) return;
|
|
24438
|
+
self._startPromise = null;
|
|
24439
|
+
self._room = room;
|
|
24440
|
+
self._presence = trackRoomPresence(room);
|
|
24441
|
+
if (!self._presence) return;
|
|
24442
|
+
self._pointers = new Map();
|
|
24443
|
+
self._cursorStates = new Map();
|
|
24444
|
+
|
|
24445
|
+
self._overlay = document.createElement('div');
|
|
24446
|
+
self._overlay.setAttribute('aria-hidden', 'true');
|
|
24447
|
+
self._overlay.style.cssText = 'position:fixed;inset:0;z-index:2147483647;pointer-events:none;overflow:hidden;';
|
|
24448
|
+
var parent = document.body || document.documentElement;
|
|
24449
|
+
if (parent) parent.appendChild(self._overlay);
|
|
24450
|
+
|
|
24451
|
+
self._onPresence = function(peers) { self._renderPeers(peers || []); };
|
|
24452
|
+
self._onPointerMove = function(event) {
|
|
24453
|
+
var size = documentSize();
|
|
24454
|
+
var scrollX = window.scrollX || window.pageXOffset || 0;
|
|
24455
|
+
var scrollY = window.scrollY || window.pageYOffset || 0;
|
|
24456
|
+
var pageX = typeof event.pageX === 'number' ? event.pageX : event.clientX + scrollX;
|
|
24457
|
+
var pageY = typeof event.pageY === 'number' ? event.pageY : event.clientY + scrollY;
|
|
24458
|
+
var x = Math.max(0, Math.min(1, pageX / size.width));
|
|
24459
|
+
var y = Math.max(0, Math.min(1, pageY / size.height));
|
|
24460
|
+
self._presence.setCursor({ x: x, y: y });
|
|
24461
|
+
};
|
|
24462
|
+
self._onPointerLeave = function() {
|
|
24463
|
+
self._presence.clearCursor();
|
|
24464
|
+
};
|
|
24465
|
+
|
|
24466
|
+
self._unsubscribe = room.on('presence', self._onPresence);
|
|
24467
|
+
document.addEventListener('pointermove', self._onPointerMove, { passive: true });
|
|
24468
|
+
document.addEventListener('pointerleave', self._onPointerLeave);
|
|
24469
|
+
self._renderPeers(room.presence.peers || []);
|
|
24470
|
+
}).catch(function(error) {
|
|
24471
|
+
self._startPromise = null;
|
|
24472
|
+
console.warn('[swarm-cursors] failed to join room:', error);
|
|
24473
|
+
});
|
|
24474
|
+
};
|
|
24475
|
+
if (document.readyState === 'loading') {
|
|
24476
|
+
this._onDomReady = function() { queueMicrotask(begin); };
|
|
24477
|
+
this._startPromise = Promise.resolve();
|
|
24478
|
+
document.addEventListener('DOMContentLoaded', this._onDomReady, { once: true });
|
|
24479
|
+
} else {
|
|
24480
|
+
begin();
|
|
24481
|
+
}
|
|
24482
|
+
}
|
|
24483
|
+
|
|
24484
|
+
_renderPeers(peers) {
|
|
24485
|
+
if (!this._pointers) return;
|
|
24486
|
+
var seen = new Set();
|
|
24487
|
+
var activePeerIds = new Set();
|
|
24488
|
+
var includeAgents = this.hasAttribute('include-agents');
|
|
24489
|
+
var ownId = this._room && this._room.me && this._room.me.userId;
|
|
24490
|
+
var now = Date.now();
|
|
24491
|
+
|
|
24492
|
+
for (var i = 0; i < peers.length; i++) {
|
|
24493
|
+
var peer = peers[i];
|
|
24494
|
+
if (!peer || !peer.userId || peer.userId === ownId) continue;
|
|
24495
|
+
if (peer.kind === 'agent' && !includeAgents) continue;
|
|
24496
|
+
var data = peer.data && typeof peer.data === 'object' ? peer.data : null;
|
|
24497
|
+
var cursor = data && data.__cursor;
|
|
24498
|
+
if (!cursor || typeof cursor.x !== 'number' || typeof cursor.y !== 'number'
|
|
24499
|
+
|| !Number.isFinite(cursor.x) || !Number.isFinite(cursor.y)) continue;
|
|
24500
|
+
|
|
24501
|
+
var id = String(peer.userId);
|
|
24502
|
+
activePeerIds.add(id);
|
|
24503
|
+
var state = this._cursorStates.get(id);
|
|
24504
|
+
if (!state || state.x !== cursor.x || state.y !== cursor.y) {
|
|
24505
|
+
state = { x: cursor.x, y: cursor.y, lastSeen: now };
|
|
24506
|
+
this._cursorStates.set(id, state);
|
|
24507
|
+
}
|
|
24508
|
+
if (now - state.lastSeen > CURSOR_IDLE_MS) continue;
|
|
24509
|
+
|
|
24510
|
+
var entry = this._pointers.get(id);
|
|
24511
|
+
if (!entry) {
|
|
24512
|
+
entry = this._createPointer(peer);
|
|
24513
|
+
this._pointers.set(id, entry);
|
|
24514
|
+
}
|
|
24515
|
+
entry.cursor = { x: state.x, y: state.y };
|
|
24516
|
+
entry.lastSeen = state.lastSeen;
|
|
24517
|
+
if (entry.name !== peer.name) {
|
|
24518
|
+
entry.name = peer.name || 'Guest';
|
|
24519
|
+
entry.node.innerHTML = cursorMarkup(entry.name, entry.color);
|
|
24520
|
+
}
|
|
24521
|
+
seen.add(id);
|
|
24522
|
+
}
|
|
24523
|
+
|
|
24524
|
+
var ids = Array.from(this._pointers.keys());
|
|
24525
|
+
for (var j = 0; j < ids.length; j++) {
|
|
24526
|
+
if (!seen.has(ids[j])) this._removePointer(ids[j]);
|
|
24527
|
+
}
|
|
24528
|
+
var stateIds = Array.from(this._cursorStates.keys());
|
|
24529
|
+
for (var k = 0; k < stateIds.length; k++) {
|
|
24530
|
+
if (!activePeerIds.has(stateIds[k])) this._cursorStates.delete(stateIds[k]);
|
|
24531
|
+
}
|
|
24532
|
+
this._scheduleFrame();
|
|
24533
|
+
}
|
|
24534
|
+
|
|
24535
|
+
_createPointer(peer) {
|
|
24536
|
+
var color = cursorColor(peer.userId);
|
|
24537
|
+
var node = document.createElement('div');
|
|
24538
|
+
node.style.cssText = 'position:absolute;left:0;top:0;display:flex;align-items:flex-start;pointer-events:none;user-select:none;will-change:transform;color:' + color + ';';
|
|
24539
|
+
node.innerHTML = cursorMarkup(peer.name || 'Guest', color);
|
|
24540
|
+
this._overlay.appendChild(node);
|
|
24541
|
+
return {
|
|
24542
|
+
node: node,
|
|
24543
|
+
color: color,
|
|
24544
|
+
name: peer.name || 'Guest',
|
|
24545
|
+
cursor: null,
|
|
24546
|
+
x: null,
|
|
24547
|
+
y: null,
|
|
24548
|
+
lastSeen: Date.now(),
|
|
24549
|
+
};
|
|
24550
|
+
}
|
|
24551
|
+
|
|
24552
|
+
_removePointer(id) {
|
|
24553
|
+
var entry = this._pointers.get(id);
|
|
24554
|
+
if (entry && entry.node.parentNode) entry.node.parentNode.removeChild(entry.node);
|
|
24555
|
+
this._pointers.delete(id);
|
|
24556
|
+
}
|
|
24557
|
+
|
|
24558
|
+
_scheduleFrame() {
|
|
24559
|
+
if (this._frameId != null || !this._pointers || !this._pointers.size
|
|
24560
|
+
|| typeof window.requestAnimationFrame !== 'function') return;
|
|
24561
|
+
var self = this;
|
|
24562
|
+
this._frameId = window.requestAnimationFrame(function() { self._animate(); });
|
|
24563
|
+
}
|
|
24564
|
+
|
|
24565
|
+
_animate() {
|
|
24566
|
+
this._frameId = null;
|
|
24567
|
+
if (!this._pointers || !this._overlay) return;
|
|
24568
|
+
var now = Date.now();
|
|
24569
|
+
var size = documentSize();
|
|
24570
|
+
var scrollX = window.scrollX || window.pageXOffset || 0;
|
|
24571
|
+
var scrollY = window.scrollY || window.pageYOffset || 0;
|
|
24572
|
+
var ids = Array.from(this._pointers.keys());
|
|
22638
24573
|
|
|
22639
|
-
|
|
24574
|
+
for (var i = 0; i < ids.length; i++) {
|
|
24575
|
+
var id = ids[i];
|
|
24576
|
+
var entry = this._pointers.get(id);
|
|
24577
|
+
if (now - entry.lastSeen > CURSOR_IDLE_MS) {
|
|
24578
|
+
this._removePointer(id);
|
|
24579
|
+
continue;
|
|
24580
|
+
}
|
|
24581
|
+
var targetX = entry.cursor.x * size.width - scrollX;
|
|
24582
|
+
var targetY = entry.cursor.y * size.height - scrollY;
|
|
24583
|
+
if (entry.x == null || entry.y == null) {
|
|
24584
|
+
entry.x = targetX;
|
|
24585
|
+
entry.y = targetY;
|
|
24586
|
+
} else {
|
|
24587
|
+
// Exponential interpolation keeps 50 ms presence updates from jumping.
|
|
24588
|
+
entry.x += (targetX - entry.x) * CURSOR_SMOOTHING;
|
|
24589
|
+
entry.y += (targetY - entry.y) * CURSOR_SMOOTHING;
|
|
24590
|
+
}
|
|
24591
|
+
entry.node.style.transform = 'translate3d(' + Math.round(entry.x) + 'px,' + Math.round(entry.y) + 'px,0)';
|
|
24592
|
+
}
|
|
24593
|
+
this._scheduleFrame();
|
|
24594
|
+
}
|
|
24595
|
+
|
|
24596
|
+
disconnectedCallback() {
|
|
24597
|
+
this._lifecycle = (this._lifecycle || 0) + 1;
|
|
24598
|
+
this._startPromise = null;
|
|
24599
|
+
if (this._onDomReady) {
|
|
24600
|
+
document.removeEventListener('DOMContentLoaded', this._onDomReady);
|
|
24601
|
+
this._onDomReady = null;
|
|
24602
|
+
}
|
|
24603
|
+
if (this._unsubscribe) this._unsubscribe();
|
|
24604
|
+
if (this._onPointerMove) document.removeEventListener('pointermove', this._onPointerMove);
|
|
24605
|
+
if (this._onPointerLeave) document.removeEventListener('pointerleave', this._onPointerLeave);
|
|
24606
|
+
if (this._roomRecord) releaseCursorRoom(this._roomRecord, this._presence);
|
|
24607
|
+
if (this._frameId != null && typeof window.cancelAnimationFrame === 'function')
|
|
24608
|
+
window.cancelAnimationFrame(this._frameId);
|
|
24609
|
+
if (this._pointers) {
|
|
24610
|
+
var ids = Array.from(this._pointers.keys());
|
|
24611
|
+
for (var i = 0; i < ids.length; i++) this._removePointer(ids[i]);
|
|
24612
|
+
}
|
|
24613
|
+
if (this._overlay && this._overlay.parentNode)
|
|
24614
|
+
this._overlay.parentNode.removeChild(this._overlay);
|
|
24615
|
+
this._room = null;
|
|
24616
|
+
this._roomRecord = null;
|
|
24617
|
+
this._presence = null;
|
|
24618
|
+
this._overlay = null;
|
|
24619
|
+
this._pointers = null;
|
|
24620
|
+
this._cursorStates = null;
|
|
24621
|
+
this._frameId = null;
|
|
24622
|
+
}
|
|
24623
|
+
}
|
|
24624
|
+
window.customElements.define('swarm-cursors', SwarmCursorsElement);
|
|
24625
|
+
})();
|
|
24626
|
+
`;
|
|
24627
|
+
|
|
24628
|
+
// src/pages/feedback-overlay.ts
|
|
22640
24629
|
var PAGE_FEEDBACK_PARAM = "__swarm-feedback";
|
|
22641
24630
|
function isPageFeedbackRequested(queryParams) {
|
|
22642
24631
|
if (!queryParams.has(PAGE_FEEDBACK_PARAM))
|
|
@@ -22644,6 +24633,33 @@ function isPageFeedbackRequested(queryParams) {
|
|
|
22644
24633
|
const value = (queryParams.get(PAGE_FEEDBACK_PARAM) ?? "").toLowerCase();
|
|
22645
24634
|
return value !== "0" && value !== "false";
|
|
22646
24635
|
}
|
|
24636
|
+
var FEEDBACK_KEY_GUARD_JS = `
|
|
24637
|
+
(function () {
|
|
24638
|
+
if (window.__swarmFeedbackKeys) return;
|
|
24639
|
+
var keys = window.__swarmFeedbackKeys = { host: null, handle: null };
|
|
24640
|
+
var owned = {};
|
|
24641
|
+
function id(e) { return e.code || e.key; }
|
|
24642
|
+
function fromOverlay(e) {
|
|
24643
|
+
return !!keys.host && typeof e.composedPath === 'function' && e.composedPath().indexOf(keys.host) !== -1;
|
|
24644
|
+
}
|
|
24645
|
+
function guard(e) {
|
|
24646
|
+
var k = id(e);
|
|
24647
|
+
var mine = fromOverlay(e);
|
|
24648
|
+
if (!mine && !owned[k]) return;
|
|
24649
|
+
if (e.type === 'keydown') {
|
|
24650
|
+
if (mine) owned[k] = true;
|
|
24651
|
+
else if (!e.repeat) { delete owned[k]; return; }
|
|
24652
|
+
}
|
|
24653
|
+
if (e.type === 'keyup') delete owned[k];
|
|
24654
|
+
e.stopImmediatePropagation();
|
|
24655
|
+
if (mine && keys.handle) keys.handle(e);
|
|
24656
|
+
}
|
|
24657
|
+
['keydown', 'keypress', 'keyup'].forEach(function (type) {
|
|
24658
|
+
window.addEventListener(type, guard, true);
|
|
24659
|
+
});
|
|
24660
|
+
window.addEventListener('blur', function () { owned = {}; });
|
|
24661
|
+
})();
|
|
24662
|
+
`;
|
|
22647
24663
|
var FEEDBACK_OVERLAY_JS = `
|
|
22648
24664
|
(function () {
|
|
22649
24665
|
var cfg = window.__swarmFeedback;
|
|
@@ -22754,6 +24770,7 @@ var FEEDBACK_OVERLAY_JS = `
|
|
|
22754
24770
|
var panel = document.createElement('div');
|
|
22755
24771
|
panel.className = 'panel';
|
|
22756
24772
|
var composer = null;
|
|
24773
|
+
var composerInput = null;
|
|
22757
24774
|
root.appendChild(hl);
|
|
22758
24775
|
root.appendChild(markers);
|
|
22759
24776
|
root.appendChild(panel);
|
|
@@ -22826,11 +24843,25 @@ var FEEDBACK_OVERLAY_JS = `
|
|
|
22826
24843
|
}
|
|
22827
24844
|
render();
|
|
22828
24845
|
}
|
|
22829
|
-
|
|
22830
|
-
if (e.key !== 'Escape') return;
|
|
24846
|
+
function onEscape() {
|
|
22831
24847
|
if (state.draft) { closeComposer(); return; }
|
|
22832
24848
|
if (state.selecting) setSelecting(false);
|
|
24849
|
+
}
|
|
24850
|
+
// Escape pressed on the page while selecting. Keys typed in the overlay never
|
|
24851
|
+
// get here: the key guard stops them and calls onOverlayKey instead.
|
|
24852
|
+
document.addEventListener('keydown', function (e) {
|
|
24853
|
+
if (e.key === 'Escape') onEscape();
|
|
22833
24854
|
}, true);
|
|
24855
|
+
function onOverlayKey(e) {
|
|
24856
|
+
if (e.type !== 'keydown') return;
|
|
24857
|
+
if (e.key === 'Escape') { onEscape(); return; }
|
|
24858
|
+
if (e.key === 'Enter' && (e.metaKey || e.ctrlKey) && composerInput && e.composedPath()[0] === composerInput) {
|
|
24859
|
+
e.preventDefault();
|
|
24860
|
+
commitDraft(composerInput.value);
|
|
24861
|
+
}
|
|
24862
|
+
}
|
|
24863
|
+
window.__swarmFeedbackKeys.host = host;
|
|
24864
|
+
window.__swarmFeedbackKeys.handle = onOverlayKey;
|
|
22834
24865
|
|
|
22835
24866
|
// ─── Composer ──────────────────────────────────────────────────────────
|
|
22836
24867
|
function openComposer(target) {
|
|
@@ -22849,9 +24880,7 @@ var FEEDBACK_OVERLAY_JS = `
|
|
|
22849
24880
|
add.type = 'button';
|
|
22850
24881
|
cancel.addEventListener('click', closeComposer);
|
|
22851
24882
|
add.addEventListener('click', function () { commitDraft(input.value); });
|
|
22852
|
-
|
|
22853
|
-
if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) { e.preventDefault(); commitDraft(input.value); }
|
|
22854
|
-
});
|
|
24883
|
+
composerInput = input;
|
|
22855
24884
|
row.appendChild(cancel);
|
|
22856
24885
|
row.appendChild(add);
|
|
22857
24886
|
composer.appendChild(row);
|
|
@@ -22872,6 +24901,7 @@ var FEEDBACK_OVERLAY_JS = `
|
|
|
22872
24901
|
function closeComposer() {
|
|
22873
24902
|
if (composer) composer.remove();
|
|
22874
24903
|
composer = null;
|
|
24904
|
+
composerInput = null;
|
|
22875
24905
|
state.draft = null;
|
|
22876
24906
|
hideHighlight();
|
|
22877
24907
|
}
|
|
@@ -23137,11 +25167,13 @@ var FEEDBACK_OVERLAY_JS = `
|
|
|
23137
25167
|
`;
|
|
23138
25168
|
function feedbackOverlayScripts(pageId) {
|
|
23139
25169
|
const config = JSON.stringify({ pageId }).replace(/</g, "\\u003c");
|
|
23140
|
-
return `<script>window.__swarmFeedback = ${config};</script><script>${FEEDBACK_OVERLAY_JS}</script>`;
|
|
25170
|
+
return `<script>window.__swarmFeedback = ${config};</script><script>${FEEDBACK_KEY_GUARD_JS}${FEEDBACK_OVERLAY_JS}</script>`;
|
|
25171
|
+
}
|
|
25172
|
+
function feedbackKeyGuardScript() {
|
|
25173
|
+
return `<script>${FEEDBACK_KEY_GUARD_JS}</script>`;
|
|
23141
25174
|
}
|
|
23142
25175
|
|
|
23143
25176
|
// src/http/pages-public.ts
|
|
23144
|
-
init_db3();
|
|
23145
25177
|
init_types();
|
|
23146
25178
|
init_constants();
|
|
23147
25179
|
init_node_env();
|
|
@@ -23246,8 +25278,8 @@ var PAGE_HEAD_DEFAULTS = `<base target="_blank">
|
|
|
23246
25278
|
.swarm-card, swarm-diff { break-inside: avoid; }
|
|
23247
25279
|
}
|
|
23248
25280
|
</style>`;
|
|
23249
|
-
function injectBrowserSdk(html) {
|
|
23250
|
-
const injection = `${PAGE_HEAD_DEFAULTS}<script>${BROWSER_SDK_JS}</script><script>${SWARM_UI_JS}</script>`;
|
|
25281
|
+
function injectBrowserSdk(html, headFirst = "") {
|
|
25282
|
+
const injection = `${headFirst}${PAGE_HEAD_DEFAULTS}<script>${BROWSER_SDK_JS}</script><script>${SWARM_UI_JS}</script>`;
|
|
23251
25283
|
const headOpenMatch = html.match(/<head\b[^>]*>/i);
|
|
23252
25284
|
if (headOpenMatch) {
|
|
23253
25285
|
const idx = headOpenMatch.index + headOpenMatch[0].length;
|
|
@@ -23550,7 +25582,8 @@ async function handlePagesPublic(req, res, pathSegments, queryParams) {
|
|
|
23550
25582
|
res.end();
|
|
23551
25583
|
return true;
|
|
23552
25584
|
}
|
|
23553
|
-
const
|
|
25585
|
+
const feedback = isPageFeedbackRequested(queryParams);
|
|
25586
|
+
const html = injectBrowserSdk(page.body, feedback ? feedbackKeyGuardScript() : "") + (wantsPrint ? PRINT_AUTOTRIGGER_SCRIPT : "") + (feedback ? feedbackOverlayScripts(page.id) : "");
|
|
23554
25587
|
const headers2 = {
|
|
23555
25588
|
"Content-Type": "text/html; charset=utf-8",
|
|
23556
25589
|
"Cache-Control": "no-store",
|
|
@@ -23682,6 +25715,7 @@ async function fetchAllBotChannels(client) {
|
|
|
23682
25715
|
// src/http/poll.ts
|
|
23683
25716
|
init_telemetry_trigger();
|
|
23684
25717
|
init_types();
|
|
25718
|
+
init_request_auth_context();
|
|
23685
25719
|
init_runtime_pools_flag();
|
|
23686
25720
|
function buildBudgetRefusedTrigger(refusal) {
|
|
23687
25721
|
const trigger = {
|
|
@@ -23709,21 +25743,11 @@ var PollTriggerAttachmentSchema = TaskAttachmentSchema.pick({
|
|
|
23709
25743
|
mimeType: true,
|
|
23710
25744
|
sizeBytes: true
|
|
23711
25745
|
});
|
|
23712
|
-
var PollRequestedBySchema = UserSchema.pick({
|
|
23713
|
-
id: true,
|
|
23714
|
-
name: true,
|
|
23715
|
-
email: true,
|
|
23716
|
-
role: true,
|
|
23717
|
-
notes: true
|
|
23718
|
-
}).extend({
|
|
23719
|
-
id: exports_external.string().optional(),
|
|
23720
|
-
comms: UserCommsPrefsSchema.optional()
|
|
23721
|
-
});
|
|
23722
25746
|
var PollTaskOfferedTriggerSchema = exports_external.object({
|
|
23723
25747
|
type: exports_external.literal("task_offered"),
|
|
23724
25748
|
taskId: exports_external.string(),
|
|
23725
25749
|
task: AgentTaskSchema,
|
|
23726
|
-
requestedBy:
|
|
25750
|
+
requestedBy: TaskRequestedBySchema.optional()
|
|
23727
25751
|
});
|
|
23728
25752
|
var PollTaskAssignedTriggerSchema = exports_external.object({
|
|
23729
25753
|
type: exports_external.literal("task_assigned"),
|
|
@@ -23732,7 +25756,7 @@ var PollTaskAssignedTriggerSchema = exports_external.object({
|
|
|
23732
25756
|
task: AgentTaskSchema.extend({
|
|
23733
25757
|
attachments: exports_external.array(PollTriggerAttachmentSchema)
|
|
23734
25758
|
}),
|
|
23735
|
-
requestedBy:
|
|
25759
|
+
requestedBy: TaskRequestedBySchema.optional()
|
|
23736
25760
|
});
|
|
23737
25761
|
var PollUnreadMentionsTriggerSchema = exports_external.object({
|
|
23738
25762
|
type: exports_external.literal("unread_mentions"),
|
|
@@ -23780,26 +25804,6 @@ var pollTriggers = route({
|
|
|
23780
25804
|
});
|
|
23781
25805
|
var CHANNEL_ACTIVITY_INTERVAL_MS = 60000;
|
|
23782
25806
|
var lastChannelActivityCheckAt = 0;
|
|
23783
|
-
function getRequesterNotes(notes) {
|
|
23784
|
-
return typeof notes === "string" && notes.trim().length > 0 ? notes : undefined;
|
|
23785
|
-
}
|
|
23786
|
-
async function buildTriggerRequestedBy(task) {
|
|
23787
|
-
const user = task.requestedByUserId ? await getUserById(task.requestedByUserId) : undefined;
|
|
23788
|
-
if (user) {
|
|
23789
|
-
return {
|
|
23790
|
-
id: user.id,
|
|
23791
|
-
name: user.name,
|
|
23792
|
-
email: user.email,
|
|
23793
|
-
role: user.role,
|
|
23794
|
-
notes: getRequesterNotes(user.notes),
|
|
23795
|
-
comms: getUserCommsPrefs(user)
|
|
23796
|
-
};
|
|
23797
|
-
}
|
|
23798
|
-
if (task.slackUserId) {
|
|
23799
|
-
return { name: renderIdentity(await resolveIdentity("slack", task.slackUserId)) };
|
|
23800
|
-
}
|
|
23801
|
-
return;
|
|
23802
|
-
}
|
|
23803
25807
|
async function attachmentsForTrigger(taskId) {
|
|
23804
25808
|
return (await getTaskAttachments(taskId)).map((a) => ({
|
|
23805
25809
|
id: a.id,
|
|
@@ -23808,12 +25812,25 @@ async function attachmentsForTrigger(taskId) {
|
|
|
23808
25812
|
sizeBytes: a.sizeBytes
|
|
23809
25813
|
}));
|
|
23810
25814
|
}
|
|
23811
|
-
async function claimModelFields(task, agent) {
|
|
25815
|
+
async function claimModelFields(task, agent, auth) {
|
|
25816
|
+
const pin = taskModelPin(task);
|
|
25817
|
+
const cleared = {
|
|
25818
|
+
model: pin,
|
|
25819
|
+
resolvedModel: undefined,
|
|
25820
|
+
modelSource: undefined,
|
|
25821
|
+
modelAlias: undefined,
|
|
25822
|
+
resolvedProfile: undefined
|
|
25823
|
+
};
|
|
23812
25824
|
try {
|
|
23813
|
-
return
|
|
25825
|
+
return {
|
|
25826
|
+
...cleared,
|
|
25827
|
+
...await recordClaimModelResolution({ ...task, model: pin }, agent, {
|
|
25828
|
+
includeRepoSecrets: readsRepoSecrets(auth, agent)
|
|
25829
|
+
})
|
|
25830
|
+
};
|
|
23814
25831
|
} catch (error) {
|
|
23815
25832
|
console.warn(`[/api/poll] model resolution failed for task ${task.id}:`, error);
|
|
23816
|
-
return
|
|
25833
|
+
return cleared;
|
|
23817
25834
|
}
|
|
23818
25835
|
}
|
|
23819
25836
|
var commitCursorsRoute = route({
|
|
@@ -23898,7 +25915,7 @@ async function handlePoll(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
23898
25915
|
const claimedTask = await claimOfferedTask(firstOfferedTask.id, myAgentId);
|
|
23899
25916
|
if (claimedTask) {
|
|
23900
25917
|
await recordRuntimeTask(claimedTask.id);
|
|
23901
|
-
const offeredRequestedBy = await
|
|
25918
|
+
const offeredRequestedBy = await buildTaskRequestedBy(claimedTask);
|
|
23902
25919
|
return {
|
|
23903
25920
|
trigger: {
|
|
23904
25921
|
type: "task_offered",
|
|
@@ -23978,7 +25995,7 @@ async function handlePoll(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
23978
25995
|
agentId: myAgentId
|
|
23979
25996
|
});
|
|
23980
25997
|
});
|
|
23981
|
-
const assignedRequestedBy = await
|
|
25998
|
+
const assignedRequestedBy = await buildTaskRequestedBy(pendingTask);
|
|
23982
25999
|
const assignedTriggerSurface = await resolveTriggerSurface(pendingTask.id, pendingTask.source);
|
|
23983
26000
|
return {
|
|
23984
26001
|
trigger: {
|
|
@@ -23987,7 +26004,7 @@ async function handlePoll(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
23987
26004
|
triggerSurface: assignedTriggerSurface,
|
|
23988
26005
|
task: {
|
|
23989
26006
|
...pendingTask,
|
|
23990
|
-
...await claimModelFields(pendingTask, agent),
|
|
26007
|
+
...await claimModelFields(pendingTask, agent, getRequestAuth(req)),
|
|
23991
26008
|
status: "in_progress",
|
|
23992
26009
|
attachments: await attachmentsForTrigger(pendingTask.id)
|
|
23993
26010
|
},
|
|
@@ -24069,7 +26086,7 @@ async function handlePoll(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
24069
26086
|
agentId: myAgentId
|
|
24070
26087
|
});
|
|
24071
26088
|
});
|
|
24072
|
-
const claimedRequestedBy = await
|
|
26089
|
+
const claimedRequestedBy = await buildTaskRequestedBy(claimed);
|
|
24073
26090
|
const claimedTriggerSurface = await resolveTriggerSurface(claimed.id, claimed.source);
|
|
24074
26091
|
return {
|
|
24075
26092
|
trigger: {
|
|
@@ -24078,7 +26095,7 @@ async function handlePoll(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
24078
26095
|
triggerSurface: claimedTriggerSurface,
|
|
24079
26096
|
task: {
|
|
24080
26097
|
...claimed,
|
|
24081
|
-
...await claimModelFields(claimed, agent),
|
|
26098
|
+
...await claimModelFields(claimed, agent, getRequestAuth(req)),
|
|
24082
26099
|
attachments: await attachmentsForTrigger(claimed.id)
|
|
24083
26100
|
},
|
|
24084
26101
|
...claimedRequestedBy && { requestedBy: claimedRequestedBy }
|
|
@@ -28984,7 +31001,7 @@ async function handleScriptRuns(req, res, pathSegments, queryParams, agentId) {
|
|
|
28984
31001
|
return true;
|
|
28985
31002
|
const result = await executeRawLlm(parsed.body);
|
|
28986
31003
|
if (result.status === "failed") {
|
|
28987
|
-
|
|
31004
|
+
jsonError(res, result.error, 500);
|
|
28988
31005
|
return true;
|
|
28989
31006
|
}
|
|
28990
31007
|
rawLlmRoute.respond(res, 200, result.output);
|
|
@@ -29188,7 +31205,8 @@ var upsertRoute2 = route({
|
|
|
29188
31205
|
schema: exports_external.object({
|
|
29189
31206
|
name: exports_external.string(),
|
|
29190
31207
|
version: exports_external.number(),
|
|
29191
|
-
contentDeduped: exports_external.boolean()
|
|
31208
|
+
contentDeduped: exports_external.boolean(),
|
|
31209
|
+
warnings: exports_external.array(exports_external.string()).optional().describe("Advisory quickjs-executor compatibility findings. Never blocks the save.")
|
|
29192
31210
|
})
|
|
29193
31211
|
},
|
|
29194
31212
|
400: { description: "Validation or typecheck failure" },
|
|
@@ -29348,7 +31366,12 @@ var createScriptApiRoute = route({
|
|
|
29348
31366
|
params: idParamsSchema4,
|
|
29349
31367
|
body: createScriptApiBodySchema,
|
|
29350
31368
|
responses: {
|
|
29351
|
-
201: {
|
|
31369
|
+
201: {
|
|
31370
|
+
description: "Endpoint created",
|
|
31371
|
+
schema: scriptApiWithSecretSchema.extend({
|
|
31372
|
+
warnings: exports_external.array(exports_external.string()).optional().describe("Advisory quickjs-executor compatibility findings for the script.")
|
|
31373
|
+
})
|
|
31374
|
+
},
|
|
29352
31375
|
400: { description: "Validation error or script has no owning agent" },
|
|
29353
31376
|
404: { description: "Script not found" }
|
|
29354
31377
|
},
|
|
@@ -29557,10 +31580,12 @@ async function handleScripts(req, res, pathSegments, queryParams, agentId) {
|
|
|
29557
31580
|
isPromotion: Boolean(existingAgentScript)
|
|
29558
31581
|
});
|
|
29559
31582
|
}
|
|
31583
|
+
const warnings = quickJSCompatWarnings(checkQuickJSCompat(parsed.body.source, { fsMode: parsed.body.fsMode }));
|
|
29560
31584
|
upsertRoute2.respond(res, 200, {
|
|
29561
31585
|
name: result.script.name,
|
|
29562
31586
|
version: result.script.version,
|
|
29563
|
-
contentDeduped: result.contentDeduped
|
|
31587
|
+
contentDeduped: result.contentDeduped,
|
|
31588
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
29564
31589
|
});
|
|
29565
31590
|
return true;
|
|
29566
31591
|
}
|
|
@@ -29605,6 +31630,7 @@ async function handleScripts(req, res, pathSegments, queryParams, agentId) {
|
|
|
29605
31630
|
runtimeInstanceId,
|
|
29606
31631
|
egressSecrets: credentials.egressSecrets,
|
|
29607
31632
|
failedBindings: credentials.failedBindings,
|
|
31633
|
+
userConfig: credentials.userConfig,
|
|
29608
31634
|
apiConnections: getScriptApiConnectionDescriptors({ agentId: agent.id }),
|
|
29609
31635
|
mcpConnections: getScriptMcpConnectionDescriptors({ agentId: agent.id })
|
|
29610
31636
|
});
|
|
@@ -29856,7 +31882,11 @@ async function handleScripts(req, res, pathSegments, queryParams, agentId) {
|
|
|
29856
31882
|
label: parsed.body.label ?? null,
|
|
29857
31883
|
createdBy: await resolveHttpAuditUserId(req, agentId)
|
|
29858
31884
|
});
|
|
29859
|
-
|
|
31885
|
+
const warnings = quickJSCompatWarnings(checkQuickJSCompat(script.source, { fsMode: script.fsMode }));
|
|
31886
|
+
createScriptApiRoute.respond(res, 201, {
|
|
31887
|
+
...endpoint,
|
|
31888
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
31889
|
+
});
|
|
29860
31890
|
return true;
|
|
29861
31891
|
}
|
|
29862
31892
|
if (listScriptApisRoute.match(req.method, pathSegments)) {
|
|
@@ -32101,21 +34131,6 @@ var getMetrics = route({
|
|
|
32101
34131
|
200: { description: "Swarm metrics counts", schema: SwarmMetricsSchema }
|
|
32102
34132
|
}
|
|
32103
34133
|
});
|
|
32104
|
-
var listServices = route({
|
|
32105
|
-
method: "get",
|
|
32106
|
-
path: "/api/services",
|
|
32107
|
-
pattern: ["api", "services"],
|
|
32108
|
-
summary: "List all registered services",
|
|
32109
|
-
tags: ["Stats"],
|
|
32110
|
-
query: exports_external.object({
|
|
32111
|
-
status: exports_external.string().optional(),
|
|
32112
|
-
agentId: exports_external.string().optional(),
|
|
32113
|
-
name: exports_external.string().optional()
|
|
32114
|
-
}),
|
|
32115
|
-
responses: {
|
|
32116
|
-
200: { description: "Service list", schema: exports_external.object({ services: exports_external.array(ServiceSchema) }) }
|
|
32117
|
-
}
|
|
32118
|
-
});
|
|
32119
34134
|
var listScheduledTasks = route({
|
|
32120
34135
|
method: "get",
|
|
32121
34136
|
path: "/api/scheduled-tasks",
|
|
@@ -32201,18 +34216,6 @@ async function handleStats(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
32201
34216
|
});
|
|
32202
34217
|
return true;
|
|
32203
34218
|
}
|
|
32204
|
-
if (listServices.match(req.method, pathSegments)) {
|
|
32205
|
-
const parsed = await listServices.parse(req, res, pathSegments, queryParams);
|
|
32206
|
-
if (!parsed)
|
|
32207
|
-
return true;
|
|
32208
|
-
const services = await getAllServices({
|
|
32209
|
-
status: parsed.query.status || undefined,
|
|
32210
|
-
agentId: parsed.query.agentId || undefined,
|
|
32211
|
-
name: parsed.query.name || undefined
|
|
32212
|
-
});
|
|
32213
|
-
listServices.respond(res, 200, { services });
|
|
32214
|
-
return true;
|
|
32215
|
-
}
|
|
32216
34219
|
if (listScheduledTasks.match(req.method, pathSegments)) {
|
|
32217
34220
|
const parsed = await listScheduledTasks.parse(req, res, pathSegments, queryParams);
|
|
32218
34221
|
if (!parsed)
|
|
@@ -34358,6 +36361,8 @@ async function routeKapsoInbound(payload) {
|
|
|
34358
36361
|
}
|
|
34359
36362
|
|
|
34360
36363
|
// src/http/webhooks.ts
|
|
36364
|
+
init_constants();
|
|
36365
|
+
init_secret_scrubber();
|
|
34361
36366
|
init_event_bus();
|
|
34362
36367
|
var WebhookDispatchResultSchema = exports_external.object({
|
|
34363
36368
|
created: exports_external.boolean(),
|
|
@@ -34453,6 +36458,57 @@ var kapsoWebhook = route({
|
|
|
34453
36458
|
503: { description: "Kapso integration not configured" }
|
|
34454
36459
|
}
|
|
34455
36460
|
});
|
|
36461
|
+
var chatSdkWebhook = route({
|
|
36462
|
+
method: "post",
|
|
36463
|
+
path: "/api/integrations/chat/{adapter}/webhook",
|
|
36464
|
+
pattern: ["api", "integrations", "chat", null, "webhook"],
|
|
36465
|
+
summary: "Handle Chat SDK platform webhooks (Telegram, Slack, Discord, Teams)",
|
|
36466
|
+
tags: ["Webhooks"],
|
|
36467
|
+
params: exports_external.object({ adapter: exports_external.string().describe("Chat SDK adapter name, e.g. telegram") }),
|
|
36468
|
+
auth: { apiKey: false },
|
|
36469
|
+
rbac: {
|
|
36470
|
+
ungated: "provider webhook: the Chat SDK adapter verifies the platform signature (Telegram secret token, Slack signing secret, Discord Ed25519, Teams JWT)"
|
|
36471
|
+
},
|
|
36472
|
+
responses: {
|
|
36473
|
+
200: { description: "Adapter response", unstructured: "adapter response proxied as-is" },
|
|
36474
|
+
401: { description: "Invalid platform signature or secret token" },
|
|
36475
|
+
403: { description: "Teams activity whose serviceUrl is not a Bot Framework host" },
|
|
36476
|
+
404: { description: "Chat SDK disabled or adapter not configured" },
|
|
36477
|
+
413: { description: "Body exceeds 1 MiB" },
|
|
36478
|
+
503: { description: "Adapter configured but failed to start" }
|
|
36479
|
+
}
|
|
36480
|
+
});
|
|
36481
|
+
var CHAT_SDK_WEBHOOK_MAX_BODY_BYTES = 1024 * 1024;
|
|
36482
|
+
function hasTrustedTeamsServiceUrl(body) {
|
|
36483
|
+
let serviceUrl;
|
|
36484
|
+
try {
|
|
36485
|
+
serviceUrl = JSON.parse(body.toString("utf-8"))?.serviceUrl;
|
|
36486
|
+
} catch {
|
|
36487
|
+
return true;
|
|
36488
|
+
}
|
|
36489
|
+
if (serviceUrl === undefined || serviceUrl === null || serviceUrl === "")
|
|
36490
|
+
return true;
|
|
36491
|
+
return typeof serviceUrl === "string" && isTrustedTeamsServiceUrl(serviceUrl);
|
|
36492
|
+
}
|
|
36493
|
+
function toFetchRequest(req, rawBody, fallbackPath) {
|
|
36494
|
+
const headers2 = new Headers;
|
|
36495
|
+
if (Array.isArray(req.rawHeaders) && req.rawHeaders.length > 0) {
|
|
36496
|
+
for (let i = 0;i + 1 < req.rawHeaders.length; i += 2) {
|
|
36497
|
+
headers2.append(req.rawHeaders[i], req.rawHeaders[i + 1]);
|
|
36498
|
+
}
|
|
36499
|
+
} else {
|
|
36500
|
+
for (const [name, value] of Object.entries(req.headers)) {
|
|
36501
|
+
for (const item of Array.isArray(value) ? value : value === undefined ? [] : [value]) {
|
|
36502
|
+
headers2.append(name, item);
|
|
36503
|
+
}
|
|
36504
|
+
}
|
|
36505
|
+
}
|
|
36506
|
+
return new Request(`${getPublicMcpBaseUrl()}${req.url ?? fallbackPath}`, {
|
|
36507
|
+
method: req.method ?? "POST",
|
|
36508
|
+
headers: headers2,
|
|
36509
|
+
body: rawBody.length > 0 ? new Uint8Array(rawBody) : undefined
|
|
36510
|
+
});
|
|
36511
|
+
}
|
|
34456
36512
|
function logKapsoAckResult(action, result) {
|
|
34457
36513
|
if (result.ok)
|
|
34458
36514
|
return;
|
|
@@ -34536,7 +36592,8 @@ async function handleWebhooks(req, res, pathSegments) {
|
|
|
34536
36592
|
res.end(JSON.stringify({ error: "Invalid JSON body" }));
|
|
34537
36593
|
return true;
|
|
34538
36594
|
}
|
|
34539
|
-
|
|
36595
|
+
const deliveryId = req.headers["x-github-delivery"];
|
|
36596
|
+
console.log(`[GitHub] Received ${eventType} event (delivery ${deliveryId ?? "unknown"})`);
|
|
34540
36597
|
let result = { created: false };
|
|
34541
36598
|
try {
|
|
34542
36599
|
result = await runBlockableHandler("GitHub", async () => {
|
|
@@ -34610,13 +36667,13 @@ async function handleWebhooks(req, res, pathSegments) {
|
|
|
34610
36667
|
}
|
|
34611
36668
|
githubWebhook.respond(res, 200, result);
|
|
34612
36669
|
} catch (err) {
|
|
34613
|
-
const
|
|
34614
|
-
console.error(`[GitHub] Error handling ${eventType} event: ${
|
|
36670
|
+
const errorMessage3 = err instanceof Error ? err.message : String(err);
|
|
36671
|
+
console.error(`[GitHub] Error handling ${eventType} event: ${errorMessage3}`);
|
|
34615
36672
|
if (err instanceof Error && err.stack) {
|
|
34616
36673
|
console.error(err.stack);
|
|
34617
36674
|
}
|
|
34618
36675
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
34619
|
-
res.end(JSON.stringify({ error: "Internal server error", message:
|
|
36676
|
+
res.end(JSON.stringify({ error: "Internal server error", message: errorMessage3 }));
|
|
34620
36677
|
}
|
|
34621
36678
|
return true;
|
|
34622
36679
|
}
|
|
@@ -34724,8 +36781,8 @@ async function handleWebhooks(req, res, pathSegments) {
|
|
|
34724
36781
|
}
|
|
34725
36782
|
gitlabWebhook.respond(res, 200, result);
|
|
34726
36783
|
} catch (err) {
|
|
34727
|
-
const
|
|
34728
|
-
console.error(`[GitLab] Error handling ${objectKind} event: ${
|
|
36784
|
+
const errorMessage3 = err instanceof Error ? err.message : String(err);
|
|
36785
|
+
console.error(`[GitLab] Error handling ${objectKind} event: ${errorMessage3}`);
|
|
34729
36786
|
if (err instanceof Error && err.stack) {
|
|
34730
36787
|
console.error(err.stack);
|
|
34731
36788
|
}
|
|
@@ -34815,8 +36872,8 @@ async function handleWebhooks(req, res, pathSegments) {
|
|
|
34815
36872
|
}
|
|
34816
36873
|
azureDevOpsWebhook.respond(res, 200, result);
|
|
34817
36874
|
} catch (err) {
|
|
34818
|
-
const
|
|
34819
|
-
console.error(`[AzureDevOps] Error handling ${eventType} event: ${
|
|
36875
|
+
const errorMessage3 = err instanceof Error ? err.message : String(err);
|
|
36876
|
+
console.error(`[AzureDevOps] Error handling ${eventType} event: ${errorMessage3}`);
|
|
34820
36877
|
if (err instanceof Error && err.stack) {
|
|
34821
36878
|
console.error(err.stack);
|
|
34822
36879
|
}
|
|
@@ -34861,8 +36918,7 @@ async function handleWebhooks(req, res, pathSegments) {
|
|
|
34861
36918
|
}
|
|
34862
36919
|
agentmailWebhook.respond(res, 200, { received: true });
|
|
34863
36920
|
if (payload.message && !isInboxAllowed(payload.message.inbox_id, process.env.AGENTMAIL_INBOX_DOMAIN_FILTER)) {
|
|
34864
|
-
|
|
34865
|
-
console.log(`[AgentMail] Ignoring event for inbox domain "${domain}" (not in AGENTMAIL_INBOX_DOMAIN_FILTER)`);
|
|
36921
|
+
console.log(`[AgentMail] Ignoring event for inbox "${payload.message.inbox_id}" (not in AGENTMAIL_INBOX_DOMAIN_FILTER)`);
|
|
34866
36922
|
return true;
|
|
34867
36923
|
}
|
|
34868
36924
|
if (payload.message && !isSenderAllowed(payload.message.from_, process.env.AGENTMAIL_SENDER_DOMAIN_FILTER)) {
|
|
@@ -34884,8 +36940,8 @@ async function handleWebhooks(req, res, pathSegments) {
|
|
|
34884
36940
|
console.log(`[AgentMail] Ignoring event type: ${payload.event_type}`);
|
|
34885
36941
|
}
|
|
34886
36942
|
} catch (err) {
|
|
34887
|
-
const
|
|
34888
|
-
console.error(`[AgentMail] Error handling ${payload.event_type} event: ${
|
|
36943
|
+
const errorMessage3 = err instanceof Error ? err.message : String(err);
|
|
36944
|
+
console.error(`[AgentMail] Error handling ${payload.event_type} event: ${errorMessage3}`);
|
|
34889
36945
|
if (err instanceof Error && err.stack) {
|
|
34890
36946
|
console.error(err.stack);
|
|
34891
36947
|
}
|
|
@@ -34945,16 +37001,84 @@ async function handleWebhooks(req, res, pathSegments) {
|
|
|
34945
37001
|
}
|
|
34946
37002
|
kapsoWebhook.respond(res, 200, { received: true, routing: routing.kind });
|
|
34947
37003
|
} catch (err) {
|
|
34948
|
-
const
|
|
37004
|
+
const errorMessage3 = err instanceof Error ? err.message : String(err);
|
|
34949
37005
|
if (err instanceof WebhookError) {
|
|
34950
|
-
console.warn(`[Kapso] Workflow dispatch rejected: ${
|
|
37006
|
+
console.warn(`[Kapso] Workflow dispatch rejected: ${errorMessage3}`);
|
|
34951
37007
|
} else {
|
|
34952
|
-
console.error(`[Kapso] Error handling inbound event: ${
|
|
37008
|
+
console.error(`[Kapso] Error handling inbound event: ${errorMessage3}`);
|
|
34953
37009
|
}
|
|
34954
37010
|
kapsoWebhook.respond(res, 200, { received: true, routing: "error" });
|
|
34955
37011
|
}
|
|
34956
37012
|
return true;
|
|
34957
37013
|
}
|
|
37014
|
+
if (chatSdkWebhook.match(req.method, pathSegments)) {
|
|
37015
|
+
const adapterName = pathSegments[3] ?? "";
|
|
37016
|
+
if (!isChatSdkEnabled() || !isChatAdapterConfigured(adapterName)) {
|
|
37017
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
37018
|
+
res.end(JSON.stringify({ error: "Chat SDK adapter not configured" }));
|
|
37019
|
+
return true;
|
|
37020
|
+
}
|
|
37021
|
+
if (isChatSdkStopping()) {
|
|
37022
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
37023
|
+
res.end(JSON.stringify({ error: "Chat SDK is shutting down" }));
|
|
37024
|
+
return true;
|
|
37025
|
+
}
|
|
37026
|
+
const chat = getChatInstance(adapterName);
|
|
37027
|
+
const handler = chat?.webhooks[adapterName];
|
|
37028
|
+
if (!handler) {
|
|
37029
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
37030
|
+
res.end(JSON.stringify({ error: "Chat SDK adapter failed to start" }));
|
|
37031
|
+
return true;
|
|
37032
|
+
}
|
|
37033
|
+
if (enforceContentLengthCap(req, res, CHAT_SDK_WEBHOOK_MAX_BODY_BYTES) === BODY_TOO_LARGE) {
|
|
37034
|
+
return true;
|
|
37035
|
+
}
|
|
37036
|
+
const chunks = [];
|
|
37037
|
+
let receivedBytes = 0;
|
|
37038
|
+
let tooLarge = false;
|
|
37039
|
+
for await (const chunk of req) {
|
|
37040
|
+
if (tooLarge)
|
|
37041
|
+
continue;
|
|
37042
|
+
receivedBytes += chunk.byteLength;
|
|
37043
|
+
if (receivedBytes > CHAT_SDK_WEBHOOK_MAX_BODY_BYTES) {
|
|
37044
|
+
tooLarge = true;
|
|
37045
|
+
chunks.length = 0;
|
|
37046
|
+
jsonError(res, `Payload too large (max ${CHAT_SDK_WEBHOOK_MAX_BODY_BYTES} bytes)`, 413);
|
|
37047
|
+
continue;
|
|
37048
|
+
}
|
|
37049
|
+
chunks.push(chunk);
|
|
37050
|
+
}
|
|
37051
|
+
if (tooLarge)
|
|
37052
|
+
return true;
|
|
37053
|
+
const rawBody = Buffer.concat(chunks);
|
|
37054
|
+
if (adapterName === "teams" && !hasTrustedTeamsServiceUrl(rawBody)) {
|
|
37055
|
+
console.warn("[chat-sdk] teams webhook refused: serviceUrl is not a Bot Framework host");
|
|
37056
|
+
res.writeHead(403, { "Content-Type": "application/json" });
|
|
37057
|
+
res.end(JSON.stringify({ error: "Untrusted Teams serviceUrl" }));
|
|
37058
|
+
return true;
|
|
37059
|
+
}
|
|
37060
|
+
const request = toFetchRequest(req, rawBody, `/api/integrations/chat/${adapterName}/webhook`);
|
|
37061
|
+
try {
|
|
37062
|
+
const response = await handler(request, {
|
|
37063
|
+
waitUntil: trackBackground
|
|
37064
|
+
});
|
|
37065
|
+
const responseHeaders = {};
|
|
37066
|
+
response.headers.forEach((value, name) => {
|
|
37067
|
+
responseHeaders[name] = value;
|
|
37068
|
+
});
|
|
37069
|
+
const body = Buffer.from(await response.arrayBuffer());
|
|
37070
|
+
res.writeHead(response.status, responseHeaders);
|
|
37071
|
+
res.end(body);
|
|
37072
|
+
} catch (err) {
|
|
37073
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
37074
|
+
console.error(`[chat-sdk] ${adapterName} webhook failed: ${scrubSecrets(message)}`);
|
|
37075
|
+
if (!res.headersSent) {
|
|
37076
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
37077
|
+
}
|
|
37078
|
+
res.end(JSON.stringify({ error: "Chat SDK webhook failed" }));
|
|
37079
|
+
}
|
|
37080
|
+
return true;
|
|
37081
|
+
}
|
|
34958
37082
|
return false;
|
|
34959
37083
|
}
|
|
34960
37084
|
|
|
@@ -35977,6 +38101,7 @@ async function handleX(req, res, pathSegments) {
|
|
|
35977
38101
|
timeoutMs,
|
|
35978
38102
|
egressSecrets: credentials.egressSecrets,
|
|
35979
38103
|
failedBindings: credentials.failedBindings,
|
|
38104
|
+
userConfig: credentials.userConfig,
|
|
35980
38105
|
apiConnections: getScriptApiConnectionDescriptors({ agentId: endpoint.agentId }),
|
|
35981
38106
|
mcpConnections: getScriptMcpConnectionDescriptors({ agentId: endpoint.agentId })
|
|
35982
38107
|
});
|
|
@@ -36125,7 +38250,6 @@ var httpServer = createHttpServer(async (req, res) => {
|
|
|
36125
38250
|
() => handleAgentRegister(req, res, pathSegments, myAgentId),
|
|
36126
38251
|
() => handlePoll(req, res, pathSegments, queryParams, myAgentId),
|
|
36127
38252
|
() => handleSessionData(req, res, pathSegments, queryParams, myAgentId),
|
|
36128
|
-
() => handleEcosystem(req, res, pathSegments, myAgentId),
|
|
36129
38253
|
() => handleTrackers(req, res, pathSegments),
|
|
36130
38254
|
() => handleWebhooks(req, res, pathSegments),
|
|
36131
38255
|
() => handleAgentsRest(req, res, pathSegments, queryParams, myAgentId),
|
|
@@ -36157,6 +38281,7 @@ var httpServer = createHttpServer(async (req, res) => {
|
|
|
36157
38281
|
() => handleDbQuery(req, res, pathSegments, queryParams),
|
|
36158
38282
|
() => handleMetrics(req, res, pathSegments, queryParams, myAgentId),
|
|
36159
38283
|
() => handleModelsCatalog(req, res, pathSegments, queryParams),
|
|
38284
|
+
() => handleModelProfiles(req, res, pathSegments, queryParams, myAgentId),
|
|
36160
38285
|
() => handleRepos(req, res, pathSegments, queryParams, myAgentId),
|
|
36161
38286
|
() => handleSkills(req, res, pathSegments, queryParams, myAgentId),
|
|
36162
38287
|
() => handleScriptConnections(req, res, pathSegments, queryParams, myAgentId),
|
|
@@ -36217,7 +38342,7 @@ var httpServer = createHttpServer(async (req, res) => {
|
|
|
36217
38342
|
});
|
|
36218
38343
|
});
|
|
36219
38344
|
var detachRealtime = attachRealtimeTransport(httpServer);
|
|
36220
|
-
var { attachControllerTransport } = await import("./controllers-connect-
|
|
38345
|
+
var { attachControllerTransport } = await import("./controllers-connect-v9yn8zb9.js");
|
|
36221
38346
|
var detachControllers = attachControllerTransport(httpServer);
|
|
36222
38347
|
var stopWorkerNudges = startWorkerNudgeListener();
|
|
36223
38348
|
var stopRoomDeletion = realtimeBus.subscribe("room:namespace-deleted", (namespace) => {
|
|
@@ -36246,7 +38371,7 @@ async function shutdown() {
|
|
|
36246
38371
|
sessionsProcessed: getServerSessionsProcessed()
|
|
36247
38372
|
});
|
|
36248
38373
|
if (hasCapability("scheduling")) {
|
|
36249
|
-
const { stopScheduler } = await import("./index-
|
|
38374
|
+
const { stopScheduler } = await import("./index-3gtmkhc6.js");
|
|
36250
38375
|
stopScheduler();
|
|
36251
38376
|
}
|
|
36252
38377
|
stopHeartbeat();
|
|
@@ -36256,8 +38381,9 @@ async function shutdown() {
|
|
|
36256
38381
|
await drainApi();
|
|
36257
38382
|
await stopScriptRunSupervisor();
|
|
36258
38383
|
await stopSlackApp();
|
|
38384
|
+
await stopChatSdk();
|
|
36259
38385
|
if (process.env.OAUTH_KEEPALIVE_DISABLE !== "true") {
|
|
36260
|
-
const { stopOAuthKeepalive } = await import("./keepalive-
|
|
38386
|
+
const { stopOAuthKeepalive } = await import("./keepalive-psj6j54c.js");
|
|
36261
38387
|
await stopOAuthKeepalive();
|
|
36262
38388
|
}
|
|
36263
38389
|
stopOAuthPendingGc();
|
|
@@ -36337,15 +38463,21 @@ try {
|
|
|
36337
38463
|
console.warn("[startup] CAPABILITIES upgrade seed failed (non-fatal):", err);
|
|
36338
38464
|
}
|
|
36339
38465
|
try {
|
|
36340
|
-
const { ensurePricingSeeded } = await import("./boot-seeds-
|
|
38466
|
+
const { ensurePricingSeeded } = await import("./boot-seeds-e9sta0yt.js");
|
|
36341
38467
|
ensurePricingSeeded();
|
|
36342
|
-
const { startPricingRefreshLoop } = await import("./pricing-refresh-
|
|
38468
|
+
const { startPricingRefreshLoop } = await import("./pricing-refresh-kefx9v3t.js");
|
|
36343
38469
|
startPricingRefreshLoop();
|
|
36344
38470
|
} catch (err) {
|
|
36345
38471
|
console.error("[startup] Failed to seed pricing rows:", err);
|
|
36346
38472
|
}
|
|
36347
38473
|
try {
|
|
36348
|
-
const {
|
|
38474
|
+
const { ensureModelProfilesSeeded } = await import("./boot-seeds-e9sta0yt.js");
|
|
38475
|
+
await ensureModelProfilesSeeded();
|
|
38476
|
+
} catch (err) {
|
|
38477
|
+
console.error("[startup] Failed to seed model profiles:", err);
|
|
38478
|
+
}
|
|
38479
|
+
try {
|
|
38480
|
+
const { ensureRbacSeeded } = await import("./boot-seeds-e9sta0yt.js");
|
|
36349
38481
|
ensureRbacSeeded();
|
|
36350
38482
|
} catch (err) {
|
|
36351
38483
|
console.error("[startup] Failed to sync RBAC seed rows:", err);
|
|
@@ -36353,7 +38485,7 @@ try {
|
|
|
36353
38485
|
throw err;
|
|
36354
38486
|
}
|
|
36355
38487
|
try {
|
|
36356
|
-
const { runAllSeeders } = await import("./index-
|
|
38488
|
+
const { runAllSeeders } = await import("./index-5wyfjc2c.js");
|
|
36357
38489
|
await runAllSeeders({ scriptEmbeddingMode: "skip" });
|
|
36358
38490
|
} catch (err) {
|
|
36359
38491
|
console.error("[startup] Failed to seed built-in entities:", err);
|
|
@@ -36401,49 +38533,53 @@ httpServer.listen(port, async () => {
|
|
|
36401
38533
|
} catch (err) {
|
|
36402
38534
|
console.error("[Slack] Failed to start, continuing boot without Slack:", err);
|
|
36403
38535
|
}
|
|
38536
|
+
startChatSdk().catch((err) => {
|
|
38537
|
+
console.error("[chat-sdk] Failed to start, continuing without Chat SDK:", scrubSecrets(err instanceof Error ? err.message : String(err)));
|
|
38538
|
+
});
|
|
36404
38539
|
startQueueStallAlarm();
|
|
36405
38540
|
initGitHub();
|
|
36406
38541
|
initGitLab();
|
|
36407
38542
|
initAzureDevOps();
|
|
38543
|
+
initAzureDevOpsOutbound();
|
|
36408
38544
|
initAgentMail();
|
|
36409
38545
|
await initLinear();
|
|
36410
38546
|
await initJira();
|
|
36411
38547
|
await initWorkflows();
|
|
36412
38548
|
await startScriptRunSupervisor(getMcpBaseUrl());
|
|
36413
38549
|
if (hasCapability("scheduling")) {
|
|
36414
|
-
const { startScheduler } = await import("./index-
|
|
36415
|
-
const { getExecutorRegistry: getExecutorRegistry2 } = await import("./index-
|
|
38550
|
+
const { startScheduler } = await import("./index-3gtmkhc6.js");
|
|
38551
|
+
const { getExecutorRegistry: getExecutorRegistry2 } = await import("./index-5e101240.js");
|
|
36416
38552
|
const intervalMs = Number(process.env.SCHEDULER_INTERVAL_MS) || 1e4;
|
|
36417
38553
|
startScheduler(getExecutorRegistry2(), intervalMs, {
|
|
36418
38554
|
runId: globalState.__runId
|
|
36419
38555
|
});
|
|
36420
38556
|
}
|
|
36421
38557
|
if (!isEnvFlagEnabled("HEARTBEAT_DISABLE", false)) {
|
|
36422
|
-
const { startHeartbeat } = await import("./index-
|
|
38558
|
+
const { startHeartbeat } = await import("./index-3k5hkz7r.js");
|
|
36423
38559
|
const heartbeatMs = Number(process.env.HEARTBEAT_INTERVAL_MS) || 90000;
|
|
36424
38560
|
startHeartbeat(heartbeatMs);
|
|
36425
38561
|
}
|
|
36426
38562
|
startPoolRouting();
|
|
36427
38563
|
startRuntimeSupervisor();
|
|
36428
38564
|
if (process.env.OAUTH_KEEPALIVE_DISABLE !== "true") {
|
|
36429
|
-
const { startOAuthKeepalive } = await import("./keepalive-
|
|
38565
|
+
const { startOAuthKeepalive } = await import("./keepalive-psj6j54c.js");
|
|
36430
38566
|
startOAuthKeepalive();
|
|
36431
38567
|
}
|
|
36432
38568
|
if (process.env.OAUTH_REFRESH_SWEEP_DISABLE !== "true") {
|
|
36433
|
-
const { startOAuthRefreshSweep } = await import("./oauth-refresh-sweep-
|
|
38569
|
+
const { startOAuthRefreshSweep } = await import("./oauth-refresh-sweep-0284h93b.js");
|
|
36434
38570
|
startOAuthRefreshSweep();
|
|
36435
38571
|
}
|
|
36436
38572
|
startOAuthPendingGc();
|
|
36437
38573
|
await startMemoryGc();
|
|
36438
38574
|
await startDbRetention();
|
|
36439
38575
|
startHumanFreeDrain();
|
|
36440
|
-
import("./boot-reembed-
|
|
38576
|
+
import("./boot-reembed-whzbdthj.js").then(({ runBootReembed }) => runBootReembed()).catch((err) => {
|
|
36441
38577
|
console.error("[boot-reembed] startup backfill failed (non-fatal):", err);
|
|
36442
38578
|
});
|
|
36443
|
-
import("./boot-reembed-
|
|
38579
|
+
import("./boot-reembed-9apm9qw6.js").then(({ runBootReembedScripts }) => runBootReembedScripts()).catch((err) => {
|
|
36444
38580
|
console.error("[boot-reembed-scripts] startup backfill failed (non-fatal):", err);
|
|
36445
38581
|
});
|
|
36446
|
-
import("./boot-scrub-sweep-
|
|
38582
|
+
import("./boot-scrub-sweep-ndzmvmgt.js").then(({ runBootScrubSweep }) => runBootScrubSweep()).catch((err) => {
|
|
36447
38583
|
console.error("[boot-scrub-sweep] startup scrub failed (non-fatal):", err);
|
|
36448
38584
|
});
|
|
36449
38585
|
}).on("error", (err) => {
|