@desplega.ai/agent-swarm 2.5.4 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/{acp-adapter-ek38cwsv.js → acp-adapter-ma1js5n7.js} +9 -9
- package/dist/{actions-92mepq03.js → actions-1vhmtvg9.js} +21 -21
- package/dist/{amp-adapter-9gaxg015.js → amp-adapter-sxqqvmdk.js} +4 -4
- package/dist/{app-ctn8gsgx.js → app-mwx1d5a9.js} +3 -3
- package/dist/{assistant-zyr5kz47.js → assistant-yx1p6g82.js} +30 -26
- package/dist/{boot-reembed-w75maga2.js → boot-reembed-5h37a2c2.js} +1 -1
- package/dist/{boot-reembed-dwzfttdr.js → boot-reembed-7h78vmvv.js} +2 -1
- package/dist/{boot-scrub-sweep-e9kq43kf.js → boot-scrub-sweep-8mawd4gw.js} +4 -4
- package/dist/{boot-seeds-x28v3fhb.js → boot-seeds-ca84dhnn.js} +4 -4
- package/dist/{claude-adapter-f0afqnne.js → claude-adapter-wqt4hfkq.js} +9 -9
- package/dist/{claude-managed-adapter-x81a7r65.js → claude-managed-adapter-6fjqtr2z.js} +2 -2
- package/dist/{claude-managed-setup-bqzwjd9x.js → claude-managed-setup-rqv98sea.js} +10 -3
- package/dist/{claude-sdk-session-7b7whkaq.js → claude-sdk-session-f29f3b2x.js} +11 -11
- package/dist/{cli-sxgtf5fw.js → cli-07q03xm2.js} +1 -1
- package/dist/{cli-kxpfx2xr.js → cli-0z1xjzwa.js} +2 -2
- package/dist/{cli-g9vd9ece.js → cli-1hhbzkbb.js} +14 -16
- package/dist/{cli-qqzmjxkf.js → cli-1m9x8dr5.js} +11 -6
- package/dist/{cli-jbnn4vxb.js → cli-2pvzmas1.js} +3 -3
- package/dist/{cli-x77gen2p.js → cli-2y7v133v.js} +7 -7
- package/dist/{cli-dew6x255.js → cli-39aet655.js} +2 -2
- package/dist/{cli-g1cw7xtt.js → cli-39rrw8yp.js} +1 -1
- package/dist/{cli-jb30dzvk.js → cli-48gxjmcz.js} +530 -190
- package/dist/{cli-pzatvw94.js → cli-54sj64fc.js} +2 -2
- package/dist/{cli-bvpgr9ny.js → cli-60sty27p.js} +5 -5
- package/dist/{cli-asq5ydm4.js → cli-6a9qfa8b.js} +411 -69
- package/dist/{cli-277c31dw.js → cli-6mgdba42.js} +225 -3
- package/dist/{cli-19202kew.js → cli-6srv653c.js} +5 -5
- package/dist/{cli-n4c8e3s5.js → cli-708wdy32.js} +1 -1
- package/dist/{cli-pasyvjmm.js → cli-737pyqxr.js} +1 -1
- package/dist/{cli-6x9ma5w5.js → cli-803188pb.js} +1 -1
- package/dist/{cli-hh5we352.js → cli-86ehn4s5.js} +7 -7
- package/dist/{cli-9s05422w.js → cli-8mbc9za9.js} +1 -1
- package/dist/{cli-ff1tm9n9.js → cli-8wmj18cz.js} +1 -1
- package/dist/{cli-1r5ad7j5.js → cli-9gsm2rzp.js} +12 -12
- package/dist/{cli-mz9afxxf.js → cli-9pshx6rd.js} +17 -3
- package/dist/{cli-s682zgqz.js → cli-a6ds2epe.js} +1 -1
- package/dist/{cli-6a1xg1p7.js → cli-a89zdqz9.js} +2 -2
- package/dist/{cli-03vpq45j.js → cli-ay40dnj3.js} +2 -2
- package/dist/{cli-tsma7p8v.js → cli-aypd09nf.js} +2 -2
- package/dist/{cli-ethgy7ha.js → cli-b3yzyc57.js} +2 -2
- package/dist/{cli-bn0rewsy.js → cli-c081bj3b.js} +2 -2
- package/dist/{cli-mmhj8vd7.js → cli-c668vvcf.js} +11 -2
- package/dist/{cli-sbwsxvtd.js → cli-d20qvpr2.js} +2 -2
- package/dist/{cli-c952c7ac.js → cli-e1595bgx.js} +3 -3
- package/dist/{cli-621ce7kw.js → cli-f12mr20z.js} +3 -3
- package/dist/{cli-dqygeeh5.js → cli-f1e0ptjj.js} +10 -1
- package/dist/{cli-xzfq4at3.js → cli-f4dbh7a5.js} +16 -12
- package/dist/{cli-ha9rmwgq.js → cli-fb8m699c.js} +2 -2
- package/dist/{cli-5bnsc0e1.js → cli-gsfk58nw.js} +5 -5
- package/dist/cli-gt4h8cxw.js +895 -0
- package/dist/{cli-nwyw3zhg.js → cli-gxee0bqa.js} +1 -1
- package/dist/{cli-25n3mv5d.js → cli-h5jeg46p.js} +6 -6
- package/dist/{cli-gn5af0x5.js → cli-he9gbdx0.js} +62 -12
- package/dist/{cli-gbfxp48k.js → cli-j0ppywdr.js} +42 -35
- package/dist/{cli-k4tjsdx9.js → cli-j3y0x2n4.js} +277 -199
- package/dist/{cli-7p4jxgtt.js → cli-j9bpmykh.js} +75 -27
- package/dist/{cli-dabehxhp.js → cli-kt907da6.js} +1 -1
- package/dist/{cli-wx2nj19m.js → cli-kv7sbc2c.js} +6 -6
- package/dist/{cli-abs8ndtr.js → cli-m30k9777.js} +26 -26
- package/dist/{cli-cz5nmdxt.js → cli-m5e802r7.js} +2 -2
- package/dist/{cli-0shx9j93.js → cli-pfk92eqz.js} +2 -2
- package/dist/{cli-xtrz6bzs.js → cli-ptfq202x.js} +5 -3
- package/dist/{cli-6n8axjv1.js → cli-qj442fws.js} +2 -2
- package/dist/{cli-c61mbdds.js → cli-rd5m70gj.js} +4 -4
- package/dist/{cli-eftzyh9y.js → cli-s09j9vp5.js} +2 -2
- package/dist/{cli-m4ecdxvz.js → cli-s0mgx214.js} +7 -7
- package/dist/{cli-wghjkr0e.js → cli-sgqm2582.js} +10 -7
- package/dist/{cli-2655k16q.js → cli-sjctjr48.js} +2 -2
- package/dist/{cli-nzfnt2gs.js → cli-tf1n0r0b.js} +1 -1
- package/dist/{cli-x0f2v69c.js → cli-twctnkng.js} +12 -7
- package/dist/{cli-j6rktc91.js → cli-v6shg7na.js} +1 -1
- package/dist/{cli-gx4rm0qx.js → cli-vz78s5wm.js} +1 -1
- package/dist/{cli-gwhjmh8g.js → cli-wqr7cea4.js} +5 -5
- package/dist/{cli-e42vss7b.js → cli-x37r7qwf.js} +16 -8
- package/dist/{cli-4b2z6n0m.js → cli-yd2nreqw.js} +17 -8
- package/dist/{cli-64mh9mf3.js → cli-ymjpkxgn.js} +69 -17
- package/dist/{cli-5kqnemen.js → cli-yvy48785.js} +1 -1
- package/dist/{cli-9j129brq.js → cli-yxbsmx56.js} +6 -6
- package/dist/cli.js +22 -20
- package/dist/{codex-adapter-wch12q99.js → codex-adapter-zk59x2zh.js} +7 -7
- package/dist/{codex-hook-brpnycn5.js → codex-hook-xyjhvxwd.js} +1 -1
- package/dist/{codex-login-rn7c5gxt.js → codex-login-8cg0vhf2.js} +1 -1
- package/dist/{codex-session-runner-41pfcr95.js → codex-session-runner-kh6s4j3n.js} +8 -8
- package/dist/{commands-9849dkj6.js → commands-68et1qv2.js} +2 -2
- package/dist/{controller-ja0sd95p.js → controller-fyrbnzg4.js} +4 -4
- package/dist/{controllers-connect-xp05dj6x.js → controllers-connect-nep9h2xv.js} +23 -23
- package/dist/{cursor-adapter-623qxqew.js → cursor-adapter-yss3pg7t.js} +2 -2
- package/dist/{db-w759qmx0.js → db-28gjyvxj.js} +4 -2
- package/dist/{db-47vj40dy.js → db-8ftwvzba.js} +3 -3
- package/dist/{devin-adapter-4ycy4vrf.js → devin-adapter-byen2cxt.js} +1 -1
- package/dist/{dispatcher-7b8e4z2j.js → dispatcher-hayexmkr.js} +20 -20
- package/dist/{docker-s84hjs91.js → docker-fdzyewpz.js} +2 -2
- package/dist/{dsh-adapter-2ezw3xea.js → dsh-adapter-46wawff9.js} +2 -2
- package/dist/{e2b-9gkdhm3c.js → e2b-222hsajc.js} +2 -2
- package/dist/{e2b-cjtf8k1z.js → e2b-fn3zacvt.js} +2 -2
- package/dist/{easter-egg-3d-e4qv8dxj.js → easter-egg-3d-8d1e0h4m.js} +1 -1
- package/dist/{event-bus-3z78ep5n.js → event-bus-pghg8hqy.js} +1 -1
- package/dist/{execute-0xpz8950.js → execute-cjrfxfnm.js} +1 -1
- package/dist/{external-channel-e2s028da.js → external-channel-xbmxcg09.js} +2 -2
- package/dist/{grok-adapter-ys7gaqee.js → grok-adapter-ag4wks2a.js} +10 -10
- package/dist/handlers-tzmevqb5.js +41 -0
- package/dist/{heartbeat-w3mntghj.js → heartbeat-psfvcgwp.js} +23 -23
- package/dist/{hook-g7kvv6x9.js → hook-8pz04697.js} +6 -6
- package/dist/{hook-3jjywnpb.js → hook-cyq4mryn.js} +8 -8
- package/dist/{http-tkp7d4kb.js → http-haj1nd6k.js} +921 -169
- package/dist/{index-q7m26kvs.js → index-jwt2bj6f.js} +265 -97
- package/dist/{index-3n8m9ea2.js → index-q52f47pe.js} +1 -1
- package/dist/{index-31ceyf42.js → index-syvhnvtw.js} +1 -1
- package/dist/index-y1ajx04a.js +43 -0
- package/dist/{index-1ef1myq2.js → index-z5h22pes.js} +20 -20
- package/dist/{index-xdaahthb.js → index-zyq8ct52.js} +20 -20
- package/dist/{keepalive-v4ta8wgg.js → keepalive-xdvsamvj.js} +4 -4
- package/dist/lead-hqg57kdp.js +79 -0
- package/dist/{maintenance-84n1ex21.js → maintenance-4y2xzh3a.js} +1 -1
- package/dist/{oauth-refresh-sweep-vjnnandw.js → oauth-refresh-sweep-jd4r7zbs.js} +9 -4
- package/dist/{onboard-sfmpe4np.js → onboard-18447q9q.js} +6 -6
- package/dist/{opencode-adapter-jmpst4q4.js → opencode-adapter-26b2frdf.js} +7 -7
- package/dist/{otel-impl-v6cc0zve.js → otel-impl-f9tgr8yw.js} +2 -2
- package/dist/{pi-mono-adapter-fk8fkcf6.js → pi-mono-adapter-qn6qbga1.js} +38 -17
- package/dist/{pricing-refresh-fsk1e1zc.js → pricing-refresh-dw2tgb53.js} +3 -3
- package/dist/{rbac-roles-prmkdnkw.js → rbac-roles-aygm2760.js} +2 -2
- package/dist/{rbac-roles-zxmxqa39.js → rbac-roles-s4nwj2m3.js} +1 -1
- package/dist/render-v2-yfvdfr0x.js +16 -0
- package/dist/{resume-frdtgfa0.js → resume-1bmvjbf3.js} +9 -9
- package/dist/{runtime-bt5pkpm1.js → runtime-f7tpzedy.js} +1 -1
- package/dist/{session-access-fpez4y5q.js → session-access-r5garej6.js} +4 -4
- package/dist/session-source-backfill-424wzv0r.js +110 -0
- package/dist/{setup-mq6m3m8q.js → setup-8tygvxkq.js} +1 -1
- package/dist/{virtual-modules-mnfbh5mr.js → virtual-modules-xsw323b3.js} +2 -2
- package/dist/{work-objects-kfahhjsq.js → work-objects-ggnghpgf.js} +23 -23
- package/dist/worker-sqjd1nqf.js +79 -0
- package/dist/{x-pbpezr0s.js → x-wxrzvzx8.js} +2 -2
- package/openapi.json +1324 -14
- package/package.json +5 -3
- package/src/assets/source-key.ts +95 -0
- package/src/be/automation-preflight.ts +2 -0
- package/src/be/db/api-keys.ts +89 -0
- package/src/be/db/session-labels.ts +28 -0
- package/src/be/db/session-moves.ts +5 -6
- package/src/be/db/session-tree.ts +153 -14
- package/src/be/db/tasks/read.ts +11 -0
- package/src/be/db/tasks/write.ts +6 -1
- package/src/be/db/workflow-runs.ts +219 -6
- package/src/be/db-retention.ts +61 -6
- package/src/be/db.ts +135 -132
- package/src/be/memory/providers/openai-embedding.ts +5 -1
- package/src/be/memory/task-learning.ts +155 -0
- package/src/be/migrations/244_api_key_validation.sql +4 -0
- package/src/be/migrations/245_script_executor.sql +3 -0
- package/src/be/oauth-refresh-sweep.ts +2 -1
- package/src/be/onboarding.ts +4 -0
- package/src/be/scripts/boot-reembed.ts +2 -0
- package/src/be/scripts/db.ts +14 -4
- package/src/be/scripts/embeddings.ts +2 -0
- package/src/be/scripts/run-saved.ts +1 -0
- package/src/be/scripts/typecheck.ts +10 -4
- package/src/be/seed-skills/index.ts +7 -0
- package/src/be/session-source-backfill.ts +163 -0
- package/src/be/swarm-config-guard.ts +1 -0
- package/src/comb/review-batch.ts +28 -1
- package/src/comb/templates.ts +7 -2
- package/src/commands/claude-managed-setup.ts +7 -1
- package/src/commands/credential-refresh.ts +76 -1
- package/src/commands/credential-wait.ts +2 -2
- package/src/commands/onboard/compose-generator.ts +5 -3
- package/src/commands/provider-credentials.ts +212 -171
- package/src/commands/runner.ts +25 -4
- package/src/extensions/apply-task-create.ts +8 -1
- package/src/extensions/contract-types.generated.ts +1 -1
- package/src/extensions/contract.ts +5 -0
- package/src/heartbeat/heartbeat.ts +14 -1
- package/src/heartbeat/script-runner-probe.ts +47 -0
- package/src/http/all-routes.ts +2 -0
- package/src/http/api-keys.ts +77 -9
- package/src/http/assets.ts +15 -0
- package/src/http/codex-oauth-accounts.ts +191 -0
- package/src/http/comb.ts +15 -2
- package/src/http/config.ts +1 -1
- package/src/http/core.ts +6 -0
- package/src/http/fleet-benchmark.ts +5 -2
- package/src/http/idempotency-key.ts +1 -1
- package/src/http/index.ts +14 -3
- package/src/http/integrations.ts +5 -1
- package/src/http/kv.ts +8 -3
- package/src/http/onboarding.ts +6 -1
- package/src/http/pages-public.ts +38 -5
- package/src/http/pool-keys.ts +354 -0
- package/src/http/scripts.ts +27 -0
- package/src/http/sessions.ts +37 -12
- package/src/http/stats.ts +1 -0
- package/src/http/status.ts +26 -12
- package/src/http/tasks.ts +3 -0
- package/src/http/used-by.ts +7 -2
- package/src/http/workflows.ts +88 -9
- package/src/http/x.ts +5 -0
- package/src/integrations/chat-sdk/inbound.ts +27 -0
- package/src/kv-reserved-namespaces.ts +19 -0
- package/src/prompts/base-prompt.ts +21 -1
- package/src/prompts/session-templates.ts +17 -3
- package/src/providers/acp-targets.ts +1 -1
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/claude-adapter.ts +5 -1
- package/src/providers/claude-managed-adapter.ts +16 -9
- package/src/providers/codex-adapter.ts +71 -11
- package/src/providers/dsh-adapter.ts +1 -1
- package/src/providers/pi-mono-adapter.ts +41 -11
- package/src/scheduler/scheduler.ts +5 -3
- package/src/scripts-runtime/eval-harness.ts +8 -1
- package/src/scripts-runtime/executors/native.ts +22 -16
- package/src/scripts-runtime/executors/quickjs-prelude.ts +103 -1
- package/src/scripts-runtime/executors/quickjs-runner.ts +2 -1
- package/src/scripts-runtime/executors/quickjs.ts +7 -0
- package/src/scripts-runtime/executors/registry.ts +32 -0
- package/src/scripts-runtime/executors/types.ts +7 -0
- package/src/scripts-runtime/host-validated-args.ts +133 -0
- package/src/scripts-runtime/loader.ts +30 -3
- package/src/scripts-runtime/swarm-sdk.ts +16 -3
- package/src/scripts-runtime/types/stdlib.d.ts +10 -2
- package/src/scripts-runtime/types/swarm-extension.d.ts +17 -8
- package/src/scripts-runtime/types/swarm-sdk.d.ts +10 -2
- package/src/slack/assistant.ts +7 -0
- package/src/slack/channel-labels.ts +74 -0
- package/src/slack/handlers.ts +8 -0
- package/src/slack/inbound-files.ts +5 -2
- package/src/slack/thread-buffer.ts +4 -0
- package/src/tasks/sibling-awareness.ts +12 -3
- package/src/telemetry-context.ts +5 -6
- package/src/telemetry-identity.ts +3 -3
- package/src/tests/api-key-status-by-suffix.test.ts +242 -0
- package/src/tests/api-keys-validation.test.ts +170 -0
- package/src/tests/base-prompt.test.ts +70 -2
- package/src/tests/claude-managed-adapter.test.ts +46 -0
- package/src/tests/claude-routes-e2e.test.ts +7 -2
- package/src/tests/codex-credential-pools.test.ts +120 -0
- package/src/tests/codex-gateway-route.test.ts +165 -0
- package/src/tests/codex-oauth-accounts.test.ts +357 -0
- package/src/tests/comb-review-batch.test.ts +63 -2
- package/src/tests/community-linear-delivery-loop.test.ts +2099 -0
- package/src/tests/config-pool-keys.test.ts +533 -0
- package/src/tests/credential-check.test.ts +294 -4
- package/src/tests/credential-refresh.test.ts +172 -1
- package/src/tests/db-retention.test.ts +9 -3
- package/src/tests/db-task-read-contract.test.ts +1 -0
- package/src/tests/db-task-write-contract.test.ts +1 -3
- package/src/tests/docs-index-footprint.test.js +3 -3
- package/src/tests/docs-index.test.ts +135 -0
- package/src/tests/dsh-adapter.test.ts +1 -1
- package/src/tests/extensions-pre-task.test.ts +53 -1
- package/src/tests/fetch-resolved-env.test.ts +16 -2
- package/src/tests/fixtures/extensions/record-task-create/hooks.ts +9 -0
- package/src/tests/fixtures/extensions/record-task-create/manifest.json +7 -0
- package/src/tests/fleet-benchmark.test.ts +8 -0
- package/src/tests/get-workflow-run-tool.test.ts +98 -0
- package/src/tests/heartbeat-checklist.test.ts +89 -0
- package/src/tests/heartbeat-script-runner-probe.test.ts +83 -0
- package/src/tests/integrations-http.test.ts +32 -2
- package/src/tests/internal-ai/complete-structured.test.ts +37 -1
- package/src/tests/internal-ai/credentials.test.ts +23 -0
- package/src/tests/kv-http.test.ts +32 -0
- package/src/tests/kv-tool.test.ts +25 -0
- package/src/tests/multi-runtime-registration.test.ts +12 -12
- package/src/tests/oauth-refresh-sweep.test.ts +34 -1
- package/src/tests/onboard-compose.test.ts +20 -0
- package/src/tests/onboarding.test.ts +108 -1
- package/src/tests/openai-embedding-credentials.test.ts +35 -0
- package/src/tests/opencode-adapter.test.ts +2 -1
- package/src/tests/openrouter-attribution-call-sites.test.ts +2 -2
- package/src/tests/org-website.test.ts +36 -0
- package/src/tests/pages-frame-ancestors.test.ts +162 -0
- package/src/tests/pi-codemode.test.ts +2 -1
- package/src/tests/pi-mono-adapter.test.ts +54 -0
- package/src/tests/pi-native-mcp.test.ts +13 -1
- package/src/tests/pi-tool-exposure.test.ts +8 -8
- package/src/tests/probe-tool-schema-tokens.test.ts +144 -0
- package/src/tests/prompt-template-session.test.ts +4 -3
- package/src/tests/provider-key-probes.test.ts +181 -0
- package/src/tests/rbac-e2e-helpers.test.ts +88 -1
- package/src/tests/rbac-e2e-helpers.ts +36 -11
- package/src/tests/request-composition-derive.test.ts +329 -0
- package/src/tests/script-connections.test.ts +4 -0
- package/src/tests/script-executor-conformance.test.ts +36 -1
- package/src/tests/script-executor-quickjs-pool.test.ts +50 -0
- package/src/tests/script-executor-quickjs.test.ts +140 -0
- package/src/tests/script-host-validated-args-native.test.ts +196 -0
- package/src/tests/script-per-script-executor.test.ts +415 -0
- package/src/tests/script-runs-retention.test.ts +212 -0
- package/src/tests/scripts-external-api.test.ts +45 -0
- package/src/tests/sdk-allowlist.test.ts +42 -2
- package/src/tests/self-improvement.test.ts +4 -4
- package/src/tests/session-source-backfill.test.ts +429 -0
- package/src/tests/session-source-keys.test.ts +236 -0
- package/src/tests/session-source-labels.test.ts +419 -0
- package/src/tests/sessions-detail-batch.test.ts +2 -0
- package/src/tests/sessions-move.test.ts +131 -0
- package/src/tests/sessions-tree.test.ts +299 -2
- package/src/tests/slack-inbound-files.test.ts +103 -23
- package/src/tests/status.test.ts +101 -18
- package/src/tests/store-progress-learning.test.ts +288 -0
- package/src/tests/tasks-workflow-run-filter.test.ts +146 -0
- package/src/tests/test-net.ts +31 -0
- package/src/tests/transcription.test.ts +8 -0
- package/src/tests/ui-logs-parser.test.ts +180 -0
- package/src/tests/used-by-route.test.ts +11 -0
- package/src/tests/workflow-cascade-cancel.test.ts +172 -0
- package/src/tests/workflow-engine-v2.test.ts +116 -1
- package/src/tests/workflow-http-v2.test.ts +131 -0
- package/src/tests/workflow-mcp-trigger-schema.test.ts +209 -0
- package/src/tests/workflow-run-finished-event.test.ts +263 -0
- package/src/tests/workflow-step-order.test.ts +92 -0
- package/src/tests/workflow-sub-workflow.test.ts +30 -0
- package/src/tests/workflow-swarm-script.test.ts +34 -0
- package/src/tools/get-tasks.ts +8 -0
- package/src/tools/kv/kv-read-auth.ts +4 -2
- package/src/tools/script-common.ts +1 -0
- package/src/tools/script-run.ts +7 -0
- package/src/tools/script-upsert.ts +7 -0
- package/src/tools/store-progress.ts +67 -1
- package/src/tools/task-action.ts +1 -1
- package/src/tools/workflows/get-workflow-run.ts +14 -4
- package/src/tools/workflows/trigger-workflow.ts +104 -10
- package/src/types.ts +36 -1
- package/src/utils/company-profile.ts +51 -1
- package/src/utils/credentials.ts +10 -4
- package/src/utils/internal-ai/complete-structured.ts +5 -0
- package/src/utils/internal-ai/credentials.ts +4 -3
- package/src/utils/provider-key-probes.ts +328 -0
- package/src/utils/transcription.ts +4 -1
- package/src/workflows/engine.ts +18 -16
- package/src/workflows/executors/sub-workflow.ts +8 -1
- package/src/workflows/executors/swarm-script.ts +5 -0
- package/src/workflows/foreach-driver.ts +5 -11
- package/src/workflows/node-timeout.ts +30 -0
- package/templates/ai-toolbox.manifest.json +2 -2
- package/templates/community/linear-delivery-loop/PLAYBOOK.md +323 -0
- package/templates/community/linear-delivery-loop/README.md +38 -0
- package/templates/community/linear-delivery-loop/config.example.json +31 -0
- package/templates/community/linear-delivery-loop/lead-prompt.md +34 -0
- package/templates/community/linear-delivery-loop/schedule.json +10 -0
- package/templates/community/linear-delivery-loop/scripts/linear-delivery-setup.ts +148 -0
- package/templates/community/linear-delivery-loop/scripts/linear-delivery-tick.ts +1781 -0
- package/templates/skills/researching/content.md +2 -0
- package/templates/skills/swarm-docs/config.json +14 -0
- package/templates/skills/swarm-docs/content.md +144 -0
- package/templates/skills/swarm-scripts/SKILL.md +1 -0
- package/templates/skills/swarm-scripts/content.md +1 -0
- package/dist/cli-7421jzsw.js +0 -84
- package/dist/cli-e45e4m6b.js +0 -413
- package/dist/cli-zw88zjba.js +0 -307
- package/dist/handlers-wy1dr6ja.js +0 -41
- package/dist/index-nmm79qgr.js +0 -43
- package/dist/lead-sawnyqxk.js +0 -79
- package/dist/render-v2-pjwxvnkw.js +0 -12
- package/dist/worker-v5e5rxj3.js +0 -79
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
import { baseUrlEnvVar, providerForKeyVar } from "@desplega/model-catalog";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import {
|
|
5
|
+
deleteSwarmConfigByKey,
|
|
6
|
+
getDbClient,
|
|
7
|
+
getKeyStatuses,
|
|
8
|
+
getSwarmConfigs,
|
|
9
|
+
recordKeyValidation,
|
|
10
|
+
} from "../be/db";
|
|
11
|
+
import { retirePoolKeyStatus } from "../be/db/api-keys";
|
|
12
|
+
import { upsertSwarmConfigWithPolicyMirror } from "../be/multi-runtime";
|
|
13
|
+
import { validateConfigEntry } from "../be/swarm-config-guard";
|
|
14
|
+
import type { SwarmConfig } from "../types";
|
|
15
|
+
import { CREDENTIAL_POOL_VARS, splitCredentialPool } from "../utils/credentials";
|
|
16
|
+
import { checkProviderPoolKey } from "../utils/provider-key-probes";
|
|
17
|
+
import { registerEnvSecrets, scrubSecrets } from "../utils/secret-scrubber";
|
|
18
|
+
import { ensureConfigAdmin, requestSecretReadScope, runConfigWriteSideEffects } from "./config";
|
|
19
|
+
import { getDeploymentEnvValue } from "./core";
|
|
20
|
+
import { route } from "./route-def";
|
|
21
|
+
import { jsonError } from "./utils";
|
|
22
|
+
|
|
23
|
+
export const PoolKeyStatusSchema = z.enum(["ok", "rejected", "rate_limited", "unchecked"]);
|
|
24
|
+
export type PoolKeyStatus = z.infer<typeof PoolKeyStatusSchema>;
|
|
25
|
+
export const PoolKeySchema = z.object({
|
|
26
|
+
index: z.number().int().min(0),
|
|
27
|
+
suffix: z.string(),
|
|
28
|
+
status: PoolKeyStatusSchema,
|
|
29
|
+
error: z.string().nullable(),
|
|
30
|
+
checkedAt: z.string().nullable(),
|
|
31
|
+
rateLimitedUntil: z.string().nullable(),
|
|
32
|
+
});
|
|
33
|
+
export type PoolKey = z.infer<typeof PoolKeySchema>;
|
|
34
|
+
export const PoolKeysResponseSchema = z.object({
|
|
35
|
+
key: z.string(),
|
|
36
|
+
source: z.enum(["stored", "env", "none"]),
|
|
37
|
+
readOnly: z.boolean(),
|
|
38
|
+
keys: z.array(PoolKeySchema),
|
|
39
|
+
});
|
|
40
|
+
export type PoolKeysResponse = z.infer<typeof PoolKeysResponseSchema>;
|
|
41
|
+
export const PoolKeyCheckResultSchema = z.enum(["verified", "rejected", "not_tested"]);
|
|
42
|
+
export type PoolKeyCheckResult = z.infer<typeof PoolKeyCheckResultSchema>;
|
|
43
|
+
export const PoolKeyCheckSchema = z.object({
|
|
44
|
+
suffix: z.string(),
|
|
45
|
+
result: PoolKeyCheckResultSchema,
|
|
46
|
+
httpStatus: z.number().nullable(),
|
|
47
|
+
error: z.string().nullable(),
|
|
48
|
+
});
|
|
49
|
+
export type PoolKeyCheck = z.infer<typeof PoolKeyCheckSchema>;
|
|
50
|
+
|
|
51
|
+
const poolVar = z
|
|
52
|
+
.string()
|
|
53
|
+
.refine(
|
|
54
|
+
(key) => key !== "CODEX_OAUTH" && CREDENTIAL_POOL_VARS.includes(key),
|
|
55
|
+
"Choose a credential pool variable.",
|
|
56
|
+
);
|
|
57
|
+
const getPoolKeys = route({
|
|
58
|
+
method: "get",
|
|
59
|
+
path: "/api/config/pool-keys",
|
|
60
|
+
pattern: ["api", "config", "pool-keys"],
|
|
61
|
+
summary: "List credential pool suffixes and status",
|
|
62
|
+
tags: ["Config"],
|
|
63
|
+
query: z.object({ key: poolVar }),
|
|
64
|
+
responses: {
|
|
65
|
+
200: { description: "Credential pool", schema: PoolKeysResponseSchema },
|
|
66
|
+
400: { description: "Invalid pool variable" },
|
|
67
|
+
403: { description: "Secret read permission required" },
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
const addPoolKeys = route({
|
|
71
|
+
method: "post",
|
|
72
|
+
path: "/api/config/pool-keys",
|
|
73
|
+
pattern: ["api", "config", "pool-keys"],
|
|
74
|
+
summary: "Test and append keys to a credential pool",
|
|
75
|
+
tags: ["Config"],
|
|
76
|
+
rbac: { permission: "config.write.any" },
|
|
77
|
+
body: z.object({
|
|
78
|
+
key: poolVar,
|
|
79
|
+
value: z.string(),
|
|
80
|
+
// The OpenRouter card can switch away from a stored gateway before the new key is saved.
|
|
81
|
+
baseUrl: z
|
|
82
|
+
.string()
|
|
83
|
+
.nullable()
|
|
84
|
+
.optional()
|
|
85
|
+
.describe(
|
|
86
|
+
"Base URL for the check only. Null tests the provider default. Omit to use the stored URL.",
|
|
87
|
+
),
|
|
88
|
+
}),
|
|
89
|
+
responses: {
|
|
90
|
+
200: {
|
|
91
|
+
description: "Updated credential pool and checks",
|
|
92
|
+
schema: PoolKeysResponseSchema.extend({ checks: z.array(PoolKeyCheckSchema) }),
|
|
93
|
+
},
|
|
94
|
+
400: { description: "Invalid key" },
|
|
95
|
+
403: { description: "Config administration and permission to read all secrets required" },
|
|
96
|
+
409: { description: "Duplicate key or deployment env pool" },
|
|
97
|
+
422: {
|
|
98
|
+
description: "Provider rejected a key. Nothing was saved.",
|
|
99
|
+
schema: z.object({ error: z.string(), checks: z.array(PoolKeyCheckSchema) }),
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
const deletePoolKey = route({
|
|
104
|
+
method: "delete",
|
|
105
|
+
path: "/api/config/pool-keys",
|
|
106
|
+
pattern: ["api", "config", "pool-keys"],
|
|
107
|
+
summary: "Remove one credential pool member",
|
|
108
|
+
tags: ["Config"],
|
|
109
|
+
rbac: { permission: "config.delete.any" },
|
|
110
|
+
query: z.object({
|
|
111
|
+
key: poolVar,
|
|
112
|
+
index: z.coerce.number().int().min(0),
|
|
113
|
+
suffix: z.string().min(1).max(5),
|
|
114
|
+
}),
|
|
115
|
+
responses: {
|
|
116
|
+
200: { description: "Updated credential pool", schema: PoolKeysResponseSchema },
|
|
117
|
+
400: { description: "Invalid pool member" },
|
|
118
|
+
403: { description: "Config administration and permission to read all secrets required" },
|
|
119
|
+
409: { description: "The pool changed or comes from the deployment env" },
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const ENV_POOL_ERROR = "These keys come from the deployment env. Change them there.";
|
|
124
|
+
|
|
125
|
+
async function readPool(key: string) {
|
|
126
|
+
const [row] = await getSwarmConfigs({ scope: "global", key });
|
|
127
|
+
const envValue = getDeploymentEnvValue(key);
|
|
128
|
+
const source = row ? "stored" : envValue ? "env" : "none";
|
|
129
|
+
const value = row?.value ?? envValue ?? "";
|
|
130
|
+
registerEnvSecrets({ [key]: value });
|
|
131
|
+
return { row, source, members: splitCredentialPool(value) } as const;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function poolResponse(key: string): Promise<PoolKeysResponse> {
|
|
135
|
+
const pool = await readPool(key);
|
|
136
|
+
const statuses = await getKeyStatuses(key, "global", null);
|
|
137
|
+
return {
|
|
138
|
+
key,
|
|
139
|
+
source: pool.source,
|
|
140
|
+
readOnly: pool.source === "env",
|
|
141
|
+
keys: pool.members.map((member, index) => {
|
|
142
|
+
const suffix = member.slice(-5);
|
|
143
|
+
const row = statuses.find((status) => status.keySuffix === suffix);
|
|
144
|
+
const rejected = !!row?.lastValidationFailedAt;
|
|
145
|
+
const limited =
|
|
146
|
+
row?.status === "rate_limited" &&
|
|
147
|
+
!!row.rateLimitedUntil &&
|
|
148
|
+
Date.parse(row.rateLimitedUntil) > Date.now();
|
|
149
|
+
return {
|
|
150
|
+
index,
|
|
151
|
+
suffix,
|
|
152
|
+
status: rejected ? "rejected" : limited ? "rate_limited" : row ? "ok" : "unchecked",
|
|
153
|
+
error: rejected ? scrubSecrets(row?.lastValidationError) || null : null,
|
|
154
|
+
checkedAt: rejected ? row!.lastValidationFailedAt : (row?.updatedAt ?? null),
|
|
155
|
+
rateLimitedUntil: row?.rateLimitedUntil ?? null,
|
|
156
|
+
};
|
|
157
|
+
}),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function duplicateError(members: readonly string[], additions: string[]): string | null {
|
|
162
|
+
const seen = new Set(members);
|
|
163
|
+
for (const member of additions) {
|
|
164
|
+
if (seen.has(member)) return `The key ending ${member.slice(-5)} is already in the pool.`;
|
|
165
|
+
seen.add(member);
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async function savePool(key: string, members: string[], row: SwarmConfig | undefined) {
|
|
171
|
+
const value = members.join(",");
|
|
172
|
+
const validationError = validateConfigEntry({
|
|
173
|
+
scope: "global",
|
|
174
|
+
key,
|
|
175
|
+
value,
|
|
176
|
+
exportToEnv: row?.exportToEnv,
|
|
177
|
+
});
|
|
178
|
+
if (validationError) return validationError;
|
|
179
|
+
await upsertSwarmConfigWithPolicyMirror({
|
|
180
|
+
scope: "global",
|
|
181
|
+
key,
|
|
182
|
+
value,
|
|
183
|
+
isSecret: true,
|
|
184
|
+
description: row?.description ?? null,
|
|
185
|
+
envPath: row?.envPath ?? null,
|
|
186
|
+
exportToEnv: row?.exportToEnv,
|
|
187
|
+
});
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** Every response lists the pool's key suffixes and statuses, so each route needs this gate. */
|
|
192
|
+
async function ensureSecretRead(req: IncomingMessage, res: ServerResponse): Promise<boolean> {
|
|
193
|
+
if ((await requestSecretReadScope(req)) === "all") return true;
|
|
194
|
+
jsonError(res, "Reading credential pools requires permission to read all secrets.", 403);
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export async function handlePoolKeys(
|
|
199
|
+
req: IncomingMessage,
|
|
200
|
+
res: ServerResponse,
|
|
201
|
+
pathSegments: string[],
|
|
202
|
+
queryParams: URLSearchParams,
|
|
203
|
+
): Promise<boolean> {
|
|
204
|
+
if (getPoolKeys.match(req.method, pathSegments)) {
|
|
205
|
+
const parsed = await getPoolKeys.parse(req, res, pathSegments, queryParams);
|
|
206
|
+
if (!parsed) return true;
|
|
207
|
+
if (!(await ensureSecretRead(req, res))) return true;
|
|
208
|
+
getPoolKeys.respond(res, 200, await poolResponse(parsed.query.key));
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
if (addPoolKeys.match(req.method, pathSegments)) {
|
|
212
|
+
const parsed = await addPoolKeys.parse(req, res, pathSegments, queryParams);
|
|
213
|
+
if (!parsed) return true;
|
|
214
|
+
if (!(await ensureConfigAdmin(req, res, "config.write.any"))) return true;
|
|
215
|
+
if (!(await ensureSecretRead(req, res))) return true;
|
|
216
|
+
const { key, value, baseUrl } = parsed.body;
|
|
217
|
+
const additions = value
|
|
218
|
+
.split(",")
|
|
219
|
+
.map((part) => part.trim())
|
|
220
|
+
.filter(Boolean);
|
|
221
|
+
if (!additions.length) {
|
|
222
|
+
jsonError(res, "Enter a key.", 400);
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
if (additions.some((part) => /\s/.test(part))) {
|
|
226
|
+
jsonError(res, "A key cannot contain spaces or line breaks.", 400);
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
registerEnvSecrets({ [key]: additions.join(",") });
|
|
230
|
+
const initial = await readPool(key);
|
|
231
|
+
if (initial.source === "env") {
|
|
232
|
+
jsonError(res, ENV_POOL_ERROR, 409);
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
const duplicate = duplicateError(initial.members, additions);
|
|
236
|
+
if (duplicate) {
|
|
237
|
+
jsonError(res, scrubSecrets(duplicate), 409);
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
240
|
+
const env = { ...process.env };
|
|
241
|
+
const provider = providerForKeyVar(key);
|
|
242
|
+
const baseUrlKey =
|
|
243
|
+
key === "OPENROUTER_API_KEY"
|
|
244
|
+
? "OPENROUTER_BASE_URL"
|
|
245
|
+
: provider
|
|
246
|
+
? baseUrlEnvVar(provider)
|
|
247
|
+
: undefined;
|
|
248
|
+
if (baseUrlKey && baseUrl !== undefined) {
|
|
249
|
+
env[baseUrlKey] = baseUrl ?? "";
|
|
250
|
+
} else if (baseUrlKey) {
|
|
251
|
+
const [row] = await getSwarmConfigs({ scope: "global", key: baseUrlKey });
|
|
252
|
+
if (row) env[baseUrlKey] = row.value;
|
|
253
|
+
}
|
|
254
|
+
const checks = await Promise.all(
|
|
255
|
+
additions.map(async (member): Promise<PoolKeyCheck> => {
|
|
256
|
+
try {
|
|
257
|
+
const probe = await checkProviderPoolKey(key, member, env);
|
|
258
|
+
return {
|
|
259
|
+
suffix: member.slice(-5),
|
|
260
|
+
result: probe.skipped
|
|
261
|
+
? "not_tested"
|
|
262
|
+
: probe.ok
|
|
263
|
+
? "verified"
|
|
264
|
+
: probe.httpStatus === 401
|
|
265
|
+
? "rejected"
|
|
266
|
+
: "not_tested",
|
|
267
|
+
httpStatus: probe.httpStatus ?? null,
|
|
268
|
+
error: scrubSecrets(probe.error?.replaceAll(member, "[REDACTED]")) || null,
|
|
269
|
+
};
|
|
270
|
+
} catch {
|
|
271
|
+
return {
|
|
272
|
+
suffix: member.slice(-5),
|
|
273
|
+
result: "not_tested",
|
|
274
|
+
httpStatus: null,
|
|
275
|
+
error: "The provider check did not complete.",
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
}),
|
|
279
|
+
);
|
|
280
|
+
const rejected = checks.find((check) => check.result === "rejected");
|
|
281
|
+
if (rejected) {
|
|
282
|
+
addPoolKeys.respond(res, 422, {
|
|
283
|
+
error: scrubSecrets(
|
|
284
|
+
`The key ending ${rejected.suffix} was rejected (HTTP 401). Nothing was saved.`,
|
|
285
|
+
),
|
|
286
|
+
checks,
|
|
287
|
+
});
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
290
|
+
const result = await getDbClient().transaction(async () => {
|
|
291
|
+
const current = await readPool(key);
|
|
292
|
+
if (current.source === "env") return { error: ENV_POOL_ERROR, status: 409 };
|
|
293
|
+
const duplicate = duplicateError(current.members, additions);
|
|
294
|
+
if (duplicate) return { error: duplicate, status: 409 };
|
|
295
|
+
const members = [...current.members, ...additions];
|
|
296
|
+
const error = await savePool(key, members, current.row);
|
|
297
|
+
if (error) return { error, status: 400 };
|
|
298
|
+
await recordKeyValidation(
|
|
299
|
+
key,
|
|
300
|
+
checks.flatMap((check, index) =>
|
|
301
|
+
check.result === "verified"
|
|
302
|
+
? [{ keyIndex: current.members.length + index, keySuffix: check.suffix, ok: true }]
|
|
303
|
+
: [],
|
|
304
|
+
),
|
|
305
|
+
"global",
|
|
306
|
+
null,
|
|
307
|
+
{ retireAbsent: false },
|
|
308
|
+
);
|
|
309
|
+
return { value: members.join(",") };
|
|
310
|
+
});
|
|
311
|
+
if ("error" in result) {
|
|
312
|
+
jsonError(res, scrubSecrets(result.error), result.status);
|
|
313
|
+
return true;
|
|
314
|
+
}
|
|
315
|
+
await runConfigWriteSideEffects("global", key, result.value);
|
|
316
|
+
addPoolKeys.respond(res, 200, { ...(await poolResponse(key)), checks });
|
|
317
|
+
return true;
|
|
318
|
+
}
|
|
319
|
+
if (deletePoolKey.match(req.method, pathSegments)) {
|
|
320
|
+
const parsed = await deletePoolKey.parse(req, res, pathSegments, queryParams);
|
|
321
|
+
if (!parsed) return true;
|
|
322
|
+
if (!(await ensureConfigAdmin(req, res, "config.delete.any"))) return true;
|
|
323
|
+
if (!(await ensureSecretRead(req, res))) return true;
|
|
324
|
+
const { key, index, suffix } = parsed.query;
|
|
325
|
+
const result = await getDbClient().transaction(async () => {
|
|
326
|
+
const current = await readPool(key);
|
|
327
|
+
if (current.source === "env") return { error: ENV_POOL_ERROR, status: 409 };
|
|
328
|
+
if (!current.members[index] || current.members[index].slice(-5) !== suffix) {
|
|
329
|
+
return { error: "The key list changed. Reload it and try again.", status: 409 };
|
|
330
|
+
}
|
|
331
|
+
const members = current.members.filter((_, position) => position !== index);
|
|
332
|
+
if (members.length) {
|
|
333
|
+
const error = await savePool(key, members, current.row);
|
|
334
|
+
if (error) return { error, status: 400 };
|
|
335
|
+
} else {
|
|
336
|
+
await deleteSwarmConfigByKey("global", null, key);
|
|
337
|
+
}
|
|
338
|
+
await retirePoolKeyStatus(
|
|
339
|
+
key,
|
|
340
|
+
suffix,
|
|
341
|
+
members.map((member, keyIndex) => ({ keySuffix: member.slice(-5), keyIndex })),
|
|
342
|
+
);
|
|
343
|
+
return { value: members.join(",") };
|
|
344
|
+
});
|
|
345
|
+
if ("error" in result) {
|
|
346
|
+
jsonError(res, scrubSecrets(result.error), result.status);
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
await runConfigWriteSideEffects("global", key, result.value);
|
|
350
|
+
deletePoolKey.respond(res, 200, await poolResponse(key));
|
|
351
|
+
return true;
|
|
352
|
+
}
|
|
353
|
+
return false;
|
|
354
|
+
}
|
package/src/http/scripts.ts
CHANGED
|
@@ -40,12 +40,14 @@ import {
|
|
|
40
40
|
typecheckScript,
|
|
41
41
|
} from "../be/scripts/typecheck";
|
|
42
42
|
import { can } from "../rbac";
|
|
43
|
+
import { isExecutorAllowedByDeployment } from "../scripts-runtime/executors/registry";
|
|
43
44
|
import { extractScriptSignature, type ScriptSignature } from "../scripts-runtime/extract-signature";
|
|
44
45
|
import { checkQuickJSCompat, quickJSCompatWarnings } from "../scripts-runtime/quickjs-compat";
|
|
45
46
|
import {
|
|
46
47
|
ScriptApiAuthModeSchema,
|
|
47
48
|
ScriptApiRecordSchema,
|
|
48
49
|
type ScriptDetail,
|
|
50
|
+
ScriptExecutorNameSchema,
|
|
49
51
|
ScriptFsModeSchema,
|
|
50
52
|
type ScriptListItem,
|
|
51
53
|
type ScriptRecord,
|
|
@@ -67,6 +69,7 @@ const upsertBodySchema = z.object({
|
|
|
67
69
|
intent: z.string().default(""),
|
|
68
70
|
scope: ScriptScopeSchema.default("agent"),
|
|
69
71
|
fsMode: ScriptFsModeSchema.default("none"),
|
|
72
|
+
executor: ScriptExecutorNameSchema.nullable().optional(),
|
|
70
73
|
});
|
|
71
74
|
|
|
72
75
|
const runBodySchema = z
|
|
@@ -77,6 +80,7 @@ const runBodySchema = z
|
|
|
77
80
|
intent: z.string().default(""),
|
|
78
81
|
scope: ScriptScopeSchema.optional(),
|
|
79
82
|
fsMode: ScriptFsModeSchema.default("none"),
|
|
83
|
+
executor: ScriptExecutorNameSchema.nullable().optional(),
|
|
80
84
|
idempotencyKey: z.string().max(200).optional(),
|
|
81
85
|
})
|
|
82
86
|
.refine((body) => Boolean(body.name) !== Boolean(body.source), {
|
|
@@ -671,6 +675,15 @@ export async function handleScripts(
|
|
|
671
675
|
}
|
|
672
676
|
}
|
|
673
677
|
|
|
678
|
+
if (parsed.body.executor && !isExecutorAllowedByDeployment(parsed.body.executor)) {
|
|
679
|
+
jsonError(
|
|
680
|
+
res,
|
|
681
|
+
'Executor "native" is not permitted when deployment SCRIPT_EXECUTOR="quickjs"',
|
|
682
|
+
400,
|
|
683
|
+
);
|
|
684
|
+
return true;
|
|
685
|
+
}
|
|
686
|
+
|
|
674
687
|
const secretRefusal = refuseSourceWithSecrets(parsed.body.source);
|
|
675
688
|
if (secretRefusal) {
|
|
676
689
|
json(res, secretRefusal, 400);
|
|
@@ -708,6 +721,7 @@ export async function handleScripts(
|
|
|
708
721
|
signatureJson: signatureJsonFor(parsed.body.source),
|
|
709
722
|
argsJsonSchema,
|
|
710
723
|
fsMode: parsed.body.fsMode,
|
|
724
|
+
executor: parsed.body.executor,
|
|
711
725
|
agentId: agent.id,
|
|
712
726
|
isScratch: false,
|
|
713
727
|
typeChecked: true,
|
|
@@ -758,6 +772,7 @@ export async function handleScripts(
|
|
|
758
772
|
|
|
759
773
|
let source = parsed.body.source;
|
|
760
774
|
let fsMode = parsed.body.fsMode;
|
|
775
|
+
let executor = parsed.body.executor ?? null;
|
|
761
776
|
let namedScript: ScriptRecord | null = null;
|
|
762
777
|
if (parsed.body.name) {
|
|
763
778
|
const script = await resolveScript(parsed.body.name, agent.id, parsed.body.scope);
|
|
@@ -768,6 +783,7 @@ export async function handleScripts(
|
|
|
768
783
|
namedScript = script;
|
|
769
784
|
source = script.source;
|
|
770
785
|
fsMode = script.fsMode;
|
|
786
|
+
executor = parsed.body.executor !== undefined ? parsed.body.executor : script.executor;
|
|
771
787
|
}
|
|
772
788
|
|
|
773
789
|
if (fsMode === "workspace-rw") {
|
|
@@ -775,6 +791,15 @@ export async function handleScripts(
|
|
|
775
791
|
return true;
|
|
776
792
|
}
|
|
777
793
|
|
|
794
|
+
if (executor && !isExecutorAllowedByDeployment(executor)) {
|
|
795
|
+
jsonError(
|
|
796
|
+
res,
|
|
797
|
+
'Executor "native" is not permitted when deployment SCRIPT_EXECUTOR="quickjs"',
|
|
798
|
+
400,
|
|
799
|
+
);
|
|
800
|
+
return true;
|
|
801
|
+
}
|
|
802
|
+
|
|
778
803
|
const startedAt = new Date().toISOString();
|
|
779
804
|
// Touch before executing so an already-stale scratch script isn't reaped
|
|
780
805
|
// by the retention sweep while this run is still in flight.
|
|
@@ -786,6 +811,7 @@ export async function handleScripts(
|
|
|
786
811
|
source: source as string,
|
|
787
812
|
args: parsed.body.args,
|
|
788
813
|
fsMode,
|
|
814
|
+
executor,
|
|
789
815
|
agentId: agent.id,
|
|
790
816
|
runtimeInstanceId,
|
|
791
817
|
egressSecrets: credentials.egressSecrets,
|
|
@@ -954,6 +980,7 @@ export async function handleScripts(
|
|
|
954
980
|
isScratch: script.isScratch,
|
|
955
981
|
typeChecked: script.typeChecked,
|
|
956
982
|
fsMode: script.fsMode,
|
|
983
|
+
executor: script.executor,
|
|
957
984
|
createdByAgentId: script.createdByAgentId,
|
|
958
985
|
createdAt: script.createdAt,
|
|
959
986
|
updatedAt: script.updatedAt,
|
package/src/http/sessions.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
normalizeAssetKey,
|
|
7
7
|
parseAssetKey,
|
|
8
8
|
} from "../assets/key";
|
|
9
|
+
import { SOURCE_GROUPS_READ_ONLY, sourceMoveError, sourceOfKey } from "../assets/source-key";
|
|
9
10
|
import { AssetKeyAuthorizationError, authorizeAssetKeyWrite } from "../be/asset-key-auth";
|
|
10
11
|
import { resolveHttpAuditUserId } from "../be/audit-user";
|
|
11
12
|
import {
|
|
@@ -20,13 +21,19 @@ import {
|
|
|
20
21
|
getTaskById,
|
|
21
22
|
listRecentSessions,
|
|
22
23
|
} from "../be/db";
|
|
24
|
+
import { getSessionLabels } from "../be/db/session-labels";
|
|
23
25
|
import {
|
|
24
26
|
listPrefixRoots,
|
|
25
27
|
listSharedPrefixRoots,
|
|
26
28
|
moveSessionChains,
|
|
27
29
|
type SessionKeyMove,
|
|
28
30
|
} from "../be/db/session-moves";
|
|
29
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
getSessionGroupKey,
|
|
33
|
+
getSessionReadState,
|
|
34
|
+
getSessionTree,
|
|
35
|
+
triageSessionList,
|
|
36
|
+
} from "../be/db/session-tree";
|
|
30
37
|
import { getUserGrant } from "../be/rbac-roles";
|
|
31
38
|
import {
|
|
32
39
|
getSessionRoot,
|
|
@@ -53,6 +60,7 @@ import {
|
|
|
53
60
|
import { can, isRbacEnabled } from "../rbac";
|
|
54
61
|
import { sessionAccessTopic } from "../realtime/auth";
|
|
55
62
|
import { realtimeBus } from "../realtime/bus";
|
|
63
|
+
import { warmSlackChannelLabels } from "../slack/channel-labels";
|
|
56
64
|
import {
|
|
57
65
|
type AgentTask,
|
|
58
66
|
AgentTaskSchema,
|
|
@@ -152,6 +160,7 @@ const SessionTreeGroupSchema = SessionSectionCountsSchema.extend({
|
|
|
152
160
|
lastActivityAt: z.string(),
|
|
153
161
|
});
|
|
154
162
|
const SessionTreeResponseSchema = z.object({
|
|
163
|
+
labels: z.record(z.string(), z.string()).optional(),
|
|
155
164
|
groups: z.array(SessionTreeGroupSchema),
|
|
156
165
|
inbox: SessionSectionCountsSchema,
|
|
157
166
|
later: SessionSectionCountsSchema,
|
|
@@ -738,7 +747,7 @@ async function checkSessionMove(
|
|
|
738
747
|
actor: SessionActor,
|
|
739
748
|
req: IncomingMessage,
|
|
740
749
|
myAgentId: string | undefined,
|
|
741
|
-
options: { ownerOnly?: boolean } = {},
|
|
750
|
+
options: { ownerOnly?: boolean; destination?: string | null } = {},
|
|
742
751
|
): Promise<{ reason: string } | { taskIds: string[] }> {
|
|
743
752
|
const root = await getTaskById(rootTaskId);
|
|
744
753
|
if (!root) return { reason: SESSION_MOVE_UNAVAILABLE };
|
|
@@ -752,6 +761,8 @@ async function checkSessionMove(
|
|
|
752
761
|
if (!hasSessionAccess(access, "write")) return { reason: "Session write access required" };
|
|
753
762
|
}
|
|
754
763
|
if (root.parentTaskId) return { reason: "Task is not a session root" };
|
|
764
|
+
const sourceError = sourceMoveError(options.destination ?? null, root);
|
|
765
|
+
if (sourceError) return { reason: sourceError };
|
|
755
766
|
const tasks = await getRootTaskChain(root.id);
|
|
756
767
|
for (const task of tasks) {
|
|
757
768
|
if (!(await canMutateTaskNamespace(task, myAgentId, req))) {
|
|
@@ -778,6 +789,8 @@ const getSession = route({
|
|
|
778
789
|
chain: z.array(TaskWithSteeringSchema),
|
|
779
790
|
access: SessionAccessSchema,
|
|
780
791
|
owner: SessionOwnerSchema.nullable(),
|
|
792
|
+
/** The group of `root` with the list's rule: a source thread with two or more roots is its own group. */
|
|
793
|
+
groupKey: z.string(),
|
|
781
794
|
approvals: z.array(SessionApprovalSchema).optional(),
|
|
782
795
|
/** The viewer's read state, with the list's rule. Omitted for callers that act for no user. */
|
|
783
796
|
readState: z.enum(["unread", "read", "later", "done"]).optional(),
|
|
@@ -1012,15 +1025,18 @@ export async function handleSessions(
|
|
|
1012
1025
|
const parsed = await sessionTree.parse(req, res, pathSegments, queryParams);
|
|
1013
1026
|
if (!parsed) return true;
|
|
1014
1027
|
const actor = await resolveSessionActor(req, myAgentId);
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1028
|
+
const tree = await getSessionTree(actor, undefined, {
|
|
1029
|
+
mine: parsed.query.show !== "all",
|
|
1030
|
+
hideSource: parsed.query.hideSource,
|
|
1031
|
+
showSource: parsed.query.showSource,
|
|
1032
|
+
});
|
|
1033
|
+
// Labels cover hidden sources too: a pane or tab of a hidden source still names its group.
|
|
1034
|
+
const { sourceGroups, slackChannels, threadLabels, ...counts } = tree;
|
|
1035
|
+
const labels = { ...threadLabels, ...(await getSessionLabels(sourceGroups)) };
|
|
1036
|
+
warmSlackChannelLabels(
|
|
1037
|
+
slackChannels.map((id) => `shared/slack/${id}/`).filter((key) => labels[key] === undefined),
|
|
1023
1038
|
);
|
|
1039
|
+
sessionTree.respond(res, 200, { ...counts, labels });
|
|
1024
1040
|
return true;
|
|
1025
1041
|
}
|
|
1026
1042
|
|
|
@@ -1054,7 +1070,9 @@ export async function handleSessions(
|
|
|
1054
1070
|
const skipped: { rootTaskId: string; reason: string }[] = [];
|
|
1055
1071
|
const moves: SessionKeyMove[] = [];
|
|
1056
1072
|
for (const rootId of new Set(parsed.body.rootTaskIds)) {
|
|
1057
|
-
const checked = await checkSessionMove(rootId, actor, req, myAgentId
|
|
1073
|
+
const checked = await checkSessionMove(rootId, actor, req, myAgentId, {
|
|
1074
|
+
destination: key,
|
|
1075
|
+
});
|
|
1058
1076
|
if ("reason" in checked) {
|
|
1059
1077
|
skipped.push({ rootTaskId: rootId, reason: checked.reason });
|
|
1060
1078
|
} else {
|
|
@@ -1076,6 +1094,10 @@ export async function handleSessions(
|
|
|
1076
1094
|
const parsed = await moveSessionPrefix.parse(req, res, pathSegments, queryParams);
|
|
1077
1095
|
if (!parsed) return true;
|
|
1078
1096
|
const from = normalizeAssetKey(parsed.body.from);
|
|
1097
|
+
if (sourceOfKey(from) || (parsed.body.to && sourceOfKey(normalizeAssetKey(parsed.body.to)))) {
|
|
1098
|
+
jsonError(res, SOURCE_GROUPS_READ_ONLY, 400);
|
|
1099
|
+
return true;
|
|
1100
|
+
}
|
|
1079
1101
|
if (parseAssetKey(from).relativeSegments.length === 0) {
|
|
1080
1102
|
jsonError(res, "Cannot move a namespace root", 400);
|
|
1081
1103
|
return true;
|
|
@@ -1126,6 +1148,7 @@ export async function handleSessions(
|
|
|
1126
1148
|
}
|
|
1127
1149
|
const checked = await checkSessionMove(root.id, actor, req, myAgentId, {
|
|
1128
1150
|
ownerOnly: true,
|
|
1151
|
+
destination: nextKey,
|
|
1129
1152
|
});
|
|
1130
1153
|
if ("reason" in checked) {
|
|
1131
1154
|
skipped.push({ rootTaskId: root.id, reason: checked.reason });
|
|
@@ -1175,7 +1198,7 @@ export async function handleSessions(
|
|
|
1175
1198
|
const chain = await getRootTaskChain(parsed.params.rootTaskId);
|
|
1176
1199
|
// Load steering, citations and pending approvals for the already loaded chain.
|
|
1177
1200
|
const tasks = [root, ...chain];
|
|
1178
|
-
const [steering, citations, approvals, read] = await Promise.all([
|
|
1201
|
+
const [steering, citations, approvals, read, groupKey] = await Promise.all([
|
|
1179
1202
|
getTaskSteeringFieldsForTasks(tasks),
|
|
1180
1203
|
getTaskCitationsForTasks(tasks.map((task) => task.id)),
|
|
1181
1204
|
getPendingApprovalsForTasks(tasks.map((task) => task.id)),
|
|
@@ -1184,6 +1207,7 @@ export async function handleSessions(
|
|
|
1184
1207
|
actor.kind === "user" && actor.userId
|
|
1185
1208
|
? getSessionReadState(sessionRoot.id, actor.userId)
|
|
1186
1209
|
: Promise.resolve(null),
|
|
1210
|
+
getSessionGroupKey(root),
|
|
1187
1211
|
]);
|
|
1188
1212
|
const decorate = (task: AgentTask) => ({
|
|
1189
1213
|
...task,
|
|
@@ -1195,6 +1219,7 @@ export async function handleSessions(
|
|
|
1195
1219
|
chain: chain.map(decorate),
|
|
1196
1220
|
access,
|
|
1197
1221
|
owner,
|
|
1222
|
+
groupKey,
|
|
1198
1223
|
...(approvals.length ? { approvals } : {}),
|
|
1199
1224
|
...(read ?? {}),
|
|
1200
1225
|
});
|
package/src/http/stats.ts
CHANGED
|
@@ -86,6 +86,7 @@ const SwarmMetricsSchema = z.object({
|
|
|
86
86
|
agentLog: RetentionTableStatsSchema.optional(),
|
|
87
87
|
events: RetentionTableStatsSchema.optional(),
|
|
88
88
|
contextVersions: RetentionTableStatsSchema.optional(),
|
|
89
|
+
scriptRuns: RetentionTableStatsSchema.optional(),
|
|
89
90
|
}),
|
|
90
91
|
});
|
|
91
92
|
|