@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
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
checkPiMonoCredentials,
|
|
18
18
|
createPiRuntimeAuth,
|
|
19
19
|
extractPiAssistantText,
|
|
20
|
+
listPiOpencodeGoModelIds,
|
|
20
21
|
PiMonoAdapter,
|
|
21
22
|
resolveModel,
|
|
22
23
|
type SessionModelsJson,
|
|
@@ -457,6 +458,18 @@ describe("Model name mapping", () => {
|
|
|
457
458
|
});
|
|
458
459
|
});
|
|
459
460
|
|
|
461
|
+
describe("listPiOpencodeGoModelIds", () => {
|
|
462
|
+
test("every reported opencode-go id resolves, so a picked id never falls back", () => {
|
|
463
|
+
const ids = listPiOpencodeGoModelIds();
|
|
464
|
+
expect(ids).toContain("deepseek-v4.1-flash");
|
|
465
|
+
for (const id of ids) {
|
|
466
|
+
expect(resolveModel(`opencode-go/${id}`, {})?.id).toBe(id);
|
|
467
|
+
}
|
|
468
|
+
expect(ids).not.toContain("deepseek-v4-flash");
|
|
469
|
+
expect(resolveModel("opencode-go/deepseek-v4-flash", {})).toBeUndefined();
|
|
470
|
+
});
|
|
471
|
+
});
|
|
472
|
+
|
|
460
473
|
describe("resolveModel — OpenRouter reroute for anthropic shortnames", () => {
|
|
461
474
|
// Regression coverage for task 37a4a87a: workers spawned with
|
|
462
475
|
// `provider: pi` + `OPENROUTER_API_KEY` (no ANTHROPIC_API_KEY) and a task
|
|
@@ -546,6 +559,16 @@ describe("createPiRuntimeAuth", () => {
|
|
|
546
559
|
});
|
|
547
560
|
});
|
|
548
561
|
|
|
562
|
+
test("passes OPENCODE_API_KEY to pi opencode-go runtime auth", async () => {
|
|
563
|
+
const modelRuntime = await createPiRuntimeAuth({
|
|
564
|
+
OPENCODE_API_KEY: "example-opencode-runtime",
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
await expect(modelRuntime.getAuth("opencode-go")).resolves.toMatchObject({
|
|
568
|
+
auth: { apiKey: "example-opencode-runtime" },
|
|
569
|
+
});
|
|
570
|
+
});
|
|
571
|
+
|
|
549
572
|
test("passes GEMINI_API_KEY to pi google runtime auth", async () => {
|
|
550
573
|
const modelRuntime = await createPiRuntimeAuth({
|
|
551
574
|
GEMINI_API_KEY: "example-gemini-runtime",
|
|
@@ -339,14 +339,18 @@ describe("parseGhPrCommands", () => {
|
|
|
339
339
|
);
|
|
340
340
|
expect(calls).toEqual([
|
|
341
341
|
{
|
|
342
|
+
action: "create",
|
|
342
343
|
cwd: "/tmp/wt",
|
|
343
344
|
repos: ["o/r"],
|
|
345
|
+
resolvedRepos: ["o/r"],
|
|
344
346
|
alsoCheckout: false,
|
|
345
347
|
unknownTarget: false,
|
|
346
348
|
texts: ["t"],
|
|
349
|
+
bodyTexts: [],
|
|
347
350
|
bodyFiles: ["body.md"],
|
|
348
351
|
unresolvedBodyFiles: [],
|
|
349
352
|
opaqueBody: false,
|
|
353
|
+
externalBody: false,
|
|
350
354
|
},
|
|
351
355
|
]);
|
|
352
356
|
});
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { codexPreToolUseBlock } from "../hooks/codex-hook";
|
|
6
|
+
import { gitRemoteOrigin, guardGhPrRoom, type PrRoomGuardDeps } from "../hooks/pr-room-guard";
|
|
7
|
+
import { expectChildOk, runChild } from "./test-proc";
|
|
8
|
+
|
|
9
|
+
const ROOM_LINE = "PR room: prr-abcde23457";
|
|
10
|
+
const BLOCK_REASON =
|
|
11
|
+
'This repo uses PR rooms. Create the room first with the pr-room skill (app action createRoom). Then pass the returned prBody with --body-file. The body must contain the line "PR room: prr-...".';
|
|
12
|
+
|
|
13
|
+
function fakeDeps(overrides: Partial<PrRoomGuardDeps> = {}): PrRoomGuardDeps {
|
|
14
|
+
return {
|
|
15
|
+
env: { PR_ROOM_REPOS: "owner/repo" },
|
|
16
|
+
readFile: async () => "No room marker",
|
|
17
|
+
remoteOrigin: async () => "https://github.com/owner/repo.git",
|
|
18
|
+
...overrides,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const check = (command: string, deps = fakeDeps()) => guardGhPrRoom({ command }, "/w", deps);
|
|
23
|
+
|
|
24
|
+
describe("guardGhPrRoom", () => {
|
|
25
|
+
test.each([undefined, "", " , , "])("allows disabled configuration %p", async (value) => {
|
|
26
|
+
expect(
|
|
27
|
+
await check(
|
|
28
|
+
"gh pr create -R owner/repo -b plain",
|
|
29
|
+
fakeDeps({ env: { PR_ROOM_REPOS: value } }),
|
|
30
|
+
),
|
|
31
|
+
).toBeNull();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("blocks a configured repo without a room marker", async () => {
|
|
35
|
+
expect(await check("gh pr create --repo owner/repo --body plain")).toBe(BLOCK_REASON);
|
|
36
|
+
expect(await check("gh pr create -Rowner/repo")).toBe(BLOCK_REASON);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("allows a marker in the inline body", async () => {
|
|
40
|
+
expect(await check(`gh pr create -R owner/repo --body 'Summary\n${ROOM_LINE}'`)).toBeNull();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("resolves body files relative to cd", async () => {
|
|
44
|
+
const paths: string[] = [];
|
|
45
|
+
const deps = fakeDeps({
|
|
46
|
+
readFile: async (path) => {
|
|
47
|
+
paths.push(path);
|
|
48
|
+
return ROOM_LINE;
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
expect(
|
|
52
|
+
await check("cd /checkout && gh pr create -R owner/repo --body-file body.md", deps),
|
|
53
|
+
).toBeNull();
|
|
54
|
+
expect(paths).toEqual(["/checkout/body.md"]);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("allows a heredoc body with a marker", async () => {
|
|
58
|
+
expect(
|
|
59
|
+
await check(`gh pr create -R owner/repo --body-file - <<'EOF'\n${ROOM_LINE}\nEOF`),
|
|
60
|
+
).toBeNull();
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("blocks resolved files and heredocs without a marker", async () => {
|
|
64
|
+
expect(await check("gh pr create -R owner/repo -F body.md")).toBe(BLOCK_REASON);
|
|
65
|
+
expect(await check("gh pr create -R owner/repo -F - <<'EOF'\nplain\nEOF")).toBe(BLOCK_REASON);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("allows another repo without reading its body", async () => {
|
|
69
|
+
expect(await check("gh pr create -R other/repo -b plain")).toBeNull();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test.each([
|
|
73
|
+
"GH_REPO=owner/repo gh pr create -b plain",
|
|
74
|
+
"export GH_REPO=owner/repo && gh pr create -b plain",
|
|
75
|
+
])("checks command targeting: %s", async (command) => {
|
|
76
|
+
expect(await check(command)).toBe(BLOCK_REASON);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("allows an ambiguous target unless every candidate is configured", async () => {
|
|
80
|
+
const otherCheckout = fakeDeps({ remoteOrigin: async () => "https://github.com/other/repo" });
|
|
81
|
+
expect(await check("GH_REPO=owner/repo && gh pr create -b plain", otherCheckout)).toBeNull();
|
|
82
|
+
expect(await check("GH_REPO=owner/repo && gh pr create -b plain")).toBe(BLOCK_REASON);
|
|
83
|
+
expect(await check("gh pr create -R owner/repo -R other/repo -b plain")).toBeNull();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("checks inherited GH_REPO and honors an explicit override", async () => {
|
|
87
|
+
const deps = fakeDeps({ env: { PR_ROOM_REPOS: "owner/repo", GH_REPO: "owner/repo" } });
|
|
88
|
+
expect(await check("gh pr create -b plain", deps)).toBe(BLOCK_REASON);
|
|
89
|
+
expect(await check("gh pr create -R other/repo -b plain", deps)).toBeNull();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("trims configured repos and matches case insensitively", async () => {
|
|
93
|
+
const deps = fakeDeps({ env: { PR_ROOM_REPOS: " other/repo, OWNER/REPO , " } });
|
|
94
|
+
expect(await check("gh -R Owner/Repo pr create -b plain", deps)).toBe(BLOCK_REASON);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("allows edits and unrelated commands", async () => {
|
|
98
|
+
expect(await check("gh pr edit 123 -R owner/repo -b plain")).toBeNull();
|
|
99
|
+
expect(await check("gh pr view 123 -R owner/repo")).toBeNull();
|
|
100
|
+
expect(await guardGhPrRoom(undefined, "/w", fakeDeps())).toBeNull();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test.each([
|
|
104
|
+
"https://github.com/Owner/Repo.git",
|
|
105
|
+
"https://github.com/Owner/Repo",
|
|
106
|
+
"git@github.com:Owner/Repo.git",
|
|
107
|
+
"ssh://git@github.com/Owner/Repo.git",
|
|
108
|
+
])("resolves checkout remote %s", async (remote) => {
|
|
109
|
+
const lookups: string[] = [];
|
|
110
|
+
const deps = fakeDeps({
|
|
111
|
+
remoteOrigin: async (cwd) => {
|
|
112
|
+
lookups.push(cwd);
|
|
113
|
+
return remote;
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
expect(await check("cd /checkout && gh pr create -b plain", deps)).toBe(BLOCK_REASON);
|
|
117
|
+
expect(lookups).toEqual(["/checkout"]);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("allows failed remote lookup and unsupported remote forms", async () => {
|
|
121
|
+
expect(
|
|
122
|
+
await check("gh pr create -b plain", fakeDeps({ remoteOrigin: async () => "local/path" })),
|
|
123
|
+
).toBeNull();
|
|
124
|
+
expect(
|
|
125
|
+
await check(
|
|
126
|
+
"gh pr create -b plain",
|
|
127
|
+
fakeDeps({
|
|
128
|
+
remoteOrigin: async () => {
|
|
129
|
+
throw new Error("no checkout");
|
|
130
|
+
},
|
|
131
|
+
}),
|
|
132
|
+
),
|
|
133
|
+
).toBeNull();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// git exports GIT_DIR to a pre-push hook in a linked worktree. The lookup then reads that repo.
|
|
137
|
+
test.skipIf(process.env.GIT_DIR !== undefined)(
|
|
138
|
+
"allows a checkout remote lookup that does not finish",
|
|
139
|
+
async () => {
|
|
140
|
+
const dir = await mkdtemp(join(tmpdir(), "pr-room-fifo-"));
|
|
141
|
+
try {
|
|
142
|
+
expectChildOk(await runChild(["git", "init", "-q", dir]), "git init");
|
|
143
|
+
await rm(join(dir, ".git", "config"));
|
|
144
|
+
expectChildOk(await runChild(["mkfifo", join(dir, ".git", "config")]), "mkfifo");
|
|
145
|
+
await expect(gitRemoteOrigin(dir, 200)).rejects.toThrow();
|
|
146
|
+
const deps = fakeDeps({ remoteOrigin: (cwd) => gitRemoteOrigin(cwd, 200) });
|
|
147
|
+
expect(await guardGhPrRoom({ command: "gh pr create -b plain" }, dir, deps)).toBeNull();
|
|
148
|
+
} finally {
|
|
149
|
+
await rm(dir, { recursive: true, force: true });
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
test.each([
|
|
155
|
+
'gh pr create -R "$UNKNOWN" -b plain',
|
|
156
|
+
'gh pr create -R owner/repo -b "$UNKNOWN"',
|
|
157
|
+
'gh pr create -R owner/repo -F "$UNKNOWN"',
|
|
158
|
+
"cd - && gh pr create -R owner/repo -b plain",
|
|
159
|
+
'cd "$UNKNOWN" && gh pr create -b plain',
|
|
160
|
+
"read GH_REPO && gh pr create -b plain",
|
|
161
|
+
])("allows unresolved inputs: %s", async (command) => {
|
|
162
|
+
expect(await check(command)).toBeNull();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test("allows unreadable body files", async () => {
|
|
166
|
+
const deps = fakeDeps({
|
|
167
|
+
readFile: async () => {
|
|
168
|
+
throw new Error("unreadable");
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
expect(await check("gh pr create -R owner/repo -F missing.md", deps)).toBeNull();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("resolves known variables without changing leak guard texts", async () => {
|
|
175
|
+
expect(await check('REPO=owner/repo && gh pr create -R "$REPO" -b plain')).toBe(BLOCK_REASON);
|
|
176
|
+
expect(await check(`BODY='${ROOM_LINE}' && gh pr create -R owner/repo -b "$BODY"`)).toBeNull();
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test.each([
|
|
180
|
+
"cat room.md | gh pr create -R owner/repo -F -",
|
|
181
|
+
"printf plain | gh pr create -R owner/repo -F -",
|
|
182
|
+
"gh pr create -R owner/repo -b $(cat room.md)",
|
|
183
|
+
"gh pr create -R owner/repo --fill",
|
|
184
|
+
"gh pr create -R owner/repo -f",
|
|
185
|
+
"gh pr create -R owner/repo --fill-first",
|
|
186
|
+
"gh pr create -R owner/repo --fill-verbose",
|
|
187
|
+
"gh pr create -R owner/repo -T template.md -b plain",
|
|
188
|
+
"gh pr create -R owner/repo --template=template.md",
|
|
189
|
+
"gh pr create -R owner/repo -dw -b plain",
|
|
190
|
+
"gh pr create -R owner/repo --web",
|
|
191
|
+
"gh pr create -R owner/repo -e",
|
|
192
|
+
"gh pr create -R owner/repo --editor",
|
|
193
|
+
"gh pr create -R owner/repo --recover input.json",
|
|
194
|
+
])("allows a body the guard cannot read: %s", async (command) => {
|
|
195
|
+
expect(await check(command)).toBeNull();
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test.each([
|
|
199
|
+
"PR room: prr-abcde2345",
|
|
200
|
+
"PR room: prr-abcde234578",
|
|
201
|
+
"PR room: prr-ABCDE23457",
|
|
202
|
+
"prefix PR room: prr-abcde23457",
|
|
203
|
+
])("rejects an invalid marker: %s", async (line) => {
|
|
204
|
+
expect(await check(`gh pr create -R owner/repo -b '${line}'`)).toBe(BLOCK_REASON);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("does not accept a marker only in the title", async () => {
|
|
208
|
+
expect(await check(`gh pr create -R owner/repo -t '${ROOM_LINE}' -b plain`)).toBe(BLOCK_REASON);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("checks every create invocation", async () => {
|
|
212
|
+
expect(await check(`gh pr edit 1 -b plain && gh pr create -R owner/repo -b plain`)).toBe(
|
|
213
|
+
BLOCK_REASON,
|
|
214
|
+
);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
test("Codex runs the room guard after the leak guard, including app-server sessions", async () => {
|
|
218
|
+
const roomDeps = fakeDeps();
|
|
219
|
+
const leakDeps = {
|
|
220
|
+
env: { SWARM_CODEX_APP_SERVER: "1" },
|
|
221
|
+
readFile: roomDeps.readFile,
|
|
222
|
+
repoVisibility: async () => "PUBLIC",
|
|
223
|
+
};
|
|
224
|
+
const msg = {
|
|
225
|
+
hook_event_name: "PreToolUse",
|
|
226
|
+
cwd: "/w",
|
|
227
|
+
tool_input: { command: "gh pr create -R owner/repo -b plain" },
|
|
228
|
+
};
|
|
229
|
+
expect(await codexPreToolUseBlock(msg, leakDeps, roomDeps)).toBe(BLOCK_REASON);
|
|
230
|
+
const leaky = {
|
|
231
|
+
...msg,
|
|
232
|
+
tool_input: {
|
|
233
|
+
command:
|
|
234
|
+
"gh pr create -R owner/repo -b 'https://acme.slack.com/archives/C01234567/p1700000000000000'",
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
expect(await codexPreToolUseBlock(leaky, leakDeps, roomDeps)).toContain("PR body leak check:");
|
|
238
|
+
expect(
|
|
239
|
+
await codexPreToolUseBlock({ ...msg, hook_event_name: "PostToolUse" }, leakDeps, roomDeps),
|
|
240
|
+
).toBeNull();
|
|
241
|
+
});
|
|
242
|
+
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { isInternalConfigKey, validateConfigValue } from "../be/swarm-config-guard";
|
|
5
|
+
import { buildListing, parseTelemetryRanking } from "../ranking/telemetry-ranking";
|
|
6
|
+
import { RANKING_TAXONOMY } from "../telemetry-contract/ranking-taxonomy.gen";
|
|
7
|
+
import { orgIdentityStatus } from "../utils/org-identity";
|
|
8
|
+
|
|
9
|
+
const FIXTURES = join(import.meta.dir, "..", "telemetry-contract", "ranking", "fixtures");
|
|
10
|
+
const read = (kind: string, file: string) => readFileSync(join(FIXTURES, kind, file), "utf8");
|
|
11
|
+
|
|
12
|
+
describe("telemetry_ranking contract fixtures", () => {
|
|
13
|
+
const expected = JSON.parse(readFileSync(join(FIXTURES, "expected.json"), "utf8")) as {
|
|
14
|
+
invalid: Record<string, string>;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
test("the fixture set is complete", () => {
|
|
18
|
+
expect(readdirSync(join(FIXTURES, "valid")).length).toBeGreaterThan(0);
|
|
19
|
+
expect(readdirSync(join(FIXTURES, "invalid")).sort()).toEqual(
|
|
20
|
+
Object.keys(expected.invalid).sort(),
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
for (const file of readdirSync(join(FIXTURES, "valid"))) {
|
|
25
|
+
test(`valid/${file} parses`, () => {
|
|
26
|
+
expect(() => parseTelemetryRanking(read("valid", file))).not.toThrow();
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
for (const file of readdirSync(join(FIXTURES, "invalid"))) {
|
|
31
|
+
test(`invalid/${file} is rejected (${expected.invalid[file]})`, () => {
|
|
32
|
+
expect(() => parseTelemetryRanking(read("invalid", file))).toThrow();
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe("buildListing", () => {
|
|
38
|
+
const identity = orgIdentityStatus({ SWARM_ORG_NAME: "Acme", SWARM_ORG_WEBSITE: "acme.dev" });
|
|
39
|
+
|
|
40
|
+
test("copies the name and domain from the org identity and blanks to null", () => {
|
|
41
|
+
const listing = buildListing(
|
|
42
|
+
identity,
|
|
43
|
+
{
|
|
44
|
+
category: "engineering",
|
|
45
|
+
subcategory: "qa-testing",
|
|
46
|
+
description: " ",
|
|
47
|
+
sourceCodeUrl: null,
|
|
48
|
+
},
|
|
49
|
+
new Date("2026-10-18T10:00:00Z"),
|
|
50
|
+
);
|
|
51
|
+
expect(listing).toEqual({
|
|
52
|
+
v: 1,
|
|
53
|
+
name: "Acme",
|
|
54
|
+
domain: "acme.dev",
|
|
55
|
+
category: "engineering",
|
|
56
|
+
subcategory: "qa-testing",
|
|
57
|
+
description: null,
|
|
58
|
+
sourceCodeUrl: null,
|
|
59
|
+
updatedAt: "2026-10-18T10:00:00.000Z",
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("refuses a missing identity and an http source URL", () => {
|
|
64
|
+
expect(() =>
|
|
65
|
+
buildListing(orgIdentityStatus({ SWARM_ORG_NAME: "Swarm", SWARM_ORG_WEBSITE: "acme.dev" }), {
|
|
66
|
+
category: "other",
|
|
67
|
+
}),
|
|
68
|
+
).toThrow("SWARM_ORG_NAME");
|
|
69
|
+
expect(() =>
|
|
70
|
+
buildListing(identity, { category: "other", sourceCodeUrl: "http://github.com/acme" }),
|
|
71
|
+
).toThrow();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("a v2.4.0 listing with a claim still parses", () => {
|
|
75
|
+
const legacy = { ...JSON.parse(read("valid", "full.json")), claim: `rk_${"0".repeat(32)}` };
|
|
76
|
+
expect(parseTelemetryRanking(JSON.stringify(legacy)).claim).toBe(legacy.claim);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("the taxonomy comes from the vendored contract", () => {
|
|
80
|
+
expect(RANKING_TAXONOMY).toEqual(
|
|
81
|
+
JSON.parse(readFileSync(join(FIXTURES, "..", "taxonomy.json"), "utf8")),
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe("config guard", () => {
|
|
87
|
+
test("telemetry_ranking is internal and validated", () => {
|
|
88
|
+
expect(isInternalConfigKey("telemetry_ranking")).toBe(true);
|
|
89
|
+
expect(validateConfigValue("telemetry_ranking", "{}")).toContain("telemetry_ranking");
|
|
90
|
+
expect(validateConfigValue("telemetry_ranking", read("valid", "full.json"))).toBeNull();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("PUBLIC_RANKING_ENABLED is a boolean", () => {
|
|
94
|
+
expect(validateConfigValue("PUBLIC_RANKING_ENABLED", "true")).toBeNull();
|
|
95
|
+
expect(validateConfigValue("PUBLIC_RANKING_ENABLED", "yes please")).not.toBeNull();
|
|
96
|
+
});
|
|
97
|
+
});
|