@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
package/src/http/tasks.ts
CHANGED
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
} from "../be/db";
|
|
38
38
|
import { modelProfileRefError, taskProfileError } from "../be/db/model-profiles";
|
|
39
39
|
import { explicitModelErrorForAgent } from "../be/model-validation";
|
|
40
|
+
import { hasSessionAccess, type SessionAccess } from "../be/session-shares";
|
|
40
41
|
import {
|
|
41
42
|
getTaskSteeringFields,
|
|
42
43
|
markSteeringUndeliverable,
|
|
@@ -72,6 +73,7 @@ import {
|
|
|
72
73
|
SteeringSourceSchema,
|
|
73
74
|
SteerModeSchema,
|
|
74
75
|
SteerResultSchema,
|
|
76
|
+
SUPERSEDED_WORKFLOW_TASK_REASON,
|
|
75
77
|
splitLegacyModelAlias,
|
|
76
78
|
TaskAttachmentSchema,
|
|
77
79
|
} from "../types";
|
|
@@ -80,7 +82,15 @@ import { getRequestAuth } from "../utils/request-auth-context";
|
|
|
80
82
|
import { scrubSecrets } from "../utils/secret-scrubber";
|
|
81
83
|
import { rejectGuest } from "./request-principal";
|
|
82
84
|
import { buildTaskRequestedBy, TaskRequestedBySchema } from "./requested-by";
|
|
83
|
-
import { route } from "./route-def";
|
|
85
|
+
import { bodyTask, route } from "./route-def";
|
|
86
|
+
import {
|
|
87
|
+
canReadSession,
|
|
88
|
+
readableByFor,
|
|
89
|
+
resolveSessionActor,
|
|
90
|
+
SESSION_READ_DENIED,
|
|
91
|
+
SESSION_WRITE_DENIED,
|
|
92
|
+
sessionAccessFor,
|
|
93
|
+
} from "./session-access";
|
|
84
94
|
import { jsonError, parseBody } from "./utils";
|
|
85
95
|
|
|
86
96
|
// ─── Response Schemas ────────────────────────────────────────────────────────
|
|
@@ -207,7 +217,7 @@ const listTasks = route({
|
|
|
207
217
|
pattern: ["api", "tasks"],
|
|
208
218
|
summary: "List tasks with filters",
|
|
209
219
|
description:
|
|
210
|
-
"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`.",
|
|
220
|
+
"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`.",
|
|
211
221
|
tags: ["Tasks"],
|
|
212
222
|
query: z.object({
|
|
213
223
|
/** Single status, or comma-separated list (e.g. "failed,cancelled"). */
|
|
@@ -263,6 +273,7 @@ const createTask = route({
|
|
|
263
273
|
method: "post",
|
|
264
274
|
path: "/api/tasks",
|
|
265
275
|
pattern: ["api", "tasks"],
|
|
276
|
+
session: bodyTask("parentTaskId"),
|
|
266
277
|
summary: "Create a new task",
|
|
267
278
|
tags: ["Tasks"],
|
|
268
279
|
body: z
|
|
@@ -340,6 +351,7 @@ const promoteDraftTaskRoute = route({
|
|
|
340
351
|
method: "post",
|
|
341
352
|
path: "/api/tasks/{id}/promote-draft",
|
|
342
353
|
pattern: ["api", "tasks", null, "promote-draft"],
|
|
354
|
+
session: { taskParam: "id" },
|
|
343
355
|
summary: "Promote a draft task out of the pre-dispatch draft state",
|
|
344
356
|
description:
|
|
345
357
|
"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.",
|
|
@@ -357,6 +369,7 @@ const updateSession = route({
|
|
|
357
369
|
method: "put",
|
|
358
370
|
path: "/api/tasks/{id}/session",
|
|
359
371
|
pattern: ["api", "tasks", null, "session"],
|
|
372
|
+
session: { taskParam: "id" },
|
|
360
373
|
summary: "Update provider session ID and harness metadata for a task",
|
|
361
374
|
tags: ["Tasks"],
|
|
362
375
|
params: z.object({ id: z.string() }),
|
|
@@ -392,6 +405,7 @@ const cancelTaskRoute = route({
|
|
|
392
405
|
method: "post",
|
|
393
406
|
path: "/api/tasks/{id}/cancel",
|
|
394
407
|
pattern: ["api", "tasks", null, "cancel"],
|
|
408
|
+
session: { taskParam: "id" },
|
|
395
409
|
summary: "Cancel a pending or in-progress task",
|
|
396
410
|
tags: ["Tasks"],
|
|
397
411
|
params: z.object({ id: z.string() }),
|
|
@@ -407,6 +421,7 @@ const steerTaskRoute = route({
|
|
|
407
421
|
method: "post",
|
|
408
422
|
path: "/api/tasks/{id}/steer",
|
|
409
423
|
pattern: ["api", "tasks", null, "steer"],
|
|
424
|
+
session: { taskParam: "id" },
|
|
410
425
|
summary: "Deliver a steering message to a running task",
|
|
411
426
|
tags: ["Tasks"],
|
|
412
427
|
params: z.object({ id: z.string() }),
|
|
@@ -439,6 +454,7 @@ const getTaskSteeringMessagesRoute = route({
|
|
|
439
454
|
method: "get",
|
|
440
455
|
path: "/api/tasks/{id}/steering-messages",
|
|
441
456
|
pattern: ["api", "tasks", null, "steering-messages"],
|
|
457
|
+
session: { taskParam: "id" },
|
|
442
458
|
summary: "List steering messages for a task",
|
|
443
459
|
tags: ["Tasks"],
|
|
444
460
|
params: z.object({ id: z.string() }),
|
|
@@ -467,6 +483,7 @@ const markSteeringDeliveredRoute = route({
|
|
|
467
483
|
method: "post",
|
|
468
484
|
path: "/api/steering-messages/{id}/delivered",
|
|
469
485
|
pattern: ["api", "steering-messages", null, "delivered"],
|
|
486
|
+
session: { steeringParam: "id" },
|
|
470
487
|
summary: "Mark a steering message delivered",
|
|
471
488
|
tags: ["Tasks"],
|
|
472
489
|
params: z.object({ id: z.string() }),
|
|
@@ -492,6 +509,7 @@ const markSteeringHandledRoute = route({
|
|
|
492
509
|
method: "post",
|
|
493
510
|
path: "/api/steering-messages/{id}/handled",
|
|
494
511
|
pattern: ["api", "steering-messages", null, "handled"],
|
|
512
|
+
session: { steeringParam: "id" },
|
|
495
513
|
summary: "Mark a steering message handled",
|
|
496
514
|
description:
|
|
497
515
|
"Optionally accepts a JSON body `{ note?: string }` — a short acceptance note describing how the steering was incorporated, persisted as `handledNote`.",
|
|
@@ -517,6 +535,7 @@ const markSteeringUndeliverableRoute = route({
|
|
|
517
535
|
method: "post",
|
|
518
536
|
path: "/api/steering-messages/{id}/undeliverable",
|
|
519
537
|
pattern: ["api", "steering-messages", null, "undeliverable"],
|
|
538
|
+
session: { steeringParam: "id" },
|
|
520
539
|
summary: "Promote an undeliverable steering message",
|
|
521
540
|
tags: ["Tasks"],
|
|
522
541
|
params: z.object({ id: z.string() }),
|
|
@@ -542,6 +561,7 @@ const getTask = route({
|
|
|
542
561
|
method: "get",
|
|
543
562
|
path: "/api/tasks/{id}",
|
|
544
563
|
pattern: ["api", "tasks", null],
|
|
564
|
+
session: { taskParam: "id" },
|
|
545
565
|
summary: "Get task details with logs and attachments",
|
|
546
566
|
description:
|
|
547
567
|
"Returns the full `AgentTask` row decorated with `logs` (capped by `logsLimit`) and `attachments` (pointer-based artifacts stored on the task, ordered by `created_at`).",
|
|
@@ -553,6 +573,7 @@ const getTask = route({
|
|
|
553
573
|
}),
|
|
554
574
|
responses: {
|
|
555
575
|
200: { description: "Task with logs and attachments", schema: GetTaskResponseSchema },
|
|
576
|
+
403: { description: "A user caller cannot read the task's session" },
|
|
556
577
|
404: { description: "Task not found" },
|
|
557
578
|
},
|
|
558
579
|
});
|
|
@@ -561,6 +582,7 @@ const updateTaskProgressRoute = route({
|
|
|
561
582
|
method: "post",
|
|
562
583
|
path: "/api/tasks/{id}/progress",
|
|
563
584
|
pattern: ["api", "tasks", null, "progress"],
|
|
585
|
+
session: { taskParam: "id" },
|
|
564
586
|
summary: "Update task progress text",
|
|
565
587
|
tags: ["Tasks"],
|
|
566
588
|
params: z.object({ id: z.string() }),
|
|
@@ -579,6 +601,7 @@ const finishTask = route({
|
|
|
579
601
|
method: "post",
|
|
580
602
|
path: "/api/tasks/{id}/finish",
|
|
581
603
|
pattern: ["api", "tasks", null, "finish"],
|
|
604
|
+
session: { taskParam: "id" },
|
|
582
605
|
summary: "Mark task as completed or failed (runner endpoint)",
|
|
583
606
|
tags: ["Tasks"],
|
|
584
607
|
params: z.object({ id: z.string() }),
|
|
@@ -631,6 +654,7 @@ const pauseTaskRoute = route({
|
|
|
631
654
|
method: "post",
|
|
632
655
|
path: "/api/tasks/{id}/pause",
|
|
633
656
|
pattern: ["api", "tasks", null, "pause"],
|
|
657
|
+
session: { taskParam: "id" },
|
|
634
658
|
summary: "Pause an in-progress task",
|
|
635
659
|
tags: ["Tasks"],
|
|
636
660
|
params: z.object({ id: z.string() }),
|
|
@@ -646,6 +670,7 @@ const resumeTaskRoute = route({
|
|
|
646
670
|
method: "post",
|
|
647
671
|
path: "/api/tasks/{id}/resume",
|
|
648
672
|
pattern: ["api", "tasks", null, "resume"],
|
|
673
|
+
session: { taskParam: "id" },
|
|
649
674
|
summary: "Resume a paused task",
|
|
650
675
|
tags: ["Tasks"],
|
|
651
676
|
params: z.object({ id: z.string() }),
|
|
@@ -661,6 +686,7 @@ const supersedeTaskRoute = route({
|
|
|
661
686
|
method: "post",
|
|
662
687
|
path: "/api/tasks/{id}/supersede",
|
|
663
688
|
pattern: ["api", "tasks", null, "supersede"],
|
|
689
|
+
session: { taskParam: "id" },
|
|
664
690
|
summary: "Supersede an in-progress task (terminate + spawn resume follow-up)",
|
|
665
691
|
description:
|
|
666
692
|
'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.',
|
|
@@ -683,6 +709,7 @@ const updateTaskVcsRoute = route({
|
|
|
683
709
|
method: "patch",
|
|
684
710
|
path: "/api/tasks/{id}/vcs",
|
|
685
711
|
pattern: ["api", "tasks", null, "vcs"],
|
|
712
|
+
session: { taskParam: "id" },
|
|
686
713
|
summary: "Update VCS (PR/MR) info for a task",
|
|
687
714
|
tags: ["Tasks"],
|
|
688
715
|
params: z.object({ id: z.string() }),
|
|
@@ -704,6 +731,7 @@ const updateTaskTitleRoute = route({
|
|
|
704
731
|
method: "patch",
|
|
705
732
|
path: "/api/tasks/{id}/title",
|
|
706
733
|
pattern: ["api", "tasks", null, "title"],
|
|
734
|
+
session: { taskParam: "id" },
|
|
707
735
|
summary: "Set or clear a task's display title (session rename)",
|
|
708
736
|
description:
|
|
709
737
|
"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.",
|
|
@@ -722,22 +750,37 @@ const updateTaskTitleRoute = route({
|
|
|
722
750
|
},
|
|
723
751
|
});
|
|
724
752
|
|
|
753
|
+
/** True unless the request acts for a user who lacks write access to the task's session. */
|
|
754
|
+
async function hasSessionWrite(
|
|
755
|
+
req: IncomingMessage,
|
|
756
|
+
myAgentId: string | undefined,
|
|
757
|
+
taskId: string,
|
|
758
|
+
): Promise<boolean> {
|
|
759
|
+
const access = await sessionAccessFor(await resolveSessionActor(req, myAgentId), taskId);
|
|
760
|
+
return hasSessionAccess(access, "write");
|
|
761
|
+
}
|
|
762
|
+
|
|
725
763
|
async function canSteerTask(
|
|
726
764
|
req: IncomingMessage,
|
|
727
765
|
myAgentId: string | undefined,
|
|
728
766
|
task: AgentTask,
|
|
767
|
+
sessionAccess: SessionAccess = "none",
|
|
729
768
|
): Promise<boolean> {
|
|
769
|
+
const auth = getRequestAuth(req);
|
|
730
770
|
const resource: RbacResource = {
|
|
731
771
|
kind: "task",
|
|
732
772
|
taskId: task.id,
|
|
733
|
-
|
|
773
|
+
// A user with a write share steers as if the session were their own; the
|
|
774
|
+
// verb gate (and any role grant) still applies. Only an actual share
|
|
775
|
+
// counts: `owner` from an ownerless root must not widen steering.
|
|
776
|
+
requestedByUserId:
|
|
777
|
+
auth?.kind === "user" && sessionAccess === "write" ? auth.userId : task.requestedByUserId,
|
|
734
778
|
creatorAgentId: task.creatorAgentId,
|
|
735
779
|
agentId: task.agentId,
|
|
736
780
|
};
|
|
737
781
|
let principal: RbacPrincipal;
|
|
738
782
|
let verb: "task.steer.any" | "task.steer.own";
|
|
739
783
|
|
|
740
|
-
const auth = getRequestAuth(req);
|
|
741
784
|
if (auth?.kind === "operator") {
|
|
742
785
|
principal = { kind: "operator" };
|
|
743
786
|
verb = "task.steer.own";
|
|
@@ -906,6 +949,9 @@ export async function handleTasks(
|
|
|
906
949
|
if (validated.length > 0) source = validated;
|
|
907
950
|
}
|
|
908
951
|
|
|
952
|
+
// Every shape carries task text, so a user sees only sessions they can
|
|
953
|
+
// read, and `total` counts the same rows.
|
|
954
|
+
const actor = await resolveSessionActor(req, myAgentId);
|
|
909
955
|
const filters = {
|
|
910
956
|
status,
|
|
911
957
|
agentId: parsed.query.agentId || undefined,
|
|
@@ -925,6 +971,7 @@ export async function handleTasks(
|
|
|
925
971
|
orderBy: parsed.query.orderBy,
|
|
926
972
|
limit: parsed.query.limit,
|
|
927
973
|
offset: parsed.query.offset,
|
|
974
|
+
readableBy: actor.kind === "user" ? { userId: actor.userId } : undefined,
|
|
928
975
|
};
|
|
929
976
|
// List responses default to slim (full `task` text → bounded `taskPreview`,
|
|
930
977
|
// heavy blobs dropped); `?fields=full` restores the full `AgentTask`.
|
|
@@ -1013,6 +1060,11 @@ export async function handleTasks(
|
|
|
1013
1060
|
jsonError(res, `You don't have context for parent task ${parentTask.id}.`, 403);
|
|
1014
1061
|
return true;
|
|
1015
1062
|
}
|
|
1063
|
+
// A follow-up in someone else's session needs a write share.
|
|
1064
|
+
if (parentTask && !(await hasSessionWrite(req, myAgentId, parentTask.id))) {
|
|
1065
|
+
jsonError(res, SESSION_WRITE_DENIED, 403);
|
|
1066
|
+
return true;
|
|
1067
|
+
}
|
|
1016
1068
|
// A chat-sdk contextKey binds the task to a chat thread that its assignee and creator
|
|
1017
1069
|
// may then read and post to. An agent may only name a thread other than the parent's
|
|
1018
1070
|
// with the lead's chat post permission.
|
|
@@ -1285,7 +1337,13 @@ export async function handleTasks(
|
|
|
1285
1337
|
jsonError(res, "Task not found", 404);
|
|
1286
1338
|
return true;
|
|
1287
1339
|
}
|
|
1288
|
-
|
|
1340
|
+
const sessionActor = await resolveSessionActor(req, myAgentId);
|
|
1341
|
+
const steerAccess = await sessionAccessFor(sessionActor, task.id);
|
|
1342
|
+
if (!hasSessionAccess(steerAccess, "write")) {
|
|
1343
|
+
jsonError(res, SESSION_WRITE_DENIED, 403);
|
|
1344
|
+
return true;
|
|
1345
|
+
}
|
|
1346
|
+
if (!(await canSteerTask(req, myAgentId, task, steerAccess))) {
|
|
1289
1347
|
jsonError(res, "Forbidden: caller cannot steer this task", 403);
|
|
1290
1348
|
return true;
|
|
1291
1349
|
}
|
|
@@ -1338,6 +1396,10 @@ export async function handleTasks(
|
|
|
1338
1396
|
jsonError(res, "Task not found", 404);
|
|
1339
1397
|
return true;
|
|
1340
1398
|
}
|
|
1399
|
+
if (!(await canReadSession(req, myAgentId, parsed.params.id))) {
|
|
1400
|
+
jsonError(res, SESSION_READ_DENIED, 403);
|
|
1401
|
+
return true;
|
|
1402
|
+
}
|
|
1341
1403
|
getTaskSteeringMessagesRoute.respond(res, 200, {
|
|
1342
1404
|
messages: await getSteeringMessagesForTask(parsed.params.id),
|
|
1343
1405
|
});
|
|
@@ -1357,7 +1419,12 @@ export async function handleTasks(
|
|
|
1357
1419
|
return true;
|
|
1358
1420
|
}
|
|
1359
1421
|
|
|
1422
|
+
// A user token naming an agent in X-Agent-ID still reads only its sessions.
|
|
1360
1423
|
if (parsed.query.taskId) {
|
|
1424
|
+
if (!(await canReadSession(req, myAgentId, parsed.query.taskId))) {
|
|
1425
|
+
jsonError(res, SESSION_READ_DENIED, 403);
|
|
1426
|
+
return true;
|
|
1427
|
+
}
|
|
1361
1428
|
const task = await getTaskById(parsed.query.taskId);
|
|
1362
1429
|
const messages =
|
|
1363
1430
|
task?.agentId === myAgentId ? await getPendingSteeringForTask(parsed.query.taskId) : [];
|
|
@@ -1365,8 +1432,9 @@ export async function handleTasks(
|
|
|
1365
1432
|
return true;
|
|
1366
1433
|
}
|
|
1367
1434
|
|
|
1435
|
+
const actor = await resolveSessionActor(req, myAgentId);
|
|
1368
1436
|
getPendingSteeringMessagesRoute.respond(res, 200, {
|
|
1369
|
-
messages: await getPendingSteeringForAgent(myAgentId),
|
|
1437
|
+
messages: await getPendingSteeringForAgent(myAgentId, readableByFor(actor)),
|
|
1370
1438
|
});
|
|
1371
1439
|
return true;
|
|
1372
1440
|
}
|
|
@@ -1529,6 +1597,11 @@ export async function handleTasks(
|
|
|
1529
1597
|
jsonError(res, "Task not found", 404);
|
|
1530
1598
|
return true;
|
|
1531
1599
|
}
|
|
1600
|
+
const readAccess = await sessionAccessFor(await resolveSessionActor(req, myAgentId), task.id);
|
|
1601
|
+
if (!hasSessionAccess(readAccess, "read")) {
|
|
1602
|
+
jsonError(res, "You don't have access to this session. Ask the owner to share it.", 403);
|
|
1603
|
+
return true;
|
|
1604
|
+
}
|
|
1532
1605
|
|
|
1533
1606
|
const logs = await getLogsByTaskId(parsed.params.id, parsed.query.logsLimit ?? 200);
|
|
1534
1607
|
const attachments = await getTaskAttachments(parsed.params.id);
|
|
@@ -1758,7 +1831,11 @@ export async function handleTasks(
|
|
|
1758
1831
|
jsonError(res, "Missing X-Agent-ID header", 400);
|
|
1759
1832
|
return true;
|
|
1760
1833
|
}
|
|
1761
|
-
|
|
1834
|
+
// A user token naming an agent in X-Agent-ID still reads only its sessions.
|
|
1835
|
+
const pausedTasks = await getPausedTasksForAgent(
|
|
1836
|
+
myAgentId,
|
|
1837
|
+
readableByFor(await resolveSessionActor(req, myAgentId)),
|
|
1838
|
+
);
|
|
1762
1839
|
const tasks = await Promise.all(
|
|
1763
1840
|
pausedTasks.map(async (task) => {
|
|
1764
1841
|
const requestedBy = await buildTaskRequestedBy(task);
|
|
@@ -1938,7 +2015,7 @@ export async function handleTasks(
|
|
|
1938
2015
|
// Check this BEFORE the supersede UPDATE so we don't leave a workflow
|
|
1939
2016
|
// step in `superseded` if the engine expects `failed`.
|
|
1940
2017
|
if (task.workflowRunStepId != null) {
|
|
1941
|
-
const failed = await failTask(parsed.params.id,
|
|
2018
|
+
const failed = await failTask(parsed.params.id, SUPERSEDED_WORKFLOW_TASK_REASON);
|
|
1942
2019
|
ensure({
|
|
1943
2020
|
id: "task.workflow_step_failed_on_supersede",
|
|
1944
2021
|
flow: "task",
|
package/src/http/telemetry-ui.ts
CHANGED
|
@@ -43,7 +43,7 @@ const enableTelemetry = route({
|
|
|
43
43
|
description:
|
|
44
44
|
"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.",
|
|
45
45
|
tags: ["Telemetry"],
|
|
46
|
-
body: z.object({ via: z.enum(["banner", "settings"]).default("banner") }),
|
|
46
|
+
body: z.object({ via: z.enum(["banner", "settings", "benchmark"]).default("banner") }),
|
|
47
47
|
responses: {
|
|
48
48
|
200: {
|
|
49
49
|
description: "Telemetry is on",
|
package/src/http/used-by.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import {
|
|
3
|
+
import { getDbClient, upsertSwarmConfig } from "../be/db";
|
|
4
4
|
import { isTelemetryEnabled, trackRelayed } from "../telemetry";
|
|
5
|
+
import { USER_FUNCTIONS } from "../utils/company-profile";
|
|
6
|
+
import { orgIdentityStatus } from "../utils/org-identity";
|
|
5
7
|
import { normalizeOrgWebsite, TELEMETRY_PROXY_ORIGIN } from "../utils/org-website";
|
|
6
8
|
import { ensureConfigAdmin } from "./config";
|
|
7
9
|
import { scheduleIntegrationsReload } from "./core";
|
|
8
10
|
import { resolveHttpFavoriteOwner } from "./favorite-owner";
|
|
11
|
+
import { globalConfig } from "./global-config";
|
|
9
12
|
import { route } from "./route-def";
|
|
10
13
|
import { jsonError } from "./utils";
|
|
11
14
|
|
|
@@ -15,6 +18,9 @@ const CONSENT_AT_KEY = "SWARM_USED_BY_CONSENT_AT";
|
|
|
15
18
|
// still reaches the proxy after the name or website is changed or cleared.
|
|
16
19
|
const GRANTED_NAME_KEY = "SWARM_USED_BY_ORG_NAME";
|
|
17
20
|
const GRANTED_WEBSITE_KEY = "SWARM_USED_BY_ORG_WEBSITE";
|
|
21
|
+
const CONTACT_EMAIL_KEY = "SWARM_USED_BY_CONTACT_EMAIL";
|
|
22
|
+
const CONTACT_FUNCTION_KEY = "SWARM_USED_BY_CONTACT_FUNCTION";
|
|
23
|
+
const QUOTE_KEY = "SWARM_USED_BY_QUOTE";
|
|
18
24
|
const TIMEOUT_MS = 10_000;
|
|
19
25
|
|
|
20
26
|
// ─── Route Definitions ───────────────────────────────────────────────────────
|
|
@@ -25,17 +31,30 @@ const usedBy = route({
|
|
|
25
31
|
pattern: ["api", "used-by"],
|
|
26
32
|
summary: "Grant or revoke the 'used by' logo consent",
|
|
27
33
|
description:
|
|
28
|
-
"Records
|
|
34
|
+
"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.",
|
|
29
35
|
tags: ["Telemetry"],
|
|
30
|
-
body: z
|
|
36
|
+
body: z
|
|
37
|
+
.object({
|
|
38
|
+
granted: z.boolean(),
|
|
39
|
+
contactEmail: z
|
|
40
|
+
.string()
|
|
41
|
+
.max(320, "Contact email must be at most 320 characters")
|
|
42
|
+
.email("Contact email must be a valid email address")
|
|
43
|
+
.optional(),
|
|
44
|
+
contactFunction: z.enum(USER_FUNCTIONS).optional(),
|
|
45
|
+
quote: z.string().max(280).optional(),
|
|
46
|
+
})
|
|
47
|
+
.refine((body) => !body.granted || body.contactEmail !== undefined, {
|
|
48
|
+
path: ["contactEmail"],
|
|
49
|
+
message: "Contact email is required when granting logo consent",
|
|
50
|
+
}),
|
|
31
51
|
responses: {
|
|
32
52
|
200: {
|
|
33
53
|
description: "Consent recorded",
|
|
34
54
|
schema: z.object({ granted: z.boolean(), changedAt: z.string() }),
|
|
35
55
|
},
|
|
36
56
|
400: {
|
|
37
|
-
description:
|
|
38
|
-
"Organization name or website is not set (a revoke falls back to the identity of the last grant)",
|
|
57
|
+
description: "Invalid contact details, or missing organization name or website",
|
|
39
58
|
},
|
|
40
59
|
401: { description: "Unauthorized" },
|
|
41
60
|
403: { description: "Only a config admin can change the consent" },
|
|
@@ -46,11 +65,6 @@ const usedBy = route({
|
|
|
46
65
|
|
|
47
66
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
48
67
|
|
|
49
|
-
async function globalConfig(key: string): Promise<string | undefined> {
|
|
50
|
-
const stored = (await getSwarmConfigs({ scope: "global", key }))?.[0]?.value?.trim();
|
|
51
|
-
return stored || process.env[key]?.trim() || undefined;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
68
|
/** A proxy path next to the configured `/v1/feedback` endpoint, so a stubbed proxy gets every route. */
|
|
55
69
|
export async function telemetryProxyEndpoint(path: `/v1/${string}`): Promise<string> {
|
|
56
70
|
const feedback = await globalConfig("feedback_endpoint");
|
|
@@ -78,9 +92,15 @@ export async function handleUsedBy(
|
|
|
78
92
|
if (!parsed) return true;
|
|
79
93
|
if (!(await ensureConfigAdmin(req, res, "config.write.any"))) return true;
|
|
80
94
|
|
|
81
|
-
const { granted } = parsed.body;
|
|
82
|
-
|
|
83
|
-
|
|
95
|
+
const { granted, contactEmail, contactFunction, quote } = parsed.body;
|
|
96
|
+
// The shared identity reader. The grant keeps its own rule (any non-empty
|
|
97
|
+
// name), so it does not apply the ranking's placeholder-name check.
|
|
98
|
+
const current = orgIdentityStatus({
|
|
99
|
+
SWARM_ORG_NAME: await globalConfig("SWARM_ORG_NAME"),
|
|
100
|
+
SWARM_ORG_WEBSITE: await globalConfig("SWARM_ORG_WEBSITE"),
|
|
101
|
+
});
|
|
102
|
+
const currentName = current.name ?? undefined;
|
|
103
|
+
const currentWebsite = current.website;
|
|
84
104
|
const grantedName = granted ? undefined : await globalConfig(GRANTED_NAME_KEY);
|
|
85
105
|
const grantedWebsite = granted
|
|
86
106
|
? null
|
|
@@ -108,6 +128,9 @@ export async function handleUsedBy(
|
|
|
108
128
|
org_website: orgWebsite,
|
|
109
129
|
...(orgDomain ? { org_domain: orgDomain } : {}),
|
|
110
130
|
granted,
|
|
131
|
+
...(granted
|
|
132
|
+
? { contact_email: contactEmail, contact_function: contactFunction, quote }
|
|
133
|
+
: {}),
|
|
111
134
|
changed_at: changedAt,
|
|
112
135
|
}),
|
|
113
136
|
signal: AbortSignal.timeout(TIMEOUT_MS),
|
|
@@ -121,18 +144,39 @@ export async function handleUsedBy(
|
|
|
121
144
|
return true;
|
|
122
145
|
}
|
|
123
146
|
|
|
124
|
-
await
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
147
|
+
await getDbClient().transaction(async () => {
|
|
148
|
+
await upsertSwarmConfig({ scope: "global", key: CONSENT_KEY, value: String(granted) });
|
|
149
|
+
await upsertSwarmConfig({ scope: "global", key: CONSENT_AT_KEY, value: changedAt });
|
|
150
|
+
if (granted) {
|
|
151
|
+
await upsertSwarmConfig({ scope: "global", key: GRANTED_NAME_KEY, value: orgName });
|
|
152
|
+
await upsertSwarmConfig({ scope: "global", key: GRANTED_WEBSITE_KEY, value: orgWebsite });
|
|
153
|
+
}
|
|
154
|
+
// Contact details stay out of agent env. The email is also a secret row,
|
|
155
|
+
// so `GET /api/config` masks it for every reader.
|
|
156
|
+
for (const [key, value, isSecret] of [
|
|
157
|
+
[CONTACT_EMAIL_KEY, contactEmail, true],
|
|
158
|
+
[CONTACT_FUNCTION_KEY, contactFunction, false],
|
|
159
|
+
[QUOTE_KEY, quote, false],
|
|
160
|
+
] as const) {
|
|
161
|
+
await upsertSwarmConfig({
|
|
162
|
+
scope: "global",
|
|
163
|
+
key,
|
|
164
|
+
value: granted ? (value ?? "") : "",
|
|
165
|
+
isSecret,
|
|
166
|
+
exportToEnv: false,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
});
|
|
130
170
|
scheduleIntegrationsReload();
|
|
131
171
|
|
|
132
172
|
const owner = await resolveHttpFavoriteOwner(req, myAgentId);
|
|
133
173
|
trackRelayed(
|
|
134
174
|
"used_by.consent_changed",
|
|
135
|
-
{
|
|
175
|
+
{
|
|
176
|
+
granted,
|
|
177
|
+
has_quote: granted && Boolean(quote),
|
|
178
|
+
has_function: granted && Boolean(contactFunction),
|
|
179
|
+
},
|
|
136
180
|
{ actor: owner?.userId ? { userId: owner.userId } : undefined, emitter: "ui" },
|
|
137
181
|
);
|
|
138
182
|
usedBy.respond(res, 200, { granted, changedAt });
|
package/src/http/users.ts
CHANGED
|
@@ -205,7 +205,7 @@ const createUserRoute = route({
|
|
|
205
205
|
tags: ["Users"],
|
|
206
206
|
body: z.object({
|
|
207
207
|
name: z.string().min(1),
|
|
208
|
-
email: z.string().optional(),
|
|
208
|
+
email: z.string().email("Email must be a valid email address").optional(),
|
|
209
209
|
role: z.string().optional(),
|
|
210
210
|
notes: z.string().optional(),
|
|
211
211
|
emailAliases: z.array(z.string()).optional(),
|
|
@@ -307,7 +307,7 @@ const updateUserRoute = route({
|
|
|
307
307
|
body: z
|
|
308
308
|
.object({
|
|
309
309
|
name: z.string().min(1).optional(),
|
|
310
|
-
email: z.string().optional(),
|
|
310
|
+
email: z.string().email("Email must be a valid email address").nullable().optional(),
|
|
311
311
|
role: z.string().optional(),
|
|
312
312
|
notes: z.string().optional(),
|
|
313
313
|
emailAliases: z.array(z.string()).optional(),
|
|
@@ -555,12 +555,14 @@ registerTemplate({
|
|
|
555
555
|
header: "",
|
|
556
556
|
defaultBody: `{{prompt}}
|
|
557
557
|
|
|
558
|
-
[App action] app={{app_id}} action={{action_name}} input={{input_json}}
|
|
558
|
+
[App action] app={{app_id}} action={{action_name}} input={{input_json}}
|
|
559
|
+
Caller (server-verified): {{caller_json}}`,
|
|
559
560
|
variables: [
|
|
560
561
|
{ name: "prompt", description: "The task prompt configured on the app action" },
|
|
561
562
|
{ name: "app_id", description: "The app identifier" },
|
|
562
563
|
{ name: "action_name", description: "The invoked action name" },
|
|
563
564
|
{ name: "input_json", description: "The action input serialized as JSON" },
|
|
565
|
+
{ name: "caller_json", description: "The server-verified action caller serialized as JSON" },
|
|
564
566
|
],
|
|
565
567
|
category: "task_lifecycle",
|
|
566
568
|
});
|
|
@@ -44,7 +44,7 @@ import type {
|
|
|
44
44
|
* default; the image installs exactly this version and the per-task settings
|
|
45
45
|
* set `amp.updates.mode: "disabled"`. Bump it deliberately, with a live run.
|
|
46
46
|
*/
|
|
47
|
-
export const AMP_PACKAGE = "@ampcode/cli@0.0.
|
|
47
|
+
export const AMP_PACKAGE = "@ampcode/cli@0.0.1792368068-g91990d";
|
|
48
48
|
|
|
49
49
|
/** MCP server name. Amp exposes its tools as `agent_swarm` / `store_progress` (dashes become underscores). */
|
|
50
50
|
const AMP_MCP_SERVER_NAME = "agent-swarm";
|
|
@@ -115,6 +115,7 @@ function modelToCredKeys(modelStr: string | undefined): string[] | null {
|
|
|
115
115
|
if (provider === "openrouter") return ["OPENROUTER_API_KEY"];
|
|
116
116
|
if (provider === "openai") return ["OPENAI_API_KEY"];
|
|
117
117
|
if (provider === "google") return ["GOOGLE_API_KEY", "GEMINI_API_KEY"];
|
|
118
|
+
if (provider === "opencode-go") return ["OPENCODE_API_KEY"];
|
|
118
119
|
// models.json custom providers (`writeSessionModelsJson`). The
|
|
119
120
|
// runner normalizes key aliases (HELMCODE_API_NAN → NAN_API_KEY), but a
|
|
120
121
|
// worker without that step (or a boot-time check) may only see the alias.
|
|
@@ -145,6 +146,19 @@ function getHarnessDrivableBedrockModels(): Array<{ id: string; name: string }>
|
|
|
145
146
|
}
|
|
146
147
|
}
|
|
147
148
|
|
|
149
|
+
/**
|
|
150
|
+
* The `opencode-go` model ids this pi-ai build resolves. models.dev can list
|
|
151
|
+
* ids pi-ai does not know yet, and pi runs its default model for those, so the
|
|
152
|
+
* worker reports this list and the picker offers only these ids.
|
|
153
|
+
*/
|
|
154
|
+
export function listPiOpencodeGoModelIds(): string[] {
|
|
155
|
+
try {
|
|
156
|
+
return getModels("opencode-go").map((m) => m.id);
|
|
157
|
+
} catch {
|
|
158
|
+
return [];
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
148
162
|
/**
|
|
149
163
|
* Enumerate the Bedrock models that are both invocable by this AWS account and
|
|
150
164
|
* drivable by the pi-ai Converse harness, and verify the credential chain in
|
|
@@ -480,6 +494,7 @@ const PI_RUNTIME_API_KEYS: readonly (readonly [envKey: string, provider: string]
|
|
|
480
494
|
["OPENAI_API_KEY", "openai"],
|
|
481
495
|
["GEMINI_API_KEY", "google"],
|
|
482
496
|
["GOOGLE_API_KEY", "google"],
|
|
497
|
+
["OPENCODE_API_KEY", "opencode-go"],
|
|
483
498
|
// models.json custom providers (the registry). A runtime key beats the
|
|
484
499
|
// file's `apiKey: "$NAN_API_KEY"`, which pi would otherwise read from the
|
|
485
500
|
// shared process.env, where a credential pool is still the raw comma list.
|
|
@@ -168,7 +168,8 @@ function buildOpencodeReasoningOptions(
|
|
|
168
168
|
// qualitative level — translate internally (see "What We're NOT Doing" in
|
|
169
169
|
// the plan: no numeric budget surface for operators, only this
|
|
170
170
|
// adapter-internal transport detail).
|
|
171
|
-
|
|
171
|
+
// Its Anthropic transport reads `effort` into `output_config.effort`.
|
|
172
|
+
if (level === "max") return { effort: level };
|
|
172
173
|
return { thinking: { type: "enabled", budgetTokens: ANTHROPIC_BUDGET_TOKENS_BY_LEVEL[level] } };
|
|
173
174
|
}
|
|
174
175
|
if (providerId === "openrouter") {
|
package/src/providers/types.ts
CHANGED
|
@@ -320,6 +320,8 @@ export interface CredStatus {
|
|
|
320
320
|
* string signals Bedrock mode with `AWS_REGION` unset (no region fabricated).
|
|
321
321
|
*/
|
|
322
322
|
bedrockRegion?: string;
|
|
323
|
+
/** Pi-mono only: the `opencode-go` model ids pi-ai resolves. Undefined for other harnesses. */
|
|
324
|
+
opencodeGoModels?: string[];
|
|
323
325
|
}
|
|
324
326
|
|
|
325
327
|
/**
|