@desplega.ai/agent-swarm 2.6.0 → 2.7.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 +2 -0
- package/dist/{acp-adapter-dgfakfpw.js → acp-adapter-8dn5tq56.js} +6 -6
- package/dist/{actions-fbqma8aa.js → actions-3r6gw7dy.js} +18 -18
- package/dist/{amp-adapter-en0jy6hs.js → amp-adapter-wjtq413p.js} +3 -3
- package/dist/{anthropic-messages-txs190dd.js → anthropic-messages-n4cby1rz.js} +4 -4
- package/dist/{app-k8a5cgpp.js → app-d53yhykk.js} +3 -3
- package/dist/{assistant-a16433gr.js → assistant-ksrawj3k.js} +20 -20
- package/dist/{boot-reembed-6c1sjfnr.js → boot-reembed-fxfw17m2.js} +11 -3
- package/dist/{boot-reembed-f4nn561n.js → boot-reembed-k5y5xbj0.js} +15 -8
- package/dist/{boot-scrub-sweep-d4w9gajf.js → boot-scrub-sweep-se41j9fd.js} +3 -3
- package/dist/{boot-seeds-9bmkspyw.js → boot-seeds-r3kwwv8b.js} +4 -4
- package/dist/{claude-adapter-nys1kjxa.js → claude-adapter-ahmyf231.js} +8 -8
- package/dist/{claude-managed-adapter-55207973.js → claude-managed-adapter-5knamegc.js} +3 -3
- package/dist/{claude-managed-setup-3cm35gey.js → claude-managed-setup-mdc7dy6b.js} +3 -3
- package/dist/{claude-sdk-session-nw5yd8cp.js → claude-sdk-session-n5wen9zk.js} +8729 -8600
- package/dist/{cli-683y0b3a.js → cli-0dqtv2jg.js} +2 -2
- package/dist/{cli-a34pr3w1.js → cli-0tww7dnz.js} +2 -2
- package/dist/{cli-w2656ka1.js → cli-0w4e4v58.js} +7 -7
- package/dist/{cli-5sckpqcx.js → cli-2sjm3t4r.js} +1 -1
- package/dist/{cli-gf2850q7.js → cli-3cywb44q.js} +18 -1
- package/dist/{cli-8wkb09q5.js → cli-3k9jej0c.js} +2 -2
- package/dist/{cli-3aqd4jaa.js → cli-3v2cyqb8.js} +3 -3
- package/dist/{cli-emhjcj2y.js → cli-5qt9vzmq.js} +89 -20
- package/dist/{cli-abkm71vr.js → cli-5rp1w4cr.js} +1 -1
- package/dist/{cli-gf95q3bm.js → cli-6vwjsetx.js} +4 -4
- package/dist/{cli-k21gx5ht.js → cli-6w2yrrc0.js} +10 -10
- package/dist/{cli-m70bajzr.js → cli-75tdydgv.js} +1 -1
- package/dist/{cli-5rj83bam.js → cli-8feswf2v.js} +47 -36
- package/dist/{cli-sxgtf5fw.js → cli-8wd3mbyj.js} +1 -1
- package/dist/{cli-ca6banwz.js → cli-9csfthbw.js} +10 -8
- package/dist/{cli-dqygeeh5.js → cli-b41p764a.js} +31 -3
- package/dist/{cli-m5a9m0ak.js → cli-c1t533hh.js} +3 -3
- package/dist/{cli-2sjhpsns.js → cli-c28jxpcv.js} +1 -1
- package/dist/{cli-bqztfyve.js → cli-cb50g9cd.js} +1 -1
- package/dist/{cli-ag2yrnt5.js → cli-d8gaphtr.js} +2 -2
- package/dist/{cli-9abdgn5t.js → cli-dgtfysne.js} +3 -3
- package/dist/{cli-nq677csy.js → cli-dscnhn18.js} +1 -1
- package/dist/{cli-3fwqy71j.js → cli-dv11pgpv.js} +39 -32
- package/dist/{cli-v49fkghp.js → cli-ekptgk2m.js} +6 -6
- package/dist/{cli-c2habvsf.js → cli-f3mzy662.js} +17 -17
- package/dist/{cli-zqne1qcj.js → cli-fk7k8s9x.js} +2 -2
- package/dist/{cli-1tem6ns9.js → cli-gt4h8cxw.js} +3 -2
- package/dist/{cli-vbhgkw9h.js → cli-h3e7bhvn.js} +3 -3
- package/dist/{cli-ffvsj2qw.js → cli-hkxqdmnb.js} +3 -3
- package/dist/{cli-a2rr0fmg.js → cli-hpkvgdad.js} +7 -4
- package/dist/{cli-art7c1zh.js → cli-hq2v4rr5.js} +37 -9
- package/dist/{cli-sqma6vw1.js → cli-k1zb7raw.js} +5 -5
- package/dist/{cli-4rvr7zfx.js → cli-k4awgvzm.js} +5 -0
- package/dist/{cli-3ndmpxxv.js → cli-k7d3hvya.js} +2 -2
- package/dist/{cli-3gfvmzht.js → cli-kbtdftbw.js} +74 -26
- package/dist/{cli-hfeb68z4.js → cli-kj69b9tw.js} +34 -6
- package/dist/{cli-az25fn5f.js → cli-kk9jy4nm.js} +2 -2
- package/dist/{cli-39aet655.js → cli-m52t4cta.js} +1 -1
- package/dist/{cli-cz37jqss.js → cli-mdadewqp.js} +5 -5
- package/dist/{cli-esakngh8.js → cli-nf6k60sn.js} +1 -1
- package/dist/{cli-j6rktc91.js → cli-nzdf09js.js} +1 -1
- package/dist/{cli-rr7zx19j.js → cli-prcyzecg.js} +12 -12
- package/dist/{cli-4hxrf0p4.js → cli-pzafyqtk.js} +393 -62
- package/dist/{cli-g8ka5j4j.js → cli-qvaa1gjp.js} +173 -52
- package/dist/{cli-d2dpf95n.js → cli-rg4b2g6j.js} +1 -1
- package/dist/{cli-kf4jree9.js → cli-rm66vqdz.js} +1 -1
- package/dist/{cli-4rf0f5vg.js → cli-s504c3w7.js} +5 -3
- package/dist/{cli-nn6vx2mq.js → cli-swkx9jm2.js} +7 -5
- package/dist/{cli-g20jjwt4.js → cli-thsaq4pd.js} +16 -16
- package/dist/{cli-aj8djyqt.js → cli-twjnyvs8.js} +2 -2
- package/dist/{cli-03gg9pd1.js → cli-typdpe8g.js} +4 -4
- package/dist/{cli-mtnj4fwa.js → cli-vg8pwmsd.js} +6 -6
- package/dist/{cli-gnsb1zxa.js → cli-vhpckzvp.js} +1 -1
- package/dist/{cli-7enjwya9.js → cli-vz78s5wm.js} +1 -1
- package/dist/{cli-dv97kdzv.js → cli-wdxgsdz5.js} +2 -2
- package/dist/{cli-7a6p4e65.js → cli-wtjg6je3.js} +1 -1
- package/dist/{cli-6y51ryva.js → cli-x3cngb1h.js} +44 -12
- package/dist/{cli-n7asmce6.js → cli-xbz6mcg4.js} +3 -3
- package/dist/{cli-24j1jeeb.js → cli-y4tdxsqb.js} +3 -3
- package/dist/{cli-mvjz5an3.js → cli-yqrsp3ny.js} +7 -7
- package/dist/cli.js +31 -21
- package/dist/{codex-adapter-d95q4qsn.js → codex-adapter-7wepzq15.js} +6 -6
- package/dist/{codex-hook-brpnycn5.js → codex-hook-pbdh4xhx.js} +2 -2
- package/dist/{codex-session-runner-gdwd8h0c.js → codex-session-runner-02sb49e4.js} +6 -6
- package/dist/{commands-jqmgk54b.js → commands-3rcspxct.js} +2 -2
- package/dist/{controller-z3jq589f.js → controller-8ar4avd6.js} +3 -3
- package/dist/{controllers-connect-r2za39ef.js → controllers-connect-ajvh9h0v.js} +19 -19
- package/dist/{cursor-adapter-4mr4jgkw.js → cursor-adapter-gd0sa7bh.js} +1 -1
- package/dist/{db-w36wwb5g.js → db-dv9j7ts5.js} +2 -2
- package/dist/{db-ppv2tze7.js → db-z26vgdvj.js} +3 -3
- package/dist/{dispatcher-pv5h77vf.js → dispatcher-8m0htjq8.js} +17 -17
- package/dist/{dsh-adapter-d4pdpb3r.js → dsh-adapter-y8gyk130.js} +1 -1
- package/dist/{e2b-fn3zacvt.js → e2b-74vg9b7e.js} +1 -1
- package/dist/{e2b-222hsajc.js → e2b-jyay8wzn.js} +1 -1
- package/dist/{easter-egg-3d-4sp4dvmc.js → easter-egg-3d-28f880xx.js} +1 -1
- package/dist/{execute-jev839y1.js → execute-9seb79vh.js} +1 -1
- package/dist/{external-channel-y6nkzhnn.js → external-channel-smz7jyth.js} +2 -2
- package/dist/{getMachineId-bsd-brn0xym3.js → getMachineId-bsd-whfre6gr.js} +3 -3
- package/dist/{getMachineId-darwin-t339sfxm.js → getMachineId-darwin-5zjynb19.js} +3 -3
- package/dist/{getMachineId-linux-5eq95d8b.js → getMachineId-linux-8mksrrsq.js} +3 -3
- package/dist/{getMachineId-unsupported-qavrtc7j.js → getMachineId-unsupported-fxtqsdt2.js} +3 -3
- package/dist/{getMachineId-win-q7fe1wx7.js → getMachineId-win-nzfmz857.js} +3 -3
- package/dist/{grok-adapter-t08b9ntm.js → grok-adapter-z0nnvtbn.js} +7 -7
- package/dist/handlers-pspryh6g.js +41 -0
- package/dist/{heartbeat-vn31xwdk.js → heartbeat-f928aqyp.js} +20 -20
- package/dist/{hook-cpjs8q51.js → hook-d7c605r4.js} +7 -7
- package/dist/{hook-6d0zbvvt.js → hook-q9hv5xf9.js} +5 -5
- package/dist/{http-9c6medda.js → http-q6j4k989.js} +1000 -251
- package/dist/{index-k0yt1tvq.js → index-5nb266jr.js} +17 -17
- package/dist/{index-ysd45kgc.js → index-8fxt5eah.js} +10 -10
- package/dist/{index-zgvqzjsw.js → index-cekzwhez.js} +5 -5
- package/dist/{index-38t35h0n.js → index-ch0c1xff.js} +17 -17
- package/dist/{index-pkma1yf8.js → index-ebnjhxh1.js} +1 -1
- package/dist/{index-yqgbfgaj.js → index-ed8dy8rn.js} +20 -20
- package/dist/{index-qhjf2htm.js → index-ejqdkkqx.js} +262 -96
- package/dist/{index-dr94zbnh.js → index-farm1zsn.js} +32661 -29443
- package/dist/{index-g6k9tctj.js → index-yn1yxg85.js} +1 -1
- package/dist/{keepalive-em0117bp.js → keepalive-zzc3hks1.js} +3 -3
- package/dist/lead-c0pjcaw4.js +79 -0
- package/dist/{maintenance-kbme3qy9.js → maintenance-kqsxzpzx.js} +1 -1
- package/dist/{oauth-refresh-sweep-xysj4yse.js → oauth-refresh-sweep-7qt0g55z.js} +8 -3
- package/dist/{onboard-p6q0vz97.js → onboard-k7x13fqg.js} +121 -8
- package/dist/{opencode-adapter-esv3k9yn.js → opencode-adapter-tqvrtter.js} +5 -5
- package/dist/{otel-impl-54qhqh1z.js → otel-impl-sqkf6k3b.js} +97 -97
- package/dist/{pi-mono-adapter-mymbwhj0.js → pi-mono-adapter-m2qnafxm.js} +89 -14
- package/dist/{pricing-refresh-yb75wv33.js → pricing-refresh-wkpwhgvs.js} +3 -3
- package/dist/{rbac-roles-dexhc9x8.js → rbac-roles-dpn0c0wj.js} +7 -4
- package/dist/{rbac-roles-1vp2mw4p.js → rbac-roles-wk7xd6a6.js} +2 -2
- package/dist/{render-v2-tjay5hhf.js → render-v2-ex2m4g2k.js} +3 -3
- package/dist/{resume-8ayf37g5.js → resume-725g9b0b.js} +7 -7
- package/dist/{runtime-hqe5cqcd.js → runtime-kajas15g.js} +1 -1
- package/dist/{session-access-fdhp987s.js → session-access-0j2ezjje.js} +8 -4
- package/dist/{session-source-backfill-qgtc86z1.js → session-source-backfill-62202awe.js} +2 -2
- package/dist/{setup-4rrmqcq3.js → setup-9sj11ax6.js} +1 -1
- package/dist/{virtual-modules-52km00d6.js → virtual-modules-c6fcq6zr.js} +2 -2
- package/dist/{work-objects-p28fgd70.js → work-objects-0y07495y.js} +20 -20
- package/dist/worker-d4q9sqbc.js +79 -0
- package/openapi.json +1536 -407
- package/package.json +7 -5
- package/src/be/automation-preflight.ts +2 -0
- package/src/be/connector-codes.ts +65 -22
- package/src/be/db/api-keys.ts +9 -0
- package/src/be/db/workflow-runs.ts +36 -5
- package/src/be/db.ts +80 -22
- package/src/be/memory/boot-reembed.ts +4 -1
- package/src/be/memory/constants.ts +6 -0
- package/src/be/memory/embedding-recovery.ts +48 -0
- package/src/be/memory/index-content.ts +5 -2
- package/src/be/memory/providers/openai-embedding.ts +65 -14
- package/src/be/memory/task-learning.ts +257 -0
- package/src/be/memory/types.ts +13 -2
- package/src/be/migrations/245_script_executor.sql +3 -0
- package/src/be/migrations/246_connector_code_kind.sql +11 -0
- package/src/be/oauth-refresh-sweep.ts +2 -1
- package/src/be/onboarding.ts +125 -15
- package/src/be/rbac-roles.ts +4 -1
- package/src/be/scripts/boot-reembed.ts +2 -0
- package/src/be/scripts/db.ts +14 -4
- package/src/be/scripts/embeddings.ts +4 -2
- package/src/be/scripts/run-saved.ts +1 -0
- package/src/be/scripts/typecheck.ts +9 -3
- package/src/be/seed/agent-fs-provision.ts +75 -14
- package/src/be/seed-skills/index.ts +7 -0
- package/src/be/swarm-config-guard.ts +3 -0
- package/src/cli.tsx +9 -0
- package/src/comb/config.ts +6 -1
- package/src/commands/onboard/agent-fs-version.ts +5 -0
- package/src/commands/onboard/compose-generator.ts +117 -4
- package/src/commands/onboard/env-generator.ts +9 -0
- package/src/commands/onboard/tei-image.ts +12 -0
- package/src/commands/onboard/types.ts +7 -0
- package/src/commands/onboard.tsx +11 -1
- package/src/commands/provider-credentials.ts +2 -1
- package/src/commands/runner.ts +14 -1
- package/src/heartbeat/heartbeat.ts +14 -1
- package/src/heartbeat/script-runner-probe.ts +47 -0
- package/src/http/agentfs-proxy.ts +369 -0
- package/src/http/all-routes.ts +1 -0
- package/src/http/api-keys.ts +24 -9
- package/src/http/codex-oauth-device.ts +8 -1
- package/src/http/fs.ts +64 -10
- package/src/http/idempotency-key.ts +1 -1
- package/src/http/inbox-state.ts +27 -1
- package/src/http/index.ts +7 -4
- package/src/http/memory.ts +7 -4
- package/src/http/onboarding.ts +139 -11
- package/src/http/pages-public.ts +38 -5
- package/src/http/scripts.ts +27 -0
- package/src/http/session-access.ts +24 -0
- package/src/http/sessions.ts +10 -6
- package/src/http/tasks.ts +85 -11
- package/src/http/telemetry-ui.ts +1 -1
- package/src/http/user-roles.ts +4 -1
- package/src/http/users.ts +272 -3
- package/src/http/utils.ts +22 -3
- package/src/http/workflows.ts +10 -0
- package/src/http/x.ts +5 -0
- package/src/prompts/base-prompt.ts +45 -10
- package/src/prompts/session-templates.ts +43 -5
- package/src/providers/amp-adapter.ts +1 -1
- package/src/providers/codex-adapter.ts +6 -1
- package/src/providers/cursor-adapter.ts +1 -1
- package/src/providers/dsh-adapter.ts +1 -1
- package/src/providers/pi-mono-adapter.ts +99 -14
- package/src/rbac/legacy-policy.ts +1 -0
- package/src/rbac/permissions.ts +4 -0
- package/src/scheduler/scheduler.ts +2 -1
- 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 +9 -2
- package/src/scripts-runtime/types/stdlib.d.ts +9 -1
- package/src/scripts-runtime/types/swarm-sdk.d.ts +9 -1
- package/src/tests/agent-fs-provision-seeder.test.ts +261 -92
- package/src/tests/agentfs-proxy.test.ts +1106 -0
- package/src/tests/api-key-status-by-suffix.test.ts +242 -0
- package/src/tests/base-prompt.test.ts +139 -4
- package/src/tests/codex-adapter.test.ts +46 -8
- package/src/tests/codex-oauth-device.test.ts +12 -1
- package/src/tests/comb-config-guard.test.ts +20 -0
- package/src/tests/comb-links.test.ts +79 -1
- package/src/tests/comb-status.test.ts +44 -0
- package/src/tests/community-linear-delivery-loop.test.ts +2099 -0
- package/src/tests/credential-check.test.ts +4 -2
- 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/fetch-resolved-env.test.ts +16 -2
- package/src/tests/fixtures/onboard/baseline-states.ts +68 -0
- package/src/tests/fixtures/onboard/content.compose.yml.txt +190 -0
- package/src/tests/fixtures/onboard/content.env.txt +29 -0
- package/src/tests/fixtures/onboard/dev.compose.yml.txt +155 -0
- package/src/tests/fixtures/onboard/dev.env.txt +28 -0
- package/src/tests/fixtures/onboard/full-all.compose.yml.txt +582 -0
- package/src/tests/fixtures/onboard/full-all.env.txt +59 -0
- package/src/tests/fixtures/onboard/full.compose.yml.txt +435 -0
- package/src/tests/fixtures/onboard/full.env.txt +36 -0
- package/src/tests/fixtures/onboard/research.compose.yml.txt +155 -0
- package/src/tests/fixtures/onboard/research.env.txt +28 -0
- package/src/tests/fixtures/onboard/solo.compose.yml.txt +85 -0
- package/src/tests/fixtures/onboard/solo.env.txt +26 -0
- package/src/tests/heartbeat-checklist.test.ts +89 -0
- package/src/tests/heartbeat-script-runner-probe.test.ts +83 -0
- package/src/tests/http-log-scrubbing.test.ts +34 -1
- package/src/tests/http-task-write-ownership.test.ts +5 -1
- package/src/tests/invites-http.test.ts +464 -0
- package/src/tests/memory-embedding-input-cap.test.ts +122 -1
- package/src/tests/memory-embedding-recovery.test.ts +137 -0
- package/src/tests/oauth-refresh-sweep.test.ts +34 -1
- package/src/tests/onboard-compose.test.ts +162 -0
- package/src/tests/onboard-env.test.ts +24 -0
- package/src/tests/onboard-non-interactive.test.ts +29 -0
- package/src/tests/onboard-tei-image.test.ts +36 -0
- package/src/tests/onboarding.test.ts +451 -4
- package/src/tests/openai-embedding-provider.test.ts +105 -2
- package/src/tests/opencode-adapter.test.ts +2 -1
- 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-mono-session-cost-on-abort.test.ts +270 -0
- package/src/tests/pi-native-mcp.test.ts +13 -1
- package/src/tests/pi-tool-exposure.test.ts +8 -8
- package/src/tests/preload-env.test.ts +4 -0
- package/src/tests/preload.ts +4 -0
- package/src/tests/probe-tool-schema-tokens.test.ts +144 -0
- package/src/tests/prompt-template-session.test.ts +20 -3
- package/src/tests/rbac-admission-e2e.test.ts +22 -6
- package/src/tests/rbac-admission.test.ts +342 -4
- package/src/tests/rbac-engine.test.ts +1 -0
- package/src/tests/rbac-roles.test.ts +9 -1
- package/src/tests/request-composition-derive.test.ts +329 -0
- package/src/tests/script-connections.test.ts +2 -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/scripts-external-api.test.ts +45 -0
- package/src/tests/sdk-allowlist.test.ts +3 -2
- package/src/tests/self-improvement.test.ts +4 -4
- package/src/tests/send-task-requester-gate.test.ts +64 -3
- package/src/tests/sessions-move.test.ts +39 -1
- package/src/tests/store-progress-learning.test.ts +381 -0
- package/src/tests/ui-logs-parser.test.ts +180 -0
- package/src/tests/whoami-route.test.ts +41 -0
- package/src/tests/workflow-engine-v2.test.ts +116 -1
- 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-swarm-script.test.ts +34 -0
- package/src/tools/inject-learning.ts +1 -1
- package/src/tools/memory-edit.ts +1 -1
- package/src/tools/memory-search.ts +1 -1
- 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 +71 -1
- package/src/tools/utils.ts +12 -1
- package/src/tools/workflows/trigger-workflow.ts +104 -10
- package/src/types.ts +12 -1
- package/src/utils/constants.ts +39 -0
- package/src/utils/credentials.ts +5 -2
- package/src/utils/task-attachment-links.ts +15 -3
- package/src/workflows/engine.ts +18 -16
- 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/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/swarm-docs/config.json +14 -0
- package/templates/skills/swarm-docs/content.md +144 -0
- package/dist/handlers-eqsnmv95.js +0 -41
- package/dist/lead-23e7z59t.js +0 -79
- package/dist/worker-e3w7vye1.js +0 -79
- package/dist/{azure-openai-responses-9kfwhmn7.js → azure-openai-responses-43asa23s.js} +0 -0
- package/dist/{node-vxx896fa.js → node-t28cc56b.js} +0 -0
- package/dist/{openai-codex-responses-79s8xnbh.js → openai-codex-responses-jhr3sjtm.js} +3 -3
- package/dist/{openai-completions-tdbyts0x.js → openai-completions-gs6yn37k.js} +3 -3
- /package/dist/{openai-responses-dm3t5vqp.js → openai-responses-jqm9vwwk.js} +0 -0
package/src/http/users.ts
CHANGED
|
@@ -20,6 +20,8 @@
|
|
|
20
20
|
* DELETE /api/users/:id/mcp-tokens/:tokenId
|
|
21
21
|
* POST /api/users/:id/connector-codes
|
|
22
22
|
* POST /api/connector/exchange (public: the code is the credential)
|
|
23
|
+
* POST /api/invites
|
|
24
|
+
* POST /api/invites/exchange (public: the code is the credential)
|
|
23
25
|
* POST /api/users/:id/merge
|
|
24
26
|
* GET /api/users/:id/events
|
|
25
27
|
* POST /api/users/:id/identities
|
|
@@ -31,7 +33,10 @@ import { z } from "zod";
|
|
|
31
33
|
import {
|
|
32
34
|
createConnectorCode,
|
|
33
35
|
DEFAULT_CONNECTOR_CODE_LABEL,
|
|
36
|
+
DEFAULT_INVITE_CODE_LABEL,
|
|
37
|
+
deleteUnusedInviteCodes,
|
|
34
38
|
exchangeConnectorCode,
|
|
39
|
+
INVITE_CODE_TTL_MS,
|
|
35
40
|
} from "../be/connector-codes";
|
|
36
41
|
import {
|
|
37
42
|
createUser,
|
|
@@ -45,8 +50,15 @@ import {
|
|
|
45
50
|
updateUser,
|
|
46
51
|
upsertBudget,
|
|
47
52
|
} from "../be/db";
|
|
48
|
-
import { assertNotLastAdmin, LastAdminError } from "../be/rbac-roles";
|
|
49
53
|
import {
|
|
54
|
+
assertNotLastAdmin,
|
|
55
|
+
getUserGrant,
|
|
56
|
+
LastAdminError,
|
|
57
|
+
listUserRoles,
|
|
58
|
+
setUserRoles,
|
|
59
|
+
} from "../be/rbac-roles";
|
|
60
|
+
import {
|
|
61
|
+
findUserByEmail,
|
|
50
62
|
getUserIdentities,
|
|
51
63
|
type IdentityEvent,
|
|
52
64
|
linkIdentity,
|
|
@@ -57,12 +69,16 @@ import {
|
|
|
57
69
|
revokeToken,
|
|
58
70
|
unlinkIdentity,
|
|
59
71
|
} from "../be/users";
|
|
72
|
+
import { isRbacEnabled } from "../rbac";
|
|
60
73
|
import { UserCommsPrefsSchema, UserSchema } from "../types";
|
|
74
|
+
import { DEFAULT_APP_URL } from "../utils/constants";
|
|
75
|
+
import { assertUrlSafe } from "../utils/egress-guard";
|
|
61
76
|
import { getRequestAuth } from "../utils/request-auth-context";
|
|
62
77
|
import { resolveAppUrl, resolveMcpBaseUrl } from "./config-values";
|
|
63
78
|
import { clientIp, createIpRateLimiter } from "./ip-rate-limit";
|
|
64
79
|
import { getOperatorActor } from "./operator-actor";
|
|
65
80
|
import { route } from "./route-def";
|
|
81
|
+
import { ensureRoleManager } from "./user-roles";
|
|
66
82
|
import { jsonError } from "./utils";
|
|
67
83
|
|
|
68
84
|
// ─── Response composition ────────────────────────────────────────────────────
|
|
@@ -172,6 +188,15 @@ const whoamiRoute = route({
|
|
|
172
188
|
// `components.schemas.User` to `object | null` for every consumer;
|
|
173
189
|
// the union keeps nullability local to this response field.
|
|
174
190
|
user: z.union([UserSchema, z.null()]),
|
|
191
|
+
// Optional so older clients and older servers stay compatible. The
|
|
192
|
+
// dashboard hides what the caller cannot do; the server still gates.
|
|
193
|
+
grants: z
|
|
194
|
+
.object({
|
|
195
|
+
grantsAll: z.boolean(),
|
|
196
|
+
verbs: z.array(z.string()),
|
|
197
|
+
roles: z.array(z.string()),
|
|
198
|
+
})
|
|
199
|
+
.optional(),
|
|
175
200
|
}),
|
|
176
201
|
},
|
|
177
202
|
401: { description: "Unauthorized" },
|
|
@@ -448,6 +473,80 @@ const exchangeConnectorCodeRoute = route({
|
|
|
448
473
|
},
|
|
449
474
|
});
|
|
450
475
|
|
|
476
|
+
const INVITE_ROLES = ["requester", "viewer", "admin"] as const;
|
|
477
|
+
|
|
478
|
+
const createInviteRoute = route({
|
|
479
|
+
method: "post",
|
|
480
|
+
path: "/api/invites",
|
|
481
|
+
pattern: ["api", "invites"],
|
|
482
|
+
summary: "Invite a teammate with a one-time API link",
|
|
483
|
+
description:
|
|
484
|
+
"Creates an invited user with the given role (requester by default) and returns a 7-day single-use API link. The API redirects to the current APP_URL dashboard with the code in the URL fragment. The browser exchanges the code at POST /api/invites/exchange for a personal MCP token. Re-inviting an invited user replaces the role and link. Active or suspended users return 409 user_exists. No email is sent.",
|
|
485
|
+
tags: ["Users"],
|
|
486
|
+
body: z.object({
|
|
487
|
+
email: z.email(),
|
|
488
|
+
name: z.string().trim().min(1).max(200).optional(),
|
|
489
|
+
role: z.enum(INVITE_ROLES).default("requester"),
|
|
490
|
+
}),
|
|
491
|
+
responses: {
|
|
492
|
+
201: {
|
|
493
|
+
description: "The invited user, API link, dashboard URL, expiry and API reachability",
|
|
494
|
+
schema: z.object({
|
|
495
|
+
user: ComposedUserSchema.nullable(),
|
|
496
|
+
inviteUrl: z.string(),
|
|
497
|
+
appUrl: z.string(),
|
|
498
|
+
expiresAt: z.string(),
|
|
499
|
+
apiReachable: z.enum(["public", "local"]),
|
|
500
|
+
}),
|
|
501
|
+
},
|
|
502
|
+
401: { description: "Unauthorized" },
|
|
503
|
+
403: { description: "Not allowed to manage roles" },
|
|
504
|
+
409: { description: "The email belongs to an active or suspended user" },
|
|
505
|
+
},
|
|
506
|
+
auth: { apiKey: true },
|
|
507
|
+
rbac: { permission: "user.role.manage" },
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
const openInviteRoute = route({
|
|
511
|
+
method: "get",
|
|
512
|
+
path: "/invite/{code}",
|
|
513
|
+
pattern: ["invite", null],
|
|
514
|
+
summary: "Open an invite in the configured dashboard",
|
|
515
|
+
description:
|
|
516
|
+
"Redirects to the current APP_URL dashboard with the code in the URL fragment. Does not validate or consume the invite.",
|
|
517
|
+
tags: ["Users"],
|
|
518
|
+
params: z.object({ code: z.string() }),
|
|
519
|
+
responses: {
|
|
520
|
+
302: { description: "Redirect to the dashboard", unstructured: "Dashboard redirect" },
|
|
521
|
+
404: { description: "Malformed code (code_invalid)" },
|
|
522
|
+
},
|
|
523
|
+
auth: { apiKey: false },
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
const exchangeInviteCodeRoute = route({
|
|
527
|
+
method: "post",
|
|
528
|
+
path: "/api/invites/exchange",
|
|
529
|
+
pattern: ["api", "invites", "exchange"],
|
|
530
|
+
summary: "Exchange an invite code for the invited user's MCP token",
|
|
531
|
+
description:
|
|
532
|
+
"Unauthenticated: the single-use invite code is the credential. Rate limited per IP. Activates the invited user. Unknown, expired, used and connector codes, and codes for users who are no longer invited, all return 404 code_invalid.",
|
|
533
|
+
tags: ["Users"],
|
|
534
|
+
body: z.object({ code: z.string().min(1).max(256) }),
|
|
535
|
+
// Public route: cap the body before it is buffered and parsed.
|
|
536
|
+
maxBodyBytes: 1024,
|
|
537
|
+
responses: {
|
|
538
|
+
200: {
|
|
539
|
+
description: "Minted MCP token for the invited user",
|
|
540
|
+
schema: z.object({ token: z.string(), userId: z.string() }),
|
|
541
|
+
},
|
|
542
|
+
404: { description: "Code unknown, malformed, expired, already used or not an invite" },
|
|
543
|
+
413: { description: "Request body too large" },
|
|
544
|
+
429: { description: "Rate limited" },
|
|
545
|
+
},
|
|
546
|
+
auth: { apiKey: false },
|
|
547
|
+
rbac: { ungated: "the one-time invite code is the credential" },
|
|
548
|
+
});
|
|
549
|
+
|
|
451
550
|
const connectorDiscoveryRoute = route({
|
|
452
551
|
method: "get",
|
|
453
552
|
path: "/api/connector/discovery",
|
|
@@ -681,6 +780,35 @@ export function _resetConnectorExchangeRateLimitForTests(): void {
|
|
|
681
780
|
connectorExchangeRateLimiter.reset();
|
|
682
781
|
}
|
|
683
782
|
|
|
783
|
+
/** 10 invite exchanges per minute per client IP. */
|
|
784
|
+
const inviteExchangeRateLimiter = createIpRateLimiter({
|
|
785
|
+
capacity: 10,
|
|
786
|
+
refillPerMs: 10 / 60_000,
|
|
787
|
+
});
|
|
788
|
+
|
|
789
|
+
export function _resetInviteExchangeRateLimitForTests(): void {
|
|
790
|
+
inviteExchangeRateLimiter.reset();
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
/**
|
|
794
|
+
* `local` when teammates' browsers likely cannot reach the API: a loopback or
|
|
795
|
+
* private IP host, a single-label name (a compose service such as
|
|
796
|
+
* `swarm-api`), or a `.local` / `.internal` / `.lan` name.
|
|
797
|
+
*/
|
|
798
|
+
function apiReachability(apiUrl: string): "public" | "local" {
|
|
799
|
+
let host: string;
|
|
800
|
+
try {
|
|
801
|
+
host = assertUrlSafe(apiUrl, { allowInsecure: true }).hostname.toLowerCase();
|
|
802
|
+
} catch {
|
|
803
|
+
return "local";
|
|
804
|
+
}
|
|
805
|
+
if (!host.includes(".") && !host.includes(":")) return "local";
|
|
806
|
+
if (/\.(local|internal|lan|home\.arpa)\.?$/.test(host)) return "local";
|
|
807
|
+
return "public";
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
class InviteUserExistsError extends Error {}
|
|
811
|
+
|
|
684
812
|
// ─── Handler ─────────────────────────────────────────────────────────────────
|
|
685
813
|
|
|
686
814
|
export async function handleUsers(
|
|
@@ -695,9 +823,26 @@ export async function handleUsers(
|
|
|
695
823
|
if (!parsed) return true;
|
|
696
824
|
const auth = getRequestAuth(req);
|
|
697
825
|
if (auth?.kind === "user") {
|
|
698
|
-
|
|
826
|
+
const [grant, roles] = await Promise.all([
|
|
827
|
+
getUserGrant(auth.user.id),
|
|
828
|
+
listUserRoles(auth.user.id),
|
|
829
|
+
]);
|
|
830
|
+
const grantsAll = !isRbacEnabled() || grant.grantsAll;
|
|
831
|
+
whoamiRoute.respond(res, 200, {
|
|
832
|
+
kind: "user",
|
|
833
|
+
user: auth.user,
|
|
834
|
+
grants: {
|
|
835
|
+
grantsAll,
|
|
836
|
+
verbs: grantsAll ? [] : [...grant.verbs].sort(),
|
|
837
|
+
roles: roles.map((role) => role.name),
|
|
838
|
+
},
|
|
839
|
+
});
|
|
699
840
|
} else {
|
|
700
|
-
whoamiRoute.respond(res, 200, {
|
|
841
|
+
whoamiRoute.respond(res, 200, {
|
|
842
|
+
kind: "operator",
|
|
843
|
+
user: null,
|
|
844
|
+
grants: { grantsAll: true, verbs: [], roles: [] },
|
|
845
|
+
});
|
|
701
846
|
}
|
|
702
847
|
return true;
|
|
703
848
|
}
|
|
@@ -934,6 +1079,130 @@ export async function handleUsers(
|
|
|
934
1079
|
return true;
|
|
935
1080
|
}
|
|
936
1081
|
|
|
1082
|
+
// ─── POST /api/invites ─────────────────────────────────────────────────────
|
|
1083
|
+
if (createInviteRoute.match(req.method, pathSegments)) {
|
|
1084
|
+
const parsed = await createInviteRoute.parse(req, res, pathSegments, queryParams);
|
|
1085
|
+
if (!parsed) return true;
|
|
1086
|
+
// Same gate as PUT /api/users/{id}/roles: an invite assigns a role, so a
|
|
1087
|
+
// custom role holding user.role.manage must not hand out admin.
|
|
1088
|
+
if (!(await ensureRoleManager(req, res))) return true;
|
|
1089
|
+
const actor = getOperatorActor(req, res);
|
|
1090
|
+
if (!actor) return true;
|
|
1091
|
+
const { email, name, role } = parsed.body;
|
|
1092
|
+
const actorUserId = actor.kind === "user" ? actor.id : null;
|
|
1093
|
+
|
|
1094
|
+
let invited: { userId: string; code: string; expiresAt: string };
|
|
1095
|
+
try {
|
|
1096
|
+
invited = await getDbClient().transaction(async () => {
|
|
1097
|
+
const existing = await findUserByEmail(email);
|
|
1098
|
+
if (existing && existing.status !== "invited") throw new InviteUserExistsError();
|
|
1099
|
+
let userId: string;
|
|
1100
|
+
if (existing) {
|
|
1101
|
+
userId = existing.id;
|
|
1102
|
+
await deleteUnusedInviteCodes(userId);
|
|
1103
|
+
} else {
|
|
1104
|
+
const user = await createUser({
|
|
1105
|
+
name: name ?? (email.split("@")[0] || email),
|
|
1106
|
+
email,
|
|
1107
|
+
status: "invited",
|
|
1108
|
+
});
|
|
1109
|
+
userId = user.id;
|
|
1110
|
+
await recordIdentityEvent(userId, "identity_added", actor, null, {
|
|
1111
|
+
email,
|
|
1112
|
+
source: "invite",
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1115
|
+
const { before, after } = await setUserRoles(userId, [role], actorUserId);
|
|
1116
|
+
if (before.join(",") !== after.join(",")) {
|
|
1117
|
+
await recordIdentityEvent(
|
|
1118
|
+
userId,
|
|
1119
|
+
"profile_changed",
|
|
1120
|
+
actor,
|
|
1121
|
+
{ rbacRoles: before },
|
|
1122
|
+
{ rbacRoles: after },
|
|
1123
|
+
);
|
|
1124
|
+
}
|
|
1125
|
+
const { code, expiresAt } = await createConnectorCode(
|
|
1126
|
+
userId,
|
|
1127
|
+
DEFAULT_INVITE_CODE_LABEL,
|
|
1128
|
+
actor,
|
|
1129
|
+
new Date(),
|
|
1130
|
+
{ kind: "invite", ttlMs: INVITE_CODE_TTL_MS },
|
|
1131
|
+
);
|
|
1132
|
+
return { userId, code, expiresAt };
|
|
1133
|
+
});
|
|
1134
|
+
} catch (err) {
|
|
1135
|
+
if (err instanceof InviteUserExistsError) {
|
|
1136
|
+
jsonError(res, "user_exists", 409);
|
|
1137
|
+
return true;
|
|
1138
|
+
}
|
|
1139
|
+
// Never echo the error: it must not carry the code or the link.
|
|
1140
|
+
jsonError(res, "Failed to create invite", 500);
|
|
1141
|
+
return true;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
const apiUrl = await resolveMcpBaseUrl();
|
|
1145
|
+
const appUrl = (await resolveAppUrl()) ?? DEFAULT_APP_URL;
|
|
1146
|
+
const inviteUrl = `${apiUrl}/invite/${invited.code}`;
|
|
1147
|
+
createInviteRoute.respond(res, 201, {
|
|
1148
|
+
user: await composeUser(invited.userId),
|
|
1149
|
+
inviteUrl,
|
|
1150
|
+
appUrl,
|
|
1151
|
+
expiresAt: invited.expiresAt,
|
|
1152
|
+
apiReachable: apiReachability(apiUrl),
|
|
1153
|
+
});
|
|
1154
|
+
return true;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
// GET /invite/:code only redirects. Link previews must not consume invites.
|
|
1158
|
+
if (openInviteRoute.match(req.method, pathSegments)) {
|
|
1159
|
+
const parsed = await openInviteRoute.parse(req, res, pathSegments, queryParams);
|
|
1160
|
+
if (!parsed) return true;
|
|
1161
|
+
const { code } = parsed.params;
|
|
1162
|
+
if (!/^[A-Za-z0-9_-]{16,256}$/.test(code)) {
|
|
1163
|
+
jsonError(res, "code_invalid", 404);
|
|
1164
|
+
return true;
|
|
1165
|
+
}
|
|
1166
|
+
const apiUrl = await resolveMcpBaseUrl();
|
|
1167
|
+
const appUrl = (await resolveAppUrl()) ?? DEFAULT_APP_URL;
|
|
1168
|
+
res.writeHead(302, {
|
|
1169
|
+
Location: `${appUrl}/join?swarm=${encodeURIComponent(apiUrl)}#code=${code}`,
|
|
1170
|
+
"Cache-Control": "no-store",
|
|
1171
|
+
"Referrer-Policy": "no-referrer",
|
|
1172
|
+
});
|
|
1173
|
+
res.end();
|
|
1174
|
+
return true;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
// ─── POST /api/invites/exchange ────────────────────────────────────────────
|
|
1178
|
+
if (exchangeInviteCodeRoute.match(req.method, pathSegments)) {
|
|
1179
|
+
if (!inviteExchangeRateLimiter.take(clientIp(req))) {
|
|
1180
|
+
res.setHeader("Retry-After", "6");
|
|
1181
|
+
jsonError(res, "rate_limited", 429);
|
|
1182
|
+
return true;
|
|
1183
|
+
}
|
|
1184
|
+
const parsed = await exchangeInviteCodeRoute.parse(req, res, pathSegments, queryParams);
|
|
1185
|
+
if (!parsed) return true;
|
|
1186
|
+
|
|
1187
|
+
// Same generated shape as connector codes; reject anything else before hashing.
|
|
1188
|
+
if (!CONNECTOR_CODE_PATTERN.test(parsed.body.code)) {
|
|
1189
|
+
jsonError(res, "code_invalid", 404);
|
|
1190
|
+
return true;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
try {
|
|
1194
|
+
const result = await exchangeConnectorCode(parsed.body.code, { kind: "invite" });
|
|
1195
|
+
if (!result) {
|
|
1196
|
+
jsonError(res, "code_invalid", 404);
|
|
1197
|
+
return true;
|
|
1198
|
+
}
|
|
1199
|
+
exchangeInviteCodeRoute.respond(res, 200, result);
|
|
1200
|
+
} catch {
|
|
1201
|
+
jsonError(res, "Failed to exchange code", 500);
|
|
1202
|
+
}
|
|
1203
|
+
return true;
|
|
1204
|
+
}
|
|
1205
|
+
|
|
937
1206
|
// ─── DELETE /api/users/:id/mcp-tokens/:tokenId ────────────────────────────
|
|
938
1207
|
if (revokeUserMcpTokenRoute.match(req.method, pathSegments)) {
|
|
939
1208
|
const parsed = await revokeUserMcpTokenRoute.parse(req, res, pathSegments, queryParams);
|
package/src/http/utils.ts
CHANGED
|
@@ -255,19 +255,38 @@ export function wireHttpSpanLifecycle(
|
|
|
255
255
|
});
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
+
/**
|
|
259
|
+
* Redact invite codes and agent-fs share tokens from logs and traces.
|
|
260
|
+
* Share paths also accept the `/agentfs` proxy prefix.
|
|
261
|
+
*/
|
|
262
|
+
export function redactShareTokenInPath(pathname: string): string {
|
|
263
|
+
const segments = pathname.split("/");
|
|
264
|
+
if (segments[1] === "invite" && segments[2]) {
|
|
265
|
+
segments[2] = "[REDACTED]";
|
|
266
|
+
return segments.join("/");
|
|
267
|
+
}
|
|
268
|
+
const at = segments[1] === "agentfs" ? 2 : 1;
|
|
269
|
+
if ((segments[at] === "share" || segments[at] === "site") && segments[at + 1]) {
|
|
270
|
+
segments[at + 1] = "[REDACTED]";
|
|
271
|
+
return segments.join("/");
|
|
272
|
+
}
|
|
273
|
+
return pathname;
|
|
274
|
+
}
|
|
275
|
+
|
|
258
276
|
export function safeRequestUrlForLog(rawUrl: string | undefined): string {
|
|
259
277
|
if (!rawUrl) return "";
|
|
260
278
|
|
|
261
279
|
try {
|
|
262
280
|
const url = new URL(rawUrl, "http://localhost");
|
|
281
|
+
const pathname = redactShareTokenInPath(url.pathname);
|
|
263
282
|
const params = Array.from(url.searchParams.keys());
|
|
264
|
-
if (params.length === 0) return
|
|
283
|
+
if (params.length === 0) return pathname;
|
|
265
284
|
|
|
266
285
|
const redactedQuery = params.map((key) => `${key}=[REDACTED]`).join("&");
|
|
267
|
-
return `${
|
|
286
|
+
return `${pathname}?${redactedQuery}`;
|
|
268
287
|
} catch {
|
|
269
288
|
const pathOnly = rawUrl.split("?")[0] || rawUrl;
|
|
270
|
-
return scrubSecrets(pathOnly);
|
|
289
|
+
return scrubSecrets(redactShareTokenInPath(pathOnly));
|
|
271
290
|
}
|
|
272
291
|
}
|
|
273
292
|
|
package/src/http/workflows.ts
CHANGED
|
@@ -307,6 +307,15 @@ const triggerWorkflowRoute = route({
|
|
|
307
307
|
"With an `Idempotency-Key`, a retry returns the accepted run's `runId` and `skipped` without starting, resuming or waiting for anything, even after the workflow was disabled or edited and whatever state the run is in now.",
|
|
308
308
|
tags: ["Workflows"],
|
|
309
309
|
params: z.object({ id: z.string() }),
|
|
310
|
+
query: z.object({
|
|
311
|
+
wait: z
|
|
312
|
+
.enum(["true", "false"])
|
|
313
|
+
.optional()
|
|
314
|
+
.describe(
|
|
315
|
+
"Default true: respond after the run's instant steps ran, when it finishes or parks at its first async step. " +
|
|
316
|
+
"false: respond as soon as the run is committed and walk it in the background.",
|
|
317
|
+
),
|
|
318
|
+
}),
|
|
310
319
|
headers: idempotencyKeyHeader("starts one run"),
|
|
311
320
|
responses: {
|
|
312
321
|
201: {
|
|
@@ -1002,6 +1011,7 @@ export async function handleWorkflows(
|
|
|
1002
1011
|
runId = await startWorkflowExecution(workflow, body, getExecutorRegistry(), {
|
|
1003
1012
|
triggerType: "api",
|
|
1004
1013
|
requestedByUserId: (await resolveHttpAuditUserId(req, myAgentId)) ?? undefined,
|
|
1014
|
+
detach: parsed.query.wait === "false",
|
|
1005
1015
|
// Recorded in the run's own transaction, before the walk starts.
|
|
1006
1016
|
accept: claim
|
|
1007
1017
|
? async (run) => {
|
package/src/http/x.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
MAX_SCRIPT_WALL_CLOCK_MS,
|
|
21
21
|
MIN_SCRIPT_WALL_CLOCK_MS,
|
|
22
22
|
} from "../scripts-runtime/executors/types";
|
|
23
|
+
import { HOST_VALIDATED_ARGS } from "../scripts-runtime/host-validated-args";
|
|
23
24
|
import type { RunScriptOutput } from "../scripts-runtime/loader";
|
|
24
25
|
import { scrubObject, scrubSecrets } from "../utils/secret-scrubber";
|
|
25
26
|
import { validateJsonSchema } from "../workflows/json-schema-validator";
|
|
@@ -214,6 +215,7 @@ export async function handleX(
|
|
|
214
215
|
// Scripts predating the schema column, or where extraction failed, have a
|
|
215
216
|
// null schema — we skip and let the in-subprocess Zod check surface as a
|
|
216
217
|
// runtime_error instead.
|
|
218
|
+
let hostValidatedArgs = false;
|
|
217
219
|
if (script.argsJsonSchema) {
|
|
218
220
|
try {
|
|
219
221
|
const schema = JSON.parse(script.argsJsonSchema) as Record<string, unknown>;
|
|
@@ -227,6 +229,7 @@ export async function handleX(
|
|
|
227
229
|
});
|
|
228
230
|
return true;
|
|
229
231
|
}
|
|
232
|
+
hostValidatedArgs = true;
|
|
230
233
|
} catch {
|
|
231
234
|
// Malformed stored schema — don't block execution on it.
|
|
232
235
|
}
|
|
@@ -245,7 +248,9 @@ export async function handleX(
|
|
|
245
248
|
const output = await runScriptWithRunCredential({
|
|
246
249
|
source: script.source,
|
|
247
250
|
args: args ?? null,
|
|
251
|
+
[HOST_VALIDATED_ARGS]: hostValidatedArgs,
|
|
248
252
|
fsMode: "none",
|
|
253
|
+
executor: script.executor,
|
|
249
254
|
agentId: endpoint.agentId,
|
|
250
255
|
// timeoutMs only raises the wall-clock limit (network-bound scripts can use
|
|
251
256
|
// the full window); the CPU ulimit (`ulimit -t`) is intentionally left at
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
import type { ProviderTraits } from "../providers/types";
|
|
23
23
|
import { getSlackConfiguration } from "../slack/config";
|
|
24
24
|
import type { ProviderName } from "../types";
|
|
25
|
+
import { resolveAgentFsLinkBase } from "../utils/constants";
|
|
25
26
|
import { isSteeringEnabled } from "../utils/steering-enabled";
|
|
26
27
|
import { matchesDefaultClaudeMd, matchesDefaultIdentityMd } from "./defaults";
|
|
27
28
|
import { resolveTemplateAsync } from "./resolver";
|
|
@@ -74,6 +75,26 @@ function claudeMdSourcePath(provider: string): string {
|
|
|
74
75
|
/** Providers that get the repo CLAUDE.md inlined until native loading is verified. */
|
|
75
76
|
const REPO_CLAUDE_MD_INLINE_PROVIDERS: ReadonlySet<string> = new Set(["opencode"]);
|
|
76
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Template event for the tool-discovery line. Amp, Codex and OpenCode run MCP
|
|
80
|
+
* tools inside their own code sandbox (`code_exec`, `exec`, `execute`), each
|
|
81
|
+
* with its own call shape, so the generic lines are wrong for them.
|
|
82
|
+
*/
|
|
83
|
+
function toolDiscoveryEvent(provider: string, hasToolSearch: boolean): string {
|
|
84
|
+
switch (provider) {
|
|
85
|
+
case "amp":
|
|
86
|
+
return "system.agent.tool_discovery.amp";
|
|
87
|
+
case "codex":
|
|
88
|
+
return "system.agent.tool_discovery.codex";
|
|
89
|
+
case "opencode":
|
|
90
|
+
return "system.agent.tool_discovery.opencode";
|
|
91
|
+
default:
|
|
92
|
+
return hasToolSearch
|
|
93
|
+
? "system.agent.tool_discovery.search"
|
|
94
|
+
: "system.agent.tool_discovery.direct";
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
77
98
|
export function areSlackPromptToolsEnabled(
|
|
78
99
|
env: NodeJS.ProcessEnv = process.env,
|
|
79
100
|
serverCapabilities?: string[],
|
|
@@ -229,7 +250,8 @@ export const getBasePrompt = async (args: BasePromptArgs): Promise<string> => {
|
|
|
229
250
|
const agentFsConfigured = hasLocalEnv && Boolean(process.env.AGENT_FS_API_URL);
|
|
230
251
|
const outputsResult = await resolveTemplateAsync(
|
|
231
252
|
agentFsConfigured ? "system.agent.outputs" : "system.agent.outputs.no_agent_fs",
|
|
232
|
-
|
|
253
|
+
// Unset AGENT_FS_LINK_TARGET keeps the literal the agent expands in its shell.
|
|
254
|
+
{ fileLinkBase: resolveAgentFsLinkBase("prompt") ?? `\${AGENT_FS_LIVE_URL}` },
|
|
233
255
|
);
|
|
234
256
|
prompt += outputsResult.text;
|
|
235
257
|
}
|
|
@@ -257,26 +279,29 @@ export const getBasePrompt = async (args: BasePromptArgs): Promise<string> => {
|
|
|
257
279
|
|
|
258
280
|
// I. Tools and skills. Skipped without MCP: the discovery tools are MCP tools.
|
|
259
281
|
if (hasMcp) {
|
|
260
|
-
|
|
261
|
-
// underscored names, so the generic "load with your tool search" line is not enough.
|
|
262
|
-
const discoveryEvent =
|
|
263
|
-
provider === "amp"
|
|
264
|
-
? "system.agent.tool_discovery.amp"
|
|
265
|
-
: hasToolSearch
|
|
266
|
-
? "system.agent.tool_discovery.search"
|
|
267
|
-
: "system.agent.tool_discovery.direct";
|
|
282
|
+
const discoveryEvent = toolDiscoveryEvent(provider, hasToolSearch);
|
|
268
283
|
const discoveryResult = await resolveTemplateAsync(discoveryEvent, {});
|
|
269
284
|
const toolsResult = await resolveTemplateAsync(
|
|
270
285
|
"system.agent.tools_skills",
|
|
271
286
|
renderToolsAndSkillsVars({
|
|
272
287
|
skillsSummary: args.skillsSummary,
|
|
273
288
|
mcpServers: args.mcpServers,
|
|
289
|
+
// pi defers installed MCP servers together with swarm tools.
|
|
290
|
+
mcpToolsDeferred: provider === "pi" && hasToolSearch,
|
|
274
291
|
nativeSkillDiscovery,
|
|
275
292
|
toolDiscovery: discoveryResult.text,
|
|
276
293
|
hasLocalEnv,
|
|
277
294
|
}),
|
|
278
295
|
);
|
|
279
296
|
prompt += toolsResult.text;
|
|
297
|
+
|
|
298
|
+
// Lead docs pointer: only when the lead has the `swarm-docs` skill. The
|
|
299
|
+
// skill list is unknown on claude-managed (the cloud sandbox owns skill
|
|
300
|
+
// delivery) and before the first skill load, so render it then.
|
|
301
|
+
if (role === "lead" && hasSwarmDocsSkill(args.skillsSummary)) {
|
|
302
|
+
const docsResult = await resolveTemplateAsync("system.agent.lead.docs", {});
|
|
303
|
+
prompt += docsResult.text;
|
|
304
|
+
}
|
|
280
305
|
}
|
|
281
306
|
|
|
282
307
|
// J. Agent notes (CLAUDE.md), only when it differs from the generated
|
|
@@ -339,6 +364,11 @@ export const getTaskContextPrompt = async (args: TaskContextPromptArgs): Promise
|
|
|
339
364
|
return prompt.replace(/\n{3,}/g, "\n\n").trim();
|
|
340
365
|
};
|
|
341
366
|
|
|
367
|
+
/** True when the skill list includes `swarm-docs`, or when the list is unknown. */
|
|
368
|
+
function hasSwarmDocsSkill(skills: { name: string }[] | undefined): boolean {
|
|
369
|
+
return skills === undefined || skills.some((skill) => skill.name === "swarm-docs");
|
|
370
|
+
}
|
|
371
|
+
|
|
342
372
|
/**
|
|
343
373
|
* Dynamic lines for `system.agent.tools_skills`. The static text lives in the
|
|
344
374
|
* template so operators can override or skip the section; only the lists that
|
|
@@ -347,6 +377,7 @@ export const getTaskContextPrompt = async (args: TaskContextPromptArgs): Promise
|
|
|
347
377
|
function renderToolsAndSkillsVars(input: {
|
|
348
378
|
skillsSummary?: { name: string; description: string }[];
|
|
349
379
|
mcpServers?: string[];
|
|
380
|
+
mcpToolsDeferred?: boolean;
|
|
350
381
|
nativeSkillDiscovery: boolean;
|
|
351
382
|
hasLocalEnv: boolean;
|
|
352
383
|
toolDiscovery: string;
|
|
@@ -382,7 +413,11 @@ function renderToolsAndSkillsVars(input: {
|
|
|
382
413
|
const servers = (input.mcpServers ?? []).filter((name) => name.trim().length > 0);
|
|
383
414
|
const mcpLine =
|
|
384
415
|
servers.length > 0
|
|
385
|
-
? `Connected MCP servers: ${servers.join(", ")}.
|
|
416
|
+
? `Connected MCP servers: ${servers.join(", ")}. ${
|
|
417
|
+
input.mcpToolsDeferred
|
|
418
|
+
? "Load their tools with your harness tool search."
|
|
419
|
+
: "Their tools are in your tool list."
|
|
420
|
+
}\n`
|
|
386
421
|
: "";
|
|
387
422
|
|
|
388
423
|
return {
|
|
@@ -15,8 +15,9 @@
|
|
|
15
15
|
* G secrets system.agent.secrets
|
|
16
16
|
*
|
|
17
17
|
* Deployment-gated blocks (E outputs, H slack, steering) and the per-agent and
|
|
18
|
-
* per-task sections (tools and skills, agent notes,
|
|
19
|
-
* profile) are appended by base-prompt.ts after the
|
|
18
|
+
* per-task sections (tools and skills, the lead docs pointer, agent notes,
|
|
19
|
+
* repository, requester profile) are appended by base-prompt.ts after the
|
|
20
|
+
* composite.
|
|
20
21
|
*
|
|
21
22
|
* Writing rules for every body: sentence case headings, one idea per sentence,
|
|
22
23
|
* active voice, plain words, no em dashes, a prohibition only where no positive
|
|
@@ -109,6 +110,19 @@ Your heartbeat runbook is the \`heartbeatMd\` profile field. Edit it with \`upda
|
|
|
109
110
|
category: "system",
|
|
110
111
|
});
|
|
111
112
|
|
|
113
|
+
// Lead docs pointer. base-prompt.ts appends it after the tools and skills
|
|
114
|
+
// section when the lead has the `swarm-docs` skill (or when the skill list is
|
|
115
|
+
// unknown). An operator override with an empty body switches it off.
|
|
116
|
+
registerTemplate({
|
|
117
|
+
eventType: "system.agent.lead.docs",
|
|
118
|
+
header: "",
|
|
119
|
+
defaultBody: `
|
|
120
|
+
Load the \`swarm-docs\` skill only when a person asks for something no skill, workflow, schedule or memory of yours covers, or asks where something is documented. Then follow its offer rules.
|
|
121
|
+
`,
|
|
122
|
+
variables: [],
|
|
123
|
+
category: "system",
|
|
124
|
+
});
|
|
125
|
+
|
|
112
126
|
// Remote providers (no MCP, no container): the task arrives in the session
|
|
113
127
|
// prompt and the final message is the output. No tools to name.
|
|
114
128
|
registerTemplate({
|
|
@@ -168,7 +182,7 @@ registerTemplate({
|
|
|
168
182
|
## Memory
|
|
169
183
|
|
|
170
184
|
Memories from past sessions are in your task message. Read them before you start. For a wider search, run the \`task-context-gathering\` script with the task ID and two to four queries.
|
|
171
|
-
|
|
185
|
+
When you learn something that would change how the next agent works (a fix, a pattern, a gotcha), pass it as \`learning\` on a \`store-progress\` call before the task ends. Completed task outputs are stored for you.
|
|
172
186
|
You MUST use the \`memory\` skill before you store, edit, or delete a memory.
|
|
173
187
|
`,
|
|
174
188
|
variables: [],
|
|
@@ -202,9 +216,15 @@ agent-fs is the shared drive between agents and people. Documents (reports, dige
|
|
|
202
216
|
Pages: only for polished artifacts shared with a wider audience or explicitly requested; see \`pages\` skill.
|
|
203
217
|
A tool a person will use, with data and actions: build an app. See the \`apps\` skill.
|
|
204
218
|
Link env: \`APP_URL\` for pages, \`MCP_BASE_URL\` for the API, \`AGENT_FS_LIVE_URL\` for files. Report missing variables.
|
|
205
|
-
Give people the live app link
|
|
219
|
+
Give people the live app link \`{{fileLinkBase}}/file/~/<org>/<drive>/<path>\`. Make an \`agent-fs share-create\` link only when the requester asks for one or the reader has no agent-fs login.
|
|
206
220
|
`,
|
|
207
|
-
variables: [
|
|
221
|
+
variables: [
|
|
222
|
+
{
|
|
223
|
+
name: "fileLinkBase",
|
|
224
|
+
description:
|
|
225
|
+
"Base URL of people-facing file links (AGENT_FS_LINK_TARGET); the literal AGENT_FS_LIVE_URL shell reference when unset",
|
|
226
|
+
},
|
|
227
|
+
],
|
|
208
228
|
category: "system",
|
|
209
229
|
});
|
|
210
230
|
|
|
@@ -428,6 +448,24 @@ registerTemplate({
|
|
|
428
448
|
category: "system",
|
|
429
449
|
});
|
|
430
450
|
|
|
451
|
+
registerTemplate({
|
|
452
|
+
eventType: "system.agent.tool_discovery.codex",
|
|
453
|
+
header: "",
|
|
454
|
+
defaultBody:
|
|
455
|
+
"Swarm tools are not in your tool list. Call them from `exec` on the `tools` object, with dashes turned into underscores: `store-progress` is `await tools.mcp__agent_swarm__store_progress({ ... })`. Call a tool you know by that name directly. To find one, filter `ALL_TOOLS` by `name` and `description`.",
|
|
456
|
+
variables: [],
|
|
457
|
+
category: "system",
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
registerTemplate({
|
|
461
|
+
eventType: "system.agent.tool_discovery.opencode",
|
|
462
|
+
header: "",
|
|
463
|
+
defaultBody:
|
|
464
|
+
'Swarm tools are not in your tool list. Call them from `execute` under `tools.swarm`, by their exact names: `store-progress` is `await tools.swarm["store-progress"]({ ... })`. To find one, use `search({ query })` inside `execute`.',
|
|
465
|
+
variables: [],
|
|
466
|
+
category: "system",
|
|
467
|
+
});
|
|
468
|
+
|
|
431
469
|
registerTemplate({
|
|
432
470
|
eventType: "system.agent.tool_discovery.direct",
|
|
433
471
|
header: "",
|
|
@@ -45,7 +45,7 @@ import type {
|
|
|
45
45
|
* default; the image installs exactly this version and the per-task settings
|
|
46
46
|
* set `amp.updates.mode: "disabled"`. Bump it deliberately, with a live run.
|
|
47
47
|
*/
|
|
48
|
-
export const AMP_PACKAGE = "@ampcode/cli@0.0.
|
|
48
|
+
export const AMP_PACKAGE = "@ampcode/cli@0.0.1793073650-g23dd39";
|
|
49
49
|
|
|
50
50
|
/** MCP server name. Amp exposes its tools as `agent_swarm` / `store_progress` (dashes become underscores). */
|
|
51
51
|
const AMP_MCP_SERVER_NAME = "agent-swarm";
|
|
@@ -1613,8 +1613,13 @@ export class CodexSession implements ProviderSession {
|
|
|
1613
1613
|
"invalid 'refresh_token'",
|
|
1614
1614
|
];
|
|
1615
1615
|
if (authPatterns.some((p) => normalized.includes(p))) {
|
|
1616
|
+
const slot = this.config.codexSlot;
|
|
1617
|
+
const guidance =
|
|
1618
|
+
slot === undefined
|
|
1619
|
+
? "Codex authentication failed — check OPENAI_API_KEY or ChatGPT login."
|
|
1620
|
+
: `Codex pool slot ${slot} authentication failed — re-run agent-swarm codex-login --slot ${slot}.`;
|
|
1616
1621
|
return {
|
|
1617
|
-
message: `[auth-error]
|
|
1622
|
+
message: `[auth-error] ${guidance} Original error: ${raw}`,
|
|
1618
1623
|
category: "authentication_failed",
|
|
1619
1624
|
};
|
|
1620
1625
|
}
|
|
@@ -35,7 +35,7 @@ import type {
|
|
|
35
35
|
} from "./types";
|
|
36
36
|
|
|
37
37
|
/** Pinned in package.json and Dockerfile.worker; bump all three together. */
|
|
38
|
-
export const CURSOR_SDK_VERSION = "1.0
|
|
38
|
+
export const CURSOR_SDK_VERSION = "1.1.0";
|
|
39
39
|
|
|
40
40
|
/** MCP server name; Cursor reports its tools as `mcp` calls with this `providerIdentifier`. */
|
|
41
41
|
const CURSOR_MCP_SERVER_NAME = "agent-swarm";
|