@desplega.ai/agent-swarm 2.3.0 → 2.4.1
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 -0
- package/dist/{acp-adapter-v85a2bqy.js → acp-adapter-zvzr538d.js} +9 -9
- package/dist/{actions-zgcxfspj.js → actions-jhtqdw69.js} +21 -19
- package/dist/{amp-adapter-jw9xfz42.js → amp-adapter-bd84rhs2.js} +4 -4
- package/dist/{app-4e1s4675.js → app-kreq1gwg.js} +3 -3
- package/dist/{assistant-v238n9dk.js → assistant-3k22sbsq.js} +23 -21
- package/dist/{boot-reembed-p666cw5n.js → boot-reembed-e53x6b9v.js} +1 -1
- package/dist/{boot-reembed-w6pm64j4.js → boot-reembed-f6z923ny.js} +1 -1
- package/dist/{boot-scrub-sweep-jcyjagn3.js → boot-scrub-sweep-0538vsb7.js} +4 -4
- package/dist/{boot-seeds-gbedmamp.js → boot-seeds-6wxqkfk2.js} +4 -4
- package/dist/{claude-adapter-q7wspzt8.js → claude-adapter-gfts5044.js} +9 -9
- package/dist/{claude-managed-adapter-n7ahjyx6.js → claude-managed-adapter-h2y889ge.js} +3 -3
- package/dist/{claude-managed-setup-wza784fb.js → claude-managed-setup-h00hsgtz.js} +2 -2
- package/dist/{claude-sdk-session-yf400ptr.js → claude-sdk-session-56besmfz.js} +13 -13
- package/dist/{cli-65q70v2v.js → cli-0bh477zn.js} +33 -6
- package/dist/{cli-4ync7m60.js → cli-0ebey9hg.js} +1 -1
- package/dist/{cli-665j3feb.js → cli-0xk505e8.js} +1 -1
- package/dist/{cli-7gh7020n.js → cli-1dmcq2eh.js} +1 -1
- package/dist/{cli-gzvb3d4a.js → cli-1jx0srjt.js} +3 -3
- package/dist/{cli-zj4d6afs.js → cli-1pa3rz69.js} +21 -6
- package/dist/{cli-7ayprxp7.js → cli-2dfbv8qr.js} +8 -0
- package/dist/{cli-30m5q8mc.js → cli-3kh4kpqy.js} +6 -6
- package/dist/{cli-qrwp40st.js → cli-3p8fqcnh.js} +2 -2
- package/dist/{cli-y3dvz6ge.js → cli-3r448dqn.js} +3 -3
- package/dist/{cli-a17tzmn0.js → cli-3tnpj2bh.js} +7 -7
- package/dist/{cli-ea7chd26.js → cli-471qk6s2.js} +93 -46
- package/dist/{cli-a49vpbf3.js → cli-5kqnemen.js} +1 -1
- package/dist/{cli-0rh20bp3.js → cli-6ncjvx55.js} +3 -3
- package/dist/{cli-fb6t92v8.js → cli-6x9ma5w5.js} +1 -1
- package/dist/{cli-k28p6xh7.js → cli-7225gwj7.js} +2 -2
- package/dist/{cli-8cvvytad.js → cli-8nyhf7za.js} +3 -3
- package/dist/{cli-e09hj50q.js → cli-8wkb09q5.js} +11 -11
- package/dist/{cli-j0v75y13.js → cli-a0kcty51.js} +4 -4
- package/dist/{cli-zes4tsbb.js → cli-a4gvwttf.js} +4 -4
- package/dist/{cli-jhp7w1fy.js → cli-a5ebc95n.js} +2 -2
- package/dist/{cli-5d1m1qca.js → cli-ash6xxk7.js} +2 -2
- package/dist/{cli-fp4xbw07.js → cli-axh29tyr.js} +14 -12
- package/dist/{cli-p22zdvnt.js → cli-bn0rewsy.js} +3 -3
- package/dist/{cli-hz64bcnj.js → cli-bn5kevnf.js} +11 -373
- package/dist/{cli-8jtrekpy.js → cli-d2mb5s8a.js} +7 -7
- package/dist/{cli-c5sbmw1v.js → cli-d82btycs.js} +94 -15
- package/dist/{cli-vjfpshn3.js → cli-ejs3m1sx.js} +1 -1
- package/dist/{cli-yv0vgscc.js → cli-fpmsqq59.js} +18 -2
- package/dist/cli-fz9kewbh.js +382 -0
- package/dist/{cli-4s1kqrqg.js → cli-g0hgtz4p.js} +6 -6
- package/dist/{cli-hn3pftj2.js → cli-gf0kfg81.js} +7 -7
- package/dist/{cli-d43xqzbe.js → cli-hqhzg1qq.js} +2 -2
- package/dist/{cli-azs9bhc5.js → cli-j4rqdf2n.js} +2 -2
- package/dist/{cli-wtg6edvd.js → cli-jh0qcrga.js} +1 -1
- package/dist/{cli-kqypty34.js → cli-k9z4zd4p.js} +6 -5
- package/dist/{cli-jhmjwqcg.js → cli-kmqgmcnb.js} +47 -14
- package/dist/{cli-8qsx817b.js → cli-kr2pmfqx.js} +583 -769
- package/dist/{cli-4g629sgr.js → cli-m2qgh5sv.js} +20 -1
- package/dist/{cli-9e9kjqsj.js → cli-mfe7f7b5.js} +2 -2
- package/dist/{cli-x3en6ct3.js → cli-nwyw3zhg.js} +1 -1
- package/dist/{cli-6vcyw6v7.js → cli-nx462e4v.js} +5 -3
- package/dist/{cli-tr24eeny.js → cli-p0rakbrk.js} +9 -9
- package/dist/{cli-4hmaheb6.js → cli-p6argxh9.js} +9 -9
- package/dist/{cli-3avfh5az.js → cli-pemywhfq.js} +2 -2
- package/dist/{cli-etvn5ej2.js → cli-pnp2j5zg.js} +2 -2
- package/dist/{cli-wk8ccrz6.js → cli-pw7v69vp.js} +2 -2
- package/dist/{cli-ka01m5rb.js → cli-rdq7tzwm.js} +17 -14
- package/dist/{cli-re4wkbzx.js → cli-rqqsfz30.js} +677 -104
- package/dist/{cli-xgn4rwyv.js → cli-s48yvzb8.js} +4 -4
- package/dist/{cli-zv4vy2b0.js → cli-s95dknxh.js} +1 -1
- package/dist/{cli-g6p7c2my.js → cli-sjyetj21.js} +2 -2
- package/dist/{cli-rdr166ht.js → cli-smvcw9z5.js} +1 -1
- package/dist/{cli-fhvqt2fp.js → cli-sv5gb0yb.js} +1 -1
- package/dist/{cli-yv3n83x8.js → cli-syhq378v.js} +2 -2
- package/dist/{cli-q1812jbw.js → cli-tbhwrxpt.js} +9 -9
- package/dist/{cli-w7z94e6c.js → cli-tx00c0h6.js} +4 -2
- package/dist/{cli-dxwh4j32.js → cli-vy0fay6k.js} +46 -37
- package/dist/{cli-ptkpaqfy.js → cli-wt645v7q.js} +1 -1
- package/dist/{cli-ppapy34w.js → cli-wxbn3yfx.js} +2 -2
- package/dist/{cli-cby329ny.js → cli-x5mq1fyq.js} +3 -3
- package/dist/{cli-rc3r0bnh.js → cli-xbzvet96.js} +6 -6
- package/dist/{cli-4mkvwgzr.js → cli-xf234ws8.js} +1 -1
- package/dist/{cli-6p7ddg72.js → cli-xftrvxag.js} +5 -5
- package/dist/cli-z071fb95.js +848 -0
- package/dist/{cli-1v8w782f.js → cli-z1ew8vbq.js} +14 -7
- package/dist/{cli-9td5ss65.js → cli-zfg9enh9.js} +3 -3
- package/dist/{cli-yxqag76b.js → cli-zw88zjba.js} +1 -1
- package/dist/cli.js +19 -19
- package/dist/{codex-adapter-nhjj2veb.js → codex-adapter-p6rmm7vv.js} +6 -6
- package/dist/{codex-hook-x6tm6y5m.js → codex-hook-x7nejyrr.js} +8 -5
- package/dist/{codex-login-35p4sk85.js → codex-login-m819c41r.js} +1 -1
- package/dist/{codex-session-runner-6hggrjbx.js → codex-session-runner-8vd5t9t0.js} +7 -7
- package/dist/{commands-mrw1ph2w.js → commands-7ems2p13.js} +2 -2
- package/dist/{controller-pt1zam5w.js → controller-m263fg43.js} +4 -4
- package/dist/{controllers-connect-ymw5541d.js → controllers-connect-bg33ae53.js} +28 -24
- package/dist/{cursor-adapter-cf4zh5x0.js → cursor-adapter-cz6nm6mj.js} +3 -3
- package/dist/{db-hy80219q.js → db-w505dwa8.js} +2 -2
- package/dist/{db-hhpy6m4x.js → db-ys2rbdft.js} +3 -2
- package/dist/{devin-adapter-7egzmt0k.js → devin-adapter-zgqadvnm.js} +1 -1
- package/dist/{dispatcher-6pdjtkr1.js → dispatcher-0s0mckqr.js} +20 -18
- package/dist/{docker-njavd3fb.js → docker-fkgtr75t.js} +2 -2
- package/dist/{dsh-adapter-fbx06re2.js → dsh-adapter-3n3net33.js} +3 -3
- package/dist/{e2b-5r6a5jeb.js → e2b-7am6tmq6.js} +2 -2
- package/dist/{e2b-pw1nvtmh.js → e2b-thdcxtjd.js} +2 -2
- package/dist/{easter-egg-3d-6hg2r942.js → easter-egg-3d-5cgynaja.js} +1 -1
- package/dist/{event-bus-101fenkh.js → event-bus-9vpk8gmq.js} +1 -1
- package/dist/{execute-bcqc69a5.js → execute-t4e6m3rw.js} +1 -1
- package/dist/{external-channel-j6vb6ya1.js → external-channel-5b23fv8z.js} +2 -2
- package/dist/{google-generative-ai-phpmwxvg.js → google-generative-ai-bmq8gkf3.js} +1 -1
- package/dist/{google-vertex-es9ybgdy.js → google-vertex-b1rvp52s.js} +1 -1
- package/dist/{grok-adapter-1193sprp.js → grok-adapter-q9fghe5q.js} +10 -10
- package/dist/handlers-mqrym347.js +39 -0
- package/dist/{heartbeat-h1w1ktce.js → heartbeat-tww6yx1w.js} +23 -21
- package/dist/{hook-ejja4bpe.js → hook-dx0pkvar.js} +16 -9
- package/dist/{hook-skrbja18.js → hook-pvmh9b6m.js} +6 -6
- package/dist/{http-mzh41mfj.js → http-k2ejbt05.js} +975 -292
- package/dist/{index-6rmnn89d.js → index-2z710v4x.js} +10 -10
- package/dist/{index-xmmkyx82.js → index-dgz61nff.js} +20 -18
- package/dist/{index-wgsw9xcz.js → index-g6tbb6ve.js} +1 -1
- package/dist/index-h2ek08d6.js +40 -0
- package/dist/index-pqfwh9nn.js +41 -0
- package/dist/{index-epr2gnbw.js → index-x5aktmp8.js} +1 -1
- package/dist/{index-x0q2a4f9.js → index-ysd45kgc.js} +1 -1
- package/dist/{index-g909y089.js → index-zgvqzjsw.js} +8 -8
- package/dist/{keepalive-4djvbm2j.js → keepalive-nhvpw10z.js} +4 -4
- package/dist/lead-jb6sf69z.js +78 -0
- package/dist/{maintenance-f3xhnjsm.js → maintenance-agakc519.js} +1 -1
- package/dist/{oauth-refresh-sweep-fpd6x6rw.js → oauth-refresh-sweep-4dcbhtn0.js} +3 -3
- package/dist/{onboard-196tjgyn.js → onboard-4n2kwa4v.js} +3 -3
- package/dist/{opencode-adapter-eeesgfqk.js → opencode-adapter-3s7ax02e.js} +7 -7
- package/dist/{otel-impl-g8fh4c21.js → otel-impl-tfa12080.js} +2 -2
- package/dist/{pi-mono-adapter-zasp3ze3.js → pi-mono-adapter-ytp7gz8b.js} +24 -13
- package/dist/{pricing-refresh-sdcf5yaz.js → pricing-refresh-3cqx3f3x.js} +3 -3
- package/dist/{rbac-roles-bsp5657v.js → rbac-roles-9fae5rnv.js} +5 -3
- package/dist/{rbac-roles-2x4g1wb5.js → rbac-roles-y6zbfzve.js} +2 -2
- package/dist/{render-v2-0pyb39bb.js → render-v2-emawjvc4.js} +3 -3
- package/dist/{resume-a86tkj6r.js → resume-zhgts7h7.js} +10 -8
- package/dist/{runtime-ar8310ba.js → runtime-xe3aqy4x.js} +1 -1
- package/dist/session-access-cm823e06.js +25 -0
- package/dist/{setup-ypbzcapd.js → setup-tkp99w1e.js} +1 -1
- package/dist/{virtual-modules-wzk6jf3a.js → virtual-modules-4r3zkc20.js} +2 -2
- package/dist/{work-objects-yjp8de12.js → work-objects-f722h263.js} +23 -21
- package/dist/worker-p7f336rq.js +78 -0
- package/dist/{x-8ewt8gxn.js → x-y45mthwn.js} +2 -2
- package/openapi.json +1086 -305
- package/package.json +2 -2
- package/src/apps/action-caller.ts +6 -0
- package/src/apps/action-runner.ts +146 -0
- package/src/apps/row-store.ts +4 -2
- package/src/apps/script-types.ts +17 -4
- package/src/be/db/active-sessions.ts +5 -0
- package/src/be/db/session-visibility.ts +29 -0
- package/src/be/db/tasks/read.ts +61 -3
- package/src/be/db/tasks/write.ts +15 -6
- package/src/be/db.ts +106 -37
- package/src/be/events.ts +11 -0
- package/src/be/fleet-benchmark-stats.ts +10 -5
- package/src/be/migrations/230_session_shares.sql +23 -0
- package/src/be/models-catalog.ts +4 -0
- package/src/be/onboarding.ts +6 -0
- package/src/be/rbac-roles.ts +2 -0
- package/src/be/scripts/typecheck.ts +13 -0
- package/src/be/session-shares.ts +244 -0
- package/src/be/swarm-config-guard.ts +33 -3
- package/src/be/telemetry-enable.ts +1 -1
- package/src/commands/provider-credentials.ts +12 -2
- package/src/commands/runner.ts +14 -0
- package/src/heartbeat/heartbeat.ts +2 -2
- package/src/hooks/codex-hook.ts +6 -2
- package/src/hooks/hook.ts +7 -0
- package/src/hooks/pr-body-guard.ts +38 -11
- package/src/hooks/pr-room-guard.ts +88 -0
- package/src/http/active-sessions.ts +13 -2
- package/src/http/agents.ts +11 -3
- package/src/http/all-routes.ts +1 -0
- package/src/http/api-keys.ts +3 -1
- package/src/http/approval-requests.ts +2 -1
- package/src/http/apps.ts +118 -190
- package/src/http/config.ts +3 -0
- package/src/http/context.ts +2 -0
- package/src/http/core.ts +39 -2
- package/src/http/events.ts +6 -2
- package/src/http/favorite-owner.ts +1 -1
- package/src/http/fleet-benchmark.ts +79 -30
- package/src/http/fs.ts +38 -3
- package/src/http/global-config.ts +7 -0
- package/src/http/index.ts +3 -1
- package/src/http/memory.ts +3 -1
- package/src/http/ranking.ts +351 -0
- package/src/http/realtime.ts +26 -21
- package/src/http/route-def.ts +77 -11
- package/src/http/script-runs.ts +2 -1
- package/src/http/scripts.ts +8 -0
- package/src/http/session-access.ts +179 -0
- package/src/http/session-data.ts +10 -2
- package/src/http/sessions.ts +212 -12
- package/src/http/stats.ts +7 -3
- package/src/http/tasks.ts +85 -8
- package/src/http/telemetry-ui.ts +1 -1
- package/src/http/used-by.ts +64 -20
- package/src/http/users.ts +2 -2
- package/src/prompts/session-templates.ts +3 -1
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/pi-mono-adapter.ts +15 -0
- package/src/providers/reasoning-effort.ts +2 -1
- package/src/providers/types.ts +2 -0
- package/src/ranking/telemetry-ranking.ts +119 -0
- package/src/ranking/usage.ts +57 -0
- package/src/rbac/admission.ts +22 -7
- package/src/rbac/legacy-policy.ts +2 -0
- package/src/rbac/permissions.ts +9 -0
- package/src/realtime/auth.ts +69 -0
- package/src/realtime/tickets.ts +4 -1
- package/src/realtime/transport.ts +77 -9
- package/src/scripts-runtime/sdk-allowlist.ts +7 -0
- package/src/scripts-runtime/types/stdlib.d.ts +31 -0
- package/src/scripts-runtime/types/swarm-sdk.d.ts +31 -0
- package/src/server.ts +4 -0
- package/src/slack/blocks.ts +18 -0
- package/src/slack/dm.ts +29 -0
- package/src/telemetry-context.ts +19 -1
- package/src/telemetry-contract/EVENTS.md +45 -8
- package/src/telemetry-contract/SOURCE.json +38 -4
- package/src/telemetry-contract/catalog.json +294 -5
- package/src/telemetry-contract/ranking/fixtures/expected.json +30 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/bad-category.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/bad-claim.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/blank-description.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/blank-name.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/description-too-long.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/domain-uppercase.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/domain-with-scheme.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/domain-with-www.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/http-source-url.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/missing-description.json +10 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/missing-version.json +10 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/name-not-string.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/name-too-long.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/not-object.json +1 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/null.json +1 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/other-with-subcategory.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/oversize.json +1 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/short-claim.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/subcategory-other-category.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/trailing-data.json +1 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/unknown-field.json +12 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/unsupported-version.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/updated-at-not-date.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/updated-at-offset.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/invalid/wrong-case-field.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/valid/category-without-subcategory.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/valid/full.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/valid/minimal.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/valid/no-claim.json +10 -0
- package/src/telemetry-contract/ranking/fixtures/valid/padded-text.json +11 -0
- package/src/telemetry-contract/ranking/fixtures/valid/unicode-name.json +11 -0
- package/src/telemetry-contract/ranking/listing.v1.schema.json +61 -0
- package/src/telemetry-contract/ranking/taxonomy.json +8 -0
- package/src/telemetry-contract/ranking-taxonomy.gen.ts +11 -0
- package/src/telemetry-contract/types.gen.ts +963 -58
- package/src/telemetry-identity.ts +47 -9
- package/src/telemetry-snapshot.ts +9 -1
- package/src/telemetry-ui-events.ts +9 -2
- package/src/telemetry.ts +40 -5
- package/src/tests/app-action-run-tool.test.ts +178 -0
- package/src/tests/app-row-tools.test.ts +360 -0
- package/src/tests/apps-rbac.test.ts +189 -2
- package/src/tests/apps-script-types.test.ts +16 -3
- package/src/tests/credential-check.test.ts +38 -0
- package/src/tests/db-extraction-contract.test.ts +1 -0
- package/src/tests/db-task-read-contract.test.ts +1 -0
- package/src/tests/docs-index-footprint.test.js +2 -2
- package/src/tests/fleet-benchmark.test.ts +122 -2
- package/src/tests/http-users.test.ts +62 -1
- package/src/tests/model-catalog-table.test.ts +63 -0
- package/src/tests/model-profiles-api.test.ts +19 -7
- package/src/tests/models-catalog.test.ts +50 -0
- package/src/tests/onboarding.test.ts +21 -0
- package/src/tests/org-identity.test.ts +44 -0
- package/src/tests/org-website.test.ts +62 -0
- package/src/tests/pi-mono-adapter.test.ts +23 -0
- package/src/tests/pr-body-leaks.test.ts +4 -0
- package/src/tests/pr-room-guard.test.ts +242 -0
- package/src/tests/ranking-listing.test.ts +97 -0
- package/src/tests/ranking-route.test.ts +321 -0
- package/src/tests/ranking-usage.test.ts +50 -0
- package/src/tests/rbac-admission-e2e.test.ts +8 -0
- package/src/tests/rbac-admission.test.ts +38 -0
- package/src/tests/rbac-charact-http.test.ts +4 -3
- package/src/tests/rbac-engine.test.ts +2 -0
- package/src/tests/rbac-roles.test.ts +2 -0
- package/src/tests/realtime-room-surfaces.test.ts +3 -1
- package/src/tests/realtime-session-join-race.test.ts +142 -0
- package/src/tests/realtime-tickets.test.ts +24 -17
- package/src/tests/realtime-transport.test.ts +91 -0
- package/src/tests/reasoning-effort.test.ts +39 -3
- package/src/tests/runner-harness-telemetry-env.test.ts +56 -1
- package/src/tests/session-route-posture.test.ts +639 -0
- package/src/tests/session-sharing-routes.test.ts +282 -0
- package/src/tests/session-sharing.test.ts +526 -0
- package/src/tests/sessions-detail-batch.test.ts +7 -1
- package/src/tests/slack-tool-blocks.test.ts +101 -0
- package/src/tests/slack-update.test.ts +55 -0
- package/src/tests/slack-user-dm.test.ts +177 -0
- package/src/tests/swarm-tool-result-gate.test.ts +2 -4
- package/src/tests/telemetry-context.test.ts +128 -2
- package/src/tests/telemetry-ui-route.test.ts +123 -0
- package/src/tests/tool-annotations.test.ts +3 -2
- package/src/tests/used-by-route.test.ts +153 -9
- package/src/tests/workflow-agent-task-retry.test.ts +130 -4
- package/src/tests/workflow-wait-recovery.test.ts +116 -4
- package/src/tools/app-action-run.ts +63 -0
- package/src/tools/app-rows.ts +234 -0
- package/src/tools/model-catalog/index.ts +1 -1
- package/src/tools/slack-post.ts +18 -12
- package/src/tools/slack-read.ts +13 -1
- package/src/tools/slack-reply.ts +7 -10
- package/src/tools/slack-start-thread.ts +18 -11
- package/src/tools/slack-update.ts +2 -9
- package/src/tools/tool-config.ts +8 -0
- package/src/tools/utils.ts +1 -1
- package/src/tools/workflows/retry-workflow-run.ts +1 -1
- package/src/types.ts +52 -0
- package/src/utils/company-profile.ts +24 -0
- package/src/utils/org-identity.ts +33 -0
- package/src/utils/org-website.ts +58 -21
- package/src/workflows/recovery.ts +10 -22
- package/src/workflows/resume.ts +16 -0
- package/src/workflows/task-step-routing.ts +47 -7
- package/templates/skills/apps/SKILL.md +58 -2
- package/templates/skills/apps/content.md +58 -2
- package/dist/handlers-tb3xgjnm.js +0 -37
- package/dist/index-nyfcdpky.js +0 -39
- package/dist/index-vjn3wbzf.js +0 -38
- package/dist/lead-61kqp604.js +0 -76
- package/dist/worker-4ptnn1ge.js +0 -76
- package/dist/{cli-n5mrsg0f.js → cli-60cgh1rv.js} +3 -3
|
@@ -69,28 +69,6 @@ import {
|
|
|
69
69
|
resolveKvView,
|
|
70
70
|
toolErr,
|
|
71
71
|
createToolRegistrar,
|
|
72
|
-
duplicateGuardedQueryKey,
|
|
73
|
-
warnIfCorsAllowsAnyOrigin,
|
|
74
|
-
isOriginAllowedForCredentials,
|
|
75
|
-
allowedCredentialRedirect,
|
|
76
|
-
setCorsHeaders,
|
|
77
|
-
parseQueryParams,
|
|
78
|
-
getPathSegments,
|
|
79
|
-
wireHttpSpanLifecycle,
|
|
80
|
-
safeRequestUrlForLog,
|
|
81
|
-
agentWithCapacity,
|
|
82
|
-
parseBody,
|
|
83
|
-
BODY_TOO_LARGE,
|
|
84
|
-
enforceContentLengthCap,
|
|
85
|
-
json,
|
|
86
|
-
jsonError,
|
|
87
|
-
writeUnhandledError,
|
|
88
|
-
triggerSchemaErrorResponse,
|
|
89
|
-
deriveApiBaseUrl,
|
|
90
|
-
httpServerSemconvAttributes,
|
|
91
|
-
runtimeInstanceHeader,
|
|
92
|
-
describeRequestRoute,
|
|
93
|
-
route,
|
|
94
72
|
handleMcpBridge,
|
|
95
73
|
Redacted,
|
|
96
74
|
SCRIPT_SDK_RESPONSE_LIMIT_BYTES,
|
|
@@ -199,9 +177,6 @@ import {
|
|
|
199
177
|
scheduleIntegrationsReload,
|
|
200
178
|
handleCore,
|
|
201
179
|
userCtx,
|
|
202
|
-
resolveTaskAuditUserId,
|
|
203
|
-
resolveHttpAuditUserId,
|
|
204
|
-
resolveHttpFavoriteOwner,
|
|
205
180
|
handleApps,
|
|
206
181
|
cancelApprovalRequest,
|
|
207
182
|
getUserCommsPrefs,
|
|
@@ -262,6 +237,8 @@ import {
|
|
|
262
237
|
resolveRoomNamespace,
|
|
263
238
|
roomRequestInfo,
|
|
264
239
|
authorizeRoomNamespace,
|
|
240
|
+
sessionAccessTopic,
|
|
241
|
+
sessionPresenceRoot,
|
|
265
242
|
RoomOperationSchema,
|
|
266
243
|
removeAwarenessStates,
|
|
267
244
|
roomTopic,
|
|
@@ -303,7 +280,7 @@ import {
|
|
|
303
280
|
hasCapability,
|
|
304
281
|
getEnabledCapabilities,
|
|
305
282
|
createServer
|
|
306
|
-
} from "./cli-
|
|
283
|
+
} from "./cli-kr2pmfqx.js";
|
|
307
284
|
import {
|
|
308
285
|
chatThreadOfContextKey,
|
|
309
286
|
agentContextKey,
|
|
@@ -311,6 +288,10 @@ import {
|
|
|
311
288
|
init_context_key,
|
|
312
289
|
init_catalog,
|
|
313
290
|
catalog_default,
|
|
291
|
+
COMPANY_TEAM_SIZES,
|
|
292
|
+
COMPANY_USE_CASES,
|
|
293
|
+
USER_FUNCTIONS,
|
|
294
|
+
init_company_profile,
|
|
314
295
|
TELEMETRY_PROXY_ORIGIN,
|
|
315
296
|
normalizeOrgWebsite,
|
|
316
297
|
orgLogoUrl,
|
|
@@ -325,6 +306,7 @@ import {
|
|
|
325
306
|
_resolveIntegrationType,
|
|
326
307
|
_resolveIntegrationProvider,
|
|
327
308
|
setTelemetryRoleResolver,
|
|
309
|
+
setTelemetryFunctionResolver,
|
|
328
310
|
isTelemetryReady,
|
|
329
311
|
trackRelayed,
|
|
330
312
|
telemetry,
|
|
@@ -349,6 +331,7 @@ import {
|
|
|
349
331
|
getDbClient2,
|
|
350
332
|
init_runtime,
|
|
351
333
|
cachedUserRole,
|
|
334
|
+
cachedUserFunction,
|
|
352
335
|
configureOrgDomainPersistence,
|
|
353
336
|
recomputeOrgDomain,
|
|
354
337
|
init_telemetry_identity,
|
|
@@ -440,6 +423,13 @@ import {
|
|
|
440
423
|
CHAT_SDK_ADAPTER_NAMES,
|
|
441
424
|
isAdapterEntryEnabled,
|
|
442
425
|
init_schema,
|
|
426
|
+
TELEMETRY_RANKING_KEY,
|
|
427
|
+
PUBLIC_RANKING_ENABLED_KEY,
|
|
428
|
+
RANKING_CATEGORIES,
|
|
429
|
+
readTelemetryRanking,
|
|
430
|
+
rankingFormSchema,
|
|
431
|
+
buildListing,
|
|
432
|
+
init_telemetry_ranking,
|
|
443
433
|
API_DRAINING_HEADER,
|
|
444
434
|
init_api_drain,
|
|
445
435
|
parseTaskToolManifest,
|
|
@@ -724,26 +714,26 @@ import {
|
|
|
724
714
|
countScriptRunJournalSteps2,
|
|
725
715
|
countScriptRunJournalAgentTaskSteps2,
|
|
726
716
|
init_db3
|
|
727
|
-
} from "./cli-
|
|
717
|
+
} from "./cli-rqqsfz30.js";
|
|
728
718
|
import {
|
|
729
719
|
getUserGrant2,
|
|
730
720
|
init_rbac_roles
|
|
731
|
-
} from "./cli-
|
|
732
|
-
import"./cli-
|
|
721
|
+
} from "./cli-nx462e4v.js";
|
|
722
|
+
import"./cli-s48yvzb8.js";
|
|
733
723
|
import {
|
|
734
724
|
normalizeModelKey
|
|
735
|
-
} from "./cli-
|
|
725
|
+
} from "./cli-3p8fqcnh.js";
|
|
736
726
|
import {
|
|
737
727
|
upsertModelCatalogOverlay,
|
|
738
728
|
deleteModelCatalogOverlay,
|
|
739
729
|
loadModelsCatalog,
|
|
740
730
|
requestModelCatalogRefresh2
|
|
741
|
-
} from "./cli-
|
|
731
|
+
} from "./cli-k9z4zd4p.js";
|
|
742
732
|
import {
|
|
743
733
|
AMP_ESTIMATE_MODELS,
|
|
744
734
|
ampBillsCacheWrites,
|
|
745
735
|
resolveAmpModel
|
|
746
|
-
} from "./cli-
|
|
736
|
+
} from "./cli-sv5gb0yb.js";
|
|
747
737
|
import {
|
|
748
738
|
automationIntegrationFixUrl,
|
|
749
739
|
preflightAutomation,
|
|
@@ -755,7 +745,7 @@ import {
|
|
|
755
745
|
cancelWorkflowRun2,
|
|
756
746
|
TriggerSchemaError,
|
|
757
747
|
startWorkflowExecution2
|
|
758
|
-
} from "./cli-
|
|
748
|
+
} from "./cli-kmqgmcnb.js";
|
|
759
749
|
import {
|
|
760
750
|
require_dist2,
|
|
761
751
|
isInvalidClientError,
|
|
@@ -835,14 +825,14 @@ import {
|
|
|
835
825
|
typecheckWithAmbient,
|
|
836
826
|
typecheckScript,
|
|
837
827
|
parseSkillContent
|
|
838
|
-
} from "./cli-
|
|
828
|
+
} from "./cli-0bh477zn.js";
|
|
839
829
|
import {
|
|
840
830
|
buildAuthorizationUrl,
|
|
841
831
|
exchangeAuthorizationCode,
|
|
842
832
|
oauthAppRowToProviderConfig,
|
|
843
833
|
forceRefreshAuthorizationOrThrow,
|
|
844
834
|
forceRefreshTokenOrThrow
|
|
845
|
-
} from "./cli-
|
|
835
|
+
} from "./cli-sjyetj21.js";
|
|
846
836
|
import {
|
|
847
837
|
signalProcessGroup,
|
|
848
838
|
detachedProcessGroup,
|
|
@@ -870,15 +860,15 @@ import {
|
|
|
870
860
|
getSlackApp2,
|
|
871
861
|
startSlackApp2,
|
|
872
862
|
stopSlackApp2
|
|
873
|
-
} from "./cli-
|
|
874
|
-
import"./cli-
|
|
863
|
+
} from "./cli-vy0fay6k.js";
|
|
864
|
+
import"./cli-mfe7f7b5.js";
|
|
875
865
|
import {
|
|
876
866
|
createApprovalFollowUpTask,
|
|
877
867
|
generateEdges,
|
|
878
868
|
definitionNodeIds,
|
|
879
869
|
validateDefinition
|
|
880
|
-
} from "./cli-
|
|
881
|
-
import"./cli-
|
|
870
|
+
} from "./cli-tbhwrxpt.js";
|
|
871
|
+
import"./cli-8nyhf7za.js";
|
|
882
872
|
import {
|
|
883
873
|
_fetchHolder,
|
|
884
874
|
CLIENT_ID,
|
|
@@ -889,11 +879,53 @@ import {
|
|
|
889
879
|
codexOAuthKeyForSlot,
|
|
890
880
|
loadAllCodexOAuthSlots,
|
|
891
881
|
getValidCodexOAuth
|
|
892
|
-
} from "./cli-
|
|
882
|
+
} from "./cli-ejs3m1sx.js";
|
|
893
883
|
import {
|
|
894
884
|
workflowEventBus2,
|
|
895
885
|
init_event_bus
|
|
896
|
-
} from "./cli-
|
|
886
|
+
} from "./cli-wt645v7q.js";
|
|
887
|
+
import {
|
|
888
|
+
duplicateGuardedQueryKey,
|
|
889
|
+
warnIfCorsAllowsAnyOrigin,
|
|
890
|
+
isOriginAllowedForCredentials,
|
|
891
|
+
allowedCredentialRedirect,
|
|
892
|
+
setCorsHeaders,
|
|
893
|
+
parseQueryParams,
|
|
894
|
+
getPathSegments,
|
|
895
|
+
wireHttpSpanLifecycle,
|
|
896
|
+
safeRequestUrlForLog,
|
|
897
|
+
agentWithCapacity,
|
|
898
|
+
parseBody,
|
|
899
|
+
BODY_TOO_LARGE,
|
|
900
|
+
enforceContentLengthCap,
|
|
901
|
+
json,
|
|
902
|
+
jsonError,
|
|
903
|
+
writeUnhandledError,
|
|
904
|
+
triggerSchemaErrorResponse,
|
|
905
|
+
deriveApiBaseUrl,
|
|
906
|
+
httpServerSemconvAttributes,
|
|
907
|
+
hasSessionAccess,
|
|
908
|
+
getSessionRoot,
|
|
909
|
+
sessionAccessForRoot,
|
|
910
|
+
listActiveSessionShares,
|
|
911
|
+
toSessionSharesState,
|
|
912
|
+
setSessionShares,
|
|
913
|
+
resolveTaskAuditUserId,
|
|
914
|
+
resolveHttpAuditUserId,
|
|
915
|
+
dashboardUserIdHeader,
|
|
916
|
+
resolveHttpFavoriteOwner,
|
|
917
|
+
resolveSessionActor2,
|
|
918
|
+
sessionAccessFor2,
|
|
919
|
+
SESSION_READ_DENIED2,
|
|
920
|
+
canReadSession2,
|
|
921
|
+
readableByFor2,
|
|
922
|
+
SESSION_WRITE_DENIED2,
|
|
923
|
+
runtimeInstanceHeader,
|
|
924
|
+
bodyTask,
|
|
925
|
+
bodyListTask,
|
|
926
|
+
describeRequestRoute,
|
|
927
|
+
route
|
|
928
|
+
} from "./cli-z071fb95.js";
|
|
897
929
|
import {
|
|
898
930
|
isMultiRuntimeEnabled
|
|
899
931
|
} from "./cli-7a91w22y.js";
|
|
@@ -906,24 +938,6 @@ import {
|
|
|
906
938
|
} from "./cli-qje9k2nh.js";
|
|
907
939
|
import {
|
|
908
940
|
RuntimePoolError2,
|
|
909
|
-
registerSessionTokenConnection,
|
|
910
|
-
init_session_token_connections,
|
|
911
|
-
findUserById,
|
|
912
|
-
getUserIdentities,
|
|
913
|
-
listUserEvents,
|
|
914
|
-
listUserTokens,
|
|
915
|
-
recordIdentityEvent,
|
|
916
|
-
linkIdentity,
|
|
917
|
-
unlinkIdentity,
|
|
918
|
-
mintToken,
|
|
919
|
-
revokeToken,
|
|
920
|
-
resolveUserByToken,
|
|
921
|
-
InvalidRuntimeTokenError,
|
|
922
|
-
mintSessionToken,
|
|
923
|
-
mintRuntimeToken,
|
|
924
|
-
revokeSessionToken,
|
|
925
|
-
fingerprintApiKey,
|
|
926
|
-
init_users,
|
|
927
941
|
mintControllerToken,
|
|
928
942
|
init_tokens,
|
|
929
943
|
listRuntimeControllers2,
|
|
@@ -942,13 +956,13 @@ import {
|
|
|
942
956
|
resolvePoolDelivery2,
|
|
943
957
|
createPoolAgent2,
|
|
944
958
|
init_db
|
|
945
|
-
} from "./cli-
|
|
959
|
+
} from "./cli-bn5kevnf.js";
|
|
946
960
|
import {
|
|
947
961
|
RATING_MODEL_MAX_LENGTH,
|
|
948
962
|
REFERENCES_SOURCE_MAX_LENGTH,
|
|
949
963
|
sanitizeReferencesSource
|
|
950
964
|
} from "./cli-3h26v7yj.js";
|
|
951
|
-
import"./cli-
|
|
965
|
+
import"./cli-smvcw9z5.js";
|
|
952
966
|
import {
|
|
953
967
|
startPoolRouting,
|
|
954
968
|
stopPoolRouting,
|
|
@@ -958,8 +972,8 @@ import {
|
|
|
958
972
|
checkHeartbeatChecklist2,
|
|
959
973
|
runHeartbeatSweep2,
|
|
960
974
|
stopHeartbeat2
|
|
961
|
-
} from "./cli-
|
|
962
|
-
import"./cli-
|
|
975
|
+
} from "./cli-rdq7tzwm.js";
|
|
976
|
+
import"./cli-hqhzg1qq.js";
|
|
963
977
|
import {
|
|
964
978
|
import_websocket,
|
|
965
979
|
import_websocket_server
|
|
@@ -968,32 +982,34 @@ import {
|
|
|
968
982
|
runtimeSupervisorStatus,
|
|
969
983
|
startRuntimeSupervisor,
|
|
970
984
|
stopRuntimeSupervisor
|
|
971
|
-
} from "./cli-
|
|
985
|
+
} from "./cli-axh29tyr.js";
|
|
972
986
|
import"./cli-ns59dc6r.js";
|
|
973
987
|
import {
|
|
974
988
|
ACP_TARGET_IDS
|
|
975
|
-
} from "./cli-
|
|
989
|
+
} from "./cli-5kqnemen.js";
|
|
976
990
|
import {
|
|
977
991
|
reasoningCapability,
|
|
978
992
|
supportedReasoningLevels
|
|
979
|
-
} from "./cli-
|
|
993
|
+
} from "./cli-bn0rewsy.js";
|
|
980
994
|
import {
|
|
981
995
|
MAX_RATE_LIMIT_RESET_MS
|
|
982
996
|
} from "./cli-gx4rm0qx.js";
|
|
983
|
-
import {
|
|
984
|
-
inviteEmailToSharedOrg,
|
|
985
|
-
ensureAgentFsCredentialsForAgent
|
|
986
|
-
} from "./cli-yv3n83x8.js";
|
|
987
|
-
import {
|
|
988
|
-
Anthropic
|
|
989
|
-
} from "./cli-er83cjbn.js";
|
|
990
997
|
import {
|
|
991
998
|
isLeadChannelMonitoringEnabled,
|
|
999
|
+
ORG_IDENTITY_KEYS,
|
|
1000
|
+
orgIdentityStatus,
|
|
992
1001
|
SIMILARITY_THRESHOLD,
|
|
993
1002
|
memoryRelevance,
|
|
994
1003
|
isUnknownModelError,
|
|
995
1004
|
writeSkillsToFilesystem
|
|
996
|
-
} from "./cli-
|
|
1005
|
+
} from "./cli-m2qgh5sv.js";
|
|
1006
|
+
import {
|
|
1007
|
+
inviteEmailToSharedOrg,
|
|
1008
|
+
ensureAgentFsCredentialsForAgent
|
|
1009
|
+
} from "./cli-syhq378v.js";
|
|
1010
|
+
import {
|
|
1011
|
+
Anthropic
|
|
1012
|
+
} from "./cli-er83cjbn.js";
|
|
997
1013
|
import {
|
|
998
1014
|
isCodexOAuthConfigKey
|
|
999
1015
|
} from "./cli-j4bwnpkm.js";
|
|
@@ -1005,7 +1021,27 @@ import"./cli-13p25grh.js";
|
|
|
1005
1021
|
import"./cli-0adyhvq2.js";
|
|
1006
1022
|
import {
|
|
1007
1023
|
require_content_type
|
|
1008
|
-
} from "./cli-
|
|
1024
|
+
} from "./cli-1jx0srjt.js";
|
|
1025
|
+
import {
|
|
1026
|
+
registerSessionTokenConnection,
|
|
1027
|
+
init_session_token_connections,
|
|
1028
|
+
findUserById,
|
|
1029
|
+
getUserIdentities,
|
|
1030
|
+
listUserEvents,
|
|
1031
|
+
listUserTokens,
|
|
1032
|
+
recordIdentityEvent,
|
|
1033
|
+
linkIdentity,
|
|
1034
|
+
unlinkIdentity,
|
|
1035
|
+
mintToken,
|
|
1036
|
+
revokeToken,
|
|
1037
|
+
resolveUserByToken,
|
|
1038
|
+
InvalidRuntimeTokenError,
|
|
1039
|
+
mintSessionToken,
|
|
1040
|
+
mintRuntimeToken,
|
|
1041
|
+
revokeSessionToken,
|
|
1042
|
+
fingerprintApiKey,
|
|
1043
|
+
init_users
|
|
1044
|
+
} from "./cli-fz9kewbh.js";
|
|
1009
1045
|
import"./cli-v41kn3cd.js";
|
|
1010
1046
|
import"./cli-wgtk7xxr.js";
|
|
1011
1047
|
import {
|
|
@@ -1019,7 +1055,7 @@ import {
|
|
|
1019
1055
|
clearAuditSink,
|
|
1020
1056
|
can,
|
|
1021
1057
|
init_rbac
|
|
1022
|
-
} from "./cli-
|
|
1058
|
+
} from "./cli-1pa3rz69.js";
|
|
1023
1059
|
import {
|
|
1024
1060
|
isSafeBundlePath,
|
|
1025
1061
|
init_bundle_path,
|
|
@@ -1039,6 +1075,10 @@ import {
|
|
|
1039
1075
|
ProviderNameSchema,
|
|
1040
1076
|
ModelProfileNameSchema,
|
|
1041
1077
|
ModelProfileSchema,
|
|
1078
|
+
SessionAccessSchema,
|
|
1079
|
+
SessionOwnerSchema,
|
|
1080
|
+
SessionSharesSchema,
|
|
1081
|
+
SessionSharesUpdateSchema,
|
|
1042
1082
|
SteerModeSchema,
|
|
1043
1083
|
SteeringSourceSchema,
|
|
1044
1084
|
SteeringMessageSchema,
|
|
@@ -1093,6 +1133,7 @@ import {
|
|
|
1093
1133
|
RuntimePoolSchema,
|
|
1094
1134
|
RuntimePoolCreateAgentSchema,
|
|
1095
1135
|
RuntimeInstanceSchema,
|
|
1136
|
+
SUPERSEDED_WORKFLOW_TASK_REASON,
|
|
1096
1137
|
WorkflowEdgeSchema,
|
|
1097
1138
|
WorkflowDefinitionSchema,
|
|
1098
1139
|
WorkflowNodePatchSchema,
|
|
@@ -1156,12 +1197,12 @@ import {
|
|
|
1156
1197
|
KvKeySchema,
|
|
1157
1198
|
KvEntrySchema,
|
|
1158
1199
|
init_types
|
|
1159
|
-
} from "./cli-
|
|
1200
|
+
} from "./cli-fpmsqq59.js";
|
|
1160
1201
|
import {
|
|
1161
1202
|
IDENTITY_FIELD_BUDGETS,
|
|
1162
1203
|
IdentityFieldBudgetError,
|
|
1163
1204
|
init_identity_field_budget
|
|
1164
|
-
} from "./cli-
|
|
1205
|
+
} from "./cli-x5mq1fyq.js";
|
|
1165
1206
|
import {
|
|
1166
1207
|
getSlackConfiguration,
|
|
1167
1208
|
init_config
|
|
@@ -1179,7 +1220,7 @@ import {
|
|
|
1179
1220
|
resolveTemplate,
|
|
1180
1221
|
init_resolver,
|
|
1181
1222
|
init_session_templates
|
|
1182
|
-
} from "./cli-
|
|
1223
|
+
} from "./cli-tx00c0h6.js";
|
|
1183
1224
|
import {
|
|
1184
1225
|
MODEL_SCOPED_WINDOWS,
|
|
1185
1226
|
activeModelBlocks,
|
|
@@ -1201,6 +1242,7 @@ import {
|
|
|
1201
1242
|
import {
|
|
1202
1243
|
NEVER,
|
|
1203
1244
|
toJSONSchema,
|
|
1245
|
+
ZodError,
|
|
1204
1246
|
string2,
|
|
1205
1247
|
email,
|
|
1206
1248
|
uuid,
|
|
@@ -1228,7 +1270,7 @@ import {
|
|
|
1228
1270
|
import {
|
|
1229
1271
|
init_package,
|
|
1230
1272
|
package_default
|
|
1231
|
-
} from "./cli-
|
|
1273
|
+
} from "./cli-ash6xxk7.js";
|
|
1232
1274
|
import {
|
|
1233
1275
|
realtimeBus,
|
|
1234
1276
|
init_bus
|
|
@@ -1257,11 +1299,11 @@ import {
|
|
|
1257
1299
|
scrubObject,
|
|
1258
1300
|
registerVolatileSecret,
|
|
1259
1301
|
init_secret_scrubber
|
|
1260
|
-
} from "./cli-
|
|
1302
|
+
} from "./cli-xf234ws8.js";
|
|
1261
1303
|
import {
|
|
1262
1304
|
isAlias,
|
|
1263
1305
|
init_src2
|
|
1264
|
-
} from "./cli-
|
|
1306
|
+
} from "./cli-nwyw3zhg.js";
|
|
1265
1307
|
import {
|
|
1266
1308
|
__toESM
|
|
1267
1309
|
} from "./cli-242037k3.js";
|
|
@@ -2367,7 +2409,8 @@ function identityFromHttpAuth(req, auth) {
|
|
|
2367
2409
|
agentId,
|
|
2368
2410
|
sourceTaskId: auth.kind === "agent" ? auth.taskId ?? undefined : header(req, "x-source-task-id"),
|
|
2369
2411
|
userId: auth.kind === "user" ? auth.userId : undefined,
|
|
2370
|
-
isOperator: auth.kind === "operator"
|
|
2412
|
+
isOperator: auth.kind === "operator",
|
|
2413
|
+
dashboardUserId: auth.kind === "operator" ? auth.dashboardUserId : undefined
|
|
2371
2414
|
},
|
|
2372
2415
|
me: {
|
|
2373
2416
|
userId: auth.kind === "user" ? auth.userId : agentId ?? guestId,
|
|
@@ -2560,6 +2603,9 @@ function connect(ws, auth, runtimeId) {
|
|
|
2560
2603
|
if (namespace.startsWith("presence:comb:") && msg.name !== "default") {
|
|
2561
2604
|
throw new Error("Comb presence uses the default room");
|
|
2562
2605
|
}
|
|
2606
|
+
if (namespace.startsWith("presence:session:") && msg.name !== "default") {
|
|
2607
|
+
throw new Error("Session presence uses the default room");
|
|
2608
|
+
}
|
|
2563
2609
|
const denied = await authorizeRoomNamespace(namespace, auth.identity, msg.op, msg.name);
|
|
2564
2610
|
if (denied)
|
|
2565
2611
|
throw new Error(typeof denied === "string" ? denied : "Forbidden");
|
|
@@ -2600,7 +2646,7 @@ function connect(ws, auth, runtimeId) {
|
|
|
2600
2646
|
} catch (error) {
|
|
2601
2647
|
if (!(error instanceof Error) || error.message !== "room does not exist")
|
|
2602
2648
|
throw error;
|
|
2603
|
-
const creationDenied = await authorizeRoomNamespace(namespace, auth.identity, namespace.startsWith("presence:comb:") ? "join" : true);
|
|
2649
|
+
const creationDenied = await authorizeRoomNamespace(namespace, auth.identity, namespace.startsWith("presence:comb:") || namespace.startsWith("presence:session:") ? "join" : true);
|
|
2604
2650
|
if (creationDenied)
|
|
2605
2651
|
throw new Error(creationDenied);
|
|
2606
2652
|
if (closed)
|
|
@@ -2610,6 +2656,8 @@ function connect(ws, auth, runtimeId) {
|
|
|
2610
2656
|
if (closed)
|
|
2611
2657
|
return;
|
|
2612
2658
|
const stopRoom = realtimeBus.subscribe(key, (event) => {
|
|
2659
|
+
if (rooms.get(key)?.suspended)
|
|
2660
|
+
return;
|
|
2613
2661
|
const view = event;
|
|
2614
2662
|
const type = view.type;
|
|
2615
2663
|
if (type === "deleted") {
|
|
@@ -2631,15 +2679,62 @@ function connect(ws, auth, runtimeId) {
|
|
|
2631
2679
|
}).catch(() => ws.close(1011, "Room reset failed"));
|
|
2632
2680
|
}
|
|
2633
2681
|
});
|
|
2634
|
-
const stopPresence = realtimeBus.subscribe(`presence:${key}`, (peers) =>
|
|
2682
|
+
const stopPresence = realtimeBus.subscribe(`presence:${key}`, (peers) => {
|
|
2683
|
+
if (rooms.get(key)?.suspended)
|
|
2684
|
+
return;
|
|
2685
|
+
send({ type: "presence", name: msg.name, namespace, peers });
|
|
2686
|
+
});
|
|
2687
|
+
const sessionRoot = sessionPresenceRoot(namespace);
|
|
2688
|
+
let accessCheck = 0;
|
|
2689
|
+
const stopAccess = sessionRoot ? realtimeBus.subscribe(sessionAccessTopic(sessionRoot), () => {
|
|
2690
|
+
const joined = rooms.get(key);
|
|
2691
|
+
if (!joined)
|
|
2692
|
+
return;
|
|
2693
|
+
joined.suspended = true;
|
|
2694
|
+
const check = ++accessCheck;
|
|
2695
|
+
authorizeRoomNamespace(namespace, auth.identity, "join").catch(() => "Session access check failed").then((denied) => {
|
|
2696
|
+
const current = rooms.get(key);
|
|
2697
|
+
if (closed || !current || check !== accessCheck)
|
|
2698
|
+
return;
|
|
2699
|
+
if (denied) {
|
|
2700
|
+
leave(key);
|
|
2701
|
+
send({ type: "error", name: msg.name, namespace, error: denied });
|
|
2702
|
+
return;
|
|
2703
|
+
}
|
|
2704
|
+
current.suspended = false;
|
|
2705
|
+
send({
|
|
2706
|
+
type: "presence",
|
|
2707
|
+
name: msg.name,
|
|
2708
|
+
namespace,
|
|
2709
|
+
peers: [...current.room.awareness.getStates().values()]
|
|
2710
|
+
});
|
|
2711
|
+
});
|
|
2712
|
+
}) : undefined;
|
|
2635
2713
|
rooms.set(key, {
|
|
2636
2714
|
room,
|
|
2637
2715
|
version: msg.schemaVersion,
|
|
2638
2716
|
stop: () => {
|
|
2639
2717
|
stopRoom();
|
|
2640
2718
|
stopPresence();
|
|
2641
|
-
|
|
2719
|
+
stopAccess?.();
|
|
2720
|
+
},
|
|
2721
|
+
suspended: sessionRoot !== undefined
|
|
2642
2722
|
});
|
|
2723
|
+
if (sessionRoot) {
|
|
2724
|
+
const check = ++accessCheck;
|
|
2725
|
+
const denied = await authorizeRoomNamespace(namespace, auth.identity, "join").catch(() => "Session access check failed");
|
|
2726
|
+
if (closed)
|
|
2727
|
+
return;
|
|
2728
|
+
if (denied) {
|
|
2729
|
+
leave(key);
|
|
2730
|
+
throw new Error(denied);
|
|
2731
|
+
}
|
|
2732
|
+
const current = rooms.get(key);
|
|
2733
|
+
if (!current)
|
|
2734
|
+
throw new Error("You don't have access to this session");
|
|
2735
|
+
if (check === accessCheck)
|
|
2736
|
+
current.suspended = false;
|
|
2737
|
+
}
|
|
2643
2738
|
room.awareness.states.set(clientId, { ...auth.me, data: {} });
|
|
2644
2739
|
room.awareness.meta.set(clientId, { clock: 1, lastUpdated: Date.now() });
|
|
2645
2740
|
publishPresence(room);
|
|
@@ -3799,6 +3894,7 @@ function startTelemetryTicker(deps) {
|
|
|
3799
3894
|
return;
|
|
3800
3895
|
stopTelemetryTicker();
|
|
3801
3896
|
setTelemetryRoleResolver(cachedUserRole);
|
|
3897
|
+
setTelemetryFunctionResolver(cachedUserFunction);
|
|
3802
3898
|
const tick = async () => {
|
|
3803
3899
|
try {
|
|
3804
3900
|
await recomputeOrgDomain();
|
|
@@ -3829,6 +3925,7 @@ function stopTelemetryTicker() {
|
|
|
3829
3925
|
tickTimer = null;
|
|
3830
3926
|
firstTimer = null;
|
|
3831
3927
|
setTelemetryRoleResolver(null);
|
|
3928
|
+
setTelemetryFunctionResolver(null);
|
|
3832
3929
|
configureOrgDomainPersistence(null);
|
|
3833
3930
|
}
|
|
3834
3931
|
|
|
@@ -3863,6 +3960,7 @@ var createActiveSession = route({
|
|
|
3863
3960
|
method: "post",
|
|
3864
3961
|
path: "/api/active-sessions",
|
|
3865
3962
|
pattern: ["api", "active-sessions"],
|
|
3963
|
+
session: bodyTask("taskId"),
|
|
3866
3964
|
summary: "Create a new active session",
|
|
3867
3965
|
tags: ["Active Sessions"],
|
|
3868
3966
|
body: object2({
|
|
@@ -3883,6 +3981,7 @@ var deleteSessionByTask = route({
|
|
|
3883
3981
|
method: "delete",
|
|
3884
3982
|
path: "/api/active-sessions/by-task/{taskId}",
|
|
3885
3983
|
pattern: ["api", "active-sessions", "by-task", null],
|
|
3984
|
+
session: { taskParam: "taskId" },
|
|
3886
3985
|
summary: "Delete active session by task ID",
|
|
3887
3986
|
tags: ["Active Sessions"],
|
|
3888
3987
|
params: object2({ taskId: string2() }),
|
|
@@ -3894,6 +3993,7 @@ var deleteSessionById = route({
|
|
|
3894
3993
|
method: "delete",
|
|
3895
3994
|
path: "/api/active-sessions/{id}",
|
|
3896
3995
|
pattern: ["api", "active-sessions", null],
|
|
3996
|
+
session: { activeSessionParam: "id" },
|
|
3897
3997
|
summary: "Delete active session by ID",
|
|
3898
3998
|
tags: ["Active Sessions"],
|
|
3899
3999
|
params: object2({ id: string2() }),
|
|
@@ -3905,6 +4005,7 @@ var heartbeatSession = route({
|
|
|
3905
4005
|
method: "put",
|
|
3906
4006
|
path: "/api/active-sessions/heartbeat/{taskId}",
|
|
3907
4007
|
pattern: ["api", "active-sessions", "heartbeat", null],
|
|
4008
|
+
session: { taskParam: "taskId" },
|
|
3908
4009
|
summary: "Update heartbeat for an active session",
|
|
3909
4010
|
tags: ["Active Sessions"],
|
|
3910
4011
|
params: object2({ taskId: string2() }),
|
|
@@ -3916,6 +4017,7 @@ var updateProviderSession = route({
|
|
|
3916
4017
|
method: "put",
|
|
3917
4018
|
path: "/api/active-sessions/provider-session/{taskId}",
|
|
3918
4019
|
pattern: ["api", "active-sessions", "provider-session", null],
|
|
4020
|
+
session: { taskParam: "taskId" },
|
|
3919
4021
|
summary: "Update provider session ID on an active session",
|
|
3920
4022
|
tags: ["Active Sessions"],
|
|
3921
4023
|
params: object2({ taskId: string2() }),
|
|
@@ -3928,6 +4030,7 @@ var cleanupSessions = route({
|
|
|
3928
4030
|
method: "post",
|
|
3929
4031
|
path: "/api/active-sessions/cleanup",
|
|
3930
4032
|
pattern: ["api", "active-sessions", "cleanup"],
|
|
4033
|
+
session: { agentWide: "Deletes every session of an agent, or every stale session." },
|
|
3931
4034
|
summary: "Clean up stale sessions",
|
|
3932
4035
|
tags: ["Active Sessions"],
|
|
3933
4036
|
body: object2({
|
|
@@ -3942,6 +4045,7 @@ var recoverOrphanedTasks = route({
|
|
|
3942
4045
|
method: "post",
|
|
3943
4046
|
path: "/api/active-sessions/recover-orphaned-tasks",
|
|
3944
4047
|
pattern: ["api", "active-sessions", "recover-orphaned-tasks"],
|
|
4048
|
+
session: { none: "Its handler admits only the calling agent, never a user." },
|
|
3945
4049
|
summary: "Recover orphaned in-progress tasks for an agent",
|
|
3946
4050
|
tags: ["Active Sessions"],
|
|
3947
4051
|
body: object2({
|
|
@@ -3961,7 +4065,7 @@ async function handleActiveSessions(req, res, pathSegments, queryParams, myAgent
|
|
|
3961
4065
|
const parsed = await listActiveSessions.parse(req, res, pathSegments, queryParams);
|
|
3962
4066
|
if (!parsed)
|
|
3963
4067
|
return true;
|
|
3964
|
-
const sessions = await getActiveSessions2(parsed.query.agentId || undefined);
|
|
4068
|
+
const sessions = await getActiveSessions2(parsed.query.agentId || undefined, readableByFor2(await resolveSessionActor2(req, myAgentId)));
|
|
3965
4069
|
listActiveSessions.respond(res, 200, { sessions });
|
|
3966
4070
|
return true;
|
|
3967
4071
|
}
|
|
@@ -4610,14 +4714,17 @@ async function handleAgentRegister(req, res, pathSegments, myAgentId) {
|
|
|
4610
4714
|
}
|
|
4611
4715
|
return false;
|
|
4612
4716
|
}
|
|
4613
|
-
async function handleAgentsRest(req, res, pathSegments, queryParams,
|
|
4717
|
+
async function handleAgentsRest(req, res, pathSegments, queryParams, myAgentId) {
|
|
4614
4718
|
if (listAgents.match(req.method, pathSegments)) {
|
|
4615
4719
|
const parsed = await listAgents.parse(req, res, pathSegments, queryParams);
|
|
4616
4720
|
if (!parsed)
|
|
4617
4721
|
return true;
|
|
4618
4722
|
const includeTasks = parsed.query.include === "tasks";
|
|
4619
4723
|
const slim = parsed.query.fields !== "full";
|
|
4620
|
-
const agents = includeTasks ? await getAllAgentsWithTasks2({
|
|
4724
|
+
const agents = includeTasks ? await getAllAgentsWithTasks2({
|
|
4725
|
+
slim,
|
|
4726
|
+
readableBy: readableByFor2(await resolveSessionActor2(req, myAgentId))
|
|
4727
|
+
}) : await getAllAgents2({ slim });
|
|
4621
4728
|
const [agentsWithCapacity, transports] = await Promise.all([
|
|
4622
4729
|
Promise.all(agents.map(agentWithCapacity)),
|
|
4623
4730
|
resolveListClaudeTransports(agents)
|
|
@@ -5118,6 +5225,7 @@ async function handleAgentsRest(req, res, pathSegments, queryParams, _myAgentId)
|
|
|
5118
5225
|
reportedAt: parsed.body.latest_model?.reportedAt ?? parsed.body.acp.reportedAt,
|
|
5119
5226
|
reportKind: "post_task",
|
|
5120
5227
|
bedrock: null,
|
|
5228
|
+
opencodeGo: null,
|
|
5121
5229
|
acp: null
|
|
5122
5230
|
};
|
|
5123
5231
|
finalAgent = await updateAgentCredStatus2(parsed.params.id, {
|
|
@@ -5155,7 +5263,7 @@ async function handleAgentsRest(req, res, pathSegments, queryParams, _myAgentId)
|
|
|
5155
5263
|
if (!parsed)
|
|
5156
5264
|
return true;
|
|
5157
5265
|
const includeTasks = parsed.query.include === "tasks";
|
|
5158
|
-
const agent = includeTasks ? await getAgentWithTasks2(parsed.params.id) : await getAgentById2(parsed.params.id);
|
|
5266
|
+
const agent = includeTasks ? await getAgentWithTasks2(parsed.params.id, readableByFor2(await resolveSessionActor2(req, myAgentId))) : await getAgentById2(parsed.params.id);
|
|
5159
5267
|
if (!agent) {
|
|
5160
5268
|
jsonError(res, "Agent not found", 404);
|
|
5161
5269
|
return true;
|
|
@@ -5234,6 +5342,7 @@ var reportUsage = route({
|
|
|
5234
5342
|
method: "post",
|
|
5235
5343
|
path: "/api/keys/report-usage",
|
|
5236
5344
|
pattern: ["api", "keys", "report-usage"],
|
|
5345
|
+
session: bodyTask("taskId"),
|
|
5237
5346
|
summary: "Record which API key was used for a task",
|
|
5238
5347
|
tags: ["API Keys"],
|
|
5239
5348
|
body: object2({
|
|
@@ -5277,6 +5386,7 @@ var reportAuthFailure = route({
|
|
|
5277
5386
|
method: "post",
|
|
5278
5387
|
path: "/api/keys/report-auth-failure",
|
|
5279
5388
|
pattern: ["api", "keys", "report-auth-failure"],
|
|
5389
|
+
session: bodyTask("taskId"),
|
|
5280
5390
|
summary: "Record an auth failure for a pooled key; bench it after 2 in a row",
|
|
5281
5391
|
tags: ["API Keys"],
|
|
5282
5392
|
body: object2({
|
|
@@ -5974,6 +6084,7 @@ var createRoute = route({
|
|
|
5974
6084
|
method: "post",
|
|
5975
6085
|
path: "/api/approval-requests",
|
|
5976
6086
|
pattern: ["api", "approval-requests"],
|
|
6087
|
+
session: bodyTask("sourceTaskId"),
|
|
5977
6088
|
summary: "Create a new approval request",
|
|
5978
6089
|
tags: ["ApprovalRequests"],
|
|
5979
6090
|
body: object2({
|
|
@@ -7295,6 +7406,10 @@ function deriveState(state, signals, funnelEvents) {
|
|
|
7295
7406
|
if (state.steps.connect.status !== "done") {
|
|
7296
7407
|
changed = completeStep(state, "connect", "api_key", true, events) || changed;
|
|
7297
7408
|
}
|
|
7409
|
+
const orgName = process.env.SWARM_ORG_NAME?.trim();
|
|
7410
|
+
if (state.steps.name.status !== "done" && orgName && orgName !== "Your Swarm") {
|
|
7411
|
+
changed = completeStep(state, "name", "custom_name", true, events) || changed;
|
|
7412
|
+
}
|
|
7298
7413
|
if (state.steps.ai.status !== "done" && signals.providers.some((entry) => entry.state === "verified")) {
|
|
7299
7414
|
changed = completeStep(state, "ai", inferAiMethod(signals), true, events) || changed;
|
|
7300
7415
|
}
|
|
@@ -7590,6 +7705,417 @@ init_rbac();
|
|
|
7590
7705
|
init_types();
|
|
7591
7706
|
init_request_auth_context();
|
|
7592
7707
|
init_secret_scrubber();
|
|
7708
|
+
|
|
7709
|
+
// src/http/ranking.ts
|
|
7710
|
+
init_zod();
|
|
7711
|
+
init_db3();
|
|
7712
|
+
init_telemetry_ranking();
|
|
7713
|
+
init_telemetry();
|
|
7714
|
+
init_env_flag();
|
|
7715
|
+
|
|
7716
|
+
// src/http/global-config.ts
|
|
7717
|
+
init_db3();
|
|
7718
|
+
async function globalConfig(key) {
|
|
7719
|
+
const stored = (await getSwarmConfigs2({ scope: "global", key }))?.[0]?.value;
|
|
7720
|
+
return (stored ?? process.env[key])?.trim() || undefined;
|
|
7721
|
+
}
|
|
7722
|
+
|
|
7723
|
+
// src/http/used-by.ts
|
|
7724
|
+
init_zod();
|
|
7725
|
+
init_db3();
|
|
7726
|
+
init_telemetry();
|
|
7727
|
+
init_company_profile();
|
|
7728
|
+
init_org_website();
|
|
7729
|
+
var CONSENT_KEY = "SWARM_USED_BY_CONSENT";
|
|
7730
|
+
var CONSENT_AT_KEY = "SWARM_USED_BY_CONSENT_AT";
|
|
7731
|
+
var GRANTED_NAME_KEY = "SWARM_USED_BY_ORG_NAME";
|
|
7732
|
+
var GRANTED_WEBSITE_KEY = "SWARM_USED_BY_ORG_WEBSITE";
|
|
7733
|
+
var CONTACT_EMAIL_KEY = "SWARM_USED_BY_CONTACT_EMAIL";
|
|
7734
|
+
var CONTACT_FUNCTION_KEY = "SWARM_USED_BY_CONTACT_FUNCTION";
|
|
7735
|
+
var QUOTE_KEY = "SWARM_USED_BY_QUOTE";
|
|
7736
|
+
var TIMEOUT_MS = 1e4;
|
|
7737
|
+
var usedBy = route({
|
|
7738
|
+
method: "post",
|
|
7739
|
+
path: "/api/used-by",
|
|
7740
|
+
pattern: ["api", "used-by"],
|
|
7741
|
+
summary: "Grant or revoke the 'used by' logo consent",
|
|
7742
|
+
description: "Records consent to show the organization's logo on agent-swarm.dev. A grant requires a valid contact email. It sends organization identity, contact details, and an optional quote to the proxy for manual review. This explicit admin action works when telemetry is off. A revoke clears the stored contact details and quote.",
|
|
7743
|
+
tags: ["Telemetry"],
|
|
7744
|
+
body: object2({
|
|
7745
|
+
granted: boolean(),
|
|
7746
|
+
contactEmail: string2().max(320, "Contact email must be at most 320 characters").email("Contact email must be a valid email address").optional(),
|
|
7747
|
+
contactFunction: _enum2(USER_FUNCTIONS).optional(),
|
|
7748
|
+
quote: string2().max(280).optional()
|
|
7749
|
+
}).refine((body) => !body.granted || body.contactEmail !== undefined, {
|
|
7750
|
+
path: ["contactEmail"],
|
|
7751
|
+
message: "Contact email is required when granting logo consent"
|
|
7752
|
+
}),
|
|
7753
|
+
responses: {
|
|
7754
|
+
200: {
|
|
7755
|
+
description: "Consent recorded",
|
|
7756
|
+
schema: object2({ granted: boolean(), changedAt: string2() })
|
|
7757
|
+
},
|
|
7758
|
+
400: {
|
|
7759
|
+
description: "Invalid contact details, or missing organization name or website"
|
|
7760
|
+
},
|
|
7761
|
+
401: { description: "Unauthorized" },
|
|
7762
|
+
403: { description: "Only a config admin can change the consent" },
|
|
7763
|
+
502: { description: "The telemetry proxy did not accept the consent" }
|
|
7764
|
+
},
|
|
7765
|
+
rbac: { permission: "config.write.any" }
|
|
7766
|
+
});
|
|
7767
|
+
async function telemetryProxyEndpoint(path) {
|
|
7768
|
+
const feedback = await globalConfig("feedback_endpoint");
|
|
7769
|
+
if (feedback && /^https?:\/\/[^/]+\/v1\/feedback\/?$/.test(feedback)) {
|
|
7770
|
+
return feedback.replace(/\/v1\/feedback\/?$/, path);
|
|
7771
|
+
}
|
|
7772
|
+
return `${TELEMETRY_PROXY_ORIGIN}${path}`;
|
|
7773
|
+
}
|
|
7774
|
+
function usedByEndpoint() {
|
|
7775
|
+
return telemetryProxyEndpoint("/v1/used-by");
|
|
7776
|
+
}
|
|
7777
|
+
async function handleUsedBy(req, res, pathSegments, queryParams, myAgentId) {
|
|
7778
|
+
if (!usedBy.match(req.method, pathSegments))
|
|
7779
|
+
return false;
|
|
7780
|
+
const parsed = await usedBy.parse(req, res, pathSegments, queryParams);
|
|
7781
|
+
if (!parsed)
|
|
7782
|
+
return true;
|
|
7783
|
+
if (!await ensureConfigAdmin(req, res, "config.write.any"))
|
|
7784
|
+
return true;
|
|
7785
|
+
const { granted, contactEmail, contactFunction, quote } = parsed.body;
|
|
7786
|
+
const current = orgIdentityStatus({
|
|
7787
|
+
SWARM_ORG_NAME: await globalConfig("SWARM_ORG_NAME"),
|
|
7788
|
+
SWARM_ORG_WEBSITE: await globalConfig("SWARM_ORG_WEBSITE")
|
|
7789
|
+
});
|
|
7790
|
+
const currentName = current.name ?? undefined;
|
|
7791
|
+
const currentWebsite = current.website;
|
|
7792
|
+
const grantedName = granted ? undefined : await globalConfig(GRANTED_NAME_KEY);
|
|
7793
|
+
const grantedWebsite = granted ? null : normalizeOrgWebsite(await globalConfig(GRANTED_WEBSITE_KEY));
|
|
7794
|
+
const [orgName, orgWebsite] = grantedName && grantedWebsite ? [grantedName, grantedWebsite] : [currentName, currentWebsite];
|
|
7795
|
+
if (!orgName || !orgWebsite) {
|
|
7796
|
+
jsonError(res, "Set the organization name and website first", 400);
|
|
7797
|
+
return true;
|
|
7798
|
+
}
|
|
7799
|
+
const installId = isTelemetryEnabled() ? await globalConfig("telemetry_installation_id") : undefined;
|
|
7800
|
+
const orgDomain = await globalConfig("telemetry_org_domain");
|
|
7801
|
+
const changedAt = new Date().toISOString();
|
|
7802
|
+
try {
|
|
7803
|
+
const response = await fetch(await usedByEndpoint(), {
|
|
7804
|
+
method: "POST",
|
|
7805
|
+
headers: { "Content-Type": "application/json" },
|
|
7806
|
+
body: JSON.stringify({
|
|
7807
|
+
...installId ? { install_id: installId } : {},
|
|
7808
|
+
org_name: orgName,
|
|
7809
|
+
org_website: orgWebsite,
|
|
7810
|
+
...orgDomain ? { org_domain: orgDomain } : {},
|
|
7811
|
+
granted,
|
|
7812
|
+
...granted ? { contact_email: contactEmail, contact_function: contactFunction, quote } : {},
|
|
7813
|
+
changed_at: changedAt
|
|
7814
|
+
}),
|
|
7815
|
+
signal: AbortSignal.timeout(TIMEOUT_MS)
|
|
7816
|
+
});
|
|
7817
|
+
if (!response.ok) {
|
|
7818
|
+
jsonError(res, `The telemetry proxy answered ${response.status}`, 502);
|
|
7819
|
+
return true;
|
|
7820
|
+
}
|
|
7821
|
+
} catch {
|
|
7822
|
+
jsonError(res, "Could not reach the telemetry proxy", 502);
|
|
7823
|
+
return true;
|
|
7824
|
+
}
|
|
7825
|
+
await getDbClient2().transaction(async () => {
|
|
7826
|
+
await upsertSwarmConfig2({ scope: "global", key: CONSENT_KEY, value: String(granted) });
|
|
7827
|
+
await upsertSwarmConfig2({ scope: "global", key: CONSENT_AT_KEY, value: changedAt });
|
|
7828
|
+
if (granted) {
|
|
7829
|
+
await upsertSwarmConfig2({ scope: "global", key: GRANTED_NAME_KEY, value: orgName });
|
|
7830
|
+
await upsertSwarmConfig2({ scope: "global", key: GRANTED_WEBSITE_KEY, value: orgWebsite });
|
|
7831
|
+
}
|
|
7832
|
+
for (const [key, value, isSecret] of [
|
|
7833
|
+
[CONTACT_EMAIL_KEY, contactEmail, true],
|
|
7834
|
+
[CONTACT_FUNCTION_KEY, contactFunction, false],
|
|
7835
|
+
[QUOTE_KEY, quote, false]
|
|
7836
|
+
]) {
|
|
7837
|
+
await upsertSwarmConfig2({
|
|
7838
|
+
scope: "global",
|
|
7839
|
+
key,
|
|
7840
|
+
value: granted ? value ?? "" : "",
|
|
7841
|
+
isSecret,
|
|
7842
|
+
exportToEnv: false
|
|
7843
|
+
});
|
|
7844
|
+
}
|
|
7845
|
+
});
|
|
7846
|
+
scheduleIntegrationsReload();
|
|
7847
|
+
const owner = await resolveHttpFavoriteOwner(req, myAgentId);
|
|
7848
|
+
trackRelayed("used_by.consent_changed", {
|
|
7849
|
+
granted,
|
|
7850
|
+
has_quote: granted && Boolean(quote),
|
|
7851
|
+
has_function: granted && Boolean(contactFunction)
|
|
7852
|
+
}, { actor: owner?.userId ? { userId: owner.userId } : undefined, emitter: "ui" });
|
|
7853
|
+
usedBy.respond(res, 200, { granted, changedAt });
|
|
7854
|
+
return true;
|
|
7855
|
+
}
|
|
7856
|
+
|
|
7857
|
+
// src/http/ranking.ts
|
|
7858
|
+
var ListingSchema = object2({
|
|
7859
|
+
v: literal2(1),
|
|
7860
|
+
name: string2(),
|
|
7861
|
+
domain: string2(),
|
|
7862
|
+
category: _enum2(RANKING_CATEGORIES),
|
|
7863
|
+
subcategory: string2().nullable(),
|
|
7864
|
+
description: string2().nullable(),
|
|
7865
|
+
sourceCodeUrl: string2().nullable(),
|
|
7866
|
+
updatedAt: string2()
|
|
7867
|
+
});
|
|
7868
|
+
var RankingStateSchema = object2({
|
|
7869
|
+
eligible: boolean(),
|
|
7870
|
+
missing: array2(_enum2(ORG_IDENTITY_KEYS)),
|
|
7871
|
+
telemetryOn: boolean(),
|
|
7872
|
+
enabled: boolean(),
|
|
7873
|
+
name: string2().nullable(),
|
|
7874
|
+
domain: string2().nullable(),
|
|
7875
|
+
listing: ListingSchema.nullable(),
|
|
7876
|
+
listingMatchesDomain: boolean(),
|
|
7877
|
+
verifyUrl: string2().nullable()
|
|
7878
|
+
});
|
|
7879
|
+
var OptInResponseSchema = object2({
|
|
7880
|
+
domain: string2(),
|
|
7881
|
+
listing: ListingSchema,
|
|
7882
|
+
verifyUrl: string2()
|
|
7883
|
+
});
|
|
7884
|
+
var OptOutResponseSchema = object2({
|
|
7885
|
+
enabled: literal2(false)
|
|
7886
|
+
});
|
|
7887
|
+
var getRanking = route({
|
|
7888
|
+
method: "get",
|
|
7889
|
+
path: "/api/ranking",
|
|
7890
|
+
pattern: ["api", "ranking"],
|
|
7891
|
+
summary: "Public ranking state",
|
|
7892
|
+
description: "Whether this install can join the public agent-swarm.dev ranking (telemetry on, organization name and website set), whether it is opted in, and its stored listing. Only a config admin can read it.",
|
|
7893
|
+
tags: ["Telemetry"],
|
|
7894
|
+
responses: {
|
|
7895
|
+
200: { description: "Ranking state", schema: RankingStateSchema },
|
|
7896
|
+
401: { description: "Unauthorized" },
|
|
7897
|
+
403: { description: "Only a config admin can read the ranking state" }
|
|
7898
|
+
},
|
|
7899
|
+
rbac: { permission: "config.write.any" }
|
|
7900
|
+
});
|
|
7901
|
+
var optIn = route({
|
|
7902
|
+
method: "post",
|
|
7903
|
+
path: "/api/ranking/opt-in",
|
|
7904
|
+
pattern: ["api", "ranking", "opt-in"],
|
|
7905
|
+
summary: "Opt this install into the public ranking",
|
|
7906
|
+
description: "Builds the telemetry_ranking listing from the organization name and website plus the chosen category, stores it, turns PUBLIC_RANKING_ENABLED on and sends ranking.opted_in. That is the whole opt-in: the install shows on agent-swarm.dev/ranking at the next hourly refresh.",
|
|
7907
|
+
tags: ["Telemetry"],
|
|
7908
|
+
body: rankingFormSchema,
|
|
7909
|
+
responses: {
|
|
7910
|
+
200: { description: "Opted in", schema: OptInResponseSchema },
|
|
7911
|
+
400: { description: "A listing field is invalid" },
|
|
7912
|
+
401: { description: "Unauthorized" },
|
|
7913
|
+
403: { description: "Only a config admin can opt in" },
|
|
7914
|
+
409: {
|
|
7915
|
+
description: "telemetry_off: telemetry is off or the install has no ID. org_identity_missing: the organization name or website is not set (`missing` lists which)."
|
|
7916
|
+
}
|
|
7917
|
+
},
|
|
7918
|
+
rbac: { permission: "config.write.any" }
|
|
7919
|
+
});
|
|
7920
|
+
var optOut = route({
|
|
7921
|
+
method: "post",
|
|
7922
|
+
path: "/api/ranking/opt-out",
|
|
7923
|
+
pattern: ["api", "ranking", "opt-out"],
|
|
7924
|
+
summary: "Leave the public ranking",
|
|
7925
|
+
description: "Turns PUBLIC_RANKING_ENABLED off and sends ranking.opted_out, so workers stop sending ranking.usage. The install leaves agent-swarm.dev/ranking at the next hourly refresh.",
|
|
7926
|
+
tags: ["Telemetry"],
|
|
7927
|
+
responses: {
|
|
7928
|
+
200: { description: "Opted out", schema: OptOutResponseSchema },
|
|
7929
|
+
401: { description: "Unauthorized" },
|
|
7930
|
+
403: { description: "Only a config admin can opt out" }
|
|
7931
|
+
},
|
|
7932
|
+
rbac: { permission: "config.write.any" }
|
|
7933
|
+
});
|
|
7934
|
+
var RANKING_CONFIG_KEYS = new Set([...ORG_IDENTITY_KEYS, PUBLIC_RANKING_ENABLED_KEY]);
|
|
7935
|
+
async function currentIdentity(changed) {
|
|
7936
|
+
const read = async (key) => changed?.key === key ? changed.value : await globalConfig(key);
|
|
7937
|
+
return orgIdentityStatus({
|
|
7938
|
+
SWARM_ORG_NAME: await read("SWARM_ORG_NAME"),
|
|
7939
|
+
SWARM_ORG_WEBSITE: await read("SWARM_ORG_WEBSITE")
|
|
7940
|
+
});
|
|
7941
|
+
}
|
|
7942
|
+
async function storedListing() {
|
|
7943
|
+
return readTelemetryRanking(await globalConfig(TELEMETRY_RANKING_KEY));
|
|
7944
|
+
}
|
|
7945
|
+
async function rankingEnabled() {
|
|
7946
|
+
const raw = await globalConfig(PUBLIC_RANKING_ENABLED_KEY);
|
|
7947
|
+
return isEnvFlagEnabled(PUBLIC_RANKING_ENABLED_KEY, false, {
|
|
7948
|
+
[PUBLIC_RANKING_ENABLED_KEY]: raw
|
|
7949
|
+
});
|
|
7950
|
+
}
|
|
7951
|
+
async function telemetryReady() {
|
|
7952
|
+
return isTelemetryEnabled() && Boolean(await globalConfig("telemetry_installation_id"));
|
|
7953
|
+
}
|
|
7954
|
+
async function verifyUrl(domain) {
|
|
7955
|
+
const params = new URLSearchParams({ domain });
|
|
7956
|
+
return `${await telemetryProxyEndpoint("/v1/ranking/verify")}?${params.toString()}`;
|
|
7957
|
+
}
|
|
7958
|
+
function withoutClaim(listing) {
|
|
7959
|
+
const { claim: _claim, ...rest } = listing;
|
|
7960
|
+
return rest;
|
|
7961
|
+
}
|
|
7962
|
+
async function setEnabled(enabled) {
|
|
7963
|
+
await upsertSwarmConfig2({
|
|
7964
|
+
scope: "global",
|
|
7965
|
+
key: PUBLIC_RANKING_ENABLED_KEY,
|
|
7966
|
+
value: String(enabled)
|
|
7967
|
+
});
|
|
7968
|
+
}
|
|
7969
|
+
function emitOptedIn(listing) {
|
|
7970
|
+
telemetry.ranking("opted_in", { listing: JSON.stringify(withoutClaim(listing)) }, { orgName: listing.name, orgWebsite: listing.domain });
|
|
7971
|
+
}
|
|
7972
|
+
function emitOptedOut(domain) {
|
|
7973
|
+
telemetry.ranking("opted_out", { domain });
|
|
7974
|
+
}
|
|
7975
|
+
async function buildRankingState() {
|
|
7976
|
+
const [identity, listing, enabled, telemetryOn] = await Promise.all([
|
|
7977
|
+
currentIdentity(),
|
|
7978
|
+
storedListing(),
|
|
7979
|
+
rankingEnabled(),
|
|
7980
|
+
telemetryReady()
|
|
7981
|
+
]);
|
|
7982
|
+
const listingMatchesDomain = Boolean(listing && listing.domain === identity.website);
|
|
7983
|
+
const current = listingMatchesDomain ? listing : null;
|
|
7984
|
+
return {
|
|
7985
|
+
eligible: telemetryOn && identity.ok,
|
|
7986
|
+
missing: identity.missing,
|
|
7987
|
+
telemetryOn,
|
|
7988
|
+
enabled: enabled && listingMatchesDomain,
|
|
7989
|
+
name: identity.name,
|
|
7990
|
+
domain: identity.website,
|
|
7991
|
+
listing: listing ? withoutClaim(listing) : null,
|
|
7992
|
+
listingMatchesDomain,
|
|
7993
|
+
verifyUrl: current ? await verifyUrl(current.domain) : null
|
|
7994
|
+
};
|
|
7995
|
+
}
|
|
7996
|
+
async function syncRankingAfterConfigChange(key, value) {
|
|
7997
|
+
if (!RANKING_CONFIG_KEYS.has(key))
|
|
7998
|
+
return;
|
|
7999
|
+
const listing = await storedListing();
|
|
8000
|
+
if (!listing)
|
|
8001
|
+
return;
|
|
8002
|
+
if (key === PUBLIC_RANKING_ENABLED_KEY) {
|
|
8003
|
+
if (!isEnvFlagEnabled(key, false, { [key]: value ?? undefined })) {
|
|
8004
|
+
emitOptedOut(listing.domain);
|
|
8005
|
+
}
|
|
8006
|
+
return;
|
|
8007
|
+
}
|
|
8008
|
+
if (!await rankingEnabled())
|
|
8009
|
+
return;
|
|
8010
|
+
const identity = await currentIdentity({ key, value });
|
|
8011
|
+
let rebuilt = null;
|
|
8012
|
+
if (identity.ok && identity.website === listing.domain) {
|
|
8013
|
+
if (identity.name === listing.name)
|
|
8014
|
+
return;
|
|
8015
|
+
try {
|
|
8016
|
+
rebuilt = buildListing(identity, listing);
|
|
8017
|
+
} catch {}
|
|
8018
|
+
}
|
|
8019
|
+
if (!rebuilt) {
|
|
8020
|
+
await setEnabled(false);
|
|
8021
|
+
emitOptedOut(listing.domain);
|
|
8022
|
+
return;
|
|
8023
|
+
}
|
|
8024
|
+
await upsertSwarmConfig2({
|
|
8025
|
+
scope: "global",
|
|
8026
|
+
key: TELEMETRY_RANKING_KEY,
|
|
8027
|
+
value: JSON.stringify(rebuilt)
|
|
8028
|
+
});
|
|
8029
|
+
emitOptedIn(rebuilt);
|
|
8030
|
+
}
|
|
8031
|
+
function conflict(res, body) {
|
|
8032
|
+
res.writeHead(409, { "Content-Type": "application/json" });
|
|
8033
|
+
res.end(JSON.stringify(body));
|
|
8034
|
+
}
|
|
8035
|
+
async function handleRanking(req, res, pathSegments, queryParams) {
|
|
8036
|
+
if (getRanking.match(req.method, pathSegments)) {
|
|
8037
|
+
const parsed = await getRanking.parse(req, res, pathSegments, queryParams);
|
|
8038
|
+
if (!parsed)
|
|
8039
|
+
return true;
|
|
8040
|
+
if (!await ensureConfigAdmin(req, res, "config.write.any"))
|
|
8041
|
+
return true;
|
|
8042
|
+
getRanking.respond(res, 200, await buildRankingState());
|
|
8043
|
+
return true;
|
|
8044
|
+
}
|
|
8045
|
+
if (optIn.match(req.method, pathSegments)) {
|
|
8046
|
+
const parsed = await optIn.parse(req, res, pathSegments, queryParams);
|
|
8047
|
+
if (!parsed)
|
|
8048
|
+
return true;
|
|
8049
|
+
if (!await ensureConfigAdmin(req, res, "config.write.any"))
|
|
8050
|
+
return true;
|
|
8051
|
+
if (!await telemetryReady()) {
|
|
8052
|
+
conflict(res, {
|
|
8053
|
+
error: "telemetry_off",
|
|
8054
|
+
message: "Turn telemetry on first: the ranking counts tokens from telemetry."
|
|
8055
|
+
});
|
|
8056
|
+
return true;
|
|
8057
|
+
}
|
|
8058
|
+
const identity = await currentIdentity();
|
|
8059
|
+
if (!identity.ok || !identity.website) {
|
|
8060
|
+
conflict(res, {
|
|
8061
|
+
error: "org_identity_missing",
|
|
8062
|
+
message: "Set the organization name and website first",
|
|
8063
|
+
missing: identity.missing
|
|
8064
|
+
});
|
|
8065
|
+
return true;
|
|
8066
|
+
}
|
|
8067
|
+
const db = getDbClient2();
|
|
8068
|
+
const built = await db.transaction(async () => {
|
|
8069
|
+
let listing;
|
|
8070
|
+
try {
|
|
8071
|
+
listing = buildListing(identity, parsed.body);
|
|
8072
|
+
} catch (error) {
|
|
8073
|
+
return { ok: false, error };
|
|
8074
|
+
}
|
|
8075
|
+
await upsertSwarmConfig2({
|
|
8076
|
+
scope: "global",
|
|
8077
|
+
key: TELEMETRY_RANKING_KEY,
|
|
8078
|
+
value: JSON.stringify(listing)
|
|
8079
|
+
});
|
|
8080
|
+
await setEnabled(true);
|
|
8081
|
+
db.afterCommit(() => emitOptedIn(listing));
|
|
8082
|
+
return { ok: true, listing };
|
|
8083
|
+
});
|
|
8084
|
+
if (!built.ok) {
|
|
8085
|
+
const issue = built.error instanceof ZodError ? built.error.issues[0] : undefined;
|
|
8086
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
8087
|
+
res.end(JSON.stringify({
|
|
8088
|
+
error: issue ? `Invalid ${issue.path.join(".") || "listing"}: ${issue.message}` : "Invalid listing"
|
|
8089
|
+
}));
|
|
8090
|
+
return true;
|
|
8091
|
+
}
|
|
8092
|
+
const { listing } = built;
|
|
8093
|
+
scheduleIntegrationsReload();
|
|
8094
|
+
optIn.respond(res, 200, {
|
|
8095
|
+
domain: listing.domain,
|
|
8096
|
+
listing,
|
|
8097
|
+
verifyUrl: await verifyUrl(listing.domain)
|
|
8098
|
+
});
|
|
8099
|
+
return true;
|
|
8100
|
+
}
|
|
8101
|
+
if (optOut.match(req.method, pathSegments)) {
|
|
8102
|
+
const parsed = await optOut.parse(req, res, pathSegments, queryParams);
|
|
8103
|
+
if (!parsed)
|
|
8104
|
+
return true;
|
|
8105
|
+
if (!await ensureConfigAdmin(req, res, "config.write.any"))
|
|
8106
|
+
return true;
|
|
8107
|
+
const listing = await storedListing();
|
|
8108
|
+
await setEnabled(false);
|
|
8109
|
+
if (listing)
|
|
8110
|
+
emitOptedOut(listing.domain);
|
|
8111
|
+
scheduleIntegrationsReload();
|
|
8112
|
+
optOut.respond(res, 200, { enabled: false });
|
|
8113
|
+
return true;
|
|
8114
|
+
}
|
|
8115
|
+
return false;
|
|
8116
|
+
}
|
|
8117
|
+
|
|
8118
|
+
// src/http/config.ts
|
|
7593
8119
|
var MAX_ENV_PRESENCE_KEYS = 200;
|
|
7594
8120
|
var SECRETS_FORCE_MASK_NOTE = " (secret values masked: this token cannot read secrets in this scope)";
|
|
7595
8121
|
function stripApiOnlyKeys(configs) {
|
|
@@ -7959,6 +8485,8 @@ async function handleConfig(req, res, pathSegments, queryParams) {
|
|
|
7959
8485
|
if (scope === "global" && key === "SWARM_ORG_WEBSITE") {
|
|
7960
8486
|
await fillOrgLogoFromWebsite(String(value));
|
|
7961
8487
|
}
|
|
8488
|
+
if (scope === "global")
|
|
8489
|
+
await syncRankingAfterConfigChange(key, String(value));
|
|
7962
8490
|
if (scope === "global") {
|
|
7963
8491
|
scheduleIntegrationsReload();
|
|
7964
8492
|
}
|
|
@@ -7994,6 +8522,7 @@ async function handleConfig(req, res, pathSegments, queryParams) {
|
|
|
7994
8522
|
return true;
|
|
7995
8523
|
}
|
|
7996
8524
|
if (existing.scope === "global") {
|
|
8525
|
+
await syncRankingAfterConfigChange(existing.key, null);
|
|
7997
8526
|
scheduleIntegrationsReload();
|
|
7998
8527
|
}
|
|
7999
8528
|
if (existing.scope === "agent" && existing.scopeId && existing.key === AGENT_MAX_TASKS_CONFIG_KEY) {
|
|
@@ -8830,6 +9359,7 @@ var postContext = route({
|
|
|
8830
9359
|
method: "post",
|
|
8831
9360
|
path: "/api/tasks/{id}/context",
|
|
8832
9361
|
pattern: ["api", "tasks", null, "context"],
|
|
9362
|
+
session: { taskParam: "id" },
|
|
8833
9363
|
summary: "Record a context usage snapshot for a task",
|
|
8834
9364
|
tags: ["Tasks"],
|
|
8835
9365
|
params: object2({ id: string2() }),
|
|
@@ -8856,6 +9386,7 @@ var getContext = route({
|
|
|
8856
9386
|
method: "get",
|
|
8857
9387
|
path: "/api/tasks/{id}/context",
|
|
8858
9388
|
pattern: ["api", "tasks", null, "context"],
|
|
9389
|
+
session: { taskParam: "id" },
|
|
8859
9390
|
summary: "Get context usage history for a task",
|
|
8860
9391
|
tags: ["Tasks"],
|
|
8861
9392
|
params: object2({ id: string2() }),
|
|
@@ -8936,6 +9467,7 @@ var createEventRoute = route({
|
|
|
8936
9467
|
method: "post",
|
|
8937
9468
|
path: "/api/events",
|
|
8938
9469
|
pattern: ["api", "events"],
|
|
9470
|
+
session: bodyTask("taskId"),
|
|
8939
9471
|
summary: "Store a single event",
|
|
8940
9472
|
tags: ["Events"],
|
|
8941
9473
|
body: eventBodySchema,
|
|
@@ -8952,6 +9484,7 @@ var createEventsBatchRoute = route({
|
|
|
8952
9484
|
method: "post",
|
|
8953
9485
|
path: "/api/events/batch",
|
|
8954
9486
|
pattern: ["api", "events", "batch"],
|
|
9487
|
+
session: bodyListTask("events", "taskId"),
|
|
8955
9488
|
summary: "Store multiple events in a batch",
|
|
8956
9489
|
tags: ["Events"],
|
|
8957
9490
|
body: object2({
|
|
@@ -9026,7 +9559,7 @@ var getEventCountsRoute = route({
|
|
|
9026
9559
|
auth: { apiKey: true }
|
|
9027
9560
|
});
|
|
9028
9561
|
var MAX_LATEST_PAIRS = 50;
|
|
9029
|
-
async function handleEvents(req, res, pathSegments, queryParams,
|
|
9562
|
+
async function handleEvents(req, res, pathSegments, queryParams, myAgentId) {
|
|
9030
9563
|
if (createEventsBatchRoute.match(req.method, pathSegments)) {
|
|
9031
9564
|
const parsed = await createEventsBatchRoute.parse(req, res, pathSegments, queryParams);
|
|
9032
9565
|
if (!parsed)
|
|
@@ -9100,7 +9633,8 @@ async function handleEvents(req, res, pathSegments, queryParams, _myAgentId) {
|
|
|
9100
9633
|
until: parsed.query.until || undefined,
|
|
9101
9634
|
limit: parsed.query.limit ?? 100,
|
|
9102
9635
|
events: eventFilter,
|
|
9103
|
-
latestPerDataField: latestPerDataField ? { events: latestEvents, dataFields } : undefined
|
|
9636
|
+
latestPerDataField: latestPerDataField ? { events: latestEvents, dataFields } : undefined,
|
|
9637
|
+
readableBy: readableByFor2(await resolveSessionActor2(req, myAgentId))
|
|
9104
9638
|
});
|
|
9105
9639
|
getEventsRoute.respond(res, 200, latestPerDataField ? { events, latestPerDataField: true } : { events });
|
|
9106
9640
|
return true;
|
|
@@ -12423,7 +12957,6 @@ async function handleFavorites(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
12423
12957
|
|
|
12424
12958
|
// src/http/fleet-benchmark.ts
|
|
12425
12959
|
init_zod();
|
|
12426
|
-
init_db3();
|
|
12427
12960
|
|
|
12428
12961
|
// src/fleet-benchmark.ts
|
|
12429
12962
|
var FLEET_WINDOW_DAYS = 30;
|
|
@@ -12502,8 +13035,8 @@ function round(value, decimals) {
|
|
|
12502
13035
|
|
|
12503
13036
|
// src/be/fleet-benchmark-stats.ts
|
|
12504
13037
|
init_runtime();
|
|
12505
|
-
async function getLocalFleetStats(now = new Date) {
|
|
12506
|
-
const start = fleetCountStart(await getTelemetryIngestAnchor(), now);
|
|
13038
|
+
async function getLocalFleetStats(now = new Date, { requireIngestAnchor = true } = {}) {
|
|
13039
|
+
const start = requireIngestAnchor ? fleetCountStart(await getTelemetryIngestAnchor(), now) : new Date(now.getTime() - FLEET_WINDOW_DAYS * 86400000);
|
|
12507
13040
|
if (!start)
|
|
12508
13041
|
return null;
|
|
12509
13042
|
const since = start.toISOString();
|
|
@@ -12524,110 +13057,8 @@ async function getLocalFleetStats(now = new Date) {
|
|
|
12524
13057
|
|
|
12525
13058
|
// src/http/fleet-benchmark.ts
|
|
12526
13059
|
init_telemetry();
|
|
12527
|
-
|
|
12528
|
-
// src/http/used-by.ts
|
|
12529
|
-
init_zod();
|
|
12530
|
-
init_db3();
|
|
12531
|
-
init_telemetry();
|
|
13060
|
+
init_company_profile();
|
|
12532
13061
|
init_org_website();
|
|
12533
|
-
var CONSENT_KEY = "SWARM_USED_BY_CONSENT";
|
|
12534
|
-
var CONSENT_AT_KEY = "SWARM_USED_BY_CONSENT_AT";
|
|
12535
|
-
var GRANTED_NAME_KEY = "SWARM_USED_BY_ORG_NAME";
|
|
12536
|
-
var GRANTED_WEBSITE_KEY = "SWARM_USED_BY_ORG_WEBSITE";
|
|
12537
|
-
var TIMEOUT_MS = 1e4;
|
|
12538
|
-
var usedBy = route({
|
|
12539
|
-
method: "post",
|
|
12540
|
-
path: "/api/used-by",
|
|
12541
|
-
pattern: ["api", "used-by"],
|
|
12542
|
-
summary: "Grant or revoke the 'used by' logo consent",
|
|
12543
|
-
description: "Records whether agent-swarm.dev may show this organization's logo as a company that uses Agent Swarm. Sends the organization name and website to the telemetry proxy, where a person approves each logo by hand. An explicit admin action, so it is not gated by the telemetry opt-out.",
|
|
12544
|
-
tags: ["Telemetry"],
|
|
12545
|
-
body: object2({ granted: boolean() }),
|
|
12546
|
-
responses: {
|
|
12547
|
-
200: {
|
|
12548
|
-
description: "Consent recorded",
|
|
12549
|
-
schema: object2({ granted: boolean(), changedAt: string2() })
|
|
12550
|
-
},
|
|
12551
|
-
400: {
|
|
12552
|
-
description: "Organization name or website is not set (a revoke falls back to the identity of the last grant)"
|
|
12553
|
-
},
|
|
12554
|
-
401: { description: "Unauthorized" },
|
|
12555
|
-
403: { description: "Only a config admin can change the consent" },
|
|
12556
|
-
502: { description: "The telemetry proxy did not accept the consent" }
|
|
12557
|
-
},
|
|
12558
|
-
rbac: { permission: "config.write.any" }
|
|
12559
|
-
});
|
|
12560
|
-
async function globalConfig(key) {
|
|
12561
|
-
const stored = (await getSwarmConfigs2({ scope: "global", key }))?.[0]?.value?.trim();
|
|
12562
|
-
return stored || process.env[key]?.trim() || undefined;
|
|
12563
|
-
}
|
|
12564
|
-
async function telemetryProxyEndpoint(path) {
|
|
12565
|
-
const feedback = await globalConfig("feedback_endpoint");
|
|
12566
|
-
if (feedback && /^https?:\/\/[^/]+\/v1\/feedback\/?$/.test(feedback)) {
|
|
12567
|
-
return feedback.replace(/\/v1\/feedback\/?$/, path);
|
|
12568
|
-
}
|
|
12569
|
-
return `${TELEMETRY_PROXY_ORIGIN}${path}`;
|
|
12570
|
-
}
|
|
12571
|
-
function usedByEndpoint() {
|
|
12572
|
-
return telemetryProxyEndpoint("/v1/used-by");
|
|
12573
|
-
}
|
|
12574
|
-
async function handleUsedBy(req, res, pathSegments, queryParams, myAgentId) {
|
|
12575
|
-
if (!usedBy.match(req.method, pathSegments))
|
|
12576
|
-
return false;
|
|
12577
|
-
const parsed = await usedBy.parse(req, res, pathSegments, queryParams);
|
|
12578
|
-
if (!parsed)
|
|
12579
|
-
return true;
|
|
12580
|
-
if (!await ensureConfigAdmin(req, res, "config.write.any"))
|
|
12581
|
-
return true;
|
|
12582
|
-
const { granted } = parsed.body;
|
|
12583
|
-
const currentName = await globalConfig("SWARM_ORG_NAME");
|
|
12584
|
-
const currentWebsite = normalizeOrgWebsite(await globalConfig("SWARM_ORG_WEBSITE"));
|
|
12585
|
-
const grantedName = granted ? undefined : await globalConfig(GRANTED_NAME_KEY);
|
|
12586
|
-
const grantedWebsite = granted ? null : normalizeOrgWebsite(await globalConfig(GRANTED_WEBSITE_KEY));
|
|
12587
|
-
const [orgName, orgWebsite] = grantedName && grantedWebsite ? [grantedName, grantedWebsite] : [currentName, currentWebsite];
|
|
12588
|
-
if (!orgName || !orgWebsite) {
|
|
12589
|
-
jsonError(res, "Set the organization name and website first", 400);
|
|
12590
|
-
return true;
|
|
12591
|
-
}
|
|
12592
|
-
const installId = isTelemetryEnabled() ? await globalConfig("telemetry_installation_id") : undefined;
|
|
12593
|
-
const orgDomain = await globalConfig("telemetry_org_domain");
|
|
12594
|
-
const changedAt = new Date().toISOString();
|
|
12595
|
-
try {
|
|
12596
|
-
const response = await fetch(await usedByEndpoint(), {
|
|
12597
|
-
method: "POST",
|
|
12598
|
-
headers: { "Content-Type": "application/json" },
|
|
12599
|
-
body: JSON.stringify({
|
|
12600
|
-
...installId ? { install_id: installId } : {},
|
|
12601
|
-
org_name: orgName,
|
|
12602
|
-
org_website: orgWebsite,
|
|
12603
|
-
...orgDomain ? { org_domain: orgDomain } : {},
|
|
12604
|
-
granted,
|
|
12605
|
-
changed_at: changedAt
|
|
12606
|
-
}),
|
|
12607
|
-
signal: AbortSignal.timeout(TIMEOUT_MS)
|
|
12608
|
-
});
|
|
12609
|
-
if (!response.ok) {
|
|
12610
|
-
jsonError(res, `The telemetry proxy answered ${response.status}`, 502);
|
|
12611
|
-
return true;
|
|
12612
|
-
}
|
|
12613
|
-
} catch {
|
|
12614
|
-
jsonError(res, "Could not reach the telemetry proxy", 502);
|
|
12615
|
-
return true;
|
|
12616
|
-
}
|
|
12617
|
-
await upsertSwarmConfig2({ scope: "global", key: CONSENT_KEY, value: String(granted) });
|
|
12618
|
-
await upsertSwarmConfig2({ scope: "global", key: CONSENT_AT_KEY, value: changedAt });
|
|
12619
|
-
if (granted) {
|
|
12620
|
-
await upsertSwarmConfig2({ scope: "global", key: GRANTED_NAME_KEY, value: orgName });
|
|
12621
|
-
await upsertSwarmConfig2({ scope: "global", key: GRANTED_WEBSITE_KEY, value: orgWebsite });
|
|
12622
|
-
}
|
|
12623
|
-
scheduleIntegrationsReload();
|
|
12624
|
-
const owner = await resolveHttpFavoriteOwner(req, myAgentId);
|
|
12625
|
-
trackRelayed("used_by.consent_changed", { granted }, { actor: owner?.userId ? { userId: owner.userId } : undefined, emitter: "ui" });
|
|
12626
|
-
usedBy.respond(res, 200, { granted, changedAt });
|
|
12627
|
-
return true;
|
|
12628
|
-
}
|
|
12629
|
-
|
|
12630
|
-
// src/http/fleet-benchmark.ts
|
|
12631
13062
|
var FLEET_TTL_MS = 24 * 60 * 60 * 1000;
|
|
12632
13063
|
var FLEET_FAILURE_TTL_MS = 60 * 60 * 1000;
|
|
12633
13064
|
var LOCAL_TTL_MS = 10 * 60 * 1000;
|
|
@@ -12641,6 +13072,12 @@ var FleetStateSchema = _enum2([
|
|
|
12641
13072
|
]);
|
|
12642
13073
|
var FleetBenchmarkResponseSchema = object2({
|
|
12643
13074
|
state: FleetStateSchema,
|
|
13075
|
+
level: _enum2(["basic", "full"]),
|
|
13076
|
+
peerGroup: object2({
|
|
13077
|
+
teamSize: string2().nullable(),
|
|
13078
|
+
useCase: string2().nullable(),
|
|
13079
|
+
orgs: number2().int()
|
|
13080
|
+
}).nullable(),
|
|
12644
13081
|
local: object2({
|
|
12645
13082
|
tasksPerWeek: number2(),
|
|
12646
13083
|
taskSuccessRate: number2().nullable(),
|
|
@@ -12664,7 +13101,7 @@ var fleetBenchmark = route({
|
|
|
12664
13101
|
path: "/api/fleet-benchmark",
|
|
12665
13102
|
pattern: ["api", "fleet-benchmark"],
|
|
12666
13103
|
summary: "Compare this swarm with similar swarms",
|
|
12667
|
-
description: "
|
|
13104
|
+
description: "Compares local task volume and success rate with fleet percentiles over the last 30 days. Comparisons require telemetry, an organization name, and a known proxy ingest date. A website, team size, and use case enable full comparisons with peer filters. The response includes the proxy's peer group when available. With telemetry off, it returns local DB counts without a proxy request. The fleet cache key includes the peer filters.",
|
|
12668
13105
|
tags: ["Telemetry"],
|
|
12669
13106
|
responses: {
|
|
12670
13107
|
200: {
|
|
@@ -12700,48 +13137,64 @@ var ProxyFleetBenchmarksSchema = object2({
|
|
|
12700
13137
|
"1": ProxyBlockSchema.optional(),
|
|
12701
13138
|
"2-5": ProxyBlockSchema.optional(),
|
|
12702
13139
|
"6+": ProxyBlockSchema.optional()
|
|
12703
|
-
})
|
|
12704
|
-
|
|
12705
|
-
|
|
12706
|
-
|
|
13140
|
+
}),
|
|
13141
|
+
peer_group: object2({
|
|
13142
|
+
team_size: string2().nullable(),
|
|
13143
|
+
use_case: string2().nullable(),
|
|
13144
|
+
orgs: number2().int().nonnegative()
|
|
13145
|
+
}).nullable().optional()
|
|
13146
|
+
});
|
|
13147
|
+
async function fleetBenchmarks(now, peerGroup) {
|
|
13148
|
+
const endpoint = new URL(await telemetryProxyEndpoint("/v1/fleet/benchmarks"));
|
|
13149
|
+
endpoint.searchParams.set("window", "30d");
|
|
13150
|
+
if (peerGroup) {
|
|
13151
|
+
endpoint.searchParams.set("team_size", peerGroup.teamSize);
|
|
13152
|
+
endpoint.searchParams.set("use_case", peerGroup.useCase);
|
|
13153
|
+
}
|
|
13154
|
+
const key = endpoint.toString();
|
|
13155
|
+
if (fleetCache?.key === key && fleetCache.until > now)
|
|
12707
13156
|
return fleetCache.value;
|
|
12708
13157
|
let value = null;
|
|
12709
13158
|
try {
|
|
12710
|
-
const response = await fetch(
|
|
12711
|
-
signal: AbortSignal.timeout(TIMEOUT_MS2)
|
|
12712
|
-
});
|
|
13159
|
+
const response = await fetch(key, { signal: AbortSignal.timeout(TIMEOUT_MS2) });
|
|
12713
13160
|
if (response.ok) {
|
|
12714
13161
|
const parsed = ProxyFleetBenchmarksSchema.safeParse(await response.json());
|
|
12715
13162
|
if (parsed.success)
|
|
12716
13163
|
value = parsed.data;
|
|
12717
13164
|
}
|
|
12718
13165
|
} catch {}
|
|
12719
|
-
fleetCache = { value, until: now + (value ? FLEET_TTL_MS : FLEET_FAILURE_TTL_MS) };
|
|
13166
|
+
fleetCache = { key, value, until: now + (value ? FLEET_TTL_MS : FLEET_FAILURE_TTL_MS) };
|
|
12720
13167
|
return value;
|
|
12721
13168
|
}
|
|
12722
|
-
async function localStats(now) {
|
|
12723
|
-
if (localCache && localCache.until > now.getTime())
|
|
13169
|
+
async function localStats(now, requireIngestAnchor = true) {
|
|
13170
|
+
if (localCache?.requireIngestAnchor === requireIngestAnchor && localCache.until > now.getTime())
|
|
12724
13171
|
return localCache.value;
|
|
12725
|
-
const value = await getLocalFleetStats(now);
|
|
12726
|
-
localCache = { value, until: now.getTime() + LOCAL_TTL_MS };
|
|
13172
|
+
const value = await getLocalFleetStats(now, { requireIngestAnchor });
|
|
13173
|
+
localCache = { requireIngestAnchor, value, until: now.getTime() + LOCAL_TTL_MS };
|
|
12727
13174
|
return value;
|
|
12728
13175
|
}
|
|
12729
|
-
async function orgName() {
|
|
12730
|
-
const stored = (await getSwarmConfigs2({ scope: "global", key: "SWARM_ORG_NAME" }))?.[0]?.value?.trim();
|
|
12731
|
-
return stored || process.env.SWARM_ORG_NAME?.trim() || undefined;
|
|
12732
|
-
}
|
|
12733
13176
|
async function buildFleetBenchmarkResponse(now = new Date) {
|
|
13177
|
+
const [name, website, teamSize, useCase] = await Promise.all([
|
|
13178
|
+
globalConfig("SWARM_ORG_NAME"),
|
|
13179
|
+
globalConfig("SWARM_ORG_WEBSITE"),
|
|
13180
|
+
globalConfig("SWARM_ORG_TEAM_SIZE"),
|
|
13181
|
+
globalConfig("SWARM_ORG_USE_CASE")
|
|
13182
|
+
]);
|
|
13183
|
+
const team = _enum2(COMPANY_TEAM_SIZES).safeParse(teamSize);
|
|
13184
|
+
const use = _enum2(COMPANY_USE_CASES).safeParse(useCase);
|
|
13185
|
+
const peerGroup = normalizeOrgWebsite(website) && team.success && use.success ? { teamSize: team.data, useCase: use.data } : null;
|
|
12734
13186
|
const empty = {
|
|
13187
|
+
level: peerGroup ? "full" : "basic",
|
|
13188
|
+
peerGroup: null,
|
|
12735
13189
|
local: null,
|
|
12736
13190
|
minFinishedTasks: FLEET_MIN_FINISHED_TASKS,
|
|
12737
13191
|
generatedAt: null,
|
|
12738
13192
|
comparisons: []
|
|
12739
13193
|
};
|
|
12740
|
-
|
|
12741
|
-
|
|
12742
|
-
if (!await orgName())
|
|
13194
|
+
const telemetryEnabled = isTelemetryEnabled();
|
|
13195
|
+
if (telemetryEnabled && !name)
|
|
12743
13196
|
return { state: "no_org_name", ...empty };
|
|
12744
|
-
const stats = await localStats(now);
|
|
13197
|
+
const stats = await localStats(now, telemetryEnabled);
|
|
12745
13198
|
if (!stats)
|
|
12746
13199
|
return { state: "unavailable", ...empty };
|
|
12747
13200
|
const finishedTasks = stats.tasksCompleted + stats.tasksFailed;
|
|
@@ -12752,14 +13205,23 @@ async function buildFleetBenchmarkResponse(now = new Date) {
|
|
|
12752
13205
|
agents: stats.agents,
|
|
12753
13206
|
bucket: fleetBucketFor(stats.agents)
|
|
12754
13207
|
};
|
|
13208
|
+
if (!telemetryEnabled)
|
|
13209
|
+
return { ...empty, state: "telemetry_off", local };
|
|
12755
13210
|
if (finishedTasks < FLEET_MIN_FINISHED_TASKS)
|
|
12756
13211
|
return { ...empty, state: "not_enough_tasks", local };
|
|
12757
|
-
const fleet = await fleetBenchmarks(now.getTime());
|
|
13212
|
+
const fleet = await fleetBenchmarks(now.getTime(), peerGroup);
|
|
13213
|
+
const responsePeerGroup = peerGroup && fleet?.peer_group ? {
|
|
13214
|
+
teamSize: fleet.peer_group.team_size,
|
|
13215
|
+
useCase: fleet.peer_group.use_case,
|
|
13216
|
+
orgs: fleet.peer_group.orgs
|
|
13217
|
+
} : null;
|
|
12758
13218
|
const comparisons = fleet ? compareWithFleet(stats, fleet, now) : [];
|
|
12759
13219
|
if (!fleet || comparisons.length === 0)
|
|
12760
|
-
return { ...empty, state: "unavailable", local };
|
|
13220
|
+
return { ...empty, state: "unavailable", local, peerGroup: responsePeerGroup };
|
|
12761
13221
|
return {
|
|
13222
|
+
...empty,
|
|
12762
13223
|
state: "ready",
|
|
13224
|
+
peerGroup: responsePeerGroup,
|
|
12763
13225
|
local,
|
|
12764
13226
|
minFinishedTasks: FLEET_MIN_FINISHED_TASKS,
|
|
12765
13227
|
generatedAt: fleet.generatedAt,
|
|
@@ -12886,6 +13348,7 @@ var listTaskFilesRoute = route({
|
|
|
12886
13348
|
method: "get",
|
|
12887
13349
|
path: "/api/fs/tasks/{taskId}/files",
|
|
12888
13350
|
pattern: ["api", "fs", "tasks", null, "files"],
|
|
13351
|
+
session: { taskParam: "taskId" },
|
|
12889
13352
|
summary: "List task file attachments",
|
|
12890
13353
|
tags: ["FS"],
|
|
12891
13354
|
params: taskParams,
|
|
@@ -12901,6 +13364,7 @@ var uploadTaskFileRoute = route({
|
|
|
12901
13364
|
method: "post",
|
|
12902
13365
|
path: "/api/fs/tasks/{taskId}/files",
|
|
12903
13366
|
pattern: ["api", "fs", "tasks", null, "files"],
|
|
13367
|
+
session: { taskParam: "taskId" },
|
|
12904
13368
|
summary: "Upload a binary task file attachment",
|
|
12905
13369
|
description: "Accepts a raw binary request body. Pass the display/path name as the `name` query parameter.",
|
|
12906
13370
|
tags: ["FS"],
|
|
@@ -12920,6 +13384,7 @@ var getTaskFileRoute = route({
|
|
|
12920
13384
|
method: "get",
|
|
12921
13385
|
path: "/api/fs/tasks/{taskId}/files/{attachmentId}",
|
|
12922
13386
|
pattern: ["api", "fs", "tasks", null, "files", null],
|
|
13387
|
+
session: { taskParam: "taskId" },
|
|
12923
13388
|
summary: "Get task file attachment metadata",
|
|
12924
13389
|
tags: ["FS"],
|
|
12925
13390
|
params: attachmentParams,
|
|
@@ -12932,6 +13397,7 @@ var downloadTaskFileRoute = route({
|
|
|
12932
13397
|
method: "get",
|
|
12933
13398
|
path: "/api/fs/tasks/{taskId}/files/{attachmentId}/raw",
|
|
12934
13399
|
pattern: ["api", "fs", "tasks", null, "files", null, "raw"],
|
|
13400
|
+
session: { taskParam: "taskId" },
|
|
12935
13401
|
summary: "Download raw task file bytes",
|
|
12936
13402
|
description: "Streams raw bytes. File content is not secret-scrubbed.",
|
|
12937
13403
|
tags: ["FS"],
|
|
@@ -12948,6 +13414,7 @@ var signedUrlTaskFileRoute = route({
|
|
|
12948
13414
|
method: "get",
|
|
12949
13415
|
path: "/api/fs/tasks/{taskId}/files/{attachmentId}/signed-url",
|
|
12950
13416
|
pattern: ["api", "fs", "tasks", null, "files", null, "signed-url"],
|
|
13417
|
+
session: { taskParam: "taskId" },
|
|
12951
13418
|
summary: "Create a provider signed GET URL for a task file",
|
|
12952
13419
|
tags: ["FS"],
|
|
12953
13420
|
params: attachmentParams,
|
|
@@ -12962,6 +13429,7 @@ var deleteTaskFileRoute = route({
|
|
|
12962
13429
|
method: "delete",
|
|
12963
13430
|
path: "/api/fs/tasks/{taskId}/files/{attachmentId}",
|
|
12964
13431
|
pattern: ["api", "fs", "tasks", null, "files", null],
|
|
13432
|
+
session: { taskParam: "taskId" },
|
|
12965
13433
|
summary: "Delete a task file attachment",
|
|
12966
13434
|
tags: ["FS"],
|
|
12967
13435
|
params: attachmentParams,
|
|
@@ -13010,7 +13478,7 @@ async function handleFs(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
13010
13478
|
const parsed = await downloadTaskFileRoute.parse(req, res, pathSegments, queryParams);
|
|
13011
13479
|
if (!parsed)
|
|
13012
13480
|
return true;
|
|
13013
|
-
const attachment = await findAttachment(parsed.params.taskId, parsed.params.attachmentId, res);
|
|
13481
|
+
const attachment = await findAttachment(req, myAgentId, parsed.params.taskId, parsed.params.attachmentId, res);
|
|
13014
13482
|
if (!attachment)
|
|
13015
13483
|
return true;
|
|
13016
13484
|
return sendDownload(res, attachment);
|
|
@@ -13019,7 +13487,7 @@ async function handleFs(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
13019
13487
|
const parsed = await signedUrlTaskFileRoute.parse(req, res, pathSegments, queryParams);
|
|
13020
13488
|
if (!parsed)
|
|
13021
13489
|
return true;
|
|
13022
|
-
const attachment = await findAttachment(parsed.params.taskId, parsed.params.attachmentId, res);
|
|
13490
|
+
const attachment = await findAttachment(req, myAgentId, parsed.params.taskId, parsed.params.attachmentId, res);
|
|
13023
13491
|
if (!attachment)
|
|
13024
13492
|
return true;
|
|
13025
13493
|
return sendSignedUrl(res, attachment, parsed.query.expiresIn);
|
|
@@ -13028,7 +13496,7 @@ async function handleFs(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
13028
13496
|
const parsed = await getTaskFileRoute.parse(req, res, pathSegments, queryParams);
|
|
13029
13497
|
if (!parsed)
|
|
13030
13498
|
return true;
|
|
13031
|
-
const attachment = await findAttachment(parsed.params.taskId, parsed.params.attachmentId, res);
|
|
13499
|
+
const attachment = await findAttachment(req, myAgentId, parsed.params.taskId, parsed.params.attachmentId, res);
|
|
13032
13500
|
if (!attachment)
|
|
13033
13501
|
return true;
|
|
13034
13502
|
getTaskFileRoute.respond(res, 200, attachment);
|
|
@@ -13067,6 +13535,10 @@ async function handleFs(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
13067
13535
|
return true;
|
|
13068
13536
|
if (!await getTaskById2(parsed.params.taskId))
|
|
13069
13537
|
return notFound(res, "Task not found");
|
|
13538
|
+
if (!await canReadSession2(req, myAgentId, parsed.params.taskId)) {
|
|
13539
|
+
jsonError(res, SESSION_READ_DENIED2, 403);
|
|
13540
|
+
return true;
|
|
13541
|
+
}
|
|
13070
13542
|
listTaskFilesRoute.respond(res, 200, {
|
|
13071
13543
|
attachments: await getTaskAttachments2(parsed.params.taskId)
|
|
13072
13544
|
});
|
|
@@ -13202,11 +13674,15 @@ function backingProviderId(attachment) {
|
|
|
13202
13674
|
return "local-fs";
|
|
13203
13675
|
return null;
|
|
13204
13676
|
}
|
|
13205
|
-
async function findAttachment(taskId, attachmentId, res) {
|
|
13677
|
+
async function findAttachment(req, myAgentId, taskId, attachmentId, res) {
|
|
13206
13678
|
if (!await getTaskById2(taskId)) {
|
|
13207
13679
|
notFound(res, "Task not found");
|
|
13208
13680
|
return null;
|
|
13209
13681
|
}
|
|
13682
|
+
if (!await canReadSession2(req, myAgentId, taskId)) {
|
|
13683
|
+
jsonError(res, SESSION_READ_DENIED2, 403);
|
|
13684
|
+
return null;
|
|
13685
|
+
}
|
|
13210
13686
|
const attachment = (await getTaskAttachments2(taskId)).find((item) => item.id === attachmentId);
|
|
13211
13687
|
if (!attachment) {
|
|
13212
13688
|
notFound(res, "Attachment not found");
|
|
@@ -17846,6 +18322,7 @@ var indexMemory = route({
|
|
|
17846
18322
|
method: "post",
|
|
17847
18323
|
path: "/api/memory/index",
|
|
17848
18324
|
pattern: ["api", "memory", "index"],
|
|
18325
|
+
session: bodyTask("sourceTaskId"),
|
|
17849
18326
|
summary: "Ingest content into memory system (async embedding)",
|
|
17850
18327
|
tags: ["Memory"],
|
|
17851
18328
|
rbac: { permission: "memory.write.consolidated" },
|
|
@@ -18323,6 +18800,7 @@ var rateMemory = route({
|
|
|
18323
18800
|
method: "post",
|
|
18324
18801
|
path: "/api/memory/rate",
|
|
18325
18802
|
pattern: ["api", "memory", "rate"],
|
|
18803
|
+
session: { none: "worker-only: it needs X-Agent-ID, which a user token cannot send" },
|
|
18326
18804
|
summary: "Submit RatingEvents to update memory usefulness posteriors",
|
|
18327
18805
|
tags: ["Memory"],
|
|
18328
18806
|
auth: { apiKey: true, agentId: true },
|
|
@@ -21770,7 +22248,7 @@ var listTasks = route({
|
|
|
21770
22248
|
path: "/api/tasks",
|
|
21771
22249
|
pattern: ["api", "tasks"],
|
|
21772
22250
|
summary: "List tasks with filters",
|
|
21773
|
-
description: "Returns tasks with the full `task` text replaced by a bounded `taskPreview` and completion/integration blobs dropped by default — list views only need the preview. Pass `fields=full` to restore the full `AgentTask`, or `fields=timeline` for the narrow shape the dashboard timeline draws. Fetch a single task in full via `GET /api/tasks/{id}`. `total` (the filtered row count, ignoring limit/offset) is computed only with `includeTotal=true`.",
|
|
22251
|
+
description: "Returns tasks with the full `task` text replaced by a bounded `taskPreview` and completion/integration blobs dropped by default — list views only need the preview. Pass `fields=full` to restore the full `AgentTask`, or `fields=timeline` for the narrow shape the dashboard timeline draws. Every shape gives a user caller only tasks of sessions they can read. Fetch a single task in full via `GET /api/tasks/{id}`. `total` (the filtered row count, ignoring limit/offset) is computed only with `includeTotal=true`.",
|
|
21774
22252
|
tags: ["Tasks"],
|
|
21775
22253
|
query: object2({
|
|
21776
22254
|
status: string2().optional(),
|
|
@@ -21809,6 +22287,7 @@ var createTask = route({
|
|
|
21809
22287
|
method: "post",
|
|
21810
22288
|
path: "/api/tasks",
|
|
21811
22289
|
pattern: ["api", "tasks"],
|
|
22290
|
+
session: bodyTask("parentTaskId"),
|
|
21812
22291
|
summary: "Create a new task",
|
|
21813
22292
|
tags: ["Tasks"],
|
|
21814
22293
|
body: object2({
|
|
@@ -21864,6 +22343,7 @@ var promoteDraftTaskRoute = route({
|
|
|
21864
22343
|
method: "post",
|
|
21865
22344
|
path: "/api/tasks/{id}/promote-draft",
|
|
21866
22345
|
pattern: ["api", "tasks", null, "promote-draft"],
|
|
22346
|
+
session: { taskParam: "id" },
|
|
21867
22347
|
summary: "Promote a draft task out of the pre-dispatch draft state",
|
|
21868
22348
|
description: "Transitions a `draft` task (#1240 — created with attachments still uploading) to its normal dispatch-eligible status: `offered` if it was offered to an agent, `pending` if it has an owning agent (the common case — UI-composer tasks default to Lead), otherwise `unassigned`. Called by the UI composer once its attachment upload batch settles, whether every file uploaded, some failed, or all failed — a draft must never be stranded by an upload error. Idempotent: calling it on a task that already left `draft` returns the current task unchanged rather than erroring, so a retried request is safe.",
|
|
21869
22349
|
tags: ["Tasks"],
|
|
@@ -21879,6 +22359,7 @@ var updateSession = route({
|
|
|
21879
22359
|
method: "put",
|
|
21880
22360
|
path: "/api/tasks/{id}/session",
|
|
21881
22361
|
pattern: ["api", "tasks", null, "session"],
|
|
22362
|
+
session: { taskParam: "id" },
|
|
21882
22363
|
summary: "Update provider session ID and harness metadata for a task",
|
|
21883
22364
|
tags: ["Tasks"],
|
|
21884
22365
|
params: object2({ id: string2() }),
|
|
@@ -21912,6 +22393,7 @@ var cancelTaskRoute = route({
|
|
|
21912
22393
|
method: "post",
|
|
21913
22394
|
path: "/api/tasks/{id}/cancel",
|
|
21914
22395
|
pattern: ["api", "tasks", null, "cancel"],
|
|
22396
|
+
session: { taskParam: "id" },
|
|
21915
22397
|
summary: "Cancel a pending or in-progress task",
|
|
21916
22398
|
tags: ["Tasks"],
|
|
21917
22399
|
params: object2({ id: string2() }),
|
|
@@ -21926,6 +22408,7 @@ var steerTaskRoute = route({
|
|
|
21926
22408
|
method: "post",
|
|
21927
22409
|
path: "/api/tasks/{id}/steer",
|
|
21928
22410
|
pattern: ["api", "tasks", null, "steer"],
|
|
22411
|
+
session: { taskParam: "id" },
|
|
21929
22412
|
summary: "Deliver a steering message to a running task",
|
|
21930
22413
|
tags: ["Tasks"],
|
|
21931
22414
|
params: object2({ id: string2() }),
|
|
@@ -21954,6 +22437,7 @@ var getTaskSteeringMessagesRoute = route({
|
|
|
21954
22437
|
method: "get",
|
|
21955
22438
|
path: "/api/tasks/{id}/steering-messages",
|
|
21956
22439
|
pattern: ["api", "tasks", null, "steering-messages"],
|
|
22440
|
+
session: { taskParam: "id" },
|
|
21957
22441
|
summary: "List steering messages for a task",
|
|
21958
22442
|
tags: ["Tasks"],
|
|
21959
22443
|
params: object2({ id: string2() }),
|
|
@@ -21980,6 +22464,7 @@ var markSteeringDeliveredRoute = route({
|
|
|
21980
22464
|
method: "post",
|
|
21981
22465
|
path: "/api/steering-messages/{id}/delivered",
|
|
21982
22466
|
pattern: ["api", "steering-messages", null, "delivered"],
|
|
22467
|
+
session: { steeringParam: "id" },
|
|
21983
22468
|
summary: "Mark a steering message delivered",
|
|
21984
22469
|
tags: ["Tasks"],
|
|
21985
22470
|
params: object2({ id: string2() }),
|
|
@@ -22003,6 +22488,7 @@ var markSteeringHandledRoute = route({
|
|
|
22003
22488
|
method: "post",
|
|
22004
22489
|
path: "/api/steering-messages/{id}/handled",
|
|
22005
22490
|
pattern: ["api", "steering-messages", null, "handled"],
|
|
22491
|
+
session: { steeringParam: "id" },
|
|
22006
22492
|
summary: "Mark a steering message handled",
|
|
22007
22493
|
description: "Optionally accepts a JSON body `{ note?: string }` — a short acceptance note describing how the steering was incorporated, persisted as `handledNote`.",
|
|
22008
22494
|
tags: ["Tasks"],
|
|
@@ -22025,6 +22511,7 @@ var markSteeringUndeliverableRoute = route({
|
|
|
22025
22511
|
method: "post",
|
|
22026
22512
|
path: "/api/steering-messages/{id}/undeliverable",
|
|
22027
22513
|
pattern: ["api", "steering-messages", null, "undeliverable"],
|
|
22514
|
+
session: { steeringParam: "id" },
|
|
22028
22515
|
summary: "Promote an undeliverable steering message",
|
|
22029
22516
|
tags: ["Tasks"],
|
|
22030
22517
|
params: object2({ id: string2() }),
|
|
@@ -22048,6 +22535,7 @@ var getTask = route({
|
|
|
22048
22535
|
method: "get",
|
|
22049
22536
|
path: "/api/tasks/{id}",
|
|
22050
22537
|
pattern: ["api", "tasks", null],
|
|
22538
|
+
session: { taskParam: "id" },
|
|
22051
22539
|
summary: "Get task details with logs and attachments",
|
|
22052
22540
|
description: "Returns the full `AgentTask` row decorated with `logs` (capped by `logsLimit`) and `attachments` (pointer-based artifacts stored on the task, ordered by `created_at`).",
|
|
22053
22541
|
tags: ["Tasks"],
|
|
@@ -22057,6 +22545,7 @@ var getTask = route({
|
|
|
22057
22545
|
}),
|
|
22058
22546
|
responses: {
|
|
22059
22547
|
200: { description: "Task with logs and attachments", schema: GetTaskResponseSchema },
|
|
22548
|
+
403: { description: "A user caller cannot read the task's session" },
|
|
22060
22549
|
404: { description: "Task not found" }
|
|
22061
22550
|
}
|
|
22062
22551
|
});
|
|
@@ -22064,6 +22553,7 @@ var updateTaskProgressRoute = route({
|
|
|
22064
22553
|
method: "post",
|
|
22065
22554
|
path: "/api/tasks/{id}/progress",
|
|
22066
22555
|
pattern: ["api", "tasks", null, "progress"],
|
|
22556
|
+
session: { taskParam: "id" },
|
|
22067
22557
|
summary: "Update task progress text",
|
|
22068
22558
|
tags: ["Tasks"],
|
|
22069
22559
|
params: object2({ id: string2() }),
|
|
@@ -22081,6 +22571,7 @@ var finishTask = route({
|
|
|
22081
22571
|
method: "post",
|
|
22082
22572
|
path: "/api/tasks/{id}/finish",
|
|
22083
22573
|
pattern: ["api", "tasks", null, "finish"],
|
|
22574
|
+
session: { taskParam: "id" },
|
|
22084
22575
|
summary: "Mark task as completed or failed (runner endpoint)",
|
|
22085
22576
|
tags: ["Tasks"],
|
|
22086
22577
|
params: object2({ id: string2() }),
|
|
@@ -22126,6 +22617,7 @@ var pauseTaskRoute = route({
|
|
|
22126
22617
|
method: "post",
|
|
22127
22618
|
path: "/api/tasks/{id}/pause",
|
|
22128
22619
|
pattern: ["api", "tasks", null, "pause"],
|
|
22620
|
+
session: { taskParam: "id" },
|
|
22129
22621
|
summary: "Pause an in-progress task",
|
|
22130
22622
|
tags: ["Tasks"],
|
|
22131
22623
|
params: object2({ id: string2() }),
|
|
@@ -22140,6 +22632,7 @@ var resumeTaskRoute = route({
|
|
|
22140
22632
|
method: "post",
|
|
22141
22633
|
path: "/api/tasks/{id}/resume",
|
|
22142
22634
|
pattern: ["api", "tasks", null, "resume"],
|
|
22635
|
+
session: { taskParam: "id" },
|
|
22143
22636
|
summary: "Resume a paused task",
|
|
22144
22637
|
tags: ["Tasks"],
|
|
22145
22638
|
params: object2({ id: string2() }),
|
|
@@ -22154,6 +22647,7 @@ var supersedeTaskRoute = route({
|
|
|
22154
22647
|
method: "post",
|
|
22155
22648
|
path: "/api/tasks/{id}/supersede",
|
|
22156
22649
|
pattern: ["api", "tasks", null, "supersede"],
|
|
22650
|
+
session: { taskParam: "id" },
|
|
22157
22651
|
summary: "Supersede an in-progress task (terminate + spawn resume follow-up)",
|
|
22158
22652
|
description: 'Marks the original task `superseded` (terminal) and creates a fresh `taskType="resume"` follow-up so a worker can pick up the work in a new provider session. Workflow-step tasks (those with `workflowRunStepId`) are carved out: the original is marked `failed` with reason `superseded_workflow_task` and no follow-up is created — the workflow engine\'s retry/failure policy applies.',
|
|
22159
22653
|
tags: ["Tasks"],
|
|
@@ -22174,6 +22668,7 @@ var updateTaskVcsRoute = route({
|
|
|
22174
22668
|
method: "patch",
|
|
22175
22669
|
path: "/api/tasks/{id}/vcs",
|
|
22176
22670
|
pattern: ["api", "tasks", null, "vcs"],
|
|
22671
|
+
session: { taskParam: "id" },
|
|
22177
22672
|
summary: "Update VCS (PR/MR) info for a task",
|
|
22178
22673
|
tags: ["Tasks"],
|
|
22179
22674
|
params: object2({ id: string2() }),
|
|
@@ -22194,6 +22689,7 @@ var updateTaskTitleRoute = route({
|
|
|
22194
22689
|
method: "patch",
|
|
22195
22690
|
path: "/api/tasks/{id}/title",
|
|
22196
22691
|
pattern: ["api", "tasks", null, "title"],
|
|
22692
|
+
session: { taskParam: "id" },
|
|
22197
22693
|
summary: "Set or clear a task's display title (session rename)",
|
|
22198
22694
|
description: "Sets a human-facing display title override on a task. The sessions UI only reads this from root tasks (session list items), but titles on child tasks are harmless. Pass `title: null` (or an empty string) to clear the override and fall back to the task prompt.",
|
|
22199
22695
|
tags: ["Tasks"],
|
|
@@ -22210,17 +22706,21 @@ var updateTaskTitleRoute = route({
|
|
|
22210
22706
|
ungated: "mirrors the pre-RBAC PATCH /api/tasks/{id}/vcs sibling route: bearer auth only"
|
|
22211
22707
|
}
|
|
22212
22708
|
});
|
|
22213
|
-
async function
|
|
22709
|
+
async function hasSessionWrite(req, myAgentId, taskId) {
|
|
22710
|
+
const access = await sessionAccessFor2(await resolveSessionActor2(req, myAgentId), taskId);
|
|
22711
|
+
return hasSessionAccess(access, "write");
|
|
22712
|
+
}
|
|
22713
|
+
async function canSteerTask(req, myAgentId, task, sessionAccess = "none") {
|
|
22714
|
+
const auth = getRequestAuth(req);
|
|
22214
22715
|
const resource = {
|
|
22215
22716
|
kind: "task",
|
|
22216
22717
|
taskId: task.id,
|
|
22217
|
-
requestedByUserId: task.requestedByUserId,
|
|
22718
|
+
requestedByUserId: auth?.kind === "user" && sessionAccess === "write" ? auth.userId : task.requestedByUserId,
|
|
22218
22719
|
creatorAgentId: task.creatorAgentId,
|
|
22219
22720
|
agentId: task.agentId
|
|
22220
22721
|
};
|
|
22221
22722
|
let principal;
|
|
22222
22723
|
let verb;
|
|
22223
|
-
const auth = getRequestAuth(req);
|
|
22224
22724
|
if (auth?.kind === "operator") {
|
|
22225
22725
|
principal = { kind: "operator" };
|
|
22226
22726
|
verb = "task.steer.own";
|
|
@@ -22329,6 +22829,7 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
22329
22829
|
if (validated.length > 0)
|
|
22330
22830
|
source = validated;
|
|
22331
22831
|
}
|
|
22832
|
+
const actor = await resolveSessionActor2(req, myAgentId);
|
|
22332
22833
|
const filters = {
|
|
22333
22834
|
status,
|
|
22334
22835
|
agentId: parsed.query.agentId || undefined,
|
|
@@ -22344,7 +22845,8 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
22344
22845
|
requestedByUserIdIsNull: parsed.query.requestedByUserId === "none" || undefined,
|
|
22345
22846
|
orderBy: parsed.query.orderBy,
|
|
22346
22847
|
limit: parsed.query.limit,
|
|
22347
|
-
offset: parsed.query.offset
|
|
22848
|
+
offset: parsed.query.offset,
|
|
22849
|
+
readableBy: actor.kind === "user" ? { userId: actor.userId } : undefined
|
|
22348
22850
|
};
|
|
22349
22851
|
const tasks = parsed.query.fields === "full" ? await getAllTasks2(filters) : parsed.query.fields === "timeline" ? await getAllTasks2(filters, { fields: "timeline" }) : await getAllTasks2(filters, { slim: true });
|
|
22350
22852
|
if (parsed.query.includeTotal === "true") {
|
|
@@ -22402,6 +22904,10 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
22402
22904
|
jsonError(res, `You don't have context for parent task ${parentTask.id}.`, 403);
|
|
22403
22905
|
return true;
|
|
22404
22906
|
}
|
|
22907
|
+
if (parentTask && !await hasSessionWrite(req, myAgentId, parentTask.id)) {
|
|
22908
|
+
jsonError(res, SESSION_WRITE_DENIED2, 403);
|
|
22909
|
+
return true;
|
|
22910
|
+
}
|
|
22405
22911
|
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) {
|
|
22406
22912
|
jsonError(res, "Permission denied: only a lead can bind a task to a chat thread.", 403);
|
|
22407
22913
|
return true;
|
|
@@ -22601,7 +23107,13 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
22601
23107
|
jsonError(res, "Task not found", 404);
|
|
22602
23108
|
return true;
|
|
22603
23109
|
}
|
|
22604
|
-
|
|
23110
|
+
const sessionActor = await resolveSessionActor2(req, myAgentId);
|
|
23111
|
+
const steerAccess = await sessionAccessFor2(sessionActor, task.id);
|
|
23112
|
+
if (!hasSessionAccess(steerAccess, "write")) {
|
|
23113
|
+
jsonError(res, SESSION_WRITE_DENIED2, 403);
|
|
23114
|
+
return true;
|
|
23115
|
+
}
|
|
23116
|
+
if (!await canSteerTask(req, myAgentId, task, steerAccess)) {
|
|
22605
23117
|
jsonError(res, "Forbidden: caller cannot steer this task", 403);
|
|
22606
23118
|
return true;
|
|
22607
23119
|
}
|
|
@@ -22641,6 +23153,10 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
22641
23153
|
jsonError(res, "Task not found", 404);
|
|
22642
23154
|
return true;
|
|
22643
23155
|
}
|
|
23156
|
+
if (!await canReadSession2(req, myAgentId, parsed.params.id)) {
|
|
23157
|
+
jsonError(res, SESSION_READ_DENIED2, 403);
|
|
23158
|
+
return true;
|
|
23159
|
+
}
|
|
22644
23160
|
getTaskSteeringMessagesRoute.respond(res, 200, {
|
|
22645
23161
|
messages: await getSteeringMessagesForTask2(parsed.params.id)
|
|
22646
23162
|
});
|
|
@@ -22659,13 +23175,18 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
22659
23175
|
return true;
|
|
22660
23176
|
}
|
|
22661
23177
|
if (parsed.query.taskId) {
|
|
23178
|
+
if (!await canReadSession2(req, myAgentId, parsed.query.taskId)) {
|
|
23179
|
+
jsonError(res, SESSION_READ_DENIED2, 403);
|
|
23180
|
+
return true;
|
|
23181
|
+
}
|
|
22662
23182
|
const task = await getTaskById2(parsed.query.taskId);
|
|
22663
23183
|
const messages = task?.agentId === myAgentId ? await getPendingSteeringForTask2(parsed.query.taskId) : [];
|
|
22664
23184
|
getPendingSteeringMessagesRoute.respond(res, 200, { messages });
|
|
22665
23185
|
return true;
|
|
22666
23186
|
}
|
|
23187
|
+
const actor = await resolveSessionActor2(req, myAgentId);
|
|
22667
23188
|
getPendingSteeringMessagesRoute.respond(res, 200, {
|
|
22668
|
-
messages: await getPendingSteeringForAgent2(myAgentId)
|
|
23189
|
+
messages: await getPendingSteeringForAgent2(myAgentId, readableByFor2(actor))
|
|
22669
23190
|
});
|
|
22670
23191
|
return true;
|
|
22671
23192
|
}
|
|
@@ -22806,6 +23327,11 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
22806
23327
|
jsonError(res, "Task not found", 404);
|
|
22807
23328
|
return true;
|
|
22808
23329
|
}
|
|
23330
|
+
const readAccess = await sessionAccessFor2(await resolveSessionActor2(req, myAgentId), task.id);
|
|
23331
|
+
if (!hasSessionAccess(readAccess, "read")) {
|
|
23332
|
+
jsonError(res, "You don't have access to this session. Ask the owner to share it.", 403);
|
|
23333
|
+
return true;
|
|
23334
|
+
}
|
|
22809
23335
|
const logs = await getLogsByTaskId2(parsed.params.id, parsed.query.logsLimit ?? 200);
|
|
22810
23336
|
const attachments = await getTaskAttachments2(parsed.params.id);
|
|
22811
23337
|
getTask.respond(res, 200, {
|
|
@@ -22957,7 +23483,7 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
22957
23483
|
jsonError(res, "Missing X-Agent-ID header", 400);
|
|
22958
23484
|
return true;
|
|
22959
23485
|
}
|
|
22960
|
-
const pausedTasks = await getPausedTasksForAgent2(myAgentId);
|
|
23486
|
+
const pausedTasks = await getPausedTasksForAgent2(myAgentId, readableByFor2(await resolveSessionActor2(req, myAgentId)));
|
|
22961
23487
|
const tasks = await Promise.all(pausedTasks.map(async (task) => {
|
|
22962
23488
|
const requestedBy = await buildTaskRequestedBy(task);
|
|
22963
23489
|
return {
|
|
@@ -23112,7 +23638,7 @@ async function handleTasks(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
23112
23638
|
return true;
|
|
23113
23639
|
}
|
|
23114
23640
|
if (task.workflowRunStepId != null) {
|
|
23115
|
-
const failed = await failTask2(parsed.params.id,
|
|
23641
|
+
const failed = await failTask2(parsed.params.id, SUPERSEDED_WORKFLOW_TASK_REASON);
|
|
23116
23642
|
ensure({
|
|
23117
23643
|
id: "task.workflow_step_failed_on_supersede",
|
|
23118
23644
|
flow: "task",
|
|
@@ -26820,6 +27346,7 @@ ${templateBody}` : templateBody;
|
|
|
26820
27346
|
|
|
26821
27347
|
// src/http/realtime.ts
|
|
26822
27348
|
init_zod();
|
|
27349
|
+
init_users();
|
|
26823
27350
|
init_rbac();
|
|
26824
27351
|
|
|
26825
27352
|
// src/realtime/browser.generated.txt
|
|
@@ -26833,14 +27360,14 @@ var realtimeTicketRoute = route({
|
|
|
26833
27360
|
pattern: ["api", "realtime", "ticket"],
|
|
26834
27361
|
summary: "Issue a one-shot dashboard realtime ticket",
|
|
26835
27362
|
tags: ["Rooms"],
|
|
26836
|
-
|
|
27363
|
+
description: "The ticket authorizes one dashboard socket for presence rooms: Comb drives (`comb.presence`) and sessions (`session.presence`). An operator-key tab's picked user (`X-Swarm-User-Id`) rides on the ticket so session presence can check that user's access.",
|
|
27364
|
+
rbac: { permission: "session.presence", alsoAdmits: ["comb.presence"] },
|
|
26837
27365
|
responses: {
|
|
26838
27366
|
200: {
|
|
26839
27367
|
description: "One-shot realtime ticket",
|
|
26840
27368
|
schema: object2({ ticket: string2(), expiresAt: number2().int() })
|
|
26841
27369
|
},
|
|
26842
|
-
403: { description: "
|
|
26843
|
-
404: { description: "Comb is not enabled" }
|
|
27370
|
+
403: { description: "Presence is not allowed" }
|
|
26844
27371
|
}
|
|
26845
27372
|
});
|
|
26846
27373
|
route({
|
|
@@ -26857,29 +27384,28 @@ route({
|
|
|
26857
27384
|
async function handleRealtimeTicket(req, res, pathSegments, queryParams) {
|
|
26858
27385
|
if (!realtimeTicketRoute.match(req.method, pathSegments))
|
|
26859
27386
|
return false;
|
|
26860
|
-
if (!getCombConfig().enabled) {
|
|
26861
|
-
jsonError(res, "Comb is not enabled", 404);
|
|
26862
|
-
return true;
|
|
26863
|
-
}
|
|
26864
27387
|
const parsed = await realtimeTicketRoute.parse(req, res, pathSegments, queryParams);
|
|
26865
27388
|
if (!parsed)
|
|
26866
27389
|
return true;
|
|
26867
27390
|
const auth = getRequestAuth(req);
|
|
26868
27391
|
if (!auth || auth.kind === "agent" || auth.kind === "guest" || auth.kind === "controller") {
|
|
26869
|
-
jsonError(res, "
|
|
27392
|
+
jsonError(res, "Presence requires dashboard authentication", 403);
|
|
26870
27393
|
return true;
|
|
26871
27394
|
}
|
|
26872
27395
|
const principal = auth.kind === "operator" ? { kind: "operator" } : { kind: "user", userId: auth.userId };
|
|
26873
|
-
|
|
26874
|
-
|
|
26875
|
-
|
|
26876
|
-
resource: { kind: "none" },
|
|
26877
|
-
source: "http"
|
|
26878
|
-
}).allow) {
|
|
26879
|
-
jsonError(res, "Comb presence requires the comb.presence permission", 403);
|
|
27396
|
+
const allowed = (verb) => can({ principal, verb, resource: { kind: "none" }, source: "http" }).allow;
|
|
27397
|
+
if (!allowed("comb.presence") && !allowed("session.presence")) {
|
|
27398
|
+
jsonError(res, "Presence requires the comb.presence or session.presence permission", 403);
|
|
26880
27399
|
return true;
|
|
26881
27400
|
}
|
|
26882
|
-
|
|
27401
|
+
let dashboardUserId;
|
|
27402
|
+
if (auth.kind === "operator" && !auth.page) {
|
|
27403
|
+
const picked = dashboardUserIdHeader(req);
|
|
27404
|
+
const user = picked ? await findUserById(picked) : null;
|
|
27405
|
+
if (user?.status === "active")
|
|
27406
|
+
dashboardUserId = user.id;
|
|
27407
|
+
}
|
|
27408
|
+
realtimeTicketRoute.respond(res, 200, issueRealtimeTicket(dashboardUserId ? { ...auth, dashboardUserId } : auth));
|
|
26883
27409
|
return true;
|
|
26884
27410
|
}
|
|
26885
27411
|
async function handleRealtimeAsset(req, res) {
|
|
@@ -30768,6 +31294,7 @@ var agentTaskRoute = route({
|
|
|
30768
31294
|
method: "post",
|
|
30769
31295
|
path: "/api/internal/script-runs/{runId}/agent-task",
|
|
30770
31296
|
pattern: ["api", "internal", "script-runs", null, "agent-task"],
|
|
31297
|
+
session: bodyTask("parentTaskId"),
|
|
30771
31298
|
operationId: "script_runs_internal_agent_task",
|
|
30772
31299
|
summary: "Create or wait for a script workflow agent task step",
|
|
30773
31300
|
tags: ["Script Runs"],
|
|
@@ -31247,6 +31774,9 @@ var upsertRoute2 = route({
|
|
|
31247
31774
|
method: "post",
|
|
31248
31775
|
path: "/api/scripts/upsert",
|
|
31249
31776
|
pattern: ["api", "scripts", "upsert"],
|
|
31777
|
+
auth: {
|
|
31778
|
+
userNamesAgent: "dashboard script playground: a user runs or saves a script in a chosen agent's scope"
|
|
31779
|
+
},
|
|
31250
31780
|
operationId: "scripts_upsert",
|
|
31251
31781
|
summary: "Create or update a reusable script",
|
|
31252
31782
|
description: "Explicit script upserts run a TypeScript typecheck before writing.",
|
|
@@ -31271,6 +31801,9 @@ var runRoute = route({
|
|
|
31271
31801
|
method: "post",
|
|
31272
31802
|
path: "/api/scripts/run",
|
|
31273
31803
|
pattern: ["api", "scripts", "run"],
|
|
31804
|
+
auth: {
|
|
31805
|
+
userNamesAgent: "dashboard script playground: a user runs or saves a script in a chosen agent's scope"
|
|
31806
|
+
},
|
|
31274
31807
|
operationId: "scripts_run",
|
|
31275
31808
|
summary: "Run a reusable or inline script",
|
|
31276
31809
|
description: "Inline source skips typecheck and is auto-saved as a scratch script only on success.",
|
|
@@ -32241,6 +32774,7 @@ var createSessionLogsRoute = route({
|
|
|
32241
32774
|
method: "post",
|
|
32242
32775
|
path: "/api/session-logs",
|
|
32243
32776
|
pattern: ["api", "session-logs"],
|
|
32777
|
+
session: bodyTask("taskId"),
|
|
32244
32778
|
summary: "Store session logs",
|
|
32245
32779
|
tags: ["Session Data"],
|
|
32246
32780
|
body: object2({
|
|
@@ -32262,6 +32796,7 @@ var getSessionLogsByTask = route({
|
|
|
32262
32796
|
method: "get",
|
|
32263
32797
|
path: "/api/tasks/{taskId}/session-logs",
|
|
32264
32798
|
pattern: ["api", "tasks", null, "session-logs"],
|
|
32799
|
+
session: { taskParam: "taskId" },
|
|
32265
32800
|
summary: "Get session logs for a task",
|
|
32266
32801
|
tags: ["Session Data"],
|
|
32267
32802
|
params: object2({ taskId: string2() }),
|
|
@@ -32280,6 +32815,7 @@ var createSessionCostRoute = route({
|
|
|
32280
32815
|
method: "post",
|
|
32281
32816
|
path: "/api/session-costs",
|
|
32282
32817
|
pattern: ["api", "session-costs"],
|
|
32818
|
+
session: bodyTask("taskId"),
|
|
32283
32819
|
summary: "Store session cost record",
|
|
32284
32820
|
tags: ["Session Data"],
|
|
32285
32821
|
body: object2({
|
|
@@ -32378,7 +32914,7 @@ var listSessionCosts = route({
|
|
|
32378
32914
|
}
|
|
32379
32915
|
}
|
|
32380
32916
|
});
|
|
32381
|
-
async function handleSessionData(req, res, pathSegments, queryParams,
|
|
32917
|
+
async function handleSessionData(req, res, pathSegments, queryParams, myAgentId) {
|
|
32382
32918
|
if (createSessionLogsRoute.match(req.method, pathSegments)) {
|
|
32383
32919
|
const parsed = await createSessionLogsRoute.parse(req, res, pathSegments, queryParams);
|
|
32384
32920
|
if (!parsed)
|
|
@@ -32417,6 +32953,10 @@ async function handleSessionData(req, res, pathSegments, queryParams, _myAgentId
|
|
|
32417
32953
|
jsonError(res, "Task not found", 404);
|
|
32418
32954
|
return true;
|
|
32419
32955
|
}
|
|
32956
|
+
if (!await canReadSession2(req, myAgentId, task.id)) {
|
|
32957
|
+
jsonError(res, SESSION_READ_DENIED2, 403);
|
|
32958
|
+
return true;
|
|
32959
|
+
}
|
|
32420
32960
|
const logs = await getSessionLogsByTaskId2(parsed.params.taskId, parsed.query?.limit);
|
|
32421
32961
|
getSessionLogsByTask.respond(res, 200, { logs });
|
|
32422
32962
|
return true;
|
|
@@ -32563,6 +33103,8 @@ init_zod();
|
|
|
32563
33103
|
init_db3();
|
|
32564
33104
|
init_steering();
|
|
32565
33105
|
init_users();
|
|
33106
|
+
init_rbac();
|
|
33107
|
+
init_bus();
|
|
32566
33108
|
init_types();
|
|
32567
33109
|
init_request_auth_context();
|
|
32568
33110
|
init_runtime_pools_flag();
|
|
@@ -32611,6 +33153,9 @@ var mintSessionTokenRoute = route({
|
|
|
32611
33153
|
method: "post",
|
|
32612
33154
|
path: "/api/sessions/tokens",
|
|
32613
33155
|
pattern: ["api", "sessions", "tokens"],
|
|
33156
|
+
session: {
|
|
33157
|
+
none: "user tokens cannot mint: the handler admits only the operator key or the task's own runtime"
|
|
33158
|
+
},
|
|
32614
33159
|
summary: "Mint an ephemeral task or runtime token",
|
|
32615
33160
|
description: "Operators can mint task or runtime tokens. Runtime tokens can mint task tokens for their own assigned tasks. Child tokens expire with their parent runtime.",
|
|
32616
33161
|
tags: ["Sessions"],
|
|
@@ -32673,7 +33218,8 @@ var listSessions = route({
|
|
|
32673
33218
|
q: string2().optional(),
|
|
32674
33219
|
requestedByUserId: string2().min(1).optional(),
|
|
32675
33220
|
contextKeyPrefix: string2().min(1).optional(),
|
|
32676
|
-
fields: _enum2(["full", "slim"]).optional()
|
|
33221
|
+
fields: _enum2(["full", "slim"]).optional(),
|
|
33222
|
+
scope: _enum2(["mine", "shared"]).optional()
|
|
32677
33223
|
}),
|
|
32678
33224
|
responses: {
|
|
32679
33225
|
200: {
|
|
@@ -32693,23 +33239,68 @@ var getSession = route({
|
|
|
32693
33239
|
method: "get",
|
|
32694
33240
|
path: "/api/sessions/{rootTaskId}",
|
|
32695
33241
|
pattern: ["api", "sessions", null],
|
|
33242
|
+
session: { taskParam: "rootTaskId" },
|
|
32696
33243
|
summary: "Get a session — root task + the entire descendant chain",
|
|
32697
33244
|
tags: ["Sessions"],
|
|
32698
33245
|
params: object2({ rootTaskId: string2() }),
|
|
32699
33246
|
responses: {
|
|
32700
33247
|
200: {
|
|
32701
|
-
description: "Root task + chain (ordered by createdAt)",
|
|
33248
|
+
description: "Root task + chain (ordered by createdAt), plus the caller's `access` and the session `owner`",
|
|
32702
33249
|
schema: object2({
|
|
32703
33250
|
root: TaskWithSteeringSchema,
|
|
32704
|
-
chain: array2(TaskWithSteeringSchema)
|
|
33251
|
+
chain: array2(TaskWithSteeringSchema),
|
|
33252
|
+
access: SessionAccessSchema,
|
|
33253
|
+
owner: SessionOwnerSchema.nullable()
|
|
32705
33254
|
})
|
|
32706
33255
|
},
|
|
32707
33256
|
401: { description: "Unauthorized" },
|
|
33257
|
+
403: { description: "The caller has no access to this session" },
|
|
33258
|
+
404: { description: "Root task not found" }
|
|
33259
|
+
},
|
|
33260
|
+
auth: { apiKey: true }
|
|
33261
|
+
});
|
|
33262
|
+
var getSessionShares = route({
|
|
33263
|
+
method: "get",
|
|
33264
|
+
path: "/api/sessions/{rootTaskId}/shares",
|
|
33265
|
+
pattern: ["api", "sessions", null, "shares"],
|
|
33266
|
+
session: { taskParam: "rootTaskId" },
|
|
33267
|
+
summary: "List a session's active shares",
|
|
33268
|
+
description: "Active shares only. `everyone` is the access every user has; `users` are per-person shares. A session with no shares is private to its owner.",
|
|
33269
|
+
tags: ["Sessions"],
|
|
33270
|
+
params: object2({ rootTaskId: string2() }),
|
|
33271
|
+
responses: {
|
|
33272
|
+
200: { description: "Active shares", schema: SessionSharesSchema },
|
|
33273
|
+
401: { description: "Unauthorized" },
|
|
33274
|
+
403: { description: "The caller has no access to this session" },
|
|
32708
33275
|
404: { description: "Root task not found" }
|
|
32709
33276
|
},
|
|
32710
33277
|
auth: { apiKey: true }
|
|
32711
33278
|
});
|
|
32712
|
-
|
|
33279
|
+
var putSessionShares = route({
|
|
33280
|
+
method: "put",
|
|
33281
|
+
path: "/api/sessions/{rootTaskId}/shares",
|
|
33282
|
+
pattern: ["api", "sessions", null, "shares"],
|
|
33283
|
+
session: { taskParam: "rootTaskId" },
|
|
33284
|
+
summary: "Set who can view or edit a session",
|
|
33285
|
+
description: "Owner only. The body is the full desired state: users left out lose access. Changed targets have their active share revoked and a new one inserted in one transaction; shares are never deleted.",
|
|
33286
|
+
tags: ["Sessions"],
|
|
33287
|
+
params: object2({ rootTaskId: string2() }),
|
|
33288
|
+
body: SessionSharesUpdateSchema,
|
|
33289
|
+
responses: {
|
|
33290
|
+
200: { description: "Active shares after the change", schema: SessionSharesSchema },
|
|
33291
|
+
400: { description: "Validation error or unknown user" },
|
|
33292
|
+
401: { description: "Unauthorized" },
|
|
33293
|
+
403: { description: "Only the session owner can share it" },
|
|
33294
|
+
404: { description: "Root task not found" },
|
|
33295
|
+
409: { description: "The session has no owner, so it is already open to everyone" }
|
|
33296
|
+
},
|
|
33297
|
+
auth: { apiKey: true },
|
|
33298
|
+
rbac: { permission: "session.share" }
|
|
33299
|
+
});
|
|
33300
|
+
async function accessForRoot(actor, root) {
|
|
33301
|
+
return actor.kind === "bypass" ? "owner" : sessionAccessForRoot(actor.userId, root);
|
|
33302
|
+
}
|
|
33303
|
+
async function handleSessions(req, res, pathSegments, queryParams, myAgentId) {
|
|
32713
33304
|
if (mintSessionTokenRoute.match(req.method, pathSegments)) {
|
|
32714
33305
|
const auth = getRequestAuth(req);
|
|
32715
33306
|
if (auth?.kind !== "operator" && !(auth?.kind === "agent" && auth.runtimeId)) {
|
|
@@ -32786,21 +33377,30 @@ async function handleSessions(req, res, pathSegments, queryParams) {
|
|
|
32786
33377
|
if (!parsed)
|
|
32787
33378
|
return true;
|
|
32788
33379
|
const sources = parsed.query.source ? parsed.query.source.split(",").map((s) => s.trim()).filter(Boolean) : undefined;
|
|
32789
|
-
const
|
|
32790
|
-
|
|
32791
|
-
|
|
33380
|
+
const actor = await resolveSessionActor2(req, myAgentId);
|
|
33381
|
+
if (actor.kind === "bypass" && parsed.query.scope) {
|
|
33382
|
+
listSessions.respond(res, 200, {
|
|
33383
|
+
sessions: [],
|
|
33384
|
+
total: 0,
|
|
33385
|
+
limit: parsed.query.limit ?? 25,
|
|
33386
|
+
offset: parsed.query.offset ?? 0
|
|
33387
|
+
});
|
|
33388
|
+
return true;
|
|
33389
|
+
}
|
|
33390
|
+
const filterOpts = {
|
|
32792
33391
|
source: sources,
|
|
32793
33392
|
q: parsed.query.q,
|
|
32794
33393
|
requestedByUserId: parsed.query.requestedByUserId,
|
|
32795
|
-
contextKeyPrefix: parsed.query.contextKeyPrefix
|
|
33394
|
+
contextKeyPrefix: parsed.query.contextKeyPrefix,
|
|
33395
|
+
...actor.kind === "user" ? { viewer: { userId: actor.userId, scope: parsed.query.scope ?? "all" } } : {}
|
|
33396
|
+
};
|
|
33397
|
+
const baseOpts = {
|
|
33398
|
+
...filterOpts,
|
|
33399
|
+
limit: parsed.query.limit,
|
|
33400
|
+
offset: parsed.query.offset
|
|
32796
33401
|
};
|
|
32797
33402
|
const sessions = parsed.query.fields === "full" ? await listRecentSessions2(baseOpts) : await listRecentSessions2({ ...baseOpts, slim: true });
|
|
32798
|
-
const total = await countSessions2(
|
|
32799
|
-
source: sources,
|
|
32800
|
-
q: parsed.query.q,
|
|
32801
|
-
requestedByUserId: parsed.query.requestedByUserId,
|
|
32802
|
-
contextKeyPrefix: parsed.query.contextKeyPrefix
|
|
32803
|
-
});
|
|
33403
|
+
const total = await countSessions2(filterOpts);
|
|
32804
33404
|
listSessions.respond(res, 200, {
|
|
32805
33405
|
sessions,
|
|
32806
33406
|
total,
|
|
@@ -32818,6 +33418,20 @@ async function handleSessions(req, res, pathSegments, queryParams) {
|
|
|
32818
33418
|
jsonError(res, "Root task not found", 404);
|
|
32819
33419
|
return true;
|
|
32820
33420
|
}
|
|
33421
|
+
const sessionRoot = await getSessionRoot(root.id) ?? {
|
|
33422
|
+
id: root.id,
|
|
33423
|
+
requestedByUserId: root.requestedByUserId ?? null
|
|
33424
|
+
};
|
|
33425
|
+
const access = await accessForRoot(await resolveSessionActor2(req, myAgentId), sessionRoot);
|
|
33426
|
+
if (access === "none") {
|
|
33427
|
+
jsonError(res, "You don't have access to this session. Ask the owner to share it.", 403);
|
|
33428
|
+
return true;
|
|
33429
|
+
}
|
|
33430
|
+
const ownerUser = sessionRoot.requestedByUserId ? await findUserById(sessionRoot.requestedByUserId) : null;
|
|
33431
|
+
const owner = sessionRoot.requestedByUserId ? {
|
|
33432
|
+
id: sessionRoot.requestedByUserId,
|
|
33433
|
+
name: ownerUser?.name ?? sessionRoot.requestedByUserId
|
|
33434
|
+
} : null;
|
|
32821
33435
|
const chain = await getRootTaskChain2(parsed.params.rootTaskId);
|
|
32822
33436
|
const tasks = [root, ...chain];
|
|
32823
33437
|
const [steering, citations] = await Promise.all([
|
|
@@ -32829,7 +33443,69 @@ async function handleSessions(req, res, pathSegments, queryParams) {
|
|
|
32829
33443
|
...steering.get(task.id),
|
|
32830
33444
|
citations: citations.get(task.id)
|
|
32831
33445
|
});
|
|
32832
|
-
getSession.respond(res, 200, {
|
|
33446
|
+
getSession.respond(res, 200, {
|
|
33447
|
+
root: decorate(root),
|
|
33448
|
+
chain: chain.map(decorate),
|
|
33449
|
+
access,
|
|
33450
|
+
owner
|
|
33451
|
+
});
|
|
33452
|
+
return true;
|
|
33453
|
+
}
|
|
33454
|
+
if (getSessionShares.match(req.method, pathSegments)) {
|
|
33455
|
+
const parsed = await getSessionShares.parse(req, res, pathSegments, queryParams);
|
|
33456
|
+
if (!parsed)
|
|
33457
|
+
return true;
|
|
33458
|
+
const root = await getSessionRoot(parsed.params.rootTaskId);
|
|
33459
|
+
if (!root) {
|
|
33460
|
+
jsonError(res, "Root task not found", 404);
|
|
33461
|
+
return true;
|
|
33462
|
+
}
|
|
33463
|
+
const access = await accessForRoot(await resolveSessionActor2(req, myAgentId), root);
|
|
33464
|
+
if (access === "none") {
|
|
33465
|
+
jsonError(res, "You don't have access to this session. Ask the owner to share it.", 403);
|
|
33466
|
+
return true;
|
|
33467
|
+
}
|
|
33468
|
+
getSessionShares.respond(res, 200, toSessionSharesState(await listActiveSessionShares(root.id)));
|
|
33469
|
+
return true;
|
|
33470
|
+
}
|
|
33471
|
+
if (putSessionShares.match(req.method, pathSegments)) {
|
|
33472
|
+
const parsed = await putSessionShares.parse(req, res, pathSegments, queryParams);
|
|
33473
|
+
if (!parsed)
|
|
33474
|
+
return true;
|
|
33475
|
+
const auth = getRequestAuth(req);
|
|
33476
|
+
const actor = await resolveSessionActor2(req, myAgentId);
|
|
33477
|
+
const principal = auth?.kind === "user" ? { kind: "user", userId: auth.userId } : auth?.kind === "operator" && !myAgentId ? { kind: "operator" } : null;
|
|
33478
|
+
if (!principal || !can({ principal, verb: "session.share", resource: { kind: "none" }, source: "http" }).allow) {
|
|
33479
|
+
jsonError(res, "Only the session owner can share it", 403);
|
|
33480
|
+
return true;
|
|
33481
|
+
}
|
|
33482
|
+
const root = await getSessionRoot(parsed.params.rootTaskId);
|
|
33483
|
+
if (!root) {
|
|
33484
|
+
jsonError(res, "Root task not found", 404);
|
|
33485
|
+
return true;
|
|
33486
|
+
}
|
|
33487
|
+
if (!root.requestedByUserId) {
|
|
33488
|
+
jsonError(res, "This session has no owner, so it is already open to everyone", 409);
|
|
33489
|
+
return true;
|
|
33490
|
+
}
|
|
33491
|
+
if (actor.kind === "user" && actor.userId !== root.requestedByUserId) {
|
|
33492
|
+
jsonError(res, "Only the session owner can share it", 403);
|
|
33493
|
+
return true;
|
|
33494
|
+
}
|
|
33495
|
+
const userIds = [...new Set(parsed.body.users.map((user) => user.userId))];
|
|
33496
|
+
if (userIds.length !== parsed.body.users.length) {
|
|
33497
|
+
jsonError(res, "Each user may appear only once", 400);
|
|
33498
|
+
return true;
|
|
33499
|
+
}
|
|
33500
|
+
for (const userId of userIds) {
|
|
33501
|
+
if (!await findUserById(userId)) {
|
|
33502
|
+
jsonError(res, `Unknown user ${userId}`, 400);
|
|
33503
|
+
return true;
|
|
33504
|
+
}
|
|
33505
|
+
}
|
|
33506
|
+
const state = await setSessionShares(root, parsed.body, actor.kind === "user" ? actor.userId : null);
|
|
33507
|
+
realtimeBus.publish(sessionAccessTopic(root.id), null);
|
|
33508
|
+
putSessionShares.respond(res, 200, state);
|
|
32833
33509
|
return true;
|
|
32834
33510
|
}
|
|
32835
33511
|
return false;
|
|
@@ -34231,11 +34907,12 @@ async function handleStats(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
34231
34907
|
return true;
|
|
34232
34908
|
const limit = parsed.query.limit ?? 100;
|
|
34233
34909
|
const agentId = parsed.query.agentId;
|
|
34910
|
+
const readableBy = readableByFor2(await resolveSessionActor2(req, myAgentId));
|
|
34234
34911
|
let logs = [];
|
|
34235
34912
|
if (agentId) {
|
|
34236
|
-
logs = (await getLogsByAgentId2(agentId)).slice(0, limit);
|
|
34913
|
+
logs = (await getLogsByAgentId2(agentId, readableBy)).slice(0, limit);
|
|
34237
34914
|
} else {
|
|
34238
|
-
logs = await getAllLogs2(limit);
|
|
34915
|
+
logs = await getAllLogs2(limit, readableBy);
|
|
34239
34916
|
}
|
|
34240
34917
|
listLogs.respond(res, 200, { logs });
|
|
34241
34918
|
return true;
|
|
@@ -34291,7 +34968,7 @@ async function handleStats(req, res, pathSegments, queryParams, myAgentId) {
|
|
|
34291
34968
|
return true;
|
|
34292
34969
|
}
|
|
34293
34970
|
if (getConcurrentContextRoute.match(req.method, pathSegments)) {
|
|
34294
|
-
const context = await getConcurrentContext2();
|
|
34971
|
+
const context = await getConcurrentContext2(readableByFor2(await resolveSessionActor2(req, myAgentId)));
|
|
34295
34972
|
getConcurrentContextRoute.respond(res, 200, context);
|
|
34296
34973
|
return true;
|
|
34297
34974
|
}
|
|
@@ -34354,9 +35031,8 @@ function uiEventEmitter(event) {
|
|
|
34354
35031
|
return relayable.get(event)?.source;
|
|
34355
35032
|
}
|
|
34356
35033
|
function resolveSpec(spec) {
|
|
34357
|
-
|
|
34358
|
-
|
|
34359
|
-
return { ...file.definitions[spec.ref], ...spec };
|
|
35034
|
+
const resolved = spec.ref ? { ...file.definitions[spec.ref], ...spec } : spec;
|
|
35035
|
+
return resolved.items ? { ...resolved, items: resolveSpec(resolved.items) } : resolved;
|
|
34360
35036
|
}
|
|
34361
35037
|
function typeMatches(type, value) {
|
|
34362
35038
|
switch (type) {
|
|
@@ -34401,6 +35077,12 @@ function validateUiEvent(event, properties) {
|
|
|
34401
35077
|
if (spec.enum && !spec.enum.includes(value)) {
|
|
34402
35078
|
problems.push(`"${key}" must be one of ${spec.enum.join(", ")}`);
|
|
34403
35079
|
}
|
|
35080
|
+
if (Array.isArray(value) && spec.items?.enum) {
|
|
35081
|
+
const allowed = spec.items.enum;
|
|
35082
|
+
if (value.some((item) => !allowed.includes(item))) {
|
|
35083
|
+
problems.push(`"${key}" items must be one of ${allowed.join(", ")}`);
|
|
35084
|
+
}
|
|
35085
|
+
}
|
|
34404
35086
|
if (typeof spec.minimum === "number" && typeof value === "number" && value < spec.minimum) {
|
|
34405
35087
|
problems.push(`"${key}" must be at least ${spec.minimum}`);
|
|
34406
35088
|
}
|
|
@@ -34441,7 +35123,7 @@ var enableTelemetry = route({
|
|
|
34441
35123
|
summary: "Turn telemetry on",
|
|
34442
35124
|
description: "Stores `ANONYMIZED_TELEMETRY=true` and applies it to the running server: the install ID is minted if needed and `telemetry.enabled` is sent once, so no restart is needed.",
|
|
34443
35125
|
tags: ["Telemetry"],
|
|
34444
|
-
body: object2({ via: _enum2(["banner", "settings"]).default("banner") }),
|
|
35126
|
+
body: object2({ via: _enum2(["banner", "settings", "benchmark"]).default("banner") }),
|
|
34445
35127
|
responses: {
|
|
34446
35128
|
200: {
|
|
34447
35129
|
description: "Telemetry is on",
|
|
@@ -35392,7 +36074,7 @@ var createUserRoute = route({
|
|
|
35392
36074
|
tags: ["Users"],
|
|
35393
36075
|
body: object2({
|
|
35394
36076
|
name: string2().min(1),
|
|
35395
|
-
email: string2().optional(),
|
|
36077
|
+
email: string2().email("Email must be a valid email address").optional(),
|
|
35396
36078
|
role: string2().optional(),
|
|
35397
36079
|
notes: string2().optional(),
|
|
35398
36080
|
emailAliases: array2(string2()).optional(),
|
|
@@ -35487,7 +36169,7 @@ var updateUserRoute = route({
|
|
|
35487
36169
|
params: object2({ id: string2() }),
|
|
35488
36170
|
body: object2({
|
|
35489
36171
|
name: string2().min(1).optional(),
|
|
35490
|
-
email: string2().optional(),
|
|
36172
|
+
email: string2().email("Email must be a valid email address").nullable().optional(),
|
|
35491
36173
|
role: string2().optional(),
|
|
35492
36174
|
notes: string2().optional(),
|
|
35493
36175
|
emailAliases: array2(string2()).optional(),
|
|
@@ -38362,10 +39044,11 @@ var httpServer = createHttpServer(async (req, res) => {
|
|
|
38362
39044
|
() => handleExtensions(req, res, pathSegments, queryParams, myAgentId),
|
|
38363
39045
|
() => handleFavorites(req, res, pathSegments, queryParams, myAgentId),
|
|
38364
39046
|
() => handleUsers(req, res, pathSegments, queryParams),
|
|
38365
|
-
() => handleSessions(req, res, pathSegments, queryParams),
|
|
39047
|
+
() => handleSessions(req, res, pathSegments, queryParams, myAgentId),
|
|
38366
39048
|
() => handleInboxState(req, res, pathSegments, queryParams),
|
|
38367
39049
|
() => handleTelemetryUi(req, res, pathSegments, queryParams, myAgentId),
|
|
38368
39050
|
() => handleUsedBy(req, res, pathSegments, queryParams, myAgentId),
|
|
39051
|
+
() => handleRanking(req, res, pathSegments, queryParams),
|
|
38369
39052
|
() => handleFleetBenchmark(req, res, pathSegments, queryParams),
|
|
38370
39053
|
() => handleTaskTemplates(req, res, pathSegments, queryParams),
|
|
38371
39054
|
() => handleMcp(req, res, transports, transportActivity, mcpSessionAgents),
|
|
@@ -38397,7 +39080,7 @@ var httpServer = createHttpServer(async (req, res) => {
|
|
|
38397
39080
|
});
|
|
38398
39081
|
});
|
|
38399
39082
|
var detachRealtime = attachRealtimeTransport(httpServer);
|
|
38400
|
-
var { attachControllerTransport } = await import("./controllers-connect-
|
|
39083
|
+
var { attachControllerTransport } = await import("./controllers-connect-bg33ae53.js");
|
|
38401
39084
|
var detachControllers = attachControllerTransport(httpServer);
|
|
38402
39085
|
var stopWorkerNudges = startWorkerNudgeListener();
|
|
38403
39086
|
var stopRoomDeletion = realtimeBus.subscribe("room:namespace-deleted", (namespace) => {
|
|
@@ -38426,7 +39109,7 @@ async function shutdown() {
|
|
|
38426
39109
|
sessionsProcessed: getServerSessionsProcessed()
|
|
38427
39110
|
});
|
|
38428
39111
|
if (hasCapability("scheduling")) {
|
|
38429
|
-
const { stopScheduler } = await import("./index-
|
|
39112
|
+
const { stopScheduler } = await import("./index-h2ek08d6.js");
|
|
38430
39113
|
stopScheduler();
|
|
38431
39114
|
}
|
|
38432
39115
|
stopHeartbeat2();
|
|
@@ -38438,7 +39121,7 @@ async function shutdown() {
|
|
|
38438
39121
|
await stopSlackApp2();
|
|
38439
39122
|
await stopChatSdk();
|
|
38440
39123
|
if (process.env.OAUTH_KEEPALIVE_DISABLE !== "true") {
|
|
38441
|
-
const { stopOAuthKeepalive } = await import("./keepalive-
|
|
39124
|
+
const { stopOAuthKeepalive } = await import("./keepalive-nhvpw10z.js");
|
|
38442
39125
|
await stopOAuthKeepalive();
|
|
38443
39126
|
}
|
|
38444
39127
|
stopOAuthPendingGc();
|
|
@@ -38518,21 +39201,21 @@ try {
|
|
|
38518
39201
|
console.warn("[startup] CAPABILITIES upgrade seed failed (non-fatal):", err);
|
|
38519
39202
|
}
|
|
38520
39203
|
try {
|
|
38521
|
-
const { ensurePricingSeeded } = await import("./boot-seeds-
|
|
39204
|
+
const { ensurePricingSeeded } = await import("./boot-seeds-6wxqkfk2.js");
|
|
38522
39205
|
ensurePricingSeeded();
|
|
38523
|
-
const { startPricingRefreshLoop } = await import("./pricing-refresh-
|
|
39206
|
+
const { startPricingRefreshLoop } = await import("./pricing-refresh-3cqx3f3x.js");
|
|
38524
39207
|
startPricingRefreshLoop();
|
|
38525
39208
|
} catch (err) {
|
|
38526
39209
|
console.error("[startup] Failed to seed pricing rows:", err);
|
|
38527
39210
|
}
|
|
38528
39211
|
try {
|
|
38529
|
-
const { ensureModelProfilesSeeded } = await import("./boot-seeds-
|
|
39212
|
+
const { ensureModelProfilesSeeded } = await import("./boot-seeds-6wxqkfk2.js");
|
|
38530
39213
|
await ensureModelProfilesSeeded();
|
|
38531
39214
|
} catch (err) {
|
|
38532
39215
|
console.error("[startup] Failed to seed model profiles:", err);
|
|
38533
39216
|
}
|
|
38534
39217
|
try {
|
|
38535
|
-
const { ensureRbacSeeded } = await import("./boot-seeds-
|
|
39218
|
+
const { ensureRbacSeeded } = await import("./boot-seeds-6wxqkfk2.js");
|
|
38536
39219
|
ensureRbacSeeded();
|
|
38537
39220
|
} catch (err) {
|
|
38538
39221
|
console.error("[startup] Failed to sync RBAC seed rows:", err);
|
|
@@ -38540,7 +39223,7 @@ try {
|
|
|
38540
39223
|
throw err;
|
|
38541
39224
|
}
|
|
38542
39225
|
try {
|
|
38543
|
-
const { runAllSeeders } = await import("./index-
|
|
39226
|
+
const { runAllSeeders } = await import("./index-2z710v4x.js");
|
|
38544
39227
|
await runAllSeeders({ scriptEmbeddingMode: "skip" });
|
|
38545
39228
|
} catch (err) {
|
|
38546
39229
|
console.error("[startup] Failed to seed built-in entities:", err);
|
|
@@ -38602,39 +39285,39 @@ httpServer.listen(port, async () => {
|
|
|
38602
39285
|
await initWorkflows2();
|
|
38603
39286
|
await startScriptRunSupervisor(getMcpBaseUrl());
|
|
38604
39287
|
if (hasCapability("scheduling")) {
|
|
38605
|
-
const { startScheduler } = await import("./index-
|
|
38606
|
-
const { getExecutorRegistry } = await import("./index-
|
|
39288
|
+
const { startScheduler } = await import("./index-h2ek08d6.js");
|
|
39289
|
+
const { getExecutorRegistry } = await import("./index-dgz61nff.js");
|
|
38607
39290
|
const intervalMs = Number(process.env.SCHEDULER_INTERVAL_MS) || 1e4;
|
|
38608
39291
|
startScheduler(getExecutorRegistry(), intervalMs, {
|
|
38609
39292
|
runId: globalState.__runId
|
|
38610
39293
|
});
|
|
38611
39294
|
}
|
|
38612
39295
|
if (!isEnvFlagEnabled("HEARTBEAT_DISABLE", false)) {
|
|
38613
|
-
const { startHeartbeat } = await import("./index-
|
|
39296
|
+
const { startHeartbeat } = await import("./index-pqfwh9nn.js");
|
|
38614
39297
|
const heartbeatMs = Number(process.env.HEARTBEAT_INTERVAL_MS) || 90000;
|
|
38615
39298
|
startHeartbeat(heartbeatMs);
|
|
38616
39299
|
}
|
|
38617
39300
|
startPoolRouting();
|
|
38618
39301
|
startRuntimeSupervisor();
|
|
38619
39302
|
if (process.env.OAUTH_KEEPALIVE_DISABLE !== "true") {
|
|
38620
|
-
const { startOAuthKeepalive } = await import("./keepalive-
|
|
39303
|
+
const { startOAuthKeepalive } = await import("./keepalive-nhvpw10z.js");
|
|
38621
39304
|
startOAuthKeepalive();
|
|
38622
39305
|
}
|
|
38623
39306
|
if (process.env.OAUTH_REFRESH_SWEEP_DISABLE !== "true") {
|
|
38624
|
-
const { startOAuthRefreshSweep } = await import("./oauth-refresh-sweep-
|
|
39307
|
+
const { startOAuthRefreshSweep } = await import("./oauth-refresh-sweep-4dcbhtn0.js");
|
|
38625
39308
|
startOAuthRefreshSweep();
|
|
38626
39309
|
}
|
|
38627
39310
|
startOAuthPendingGc();
|
|
38628
39311
|
await startMemoryGc();
|
|
38629
39312
|
await startDbRetention();
|
|
38630
39313
|
startHumanFreeDrain();
|
|
38631
|
-
import("./boot-reembed-
|
|
39314
|
+
import("./boot-reembed-e53x6b9v.js").then(({ runBootReembed }) => runBootReembed()).catch((err) => {
|
|
38632
39315
|
console.error("[boot-reembed] startup backfill failed (non-fatal):", err);
|
|
38633
39316
|
});
|
|
38634
|
-
import("./boot-reembed-
|
|
39317
|
+
import("./boot-reembed-f6z923ny.js").then(({ runBootReembedScripts }) => runBootReembedScripts()).catch((err) => {
|
|
38635
39318
|
console.error("[boot-reembed-scripts] startup backfill failed (non-fatal):", err);
|
|
38636
39319
|
});
|
|
38637
|
-
import("./boot-scrub-sweep-
|
|
39320
|
+
import("./boot-scrub-sweep-0538vsb7.js").then(({ runBootScrubSweep }) => runBootScrubSweep()).catch((err) => {
|
|
38638
39321
|
console.error("[boot-scrub-sweep] startup scrub failed (non-fatal):", err);
|
|
38639
39322
|
});
|
|
38640
39323
|
}).on("error", (err) => {
|